26 KiB
P5-06 — Personal Dashboard presentation
Source: 384dfa8a4b619fb1dec1d4e650f863f7f6fd5ebd, codex/ui-redesign-phase4. Scope: approved P5-06, metric definitions, visual protocol. No P5-07, Phase 8 or B07 work. The containing commit is this slice's implementation checkpoint; validation results below determine acceptance.
Inspection and data contract, before implementation
All six existing reads are owner-scoped by the API. Summary uses stats, analytics-overview and reminders(upcomingDays=14); trends uses analytics(months), tags(limit=10) and tag-trends(months,limit=5). Existing summary/trends failure boundaries are independent; preserve them without adding an endpoint or global job crawl.
| Data / proposed presentation | Availability | Actual definition / limit |
|---|---|---|
| Tracked jobs | Direct stats.active | Non-deleted jobs, including prospects and closed statuses; never call this in-flight applications |
| Last 30 days | Direct stats.appliedLast30Days | DateApplied >= server-local now minus 30 days; existing query has no upper-date bound. Disclose recorded dates, not growth or a weekly target |
| Current interviews/offers | Direct overview.funnel | Current canonical stage counts; not meetings or historical conversion |
| Attention / preparation / package gaps | Direct reminders and workflowSignal | Existing ranked reminder feed, first five displayed, full Reminders link; interview-prep is preparation, not an appointment |
| Monthly applications and dated responses | Direct analytics | Month-aligned selected application cohort; response events only for jobs applied in that window and response months within it. Totals/ratio safely derived from those returned series; not eventual response probability |
| Pipeline and raw statuses | Direct overview.funnel + stats.byStatus | Canonical snapshot may omit custom values. Show raw breakdown separately, never subtract unlike populations to invent Other |
| Current stage age | Direct overview.timeInStage | Median current occupancy, fallback to DateApplied/SavedAt; not completed duration |
| Source / company reports | Direct overview lists | Current non-deleted jobs, including prospects; responses use flag OR response date. Sources top six, companies top eight; current Company.Source, not original capture source |
| Job tags / monthly tag trends | Direct tags / tag-trends | All-time non-deleted job tags versus selected applied cohort respectively. Tags are not verified user skills; months is ignored by tags endpoint |
| Salary | Direct overview.salaryInsights | Mean of recorded min/max midpoint; currencies/pay periods kept separate, count and range preserved |
| Other returned fields, not previously displayed | Direct stats.total/deleted/averageDaysSinceApplied; overview.totalActive/totalResponses/medianDaysToFirstResponse | total continues to drive existing hasJobs/onboarding behavior; deleted and age are not new widgets. Age averages non-deleted recorded application dates, clamping negative age to zero. Overview totals count non-deleted jobs and response flag OR date; the median uses recorded ResponseDate−DateApplied, clamped to zero, with null for no sample. None proves first-response event history or average response time. These unused fields remain deliberately unpromoted, not unavailable or removed functionality |
| Upcoming appointments, global tasks/activity, sectors, role/location aggregates, CV performance, AI recommendations, goals | Unavailable on current Dashboard reads / future contracts | No inferred schedule from status, no per-job N+1 aggregation, no generated facts, no new widgets pretending these sources exist |
G-METRICS requires exact labels/transformations, all five owner-local flags and independent error evidence before acceptance. Inspection found a funnel flag that did not hide its panel, independently rescaled activity lines, ambiguous response/active/upcoming captions and hover-only tag values. These are directly owned presentation/preservation gates, not backend metric repair.
Design and preservation plan
Use the existing MUI semantic palette and Archivo typography; no new palette/font/brand. One compact snapshot strip above a two-column working area: attention on the wider side, current pipeline alongside; trends and detailed reports below. One column on phones; no oversized KPI cards or hover-lifting noninteractive surfaces. Reuse PageSection, existing workflow action builder, resource hook and onboarding. Native details/table alternatives keep precise chart values accessible. Keep all five dashboardPrefs:<owner> keys and the 6/12/24 month control; no order/resize/reset or storage migration.
| PRES contracts | Existing behavior → P5-06 change | Required evidence |
|---|---|---|
| S01 | All summary/attention/chart/detail reports → meaningful hierarchy, honest populations, exact accessible values | daily-control-loop, workflow-trust-signals, StageAnalytics/AnalyticsSalary; new Dashboard fixture and native populated/partial-error proof |
| S02 | Five owner-local visibility flags and onboarding actions/dismissal → accessible checkbox menu, explicit all-hidden guidance | Toggle/reload/A→B isolation; original onboarding tests retained |
| E07–E09 | Existing routes, themes/locales, private identity boundary → unchanged providers, localized labels and same API reads | Source link/query tests, full auth/browser regressions, light/dark EN/NB screenshots |
| W11/W12, J05/J09 | Existing reminder/job/company entry points → same workflow paths, supported Jobs filters | Existing attention tests plus native source links; no new task/calendar/AI flow |
No unrelated component or backend owner is authorized by this plan. Phase 8 can reorganize these plain sections later; P5-06 does not introduce a widget framework.
This is scoped evidence for the affected contracts, not exhaustive certification of all 57 groups. Other contracts retain their existing tests/route/provider owners; unchanged broad-suite passes do not replace missing provider, production, screen-reader or rendering-fidelity evidence.
Implementation and retained behavior
Application changes are limited to src/components/DashboardView.tsx and src/i18n/translations.ts in job-tracker-ui. DashboardView remains the owner, with no new component layer, dependency, chart library, route, provider, API, backend, schema, CI or storage contract. Existing PageSection, ViewStateNotice, useViewResource, OnboardingChecklist, getWorkflowAction, buildWorkflowPath and MUI/Emotion are retained. Dashboard subtitle is localized through its existing key, without changing the AppShell.
The snapshot is a compact definition list rather than four separate oversized cards. Attention is paired with the current pipeline on wider desktops; both stack at intermediate/phone widths. Existing current-stage ages, source response samples, separate salary groups, company reports, tags and monthly tag trends remain below. Source detail now exposes all six returned sources rather than only the first. Onboarding, dismissal, reminder actions and the complete Reminders destination are retained. The feed still shows its first five jobs, with total/feed category counts stated explicitly.
Five stored visibility flags retain their existing names, defaults and owner key. Cards controls the snapshot, activity the activity report, funnel the pipeline/raw-status breakdown, companies the company report, skills the job-tag/monthly-tag reports. The previously ineffective funnel flag now hides its intended panel. Attention, stage-age, source and salary reports remain independent of these five flags, as before: hiding the five configurable groups is not a promise to hide every Dashboard section. The customization trigger always remains available, including on empty accounts and with all five groups hidden. No reset/layout/persistence engine was added.
The menu's single interactive owner is menuitemcheckbox with aria-checked; decorative checked/unchecked icons replace nested checkbox inputs. Escape returns focus to the trigger through existing MUI behavior. Native details/summary controls disclose raw statuses, monthly activity values and every returned tag/month value. Explicit summary tab stops participate in the shared Phase 4 focus-visible rule. Non-interactive metrics/sections remain outside the tab order.
Activity lines now share one zero-based scale; independent scaling previously made different magnitudes appear comparable. Applications are solid and responses dashed, with readable localized labels, a shared-scale statement and exact native table values. Chart geometry is hidden from assistive technology because equivalent textual data is present. SVG strokes use the existing CSS-variable semantic palette, with the ordinary MUI palette fallback in non-CSS-variable test contexts. Status names use the shared standard-status labels; raw custom status names remain unchanged. Bars are supplemental to names/counts, never the only status signal.
Routes, period, errors and empty states
- Tracked count opens existing
/jobs; offers opens/offers; full attention opens/reminders; each attention action uses the unchanged workflow path builder. - Raw status links use the existing
/jobs?status=…parser, including custom names. Company links retain/jobs?companyId=…. These are existing supported query contracts, proven against native Jobs results; no new preview/workflow route. - The 6/12/24-month buttons keep local state and reload the trends group only. Captions distinguish activity/tag cohorts from all-time tags and the current summary. Removing the ignored
monthsargument from the tags call changes no server population. - Summary failure leaves successful trends usable; trends failure leaves summary usable. Both retain explicit errors/Retry and existing loading semantics. A failed read is never replaced with a successful empty/zero metric.
- A zero-job account sees existing first-job and Career Profile entry points plus its retained setup checklist. A single saved prospect shows tracked count and a truthful no-activity window; no fabricated conversion percentage or forced onboarding wizard. Zero response samples on real nonempty populations remain valid data, with denominator visible.
Regression protection
src/dashboard-presentation.test.tsx adds eight tests: precise populations/monthly values/custom statuses/separate salary groups; all five flags and remount; independent trends failure/retry; independent summary failure; supported status/company URLs; period changes affecting trends only; zero-window behavior; and shared line scaling. Existing daily-control-loop/workflow-trust-signals tests remain unchanged (15 targeted tests across the three files).
e2e/dashboard-presentation.spec.ts adds two browser cases:
- Native owner account, company and eight jobs across six applied/current statuses plus Saved/Preparing, a raw custom status, Norwegian names, tags and NOK salary. Response dates are persisted through the existing update DTO, not assumed to exist after create. Compare exact returned month values and sample totals to the actual UI; use keyboard disclosure/focus, native status/company links and Jobs membership; exercise all five flags/reload, UI logout/login A→B→A, a fresh account and one prospect; cover viewport/theme/language combinations and reduced motion.
- Controlled local transport holds/503s for summary/trends, with actual application rendering. Verify loading, independent failure presentation, Retry and exact recovered values. These fixtures are deliberate error-state simulation, not proof of production metric data.
Development checks caught and corrected invalid definition-list caption placement and nested interactive inputs in the menu. A transient post-Escape contrast finding came from scanning fading menu DOM: the test now waits for that DOM to unmount, without disabling a rule or claiming that animation frames passed contrast certification. A create-fixture response field was corrected to use the supported update operation. These checks were kept, not weakened. No existing expected failure was renamed or removed.
The first full browser attempt also received one generic 500 from the unchanged native language-preference endpoint during concurrent Dashboard/setup reads immediately after Browser Back. Trace confirmed the response/status and overlapping reads, but supplied no underlying exception: SQLite contention is a hypothesis, not a proven root cause. Visual fixture preparation now configures the native preference before mounting the next Dashboard (blank document → existing preference PUT requiring 204 → Dashboard). No retry, mocked successful preference response, weaker assertion or application/backend repair was introduced. This proves native configured locale rendering, not acceptance of concurrent language writes during loading. That unchanged endpoint's intermittent 500 remains a separate baseline observation pending diagnosis; final full-suite results must be reported separately from this initial failure.
Accessibility and visual protocol
Use native section headings, definition lists, links, tables with column/row headers, details disclosures and one checked menu owner. Exact values remain available without hover, color perception or SVG interpretation. Manual rendered review must inspect typography, secondary text, focus, wrapping and hierarchy; browser assertions additionally exercise keyboard disclosure, a visible 3px focus outline, menu focus entry/Escape return, no document overflow, persisted settings and account boundaries.
Automated axe scans cover populated phone English light/dark states and the expanded customization menu, with WCAG A/AA rules retained. Violations and incomplete results are recorded separately in the evidence summary. An incomplete result is not a pass. These scans do not certify every chart/table disclosure or locale/state. The P5-02 spoken-name/live-announcement screen-reader acceptance remains open and mandatory for P5-15; this slice does not claim a manual screen-reader session, native touch certification, forced-colors or exhaustive zoom validation.
The final axe summary records zero detected violations in all four scans. Both expanded-menu scans have incomplete aria-hidden-focus (one modal-background node) and color-contrast (two nodes); both collapsed-page scans have incomplete color-contrast (two nodes). The latter concerns the invisible zero notification badge and a customization trigger whose background could not be resolved due to overlap/focus rendering. These remain manual acceptance items, not automated contrast passes. Menu entry/Escape return and visible focus have separate behavioral evidence; no background isolation or axe rule was disabled. The summary retains rule/check/target information without raw rendered HTML.
Evidence filenames identify state, theme, language and viewport; the evidence manifest records actual capture dates/dimensions. Populated fixtures are synthetic private development accounts using the real disposable API, never production/user career data. The before image is a historical layout reference captured before this slice: its response seeding/clock differs from the corrected final fixture, so it is not a frozen-data pixel or metric comparison. No credentials, API bodies, cookie storage or raw traces are included in committed evidence.
Rendered review — VS-DASHBOARD
Final static-export screenshots were inspected in all four theme/language pairs at 1440×900 and 390×844, plus dark/Bokmål at 320×740, 768×1024, 1024×768 and 1920×1080. Desktop keeps four compact snapshot values and an attention/pipeline composition; intermediate widths stack reports and use two summary columns; phones use one column, wrapped controls and readable chart captions outside the SVG. No document overflow occurred at those measured widths. Long Norwegian labels/company names remain available; no columns/actions were removed to fit.
| State / evidence | Observed result |
|---|---|
| Light English desktop, light Bokmål, dark English, dark Bokmål | Clear snapshot/attention hierarchy, restrained report surfaces, legible secondary text and chart/status colors; no new gradients or noninteractive hover elevation |
| Light English phone, light Bokmål phone, dark English phone, dark Bokmål phone | Actions/period controls wrap, reports stack, names remain readable; shared surfaces/focus and state meaning retained |
| Minimum 320px, tablet 768px, desktop threshold 1024px, wide 1920px | Intentional breakpoint reflow, visible customization focus, bounded content width; no missing controls or horizontal document overflow |
| New account, one saved prospect, five groups hidden | Optional setup/first-job guidance, no fabricated window response percentage; customization remains available and independent report groups remain intact |
| Loading, summary error, trends error | Existing progress/error/Retry treatment; successful independent resource remains usable. Transport fixtures are explicitly synthetic |
| Keyboard monthly values | Native disclosure and exact twelve-month table, visible shared focus outline; keyboard assertion verifies focus remains on the disclosure |
The keyboard full-page capture includes the fixed shell at the current scroll offset, a full-page stitching limitation. It is evidence of the focused data disclosure, not approval of that stitched header placement. Default/viewport-width captures and unchanged shell regressions separately establish the actual shell composition. The manifest records capture timestamps, viewports, full-page dimensions, fixture kinds and which images were inspected. The before layout reference was captured 2026-09-30; final evidence is from 2026-10-06.
Deferred work and Phase 8 boundary
- No new appointment/global-task/event history, conversion, response-time, role/location/sector aggregate, CV-performance, AI insight or weekly goal is inferred. Existing onboarding supplies CV/Profile entry points; no new first-CV wizard or invented user fact.
- Ordering, resizing, reset and cross-device layout persistence remain Phase 8. Plain sections can be hosted by that later system without inventing it now. The five existing owner-local visibility flags are preserved today.
- P5-07 owns the new destination for retained existing reports. All report families remain on Dashboard at this checkpoint; there is no
/analyticsroute change here. - Existing AppShell Dashboard breadcrumbs still use historical Analytics/Overview metadata; no AppShell/header owner was changed. Reconcile that metadata when P5-07 adds the actual destination. Backend-provided reminder explanations and fallback source labels are rendered as returned, while UI labels/dates/numbers are localized; this slice does not claim that all server-generated prose has Bokmål translations.
- F04 production ingress, P2-D01 native package failure, P5-02 manual screen-reader acceptance, incomplete accessibility checks, Board native touch/full-result scaling, CV revision/rendering gates and unrelated SQLite/toast debt remain open. No production readiness or backend metric/timezone repair is claimed.
- Current application snapshots are not historical funnel conversions. Last-30-day counting includes future recorded dates under the existing query. Current-stage fallback ages and current Company.Source remain the documented server definitions. Correcting those backend/product contracts requires separate work.
Validation commands and baseline
Commands are run through the repository's RTK wrapper; frontend commands use job-tracker-ui as working directory. Native browser/security suites run sequentially on their disposable local API, never production. Static-export browser runs set DOTNET_ROOT=C:/Users/Connor/.dotnet, DOTNET_HOST_PATH=C:/Users/Connor/.dotnet/dotnet.exe, PLAYWRIGHT_STATIC_EXPORT=true, PLAYWRIGHT_PREBUILT_EXPORT=true. The UI export is built with NEXT_PUBLIC_API_BASE_URL=http://localhost:5302/api, CI=false. No infrastructure/test-runner/CI workaround or dependency change is needed for this slice.
| Check | Exact command after rtk proxy |
Final result / inherited comparison |
|---|---|---|
| Backend | C:/Users/Connor/.dotnet/dotnet.exe test JobTrackerApi.Tests/JobTrackerApi.Tests.csproj -c Release --no-restore --logger 'console;verbosity=minimal' |
934 passed, 0 failed/skipped; unchanged |
| Frontend | npm.cmd test -- --runInBand --silent |
398 passed in 79 suites; +8 Dashboard cases from inherited 390; no removed tests |
| Browser | npm.cmd run test:e2e -- --reporter=line,json |
79 actual passes (+2), unchanged P2-D01 expected failure; 0 unexpected/skipped/retries. 16m47s. Reporter counts the expected failure in its “80 passed”; this record does not |
| Standalone TypeScript | npm.cmd run typecheck |
Passed; historical TS2571 is not present |
| Lint | npm.cmd run lint |
Passed, zero warnings |
| Frontend production export | npm.cmd run build |
Passed compilation, TypeScript and static generation after final palette change |
| API Release | C:/Users/Connor/.dotnet/dotnet.exe build JobTrackerApi/JobTrackerApi.csproj -c Release --no-restore -warnaserror |
Passed, 0 warnings/errors |
| Rate-limit | npm.cmd run test:rate-limits -- --reporter=line |
9 passed; unchanged |
| Ingress | npm.cmd exec playwright test -- --config=playwright.ingress.config.ts --reporter=line |
6 passed; unchanged. Local application tests do not close F04 production evidence |
| PDF admission | npm.cmd run test:pdf-admission -- --reporter=line |
1 passed; unchanged |
| Effective Compose/config | python scripts/test-ingress.py |
3 passed; unchanged |
| Supply-chain fixtures | python scripts/test-supply-chain.py |
3 passed; unchanged |
| Ollama evaluation fixtures | python scripts/test-ollama-evaluation.py |
5 passed; unchanged |
| Extension capture URL | node --test browser-extension/tests/capture-url.test.mjs |
3 passed; unchanged |
| Secrets | python scripts/supply-chain.py scan-secrets |
Passed against staged final candidate |
| Whitespace | git diff --check and git diff --cached --check |
Passed against final candidate |
| Document/evidence checks | Python local Markdown-target and manifest file checks | 110 local document links resolve; all 20 final screenshot files present, 19 individually inspected |
The first extension command used a nonexistent background.test.mjs filename; file inventory established capture-url.test.mjs, whose three tests passed. A direct theme.vars access initially failed TS18048; the standard MUI palette fallback corrected it before the final export/typecheck. These are recorded validation-development corrections, not baseline defects or suppressed failures.
The initial browser summary preserves the first full run's native preference failure. The final browser summary records every case's expected and actual outcome: all 77 inherited successful cases remain successful, both new Dashboard cases pass, and the native frozen-package test still fails under its unchanged P2-D01 annotation. No test was removed, skipped or retried to obtain acceptance. Screenshots and summaries were retained before the subsequent API-only suites replaced their working output directories.
Performance and rollback
The Dashboard still makes six aggregate/feed requests across two resource groups, plus unchanged onboarding/shell reads. No request per job, no full Jobs crawl and no new persisted model. Top-company/source/tag lists retain existing bounded API limits; monthly series stay at 6/12/24 months. Exact-value disclosures add at most the returned months × five tag-series rows. Local populated/sparse fixtures establish presentation, not production load certification.
Rollback is a revert of this presentation/translation/test/document checkpoint; owner preference keys and backend data remain compatible and must not be cleared. No migration, irreversible data write or production deployment is involved.
Slice acceptance and stopping point
P5-06 presentation/preservation acceptance is satisfied within its approved scope: 934 backend, 398 frontend and 79 actual browser passes, plus the unchanged P2-D01 expected failure; rate-limit 9, ingress 6, PDF admission 1 and configuration 3. The increase is exactly eight frontend and two browser cases; other counts are unchanged. TypeScript, lint, both builds and final repository checks pass. Scoped automated keyboard behavior, rendered light/dark EN/Bokmål review and responsive checks pass; incomplete axe results and manual screen-reader acceptance remain explicitly open.
P5-07 is the recommended next ticket, with G-METRICS revalidation for its destination. It has not begun. Native package and CV gates, production F04, Board limitations and P5-06-O01 are not closed by this checkpoint; no Phase 8 or B07 implementation occurred.