diff options
Diffstat (limited to 'lib/rfq-last/table/rfq-table-columns.tsx')
| -rw-r--r-- | lib/rfq-last/table/rfq-table-columns.tsx | 1206 |
1 files changed, 858 insertions, 348 deletions
diff --git a/lib/rfq-last/table/rfq-table-columns.tsx b/lib/rfq-last/table/rfq-table-columns.tsx index 3fac8881..1b523adc 100644 --- a/lib/rfq-last/table/rfq-table-columns.tsx +++ b/lib/rfq-last/table/rfq-table-columns.tsx @@ -5,7 +5,7 @@ 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 { Eye, FileText, Send, Lock, LockOpen } from "lucide-react"; import { Tooltip, TooltipContent, @@ -17,10 +17,14 @@ import { RfqsLastView } from "@/db/schema"; import { DataTableRowAction } from "@/types/table"; import { format } from "date-fns"; import { ko } from "date-fns/locale"; +import { useRouter } from "next/navigation"; + +type NextRouter = ReturnType<typeof useRouter>; interface GetColumnsProps { setRowAction: React.Dispatch<React.SetStateAction<DataTableRowAction<RfqsLastView> | null>>; - rfqCategory?: "all" | "general" | "itb" | "rfq"; + rfqCategory?: "general" | "itb" | "rfq"; + router: NextRouter; } // RFQ 상태별 색상 @@ -38,408 +42,914 @@ const getStatusBadgeVariant = (status: string) => { } }; -// 시리즈 배지 -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 <Badge variant={variant} className="text-xs">{label}</Badge>; -}; - export function getRfqColumns({ setRowAction, - rfqCategory = "all" + rfqCategory = "itb", + router }: GetColumnsProps): ColumnDef<RfqsLastView>[] { - - const baseColumns: ColumnDef<RfqsLastView>[] = [ - // ═══════════════════════════════════════════════════════════════ - // 선택 및 기본 정보 - // ═══════════════════════════════════════════════════════════════ - - // Checkbox - { - id: "select", - header: ({ table }) => ( - <Checkbox - checked={table.getIsAllPageRowsSelected() || (table.getIsSomePageRowsSelected() && "indeterminate")} - onCheckedChange={(v) => table.toggleAllPageRowsSelected(!!v)} - aria-label="select all" - className="translate-y-0.5" - /> - ), - cell: ({ row }) => ( - <Checkbox - checked={row.getIsSelected()} - onCheckedChange={(v) => row.toggleSelected(!!v)} - aria-label="select row" - className="translate-y-0.5" - /> - ), - size: 40, - enableSorting: false, - enableHiding: false, - }, - - // RFQ 코드 - { - accessorKey: "rfqCode", - header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="RFQ 코드" />, - cell: ({ row }) => { - const rfqSealed = row.original.rfqSealedYn; - return ( - <div className="flex items-center gap-1"> - <span className="font-mono font-medium">{row.original.rfqCode}</span> - {rfqSealed && ( - <Badge variant="destructive" className="text-xs">봉인</Badge> - )} - </div> - ); - }, - size: 140, - }, - - // 상태 - { - accessorKey: "status", - header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="상태" />, - cell: ({ row }) => ( - <Badge variant={getStatusBadgeVariant(row.original.status)}> - {row.original.status} - </Badge> - ), - size: 120, - }, - - // ═══════════════════════════════════════════════════════════════ - // 일반견적 필드 (rfqCategory가 'general' 또는 'all'일 때만) - // ═══════════════════════════════════════════════════════════════ - ...(rfqCategory === "general" || rfqCategory === "all" ? [ - { - id: "rfqType", - accessorKey: "rfqType", - header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="견적 유형" />, - cell: ({ row }) => row.original.rfqType || "-", + + // ═══════════════════════════════════════════════════════════════ + // ITB 컬럼 정의 + // ═══════════════════════════════════════════════════════════════ + if (rfqCategory === "itb") { + return [ + // Checkbox + { + id: "select", + header: ({ table }) => ( + <Checkbox + checked={table.getIsAllPageRowsSelected() || (table.getIsSomePageRowsSelected() && "indeterminate")} + onCheckedChange={(v) => table.toggleAllPageRowsSelected(!!v)} + aria-label="select all" + className="translate-y-0.5" + /> + ), + cell: ({ row }) => ( + <Checkbox + checked={row.getIsSelected()} + onCheckedChange={(v) => row.toggleSelected(!!v)} + aria-label="select row" + className="translate-y-0.5" + /> + ), + size: 40, + enableSorting: false, + enableHiding: false, + }, + + // ITB No. + { + accessorKey: "rfqCode", + header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="ITB No." />, + cell: ({ row }) => ( + <span className="font-mono font-medium">{row.original.rfqCode}</span> + ), + size: 120, + }, + + // 상세 (액션 버튼) - 수정됨 + { + id: "detail", + header: "상세", + cell: ({ row }) => ( + <Button + variant="ghost" + size="icon" + className="h-8 w-8" + onClick={() => router.push(`/evcp/rfq-last/${row.original.id}`)} + > + <Eye className="h-4 w-4" /> + </Button> + ), + size: 60, + }, + + // 견적상태 + { + accessorKey: "status", + header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="견적상태" />, + cell: ({ row }) => ( + <Badge variant={getStatusBadgeVariant(row.original.status)}> + {row.original.status} + </Badge> + ), + size: 120, + }, + + // 견적 밀봉 + { + accessorKey: "rfqSealedYn", + header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="견적 밀봉" />, + cell: ({ row }) => { + const isSealed = row.original.rfqSealedYn; + return ( + <div className="flex justify-center"> + {isSealed ? ( + <Lock className="h-4 w-4 text-red-500" /> + ) : ( + <LockOpen className="h-4 w-4 text-gray-400" /> + )} + </div> + ); + }, + size: 80, + }, + + // 구매담당자 + { + accessorKey: "picUserName", + header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="구매담당자" />, + cell: ({ row }) => row.original.picUserName || row.original.picName || "-", size: 100, }, + + // 프로젝트 (프로젝트명) { - id: "rfqTitle", - accessorKey: "rfqTitle", - header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="견적 제목" />, + accessorKey: "projectName", + header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="프로젝트 (프로젝트명)" />, cell: ({ row }) => ( - <div className="max-w-[200px] truncate" title={row.original.rfqTitle || ""}> - {row.original.rfqTitle || "-"} + <div className="flex flex-col"> + <span className="font-mono text-xs text-muted-foreground"> + {row.original.projectCode} + </span> + <span className="max-w-[200px] truncate" title={row.original.projectName || ""}> + {row.original.projectName || "-"} + </span> </div> ), - size: 200, + size: 220, + }, + + // 선급 + { + accessorKey: "classNo", + header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="선급" />, + cell: ({ row }) => row.original.classNo || "-", + size: 80, + }, + + // PKG No. (PKG명) + { + accessorKey: "packageName", + header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="PKG No. (PKG명)" />, + cell: ({ row }) => ( + <div className="flex flex-col"> + <span className="font-mono text-xs text-muted-foreground"> + {row.original.packageNo} + </span> + <span className="max-w-[150px] truncate" title={row.original.packageName || ""}> + {row.original.packageName || "-"} + </span> + </div> + ), + size: 180, + }, + + // 자재그룹 (자재그룹명) + { + accessorKey: "itemName", + header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="자재그룹 (자재그룹명)" />, + cell: ({ row }) => ( + <div className="flex flex-col"> + <span className="font-mono text-xs text-muted-foreground"> + {row.original.itemCode} + </span> + <span className="max-w-[150px] truncate" title={row.original.itemName || ""}> + {row.original.itemName || "-"} + </span> + </div> + ), + size: 180, + }, + + // SM Code + { + accessorKey: "smCode", + header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="SM Code" />, + cell: ({ row }) => row.original.smCode || "-", + size: 80, + }, + + // 견적문서 - 수정됨 + { + id: "rfqDocument", + header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="견적문서" />, + cell: ({ row }) => ( + <Button + variant="ghost" + size="sm" + onClick={() => setRowAction({ row, type: "attachment" })} + > + <FileText className="h-4 w-4" /> + </Button> + ), + size: 80, + }, + + // 견적생성일 + { + accessorKey: "createdAt", + header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="견적생성일" />, + cell: ({ row }) => { + const date = row.original.createdAt; + return date ? format(new Date(date), "yyyy-MM-dd") : "-"; + }, + size: 100, + }, + + // 견적발송일 + { + accessorKey: "rfqSendDate", + header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="견적발송일" />, + cell: ({ row }) => { + const date = row.original.rfqSendDate; + return date ? format(new Date(date), "yyyy-MM-dd") : "-"; + }, + size: 100, + }, + + // 견적마감일 + { + accessorKey: "dueDate", + header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="견적마감일" />, + cell: ({ row }) => { + const date = row.original.dueDate; + if (!date) return "-"; + + const now = new Date(); + const dueDate = new Date(date); + const isOverdue = now > dueDate; + + return ( + <span className={`text-sm ${isOverdue ? "text-red-600 font-medium" : ""}`}> + {format(dueDate, "yyyy-MM-dd")} + </span> + ); + }, + size: 100, + }, + + // 설계담당자 + { + accessorKey: "engPicName", + header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="설계담당자" />, + cell: ({ row }) => row.original.engPicName || "-", + size: 100, }, - ] as ColumnDef<RfqsLastView>[] : []), - // ═══════════════════════════════════════════════════════════════ - // ITB 필드 (rfqCategory가 'itb' 또는 'all'일 때만) - // ═══════════════════════════════════════════════════════════════ - ...(rfqCategory === "itb" || rfqCategory === "all" ? [ + // 프로젝트 Company { - id: "projectCompany", accessorKey: "projectCompany", - header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="프로젝트 회사" />, + header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="프로젝트 Company" />, cell: ({ row }) => row.original.projectCompany || "-", size: 120, }, + + // TBE 결과접수 + { + accessorKey: "tbeResultReceived", + header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="TBE 결과접수" />, + cell: ({ row }) => { + const received = row.original.quotationReceivedCount || 0; + const total = row.original.vendorCount || 0; + return `${received}/${total}`; + }, + size: 100, + }, + + // 프로젝트 Flag { - id: "projectFlag", accessorKey: "projectFlag", - header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="프로젝트 플래그" />, + header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="프로젝트 Flag" />, cell: ({ row }) => row.original.projectFlag || "-", size: 100, }, + + // 프로젝트 Site { - id: "projectSite", accessorKey: "projectSite", - header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="프로젝트 사이트" />, + header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="프로젝트 Site" />, cell: ({ row }) => row.original.projectSite || "-", + size: 100, + }, + + // 최종수정일 + { + accessorKey: "updatedAt", + header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="최종수정일" />, + cell: ({ row }) => { + const date = row.original.updatedAt; + return date ? format(new Date(date), "yyyy-MM-dd HH:mm") : "-"; + }, size: 120, }, + + // 최종수정자 { - id: "smCode", - accessorKey: "smCode", - header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="SM 코드" />, - cell: ({ row }) => row.original.smCode || "-", - size: 80, + accessorKey: "updatedByUserName", + header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="최종수정자" />, + cell: ({ row }) => row.original.updatedByUserName || "-", + size: 100, }, - ] as ColumnDef<RfqsLastView>[] : []), - // ═══════════════════════════════════════════════════════════════ - // RFQ(PR) 필드 (rfqCategory가 'rfq' 또는 'all'일 때만) - // ═══════════════════════════════════════════════════════════════ - ...(rfqCategory === "rfq" || rfqCategory === "all" ? [ + // 비고 { - id: "prNumber", - accessorKey: "prNumber", - header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="PR 번호" />, + accessorKey: "remark", + header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="비고" />, + cell: ({ row }) => row.original.remark || "-", + size: 150, + }, + ]; + } + + // ═══════════════════════════════════════════════════════════════ + // RFQ 컬럼 정의 + // ═══════════════════════════════════════════════════════════════ + if (rfqCategory === "rfq") { + return [ + // Checkbox + { + id: "select", + header: ({ table }) => ( + <Checkbox + checked={table.getIsAllPageRowsSelected() || (table.getIsSomePageRowsSelected() && "indeterminate")} + onCheckedChange={(v) => table.toggleAllPageRowsSelected(!!v)} + aria-label="select all" + className="translate-y-0.5" + /> + ), cell: ({ row }) => ( - <span className="font-mono text-sm">{row.original.prNumber || "-"}</span> + <Checkbox + checked={row.getIsSelected()} + onCheckedChange={(v) => row.toggleSelected(!!v)} + aria-label="select row" + className="translate-y-0.5" + /> + ), + size: 40, + enableSorting: false, + enableHiding: false, + }, + + // RFQ No. + { + accessorKey: "rfqCode", + header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="RFQ No." />, + cell: ({ row }) => ( + <span className="font-mono font-medium">{row.original.rfqCode}</span> ), size: 120, }, + + // 상세 - 수정됨 { - id: "prIssueDate", - accessorKey: "prIssueDate", - header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="PR 발행일" />, + id: "detail", + header: "상세", + cell: ({ row }) => ( + <Button + variant="ghost" + size="icon" + className="h-8 w-8" + onClick={() => router.push(`/evcp/rfq-last/${row.original.id}`)} + > + <Eye className="h-4 w-4" /> + </Button> + ), + size: 60, + }, + + // 견적상태 + { + accessorKey: "status", + header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="견적상태" />, + cell: ({ row }) => ( + <Badge variant={getStatusBadgeVariant(row.original.status)}> + {row.original.status} + </Badge> + ), + size: 120, + }, + + // 견적 밀봉 + { + accessorKey: "rfqSealedYn", + header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="견적 밀봉" />, cell: ({ row }) => { - const date = row.original.prIssueDate; - return date ? format(new Date(date), "yyyy-MM-dd") : "-"; + const isSealed = row.original.rfqSealedYn; + return ( + <div className="flex justify-center"> + {isSealed ? ( + <Lock className="h-4 w-4 text-red-500" /> + ) : ( + <LockOpen className="h-4 w-4 text-gray-400" /> + )} + </div> + ); }, + size: 80, + }, + + // 구매담당자 + { + accessorKey: "picUserName", + header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="구매담당자" />, + cell: ({ row }) => row.original.picUserName || row.original.picName || "-", size: 100, }, + + // 프로젝트 (프로젝트명) + { + accessorKey: "projectName", + header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="프로젝트 (프로젝트명)" />, + cell: ({ row }) => ( + <div className="flex flex-col"> + <span className="font-mono text-xs text-muted-foreground"> + {row.original.projectCode} + </span> + <span className="max-w-[200px] truncate" title={row.original.projectName || ""}> + {row.original.projectName || "-"} + </span> + </div> + ), + size: 220, + }, + + // 시리즈 { - id: "series", accessorKey: "series", header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="시리즈" />, - cell: ({ row }) => getSeriesBadge(row.original.series), + cell: ({ row }) => { + const series = row.original.series; + if (!series) return "-"; + const label = series === "SS" ? "시리즈 통합" : series === "II" ? "품목 통합" : series; + return <Badge variant="outline">{label}</Badge>; + }, size: 100, }, - ] as ColumnDef<RfqsLastView>[] : []), - - // ═══════════════════════════════════════════════════════════════ - // 공통 프로젝트 정보 - // ═══════════════════════════════════════════════════════════════ - { - header: "프로젝트 정보", - columns: [ - { - accessorKey: "projectCode", - header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="프로젝트 코드" />, - cell: ({ row }) => ( - <span className="font-mono text-sm">{row.original.projectCode || "-"}</span> - ), - size: 120, - }, - { - accessorKey: "projectName", - header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="프로젝트명" />, - cell: ({ row }) => ( - <div className="max-w-[200px] truncate" title={row.original.projectName || ""}> - {row.original.projectName || "-"} - </div> - ), - size: 200, - }, - ] - }, - - // ═══════════════════════════════════════════════════════════════ - // 품목 정보 - // ═══════════════════════════════════════════════════════════════ - { - header: "품목 정보", - columns: [ - { - accessorKey: "itemCode", - header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="자재코드" />, - cell: ({ row }) => ( - <span className="font-mono text-sm">{row.original.itemCode || "-"}</span> - ), - size: 100, - }, - { - accessorKey: "itemName", - header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="자재명" />, - cell: ({ row }) => ( - <div className="max-w-[200px] truncate" title={row.original.itemName || ""}> + + // 선급 + { + accessorKey: "classNo", + header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="선급" />, + cell: ({ row }) => row.original.classNo || "-", + size: 80, + }, + + // PKG No. (PKG명) + { + accessorKey: "packageName", + header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="PKG No. (PKG명)" />, + cell: ({ row }) => ( + <div className="flex flex-col"> + <span className="font-mono text-xs text-muted-foreground"> + {row.original.packageNo} + </span> + <span className="max-w-[150px] truncate" title={row.original.packageName || ""}> + {row.original.packageName || "-"} + </span> + </div> + ), + size: 180, + }, + + // 자재그룹 (자재그룹명) + { + accessorKey: "itemName", + header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="자재그룹 (자재그룹명)" />, + cell: ({ row }) => ( + <div className="flex flex-col"> + <span className="font-mono text-xs text-muted-foreground"> + {row.original.itemCode} + </span> + <span className="max-w-[150px] truncate" title={row.original.itemName || ""}> {row.original.itemName || "-"} - </div> - ), - size: 200, - }, - { - accessorKey: "packageNo", - header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="패키지 번호" />, - cell: ({ row }) => row.original.packageNo || "-", - size: 100, + </span> + </div> + ), + size: 180, + }, + + // 자재코드 + { + accessorKey: "itemCode", + header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="자재코드" />, + cell: ({ row }) => ( + <span className="font-mono text-sm">{row.original.itemCode || "-"}</span> + ), + size: 100, + }, + + // 견적문서 - 수정됨 + { + id: "rfqDocument", + header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="견적문서" />, + cell: ({ row }) => ( + <Button + variant="ghost" + size="sm" + onClick={() => setRowAction({ row, type: "attachment" })} + > + <FileText className="h-4 w-4" /> + </Button> + ), + size: 80, + }, + + // PR건수 - 수정됨 + { + accessorKey: "prItemsCount", + header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="PR건수" />, + cell: ({ row }) => ( + <Button + variant="ghost" + size="sm" + className="font-mono text-sm p-1 h-auto" + onClick={() => setRowAction({ row, type: "items" })} + > + {row.original.prItemsCount || 0} + </Button> + ), + size: 80, + }, + + // 견적생성일 + { + accessorKey: "createdAt", + header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="견적생성일" />, + cell: ({ row }) => { + const date = row.original.createdAt; + return date ? format(new Date(date), "yyyy-MM-dd") : "-"; }, - { - accessorKey: "packageName", - header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="패키지명" />, - cell: ({ row }) => ( - <div className="max-w-[200px] truncate" title={row.original.packageName || ""}> - {row.original.packageName || "-"} - </div> - ), - size: 200, + size: 100, + }, + + // 견적발송일 + { + accessorKey: "rfqSendDate", + header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="견적발송일" />, + cell: ({ row }) => { + const date = row.original.rfqSendDate; + return date ? format(new Date(date), "yyyy-MM-dd") : "-"; }, - ] - }, - - // ═══════════════════════════════════════════════════════════════ - // 담당자 정보 - // ═══════════════════════════════════════════════════════════════ - { - header: "담당자", - columns: [ - { - accessorKey: "picUserName", - header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="구매담당자" />, - cell: ({ row }) => row.original.picUserName || row.original.picName || "-", - size: 100, + size: 100, + }, + + // 견적마감일 + { + accessorKey: "dueDate", + header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="견적마감일" />, + cell: ({ row }) => { + const date = row.original.dueDate; + if (!date) return "-"; + + const now = new Date(); + const dueDate = new Date(date); + const isOverdue = now > dueDate; + + return ( + <span className={`text-sm ${isOverdue ? "text-red-600 font-medium" : ""}`}> + {format(dueDate, "yyyy-MM-dd")} + </span> + ); }, - { - accessorKey: "engPicName", - header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="엔지니어링 담당" />, - cell: ({ row }) => row.original.engPicName || "-", - size: 120, + size: 100, + }, + + // 설계담당자 + { + accessorKey: "engPicName", + header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="설계담당자" />, + cell: ({ row }) => row.original.engPicName || "-", + size: 100, + }, + + // TBE 결과접수 + { + accessorKey: "tbeResultReceived", + header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="TBE 결과접수" />, + cell: ({ row }) => { + const received = row.original.quotationReceivedCount || 0; + const total = row.original.vendorCount || 0; + return `${received}/${total}`; }, - ] - }, - - // ═══════════════════════════════════════════════════════════════ - // 일정 정보 - // ═══════════════════════════════════════════════════════════════ - { - header: "일정", - columns: [ - { - accessorKey: "rfqSendDate", - header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="발송일" />, - cell: ({ row }) => { - const date = row.original.rfqSendDate; - return date ? ( - <div className="flex items-center gap-1"> - <Send className="h-3 w-3 text-muted-foreground" /> - <span className="text-sm"> - {format(new Date(date), "MM-dd", { locale: ko })} - </span> - </div> - ) : "-"; - }, - size: 90, + size: 100, + }, + + // 대표 PR No. + { + accessorKey: "prNumber", + header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="대표 PR No." />, + cell: ({ row }) => ( + <span className="font-mono text-sm">{row.original.prNumber || "-"}</span> + ), + size: 120, + }, + + // PR발행일 + { + accessorKey: "prIssueDate", + header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="PR발행일" />, + cell: ({ row }) => { + const date = row.original.prIssueDate; + return date ? format(new Date(date), "yyyy-MM-dd") : "-"; }, - { - accessorKey: "dueDate", - header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="마감일" />, - cell: ({ row }) => { - const date = row.original.dueDate; - if (!date) return "-"; - - const now = new Date(); - const dueDate = new Date(date); - const isOverdue = now > dueDate; - - return ( - <span className={`text-sm ${isOverdue ? "text-red-600 font-medium" : ""}`}> - {format(dueDate, "MM-dd", { locale: ko })} - </span> - ); - }, - size: 90, + size: 100, + }, + + // 최종수정일 + { + accessorKey: "updatedAt", + header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="최종수정일" />, + cell: ({ row }) => { + const date = row.original.updatedAt; + return date ? format(new Date(date), "yyyy-MM-dd HH:mm") : "-"; }, - ] - }, - - // ═══════════════════════════════════════════════════════════════ - // 벤더 및 견적 현황 - // ═══════════════════════════════════════════════════════════════ - { - header: "견적 현황", - columns: [ - { - accessorKey: "vendorCount", - header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="업체수" />, - cell: ({ row }) => ( - <div className="flex items-center gap-1"> - <Users className="h-3 w-3 text-muted-foreground" /> - <span className="font-medium">{row.original.vendorCount || 0}</span> + size: 120, + }, + + // 최종수정자 + { + accessorKey: "updatedByUserName", + header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="최종수정자" />, + cell: ({ row }) => row.original.updatedByUserName || "-", + size: 100, + }, + + // 비고 + { + accessorKey: "remark", + header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="비고" />, + cell: ({ row }) => row.original.remark || "-", + size: 150, + }, + ]; + } + + // ═══════════════════════════════════════════════════════════════ + // 일반견적 컬럼 정의 + // ═══════════════════════════════════════════════════════════════ + if (rfqCategory === "general") { + return [ + // Checkbox + { + id: "select", + header: ({ table }) => ( + <Checkbox + checked={table.getIsAllPageRowsSelected() || (table.getIsSomePageRowsSelected() && "indeterminate")} + onCheckedChange={(v) => table.toggleAllPageRowsSelected(!!v)} + aria-label="select all" + className="translate-y-0.5" + /> + ), + cell: ({ row }) => ( + <Checkbox + checked={row.getIsSelected()} + onCheckedChange={(v) => row.toggleSelected(!!v)} + aria-label="select row" + className="translate-y-0.5" + /> + ), + size: 40, + enableSorting: false, + enableHiding: false, + }, + + // 견적 No. + { + accessorKey: "rfqCode", + header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="견적 No." />, + cell: ({ row }) => ( + <span className="font-mono font-medium">{row.original.rfqCode}</span> + ), + size: 120, + }, + + // 상세 - 수정됨 + { + id: "detail", + header: "상세", + cell: ({ row }) => ( + <Button + variant="ghost" + size="icon" + className="h-8 w-8" + onClick={() => router.push(`/evcp/rfq-last/${row.original.id}`)} + > + <Eye className="h-4 w-4" /> + </Button> + ), + size: 60, + }, + + // 견적상태 + { + accessorKey: "status", + header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="견적상태" />, + cell: ({ row }) => ( + <Badge variant={getStatusBadgeVariant(row.original.status)}> + {row.original.status} + </Badge> + ), + size: 120, + }, + + // 견적 밀봉 + { + accessorKey: "rfqSealedYn", + header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="견적 밀봉" />, + cell: ({ row }) => { + const isSealed = row.original.rfqSealedYn; + return ( + <div className="flex justify-center"> + {isSealed ? ( + <Lock className="h-4 w-4 text-red-500" /> + ) : ( + <LockOpen className="h-4 w-4 text-gray-400" /> + )} </div> - ), - size: 80, - }, - { - accessorKey: "shortListedVendorCount", - header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="Short List" />, - cell: ({ row }) => { - const count = row.original.shortListedVendorCount || 0; - return count > 0 ? ( - <Badge variant="default" className="font-mono"> - {count} - </Badge> - ) : "-"; - }, - size: 90, + ); }, - { - accessorKey: "quotationReceivedCount", - header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="견적접수" />, - cell: ({ row }) => { - const received = row.original.quotationReceivedCount || 0; - const total = row.original.vendorCount || 0; - - return ( - <div className="flex items-center gap-1"> - <FileText className="h-3 w-3 text-muted-foreground" /> - <span className={`text-sm ${received === total && total > 0 ? "text-green-600 font-medium" : ""}`}> - {received}/{total} - </span> - </div> - ); - }, - size: 90, + size: 80, + }, + + // 견적종류 + { + accessorKey: "rfqType", + header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="견적종류" />, + cell: ({ row }) => row.original.rfqType || "-", + size: 100, + }, + + // 프로젝트 (프로젝트명) + { + accessorKey: "projectName", + header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="프로젝트 (프로젝트명)" />, + cell: ({ row }) => ( + <div className="flex flex-col"> + <span className="font-mono text-xs text-muted-foreground"> + {row.original.projectCode} + </span> + <span className="max-w-[200px] truncate" title={row.original.projectName || ""}> + {row.original.projectName || "-"} + </span> + </div> + ), + size: 220, + }, + + // 시리즈 + { + accessorKey: "series", + header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="시리즈" />, + cell: ({ row }) => { + const series = row.original.series; + if (!series) return "-"; + const label = series === "SS" ? "시리즈 통합" : series === "II" ? "품목 통합" : series; + return <Badge variant="outline">{label}</Badge>; }, - ] - }, - - // PR Items 정보 - { - accessorKey: "prItemsCount", - header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="PR Items" />, - cell: ({ row }) => { - const prItems = row.original.prItemsCount || 0; - const majorItems = row.original.majorItemsCount || 0; - - return ( - <div className="flex flex-col gap-0.5"> - <span className="text-sm font-medium">{prItems}개</span> - {majorItems > 0 && ( - <Badge variant="secondary" className="text-xs"> - 주요 {majorItems} - </Badge> - )} + size: 100, + }, + + // 선급 + { + accessorKey: "classNo", + header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="선급" />, + cell: ({ row }) => row.original.classNo || "-", + size: 80, + }, + + // 견적명 + { + accessorKey: "rfqTitle", + header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="견적명" />, + cell: ({ row }) => ( + <div className="max-w-[200px] truncate" title={row.original.rfqTitle || ""}> + {row.original.rfqTitle || "-"} </div> - ); - }, - size: 90, - }, - - // 액션 - { - id: "actions", - header: "액션", - enableHiding: false, - size: 80, - minSize: 80, - cell: ({ row }) => { - return ( - <div className="flex items-center gap-1"> - <TooltipProvider> - <Tooltip> - <TooltipTrigger asChild> - <Button - variant="ghost" - size="icon" - className="h-8 w-8" - onClick={() => setRowAction({ row, type: "view" })} - > - <Eye className="h-4 w-4" /> - </Button> - </TooltipTrigger> - <TooltipContent>상세보기</TooltipContent> - </Tooltip> - </TooltipProvider> + ), + size: 200, + }, + + // 자재그룹 (자재그룹명) + { + accessorKey: "itemName", + header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="자재그룹 (자재그룹명)" />, + cell: ({ row }) => ( + <div className="flex flex-col"> + <span className="font-mono text-xs text-muted-foreground"> + {row.original.itemCode} + </span> + <span className="max-w-[150px] truncate" title={row.original.itemName || ""}> + {row.original.itemName || "-"} + </span> </div> - ); + ), + size: 180, + }, + + // 자재코드 + { + accessorKey: "itemCode", + header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="자재코드" />, + cell: ({ row }) => ( + <span className="font-mono text-sm">{row.original.itemCode || "-"}</span> + ), + size: 100, }, - }, - ]; - return baseColumns; + // 견적 자료 - 수정됨 + { + id: "rfqDocument", + header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="견적 자료" />, + cell: ({ row }) => ( + <Button + variant="ghost" + size="sm" + onClick={() => setRowAction({ row, type: "attachment" })} + > + <FileText className="h-4 w-4" /> + </Button> + ), + size: 80, + }, + + // 견적품목수 - 수정됨 + { + accessorKey: "prItemsCount", + header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="견적품목수" />, + cell: ({ row }) => ( + <Button + variant="ghost" + size="sm" + className="font-mono text-sm p-1 h-auto" + onClick={() => setRowAction({ row, type: "items" })} + > + {row.original.prItemsCount || 0} + </Button> + ), + size: 90, + }, + + // 견적생성일 + { + accessorKey: "createdAt", + header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="견적생성일" />, + cell: ({ row }) => { + const date = row.original.createdAt; + return date ? format(new Date(date), "yyyy-MM-dd") : "-"; + }, + size: 100, + }, + + // 견적발송일 + { + accessorKey: "rfqSendDate", + header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="견적발송일" />, + cell: ({ row }) => { + const date = row.original.rfqSendDate; + return date ? format(new Date(date), "yyyy-MM-dd") : "-"; + }, + size: 100, + }, + + // 견적마감일 + { + accessorKey: "dueDate", + header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="견적마감일" />, + cell: ({ row }) => { + const date = row.original.dueDate; + if (!date) return "-"; + + const now = new Date(); + const dueDate = new Date(date); + const isOverdue = now > dueDate; + + return ( + <span className={`text-sm ${isOverdue ? "text-red-600 font-medium" : ""}`}> + {format(dueDate, "yyyy-MM-dd")} + </span> + ); + }, + size: 100, + }, + + // 구매담당자 + { + accessorKey: "picUserName", + header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="구매담당자" />, + cell: ({ row }) => row.original.picUserName || row.original.picName || "-", + size: 100, + }, + + // 최종수정일 + { + accessorKey: "updatedAt", + header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="최종수정일" />, + cell: ({ row }) => { + const date = row.original.updatedAt; + return date ? format(new Date(date), "yyyy-MM-dd HH:mm") : "-"; + }, + size: 120, + }, + + // 최종수정자 + { + accessorKey: "updatedByUserName", + header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="최종수정자" />, + cell: ({ row }) => row.original.updatedByUserName || "-", + size: 100, + }, + + // 비고 + { + accessorKey: "remark", + header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="비고" />, + cell: ({ row }) => row.original.remark || "-", + size: 150, + }, + ]; + } + + // 기본값 (fallback) + return []; }
\ No newline at end of file |
