'use client'; /** * 자재투입 모달 (기획서 기반) * * 기획서 변경: BOM 체크박스 → 투입수량 입력 테이블 * 컬럼: 로트번호 | 품목명 | 수량 | 단위 | 투입 수량 (input, 숫자만) * 하단: 취소 / 투입 */ import { useState, useEffect, useCallback } from 'react'; import { Loader2 } from 'lucide-react'; import { ContentSkeleton } from '@/components/ui/skeleton'; import { Dialog, DialogContent, DialogHeader, DialogTitle, } from '@/components/ui/dialog'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from '@/components/ui/table'; import { toast } from 'sonner'; import { isNextRedirectError } from '@/lib/utils/redirect-error'; import { getMaterialsForWorkOrder, registerMaterialInput, type MaterialForInput } from './actions'; import type { WorkOrder } from '../ProductionDashboard/types'; import type { MaterialInput } from './types'; interface MaterialInputModalProps { open: boolean; onOpenChange: (open: boolean) => void; order: WorkOrder | null; onComplete?: () => void; isCompletionFlow?: boolean; onSaveMaterials?: (orderId: string, materials: MaterialInput[]) => void; savedMaterials?: MaterialInput[]; } export function MaterialInputModal({ open, onOpenChange, order, onComplete, isCompletionFlow = false, onSaveMaterials, }: MaterialInputModalProps) { const [materials, setMaterials] = useState([]); const [inputQuantities, setInputQuantities] = useState>({}); const [isLoading, setIsLoading] = useState(false); const [isSubmitting, setIsSubmitting] = useState(false); // 목업 자재 데이터 (기획서 기반 10행) const MOCK_MATERIALS: MaterialForInput[] = Array.from({ length: 10 }, (_, i) => ({ id: 100 + i, materialCode: '123123', materialName: '품목명', unit: 'm', currentStock: 500, fifoRank: i + 1, })); // API로 자재 목록 로드 const loadMaterials = useCallback(async () => { if (!order) return; setIsLoading(true); try { // 목업 아이템인 경우 목업 자재 데이터 사용 if (order.id.startsWith('mock-')) { setMaterials(MOCK_MATERIALS); const initialQuantities: Record = {}; MOCK_MATERIALS.forEach((m) => { initialQuantities[String(m.id)] = ''; }); setInputQuantities(initialQuantities); setIsLoading(false); return; } const result = await getMaterialsForWorkOrder(order.id); if (result.success) { setMaterials(result.data); // 초기 투입 수량 비우기 const initialQuantities: Record = {}; result.data.forEach((m) => { initialQuantities[String(m.id)] = ''; }); setInputQuantities(initialQuantities); } else { toast.error(result.error || '자재 목록 조회에 실패했습니다.'); } } catch (error) { if (isNextRedirectError(error)) throw error; console.error('[MaterialInputModal] loadMaterials error:', error); toast.error('자재 목록 로드 중 오류가 발생했습니다.'); } finally { setIsLoading(false); } }, [order]); // 모달이 열릴 때 데이터 로드 useEffect(() => { if (open && order) { loadMaterials(); } }, [open, order, loadMaterials]); // 투입 수량 변경 핸들러 (숫자만 허용) const handleQuantityChange = (materialId: string, value: string) => { // 숫자만 허용 const numericValue = value.replace(/[^0-9]/g, ''); setInputQuantities((prev) => ({ ...prev, [materialId]: numericValue, })); }; // 투입 등록 const handleSubmit = async () => { if (!order) return; // 투입 수량이 입력된 항목 필터 const materialsWithQuantity = materials.filter((m) => { const qty = inputQuantities[String(m.id)]; return qty && parseInt(qty) > 0; }); if (materialsWithQuantity.length === 0) { toast.error('투입 수량을 입력해주세요.'); return; } setIsSubmitting(true); try { const materialIds = materialsWithQuantity.map((m) => m.id); const result = await registerMaterialInput(order.id, materialIds); if (result.success) { toast.success('자재 투입이 등록되었습니다.'); // onSaveMaterials 콜백 호출 if (onSaveMaterials) { const savedList: MaterialInput[] = materialsWithQuantity.map((m) => ({ id: String(m.id), lotNo: '', // API에서 가져올 필드 materialName: m.materialName, quantity: m.currentStock, unit: m.unit, inputQuantity: parseInt(inputQuantities[String(m.id)] || '0'), })); onSaveMaterials(order.id, savedList); } resetAndClose(); if (isCompletionFlow && onComplete) { onComplete(); } } else { toast.error(result.error || '자재 투입 등록에 실패했습니다.'); } } catch (error) { if (isNextRedirectError(error)) throw error; console.error('[MaterialInputModal] handleSubmit error:', error); toast.error('자재 투입 등록 중 오류가 발생했습니다.'); } finally { setIsSubmitting(false); } }; const handleCancel = () => { resetAndClose(); }; const resetAndClose = () => { setInputQuantities({}); onOpenChange(false); }; if (!order) return null; return ( {/* 헤더 */} 자재 투입
{/* 자재 목록 테이블 */} {isLoading ? ( ) : materials.length === 0 ? (
로트번호 품목명 수량 단위 투입 수량 이 공정에 배정된 자재가 없습니다.
) : (
로트번호 품목명 수량 단위 투입 수량 {materials.map((material, index) => ( {material.materialCode} {material.materialName} {material.currentStock.toLocaleString()} {material.unit} handleQuantityChange(String(material.id), e.target.value) } className="w-20 mx-auto text-center h-8 text-sm" /> ))}
)} {/* 버튼 영역 */}
); }