summaryrefslogtreecommitdiff
path: root/lib/rfq-last/table/rfq-table-columns.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'lib/rfq-last/table/rfq-table-columns.tsx')
-rw-r--r--lib/rfq-last/table/rfq-table-columns.tsx1206
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