"use client" import * as React from "react" import { type DataTableRowAction } from "@/types/table" import { type ColumnDef } from "@tanstack/react-table" import { Ellipsis, InfoIcon, PenToolIcon, FileTextIcon, ClipboardListIcon, DownloadIcon, CheckIcon, XIcon, ClockIcon, Send } from "lucide-react" import { formatDate, formatCurrency } from "@/lib/utils" import { Button } from "@/components/ui/button" import { Checkbox } from "@/components/ui/checkbox" import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, } from "@/components/ui/tooltip" import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu" import { Badge } from "@/components/ui/badge" import { DataTableColumnHeaderSimple } from "@/components/data-table/data-table-column-simple-header" import { EvaluationSubmissionWithVendor } from "../service" interface GetColumnsProps { setRowAction: React.Dispatch | null>> } /** * 제출 상태에 따른 배지 스타일 및 아이콘 */ const getStatusBadge = (status: string) => { switch (status) { case 'draft': return { variant: "secondary" as const, icon: , label: "임시저장" } case 'submitted': return { variant: "default" as const, icon: , label: "제출완료" } case 'under_review': return { variant: "outline" as const, icon: , label: "검토중" } case 'approved': return { variant: "default" as const, icon: , label: "승인", className: "bg-green-100 text-green-800 border-green-200" } case 'rejected': return { variant: "destructive" as const, icon: , label: "반려" } default: return { variant: "secondary" as const, icon: null, label: status } } } /** * 평가 제출 테이블 컬럼 정의 */ export function getColumns({ setRowAction }: 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" /> ), enableSorting: false, enableHiding: false, size: 40, } // ---------------------------------------------------------------- // 2) 기본 정보 컬럼들 // ---------------------------------------------------------------- const basicColumns: ColumnDef[] = [ // { // accessorKey: "submissionId", // header: ({ column }) => ( // // ), // cell: ({ row }) => ( //
// {row.getValue("submissionId")} //
// ), // enableSorting: true, // enableHiding: true, // size: 400, // minSize: 400, // }, // { // id: "vendorInfo", // header: ({ column }) => ( // // ), // cell: ({ row }) => { // const vendor = row.original.vendor; // return ( //
//
{vendor.vendorName}
//
// {vendor.vendorCode} • {vendor.countryCode} //
//
// ); // }, // enableSorting: false, // size: 200, // }, // { // accessorKey: "evaluationRound", // header: ({ column }) => ( // // ), // cell: ({ row }) => { // const round = row.getValue("evaluationRound") as string; // return round ? ( // {round} // ) : ( // - // ); // }, // size: 60, // }, ] // ---------------------------------------------------------------- // 3) 상태 정보 컬럼들 // ---------------------------------------------------------------- const statusColumns: ColumnDef[] = [ { accessorKey: "submissionStatus", header: ({ column }) => ( ), cell: ({ row }) => { const status = row.getValue("submissionStatus") as string; const badgeInfo = getStatusBadge(status); return ( {badgeInfo.icon} {badgeInfo.label} ); }, size: 120, }, { accessorKey: "submittedAt", header: ({ column }) => ( ), cell: ({ row }) => { const date = row.getValue("submittedAt") as Date; return date ? formatDate(date) : ( - ); }, size: 140, }, { id: "reviewInfo", header: ({ column }) => ( ), cell: ({ row }) => { const reviewedAt = row.original.reviewedAt; const reviewedBy = row.original.reviewedBy; if (!reviewedAt) { return 미검토; } return (
{formatDate(reviewedAt)}
{reviewedBy && (
{reviewedBy}
)}
); }, enableSorting: false, size: 140, }, ] // ---------------------------------------------------------------- // 4) 점수 및 통계 컬럼들 // ---------------------------------------------------------------- const scoreColumns: ColumnDef[] = [ { id: "generalProgress", header: ({ column }) => ( ), cell: ({ row }) => { const totalItems = row.original.totalGeneralItems || 0; const completedItems = row.original.completedGeneralItems || 0; const completionRate = totalItems > 0 ? (completedItems / totalItems) * 100 : 0; return (
{/* ❌ 점수 표시 제거 */}
{completionRate === 100 ? "완료" : "진행중"}
{completedItems}/{totalItems}개 {completionRate > 0 && ( ({completionRate.toFixed(0)}%) )}
{/* 📊 진행률 바 */}
= 50 ? 'bg-blue-500' : 'bg-yellow-500' }`} style={{ width: `${completionRate}%` }} />
); }, enableSorting: false, size: 120, }, { id: "esgScore", header: ({ column }) => ( ), cell: ({ row }) => { const averageScore = row.original.averageEsgScore; const totalItems = row.original.totalEsgItems || 0; const completedItems = row.original.completedEsgItems || 0; const completionRate = totalItems > 0 ? (completedItems / totalItems) * 100 : 0; const isKorean = row.original.vendor.countryCode === 'KR'; if (!isKorean) { return (
해당없음
); } return (
{/* ✅ ESG는 평균점수 표시 */}
{averageScore ? ( 평균 {parseFloat(averageScore.toString()).toFixed(1)}점 ) : ( 미완료 )}
{completedItems}/{totalItems}개 {completionRate > 0 && ( ({completionRate.toFixed(0)}%) )}
{/* 📊 진행률 바 */}
= 50 ? 'bg-blue-500' : 'bg-yellow-500' }`} style={{ width: `${completionRate}%` }} />
); }, enableSorting: false, size: 140, }, { id: "overallProgress", header: ({ column }) => ( ), cell: ({ row }) => { const totalGeneral = row.original.totalGeneralItems || 0; const completedGeneral = row.original.completedGeneralItems || 0; const totalEsg = row.original.totalEsgItems || 0; const completedEsg = row.original.completedEsgItems || 0; const isKorean = row.original.vendor.countryCode === 'KR'; const totalItems = totalGeneral + (isKorean ? totalEsg : 0); const completedItems = completedGeneral + (isKorean ? completedEsg : 0); const completionRate = totalItems > 0 ? (completedItems / totalItems) * 100 : 0; return (
= 50 ? 'bg-blue-500' : 'bg-yellow-500' }`} style={{ width: `${completionRate}%` }} />
{completionRate.toFixed(0)}% 완료
{completedItems}/{totalItems}개 항목
); }, enableSorting: false, size: 120, }, { id: "attachments", header: ({ column }) => ( ), cell: ({ row }) => { const count = row.original._count.attachments; return (
{count}개 파일
); }, enableSorting: false, size: 100, }, ] // ---------------------------------------------------------------- // 5) 메타데이터 컬럼들 // ---------------------------------------------------------------- const metaColumns: ColumnDef[] = [ { accessorKey: "createdAt", header: ({ column }) => ( ), cell: ({ row }) => { const date = row.getValue("createdAt") as Date; return formatDate(date); }, size: 140, }, { accessorKey: "updatedAt", header: ({ column }) => ( ), cell: ({ row }) => { const date = row.getValue("updatedAt") as Date; return formatDate(date); }, size: 140, }, ] // ---------------------------------------------------------------- // 6) actions 컬럼 (드롭다운 메뉴) // ---------------------------------------------------------------- const actionsColumn: ColumnDef = { id: "actions", header: "작업", enableHiding: false, cell: function Cell({ row }) { const status = row.original.submissionStatus; const isKorean = row.original.vendor.countryCode === 'KR'; return ( setRowAction({ row, type: "general_evaluation" })} > 일반평가 작성 {isKorean && ( setRowAction({ row, type: "esg_evaluation" })} > ESG평가 작성 )} setRowAction({ row, type: "submit" })} > 제출 ) }, size: 80, } // ---------------------------------------------------------------- // 7) 최종 컬럼 배열 (그룹화 버전) // ---------------------------------------------------------------- return [ selectColumn, { accessorKey: "evaluationYear", header: ({ column }) => ( ), cell: ({ row }) => ( {row.getValue("evaluationYear")}년 ), size: 60, }, { id: "statusInfo", header: "상태 정보", columns: statusColumns, }, { id: "scoreInfo", header: "점수 및 통계", columns: scoreColumns, }, { id: "metadata", header: "메타데이터", columns: metaColumns, }, actionsColumn, ] } // ---------------------------------------------------------------- // 8) 컬럼 설정 (필터링용) // ---------------------------------------------------------------- export const evaluationSubmissionsColumnsConfig = [ { id: "submissionId", label: "제출 ID", group: "기본 정보", type: "text", excelHeader: "Submission ID", }, { id: "vendorName", label: "협력업체명", group: "기본 정보", type: "text", excelHeader: "Vendor Name", }, { id: "vendorCode", label: "협력업체 코드", group: "기본 정보", type: "text", excelHeader: "Vendor Code", }, { id: "evaluationYear", label: "평가연도", group: "기본 정보", type: "number", excelHeader: "Evaluation Year", }, { id: "evaluationRound", label: "평가회차", group: "기본 정보", type: "text", excelHeader: "Evaluation Round", }, { id: "submissionStatus", label: "제출상태", group: "상태 정보", type: "select", options: [ { label: "임시저장", value: "draft" }, { label: "제출완료", value: "submitted" }, { label: "검토중", value: "under_review" }, { label: "승인", value: "approved" }, { label: "반려", value: "rejected" }, ], excelHeader: "Submission Status", }, { id: "submittedAt", label: "제출일시", group: "상태 정보", type: "date", excelHeader: "Submitted At", }, { id: "reviewedAt", label: "검토일시", group: "상태 정보", type: "date", excelHeader: "Reviewed At", }, { id: "totalGeneralScore", label: "일반평가 점수", group: "점수 정보", type: "number", excelHeader: "Total General Score", }, { id: "totalEsgScore", label: "ESG평가 점수", group: "점수 정보", type: "number", excelHeader: "Total ESG Score", }, { id: "createdAt", label: "생성일", group: "메타데이터", type: "date", excelHeader: "Created At", }, { id: "updatedAt", label: "수정일", group: "메타데이터", type: "date", excelHeader: "Updated At", }, ] as const;