[dyad] Added a data table to the Book Out page to display booked-out items - wrote 3 file(s)
This commit is contained in:
parent
3729a13e96
commit
f48d7fdb2f
3 changed files with 302 additions and 6 deletions
186
src/components/book-out/BookOutDataTable.tsx
Normal file
186
src/components/book-out/BookOutDataTable.tsx
Normal file
|
|
@ -0,0 +1,186 @@
|
|||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import {
|
||||
ColumnDef,
|
||||
ColumnFiltersState,
|
||||
SortingState,
|
||||
VisibilityState,
|
||||
flexRender,
|
||||
getCoreRowModel,
|
||||
getFilteredRowModel,
|
||||
getPaginationRowModel,
|
||||
getSortedRowModel,
|
||||
useReactTable,
|
||||
} from "@tanstack/react-table"
|
||||
import { Columns } from "lucide-react"
|
||||
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableHeader,
|
||||
TableRow,
|
||||
} from "@/components/ui/table"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { Input } from "@/components/ui/input"
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuCheckboxItem,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/components/ui/dropdown-menu"
|
||||
|
||||
interface BookOutDataTableProps<TData, TValue> {
|
||||
columns: ColumnDef<TData, TValue>[]
|
||||
data: TData[]
|
||||
}
|
||||
|
||||
export function BookOutDataTable<TData, TValue>({
|
||||
columns,
|
||||
data,
|
||||
}: BookOutDataTableProps<TData, TValue>) {
|
||||
const [sorting, setSorting] = React.useState<SortingState>([])
|
||||
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>([])
|
||||
const [columnVisibility, setColumnVisibility] = React.useState<VisibilityState>(() => {
|
||||
if (typeof window !== 'undefined' && window.innerWidth < 768) {
|
||||
return {
|
||||
sku: false,
|
||||
dateBookedOut: false,
|
||||
actions: false,
|
||||
};
|
||||
}
|
||||
return {};
|
||||
});
|
||||
const [rowSelection, setRowSelection] = React.useState({})
|
||||
|
||||
const table = useReactTable({
|
||||
data,
|
||||
columns,
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
getPaginationRowModel: getPaginationRowModel(),
|
||||
onSortingChange: setSorting,
|
||||
getSortedRowModel: getSortedRowModel(),
|
||||
onColumnFiltersChange: setColumnFilters,
|
||||
getFilteredRowModel: getFilteredRowModel(),
|
||||
onColumnVisibilityChange: setColumnVisibility,
|
||||
onRowSelectionChange: setRowSelection,
|
||||
state: {
|
||||
sorting,
|
||||
columnFilters,
|
||||
columnVisibility,
|
||||
rowSelection,
|
||||
},
|
||||
})
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="flex items-center py-4">
|
||||
<Input
|
||||
placeholder="Filter by customer name..."
|
||||
value={(table.getColumn("customerName")?.getFilterValue() as string) ?? ""}
|
||||
onChange={(event) =>
|
||||
table.getColumn("customerName")?.setFilterValue(event.target.value)
|
||||
}
|
||||
className="max-w-sm"
|
||||
/>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant="outline" size="icon" className="ml-auto">
|
||||
<Columns className="h-4 w-4" />
|
||||
<span className="sr-only">Toggle columns</span>
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end">
|
||||
{table
|
||||
.getAllColumns()
|
||||
.filter(
|
||||
(column) => column.getCanHide()
|
||||
)
|
||||
.map((column) => {
|
||||
return (
|
||||
<DropdownMenuCheckboxItem
|
||||
key={column.id}
|
||||
className="capitalize"
|
||||
checked={column.getIsVisible()}
|
||||
onCheckedChange={(value) =>
|
||||
column.toggleVisibility(!!value)
|
||||
}
|
||||
>
|
||||
{column.id}
|
||||
</DropdownMenuCheckboxItem>
|
||||
)
|
||||
})}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
<div className="rounded-md border bg-white">
|
||||
<Table>
|
||||
<TableHeader>
|
||||
{table.getHeaderGroups().map((headerGroup) => (
|
||||
<TableRow key={headerGroup.id}>
|
||||
{headerGroup.headers.map((header) => {
|
||||
return (
|
||||
<TableHead key={header.id}>
|
||||
{header.isPlaceholder
|
||||
? null
|
||||
: flexRender(
|
||||
header.column.columnDef.header,
|
||||
header.getContext()
|
||||
)}
|
||||
</TableHead>
|
||||
)
|
||||
})}
|
||||
</TableRow>
|
||||
))}
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{table.getRowModel().rows?.length ? (
|
||||
table.getRowModel().rows.map((row) => (
|
||||
<TableRow
|
||||
key={row.id}
|
||||
data-state={row.getIsSelected() && "selected"}
|
||||
>
|
||||
{row.getVisibleCells().map((cell) => (
|
||||
<TableCell key={cell.id}>
|
||||
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
||||
</TableCell>
|
||||
))}
|
||||
</TableRow>
|
||||
))
|
||||
) : (
|
||||
<TableRow>
|
||||
<TableCell colSpan={columns.length} className="h-24 text-center">
|
||||
No results.
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
<div className="flex items-center justify-end space-x-2 py-4">
|
||||
<div className="flex-1 text-sm text-muted-foreground">
|
||||
{table.getFilteredSelectedRowModel().rows.length} of{" "}
|
||||
{table.getFilteredRowModel().rows.length} row(s) selected.
|
||||
</div>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => table.previousPage()}
|
||||
disabled={!table.getCanPreviousPage()}
|
||||
>
|
||||
Previous
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => table.nextPage()}
|
||||
disabled={!table.getCanNextPage()}
|
||||
>
|
||||
Next
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
84
src/components/book-out/columns.tsx
Normal file
84
src/components/book-out/columns.tsx
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
"use client"
|
||||
|
||||
import { ColumnDef } from "@tanstack/react-table"
|
||||
import { MoreHorizontal } from "lucide-react"
|
||||
|
||||
import { Button } from "@/components/ui/button"
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuLabel,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/components/ui/dropdown-menu"
|
||||
|
||||
export type BookOutRecord = {
|
||||
id: string
|
||||
customerName: string
|
||||
itemName: string
|
||||
sku: string
|
||||
quantity: number
|
||||
dateBookedOut: string
|
||||
}
|
||||
|
||||
export const columns: ColumnDef<BookOutRecord>[] = [
|
||||
{
|
||||
accessorKey: "customerName",
|
||||
header: "Customer",
|
||||
},
|
||||
{
|
||||
accessorKey: "itemName",
|
||||
header: "Item Name",
|
||||
},
|
||||
{
|
||||
accessorKey: "sku",
|
||||
header: "SKU",
|
||||
},
|
||||
{
|
||||
accessorKey: "quantity",
|
||||
header: "Quantity",
|
||||
},
|
||||
{
|
||||
accessorKey: "dateBookedOut",
|
||||
header: "Date Booked Out",
|
||||
cell: ({ row }) => {
|
||||
const date = new Date(row.getValue("dateBookedOut"));
|
||||
const formattedDate = date.toLocaleDateString("en-ZA", {
|
||||
year: "numeric",
|
||||
month: "short",
|
||||
day: "numeric",
|
||||
});
|
||||
return <div className="font-medium">{formattedDate}</div>;
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "actions",
|
||||
enableHiding: false,
|
||||
cell: ({ row }) => {
|
||||
const record = row.original
|
||||
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant="ghost" className="h-8 w-8 p-0">
|
||||
<span className="sr-only">Open menu</span>
|
||||
<MoreHorizontal className="h-4 w-4" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end">
|
||||
<DropdownMenuLabel>Actions</DropdownMenuLabel>
|
||||
<DropdownMenuItem
|
||||
onClick={() => navigator.clipboard.writeText(record.id)}
|
||||
>
|
||||
Copy record ID
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem>View Details</DropdownMenuItem>
|
||||
{/* Add more actions like 'Edit' or 'Delete' if needed */}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
)
|
||||
},
|
||||
},
|
||||
]
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
import { useState } from "react";
|
||||
import { useState, useEffect, useCallback } from "react";
|
||||
import { BookOutForm } from "../components/book-out/BookOutForm";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { PlusCircle } from "lucide-react";
|
||||
|
|
@ -10,13 +10,42 @@ import {
|
|||
DialogTitle,
|
||||
DialogTrigger,
|
||||
} from "@/components/ui/dialog";
|
||||
import { columns, BookOutRecord } from "../components/book-out/columns";
|
||||
import { BookOutDataTable } from "../components/book-out/BookOutDataTable";
|
||||
import pb from "@/lib/pocketbase";
|
||||
|
||||
const BookOut = () => {
|
||||
const [bookOutOpen, setBookOutOpen] = useState(false);
|
||||
const [bookOutRecords, setBookOutRecords] = useState<BookOutRecord[]>([]);
|
||||
const [refreshTrigger, setRefreshTrigger] = useState(0); // State to trigger data refresh
|
||||
|
||||
const fetchBookOutRecords = useCallback(async () => {
|
||||
try {
|
||||
const records = await pb.collection('book_out_records').getList(1, 50, {
|
||||
sort: '-created',
|
||||
expand: 'customer,items', // Expand related customer and stock items
|
||||
});
|
||||
|
||||
const formattedData: BookOutRecord[] = records.items.map((record: any) => ({
|
||||
id: record.id,
|
||||
customerName: record.expand?.customer?.comapny_name || 'N/A',
|
||||
itemName: record.expand?.items?.name || 'N/A',
|
||||
sku: record.expand?.items?.sku || 'N/A',
|
||||
quantity: record.quantity,
|
||||
dateBookedOut: record.date_booked_out,
|
||||
}));
|
||||
setBookOutRecords(formattedData);
|
||||
} catch (error) {
|
||||
console.error("Error fetching book out records:", error);
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
fetchBookOutRecords();
|
||||
}, [fetchBookOutRecords, refreshTrigger]);
|
||||
|
||||
const handleRefreshData = () => {
|
||||
setRefreshTrigger(prev => prev + 1); // Increment to trigger any future data fetching
|
||||
setRefreshTrigger(prev => prev + 1); // Increment to trigger useEffect
|
||||
};
|
||||
|
||||
return (
|
||||
|
|
@ -44,10 +73,7 @@ const BookOut = () => {
|
|||
</DialogContent>
|
||||
</Dialog>
|
||||
</div>
|
||||
{/* Placeholder for a table or list of booked out items, if needed later */}
|
||||
<div className="mt-8 p-4 border rounded-md text-gray-600">
|
||||
No booked out items recorded yet. Use the "Book Out Item" button to add one.
|
||||
</div>
|
||||
<BookOutDataTable columns={columns} data={bookOutRecords} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue