From 92766c1f2096852e7f224629963a412af8a16586 Mon Sep 17 00:00:00 2001 From: joonhoekim <26rote@gmail.com> Date: Mon, 27 Oct 2025 10:54:48 +0900 Subject: (김준회) SWP 코드추출 리팩터링, 파일수 조회 쿼리 통일, 헬프다이얼로그 문구 개선 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/swp/table/swp-revision-list-dialog.tsx | 310 +++++++++++++++++++++++++++++ 1 file changed, 310 insertions(+) create mode 100644 lib/swp/table/swp-revision-list-dialog.tsx (limited to 'lib/swp/table/swp-revision-list-dialog.tsx') diff --git a/lib/swp/table/swp-revision-list-dialog.tsx b/lib/swp/table/swp-revision-list-dialog.tsx new file mode 100644 index 00000000..74402bd9 --- /dev/null +++ b/lib/swp/table/swp-revision-list-dialog.tsx @@ -0,0 +1,310 @@ +"use client"; + +import React, { useState } from "react"; +import { + useReactTable, + getCoreRowModel, + getExpandedRowModel, + flexRender, + ExpandedState, +} from "@tanstack/react-table"; +import { + Dialog, + DialogContent, + DialogHeader, + DialogTitle, + DialogDescription, +} from "@/components/ui/dialog"; +import { + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, +} from "@/components/ui/table"; +import { Button } from "@/components/ui/button"; +import { Loader2 } from "lucide-react"; +import { swpRevisionColumns, swpFileColumns, type RevisionRow, type FileRow } from "./swp-table-columns"; +import type { SwpDocumentWithStats } from "../actions"; + +interface SwpRevisionListDialogProps { + open: boolean; + onOpenChange: (open: boolean) => void; + document: SwpDocumentWithStats | null; + revisions: RevisionRow[]; + fileData: Record; + loadingRevisions: boolean; + loadingFiles: Set; + onLoadFiles: (revisionId: number) => void; + onLoadAllFiles: () => void; +} + +export function SwpRevisionListDialog({ + open, + onOpenChange, + document, + revisions, + fileData, + loadingRevisions, + loadingFiles, + onLoadFiles, + onLoadAllFiles, +}: SwpRevisionListDialogProps) { + return ( + + + + 문서 상세 + {document && ( + + {document.DOC_NO} - {document.DOC_TITLE} + + )} + + + {document && ( +
+ {/* 문서 정보 */} +
+
+ 프로젝트: +
{document.PROJ_NO}
+ {document.PROJ_NM && ( +
{document.PROJ_NM}
+ )} +
+
+ 패키지: +
{document.PKG_NO || "-"}
+
+
+ 업체: +
{document.CPY_NM || "-"}
+ {document.VNDR_CD && ( +
{document.VNDR_CD}
+ )} +
+
+ 최신 리비전: +
{document.LTST_REV_NO || "-"}
+
+
+ + {/* 리비전 및 파일 목록 */} + {loadingRevisions ? ( +
+ + 리비전 로딩 중... +
+ ) : revisions.length ? ( + + ) : ( +
+ 리비전 없음 +
+ )} +
+ )} +
+
+ ); +} + +// ============================================================================ +// 문서 상세 뷰 (Dialog용) +// ============================================================================ + +interface DocumentDetailViewProps { + revisions: RevisionRow[]; + fileData: Record; + loadingFiles: Set; + onLoadFiles: (revisionId: number) => void; + onLoadAllFiles: () => void; +} + +function DocumentDetailView({ + revisions, + fileData, + loadingFiles, + onLoadFiles, + onLoadAllFiles, +}: DocumentDetailViewProps) { + const [expandedRevisions, setExpandedRevisions] = useState({}); + const [allExpanded, setAllExpanded] = useState(false); + + const revisionTable = useReactTable({ + data: revisions, + columns: swpRevisionColumns, + state: { + expanded: expandedRevisions, + }, + onExpandedChange: setExpandedRevisions, + getCoreRowModel: getCoreRowModel(), + getExpandedRowModel: getExpandedRowModel(), + getRowCanExpand: () => true, + }); + + const handleExpandAll = () => { + if (allExpanded) { + setExpandedRevisions({}); + } else { + const expanded: ExpandedState = {}; + revisions.forEach((_, index) => { + expanded[index] = true; + }); + setExpandedRevisions(expanded); + onLoadAllFiles(); + } + setAllExpanded(!allExpanded); + }; + + const handleRevisionExpand = (revisionId: number) => { + onLoadFiles(revisionId); + }; + + return ( +
+ {/* 전체 펼치기/접기 버튼 */} +
+ +
+ + {/* 리비전 테이블 */} +
+ + + {revisionTable.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => ( + + {header.isPlaceholder + ? null + : flexRender( + header.column.columnDef.header, + header.getContext() + )} + + ))} + + ))} + + + {revisionTable.getRowModel().rows.map((row) => ( + + {/* 리비전 행 */} + + {row.getVisibleCells().map((cell) => ( + + {cell.column.id === "expander" ? ( +
{ + row.toggleExpanded(); + if (!row.getIsExpanded()) { + handleRevisionExpand(row.original.id); + } + }} + className="cursor-pointer" + > + {flexRender( + cell.column.columnDef.cell, + cell.getContext() + )} +
+ ) : ( + flexRender(cell.column.columnDef.cell, cell.getContext()) + )} +
+ ))} +
+ + {/* 파일 행들 (확장 시) */} + {row.getIsExpanded() && ( + + + {loadingFiles.has(row.original.id) ? ( +
+ + 파일 로딩 중... +
+ ) : fileData[row.original.id]?.length ? ( + + ) : ( +
+ 파일 없음 +
+ )} +
+
+ )} +
+ ))} +
+
+
+
+ ); +} + +// ============================================================================ +// 파일 서브 테이블 +// ============================================================================ + +interface FileSubTableProps { + files: FileRow[]; +} + +function FileSubTable({ files }: FileSubTableProps) { + const fileTable = useReactTable({ + data: files, + columns: swpFileColumns, + getCoreRowModel: getCoreRowModel(), + }); + + return ( +
+ + + {fileTable.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => ( + + {header.isPlaceholder + ? null + : flexRender( + header.column.columnDef.header, + header.getContext() + )} + + ))} + + ))} + + + {fileTable.getRowModel().rows.map((row) => ( + + {row.getVisibleCells().map((cell) => ( + + {flexRender(cell.column.columnDef.cell, cell.getContext())} + + ))} + + ))} + +
+
+ ); +} + -- cgit v1.2.3