diff options
Diffstat (limited to 'components/po-rfq/detail-table/rfq-detail-column.tsx')
| -rw-r--r-- | components/po-rfq/detail-table/rfq-detail-column.tsx | 369 |
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 |
