WFDASH-WEBAPP/docs/driver-app-data-source.md
SJCoetz 7df7a8850f feat(item-history): add getItemHistory service with 5 PB queries
Co-authored-by: Orca <help@stably.ai>
2026-07-29 14:51:58 +02:00

191 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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-map` shows 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:
1. **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.
2. **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_id`** is a relation, so in PocketBase filter syntax you compare against the relation's record id.
- **`item_status`** — include `assigned` (active work) and optionally `collected` (just-picked-up, before the operator flips it forward). Do **not** include `collection_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_status` from `assigned` → `collected` and set `drv_status = "collected"`, `drv_collected_at = <now>`. The operator's collection-confirm page then handles the rest. Use the existing `/api/v1/order-items/<id>/status` route (Bearer `AGENT_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/stops` that runs the filter above through the admin PB client and returns shaped JSON, **or**
- A small wrapper around the existing `/api/v1/order-items` list 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
```js
// 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_status` ever diverge from `item_status` for the driver? (Today they mirror — pick `item_status` as truth.)
- Should the driver see **historical** collected items from today, or only active `assigned`? (`collected` rows 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 shows `po_number`; safe default = show on card.