Screens reference
Every page in the live app (https://idp.ethanseow.com), grouped by nav area: what each page is, what it shows, what you can do, and the API behind it. For task walkthroughs see Users; for the full endpoint list see Functions.
A captioned screenshot set exists for the user-facing pages (kept out of git to avoid committing live client/member data); ask for an illustrated internal version.
The nav: Quotes · Builder · SOWs · Procurement▾ · P&L · Forecast · Carpentry▾ · Team · Admin,
with a 🌙/☀️ theme toggle and a workspace switcher (when you belong to more than one).
Sign in lands you on a launcher grouping every destination.
Sales
Quotes — /ui/quote_send_accept
Send & accept quotes, and import a BOQ into a structured quote.
- You see: every quote for the workspace (number · project · client · status pill · total), newest first.
- You can: open a quote (→ line items, totals, timeline, Send to client / Mark accepted); or Import a BOQ → grouped quote — drop a BOQ PDF (Gemini transcribes it) or paste CSV rows; lines are auto-classified onto the trade taxonomy and rolled into Section → Group → Line.
- Behind it: GET /quotes, GET /quotes/{id}, POST /quotes/{id}/send|accept, POST /quotes/extract-boq-pdf · extract-boq · import-boq.
Quote builder — /ui/quote_builder?quote_id=…
Build/edit a quote line by line.
- You see: the editable line grid (description · trade · qty · unit · unit-cost · markup · line price), running subtotal/total.
- You can: add lines from the catalog (bilingual 中⇄EN search, e.g. hinge finds 铰链), apply a cost suggestion from price history, recommend lines from a materials list (paste → auto-match SKUs → review → add), and save/apply templates. Lines are editable while the quote is draft.
- Behind it: GET/POST/PATCH/DELETE /quotes/{id}/lines, /quotes/{id}/lines/reorder|{line}/choose, /catalog/skus/search, /quotes/{id}/recommend-lines, /quote-templates.
BOQ view — /ui/boq?quote_id=…
The grouped Bill of Quantities for a quote, with sub-totals + PDF.
- You see: a summary table (section totals → grand total, always reconciling), then every section with group sub-totals and line items; Included = bundled, TBC = provisional (excluded from total).
- You can: Download PDF (server-rendered, branded) or Print; and verify flagged lines — a "verify trade" dropdown lets a human confirm the trade. Confirmations are learned per workspace, so the same line auto-classifies (unflagged) on future imports.
- Behind it: GET /quotes/{id}/boq, GET /quotes/{id}/boq.pdf, PATCH /quotes/{id}/lines/{line}/classify.
Quotation (printable) — /ui/quote?quote_id=…
A clean, standalone rendering of one quotation, grouped for printing/sharing. - You see: the quote rendered server-side, grouped by kind, formatted for a tidy print/PDF or a quick read — distinct from the editable Builder and the BOQ breakdown. - You can: print, or use it as the human-readable face of a quote.
SOWs & milestones — /ui/sow_milestones
Statements of Work (created when a quote is accepted) and their billing.
- You see: every SOW (number · project · client · status · milestone count · contract value); open one for milestones (name · % · amount), scheduled client invoices, variations.
- You can: Schedule invoices (draft invoices from the milestone schedule), Generate POs (draft purchase orders grouped by vendor).
- Behind it: GET /sows, GET /sows/{id}, POST /sows/{id}/invoices/schedule · /generate-pos, POST /billing/schedule-milestones.
Forecast — /ui/forecast
The weighted sales pipeline.
- You see: pipeline value weighted by win-probability, with expected margin.
- Behind it: GET /forecast/pipeline.
Project P&L — /ui/project_pnl
Per-project revenue vs cost.
- You see: projects with contract value; open one for revenue (contract) vs cost (committed POs / actual bills), gross margin, and cost variance.
- Behind it: GET /projects, GET /projects/{id}, GET /pricing/projects/{id}/actuals.
Margin tracker — /ui/margin_tracker
Where margin is set and watched across quotes/projects.
- You see: margin per quote/project against the workspace's margin rules.
- You can (admin): set margin rules and per-SKU cost overrides that feed the sell-side maths.
- Behind it: GET /admin/margin-tracker, POST /admin/margin-rules, POST /admin/sku-overrides.
Carpentry & warehouse
Warehouse — /ui/warehouse
Stock levels derived from an append-only ledger.
- You see: stat cards (SKUs/items · on-hand · reserved), a Record event form, and a low-stock table (item · available · reorder point).
- You can: record stock events (receive / consume / adjust…) and Draft reorder POs for everything under its reorder point.
- Behind it: GET /warehouse/{levels,low-stock,locations}, POST /warehouse/events, POST /approvals/reorder.
Inventory dashboard — /ui/inventory_dashboard
The visual at-a-glance view of stock.
- You see: total inventory value, distinct SKUs, units on hand, low-stock & negative-stock counts; value by material type (hardware · tools · lighting · boards · adhesives · fixings · finishing · measuring · electrical), value by location, and the most-urgent low-stock items.
- You can: spot where value sits and what's running low at a glance. Material type is inferred from each SKU's name; correct a mislabel by setting Material type on the SKU (Catalog). When SKUs span currencies, value is shown for the dominant currency with a "mixed currencies" note.
- Behind it: GET /warehouse/dashboard.
Job board — /ui/job_board
Fabrication work-orders by status.
- You see: work-orders grouped by stage (cutting → assembly → finishing → done).
- You can: open a work-order; create new ones; seed a cutting list from a quote.
- Behind it: GET /work-orders, POST /work-orders, POST /work-orders/{id}/seed-from-quote.
Work order — /ui/work_order?wo=…
One fabrication job in detail.
- You see: the work-order Status, its Cutting list (BOM), and a Draw stock panel.
- You can: move the job through stages, add BOM lines, and reserve/consume materials against the warehouse ledger (drawing stock from a chosen location).
- Behind it: GET /work-orders/{id}, POST /work-orders/{id}/status · /bom · /reserve · /consume, GET /warehouse/locations.
Approvals — /ui/approvals
The draft-and-approve gate (human-in-the-loop).
- You see: pending drafts for outward/irreversible actions (reorder PO, customer reply).
- You can: approve (executes) or reject. Internal stock effects auto-apply; only external actions queue here.
- Behind it: GET /approvals, POST /approvals/{id}/approve|reject.
Catalog & locations — /ui/catalog
SKUs, vendors, categories, and stock locations.
- You see: stock locations, vendors, categories, and the full SKU table (code · description · vendor · cost · reorder point/qty) — bilingual SKU names supported.
- You can: add/edit SKUs (with aliases), locations, set reorder thresholds, and set a material type override for the inventory dashboard.
- Behind it: GET /catalog/{skus,vendors,categories}, POST /catalog/skus, PATCH /catalog/skus/{id}, GET /warehouse/locations.
Procurement & pricing (Procurement▾ menu)
Purchase orders & bills — /ui/po_bill
POs with goods receipts and vendor bills.
- You see: every PO (vendor · project · status · GRN/bill counts · total); open one for line items, goods receipts, vendor bills.
- You can: Send to vendor (issue a draft PO), record a goods receipt, capture a bill, run the 3-way match, record a vendor payment.
- Behind it: GET /pos, GET /pos/{id}, POST /pos/{id}/send · /grn · /bills, POST /bills/{id}/match · /payments.
RFQ — /ui/rfq_compose
Request quotes from suppliers via magic links.
- You see: every RFQ (title · project · status · quote count).
- You can: create an RFQ, invite a vendor by email (issues a magic link — no account needed), Send, view received supplier quotes, Compare →.
- Behind it: GET /rfqs, GET /rfq/{id}, POST /rfq · /rfq/{id}/invite|send, GET /rfq/{id}/compare.
Supplier comparison — /ui/supplier_comparison?rfq_id=…
Side-by-side supplier quotes for an RFQ.
- You see: a line-by-line matrix across suppliers, cheapest cell marked ✓, totals + lead times.
- You can: Accept a supplier quote → drafts a PO automatically.
- Behind it: GET /rfq/{id}/compare, POST /supplier-quotes/{id}/accept.
Historical import — /ui/historical_import
Seed pricing intelligence from past quotes/bills.
- You see: recent imports (file · kind · status · rows/matched/pending).
- You can: upload a CSV/Excel/PDF; per-import, review fuzzy SKU matches (Link / New SKU). Each line lands as a price observation → smarter cost suggestions.
- Behind it: GET /imports, GET /imports/{id} · /candidates, POST /imports, POST /imports/candidates/{id}/resolve.
Design intake (sketch / scan → plan)
Sketch → priced quote — /sketch
Legacy VLM demo: a plan photo/sketch → walls, surfaces, fixtures → a priced quote.
- You see: project + dimensions inputs, a photo/scan dropzone, and steps to Decompose, Detect interior walls (VLM), doors, fixtures, and finishes.
- You can: run the extraction pipeline that absorbs the sketch2plan prototype (chain-solver, room detection) and turns a drawing into priced lines.
- Behind it: POST /projects/{id}/ingest/{photo,pdf,dxf,disto}, POST /plans/{id}/{solve,detect-walls,confirm-walls,detect-doors,detect-fixtures,extract-specs,apply-specs,…}, GET /plans/{id}.
Floorplanner — /ui/floorplanner?plan_id=…
The embedded 3D canvas (Floorplanner SDK), with the scene saved on the plan.
- You see: the Floorplanner iframe for a plan.
- You can: design in the canvas; the scene JSON is persisted on the plan and read back for downstream BOM work.
- Behind it: GET/POST /plans/{plan_id}/scene.
External pages (no login — magic-link)
Client portal — /ui/client_portal (client opens /portal/{token})
The client-facing quote approval page.
- The client sees: What you're approving (the quote summary) and an Approve this quote action — reached through a single-use magic link you issue from the project.
- You can (staff): issue the portal link for a project.
- Behind it: POST /projects/{id}/client-portal/issue, GET /portal/{token}, POST /portal/{token}/accept-quote.
Respond to RFQ (supplier) — /ui/supplier_response (supplier opens /supplier/respond/{token})
The supplier-facing quote submission page.
- The supplier sees: the RFQ lines and a Your quote form — no account required (magic link). They may optionally create a persistent supplier login.
- Behind it: GET /supplier/respond/{token}, POST /supplier/respond/{token}, POST /supplier/{signup,login,account/history}.
Admin
Team & roles — /ui/members
Workspace members, roles, and invitations.
- You see: members with role (owner / admin / manager / member / staff / viewer) and status; pending invitations.
- You can: change a member's role, invite by email, revoke invites. Roles map to granular permissions (who can write quotes, act on approvals, manage warehouse…).
- Behind it: GET /members, GET /members/invites, POST/PATCH/DELETE members & invites.
Security (2FA) — /ui/mfa_setup
Enrol an authenticator app (TOTP); scan the QR, confirm a code, save recovery codes. (Optional.)
- Behind it: POST /auth/mfa/enroll · /confirm · /disable.
Catalog admin — /admin
Bulk SKU management, designer assignment, and per-user notification channels.
- Behind it: POST /admin/sku-overrides · /margin-rules · /projects/{id}/designer · /users/{id}/channels.
Webhook outbox — /ui/admin_outbox
Inspector for outbound integration deliveries.
- You see: the IntegrationOutbox rows, filterable by provider + status — what we've sent (or failed to send) to Attio / PandaDoc / ERP / etc.
- You can: retry a failed/stuck row (resets it to pending for the next drain), or replay a webhook event.
- Behind it: GET /admin/outbox, POST /admin/outbox/{id}/retry, POST /admin/webhooks/{event_id}/replay.
Auth & utility pages
These are reached as part of a flow rather than from the nav:
| Page | Route | Purpose |
|---|---|---|
| Home launcher | / |
Post-sign-in tile launcher grouping every destination. |
| Quick start / Help centre | /ui/help |
In-app version of this wiki (generated from users + this page). |
| Invite accept | /ui/invite_accept?token=… |
A teammate accepts a workspace invitation. |
| Verify email | /ui/verify_email?token=… |
Confirm an email address. |
| Forgot password | /ui/forgot_password |
Request a reset link. |
| Reset password | /ui/reset_password?token=… |
Set a new password from the emailed link. |
See Functions for the complete endpoint reference, or the live interactive
API at /docs.