diff options
Diffstat (limited to 'lib/procurement-rfqs/vendor-response/table')
| -rw-r--r-- | lib/procurement-rfqs/vendor-response/table/vendor-quotations-table-columns.tsx | 239 | ||||
| -rw-r--r-- | lib/procurement-rfqs/vendor-response/table/vendor-quotations-table.tsx | 145 |
2 files changed, 384 insertions, 0 deletions
diff --git a/lib/procurement-rfqs/vendor-response/table/vendor-quotations-table-columns.tsx b/lib/procurement-rfqs/vendor-response/table/vendor-quotations-table-columns.tsx new file mode 100644 index 00000000..9eecc72f --- /dev/null +++ b/lib/procurement-rfqs/vendor-response/table/vendor-quotations-table-columns.tsx @@ -0,0 +1,239 @@ +"use client" + +import * as React from "react" +import { type DataTableRowAction } from "@/types/table" +import { type ColumnDef } from "@tanstack/react-table" +import { Ellipsis, FileText, Pencil, Edit, Trash2 } from "lucide-react" +import { formatCurrency, formatDate, formatDateTime } from "@/lib/utils" +import { Badge } from "@/components/ui/badge" +import { Button } from "@/components/ui/button" +import { Checkbox } from "@/components/ui/checkbox" +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuSeparator, + DropdownMenuShortcut, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu" +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "@/components/ui/tooltip" +import { DataTableColumnHeaderSimple } from "@/components/data-table/data-table-column-simple-header" +import Link from "next/link" +import { ProcurementVendorQuotations } from "@/db/schema" +import { useRouter } from "next/navigation" + +// 상태에 따른 배지 컴포넌트 +function StatusBadge({ status }: { status: string }) { + switch (status) { + case "Draft": + return <Badge variant="outline">초안</Badge> + case "Submitted": + return <Badge variant="default">제출됨</Badge> + case "Revised": + return <Badge variant="secondary">수정됨</Badge> + case "Rejected": + return <Badge variant="destructive">반려됨</Badge> + case "Accepted": + return <Badge variant="default">승인됨</Badge> + default: + return <Badge>{status}</Badge> + } +} + +interface QuotationWithRfqCode extends ProcurementVendorQuotations { + rfqCode?: string; + rfq?: { + rfqCode?: string; + dueDate?: Date | string | null; + + } | null; +} + +type NextRouter = ReturnType<typeof useRouter>; + +interface GetColumnsProps { + setRowAction: React.Dispatch< + React.SetStateAction<DataTableRowAction<QuotationWithRfqCode> | null> + > + router: NextRouter +} + +/** + * tanstack table 컬럼 정의 + */ +export function getColumns({ + setRowAction, + router, +}: GetColumnsProps): ColumnDef<QuotationWithRfqCode>[] { + // ---------------------------------------------------------------- + // 1) select 컬럼 (체크박스) + // ---------------------------------------------------------------- + const selectColumn: ColumnDef<QuotationWithRfqCode> = { + 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, + } + + + // ---------------------------------------------------------------- + // 3) 일반 컬럼들 + // ---------------------------------------------------------------- + + // 견적서 액션 컬럼 (아이콘 버튼으로 변경) + const quotationActionColumn: ColumnDef<QuotationWithRfqCode> = { + id: "actions", + enableHiding: false, + cell: ({ row }) => { + const id = row.original.id + const code = row.getValue("quotationCode") as string + const tooltipText = `${code} 작성하기` + + return ( + <TooltipProvider> + <Tooltip> + <TooltipTrigger asChild> + <Button + variant="ghost" + size="icon" + onClick={() => router.push(`/partners/rfq-all/${id}`)} + className="h-8 w-8" + > + <Edit className="h-4 w-4" /> + <span className="sr-only">견적서 작성</span> + </Button> + </TooltipTrigger> + <TooltipContent> + <p>{tooltipText}</p> + </TooltipContent> + </Tooltip> + </TooltipProvider> + ) + }, + size: 50, // 아이콘으로 변경했으므로 크기 줄임 + } + + // RFQ 번호 컬럼 + const rfqCodeColumn: ColumnDef<QuotationWithRfqCode> = { + accessorKey: "quotationCode", + header: ({ column }) => ( + <DataTableColumnHeaderSimple column={column} title="RFQ 번호" /> + ), + cell: ({ row }) => row.original.quotationCode || "-", + size: 150, + } + + // RFQ 버전 컬럼 + const quotationVersionColumn: ColumnDef<QuotationWithRfqCode> = { + accessorKey: "quotationVersion", + header: ({ column }) => ( + <DataTableColumnHeaderSimple column={column} title="RFQ 버전" /> + ), + cell: ({ row }) => row.original.quotationVersion || "-", + size: 100, + } + + const dueDateColumn: ColumnDef<QuotationWithRfqCode> = { + accessorKey: "dueDate", + header: ({ column }) => ( + <DataTableColumnHeaderSimple column={column} title="RFQ 마감일" /> + ), + cell: ({ row }) => { + // 타입 단언 사용 + const rfq = row.original.rfq as any; + const date = rfq?.dueDate as string | null; + return date ? formatDateTime(new Date(date)) : "-"; + }, + size: 100, + } + + // 상태 컬럼 + const statusColumn: ColumnDef<QuotationWithRfqCode> = { + accessorKey: "status", + header: ({ column }) => ( + <DataTableColumnHeaderSimple column={column} title="상태" /> + ), + cell: ({ row }) => <StatusBadge status={row.getValue("status") as string} />, + size: 100, + } + + // 총액 컬럼 + const totalPriceColumn: ColumnDef<QuotationWithRfqCode> = { + accessorKey: "totalPrice", + header: ({ column }) => ( + <DataTableColumnHeaderSimple column={column} title="총액" /> + ), + cell: ({ row }) => { + const price = parseFloat(row.getValue("totalPrice") as string || "0") + const currency = row.original.currency + + return formatCurrency(price, currency) + }, + size: 120, + } + + // 제출일 컬럼 + const submittedAtColumn: ColumnDef<QuotationWithRfqCode> = { + accessorKey: "submittedAt", + header: ({ column }) => ( + <DataTableColumnHeaderSimple column={column} title="제출일" /> + ), + cell: ({ row }) => { + const date = row.getValue("submittedAt") as string | null + return date ? formatDate(new Date(date)) : "-" + }, + size: 120, + } + + // 유효기간 컬럼 + const validUntilColumn: ColumnDef<QuotationWithRfqCode> = { + accessorKey: "validUntil", + header: ({ column }) => ( + <DataTableColumnHeaderSimple column={column} title="유효기간" /> + ), + cell: ({ row }) => { + const date = row.getValue("validUntil") as string | null + return date ? formatDate(new Date(date)) : "-" + }, + size: 120, + } + + // ---------------------------------------------------------------- + // 4) 최종 컬럼 배열 + // ---------------------------------------------------------------- + return [ + selectColumn, + rfqCodeColumn, + quotationVersionColumn, + dueDateColumn, + statusColumn, + totalPriceColumn, + submittedAtColumn, + validUntilColumn, + quotationActionColumn // 이름을 변경하고 마지막에 배치 + ] +}
\ No newline at end of file diff --git a/lib/procurement-rfqs/vendor-response/table/vendor-quotations-table.tsx b/lib/procurement-rfqs/vendor-response/table/vendor-quotations-table.tsx new file mode 100644 index 00000000..92bda337 --- /dev/null +++ b/lib/procurement-rfqs/vendor-response/table/vendor-quotations-table.tsx @@ -0,0 +1,145 @@ +// lib/vendor-quotations/vendor-quotations-table.tsx +"use client" + +import * as React from "react" +import { type DataTableAdvancedFilterField, type DataTableFilterField, type DataTableRowAction } from "@/types/table" +import { useDataTable } from "@/hooks/use-data-table" +import { DataTable } from "@/components/data-table/data-table" +import { DataTableAdvancedToolbar } from "@/components/data-table/data-table-advanced-toolbar" +import { Button } from "@/components/ui/button" +import { ProcurementVendorQuotations } from "@/db/schema" +import { useRouter } from "next/navigation" +import { getColumns } from "./vendor-quotations-table-columns" + +interface QuotationWithRfqCode extends ProcurementVendorQuotations { + rfqCode?: string; + rfq?: { + rfqCode?: string; + } | null; +} + +interface VendorQuotationsTableProps { + promises: Promise<[{ data: ProcurementVendorQuotations[], pageCount: number }]>; +} + +export function VendorQuotationsTable({ promises }: VendorQuotationsTableProps) { + const [{ data, pageCount }] = React.use(promises); + const router = useRouter(); + + console.log(data ,"data") + + // 선택된 행 액션 상태 + const [rowAction, setRowAction] = React.useState<DataTableRowAction<QuotationWithRfqCode> | null>(null); + + // 테이블 컬럼 정의 + const columns = React.useMemo(() => getColumns({ + setRowAction, + router, + }), [setRowAction, router]); + + // 상태별 견적서 수 계산 + const statusCounts = React.useMemo(() => { + return { + Draft: data.filter(q => q.status === "Draft").length, + Submitted: data.filter(q => q.status === "Submitted").length, + Revised: data.filter(q => q.status === "Revised").length, + Rejected: data.filter(q => q.status === "Rejected").length, + Accepted: data.filter(q => q.status === "Accepted").length, + }; + }, [data]); + + // 필터 필드 + const filterFields: DataTableFilterField<QuotationWithRfqCode>[] = [ + { + id: "status", + label: "상태", + options: [ + { label: "초안", value: "Draft", count: statusCounts.Draft }, + { label: "제출됨", value: "Submitted", count: statusCounts.Submitted }, + { label: "수정됨", value: "Revised", count: statusCounts.Revised }, + { label: "반려됨", value: "Rejected", count: statusCounts.Rejected }, + { label: "승인됨", value: "Accepted", count: statusCounts.Accepted }, + ] + }, + { + id: "quotationCode", + label: "견적서 번호", + placeholder: "견적서 번호 검색...", + }, + { + id: "rfqCode", + label: "RFQ 번호", + placeholder: "RFQ 번호 검색...", + } + ]; + + // 고급 필터 필드 + const advancedFilterFields: DataTableAdvancedFilterField<QuotationWithRfqCode>[] = [ + { + id: "quotationCode", + label: "견적서 번호", + type: "text", + }, + { + id: "rfqCode", + label: "RFQ 번호", + type: "text", + }, + { + id: "status", + label: "상태", + type: "multi-select", + options: [ + { label: "초안", value: "Draft" }, + { label: "제출됨", value: "Submitted" }, + { label: "수정됨", value: "Revised" }, + { label: "반려됨", value: "Rejected" }, + { label: "승인됨", value: "Accepted" }, + ], + }, + { + id: "validUntil", + label: "유효기간", + type: "date", + }, + { + id: "submittedAt", + label: "제출일", + type: "date", + }, + ]; + + // useDataTable 훅 사용 + const { table } = useDataTable({ + data, + columns, + pageCount, + filterFields, + enablePinning: true, + enableAdvancedFilter: true, + initialState: { + sorting: [{ id: "updatedAt", desc: true }], + columnPinning: { right: ["actions"] }, + }, + getRowId: (originalRow) => String(originalRow.id), + shallow: false, + clearOnDefault: true, + }); + + return ( + <div style={{ maxWidth: '100vw' }}> + <DataTable + table={table} + > + <DataTableAdvancedToolbar + table={table} + filterFields={advancedFilterFields} + shallow={false} + > + </DataTableAdvancedToolbar> + </DataTable> + + + </div> + ); +}
\ No newline at end of file |
