diff options
| author | joonhoekim <26rote@gmail.com> | 2025-03-25 15:55:45 +0900 |
|---|---|---|
| committer | joonhoekim <26rote@gmail.com> | 2025-03-25 15:55:45 +0900 |
| commit | 1a2241c40e10193c5ff7008a7b7b36cc1d855d96 (patch) | |
| tree | 8a5587f10ca55b162d7e3254cb088b323a34c41b /lib/rfqs/vendor-table/vendor-list | |
initial commit
Diffstat (limited to 'lib/rfqs/vendor-table/vendor-list')
| -rw-r--r-- | lib/rfqs/vendor-table/vendor-list/vendor-list-table-column.tsx | 154 | ||||
| -rw-r--r-- | lib/rfqs/vendor-table/vendor-list/vendor-list-table.tsx | 142 |
2 files changed, 296 insertions, 0 deletions
diff --git a/lib/rfqs/vendor-table/vendor-list/vendor-list-table-column.tsx b/lib/rfqs/vendor-table/vendor-list/vendor-list-table-column.tsx new file mode 100644 index 00000000..bfcbe75b --- /dev/null +++ b/lib/rfqs/vendor-table/vendor-list/vendor-list-table-column.tsx @@ -0,0 +1,154 @@ +"use client" +// Because columns rely on React state/hooks for row actions + +import * as React from "react" +import { ColumnDef, Row } from "@tanstack/react-table" +import { VendorData } from "./vendor-list-table" +import { ClientDataTableColumnHeaderSimple } from "@/components/client-data-table/data-table-column-simple-header" +import { formatDate } from "@/lib/utils" +import { Checkbox } from "@/components/ui/checkbox" + +export interface DataTableRowAction<TData> { + row: Row<TData> + type: "open" | "update" | "delete" +} + +interface GetColumnsProps { + setRowAction: React.Dispatch<React.SetStateAction<DataTableRowAction<VendorData> | null>> + setSelectedVendorIds: React.Dispatch<React.SetStateAction<number[]>> // Changed to array +} + +/** getColumns: return array of ColumnDef for 'vendors' data */ +export function getColumns({ + setRowAction, + setSelectedVendorIds, // Changed parameter name +}: GetColumnsProps): ColumnDef<VendorData>[] { + return [ + // MULTIPLE SELECT COLUMN + { + id: "select", + enableSorting: false, + enableHiding: false, + size: 40, + // Add checkbox in header for select all functionality + header: ({ table }) => ( + <Checkbox + checked={ + table.getFilteredSelectedRowModel().rows.length > 0 && + table.getFilteredSelectedRowModel().rows.length === table.getFilteredRowModel().rows.length + } + onCheckedChange={(checked) => { + table.toggleAllRowsSelected(!!checked) + + // Update selectedVendorIds based on all rows selection + if (checked) { + const allIds = table.getFilteredRowModel().rows.map(row => row.original.id) + setSelectedVendorIds(allIds) + } else { + setSelectedVendorIds([]) + } + }} + aria-label="Select all" + /> + ), + cell: ({ row }) => { + const isSelected = row.getIsSelected() + + return ( + <Checkbox + checked={isSelected} + onCheckedChange={(checked) => { + row.toggleSelected(!!checked) + + // Update the selectedVendorIds state by adding or removing this ID + setSelectedVendorIds(prevIds => { + if (checked) { + // Add this ID if it doesn't exist + return prevIds.includes(row.original.id) + ? prevIds + : [...prevIds, row.original.id] + } else { + // Remove this ID + return prevIds.filter(id => id !== row.original.id) + } + }) + }} + aria-label="Select row" + /> + ) + }, + }, + + // Vendor Name + { + accessorKey: "vendorName", + header: ({ column }) => ( + <ClientDataTableColumnHeaderSimple column={column} title="Vendor Name" /> + ), + cell: ({ row }) => row.getValue("vendorName"), + }, + + // Vendor Code + { + accessorKey: "vendorCode", + header: ({ column }) => ( + <ClientDataTableColumnHeaderSimple column={column} title="Vendor Code" /> + ), + cell: ({ row }) => row.getValue("vendorCode"), + }, + + // Status + { + accessorKey: "status", + header: ({ column }) => ( + <ClientDataTableColumnHeaderSimple column={column} title="Status" /> + ), + cell: ({ row }) => row.getValue("status"), + }, + + // Country + { + accessorKey: "country", + header: ({ column }) => ( + <ClientDataTableColumnHeaderSimple column={column} title="Country" /> + ), + cell: ({ row }) => row.getValue("country"), + }, + + // Email + { + accessorKey: "email", + header: ({ column }) => ( + <ClientDataTableColumnHeaderSimple column={column} title="Email" /> + ), + cell: ({ row }) => row.getValue("email"), + }, + + // Phone + { + accessorKey: "phone", + header: ({ column }) => ( + <ClientDataTableColumnHeaderSimple column={column} title="Phone" /> + ), + cell: ({ row }) => row.getValue("phone"), + }, + + // Created At + { + accessorKey: "createdAt", + header: ({ column }) => ( + <ClientDataTableColumnHeaderSimple column={column} title="Created At" /> + ), + cell: ({ cell }) => formatDate(cell.getValue() as Date), + }, + + // Updated At + { + accessorKey: "updatedAt", + header: ({ column }) => ( + <ClientDataTableColumnHeaderSimple column={column} title="Updated At" /> + ), + cell: ({ cell }) => formatDate(cell.getValue() as Date), + }, + ] +}
\ No newline at end of file diff --git a/lib/rfqs/vendor-table/vendor-list/vendor-list-table.tsx b/lib/rfqs/vendor-table/vendor-list/vendor-list-table.tsx new file mode 100644 index 00000000..c436eebd --- /dev/null +++ b/lib/rfqs/vendor-table/vendor-list/vendor-list-table.tsx @@ -0,0 +1,142 @@ +"use client" + +import * as React from "react" +import { ClientDataTable } from "@/components/client-data-table/data-table" +import { DataTableRowAction, getColumns } from "./vendor-list-table-column" +import { DataTableAdvancedFilterField } from "@/types/table" +import { addItemToVendors, getAllVendors } from "../../service" +import { Loader2, Plus } from "lucide-react" +import { Button } from "@/components/ui/button" +import { useToast } from "@/hooks/use-toast" + +export interface VendorData { + id: number + vendorName: string + vendorCode: string | null + taxId: string + address: string | null + country: string | null + phone: string | null + email: string | null + website: string | null + status: string + createdAt: Date + updatedAt: Date +} + +interface VendorsListTableProps { + rfqId: number +} + +export function VendorsListTable({ rfqId }: VendorsListTableProps) { + const { toast } = useToast() + const [rowAction, setRowAction] = + React.useState<DataTableRowAction<VendorData> | null>(null) + + // Changed to array for multiple selection + const [selectedVendorIds, setSelectedVendorIds] = React.useState<number[]>([]) + const [isSubmitting, setIsSubmitting] = React.useState(false) + + const columns = React.useMemo( + () => getColumns({ setRowAction, setSelectedVendorIds }), + [setRowAction, setSelectedVendorIds] + ) + + const [vendors, setVendors] = React.useState<VendorData[]>([]) + const [isLoading, setIsLoading] = React.useState(false) + + React.useEffect(() => { + async function loadAllVendors() { + setIsLoading(true) + try { + const allVendors = await getAllVendors() + setVendors(allVendors) + } catch (error) { + console.error("벤더 목록 로드 오류:", error) + toast({ + title: "Error", + description: "Failed to load vendors", + variant: "destructive", + }) + } finally { + setIsLoading(false) + } + } + loadAllVendors() + }, [toast]) + + const advancedFilterFields: DataTableAdvancedFilterField<VendorData>[] = [] + + async function handleAddVendors() { + if (selectedVendorIds.length === 0) return // Safety check + + setIsSubmitting(true) + try { + // Update to use the multiple vendor service + const result = await addItemToVendors(rfqId, selectedVendorIds) + + if (result.success) { + toast({ + title: "Success", + description: `Added items to ${selectedVendorIds.length} vendors`, + }) + // Reset selection after successful addition + setSelectedVendorIds([]) + } else { + toast({ + title: "Error", + description: result.error || "Failed to add items to vendors", + variant: "destructive", + }) + } + } catch (err) { + console.error("Failed to add vendors:", err) + toast({ + title: "Error", + description: "An unexpected error occurred", + variant: "destructive", + }) + } finally { + setIsSubmitting(false) + } + } + + // If loading, show a flex container that fills the parent and centers the spinner + if (isLoading) { + return ( + <div className="flex h-full w-full items-center justify-center"> + <Loader2 className="h-8 w-8 animate-spin text-muted-foreground" /> + </div> + ) + } + + // Otherwise, show the table + return ( + <ClientDataTable + data={vendors} + columns={columns} + advancedFilterFields={advancedFilterFields} + > + <div className="flex items-center gap-2"> + <Button + variant="default" + size="sm" + onClick={handleAddVendors} + disabled={selectedVendorIds.length === 0 || isSubmitting} + > + {isSubmitting ? ( + <> + <Loader2 className="mr-2 h-4 w-4 animate-spin" /> + Adding... + </> + ) : ( + <> + <Plus className="mr-2 h-4 w-4" /> + Add Vendors ({selectedVendorIds.length}) + </> + )} + </Button> + </div> + </ClientDataTable> + ) +}
\ No newline at end of file |
