From fccb00d15466cd0b2d861163663a5070c768ff77 Mon Sep 17 00:00:00 2001 From: dujinkim Date: Tue, 2 Sep 2025 09:52:21 +0000 Subject: (대표님) OCR 박진석프로 요청 대응, rfq 변경된 요구사항 구현 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/rfq-last/table/rfq-table-columns.tsx | 445 +++++++++++++++++++++++++++++++ 1 file changed, 445 insertions(+) create mode 100644 lib/rfq-last/table/rfq-table-columns.tsx (limited to 'lib/rfq-last/table/rfq-table-columns.tsx') diff --git a/lib/rfq-last/table/rfq-table-columns.tsx b/lib/rfq-last/table/rfq-table-columns.tsx new file mode 100644 index 00000000..3fac8881 --- /dev/null +++ b/lib/rfq-last/table/rfq-table-columns.tsx @@ -0,0 +1,445 @@ +"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, Package, Users, ChevronRight } 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 } from "date-fns"; +import { ko } from "date-fns/locale"; + +interface GetColumnsProps { + setRowAction: React.Dispatch | null>>; + rfqCategory?: "all" | "general" | "itb" | "rfq"; +} + +// RFQ 상태별 색상 +const getStatusBadgeVariant = (status: string) => { + switch (status) { + case "RFQ 생성": return "outline"; + case "구매담당지정": return "secondary"; + case "견적요청문서 확정": return "default"; + case "Short List 확정": return "default"; + case "TBE 완료": return "default"; + case "RFQ 발송": return "default"; + case "견적접수": return "default"; + case "최종업체선정": return "default"; + default: return "outline"; + } +}; + +// 시리즈 배지 +const getSeriesBadge = (series: string | null) => { + if (!series) return null; + + const label = series === "SS" ? "시리즈 통합" : series === "II" ? "품목 통합" : series; + const variant = series === "SS" ? "default" : series === "II" ? "secondary" : "outline"; + + return {label}; +}; + +export function getRfqColumns({ + setRowAction, + rfqCategory = "all" +}: GetColumnsProps): ColumnDef[] { + + const baseColumns: ColumnDef[] = [ + // ═══════════════════════════════════════════════════════════════ + // 선택 및 기본 정보 + // ═══════════════════════════════════════════════════════════════ + + // 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 코드 + { + accessorKey: "rfqCode", + header: ({ column }) => , + cell: ({ row }) => { + const rfqSealed = row.original.rfqSealedYn; + return ( +
+ {row.original.rfqCode} + {rfqSealed && ( + 봉인 + )} +
+ ); + }, + size: 140, + }, + + // 상태 + { + accessorKey: "status", + header: ({ column }) => , + cell: ({ row }) => ( + + {row.original.status} + + ), + size: 120, + }, + + // ═══════════════════════════════════════════════════════════════ + // 일반견적 필드 (rfqCategory가 'general' 또는 'all'일 때만) + // ═══════════════════════════════════════════════════════════════ + ...(rfqCategory === "general" || rfqCategory === "all" ? [ + { + id: "rfqType", + accessorKey: "rfqType", + header: ({ column }) => , + cell: ({ row }) => row.original.rfqType || "-", + size: 100, + }, + { + id: "rfqTitle", + accessorKey: "rfqTitle", + header: ({ column }) => , + cell: ({ row }) => ( +
+ {row.original.rfqTitle || "-"} +
+ ), + size: 200, + }, + ] as ColumnDef[] : []), + + // ═══════════════════════════════════════════════════════════════ + // ITB 필드 (rfqCategory가 'itb' 또는 'all'일 때만) + // ═══════════════════════════════════════════════════════════════ + ...(rfqCategory === "itb" || rfqCategory === "all" ? [ + { + id: "projectCompany", + accessorKey: "projectCompany", + header: ({ column }) => , + cell: ({ row }) => row.original.projectCompany || "-", + size: 120, + }, + { + id: "projectFlag", + accessorKey: "projectFlag", + header: ({ column }) => , + cell: ({ row }) => row.original.projectFlag || "-", + size: 100, + }, + { + id: "projectSite", + accessorKey: "projectSite", + header: ({ column }) => , + cell: ({ row }) => row.original.projectSite || "-", + size: 120, + }, + { + id: "smCode", + accessorKey: "smCode", + header: ({ column }) => , + cell: ({ row }) => row.original.smCode || "-", + size: 80, + }, + ] as ColumnDef[] : []), + + // ═══════════════════════════════════════════════════════════════ + // RFQ(PR) 필드 (rfqCategory가 'rfq' 또는 'all'일 때만) + // ═══════════════════════════════════════════════════════════════ + ...(rfqCategory === "rfq" || rfqCategory === "all" ? [ + { + id: "prNumber", + accessorKey: "prNumber", + header: ({ column }) => , + cell: ({ row }) => ( + {row.original.prNumber || "-"} + ), + size: 120, + }, + { + id: "prIssueDate", + accessorKey: "prIssueDate", + header: ({ column }) => , + cell: ({ row }) => { + const date = row.original.prIssueDate; + return date ? format(new Date(date), "yyyy-MM-dd") : "-"; + }, + size: 100, + }, + { + id: "series", + accessorKey: "series", + header: ({ column }) => , + cell: ({ row }) => getSeriesBadge(row.original.series), + size: 100, + }, + ] as ColumnDef[] : []), + + // ═══════════════════════════════════════════════════════════════ + // 공통 프로젝트 정보 + // ═══════════════════════════════════════════════════════════════ + { + header: "프로젝트 정보", + columns: [ + { + accessorKey: "projectCode", + header: ({ column }) => , + cell: ({ row }) => ( + {row.original.projectCode || "-"} + ), + size: 120, + }, + { + accessorKey: "projectName", + header: ({ column }) => , + cell: ({ row }) => ( +
+ {row.original.projectName || "-"} +
+ ), + size: 200, + }, + ] + }, + + // ═══════════════════════════════════════════════════════════════ + // 품목 정보 + // ═══════════════════════════════════════════════════════════════ + { + header: "품목 정보", + columns: [ + { + accessorKey: "itemCode", + header: ({ column }) => , + cell: ({ row }) => ( + {row.original.itemCode || "-"} + ), + size: 100, + }, + { + accessorKey: "itemName", + header: ({ column }) => , + cell: ({ row }) => ( +
+ {row.original.itemName || "-"} +
+ ), + size: 200, + }, + { + accessorKey: "packageNo", + header: ({ column }) => , + cell: ({ row }) => row.original.packageNo || "-", + size: 100, + }, + { + accessorKey: "packageName", + header: ({ column }) => , + cell: ({ row }) => ( +
+ {row.original.packageName || "-"} +
+ ), + size: 200, + }, + ] + }, + + // ═══════════════════════════════════════════════════════════════ + // 담당자 정보 + // ═══════════════════════════════════════════════════════════════ + { + header: "담당자", + columns: [ + { + accessorKey: "picUserName", + header: ({ column }) => , + cell: ({ row }) => row.original.picUserName || row.original.picName || "-", + size: 100, + }, + { + accessorKey: "engPicName", + header: ({ column }) => , + cell: ({ row }) => row.original.engPicName || "-", + size: 120, + }, + ] + }, + + // ═══════════════════════════════════════════════════════════════ + // 일정 정보 + // ═══════════════════════════════════════════════════════════════ + { + header: "일정", + columns: [ + { + accessorKey: "rfqSendDate", + header: ({ column }) => , + cell: ({ row }) => { + const date = row.original.rfqSendDate; + return date ? ( +
+ + + {format(new Date(date), "MM-dd", { locale: ko })} + +
+ ) : "-"; + }, + size: 90, + }, + { + accessorKey: "dueDate", + header: ({ column }) => , + cell: ({ row }) => { + const date = row.original.dueDate; + if (!date) return "-"; + + const now = new Date(); + const dueDate = new Date(date); + const isOverdue = now > dueDate; + + return ( + + {format(dueDate, "MM-dd", { locale: ko })} + + ); + }, + size: 90, + }, + ] + }, + + // ═══════════════════════════════════════════════════════════════ + // 벤더 및 견적 현황 + // ═══════════════════════════════════════════════════════════════ + { + header: "견적 현황", + columns: [ + { + accessorKey: "vendorCount", + header: ({ column }) => , + cell: ({ row }) => ( +
+ + {row.original.vendorCount || 0} +
+ ), + size: 80, + }, + { + accessorKey: "shortListedVendorCount", + header: ({ column }) => , + cell: ({ row }) => { + const count = row.original.shortListedVendorCount || 0; + return count > 0 ? ( + + {count} + + ) : "-"; + }, + size: 90, + }, + { + accessorKey: "quotationReceivedCount", + header: ({ column }) => , + cell: ({ row }) => { + const received = row.original.quotationReceivedCount || 0; + const total = row.original.vendorCount || 0; + + return ( +
+ + 0 ? "text-green-600 font-medium" : ""}`}> + {received}/{total} + +
+ ); + }, + size: 90, + }, + ] + }, + + // PR Items 정보 + { + accessorKey: "prItemsCount", + header: ({ column }) => , + cell: ({ row }) => { + const prItems = row.original.prItemsCount || 0; + const majorItems = row.original.majorItemsCount || 0; + + return ( +
+ {prItems}개 + {majorItems > 0 && ( + + 주요 {majorItems} + + )} +
+ ); + }, + size: 90, + }, + + // 액션 + { + id: "actions", + header: "액션", + enableHiding: false, + size: 80, + minSize: 80, + cell: ({ row }) => { + return ( +
+ + + + + + 상세보기 + + +
+ ); + }, + }, + ]; + + return baseColumns; +} \ No newline at end of file -- cgit v1.2.3