summaryrefslogtreecommitdiff
path: root/lib/swp/table/swp-table-columns.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'lib/swp/table/swp-table-columns.tsx')
-rw-r--r--lib/swp/table/swp-table-columns.tsx394
1 files changed, 394 insertions, 0 deletions
diff --git a/lib/swp/table/swp-table-columns.tsx b/lib/swp/table/swp-table-columns.tsx
new file mode 100644
index 00000000..dd605453
--- /dev/null
+++ b/lib/swp/table/swp-table-columns.tsx
@@ -0,0 +1,394 @@
+"use client";
+
+import { ColumnDef } from "@tanstack/react-table";
+import { Badge } from "@/components/ui/badge";
+import { Button } from "@/components/ui/button";
+import { ChevronDown, ChevronRight, FileIcon } from "lucide-react";
+import { formatDistanceToNow } from "date-fns";
+import { ko } from "date-fns/locale";
+import type { SwpDocumentWithStats } from "../actions";
+
+export const swpDocumentColumns: ColumnDef<SwpDocumentWithStats>[] = [
+ {
+ id: "expander",
+ header: () => null,
+ cell: ({ row }) => {
+ return row.getCanExpand() ? (
+ <Button
+ variant="ghost"
+ size="sm"
+ onClick={row.getToggleExpandedHandler()}
+ className="h-8 w-8 p-0"
+ >
+ {row.getIsExpanded() ? (
+ <ChevronDown className="h-4 w-4" />
+ ) : (
+ <ChevronRight className="h-4 w-4" />
+ )}
+ </Button>
+ ) : null;
+ },
+ size: 50,
+ },
+ {
+ accessorKey: "DOC_NO",
+ header: "문서번호",
+ cell: ({ row }) => (
+ <div className="font-mono text-sm">{row.original.DOC_NO}</div>
+ ),
+ size: 250,
+ },
+ {
+ accessorKey: "DOC_TITLE",
+ header: "문서제목",
+ cell: ({ row }) => (
+ <div className="max-w-md truncate" title={row.original.DOC_TITLE}>
+ {row.original.DOC_TITLE}
+ </div>
+ ),
+ size: 300,
+ },
+ {
+ accessorKey: "PROJ_NO",
+ header: "프로젝트",
+ cell: ({ row }) => (
+ <div>
+ <div className="font-medium">{row.original.PROJ_NO}</div>
+ {row.original.PROJ_NM && (
+ <div className="text-xs text-muted-foreground truncate max-w-[150px]">
+ {row.original.PROJ_NM}
+ </div>
+ )}
+ </div>
+ ),
+ size: 150,
+ },
+ {
+ accessorKey: "PKG_NO",
+ header: "패키지",
+ cell: ({ row }) => row.original.PKG_NO || "-",
+ size: 100,
+ },
+ {
+ accessorKey: "VNDR_CD",
+ header: "업체",
+ cell: ({ row }) => (
+ <div>
+ {row.original.VNDR_CD && (
+ <div className="text-xs text-muted-foreground">{row.original.VNDR_CD}</div>
+ )}
+ {row.original.CPY_NM && (
+ <div className="text-sm truncate max-w-[120px]" title={row.original.CPY_NM}>
+ {row.original.CPY_NM}
+ </div>
+ )}
+ </div>
+ ),
+ size: 120,
+ },
+ {
+ accessorKey: "STAGE",
+ header: "스테이지",
+ cell: ({ row }) => {
+ const stage = row.original.STAGE;
+ 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: 80,
+ },
+ {
+ accessorKey: "LTST_REV_NO",
+ header: "최신 REV",
+ cell: ({ row }) => row.original.LTST_REV_NO || "-",
+ size: 80,
+ },
+ {
+ id: "stats",
+ header: "REV/파일",
+ cell: ({ row }) => (
+ <div className="text-center">
+ <div className="text-sm font-medium">
+ {row.original.revision_count} / {row.original.file_count}
+ </div>
+ </div>
+ ),
+ size: 100,
+ },
+ {
+ accessorKey: "sync_status",
+ header: "상태",
+ cell: ({ row }) => {
+ const status = row.original.sync_status;
+ const color =
+ status === "synced" ? "bg-green-100 text-green-800" :
+ status === "pending" ? "bg-yellow-100 text-yellow-800" :
+ "bg-red-100 text-red-800";
+
+ return (
+ <Badge variant="outline" className={color}>
+ {status}
+ </Badge>
+ );
+ },
+ size: 80,
+ },
+ {
+ accessorKey: "last_synced_at",
+ header: "동기화",
+ cell: ({ row }) => (
+ <div className="text-xs text-muted-foreground">
+ {formatDistanceToNow(new Date(row.original.last_synced_at), {
+ addSuffix: true,
+ locale: ko,
+ })}
+ </div>
+ ),
+ size: 100,
+ },
+];
+
+// ============================================================================
+// 리비전 컬럼 (서브 테이블용)
+// ============================================================================
+
+export interface RevisionRow {
+ id: number;
+ DOC_NO: string;
+ REV_NO: string;
+ STAGE: string;
+ ACTV_NO: string | null;
+ OFDC_NO: string | null;
+ sync_status: "synced" | "pending" | "error";
+ last_synced_at: Date;
+ file_count: number;
+}
+
+export const swpRevisionColumns: ColumnDef<RevisionRow>[] = [
+ {
+ id: "expander",
+ header: () => null,
+ cell: ({ row }) => {
+ return row.getCanExpand() ? (
+ <Button
+ variant="ghost"
+ size="sm"
+ onClick={row.getToggleExpandedHandler()}
+ className="h-8 w-8 p-0 ml-8"
+ >
+ {row.getIsExpanded() ? (
+ <ChevronDown className="h-4 w-4" />
+ ) : (
+ <ChevronRight className="h-4 w-4" />
+ )}
+ </Button>
+ ) : null;
+ },
+ size: 100,
+ },
+ {
+ accessorKey: "REV_NO",
+ header: "리비전",
+ cell: ({ row }) => (
+ <Badge variant="secondary" className="font-mono">
+ REV {row.original.REV_NO}
+ </Badge>
+ ),
+ size: 100,
+ },
+ {
+ accessorKey: "STAGE",
+ header: "스테이지",
+ cell: ({ row }) => {
+ const stage = row.original.STAGE;
+ 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: 100,
+ },
+ {
+ accessorKey: "OFDC_NO",
+ header: "OFDC 번호",
+ cell: ({ row }) => (
+ <div className="font-mono text-sm">{row.original.OFDC_NO || "-"}</div>
+ ),
+ size: 200,
+ },
+ {
+ accessorKey: "ACTV_NO",
+ header: "Activity",
+ cell: ({ row }) => (
+ <div className="font-mono text-xs text-muted-foreground">
+ {row.original.ACTV_NO || "-"}
+ </div>
+ ),
+ size: 250,
+ },
+ {
+ id: "file_count",
+ header: "파일 수",
+ cell: ({ row }) => (
+ <div className="flex items-center gap-2">
+ <FileIcon className="h-4 w-4 text-muted-foreground" />
+ <span className="font-medium">{row.original.file_count}</span>
+ </div>
+ ),
+ size: 100,
+ },
+ {
+ accessorKey: "sync_status",
+ header: "상태",
+ cell: ({ row }) => {
+ const status = row.original.sync_status;
+ const color =
+ status === "synced" ? "bg-green-100 text-green-800" :
+ status === "pending" ? "bg-yellow-100 text-yellow-800" :
+ "bg-red-100 text-red-800";
+
+ return (
+ <Badge variant="outline" className={color}>
+ {status}
+ </Badge>
+ );
+ },
+ size: 80,
+ },
+ {
+ accessorKey: "last_synced_at",
+ header: "동기화",
+ cell: ({ row }) => (
+ <div className="text-xs text-muted-foreground">
+ {formatDistanceToNow(new Date(row.original.last_synced_at), {
+ addSuffix: true,
+ locale: ko,
+ })}
+ </div>
+ ),
+ size: 100,
+ },
+];
+
+// ============================================================================
+// 파일 컬럼 (서브 서브 테이블용)
+// ============================================================================
+
+export interface FileRow {
+ id: number;
+ FILE_NM: string;
+ FILE_SEQ: string;
+ FILE_SZ: string | null;
+ FLD_PATH: string | null;
+ STAT: string | null;
+ STAT_NM: string | null;
+ sync_status: "synced" | "pending" | "error";
+ created_at: Date;
+}
+
+export const swpFileColumns: ColumnDef<FileRow>[] = [
+ {
+ id: "spacer",
+ header: () => null,
+ cell: () => <div className="w-16" />,
+ size: 150,
+ },
+ {
+ accessorKey: "FILE_SEQ",
+ header: "순서",
+ cell: ({ row }) => (
+ <Badge variant="outline" className="font-mono">
+ #{row.original.FILE_SEQ}
+ </Badge>
+ ),
+ size: 80,
+ },
+ {
+ accessorKey: "FILE_NM",
+ header: "파일명",
+ cell: ({ row }) => (
+ <div className="flex items-center gap-2">
+ <FileIcon className="h-4 w-4 text-blue-500" />
+ <span className="font-mono text-sm">{row.original.FILE_NM}</span>
+ </div>
+ ),
+ size: 400,
+ },
+ {
+ accessorKey: "FILE_SZ",
+ header: "크기",
+ cell: ({ row }) => {
+ const size = row.original.FILE_SZ;
+ if (!size) return "-";
+
+ const bytes = parseInt(size, 10);
+ if (isNaN(bytes)) return size;
+
+ const kb = bytes / 1024;
+ const mb = kb / 1024;
+
+ return mb >= 1
+ ? `${mb.toFixed(2)} MB`
+ : `${kb.toFixed(2)} KB`;
+ },
+ size: 100,
+ },
+ {
+ accessorKey: "STAT_NM",
+ header: "상태",
+ cell: ({ row }) => {
+ const status = row.original.STAT_NM;
+ if (!status) return "-";
+
+ const color = status === "Complete"
+ ? "bg-green-100 text-green-800"
+ : "bg-gray-100 text-gray-800";
+
+ return (
+ <Badge variant="outline" className={color}>
+ {status}
+ </Badge>
+ );
+ },
+ size: 100,
+ },
+ {
+ accessorKey: "FLD_PATH",
+ header: "경로",
+ cell: ({ row }) => (
+ <div className="font-mono text-xs text-muted-foreground truncate max-w-[200px]" title={row.original.FLD_PATH || ""}>
+ {row.original.FLD_PATH || "-"}
+ </div>
+ ),
+ size: 200,
+ },
+ {
+ accessorKey: "created_at",
+ header: "생성일",
+ cell: ({ row }) => (
+ <div className="text-xs text-muted-foreground">
+ {formatDistanceToNow(new Date(row.original.created_at), {
+ addSuffix: true,
+ locale: ko,
+ })}
+ </div>
+ ),
+ size: 100,
+ },
+];
+