feat(GRO-2158): route planner page at /admin/routes
CI / Test (pull_request) Successful in 27s
CI / Lint & Typecheck (pull_request) Successful in 34s
CI / Build & Push Docker Image (pull_request) Successful in 53s

Build the Route Optimization route planner UI in groombook/web:
- New /admin/routes page with date picker + groomer selector
  (auto-filtered for groomer role via GET /api/staff/me)
- react-leaflet + OpenStreetMap tiles, lazy-loaded so Leaflet ships
  as a separate code-split chunk (RouteMap-*.js / .css)
- Map with numbered teardrop divIcon pins + route polyline, auto-fit
  bounds; numbered pins avoid the Leaflet default-marker asset issue
- Stop-list panel: per-stop card with time, client name, address,
  travel time from previous stop, and tight-schedule conflict flag
- Total travel time + distance summary + route status badge
  (draft / optimized / in_progress / completed)
- "Optimize" button wired to POST /api/routes/optimize
- Reads daily route via GET /api/routes/daily?staffId=&date=
- Tests for stop rendering, summary, role auto-filter, optimize call
- Updated UAT_PLAYBOOK.md §5.28 — Route Planner Page

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
Flea Flicker
2026-06-09 01:48:35 +00:00
parent 98c8a7bb83
commit bb306ea41d
7 changed files with 718 additions and 3 deletions
+20
View File
@@ -471,6 +471,26 @@ These cases guard against the regression where an SSO-bridge customer (no `?sess
| TC-WEB-5.26.3 | Impersonation flow reschedule is unchanged (no regression) | 1. With an active impersonation session (`?sessionId=<active>`), load `/`. 2. Click **Reschedule** on an appointment. 3. Pick a date. | `GET /api/book/availability` includes `X-Impersonation-Session-Id` equal to the impersonation `sessionId` (not `portalSessionId`). Returns 200. Behaves identically to the pre-fix build. |
| TC-WEB-5.26.4 | No `X-Impersonation-Session-Id` is empty / null | From TC-WEB-5.26.1, inspect every `/api/portal/*` and `/api/book/*` request. | No request has an empty or `null` `X-Impersonation-Session-Id` header. |
### 5.28 Route Planner Page (GRO-2158)
The admin Route Planner lives at `/admin/routes`. It shows a groomer's geocoded appointment stops for a chosen date on a `react-leaflet` / OpenStreetMap map (numbered pins + a connecting polyline), a stop-list panel, a travel-time/distance summary, a route status badge, and an **Optimize** button wired to `POST /api/routes/optimize`. Leaflet is loaded via a dynamic import so it ships as a separate code-split chunk. Groomers are auto-filtered to their own route (no groomer selector); managers/receptionists pick a groomer.
**Pre-conditions:**
- Sign in to `/admin` as a manager (e.g. uat-manager) and, separately, as a groomer (uat-groomer).
- At least one groomer has appointments on the test date whose clients have geocoded addresses.
| # | Scenario | Steps | Expected |
|---|----------|-------|----------|
| TC-WEB-5.28.1 | Page loads and is reachable from nav | 1. Sign in as a manager. 2. Click **Routes** in the admin nav. | URL is `/admin/routes`. The "Route Planner" heading, a Date picker, a Groomer selector, and an **Optimize** button render. No console errors. |
| TC-WEB-5.28.2 | Leaflet map is code-split | 1. Open DevTools → Network (JS filter). 2. Load `/admin/reports` first, confirm no `RouteMap` chunk loads. 3. Navigate to `/admin/routes`. | A separate `RouteMap-*.js` chunk (and `RouteMap-*.css`) is fetched only when the Routes page renders, not on other admin pages. |
| TC-WEB-5.28.3 | Map shows numbered pins + polyline | Select a groomer + date that has a built route with ≥2 geocoded stops. | The OSM map renders with one numbered pin per stop (1, 2, 3…) and a polyline connecting them in order. Tile attribution to OpenStreetMap is visible. |
| TC-WEB-5.28.4 | Stop-list panel cards | Inspect the panel beside the map. | Each stop card shows the stop number, client name, appointment time, address, and travel time from the previous stop (stop 1 shows "Start of route"). |
| TC-WEB-5.28.5 | Summary + status badge | Inspect the summary bar and badge. | Stops count, total travel time, and total distance (km) are shown. A status badge reads one of Draft / Optimized / In progress / Completed matching the route's status. |
| TC-WEB-5.28.6 | Optimize button | Click **Optimize**. | A `POST /api/routes/optimize` with `{ staffId, date }` fires. On success the map, stop order, summary, and status badge refresh. Any skipped (non-geocoded) clients surface as a warning. |
| TC-WEB-5.28.7 | Groomer role auto-filter | Sign in as a groomer and open `/admin/routes`. | No groomer selector is shown. The page loads the signed-in groomer's own route for the selected date. The groomer cannot view another groomer's route. |
| TC-WEB-5.28.8 | Empty / no-route state | Select a date with no appointments. | The map area and stop panel show a friendly empty state ("No stops…"). No crash; **Optimize** is still clickable. |
## 6. Pass/Fail Criteria
**Pass:**