WFDASH-WEBAPP/docs/manager-orders.md

102 lines
4.6 KiB
Markdown
Raw Normal View History

# Manager orders view (`/manager`)
A hidden, installable PWA for the managers: one link, opened on a
phone, showing where every order is — **New / Processing / Completed**.
It is deliberately not in the sidebar. The route lives in the
`(mobile)` route group, outside `(app)`, so `<Sidebar />` never
renders on it and there is no nav entry anywhere.
## Where things live
| Piece | File |
|---|---|
| Page (route group) | `src/app/(mobile)/manager/` — `layout.tsx`, `auth-gate.tsx`, `page.tsx`, `manager-orders-view.tsx` |
| Read model | `src/lib/server/manager-orders-service.ts` |
| API route | `src/app/api/v1/manager-orders/route.ts` |
| PWA manifest | `public/manifest-manager.json` |
| Service worker | `public/sw-manager.js` |
| SW registration | `src/components/service-worker-registration.tsx` (adds `/manager` → `/sw-manager.js`) |
| Bucket + settled-status contract | `src/lib/item-status-ui.ts` |
| Shared window filter | `settledWindowFilter` in `src/lib/server/orders-service.ts` |
## The three buckets
`PIPELINE_GROUP_ITEM_STATUSES` in `src/lib/item-status-ui.ts` is a
coarse view of the desk's eight workflow tabs. Both surfaces read the
same status lists, so they can never disagree about what a status
means.
| Bucket | Statuses | Orders (live, 2026-10-26) |
|---|---|---|
| **New** | `pending`, `approval_needed`, `approval_rejected` | 8 |
| **Processing** | `processed`, `po_generated`, `collection_pool`, `assigned`, `ready_delivery`, `collected`, `stock_verified`, `picking`, `ready_invoice`, `stock_booked`, `with_driver` | 79 |
| **Completed** | the settled set, windowed to 7 / 14 / 30 days | 65 / 112 / 200 |
An order lands in a bucket when **at least one of its lines** is in
that bucket. That is why Processing holds 79 orders for only 206
open lines: 45 of those orders have only ONE line left, and 40 have
other lines already invoiced or collected. `cancelled` and `removed`
are deliberately outside the pipeline (they live on /orders/archive).
## The Completed window — read before changing it
There is no single "finished at" column. `SETTLED_STATUSES` +
`SETTLED_DATE_FIELDS` in `src/lib/item-status-ui.ts` encode which
field dates each settled status:
| Status | Dated on | Why |
|---|---|---|
| `delivered` | `delivered_at` | populated on only 2 of 12,957 lines; the rest are legacy rows whose `updated` is the MIGRATION timestamp, so an un-dated delivered row is ARCHIVED, not recent |
| `invoiced` | `invoiced_date` | the only properly stamped settlement date in the schema (2,199 lines) |
| `completed`, `checked_in`, `delivery_note` | `updated` | no dedicated column exists, so `updated` is the only signal |
`primary` and `fallback` are mutually exclusive. Giving a status both
would resurface archived rows every time an old row was touched.
Before this change the Completed tab matched **0** orders: the filter
only looked at `delivered` + `completed` dated on `delivered_at` /
`updated`, and the business stopped writing `delivered` on 2026-08-14
(the live pipeline now ends at `invoiced` / `checked_in`). It now
matches 341 orders over 30 days, 112 over 7.
`settledWindowFilter(days)` is shared with `/orders` (30 days) — only
the window length differs, so the two surfaces never disagree.
## The preview popup is reused, not rebuilt
The service emits `UniversalHit`s — the same shape the universal
search returns — and the page hands them to the existing
`SearchPreviewDialog` (`src/app/(app)/search/search-preview.tsx`). So
the manager gets the desk's quick-overview verbatim: status chips,
stats, invoice refs, delivery-note numbers, the line list and the
lifecycle timeline, plus a link out to the full order page.
**Read-only.** Nothing on the phone can change an order's state.
## API
```
GET /api/v1/manager-orders?group=new|processing|completed
&window=5|7|14|30
&search=<order no|customer|company|rep>
&page=1&perPage=20
```
Auth is the PB browser session in the `Authorization` header — the
same `assertBrowserSession` pattern as `/api/v1/boss-dashboard`. The
response carries `counts` (distinct orders per bucket), `total`,
`totalPages`, the page of `orders` and `viewerName`.
## Installing it on a phone
Open the link on the phone → browser menu → **Add to Home Screen**. It
installs as a standalone app ("WFDASH — Orders") with its own icon,
launched in portrait with no browser chrome. iOS needs the
`apple-mobile-web-app-capable` meta, which the layout sets (Next 16
only emits the standardised name, which iOS ignores — see the note in
`(mobile)/m/layout.tsx`).
The service worker precaches the shell and never caches `/api/`, so a
stale order count can never be shown.