import type { InventoryItemOptionDto, PurchaseLineInput, PurchaseOrderInput, PurchaseVendorOptionDto } from "@mrp/shared"; import { useEffect, useState } from "react"; import { Link, useNavigate, useParams } from "react-router-dom"; import { useAuth } from "../../auth/AuthProvider"; import { api, ApiError } from "../../lib/api"; import { inventoryUnitOptions } from "../inventory/config"; import { emptyPurchaseOrderInput, purchaseStatusOptions } from "./config"; export function PurchaseFormPage({ mode }: { mode: "create" | "edit" }) { const { token } = useAuth(); const navigate = useNavigate(); const { orderId } = useParams(); const [form, setForm] = useState(emptyPurchaseOrderInput); const [status, setStatus] = useState(mode === "create" ? "Create a new purchase order." : "Loading purchase order..."); const [vendors, setVendors] = useState([]); const [vendorSearchTerm, setVendorSearchTerm] = useState(""); const [vendorPickerOpen, setVendorPickerOpen] = useState(false); const [itemOptions, setItemOptions] = useState([]); const [lineSearchTerms, setLineSearchTerms] = useState([]); const [activeLinePicker, setActiveLinePicker] = useState(null); const [isSaving, setIsSaving] = useState(false); const subtotal = form.lines.reduce((sum: number, line: PurchaseLineInput) => sum + line.quantity * line.unitCost, 0); const taxAmount = subtotal * (form.taxPercent / 100); const total = subtotal + taxAmount + form.freightAmount; useEffect(() => { if (!token) { return; } api.getPurchaseVendors(token).then(setVendors).catch(() => setVendors([])); api.getInventoryItemOptions(token).then((options) => setItemOptions(options.filter((option: InventoryItemOptionDto) => option.isPurchasable))).catch(() => setItemOptions([])); }, [token]); useEffect(() => { if (!token || mode !== "edit" || !orderId) { return; } api.getPurchaseOrder(token, orderId) .then((document) => { setForm({ vendorId: document.vendorId, status: document.status, issueDate: document.issueDate, taxPercent: document.taxPercent, freightAmount: document.freightAmount, notes: document.notes, lines: document.lines.map((line: { itemId: string; description: string; quantity: number; unitOfMeasure: PurchaseLineInput["unitOfMeasure"]; unitCost: number; position: number }) => ({ itemId: line.itemId, description: line.description, quantity: line.quantity, unitOfMeasure: line.unitOfMeasure, unitCost: line.unitCost, position: line.position, })), }); setVendorSearchTerm(document.vendorName); setLineSearchTerms(document.lines.map((line: { itemSku: string }) => line.itemSku)); setStatus("Purchase order loaded."); }) .catch((error: unknown) => { const message = error instanceof ApiError ? error.message : "Unable to load purchase order."; setStatus(message); }); }, [mode, orderId, token]); function updateField(key: Key, value: PurchaseOrderInput[Key]) { setForm((current: PurchaseOrderInput) => ({ ...current, [key]: value })); } function getSelectedVendorName(vendorId: string) { return vendors.find((vendor) => vendor.id === vendorId)?.name ?? ""; } function getSelectedVendor(vendorId: string) { return vendors.find((vendor) => vendor.id === vendorId) ?? null; } function updateLine(index: number, nextLine: PurchaseLineInput) { setForm((current: PurchaseOrderInput) => ({ ...current, lines: current.lines.map((line: PurchaseLineInput, lineIndex: number) => (lineIndex === index ? nextLine : line)), })); } function updateLineSearchTerm(index: number, value: string) { setLineSearchTerms((current) => { const next = [...current]; next[index] = value; return next; }); } function addLine() { setForm((current: PurchaseOrderInput) => ({ ...current, lines: [ ...current.lines, { itemId: "", description: "", quantity: 1, unitOfMeasure: "EA", unitCost: 0, position: current.lines.length === 0 ? 10 : Math.max(...current.lines.map((line: PurchaseLineInput) => line.position)) + 10, }, ], })); setLineSearchTerms((current) => [...current, ""]); } function removeLine(index: number) { setForm((current: PurchaseOrderInput) => ({ ...current, lines: current.lines.filter((_line: PurchaseLineInput, lineIndex: number) => lineIndex !== index), })); setLineSearchTerms((current) => current.filter((_term, termIndex) => termIndex !== index)); } async function handleSubmit(event: React.FormEvent) { event.preventDefault(); if (!token) { return; } setIsSaving(true); setStatus("Saving purchase order..."); try { const saved = mode === "create" ? await api.createPurchaseOrder(token, form) : await api.updatePurchaseOrder(token, orderId ?? "", form); navigate(`/purchasing/orders/${saved.id}`); } catch (error: unknown) { const message = error instanceof ApiError ? error.message : "Unable to save purchase order."; setStatus(message); setIsSaving(false); } } const filteredVendorCount = vendors.filter((vendor) => { const query = vendorSearchTerm.trim().toLowerCase(); if (!query) { return true; } return vendor.name.toLowerCase().includes(query) || vendor.email.toLowerCase().includes(query); }).length; return (

Purchasing Editor

{mode === "create" ? "New Purchase Order" : "Edit Purchase Order"}

Cancel