summaryrefslogtreecommitdiff
path: root/lib/itb/table/purchase-request-columns.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'lib/itb/table/purchase-request-columns.tsx')
-rw-r--r--lib/itb/table/purchase-request-columns.tsx380
1 files changed, 380 insertions, 0 deletions
diff --git a/lib/itb/table/purchase-request-columns.tsx b/lib/itb/table/purchase-request-columns.tsx
new file mode 100644
index 00000000..55321a21
--- /dev/null
+++ b/lib/itb/table/purchase-request-columns.tsx
@@ -0,0 +1,380 @@
+// components/purchase-requests/purchase-request-columns.tsx
+"use client";
+
+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,
+ Edit,
+ Trash2,
+ CheckCircle,
+ XCircle,
+ FileText,
+ Package,
+ Send,
+ Clock
+} from "lucide-react";
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuLabel,
+ DropdownMenuSeparator,
+ DropdownMenuTrigger,
+} from "@/components/ui/dropdown-menu";
+import { MoreHorizontal } from "lucide-react";
+import { DataTableColumnHeaderSimple } from "@/components/data-table/data-table-column-simple-header";
+import { format } from "date-fns";
+import { ko } from "date-fns/locale";
+import type { DataTableRowAction } from "@/types/table";
+import type { PurchaseRequestView } from "@/db/schema";
+
+interface GetColumnsProps {
+ setRowAction: React.Dispatch<React.SetStateAction<DataTableRowAction<PurchaseRequestView> | null>>;
+}
+
+const statusConfig = {
+ "작성중": {
+ label: "작성중",
+ variant: "secondary" as const,
+ icon: Edit,
+ color: "text-gray-500"
+ },
+ "요청완료": {
+ label: "요청완료",
+ variant: "default" as const,
+ icon: Send,
+ color: "text-blue-500"
+ },
+ "검토중": {
+ label: "검토중",
+ variant: "warning" as const,
+ icon: Clock,
+ color: "text-yellow-500"
+ },
+ "승인": {
+ label: "승인",
+ variant: "success" as const,
+ icon: CheckCircle,
+ color: "text-green-500"
+ },
+ "반려": {
+ label: "반려",
+ variant: "destructive" as const,
+ icon: XCircle,
+ color: "text-red-500"
+ },
+ "RFQ생성완료": {
+ label: "RFQ생성완료",
+ variant: "outline" as const,
+ icon: FileText,
+ color: "text-purple-500"
+ },
+};
+
+export function getPurchaseRequestColumns({
+ setRowAction
+}: GetColumnsProps): ColumnDef<PurchaseRequestView>[] {
+ return [
+ {
+ id: "select",
+ header: ({ table }) => (
+ <Checkbox
+ checked={
+ table.getIsAllPageRowsSelected() ||
+ (table.getIsSomePageRowsSelected() && "indeterminate")
+ }
+ onCheckedChange={(value) => table.toggleAllPageRowsSelected(!!value)}
+ aria-label="Select all"
+ />
+ ),
+ cell: ({ row }) => (
+ <Checkbox
+ checked={row.getIsSelected()}
+ onCheckedChange={(value) => row.toggleSelected(!!value)}
+ aria-label="Select row"
+ />
+ ),
+ enableSorting: false,
+ enableHiding: false,
+ size: 40,
+ },
+ {
+ accessorKey: "requestCode",
+ header: ({ column }) => (
+ <DataTableColumnHeaderSimple column={column} title="요청번호" />
+ ),
+ cell: ({ row }) => (
+ <Button
+ variant="ghost"
+ className="h-auto p-0 font-mono font-medium text-primary hover:underline"
+ onClick={() => setRowAction({ row, type: "view" })}
+ >
+ {row.getValue("requestCode")}
+ </Button>
+ ),
+ size: 130,
+ },
+ {
+ accessorKey: "status",
+ header: ({ column }) => (
+ <DataTableColumnHeaderSimple column={column} title="상태" />
+ ),
+ cell: ({ row }) => {
+ const status = row.getValue("status") as keyof typeof statusConfig;
+ const config = statusConfig[status];
+ const Icon = config?.icon;
+
+ return (
+ <Badge variant={config?.variant} className="font-medium">
+ {Icon && <Icon className={`mr-1 h-3 w-3 ${config.color}`} />}
+ {config?.label || status}
+ </Badge>
+ );
+ },
+ filterFn: (row, id, value) => {
+ return value.includes(row.getValue(id));
+ },
+ size: 120,
+ },
+ {
+ accessorKey: "requestTitle",
+ header: ({ column }) => (
+ <DataTableColumnHeaderSimple column={column} title="요청제목" />
+ ),
+ cell: ({ row }) => (
+ <div className="max-w-[300px]">
+ <div className="truncate font-medium" title={row.getValue("requestTitle")}>
+ {row.getValue("requestTitle")}
+ </div>
+ {row.original.requestDescription && (
+ <div className="truncate text-xs text-muted-foreground mt-0.5"
+ title={row.original.requestDescription}>
+ {row.original.requestDescription}
+ </div>
+ )}
+ </div>
+ ),
+ size: 300,
+ },
+ {
+ accessorKey: "projectName",
+ header: ({ column }) => (
+ <DataTableColumnHeaderSimple column={column} title="프로젝트" />
+ ),
+ cell: ({ row }) => (
+ <div className="flex flex-col space-y-0.5">
+ {row.original.projectCode && (
+ <span className="font-mono text-xs text-muted-foreground">
+ {row.original.projectCode}
+ </span>
+ )}
+ <span className="truncate text-sm" title={row.original.projectName}>
+ {row.original.projectName || "-"}
+ </span>
+ </div>
+ ),
+ size: 180,
+ },
+ {
+ accessorKey: "packageName",
+ header: ({ column }) => (
+ <DataTableColumnHeaderSimple column={column} title="패키지" />
+ ),
+ cell: ({ row }) => (
+ <div className="flex flex-col space-y-0.5">
+ {row.original.packageNo && (
+ <span className="font-mono text-xs text-muted-foreground">
+ {row.original.packageNo}
+ </span>
+ )}
+ <span className="truncate text-sm" title={row.original.packageName}>
+ {row.original.packageName || "-"}
+ </span>
+ </div>
+ ),
+ size: 150,
+ },
+ {
+ id: "attachments",
+ header: "첨부",
+ cell: ({ row }) => {
+ const count = Number(row.original.attachmentCount) || 0;
+ return count > 0 ? (
+ <Button
+ variant="ghost"
+ size="sm"
+ className="h-8 px-2"
+ onClick={() => setRowAction({ row, type: "attachments" })}
+ >
+ <FileText className="h-4 w-4 mr-1" />
+ {count}
+ </Button>
+ ) : (
+ <span className="text-muted-foreground text-sm">-</span>
+ );
+ },
+ size: 70,
+ },
+ {
+ id: "items",
+ header: "품목",
+ cell: ({ row }) => {
+ const count = row.original.itemCount || 0;
+ const totalQuantity = row.original.totalQuantity;
+ return count > 0 ? (
+ <Button
+ variant="ghost"
+ size="sm"
+ className="h-8 px-2"
+ onClick={() => setRowAction({ row, type: "items" })}
+ >
+ <Package className="h-4 w-4 mr-1" />
+ <div className="flex flex-col items-start">
+ <span>{count}종</span>
+ {totalQuantity && (
+ <span className="text-xs text-muted-foreground">
+ 총 {totalQuantity}개
+ </span>
+ )}
+ </div>
+ </Button>
+ ) : (
+ <span className="text-muted-foreground text-sm">-</span>
+ );
+ },
+ size: 90,
+ },
+ {
+ accessorKey: "engPicName",
+ header: ({ column }) => (
+ <DataTableColumnHeaderSimple column={column} title="설계담당" />
+ ),
+ cell: ({ row }) => (
+ <div className="text-sm">
+ {row.getValue("engPicName") || "-"}
+ </div>
+ ),
+ size: 100,
+ },
+ {
+ accessorKey: "purchasePicName",
+ header: ({ column }) => (
+ <DataTableColumnHeaderSimple column={column} title="구매담당" />
+ ),
+ cell: ({ row }) => (
+ <div className="text-sm">
+ {row.getValue("purchasePicName") || "-"}
+ </div>
+ ),
+ size: 100,
+ },
+ {
+ accessorKey: "estimatedBudget",
+ header: ({ column }) => (
+ <DataTableColumnHeaderSimple column={column} title="예상예산" />
+ ),
+ cell: ({ row }) => (
+ <div className="text-sm font-mono">
+ {row.getValue("estimatedBudget") || "-"}
+ </div>
+ ),
+ size: 100,
+ },
+ {
+ accessorKey: "requestedDeliveryDate",
+ header: ({ column }) => (
+ <DataTableColumnHeaderSimple column={column} title="희망납기" />
+ ),
+ cell: ({ row }) => {
+ const date = row.getValue("requestedDeliveryDate") as Date | null;
+ return (
+ <div className="text-sm">
+ {date ? format(new Date(date), "yyyy-MM-dd", { locale: ko }) : "-"}
+ </div>
+ );
+ },
+ size: 100,
+ },
+ {
+ accessorKey: "createdAt",
+ header: ({ column }) => (
+ <DataTableColumnHeaderSimple column={column} title="등록일" />
+ ),
+ cell: ({ row }) => {
+ const date = row.getValue("createdAt") as Date;
+ return (
+ <div className="text-sm text-muted-foreground">
+ {date ? format(new Date(date), "yyyy-MM-dd", { locale: ko }) : "-"}
+ </div>
+ );
+ },
+ size: 100,
+ },
+ {
+ id: "actions",
+ header: "작업",
+ cell: ({ row }) => {
+ const status = row.original.status;
+
+ return (
+ <DropdownMenu>
+ <DropdownMenuTrigger asChild>
+ <Button variant="ghost" className="h-8 w-8 p-0">
+ <span className="sr-only">메뉴 열기</span>
+ <MoreHorizontal className="h-4 w-4" />
+ </Button>
+ </DropdownMenuTrigger>
+ <DropdownMenuContent align="end">
+ <DropdownMenuLabel>작업</DropdownMenuLabel>
+ <DropdownMenuItem
+ onClick={() => setRowAction({ row, type: "view" })}
+ >
+ <Eye className="mr-2 h-4 w-4" />
+ 상세보기
+ </DropdownMenuItem>
+ <DropdownMenuSeparator />
+
+
+
+ <DropdownMenuItem
+ onClick={() => setRowAction({ row, type: "update" })}
+ >
+ <Edit className="mr-2 h-4 w-4" />
+ 수정
+ </DropdownMenuItem>
+ <DropdownMenuItem
+ onClick={() => setRowAction({ row, type: "delete" })}
+ className="text-destructive"
+ >
+ <Trash2 className="mr-2 h-4 w-4" />
+ 삭제
+ </DropdownMenuItem>
+
+
+
+
+{status ==="작성중" &&
+<>
+ <DropdownMenuSeparator />
+ <DropdownMenuItem
+ onClick={() => setRowAction({ row, type: "createRfq" })}
+ className="text-primary"
+ >
+ <FileText className="mr-2 h-4 w-4" />
+ RFQ 생성
+ </DropdownMenuItem>
+ </>
+ }
+
+ </DropdownMenuContent>
+ </DropdownMenu>
+
+ );
+ },
+ size: 80,
+ },
+ ];
+} \ No newline at end of file