// lib/tbe-last/table/tbe-last-table-columns.tsx "use client" import * as React from "react" import { type ColumnDef } from "@tanstack/react-table" import { FileText, Package, ListChecks } from "lucide-react" import { Badge } from "@/components/ui/badge" import { Button } from "@/components/ui/button" import { Checkbox } from "@/components/ui/checkbox" import { DataTableColumnHeaderSimple } from "@/components/data-table/data-table-column-simple-header" import { formatDate } from "@/lib/utils" import { TbeLastView } from "@/db/schema/tbeLastView" interface GetColumnsProps { onOpenSessionDetail: (sessionId: number) => void; onOpenDocuments: (sessionId: number) => void; onOpenPrItems: (sessionId: number) => void; onOpenEvaluation: (session: TbeLastView) => void; getPrCountsOverride?: (sessionId: number) => { total: number, major: number } | undefined; } export function getColumns({ onOpenSessionDetail, onOpenDocuments, onOpenPrItems, onOpenEvaluation, getPrCountsOverride, }: GetColumnsProps): ColumnDef[] { const columns: ColumnDef[] = [ // Select Column { id: "select", header: ({ table }) => ( table.toggleAllPageRowsSelected(!!value)} aria-label="Select all" className="translate-y-0.5" /> ), cell: ({ row }) => ( row.toggleSelected(!!value)} aria-label="Select row" className="translate-y-0.5" /> ), size: 40, enableSorting: false, enableHiding: false, }, // TBE Session Code { accessorKey: "sessionCode", header: ({ column }) => ( ), meta: { excelHeader: "TBE Code" }, cell: ({ row }) => { const sessionId = row.original.tbeSessionId; const sessionCode = row.original.sessionCode; return ( ); }, size: 120, }, // RFQ Code { accessorKey: "rfqCode", header: ({ column }) => ( ), cell: ({ row }) => row.original.rfqCode, size: 120, meta: { excelHeader: "RFQ Code" }, }, { id: "tbeRequired", header: ({ column }) => ( ), meta: { excelHeader: "TBE 필요" }, cell: ({ row, table }) => { const rfqCode = row.original.rfqCode; const sessionStatus = row.original.sessionStatus; // 같은 RFQ의 첫 번째 행에만 체크박스 표시 const allRows = table.getRowModel().rows; const isFirstInGroup = allRows.findIndex( r => r.original.rfqCode === rfqCode ) === allRows.indexOf(row); if (!isFirstInGroup) return null; // 같은 RFQ의 모든 row const rfqRows = allRows.filter( r => r.original.rfqCode === rfqCode ); const vendorCount = rfqRows.length; // 같은 RFQ의 row들이 선택되었는지 확인 const isChecked = rfqRows.every(r => r.getIsSelected()); const isIndeterminate = rfqRows.some(r => r.getIsSelected()) && !isChecked; return (
{ // RFQ의 모든 벤더 선택/해제 rfqRows.forEach(r => { r.toggleSelected(!!checked); }); }} /> ({vendorCount} vendors)
); }, size: 120, }, // RFQ Title { accessorKey: "rfqTitle", header: ({ column }) => ( ), cell: ({ row }) => row.original.rfqTitle || "-", size: 200, meta: { excelHeader: "RFQ Title" }, }, // RFQ Due Date { accessorKey: "rfqDueDate", header: ({ column }) => ( ), cell: ({ row }) => { const date = row.original.rfqDueDate; return date ? formatDate(date, "KR") : "-"; }, size: 100, meta: { excelHeader: "Due Date" }, }, // Package No { accessorKey: "packageNo", header: ({ column }) => ( ), cell: ({ row }) => { const packageNo = row.original.packageNo; const packageName = row.original.packageName; if (!packageNo) return "-"; return (
{packageNo} {packageName && ( {packageName} )}
); }, size: 150, meta: { excelHeader: "Package No" }, }, // Project { accessorKey: "projectCode", header: ({ column }) => ( ), cell: ({ row }) => { const projectCode = row.original.projectCode; const projectName = row.original.projectName; if (!projectCode) return "-"; return (
{projectCode} {projectName && ( {projectName} )}
); }, size: 150, meta: { excelHeader: "Project" }, }, // Vendor Code { accessorKey: "vendorCode", header: ({ column }) => ( ), cell: ({ row }) => row.original.vendorCode || "-", size: 100, meta: { excelHeader: "Vendor Code" }, }, // Vendor Name { accessorKey: "vendorName", header: ({ column }) => ( ), cell: ({ row }) => row.original.vendorName, size: 200, meta: { excelHeader: "Vendor Name" }, }, // 구매담당자 (PIC Name) { accessorKey: "picName", header: ({ column }) => ( ), cell: ({ row }) => row.original.picName || "-", size: 120, meta: { excelHeader: "구매담당자" }, }, // 설계담당자 (Engineering PIC Name) { accessorKey: "EngPicName", header: ({ column }) => ( ), cell: ({ row }) => row.original.EngPicName || "-", size: 120, meta: { excelHeader: "설계담당자" }, }, // TBE Status { accessorKey: "sessionStatus", header: ({ column }) => ( ), meta: { excelHeader: "Status" }, cell: ({ row }) => { const status = row.original.sessionStatus; let variant: "default" | "secondary" | "outline" | "destructive" = "outline"; switch (status) { case "준비중": variant = "outline"; break; case "진행중": variant = "default"; break; case "검토중": variant = "secondary"; break; case "완료": variant = "default"; break; case "보류": variant = "destructive"; break; } return {status}; }, size: 100, }, // Evaluation Result { accessorKey: "evaluationResult", header: ({ column }) => ( ), meta: { excelHeader: "Result" }, cell: ({ row }) => { const result = row.original.evaluationResult; const session = row.original; if (!result) { return ( ); } let variant: "default" | "secondary" | "destructive" = "default"; let displayText = result; switch (result) { case "pass": variant = "default"; displayText = "Pass"; break; case "conditional_pass": variant = "secondary"; displayText = "Conditional"; break; case "non_pass": variant = "destructive"; displayText = "Non-Pass"; break; } return ( ); }, size: 120, }, // PR Items { id: "prItems", header: ({ column }) => ( ), meta: { excelHeader: "PR Items" }, cell: ({ row }) => { const sessionId = row.original.tbeSessionId; const rfqId = row.original.rfqId; const override = getPrCountsOverride?.(sessionId) const totalCount = override?.total ?? row.original.prItemsCount; const majorCount = override?.major ?? row.original.majorItemsCount; // counts rendered from override when available return ( ); }, size: 100, enableSorting: false, }, // Documents { id: "documents", header: ({ column }) => ( ), meta: { excelHeader: "Documents" }, cell: ({ row }) => { const sessionId = row.original.tbeSessionId; const buyerDocs = Number(row.original.buyerDocumentsCount); const vendorDocs = Number(row.original.vendorDocumentsCount); const reviewedDocs = Number(row.original.reviewedDocumentsCount); const totalDocs = buyerDocs + vendorDocs; return ( ); }, size: 100, enableSorting: false, }, ]; return columns; }