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