- 모달 컴포넌트에서 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>
219 lines
8.5 KiB
TypeScript
219 lines
8.5 KiB
TypeScript
'use client';
|
||
|
||
/**
|
||
* 견적서 문서 콘텐츠
|
||
*
|
||
* 공통 컴포넌트 사용:
|
||
* - DocumentHeader: simple 레이아웃 (결재란 없음)
|
||
* - SectionHeader: 섹션 제목
|
||
*/
|
||
|
||
import type { QuoteFormDataV2 } from './QuoteRegistrationV2';
|
||
import { DocumentHeader, SectionHeader } from '@/components/document-system';
|
||
|
||
interface QuotePreviewContentProps {
|
||
data: QuoteFormDataV2;
|
||
}
|
||
|
||
export function QuotePreviewContent({ data: quoteData }: QuotePreviewContentProps) {
|
||
// 총 금액 계산
|
||
const totalAmount = quoteData.locations.reduce(
|
||
(sum, loc) => sum + (loc.totalPrice || 0),
|
||
0
|
||
);
|
||
|
||
// 부가세
|
||
const vat = Math.round(totalAmount * 0.1);
|
||
const grandTotal = totalAmount + vat;
|
||
|
||
return (
|
||
<div className="max-w-[210mm] mx-auto bg-white shadow-lg rounded-lg p-8">
|
||
{/* 제목 (공통 컴포넌트) */}
|
||
<DocumentHeader
|
||
title="견 적 서"
|
||
subtitle={`문서번호: ${quoteData.id || "-"} | 작성일자: ${quoteData.registrationDate || "-"}`}
|
||
layout="simple"
|
||
approval={null}
|
||
/>
|
||
|
||
{/* 수요자 정보 */}
|
||
<div className="border border-gray-300 mb-4">
|
||
<div className="bg-gray-100 px-3 py-2 font-semibold border-b border-gray-300">
|
||
수 요 자
|
||
</div>
|
||
<div className="p-3 grid grid-cols-2 gap-2 text-sm">
|
||
<div className="flex">
|
||
<span className="w-20 text-gray-600">업체명</span>
|
||
<span className="font-medium">{quoteData.clientName || "-"}</span>
|
||
</div>
|
||
<div className="flex">
|
||
<span className="w-20 text-gray-600">담당자</span>
|
||
<span className="font-medium">{quoteData.manager || "-"}</span>
|
||
</div>
|
||
<div className="flex">
|
||
<span className="w-20 text-gray-600">프로젝트명</span>
|
||
<span className="font-medium">{quoteData.siteName || "-"}</span>
|
||
</div>
|
||
<div className="flex">
|
||
<span className="w-20 text-gray-600">연락처</span>
|
||
<span className="font-medium">{quoteData.contact || "-"}</span>
|
||
</div>
|
||
<div className="flex">
|
||
<span className="w-20 text-gray-600">견적일자</span>
|
||
<span className="font-medium">{quoteData.registrationDate || "-"}</span>
|
||
</div>
|
||
<div className="flex">
|
||
<span className="w-20 text-gray-600">유효기간</span>
|
||
<span className="font-medium">{quoteData.dueDate || "-"}</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* 공급자 정보 */}
|
||
<div className="border border-gray-300 mb-6">
|
||
<div className="bg-gray-100 px-3 py-2 font-semibold border-b border-gray-300">
|
||
공 급 자
|
||
</div>
|
||
<div className="p-3 grid grid-cols-2 gap-2 text-sm">
|
||
<div className="flex">
|
||
<span className="w-24 text-gray-600">상호</span>
|
||
<span className="font-medium">프론트_테스트회사</span>
|
||
</div>
|
||
<div className="flex">
|
||
<span className="w-24 text-gray-600">사업자등록번호</span>
|
||
<span className="font-medium">123-45-67890</span>
|
||
</div>
|
||
<div className="flex">
|
||
<span className="w-24 text-gray-600">대표자</span>
|
||
<span className="font-medium">프론트</span>
|
||
</div>
|
||
<div className="flex">
|
||
<span className="w-24 text-gray-600">업태</span>
|
||
<span className="font-medium">업태명</span>
|
||
</div>
|
||
<div className="flex col-span-2">
|
||
<span className="w-24 text-gray-600">종목</span>
|
||
<span className="font-medium">김종명</span>
|
||
</div>
|
||
<div className="flex col-span-2">
|
||
<span className="w-24 text-gray-600">사업장주소</span>
|
||
<span className="font-medium">07547 서울 강서구 양천로 583 B-1602</span>
|
||
</div>
|
||
<div className="flex">
|
||
<span className="w-24 text-gray-600">전화</span>
|
||
<span className="font-medium">01048209104</span>
|
||
</div>
|
||
<div className="flex">
|
||
<span className="w-24 text-gray-600">이메일</span>
|
||
<span className="font-medium">codebridgex@codebridge-x.com</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* 총 견적금액 */}
|
||
<div className="border-2 border-gray-800 p-4 mb-6 text-center">
|
||
<p className="text-sm text-gray-600 mb-1">총 견적금액</p>
|
||
<p className="text-3xl font-bold">
|
||
₩ {grandTotal.toLocaleString()}
|
||
</p>
|
||
<p className="text-xs text-gray-500 mt-1">※ 부가가치세 포함</p>
|
||
</div>
|
||
|
||
{/* 제품 구성정보 */}
|
||
<div className="border border-gray-300 mb-6">
|
||
<SectionHeader>제 품 구 성 정 보</SectionHeader>
|
||
<div className="p-3 grid grid-cols-2 gap-2 text-sm">
|
||
<div className="flex">
|
||
<span className="w-20 text-gray-600">모델</span>
|
||
<span className="font-medium">
|
||
{quoteData.locations[0]?.productCode || "-"}
|
||
</span>
|
||
</div>
|
||
<div className="flex">
|
||
<span className="w-20 text-gray-600">총 수량</span>
|
||
<span className="font-medium">{quoteData.locations.length}개소</span>
|
||
</div>
|
||
<div className="flex">
|
||
<span className="w-20 text-gray-600">오픈사이즈</span>
|
||
<span className="font-medium">
|
||
{quoteData.locations[0]?.openWidth || "-"} × {quoteData.locations[0]?.openHeight || "-"}
|
||
</span>
|
||
</div>
|
||
<div className="flex">
|
||
<span className="w-20 text-gray-600">설치유형</span>
|
||
<span className="font-medium">-</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* 품목 내역 */}
|
||
<div className="border border-gray-300 mb-6">
|
||
<SectionHeader>품 목 내 역</SectionHeader>
|
||
<table className="w-full text-sm">
|
||
<thead>
|
||
<tr className="bg-gray-100 border-b border-gray-300">
|
||
<th className="px-3 py-2 text-left">No.</th>
|
||
<th className="px-3 py-2 text-left">품목명</th>
|
||
<th className="px-3 py-2 text-center">규격</th>
|
||
<th className="px-3 py-2 text-center">수량</th>
|
||
<th className="px-3 py-2 text-center">단위</th>
|
||
<th className="px-3 py-2 text-right">단가</th>
|
||
<th className="px-3 py-2 text-right">금액</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
{quoteData.locations.map((loc, index) => (
|
||
<tr key={loc.id} className="border-b border-gray-200">
|
||
<td className="px-3 py-2">{index + 1}</td>
|
||
<td className="px-3 py-2">{loc.productCode}</td>
|
||
<td className="px-3 py-2 text-center">
|
||
{loc.openWidth}×{loc.openHeight}
|
||
</td>
|
||
<td className="px-3 py-2 text-center">{loc.quantity}</td>
|
||
<td className="px-3 py-2 text-center">EA</td>
|
||
<td className="px-3 py-2 text-right">
|
||
{(loc.unitPrice || 0).toLocaleString()}
|
||
</td>
|
||
<td className="px-3 py-2 text-right">
|
||
{(loc.totalPrice || 0).toLocaleString()}
|
||
</td>
|
||
</tr>
|
||
))}
|
||
</tbody>
|
||
<tfoot>
|
||
<tr className="border-t-2 border-gray-400">
|
||
<td colSpan={5}></td>
|
||
<td className="px-3 py-2 text-right font-medium">공급가액 합계</td>
|
||
<td className="px-3 py-2 text-right font-bold">
|
||
{totalAmount.toLocaleString()}
|
||
</td>
|
||
</tr>
|
||
<tr>
|
||
<td colSpan={5}></td>
|
||
<td className="px-3 py-2 text-right font-medium">부가가치세 (10%)</td>
|
||
<td className="px-3 py-2 text-right font-bold">
|
||
{vat.toLocaleString()}
|
||
</td>
|
||
</tr>
|
||
<tr className="bg-gray-100">
|
||
<td colSpan={5}></td>
|
||
<td className="px-3 py-2 text-right font-medium">총 견적금액</td>
|
||
<td className="px-3 py-2 text-right font-bold text-lg">
|
||
{grandTotal.toLocaleString()}
|
||
</td>
|
||
</tr>
|
||
</tfoot>
|
||
</table>
|
||
</div>
|
||
|
||
{/* 비고사항 */}
|
||
<div className="border border-gray-300">
|
||
<SectionHeader>비 고 사 항</SectionHeader>
|
||
<div className="p-3 min-h-[80px] text-sm text-gray-600">
|
||
{quoteData.remarks || "비고 테스트"}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|