feat(WEB): 공정관리/작업지시/작업자화면 기능 강화 및 템플릿 개선

- 공정관리: ProcessDetail/ProcessForm/ProcessList 개선, StepDetail/StepForm 신규 추가
- 작업지시: WorkOrderDetail/Edit/List UI 개선, 작업지시서 문서 추가
- 작업자화면: WorkerScreen 대폭 개선, MaterialInputModal/WorkLogModal 수정, WorkItemCard 신규
- 영업주문: 주문 상세 페이지 개선
- 입고관리: 상세/actions 수정
- 템플릿: IntegratedDetailTemplate/IntegratedListTemplateV2/UniversalListPage 기능 확장
- UI: confirm-dialog 개선

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
유병철
2026-01-29 22:56:01 +09:00
parent 106ce09482
commit 3fc63d0b3e
50 changed files with 5801 additions and 1377 deletions

View File

@@ -4,31 +4,85 @@
* 작업일지 모달
*
* document-system 통합 버전 (2026-01-22)
* 공정별 작업일지 지원 (2026-01-29)
* - DocumentViewer 사용
* - WorkLogContent로 문서 본문
* - 공정 타입에 따라 스크린/슬랫/절곡 작업일지
* - processType 미지정 시 기존 WorkLogContent (범용) 사용
*/
import { useState, useEffect } from 'react';
import { Loader2 } from 'lucide-react';
import { DocumentViewer } from '@/components/document-system';
import { getWorkOrderById } from '../WorkOrders/actions';
import type { WorkOrder } from '../WorkOrders/types';
import type { WorkOrder, ProcessType } from '../WorkOrders/types';
import { WorkLogContent } from './WorkLogContent';
import {
ScreenWorkLogContent,
SlatWorkLogContent,
BendingWorkLogContent,
} from '../WorkOrders/documents';
interface WorkLogModalProps {
open: boolean;
onOpenChange: (open: boolean) => void;
workOrderId: string | null;
processType?: ProcessType;
}
export function WorkLogModal({ open, onOpenChange, workOrderId }: WorkLogModalProps) {
export function WorkLogModal({ open, onOpenChange, workOrderId, processType }: WorkLogModalProps) {
const [order, setOrder] = useState<WorkOrder | null>(null);
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
// 목업 WorkOrder 생성
const createMockOrder = (id: string, pType?: ProcessType): WorkOrder => ({
id,
workOrderNo: 'KD-WO-260129-01',
lotNo: 'KD-SA-260129-01',
processId: 1,
processName: pType === 'slat' ? '슬랫' : pType === 'bending' ? '절곡' : '스크린',
processCode: pType || 'screen',
processType: pType || 'screen',
status: 'in_progress',
client: '(주)경동',
projectName: '서울 강남 현장',
dueDate: '2026-02-05',
assignee: '홍길동',
assignees: [{ id: '1', name: '홍길동', isPrimary: true }],
orderDate: '2026-01-20',
scheduledDate: '2026-01-29',
shipmentDate: '2026-02-05',
salesOrderDate: '2026-01-15',
isAssigned: true,
isStarted: true,
priority: 3,
priorityLabel: '긴급',
shutterCount: 12,
department: '생산부',
items: [
{ id: '1', no: 1, status: 'in_progress', productName: '와이어 스크린', floorCode: '1층/FSS-01', specification: '8,260 X 8,350', quantity: 2, unit: 'EA' },
{ id: '2', no: 2, status: 'waiting', productName: '메쉬 스크린', floorCode: '2층/FSS-03', specification: '6,400 X 5,200', quantity: 4, unit: 'EA' },
{ id: '3', no: 3, status: 'completed', productName: '광폭 와이어', floorCode: '3층/FSS-05', specification: '12,000 X 4,500', quantity: 1, unit: 'EA' },
],
currentStep: { key: 'cutting', label: '절단', order: 2 },
completedSteps: ['material_input'],
totalProgress: 25,
issues: [],
memo: '',
createdAt: '2026-01-20T09:00:00',
updatedAt: '2026-01-29T14:00:00',
});
// 모달 열릴 때 데이터 fetch
useEffect(() => {
if (open && workOrderId) {
// 목업 ID인 경우 API 호출 생략
if (workOrderId.startsWith('mock-')) {
setOrder(createMockOrder(workOrderId, processType));
setError(null);
return;
}
setIsLoading(true);
setError(null);
@@ -51,13 +105,32 @@ export function WorkLogModal({ open, onOpenChange, workOrderId }: WorkLogModalPr
setOrder(null);
setError(null);
}
}, [open, workOrderId]);
}, [open, workOrderId, processType]);
if (!workOrderId) return null;
// 로딩/에러 상태는 DocumentViewer 내부에서 처리
const subtitle = order ? `${order.processName} 생산부서` : undefined;
// 공정 타입에 따라 콘텐츠 분기
const renderContent = () => {
if (!order) return null;
// processType prop 또는 order의 processType 사용
const type = processType || order.processType;
switch (type) {
case 'screen':
return <ScreenWorkLogContent data={order} />;
case 'slat':
return <SlatWorkLogContent data={order} />;
case 'bending':
return <BendingWorkLogContent data={order} />;
default:
return <WorkLogContent data={order} />;
}
};
return (
<DocumentViewer
title="작업일지"
@@ -75,7 +148,7 @@ export function WorkLogModal({ open, onOpenChange, workOrderId }: WorkLogModalPr
<p className="text-muted-foreground">{error || '데이터를 불러올 수 없습니다.'}</p>
</div>
) : (
<WorkLogContent data={order} />
renderContent()
)}
</DocumentViewer>
);