/* Pelagia Portal — Approvals, PO Detail, New PO */ /* ═══════════════════ APPROVAL QUEUE ═══════════════════ */ const ApprovalsPage = ({ go }) => ( <>

Approval queue · {APPROVAL_QUEUE.length} pending

POs awaiting manager decision · oldest first
Sorted by submitted date
{APPROVAL_QUEUE.map(o => ( go("approval-detail", o.id)}> ))}
PO NumberTitleSubmitterVesselSubmittedAmount
{o.id} {o.title} {o.submitter} {o.vessel} {o.submitted} {inr(o.amount)} Review →
); /* ═══════════════════ PO DETAIL (shared) ═══════════════════ */ const ItemsTable = ({ items }) => { if (!items?.length) return
No line items.
; const taxable = items.reduce((s, i) => s + i.qty * i.price, 0); const gst = items.reduce((s, i) => s + i.qty * i.price * i.gst / 100, 0); return ( <> {items.map((i, idx) => ( ))}
ItemDescriptionQtyUnit Unit PriceGSTTotal
{i.name} {i.desc} {i.qty} {i.unit} {inrFull(i.price)} {i.gst}% {inrFull(i.qty * i.price * (1 + i.gst/100))}
TAXABLE
{inrFull(taxable)}
GST
{inrFull(gst)}
GRAND TOTAL
{inrFull(taxable + gst)}
); }; const PODetailBase = ({ order, go, role, isApproval }) => { const isOwner = order.submitter === "Rajesh Pillai" && role === "TECHNICAL"; const isManager = role === "MANAGER" || role === "SUPERUSER"; const showEdit = ["DRAFT", "EDITS_REQUESTED"].includes(order.status) && (isOwner || role === "SUPERUSER"); const showReceipt = order.status === "PAID_DELIVERED" && (isOwner || role === "SUPERUSER"); const showVendorPicker = order.status === "VENDOR_ID_PENDING"; return ( <>

{order.title}

Created by {order.submitter} · {order.created || order.submitted}
{showEdit && } {!isApproval && order.status === "DRAFT" && }
{order.id}
{order.project && {order.project}}
GRAND TOTAL
{inr(order.amount)}
{order.managerNote && (
Manager note · Anjali Krishnan: {order.managerNote}
)}
{/* MAIN */}
{isApproval && ( )} {showVendorPicker && (
The manager has requested that a vendor be selected before approval.
)}

Summary

Vessel
{order.vessel}
Account
{order.account}
Vendor
{order.vendor || Not yet assigned}
Project code
{order.project ? {order.project} : }
Date required
{order.required || "—"}
Currency
INR (₹)

Line items

Terms & conditions

Delivery
{order.terms?.delivery || ORDERS[0].terms.delivery}
Dispatch
{order.terms?.dispatch || ORDERS[0].terms.dispatch}
Inspection
{order.terms?.inspection || ORDERS[0].terms.inspection}
Transit insurance
{order.terms?.insurance || ORDERS[0].terms.insurance}
Payment terms
{order.terms?.payment || ORDERS[0].terms.payment}
Other
{order.terms?.others || ORDERS[0].terms.others}

Documents

{(order.docs || ORDERS[0].docs).map((d, i) => (
{d.name} {d.size}
))}
{showReceipt && ( <>

Confirm receipt

Goods have been marked as paid and dispatched. Upload the delivery receipt to close this PO.
  Drop delivery receipt file or browse
)}
{/* SIDEBAR */}
Created
{order.created || "May 10, 2026"}
Submitted
{order.submitted || "—"}
Approved
{order.approved || "—"}
Paid
{order.paid || "—"}
Closed
{order.closed || "—"}
{(order.audit || ORDERS[0].audit).map((a, i) => (
{a.who}
{a.what}
{a.when.split(" · ")[1] || a.when}
))}
); }; const ApprovalActions = () => { const [mode, setMode] = useS(null); // null | edits | reject | note | vendor if (mode === null) { return (
); } const titles = { edits: ["Request edits", "Describe what needs to change before approval.", "var(--st-edits-fg)", "Send back to submitter"], reject: ["Reject PO", "Provide a reason — this terminates the PO.", "var(--st-rejected-fg)", "Reject"], note: ["Approve with note", "Optional note visible to the submitter.", "var(--primary-ink)", "Approve"], vendor: ["Request vendor selection", "Note for submitter (optional). PO returns here once a vendor is chosen.", "var(--st-vendor-fg)", "Send back for vendor"], }; const [title, sub, color, cta] = titles[mode]; return (
{title}
{sub}
); }; /* ═══════════════════ NEW PO ═══════════════════ */ const NewPOPage = ({ go }) => { const [rows, setRows] = useS([ { name: "Marine Bearing 6310-2RS", desc: "50×110×27mm, sealed", qty: 24, unit: "ea", size: "", price: 4250, gst: 18, suggest: false }, { name: "Cylinder Head Gasket Set", desc: "MAN B&W L28/32H", qty: 4, unit: "set", size: "", price: 18750, gst: 18, suggest: false }, ]); const [suggestIdx, setSuggestIdx] = useS(-1); const taxable = rows.reduce((s, r) => s + (Number(r.qty) || 0) * (Number(r.price) || 0), 0); const gst = rows.reduce((s, r) => s + (Number(r.qty) || 0) * (Number(r.price) || 0) * (Number(r.gst) || 0) / 100, 0); const addRow = () => setRows([...rows, { name: "", desc: "", qty: 1, unit: "ea", size: "", price: 0, gst: 18 }]); const updateRow = (idx, key, val) => setRows(rows.map((r, i) => i === idx ? { ...r, [key]: val } : r)); const removeRow = (idx) => setRows(rows.filter((_, i) => i !== idx)); return ( <>

New purchase order

Fill the four sections below. You can save as draft at any time.

1 · Header

2 · Line items

{rows.map((r, idx) => ( {suggestIdx === idx && r.name && ( )} ))}
Name Description Qty Unit Unit price GST % Total
updateRow(idx, "name", e.target.value)} onFocus={() => setSuggestIdx(idx)} onBlur={() => setTimeout(() => setSuggestIdx(-1), 200)} /> updateRow(idx, "desc", e.target.value)} /> updateRow(idx, "qty", e.target.value)} /> updateRow(idx, "unit", e.target.value)} /> updateRow(idx, "price", e.target.value)} /> updateRow(idx, "gst", e.target.value)} /> {inr((Number(r.qty)||0) * (Number(r.price)||0) * (1 + (Number(r.gst)||0)/100))}
Last seen at: · Mahalakshmi Marine Stores ₹4,250 · Coastline Engineering ₹4,380 · Konark Industrial Spares ₹4,520
Tip: start typing a product name to see vendor price hints
TAXABLE
{inrFull(taxable)}
GST
{inrFull(gst)}
GRAND TOTAL
{inrFull(taxable + gst)}

3 · Terms & conditions

{[ ["Delivery", "FOB Cochin Port. Vendor responsible until cargo cleared at gate."], ["Dispatch", "Within 14 days of approval."], ["Inspection", "Joint inspection at vendor warehouse prior to dispatch."], ["Transit insurance", "Transit insurance included in vendor scope."], ["Payment terms", "30 days from receipt of goods and invoice."], ["Other", "All items to be packed with desiccant and shrink-wrapped."], ].map(([k, v]) => (
))}

4 · Documents

  Drop files here or browse
Quotations, technical sheets, requisitions. Max 25 MB per file.
{[ ["Vendor Quotation Q-2841.pdf", "412 KB"], ["Engine Inspection Report.pdf", "1.8 MB"], ].map(([n, s]) => (
{n} {s}
))}
); }; Object.assign(window, { ApprovalsPage, PODetailBase, NewPOPage });