summaryrefslogtreecommitdiff
path: root/lib/swp/table/swp-inbox-table.tsx
diff options
context:
space:
mode:
authorjoonhoekim <26rote@gmail.com>2025-11-17 15:42:29 +0900
committerjoonhoekim <26rote@gmail.com>2025-11-17 15:42:29 +0900
commitb9f575f6110faabc7b062f84bf491d69d88a10a5 (patch)
tree23babc36a1131a861bcb9fbae7fed7314e6ce7e7 /lib/swp/table/swp-inbox-table.tsx
parent85d32a79dcf0f7047406039363baa2e06b859ddd (diff)
(김준회) swp: GetExternalInboxList를 메인 테이블로 변경
Diffstat (limited to 'lib/swp/table/swp-inbox-table.tsx')
-rw-r--r--lib/swp/table/swp-inbox-table.tsx244
1 files changed, 244 insertions, 0 deletions
diff --git a/lib/swp/table/swp-inbox-table.tsx b/lib/swp/table/swp-inbox-table.tsx
new file mode 100644
index 00000000..c3f3a243
--- /dev/null
+++ b/lib/swp/table/swp-inbox-table.tsx
@@ -0,0 +1,244 @@
+"use client";
+
+import React, { useState, useMemo } from "react";
+import {
+ useReactTable,
+ getCoreRowModel,
+ flexRender,
+} from "@tanstack/react-table";
+import {
+ Table,
+ TableBody,
+ TableCell,
+ TableHead,
+ TableHeader,
+ TableRow,
+} from "@/components/ui/table";
+import { Badge } from "@/components/ui/badge";
+import { ColumnDef } from "@tanstack/react-table";
+import { SwpInboxDocumentDetailDialog } from "./swp-inbox-document-detail-dialog";
+import type { SwpFileApiResponse } from "@/lib/swp/api-client";
+
+interface SwpInboxTableProps {
+ files: SwpFileApiResponse[];
+ projNo: string;
+ vendorCode: string;
+ userId: string;
+}
+
+// 문서별로 그룹핑된 데이터 타입
+export interface InboxDocumentItem {
+ ownDocNo: string;
+ latestRevFileCount: number; // 최신 REV의 파일 개수
+ latestStage: string;
+ latestRevNo: string;
+ latestStatus: string | null;
+ latestStatusNm: string | null;
+ files: SwpFileApiResponse[];
+}
+
+// 테이블 컬럼 정의
+const inboxDocumentColumns: ColumnDef<InboxDocumentItem>[] = [
+ {
+ accessorKey: "latestStatusNm",
+ header: "상태",
+ cell: ({ row }) => {
+ const statNm = row.original.latestStatusNm;
+ const stat = row.original.latestStatus;
+ const displayStatus = statNm || stat || "-";
+
+ if (!stat) return displayStatus;
+
+ // STAT 코드 기반 색상 결정
+ const color =
+ stat === "SCW03" || stat === "SCW08" ? "bg-green-100 text-green-800" : // Complete, Checked
+ stat === "SCW02" ? "bg-blue-100 text-blue-800" : // Processing
+ stat === "SCW01" ? "bg-yellow-100 text-yellow-800" : // Standby
+ stat === "SCW04" || stat === "SCW05" || stat === "SCW06" ? "bg-red-100 text-red-800" : // Reject, Error Zip, Error Meta
+ stat === "SCW07" ? "bg-purple-100 text-purple-800" : // Send for Eng Verification
+ stat === "SCW09" ? "bg-gray-100 text-gray-800" : // Cancelled
+ stat === "SCW00" ? "bg-orange-100 text-orange-800" : // Upload
+ "bg-gray-100 text-gray-800"; // 기타
+
+ return (
+ <Badge variant="outline" className={color}>
+ {displayStatus}
+ </Badge>
+ );
+ },
+ size: 120,
+ },
+ {
+ accessorKey: "ownDocNo",
+ header: "OWN_DOC_NO",
+ cell: ({ row }) => (
+ <div className="font-mono text-sm">{row.original.ownDocNo}</div>
+ ),
+ size: 300,
+ },
+ {
+ accessorKey: "latestStage",
+ header: "최신 스테이지",
+ cell: ({ row }) => {
+ const stage = row.original.latestStage;
+ if (!stage) return "-";
+
+ const color =
+ stage === "IFC" ? "bg-green-100 text-green-800" :
+ stage === "IFA" ? "bg-blue-100 text-blue-800" :
+ "bg-gray-100 text-gray-800";
+
+ return (
+ <Badge variant="outline" className={color}>
+ {stage}
+ </Badge>
+ );
+ },
+ size: 120,
+ },
+ {
+ accessorKey: "latestRevNo",
+ header: "최신 REV",
+ cell: ({ row }) => row.original.latestRevNo || "-",
+ size: 100,
+ },
+ {
+ accessorKey: "latestRevFileCount",
+ header: "최신 REV 파일 수",
+ cell: ({ row }) => (
+ <div className="text-center">
+ <div className="text-sm font-medium">
+ {row.original.latestRevFileCount}개
+ </div>
+ </div>
+ ),
+ size: 100,
+ },
+];
+
+export function SwpInboxTable({
+ files,
+ projNo,
+ vendorCode,
+ userId,
+}: SwpInboxTableProps) {
+ const [dialogOpen, setDialogOpen] = useState(false);
+ const [selectedDocument, setSelectedDocument] = useState<InboxDocumentItem | null>(null);
+
+ // 파일들을 문서별로 그룹핑
+ const documents = useMemo(() => {
+ const docMap = new Map<string, SwpFileApiResponse[]>();
+
+ files.forEach((file) => {
+ const docNo = file.OWN_DOC_NO;
+ if (!docMap.has(docNo)) {
+ docMap.set(docNo, []);
+ }
+ docMap.get(docNo)!.push(file);
+ });
+
+ const result: InboxDocumentItem[] = [];
+
+ docMap.forEach((docFiles, ownDocNo) => {
+ // 최신 REV 찾기 (REV_NO 기준으로 정렬)
+ const sortedByRev = [...docFiles].sort((a, b) =>
+ (b.REV_NO || "").localeCompare(a.REV_NO || "")
+ );
+ const latestRevNo = sortedByRev[0].REV_NO || "";
+
+ // 최신 REV의 파일들만 필터링
+ const latestRevFiles = docFiles.filter(file => file.REV_NO === latestRevNo);
+
+ // 최신 REV 내에서 가장 최근 생성된 파일 찾기 (상태 표시용)
+ const sortedLatestRevFiles = [...latestRevFiles].sort((a, b) =>
+ (b.CRTE_DTM || "").localeCompare(a.CRTE_DTM || "")
+ );
+ const latestFile = sortedLatestRevFiles[0];
+
+ result.push({
+ ownDocNo,
+ latestRevFileCount: latestRevFiles.length, // 최신 REV의 파일 개수
+ latestStage: latestFile.STAGE || "",
+ latestRevNo: latestRevNo,
+ latestStatus: latestFile.STAT,
+ latestStatusNm: latestFile.STAT_NM,
+ files: docFiles, // 전체 파일 목록 (상세보기용)
+ });
+ });
+
+ return result.sort((a, b) => a.ownDocNo.localeCompare(b.ownDocNo));
+ }, [files]);
+
+ const table = useReactTable({
+ data: documents,
+ columns: inboxDocumentColumns,
+ getCoreRowModel: getCoreRowModel(),
+ });
+
+ // 문서 클릭 핸들러
+ const handleDocumentClick = (document: InboxDocumentItem) => {
+ setSelectedDocument(document);
+ setDialogOpen(true);
+ };
+
+ return (
+ <div className="space-y-4">
+ {/* 테이블 */}
+ <div className="rounded-md border">
+ <Table>
+ <TableHeader>
+ {table.getHeaderGroups().map((headerGroup) => (
+ <TableRow key={headerGroup.id}>
+ {headerGroup.headers.map((header) => (
+ <TableHead key={header.id}>
+ {header.isPlaceholder
+ ? null
+ : flexRender(
+ header.column.columnDef.header,
+ header.getContext()
+ )}
+ </TableHead>
+ ))}
+ </TableRow>
+ ))}
+ </TableHeader>
+ <TableBody>
+ {table.getRowModel().rows?.length ? (
+ table.getRowModel().rows.map((row) => (
+ <TableRow
+ key={row.id}
+ data-state={row.getIsSelected() && "selected"}
+ className="hover:bg-muted/50 cursor-pointer"
+ onClick={() => handleDocumentClick(row.original)}
+ >
+ {row.getVisibleCells().map((cell) => (
+ <TableCell key={cell.id}>
+ {flexRender(cell.column.columnDef.cell, cell.getContext())}
+ </TableCell>
+ ))}
+ </TableRow>
+ ))
+ ) : (
+ <TableRow>
+ <TableCell colSpan={inboxDocumentColumns.length} className="h-24 text-center">
+ 업로드한 파일이 없습니다.
+ </TableCell>
+ </TableRow>
+ )}
+ </TableBody>
+ </Table>
+ </div>
+
+ {/* 문서 상세 Dialog */}
+ <SwpInboxDocumentDetailDialog
+ open={dialogOpen}
+ onOpenChange={setDialogOpen}
+ document={selectedDocument}
+ projNo={projNo}
+ vendorCode={vendorCode}
+ userId={userId}
+ />
+ </div>
+ );
+}
+