summaryrefslogtreecommitdiff
path: root/components/po-rfq/detail-table/rfq-detail-column.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'components/po-rfq/detail-table/rfq-detail-column.tsx')
-rw-r--r--components/po-rfq/detail-table/rfq-detail-column.tsx369
1 files changed, 369 insertions, 0 deletions
diff --git a/components/po-rfq/detail-table/rfq-detail-column.tsx b/components/po-rfq/detail-table/rfq-detail-column.tsx
new file mode 100644
index 00000000..31f251ce
--- /dev/null
+++ b/components/po-rfq/detail-table/rfq-detail-column.tsx
@@ -0,0 +1,369 @@
+"use client"
+
+import * as React from "react"
+import type { ColumnDef, Row } from "@tanstack/react-table";
+import { formatDate, formatDateTime } from "@/lib/utils"
+import { DataTableColumnHeaderSimple } from "@/components/data-table/data-table-column-simple-header"
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuRadioGroup,
+ DropdownMenuRadioItem,
+ DropdownMenuSeparator,
+ DropdownMenuShortcut,
+ DropdownMenuSub,
+ DropdownMenuSubContent,
+ DropdownMenuSubTrigger,
+ DropdownMenuTrigger,
+} from "@/components/ui/dropdown-menu";
+import { Ellipsis, MessageCircle } from "lucide-react";
+import { Button } from "@/components/ui/button";
+import { Badge } from "@/components/ui/badge";
+
+export interface DataTableRowAction<TData> {
+ row: Row<TData>;
+ type: "delete" | "update" | "communicate"; // communicate 타입 추가
+}
+
+// procurementRfqDetailsView 타입 정의 (DB 스키마에 맞게 조정 필요)
+export interface RfqDetailView {
+ detailId: number
+ rfqId: number
+ rfqCode: string
+ vendorId?: number | null // 벤더 ID 필드 추가
+ projectCode: string | null
+ projectName: string | null
+ vendorCountry: string | null
+ itemCode: string | null
+ itemName: string | null
+ vendorName: string | null
+ vendorCode: string | null
+ currency: string | null
+ paymentTermsCode: string | null
+ paymentTermsDescription: string | null
+ incotermsCode: string | null
+ incotermsDescription: string | null
+ incotermsDetail: string | null
+ deliveryDate: Date | null
+ taxCode: string | null
+ placeOfShipping: string | null
+ placeOfDestination: string | null
+ materialPriceRelatedYn: boolean | null
+ hasQuotation: boolean | null
+ updatedByUserName: string | null
+ quotationStatus: string | null
+ updatedAt: Date | null
+ prItemsCount: number
+ majorItemsCount: number
+ quotationVersion:number | null
+ // 커뮤니케이션 관련 필드 추가
+ commentCount?: number // 전체 코멘트 수
+ unreadCount?: number // 읽지 않은 코멘트 수
+ lastCommentDate?: Date // 마지막 코멘트 날짜
+}
+
+interface GetColumnsProps<TData> {
+ setRowAction: React.Dispatch<
+ React.SetStateAction<DataTableRowAction<TData> | null>
+ >;
+ unreadMessages?: Record<number, number>; // 벤더 ID별 읽지 않은 메시지 수
+}
+
+export function getRfqDetailColumns({
+ setRowAction,
+ unreadMessages = {},
+}: GetColumnsProps<RfqDetailView>): ColumnDef<RfqDetailView>[] {
+ return [
+ {
+ accessorKey: "quotationStatus",
+ header: ({ column }) => (
+ <DataTableColumnHeaderSimple column={column} title="견적 상태" />
+ ),
+ cell: ({ row }) => <div>{row.getValue("quotationStatus")}</div>,
+ meta: {
+ excelHeader: "견적 상태"
+ },
+ enableResizing: true,
+ size: 120,
+ },
+ {
+ accessorKey: "quotationVersion",
+ header: ({ column }) => (
+ <DataTableColumnHeaderSimple column={column} title="견적 버전" />
+ ),
+ cell: ({ row }) => <div>{row.getValue("quotationVersion")}</div>,
+ meta: {
+ excelHeader: "견적 버전"
+ },
+ enableResizing: true,
+ size: 120,
+ },
+ {
+ accessorKey: "vendorCode",
+ header: ({ column }) => (
+ <DataTableColumnHeaderSimple column={column} title="벤더 코드" />
+ ),
+ cell: ({ row }) => <div>{row.getValue("vendorCode")}</div>,
+ meta: {
+ excelHeader: "벤더 코드"
+ },
+ enableResizing: true,
+ size: 120,
+ },
+ {
+ accessorKey: "vendorName",
+ header: ({ column }) => (
+ <DataTableColumnHeaderSimple column={column} title="벤더명" />
+ ),
+ cell: ({ row }) => <div>{row.getValue("vendorName")}</div>,
+ meta: {
+ excelHeader: "벤더명"
+ },
+ enableResizing: true,
+ size: 160,
+ },
+ {
+ accessorKey: "vendorType",
+ header: ({ column }) => (
+ <DataTableColumnHeaderSimple column={column} title="내외자" />
+ ),
+ cell: ({ row }) => <div>{row.original.vendorCountry === "KR"?"D":"F"}</div>,
+ meta: {
+ excelHeader: "내외자"
+ },
+ enableResizing: true,
+ size: 80,
+ },
+ {
+ accessorKey: "currency",
+ header: ({ column }) => (
+ <DataTableColumnHeaderSimple column={column} title="통화" />
+ ),
+ cell: ({ row }) => <div>{row.getValue("currency")}</div>,
+ meta: {
+ excelHeader: "통화"
+ },
+ enableResizing: true,
+ size: 80,
+ },
+ {
+ accessorKey: "paymentTermsCode",
+ header: ({ column }) => (
+ <DataTableColumnHeaderSimple column={column} title="지불 조건 코드" />
+ ),
+ cell: ({ row }) => <div>{row.getValue("paymentTermsCode")}</div>,
+ meta: {
+ excelHeader: "지불 조건 코드"
+ },
+ enableResizing: true,
+ size: 140,
+ },
+ {
+ accessorKey: "paymentTermsDescription",
+ header: ({ column }) => (
+ <DataTableColumnHeaderSimple column={column} title="지불 조건" />
+ ),
+ cell: ({ row }) => <div>{row.getValue("paymentTermsDescription")}</div>,
+ meta: {
+ excelHeader: "지불 조건"
+ },
+ enableResizing: true,
+ size: 160,
+ },
+ {
+ accessorKey: "incotermsCode",
+ header: ({ column }) => (
+ <DataTableColumnHeaderSimple column={column} title="인코텀스 코드" />
+ ),
+ cell: ({ row }) => <div>{row.getValue("incotermsCode")}</div>,
+ meta: {
+ excelHeader: "인코텀스 코드"
+ },
+ enableResizing: true,
+ size: 140,
+ },
+ {
+ accessorKey: "incotermsDescription",
+ header: ({ column }) => (
+ <DataTableColumnHeaderSimple column={column} title="인코텀스" />
+ ),
+ cell: ({ row }) => <div>{row.getValue("incotermsDescription")}</div>,
+ meta: {
+ excelHeader: "인코텀스"
+ },
+ enableResizing: true,
+ size: 160,
+ },
+ {
+ accessorKey: "incotermsDetail",
+ header: ({ column }) => (
+ <DataTableColumnHeaderSimple column={column} title="인코텀스 상세" />
+ ),
+ cell: ({ row }) => <div>{row.getValue("incotermsDetail")}</div>,
+ meta: {
+ excelHeader: "인코텀스 상세"
+ },
+ enableResizing: true,
+ size: 160,
+ },
+ {
+ accessorKey: "deliveryDate",
+ header: ({ column }) => (
+ <DataTableColumnHeaderSimple column={column} title="납품일" />
+ ),
+ cell: ({ cell }) => {
+ const value = cell.getValue();
+ return value ? formatDate(value as Date, "KR") : "";
+ },
+ meta: {
+ excelHeader: "납품일"
+ },
+ enableResizing: true,
+ size: 120,
+ },
+ {
+ accessorKey: "taxCode",
+ header: ({ column }) => (
+ <DataTableColumnHeaderSimple column={column} title="세금 코드" />
+ ),
+ cell: ({ row }) => <div>{row.getValue("taxCode")}</div>,
+ meta: {
+ excelHeader: "세금 코드"
+ },
+ enableResizing: true,
+ size: 120,
+ },
+ {
+ accessorKey: "placeOfShipping",
+ header: ({ column }) => (
+ <DataTableColumnHeaderSimple column={column} title="선적지" />
+ ),
+ cell: ({ row }) => <div>{row.getValue("placeOfShipping")}</div>,
+ meta: {
+ excelHeader: "선적지"
+ },
+ enableResizing: true,
+ size: 140,
+ },
+ {
+ accessorKey: "placeOfDestination",
+ header: ({ column }) => (
+ <DataTableColumnHeaderSimple column={column} title="도착지" />
+ ),
+ cell: ({ row }) => <div>{row.getValue("placeOfDestination")}</div>,
+ meta: {
+ excelHeader: "도착지"
+ },
+ enableResizing: true,
+ size: 140,
+ },
+ {
+ accessorKey: "materialPriceRelatedYn",
+ header: ({ column }) => (
+ <DataTableColumnHeaderSimple column={column} title="원자재 가격 연동" />
+ ),
+ cell: ({ row }) => <div>{row.getValue("materialPriceRelatedYn") ? "Y" : "N"}</div>,
+ meta: {
+ excelHeader: "원자재 가격 연동"
+ },
+ enableResizing: true,
+ size: 140,
+ },
+ {
+ accessorKey: "updatedByUserName",
+ header: ({ column }) => (
+ <DataTableColumnHeaderSimple column={column} title="수정자" />
+ ),
+ cell: ({ row }) => <div>{row.getValue("updatedByUserName")}</div>,
+ meta: {
+ excelHeader: "수정자"
+ },
+ enableResizing: true,
+ size: 120,
+ },
+ {
+ accessorKey: "updatedAt",
+ header: ({ column }) => (
+ <DataTableColumnHeaderSimple column={column} title="수정일시" />
+ ),
+ cell: ({ cell }) => {
+ const value = cell.getValue();
+ return value ? formatDateTime(value as Date, "KR") : "";
+ },
+ meta: {
+ excelHeader: "수정일시"
+ },
+ enableResizing: true,
+ size: 140,
+ },
+ // 커뮤니케이션 컬럼 추가
+ {
+ id: "communication",
+ header: ({ column }) => (
+ <DataTableColumnHeaderSimple column={column} title="커뮤니케이션" />
+ ),
+ cell: ({ row }) => {
+ const vendorId = row.original.vendorId || 0;
+ const unreadCount = unreadMessages[vendorId] || 0;
+
+ return (
+ <Button
+ variant="ghost"
+ size="sm"
+ className="relative p-0 h-8 w-8 flex items-center justify-center"
+ onClick={() => setRowAction({ row, type: "communicate" })}
+ >
+ <MessageCircle className="h-4 w-4" />
+ {unreadCount > 0 && (
+ <Badge
+ variant="destructive"
+ className="absolute -top-1 -right-1 h-5 w-5 flex items-center justify-center p-0 text-xs"
+ >
+ {unreadCount}
+ </Badge>
+ )}
+ </Button>
+ );
+ },
+ enableResizing: false,
+ size: 80,
+ },
+ {
+ id: "actions",
+ enableHiding: false,
+ cell: function Cell({ row }) {
+ const [isUpdatePending, startUpdateTransition] = React.useTransition()
+
+ return (
+ <DropdownMenu>
+ <DropdownMenuTrigger asChild>
+ <Button
+ aria-label="Open menu"
+ variant="ghost"
+ className="flex size-7 p-0 data-[state=open]:bg-muted"
+ >
+ <Ellipsis className="size-4" aria-hidden="true" />
+ </Button>
+ </DropdownMenuTrigger>
+ <DropdownMenuContent align="end" className="w-40">
+ <DropdownMenuItem
+ onSelect={() => setRowAction({ row, type: "update" })}
+ >
+ Edit
+ </DropdownMenuItem>
+
+ <DropdownMenuItem
+ onSelect={() => setRowAction({ row, type: "delete" })}
+ >
+ Delete
+ <DropdownMenuShortcut>⌘⌫</DropdownMenuShortcut>
+ </DropdownMenuItem>
+ </DropdownMenuContent>
+ </DropdownMenu>
+ )
+ },
+ size: 40,
+ }
+ ]
+} \ No newline at end of file