/* Pelagia Portal — Sites, Cart, Vessels, Accounts, Users, Import PO */ /* ═══════════════════ SITES ═══════════════════ */ const SitesPage = ({ go }) => ( <>

Sites

{SITES.length} ports, depots, and offices that hold inventory
{SITES.map(s => ( go("site-detail", s.id)}> ))}
NameCodeAddress VesselsItems LocationStatus
{s.name} {s.code} {s.address} {s.vessels} {s.items} {["9.95° N","19.04° N","13.07° N","17.69° N"][SITES.indexOf(s)]} · {["76.26° E","72.85° E","80.26° E","83.21° E"][SITES.indexOf(s)]} Active
); /* ═══════════════════ SITE DETAIL ═══════════════════ */ const SiteDetailPage = ({ go, id }) => { const s = SITES.find(x => x.id === id) || SITES[0]; const max = Math.max(...SITE_INVENTORY.map(i => i.qty)); return ( <>

{s.name}

{s.code}· {s.address}· 9.9489° N · 76.2622° E
last calculated 12:30} /> across all items} />
Quantity on hand}>
{SITE_INVENTORY.map((it, i) => (
{it.name}
{it.qty} ea
))}
Daily draw-down}>
{/* grid lines */} {[0, 1, 2, 3].map(i => ( ))} {[0, 1, 2, 3, 4].map(seriesIdx => { const stroke = ["var(--primary)", "oklch(55% 0.09 30)", "oklch(50% 0.06 150)", "oklch(50% 0.07 280)", "oklch(55% 0.08 60)"][seriesIdx]; const pts = CONSUMPTION.map((d, i) => { const x = (i / (CONSUMPTION.length - 1)) * 350 + 5; const y = 130 - (d.vals[seriesIdx] / 4) * 110; return `${x},${y}`; }).join(" "); return ( ); })}
{SITE_INVENTORY.slice(0, 5).map((it, i) => ( {it.name.split("—")[0].trim().split(" ").slice(0,2).join(" ")} ))}

Inventory

{SITE_INVENTORY.map((it, i) => ( go("item-detail", "p1")}> ))}
ProductQty on handLast updated
{it.name} {it.qty} ea {it.updated}

Recent POs for this site

{ORDERS.slice(0, 5).map(o => ( go("po-detail", o.id)}> ))}
PO NumberStatusVendorCreatedAmount
{o.id} {o.vendor || "—"} {o.submitted || "—"} {inr(o.amount)}
MV Pelagia Voyager MV Coral Crescent
); }; /* ═══════════════════ VESSELS ═══════════════════ */ const VesselsPage = () => ( <>

Vessels

{VESSELS.length} vessels in service
{VESSELS.map(v => ( ))}
NameIMO NumberAssigned siteStatus
{v.name} {v.imo} {["Cochin Port Depot","Mumbai BPX Office","Visakhapatnam Yard","Chennai South Dock","Cochin Port Depot"][VESSELS.indexOf(v)]} Active
); /* ═══════════════════ ACCOUNTS ═══════════════════ */ const AccountsPage = () => ( <>

Accounts / Cost centres

{ACCOUNTS.length} active cost centres
{ACCOUNTS.map(a => ( ))}
CodeNameDescriptionStatus
{a.code} {a.name} {a.desc} Active
); /* ═══════════════════ USERS ═══════════════════ */ const UsersPage = () => ( <>

Users

{USERS.length} active users · 7 roles
{USERS.map(u => ( ))}
Employee IDNameEmailRoleStatusCreated
{u.emp} {u.name} {u.email} {u.role} Active {u.created}
); /* ═══════════════════ CART ═══════════════════ */ const CartPage = ({ go }) => { const [items, setItems] = useS([ { id: 1, name: "Marine Bearing 6310-2RS", desc: "50×110×27mm sealed", vendor: "Mahalakshmi Marine Stores", price: 4250, qty: 24, gst: 18 }, { id: 2, name: "Lube Oil Filter — Element", desc: "Cellulose, 10µ", vendor: "Coastline Engineering Co.", price: 1180, qty: 48, gst: 18 }, { id: 3, name: "CO₂ Fire Extinguisher 9kg", desc: "Marine-grade, BIS certified", vendor: "Sealine Maritime Pvt Ltd", price: 8400, qty: 4, gst: 18 }, ]); const taxable = items.reduce((s, i) => s + i.price * i.qty, 0); const gst = items.reduce((s, i) => s + i.price * i.qty * i.gst / 100, 0); return ( <>

Cart

{items.length} items · saved locally to this device
{items.length === 0 ?
Cart is empty. Add items from the Item catalogue.
: ( <>
Item · Vendor
Unit price
Quantity
Subtotal
{items.map(it => (
{it.name}
{it.desc} · {it.vendor}
{inrFull(it.price)}
setItems(items.map(x => x.id === it.id ? { ...x, qty: Number(e.target.value) || 0 } : x))} style={{ height: 28, width: 64, textAlign: "right", fontSize: 12 }} />
{inrFull(it.price * it.qty)}
))} )}
Taxable
{inrFull(taxable)}
GST
{inrFull(gst)}
Grand total {inrFull(taxable + gst)}
The selected site pre-fills as place of delivery on the new PO.
); }; /* ═══════════════════ IMPORT PO ═══════════════════ */ const ImportPOPage = ({ go }) => ( <>

Import PO from Excel

Upload a file in Pelagia's standard PO template format
  Drop .xlsx file here or browse
Template downloadable from /docs/po-template.xlsx
Q-Mahalakshmi-2841.xlsx · 84 KB · parsed 5 line items detected

3 · Extracted line items — review before saving

{ORDERS[0].items.concat([ { name: "Marine Grease — EP2", desc: "Lithium complex, 18kg pail", qty: 6, unit: "ea", price: 4800, gst: 18 }, { name: "O-Ring Set — FKM", desc: "Assorted sizes 40-pc kit", qty: 2, unit: "set", price: 2400, gst: 18 }, ]).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))}
); Object.assign(window, { SitesPage, SiteDetailPage, VesselsPage, AccountsPage, UsersPage, CartPage, ImportPOPage });