'use client'; /** * 작업지시 수정 페이지 * WorkOrderCreate 패턴 기반 * IntegratedDetailTemplate 마이그레이션 (2025-01-20) */ import { useState, useEffect, useCallback } from 'react'; import { useRouter } from 'next/navigation'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Textarea } from '@/components/ui/textarea'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select'; import { Alert, AlertDescription } from '@/components/ui/alert'; import { IntegratedDetailTemplate } from '@/components/templates/IntegratedDetailTemplate'; import { AssigneeSelectModal } from './AssigneeSelectModal'; import { toast } from 'sonner'; import { isNextRedirectError } from '@/lib/utils/redirect-error'; import { getWorkOrderById, updateWorkOrder, getProcessOptions, type ProcessOption } from './actions'; import type { WorkOrder } from './types'; import { workOrderEditConfig } from './workOrderConfig'; // Validation 에러 타입 interface ValidationErrors { [key: string]: string; } // 필드명 매핑 const FIELD_NAME_MAP: Record = { processId: '공정', scheduledDate: '출고예정일', }; interface FormData { // 기본 정보 (읽기 전용) client: string; projectName: string; orderNo: string; itemCount: number; // 수정 가능 정보 processId: number | null; scheduledDate: string; priority: number; assignees: string[]; // 비고 note: string; } interface WorkOrderEditProps { orderId: string; } export function WorkOrderEdit({ orderId }: WorkOrderEditProps) { const router = useRouter(); const [workOrder, setWorkOrder] = useState(null); const [formData, setFormData] = useState({ client: '', projectName: '', orderNo: '', itemCount: 0, processId: null, scheduledDate: '', priority: 5, assignees: [], note: '', }); const [isAssigneeModalOpen, setIsAssigneeModalOpen] = useState(false); const [assigneeNames, setAssigneeNames] = useState([]); const [validationErrors, setValidationErrors] = useState({}); const [isLoading, setIsLoading] = useState(true); const [isSubmitting, setIsSubmitting] = useState(false); const [processOptions, setProcessOptions] = useState([]); const [isLoadingProcesses, setIsLoadingProcesses] = useState(true); // 데이터 로드 const loadData = useCallback(async () => { setIsLoading(true); try { const [orderResult, processResult] = await Promise.all([ getWorkOrderById(orderId), getProcessOptions(), ]); if (orderResult.success && orderResult.data) { const order = orderResult.data; setWorkOrder(order); setFormData({ client: order.client, projectName: order.projectName, orderNo: order.lotNo, itemCount: order.items?.length || 0, processId: order.processId, scheduledDate: order.scheduledDate || '', priority: order.priority || 5, assignees: order.assignees?.map(a => a.id) || [], note: order.note || '', }); // 담당자 이름 설정 if (order.assignees) { setAssigneeNames(order.assignees.map(a => a.name)); } } else { toast.error(orderResult.error || '작업지시 조회에 실패했습니다.'); router.push('/production/work-orders'); return; } if (processResult.success) { setProcessOptions(processResult.data); } else { toast.error(processResult.error || '공정 목록을 불러오는데 실패했습니다.'); } setIsLoadingProcesses(false); } catch (error) { if (isNextRedirectError(error)) throw error; console.error('[WorkOrderEdit] loadData error:', error); toast.error('데이터 로드 중 오류가 발생했습니다.'); } finally { setIsLoading(false); } }, [orderId, router]); useEffect(() => { loadData(); }, [loadData]); // 폼 제출 const handleSubmit = async (): Promise<{ success: boolean; error?: string }> => { // Validation 체크 const errors: ValidationErrors = {}; if (!formData.processId) { errors.processId = '공정을 선택해주세요'; } if (!formData.scheduledDate) { errors.scheduledDate = '출고예정일을 선택해주세요'; } // 에러가 있으면 상태 업데이트 후 리턴 if (Object.keys(errors).length > 0) { setValidationErrors(errors); window.scrollTo({ top: 0, behavior: 'smooth' }); return { success: false, error: '입력 정보를 확인해주세요.' }; } // 에러 초기화 setValidationErrors({}); setIsSubmitting(true); try { // 담당자 ID 배열 변환 (string[] → number[]) const assigneeIds = formData.assignees .map(id => parseInt(id, 10)) .filter(id => !isNaN(id)); const result = await updateWorkOrder(orderId, { projectName: formData.projectName, processId: formData.processId!, scheduledDate: formData.scheduledDate, priority: formData.priority, assigneeIds: assigneeIds.length > 0 ? assigneeIds : undefined, note: formData.note || undefined, }); if (result.success) { toast.success('작업지시가 수정되었습니다.'); router.push(`/production/work-orders/${orderId}?mode=view`); return { success: true }; } else { return { success: false, error: result.error || '작업지시 수정에 실패했습니다.' }; } } catch (error) { if (isNextRedirectError(error)) throw error; console.error('[WorkOrderEdit] handleSubmit error:', error); return { success: false, error: '작업지시 수정 중 오류가 발생했습니다.' }; } finally { setIsSubmitting(false); } }; // 취소 const handleCancel = () => { router.back(); }; // 선택된 공정의 코드 가져오기 const getSelectedProcessCode = (): string => { const selectedProcess = processOptions.find(p => p.id === formData.processId); return selectedProcess?.processCode || '-'; }; // 동적 config (작업지시 번호 포함) const dynamicConfig = { ...workOrderEditConfig, title: workOrder ? `작업지시 (${workOrder.workOrderNo})` : '작업지시', }; // 폼 컨텐츠 렌더링 const renderFormContent = useCallback(() => (
{/* Validation 에러 표시 */} {Object.keys(validationErrors).length > 0 && (
⚠️
입력 내용을 확인해주세요 ({Object.keys(validationErrors).length}개 오류)
    {Object.entries(validationErrors).map(([field, message]) => { const fieldName = FIELD_NAME_MAP[field] || field; return (
  • {fieldName}: {message}
  • ); })}
)} {/* 기본 정보 (읽기 전용) */}

기본 정보

setFormData({ ...formData, projectName: e.target.value })} className="bg-white" />
{/* 작업지시 정보 */}

작업지시 정보

공정코드: {getSelectedProcessCode()}

setFormData({ ...formData, scheduledDate: e.target.value })} className="bg-white" />
setIsAssigneeModalOpen(true)} className="flex min-h-10 w-full cursor-pointer items-center rounded-md border border-input bg-white px-3 py-2 text-sm ring-offset-background hover:bg-accent/50" > {assigneeNames.length > 0 ? ( {assigneeNames.join(', ')} ) : ( 담당자를 선택하세요 (팀/개인) )}
{/* 비고 */}

비고