// updated-enhanced-doc-table-columns.tsx "use client" import * as React from "react" import { ColumnDef } from "@tanstack/react-table" import { formatDate, formatDateTime } from "@/lib/utils" import { Checkbox } from "@/components/ui/checkbox" import { DataTableColumnHeaderSimple } from "@/components/data-table/data-table-column-simple-header" import { DataTableRowAction } from "@/types/table" import { EnhancedDocumentsView } from "@/db/schema/vendorDocu" import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu" import { Button } from "@/components/ui/button" import { Badge } from "@/components/ui/badge" import { Progress } from "@/components/ui/progress" import { Ellipsis, AlertTriangle, Clock, CheckCircle, Upload, Calendar, User, FileText, Eye, Edit, Trash2, Building, Code } from "lucide-react" import { cn } from "@/lib/utils" interface GetColumnsProps { setRowAction: React.Dispatch | null>> projectType: string | null } // 유틸리티 함수들 const getStatusColor = (status: string, isOverdue = false) => { if (isOverdue) return 'destructive' switch (status) { case 'COMPLETED': case 'APPROVED': return 'success' case 'IN_PROGRESS': return 'default' case 'SUBMITTED': case 'UNDER_REVIEW': return 'secondary' case 'REJECTED': return 'destructive' default: return 'outline' } } const getPriorityColor = (priority: string) => { switch (priority) { case 'HIGH': return 'destructive' case 'MEDIUM': return 'default' case 'LOW': return 'secondary' default: return 'outline' } } const getStatusText = (status: string) => { switch (status) { case 'PLANNED': return '계획됨' case 'IN_PROGRESS': return '진행중' case 'SUBMITTED': return '제출됨' case 'UNDER_REVIEW': return '검토중' case 'APPROVED': return '승인됨' case 'REJECTED': return '반려됨' case 'COMPLETED': return '완료됨' default: return status } } const getPriorityText = (priority: string) => { switch (priority) { case 'HIGH': return '높음' case 'MEDIUM': return '보통' case 'LOW': return '낮음' default: return priority } } // 마감일 정보 컴포넌트 const DueDateInfo = ({ daysUntilDue, isOverdue, className = "" }: { daysUntilDue: number | null isOverdue: boolean className?: string }) => { if (isOverdue && daysUntilDue !== null && daysUntilDue < 0) { return (
{Math.abs(daysUntilDue)}일 지연
) } if (daysUntilDue === 0) { return (
오늘 마감
) } if (daysUntilDue && daysUntilDue > 0 && daysUntilDue <= 3) { return (
{daysUntilDue}일 남음
) } if (daysUntilDue && daysUntilDue > 0) { return (
{daysUntilDue}일 남음
) } return (
완료
) } export function getUpdatedEnhancedColumns({ setRowAction, projectType }: GetColumnsProps): ColumnDef[] { const isPlantProject = projectType === "plant" // 기본 컬럼들 const baseColumns: 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, }, // 문서번호 + 우선순위 { accessorKey: "docNumber", header: ({ column }) => ( ), cell: ({ row }) => { const doc = row.original return (
{doc.docNumber}
) }, size: 120, enableResizing: true, meta: { excelHeader: "문서번호" }, }, ] // ✅ Ship 프로젝트용 추가 컬럼들 const plantColumns: ColumnDef[] = isPlantProject ? [ // 벤더 문서번호 { accessorKey: "vendorDocNumber", header: ({ column }) => ( ), cell: ({ row }) => { const doc = row.original return (
{doc.vendorDocNumber ? ( {doc.vendorDocNumber} ) : ( - )}
) }, size: 120, enableResizing: true, meta: { excelHeader: "벤더 문서번호" }, }, // 프로젝트 코드 { accessorKey: "projectCode", header: ({ column }) => ( ), cell: ({ row }) => { const doc = row.original return (
{/* */} {doc.projectCode || '-'}
) }, size: 100, enableResizing: true, meta: { excelHeader: "프로젝트 코드" }, }, // 벤더 정보 { accessorKey: "vendorName", header: ({ column }) => ( ), cell: ({ row }) => { const doc = row.original return (
{/* */} {doc.vendorName || '-'}
{doc.vendorCode && ( {doc.vendorCode} )}
) }, size: 150, enableResizing: true, meta: { excelHeader: "벤더명" }, }, ] : [] // 나머지 공통 컬럼들 const commonColumns: ColumnDef[] = [ // 문서명 + 담당자 { accessorKey: "title", header: ({ column }) => ( ), cell: ({ row }) => { const doc = row.original return (
{doc.title}
{doc.pic && ( PIC: {doc.pic} )} {doc.currentStageAssigneeName && (
{doc.currentStageAssigneeName}
)}
) }, size: isPlantProject ? 200 : 250, // Ship 프로젝트일 때는 너비 조정 enableResizing: true, meta: { excelHeader: "문서명" }, }, // 현재 스테이지 { accessorKey: "currentStageName", header: ({ column }) => ( ), cell: ({ row }) => { const doc = row.original if (!doc.currentStageName) return - return (
{doc.currentStageName} {getStatusText(doc.currentStageStatus || '')}
) }, size: 140, enableResizing: true, meta: { excelHeader: "현재 스테이지" }, }, // 일정 정보 { accessorKey: "currentStagePlanDate", header: ({ column }) => ( ), cell: ({ row }) => { const doc = row.original if (!doc.currentStagePlanDate) return - return (
계획: {formatDate(doc.currentStagePlanDate)}
{doc.currentStageActualDate && (
실제: {formatDate(doc.currentStageActualDate)}
)}
) }, size: 140, enableResizing: true, meta: { excelHeader: "계획일" }, }, // 진행률 { accessorKey: "progressPercentage", header: ({ column }) => ( ), cell: ({ row }) => { const doc = row.original const progress = doc.progressPercentage || 0 const completed = doc.completedStages || 0 const total = doc.totalStages || 0 return (
{progress}%
{completed} / {total} 스테이지
) }, size: 120, enableResizing: true, meta: { excelHeader: "진행률" }, }, // 최신 리비전 { accessorKey: "latestRevision", header: ({ column }) => ( ), cell: ({ row }) => { const doc = row.original if (!doc.latestRevision) return 없음 return (
{doc.latestRevision} {doc.latestRevisionStatus && ( {getStatusText(doc.latestRevisionStatus)} )} {doc.latestSubmittedDate && (
{formatDate(doc.latestSubmittedDate)}
)}
) }, size: 140, enableResizing: true, meta: { excelHeader: "최신 리비전" }, }, // 업데이트 일시 { accessorKey: "updatedAt", header: ({ column }) => ( ), cell: ({ cell }) => ( {formatDateTime(cell.getValue() as Date)} ), size: 140, enableResizing: true, meta: { excelHeader: "업데이트" }, }, // 액션 메뉴 { id: "actions", enableHiding: false, cell: function Cell({ row }) { const doc = row.original const canSubmit = doc.currentStageStatus === 'IN_PROGRESS' const canApprove = doc.currentStageStatus === 'SUBMITTED' const isPlantProject = projectType === "plant" // 메뉴 아이템들을 그룹별로 정의 const viewActions = [ { key: "view", label: "상세보기", icon: Eye, action: () => setRowAction({ row, type: "view" }), show: true } ] const editActions = [ { key: "update", label: "편집", icon: Edit, action: () => setRowAction({ row, type: "update" }), show: isPlantProject } ] const fileActions = [ { key: "upload", label: "리비전 업로드", icon: Upload, action: () => setRowAction({ row, type: "upload" }), show: canSubmit } ] const dangerActions = [ { key: "delete", label: "삭제", icon: Trash2, action: () => setRowAction({ row, type: "delete" }), show: isPlantProject, className: "text-red-600", shortcut: "⌘⌫" } ] // 각 그룹에서 표시될 아이템이 있는지 확인 const hasEditActions = editActions.some(action => action.show) const hasFileActions = fileActions.some(action => action.show) const hasDangerActions = dangerActions.some(action => action.show) return ( {/* 기본 액션 그룹 */} {viewActions.map(action => action.show && ( {action.label} {action.shortcut && ( {action.shortcut} )} ))} {/* 편집 액션 그룹 */} {hasEditActions && ( <> {editActions.map(action => action.show && ( {action.label} {action.shortcut && ( {action.shortcut} )} ))} )} {/* 파일 액션 그룹 */} {hasFileActions && ( <> {fileActions.map(action => action.show && ( {action.label} {action.shortcut && ( {action.shortcut} )} ))} )} {/* 위험한 액션 그룹 */} {hasDangerActions && ( <> {dangerActions.map(action => action.show && ( {action.label} {action.shortcut && ( {action.shortcut} )} ))} )} ) }, size: 40, } ] // ✅ 모든 컬럼을 순서대로 결합 return [ ...baseColumns, // 체크박스, 문서번호 ...plantColumns, // Ship 전용 컬럼들 (조건부) ...commonColumns // 나머지 공통 컬럼들 ] } // 확장된 행 컨텐츠 컴포넌트 (업데이트된 버전) export const UpdatedExpandedRowContent = ({ document }: { document: EnhancedDocumentsView }) => { if (!document.allStages || document.allStages.length === 0) { return (
스테이지 정보가 없습니다.
) } return (

전체 스테이지 현황

{document.allStages.map((stage, index) => (
{stage.stageOrder || index + 1}
{stage.stageName}
{stage.assigneeName && (
{stage.assigneeName}
)}
계획: {formatDate(stage.planDate)}
{stage.actualDate && (
완료: {formatDate(stage.actualDate)}
)}
{getPriorityText(stage.priority)} {getStatusText(stage.stageStatus)}
))}
) }