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

@@ -35,16 +35,7 @@ import {
DialogContent,
DialogTitle,
} from '@/components/ui/dialog';
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
} from '@/components/ui/alert-dialog';
import { DeleteConfirmDialog } from '@/components/ui/confirm-dialog';
import {
DialogDescription,
DialogFooter,
@@ -557,35 +548,20 @@ export function ShipmentDetail({ id }: ShipmentDetailProps) {
</Dialog>
{/* 삭제 확인 다이얼로그 */}
<AlertDialog open={showDeleteDialog} onOpenChange={setShowDeleteDialog}>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle> </AlertDialogTitle>
<AlertDialogDescription>
{detail?.shipmentNo}() ?
<br />
.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel disabled={isDeleting}></AlertDialogCancel>
<AlertDialogAction
onClick={handleDelete}
disabled={isDeleting}
className="bg-red-600 hover:bg-red-700"
>
{isDeleting ? (
<>
<Loader2 className="w-4 h-4 mr-2 animate-spin" />
...
</>
) : (
'삭제'
)}
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
<DeleteConfirmDialog
open={showDeleteDialog}
onOpenChange={setShowDeleteDialog}
onConfirm={handleDelete}
title="출하 정보 삭제"
description={
<>
{detail?.shipmentNo}() ?
<br />
.
</>
}
loading={isDeleting}
/>
{/* 상태 변경 다이얼로그 */}
<Dialog open={showStatusDialog} onOpenChange={setShowStatusDialog}>

View File

@@ -2,9 +2,13 @@
/**
* 납품확인서 미리보기/인쇄 문서
*
* 공통 컴포넌트 사용:
* - DocumentHeader: default 레이아웃 + 4col 결재란
*/
import type { ShipmentDetail } from '../types';
import { DocumentHeader } from '@/components/document-system';
interface DeliveryConfirmationProps {
data: ShipmentDetail;
@@ -13,44 +17,18 @@ interface DeliveryConfirmationProps {
export function DeliveryConfirmation({ data }: DeliveryConfirmationProps) {
return (
<div className="bg-white p-8 max-w-3xl mx-auto text-sm print:p-0 print:max-w-none">
{/* 헤더 */}
<div className="flex justify-between items-start mb-6">
<div className="flex items-center gap-4">
<div className="text-2xl font-bold">KD</div>
<div>
<div className="text-xs"></div>
</div>
</div>
<div className="text-2xl font-bold tracking-[1rem]"> </div>
<table className="text-xs border-collapse">
<tbody>
{/* 헤더: 결재 + 작성/검토/승인 */}
<tr>
<td className="border px-2 py-1 bg-muted" rowSpan={3}>
<div className="flex flex-col items-center justify-center h-full">
<span></span>
<span></span>
</div>
</td>
<td className="border px-2 py-1 bg-muted text-center w-16"></td>
<td className="border px-2 py-1 bg-muted text-center w-16"></td>
<td className="border px-2 py-1 bg-muted text-center w-16"></td>
</tr>
{/* 내용: 서명란 */}
<tr>
<td className="border px-2 py-1 text-center h-10"></td>
<td className="border px-2 py-1 text-center h-10"></td>
<td className="border px-2 py-1 text-center h-10"></td>
</tr>
{/* 부서 */}
<tr>
<td className="border px-2 py-1 text-center bg-muted/50">/<br/></td>
<td className="border px-2 py-1 text-center bg-muted/50"></td>
<td className="border px-2 py-1 text-center bg-muted/50"></td>
</tr>
</tbody>
</table>
</div>
{/* 문서 헤더 (공통 컴포넌트) */}
<DocumentHeader
title="납 품 확 인 서"
logo={{ text: 'KD', subtext: '경동기업' }}
layout="default"
approval={{
type: '4col',
showDepartment: true,
departmentLabels: { writer: '판매/전산', reviewer: '출하', approver: '품질' },
}}
className="mb-6"
/>
{/* 출하 관리부서 */}
<div className="text-xs text-muted-foreground mb-2"> </div>

View File

@@ -2,9 +2,13 @@
/**
* 출고증 미리보기/인쇄 문서
*
* 공통 컴포넌트 사용:
* - DocumentHeader: default 레이아웃 + 4col 결재란
*/
import type { ShipmentDetail } from '../types';
import { DocumentHeader } from '@/components/document-system';
interface ShippingSlipProps {
data: ShipmentDetail;
@@ -13,49 +17,19 @@ interface ShippingSlipProps {
export function ShippingSlip({ data }: ShippingSlipProps) {
return (
<div className="bg-white p-8 max-w-4xl mx-auto text-sm print:p-0 print:max-w-none">
{/* 헤더 */}
<div className="flex justify-between items-start mb-6 border-b pb-4">
<div className="flex items-center gap-4">
<div className="text-2xl font-bold">KD</div>
<div>
<div className="text-xs text-muted-foreground"></div>
<div className="text-xs text-muted-foreground">KYUNGDONG COMPANY</div>
</div>
</div>
<div className="text-2xl font-bold tracking-widest"> </div>
<table className="text-xs border-collapse">
<tbody>
{/* 헤더: 결재 + 작성/검토/승인 */}
<tr>
<td className="border px-2 py-1 bg-muted" rowSpan={3}>
<div className="flex flex-col items-center justify-center h-full">
<span></span>
<span></span>
</div>
</td>
<td className="border px-2 py-1 bg-muted text-center w-20"></td>
<td className="border px-2 py-1 bg-muted text-center w-16"></td>
<td className="border px-2 py-1 bg-muted text-center w-16"></td>
</tr>
{/* 내용: 담당자 정보 */}
<tr>
<td className="border px-2 py-1 text-center">
<div>1 </div>
<div></div>
<div className="text-muted-foreground">12-20</div>
</td>
<td className="border px-2 py-1 text-center"></td>
<td className="border px-2 py-1 text-center"></td>
</tr>
{/* 부서 */}
<tr>
<td className="border px-2 py-1 text-center bg-muted/50">/</td>
<td className="border px-2 py-1 text-center bg-muted/50"></td>
<td className="border px-2 py-1 text-center bg-muted/50"></td>
</tr>
</tbody>
</table>
</div>
{/* 문서 헤더 (공통 컴포넌트) */}
<DocumentHeader
title="출 고 증"
logo={{ text: 'KD', subtext: '경동기업' }}
layout="default"
approval={{
type: '4col',
writer: { name: '판매1팀 임', date: '12-20' },
showDepartment: true,
departmentLabels: { writer: '판매/전진', reviewer: '출하', approver: '생산관리' },
}}
className="mb-6 border-b pb-4"
/>
{/* 출하 관리 */}
<div className="text-xs text-muted-foreground mb-2"> </div>

View File

@@ -2,9 +2,13 @@
/**
* 거래명세서 미리보기/인쇄 문서
*
* 공통 컴포넌트 사용:
* - DocumentHeader: simple 레이아웃 (결재란 없음)
*/
import type { ShipmentDetail } from '../types';
import { DocumentHeader } from '@/components/document-system';
interface TransactionStatementProps {
data: ShipmentDetail;
@@ -19,13 +23,14 @@ export function TransactionStatement({ data }: TransactionStatementProps) {
return (
<div className="bg-white p-8 max-w-3xl mx-auto text-sm print:p-0 print:max-w-none">
{/* 제목 */}
<h1 className="text-2xl font-bold text-center tracking-[1rem] mb-8">
</h1>
<p className="text-center text-xs text-muted-foreground mb-6">
TRANSACTION STATEMENT
</p>
{/* 문서 헤더 (공통 컴포넌트) */}
<DocumentHeader
title="거 래 명 세 서"
subtitle="TRANSACTION STATEMENT"
layout="simple"
approval={null}
className="mb-6"
/>
{/* 공급받는자 / 공급자 정보 */}
<div className="grid grid-cols-2 gap-6 mb-6">