Files
sam-react-prod/src/components/production/WorkerScreen/WorkLogContent.tsx
유병철 9464a368ba 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>
2026-01-22 15:07:17 +09:00

195 lines
7.4 KiB
TypeScript

'use client';
/**
* 작업일지 문서 콘텐츠
*
* DocumentViewer와 함께 사용하는 문서 본문 컴포넌트
* 인쇄 영역만 포함 (모달 헤더/툴바는 DocumentViewer가 처리)
*
* 공통 컴포넌트 사용:
* - DocumentHeader: 로고 + 제목 + 결재라인
* - InfoTable: 라벨-값 정보 테이블
* - SectionHeader: 섹션 제목 (작업내역, 특이사항)
*/
import type { WorkOrder, WorkOrderItem } from '../WorkOrders/types';
import { ITEM_STATUS_LABELS } from '../WorkOrders/types';
import {
DocumentHeader,
InfoTable,
SectionHeader,
} from '@/components/document-system';
interface WorkLogContentProps {
data: WorkOrder;
}
// 작업 통계 타입
interface WorkStats {
orderQty: number;
completedQty: number;
inProgressQty: number;
waitingQty: number;
progress: number;
}
// 품목 데이터에서 작업 통계 계산
function calculateWorkStats(items: WorkOrderItem[]): WorkStats {
const orderQty = items.length;
const completedQty = items.filter(i => i.status === 'completed').length;
const inProgressQty = items.filter(i => i.status === 'in_progress').length;
const waitingQty = items.filter(i => i.status === 'waiting').length;
const progress = orderQty > 0 ? Math.round((completedQty / orderQty) * 100) : 0;
return {
orderQty,
completedQty,
inProgressQty,
waitingQty,
progress,
};
}
export function WorkLogContent({ data: order }: WorkLogContentProps) {
const today = new Date().toLocaleDateString('ko-KR', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
}).replace(/\. /g, '-').replace('.', '');
const documentNo = `WL-${order.processCode.toUpperCase().slice(0, 3)}`;
// 품목 데이터
const items = order.items || [];
// 작업 통계 계산
const workStats = calculateWorkStats(items);
// 주 담당자
const primaryAssignee = order.assignees?.find(a => a.isPrimary)?.name || order.assignee || '-';
// 포맷된 납기일
const formattedDueDate = order.dueDate !== '-'
? new Date(order.dueDate).toLocaleDateString('ko-KR', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
}).replace(/\. /g, '-').replace('.', '')
: '-';
// 작성자 날짜 포맷
const writerDate = new Date().toLocaleDateString('ko-KR', {
month: '2-digit',
day: '2-digit',
}).replace('. ', '/').replace('.', '');
return (
<div className="p-6 bg-white">
{/* 문서 헤더: 로고 + 제목 + 결재라인 (공통 컴포넌트) */}
<DocumentHeader
title="작 업 일 지"
documentCode={documentNo}
subtitle={`${order.processName} 생산부서`}
logo={{ text: 'KD', subtext: '정동기업' }}
approval={{
type: '4col',
writer: { name: primaryAssignee, date: writerDate },
showDepartment: true,
departmentLabels: {
writer: '판매/전진',
reviewer: '생산',
approver: '품질',
},
}}
/>
{/* 기본 정보 테이블 (공통 컴포넌트) */}
<InfoTable
className="mb-6"
rows={[
[
{ label: '발주처', value: order.client },
{ label: '현장명', value: order.projectName },
],
[
{ label: '작업일자', value: today },
{ label: 'LOT NO.', value: order.lotNo },
],
[
{ label: '납기일', value: formattedDueDate },
{ label: '작업지시번호', value: order.workOrderNo },
],
]}
/>
{/* 품목 테이블 */}
<div className="border border-gray-300 mb-6">
{/* 테이블 헤더 */}
<div className="grid grid-cols-12 border-b border-gray-300 bg-gray-100">
<div className="col-span-1 p-2 text-sm font-medium text-center border-r border-gray-300">No</div>
<div className="col-span-4 p-2 text-sm font-medium text-center border-r border-gray-300"></div>
<div className="col-span-2 p-2 text-sm font-medium text-center border-r border-gray-300">/</div>
<div className="col-span-2 p-2 text-sm font-medium text-center border-r border-gray-300"></div>
<div className="col-span-1 p-2 text-sm font-medium text-center border-r border-gray-300"></div>
<div className="col-span-2 p-2 text-sm font-medium text-center"></div>
</div>
{/* 테이블 데이터 */}
{items.length > 0 ? (
items.map((item, index) => (
<div
key={item.id}
className={`grid grid-cols-12 ${index < items.length - 1 ? 'border-b border-gray-300' : ''}`}
>
<div className="col-span-1 p-2 text-sm text-center border-r border-gray-300">{item.no}</div>
<div className="col-span-4 p-2 text-sm border-r border-gray-300">{item.productName}</div>
<div className="col-span-2 p-2 text-sm text-center border-r border-gray-300">{item.floorCode}</div>
<div className="col-span-2 p-2 text-sm text-center border-r border-gray-300">{item.specification}</div>
<div className="col-span-1 p-2 text-sm text-center border-r border-gray-300">{item.quantity}</div>
<div className="col-span-2 p-2 text-sm text-center">{ITEM_STATUS_LABELS[item.status]}</div>
</div>
))
) : (
<div className="p-4 text-center text-muted-foreground text-sm">
.
</div>
)}
</div>
{/* 작업내역 */}
<div className="border border-gray-300 mb-6">
{/* 섹션 헤더 (공통 컴포넌트) */}
<SectionHeader>{order.processName} </SectionHeader>
{/* 수량 및 진행률 */}
<div className="grid grid-cols-6 border-b border-gray-300">
<div className="p-2 text-sm bg-gray-100 border-r border-gray-300 text-center font-medium"></div>
<div className="p-2 text-sm border-r border-gray-300 text-center">{workStats.orderQty} EA</div>
<div className="p-2 text-sm bg-gray-100 border-r border-gray-300 text-center font-medium"></div>
<div className="p-2 text-sm border-r border-gray-300 text-center">{workStats.completedQty} EA</div>
<div className="p-2 text-sm bg-gray-100 border-r border-gray-300 text-center font-medium"></div>
<div className="p-2 text-sm text-center font-medium text-blue-600">{workStats.progress}%</div>
</div>
{/* 상세 상태 */}
<div className="grid grid-cols-6">
<div className="p-2 text-sm bg-gray-100 border-r border-gray-300 text-center font-medium"></div>
<div className="p-2 text-sm border-r border-gray-300 text-center">{workStats.waitingQty} EA</div>
<div className="p-2 text-sm bg-gray-100 border-r border-gray-300 text-center font-medium"></div>
<div className="p-2 text-sm border-r border-gray-300 text-center">{workStats.inProgressQty} EA</div>
<div className="p-2 text-sm bg-gray-100 border-r border-gray-300 text-center font-medium"></div>
<div className="p-2 text-sm text-center">{workStats.completedQty} EA</div>
</div>
</div>
{/* 특이사항 (공통 컴포넌트) */}
<div className="border border-gray-300">
<SectionHeader></SectionHeader>
<div className="p-4 min-h-[60px] text-sm">
{order.note || '-'}
</div>
</div>
</div>
);
}