"use client" import * as React from "react" import { type DataTableRowAction } from "@/types/table" import { type ColumnDef } from "@tanstack/react-table" import { Ellipsis, Eye, PaperclipIcon, FileEdit } from "lucide-react" import { formatDate } from "@/lib/utils" import { Badge } from "@/components/ui/badge" import { Button } from "@/components/ui/button" import { Checkbox } from "@/components/ui/checkbox" import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu" import { DataTableColumnHeader } from "@/components/data-table/data-table-column-header" import { useRouter } from "next/navigation" // PQ 제출 타입 정의 export interface PQSubmission { id: number pqNumber: string type: string status: string requesterName: string | null // 요청자 이름 createdAt: Date updatedAt: Date submittedAt: Date | null approvedAt: Date | null rejectedAt: Date | null rejectReason: string | null vendorId: number vendorName: string vendorCode: string taxId: string vendorStatus: string projectId: number | null projectName: string | null projectCode: string | null answerCount: number attachmentCount: number pqStatus: string pqTypeLabel: string investigation: { id: number investigationStatus: string requesterName: string | null // 실사 요청자 이름 evaluationType: "SITE_AUDIT" | "QM_SELF_AUDIT" | null qmManagerId: number | null qmManagerName: string | null // QM 담당자 이름 qmManagerEmail: string | null // QM 담당자 이메일 investigationAddress: string | null investigationMethod: string | null scheduledStartAt: Date | null scheduledEndAt: Date | null requestedAt: Date | null confirmedAt: Date | null completedAt: Date | null forecastedAt: Date | null evaluationScore: number | null evaluationResult: "APPROVED" | "SUPPLEMENT" | "REJECTED" | null investigationNotes: string | null } | null // 통합 상태를 위한 새 필드 combinedStatus: { status: string label: string variant: "default" | "outline" | "secondary" | "destructive" | "success" } } type NextRouter = ReturnType; interface GetColumnsProps { setRowAction: React.Dispatch | null>>; router: NextRouter; } // 상태에 따른 Badge 변형 결정 함수 function getStatusBadge(status: string) { switch (status) { case "REQUESTED": return 요청됨 case "IN_PROGRESS": return 진행 중 case "SUBMITTED": return 제출됨 case "APPROVED": return 승인됨 case "REJECTED": return 거부됨 default: return {status} } } /** * tanstack table 컬럼 정의 */ export function getColumns({ setRowAction, 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) 일반 컬럼들 // -------------------------- // -------------------------------------- const pqNoColumn: ColumnDef = { accessorKey: "pqNumber", header: ({ column }) => ( ), cell: ({ row }) => (
{row.getValue("pqNumber")}
), } // 협력업체 컬럼 const vendorColumn: ColumnDef = { accessorKey: "vendorName", header: ({ column }) => ( ), cell: ({ row }) => (
{row.getValue("vendorName")} {row.original.vendorCode ? row.original.vendorCode : "-"}/{row.original.taxId}
), } // PQ 유형 컬럼 const typeColumn: ColumnDef = { accessorKey: "type", header: ({ column }) => ( ), cell: ({ row }) => { return (
{row.original.pqTypeLabel}
) }, filterFn: (row, id, value) => { return value.includes(row.getValue(id)) }, } // 프로젝트 컬럼 const projectColumn: ColumnDef = { accessorKey: "projectName", header: ({ column }) => ( ), cell: ({ row }) => { const projectName = row.original.projectName const projectCode = row.original.projectCode if (!projectName) { return - } return (
{projectName} {projectCode && ( {projectCode} )}
) }, } // 상태 컬럼 const statusColumn: ColumnDef = { accessorKey: "combinedStatus", header: ({ column }) => ( ), cell: ({ row }) => { const combinedStatus = getCombinedStatus(row.original); return {combinedStatus.label}; }, filterFn: (row, id, value) => { const combinedStatus = getCombinedStatus(row.original); return value.includes(combinedStatus.status); }, }; // PQ 상태와 실사 상태를 결합하는 헬퍼 함수 function getCombinedStatus(submission: PQSubmission) { // PQ가 승인되지 않은 경우, PQ 상태를 우선 표시 if (submission.status !== "APPROVED") { switch (submission.status) { case "REQUESTED": return { status: "PQ_REQUESTED", label: "PQ 요청됨", variant: "outline" as const }; case "IN_PROGRESS": return { status: "PQ_IN_PROGRESS", label: "PQ 진행 중", variant: "secondary" as const }; case "SUBMITTED": return { status: "PQ_SUBMITTED", label: "PQ 제출됨", variant: "default" as const }; case "REJECTED": return { status: "PQ_REJECTED", label: "PQ 거부됨", variant: "destructive" as const }; default: return { status: submission.status, label: submission.status, variant: "outline" as const }; } } // PQ가 승인되었지만 실사가 없는 경우 if (!submission.investigation) { return { status: "PQ_APPROVED", label: "PQ 승인됨", variant: "success" as const }; } // PQ가 승인되고 실사가 있는 경우 switch (submission.investigation.investigationStatus) { case "PLANNED": return { status: "INVESTIGATION_PLANNED", label: "실사 계획됨", variant: "outline" as const }; case "IN_PROGRESS": return { status: "INVESTIGATION_IN_PROGRESS", label: "실사 진행 중", variant: "secondary" as const }; case "COMPLETED": // 실사 완료 후 평가 결과에 따라 다른 상태 표시 if (submission.investigation.evaluationResult) { switch (submission.investigation.evaluationResult) { case "APPROVED": return { status: "INVESTIGATION_APPROVED", label: "실사 승인", variant: "success" as const }; case "SUPPLEMENT": return { status: "INVESTIGATION_SUPPLEMENT", label: "실사 보완필요", variant: "secondary" as const }; case "REJECTED": return { status: "INVESTIGATION_REJECTED", label: "실사 불가", variant: "destructive" as const }; default: return { status: "INVESTIGATION_COMPLETED", label: "실사 완료", variant: "default" as const }; } } return { status: "INVESTIGATION_COMPLETED", label: "실사 완료", variant: "default" as const }; case "CANCELED": return { status: "INVESTIGATION_CANCELED", label: "실사 취소됨", variant: "destructive" as const }; default: return { status: `INVESTIGATION_${submission.investigation.investigationStatus}`, label: `실사 ${submission.investigation.investigationStatus}`, variant: "outline" as const }; } } const evaluationTypeColumn: ColumnDef = { accessorKey: "evaluationType", header: ({ column }) => ( ), cell: ({ row }) => { const investigation = row.original.investigation; if (!investigation || !investigation.evaluationType) { return -; } switch (investigation.evaluationType) { case "SITE_AUDIT": return 실사의뢰평가; case "QM_SELF_AUDIT": return QM자체평가; default: return {investigation.evaluationType}; } }, filterFn: (row, id, value) => { const investigation = row.original.investigation; if (!investigation || !investigation.evaluationType) return value.includes("null"); return value.includes(investigation.evaluationType); }, }; const evaluationResultColumn: ColumnDef = { accessorKey: "evaluationResult", header: ({ column }) => ( ), cell: ({ row }) => { const investigation = row.original.investigation; if (!investigation || !investigation.evaluationResult) { return -; } switch (investigation.evaluationResult) { case "APPROVED": return 승인; case "SUPPLEMENT": return 보완; case "REJECTED": return 불가; default: return {investigation.evaluationResult}; } }, filterFn: (row, id, value) => { const investigation = row.original.investigation; if (!investigation || !investigation.evaluationResult) return value.includes("null"); return value.includes(investigation.evaluationResult); }, }; // 답변 수 컬럼 const answerCountColumn: ColumnDef = { accessorKey: "answerCount", header: ({ column }) => ( ), cell: ({ row }) => { return (
{row.original.answerCount}
) }, } const investigationAddressColumn: ColumnDef = { accessorKey: "investigationAddress", header: ({ column }) => ( ), cell: ({ row }) => { const investigation = row.original.investigation; if (!investigation || !investigation.evaluationType) { return -; } return (
{investigation.investigationAddress}
) }, } const investigationNotesColumn: ColumnDef = { accessorKey: "investigationNotes", header: ({ column }) => ( ), cell: ({ row }) => { const investigation = row.original.investigation; if (!investigation || !investigation.investigationNotes) { return -; } return (
{investigation.investigationNotes}
) }, } const investigationRequestedAtColumn: ColumnDef = { accessorKey: "investigationRequestedAt", header: ({ column }) => ( ), cell: ({ row }) => { const investigation = row.original.investigation; if (!investigation || !investigation.requestedAt) { return -; } const dateVal = investigation.requestedAt return (
{dateVal ? formatDate(dateVal, 'KR') : "-"}
) }, } const investigationForecastedAtColumn: ColumnDef = { accessorKey: "investigationForecastedAt", header: ({ column }) => ( ), cell: ({ row }) => { const investigation = row.original.investigation; if (!investigation || !investigation.forecastedAt) { return -; } const dateVal = investigation.forecastedAt return (
{dateVal ? formatDate(dateVal, 'KR') : "-"}
) }, } const investigationConfirmedAtColumn: ColumnDef = { accessorKey: "investigationConfirmedAt", header: ({ column }) => ( ), cell: ({ row }) => { const investigation = row.original.investigation; if (!investigation || !investigation.confirmedAt) { return -; } const dateVal = investigation.confirmedAt return (
{dateVal ? formatDate(dateVal, 'KR') : "-"}
) }, } const investigationCompletedAtColumn: ColumnDef = { accessorKey: "investigationCompletedAt", header: ({ column }) => ( ), cell: ({ row }) => { const investigation = row.original.investigation; if (!investigation || !investigation.completedAt) { return -; } const dateVal = investigation.completedAt return (
{dateVal ? formatDate(dateVal, 'KR') : "-"}
) }, } // 제출일 컬럼 const createdAtColumn: ColumnDef = { accessorKey: "createdAt", header: ({ column }) => ( ), cell: ({ row }) => { const dateVal = row.original.createdAt as Date return formatDate(dateVal, 'KR') }, } // 제출일 컬럼 const submittedAtColumn: ColumnDef = { accessorKey: "submittedAt", header: ({ column }) => ( ), cell: ({ row }) => { const dateVal = row.original.submittedAt as Date return dateVal ? formatDate(dateVal, 'KR') : "-" }, } // 승인/거부일 컬럼 const approvalDateColumn: ColumnDef = { accessorKey: "approvedAt", header: ({ column }) => ( ), cell: ({ row }) => { if (row.original.approvedAt) { return {formatDate(row.original.approvedAt)} } if (row.original.rejectedAt) { return {formatDate(row.original.rejectedAt)} } return "-" }, } // ---------------------------------------------------------------- // 3) actions 컬럼 (Dropdown 메뉴) // ---------------------------------------------------------------- const actionsColumn: ColumnDef = { id: "actions", enableHiding: false, cell: function Cell({ row }) { const pq = row.original const isSubmitted = pq.status === "SUBMITTED" const reviewUrl = `/evcp/pq_new/${pq.vendorId}/${pq.id}` return ( { router.push(reviewUrl); }} > {isSubmitted ? ( <> 검토 ) : ( <> 보기 )} ) }, size: 40, } // 요청자 컬럼 추가 const requesterColumn: ColumnDef = { accessorKey: "requesterName", header: ({ column }) => ( ), cell: ({ row }) => { // PQ 요청자와 실사 요청자를 모두 표시 const pqRequesterName = row.original.requesterName; const investigationRequesterName = row.original.investigation?.requesterName; // 상태에 따라 적절한 요청자 표시 const status = getCombinedStatus(row.original).status; if (status.startsWith('INVESTIGATION_') && investigationRequesterName) { return {investigationRequesterName}; } return pqRequesterName ? {pqRequesterName} : -; }, }; const qmManagerColumn: ColumnDef = { accessorKey: "qmManager", header: ({ column }) => ( ), cell: ({ row }) => { const investigation = row.original.investigation; if (!investigation || !investigation.qmManagerName) { return -; } return (
{investigation.qmManagerName} {investigation.qmManagerEmail && ( {investigation.qmManagerEmail} )}
); }, }; // ---------------------------------------------------------------- // 4) 최종 컬럼 배열 // ---------------------------------------------------------------- return [ selectColumn, statusColumn, // 통합된 진행현황 컬럼 pqNoColumn, vendorColumn, investigationAddressColumn, typeColumn, projectColumn, createdAtColumn, submittedAtColumn, approvalDateColumn, answerCountColumn, evaluationTypeColumn, // 평가 유형 컬럼 investigationForecastedAtColumn, investigationRequestedAtColumn, investigationConfirmedAtColumn, investigationCompletedAtColumn, evaluationResultColumn, // 평가 결과 컬럼 requesterColumn, qmManagerColumn, investigationNotesColumn, actionsColumn, ]; }