10 KiB
P5-04a — Board presentation
Source: approved Phase 5 plan. This checkpoint starts at 7127005e48703eb2147e6f44bd98975975d0eb62. It changes the existing /kanban presentation and its focused tests. It does not merge Table and Board, change the board API or status values, or start P5-04b.
Preservation and implementation
The Board still reads every owner-visible job from /jobapplications/board, groups standard stages into Not Applied, Active and Closed, and shows custom/unknown statuses in Other. A group drop still applies only its existing entry stage (Saved, Applied or Rejected); the card menu still offers precise stages. Other is not a drop target. The existing ordering, announcements, retry/error state, permission and session boundaries remain. No new data source, backend field, status mutation, route, drag library or saved preference was added.
Application files changed: job-tracker-ui/src/components/KanbanBoard.tsx and the Board labels in job-tracker-ui/src/i18n/translations.ts. Test files changed: src/kanban-grouped-board.test.tsx, e2e/kanban-theme.spec.ts, new e2e/board-presentation.spec.ts, and the narrow keyboard-path fixture in e2e/semantic-foundations.spec.ts. The last file changes no application behavior: its old programmatic focus → Tab → reverse-Tab sequence repeatedly measured a false 0px command ring in dark mode, while real keyboard Tab from the search input retained the required 3px ring. The fixture now asserts that real path without weakening the ring requirement.
The column and card surfaces now use the Phase 4 semantic palette in both themes. Cards have a clearer title/company/location/status/deadline hierarchy, a persistent status action, compact readable spacing and visible keyboard/drag-target focus. Column headings are semantic headings with localized accessible names. Cards are articles containing separate open and status controls, avoiding nested interactive semantics. Long English/Bokmål content wraps. A horizontal column rail remains below the desktop grid threshold, including phone and tablet widths; the page itself does not overflow. The Table link and current Board indicator are visible, with explicit copy that Board currently shows all tracked jobs and Table filters do not transfer. This is navigation clarity, not integration.
The existing Board job shape has no priority/favourite flag, so the cards do not invent one. The existing salary/follow-up/deadline pill still chooses one grounded signal, now with a localized deadline label/date. Standard stages retain the shared status-tone semantics and a text label; custom values remain raw text with a neutral tone, never silently translated or remapped.
Opening a card takes the existing /jobs/:id workspace route. The originating /kanban history entry stores only the numeric focus target; Browser Back returns to the same URL/query and focuses that card after Board data loads. Successful keyboard/pointer/menu moves focus the moved card in its resulting group; failed moves retain the original card/status and return focus. The title/card control uses Enter to open and Space to pick up/cancel for keyboard movement, while the status menu remains a distinct action. No ownership or job data enters history state.
| PRES contract | Existing behavior retained and P5-04a change | Automated evidence | Visual/UX evidence |
|---|---|---|---|
| PRES-J07 | Full grouped Board, custom Other, pointer drag, keyboard pickup/drop/cancel, group entry statuses, precise menu, failure/retry and announcements → semantic columns/cards and move focus | src/kanban-grouped-board.test.tsx; e2e/board-presentation.spec.ts; e2e/kanban-theme.spec.ts; backend JobPipeline tests |
Populated/empty/error, active target, successful/failed move, themes/locales and widths in visual/ |
| PRES-J05 | /jobs Table filters, query/page and Board's separate full population remain distinct → visible Table/Board links and explanatory scope copy |
Native 27-job browser fixture proves Board 27 while Table's q=Board page contains 15 of 26 matches |
Desktop/phone captures show the links and scope copy |
| PRES-W01/W02 | Dedicated job route, status/progression and return context → card activation to /jobs/:id, Browser Back focus, unchanged status API |
Unit route-state test and native browser open/Back/move tests | Focus/move captures; no workspace layout change |
| PRES-E07/E08/E09 | Shell entry, light/dark, EN/Bokmål, owner-private Board data and session/permission boundary → localized Board labels and semantic theme use | Existing shell/session suites, scoped axe and native owner fixture | Four theme/locale combinations; desktop/phone/320px captures |
Validation
Inherited baseline: backend 918, frontend 377, browser 74 actual passes + unchanged P2-D01 expected failure, rate-limit 9, ingress 6, PDF admission 1, configuration 3. The focused Board unit suite passes 12/12; four new cases cover full population/navigation, keyboard cancellation/success/focus, direct open/return and precise status-menu behavior. The new native browser fixture covers 27 jobs, a custom status, pointer and keyboard movement, failure, Browser Back, 1440/1024/768/390/320px, themes/locales, scoped axe, loaded/error/retry/empty states. The existing kanban-theme test was updated to assert semantic surfaces and visible focus rather than the removed decorative hover lift.
Validation commands: dotnet test JobTrackerApi.Tests/JobTrackerApi.Tests.csproj -c Release --no-restore, npm test -- --runInBand --silent, npm run typecheck, npm run lint, npm run build with the Playwright API URL embedded, dotnet build JobTrackerApi/JobTrackerApi.csproj -c Release --no-restore -warnaserror, npm run test:e2e with the static export, npm run test:rate-limits, Playwright ingress/PDF configurations, python scripts/test-ingress.py, python scripts/test-supply-chain.py, python scripts/test-ollama-evaluation.py, node --test browser-extension/tests/*.test.mjs, tracked-secret scan, git diff --check, and local Markdown-link resolution. The isolated API/browser suites use fresh local fixtures; no production service was contacted.
Final post-change baseline: backend 918/918, frontend 381/381 (+4 Board cases), production-static Playwright 76 actual passes + unchanged P2-D01 expected failure (77 reported passed, zero unexpected failures; +2 Board cases). Rate-limit 9/9, ingress 6/6, PDF admission 1/1, configuration 3/3, supply-chain 3/3, model eval 5/5, browser extension 3/3, TypeScript, lint, frontend build, API Release build, tracked-secret scan, whitespace and local document links all passed. The first full browser run had one inherited command-focus fixture failure from its programmatic focus sequence; an isolated real-keyboard check and the second complete run passed with the 3px ring assertion retained. No application code changed between these two full browser runs.
Visual evidence: populated light English desktop, dark Bokmål desktop, light English phone, dark Bokmål phone, and 320px dark Bokmål. The full light/dark × EN/Bokmål desktop/phone set is in visual/, along with active keyboard target, keyboard move, pointer move, custom-status move, failed move, loading, error/retry, and fully empty columns. Browser screenshots were inspected directly; long text and actions remain readable, the narrow phone shows one usable column and a clear next-column edge, and both themes retain status and focus contrast. Automated axe is scoped to WCAG A/AA and is not spoken screen-reader verification.
P5-04b read/query parity gate
NO-GO for P5-04b integration. The approved read/query contract is still missing:
| Boundary | Observed P5-04a behavior | P5-04b requirement |
|---|---|---|
| Filters/search/query | Table /jobapplications accepts q, filters, sorting and pagination. Board /jobapplications/board has no corresponding filter/query contract; /kanban?q=table-only preserves the URL but Board still shows all jobs. |
Explicit, accepted shared query and URL/saved-view semantics. |
| Population/results | Native owner fixture: Board shows all 27; Table q=Board&pageSize=15 reports 26 matches and returns 15 on its first page. |
Same complete filtered population, including >25 rows, ordering, custom/deleted/follow-up cases. |
| Status/custom status | Both use the existing pipeline values; Board groups standard statuses and places the custom value in Other. | Preserve this mapping across a shared view switch. |
| Navigation | /jobs and /kanban remain separate routes; job cards open /jobs/:id and restore /kanban focus/query on Back. |
Only after read parity: compatible `view=table |
P5-04b needs separately accepted owner-scoped read/query contract work and native parity tests before its route/workspace integration can start. This checkpoint does not silently replace full-board access with a first-page subset.
Open acceptance and rollback
P5-02-D01 manual screen-reader spoken-name/live-announcement acceptance remains open for P5-15; Board axe/keyboard checks do not close it. P2-D01 remains the documented expected native-package browser failure. F04 remains open pending production ingress evidence. SQLite contention, CV rendering fidelity and other separately owned debt are unchanged. Touch-specific native HTML drag is not claimed from desktop automation; the visible status action and keyboard move remain available alternatives on narrow screens.
Rollback boundary: revert KanbanBoard presentation/activation/focus and its localization/test changes together. The existing /kanban route, read endpoint and status mutation contract are unchanged.