Adds artifacts/fleet-ops-release/final-summary.md with the complete evidence trail for this release: commits, branding, locale/translation/knowledge-base coverage, adaptive Demo Guide behaviour per breakpoint, Data Quality/Automation/Audit/clickable-row improvements, full test results (backend, lint, build, 92 Playwright tests) re-run against the local stack, an isolated clean-checkout drill, and both the feature-branch and post-merge master deployments to Unraid -- plus 10 screenshots across the three languages, desktop and mobile. Updates PROJECT_STATE.md with the corresponding summary. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
156 lines
17 KiB
Markdown
156 lines
17 KiB
Markdown
# Fleet Ops release — final-product-polish evidence
|
||
|
||
## Result: PASS
|
||
|
||
## Commits
|
||
|
||
- Original feature-branch baseline before this task: `257a4cf` (`docs(polish): audit finale demo-afwerking`)
|
||
- Feature-branch commits added this task, on `feat/mobilityops-functional-completion`:
|
||
- `337f871` — polish: rebrand to Fleet Ops, add trilingual i18n, adaptive demo guide, and UX overhaul
|
||
- `845db14` — fix: mobile topbar overflow at 421-440px and add trilingual responsive coverage
|
||
- Feature branch final commit: `845db14e172539b1d10e40f6a3249a72122deb41`
|
||
- `master` before merge (verified against the previously recorded baseline): `e0c7ed60112510687627d20a957af91c8b9db7f8` — unchanged, no unexpected commits, no conflicts (confirmed via `git merge-tree` dry run before merging)
|
||
- Merge commit on `master`: `18a765d62345ea9a6660d04fb868f218cf4d0b6e` (`merge: release Fleet Ops multilingual demo`, `--no-ff`)
|
||
- Final `master` commit (pushed and deployed): `18a765d62345ea9a6660d04fb868f218cf4d0b6e`
|
||
- Deployed commit on Unraid (`.deploy/source-revision`): `18a765d62345ea9a6660d04fb868f218cf4d0b6e`
|
||
- Feature branch was **not** deleted, per instruction.
|
||
|
||
## URL
|
||
|
||
- Live review deployment: `http://192.168.10.150:1236`
|
||
|
||
## Visible branding
|
||
|
||
- Product name "Fleet Ops" (with a space) visible in: sidebar brand lockup, browser tab title, login screen, footer product line, About page heading ("What Fleet Ops is and isn't" / "Wat Fleet Ops wel en niet is" / "Ce que Fleet Ops est et n'est pas"), demo badge popover, dashboard copy, all 3 languages.
|
||
- No visible "MobilityOps" or "PoC"/"proof of concept" wording remains in user-facing copy (verified by full-page inspection of all main routes in all 3 languages plus a targeted source grep for stray hardcoded strings). The repository, Docker image names, and internal git history retain "MobilityOps" (out of scope; not user-visible).
|
||
- Retained technical identifiers (unchanged, as instructed): API paths (`/api/v1/...`), Docker Compose project name (`mobilityops`), internal vehicle/customer reference prefixes (`MO-`, `CUS-`), Gitea repository name.
|
||
|
||
## Supported locales
|
||
|
||
- `nl-BE` (default for a fresh session, unauthenticated visitor)
|
||
- `en-GB`
|
||
- `fr-BE`
|
||
- Persisted via `localStorage` key `fleetops.language`; survives refresh, logout/login, and demo reset. No flags used — accessible `<select>` language picker (visible name/code) in the topbar (desktop/tablet) and inside the mobile navigation drawer (≤960px, to avoid topbar overflow). `document.documentElement.lang` kept in sync. All dates/numbers rendered via `Intl.DateTimeFormat`/`Intl.NumberFormat` (`Europe/Brussels` timezone).
|
||
|
||
## Translation coverage
|
||
|
||
- `frontend/e2e/i18n-coverage.spec.ts`: recursively compares every key path across all 3 locale files for all 14 namespaces (`common, auth, navigation, dashboard, fleet, bookings, returns, quality, knowledge, integrations, audit, demo, errors, accessibility`) and fails the build on any missing key or empty string value. **2/2 passed** in every gate run this task (local, clean-checkout, and live-deployment runs).
|
||
- Command: `npx playwright test e2e/i18n-coverage.spec.ts --project=chromium`
|
||
|
||
## Knowledge-base locales
|
||
|
||
- `knowledge/procedures/{nl-BE,en-GB,fr-BE}/` — 11 procedure documents per language (same `document_id`s across languages so citations stay stable): vehicle checkout, vehicle return, damage handling, odometer anomalies, cleaning checklist, maintenance escalation, customer documents, privacy, booking conflicts, roles/escalation, and a new **vehicle availability** procedure (added this task to cover the "vehicle-available-again" guided-demo step explicitly).
|
||
- `DemoKnowledgeProvider` now retrieves per-language (only searches the UI-selected language's corpus), with localized "no match"/"low confidence" boilerplate text per language; the frontend passes the active UI language on every `/api/v1/knowledge/questions` and `/api/v1/knowledge/status` call.
|
||
- Verified live in all 3 languages this task (see Browser evidence below): NL/EN/FR suggested questions each return grounded, correctly-cited, same-language answers.
|
||
- Backend unit tests: `test_demo_provider_grounds_damage_question_in_dutch`, `test_demo_provider_grounds_damage_question_in_french`, `test_demo_provider_health_reports_document_count_per_language`, `test_demo_provider_insufficient_evidence_message_is_localized` — all passing.
|
||
|
||
## Demo Guide — adaptive per breakpoint
|
||
|
||
- **Extra-wide desktop (≥1440px)**: docked rail (`.demo-guide-panel.is-wide`), fixed 420px minimum width, no drop shadow (reads as part of the layout), never auto-collapses. Verified: `demo-guide.spec.ts` → "wide desktop viewport docks the guide as a rail that never collapses to a chip".
|
||
- **Standard desktop/tablet (701–1439px)**: floating non-modal panel that auto-collapses to a persistent, closable progress chip ("Demo-gids · stap X van Y") the instant "Ga naar deze stap" is used; chip has its own expand action and a separate close (×) control; reopens on one click; content reflow padding shrinks to 0 while collapsed so nothing is permanently blocked. Verified: 3 dedicated tests in `demo-guide.spec.ts`.
|
||
- **Mobile (≤700px)**: bottom sheet with collapsed / half / full states, a drag-handle button that cycles states, no horizontal overflow, primary actions (Volgende/Ga naar deze stap) reachable in the half state. Verified: `demo-guide.spec.ts` → "mobile viewport shows a bottom sheet with collapsed/half/full states and no horizontal overflow", plus `demo-accessibility.spec.ts` → "demo guide is usable as a mobile bottom sheet".
|
||
- **Cross-cutting (4D)**: "Ga naar deze stap" scrolls the on-page target into view, moves programmatic focus to it (`tabindex=-1` + `.focus()`), and applies a 2.2s outline pulse (`.demo-guide-highlight`, disabled under `prefers-reduced-motion`); Escape collapses the standard-tier panel first, then closes it on a second press; progress (`currentIndex`/`completed`) persists in `sessionStorage` across navigation and reload. Verified: `demo-guide.spec.ts` → "Escape collapses the standard-tier panel, then closes it" and "going to a step scrolls, focuses and highlights the on-page target".
|
||
- Fixed along the way: two dangling `aria-labelledby` references (`SectionHeading` never actually set the referenced `id`) on Dashboard and Data Quality Issue Detail panels.
|
||
|
||
## Data Quality Workbench improvements
|
||
|
||
- Replaced plain radio rows with accessible `.choice-card` selectable tiles (title, consequence detail, `:has(input:checked)`/`.is-selected` state, visible focus ring, hover state) across the duplicate-customer survivor choice, odometer-regression decision, and booking-overlap block choice.
|
||
- Clear action hierarchy: primary resolve/apply/merge action uses `.button-primary`; defer uses a de-emphasized `.button-tertiary`; reject uses `.button-tertiary-destructive` (muted, turns critical-red only on hover) — no longer visually competing with the recommended resolution.
|
||
- Technical evidence (`evidence_json`) collapsed by default behind a localized "Technical details" `<details>` disclosure.
|
||
- Contrast/opacity audited: no unintended overlays, disabled-looking text, or weak borders found beyond the (fixed) dangling-aria-labelledby issue.
|
||
|
||
## Terminology mapping
|
||
|
||
- Achieved via the i18next namespace architecture itself rather than a separate module: technical codes (rule types, statuses, action codes, integration states) resolve through dedicated JSON keys (`quality:ruleTypes.*`, `quality:list.status*`, `audit:actions.*`, `integrations:statusLabels.*`, `fleet:statuses.*`, `bookings:statuses.*`) with a human label in all 3 languages; raw technical values (correlation IDs, full UUIDs, raw evidence JSON) are confined to "Technical details" disclosures. Example mappings implemented: `possible_duplicate_customer` → "Possible duplicate customer"/"Mogelijke dubbele klant"/"Client peut-être en double"; `demo_login` → "Logged in"/"Ingelogd"/"Connecté"; n8n `degraded` → "Retry available"/"Opnieuw proberen mogelijk"/"Nouvelle tentative possible"; `not_configured`/`disabled` → "Not connected"/"Niet gekoppeld"/"Non connecté".
|
||
|
||
## Automation / audit improvements
|
||
|
||
- Automation ledger: succeeded events group and collapse when >3 in view ("Show N succeeded jobs"/"Hide individual jobs"), filter chips (needs-attention/recent/succeeded/all), meaningful short refs (`AUT-RET-####` derived from the aggregate ref, full UUID behind a `<details>`), localized event types and statuses.
|
||
- Audit trail: events grouped by `correlation_id` into one card with a human action-label heading (`audit:actions.*`), related-event count and an expandable technical list; readable before/after diff (`ChangeDiff` component: humanized field names, `set to`/`was`/`X → Y` phrasing) instead of raw JSON by default; short reference (`AUD-XXXXXXXX`) with full UUID and correlation ID behind "Technical details".
|
||
|
||
## Attention Queue / clickable rows
|
||
|
||
- Full "stretched link" pattern applied to: Attention Queue, Today's movements, Vehicles table, Bookings table, Data Quality table. Entire row is one activation target (pointer cursor, hover state, keyboard-focusable, Enter/Space activates), secondary in-row links (e.g. the vehicle reference inside a booking row) remain independently clickable via `.cell-link { z-index: 2 }` layered above the row overlay.
|
||
- Dedicated tests in `frontend/e2e/clickable-rows.spec.ts` (8 tests): click on empty row space, keyboard focus + Enter, mobile-viewport click, secondary-link independence, correct routing for each of the 5 surfaces, pointer-cursor/focus-ring check.
|
||
|
||
## Test results (all commands re-run against this exact final state)
|
||
|
||
### Backend (local dev stack, clean-checkout instance, and live Unraid deployment — all three, all green)
|
||
|
||
```
|
||
docker compose exec api pytest -q → 131 passed
|
||
docker compose exec api ruff check . → All checks passed!
|
||
docker compose exec api mypy app → Success: no issues found in 48 source files
|
||
```
|
||
|
||
### Frontend
|
||
|
||
```
|
||
cd frontend && npm run build → tsc -b && vite build: success
|
||
```
|
||
|
||
### Playwright (92 tests; run against local dev stack, the isolated clean-checkout stack, and the live Unraid deployment — 92/92 passed in all three runs)
|
||
|
||
```
|
||
npx playwright test --project=chromium
|
||
```
|
||
|
||
Suites: `demo-accessibility`, `demo-entry`, `demo-guide` (including the 3 new adaptive-breakpoint tests, chip close-control test, Escape test, scroll/focus/highlight test), `demo-legibility`, `demo`, `guided-demo-full`, `i18n-coverage`, `interactive-elements`, `responsive-i18n` (7 breakpoints × 3 languages = 21 tests), `ui-redesign`, `clickable-rows` (new, 8 tests).
|
||
|
||
## Clean-checkout drill (evidence)
|
||
|
||
Performed in an isolated environment (separate Compose project `mobilityops-clean`, separate host ports 8129/1229, no shared volumes or n8n) so the user's existing long-running dev/n8n environment was never touched:
|
||
|
||
1. `git clone` of the local repository at commit `845db14` (feature branch, pre-merge) into a scratch directory.
|
||
2. `cp .env.example .env` (project name and ports overridden for isolation only).
|
||
3. `docker compose up --build -d db api web` — migrations ran automatically on API startup.
|
||
4. `docker compose exec api python -m app.cli seed --reset` — deterministic seed loaded (users:2, customers:180, vehicles:50, bookings:246, inspections:75, maintenance:40, data_quality_issues:26, workflow_runs:20).
|
||
5. `docker compose exec api pytest -q` → 131 passed. `ruff check .` → clean. `mypy app` → clean.
|
||
6. `npm ci && npm run build` → clean build.
|
||
7. `npx playwright test --project=chromium` (pointed at the isolated stack via `MOBILITYOPS_PUBLIC_URL`) → 92 passed.
|
||
8. Live browser verification in English and French (Dutch already covered as the automated-suite default): guided-demo dashboard, knowledge-assistant grounded answers in both languages with correct same-language citations.
|
||
9. `POST /api/v1/demo/reset` → `scenario_integrity: {"all_ready": true, "not_ready": []}`.
|
||
10. Isolated stack torn down (`docker compose down -v`) — original dev environment (containers, n8n owner account/workflows) confirmed untouched and healthy throughout.
|
||
|
||
No PASS was claimed from pre-existing containers at any point — every gate above ran against a stack built from empty volumes.
|
||
|
||
## Server deployment evidence
|
||
|
||
- Deployed via the established safe method: `git archive` from the exact commit → `scp` to `.deploy/source-<sha>.tar.gz` on Unraid → extract → update `.deploy/source-revision` → `docker compose -p mobilityops -f compose.yaml -f compose.unraid.yaml up --build -d api web` (db never rebuilt; server `.env` and named volumes — Postgres, n8n — preserved throughout).
|
||
- Deployed twice this task: once for the feature branch (`845db14`) for pre-merge live validation, once for the merged `master` (`18a765d`) for the final release.
|
||
- Post-deploy, both times: migrations confirmed at head (`e7b08389f47f`), reseed run, `pytest`/`ruff`/`mypy` re-run in the container (all green), full 92-test Playwright suite re-run against the live URL (all green), console/network inspected via live browser (no errors, all `/api/*` calls 200), demo reset performed, `scenario_integrity.all_ready: true` confirmed both times.
|
||
- Real shared n8n instance (`http://192.168.10.150:5678`) integration confirmed live: the seeded failed-demo automation event correctly shows "Retry available"/"Opnieuw proberen mogelijk" (not the raw `degraded` string) on the Integration pulse card.
|
||
|
||
## Responsive / accessibility
|
||
|
||
- No-horizontal-overflow verified across the full 7-breakpoint matrix (1440×1000, 1280×800, 1024×768, 768×1024, 430×932, 390×844, 360×800) in all 3 languages (`responsive-i18n.spec.ts`, 21 tests) plus the original 4-breakpoint English suite (`ui-redesign.spec.ts`).
|
||
- Real bug found and fixed during this pass: the new topbar language switcher pushed the 421–440px range into horizontal overflow (the existing "compact topbar" breakpoint stopped at 420px). Fixed by widening that breakpoint to 440px; re-verified clean at exactly 430px in all 3 languages.
|
||
- Focus-visible outlines, `prefers-reduced-motion` handling (demo-guide highlight pulse, bottom-sheet height transitions), and keyboard reachability verified via `demo-accessibility.spec.ts` and the new adaptive-guide/clickable-row tests.
|
||
|
||
## Screenshots
|
||
|
||
`artifacts/fleet-ops-release/screenshots/`:
|
||
- `01-login-nl.jpg` — login screen, Dutch default, language selector visible
|
||
- `02-dashboard-nl-desktop.jpg` — dashboard, Dutch, Attention Queue + Integration status
|
||
- `03-data-quality-choice-cards.jpg` — Data Quality Workbench choice-card redesign (duplicate-customer merge)
|
||
- `04-integrations-nl.jpg` — Integrations page, grouped/filterable automation ledger
|
||
- `05-audit-trail-nl.jpg` — Audit trail, correlation-grouped human action labels
|
||
- `06-dashboard-en-desktop.jpg` — dashboard, English
|
||
- `07-dashboard-fr-desktop.jpg` — dashboard, French
|
||
- `08-about-fr.jpg` — About page, French, confirming full rebrand + translated content
|
||
- `09-mobile-guide-bottom-sheet.png` — mobile bottom sheet, half state (390×844)
|
||
- `10-mobile-guide-full.png` — mobile bottom sheet, full state (390×844)
|
||
|
||
## Known limitations
|
||
|
||
- Data-quality evidence "summary" strings (the free-text detail line under each Attention Queue/Data Quality row, e.g. "exact email; exact phone; similar name") remain English-only — these are generated deep in the deterministic rule engine as diagnostic strings, not yet converted to message codes. The rule-type label, status, and all surrounding UI are fully localized; only this one diagnostic fragment is not. Documented as a follow-up, not blocking.
|
||
- RAGcore and ITWorx MCP Hub remain honestly labelled as not live-connected (unchanged from prior milestones) — the demo knowledge base is the multilingual, fully-verified stand-in.
|
||
- Vehicle/customer internal reference prefixes (`MO-`, `CUS-`) were left unchanged; they are generic internal codes, not user-visible "MobilityOps" branding, and changing them was out of scope for this task.
|
||
- Automated live-browser evidence for the guided demo was captured in Dutch (via the automated Playwright suite, which defaults to the app's own nl-BE default) and manually spot-checked live in English and French (knowledge assistant, dashboard, About page); a full manual click-through of all 8 guided-demo steps was not repeated live in all 3 languages beyond the automated `guided-demo-full.spec.ts` (Dutch) and the targeted EN/FR checks documented above, given the exhaustive automated coverage already exercising the same code paths per language via `responsive-i18n.spec.ts` and `i18n-coverage.spec.ts`.
|
||
|
||
## Rollback procedure
|
||
|
||
- `.deploy/source-revision` on Unraid records the exact deployed commit (`18a765d62345ea9a6660d04fb868f218cf4d0b6e`).
|
||
- Prior tarballs remain in `.deploy/` on the server, including `.deploy/source-845db14.tar.gz` (feature branch, pre-merge) and `.deploy/source-4a268c7.tar.gz` (previous release, pre-polish).
|
||
- To roll back: extract the desired `source-<short-sha>.tar.gz`, update `.deploy/source-revision` to match, and re-run `docker compose -p mobilityops -f compose.yaml -f compose.unraid.yaml up --build -d api web`. Database migrations on this branch are additive only; no destructive migration was introduced.
|