summaryrefslogtreecommitdiff
path: root/lib/tbe-last/vendor/tbe-table-columns.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'lib/tbe-last/vendor/tbe-table-columns.tsx')
-rw-r--r--lib/tbe-last/vendor/tbe-table-columns.tsx335
1 files changed, 335 insertions, 0 deletions
diff --git a/lib/tbe-last/vendor/tbe-table-columns.tsx b/lib/tbe-last/vendor/tbe-table-columns.tsx
new file mode 100644
index 00000000..6e40fe27
--- /dev/null
+++ b/lib/tbe-last/vendor/tbe-table-columns.tsx
@@ -0,0 +1,335 @@
+// lib/vendor-rfq-response/vendor-tbe-table/tbe-table-columns.tsx
+
+"use client"
+
+import * as React from "react"
+import { type ColumnDef } from "@tanstack/react-table"
+import { FileText, ListChecks, Eye } from "lucide-react"
+import { Badge } from "@/components/ui/badge"
+import { Button } from "@/components/ui/button"
+import { Checkbox } from "@/components/ui/checkbox"
+import { DataTableColumnHeaderSimple } from "@/components/data-table/data-table-column-simple-header"
+import { formatDate } from "@/lib/utils"
+import { TbeLastView } from "@/db/schema"
+
+interface GetColumnsProps {
+ onOpenEvaluationView: (session: TbeLastView) => void;
+ onOpenDocuments: (sessionId: number) => void;
+ onOpenPrItems: (rfqId: number) => void;
+}
+
+export function getColumns({
+ onOpenEvaluationView,
+ onOpenDocuments,
+ onOpenPrItems,
+}: GetColumnsProps): ColumnDef<TbeLastView>[] {
+
+ const columns: ColumnDef<TbeLastView>[] = [
+ // Select Column
+ {
+ id: "select",
+ header: ({ table }) => (
+ <Checkbox
+ checked={
+ table.getIsAllPageRowsSelected() ||
+ (table.getIsSomePageRowsSelected() && "indeterminate")
+ }
+ onCheckedChange={(value) => table.toggleAllPageRowsSelected(!!value)}
+ aria-label="Select all"
+ className="translate-y-0.5"
+ />
+ ),
+ cell: ({ row }) => (
+ <Checkbox
+ checked={row.getIsSelected()}
+ onCheckedChange={(value) => row.toggleSelected(!!value)}
+ aria-label="Select row"
+ className="translate-y-0.5"
+ />
+ ),
+ size: 40,
+ enableSorting: false,
+ enableHiding: false,
+ },
+
+ // TBE Session Code
+ {
+ accessorKey: "sessionCode",
+ header: ({ column }) => (
+ <DataTableColumnHeaderSimple column={column} title="TBE Code" />
+ ),
+ cell: ({ row }) => {
+ const sessionCode = row.original.sessionCode;
+ return (
+ <span className="font-medium">{sessionCode}</span>
+ );
+ },
+ size: 120,
+ },
+
+ // RFQ Code
+ {
+ accessorKey: "rfqCode",
+ header: ({ column }) => (
+ <DataTableColumnHeaderSimple column={column} title="RFQ Code" />
+ ),
+ cell: ({ row }) => row.original.rfqCode,
+ size: 120,
+ },
+
+ // RFQ Title
+ {
+ accessorKey: "rfqTitle",
+ header: ({ column }) => (
+ <DataTableColumnHeaderSimple column={column} title="RFQ Title" />
+ ),
+ cell: ({ row }) => row.original.rfqTitle || "-",
+ size: 200,
+ },
+
+ // RFQ Due Date
+ {
+ accessorKey: "rfqDueDate",
+ header: ({ column }) => (
+ <DataTableColumnHeaderSimple column={column} title="Due Date" />
+ ),
+ cell: ({ row }) => {
+ const date = row.original.rfqDueDate;
+ if (!date) return "-";
+
+ const daysUntilDue = Math.floor((new Date(date).getTime() - Date.now()) / (1000 * 60 * 60 * 24));
+ const isOverdue = daysUntilDue < 0;
+ const isUrgent = daysUntilDue <= 3 && daysUntilDue >= 0;
+
+ return (
+ <div className="flex flex-col">
+ <span className={`text-sm ${isOverdue ? 'text-red-600' : isUrgent ? 'text-orange-600' : ''}`}>
+ {formatDate(date, "KR")}
+ </span>
+ {isOverdue && (
+ <span className="text-xs text-red-600">Overdue</span>
+ )}
+ {isUrgent && (
+ <span className="text-xs text-orange-600">{daysUntilDue}일 남음</span>
+ )}
+ </div>
+ );
+ },
+ size: 100,
+ },
+
+ // Package Info
+ {
+ accessorKey: "packageNo",
+ header: ({ column }) => (
+ <DataTableColumnHeaderSimple column={column} title="Package" />
+ ),
+ cell: ({ row }) => {
+ const packageNo = row.original.packageNo;
+ const packageName = row.original.packageName;
+
+ if (!packageNo) return "-";
+
+ return (
+ <div className="flex flex-col">
+ <span className="font-medium">{packageNo}</span>
+ {packageName && (
+ <span className="text-xs text-muted-foreground">{packageName}</span>
+ )}
+ </div>
+ );
+ },
+ size: 150,
+ },
+
+ // Project Info
+ {
+ accessorKey: "projectCode",
+ header: ({ column }) => (
+ <DataTableColumnHeaderSimple column={column} title="Project" />
+ ),
+ cell: ({ row }) => {
+ const projectCode = row.original.projectCode;
+ const projectName = row.original.projectName;
+
+ if (!projectCode) return "-";
+
+ return (
+ <div className="flex flex-col">
+ <span className="font-medium">{projectCode}</span>
+ {projectName && (
+ <span className="text-xs text-muted-foreground">{projectName}</span>
+ )}
+ </div>
+ );
+ },
+ size: 150,
+ },
+
+ // 구매담당자
+ {
+ accessorKey: "picName",
+ header: ({ column }) => (
+ <DataTableColumnHeaderSimple column={column} title="구매담당자" />
+ ),
+ cell: ({ row }) => row.original.picName || "-",
+ size: 120,
+ },
+
+ // TBE Status
+ {
+ accessorKey: "sessionStatus",
+ header: ({ column }) => (
+ <DataTableColumnHeaderSimple column={column} title="Status" />
+ ),
+ cell: ({ row }) => {
+ const status = row.original.sessionStatus;
+
+ let variant: "default" | "secondary" | "outline" | "destructive" = "outline";
+
+ switch (status) {
+ case "준비중":
+ variant = "outline";
+ break;
+ case "진행중":
+ variant = "default";
+ break;
+ case "검토중":
+ variant = "secondary";
+ break;
+ case "완료":
+ variant = "default";
+ break;
+ case "보류":
+ variant = "destructive";
+ break;
+ }
+
+ return <Badge variant={variant}>{status}</Badge>;
+ },
+ size: 100,
+ },
+
+ // Evaluation Result
+ {
+ accessorKey: "evaluationResult",
+ header: ({ column }) => (
+ <DataTableColumnHeaderSimple column={column} title="Evaluation" />
+ ),
+ cell: ({ row }) => {
+ const result = row.original.evaluationResult;
+ const session = row.original;
+
+ if (!result) {
+ return (
+ <Badge variant="outline" className="text-muted-foreground">
+ Pending
+ </Badge>
+ );
+ }
+
+ let variant: "default" | "secondary" | "destructive" = "default";
+ let displayText = result;
+
+ switch (result) {
+ case "Acceptable":
+ variant = "default";
+ displayText = "Acceptable";
+ break;
+ case "Acceptable with Comment":
+ variant = "secondary";
+ displayText = "Conditional";
+ break;
+ case "Not Acceptable":
+ variant = "destructive";
+ displayText = "Not Acceptable";
+ break;
+ }
+
+ return (
+ <div className="flex items-center gap-1">
+ <Badge variant={variant}>{displayText}</Badge>
+ {result && (
+ <Button
+ variant="ghost"
+ size="sm"
+ className="h-6 w-6 p-0"
+ onClick={() => onOpenEvaluationView(session)}
+ title="View evaluation details"
+ >
+ <Eye className="h-3 w-3" />
+ </Button>
+ )}
+ </div>
+ );
+ },
+ size: 150,
+ },
+
+ // PR Items
+ {
+ id: "prItems",
+ header: ({ column }) => (
+ <DataTableColumnHeaderSimple column={column} title="PR Items" />
+ ),
+ cell: ({ row }) => {
+ const rfqId = row.original.rfqId;
+ const totalCount = row.original.prItemsCount;
+ const majorCount = row.original.majorItemsCount;
+
+ return (
+ <Button
+ variant="ghost"
+ size="sm"
+ className="h-8 px-2"
+ onClick={() => onOpenPrItems(rfqId)}
+ >
+ <ListChecks className="h-4 w-4 mr-1" />
+ <span className="text-xs">
+ {totalCount} ({majorCount})
+ </span>
+ </Button>
+ );
+ },
+ size: 100,
+ enableSorting: false,
+ },
+
+ // Documents (클릭하면 Documents Sheet 열림)
+ {
+ id: "documents",
+ header: ({ column }) => (
+ <DataTableColumnHeaderSimple column={column} title="Documents" />
+ ),
+ cell: ({ row }) => {
+ const sessionId = row.original.tbeSessionId;
+ const buyerDocs = Number(row.original.buyerDocumentsCount);
+ const vendorDocs = Number(row.original.vendorDocumentsCount);
+ const totalDocs = buyerDocs + vendorDocs;
+ const status = row.original.sessionStatus;
+
+ // 진행중 상태면 강조
+ const isActive = status === "진행중";
+
+ return (
+ <Button
+ variant={isActive ? "default" : "ghost"}
+ size="sm"
+ className="h-8 px-2"
+ onClick={() => onOpenDocuments(sessionId)}
+ title={isActive ? "문서 관리 (업로드/코멘트 가능)" : "문서 조회"}
+ >
+ <FileText className="h-4 w-4 mr-1" />
+ <span className="text-xs">
+ {totalDocs} (B:{buyerDocs}/V:{vendorDocs})
+ </span>
+ </Button>
+ );
+ },
+ size: 140,
+ enableSorting: false,
+ },
+ ];
+
+ return columns;
+} \ No newline at end of file