refactor: 모달 Content 컴포넌트 분리 및 파일 입력 UI 공통화
- 모달 컴포넌트에서 Content 분리하여 재사용성 향상 - EstimateDocumentContent, DirectConstructionContent 등 - WorkLogContent, QuotePreviewContent, ReceivingReceiptContent - 파일 입력 공통 UI 컴포넌트 추가 - file-dropzone, file-input, file-list, image-upload - 폼 컴포넌트 코드 정리 및 중복 제거 (-4,056줄) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,130 @@
|
||||
'use client';
|
||||
|
||||
/**
|
||||
* 입고증 문서 콘텐츠
|
||||
*
|
||||
* 공통 컴포넌트 사용:
|
||||
* - DocumentHeader: simple 레이아웃 (결재란 없음, 서명란 별도)
|
||||
*/
|
||||
|
||||
import type { ReceivingDetail } from './types';
|
||||
import { DocumentHeader } from '@/components/document-system';
|
||||
|
||||
interface ReceivingReceiptContentProps {
|
||||
data: ReceivingDetail;
|
||||
}
|
||||
|
||||
export function ReceivingReceiptContent({ data: detail }: ReceivingReceiptContentProps) {
|
||||
const today = new Date();
|
||||
const formattedDate = `${today.getFullYear()}년 ${today.getMonth() + 1}월 ${today.getDate()}일`;
|
||||
|
||||
return (
|
||||
<div className="p-8 bg-white rounded-lg">
|
||||
{/* 제목 (공통 컴포넌트) */}
|
||||
<DocumentHeader
|
||||
title="입고증"
|
||||
subtitle="RECEIVING SLIP"
|
||||
layout="simple"
|
||||
approval={null}
|
||||
className="mb-8"
|
||||
/>
|
||||
|
||||
{/* 입고 정보 / 공급업체 정보 */}
|
||||
<div className="grid grid-cols-2 gap-8 mb-8">
|
||||
{/* 입고 정보 */}
|
||||
<div className="space-y-3 text-sm">
|
||||
<h3 className="font-semibold border-b pb-2">입고 정보</h3>
|
||||
<div className="grid grid-cols-[100px_1fr] gap-y-2">
|
||||
<span className="text-muted-foreground">입고번호</span>
|
||||
<span className="font-medium">{detail.orderNo}</span>
|
||||
<span className="text-muted-foreground">입고일자</span>
|
||||
<span>{detail.receivingDate || today.toISOString().split('T')[0]}</span>
|
||||
<span className="text-muted-foreground">발주번호</span>
|
||||
<span>{detail.orderNo}</span>
|
||||
<span className="text-muted-foreground">입고LOT</span>
|
||||
<span>{detail.receivingLot || '-'}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 공급업체 정보 */}
|
||||
<div className="space-y-3 text-sm">
|
||||
<h3 className="font-semibold border-b pb-2">공급업체 정보</h3>
|
||||
<div className="grid grid-cols-[100px_1fr] gap-y-2">
|
||||
<span className="text-muted-foreground">업체명</span>
|
||||
<span className="font-medium">{detail.supplier}</span>
|
||||
<span className="text-muted-foreground">공급업체LOT</span>
|
||||
<span>{detail.supplierLot || '-'}</span>
|
||||
<span className="text-muted-foreground">담당자</span>
|
||||
<span>{detail.receivingManager || '-'}</span>
|
||||
<span className="text-muted-foreground">입고위치</span>
|
||||
<span>{detail.receivingLocation || '-'}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 입고 품목 상세 */}
|
||||
<div className="mb-8">
|
||||
<h3 className="font-semibold text-sm mb-3">입고 품목 상세</h3>
|
||||
<table className="w-full text-sm border-collapse">
|
||||
<thead>
|
||||
<tr className="border-y bg-gray-50">
|
||||
<th className="px-3 py-2 text-center font-medium w-12">No</th>
|
||||
<th className="px-3 py-2 text-left font-medium">품목코드</th>
|
||||
<th className="px-3 py-2 text-left font-medium">품목명</th>
|
||||
<th className="px-3 py-2 text-left font-medium">규격</th>
|
||||
<th className="px-3 py-2 text-center font-medium w-24">발주수량</th>
|
||||
<th className="px-3 py-2 text-center font-medium w-24">입고수량</th>
|
||||
<th className="px-3 py-2 text-center font-medium w-16">단위</th>
|
||||
<th className="px-3 py-2 text-left font-medium w-24">비고</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr className="border-b">
|
||||
<td className="px-3 py-2 text-center">1</td>
|
||||
<td className="px-3 py-2">{detail.itemCode}</td>
|
||||
<td className="px-3 py-2">{detail.itemName}</td>
|
||||
<td className="px-3 py-2">{detail.specification || '-'}</td>
|
||||
<td className="px-3 py-2 text-center">{detail.orderQty}</td>
|
||||
<td className="px-3 py-2 text-center">{detail.receivingQty || '-'}</td>
|
||||
<td className="px-3 py-2 text-center">{detail.orderUnit}</td>
|
||||
<td className="px-3 py-2">-</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{/* 수입검사 안내 */}
|
||||
<div className="mb-8 p-4 bg-gray-50 rounded-lg text-sm">
|
||||
<p className="flex items-start gap-2">
|
||||
<span className="font-medium">📋 수입검사 안내</span>
|
||||
</p>
|
||||
<p className="text-muted-foreground mt-1">
|
||||
본 입고건에 대한 <span className="font-medium text-blue-600">수입검사(IQC)</span>가 필요합니다.<br />
|
||||
품질관리 > 수입검사(IQC) 메뉴에서 검사를 진행해주세요.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* 서명란 */}
|
||||
<div className="grid grid-cols-3 gap-6 mb-8">
|
||||
<div className="border rounded p-4 text-center">
|
||||
<p className="text-sm font-medium mb-12">입고담당</p>
|
||||
<p className="text-xs text-muted-foreground">(인)</p>
|
||||
</div>
|
||||
<div className="border rounded p-4 text-center">
|
||||
<p className="text-sm font-medium mb-12">품질검사</p>
|
||||
<p className="text-xs text-muted-foreground">(인)</p>
|
||||
</div>
|
||||
<div className="border rounded p-4 text-center">
|
||||
<p className="text-sm font-medium mb-12">창고담당</p>
|
||||
<p className="text-xs text-muted-foreground">(인)</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 발행일 / 회사명 */}
|
||||
<div className="text-right text-sm text-muted-foreground">
|
||||
<p>발행일: {formattedDate}</p>
|
||||
<p>(주) 코드빌더스</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,20 +1,16 @@
|
||||
'use client';
|
||||
|
||||
/**
|
||||
* 입고증 다이얼로그 (인쇄용)
|
||||
* - 작업일지(WorkLogModal) 스타일 적용
|
||||
* 입고증 다이얼로그
|
||||
*
|
||||
* document-system 통합 버전 (2026-01-22)
|
||||
*/
|
||||
|
||||
import { Printer, Download, X } from 'lucide-react';
|
||||
import { Download } from 'lucide-react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogTitle,
|
||||
} from '@/components/ui/dialog';
|
||||
import { VisuallyHidden } from '@radix-ui/react-visually-hidden';
|
||||
import { printArea } from '@/lib/print-utils';
|
||||
import { DocumentViewer } from '@/components/document-system';
|
||||
import type { ReceivingDetail } from './types';
|
||||
import { ReceivingReceiptContent } from './ReceivingReceiptContent';
|
||||
|
||||
interface Props {
|
||||
open: boolean;
|
||||
@@ -23,163 +19,28 @@ interface Props {
|
||||
}
|
||||
|
||||
export function ReceivingReceiptDialog({ open, onOpenChange, detail }: Props) {
|
||||
const handlePrint = () => {
|
||||
printArea({ title: '입고증 인쇄' });
|
||||
};
|
||||
|
||||
const handleDownload = () => {
|
||||
// TODO: PDF 다운로드 기능
|
||||
console.log('PDF 다운로드:', detail);
|
||||
};
|
||||
|
||||
const today = new Date();
|
||||
const formattedDate = `${today.getFullYear()}년 ${today.getMonth() + 1}월 ${today.getDate()}일`;
|
||||
const toolbarExtra = (
|
||||
<Button variant="outline" size="sm" onClick={handleDownload}>
|
||||
<Download className="w-4 h-4 mr-1" />
|
||||
다운로드
|
||||
</Button>
|
||||
);
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||
<DialogContent className="sm:max-w-4xl max-h-[90vh] overflow-y-auto p-0 gap-0 bg-gray-100">
|
||||
{/* 접근성을 위한 숨겨진 타이틀 */}
|
||||
<VisuallyHidden>
|
||||
<DialogTitle>입고증 - {detail.orderNo}</DialogTitle>
|
||||
</VisuallyHidden>
|
||||
|
||||
{/* 모달 헤더 - 작업일지 스타일 (인쇄 시 숨김) */}
|
||||
<div className="print-hidden flex items-center justify-between px-6 py-4 border-b bg-white sticky top-0 z-10">
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="font-semibold text-lg">입고증</span>
|
||||
<span className="text-sm text-muted-foreground">
|
||||
{detail.supplier}
|
||||
</span>
|
||||
<span className="text-sm text-muted-foreground">
|
||||
({detail.orderNo})
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Button variant="outline" size="sm" onClick={handlePrint}>
|
||||
<Printer className="w-4 h-4 mr-1.5" />
|
||||
인쇄
|
||||
</Button>
|
||||
<Button variant="outline" size="sm" onClick={handleDownload}>
|
||||
<Download className="w-4 h-4 mr-1.5" />
|
||||
다운로드
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="h-8 w-8"
|
||||
onClick={() => onOpenChange(false)}
|
||||
>
|
||||
<X className="w-4 h-4" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 문서 본문 (인쇄 시 이 영역만 출력) */}
|
||||
<div className="print-area m-6 p-8 bg-white rounded-lg shadow-sm">
|
||||
{/* 제목 */}
|
||||
<div className="text-center mb-8">
|
||||
<h2 className="text-2xl font-bold">입고증</h2>
|
||||
<p className="text-sm text-muted-foreground">RECEIVING SLIP</p>
|
||||
</div>
|
||||
|
||||
{/* 입고 정보 / 공급업체 정보 */}
|
||||
<div className="grid grid-cols-2 gap-8 mb-8">
|
||||
{/* 입고 정보 */}
|
||||
<div className="space-y-3 text-sm">
|
||||
<h3 className="font-semibold border-b pb-2">입고 정보</h3>
|
||||
<div className="grid grid-cols-[100px_1fr] gap-y-2">
|
||||
<span className="text-muted-foreground">입고번호</span>
|
||||
<span className="font-medium">{detail.orderNo}</span>
|
||||
<span className="text-muted-foreground">입고일자</span>
|
||||
<span>{detail.receivingDate || today.toISOString().split('T')[0]}</span>
|
||||
<span className="text-muted-foreground">발주번호</span>
|
||||
<span>{detail.orderNo}</span>
|
||||
<span className="text-muted-foreground">입고LOT</span>
|
||||
<span>{detail.receivingLot || '-'}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 공급업체 정보 */}
|
||||
<div className="space-y-3 text-sm">
|
||||
<h3 className="font-semibold border-b pb-2">공급업체 정보</h3>
|
||||
<div className="grid grid-cols-[100px_1fr] gap-y-2">
|
||||
<span className="text-muted-foreground">업체명</span>
|
||||
<span className="font-medium">{detail.supplier}</span>
|
||||
<span className="text-muted-foreground">공급업체LOT</span>
|
||||
<span>{detail.supplierLot || '-'}</span>
|
||||
<span className="text-muted-foreground">담당자</span>
|
||||
<span>{detail.receivingManager || '-'}</span>
|
||||
<span className="text-muted-foreground">입고위치</span>
|
||||
<span>{detail.receivingLocation || '-'}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 입고 품목 상세 */}
|
||||
<div className="mb-8">
|
||||
<h3 className="font-semibold text-sm mb-3">입고 품목 상세</h3>
|
||||
<table className="w-full text-sm border-collapse">
|
||||
<thead>
|
||||
<tr className="border-y bg-gray-50">
|
||||
<th className="px-3 py-2 text-center font-medium w-12">No</th>
|
||||
<th className="px-3 py-2 text-left font-medium">품목코드</th>
|
||||
<th className="px-3 py-2 text-left font-medium">품목명</th>
|
||||
<th className="px-3 py-2 text-left font-medium">규격</th>
|
||||
<th className="px-3 py-2 text-center font-medium w-24">발주수량</th>
|
||||
<th className="px-3 py-2 text-center font-medium w-24">입고수량</th>
|
||||
<th className="px-3 py-2 text-center font-medium w-16">단위</th>
|
||||
<th className="px-3 py-2 text-left font-medium w-24">비고</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr className="border-b">
|
||||
<td className="px-3 py-2 text-center">1</td>
|
||||
<td className="px-3 py-2">{detail.itemCode}</td>
|
||||
<td className="px-3 py-2">{detail.itemName}</td>
|
||||
<td className="px-3 py-2">{detail.specification || '-'}</td>
|
||||
<td className="px-3 py-2 text-center">{detail.orderQty}</td>
|
||||
<td className="px-3 py-2 text-center">{detail.receivingQty || '-'}</td>
|
||||
<td className="px-3 py-2 text-center">{detail.orderUnit}</td>
|
||||
<td className="px-3 py-2">-</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{/* 수입검사 안내 */}
|
||||
<div className="mb-8 p-4 bg-gray-50 rounded-lg text-sm">
|
||||
<p className="flex items-start gap-2">
|
||||
<span className="font-medium">📋 수입검사 안내</span>
|
||||
</p>
|
||||
<p className="text-muted-foreground mt-1">
|
||||
본 입고건에 대한 <span className="font-medium text-blue-600">수입검사(IQC)</span>가 필요합니다.<br />
|
||||
품질관리 > 수입검사(IQC) 메뉴에서 검사를 진행해주세요.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* 서명란 */}
|
||||
<div className="grid grid-cols-3 gap-6 mb-8">
|
||||
<div className="border rounded p-4 text-center">
|
||||
<p className="text-sm font-medium mb-12">입고담당</p>
|
||||
<p className="text-xs text-muted-foreground">(인)</p>
|
||||
</div>
|
||||
<div className="border rounded p-4 text-center">
|
||||
<p className="text-sm font-medium mb-12">품질검사</p>
|
||||
<p className="text-xs text-muted-foreground">(인)</p>
|
||||
</div>
|
||||
<div className="border rounded p-4 text-center">
|
||||
<p className="text-sm font-medium mb-12">창고담당</p>
|
||||
<p className="text-xs text-muted-foreground">(인)</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 발행일 / 회사명 */}
|
||||
<div className="text-right text-sm text-muted-foreground">
|
||||
<p>발행일: {formattedDate}</p>
|
||||
<p>(주) 코드빌더스</p>
|
||||
</div>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
<DocumentViewer
|
||||
title="입고증"
|
||||
subtitle={`${detail.supplier} (${detail.orderNo})`}
|
||||
preset="inspection"
|
||||
open={open}
|
||||
onOpenChange={onOpenChange}
|
||||
toolbarExtra={toolbarExtra}
|
||||
>
|
||||
<ReceivingReceiptContent data={detail} />
|
||||
</DocumentViewer>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user