Add full Tangerine brand theme with light and dark mode variants to the app theme system. Add four new design and integration documents for collection pool, map and driver app workflows. Add initial scaffolding for three new plugin source directories for the collection pool bulk assignment feature.
12 KiB
Driver App — Data Source & Integration Notes
Audience: the agent building the driver-facing app. Author context: WFDash is the operator app (Next.js + PocketBase). This doc describes how the driver app should read the same data so the operator's "Assign driver" action on
/collection-mapshows up on the driver's phone in real time.
TL;DR
A driver's stop list = every order_items row where drv_id == currentDriver.id and item_status == "assigned", regardless of the ready_for_collection flag. Show ready rows one colour, not-yet-ready rows another colour. Do not filter ready_for_collection server-side — that flag controls whether the operator may assign the row, not whether the driver should see it once assigned.
Worked example
"Bright Packaging" supplier, assigned to Driver A. The supplier has 6 items on the same PO:
| id | item_status | drv_id | ready_for_collection | what driver sees |
|---|---|---|---|---|
| 1 | assigned | A | true |
blue pill — "Ready" |
| 2 | assigned | A | true |
blue pill — "Ready" |
| 3 | assigned | A | true |
blue pill — "Ready" |
| 4 | assigned | A | false |
amber pill — "Waiting — ask supplier" |
| 5 | assigned | A | false |
amber pill — "Waiting — ask supplier" |
| 6 | assigned | A | false |
amber pill — "Waiting — ask supplier" |
The driver app should show all 6 under one supplier card. The driver picks up the 3 ready items and asks the supplier about the other 3 — that's the whole point of surfacing both states.
1. Topology
┌──────────────┐ HTTPS ┌─────────────────────┐
│ Driver App │ ──────────► │ WFDash Next.js API │ (recommended)
│ (mobile) │ │ /api/v1/... │
└──────────────┘ └──────────┬──────────┘
│ admin client
▼
┌─────────────────────┐
│ PocketBase │
│ - order_items │
│ - suppliers │
│ - users (drivers) │
└─────────────────────┘
Two reasonable shapes:
- Through the WFDash API — easier to evolve, single auth surface, but WFDash already does its own UI-side loading; no dedicated driver endpoint exists yet. You'll need to either add one or call an existing route.
- Direct PocketBase — driver app talks to PB with its own auth. Faster, but you re-implement filters, expansions, and auth yourself.
Recommendation: start direct-to-PocketBase for the read path. The schema is small and the filters are simple. Add a WFDash proxy only if you need server-side validation (e.g., before letting the driver mark something collected).
2. Source collection: order_items
This is the canonical row. The operator's "Assign driver" modal on /collection-map flips these fields:
| field | type | meaning |
|---|---|---|
item_status |
select | One of pending, processed, po_generated, collection_pool, assigned, collected, stock_verified, picking, ready_delivery, delivered, cancelled. For driver app purposes, you care about assigned (in your list) and collected (you've picked it up — drop from list or move to history). |
drv_id |
relation → users |
The driver this row is assigned to. Filter on this. |
drv_status |
select | Operator-side mirror of item_status for drivers — unassigned, assigned, en_route, collected, failed. You can ignore this; item_status is the truth. |
drv_assigned_at |
date | When the operator hit "Assign". Sort by this if you want a "newest first" list. |
ready_for_collection |
bool | Operator-side gate. Set to true by /collection-pool "Mark ready". Do not filter on this — surface it as a colour. |
notes |
text | Operator note (often set alongside ready_for_collection=true, e.g. "Boxes at back loading dock"). Show this prominently when set. |
supplier |
relation → suppliers |
Expand to get suppliername, email, coordinates (semicolon-separated "lat;lng" string in WFDash), phone, etc. |
description |
text | Item description. |
quantity |
number | Quantity ordered for this line. |
po_description |
text | PO-level description if different from the item line. |
order |
relation → orders |
Expand to get order_number. |
purchase_order |
relation → purchase_orders |
Expand to get po_number. |
Other fields you can ignore (for now)
cost_price, selling_price, markup, markup_percent, qty_*, problem_flag, problem_type, problem_notes, legacy_order_item_id, in_stock, driver_status, delivery_status, invoice_status, delivered_at, drv_collected_at.
3. The driver-stop query
PocketBase filter syntax
collection: order_items
filter: drv_id = "<driverId>" && (item_status = "assigned" || item_status = "collected")
sort: -drv_assigned_at
expand: supplier, purchase_order, order
drv_idis a relation, so in PocketBase filter syntax you compare against the relation's record id.item_status— includeassigned(active work) and optionallycollected(just-picked-up, before the operator flips it forward). Do not includecollection_pool(operator hasn't picked a driver),delivered/cancelled(terminal), or anything else.- Expand keeps you off N+1 lookups. PB will inline supplier / PO / order objects.
Important: do NOT add ready_for_collection=true to the filter
The operator-side gate that prevents assignment does not mean the driver should be blind to unready items. The whole point is for the driver to nudge the supplier ("by the way, I see you have a few more items on the PO — are they ready too?"). Server-side filtering on ready_for_collection defeats the purpose.
4. UI — colour and grouping
Match WFDash's existing palette so when an operator looks over the driver's shoulder it lines up:
| State | Pill label | Colour (Tailwind) |
|---|---|---|
item_status == "collected" |
"Collected" | emerald — bg-emerald-100 text-emerald-900 border-emerald-300 |
ready_for_collection == true (not collected) |
"Ready" | blue — bg-blue-100 text-blue-900 border-blue-300 |
ready_for_collection == false (not collected) |
"Waiting — ask supplier" | amber — bg-amber-100 text-amber-900 border-amber-300 |
Counts strip per driver (header)
6 stops · 🟢 0 collected · 🔵 3 ready · 🟠 3 waiting
Hide segments that are zero (omit "0 collected" if nothing's collected yet). This mirrors /collection-map's right-sidebar driver cards exactly.
Grouping
Group rows by supplier.id. Within a supplier, sort however you like. The supplier card header should show the supplier name + the counts strip for that supplier's items:
┌─ Bright Packaging ─────────────────────────┐
│ 6 items · 🔵 3 ready · 🟠 3 waiting │
│ • 100× Cardboard boxes [Ready] │
│ • 50× Bubble wrap rolls [Ready] │
│ • 20× Tape dispensers [Ready] │
│ • 10× Pallet wrap [Waiting] │
│ • 5× Strapping rolls [Waiting] │
│ • 2× Stretch film [Waiting] │
│ [Open in maps]│
└────────────────────────────────────────────┘
Notes
If notes is set on a row, surface it inline (the operator often writes "back loading dock", "ask for John", "ring bell"). WFDash renders these as a hover tooltip; in a phone UI a chevron expand or bottom-sheet works fine.
5. "What should the driver be able to do?"
Out of scope for this doc, but: the only writes the operator expects from the driver app today are:
- Mark collected — flip
item_statusfromassigned→collectedand setdrv_status = "collected",drv_collected_at = <now>. The operator's collection-confirm page then handles the rest. Use the existing/api/v1/order-items/<id>/statusroute (BearerAGENT_API_KEY) if going through WFDash, or hit PB directly. - Report a problem — set
problem_flag = true,problem_type = "supplier",problem_notes = "<text>". The operator surfaces these on/collection-confirm.
Do not let the driver flip ready_for_collection or reassign themselves — those are operator-only actions.
6. Auth
Drivers are PB users records. They log in with email/password (PB's built-in users collection, _pb_users_auth_). The driver app stores the PB auth token after login and passes it as Authorization: <token> on every API call. PB enforces row-level listRule / viewRule (@request.auth.id != '') — drivers will see only what they're allowed to read, which today is all order_items. You'll likely want to tighten that to drv_id = @request.auth.id via PB rule, but that's a schema change — confirm with the WFDash maintainer first.
7. What changes you'll need from WFDash
If you go the direct-PB route, you can ship without any WFDash changes — just consume the existing schema and filter.
If you go the WFDash-API route, you'll need one of:
- A new endpoint
GET /api/v1/drivers/me/stopsthat runs the filter above through the admin PB client and returns shaped JSON, or - A small wrapper around the existing
/api/v1/order-itemslist route (if you want to reuse pagination/auth shape).
Either way, the filter logic is identical to section 3.
8. Quick reference — the canonical filter
// PocketBase JS SDK
const stops = await pb.collection("order_items").getList(1, 200, {
filter: 'drv_id = "' + driverId + '" && (item_status = "assigned" || item_status = "collected")',
sort: "-drv_assigned_at",
expand: "supplier,purchase_order,order",
});
Each row in stops.items carries everything section 2 lists. Use expand.supplier.suppliername, expand.supplier.coordinates, etc. for display.
9. Open questions for the operator (WFDash maintainer)
- Should
drv_statusever diverge fromitem_statusfor the driver? (Today they mirror — pickitem_statusas truth.) - Should the driver see historical collected items from today, or only active
assigned? (collectedrows have already moved past the driver's hand — usually safe to drop from the live list, but the operator may want a "today's history" pane.) - Are PO numbers (
po_number) surfaced on the supplier card, or only on drill-in? Operator's map showspo_number; safe default = show on card.