"use client"; import * as React from "react"; import { type ColumnDef } from "@tanstack/react-table"; import { Checkbox } from "@/components/ui/checkbox"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Eye, FileText, Send, Lock, LockOpen,Clock, AlertTriangle, CheckCircle, XCircle, AlertCircle } from "lucide-react"; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, } from "@/components/ui/tooltip"; import { DataTableColumnHeaderSimple } from "@/components/data-table/data-table-column-simple-header"; import { RfqsLastView } from "@/db/schema"; import { DataTableRowAction } from "@/types/table"; import { format, differenceInDays } from "date-fns"; import { ko } from "date-fns/locale"; import { useRouter } from "next/navigation"; import { RfqSealToggleCell } from "./rfq-seal-toggle-cell"; type NextRouter = ReturnType; interface GetColumnsProps { setRowAction: React.Dispatch | null>>; rfqCategory?: "general" | "itb" | "rfq"; router: NextRouter; } // RFQ 상태별 색상 const getStatusBadgeVariant = (status: string) => { switch (status) { case "RFQ 생성": return "outline"; case "구매담당지정": return "secondary"; case "견적요청문서 확정": return "default"; case "TBE 요청": return "default"; case "TBE 완료": return "default"; case "RFQ 발송": return "default"; case "견적접수": return "default"; case "최종업체선정": return "default"; case "RFQ 삭제": return "destructive"; default: return "outline"; } }; export function getRfqColumns({ setRowAction, rfqCategory = "itb", router }: GetColumnsProps): ColumnDef[] { // ═══════════════════════════════════════════════════════════════ // ITB 컬럼 정의 // ═══════════════════════════════════════════════════════════════ if (rfqCategory === "itb") { return [ // Checkbox { id: "select", header: ({ table }) => ( table.toggleAllPageRowsSelected(!!v)} aria-label="select all" className="translate-y-0.5" /> ), cell: ({ row }) => ( row.toggleSelected(!!v)} aria-label="select row" className="translate-y-0.5" /> ), size: 40, enableSorting: false, enableHiding: false, }, // ITB No. { accessorKey: "rfqCode", header: ({ column }) => , cell: ({ row }) => ( {row.original.rfqCode} ), size: 120, }, // 상세 (액션 버튼) - 수정됨 { id: "detail", header: "상세", cell: ({ row }) => ( ), size: 60, }, // 견적상태 { accessorKey: "status", header: ({ column }) => , cell: ({ row }) => ( {row.original.status} ), size: 120, }, // 견적 밀봉 { accessorKey: "rfqSealedYn", header: ({ column }) => , cell: ({ row, table }) => ( { // 테이블 데이터를 새로고침하는 로직 // 이 부분은 상위 컴포넌트에서 refreshData 함수를 prop으로 전달받아 사용 const meta = table.options.meta as any; meta?.refreshData?.(); }} /> ), size: 80, }, // 구매담당자 { accessorKey: "picUserName", header: ({ column }) => , cell: ({ row }) => { const name = row.original.picUserName || row.original.picName || "-"; const picCode = row.original.picCode || ""; return name === "-" ? "-" : `${name}(${picCode})`; }, size: 100, }, // 프로젝트 (프로젝트명) { accessorKey: "projectName", header: ({ column }) => , cell: ({ row }) => (
{row.original.projectCode} {row.original.projectName || "-"}
), size: 220, }, // 선급 { accessorKey: "classNo", header: ({ column }) => , cell: ({ row }) => row.original.classNo || "-", size: 80, }, // PKG No. (PKG명) { accessorKey: "packageName", header: ({ column }) => , cell: ({ row }) => (
{row.original.packageNo} {row.original.packageName || "-"}
), size: 180, }, // 자재그룹 (자재그룹명) { accessorKey: "majorItemMaterialDescription", header: ({ column }) => , cell: ({ row }) => (
{row.original.majorItemMaterialCategory} {row.original.majorItemMaterialDescription || "-"}
), size: 180, }, // SM Code { accessorKey: "smCode", header: ({ column }) => , cell: ({ row }) => row.original.smCode || "-", size: 80, }, // 견적문서 - 수정됨 { id: "rfqDocument", header: ({ column }) => , cell: ({ row }) => ( ), size: 80, }, // 견적생성일 { accessorKey: "createdAt", header: ({ column }) => , cell: ({ row }) => { const date = row.original.createdAt; return date ? format(new Date(date), "yyyy-MM-dd") : "-"; }, size: 100, }, // 견적발송일 { accessorKey: "rfqSendDate", header: ({ column }) => , cell: ({ row }) => { const date = row.original.rfqSendDate; return date ? format(new Date(date), "yyyy-MM-dd") : "-"; }, size: 100, }, // 견적마감일 { accessorKey: "dueDate", header: ({ column }) => , cell: ({ row }) => { const date = row.original.dueDate; if (!date) return "-"; const now = new Date(); const dueDate = new Date(date); const daysLeft = differenceInDays(dueDate, now); // 상태별 스타일과 아이콘 설정 let statusIcon; let statusText; let statusClass; if (daysLeft < 0) { // 마감일 지남 const daysOverdue = Math.abs(daysLeft); statusIcon = ; statusText = `${daysOverdue}일 지남`; statusClass = "text-red-600"; } else if (daysLeft === 0) { // 오늘 마감 statusIcon = ; statusText = "오늘 마감"; statusClass = "text-orange-600"; } else if (daysLeft <= 3) { // 3일 이내 마감 임박 statusIcon = ; statusText = `${daysLeft}일 남음`; statusClass = "text-amber-600"; } else if (daysLeft <= 7) { // 일주일 이내 statusIcon = ; statusText = `${daysLeft}일 남음`; statusClass = "text-blue-600"; } else { // 여유 있음 statusIcon = ; statusText = `${daysLeft}일 남음`; statusClass = "text-green-600"; } return (
{format(dueDate, "yyyy-MM-dd")}
{statusIcon} {statusText}
); }, size: 120, // 크기를 약간 늘림 }, // 설계담당자 { accessorKey: "engPicName", header: ({ column }) => , cell: ({ row }) => row.original.engPicName || "-", size: 100, }, // 프로젝트 Company { accessorKey: "projectCompany", header: ({ column }) => , cell: ({ row }) => row.original.projectCompany || "-", size: 120, }, // TBE 결과접수 { accessorKey: "tbeResultReceived", header: ({ column }) => , cell: ({ row }) => { const received = row.original.quotationReceivedCount || 0; const total = row.original.vendorCount || 0; return `${received}/${total}`; }, size: 100, }, // 프로젝트 Flag { accessorKey: "projectFlag", header: ({ column }) => , cell: ({ row }) => row.original.projectFlag || "-", size: 100, }, // 프로젝트 Site { accessorKey: "projectSite", header: ({ column }) => , cell: ({ row }) => row.original.projectSite || "-", size: 100, }, // 최종수정일 { accessorKey: "updatedAt", header: ({ column }) => , cell: ({ row }) => { const date = row.original.updatedAt; return date ? format(new Date(date), "yyyy-MM-dd HH:mm") : "-"; }, size: 120, }, // 최종수정자 { accessorKey: "updatedByUserName", header: ({ column }) => , cell: ({ row }) => row.original.updatedByUserName || "-", size: 100, }, // 비고 { accessorKey: "remark", header: ({ column }) => , cell: ({ row }) => row.original.remark || "-", size: 150, }, ]; } // ═══════════════════════════════════════════════════════════════ // RFQ 컬럼 정의 // ═══════════════════════════════════════════════════════════════ if (rfqCategory === "rfq") { return [ // Checkbox { id: "select", header: ({ table }) => ( table.toggleAllPageRowsSelected(!!v)} aria-label="select all" className="translate-y-0.5" /> ), cell: ({ row }) => ( row.toggleSelected(!!v)} aria-label="select row" className="translate-y-0.5" /> ), size: 40, enableSorting: false, enableHiding: false, }, // RFQ No. { accessorKey: "rfqCode", header: ({ column }) => , cell: ({ row }) => ( {row.original.rfqCode} ), size: 120, }, // 상세 - 수정됨 { id: "detail", header: "상세", cell: ({ row }) => ( ), size: 60, }, // 견적상태 { accessorKey: "status", header: ({ column }) => , cell: ({ row }) => ( {row.original.status} ), size: 120, }, // 견적 밀봉 { accessorKey: "rfqSealedYn", header: ({ column }) => , cell: ({ row, table }) => ( { // 테이블 데이터를 새로고침하는 로직 // 이 부분은 상위 컴포넌트에서 refreshData 함수를 prop으로 전달받아 사용 const meta = table.options.meta as any; meta?.refreshData?.(); }} /> ), size: 80, }, // 구매담당자 { accessorKey: "picUserName", header: ({ column }) => , cell: ({ row }) => { const name = row.original.picUserName || row.original.picName || "-"; const picCode = row.original.picCode || ""; return name === "-" ? "-" : `${name}(${picCode})`; }, size: 100, }, // 프로젝트 (프로젝트명) { accessorKey: "projectName", header: ({ column }) => , cell: ({ row }) => (
{row.original.projectCode} {row.original.projectName || "-"}
), size: 220, }, // 시리즈 { accessorKey: "series", header: ({ column }) => , cell: ({ row }) => { const series = row.original.series; if (!series) return "-"; // const label = series === "SS" ? "시리즈 통합" : series === "II" ? "품목 통합" : series; return {series}; }, size: 100, }, // 선급 { accessorKey: "classNo", header: ({ column }) => , cell: ({ row }) => row.original.classNo || "-", size: 80, }, // PKG No. (PKG명) { accessorKey: "packageName", header: ({ column }) => , cell: ({ row }) => (
{row.original.packageNo} {row.original.packageName || "-"}
), size: 180, }, // 자재그룹 (자재그룹명) { accessorKey: "majorItemMaterialDescription", header: ({ column }) => , cell: ({ row }) => (
{row.original.majorItemMaterialCategory} {row.original.majorItemMaterialDescription || "-"}
), size: 180, }, // 자재코드 { accessorKey: "itemCode", header: ({ column }) => , cell: ({ row }) => ( {row.original.itemCode || "-"} ), size: 100, }, // 견적문서 - 수정됨 { id: "rfqDocument", header: ({ column }) => , cell: ({ row }) => ( ), size: 80, }, // PR건수 - 수정됨 { accessorKey: "prItemsCount", header: ({ column }) => , cell: ({ row }) => ( ), size: 80, }, // 견적생성일 { accessorKey: "createdAt", header: ({ column }) => , cell: ({ row }) => { const date = row.original.createdAt; return date ? format(new Date(date), "yyyy-MM-dd") : "-"; }, size: 100, }, // 견적발송일 { accessorKey: "rfqSendDate", header: ({ column }) => , cell: ({ row }) => { const date = row.original.rfqSendDate; return date ? format(new Date(date), "yyyy-MM-dd") : "-"; }, size: 100, }, // 견적마감일 { accessorKey: "dueDate", header: ({ column }) => , cell: ({ row }) => { const date = row.original.dueDate; if (!date) return "-"; const now = new Date(); const dueDate = new Date(date); const daysLeft = differenceInDays(dueDate, now); // 상태별 스타일과 아이콘 설정 let statusIcon; let statusText; let statusClass; if (daysLeft < 0) { // 마감일 지남 const daysOverdue = Math.abs(daysLeft); statusIcon = ; statusText = `${daysOverdue}일 지남`; statusClass = "text-red-600"; } else if (daysLeft === 0) { // 오늘 마감 statusIcon = ; statusText = "오늘 마감"; statusClass = "text-orange-600"; } else if (daysLeft <= 3) { // 3일 이내 마감 임박 statusIcon = ; statusText = `${daysLeft}일 남음`; statusClass = "text-amber-600"; } else if (daysLeft <= 7) { // 일주일 이내 statusIcon = ; statusText = `${daysLeft}일 남음`; statusClass = "text-blue-600"; } else { // 여유 있음 statusIcon = ; statusText = `${daysLeft}일 남음`; statusClass = "text-green-600"; } return (
{format(dueDate, "yyyy-MM-dd")}
{statusIcon} {statusText}
); }, size: 120, // 크기를 약간 늘림 }, // 설계담당자 { accessorKey: "engPicName", header: ({ column }) => , cell: ({ row }) => row.original.engPicName || "-", size: 100, }, // TBE 결과접수 { accessorKey: "tbeResultReceived", header: ({ column }) => , cell: ({ row }) => { const received = row.original.quotationReceivedCount || 0; const total = row.original.vendorCount || 0; return `${received}/${total}`; }, size: 100, }, // 대표 PR No. { accessorKey: "prNumber", header: ({ column }) => , cell: ({ row }) => ( {row.original.prNumber || "-"} ), size: 120, }, // PR발행일 { accessorKey: "prIssueDate", header: ({ column }) => , cell: ({ row }) => { const date = row.original.prIssueDate; return date ? format(new Date(date), "yyyy-MM-dd") : "-"; }, size: 100, }, // 최종수정일 { accessorKey: "updatedAt", header: ({ column }) => , cell: ({ row }) => { const date = row.original.updatedAt; return date ? format(new Date(date), "yyyy-MM-dd HH:mm") : "-"; }, size: 120, }, // 최종수정자 { accessorKey: "updatedByUserName", header: ({ column }) => , cell: ({ row }) => row.original.updatedByUserName || "-", size: 100, }, // 비고 { accessorKey: "remark", header: ({ column }) => , cell: ({ row }) => row.original.remark || "-", size: 150, }, ]; } // ═══════════════════════════════════════════════════════════════ // 일반견적 컬럼 정의 // ═══════════════════════════════════════════════════════════════ if (rfqCategory === "general") { return [ // Checkbox { id: "select", header: ({ table }) => ( table.toggleAllPageRowsSelected(!!v)} aria-label="select all" className="translate-y-0.5" /> ), cell: ({ row }) => ( row.toggleSelected(!!v)} aria-label="select row" className="translate-y-0.5" /> ), size: 40, enableSorting: false, enableHiding: false, }, // 견적 No. { accessorKey: "rfqCode", header: ({ column }) => , cell: ({ row }) => ( {row.original.rfqCode} ), size: 120, }, // 상세 - 수정됨 { id: "detail", header: "상세", cell: ({ row }) => ( ), size: 60, }, // 견적상태 { accessorKey: "status", header: ({ column }) => , cell: ({ row }) => ( {row.original.status} ), size: 120, }, // 견적 밀봉 { accessorKey: "rfqSealedYn", header: ({ column }) => , cell: ({ row, table }) => ( { // 테이블 데이터를 새로고침하는 로직 // 이 부분은 상위 컴포넌트에서 refreshData 함수를 prop으로 전달받아 사용 const meta = table.options.meta as any; meta?.refreshData?.(); }} /> ), size: 80, }, // 견적종류 { accessorKey: "rfqType", header: ({ column }) => , cell: ({ row }) => row.original.rfqType || "-", size: 100, }, // 프로젝트 (프로젝트명) { accessorKey: "projectName", header: ({ column }) => , cell: ({ row }) => (
{row.original.projectCode} {row.original.projectName || "-"}
), size: 220, }, // 시리즈 { accessorKey: "series", header: ({ column }) => , cell: ({ row }) => { const series = row.original.series; if (!series) return "-"; const label = series === "SS" ? "시리즈 통합" : series === "II" ? "품목 통합" : series; return {label}; }, size: 100, }, // 선급 { accessorKey: "classNo", header: ({ column }) => , cell: ({ row }) => row.original.classNo || "-", size: 80, }, // 견적명 { accessorKey: "rfqTitle", header: ({ column }) => , cell: ({ row }) => (
{row.original.rfqTitle || "-"}
), size: 200, }, // 자재그룹 (자재그룹명) { accessorKey: "majorItemMaterialDescription", header: ({ column }) => , cell: ({ row }) => (
{row.original.majorItemMaterialCategory} {row.original.majorItemMaterialDescription || "-"}
), size: 180, }, // 자재코드 { accessorKey: "itemCode", header: ({ column }) => , cell: ({ row }) => ( {row.original.itemCode || "-"} ), size: 100, }, // 견적 자료 - 수정됨 { id: "rfqDocument", header: ({ column }) => , cell: ({ row }) => ( ), size: 80, }, // 견적품목수 - 수정됨 { accessorKey: "prItemsCount", header: ({ column }) => , cell: ({ row }) => ( ), size: 90, }, // 견적생성일 { accessorKey: "createdAt", header: ({ column }) => , cell: ({ row }) => { const date = row.original.createdAt; return date ? format(new Date(date), "yyyy-MM-dd") : "-"; }, size: 100, }, // 견적발송일 { accessorKey: "rfqSendDate", header: ({ column }) => , cell: ({ row }) => { const date = row.original.rfqSendDate; return date ? format(new Date(date), "yyyy-MM-dd") : "-"; }, size: 100, }, // 견적마감일 { accessorKey: "dueDate", header: ({ column }) => , cell: ({ row }) => { const date = row.original.dueDate; if (!date) return "-"; const now = new Date(); const dueDate = new Date(date); const daysLeft = differenceInDays(dueDate, now); // 상태별 스타일과 아이콘 설정 let statusIcon; let statusText; let statusClass; if (daysLeft < 0) { // 마감일 지남 const daysOverdue = Math.abs(daysLeft); statusIcon = ; statusText = `${daysOverdue}일 지남`; statusClass = "text-red-600"; } else if (daysLeft === 0) { // 오늘 마감 statusIcon = ; statusText = "오늘 마감"; statusClass = "text-orange-600"; } else if (daysLeft <= 3) { // 3일 이내 마감 임박 statusIcon = ; statusText = `${daysLeft}일 남음`; statusClass = "text-amber-600"; } else if (daysLeft <= 7) { // 일주일 이내 statusIcon = ; statusText = `${daysLeft}일 남음`; statusClass = "text-blue-600"; } else { // 여유 있음 statusIcon = ; statusText = `${daysLeft}일 남음`; statusClass = "text-green-600"; } return (
{format(dueDate, "yyyy-MM-dd")}
{statusIcon} {statusText}
); }, size: 120, // 크기를 약간 늘림 }, // 구매담당자 { accessorKey: "picUserName", header: ({ column }) => , cell: ({ row }) => { const name = row.original.picUserName || row.original.picName || "-"; const picCode = row.original.picCode || ""; return name === "-" ? "-" : `${name}(${picCode})`; }, size: 100, }, // 최종수정일 { accessorKey: "updatedAt", header: ({ column }) => , cell: ({ row }) => { const date = row.original.updatedAt; return date ? format(new Date(date), "yyyy-MM-dd HH:mm") : "-"; }, size: 120, }, // 최종수정자 { accessorKey: "updatedByUserName", header: ({ column }) => , cell: ({ row }) => row.original.updatedByUserName || "-", size: 100, }, // 비고 { accessorKey: "remark", header: ({ column }) => , cell: ({ row }) => row.original.remark || "-", size: 150, }, ]; } // 기본값 (fallback) return []; }