import { useState, useEffect } from 'react'; import { X, Save, Trash2, Plus, RefreshCw } from 'lucide-react'; import { comms } from '@/communication'; import { PartListItem } from '@/types'; interface PartListDialogProps { projectIdx: number; projectName: string; onClose: () => void; } export function PartListDialog({ projectIdx, projectName, onClose }: PartListDialogProps) { const [parts, setParts] = useState([]); const [loading, setLoading] = useState(false); const [editingIdx, setEditingIdx] = useState(null); const [editForm, setEditForm] = useState>({}); // ESC 키 핸들러 useEffect(() => { const handleKeyDown = (e: KeyboardEvent) => { if (e.key === 'Escape') { if (editingIdx !== null) { setEditingIdx(null); setEditForm({}); } else { onClose(); } } }; window.addEventListener('keydown', handleKeyDown); return () => window.removeEventListener('keydown', handleKeyDown); }, [editingIdx, onClose]); // 데이터 로드 const loadParts = async () => { setLoading(true); try { console.log('[PartList] 로드 시작, projectIdx:', projectIdx); const result = await comms.getPartList(projectIdx); console.log('[PartList] 결과:', result); if (result.Success && result.Data) { console.log('[PartList] 데이터 개수:', result.Data.length); setParts(result.Data); } else { console.error('[PartList] 실패:', result.Message); alert(result.Message || '파트리스트 로드 실패'); } } catch (error) { console.error('파트리스트 로드 실패:', error); alert('파트리스트 로드 중 오류: ' + error); } finally { setLoading(false); } }; useEffect(() => { loadParts(); }, [projectIdx]); // 편집 시작 const startEdit = (part: PartListItem) => { setEditingIdx(part.idx); setEditForm({ ...part }); }; // 편집 취소 const cancelEdit = () => { setEditingIdx(null); setEditForm({}); }; // 저장 const handleSave = async () => { if (!editForm.itemname || !editForm.item) { alert('품명과 자재번호는 필수입니다.'); return; } try { const result = await comms.savePartList( editingIdx || 0, projectIdx, editForm.itemgroup || '', editForm.itemname || '', editForm.item || '', editForm.itemmodel || '', editForm.itemscale || '', editForm.itemunit || '', editForm.qty || 0, editForm.price || 0, editForm.itemsupply || '', editForm.itemsupplyidx || 0, editForm.itemmanu || '', editForm.itemsid || '', editForm.option1 || '', editForm.remark || '', editForm.no || 0, editForm.qtybuy || 0 ); if (result.Success) { await loadParts(); cancelEdit(); } else { alert(result.Message || '저장 실패'); } } catch (error) { console.error('저장 실패:', error); alert('저장 중 오류가 발생했습니다.'); } }; // 삭제 const handleDelete = async (idx: number) => { if (!confirm('정말 삭제하시겠습니까?')) return; try { const result = await comms.deletePartList(idx); if (result.Success) { await loadParts(); } else { alert(result.Message || '삭제 실패'); } } catch (error) { console.error('삭제 실패:', error); alert('삭제 중 오류가 발생했습니다.'); } }; // 새 항목 추가 const addNew = () => { setEditingIdx(-1); setEditForm({ Project: projectIdx, itemgroup: '', itemname: '', item: '', itemmodel: '', itemscale: '', itemunit: 'EA', qty: 1, price: 0, itemsupply: '', itemsupplyidx: 0, itemmanu: '', itemsid: '', option1: '', remark: '', no: 0, qtybuy: 0, }); }; // 금액 계산 const getAmount = (qty: number, price: number) => qty * price; return (
{/* 헤더 */}

파트리스트

{projectName}

{/* 테이블 */}
{loading && parts.length === 0 ? (
) : ( {parts.length === 0 && !loading ? ( ) : ( parts.map((part) => { const isEditing = editingIdx === part.idx; return ( ); }) )} {/* 새 항목 추가 행 */} {editingIdx === -1 && ( )}
No 그룹 품명 모델 규격 단위 수량 단가 금액 공급처 작업
등록된 파트가 없습니다.
{isEditing ? ( setEditForm({ ...editForm, no: parseInt(e.target.value) || 0 })} className="w-full bg-slate-700/50 text-white text-xs px-2 py-1 rounded border border-white/10 focus:border-primary-500 focus:outline-none" /> ) : ( {part.no || ''} )} {isEditing ? ( setEditForm({ ...editForm, itemgroup: e.target.value })} className="w-full bg-slate-700/50 text-white text-xs px-2 py-1 rounded border border-white/10 focus:border-primary-500 focus:outline-none" /> ) : ( {part.itemgroup || ''} )} {isEditing ? ( setEditForm({ ...editForm, itemname: e.target.value })} className="w-full bg-slate-700/50 text-white text-xs px-2 py-1 rounded border border-white/10 focus:border-primary-500 focus:outline-none" required /> ) : ( {part.itemname || ''} )} {isEditing ? ( setEditForm({ ...editForm, itemmodel: e.target.value })} className="w-full bg-slate-700/50 text-white text-xs px-2 py-1 rounded border border-white/10 focus:border-primary-500 focus:outline-none" /> ) : ( {part.itemmodel || ''} )} {isEditing ? ( setEditForm({ ...editForm, itemscale: e.target.value })} className="w-full bg-slate-700/50 text-white text-xs px-2 py-1 rounded border border-white/10 focus:border-primary-500 focus:outline-none" /> ) : ( {part.itemscale || ''} )} {isEditing ? ( setEditForm({ ...editForm, itemunit: e.target.value })} className="w-full bg-slate-700/50 text-white text-xs px-2 py-1 rounded border border-white/10 focus:border-primary-500 focus:outline-none" /> ) : ( {part.itemunit || ''} )} {isEditing ? ( setEditForm({ ...editForm, qty: parseFloat(e.target.value) || 0 })} className="w-full bg-slate-700/50 text-white text-xs px-2 py-1 rounded border border-white/10 focus:border-primary-500 focus:outline-none text-right" /> ) : ( {part.qty?.toLocaleString() || 0} )} {isEditing ? ( setEditForm({ ...editForm, price: parseFloat(e.target.value) || 0 })} className="w-full bg-slate-700/50 text-white text-xs px-2 py-1 rounded border border-white/10 focus:border-primary-500 focus:outline-none text-right" /> ) : ( {part.price?.toLocaleString() || 0} )} {getAmount( isEditing ? editForm.qty || 0 : part.qty || 0, isEditing ? editForm.price || 0 : part.price || 0 ).toLocaleString()} {isEditing ? ( setEditForm({ ...editForm, itemsupply: e.target.value })} className="w-full bg-slate-700/50 text-white text-xs px-2 py-1 rounded border border-white/10 focus:border-primary-500 focus:outline-none" /> ) : ( {part.itemsupply || ''} )} {isEditing ? (
) : (
)}
setEditForm({ ...editForm, no: parseInt(e.target.value) || 0 })} className="w-full bg-slate-700/50 text-white text-xs px-2 py-1 rounded border border-white/10 focus:border-primary-500 focus:outline-none" /> setEditForm({ ...editForm, itemgroup: e.target.value })} className="w-full bg-slate-700/50 text-white text-xs px-2 py-1 rounded border border-white/10 focus:border-primary-500 focus:outline-none" placeholder="그룹" /> setEditForm({ ...editForm, itemname: e.target.value })} className="w-full bg-slate-700/50 text-white text-xs px-2 py-1 rounded border border-white/10 focus:border-primary-500 focus:outline-none" placeholder="품명 *" required /> setEditForm({ ...editForm, itemmodel: e.target.value })} className="w-full bg-slate-700/50 text-white text-xs px-2 py-1 rounded border border-white/10 focus:border-primary-500 focus:outline-none" placeholder="모델" /> setEditForm({ ...editForm, itemscale: e.target.value })} className="w-full bg-slate-700/50 text-white text-xs px-2 py-1 rounded border border-white/10 focus:border-primary-500 focus:outline-none" placeholder="규격" /> setEditForm({ ...editForm, itemunit: e.target.value })} className="w-full bg-slate-700/50 text-white text-xs px-2 py-1 rounded border border-white/10 focus:border-primary-500 focus:outline-none" placeholder="단위" /> setEditForm({ ...editForm, qty: parseFloat(e.target.value) || 0 })} className="w-full bg-slate-700/50 text-white text-xs px-2 py-1 rounded border border-white/10 focus:border-primary-500 focus:outline-none text-right" /> setEditForm({ ...editForm, price: parseFloat(e.target.value) || 0 })} className="w-full bg-slate-700/50 text-white text-xs px-2 py-1 rounded border border-white/10 focus:border-primary-500 focus:outline-none text-right" /> {getAmount(editForm.qty || 0, editForm.price || 0).toLocaleString()} setEditForm({ ...editForm, itemsupply: e.target.value })} className="w-full bg-slate-700/50 text-white text-xs px-2 py-1 rounded border border-white/10 focus:border-primary-500 focus:outline-none" placeholder="공급처" />
)}
{/* 합계 */} {parts.length > 0 && (
{parts.length}개 항목 합계: {parts.reduce((sum, part) => sum + getAmount(part.qty || 0, part.price || 0), 0).toLocaleString()}
)}
); }