diff --git a/src/components/production/WorkOrders/WorkOrderEdit.tsx b/src/components/production/WorkOrders/WorkOrderEdit.tsx index b2eacbc4..644b5381 100644 --- a/src/components/production/WorkOrders/WorkOrderEdit.tsx +++ b/src/components/production/WorkOrders/WorkOrderEdit.tsx @@ -7,10 +7,9 @@ * 공정 진행 토글 + 품목 테이블 추가 (2026-02-05) */ -import { useState, useEffect, useCallback } from 'react'; +import React, { useState, useEffect, useCallback } from 'react'; import { invalidateDashboard } from '@/lib/dashboard-invalidation'; import { useRouter } from 'next/navigation'; -import { SquarePen, Trash2 } from 'lucide-react'; import { Input } from '@/components/ui/input'; import { DatePicker } from '@/components/ui/date-picker'; import { Label } from '@/components/ui/label'; @@ -34,7 +33,6 @@ import { import { IntegratedDetailTemplate } from '@/components/templates/IntegratedDetailTemplate'; import { AssigneeSelectModal } from './AssigneeSelectModal'; import { toast } from 'sonner'; -import { DeleteConfirmDialog } from '@/components/ui/confirm-dialog'; import { isNextRedirectError } from '@/lib/utils/redirect-error'; import { getWorkOrderById, updateWorkOrder, getProcessOptions, type ProcessOption } from './actions'; import type { WorkOrder, WorkOrderItem, ProcessStep } from './types'; @@ -46,11 +44,6 @@ interface ProcessStepStatus { [key: string]: boolean; } -// 수정 가능한 품목 타입 -interface EditableItem extends WorkOrderItem { - isEditing?: boolean; - editQuantity?: number; -} interface FormData { // 기본 정보 (읽기 전용) @@ -61,6 +54,7 @@ interface FormData { // 수정 가능 정보 processId: number | null; + department: string; scheduledDate: string; priority: number; assignees: string[]; @@ -82,13 +76,13 @@ export function WorkOrderEdit({ orderId }: WorkOrderEditProps) { orderNo: '', itemCount: 0, processId: null, + department: '', scheduledDate: '', priority: 5, assignees: [], note: '', }); const [isAssigneeModalOpen, setIsAssigneeModalOpen] = useState(false); - const [deleteTargetItemId, setDeleteTargetItemId] = useState(null); const [assigneeNames, setAssigneeNames] = useState([]); const [validationErrors, setValidationErrors] = useState>({}); const [isLoading, setIsLoading] = useState(true); @@ -101,7 +95,7 @@ export function WorkOrderEdit({ orderId }: WorkOrderEditProps) { const [stepStatus, setStepStatus] = useState({}); // 품목 목록 (수정 가능) - const [items, setItems] = useState([]); + const [items, setItems] = useState([]); // 데이터 로드 const loadData = useCallback(async () => { @@ -121,6 +115,7 @@ export function WorkOrderEdit({ orderId }: WorkOrderEditProps) { orderNo: order.lotNo, itemCount: order.items?.length || 0, processId: order.processId, + department: order.department || '', scheduledDate: order.scheduledDate || '', priority: order.priority || 5, assignees: order.assignees?.map(a => a.id) || [], @@ -271,60 +266,7 @@ export function WorkOrderEdit({ orderId }: WorkOrderEditProps) { toast.success(`공정 단계 상태가 변경되었습니다.`); }, []); - // 품목 수량 수정 시작 - const handleItemEditStart = useCallback((itemId: string) => { - setItems(prev => - prev.map(item => - item.id === itemId - ? { ...item, isEditing: true, editQuantity: item.quantity } - : item - ) - ); - }, []); - - // 품목 수량 변경 - const handleItemQuantityChange = useCallback((itemId: string, quantity: number) => { - setItems(prev => - prev.map(item => - item.id === itemId ? { ...item, editQuantity: quantity } : item - ) - ); - }, []); - - // 품목 수량 수정 저장 - const handleItemEditSave = useCallback((itemId: string) => { - setItems(prev => - prev.map(item => - item.id === itemId - ? { ...item, quantity: item.editQuantity || item.quantity, isEditing: false } - : item - ) - ); - // TODO: API 호출로 서버에 저장 - toast.success('수량이 수정되었습니다.'); - }, []); - - // 품목 수량 수정 취소 - const handleItemEditCancel = useCallback((itemId: string) => { - setItems(prev => - prev.map(item => - item.id === itemId ? { ...item, isEditing: false } : item - ) - ); - }, []); - - // 품목 삭제 - const handleItemDelete = useCallback((itemId: string) => { - setDeleteTargetItemId(itemId); - }, []); - - const handleItemDeleteConfirm = useCallback(() => { - if (!deleteTargetItemId) return; - setItems(prev => prev.filter(item => item.id !== deleteTargetItemId)); - // TODO: API 호출로 서버에서 삭제 - toast.success('품목이 삭제되었습니다.'); - setDeleteTargetItemId(null); - }, [deleteTargetItemId]); + // (품목 테이블은 읽기전용 — 수정/삭제 기능 제거됨) // 동적 config (작업지시 번호 포함) const dynamicConfig = { @@ -350,29 +292,12 @@ export function WorkOrderEdit({ orderId }: WorkOrderEditProps) {
- - - {validationErrors.processId &&

{validationErrors.processId}

} + + p.id === formData.processId)?.processName || workOrder?.processName || '-'} + disabled + className="bg-muted" + />
@@ -390,11 +315,7 @@ export function WorkOrderEdit({ orderId }: WorkOrderEditProps) {
- setFormData({ ...formData, projectName: e.target.value })} - className="bg-white" - /> +
@@ -446,7 +367,12 @@ export function WorkOrderEdit({ orderId }: WorkOrderEditProps) { {/* 4행: 부서(읽기) | 생산 담당자(선택) | 상태(읽기) | 비고(입력) */}
- + setFormData({ ...formData, department: e.target.value })} + className="bg-white" + placeholder="부서명 입력" + />
@@ -523,86 +449,99 @@ export function WorkOrderEdit({ orderId }: WorkOrderEditProps) {
- {/* 품목 테이블 */} + {/* 작업 품목 - 개소별 그룹 */} {items.length > 0 ? ( - - - - 로트번호 - 품목명 - 수량 - 단위 - 관리 - - - - {items.map((item) => ( - - {workOrder?.lotNo || '-'} - {item.productName} - - {item.isEditing ? ( -
- - handleItemQuantityChange(item.id, parseInt(e.target.value) || 0) - } - className="w-20 h-8 text-right" - min={0} - /> - - -
- ) : ( - item.quantity - )} -
- {item.unit} - -
- - -
-
-
- ))} -
-
+
+ {/* 개소별 품목 그룹 */} +
+

개소별 품목

+ + + + 개소 + 품목명 + 수량 + 단위 + + + + {(() => { + const nodeGroups = new Map(); + for (const item of items) { + const floorLabel = item.floorCode !== '-' ? item.floorCode : ''; + const key = item.orderNodeId != null ? String(item.orderNodeId) : (floorLabel || 'none'); + const label = floorLabel || item.orderNodeName || `개소 ${nodeGroups.size + 1}`; + if (!nodeGroups.has(key)) { + nodeGroups.set(key, { label, items: [] }); + } + nodeGroups.get(key)!.items.push(item); + } + const rows: React.ReactNode[] = []; + for (const [key, group] of nodeGroups) { + group.items.forEach((item, idx) => { + rows.push( + + {idx === 0 && ( + + {group.label} + + )} + {item.productName} + {item.quantity} + {item.unit} + + ); + }); + } + return rows; + })()} + +
+
+ + {/* 품목별 합산 그룹 */} +
+

품목별 합산

+ + + + No + 품목명 + 합산수량 + 단위 + 개소수 + + + + {(() => { + const itemMap = new Map(); + for (const item of items) { + const key = `${item.productName}||${item.unit}`; + if (!itemMap.has(key)) { + itemMap.set(key, { productName: item.productName, totalQty: 0, unit: item.unit, nodeCount: 0 }); + } + const entry = itemMap.get(key)!; + entry.totalQty += Number(item.quantity); + entry.nodeCount += 1; + } + let no = 0; + return Array.from(itemMap.values()).map((entry) => { + no++; + return ( + + {no} + {entry.productName} + {entry.totalQty} + {entry.unit} + {entry.nodeCount} + + ); + }); + })()} + +
+
+
) : (

등록된 품목이 없습니다. @@ -610,7 +549,7 @@ export function WorkOrderEdit({ orderId }: WorkOrderEditProps) { )} - ), [formData, validationErrors, processOptions, isLoadingProcesses, assigneeNames, workOrder, processSteps, stepStatus, items, handleStepToggle, handleItemEditStart, handleItemQuantityChange, handleItemEditSave, handleItemEditCancel, handleItemDelete]); + ), [formData, validationErrors, processOptions, isLoadingProcesses, assigneeNames, workOrder, processSteps, stepStatus, items, handleStepToggle]); return ( <> @@ -636,14 +575,6 @@ export function WorkOrderEdit({ orderId }: WorkOrderEditProps) { }} /> - {/* 품목 삭제 확인 다이얼로그 */} - !open && setDeleteTargetItemId(null)} - onConfirm={handleItemDeleteConfirm} - title="품목 삭제" - description="이 품목을 삭제하시겠습니까?" - /> ); }