diff options
Diffstat (limited to 'lib/swp/table/swp-table-columns.tsx')
| -rw-r--r-- | lib/swp/table/swp-table-columns.tsx | 394 |
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, + }, +]; + |
