import { useState, useEffect } from 'react'; import { ClipboardList, Search, RefreshCw, Plus, Save, Trash2, X, DollarSign, } from 'lucide-react'; import { comms } from '@/communication'; import { PartListItem } from '@/types'; import { useSearchParams } from 'react-router-dom'; export function PartList() { const [searchParams] = useSearchParams(); const projectIdx = parseInt(searchParams.get('idx') || '0'); const projectName = searchParams.get('name') || ''; const [parts, setParts] = useState([]); const [filteredParts, setFilteredParts] = useState([]); const [loading, setLoading] = useState(false); const [searchKey, setSearchKey] = useState(''); const [editingIdx, setEditingIdx] = useState(null); const [editForm, setEditForm] = useState>({}); const [showSummary, setShowSummary] = useState(false); // 데이터 로드 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); setFilteredParts(result.Data); } else { console.error('[PartList] 실패:', result.Message); alert(result.Message || '파트리스트 로드 실패'); } } catch (error) { console.error('파트리스트 로드 실패:', error); alert('파트리스트 로드 중 오류: ' + error); } finally { setLoading(false); } }; useEffect(() => { if (projectIdx > 0) { loadParts(); } }, [projectIdx]); // 검색 useEffect(() => { if (!searchKey.trim()) { setFilteredParts(parts); return; } const search = searchKey.toLowerCase(); const filtered = parts.filter((part) => { return ( part.itemsid?.toLowerCase().includes(search) || part.itemname?.toLowerCase().includes(search) || part.itemmodel?.toLowerCase().includes(search) ); }); setFilteredParts(filtered); }, [searchKey, parts]); // 편집 시작 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 || '', '', // 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: '', 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; // 합계 계산 const totalAmount = filteredParts.reduce((sum, part) => sum + getAmount(part.qty || 0, part.price || 0), 0); // 그룹별 합계 const groupSummary = filteredParts.reduce((acc, part) => { const group = part.itemgroup || '미분류'; if (!acc[group]) { acc[group] = { count: 0, amount: 0 }; } acc[group].count++; acc[group].amount += getAmount(part.qty || 0, part.price || 0); return acc; }, {} as Record); return (
{/* 헤더 */}

파트리스트

{projectName}

({filteredParts.length}건)
{/* 검색 */}
setSearchKey(e.target.value)} placeholder="SID, 품명, 모델로 검색..." className="flex-1 bg-transparent text-white placeholder-white/30 focus:outline-none text-sm" /> {searchKey && ( )}
{/* 테이블 */}
{loading && parts.length === 0 ? (
) : ( {filteredParts.length === 0 && !loading ? ( ) : ( filteredParts.map((part) => { const isEditing = editingIdx === part.idx; return ( ); }) )} {/* 새 항목 추가 행 */} {editingIdx === -1 && ( )}
No 그룹 SID 품명 모델 단위 수량 단가 금액 공급처 작업
{searchKey ? '검색 결과가 없습니다.' : '등록된 파트가 없습니다.'}
{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, itemsid: 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.itemsid || ''} )} {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, 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, itemsid: 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="SID" /> 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, 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="공급처" />
)}
{/* 하단 정보 */}
{/* 좌측: 비용 요약 */}
{showSummary && (
{Object.entries(groupSummary).map(([group, data]) => (
{group}
{data.count}건 {data.amount.toLocaleString()}원
))}
)}
{/* 우측: 합계 */}
총 항목 {filteredParts.length}개
총 금액 {totalAmount.toLocaleString()}원
); }