// lib/vendor-responses/table/vendor-responses-table-columns.tsx "use client" import * as React from "react" import { type DataTableRowAction } from "@/types/table" import { type ColumnDef } from "@tanstack/react-table" import { Ellipsis, FileText, Pencil, Edit, Trash2, Eye, MessageSquare, Clock, CheckCircle, AlertTriangle, FileX } from "lucide-react" import { formatDate, formatDateTime } from "@/lib/utils" import { Badge } from "@/components/ui/badge" import { Button } from "@/components/ui/button" import { Checkbox } from "@/components/ui/checkbox" import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, } from "@/components/ui/tooltip" import { DataTableColumnHeaderSimple } from "@/components/data-table/data-table-column-simple-header" import Link from "next/link" import { useRouter } from "next/navigation" import { VendorResponseDetail } from "../service" import { VendorRfqResponseSummary } from "../validations" // 응답 상태에 따른 배지 컴포넌트 function ResponseStatusBadge({ status }: { status: string }) { switch (status) { case "NOT_RESPONDED": return ( 미응답 ) case "RESPONDED": return ( 응답완료 ) case "REVISION_REQUESTED": return ( 수정요청 ) case "WAIVED": return ( 포기 ) default: return {status} } } type NextRouter = ReturnType; interface GetColumnsProps { router: NextRouter } /** * tanstack table 컬럼 정의 */ export function getColumns({ router, }: GetColumnsProps): ColumnDef[] { // ---------------------------------------------------------------- // 1) select 컬럼 (체크박스) // ---------------------------------------------------------------- const selectColumn: ColumnDef = { 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, } // ---------------------------------------------------------------- // 2) actions 컬럼 (작성하기 버튼만) // ---------------------------------------------------------------- const actionsColumn: ColumnDef = { id: "actions", enableHiding: false, cell: ({ row }) => { const vendorId = row.original.vendorId const rfqRecordId = row.original.rfqRecordId const rfqType = row.original.rfqType const rfqCode = row.original.rfq?.rfqCode || "RFQ" return (

{rfqCode} 응답 작성하기

) }, size: 100, minSize: 100, maxSize: 150, } // ---------------------------------------------------------------- // 3) 컬럼 정의 배열 // ---------------------------------------------------------------- const columnDefinitions = [ { id: "rfqCode", label: "RFQ 번호", group: "RFQ 정보", size: 120, minSize: 100, maxSize: 150, }, { id: "rfqDueDate", label: "RFQ 마감일", group: "RFQ 정보", size: 120, minSize: 100, maxSize: 150, }, { id: "overallStatus", label: "전체 상태", group: null, size: 80, minSize: 60, maxSize: 100, }, { id: "totalAttachments", label: "총 첨부파일", group: "응답 통계", size: 100, minSize: 80, maxSize: 120, }, { id: "respondedCount", label: "응답완료", group: "응답 통계", size: 100, minSize: 80, maxSize: 120, }, { id: "pendingCount", label: "미응답", group: "응답 통계", size: 100, minSize: 80, maxSize: 120, }, { id: "responseRate", label: "응답률", group: "진행률", size: 100, minSize: 80, maxSize: 120, }, { id: "completionRate", label: "완료율", group: "진행률", size: 100, minSize: 80, maxSize: 120, }, { id: "requestedAt", label: "요청일", group: "날짜 정보", size: 120, minSize: 100, maxSize: 150, }, { id: "lastRespondedAt", label: "최종 응답일", group: "날짜 정보", size: 120, minSize: 100, maxSize: 150, }, ]; // ---------------------------------------------------------------- // 4) 그룹별로 컬럼 정리 (중첩 헤더 생성) // ---------------------------------------------------------------- const groupMap: Record[]> = {} columnDefinitions.forEach((cfg) => { const groupName = cfg.group || "_noGroup" if (!groupMap[groupName]) { groupMap[groupName] = [] } // 개별 컬럼 정의 const columnDef: ColumnDef = { accessorKey: cfg.id, enableResizing: true, header: ({ column }) => ( ), cell: ({ row, cell }) => { // 각 컬럼별 특별한 렌더링 처리 switch (cfg.id) { case "rfqCode": return row.original.rfq?.rfqCode || "-" case "rfqDueDate": const dueDate = row.original.rfq?.dueDate; return dueDate ? formatDate(new Date(dueDate)) : "-"; case "overallStatus": return case "totalAttachments": return (
{row.original.totalAttachments}
) case "respondedCount": return (
{row.original.respondedCount}
) case "pendingCount": return (
{row.original.pendingCount}
) case "responseRate": const responseRate = row.original.responseRate; return (
= 80 ? 'text-green-600' : responseRate >= 50 ? 'text-yellow-600' : 'text-red-600'}`}> {responseRate}%
) case "completionRate": const completionRate = row.original.completionRate; return (
= 80 ? 'text-green-600' : completionRate >= 50 ? 'text-yellow-600' : 'text-red-600'}`}> {completionRate}%
) case "requestedAt": return formatDateTime(new Date(row.original.requestedAt)) case "lastRespondedAt": const lastRespondedAt = row.original.lastRespondedAt; return lastRespondedAt ? formatDateTime(new Date(lastRespondedAt)) : "-"; default: return row.getValue(cfg.id) ?? "" } }, size: cfg.size, minSize: cfg.minSize, maxSize: cfg.maxSize, } groupMap[groupName].push(columnDef) }) // ---------------------------------------------------------------- // 5) 그룹별 중첩 컬럼 생성 // ---------------------------------------------------------------- const nestedColumns: ColumnDef[] = [] Object.entries(groupMap).forEach(([groupName, colDefs]) => { if (groupName === "_noGroup") { // 그룹이 없는 컬럼들은 직접 추가 nestedColumns.push(...colDefs) } else { // 그룹이 있는 컬럼들은 중첩 구조로 추가 nestedColumns.push({ id: groupName, header: groupName, columns: colDefs, }) } }) // ---------------------------------------------------------------- // 6) 최종 컬럼 배열 // ---------------------------------------------------------------- return [ selectColumn, ...nestedColumns, actionsColumn, ] }