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-help-dialog.tsx | 10 +- lib/swp/table/swp-revision-list-dialog.tsx | 310 +++++++++++++++++++++++++++++ lib/swp/table/swp-table-toolbar.tsx | 4 - lib/swp/table/swp-table.tsx | 275 ++----------------------- 4 files changed, 328 insertions(+), 271 deletions(-) create mode 100644 lib/swp/table/swp-revision-list-dialog.tsx (limited to 'lib/swp/table') diff --git a/lib/swp/table/swp-help-dialog.tsx b/lib/swp/table/swp-help-dialog.tsx index 18f29644..6880a8c7 100644 --- a/lib/swp/table/swp-help-dialog.tsx +++ b/lib/swp/table/swp-help-dialog.tsx @@ -32,7 +32,7 @@ export function SwpUploadHelpDialog() {
{/* 파일명 형식 */}
-

📋 파일명 형식

+

파일명 형식

[OWN_DOC_NO]_[REV_NO]_[STAGE]_[YYYYMMDDhhmmss].[확장자]
@@ -43,7 +43,7 @@ export function SwpUploadHelpDialog() { {/* 각 항목 설명 - 1라인 형태 */}
-

📝 항목 설명

+

항목 설명

@@ -71,7 +71,7 @@ export function SwpUploadHelpDialog() {
스테이지 - - 중공업이 설정한 스테이지입니다 (예: IFA, IFC, AFC, BFC) + - 스테이지 정보를 입력해주세요. (예: IFA, IFC)
@@ -88,7 +88,7 @@ export function SwpUploadHelpDialog() { {/* 예시 */}
-

✅ 올바른 예시

+

올바른 예시

@@ -105,7 +105,7 @@ export function SwpUploadHelpDialog() { {/* 잘못된 예시 */}
-

❌ 잘못된 예시

+

잘못된 예시

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())} + + ))} + + ))} + +
+
+ ); +} + diff --git a/lib/swp/table/swp-table-toolbar.tsx b/lib/swp/table/swp-table-toolbar.tsx index fc8337f5..e7a2ef30 100644 --- a/lib/swp/table/swp-table-toolbar.tsx +++ b/lib/swp/table/swp-table-toolbar.tsx @@ -227,10 +227,6 @@ export function SwpTableToolbar({ {isSyncing ? "동기화 중..." : "SWP 동기화"}
- -
- SWP 문서 관리 시스템 -
{/* 벤더만 파일 업로드 기능 사용 가능 */} 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({
{/* 문서 상세 Dialog */} - - - - 문서 상세 - {selectedDocument && ( - - {selectedDocument.DOC_NO} - {selectedDocument.DOC_TITLE} - - )} - - - {selectedDocument && ( -
- {/* 문서 정보 */} -
-
- 프로젝트: -
{selectedDocument.PROJ_NO}
- {selectedDocument.PROJ_NM && ( -
{selectedDocument.PROJ_NM}
- )} -
-
- 패키지: -
{selectedDocument.PKG_NO || "-"}
-
-
- 업체: -
{selectedDocument.CPY_NM || "-"}
- {selectedDocument.VNDR_CD && ( -
{selectedDocument.VNDR_CD}
- )} -
-
- 최신 리비전: -
{selectedDocument.LTST_REV_NO || "-"}
-
-
- - {/* 리비전 및 파일 목록 */} - {loadingRevisions.has(selectedDocument.DOC_NO) ? ( -
- - 리비전 로딩 중... -
- ) : revisionData[selectedDocument.DOC_NO]?.length ? ( - loadAllFiles(selectedDocument.DOC_NO)} - /> - ) : ( -
- 리비전 없음 -
- )} -
- )} -
-
-
- ); -} - -// ============================================================================ -// 문서 상세 뷰 (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())} - - ))} - - ))} - -
+ selectedDocument && loadAllFiles(selectedDocument.DOC_NO)} + />
); } -- cgit v1.2.3