[dyad] wrote 1 file(s)

This commit is contained in:
[dyad] 2025-09-12 11:38:42 +02:00
parent 5362363c31
commit 274c8a833b

View file

@ -24,9 +24,9 @@ import {
import { jsPDF } from "jspdf" import { jsPDF } from "jspdf"
import { ReceiveStockDialog } from "./ReceiveStockDialog" import { ReceiveStockDialog } from "./ReceiveStockDialog"
import pb from "@/lib/pocketbase" import pb from "@/lib/pocketbase"
import { createColumns, PurchaseOrder } from "./columns" // Import createColumns and PurchaseOrder type import { createColumns, PurchaseOrder } from "./columns"
import { import {
Dialog, // Import Dialog components Dialog,
DialogContent, DialogContent,
DialogDescription, DialogDescription,
DialogHeader, DialogHeader,
@ -43,7 +43,7 @@ interface PurchaseOrderDataTableProps<TData, TValue> {
} }
export function PurchaseOrderDataTable<TData, TValue>({ export function PurchaseOrderDataTable<TData, TValue>({
columns: columnsProp, // Rename to avoid conflict columns: columnsProp,
data, data,
filterValue, filterValue,
onFilterChange, onFilterChange,
@ -64,7 +64,7 @@ export function PurchaseOrderDataTable<TData, TValue>({
const [rowSelection, setRowSelection] = React.useState({}) const [rowSelection, setRowSelection] = React.useState({})
const [receiveDialogOpen, setReceiveDialogOpen] = React.useState(false) const [receiveDialogOpen, setReceiveDialogOpen] = React.useState(false)
const [selectedOrder, setSelectedOrder] = React.useState<PurchaseOrder | null>(null) const [selectedOrder, setSelectedOrder] = React.useState<PurchaseOrder | null>(null)
const [viewDialogOpen, setViewDialogOpen] = React.useState(false) // State for the view dialog const [viewDialogOpen, setViewDialogOpen] = React.useState(false)
const openReceiveDialog = (order: PurchaseOrder) => { const openReceiveDialog = (order: PurchaseOrder) => {
setSelectedOrder(order) setSelectedOrder(order)
@ -87,23 +87,224 @@ export function PurchaseOrderDataTable<TData, TValue>({
} }
const generatePDF = (order: PurchaseOrder) => { const generatePDF = (order: PurchaseOrder) => {
const doc = new jsPDF() const doc = new jsPDF({
doc.setFontSize(16) unit: "mm",
doc.text("Purchase Order", 14, 20) format: "a4",
doc.setFontSize(12) orientation: "portrait",
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 const pageWidth = doc.internal.pageSize.getWidth();
// Colors
const primaryColor = "#FFC905";
const blackColor = "#000000";
const darkGray = "#333333";
const lightGray = "#F3F4F6";
// --- Header ---
doc.setFillColor(primaryColor);
doc.rect(0, 0, pageWidth, 40, "F");
// Logo rectangle
doc.setFillColor("#FFA500");
doc.rect(15, 10, 40, 20, "F");
doc.setTextColor(blackColor);
doc.setFontSize(12);
doc.setFont("helvetica", "bold");
const logoText = "LOGO";
const logoTextWidth = doc.getTextWidth(logoText);
doc.text(logoText, 15 + 20 - logoTextWidth / 2, 22);
// Header title
doc.setTextColor(blackColor);
doc.setFontSize(24);
doc.text("PURCHASE ORDER", 65, 25);
// --- Company Info (left) ---
doc.setFontSize(10);
doc.setTextColor(darkGray);
const companyInfoX = 15;
let y = 50;
doc.text("Your Company Name", companyInfoX, y);
y += 6;
doc.text("77 Hammersmith Road, West Kensington", companyInfoX, y);
y += 6;
doc.text("London, W14 0QH", companyInfoX, y);
y += 6;
doc.text("Phone: 0208 668 381", companyInfoX, y);
// --- PO Details (right) ---
const poDetailsX = 130;
const poDetailsY = 45;
const poDetailsWidth = 65;
const poDetailsHeight = 55;
doc.setFillColor(lightGray);
doc.roundedRect(poDetailsX, poDetailsY, poDetailsWidth, poDetailsHeight, 3, 3, "F");
doc.setTextColor(darkGray);
doc.setFontSize(10);
const poLabelX = poDetailsX + 5;
let poLabelY = poDetailsY + 8;
const poValueX = poDetailsX + poDetailsWidth - 5;
const poLabels = [
"PO no:",
"Status:",
"Date:",
"Requested By:",
"Approved By:",
"Department:",
];
const poValues = [
"POO1234",
order.status,
order.orderDate ? new Date(order.orderDate).toLocaleDateString() : "",
"Billy Buyer",
"John Smith",
"IT Department",
];
poLabels.forEach((label, i) => {
doc.text(label, poLabelX, poLabelY);
doc.text(poValues[i], poValueX - doc.getTextWidth(poValues[i]), poLabelY);
poLabelY += 8;
});
// --- Ship To ---
const shipToX = 15;
let shipToY = 110;
doc.setFontSize(12);
doc.setTextColor(blackColor);
doc.setFont("helvetica", "bold");
doc.text("Ship to", shipToX, shipToY);
shipToY += 8;
doc.setFontSize(10);
doc.setFont("helvetica", "normal");
const shipToLines = [
"Manchester Office",
"Attn: Billy Buyer",
"43 Customer Road",
"Manchester, M4 1HS",
"Phone: 07394 123456",
];
shipToLines.forEach(line => {
doc.text(line, shipToX, shipToY);
shipToY += 6;
});
// --- Supplier ---
const supplierX = 15;
let supplierY = 160;
doc.setFontSize(12);
doc.setFont("helvetica", "bold");
doc.text("Supplier", supplierX, supplierY);
supplierY += 8;
doc.setFontSize(10);
doc.setFont("helvetica", "normal");
const supplierLines = [
"Mr. Peter McDonald",
"17 Springdale Rd,",
"N16 9NS, London",
"Phone: 1234 567 890",
"Email: supplier@email.co.uk",
"Terms: 30 days",
];
supplierLines.forEach(line => {
doc.text(line, supplierX, supplierY);
supplierY += 6;
});
// --- Terms of sale ---
const termsX = 130;
let termsY = 110;
doc.setFontSize(12);
doc.setFont("helvetica", "bold");
doc.text("Terms of sale and other comments", termsX, termsY);
termsY += 8;
doc.setFontSize(10);
doc.setFont("helvetica", "normal");
doc.text("Add any additional instructions or terms here.", termsX, termsY);
// --- Items Table Header ---
const tableX = 10;
let tableY = 190;
const tableWidth = pageWidth - 20;
doc.setFillColor(lightGray);
doc.roundedRect(tableX, tableY, tableWidth, 10, 3, 3, "F");
doc.setTextColor(darkGray);
doc.setFontSize(11);
doc.setFont("helvetica", "bold");
const headers = [
{ label: "Item #", x: tableX + 5, width: 15 },
{ label: "Description", x: tableX + 25, width: 80 },
{ label: "Qty", x: tableX + 110, width: 15 },
{ label: "Unit", x: tableX + 130, width: 20 },
{ label: "Unit Price", x: tableX + 155, width: 25 },
{ label: "Total", x: tableX + 185, width: 20 },
];
headers.forEach(h => {
doc.text(h.label, h.x, tableY + 7);
});
// --- Items Table Rows ---
doc.setFont("helvetica", "normal");
let rowY = tableY + 17;
order.items.forEach((item, index) => { order.items.forEach((item, index) => {
doc.text(`${index + 1}. ${item.itemName} - Qty: ${item.quantity}`, 14, y) if (rowY > 270) {
doc.text(item.sku, 70, y); doc.addPage();
y += 10 rowY = 20;
}) }
doc.setTextColor(darkGray);
doc.setFontSize(10);
doc.text(String(index + 1), headers[0].x, rowY);
doc.text(item.itemName, headers[1].x, rowY);
doc.text(String(item.quantity), headers[2].x, rowY);
doc.text("Unit", headers[3].x, rowY); // Placeholder for unit
doc.text("£75.00", headers[4].x, rowY); // Placeholder for unit price
doc.text("£75.00", headers[5].x, rowY); // Placeholder for total
rowY += 8;
});
doc.save(`purchase_order_${order.id}.pdf`) // --- Totals Section ---
const totalsX = tableX + tableWidth - 60;
let totalsY = rowY + 10;
const totalsLabels = ["Subtotal", "Tax", "Shipping", "Other"];
const totalsValues = ["£2,325.00", "£0", "£0", "£0"];
doc.setFontSize(10);
doc.setFont("helvetica", "normal");
doc.setTextColor(darkGray);
totalsLabels.forEach((label, i) => {
doc.text(label, totalsX, totalsY + i * 7);
doc.text(totalsValues[i], totalsX + 40, totalsY + i * 7);
});
// Total line
totalsY += totalsLabels.length * 7 + 3;
doc.setDrawColor(primaryColor);
doc.setLineWidth(0.5);
doc.line(totalsX, totalsY, totalsX + 50, totalsY);
// Total amount
totalsY += 7;
doc.setFontSize(14);
doc.setFont("helvetica", "bold");
doc.setTextColor(primaryColor);
doc.text("Total", totalsX, totalsY);
doc.text("£2,325.00", totalsX + 40, totalsY);
doc.save(`purchase_order_${order.id}.pdf`);
} }
const approveOrder = async (order: PurchaseOrder) => { const approveOrder = async (order: PurchaseOrder) => {
@ -126,9 +327,9 @@ export function PurchaseOrderDataTable<TData, TValue>({
const columnsWithActions = React.useMemo(() => createColumns({ onDownloadPdf: generatePDF, onViewOrder: handleViewOrder }), [generatePDF, handleViewOrder]); const columnsWithActions = React.useMemo(() => createColumns({ onDownloadPdf: generatePDF, onViewOrder: handleViewOrder }), [generatePDF, handleViewOrder]);
const table = useReactTable<PurchaseOrder>({ // Specify TData as PurchaseOrder const table = useReactTable<PurchaseOrder>({
data: data as PurchaseOrder[], // Explicitly type data as PurchaseOrder[] data: data as PurchaseOrder[],
columns: columnsWithActions, // Use the columns with actions columns: columnsWithActions,
getCoreRowModel: getCoreRowModel(), getCoreRowModel: getCoreRowModel(),
getPaginationRowModel: getPaginationRowModel(), getPaginationRowModel: getPaginationRowModel(),
onSortingChange: setSorting, onSortingChange: setSorting,
@ -145,7 +346,6 @@ export function PurchaseOrderDataTable<TData, TValue>({
}, },
}) })
// Sync external filter value with the table's internal state
React.useEffect(() => { React.useEffect(() => {
table.getColumn("supplierName")?.setFilterValue(filterValue) table.getColumn("supplierName")?.setFilterValue(filterValue)
}, [filterValue, table]) }, [filterValue, table])
@ -174,20 +374,18 @@ export function PurchaseOrderDataTable<TData, TValue>({
.filter( .filter(
(column) => column.getCanHide() (column) => column.getCanHide()
) )
.map((column) => { .map((column) => (
return ( <DropdownMenuCheckboxItem
<DropdownMenuCheckboxItem key={column.id}
key={column.id} className="capitalize"
className="capitalize" checked={column.getIsVisible()}
checked={column.getIsVisible()} onCheckedChange={(value) =>
onCheckedChange={(value) => column.toggleVisibility(!!value)
column.toggleVisibility(!!value) }
} >
> {column.id}
{column.id} </DropdownMenuCheckboxItem>
</DropdownMenuCheckboxItem> ))}
)
})}
</DropdownMenuContent> </DropdownMenuContent>
</DropdownMenu> </DropdownMenu>
</div> </div>