diff options
| author | joonhoekim <26rote@gmail.com> | 2025-10-27 10:54:48 +0900 |
|---|---|---|
| committer | joonhoekim <26rote@gmail.com> | 2025-10-27 10:54:48 +0900 |
| commit | 92766c1f2096852e7f224629963a412af8a16586 (patch) | |
| tree | e79600d6df56fce1a84fe8a76d69339e5d1a85c4 /lib/swp/table/swp-table.tsx | |
| parent | 65a68325658401dd8a90ea900c1542c17c63d7ce (diff) | |
(김준회) SWP 코드추출 리팩터링, 파일수 조회 쿼리 통일, 헬프다이얼로그 문구 개선
Diffstat (limited to 'lib/swp/table/swp-table.tsx')
| -rw-r--r-- | lib/swp/table/swp-table.tsx | 275 |
1 files changed, 13 insertions, 262 deletions
diff --git a/lib/swp/table/swp-table.tsx b/lib/swp/table/swp-table.tsx index 8ae90bdd..47c9905a 100644 --- a/lib/swp/table/swp-table.tsx +++ b/lib/swp/table/swp-table.tsx @@ -17,16 +17,9 @@ import { TableRow, } from "@/components/ui/table"; import { Button } from "@/components/ui/button"; -import { - Dialog, - DialogContent, - DialogHeader, - DialogTitle, - DialogDescription, -} from "@/components/ui/dialog"; -import { Loader2 } from "lucide-react"; -import { swpDocumentColumns, swpRevisionColumns, swpFileColumns, type RevisionRow, type FileRow } from "./swp-table-columns"; +import { swpDocumentColumns, type RevisionRow, type FileRow } from "./swp-table-columns"; import { fetchDocumentRevisions, fetchRevisionFiles, type SwpDocumentWithStats } from "../actions"; +import { SwpRevisionListDialog } from "./swp-revision-list-dialog"; interface SwpTableProps { initialData: SwpDocumentWithStats[]; @@ -227,259 +220,17 @@ export function SwpTable({ </div> {/* 문서 상세 Dialog */} - <Dialog open={dialogOpen} onOpenChange={setDialogOpen}> - <DialogContent className="max-w-6xl max-h-[90vh]"> - <DialogHeader> - <DialogTitle>문서 상세</DialogTitle> - {selectedDocument && ( - <DialogDescription> - {selectedDocument.DOC_NO} - {selectedDocument.DOC_TITLE} - </DialogDescription> - )} - </DialogHeader> - - {selectedDocument && ( - <div className="space-y-4 overflow-y-auto"> - {/* 문서 정보 */} - <div className="grid grid-cols-1 md:grid-cols-4 gap-4 p-4 bg-muted/30 rounded-lg"> - <div> - <span className="text-sm font-semibold">프로젝트:</span> - <div className="text-sm">{selectedDocument.PROJ_NO}</div> - {selectedDocument.PROJ_NM && ( - <div className="text-xs text-muted-foreground">{selectedDocument.PROJ_NM}</div> - )} - </div> - <div> - <span className="text-sm font-semibold">패키지:</span> - <div className="text-sm">{selectedDocument.PKG_NO || "-"}</div> - </div> - <div> - <span className="text-sm font-semibold">업체:</span> - <div className="text-sm">{selectedDocument.CPY_NM || "-"}</div> - {selectedDocument.VNDR_CD && ( - <div className="text-xs text-muted-foreground">{selectedDocument.VNDR_CD}</div> - )} - </div> - <div> - <span className="text-sm font-semibold">최신 리비전:</span> - <div className="text-sm">{selectedDocument.LTST_REV_NO || "-"}</div> - </div> - </div> - - {/* 리비전 및 파일 목록 */} - {loadingRevisions.has(selectedDocument.DOC_NO) ? ( - <div className="flex items-center justify-center p-8"> - <Loader2 className="h-6 w-6 animate-spin" /> - <span className="ml-2">리비전 로딩 중...</span> - </div> - ) : revisionData[selectedDocument.DOC_NO]?.length ? ( - <DocumentDetailView - revisions={revisionData[selectedDocument.DOC_NO]} - fileData={fileData} - loadingFiles={loadingFiles} - onLoadFiles={loadFiles} - onLoadAllFiles={() => loadAllFiles(selectedDocument.DOC_NO)} - /> - ) : ( - <div className="p-8 text-center text-muted-foreground"> - 리비전 없음 - </div> - )} - </div> - )} - </DialogContent> - </Dialog> - </div> - ); -} - -// ============================================================================ -// 문서 상세 뷰 (Dialog용) -// ============================================================================ - -interface DocumentDetailViewProps { - revisions: RevisionRow[]; - fileData: Record<number, FileRow[]>; - loadingFiles: Set<number>; - onLoadFiles: (revisionId: number) => void; - onLoadAllFiles: () => void; -} - -function DocumentDetailView({ - revisions, - fileData, - loadingFiles, - onLoadFiles, - onLoadAllFiles, -}: DocumentDetailViewProps) { - const [expandedRevisions, setExpandedRevisions] = useState<ExpandedState>({}); - 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 ( - <div className="space-y-4"> - {/* 전체 펼치기/접기 버튼 */} - <div className="flex justify-end"> - <Button - variant="outline" - size="sm" - onClick={handleExpandAll} - > - {allExpanded ? "모두 접기" : "모두 펼치기"} - </Button> - </div> - - {/* 리비전 테이블 */} - <div className="rounded-md border"> - <Table> - <TableHeader> - {revisionTable.getHeaderGroups().map((headerGroup) => ( - <TableRow key={headerGroup.id} className="bg-muted/50"> - {headerGroup.headers.map((header) => ( - <TableHead key={header.id} className="font-semibold"> - {header.isPlaceholder - ? null - : flexRender( - header.column.columnDef.header, - header.getContext() - )} - </TableHead> - ))} - </TableRow> - ))} - </TableHeader> - <TableBody> - {revisionTable.getRowModel().rows.map((row) => ( - <React.Fragment key={row.id}> - {/* 리비전 행 */} - <TableRow className="bg-muted/20"> - {row.getVisibleCells().map((cell) => ( - <TableCell key={cell.id}> - {cell.column.id === "expander" ? ( - <div - onClick={() => { - row.toggleExpanded(); - if (!row.getIsExpanded()) { - handleRevisionExpand(row.original.id); - } - }} - className="cursor-pointer" - > - {flexRender( - cell.column.columnDef.cell, - cell.getContext() - )} - </div> - ) : ( - flexRender(cell.column.columnDef.cell, cell.getContext()) - )} - </TableCell> - ))} - </TableRow> - - {/* 파일 행들 (확장 시) */} - {row.getIsExpanded() && ( - <TableRow> - <TableCell colSpan={swpRevisionColumns.length} className="p-0 bg-blue-50/30"> - {loadingFiles.has(row.original.id) ? ( - <div className="flex items-center justify-center p-4"> - <Loader2 className="h-5 w-5 animate-spin" /> - <span className="ml-2 text-sm">파일 로딩 중...</span> - </div> - ) : fileData[row.original.id]?.length ? ( - <FileSubTable files={fileData[row.original.id]} /> - ) : ( - <div className="p-4 text-center text-sm text-muted-foreground"> - 파일 없음 - </div> - )} - </TableCell> - </TableRow> - )} - </React.Fragment> - ))} - </TableBody> - </Table> - </div> - </div> - ); -} - -// ============================================================================ -// 파일 서브 테이블 -// ============================================================================ - -interface FileSubTableProps { - files: FileRow[]; -} - -function FileSubTable({ files }: FileSubTableProps) { - const fileTable = useReactTable({ - data: files, - columns: swpFileColumns, - getCoreRowModel: getCoreRowModel(), - }); - - return ( - <div className="border-l-4 border-green-200"> - <Table> - <TableHeader> - {fileTable.getHeaderGroups().map((headerGroup) => ( - <TableRow key={headerGroup.id} className="bg-blue-50/50"> - {headerGroup.headers.map((header) => ( - <TableHead key={header.id} className="font-semibold text-xs"> - {header.isPlaceholder - ? null - : flexRender( - header.column.columnDef.header, - header.getContext() - )} - </TableHead> - ))} - </TableRow> - ))} - </TableHeader> - <TableBody> - {fileTable.getRowModel().rows.map((row) => ( - <TableRow key={row.id} className="bg-green-50/20 hover:bg-green-50/40"> - {row.getVisibleCells().map((cell) => ( - <TableCell key={cell.id} className="py-2"> - {flexRender(cell.column.columnDef.cell, cell.getContext())} - </TableCell> - ))} - </TableRow> - ))} - </TableBody> - </Table> + <SwpRevisionListDialog + open={dialogOpen} + onOpenChange={setDialogOpen} + document={selectedDocument} + revisions={selectedDocument ? revisionData[selectedDocument.DOC_NO] || [] : []} + fileData={fileData} + loadingRevisions={selectedDocument ? loadingRevisions.has(selectedDocument.DOC_NO) : false} + loadingFiles={loadingFiles} + onLoadFiles={loadFiles} + onLoadAllFiles={() => selectedDocument && loadAllFiles(selectedDocument.DOC_NO)} + /> </div> ); } |
