Files
jobtrackingapp/docs/redesign/phase-5/p5-04b/README.md
T
2026-09-29 23:01:05 +02:00

18 KiB
Raw Blame History

P5-04b — Table/Board integration

Source checkpoint: 6fc8937701a073f89fb680235bce58029a27550b, branch codex/ui-redesign-phase4. This slice implements only the approved P5-04b plan. The separate readiness checkpoint established matching-ID parity first; its backend contract, authorization, ordering and complete Board response are unchanged here.

Structure and compatibility

  • /jobs defaults to Table; /jobs?view=table and /jobs?view=board explicitly select presentation. Unknown view values safely use Table. No per-account presentation preference or SavedView schema is added.
  • /kanban remains an authenticated route. Its replace redirect targets /jobs, sets view=board and retains all other query parameters, hash and router state. A conflicting view=table on the legacy Board URL is overwritten so historical Board links still mean Board. The alias does not add a history entry or redirect externally. All 31 route/compatibility patterns remain.
  • The existing Jobs sidebar destination now covers both views; the separate Board child is removed. Discover, Reminders, Companies, Offers, Trash and other destinations remain. No provider/auth/permission change or command-catalogue expansion.
  • App retains the shared heading and Add Job action. The existing JobTable is the bounded workspace owner: one responsive filter/saved-view toolbar, a labelled Table/Board link group, matching-result count, and either the original Table/mobile cards or KanbanBoard. A new wrapper/framework would duplicate the existing mature filter owner, so none was introduced.
  • The view links have aria-current="page", text labels, shared theme focus treatment and distinct filled/outlined states. They use native navigation links, support keyboard Enter and remain mounted across switches. Activating the current view is a no-op. New view choices push history; filter edits retain the established replace behavior. Back/Forward restores the recorded query/view.

State and lifetime contract

Search, status (including raw custom values), company, location, follow-up, readiness, include-deleted scope and sort parameters survive switching. The Table's page stays in the URL, is ignored by the Board API parser, and is restored on return. Changing filters or applying a saved view resets the Table page as before. Board grouping/stage ordering still controls visual card placement; Table sort does not redefine Board columns.

Table selection is explicitly page/view-local: it clears when leaving or re-entering Table, including history traversal. Hidden selections cannot drive bulk actions. Comfortable/Compact remains local to the mounted workspace and survives a view switch; persisted page-size/column preferences keep their existing owner. Density/columns/pagination/bulk controls are Table-only. Trash always renders Table, even with a crafted view=board parameter.

Job opening still records the originating row/card on its history entry. Full workspace Back and browser Back restore the originating query and focus. Switching presentation clears only the transient focusJobId hint on the new history entry, preventing an old job-return hint from stealing focus from the switch; unrelated route state is retained.

Loading and performance

Board code is lazy-loaded on first selection, with an accessible local fallback. Only the active results representation is mounted. The Table controller/filter owner remains mounted, but its useViewResource read is disabled while Board is active; it reloads on return. Table data/preferences can remain in local memory during the switch, not in a new shared/account cache. Board unmounts on leaving and reloads on return. It receives the existing creation refresh token, so Add Job refreshes whichever representation is active.

Board query edits debounce for 250 ms using the existing hook, avoiding a full read per keystroke. Initial Board entry reads immediately. One settled Board query uses one complete read; there is no Table-page crawl, arbitrary page size, hidden full-result preload or duplicate Table read. The native regression counts Jobs-result requests across both switch directions. Company-filter metadata still loads through the existing useCompanies owner and is now available in Board too; the one-read statement does not claim the entire application makes only one HTTP request. Reload/error behavior and status PATCH → membership reload remain unchanged.

Residual ceiling: Board still returns and renders the full matching array (O(N)); this integration does not add a result cap or virtualization. Server data can change between separate reads; matching-ID parity is the same-query/stable-data guarantee established by readiness, not a database snapshot across requests. Large-account virtualization/paging requires separate design work.

Preservation traceability

This maps affected contracts, not exhaustive coverage of every branch in the 57-contract catalogue.

Contract Existing behavior → integration Automated evidence Visual/UX evidence
PRES-J03/J04 Reviewed creation/capture → existing Add Job action now shared with Board, same modal and post-create refresh Existing quick-capture/command/create regressions and extension checks; new active-view creation-refresh case Shared Add Job entry visible in Table and Board screenshots; no new creation flow
PRES-J05 Filters, saved views, sort/page/columns, Table/mobile actions → shared query owner and view links jobs-view-integration.test.tsx, retained job-table-density, native board-presentation membership/history cases and table-density Same filtered population, page-two return, desktop/mobile Table, EN/NB and both themes
PRES-J06 Bulk/edit/Trash/restore → selection cleared at view boundary, no Board bulk UI Existing partial-bulk/Table/Trash tests; new Trash view-parameter and selection-lifetime checks Retained Table controls; density/browser suite
PRES-J07 Full population, custom statuses, pointer/keyboard moves and precise menu → existing Board renderer below shared filters Retained 15 Board unit cases; native move/failure/custom/filter tests; integration membership/count checks Keyboard switch/drop/focus, custom query, empty/error/loading, narrow Board
PRES-W01/W02 Open job/full workspace/Back and status progression → canonical query preserved Native Table row Back and Board card Back; unchanged workspace/status tests Focus returns to initiating row/card
PRES-E07 Navigation, mobile shell and active destination → one Jobs item, Board alias remains Native legacy replace/history/hash/state case; updated shell test now asserts Jobs current + Board switch current Desktop/rail/mobile shell retained
PRES-E08 Locale/theme/focus/error semantics → existing MUI tokens and labels Full frontend/browser suites; integrated axe/focus checks Light/dark × English/Bokmål, phone/tablet/desktop
PRES-E09 Account/private-state disposal, authenticated reads → unchanged provider/API boundaries Full B02/account isolation and backend parity/ownership regressions; native fixture ID ownership comparison No new private storage or identity source
PRES-S03 Page-size/column/theme/language preferences → existing mechanisms retained Existing preference/density tests plus local density switch test Comfortable/Compact retained in Table; no Board density claim

Files and scope

Application changes are limited to src/App.tsx (alias/sidebar route wiring), components/JobTable.tsx (existing shared owner and conditional renderer), components/KanbanBoard.tsx (embedded toolbar removal, debounce, active refresh/count), and i18n/translations.ts (EN/NB view/count labels and workspace subtitle). No backend, schema, query parser, drag/drop engine, authentication, theme, CV, package or production infrastructure change.

Tests: new jobs-view-integration.test.tsx; updated Board unit assertions move the old standalone switch responsibility to the integration fixture; e2e/board-presentation.spec.ts adds canonical routing/history and both-view evidence without removing movement/failure checks; shell-readiness.spec.ts reflects the approved single Jobs destination while retaining rail/mobile/heading-focus coverage. kanban-theme.spec.ts now polls the observable no-horizontal-scroll result after resize, allowing the shared responsive toolbar's React update to settle; it records the measured samples and still fails persistent overflow.

Validation and evidence

Inherited baseline: 934 backend, 384 frontend, 76 actual browser passes + P2-D01 expected failure; rate-limit 9, ingress 6, PDF 1, configuration 3. Final validation is complete; no unexpected failures remain.

Six new frontend cases cover: both-direction history/page/density/selection and hidden-read behavior; custom-status membership; zero-result membership; shared Board filters; Trash isolation; and saved-query application plus active creation-refresh. The new native browser case proves legacy replacement (including conflicting view, hash and router state) and unknown-view fallback. Existing native Board coverage additionally exercises both-direction ID parity, page-two return, switch focus, one request per selected view, no-results/custom queries and four locale/theme combinations. Existing move/error/Back assertions remain.

Check Current result
Backend Release regression suite 934 passed, unchanged
Frontend Jest suite Final-code run: 390 passed in 78 suites, 119.9 seconds; 384 + 6 integration cases
Full native browser suite 77 actual passes + unchanged P2-D01 expected failure, 9.8 minutes; 76 + 1 legacy-route case. Zero skipped, unexpected or flaky outcomes
Rate-limit / production-mode ingress / PDF admission 9 / 6 / 1 passed, 10.3 / 6.4 / 7.7 seconds; unchanged
TypeScript / ESLint Passed
Frontend production export / API Release -warnaserror Passed; API zero warnings/errors
Ingress configuration 3 passed
Supply-chain policy / Ollama evaluation / browser extension 3 / 5 / 3 passed
Staged secret scan / whitespace / local Markdown targets Passed; links check local files, not external URLs or anchor fragments

Commands use the repository-required rtk/rtk proxy wrapper. UI commands: npm.cmd test -- --runInBand --silent, npm.cmd run typecheck, npm.cmd run lint, npm.cmd run build; build uses NEXT_PUBLIC_API_BASE_URL=http://localhost:5302/api, CI=false. Backend: C:/Users/Connor/.dotnet/dotnet.exe test JobTrackerApi.Tests/JobTrackerApi.Tests.csproj -c Release --no-restore --logger 'console;verbosity=minimal' and build JobTrackerApi/JobTrackerApi.csproj -c Release --no-restore -warnaserror. Offline checks: python scripts/test-ingress.py, python scripts/test-supply-chain.py, python scripts/test-ollama-evaluation.py, node --test browser-extension/tests/*.test.mjs, python scripts/supply-chain.py scan-secrets, git diff --check, git diff --cached --check.

Native browser command: npm.cmd run test:e2e -- --reporter=line,json, with DOTNET_ROOT=C:/Users/Connor/.dotnet, DOTNET_HOST_PATH=C:/Users/Connor/.dotnet/dotnet.exe, PLAYWRIGHT_STATIC_EXPORT=true, PLAYWRIGHT_PREBUILT_EXPORT=true, PLAYWRIGHT_JSON_OUTPUT_NAME=test-results/integration-accepted.json. Security commands ran sequentially against the same local API port with the same .NET environment: npm.cmd run test:rate-limits -- --reporter=line, npm.cmd exec playwright test -- --config=playwright.ingress.config.ts --reporter=line, npm.cmd run test:pdf-admission -- --reporter=line. These are local regressions, not production ingress proof.

Rendered visual/interaction review

Evidence is synthetic local data captured through the actual API + exported application, not mocked UI screenshots. Reviewed the actual rendered images for readable hierarchy, status labels, restrained shared surfaces, wrapping, selected links, focus and result context:

Capture date: 2026-09-29, local Playwright Chromium, standard synthetic owner (27 fixture jobs including one custom stage), EN/Bokmål and current test clock; no real user data or provider. The readiness screenshots and P5-04a record are the pre-integration reference. This is reviewed state evidence, not a clock-frozen pixel-diff claim. Existing fixture viewport heights are retained (320×720, 768×900, 1024×900); the broader Phase 5 visual protocol's other heights/1920-wide matrix is not newly certified by this bounded slice. Resolve this implementation's SHA with git log -1 -- docs/redesign/phase-5/p5-04b/README.md, avoiding a self-referential hash placeholder.

Native keyboard automation verifies Enter on the view link keeps focus on that same control after content replacement; the screenshot shows the Phase 4 focus ring. Existing pointer/keyboard move, custom-stage menu, failed-write, row/card Back and shell drawer checks remain active. These are automated browser interactions plus human-style screenshot review by the agent, not a manual screen-reader session. The inherited post-axe missing-glyph screenshot artifact remains an evidence limitation; no typography/icon fix was smuggled into integration.

Development checks found and corrected test-only label/matcher/type assumptions. The old loading fixture initially observed the new lazy-code fallback before its network handler existed; it now waits for the actual Board request before releasing its simulated error. No production behavior was weakened to make that fixture pass.

The first full browser run (13.2 minutes) had 73 actual passes + P2-D01 expected failure and four unexpected Table-density failures (one per theme/locale). The added result summary duplicated the existing visible loading message: main.getByText(t.loading, { exact: true }) found two elements at table-density.spec.ts:235. The summary now reports only settled results; the established results area owns loading/error feedback. The original four assertions were left unchanged. This was an integration regression, not inherited debt or an expected failure.

The next browser run (10.1 minutes) passed all four unchanged Table-density cases and had 76 actual passes + P2-D01 expected failure, with one unexpected failure: the old immediate post-resize Board assertion read 27px horizontal scroll at 375px. Its failure screenshot showed the settled mobile toolbar fitting the viewport; the same test had passed in the first full run. Integration adds a useMediaQuery-driven shared toolbar, so reading the frame immediately after setViewportSize is not a stable readiness check. The assertion now uses bounded Playwright polling with the same zero-scroll expectation and records samples. No application overflow-hiding rule or relaxed threshold was added.

Three focused repetitions passed after that test correction: measured resize samples were [27, 0], [27, 0], [0]. This independently confirms the brief pre-reflow measurement and subsequent zero overflow, rather than merely obtaining a lucky passing rerun.

The final full browser result records 77 actual passes plus P2-D01's unchanged expected failure, with no retries, unexpected failures or skipped cases. Its resize sample is [0]. The fixture retains all prior Board movement/failure and Table density assertions.

Accessibility evidence limits: final Board phone axe checks reported no violations but left color-contrast incomplete for eight nodes in each theme. The empty Board check left six contrast nodes and one aria-prohibited-attr node incomplete. These are unresolved automated checks, not passes or screen-reader acceptance. Keyboard/focus regressions pass and rendered screenshots were inspected; manual spoken-name/live-state acceptance remains mandatory for P5-15. No existing expected failure or incomplete check was reclassified to obtain this checkpoint.

Open acceptance items / stop

Keep open: P5-02 manual screen-reader spoken names/live-state acceptance for P5-15, native touch-drag verification, P2-D01 expected failure/native package proof, F04 production ingress evidence, Board full-array scaling, and readiness R01/R02 command-focus/CV baseline observations. Automated axe/keyboard checks do not close manual screen-reader/touch requirements. Other inherited debt remains with its existing owner.

P5-06, P5-05's unapproved quick preview, CV prerequisites and B07 are outside this slice. Stop after the separate integration checkpoint; no subsequent implementation is authorized by this record.

P5-04b acceptance: complete within the approved scope. The shared-query prerequisite remains satisfied; native membership, history, alias, active-view request and focus evidence prove the integration. No integration blocker remains. The open items above retain their separate acceptance owners; completion does not certify production ingress, native touch or manual screen-reader behavior.