feat(ui): redesign grouped application shell

This commit is contained in:
cesnimda
2026-09-28 17:26:02 +02:00
parent 57a6bd02c6
commit 8e56ceabf5
30 changed files with 398 additions and 92 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
# Phase 5 — Representative screens / major UI redesign plan
**Current successor: [P5-00 readiness implementation](p5-00/README.md).** The five bounded shell conditions are implemented and P5-00-D01 is closed with a deterministic test clock and **916/916** backend rerun. P5-01 has an explicit **GO**. No P5-01 visible redesign, B07 or production work has started. F04 remains open.
**Completed successor: [P5-01 AppShell and grouped navigation](p5-01/README.md).** The first major visible redesign groups every existing destination without changing the 31-pattern route/provider contract. Its inherited backend prerequisite repair established **918/918**, and the P5-01 shell route/focus/accessibility contract is protected. P5-02 and B07 have not started. F04 remains open.
Historical planning checkpoint: source `2a2f9e48052c562ef599825458d0d3a6f9f9fc61`, inspected on 2026-09-13 on `codex/ui-redesign-phase4`. That checkpoint was documentation-only; the successor record above owns implementation/validation status.
+97
View File
@@ -0,0 +1,97 @@
# P5-01 — AppShell and grouped navigation
Source checkpoint: `38cf37e`, branch `codex/ui-redesign-phase4`. Validated locally on Windows with the disposable API/SQLite and Chromium environment. The containing checkpoint is discoverable with `git log -- docs/redesign/phase-5/p5-01`.
This slice is the first major visible Phase 5 redesign. It implements the approved grouped shell in [NAVIGATION_MIGRATION](../NAVIGATION_MIGRATION.md) without changing the router, providers, authentication boundary, route patterns or business workflows. P5-02 utilities, P5-03 Jobs presentation, P5-05 quick preview, B07 and later redesign work are not included. F04 remains open.
## Scope and result
- Dashboard remains an immediate primary destination.
- **Job search** owns Jobs plus Discover, Follow-ups, Board, Companies, Offers and Trash. Job email remains a distinct destination in the same group.
- **Career** owns Career Profile and CVs. Longest-path ownership keeps `/career/builder/:id` with CVs, never Career Profile.
- **Admin** remains permission-filtered and retains Audit log, Users and System routes.
- **Account** owns Settings with Profile and Connected accounts as explicit secondary destinations; the existing account menu remains available.
- The expanded sidebar displays primary and secondary hierarchy. The persisted desktop rail uses labelled MUI tooltips and a keyboard-operable secondary menu. The mobile drawer keeps full labels and a scrollable hierarchy.
- The permanent drawer allocates navigation only the height remaining below the app bar; the upper destination group scrolls when necessary and Account links remain reachable at 900px desktop height.
- A selected secondary destination identifies itself with `aria-current="page"`; its owner is marked `aria-current="location"`. The visual selected indicator and keyboard focus remain separate.
- The shell now has a visible-on-focus skip link, one named navigation landmark, one named main target and one shared `PageHeader` h1. A successful shell route selection moves focus to the new h1. Escape from the drawer still returns focus through the existing MUI modal behavior; a cancelled dirty-editor transition does not produce a false route-focus change.
- Existing desktop collapse persistence remains separate from temporary mobile-open state and retains the accepted 1024px boundary from P5-00.
No new route, navigation framework, store, API, dependency, branding asset, notification destination, Documents destination or Admin dashboard was introduced. The existing quick search, notifications, account menu, language controls and contextual Add Job action remain in place for P5-02.
## Changed owners
| File | Bounded responsibility |
|---|---|
| `job-tracker-ui/src/App.tsx` | Re-express the existing route destinations as the approved grouped navigation tree; retain callbacks, permissions and route elements. `/applications/:id` remains an explicit Jobs ownership alias. |
| `job-tracker-ui/src/layout/AppShell.tsx` | Render hierarchy in expanded/mobile modes and an accessible menu in rail mode; compute parent/child ownership; add skip/main/route-heading focus semantics; apply the visible selected indicator and bound the permanent drawer below the app bar. |
| `job-tracker-ui/src/layout/PageLayout.tsx` | Allow the existing shared `PageHeader` title to receive the shell focus ref. It does not alter section layout or consumer workflow behavior. |
| `job-tracker-ui/src/components/QuickCommandDialog.tsx` | Preserve the inherited command-dialog initial-focus contract after the shell transition by focusing the existing search field when the MUI transition has entered. The command catalogue and execution behavior are unchanged. |
| `job-tracker-ui/src/prompt.tsx`, `confirm.tsx`, `prompt.test.tsx` | Keep existing dialog keyboard entry deterministic; the prompt input is focused once after opening and the confirmation cancel action regains initial focus. No dialog result or action changed. |
| `job-tracker-ui/src/i18n/translations.ts` | English/Bokmål group names and skip-link label only. |
| `job-tracker-ui/src/sidebar-active-nav.test.ts` | Non-nested Jobs ownership, legacy application ownership and Career/CV specificity. |
| `job-tracker-ui/src/app-shell-navigation.test.tsx` | Group/current semantics, rail menu operation and successful route-heading focus. |
| `job-tracker-ui/e2e/shell-readiness.spec.ts` | Four real-App light/dark × English/Bokmål journeys across expanded, rail and phone navigation. |
| `job-tracker-ui/e2e/accessibility.spec.ts`, `redesign-preservation.spec.ts`, `smoke.spec.ts`, `csv-export.spec.ts` | Give long real-browser journeys sufficient time, target the exact created job ID, and set up API-owned data before opening the Dashboard so unrelated concurrent startup requests do not race setup writes. The separate UI login smoke case still protects sign-in. No domain assertions or routes were removed. |
`PageLayout.tsx` is necessary because focus must land on the actual shared page heading rather than a duplicate or wrapper; its optional ref is unused elsewhere. The dialog changes preserve the inherited initial-focus contract exposed by the loaded shell browser run. A transition callback on the prompt was removed after it disturbed the mobile Tab wrap in the production build; a one-time open effect preserves initial focus in both dev and static-export modes. No dialog outcome or domain workflow changed.
## Preservation trace
| PRES → existing behavior | P5-01 change | Automated evidence | Visual/UX evidence |
|---|---|---|---|
| **PRES-E07** → authenticated shell, every existing destination, collapse/drawer and active route | Same destinations grouped under readable owners; rail children remain keyboard reachable | `sidebar-active-nav`, `app-shell-navigation`, all existing route-contract and shell-readiness coverage | Expanded/rail/mobile captures in all four theme/locale pairs |
| **PRES-E08** → focus, theme, locale and responsive shell behavior | Skip/main target, one h1, current parent/child semantics and route-heading focus | Unit focus contract; real route selection to Trash/Board; P5-00 drawer entry/Escape and dirty-CV fixtures retained | Focused rail menu and mobile drawer; light/dark and EN/NB |
| **PRES-E05/E06/E09** → auth/session disposal, permissions and protected shell | No provider/auth/session change; hidden admin rows remain filtered | Complete frontend/backend/account-isolation/browser baseline | Standard/admin shell states; no private data added to navigation |
| **PRES-S02/S03** → notification and Settings/account entry | Existing header utilities retained; Settings owns existing Profile/Connections links | Existing notification, Settings and account-switch tests plus grouped-shell tests | Account group in expanded/mobile shell |
| **PRES-A01–A03** → protected Admin audit/users/system and metadata | Same three permission-gated routes, now under a stable Admin group | Existing admin/API permission tests and complete browser baseline | Admin group in both locales/themes and narrow drawer |
| **PRES-J/W/C/M route entry contracts** | Presentation/ownership only; all approved entries and compatibility paths remain routed by unchanged `Routes` | Existing 31-pattern route contract plus complete domain suites; direct Jobs/Career ownership cases added | Actual populated Board and Trash states; no domain forms or mutations changed |
Mapping a capability does not claim exhaustive coverage of that capability. This slice protects navigation entry and shell behavior; domain behavior remains owned by its existing tests and later gated slices.
## Accessibility, responsive and visual validation
The real-App fixture uses synthetic owned data and blocks external browser traffic. It verifies:
- expanded desktop group labels, exact current parent/child semantics and fully visible Account destinations at 900px height;
- rail activation by keyboard and access to every Jobs secondary destination through a labelled MUI menu;
- mobile drawer labels, current state, route selection, close and h1 focus;
- skip-link target and named navigation/main regions;
- light/dark and English/Bokmål at 1440px and 390px, retaining P5-00 checks at 320/767/768/900/1023/1024px;
- axe WCAG A/AA scans for the loaded grouped navigation and mobile dialog.
The first browser run found a serious list-structure violation: a secondary `<ul>` was a direct child of its owning `<ul>`. The final implementation nests the secondary list inside the owning `<li>`; all four reruns then reported zero violations for the scoped scans. Visual review also found the permanent drawer's full-height content extending below the app bar and clipping Account links; a remaining-height flex layout and full-viewport assertion now cover that boundary. Incomplete axe nodes, manual screen-reader/touch/zoom/forced-colors limits and the inherited capture-quality caveat remain evidence limits rather than passes.
Representative evidence is stored under `visual/`: expanded shell, rail menu and mobile drawer for all four theme/locale pairs. Review confirmed useful density, readable hierarchy, selected/focus distinction, no new horizontal overflow and continued access at phone width. The historical post-axe missing-glyph fragments remain a capture limitation when present; no unrelated typography repair is inferred from them.
## Validation baseline
Inherited P5-00 baseline: backend **916**, frontend **359**, browser **66 actual passes + P2-D01 expected failure**, rate-limit **9**, ingress **6**, PDF **1**, configuration **3**. A separately committed prerequisite repair, `57a6bd0`, made default Rules settings and workspace checklist initialization safe under concurrent first reads; its two backend regressions establish **918/918** before this shell checkpoint.
Final P5-01 candidate:
| Check | Result / change |
|---|---|
| Backend Release tests | **918/918 passed**, no skips; +2 from the separate initialization repair, no P5-01 backend change |
| Frontend Jest | **365 passed / 77 suites**, +6 behavioral cases (three route-ownership and three shell-navigation cases) |
| Main Chromium browser | **70 actual passes + unchanged P2-D01 expected failure**, zero unexpected failures or flaky cases; +4 grouped-shell journeys over P5-00. Production static export, `PLAYWRIGHT_STATIC_EXPORT=true`, `PLAYWRIGHT_PREBUILT_EXPORT=true`, `npm.cmd run test:e2e -- --reporter=list,json`; result in ignored `.artifacts/p5-01-browser-static-accepted.json` |
| TypeScript / edited E2E TypeScript / lint | Passed (`npm.cmd run typecheck`; `node node_modules/typescript/bin/tsc --project ../.artifacts/p5-00-e2e-tsconfig.json --noEmit`; `npm.cmd run lint`) |
| Frontend production build | Passed |
| API Release build | Passed, zero warnings/errors |
| Rate-limit / ingress / PDF / configuration | **9 / 6 / 1 / 3 passed**, respectively. Playwright security configurations use disposable local APIs; `python scripts/test-ingress.py` validates configuration. |
| Ancillary local checks | Supply-chain / model-evaluation harness / extension **3 / 5 / 3 passed**; no live-provider claim |
| Secret / whitespace / documentation links | Tracked-secret scan passed; `git diff --check` and staged checks passed; **133** current/root Markdown documents, zero missing local targets (historical archive excluded) |
The backend command was `dotnet test JobTrackerApi.Tests/JobTrackerApi.Tests.csproj --configuration Release --no-restore` with xUnit collections/threads serialized; the API Release build used `-warnaserror`. The frontend production build set the local test API base URL and disabled telemetry. The complete browser run used a prebuilt production static export and the repository's disposable backend. No expected-failure marker, retry or exclusion was added. P2-D01 remains separately identified. Test-result artifacts contain disposable synthetic data only and are not committed.
## Deferred issues and next gate
- **P5-00-D02 remains open**: browser Back from the loaded Job Workspace preserves the query but does not restore row focus. The explicit workspace Back control remains protected. This blocks P5-03/P5-08 affected navigation acceptance, not P5-01.
- **Initialization gate resolved separately:** React development-mode duplicate GETs exposed competing first-read inserts in Rules settings and workspace checklist seeding at the clean inherited checkpoint. Commit `57a6bd0` makes both owners reload the winning row after a unique-key race and adds two backend regression cases. P5-01 itself contains no backend or database change.
- **SQLite write contention remains open (P4-04-O02):** one complete browser run returned 500 while test setup created a company and a Free user and published a CV. The failures occurred on three different existing API writes, outside the grouped-shell code. The CSV case passed ten isolated repeats; API setup now authenticates through the existing API before rendering a Dashboard with concurrent background reads/writes. This narrows the browser fixtures to the workflows they actually assert; it does not claim server-side concurrent writes are safe or close P4-04-O02. A separate ten-repeat Free-user probe exposed two navigation aborts when clearing cookies on a live signed-in page; API setup also removes that test-only race. The failed full-run traces remain in ignored local artifacts and are not represented as passes.
- **P3-O02 command failure/pending isolation** remains the P5-02 acceptance gate. P5-01 retains the current catalogue and behavior.
- P4-05-O01 table localization blocks P5-03 acceptance; Table/Board parity blocks P5-04b; package proof blocks P5-08b changes; stale CV revision and rendering fidelity block P5-09–14.
- P2-D01 and F04 remain open with their original classifications. No B07, backend contract or production verification work occurred.
- Final product name/logo/brand color remain substitutable decisions. The current provisional identity is unchanged.
P5-02 may start only after this checkpoint is committed cleanly and its command failure/pending-isolation gate is characterized and protected. P5-03 must not start until P5-00-D02 and its own localization gate are resolved.
Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB