"use client"; /** * 수주 수정 컴포넌트 (Edit Mode) * IntegratedDetailTemplate 마이그레이션 완료 (2026-01-20) * * - 기본 정보 (읽기전용) * - 수주/배송 정보 (편집 가능) * - 비고 (편집 가능) * - 품목 내역 (생산 시작 후 수정 불가) */ import { useState, useEffect, useMemo, useCallback } from "react"; import { useRouter } from "next/navigation"; import { Input } from "@/components/ui/input"; import { Textarea } from "@/components/ui/textarea"; import { PhoneInput } from "@/components/ui/phone-input"; import { Checkbox } from "@/components/ui/checkbox"; import { Label } from "@/components/ui/label"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@/components/ui/table"; import { AlertTriangle, ChevronDown, ChevronRight, ChevronsUpDown, Package } from "lucide-react"; import { toast } from "sonner"; import { IntegratedDetailTemplate } from "@/components/templates/IntegratedDetailTemplate"; import { orderSalesConfig } from "./orderSalesConfig"; import { BadgeSm } from "@/components/atoms/BadgeSm"; import { formatAmount } from "@/utils/formatAmount"; import { OrderItem, getOrderById, updateOrder, type OrderStatus, } from "@/components/orders"; import { getDeliveryMethodOptions, getCommonCodeOptions } from "@/lib/api/common-codes"; // 수정 폼 데이터 interface EditFormData { // 읽기전용 정보 lotNumber: string; orderDate: string; // 접수일 quoteNumber: string; client: string; siteName: string; manager: string; contact: string; status: OrderStatus; // 수정 가능 정보 expectedShipDate: string; expectedShipDateUndecided: boolean; deliveryRequestDate: string; deliveryMethod: string; shippingCost: string; receiver: string; receiverContact: string; address: string; addressDetail: string; remarks: string; // 품목 (수정 제한) items: OrderItem[]; canEditItems: boolean; subtotal: number; discountRate: number; totalAmount: number; // 제품 정보 (아코디언용) products: Array<{ productName: string; productCategory?: string; openWidth?: string; openHeight?: string; quantity: number; floor?: string; code?: string; }>; } // 옵션 타입 정의 interface SelectOption { value: string; label: string; } // 상태 뱃지 헬퍼 function getOrderStatusBadge(status: OrderStatus) { const statusConfig: Record = { order_registered: { label: "수주등록", className: "bg-gray-100 text-gray-700 border-gray-200" }, order_confirmed: { label: "수주확정", className: "bg-gray-100 text-gray-700 border-gray-200" }, production_ordered: { label: "생산지시완료", className: "bg-blue-100 text-blue-700 border-blue-200" }, in_production: { label: "생산중", className: "bg-green-100 text-green-700 border-green-200" }, produced: { label: "생산완료", className: "bg-blue-100 text-blue-700 border-blue-200" }, rework: { label: "재작업중", className: "bg-orange-100 text-orange-700 border-orange-200" }, work_completed: { label: "작업완료", className: "bg-blue-600 text-white border-blue-600" }, shipping: { label: "출하중", className: "bg-purple-100 text-purple-700 border-purple-200" }, shipped: { label: "출하완료", className: "bg-gray-500 text-white border-gray-500" }, completed: { label: "완료", className: "bg-gray-500 text-white border-gray-500" }, cancelled: { label: "취소", className: "bg-red-100 text-red-700 border-red-200" }, }; const config = statusConfig[status] || { label: status, className: "bg-gray-100 text-gray-700 border-gray-200" }; return ( {config.label} ); } interface OrderSalesDetailEditProps { orderId: string; } export function OrderSalesDetailEdit({ orderId }: OrderSalesDetailEditProps) { const router = useRouter(); const [form, setForm] = useState(null); const [loading, setLoading] = useState(true); const [isSaving, setIsSaving] = useState(false); const [expandedProducts, setExpandedProducts] = useState>(new Set()); // 공통코드 옵션 const [deliveryMethods, setDeliveryMethods] = useState([]); const [shippingCosts, setShippingCosts] = useState([]); // 제품-부품 트리 토글 const toggleProduct = (key: string) => { setExpandedProducts((prev) => { const next = new Set(prev); if (next.has(key)) { next.delete(key); } else { next.add(key); } return next; }); }; // 모든 제품 확장 const expandAllProducts = () => { if (form?.products) { const allKeys = form.products.map((p) => `${p.floor || ""}-${p.code || ""}`); allKeys.push("other-parts"); // 기타부품도 포함 setExpandedProducts(new Set(allKeys)); } }; // 모든 제품 축소 const collapseAllProducts = () => { setExpandedProducts(new Set()); }; // 제품별로 부품 그룹화 (floor_code, symbol_code 매칭) const getItemsForProduct = (floor: string | undefined, code: string | undefined) => { if (!form?.items) return []; return form.items.filter((item) => { const itemFloor = item.type || ""; const itemSymbol = item.symbol || ""; const productFloor = floor || ""; const productCode = code || ""; return itemFloor === productFloor && itemSymbol === productCode; }); }; // 매칭되지 않은 부품 (orphan items) const getUnmatchedItems = () => { if (!form?.items || !form?.products) return form?.items || []; const matchedIds = new Set(); form.products.forEach((product) => { const items = getItemsForProduct(product.floor, product.code); items.forEach((item) => matchedIds.add(item.id)); }); return form.items.filter((item) => !matchedIds.has(item.id)); }; /** * 수량 포맷 함수 * - EA, SET, PCS 등 개수 단위: 정수로 표시 * - M, M2, KG, L 등 측정 단위: 소수점 이하 불필요한 0 제거 */ const formatQuantity = (quantity: number, unit?: string): string => { const countableUnits = ["EA", "SET", "PCS", "개", "세트", "BOX", "ROLL"]; const upperUnit = (unit || "").toUpperCase(); if (countableUnits.includes(upperUnit)) { return Math.round(quantity).toLocaleString(); } const rounded = Math.round(quantity * 10000) / 10000; return rounded.toLocaleString(undefined, { minimumFractionDigits: 0, maximumFractionDigits: 4 }); }; // 데이터 로드 (API) useEffect(() => { async function loadOrder() { try { setLoading(true); const result = await getOrderById(orderId); if (result.success && result.data) { const order = result.data; // 상태에 따라 품목 수정 가능 여부 결정 const canEditItems = !["in_production", "rework", "work_completed", "shipped"].includes( order.status ); // Order 데이터를 EditFormData로 변환 setForm({ lotNumber: order.lotNumber, orderDate: order.orderDate || "", quoteNumber: order.quoteNumber || "", client: order.client, siteName: order.siteName, manager: order.manager || "", contact: order.contact || "", status: order.status, expectedShipDate: order.expectedShipDate || "", expectedShipDateUndecided: !order.expectedShipDate, deliveryRequestDate: order.deliveryRequestDate || "", deliveryMethod: order.deliveryMethod || "", shippingCost: order.shippingCost || "", receiver: order.receiver || "", receiverContact: order.receiverContact || "", address: order.address || "", addressDetail: order.addressDetail || "", remarks: order.remarks || "", items: order.items || [], canEditItems, subtotal: order.subtotal || order.amount, discountRate: order.discountRate || 0, totalAmount: order.amount, products: order.products || [], }); } else { toast.error(result.error || "수주 정보를 불러오는데 실패했습니다."); router.push("/sales/order-management-sales"); } } catch (error) { console.error("Error loading order:", error); toast.error("수주 정보를 불러오는 중 오류가 발생했습니다."); router.push("/sales/order-management-sales"); } finally { setLoading(false); } } loadOrder(); }, [orderId, router]); // 공통코드 옵션 로드 useEffect(() => { async function loadCommonCodes() { const [deliveryResult, shippingResult] = await Promise.all([ getDeliveryMethodOptions(), getCommonCodeOptions('shipping_cost'), ]); if (deliveryResult.success && deliveryResult.data) { setDeliveryMethods(deliveryResult.data); } if (shippingResult.success && shippingResult.data) { setShippingCosts(shippingResult.data); } } loadCommonCodes(); }, []); const handleCancel = () => { // V2 패턴: ?mode=view로 이동 router.push(`/sales/order-management-sales/${orderId}?mode=view`); }; // IntegratedDetailTemplate용 onSubmit 핸들러 const handleSubmit = useCallback(async (): Promise<{ success: boolean; error?: string }> => { if (!form) return { success: false, error: "폼 데이터가 없습니다." }; // 유효성 검사 if (!form.deliveryRequestDate) { return { success: false, error: "납품요청일을 입력해주세요." }; } if (!form.receiver.trim()) { return { success: false, error: "수신자를 입력해주세요." }; } if (!form.receiverContact.trim()) { return { success: false, error: "수신처를 입력해주세요." }; } try { // API 연동 const result = await updateOrder(orderId, { clientId: undefined, // 기존 값 유지 siteName: form.siteName, expectedShipDate: form.expectedShipDateUndecided ? undefined : form.expectedShipDate, deliveryRequestDate: form.deliveryRequestDate, deliveryMethod: form.deliveryMethod, shippingCost: form.shippingCost, receiver: form.receiver, receiverContact: form.receiverContact, address: form.address, addressDetail: form.addressDetail, remarks: form.remarks, items: form.items.map((item) => ({ itemId: item.id ? parseInt(item.id, 10) : undefined, itemCode: item.itemCode, itemName: item.itemName, specification: item.spec, quantity: item.quantity, unit: item.unit, unitPrice: item.unitPrice, })), }); if (result.success) { toast.success("수주가 수정되었습니다."); // V2 패턴: 저장 후 view 모드로 이동 router.push(`/sales/order-management-sales/${orderId}?mode=view`); return { success: true }; } else { return { success: false, error: result.error || "수주 수정에 실패했습니다." }; } } catch (error) { console.error("Error updating order:", error); return { success: false, error: "수주 수정 중 오류가 발생했습니다." }; } }, [form, orderId, router]); // 동적 config (수정 모드용 타이틀) const dynamicConfig = useMemo(() => { return { ...orderSalesConfig, title: "수주", actions: { ...orderSalesConfig.actions, showEdit: false, // 수정 모드에서는 수정 버튼 숨김 showDelete: false, }, }; }, []); // 커스텀 헤더 액션 (상태 뱃지) const customHeaderActions = useMemo(() => { if (!form) return null; return (
{form.lotNumber} {getOrderStatusBadge(form.status)}
); }, [form]); // 폼 콘텐츠 렌더링 const renderFormContent = useCallback(() => { if (!form) return null; return (
{/* 기본 정보 (읽기전용) */} 기본 정보 (읽기전용)

{form.lotNumber}

{form.orderDate || "-"}

{form.client}

{form.siteName}

{form.manager || "-"}

{form.contact || "-"}

{getOrderStatusBadge(form.status)}
{/* 수주/배송 정보 (편집 가능) */} 수주/배송 정보
{/* 첫 번째 줄: 수주일, 납품요청일, 출고예정일, 배송방식 */}
setForm({ ...form, deliveryRequestDate: e.target.value }) } />
setForm({ ...form, expectedShipDate: e.target.value }) } disabled={form.expectedShipDateUndecided} />
{/* 두 번째 줄: 운임비용, 수신자, 수신처 */}
setForm({ ...form, receiver: e.target.value }) } placeholder="수신자명 입력" />
setForm({ ...form, receiverContact: e.target.value }) } placeholder="수신처 입력" />
{/* 주소 - 전체 너비 */}
setForm({ ...form, address: e.target.value }) } placeholder="주소" className="flex-1" />
{/* 비고 */} 비고