feat(purchase-orders): add edit functionality and improve form validation

- Implement EditPurchaseOrderDialog for modifying existing orders
- Enhance form validation with better number handling and error messages
- Add subtotal calculation to purchase order forms
- Improve PDF generation with logo support and better layout
- Update column definitions to include edit action
- Add className prop to StockItemSearch for better styling control
This commit is contained in:
davidels1 2026-01-14 09:01:49 +02:00
parent e304a0a283
commit 315a9c7759
11 changed files with 378 additions and 50 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
public/jeilogo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

View file

@ -0,0 +1,178 @@
"use client"
import * as React from "react"
import { zodResolver } from "@hookform/resolvers/zod"
import { useForm, useFieldArray } from "react-hook-form"
import * as z from "zod"
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog"
import { Button } from "@/components/ui/button"
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form"
import { Input } from "@/components/ui/input"
import pb from "@/lib/pocketbase"
import { PurchaseOrder } from "./columns"
import { showError, showSuccess } from "@/utils/toast"
const rowSchema = z.object({
stockItemId: z.string(),
itemName: z.string(),
quantity: z.coerce.number().int().min(1),
unitPriceGBP: z.coerce.number().min(0),
})
const formSchema = z.object({
rows: z.array(rowSchema),
})
interface EditPurchaseOrderDialogProps {
open: boolean
onOpenChange: (open: boolean) => void
order: PurchaseOrder
refreshData: () => void
}
export function EditPurchaseOrderDialog({ open, onOpenChange, order, refreshData }: EditPurchaseOrderDialogProps) {
const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
defaultValues: {
rows: order.items.map((item, idx) => ({
stockItemId: item.stockItemId,
itemName: item.itemName,
quantity: order.quantities[idx] ?? 1,
unitPriceGBP: Number(item.costPriceGBP ?? 0),
})),
},
})
const { fields } = useFieldArray({
control: form.control,
name: "rows",
})
const rowsValues = form.watch("rows")
const subtotalGBP = React.useMemo(
() =>
(rowsValues || []).reduce(
(sum, r) => sum + (Number(r.quantity || 0) * Number(r.unitPriceGBP || 0)),
0
),
[rowsValues]
)
const onSubmit = async (values: z.infer<typeof formSchema>) => {
try {
await pb.collection("purchase_orders").update(order.id, {
quantities: values.rows.map(r => r.quantity),
})
try {
await Promise.all(values.rows.map(async (r, idx) => {
const existingGBP = Number(order.items[idx].costPriceGBP ?? 0)
const newGBP = Number(r.unitPriceGBP || 0)
if (Number.isFinite(newGBP) && newGBP >= 0 && newGBP !== existingGBP) {
await pb.collection("stock_items").update(r.stockItemId, { cost_price_gbp: newGBP })
}
}))
} catch (e) {
console.error("Stock price update error:", e)
}
showSuccess("Purchase order updated")
refreshData()
onOpenChange(false)
} catch (e: any) {
console.error("Update order error:", e)
showError("Failed to update purchase order")
}
}
return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className="mx-4 sm:max-w-[800px] max-h-[90vh] overflow-y-auto">
<DialogHeader>
<DialogTitle>Edit Purchase Order</DialogTitle>
<DialogDescription>Order ID: {order.id}</DialogDescription>
</DialogHeader>
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
<div className="border p-4 rounded-md">
<h3 className="text-lg font-semibold mb-4">Items</h3>
{fields.map((field, index) => (
<div
key={field.id}
className="grid grid-cols-1 md:grid-cols-[minmax(220px,1fr),110px,140px] gap-4 mb-4 items-start"
>
<FormField
control={form.control}
name={`rows.${index}.itemName`}
render={({ field }) => (
<FormItem>
<FormLabel>Item</FormLabel>
<FormControl>
<Input readOnly {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name={`rows.${index}.quantity`}
render={({ field }) => (
<FormItem>
<FormLabel>Qty</FormLabel>
<FormControl>
<Input
type="text"
inputMode="numeric"
pattern="[0-9]*"
className="text-right md:w-28"
value={field.value ?? ""}
onChange={(e) => field.onChange(e.target.value.replace(/[^0-9]/g, ""))}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name={`rows.${index}.unitPriceGBP`}
render={({ field }) => (
<FormItem>
<FormLabel>Unit Price (£)</FormLabel>
<FormControl>
<Input
type="text"
inputMode="decimal"
pattern="^[0-9]*[.,]?[0-9]*$"
className="text-right md:w-36"
value={field.value ?? ""}
onChange={(e) => field.onChange(e.target.value.replace(",", "."))}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</div>
))}
<div className="mt-4 border-t pt-4">
<div className="flex items-center justify-end gap-6">
<div className="text-sm text-gray-600">Items total (GBP)</div>
<div className="text-lg font-semibold">£{subtotalGBP.toFixed(2)}</div>
</div>
</div>
</div>
<Button type="submit" className="w-full">Save Changes</Button>
</form>
</Form>
</DialogContent>
</Dialog>
)
}

View file

@ -25,6 +25,7 @@ import { jsPDF } from "jspdf"
import { ReceiveStockDialog } from "./ReceiveStockDialog"
import pb from "@/lib/pocketbase"
import { createColumns, PurchaseOrder } from "./columns"
import { EditPurchaseOrderDialog } from "./EditPurchaseOrderDialog"
import {
Dialog,
DialogContent,
@ -65,6 +66,7 @@ export function PurchaseOrderDataTable<TData, TValue>({
const [receiveDialogOpen, setReceiveDialogOpen] = React.useState(false)
const [selectedOrder, setSelectedOrder] = React.useState<PurchaseOrder | null>(null)
const [viewDialogOpen, setViewDialogOpen] = React.useState(false)
const [editDialogOpen, setEditDialogOpen] = React.useState(false)
const openReceiveDialog = (order: PurchaseOrder) => {
setSelectedOrder(order)
@ -81,12 +83,53 @@ export function PurchaseOrderDataTable<TData, TValue>({
setViewDialogOpen(true)
}
const openEditDialog = (order: PurchaseOrder) => {
setSelectedOrder(order)
setEditDialogOpen(true)
}
const closeViewDialog = () => {
setSelectedOrder(null)
setViewDialogOpen(false)
}
const generatePDF = (order: PurchaseOrder) => {
let logoDataUrlCache: string | null = null
const getLogoDataUrl = async (): Promise<string | null> => {
if (logoDataUrlCache) return logoDataUrlCache
try {
const tryPaths = ["/brand.png", "/jei-logo.svg", "/icons/icon-512x512.png"]
const srcPath = tryPaths[0]
const img = new Image()
img.crossOrigin = "anonymous"
img.src = srcPath
await new Promise((resolve, reject) => {
img.onload = resolve
img.onerror = reject
})
const canvas = document.createElement("canvas")
const targetW = 80
const targetH = 80
canvas.width = targetW
canvas.height = targetH
const ctx = canvas.getContext("2d")
if (!ctx) return null
ctx.clearRect(0, 0, targetW, targetH)
// preserve aspect ratio inside target box
const scale = Math.min(targetW / img.width, targetH / img.height)
const drawW = img.width * scale
const drawH = img.height * scale
const dx = (targetW - drawW) / 2
const dy = (targetH - drawH) / 2
ctx.drawImage(img, dx, dy, drawW, drawH)
const dataUrl = canvas.toDataURL("image/png")
logoDataUrlCache = dataUrl
return dataUrl
} catch {
return null
}
}
const generatePDF = async (order: PurchaseOrder) => {
try {
console.log("Generating PDF for order:", order);
const doc = new jsPDF();
@ -97,16 +140,34 @@ export function PurchaseOrderDataTable<TData, TValue>({
const darkText = "#1a1a1a";
const lightGray = "#f5f5f5";
const mediumGray = "#666666";
const pageHeight = (doc.internal as any).pageSize.getHeight ? (doc.internal as any).pageSize.getHeight() : doc.internal.pageSize.height;
const bottomMargin = 20;
const itemsTopY = 20;
const renderItemsHeader = (startY: number) => {
doc.setFillColor(brandBlack);
doc.roundedRect(14, startY, 182, 10, 1, 1, 'F');
doc.setFont("helvetica", "bold");
doc.setFontSize(9);
doc.setTextColor(255, 255, 255);
doc.text("#", 17, startY + 6);
doc.text("Description", 28, startY + 6);
doc.text("SKU", 100, startY + 6);
doc.text("Qty", 130, startY + 6, { align: "center" });
doc.text("Unit Price", 155, startY + 6, { align: "right" });
doc.text("Total", 188, startY + 6, { align: "right" });
};
// Create branded logo box
doc.setFillColor(brandBlack);
doc.roundedRect(14, 10, 20, 20, 3, 3, 'F');
doc.setFont("helvetica", "bold");
doc.setFontSize(16);
doc.setTextColor(255, 255, 255);
doc.text("JEI", 24, 23, { align: "center" }); // --- Modern Header with black background ---
// --- Modern Header with black background ---
doc.setFillColor(brandBlack);
doc.rect(0, 0, 210, 45, 'F');
const logoDataUrl = await getLogoDataUrl()
if (logoDataUrl) {
doc.addImage(logoDataUrl, "PNG", 14, 9, 26, 26)
} else {
// Fallback simple box if logo couldn't load
doc.setFillColor(brandBlack);
doc.roundedRect(14, 9, 26, 26, 3, 3, 'F');
}
doc.setFillColor(accentYellow);
doc.rect(0, 45, 210, 2, 'F'); // Accent line
@ -221,20 +282,7 @@ export function PurchaseOrderDataTable<TData, TValue>({
y += 8;
// Modern table header
doc.setFillColor(brandBlack);
doc.roundedRect(14, y, 182, 10, 1, 1, 'F');
doc.setFont("helvetica", "bold");
doc.setFontSize(9);
doc.setTextColor(255, 255, 255);
doc.text("#", 17, y + 6);
doc.text("Description", 28, y + 6);
doc.text("SKU", 100, y + 6);
doc.text("Qty", 130, y + 6, { align: "center" });
doc.text("Unit Price", 155, y + 6, { align: "right" });
doc.text("Total", 188, y + 6, { align: "right" });
renderItemsHeader(y);
y += 10;
// Table rows with alternating colors and proper text wrapping
@ -253,6 +301,13 @@ export function PurchaseOrderDataTable<TData, TValue>({
const maxDescWidth = 65;
const wrappedDesc = doc.splitTextToSize(item.itemName, maxDescWidth);
const rowHeight = Math.max(10, wrappedDesc.length * 5 + 5); // Dynamic height
const bottomLimit = pageHeight - bottomMargin;
if (y + rowHeight > bottomLimit) {
doc.addPage();
y = itemsTopY;
renderItemsHeader(y);
y += 10;
}
// Alternating row background
if (rowColor) {
@ -282,6 +337,11 @@ export function PurchaseOrderDataTable<TData, TValue>({
// --- Modern Totals Section ---
y += 5;
const bottomLimit = pageHeight - bottomMargin;
if (y + 48 > bottomLimit) {
doc.addPage();
y = itemsTopY;
}
const carriageCharge = order.carriageCharge || 0;
const grandTotal = subtotal + carriageCharge;
@ -289,7 +349,7 @@ export function PurchaseOrderDataTable<TData, TValue>({
// Totals box - increased height to accommodate carriage charge
doc.setFillColor(lightGray);
doc.roundedRect(130, y, 66, 38, 2, 2, 'F');
y += 7;
doc.setFont("helvetica", "normal");
doc.setFontSize(9);
@ -324,6 +384,10 @@ export function PurchaseOrderDataTable<TData, TValue>({
// --- Footer (with more spacing from grand total) ---
y += 25; // Add more space between grand total and footer
if (y + 10 > bottomLimit) {
doc.addPage();
y = itemsTopY;
}
doc.setFont("helvetica", "italic");
doc.setFontSize(8);
doc.setTextColor(mediumGray);
@ -331,7 +395,9 @@ export function PurchaseOrderDataTable<TData, TValue>({
doc.text("This is a computer-generated document and does not require a signature.", 105, y + 5, { align: "center" });
console.log("Saving PDF...");
doc.save(`PO_${order.id}_${new Date().toISOString().split('T')[0]}.pdf`);
const rawName = order.orderNumber || order.id || `PO_${Date.now()}`
const safeName = String(rawName).replace(/[^A-Za-z0-9_-]/g, "_")
doc.save(`PO_${safeName}.pdf`);
console.log("PDF saved successfully!");
} catch (error: any) {
console.error("Error generating PDF:", error);
@ -360,7 +426,11 @@ export function PurchaseOrderDataTable<TData, TValue>({
}
}
const columnsWithActions = React.useMemo(() => createColumns({ onDownloadPdf: generatePDF, onViewOrder: handleViewOrder }), [generatePDF, handleViewOrder]);
const handleDownloadPdf = React.useCallback((order: PurchaseOrder) => { void generatePDF(order) }, [])
const columnsWithActions = React.useMemo(
() => createColumns({ onDownloadPdf: handleDownloadPdf, onViewOrder: handleViewOrder, onEditOrder: openEditDialog }),
[handleDownloadPdf, handleViewOrder]
);
const table = useReactTable<PurchaseOrder>({
data: data as PurchaseOrder[],
@ -449,16 +519,18 @@ export function PurchaseOrderDataTable<TData, TValue>({
</td>
))}
<td className="px-4 py-2 space-x-2">
{order.status === "Pending Approval" && (
{(order.status === "Pending" || order.status === "Pending Approval") && (
<>
<Button size="sm" variant="outline" onClick={() => approveOrder(order)}>Approve</Button>
<Button size="sm" variant="destructive" onClick={() => rejectOrder(order)}>Reject</Button>
<Button size="sm" onClick={() => openEditDialog(order)}>Edit</Button>
</>
)}
{order.status === "Approved" && (
{(order.status === "Approved" || order.status === "Ordered") && (
<>
<Button size="sm" onClick={() => generatePDF(order)}>Download PDF</Button>
<Button size="sm" variant="secondary" onClick={() => openReceiveDialog(order)}>Receive Stock</Button>
<Button size="sm" variant="outline" onClick={() => openEditDialog(order)}>Edit</Button>
</>
)}
{order.status === "Received" && (
@ -520,6 +592,21 @@ export function PurchaseOrderDataTable<TData, TValue>({
refreshData={refreshData}
/>
)}
{selectedOrder && (
<EditPurchaseOrderDialog
open={editDialogOpen}
onOpenChange={(open) => {
if (!open) {
setEditDialogOpen(false)
setSelectedOrder(null)
} else {
setEditDialogOpen(true)
}
}}
order={selectedOrder}
refreshData={refreshData}
/>
)}
</>
)
}
}

View file

@ -31,9 +31,9 @@ import { StockItemSearch } from "./StockItemSearch"
const itemSchema = z.object({
stockItemId: z.string().min(1, { message: "Stock item ID is required." }),
itemName: z.string().min(1, { message: "Item name is required." }),
quantity: z.number().int().positive(),
quantity: z.coerce.number().int().positive(),
sku: z.string(),
costPrice: z.number(),
costPrice: z.coerce.number(),
})
// Define the type for the items array
@ -45,7 +45,7 @@ const purchaseOrderFormSchema = z.object({
supplierName: z.string().min(1, { message: "Supplier name is required." }),
accountRef: z.string().optional(),
salesOrderNo: z.string().optional(),
carriageCharge: z.number().min(0).optional(),
carriageCharge: z.coerce.number().min(0).optional(),
orderDate: z.date({ required_error: "Order date is required." }),
items: z.array(itemSchema).min(1, { message: "At least one item is required." }),
})
@ -180,6 +180,17 @@ export function PurchaseOrderForm({ setOpen, refreshData }: PurchaseOrderFormPro
name: "items",
});
const itemsValues = form.watch("items") as ItemSchemaType[];
const subtotalGBP = React.useMemo(
() =>
(itemsValues || []).reduce(
(sum, item) =>
sum + (Number(item?.quantity || 0) * Number(item?.costPrice || 0)),
0
),
[itemsValues]
);
// Function to add a new item row
const addItem = () => {
@ -218,14 +229,15 @@ export function PurchaseOrderForm({ setOpen, refreshData }: PurchaseOrderFormPro
const selectedStockItem = stockItems.find(item => item.id === selectedItemId);
if (selectedStockItem) {
// Update the form fields with the selected stock item's details
handleItemChange(index, "stockItemId", selectedStockItem.id);
handleItemChange(index, "itemName", selectedStockItem.name);
handleItemChange(index, "sku", selectedStockItem.sku);
form.setValue(`items.${index}.stockItemId`, selectedStockItem.id, { shouldDirty: true });
form.setValue(`items.${index}.itemName`, selectedStockItem.name, { shouldDirty: true });
form.setValue(`items.${index}.sku`, selectedStockItem.sku, { shouldDirty: true });
// Use GBP price for purchase orders
handleItemChange(index, "costPrice", selectedStockItem.costPriceGBP);
// Quantity remains as is or defaults to 1 if not set
if (form.getValues("items")[index].quantity === 0) {
handleItemChange(index, "quantity", 1);
form.setValue(`items.${index}.costPrice`, selectedStockItem.costPriceGBP, { shouldDirty: true, shouldValidate: true });
// Ensure quantity has a sensible default
const currentQty = form.getValues(`items.${index}.quantity`);
if (!currentQty || Number.isNaN(currentQty)) {
form.setValue(`items.${index}.quantity`, 1, { shouldDirty: true, shouldValidate: true });
}
}
}
@ -257,6 +269,24 @@ export function PurchaseOrderForm({ setOpen, refreshData }: PurchaseOrderFormPro
console.log("Form values:", values);
await pb.collection('purchase_orders').create(purchaseOrderData);
// Update stock item GBP prices if missing or changed
try {
await Promise.all(
values.items.map(async (item) => {
const current = stockItems.find(si => si.id === item.stockItemId);
const newPrice = Number(item.costPrice || 0);
if (!current) return;
const existing = Number(current.costPriceGBP ?? 0);
if (Number.isFinite(newPrice) && newPrice > 0 && newPrice !== existing) {
await pb.collection('stock_items').update(item.stockItemId, { cost_price_gbp: newPrice });
}
})
);
} catch (updateErr) {
console.error("Error updating stock item GBP prices:", updateErr);
}
showSuccess(`Purchase order ${values.orderNumber} created successfully!`);
form.reset();
// Generate new order number for next use
@ -383,7 +413,10 @@ export function PurchaseOrderForm({ setOpen, refreshData }: PurchaseOrderFormPro
<div className="border p-4 rounded-md">
<h3 className="text-lg font-semibold mb-4">Order Items</h3>
{fields.map((item, index) => (
<div key={item.id} className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-[2fr,1fr,1fr,1fr,auto] gap-4 mb-4 items-center">
<div
key={item.id}
className="grid grid-cols-1 md:grid-cols-[minmax(220px,1fr),160px,110px,140px,auto] gap-4 mb-4 items-start"
>
<FormField
control={form.control}
name={`items.${index}.itemName`}
@ -395,6 +428,7 @@ export function PurchaseOrderForm({ setOpen, refreshData }: PurchaseOrderFormPro
selectedItemName={field.value}
onSelectItem={(selectedItemId) => handleStockItemSelect(index, selectedItemId)}
placeholder="Search by SKU or name..."
className="min-w-0"
/>
<FormMessage />
</FormItem>
@ -408,7 +442,7 @@ export function PurchaseOrderForm({ setOpen, refreshData }: PurchaseOrderFormPro
<FormLabel>SKU</FormLabel>
<FormControl>
{/* Display SKU, but it's managed by handleStockItemSelect */}
<Input {...field} readOnly placeholder="SKU" />
<Input {...field} readOnly placeholder="SKU" className="md:w-40" />
</FormControl>
<FormMessage />
</FormItem>
@ -421,7 +455,14 @@ export function PurchaseOrderForm({ setOpen, refreshData }: PurchaseOrderFormPro
<FormItem>
<FormLabel>Quantity</FormLabel>
<FormControl>
<Input type="number" {...field} onChange={(e) => handleItemChange(index, "quantity", e.target.value)} />
<Input
type="text"
inputMode="numeric"
pattern="[0-9]*"
className="text-right md:w-28"
value={field.value ?? ""}
onChange={(e) => field.onChange(e.target.value.replace(/[^0-9]/g, ""))}
/>
</FormControl>
<FormMessage />
</FormItem>
@ -434,7 +475,15 @@ export function PurchaseOrderForm({ setOpen, refreshData }: PurchaseOrderFormPro
<FormItem>
<FormLabel>Unit Price (£ GBP)</FormLabel>
<FormControl>
<Input type="number" step="0.01" placeholder="£0.00" {...field} onChange={(e) => handleItemChange(index, "costPrice", e.target.value)} />
<Input
type="text"
inputMode="decimal"
pattern="^[0-9]*[.,]?[0-9]*$"
placeholder="£0.00"
className="text-right md:w-36"
value={field.value ?? ""}
onChange={(e) => field.onChange(e.target.value.replace(",", "."))}
/>
</FormControl>
<FormMessage />
</FormItem>
@ -452,10 +501,16 @@ export function PurchaseOrderForm({ setOpen, refreshData }: PurchaseOrderFormPro
<Button type="button" variant="outline" onClick={addItem} className="mt-2">
Add Item
</Button>
<div className="mt-4 border-t pt-4">
<div className="flex items-center justify-end gap-6">
<div className="text-sm text-gray-600">Items total (GBP)</div>
<div className="text-lg font-semibold">£{subtotalGBP.toFixed(2)}</div>
</div>
</div>
</div>
<Button type="submit" className="w-full">Create Purchase Order</Button>
</form>
</Form>
)
}
}

View file

@ -35,12 +35,13 @@ interface StockItemSearchProps {
selectedItemName: string;
onSelectItem: (itemId: string) => void;
placeholder: string;
className?: string;
}
export function StockItemSearch({ stockItems, selectedItemName, onSelectItem, placeholder }: StockItemSearchProps) {
export function StockItemSearch({ stockItems, selectedItemName, onSelectItem, placeholder, className }: StockItemSearchProps) {
const [open, setOpen] = React.useState(false)
const [search, setSearch] = React.useState("")
@ -51,7 +52,7 @@ export function StockItemSearch({ stockItems, selectedItemName, onSelectItem, pl
variant="outline"
role="combobox"
aria-expanded={open}
className="w-full justify-between text-left font-normal"
className={cn("w-full justify-between text-left font-normal", className)}
>
{selectedItemName ? (
<div className="flex flex-col">

View file

@ -1,7 +1,7 @@
"use client"
import { ColumnDef } from "@tanstack/react-table"
import { MoreHorizontal, Download, Eye } from "lucide-react" // Import Eye icon
import { MoreHorizontal, Download, Eye, Pencil } from "lucide-react" // Import Eye icon
import { Button } from "@/components/ui/button"
import {
Dialog,
@ -45,10 +45,11 @@ export type PurchaseOrder = {
interface ActionsColumnProps {
onDownloadPdf: (order: PurchaseOrder) => void;
onViewOrder: (order: PurchaseOrder) => void; // Add onViewOrder prop
onEditOrder?: (order: PurchaseOrder) => void;
}
// Export createColumns function
export const createColumns = ({ onDownloadPdf, onViewOrder }: ActionsColumnProps): ColumnDef<PurchaseOrder>[] => [
export const createColumns = ({ onDownloadPdf, onViewOrder, onEditOrder }: ActionsColumnProps): ColumnDef<PurchaseOrder>[] => [
{
accessorKey: "supplierName",
header: "Supplier Name",
@ -101,6 +102,12 @@ export const createColumns = ({ onDownloadPdf, onViewOrder }: ActionsColumnProps
<Eye className="mr-2 h-4 w-4" />
View Order
</DropdownMenuItem>
{!!onEditOrder && (
<DropdownMenuItem onClick={() => onEditOrder(order)}>
<Pencil className="mr-2 h-4 w-4" />
Edit Order
</DropdownMenuItem>
)}
<DropdownMenuItem onClick={() => onDownloadPdf(order)}>
<Download className="mr-2 h-4 w-4" />
Download PDF
@ -110,4 +117,4 @@ export const createColumns = ({ onDownloadPdf, onViewOrder }: ActionsColumnProps
)
},
},
]
]

View file

@ -100,7 +100,7 @@ const PurchaseOrders = () => {
New Purchase Order
</Button>
</DialogTrigger>
<DialogContent className="mx-4 sm:max-w-[600px] max-h-[90vh] overflow-y-auto">
<DialogContent className="mx-4 sm:max-w-[800px] lg:max-w-[900px] max-h-[90vh] overflow-y-auto">
<DialogHeader>
<DialogTitle>New Purchase Order</DialogTitle>
<DialogDescription>
@ -150,4 +150,4 @@ const PurchaseOrders = () => {
);
};
export default PurchaseOrders;
export default PurchaseOrders;