refactor: UI 컴포넌트 추상화 및 입금/출금 등록 버튼 추가

- 입금관리, 출금관리 리스트에 등록 버튼 추가
- skeleton, confirm-dialog, empty-state, status-badge UI 컴포넌트 추가
- document-system 컴포넌트 추상화 (ApprovalLine, DocumentHeader 등)
- 여러 페이지 컴포넌트 리팩토링 및 코드 정리

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
유병철
2026-01-22 17:21:42 +09:00
parent 777dccc7bd
commit 269b901e64
86 changed files with 3761 additions and 2614 deletions

View File

@@ -43,6 +43,7 @@ import {
AlertDialogHeader,
AlertDialogTitle,
} from '@/components/ui/alert-dialog';
import { ConfirmDialog } from '@/components/ui/confirm-dialog';
import {
UniversalListPage,
type UniversalListConfig,
@@ -690,20 +691,15 @@ export function ApprovalBox() {
renderDialogs: () => (
<>
{/* 승인 확인 다이얼로그 */}
<AlertDialog open={approveDialogOpen} onOpenChange={setApproveDialogOpen}>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle> </AlertDialogTitle>
<AlertDialogDescription>
{pendingSelectedItems.size} ?
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel></AlertDialogCancel>
<AlertDialogAction onClick={handleApproveConfirm}></AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
<ConfirmDialog
open={approveDialogOpen}
onOpenChange={setApproveDialogOpen}
onConfirm={handleApproveConfirm}
title="결재 승인"
description={`정말 ${pendingSelectedItems.size}건을 승인하시겠습니까?`}
variant="success"
confirmText="승인"
/>
{/* 반려 확인 다이얼로그 */}
<AlertDialog open={rejectDialogOpen} onOpenChange={setRejectDialogOpen}>

View File

@@ -1,8 +1,16 @@
'use client';
/**
* 지출 예상 내역서 문서 컴포넌트
*
* 공통 컴포넌트 사용:
* - DocumentHeader: centered 레이아웃 + customApproval (ApprovalLineBox)
*/
import { Fragment } from 'react';
import { ApprovalLineBox } from './ApprovalLineBox';
import type { ExpenseEstimateDocumentData } from './types';
import { DocumentHeader } from '@/components/document-system';
interface ExpenseEstimateDocumentProps {
data: ExpenseEstimateDocumentData;
@@ -34,18 +42,13 @@ export function ExpenseEstimateDocument({ data }: ExpenseEstimateDocumentProps)
return (
<div className="bg-white p-8 min-h-full">
{/* 문서 헤더 */}
<div className="flex justify-between items-start mb-6">
<div className="flex-1">
<h1 className="text-2xl font-bold text-center mb-2"> </h1>
<p className="text-sm text-gray-600 text-center">
: {data.documentNo} | : {data.createdAt}
</p>
</div>
<div className="ml-4">
<ApprovalLineBox drafter={data.drafter} approvers={data.approvers} />
</div>
</div>
{/* 문서 헤더 (공통 컴포넌트) */}
<DocumentHeader
title="지출 예상 내역서"
subtitle={`문서번호: ${data.documentNo} | 작성일자: ${data.createdAt}`}
layout="centered"
customApproval={<ApprovalLineBox drafter={data.drafter} approvers={data.approvers} />}
/>
{/* 문서 내용 */}
<div className="border border-gray-300">

View File

@@ -1,7 +1,15 @@
'use client';
/**
* 지출결의서 문서 컴포넌트
*
* 공통 컴포넌트 사용:
* - DocumentHeader: centered 레이아웃 + customApproval (ApprovalLineBox)
*/
import { ApprovalLineBox } from './ApprovalLineBox';
import type { ExpenseReportDocumentData } from './types';
import { DocumentHeader } from '@/components/document-system';
interface ExpenseReportDocumentProps {
data: ExpenseReportDocumentData;
@@ -14,18 +22,13 @@ export function ExpenseReportDocument({ data }: ExpenseReportDocumentProps) {
return (
<div className="bg-white p-8 min-h-full">
{/* 문서 헤더 */}
<div className="flex justify-between items-start mb-6">
<div className="flex-1">
<h1 className="text-2xl font-bold text-center mb-2"></h1>
<p className="text-sm text-gray-600 text-center">
: {data.documentNo} | : {data.createdAt}
</p>
</div>
<div className="ml-4">
<ApprovalLineBox drafter={data.drafter} approvers={data.approvers} />
</div>
</div>
{/* 문서 헤더 (공통 컴포넌트) */}
<DocumentHeader
title="지출결의서"
subtitle={`문서번호: ${data.documentNo} | 작성일자: ${data.createdAt}`}
layout="centered"
customApproval={<ApprovalLineBox drafter={data.drafter} approvers={data.approvers} />}
/>
{/* 문서 내용 */}
<div className="border border-gray-300">

View File

@@ -1,7 +1,15 @@
'use client';
/**
* 품의서 문서 컴포넌트
*
* 공통 컴포넌트 사용:
* - DocumentHeader: centered 레이아웃 + customApproval (ApprovalLineBox)
*/
import { ApprovalLineBox } from './ApprovalLineBox';
import type { ProposalDocumentData } from './types';
import { DocumentHeader } from '@/components/document-system';
interface ProposalDocumentProps {
data: ProposalDocumentData;
@@ -14,18 +22,13 @@ export function ProposalDocument({ data }: ProposalDocumentProps) {
return (
<div className="bg-white p-8 min-h-full">
{/* 문서 헤더 */}
<div className="flex justify-between items-start mb-6">
<div className="flex-1">
<h1 className="text-2xl font-bold text-center mb-2"></h1>
<p className="text-sm text-gray-600 text-center">
: {data.documentNo} | : {data.createdAt}
</p>
</div>
<div className="ml-4">
<ApprovalLineBox drafter={data.drafter} approvers={data.approvers} />
</div>
</div>
{/* 문서 헤더 (공통 컴포넌트) */}
<DocumentHeader
title="품의서"
subtitle={`문서번호: ${data.documentNo} | 작성일자: ${data.createdAt}`}
layout="centered"
customApproval={<ApprovalLineBox drafter={data.drafter} approvers={data.approvers} />}
/>
{/* 문서 내용 */}
<div className="border border-gray-300">

View File

@@ -25,16 +25,7 @@ import {
SelectTrigger,
SelectValue,
} from '@/components/ui/select';
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
} from '@/components/ui/alert-dialog';
import { ConfirmDialog } from '@/components/ui/confirm-dialog';
import {
UniversalListPage,
type UniversalListConfig,
@@ -595,40 +586,22 @@ export function ReferenceBox() {
renderDialogs: () => (
<>
{/* 열람 처리 확인 다이얼로그 */}
<AlertDialog open={markReadDialogOpen} onOpenChange={setMarkReadDialogOpen}>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle> </AlertDialogTitle>
<AlertDialogDescription>
{selectedItems.size} ?
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel></AlertDialogCancel>
<AlertDialogAction onClick={handleMarkReadConfirm}>
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
<ConfirmDialog
open={markReadDialogOpen}
onOpenChange={setMarkReadDialogOpen}
onConfirm={handleMarkReadConfirm}
title="열람 처리"
description={`정말 ${selectedItems.size}건을 열람 처리하시겠습니까?`}
/>
{/* 미열람 처리 확인 다이얼로그 */}
<AlertDialog open={markUnreadDialogOpen} onOpenChange={setMarkUnreadDialogOpen}>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle> </AlertDialogTitle>
<AlertDialogDescription>
{selectedItems.size} ?
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel></AlertDialogCancel>
<AlertDialogAction onClick={handleMarkUnreadConfirm}>
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
<ConfirmDialog
open={markUnreadDialogOpen}
onOpenChange={setMarkUnreadDialogOpen}
onConfirm={handleMarkUnreadConfirm}
title="미열람 처리"
description={`정말 ${selectedItems.size}건을 미열람 처리하시겠습니까?`}
/>
{/* 문서 상세 모달 */}
{selectedDocument && (