From 2b490956c9752c1b756780a3461bc1c37b6fe0a7 Mon Sep 17 00:00:00 2001 From: joonhoekim <26rote@gmail.com> Date: Mon, 15 Sep 2025 18:58:07 +0900 Subject: (김준회) AVL 관리 및 상세 - 기능 구현 1차 + docker compose 내 오류 수정 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/avl/components/avl-history-modal.tsx | 297 +++++++++++++++++++++++++++++++ 1 file changed, 297 insertions(+) create mode 100644 lib/avl/components/avl-history-modal.tsx (limited to 'lib/avl/components/avl-history-modal.tsx') diff --git a/lib/avl/components/avl-history-modal.tsx b/lib/avl/components/avl-history-modal.tsx new file mode 100644 index 00000000..4f0c354b --- /dev/null +++ b/lib/avl/components/avl-history-modal.tsx @@ -0,0 +1,297 @@ +"use client" + +import * as React from "react" +import { + Dialog, + DialogContent, + DialogHeader, + DialogTitle, +} from "@/components/ui/dialog" +import { Badge } from "@/components/ui/badge" +import { Button } from "@/components/ui/button" +import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible" +import { + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, +} from "@/components/ui/table" +import { Calendar, Users, FileText, ChevronDown, ChevronRight } from "lucide-react" +import type { AvlListItem } from "@/lib/avl/types" + +interface AvlHistoryModalProps { + isOpen: boolean + onClose: () => void + avlItem: AvlListItem | null + historyData?: AvlHistoryRecord[] + onLoadHistory?: (avlItem: AvlListItem) => Promise +} + +export interface VendorSnapshot { + id: number + vendorName?: string + avlVendorName?: string + vendorCode?: string + disciplineName?: string + materialNameCustomerSide?: string + materialGroupCode?: string + materialGroupName?: string + tier?: string + hasAvl?: boolean + faTarget?: boolean + headquarterLocation?: string + ownerSuggestion?: boolean + shiSuggestion?: boolean + [key: string]: unknown // 다른 모든 속성들 +} + +export interface AvlHistoryRecord { + id: number + rev: number + createdAt: string + createdBy: string + vendorInfoSnapshot: VendorSnapshot[] // JSON 데이터 + changeDescription?: string +} + +// 스냅샷 테이블 컴포넌트 +interface SnapshotTableProps { + snapshot: VendorSnapshot[] + isOpen: boolean + onToggle: () => void +} + +function SnapshotTable({ snapshot, isOpen, onToggle }: SnapshotTableProps) { + if (!snapshot || snapshot.length === 0) { + return ( +
+ 스냅샷 데이터가 없습니다. +
+ ) + } + + return ( + + + + + +
+
+ + + + No. + 설계공종 + 고객사 AVL 자재명 + 자재그룹 코드 + 자재그룹 명 + AVL 등재업체명 + 협력업체 코드 + 협력업체 명 + 선주제안 + SHI 제안 + 본사 위치 + 등급 + AVL + FA대상 + + + + {snapshot.map((item, index) => ( + + {index + 1} + {item.disciplineName || '-'} + {item.materialNameCustomerSide || '-'} + {item.materialGroupCode || '-'} + {item.materialGroupName || '-'} + {item.avlVendorName || '-'} + {item.vendorCode || '-'} + {item.vendorName || '-'} + + + {item.ownerSuggestion ? "예" : "아니오"} + + + + + {item.shiSuggestion ? "예" : "아니오"} + + + {item.headquarterLocation || '-'} + + {item.tier ? ( + + {item.tier} + + ) : '-'} + + + + {item.hasAvl ? "Y" : "N"} + + + + + {item.faTarget ? "Y" : "N"} + + + + ))} + +
+
+
+
+
+ ) +} + +export function AvlHistoryModal({ + isOpen, + onClose, + avlItem, + historyData, + onLoadHistory +}: AvlHistoryModalProps) { + const [loading, setLoading] = React.useState(false) + const [history, setHistory] = React.useState([]) + const [openSnapshots, setOpenSnapshots] = React.useState>({}) + + // 히스토리 데이터 로드 + React.useEffect(() => { + if (isOpen && avlItem && onLoadHistory) { + setLoading(true) + onLoadHistory(avlItem) + .then(setHistory) + .catch(console.error) + .finally(() => setLoading(false)) + } else if (historyData) { + setHistory(historyData) + } + }, [isOpen, avlItem, onLoadHistory, historyData]) + + // 스냅샷 테이블 토글 함수 + const toggleSnapshot = (recordId: number) => { + setOpenSnapshots(prev => ({ + ...prev, + [recordId]: !prev[recordId] + })) + } + + if (!avlItem) return null + + return ( + + + + + + AVL 리비전 히스토리 + +
+ {avlItem.isTemplate ? "표준 AVL" : "프로젝트 AVL"} - {avlItem.avlKind} + {avlItem.projectCode && ` (${avlItem.projectCode})`} +
+
+ +
+
+ {loading ? ( +
+
히스토리를 불러오는 중...
+
+ ) : history.length === 0 ? ( +
+
히스토리 데이터가 없습니다.
+
+ ) : ( +
+ {history.map((record, index) => ( +
+ {/* 리비전 헤더 */} +
+
+ + Rev {record.rev} + + {index === 0 && ( + + 현재 + + )} +
+
+
+ + {new Date(record.createdAt).toLocaleDateString('ko-KR')} +
+
+
+ + {/* 변경 설명 */} + {record.changeDescription && ( +
+ {record.changeDescription} +
+ )} + + {/* Vendor Info 요약 */} +
+
+
+ {record.vendorInfoSnapshot?.length || 0} +
+
총 협력업체
+
+
+
+ {record.vendorInfoSnapshot?.filter(v => v.hasAvl).length || 0} +
+
AVL 등재
+
+
+
+ {record.vendorInfoSnapshot?.filter(v => v.faTarget).length || 0} +
+
FA 대상
+
+
+ + {/* 스냅샷 테이블 */} +
+ toggleSnapshot(record.id)} + /> +
+
+ ))} +
+ )} +
+
+ +
+ +
+
+
+ ) +} \ No newline at end of file -- cgit v1.2.3