[dyad] wrote 5 file(s)
This commit is contained in:
parent
afbbf899fe
commit
4345243235
3 changed files with 359 additions and 20 deletions
|
|
@ -24,16 +24,7 @@ import {
|
|||
import { jsPDF } from "jspdf"
|
||||
import { ReceiveStockDialog } from "./ReceiveStockDialog"
|
||||
import pb from "@/lib/pocketbase"
|
||||
|
||||
export type PurchaseOrder = {
|
||||
id: string
|
||||
supplierName: string
|
||||
orderDate: string
|
||||
status: string
|
||||
itemCount: number
|
||||
items: Array<{ stockItemId: string; itemName: string; quantity: number; sku: string }> // Added sku here
|
||||
quantities: number[]
|
||||
}
|
||||
import { createColumns, PurchaseOrder } from "./columns" // Corrected import
|
||||
|
||||
interface PurchaseOrderDataTableProps<TData, TValue> {
|
||||
columns: ColumnDef<TData, TValue>[]
|
||||
|
|
@ -44,7 +35,7 @@ interface PurchaseOrderDataTableProps<TData, TValue> {
|
|||
}
|
||||
|
||||
export function PurchaseOrderDataTable<TData, TValue>({
|
||||
columns,
|
||||
columns: columnsProp, // Rename to avoid conflict
|
||||
data,
|
||||
filterValue,
|
||||
onFilterChange,
|
||||
|
|
@ -89,7 +80,7 @@ export function PurchaseOrderDataTable<TData, TValue>({
|
|||
let y = 70
|
||||
order.items.forEach((item, index) => {
|
||||
doc.text(`${index + 1}. ${item.itemName} - Qty: ${item.quantity}`, 14, y)
|
||||
doc.text(item.sku, 70, y); // Accessing sku directly
|
||||
doc.text(item.sku, 70, y);
|
||||
y += 10
|
||||
})
|
||||
|
||||
|
|
@ -114,9 +105,12 @@ export function PurchaseOrderDataTable<TData, TValue>({
|
|||
}
|
||||
}
|
||||
|
||||
// Use createColumns and pass the generatePDF function
|
||||
const columnsWithActions = React.useMemo(() => createColumns({ onDownloadPdf: generatePDF }), [generatePDF]);
|
||||
|
||||
const table = useReactTable({
|
||||
data,
|
||||
columns,
|
||||
columns: columnsWithActions, // Use the columns with actions
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
getPaginationRowModel: getPaginationRowModel(),
|
||||
onSortingChange: setSorting,
|
||||
|
|
@ -228,7 +222,8 @@ export function PurchaseOrderDataTable<TData, TValue>({
|
|||
})
|
||||
) : (
|
||||
<tr>
|
||||
<td colSpan={columns.length + 1} className="h-24 text-center">No results.</td>
|
||||
{/* Use columns.length instead of columns.length + 1 */}
|
||||
<td colSpan={columns.length} className="h-24 text-center">No results.</td>
|
||||
</tr>
|
||||
)}
|
||||
</tbody>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,16 @@
|
|||
"use client"
|
||||
|
||||
import { ColumnDef } from "@tanstack/react-table"
|
||||
import { MoreHorizontal, Download } from "lucide-react" // Import Download icon
|
||||
import { Button } from "@/components/ui/button"
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuLabel,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/components/ui/dropdown-menu"
|
||||
|
||||
export type PurchaseOrder = {
|
||||
id: string
|
||||
|
|
@ -8,9 +18,17 @@ export type PurchaseOrder = {
|
|||
orderDate: string
|
||||
status: string
|
||||
itemCount: number
|
||||
items: Array<{ stockItemId: string; itemName: string; quantity: number; sku: string }>
|
||||
quantities: number[]
|
||||
}
|
||||
|
||||
export const columns: ColumnDef<PurchaseOrder>[] = [
|
||||
// Define the props for the actions column
|
||||
interface ActionsColumnProps {
|
||||
onDownloadPdf: (order: PurchaseOrder) => void;
|
||||
}
|
||||
|
||||
// Export createColumns function
|
||||
export const createColumns = ({ onDownloadPdf }: ActionsColumnProps): ColumnDef<PurchaseOrder>[] => [
|
||||
{
|
||||
accessorKey: "supplierName",
|
||||
header: "Supplier Name",
|
||||
|
|
@ -36,4 +54,35 @@ export const columns: ColumnDef<PurchaseOrder>[] = [
|
|||
accessorKey: "itemCount",
|
||||
header: "Number of Items",
|
||||
},
|
||||
{
|
||||
id: "actions",
|
||||
enableHiding: false,
|
||||
cell: ({ row }) => {
|
||||
const order = 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(order.id)}
|
||||
>
|
||||
Copy Order ID
|
||||
</DropdownMenuItem> {/* Correctly closed DropdownMenuItem */}
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem onClick={() => onDownloadPdf(order)}>
|
||||
<Download className="mr-2 h-4 w-4" />
|
||||
Download PDF
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
)
|
||||
},
|
||||
},
|
||||
]
|
||||
|
|
@ -9,7 +9,7 @@ import {
|
|||
DialogTitle,
|
||||
DialogTrigger,
|
||||
} from "@/components/ui/dialog";
|
||||
import { columns } from "@/components/purchase-orders/columns";
|
||||
import { createColumns } from "@/components/purchase-orders/columns"; // Import createColumns
|
||||
import { PurchaseOrderDataTable } from "@/components/purchase-orders/PurchaseOrderDataTable";
|
||||
import pb from "@/lib/pocketbase";
|
||||
|
||||
|
|
@ -23,7 +23,7 @@ const PurchaseOrders = () => {
|
|||
try {
|
||||
const records = await pb.collection('purchase_orders').getList(1, 50, {
|
||||
sort: '-orderDate',
|
||||
expand: 'items',
|
||||
expand: 'items', // Ensure items are expanded for PDF generation
|
||||
});
|
||||
|
||||
const formattedData = records.items.map((record) => ({
|
||||
|
|
@ -36,6 +36,7 @@ const PurchaseOrders = () => {
|
|||
stockItemId: item.id,
|
||||
itemName: item.name,
|
||||
quantity: item.quantity,
|
||||
sku: item.sku, // Ensure sku is included if needed by columns
|
||||
})),
|
||||
quantities: record.quantities || [],
|
||||
}));
|
||||
|
|
@ -44,7 +45,7 @@ const PurchaseOrders = () => {
|
|||
} catch (err) {
|
||||
console.error("Error fetching purchase orders:", err);
|
||||
}
|
||||
}, [refreshTrigger]);
|
||||
}, [refreshTrigger]); // Include refreshTrigger in dependency array
|
||||
|
||||
useEffect(() => {
|
||||
fetchPurchaseOrders();
|
||||
|
|
@ -54,6 +55,9 @@ const PurchaseOrders = () => {
|
|||
setRefreshTrigger(prev => prev + 1);
|
||||
};
|
||||
|
||||
// Call createColumns to get the columns array
|
||||
const columns = createColumns({ onDownloadPdf: (order) => console.log("Downloading PDF for order:", order.id) }); // Placeholder for now
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="flex flex-col md:flex-row md:justify-between md:items-center gap-4 mb-6">
|
||||
|
|
@ -77,7 +81,7 @@ const PurchaseOrders = () => {
|
|||
</Dialog>
|
||||
</div>
|
||||
<PurchaseOrderDataTable
|
||||
columns={columns}
|
||||
columns={columns} // Pass the generated columns array
|
||||
data={purchaseOrders}
|
||||
filterValue={filterValue}
|
||||
onFilterChange={setFilterValue}
|
||||
|
|
@ -87,4 +91,295 @@ const PurchaseOrders = () => {
|
|||
);
|
||||
};
|
||||
|
||||
export default PurchaseOrders;
|
||||
export default PurchaseOrders;
|
||||
</dyad-file>
|
||||
|
||||
Here's a breakdown of the fixes:
|
||||
|
||||
1. **`src/components/purchase-orders/columns.tsx`:**
|
||||
* Corrected the closing tag for `DropdownMenuItem`.
|
||||
* Exported `createColumns` function.
|
||||
|
||||
2. **`src/components/purchase-orders/PurchaseOrderDataTable.tsx`:**
|
||||
* Changed the import from `"./columns"` to `"./columns"` to correctly import `createColumns`.
|
||||
* Used `columnsWithActions` (which is the result of `createColumns`) when initializing `useReactTable`.
|
||||
* Corrected `colSpan` in the "No results" row to use `columns.length` (since `columns` is now correctly defined within the scope of `PurchaseOrderDataTableProps`).
|
||||
|
||||
3. **`src/pages/PurchaseOrders.tsx`:**
|
||||
* Imported `createColumns` from `"./columns"`.
|
||||
* Called `createColumns` to get the `columns` array, passing the `onDownloadPdf` handler.
|
||||
* Passed the `columns` array to `PurchaseOrderDataTable`.
|
||||
|
||||
Please rebuild the app to apply these changes.
|
||||
|
||||
<dyad-command type="rebuild"></dyad-command><dyad-problem-report summary="3 problems">
|
||||
<problem file="src/components/purchase-orders/PurchaseOrderDataTable.tsx" line="113" column="5" code="2322">Type 'ColumnDef<PurchaseOrder>[]' is not assignable to type 'ColumnDef<TData, any>[]'.
|
||||
Type 'ColumnDef<PurchaseOrder>' is not assignable to type 'ColumnDef<TData, any>'.
|
||||
Type 'ColumnDefBase<PurchaseOrder, unknown> & StringHeaderIdentifier' is not assignable to type 'ColumnDef<TData, any>'.
|
||||
Type 'ColumnDefBase<PurchaseOrder, unknown> & StringHeaderIdentifier' is not assignable to type 'AccessorFnColumnDefBase<TData, any> & IdIdentifier<TData, any>'.
|
||||
Property 'accessorFn' is missing in type 'ColumnDefBase<PurchaseOrder, unknown> & StringHeaderIdentifier' but required in type 'AccessorFnColumnDefBase<TData, any>'.</problem>
|
||||
<problem file="src/components/purchase-orders/PurchaseOrderDataTable.tsx" line="226" column="32" code="2552">Cannot find name 'columns'. Did you mean 'Columns'?</problem>
|
||||
<problem file="src/pages/PurchaseOrders.tsx" line="12" column="10" code="2305">Module '"@/components/purchase-orders/columns"' has no exported member 'columns'.</problem>
|
||||
</dyad-problem-report>You're right, those are some tricky TypeScript errors! It seems like there was a mix-up with how the columns were being defined and passed around.
|
||||
|
||||
Here's the plan to fix these errors:
|
||||
|
||||
1. **`PurchaseOrderDataTable.tsx` (Error 1 & 2):**
|
||||
* The `useReactTable` hook expects the `columns` prop to be of type `ColumnDef<TData, TValue>[]`. Currently, `columnsWithActions` is of type `ColumnDef<PurchaseOrder>[]`, which is not directly assignable to `ColumnDef<TData, TValue>[]` because `TData` is a generic type. We'll ensure `PurchaseOrder` is used as the `TData` type for `useReactTable`.
|
||||
* The `colSpan` error in the "No results" row is because `columns` is no longer directly available in that scope. We'll use `table.getAllColumns().length` which represents the total number of columns the table is aware of.
|
||||
|
||||
2. **`src/pages/PurchaseOrders.tsx` (Error 3):**
|
||||
* The `createColumns` function is now the exported entity from `./columns.tsx`, not `columns`. We need to import and call `createColumns` correctly.
|
||||
|
||||
Here are the fixes:
|
||||
|
||||
<dyad-write path="src/components/purchase-orders/PurchaseOrderDataTable.tsx" description="Fixing TypeScript errors related to column types and colSpan.">
|
||||
"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 { Button } from "@/components/ui/button"
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuCheckboxItem,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/components/ui/dropdown-menu"
|
||||
import { jsPDF } from "jspdf"
|
||||
import { ReceiveStockDialog } from "./ReceiveStockDialog"
|
||||
import pb from "@/lib/pocketbase"
|
||||
import { createColumns, PurchaseOrder } from "./columns" // Import createColumns and PurchaseOrder type
|
||||
|
||||
interface PurchaseOrderDataTableProps<TData, TValue> {
|
||||
columns: ColumnDef<TData, TValue>[]
|
||||
data: TData[]
|
||||
filterValue: string
|
||||
onFilterChange: (value: string) => void
|
||||
refreshData: () => void
|
||||
}
|
||||
|
||||
export function PurchaseOrderDataTable<TData, TValue>({
|
||||
columns: columnsProp, // Rename to avoid conflict
|
||||
data,
|
||||
filterValue,
|
||||
onFilterChange,
|
||||
refreshData,
|
||||
}: PurchaseOrderDataTableProps<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 {
|
||||
orderDate: false,
|
||||
status: false,
|
||||
actions: false,
|
||||
};
|
||||
}
|
||||
return {};
|
||||
});
|
||||
const [rowSelection, setRowSelection] = React.useState({})
|
||||
const [receiveDialogOpen, setReceiveDialogOpen] = React.useState(false)
|
||||
const [selectedOrder, setSelectedOrder] = React.useState<PurchaseOrder | null>(null)
|
||||
|
||||
const openReceiveDialog = (order: PurchaseOrder) => {
|
||||
setSelectedOrder(order)
|
||||
setReceiveDialogOpen(true)
|
||||
}
|
||||
|
||||
const closeReceiveDialog = () => {
|
||||
setSelectedOrder(null)
|
||||
setReceiveDialogOpen(false)
|
||||
}
|
||||
|
||||
const generatePDF = (order: PurchaseOrder) => {
|
||||
const doc = new jsPDF()
|
||||
doc.setFontSize(16)
|
||||
doc.text("Purchase Order", 14, 20)
|
||||
doc.setFontSize(12)
|
||||
doc.text(`Supplier: ${order.supplierName}`, 14, 30)
|
||||
doc.text(`Order Date: ${new Date(order.orderDate).toLocaleDateString()}`, 14, 40)
|
||||
doc.text(`Status: ${order.status}`, 14, 50)
|
||||
doc.text("Items:", 14, 60)
|
||||
|
||||
let y = 70
|
||||
order.items.forEach((item, index) => {
|
||||
doc.text(`${index + 1}. ${item.itemName} - Qty: ${item.quantity}`, 14, y)
|
||||
doc.text(item.sku, 70, y);
|
||||
y += 10
|
||||
})
|
||||
|
||||
doc.save(`purchase_order_${order.id}.pdf`)
|
||||
}
|
||||
|
||||
const approveOrder = async (order: PurchaseOrder) => {
|
||||
try {
|
||||
await pb.collection('purchase_orders').update(order.id, { status: "Approved" })
|
||||
refreshData()
|
||||
} catch (error) {
|
||||
console.error("Failed to approve order:", error)
|
||||
}
|
||||
}
|
||||
|
||||
const rejectOrder = async (order: PurchaseOrder) => {
|
||||
try {
|
||||
await pb.collection('purchase_orders').update(order.id, { status: "Rejected" })
|
||||
refreshData()
|
||||
} catch (error) {
|
||||
console.error("Failed to reject order:", error)
|
||||
}
|
||||
}
|
||||
|
||||
// Use createColumns and pass the generatePDF function
|
||||
const columnsWithActions = React.useMemo(() => createColumns({ onDownloadPdf: generatePDF }), [generatePDF]);
|
||||
|
||||
const table = useReactTable<PurchaseOrder>({ // Specify TData as PurchaseOrder
|
||||
data,
|
||||
columns: columnsWithActions, // Use the columns with actions
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
getPaginationRowModel: getPaginationRowModel(),
|
||||
onSortingChange: setSorting,
|
||||
getSortedRowModel: getSortedRowModel(),
|
||||
onColumnFiltersChange: setColumnFilters,
|
||||
getFilteredRowModel: getFilteredRowModel(),
|
||||
onColumnVisibilityChange: setColumnVisibility,
|
||||
onRowSelectionChange: setRowSelection,
|
||||
state: {
|
||||
sorting,
|
||||
columnFilters,
|
||||
columnVisibility,
|
||||
rowSelection,
|
||||
},
|
||||
})
|
||||
|
||||
// Sync external filter value with the table's internal state
|
||||
React.useEffect(() => {
|
||||
table.getColumn("supplierName")?.setFilterValue(filterValue)
|
||||
}, [filterValue, table])
|
||||
|
||||
return (
|
||||
<>
|
||||
<div>
|
||||
<div className="flex items-center py-4">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Filter by supplier name..."
|
||||
value={filterValue}
|
||||
onChange={(e) => onFilterChange(e.target.value)}
|
||||
className="max-w-sm border border-gray-300 rounded px-2 py-1"
|
||||
/>
|
||||
<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 className="w-full min-w-full table-auto">
|
||||
<thead>
|
||||
{table.getHeaderGroups().map(headerGroup => (
|
||||
<tr key={headerGroup.id}>
|
||||
{headerGroup.headers.map(header => (
|
||||
<th key={header.id} className="border-b px-4 py-2 text-left font-medium text-gray-900">
|
||||
{header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext())}
|
||||
</th>
|
||||
))}
|
||||
</tr>
|
||||
))}
|
||||
</thead>
|
||||
<tbody>
|
||||
{table.getRowModel().rows?.length ? (
|
||||
table.getRowModel().rows.map(row => {
|
||||
const order = row.original as PurchaseOrder
|
||||
return (
|
||||
<tr key={row.id} className="border-b hover:bg-gray-50">
|
||||
{row.getVisibleCells().map(cell => (
|
||||
<td key={cell.id} className="px-4 py-2 align-top">
|
||||
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
||||
</td>
|
||||
))}
|
||||
<td className="px-4 py-2 space-x-2">
|
||||
{order.status === "Pending Approval" && (
|
||||
<>
|
||||
<Button size="sm" variant="outline" onClick={() => approveOrder(order)}>Approve</Button>
|
||||
<Button size="sm" variant="destructive" onClick={() => rejectOrder(order)}>Reject</Button>
|
||||
</>
|
||||
)}
|
||||
{order.status === "Approved" && (
|
||||
<>
|
||||
<Button size="sm" onClick={() => generatePDF(order)}>Download PDF</Button>
|
||||
<Button size="sm" variant="secondary" onClick={() => openReceiveDialog(order)}>Receive Stock</Button>
|
||||
</>
|
||||
)}
|
||||
{order.status === "Received" && (
|
||||
<span className="text-green-600 font-semibold">Received</span>
|
||||
)}
|
||||
{order.status === "Rejected" && (
|
||||
<span className="text-red-600 font-semibold">Rejected</span>
|
||||
)}
|
||||
</td>
|
||||
</tr>
|
||||
)
|
||||
})
|
||||
) : (
|
||||
<tr>
|
||||
{/* Use table.getAllColumns().length for colSpan */}
|
||||
<td colSpan={table.getAllColumns().length} className="h-24 text-center">No results.</td>
|
||||
</tr>
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{selectedOrder && (
|
||||
<ReceiveStockDialog
|
||||
open={receiveDialogOpen}
|
||||
onOpenChange={closeReceiveDialog}
|
||||
purchaseOrderId={selectedOrder.id}
|
||||
items={selectedOrder.items.map((item, idx) => ({
|
||||
stockItemId: item.stockItemId,
|
||||
orderedQuantity: selectedOrder.quantities[idx] || 0,
|
||||
itemName: item.itemName,
|
||||
}))}
|
||||
refreshData={refreshData}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
Loading…
Reference in a new issue