191 lines
12 KiB
Markdown
191 lines
12 KiB
Markdown
# 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.
|