// ================================================================ // 1. PERIODIC EVALUATIONS COLUMNS // ================================================================ "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 { Pencil, Eye, MessageSquare, Check, X, Clock, FileText, Circle } from "lucide-react"; import { DataTableColumnHeaderSimple } from "@/components/data-table/data-table-column-simple-header"; import { PeriodicEvaluationView } from "@/db/schema"; import { DataTableRowAction } from "@/types/table"; import { vendortypeMap } from "@/types/evaluation"; interface GetColumnsProps { setRowAction: React.Dispatch | null>>; } // 상태별 색상 매핑 const getStatusBadgeVariant = (status: string) => { switch (status) { case "PENDING_SUBMISSION": return "outline"; case "SUBMITTED": return "secondary"; case "IN_REVIEW": return "default"; case "REVIEW_COMPLETED": return "default"; case "FINALIZED": return "default"; default: return "outline"; } }; const getStatusLabel = (status: string) => { const statusMap = { PENDING_SUBMISSION: "제출대기", SUBMITTED: "제출완료", IN_REVIEW: "검토중", REVIEW_COMPLETED: "검토완료", FINALIZED: "최종확정" }; return statusMap[status] || status; }; // 부서별 상태 배지 함수 const getDepartmentStatusBadge = (status: string | null) => { if (!status) return (
{/* */} -
); switch (status) { case "NOT_ASSIGNED": return (
{/* */} 미지정
); case "NOT_STARTED": return (
{/* 시작전 */}
); case "IN_PROGRESS": return (
{/* 진행중 */}
); case "COMPLETED": return (
{/* 완료 */}
); default: return (
{/* */} -
); } }; // 부서명 라벨 const DEPARTMENT_LABELS = { ORDER_EVAL: "발주", PROCUREMENT_EVAL: "조달", QUALITY_EVAL: "품질", DESIGN_EVAL: "설계", CS_EVAL: "CS" } as const; // 등급별 색상 const getGradeBadgeVariant = (grade: string | null) => { if (!grade) return "outline"; switch (grade) { case "S": return "default"; case "A": return "secondary"; case "B": return "outline"; case "C": return "destructive"; case "D": return "destructive"; default: return "outline"; } }; // 구분 배지 const getDivisionBadge = (division: string) => { return ( {division === "PLANT" ? "해양" : "조선"} ); }; // 자재구분 배지 const getMaterialTypeBadge = (materialType: string) => { return {vendortypeMap[materialType] || materialType}; }; // 내외자 배지 const getDomesticForeignBadge = (domesticForeign: string) => { return ( {domesticForeign === "DOMESTIC" ? "D" : "F"} ); }; // 진행률 배지 const getProgressBadge = (completed: number, total: number) => { if (total === 0) return -; const percentage = Math.round((completed / total) * 100); const variant = percentage === 100 ? "default" : percentage >= 50 ? "secondary" : "destructive"; return {completed}/{total} ({percentage}%); }; export function getPeriodicEvaluationsColumns({setRowAction}: GetColumnsProps): ColumnDef[] { 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, }, // ░░░ 평가년도 ░░░ { accessorKey: "evaluationYear", header: ({ column }) => , cell: ({ row }) => {row.original.evaluationYear}, size: 100, }, // ░░░ 평가기간 ░░░ // { // accessorKey: "evaluationPeriod", // header: ({ column }) => , // cell: ({ row }) => ( // {row.getValue("evaluationPeriod")} // ), // size: 100, // }, // ░░░ 구분 ░░░ { accessorKey: "division", header: ({ column }) => , cell: ({ row }) => getDivisionBadge(row.original.division || ""), size: 80, }, // ═══════════════════════════════════════════════════════════════ // 협력업체 정보 // ═══════════════════════════════════════════════════════════════ { header: "협력업체 정보", columns: [ { accessorKey: "vendorCode", header: ({ column }) => , cell: ({ row }) => ( {row.original.vendorCode} ), size: 120, }, { accessorKey: "vendorName", header: ({ column }) => , cell: ({ row }) => (
{row.original.vendorName}
), size: 200, }, { accessorKey: "domesticForeign", header: ({ column }) => , cell: ({ row }) => getDomesticForeignBadge(row.original.domesticForeign || ""), size: 80, }, { accessorKey: "materialType", header: ({ column }) => , cell: ({ row }) => getMaterialTypeBadge(row.original.materialType || ""), size: 120, }, ] }, { accessorKey: "finalScore", header: ({ column }) => , cell: ({ row }) => { const finalScore = row.getValue("finalScore"); return finalScore ? ( {finalScore.toFixed(1)} ) : ( - ); }, size: 90, }, { accessorKey: "finalGrade", header: ({ column }) => , cell: ({ row }) => { const finalGrade = row.getValue("finalGrade"); return finalGrade ? ( {finalGrade} ) : ( - ); }, size: 90, }, // ═══════════════════════════════════════════════════════════════ // 진행 현황 // ═══════════════════════════════════════════════════════════════ { header: "부서별 평가 현황", columns: [ { accessorKey: "orderEvalStatus", header: ({ column }) => , cell: ({ row }) => getDepartmentStatusBadge(row.getValue("orderEvalStatus")), size: 60, }, { accessorKey: "procurementEvalStatus", header: ({ column }) => , cell: ({ row }) => getDepartmentStatusBadge(row.getValue("procurementEvalStatus")), size: 70, }, { accessorKey: "qualityEvalStatus", header: ({ column }) => , cell: ({ row }) => getDepartmentStatusBadge(row.getValue("qualityEvalStatus")), size: 70, }, { accessorKey: "designEvalStatus", header: ({ column }) => , cell: ({ row }) => getDepartmentStatusBadge(row.getValue("designEvalStatus")), size: 70, }, { accessorKey: "csEvalStatus", header: ({ column }) => , cell: ({ row }) => getDepartmentStatusBadge(row.getValue("csEvalStatus")), size: 70, }, ] }, // ═══════════════════════════════════════════════════════════════ // 제출 현황 // ═══════════════════════════════════════════════════════════════ { header: "협력업체 제출 현황", columns: [ { accessorKey: "documentsSubmitted", header: ({ column }) => , cell: ({ row }) => { const submitted = row.getValue("documentsSubmitted"); return ( {submitted ? "제출완료" : "미제출"} ); }, size: 120, }, { accessorKey: "submissionDate", header: ({ column }) => , cell: ({ row }) => { const submissionDate = row.getValue("submissionDate"); return submissionDate ? ( {new Intl.DateTimeFormat("ko-KR", { month: "2-digit", day: "2-digit", }).format(new Date(submissionDate))} ) : ( - ); }, size: 80, }, { accessorKey: "submissionDeadline", header: ({ column }) => , cell: ({ row }) => { const deadline = row.getValue("submissionDeadline"); if (!deadline) return -; const now = new Date(); const isOverdue = now > deadline; return ( {new Intl.DateTimeFormat("ko-KR", { month: "2-digit", day: "2-digit", }).format(new Date(deadline))} ); }, size: 80, }, ] }, // ═══════════════════════════════════════════════════════════════ // 평가 점수 // ═══════════════════════════════════════════════════════════════ { header: "평가 점수", columns: [ { accessorKey: "processScore", header: ({ column }) => , cell: ({ row }) => { const score = row.getValue("processScore"); return score ? ( {Number(score).toFixed(1)} ) : ( - ); }, size: 80, }, { accessorKey: "priceScore", header: ({ column }) => , cell: ({ row }) => { const score = row.getValue("priceScore"); return score ? ( {Number(score).toFixed(1)} ) : ( - ); }, size: 80, }, { accessorKey: "deliveryScore", header: ({ column }) => , cell: ({ row }) => { const score = row.getValue("deliveryScore"); return score ? ( {Number(score).toFixed(1)} ) : ( - ); }, size: 80, }, { accessorKey: "selfEvaluationScore", header: ({ column }) => , cell: ({ row }) => { const score = row.getValue("selfEvaluationScore"); return score ? ( {Number(score).toFixed(1)} ) : ( - ); }, size: 80, }, // ✅ 합계 - 4개 점수의 합으로 계산 { id: "totalScore", header: ({ column }) => , cell: ({ row }) => { const processScore = Number(row.getValue("processScore") || 0); const priceScore = Number(row.getValue("priceScore") || 0); const deliveryScore = Number(row.getValue("deliveryScore") || 0); const selfEvaluationScore = Number(row.getValue("selfEvaluationScore") || 0); const total = processScore + priceScore + deliveryScore + selfEvaluationScore; return total > 0 ? ( {total.toFixed(1)} ) : ( - ); }, size: 80, }, { accessorKey: "participationBonus", header: ({ column }) => , cell: ({ row }) => { const score = row.getValue("participationBonus"); return score ? ( +{Number(score).toFixed(1)} ) : ( - ); }, size: 100, }, { accessorKey: "qualityDeduction", header: ({ column }) => , cell: ({ row }) => { const score = row.getValue("qualityDeduction"); return score ? ( -{Number(score).toFixed(1)} ) : ( - ); }, size: 100, }, // ✅ 새로운 평가점수 컬럼 추가 { id: "evaluationScore", header: ({ column }) => , cell: ({ row }) => { const processScore = Number(row.getValue("processScore") || 0); const priceScore = Number(row.getValue("priceScore") || 0); const deliveryScore = Number(row.getValue("deliveryScore") || 0); const selfEvaluationScore = Number(row.getValue("selfEvaluationScore") || 0); const participationBonus = Number(row.getValue("participationBonus") || 0); const qualityDeduction = Number(row.getValue("qualityDeduction") || 0); const totalScore = processScore + priceScore + deliveryScore + selfEvaluationScore; const evaluationScore = totalScore + participationBonus - qualityDeduction; return totalScore > 0 ? ( {evaluationScore.toFixed(1)} ) : ( - ); }, size: 90, }, { accessorKey: "evaluationGrade", header: ({ column }) => , cell: ({ row }) => { const grade = row.getValue("evaluationGrade"); return grade ? ( {grade} ) : ( - ); }, minSize: 100, }, ] }, // ░░░ Actions ░░░ { id: "actions", enableHiding: false, size: 40, minSize: 40, cell: ({ row }) => { return (
); }, }, ]; }