Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f696f23fc5 | |||
| d6d3dd977d | |||
| 2d0f131a99 | |||
| fc749b1a30 | |||
| 32adac74f3 | |||
| 957fb5740f | |||
| 68a371c8ed | |||
| 0856ea64d6 | |||
| 0e3eab08ec | |||
| b1adae6b1c | |||
| 0b932da4a5 |
@@ -217,6 +217,11 @@ jobs:
|
||||
echo "Frontend lint process crashed with SIGSEGV — retrying once..."
|
||||
npm run lint
|
||||
|
||||
- name: Typecheck frontend
|
||||
working-directory: job-tracker-ui
|
||||
# Include application and test sources; Jest transpilation is not type checking.
|
||||
run: npm run typecheck
|
||||
|
||||
- name: Test frontend
|
||||
working-directory: job-tracker-ui
|
||||
# Run the WHOLE suite. Never whitelist test files here again: the previous
|
||||
|
||||
@@ -366,3 +366,39 @@ Logs are under `.artifacts/redesign-audit/`. The latest full Linux backend run r
|
||||
Independent verification downloaded the exact `es-abstract` **1.24.2** tarball URL from the lockfile, verified its SHA-512 integrity against that lock and read the member without extracting it. The official member and locally installed file match byte-for-byte; line 19 is `var flags = typeof F === 'undefined' ? '' : ToString(F);`. This is evidence of different dependency bytes in the runner's failure, not a source lint violation or a need to edit the dependency. The corruption mechanism is unproven; the existing [runner investigation](../../../infrastructure/runner-investigation.md) documents analogous failures.
|
||||
|
||||
This evidence-only push makes **one further unchanged-code CI attempt** with the workflow's existing isolated install cache. No new retry loop, lint suppression or gate bypass is added. If corrupted dependency bytes or assertion-free process crashes recur, stop release retries and request runner-host diagnostics; do not generate repeated empty commits. Public `/health` still reports version **299**, `/ready` reports ready before this attempt; that is the existing deployment, not proof that `a861ca4` is live. The completed Phase 1 audit is pushed separately at `957fb57` on `codex/ui-redesign-audit`.
|
||||
|
||||
### Final release retry — 2026-09-10
|
||||
|
||||
[Run 743, test job 1621](https://git.cesnimda.uk/cesnimda/jobtrackingapp/actions/runs/743/jobs/1621), for `27657f31131cd95d42f0e1290853ec9c9b3959a4`, completed in **7m25s**; Gitea recorded the failed status at **2026-09-09 22:32:25 UTC** (September 10 locally). Repository safety/Compose, extension, parser, secret/SBOM, backend build and dependency checks passed. The full backend suite passed **916/916**, zero skipped (15s test duration); frontend passed **67/67 suites, 298/298 tests** (82.867s). Locked installation, npm audit, lint and the production frontend build all passed. The previous malformed `es-abstract` source did not recur at lint.
|
||||
|
||||
The browser gate failed: **6 passed, 12 failed** (1.4m). The first failure was `worker process exited unexpectedly (code=null, signal=SIGSEGV)` in the authenticated accessibility case; its existing retry timed out in `beforeEach` after 30 seconds. The remaining eleven failures report `page.goto: Target page, context or browser has been closed`, including after their existing retries. There is no reported accessibility violation or application assertion mismatch in these failures. This establishes a process/browser execution failure, but does **not** establish its underlying host, runtime or hardware cause. The local **18/18** browser result above remains separate evidence and cannot replace the failed CI gate.
|
||||
|
||||
[Deploy job 1622](https://git.cesnimda.uk/cesnimda/jobtrackingapp/actions/runs/743/jobs/1622) was **skipped**. At **2026-09-10 05:07:21 UTC**, read-only HTTPS requests still returned `/health` → `{"status":"ok","version":"299"}` and `/ready` → `{"status":"ready"}`. The requested commits are pushed to main, but this release has **not deployed**. No production service was replaced by this run.
|
||||
|
||||
The documented stop condition is now reached: **no further unchanged-code release retries or gate bypasses**. Capture runner diagnostics before another attempt. This result is recorded on `codex/ui-redesign-audit` so that documenting the block does not itself start another main deployment. Phase 1 is complete; Phase 2 and B07 have not started. **B06 implementation complete, but F04 remains open**, for the runtime/production reasons in the closure checklist above as well as the unshipped release.
|
||||
|
||||
#### Runner evidence to collect before another attempt
|
||||
|
||||
Run these read-only commands on the actual Ubuntu **runner host**, which must be identified independently of the application host. No SSH alias or credentials are assumed. Use the run's timestamps for a bounded kernel-log window; the example covers this observed attempt. Do not reset Docker, delete caches, alter limits or restart production services just to obtain evidence.
|
||||
|
||||
```bash
|
||||
date -u
|
||||
uname -srmo
|
||||
free -h
|
||||
df -h / /tmp /dev/shm
|
||||
df -i / /tmp
|
||||
docker ps -a --no-trunc --format '{{.ID}} {{.Names}} {{.Image}} {{.Status}}'
|
||||
docker stats --no-stream --format '{{.Name}} memory={{.MemUsage}} cpu={{.CPUPerc}} pids={{.PIDs}}'
|
||||
sudo journalctl -k --utc --since '2026-09-09 22:20:00 UTC' --until '2026-09-09 22:40:00 UTC' --no-pager \
|
||||
| grep -Ei 'segfault|oom|out of memory|killed process|I/O error|machine check|hardware error|EDAC'
|
||||
# If systemd-coredump is installed: list metadata only, not memory dumps.
|
||||
command -v coredumpctl >/dev/null && \
|
||||
coredumpctl --since '2026-09-09 22:20:00 UTC' --until '2026-09-09 22:40:00 UTC' --no-pager list
|
||||
# Select a verified runner/job container ID from the inventory, if it still exists.
|
||||
read -r -p 'Observed runner/job container ID: ' runner_container_id
|
||||
test -n "$runner_container_id" && docker inspect --format \
|
||||
'{{.Name}} image={{.Image}} state={{json .State}} memory={{.HostConfig.Memory}} memorySwap={{.HostConfig.MemorySwap}} pidsLimit={{.HostConfig.PidsLimit}} shmSize={{.HostConfig.ShmSize}} ulimits={{json .HostConfig.Ulimits}}' \
|
||||
"$runner_container_id"
|
||||
```
|
||||
|
||||
Return selected, sanitized output plus the job-container identity/image and exact failure window. **Do not provide environment dumps, runner registration/config files, tokens, core files, full Docker inspection or unrelated logs.** A removed job container or unavailable kernel/core records is an evidence gap, not a clean bill of health. Host free memory after completion does not prove the earlier job's cgroup or shared-memory capacity. OOM/cgroup failures, segfault records, or storage/hardware errors would direct the next investigation; absent records do not justify guessing a cause. Retain the existing failed-job browser trace/error-context artifacts if available, keeping synthetic test data separate from production information. A successful full CI run, actual deploy completion and live version/health verification are still required before calling the push live; F04 then additionally requires its independent ingress evidence.
|
||||
|
||||
@@ -37,4 +37,10 @@ AI should support this flow naturally and contextually. It must never invent fac
|
||||
|
||||
## Status
|
||||
|
||||
2026-09-10: [Phase 3 UX architecture checkpoint](phase-3/README.md), based on Phase 2 `d6d3dd9`, defines grouped navigation, all 31 route destinations, 57 PRES impacts, workflow/data boundaries and the bounded Phase 4 foundation plan. Documentation only; no visual implementation or B07. F04 remains open. [Phase 2](phase-2/README.md) retains the executed 916 backend / 313 frontend baseline and explicit browser/provider/production gaps. Earlier status entries below are historical.
|
||||
|
||||
These documents define the intended target state. They should be updated as the remaining functional work is completed. The full redesign implementation should begin only after a stable pre-redesign baseline has been created and protected by tests.
|
||||
|
||||
2026-09-09: [Phase 1 source audit](audit/README.md) completed on `codex/ui-redesign-audit`: current routes, protected functionality, component/test/API inventories, identity/storage boundaries and proposed migration gates. No redesign implementation or B07 started. Required release checks and F04 production verification remain separate from this audit's completion.
|
||||
|
||||
2026-09-10: [Final Phase 1 summary and scope classification](audit/PHASE1_SUMMARY.md) completes the audit checkpoint: 57 protected capability groups, 31 route patterns, existing-versus-new distinctions, preservation contract, baseline defects/tests and the recommended Phase 2 baseline session. This finalization changes documentation only. B06/F04 runtime and production closure remain open; no Phase 2+ work has started.
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
# API integration and identity boundaries
|
||||
|
||||
## Transport
|
||||
|
||||
[api.ts](../../../job-tracker-ui/src/api.ts) is the shared Axios transport. Production uses `/api`; local development defaults to port 5202 unless `NEXT_PUBLIC_API_BASE_URL` overrides it. The value is embedded in the static build. All discovered direct frontend `api`/`axios` calls are in [API_CALL_SITES](API_CALL_SITES.md); UI wrappers such as `cvBuilder`, `aiWorkspace` and `applicationWorkspace` are included. SDK OAuth/navigation and server-to-server provider calls are separate from that census.
|
||||
|
||||
Credentialed requests use the HttpOnly auth cookie and XSRF handling. B02 adds immutable session provenance, cancellation and late-response rejection. Downloads use authorized blob requests where required; B05 CSV bytes must not be decoded/re-encoded through `Blob.text()`. Error handling uses shared extraction plus screen-specific recovery. A new fetch/cache/form abstraction must preserve all of these behaviors.
|
||||
|
||||
## Domain contract map
|
||||
|
||||
Paths are relative to `/api`. See the linked census for each method, interpolated ID and exact source line. DTOs are in [frontend types](../../../job-tracker-ui/src/types.ts), domain wrappers, and [API controllers](../../../JobTrackerApi/Controllers).
|
||||
|
||||
| Frontend owner | API groups | Identity / mutation contract |
|
||||
|---|---|---|
|
||||
| Login/account/security cards | `/auth/config`, `/auth/me`, login/register/reset/verification, `/auth/profile`, avatar, email-change, 2FA, sessions, language preferences | Public challenge/setup entry versus authenticated self-account actions; client never chooses authoritative owner |
|
||||
| Table/Kanban/edit/add | `/jobapplications`, `/board`, choices, duplicate-check, status, restore; `/companies` and relationship | Owner-scoped job/company lookup and associations; status changes and soft deletion distinct from creation intent |
|
||||
| Capture/discovery | `/jobimport/preview`, `/job-discovery/search`, saved-searches/results, Gmail suggested jobs | Review/preview and create separate; B01 connection enforcement; saved searches belong to account |
|
||||
| Personal analytics/reminders | Dashboard/statistics call sites, reminders/rules, notification-settings | Current-account reporting and dates; global/admin reporting separate |
|
||||
| Full/overlay job workspace | `/jobapplications/:id/workspace`, timeline, analysis, match, CV, tailoring, cover-letter, drafts, submitted-packages, checklist, interview-prep, follow-up | Job owner boundary plus nested artifact IDs; workspace aggregate reads do not authorize child writes |
|
||||
| Profile/evidence | `/career/profile`, versions/restore, `/career/evidence`, `/profile-cv/*` | Principal-owned singleton; extraction proposal review; history/projection distinct from CV overrides |
|
||||
| CV library/editor | `/cv/*` through `cvBuilderApi`, themes, outline, variant save/copy/history/publish/preview/export | Private owner + resource ID; job association validates same owner; B03 admission shared with other PDF callers |
|
||||
| Public CV | Public-CV slug endpoints | Anonymous access only through explicitly published variant; not arbitrary private ID download |
|
||||
| Contextual AI | `/ai/usage`, `/jobapplications/:id/ai/modules`, generate/history; profile and CV AI endpoints | Server entitlement, context ownership/consent and quotas; UI accept step does not replace backend authorization |
|
||||
| Documents | `/attachments/*`, submitted-package attachments, PDF export | Authorized upload/download/delete and scoped references; preserve content disposition and blob handling |
|
||||
| Correspondence/mail | `/correspondence/*`, `/gmail/*`, `/microsoft-graph/*`, `/imap/*`, `/email/providers`, message, drafts, send | OAuth mailbox distinct from login; owner/job association, draft revision, send attempt/delivery state |
|
||||
| Calendar | `/calendar/status`, `/calendar/jobs/:id/events`, `events.ics` | Owned job dates + provider permission; date-only and UTC timestamp semantics remain distinct |
|
||||
| Operations/notifications | `/operations`, cancel/retry, `/notifications`, read/dismiss | Owner-scoped asynchronous work; rerender does not mean enqueue again |
|
||||
| Export/lifecycle/billing | `/export/jobs`, `/export/account`, `/backup/encrypted`, `/account-lifecycle/*`, `/billing/status`, checkout/portal | Owner data; recent-auth/destructive/provider constraints; B05 at CSV boundary |
|
||||
| Admin | `/users`, `/admin/audit`, `/admin/system` and maintenance/email/AI subroutes | Server Admin role; sensitive system views kept separate from personal workspace |
|
||||
|
||||
No backend contract changes are proposed for Phase 1. API endpoint names and old route query values are compatibility surface even if new screen labels differ.
|
||||
|
||||
## Authentication and authorization
|
||||
|
||||
[Program.cs](../../../JobTrackerApi/Program.cs#L299) configures the smart authentication selector and local/provider validation. Local bearer or cookie JWT validation includes issuer/audience/signature/lifetime and a live local-session validation step. Production account-aware policies resolve authenticated identity before limiting. Relevant middleware order is forwarded headers/security/routing/CORS and CSRF handling, then [authentication → authorization → rate limiter → B02 SessionContextMiddleware → controllers](../../../JobTrackerApi/Program.cs#L654). Do not move owner limiting ahead of authentication during shell/API cleanup.
|
||||
|
||||
[CurrentUserService](../../../JobTrackerApi/Services/CurrentUserService.cs) derives local owner identity from the principal. Its background-owner scope cannot replace an HTTP request identity. [JobTrackerContext](../../../JobTrackerApi/Data/JobTrackerContext.cs#L132) uses live owner evaluation and fail-closed query filters. These are additional guards; not a license to remove explicit ownership checks from services or to introduce unscoped `IgnoreQueryFilters()` calls.
|
||||
|
||||
The [CV service](../../../JobTrackerApi/Services/CvVariantService.cs#L74) looks up `id + owner` and validates job associations with `jobId + owner`. Its explicit public lookup bypasses ordinary filters only for the published-slug path. Career Profile PUT derives the owner from current authentication and updates that singleton, explaining why B02 session provenance is separate from ID-based authorization. The old retained A form replay under B was not simply an IDOR. B02 now rejects retained/missing/forged session provenance server-side; the client also unmounts/aborts private state.
|
||||
|
||||
Admin controllers enforce Admin roles; Pro operations have server entitlement checks. The permissive default in `AccountPlanContext` is for isolated previews, while routed App supplies server-backed values. It is not proof that an ordinary user can obtain Pro server access. The frontend's auth/config and role-based navigation are presentation logic; a stale or crafted frontend must remain constrained by the API.
|
||||
|
||||
This is an integration map with targeted service verification, not a fresh claim that every authorization path in the application has been penetration-tested. Keep the permanent B01–B06 tests and original independent audit evidence.
|
||||
|
||||
## State and storage map
|
||||
|
||||
| State | Current scope | Preservation condition |
|
||||
|---|---|---|
|
||||
| Auth credential | HttpOnly cookie; CSRF companion cookie | Do not move tokens into browser storage or let client headers select owner |
|
||||
| `authUserKey`, auth persistence preferences | Browser identity marker/preferences | Not an authoritative server identity; B02 handles account-generation change |
|
||||
| `authSessionContext` storage signal | Cross-tab change notification | Immutable per page context; stale work aborts/reauthenticates, it does not adopt B's identity |
|
||||
| Form/editor state and workspace tab cache | React/component memory | Dispose on B02 invalidation; preserve unsaved-navigation guard within one identity |
|
||||
| Dashboard visibility | `dashboardPrefs:<owner>` localStorage | Owner-scoped, five booleans; not cross-device persistence |
|
||||
| Saved job views, headline fallback, onboarding dismissal | Owner-keyed localStorage | Never rehydrate A's private view/draft data into B's page |
|
||||
| Theme/language/sidebar collapse/table page size and columns | Device/browser preferences (language also server-hydrated) | These are presentation preferences, not private document caches |
|
||||
| Pro notice dismissal | Feature-keyed sessionStorage | Presentation-only; no CV/account content, entitlement still server-derived |
|
||||
| Mail drafts / application drafts / CV history | Server persistence with domain-specific APIs | Preserve each existing contract; do not replace with a generic browser draft mechanism |
|
||||
|
||||
Sources: [auth.ts](../../../job-tracker-ui/src/auth.ts), [sessionBoundary.ts](../../../job-tracker-ui/src/sessionBoundary.ts), [App.tsx](../../../job-tracker-ui/src/App.tsx), `SavedViewsMenu`, `DashboardView`, `ProfilePage`, `ProFeatureNotice`. Any future cache must be account-scoped and must not replay mutations after the page's identity generation changes. Record revision conflicts, identity isolation and duplicate-creation idempotency remain **three distinct contracts**.
|
||||
@@ -0,0 +1,272 @@
|
||||
# API call-site inventory
|
||||
|
||||
Generated with the installed TypeScript AST from the committed baseline. Each row is an actual `api`/`axios` call expression, not a claim of runtime test coverage. Paths are relative to the shared `/api` base; interpolations are preserved. Domain wrappers are included. Companion technical audit maps contracts and authorization.
|
||||
|
||||
| Source | Method | URL expression |
|
||||
|---|---|---|
|
||||
| [src/App.tsx:189](../../../job-tracker-ui/src/App.tsx#L189) | GET | "/auth/config" |
|
||||
| [src/App.tsx:206](../../../job-tracker-ui/src/App.tsx#L206) | GET | "/auth/me" |
|
||||
| [src/App.tsx:229](../../../job-tracker-ui/src/App.tsx#L229) | GET | "/jobapplications/reminders" |
|
||||
| [src/App.tsx:237](../../../job-tracker-ui/src/App.tsx#L237) | GET | "/notifications/unread-count" |
|
||||
| [src/App.tsx:250](../../../job-tracker-ui/src/App.tsx#L250) | GET | "/auth/me" |
|
||||
| [src/App.tsx:374](../../../job-tracker-ui/src/App.tsx#L374) | POST | "/auth/logout" |
|
||||
| [src/aiWorkspace.ts:29](../../../job-tracker-ui/src/aiWorkspace.ts#L29) | GET | "/ai/usage" |
|
||||
| [src/aiWorkspace.ts:30](../../../job-tracker-ui/src/aiWorkspace.ts#L30) | GET | `/jobapplications/${jobId}/ai/modules` |
|
||||
| [src/aiWorkspace.ts:32](../../../job-tracker-ui/src/aiWorkspace.ts#L32) | POST | `/jobapplications/${jobId}/ai/generate` |
|
||||
| [src/aiWorkspace.ts:34](../../../job-tracker-ui/src/aiWorkspace.ts#L34) | GET | `/jobapplications/${jobId}/ai/history` |
|
||||
| [src/aiWorkspace.ts:35](../../../job-tracker-ui/src/aiWorkspace.ts#L35) | DELETE | `/jobapplications/${jobId}/ai/history/${id}` |
|
||||
| [src/applicationWorkspace.ts:114](../../../job-tracker-ui/src/applicationWorkspace.ts#L114) | GET | `/jobapplications/${jobId}/workspace` |
|
||||
| [src/applicationWorkspace.ts:188](../../../job-tracker-ui/src/applicationWorkspace.ts#L188) | GET | `/jobapplications/${jobId}/timeline` |
|
||||
| [src/applicationWorkspace.ts:194](../../../job-tracker-ui/src/applicationWorkspace.ts#L194) | GET | `/jobapplications/${jobId}/analysis` |
|
||||
| [src/applicationWorkspace.ts:195](../../../job-tracker-ui/src/applicationWorkspace.ts#L195) | GET | `/jobapplications/${jobId}/match` |
|
||||
| [src/applicationWorkspace.ts:279](../../../job-tracker-ui/src/applicationWorkspace.ts#L279) | GET | `/jobapplications/${jobId}/cv` |
|
||||
| [src/applicationWorkspace.ts:281](../../../job-tracker-ui/src/applicationWorkspace.ts#L281) | PUT | `/jobapplications/${jobId}/cv` |
|
||||
| [src/applicationWorkspace.ts:283](../../../job-tracker-ui/src/applicationWorkspace.ts#L283) | GET | `/jobapplications/${jobId}/tailoring` |
|
||||
| [src/applicationWorkspace.ts:285](../../../job-tracker-ui/src/applicationWorkspace.ts#L285) | GET | `/jobapplications/${jobId}/cover-letter` |
|
||||
| [src/applicationWorkspace.ts:287](../../../job-tracker-ui/src/applicationWorkspace.ts#L287) | PUT | `/jobapplications/${jobId}/cover-letter` |
|
||||
| [src/applicationWorkspace.ts:289](../../../job-tracker-ui/src/applicationWorkspace.ts#L289) | POST | `/jobapplications/${jobId}/cover-letter/versions/${version}/restore` |
|
||||
| [src/applicationWorkspace.ts:291](../../../job-tracker-ui/src/applicationWorkspace.ts#L291) | GET | `/jobapplications/${jobId}/submitted-packages` |
|
||||
| [src/applicationWorkspace.ts:293](../../../job-tracker-ui/src/applicationWorkspace.ts#L293) | POST | `/jobapplications/${jobId}/submitted-packages` |
|
||||
| [src/applicationWorkspace.ts:295](../../../job-tracker-ui/src/applicationWorkspace.ts#L295) | GET | `/jobapplications/${jobId}/submitted-packages/${packageId}` |
|
||||
| [src/applicationWorkspace.ts:297](../../../job-tracker-ui/src/applicationWorkspace.ts#L297) | GET | `/jobapplications/${jobId}/submitted-packages/${packageId}/attachments/${attachmentId}` |
|
||||
| [src/applicationWorkspace.ts:299](../../../job-tracker-ui/src/applicationWorkspace.ts#L299) | PUT | `/jobapplications/${jobId}/application-drafts` |
|
||||
| [src/applicationWorkspace.ts:345](../../../job-tracker-ui/src/applicationWorkspace.ts#L345) | GET | `/jobapplications/${jobId}/interview-prep` |
|
||||
| [src/applicationWorkspace.ts:347](../../../job-tracker-ui/src/applicationWorkspace.ts#L347) | POST | `/jobapplications/${jobId}/interview-prep` |
|
||||
| [src/applicationWorkspace.ts:349](../../../job-tracker-ui/src/applicationWorkspace.ts#L349) | PATCH | `/jobapplications/${jobId}/interview-prep/${itemId}` |
|
||||
| [src/applicationWorkspace.ts:351](../../../job-tracker-ui/src/applicationWorkspace.ts#L351) | DELETE | `/jobapplications/${jobId}/interview-prep/${itemId}` |
|
||||
| [src/applicationWorkspace.ts:353](../../../job-tracker-ui/src/applicationWorkspace.ts#L353) | GET | `/jobapplications/${jobId}/follow-up` |
|
||||
| [src/applicationWorkspace.ts:355](../../../job-tracker-ui/src/applicationWorkspace.ts#L355) | PUT | `/jobapplications/${jobId}/follow-up` |
|
||||
| [src/applicationWorkspace.ts:360](../../../job-tracker-ui/src/applicationWorkspace.ts#L360) | GET | `/jobapplications/${jobId}/checklist` |
|
||||
| [src/applicationWorkspace.ts:362](../../../job-tracker-ui/src/applicationWorkspace.ts#L362) | POST | `/jobapplications/${jobId}/checklist` |
|
||||
| [src/applicationWorkspace.ts:364](../../../job-tracker-ui/src/applicationWorkspace.ts#L364) | PATCH | `/jobapplications/${jobId}/checklist/${itemId}` |
|
||||
| [src/applicationWorkspace.ts:366](../../../job-tracker-ui/src/applicationWorkspace.ts#L366) | DELETE | `/jobapplications/${jobId}/checklist/${itemId}` |
|
||||
| [src/applicationWorkspace.ts:368](../../../job-tracker-ui/src/applicationWorkspace.ts#L368) | PUT | `/jobapplications/${jobId}/checklist/order` |
|
||||
| [src/components/AddJobModal.tsx:224](../../../job-tracker-ui/src/components/AddJobModal.tsx#L224) | GET | "/jobapplications/duplicate-check" |
|
||||
| [src/components/AddJobModal.tsx:247](../../../job-tracker-ui/src/components/AddJobModal.tsx#L247) | POST | "/companies" |
|
||||
| [src/components/AddJobModal.tsx:271](../../../job-tracker-ui/src/components/AddJobModal.tsx#L271) | POST | "/jobimport/preview" |
|
||||
| [src/components/AddJobModal.tsx:312](../../../job-tracker-ui/src/components/AddJobModal.tsx#L312) | POST | "/attachments" |
|
||||
| [src/components/AddJobModal.tsx:331](../../../job-tracker-ui/src/components/AddJobModal.tsx#L331) | POST | "/jobapplications" |
|
||||
| [src/components/AddJobModal.tsx:358](../../../job-tracker-ui/src/components/AddJobModal.tsx#L358) | POST | `/jobapplications/${response.data.id}/generate-tailored-cv-draft` |
|
||||
| [src/components/AiPrivacySettingsCard.tsx:24](../../../job-tracker-ui/src/components/AiPrivacySettingsCard.tsx#L24) | GET | "/ai/settings" |
|
||||
| [src/components/AiPrivacySettingsCard.tsx:35](../../../job-tracker-ui/src/components/AiPrivacySettingsCard.tsx#L35) | PUT | "/ai/settings" |
|
||||
| [src/components/AiUsageCard.tsx:33](../../../job-tracker-ui/src/components/AiUsageCard.tsx#L33) | GET | "/ai/usage" |
|
||||
| [src/components/AiUsageCard.tsx:36](../../../job-tracker-ui/src/components/AiUsageCard.tsx#L36) | GET | "/billing/status" |
|
||||
| [src/components/AiUsageCard.tsx:46](../../../job-tracker-ui/src/components/AiUsageCard.tsx#L46) | POST | `/billing/${path}` |
|
||||
| [src/components/ApplicationCalendarActions.tsx:23](../../../job-tracker-ui/src/components/ApplicationCalendarActions.tsx#L23) | GET | "/calendar/status" |
|
||||
| [src/components/ApplicationCalendarActions.tsx:32](../../../job-tracker-ui/src/components/ApplicationCalendarActions.tsx#L32) | POST | `/calendar/jobs/${jobId}/events` |
|
||||
| [src/components/ApplicationCalendarActions.tsx:41](../../../job-tracker-ui/src/components/ApplicationCalendarActions.tsx#L41) | GET | `/calendar/jobs/${jobId}/events.ics` |
|
||||
| [src/components/ApplicationWorkflowAssist.tsx:23](../../../job-tracker-ui/src/components/ApplicationWorkflowAssist.tsx#L23) | GET | `/jobapplications/${jobId}/status-suggestion` |
|
||||
| [src/components/ApplicationWorkflowAssist.tsx:34](../../../job-tracker-ui/src/components/ApplicationWorkflowAssist.tsx#L34) | PATCH | `/jobapplications/${jobId}/status` |
|
||||
| [src/components/ApplicationWorkflowAssist.tsx:69](../../../job-tracker-ui/src/components/ApplicationWorkflowAssist.tsx#L69) | GET | `/jobapplications/${jobId}/focus-plan` |
|
||||
| [src/components/ApplicationWorkflowAssist.tsx:81](../../../job-tracker-ui/src/components/ApplicationWorkflowAssist.tsx#L81) | GET | `/jobapplications/${jobId}/focus-plan/operation` |
|
||||
| [src/components/ApplicationWorkflowAssist.tsx:91](../../../job-tracker-ui/src/components/ApplicationWorkflowAssist.tsx#L91) | GET | `/operations/${operation.id}` |
|
||||
| [src/components/ApplicationWorkflowAssist.tsx:113](../../../job-tracker-ui/src/components/ApplicationWorkflowAssist.tsx#L113) | POST | `/jobapplications/${jobId}/focus-plan/operations` |
|
||||
| [src/components/ApplicationWorkflowAssist.tsx:128](../../../job-tracker-ui/src/components/ApplicationWorkflowAssist.tsx#L128) | POST | `/operations/${operation.id}/${action}` |
|
||||
| [src/components/Attachment.tsx:15](../../../job-tracker-ui/src/components/Attachment.tsx#L15) | POST | "/attachments" |
|
||||
| [src/components/Attachments.tsx:98](../../../job-tracker-ui/src/components/Attachments.tsx#L98) | GET | `/attachments/${jobId}` |
|
||||
| [src/components/Attachments.tsx:125](../../../job-tracker-ui/src/components/Attachments.tsx#L125) | POST | "/attachments" |
|
||||
| [src/components/Attachments.tsx:143](../../../job-tracker-ui/src/components/Attachments.tsx#L143) | PATCH | `/attachments/${a.id}` |
|
||||
| [src/components/Attachments.tsx:153](../../../job-tracker-ui/src/components/Attachments.tsx#L153) | PATCH | `/attachments/${a.id}` |
|
||||
| [src/components/Attachments.tsx:164](../../../job-tracker-ui/src/components/Attachments.tsx#L164) | DELETE | `/attachments/${a.id}` |
|
||||
| [src/components/Attachments.tsx:179](../../../job-tracker-ui/src/components/Attachments.tsx#L179) | GET | `/attachments/download/${id}` |
|
||||
| [src/components/AuthStatusCard.tsx:32](../../../job-tracker-ui/src/components/AuthStatusCard.tsx#L32) | GET | "/auth/me" |
|
||||
| [src/components/AuthStatusCard.tsx:73](../../../job-tracker-ui/src/components/AuthStatusCard.tsx#L73) | POST | "/auth/logout" |
|
||||
| [src/components/BackupCard.tsx:28](../../../job-tracker-ui/src/components/BackupCard.tsx#L28) | GET | "/account-lifecycle/status" |
|
||||
| [src/components/BackupCard.tsx:49](../../../job-tracker-ui/src/components/BackupCard.tsx#L49) | POST | "/export/account" |
|
||||
| [src/components/BackupCard.tsx:62](../../../job-tracker-ui/src/components/BackupCard.tsx#L62) | POST | "/backup/encrypted" |
|
||||
| [src/components/BackupCard.tsx:87](../../../job-tracker-ui/src/components/BackupCard.tsx#L87) | POST | "/account-lifecycle/delete" |
|
||||
| [src/components/CompaniesTable.tsx:57](../../../job-tracker-ui/src/components/CompaniesTable.tsx#L57) | GET | "/companies" |
|
||||
| [src/components/CompaniesTable.tsx:94](../../../job-tracker-ui/src/components/CompaniesTable.tsx#L94) | GET | `/companies/${company.id}/relationship` |
|
||||
| [src/components/CompaniesTable.tsx:108](../../../job-tracker-ui/src/components/CompaniesTable.tsx#L108) | PUT | `/companies/${editing.id}` |
|
||||
| [src/components/Correspondence.tsx:157](../../../job-tracker-ui/src/components/Correspondence.tsx#L157) | GET | `/correspondence/${jobId}` |
|
||||
| [src/components/Correspondence.tsx:164](../../../job-tracker-ui/src/components/Correspondence.tsx#L164) | GET | "/gmail/status" |
|
||||
| [src/components/Correspondence.tsx:176](../../../job-tracker-ui/src/components/Correspondence.tsx#L176) | GET | "/gmail/job-candidates" |
|
||||
| [src/components/Correspondence.tsx:194](../../../job-tracker-ui/src/components/Correspondence.tsx#L194) | GET | "/jobapplications/choices" |
|
||||
| [src/components/Correspondence.tsx:213](../../../job-tracker-ui/src/components/Correspondence.tsx#L213) | POST | "/gmail/refresh-linked-threads" |
|
||||
| [src/components/Correspondence.tsx:311](../../../job-tracker-ui/src/components/Correspondence.tsx#L311) | POST | "/correspondence" |
|
||||
| [src/components/Correspondence.tsx:327](../../../job-tracker-ui/src/components/Correspondence.tsx#L327) | POST | "/correspondence" |
|
||||
| [src/components/Correspondence.tsx:348](../../../job-tracker-ui/src/components/Correspondence.tsx#L348) | GET | "/gmail/connect-url" |
|
||||
| [src/components/Correspondence.tsx:358](../../../job-tracker-ui/src/components/Correspondence.tsx#L358) | DELETE | "/gmail/connection" |
|
||||
| [src/components/Correspondence.tsx:371](../../../job-tracker-ui/src/components/Correspondence.tsx#L371) | DELETE | `/correspondence/${messageId}` |
|
||||
| [src/components/Correspondence.tsx:382](../../../job-tracker-ui/src/components/Correspondence.tsx#L382) | POST | "/gmail/import" |
|
||||
| [src/components/Correspondence.tsx:400](../../../job-tracker-ui/src/components/Correspondence.tsx#L400) | POST | "/gmail/import-thread" |
|
||||
| [src/components/Correspondence.tsx:422](../../../job-tracker-ui/src/components/Correspondence.tsx#L422) | POST | "/gmail/unlink-thread" |
|
||||
| [src/components/Correspondence.tsx:443](../../../job-tracker-ui/src/components/Correspondence.tsx#L443) | POST | "/gmail/relink-thread" |
|
||||
| [src/components/DashboardView.tsx:133](../../../job-tracker-ui/src/components/DashboardView.tsx#L133) | GET | "/jobapplications/stats" |
|
||||
| [src/components/DashboardView.tsx:134](../../../job-tracker-ui/src/components/DashboardView.tsx#L134) | GET | "/jobapplications/analytics-overview" |
|
||||
| [src/components/DashboardView.tsx:135](../../../job-tracker-ui/src/components/DashboardView.tsx#L135) | GET | "/jobapplications/reminders" |
|
||||
| [src/components/DashboardView.tsx:157](../../../job-tracker-ui/src/components/DashboardView.tsx#L157) | GET | "/jobapplications/analytics" |
|
||||
| [src/components/DashboardView.tsx:158](../../../job-tracker-ui/src/components/DashboardView.tsx#L158) | GET | "/jobapplications/tags" |
|
||||
| [src/components/DashboardView.tsx:159](../../../job-tracker-ui/src/components/DashboardView.tsx#L159) | GET | "/jobapplications/tag-trends" |
|
||||
| [src/components/EditJobDialog.tsx:108](../../../job-tracker-ui/src/components/EditJobDialog.tsx#L108) | GET | `/jobapplications/${jobId}` |
|
||||
| [src/components/EditJobDialog.tsx:147](../../../job-tracker-ui/src/components/EditJobDialog.tsx#L147) | PUT | `/jobapplications/${jobId}` |
|
||||
| [src/components/EmailProviderConnections.tsx:28](../../../job-tracker-ui/src/components/EmailProviderConnections.tsx#L28) | GET | "/gmail/status" |
|
||||
| [src/components/EmailProviderConnections.tsx:37](../../../job-tracker-ui/src/components/EmailProviderConnections.tsx#L37) | GET | "/microsoft-graph/status" |
|
||||
| [src/components/EmailProviderConnections.tsx:46](../../../job-tracker-ui/src/components/EmailProviderConnections.tsx#L46) | GET | "/imap/status" |
|
||||
| [src/components/EmailProviderConnections.tsx:85](../../../job-tracker-ui/src/components/EmailProviderConnections.tsx#L85) | GET | connectUrlPath |
|
||||
| [src/components/EmailProviderConnections.tsx:95](../../../job-tracker-ui/src/components/EmailProviderConnections.tsx#L95) | DELETE | path |
|
||||
| [src/components/EmailProviderConnections.tsx:110](../../../job-tracker-ui/src/components/EmailProviderConnections.tsx#L110) | POST | "/imap/connect" |
|
||||
| [src/components/ErrorBoundary.tsx:30](../../../job-tracker-ui/src/components/ErrorBoundary.tsx#L30) | POST | "/client-errors" |
|
||||
| [src/components/GoogleAuthCard.tsx:98](../../../job-tracker-ui/src/components/GoogleAuthCard.tsx#L98) | GET | "/auth/me" |
|
||||
| [src/components/GoogleAuthCard.tsx:108](../../../job-tracker-ui/src/components/GoogleAuthCard.tsx#L108) | GET | "/auth/config" |
|
||||
| [src/components/GoogleAuthCard.tsx:140](../../../job-tracker-ui/src/components/GoogleAuthCard.tsx#L140) | POST | "/auth/google/link" |
|
||||
| [src/components/GoogleAuthCard.tsx:144](../../../job-tracker-ui/src/components/GoogleAuthCard.tsx#L144) | POST | "/auth/google/exchange" |
|
||||
| [src/components/GoogleAuthCard.tsx:259](../../../job-tracker-ui/src/components/GoogleAuthCard.tsx#L259) | POST | "/auth/logout" |
|
||||
| [src/components/GoogleAuthCard.tsx:277](../../../job-tracker-ui/src/components/GoogleAuthCard.tsx#L277) | DELETE | "/auth/google/link" |
|
||||
| [src/components/ImportExportJobs.tsx:187](../../../job-tracker-ui/src/components/ImportExportJobs.tsx#L187) | GET | url |
|
||||
| [src/components/ImportExportJobs.tsx:208](../../../job-tracker-ui/src/components/ImportExportJobs.tsx#L208) | POST | "/companies" |
|
||||
| [src/components/ImportExportJobs.tsx:210](../../../job-tracker-ui/src/components/ImportExportJobs.tsx#L210) | POST | "/jobapplications" |
|
||||
| [src/components/JobTable.tsx:313](../../../job-tracker-ui/src/components/JobTable.tsx#L313) | GET | "/jobapplications" |
|
||||
| [src/components/JobTable.tsx:380](../../../job-tracker-ui/src/components/JobTable.tsx#L380) | DELETE | `/jobapplications/${job.id}` |
|
||||
| [src/components/JobTable.tsx:390](../../../job-tracker-ui/src/components/JobTable.tsx#L390) | POST | `/jobapplications/${id}/restore` |
|
||||
| [src/components/JobTable.tsx:400](../../../job-tracker-ui/src/components/JobTable.tsx#L400) | PATCH | `/jobapplications/${id}/status` |
|
||||
| [src/components/JobTable.tsx:414](../../../job-tracker-ui/src/components/JobTable.tsx#L414) | DELETE | `/jobapplications/${id}` |
|
||||
| [src/components/JobTable.tsx:415](../../../job-tracker-ui/src/components/JobTable.tsx#L415) | POST | `/jobapplications/${id}/restore` |
|
||||
| [src/components/JobTable.tsx:416](../../../job-tracker-ui/src/components/JobTable.tsx#L416) | PATCH | `/jobapplications/${id}/status` |
|
||||
| [src/components/KanbanBoard.tsx:100](../../../job-tracker-ui/src/components/KanbanBoard.tsx#L100) | GET | "/jobapplications/board" |
|
||||
| [src/components/KanbanBoard.tsx:144](../../../job-tracker-ui/src/components/KanbanBoard.tsx#L144) | PATCH | `/jobapplications/${movingJobId}/status` |
|
||||
| [src/components/KanbanBoard.tsx:156](../../../job-tracker-ui/src/components/KanbanBoard.tsx#L156) | PATCH | `/jobapplications/${id}/status` |
|
||||
| [src/components/MicrosoftAuthCard.tsx:54](../../../job-tracker-ui/src/components/MicrosoftAuthCard.tsx#L54) | GET | "/auth/me" |
|
||||
| [src/components/MicrosoftAuthCard.tsx:84](../../../job-tracker-ui/src/components/MicrosoftAuthCard.tsx#L84) | POST | "/auth/microsoft/link" |
|
||||
| [src/components/MicrosoftAuthCard.tsx:91](../../../job-tracker-ui/src/components/MicrosoftAuthCard.tsx#L91) | POST | "/auth/microsoft/exchange" |
|
||||
| [src/components/MicrosoftAuthCard.tsx:202](../../../job-tracker-ui/src/components/MicrosoftAuthCard.tsx#L202) | POST | "/auth/logout" |
|
||||
| [src/components/MicrosoftAuthCard.tsx:220](../../../job-tracker-ui/src/components/MicrosoftAuthCard.tsx#L220) | DELETE | "/auth/microsoft/link" |
|
||||
| [src/components/NotificationsPopover.tsx:41](../../../job-tracker-ui/src/components/NotificationsPopover.tsx#L41) | GET | "/notifications?limit=10" |
|
||||
| [src/components/NotificationsPopover.tsx:59](../../../job-tracker-ui/src/components/NotificationsPopover.tsx#L59) | POST | `/notifications/${notification.id}/read` |
|
||||
| [src/components/NotificationsPopover.tsx:76](../../../job-tracker-ui/src/components/NotificationsPopover.tsx#L76) | DELETE | `/notifications/${notification.id}` |
|
||||
| [src/components/OnboardingChecklist.tsx:47](../../../job-tracker-ui/src/components/OnboardingChecklist.tsx#L47) | GET | "/auth/me" |
|
||||
| [src/components/OnboardingChecklist.tsx:48](../../../job-tracker-ui/src/components/OnboardingChecklist.tsx#L48) | GET | "/career/profile" |
|
||||
| [src/components/OnboardingChecklist.tsx:50](../../../job-tracker-ui/src/components/OnboardingChecklist.tsx#L50) | GET | url |
|
||||
| [src/components/QuickCommandDialog.tsx:69](../../../job-tracker-ui/src/components/QuickCommandDialog.tsx#L69) | GET | "/jobapplications" |
|
||||
| [src/components/QuickCommandDialog.tsx:73](../../../job-tracker-ui/src/components/QuickCommandDialog.tsx#L73) | GET | "/companies" |
|
||||
| [src/components/RemindersView.tsx:78](../../../job-tracker-ui/src/components/RemindersView.tsx#L78) | GET | "/jobapplications/reminders" |
|
||||
| [src/components/RemindersView.tsx:98](../../../job-tracker-ui/src/components/RemindersView.tsx#L98) | PATCH | `/jobapplications/${id}/followup` |
|
||||
| [src/components/RulesSettingsCard.tsx:27](../../../job-tracker-ui/src/components/RulesSettingsCard.tsx#L27) | GET | "/rules" |
|
||||
| [src/components/RulesSettingsCard.tsx:42](../../../job-tracker-ui/src/components/RulesSettingsCard.tsx#L42) | PUT | "/rules" |
|
||||
| [src/components/SessionsSettingsCard.tsx:49](../../../job-tracker-ui/src/components/SessionsSettingsCard.tsx#L49) | GET | "/auth/sessions" |
|
||||
| [src/components/SessionsSettingsCard.tsx:60](../../../job-tracker-ui/src/components/SessionsSettingsCard.tsx#L60) | DELETE | `/auth/sessions/${id}` |
|
||||
| [src/components/SessionsSettingsCard.tsx:77](../../../job-tracker-ui/src/components/SessionsSettingsCard.tsx#L77) | POST | "/auth/sessions/revoke-others" |
|
||||
| [src/components/SettingsView.tsx:80](../../../job-tracker-ui/src/components/SettingsView.tsx#L80) | GET | "/notification-settings" |
|
||||
| [src/components/SettingsView.tsx:91](../../../job-tracker-ui/src/components/SettingsView.tsx#L91) | PUT | "/notification-settings" |
|
||||
| [src/components/TwoFactorChallenge.tsx:29](../../../job-tracker-ui/src/components/TwoFactorChallenge.tsx#L29) | POST | "/auth/2fa/challenge" |
|
||||
| [src/components/TwoFactorSettingsCard.tsx:73](../../../job-tracker-ui/src/components/TwoFactorSettingsCard.tsx#L73) | GET | "/auth/2fa/status" |
|
||||
| [src/components/TwoFactorSettingsCard.tsx:79](../../../job-tracker-ui/src/components/TwoFactorSettingsCard.tsx#L79) | GET | "/auth/2fa/trusted-devices" |
|
||||
| [src/components/TwoFactorSettingsCard.tsx:90](../../../job-tracker-ui/src/components/TwoFactorSettingsCard.tsx#L90) | DELETE | `/auth/2fa/trusted-devices/${id}` |
|
||||
| [src/components/TwoFactorSettingsCard.tsx:99](../../../job-tracker-ui/src/components/TwoFactorSettingsCard.tsx#L99) | POST | "/auth/2fa/trusted-devices/revoke-all" |
|
||||
| [src/components/TwoFactorSettingsCard.tsx:123](../../../job-tracker-ui/src/components/TwoFactorSettingsCard.tsx#L123) | POST | "/auth/2fa/setup" |
|
||||
| [src/components/TwoFactorSettingsCard.tsx:128](../../../job-tracker-ui/src/components/TwoFactorSettingsCard.tsx#L128) | POST | "/auth/2fa/disable" |
|
||||
| [src/components/TwoFactorSettingsCard.tsx:133](../../../job-tracker-ui/src/components/TwoFactorSettingsCard.tsx#L133) | POST | "/auth/2fa/recovery-codes/regenerate" |
|
||||
| [src/components/TwoFactorSettingsCard.tsx:149](../../../job-tracker-ui/src/components/TwoFactorSettingsCard.tsx#L149) | POST | "/auth/2fa/verify-setup" |
|
||||
| [src/components/UserManagementCard.tsx:34](../../../job-tracker-ui/src/components/UserManagementCard.tsx#L34) | GET | "/users" |
|
||||
| [src/components/UserManagementCard.tsx:98](../../../job-tracker-ui/src/components/UserManagementCard.tsx#L98) | POST | "/users" |
|
||||
| [src/components/UserManagementCard.tsx:152](../../../job-tracker-ui/src/components/UserManagementCard.tsx#L152) | DELETE | `/users/${u.id}` |
|
||||
| [src/components/UserManagementCard.tsx:167](../../../job-tracker-ui/src/components/UserManagementCard.tsx#L167) | POST | `/users/${u.id}/send-password-reset` |
|
||||
| [src/components/job-workspace/useJobWorkspaceBaseData.ts:51](../../../job-tracker-ui/src/components/job-workspace/useJobWorkspaceBaseData.ts#L51) | GET | `/jobapplications/${jobId}` |
|
||||
| [src/components/job-workspace/useJobWorkspaceBaseData.ts:64](../../../job-tracker-ui/src/components/job-workspace/useJobWorkspaceBaseData.ts#L64) | GET | `/attachments/${jobId}` |
|
||||
| [src/components/job-workspace/useJobWorkspaceBaseData.ts:74](../../../job-tracker-ui/src/components/job-workspace/useJobWorkspaceBaseData.ts#L74) | GET | `/auth/me` |
|
||||
| [src/components/job-workspace/useJobWorkspaceBaseData.ts:82](../../../job-tracker-ui/src/components/job-workspace/useJobWorkspaceBaseData.ts#L82) | GET | `/jobapplications/${jobId}/history` |
|
||||
| [src/cvBuilder.ts:265](../../../job-tracker-ui/src/cvBuilder.ts#L265) | GET | "/cv/themes" |
|
||||
| [src/cvBuilder.ts:266](../../../job-tracker-ui/src/cvBuilder.ts#L266) | GET | `/cv/themes/${encodeURIComponent(themeId)}/preview` |
|
||||
| [src/cvBuilder.ts:267](../../../job-tracker-ui/src/cvBuilder.ts#L267) | GET | "/cv/outline" |
|
||||
| [src/cvBuilder.ts:268](../../../job-tracker-ui/src/cvBuilder.ts#L268) | GET | "/cv/variants" |
|
||||
| [src/cvBuilder.ts:270](../../../job-tracker-ui/src/cvBuilder.ts#L270) | POST | "/cv/variants" |
|
||||
| [src/cvBuilder.ts:271](../../../job-tracker-ui/src/cvBuilder.ts#L271) | GET | `/cv/variants/${id}` |
|
||||
| [src/cvBuilder.ts:273](../../../job-tracker-ui/src/cvBuilder.ts#L273) | PUT | `/cv/variants/${id}` |
|
||||
| [src/cvBuilder.ts:274](../../../job-tracker-ui/src/cvBuilder.ts#L274) | DELETE | `/cv/variants/${id}` |
|
||||
| [src/cvBuilder.ts:275](../../../job-tracker-ui/src/cvBuilder.ts#L275) | POST | `/cv/variants/${id}/duplicate` |
|
||||
| [src/cvBuilder.ts:276](../../../job-tracker-ui/src/cvBuilder.ts#L276) | PUT | `/cv/variants/${id}/public` |
|
||||
| [src/cvBuilder.ts:277](../../../job-tracker-ui/src/cvBuilder.ts#L277) | GET | `/cv/variants/${id}/versions` |
|
||||
| [src/cvBuilder.ts:278](../../../job-tracker-ui/src/cvBuilder.ts#L278) | POST | `/cv/variants/${id}/versions/${version}/restore` |
|
||||
| [src/cvBuilder.ts:279](../../../job-tracker-ui/src/cvBuilder.ts#L279) | POST | "/cv/preview" |
|
||||
| [src/cvBuilder.ts:281](../../../job-tracker-ui/src/cvBuilder.ts#L281) | POST | "/cv/ai/assist" |
|
||||
| [src/hooks/useCompanies.ts:31](../../../job-tracker-ui/src/hooks/useCompanies.ts#L31) | GET | '/companies' |
|
||||
| [src/i18n/I18nProvider.tsx:35](../../../job-tracker-ui/src/i18n/I18nProvider.tsx#L35) | PUT | "/auth/preferences/language" |
|
||||
| [src/views/AdminAuditPage.tsx:49](../../../job-tracker-ui/src/views/AdminAuditPage.tsx#L49) | GET | "/admin/audit?take=200" |
|
||||
| [src/views/AdminAuditPage.tsx:59](../../../job-tracker-ui/src/views/AdminAuditPage.tsx#L59) | POST | `/admin/audit/${e.id}/undo` |
|
||||
| [src/views/AdminAuditPage.tsx:73](../../../job-tracker-ui/src/views/AdminAuditPage.tsx#L73) | POST | `/jobapplications/${e.jobApplicationId}/restore` |
|
||||
| [src/views/AdminSystemPage.tsx:241](../../../job-tracker-ui/src/views/AdminSystemPage.tsx#L241) | GET | "/admin/system" |
|
||||
| [src/views/AdminSystemPage.tsx:242](../../../job-tracker-ui/src/views/AdminSystemPage.tsx#L242) | GET | "/admin/system/email-settings" |
|
||||
| [src/views/AdminSystemPage.tsx:243](../../../job-tracker-ui/src/views/AdminSystemPage.tsx#L243) | GET | "/admin/system/cv-benchmark" |
|
||||
| [src/views/AdminSystemPage.tsx:244](../../../job-tracker-ui/src/views/AdminSystemPage.tsx#L244) | GET | "/admin/system/maintenance-jobs" |
|
||||
| [src/views/AdminSystemPage.tsx:313](../../../job-tracker-ui/src/views/AdminSystemPage.tsx#L313) | POST | "/users/send-test-email" |
|
||||
| [src/views/AdminSystemPage.tsx:330](../../../job-tracker-ui/src/views/AdminSystemPage.tsx#L330) | PUT | "/admin/system/email-settings" |
|
||||
| [src/views/AdminSystemPage.tsx:358](../../../job-tracker-ui/src/views/AdminSystemPage.tsx#L358) | POST | `/admin/system/maintenance-jobs/${encodeURIComponent(jobId)}/runs` |
|
||||
| [src/views/AdminSystemPage.tsx:381](../../../job-tracker-ui/src/views/AdminSystemPage.tsx#L381) | POST | "/admin/system/ai/probe" |
|
||||
| [src/views/AdminUsersPage.tsx:49](../../../job-tracker-ui/src/views/AdminUsersPage.tsx#L49) | GET | "/users" |
|
||||
| [src/views/AdminUsersPage.tsx:93](../../../job-tracker-ui/src/views/AdminUsersPage.tsx#L93) | PUT | `/users/${u.id}/roles` |
|
||||
| [src/views/AdminUsersPage.tsx:103](../../../job-tracker-ui/src/views/AdminUsersPage.tsx#L103) | POST | `/users/${u.id}/send-password-reset` |
|
||||
| [src/views/AdminUsersPage.tsx:117](../../../job-tracker-ui/src/views/AdminUsersPage.tsx#L117) | DELETE | `/users/${u.id}` |
|
||||
| [src/views/AdminUsersPage.tsx:215](../../../job-tracker-ui/src/views/AdminUsersPage.tsx#L215) | POST | "/users" |
|
||||
| [src/views/CareerProfilePage.tsx:185](../../../job-tracker-ui/src/views/CareerProfilePage.tsx#L185) | GET | "/career/profile/versions" |
|
||||
| [src/views/CareerProfilePage.tsx:195](../../../job-tracker-ui/src/views/CareerProfilePage.tsx#L195) | POST | `/career/profile/versions/${version}/restore` |
|
||||
| [src/views/CareerProfilePage.tsx:220](../../../job-tracker-ui/src/views/CareerProfilePage.tsx#L220) | GET | "/profile-cv/runs" |
|
||||
| [src/views/CareerProfilePage.tsx:232](../../../job-tracker-ui/src/views/CareerProfilePage.tsx#L232) | POST | "/profile-cv/upload" |
|
||||
| [src/views/CareerProfilePage.tsx:248](../../../job-tracker-ui/src/views/CareerProfilePage.tsx#L248) | GET | "/career/profile" |
|
||||
| [src/views/CareerProfilePage.tsx:249](../../../job-tracker-ui/src/views/CareerProfilePage.tsx#L249) | GET | "/auth/me" |
|
||||
| [src/views/CareerProfilePage.tsx:289](../../../job-tracker-ui/src/views/CareerProfilePage.tsx#L289) | GET | `/profile-cv/runs/${run.id}/diff` |
|
||||
| [src/views/CareerProfilePage.tsx:408](../../../job-tracker-ui/src/views/CareerProfilePage.tsx#L408) | POST | "/auth/avatar" |
|
||||
| [src/views/CareerProfilePage.tsx:495](../../../job-tracker-ui/src/views/CareerProfilePage.tsx#L495) | DELETE | "/auth/avatar" |
|
||||
| [src/views/CareerProfilePage.tsx:556](../../../job-tracker-ui/src/views/CareerProfilePage.tsx#L556) | POST | "/profile-cv/rebuild" |
|
||||
| [src/views/CareerProfilePage.tsx:574](../../../job-tracker-ui/src/views/CareerProfilePage.tsx#L574) | POST | "/profile-cv/improve" |
|
||||
| [src/views/CareerProfilePage.tsx:592](../../../job-tracker-ui/src/views/CareerProfilePage.tsx#L592) | POST | "/profile-cv/reprocess" |
|
||||
| [src/views/CareerProfilePage.tsx:669](../../../job-tracker-ui/src/views/CareerProfilePage.tsx#L669) | POST | `/operations/${run.operation!.id}/cancel` |
|
||||
| [src/views/CareerProfilePage.tsx:680](../../../job-tracker-ui/src/views/CareerProfilePage.tsx#L680) | POST | `/operations/${run.operation!.id}/retry` |
|
||||
| [src/views/CareerProfilePage.tsx:726](../../../job-tracker-ui/src/views/CareerProfilePage.tsx#L726) | POST | `/profile-cv/runs/${run.id}/accept` |
|
||||
| [src/views/CareerProfilePage.tsx:737](../../../job-tracker-ui/src/views/CareerProfilePage.tsx#L737) | POST | `/profile-cv/runs/${run.id}/discard` |
|
||||
| [src/views/CareerProfilePage.tsx:772](../../../job-tracker-ui/src/views/CareerProfilePage.tsx#L772) | POST | "/profile-cv/parse" |
|
||||
| [src/views/CareerProfilePage.tsx:855](../../../job-tracker-ui/src/views/CareerProfilePage.tsx#L855) | PUT | "/career/profile" |
|
||||
| [src/views/CorrespondenceInboxPage.tsx:158](../../../job-tracker-ui/src/views/CorrespondenceInboxPage.tsx#L158) | GET | "/correspondence/page" |
|
||||
| [src/views/CorrespondenceInboxPage.tsx:185](../../../job-tracker-ui/src/views/CorrespondenceInboxPage.tsx#L185) | GET | "/email/providers" |
|
||||
| [src/views/CorrespondenceInboxPage.tsx:200](../../../job-tracker-ui/src/views/CorrespondenceInboxPage.tsx#L200) | GET | "/jobapplications/choices" |
|
||||
| [src/views/CorrespondenceInboxPage.tsx:213](../../../job-tracker-ui/src/views/CorrespondenceInboxPage.tsx#L213) | GET | "/email/drafts" |
|
||||
| [src/views/CorrespondenceInboxPage.tsx:248](../../../job-tracker-ui/src/views/CorrespondenceInboxPage.tsx#L248) | GET | "/email/message" |
|
||||
| [src/views/CorrespondenceInboxPage.tsx:260](../../../job-tracker-ui/src/views/CorrespondenceInboxPage.tsx#L260) | GET | `/correspondence/message/${item.id}` |
|
||||
| [src/views/CorrespondenceInboxPage.tsx:375](../../../job-tracker-ui/src/views/CorrespondenceInboxPage.tsx#L375) | DELETE | `/email/drafts/${draft.id}` |
|
||||
| [src/views/CorrespondenceInboxPage.tsx:394](../../../job-tracker-ui/src/views/CorrespondenceInboxPage.tsx#L394) | PUT | `/email/drafts/${draft.id}` |
|
||||
| [src/views/CorrespondenceInboxPage.tsx:400](../../../job-tracker-ui/src/views/CorrespondenceInboxPage.tsx#L400) | POST | "/email/drafts" |
|
||||
| [src/views/CorrespondenceInboxPage.tsx:432](../../../job-tracker-ui/src/views/CorrespondenceInboxPage.tsx#L432) | POST | "/email/send" |
|
||||
| [src/views/CorrespondenceInboxPage.tsx:447](../../../job-tracker-ui/src/views/CorrespondenceInboxPage.tsx#L447) | DELETE | `/email/drafts/${draft.id}` |
|
||||
| [src/views/CorrespondenceInboxPage.tsx:476](../../../job-tracker-ui/src/views/CorrespondenceInboxPage.tsx#L476) | POST | `/email/drafts/${draft.id}/new-attempt` |
|
||||
| [src/views/CorrespondenceInboxPage.tsx:503](../../../job-tracker-ui/src/views/CorrespondenceInboxPage.tsx#L503) | POST | "/gmail/unlink-thread" |
|
||||
| [src/views/CvBuilderEditor.tsx:312](../../../job-tracker-ui/src/views/CvBuilderEditor.tsx#L312) | POST | cvBuilderApi.exportPdfUrl(variantId) |
|
||||
| [src/views/CvBuilderPage.tsx:112](../../../job-tracker-ui/src/views/CvBuilderPage.tsx#L112) | POST | cvBuilderApi.exportPdfUrl(id) |
|
||||
| [src/views/ForgotPasswordPage.tsx:62](../../../job-tracker-ui/src/views/ForgotPasswordPage.tsx#L62) | POST | "/auth/request-password-reset" |
|
||||
| [src/views/GmailReviewPage.tsx:35](../../../job-tracker-ui/src/views/GmailReviewPage.tsx#L35) | GET | "/gmail/review-candidates" |
|
||||
| [src/views/GmailReviewPage.tsx:36](../../../job-tracker-ui/src/views/GmailReviewPage.tsx#L36) | GET | "/gmail/suggested-jobs" |
|
||||
| [src/views/GmailReviewPage.tsx:61](../../../job-tracker-ui/src/views/GmailReviewPage.tsx#L61) | POST | "/gmail/review-decision" |
|
||||
| [src/views/GmailReviewPage.tsx:88](../../../job-tracker-ui/src/views/GmailReviewPage.tsx#L88) | POST | "/gmail/manual-sync" |
|
||||
| [src/views/GmailReviewPage.tsx:112](../../../job-tracker-ui/src/views/GmailReviewPage.tsx#L112) | POST | "/gmail/create-suggested-job" |
|
||||
| [src/views/JobDiscoveryPage.tsx:54](../../../job-tracker-ui/src/views/JobDiscoveryPage.tsx#L54) | GET | "/job-discovery/saved-searches" |
|
||||
| [src/views/JobDiscoveryPage.tsx:63](../../../job-tracker-ui/src/views/JobDiscoveryPage.tsx#L63) | GET | "/job-discovery/search" |
|
||||
| [src/views/JobDiscoveryPage.tsx:78](../../../job-tracker-ui/src/views/JobDiscoveryPage.tsx#L78) | POST | "/job-discovery/saved-searches" |
|
||||
| [src/views/JobDiscoveryPage.tsx:89](../../../job-tracker-ui/src/views/JobDiscoveryPage.tsx#L89) | POST | `/job-discovery/saved-searches/${saved.id}/run` |
|
||||
| [src/views/JobDiscoveryPage.tsx:100](../../../job-tracker-ui/src/views/JobDiscoveryPage.tsx#L100) | DELETE | `/job-discovery/saved-searches/${saved.id}` |
|
||||
| [src/views/JobDiscoveryPage.tsx:109](../../../job-tracker-ui/src/views/JobDiscoveryPage.tsx#L109) | PATCH | `/job-discovery/saved-searches/${saved.id}` |
|
||||
| [src/views/JobDiscoveryPage.tsx:118](../../../job-tracker-ui/src/views/JobDiscoveryPage.tsx#L118) | PATCH | `/job-discovery/saved-searches/${activeSavedSearchId}/results/${encodeURIComponent(jobId)}/dismiss` |
|
||||
| [src/views/LandingPage.tsx:51](../../../job-tracker-ui/src/views/LandingPage.tsx#L51) | GET | "/auth/me" |
|
||||
| [src/views/LoginPage.tsx:59](../../../job-tracker-ui/src/views/LoginPage.tsx#L59) | GET | "/auth/config" |
|
||||
| [src/views/LoginPage.tsx:67](../../../job-tracker-ui/src/views/LoginPage.tsx#L67) | GET | "/auth/me" |
|
||||
| [src/views/LoginPage.tsx:96](../../../job-tracker-ui/src/views/LoginPage.tsx#L96) | POST | url |
|
||||
| [src/views/LoginPage.tsx:121](../../../job-tracker-ui/src/views/LoginPage.tsx#L121) | POST | "/auth/resend-verification-email" |
|
||||
| [src/views/MicrosoftLegacyRelinkPage.tsx:32](../../../job-tracker-ui/src/views/MicrosoftLegacyRelinkPage.tsx#L32) | POST | "/auth/microsoft/legacy-relink/confirm" |
|
||||
| [src/views/OfferComparisonPage.tsx:14](../../../job-tracker-ui/src/views/OfferComparisonPage.tsx#L14) | GET | "/jobapplications" |
|
||||
| [src/views/OperationsPage.tsx:47](../../../job-tracker-ui/src/views/OperationsPage.tsx#L47) | GET | "/operations?limit=50" |
|
||||
| [src/views/OperationsPage.tsx:48](../../../job-tracker-ui/src/views/OperationsPage.tsx#L48) | GET | "/notifications?limit=50" |
|
||||
| [src/views/OperationsPage.tsx:101](../../../job-tracker-ui/src/views/OperationsPage.tsx#L101) | POST | `/notifications/${notification.id}/read` |
|
||||
| [src/views/OperationsPage.tsx:105](../../../job-tracker-ui/src/views/OperationsPage.tsx#L105) | DELETE | `/notifications/${notification.id}` |
|
||||
| [src/views/OperationsPage.tsx:131](../../../job-tracker-ui/src/views/OperationsPage.tsx#L131) | POST | `/operations/${operation.id}/cancel` |
|
||||
| [src/views/OperationsPage.tsx:136](../../../job-tracker-ui/src/views/OperationsPage.tsx#L136) | POST | `/operations/${operation.id}/retry` |
|
||||
| [src/views/ProfilePage.tsx:91](../../../job-tracker-ui/src/views/ProfilePage.tsx#L91) | GET | "/auth/me" |
|
||||
| [src/views/ProfilePage.tsx:103](../../../job-tracker-ui/src/views/ProfilePage.tsx#L103) | GET | "/auth/email-change" |
|
||||
| [src/views/ProfilePage.tsx:149](../../../job-tracker-ui/src/views/ProfilePage.tsx#L149) | POST | "/auth/avatar" |
|
||||
| [src/views/ProfilePage.tsx:202](../../../job-tracker-ui/src/views/ProfilePage.tsx#L202) | DELETE | "/auth/avatar" |
|
||||
| [src/views/ProfilePage.tsx:272](../../../job-tracker-ui/src/views/ProfilePage.tsx#L272) | POST | "/auth/email-change/request" |
|
||||
| [src/views/ProfilePage.tsx:290](../../../job-tracker-ui/src/views/ProfilePage.tsx#L290) | POST | "/auth/email-change/cancel" |
|
||||
| [src/views/ProfilePage.tsx:316](../../../job-tracker-ui/src/views/ProfilePage.tsx#L316) | PUT | "/auth/profile" |
|
||||
| [src/views/ProfilePage.tsx:350](../../../job-tracker-ui/src/views/ProfilePage.tsx#L350) | POST | "/auth/change-password" |
|
||||
| [src/views/PublicCvPage.tsx:26](../../../job-tracker-ui/src/views/PublicCvPage.tsx#L26) | GET | `/public-cv/${slug}` |
|
||||
| [src/views/ResetPasswordPage.tsx:66](../../../job-tracker-ui/src/views/ResetPasswordPage.tsx#L66) | POST | "/auth/reset-password" |
|
||||
| [src/views/VerifyEmailPage.tsx:31](../../../job-tracker-ui/src/views/VerifyEmailPage.tsx#L31) | POST | emailChange ? "/auth/email-change/confirm" : "/auth/verify-email" |
|
||||
| [src/views/career/CareerEvidenceBank.tsx:15](../../../job-tracker-ui/src/views/career/CareerEvidenceBank.tsx#L15) | GET | "/career/evidence" |
|
||||
| [src/views/career/CareerEvidenceBank.tsx:17](../../../job-tracker-ui/src/views/career/CareerEvidenceBank.tsx#L17) | POST | "/career/evidence" |
|
||||
| [src/views/career/CareerEvidenceBank.tsx:18](../../../job-tracker-ui/src/views/career/CareerEvidenceBank.tsx#L18) | DELETE | `/career/evidence/${item.id}` |
|
||||
@@ -0,0 +1,97 @@
|
||||
# Component and source inventory
|
||||
|
||||
Every production TSX module under `src` and `app` is listed. Lines are size indicators, not complexity scores. Reachability is relative-import reachability from ClientApp; app entry files are expected to be outside that graph. MUI primitives are dependencies, not missing local components. No unreachable file is approved for deletion.
|
||||
|
||||
| Module | Lines | Client graph | Local direct consumers | Rendered component names |
|
||||
|---|---|---|---|---|
|
||||
| [app/[[...slug]]/ClientShell.tsx:1](../../../job-tracker-ui/app/[[...slug]]/ClientShell.tsx#L1) | 14 | outside graph | page.tsx | ClientApp |
|
||||
| [app/layout.tsx:1](../../../job-tracker-ui/app/layout.tsx#L1) | 42 | outside graph | — | Script |
|
||||
| [app/page.tsx:1](../../../job-tracker-ui/app/page.tsx#L1) | 6 | outside graph | — | ClientShell |
|
||||
| [src/App.tsx:1](../../../job-tracker-ui/src/App.tsx#L1) | 500 | reachable | ClientApp.tsx | AccountCircleIcon, AccountPlanProvider, AddJobModal, AdminAuditPage, AdminSystemPage, AdminUsersPage, AlarmIcon, AppShell, Application, ApplicationWorkspacePage, Box, BusinessIcon, Button, CareerWorkspacePage, CompaniesTable, CompareArrowsIcon, ConfirmProvider, ConnectedAccountsPage, CorrespondenceInboxPage, CvBuilderEditor, CvBuilderPage, DashboardIcon, DashboardView, DeleteOutlineIcon, DescriptionOutlinedIcon, ForgotPasswordPage, I18nProvider, IconButton, JobDiscoveryPage, JobTable, JobTrackerThemeProvider, KanbanBoard, LandingPage, LegacyApplicationRedirect, LoginPage, MailOutlineIcon, MemoryIcon, MicrosoftLegacyRelinkPage, Navigate, NotFoundPage, NotificationsPopover, OfferComparisonPage, OperationsPage, PageLoader, ProfilePage, PromptProvider, PublicCvPage, QuickCommandDialog, RemindersView, ResetPasswordPage, Route, RouteErrorPage, RouterProvider, Routes, SearchIcon, SettingsIcon, SettingsView, Shell, ShieldIcon, Suspense, ThemeAwareShell, ThemeControlsContext.Provider, ToastProvider, Typography, VerifyEmailPage, ViewKanbanIcon, WorkOutlineIcon |
|
||||
| [src/ClientApp.tsx:1](../../../job-tracker-ui/src/ClientApp.tsx#L1) | 21 | reachable | ClientShell.tsx | App, ErrorBoundary, I18nProvider, LocalizationProvider |
|
||||
| [src/ThemeProvider.tsx:1](../../../job-tracker-ui/src/ThemeProvider.tsx#L1) | 26 | reachable | App.tsx | CssBaseline, CssVarsProvider, ThemeModeSynchronizer |
|
||||
| [src/accountPlan.tsx:1](../../../job-tracker-ui/src/accountPlan.tsx#L1) | 21 | reachable | App.tsx, AddJobModal.tsx, ApplicationAssets.tsx, ApplicationWorkflowAssist.tsx, InterviewPrep.tsx, AiSectionAssistant.tsx, CareerProfilePage.tsx, CvBuilderEditor.tsx | AccountPlanContext.Provider |
|
||||
| [src/assets/JobbjaktMark.tsx:1](../../../job-tracker-ui/src/assets/JobbjaktMark.tsx#L1) | 19 | reachable | AppShell.tsx | |
|
||||
| [src/components/AddJobModal.tsx:1](../../../job-tracker-ui/src/components/AddJobModal.tsx#L1) | 652 | reachable | App.tsx | Alert, Autocomplete, Box, Button, Checkbox, Chip, CloseIcon, DatePicker, Dialog, DialogActions, DialogContent, DialogTitle, Divider, FormControlLabel, IconButton, List, ListItem, ListItemText, MenuItem, Step, StepLabel, Stepper, TagsInput, TextField, Typography, UploadFileOutlinedIcon |
|
||||
| [src/components/AiPrivacySettingsCard.tsx:1](../../../job-tracker-ui/src/components/AiPrivacySettingsCard.tsx#L1) | 76 | reachable | SettingsView.tsx | Alert, Box, Button, FormControlLabel, Paper, Skeleton, Switch, Typography |
|
||||
| [src/components/AiUsageCard.tsx:1](../../../job-tracker-ui/src/components/AiUsageCard.tsx#L1) | 91 | reachable | SettingsView.tsx | Alert, Box, Button, LinearProgress, Paper, Skeleton, Stack, Typography |
|
||||
| [src/components/ApplicationAssets.tsx:1](../../../job-tracker-ui/src/components/ApplicationAssets.tsx#L1) | 769 | reachable | ApplicationWorkspacePage.tsx | Alert, ApplicationTailoringSection, ArchiveOutlinedIcon, AutoFixHighIcon, Box, Button, Chip, CoverLetterAiAssistant, Dialog, DialogActions, DialogContent, DialogTitle, Divider, DownloadOutlinedIcon, FormControl, IconButton, InputLabel, MenuItem, OpenInNewIcon, Paper, RestoreIcon, RichTextField, Select, Shell, Skeleton, Stack, SubmittedPackagesSection, TextField, Tooltip, Typography |
|
||||
| [src/components/ApplicationCalendarActions.tsx:1](../../../job-tracker-ui/src/components/ApplicationCalendarActions.tsx#L1) | 81 | reachable | ApplicationWorkspacePage.tsx | Alert, Box, Button, CalendarMonthOutlinedIcon, CircularProgress, DownloadOutlinedIcon, OpenInNewIcon, Stack, Typography |
|
||||
| [src/components/ApplicationChecklist.tsx:1](../../../job-tracker-ui/src/components/ApplicationChecklist.tsx#L1) | 224 | reachable | ApplicationWorkspacePage.tsx | Alert, ArrowDownwardIcon, ArrowUpwardIcon, Box, Button, Checkbox, Chip, DeleteOutlineIcon, IconButton, LinearProgress, Paper, Skeleton, Stack, TextField, Tooltip, Typography |
|
||||
| [src/components/ApplicationIntelligence.tsx:1](../../../job-tracker-ui/src/components/ApplicationIntelligence.tsx#L1) | 380 | reachable | ApplicationWorkspacePage.tsx | Alert, Box, Bullets, Chip, Chips, Divider, LinearProgress, Paper, React.Fragment, SectionShell, Skeleton, Stack, ToggleButton, ToggleButtonGroup, Typography |
|
||||
| [src/components/ApplicationWorkflowAssist.tsx:1](../../../job-tracker-ui/src/components/ApplicationWorkflowAssist.tsx#L1) | 166 | reachable | ApplicationWorkspacePage.tsx | Alert, Box, Button, CircularProgress, DraftCard, ListCard, Paper, Stack, TwoColumnSection, Typography |
|
||||
| [src/components/Attachment.tsx:1](../../../job-tracker-ui/src/components/Attachment.tsx#L1) | 26 | outside graph | — | |
|
||||
| [src/components/Attachments.tsx:1](../../../job-tracker-ui/src/components/Attachments.tsx#L1) | 373 | reachable | ApplicationWorkspacePage.tsx | Alert, Box, Button, Chip, CloudUploadOutlinedIcon, DeleteOutlineIcon, Dialog, DialogActions, DialogContent, DialogTitle, DownloadIcon, DriveFileRenameOutlineIcon, FormControl, IconButton, InputLabel, LinearProgress, MenuItem, Select, Switch, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, Typography, VisibilityOutlinedIcon |
|
||||
| [src/components/AuthStatusCard.tsx:1](../../../job-tracker-ui/src/components/AuthStatusCard.tsx#L1) | 88 | reachable | ProfilePage.tsx | Box, Button, Paper, Typography |
|
||||
| [src/components/BackupCard.tsx:1](../../../job-tracker-ui/src/components/BackupCard.tsx#L1) | 138 | reachable | SettingsView.tsx | Alert, Box, Button, Divider, Paper, Typography |
|
||||
| [src/components/CompaniesTable.tsx:1](../../../job-tracker-ui/src/components/CompaniesTable.tsx#L1) | 346 | reachable | App.tsx | Alert, Box, Button, Dialog, DialogActions, DialogContent, DialogTitle, EditOutlinedIcon, IconButton, Paper, PeopleAltOutlinedIcon, Stack, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, TextField, Typography, ViewStateNotice |
|
||||
| [src/components/Correspondence.tsx:1](../../../job-tracker-ui/src/components/Correspondence.tsx#L1) | 747 | reachable | ApplicationWorkspacePage.tsx | AutoAwesomeIcon, Autocomplete, Box, Button, Chip, CircularProgress, DeleteOutlineIcon, Dialog, DialogActions, DialogContent, DialogTitle, Divider, IconButton, List, ListItemButton, ListItemText, MailOutlineIcon, Paper, React.Fragment, SearchIcon, Tab, Tabs, TextField, ToggleButton, ToggleButtonGroup, Typography |
|
||||
| [src/components/CropImageDialog.tsx:1](../../../job-tracker-ui/src/components/CropImageDialog.tsx#L1) | 225 | reachable | CareerProfilePage.tsx, ProfilePage.tsx | Box, Button, Dialog, DialogActions, DialogContent, DialogTitle, Slider, Typography |
|
||||
| [src/components/CvTemplateGallery.tsx:1](../../../job-tracker-ui/src/components/CvTemplateGallery.tsx#L1) | 57 | reachable | CvCustomizeTab.tsx, CvBuilderPage.tsx | Box, Button, CheckCircleIcon, Chip, CvTemplateThumbnail, Paper, Stack, Typography |
|
||||
| [src/components/CvTemplateThumbnail.tsx:1](../../../job-tracker-ui/src/components/CvTemplateThumbnail.tsx#L1) | 35 | reachable | CvTemplateGallery.tsx, CvBuilderPage.tsx | Box |
|
||||
| [src/components/DashboardView.tsx:1](../../../job-tracker-ui/src/components/DashboardView.tsx#L1) | 682 | reachable | App.tsx | ArrowDownwardIcon, ArrowUpwardIcon, Box, Button, Card, Checkbox, Chip, LinearProgress, Menu, MenuItem, OnboardingChecklist, SectionCard, Stack, TuneIcon, Typography, ViewStateNotice |
|
||||
| [src/components/EditJobDialog.tsx:1](../../../job-tracker-ui/src/components/EditJobDialog.tsx#L1) | 261 | reachable | JobTable.tsx, ApplicationWorkspacePage.tsx | Autocomplete, Box, Button, Checkbox, Chip, DatePicker, Dialog, DialogActions, DialogContent, DialogTitle, FormControlLabel, MenuItem, Paper, TagsInput, TextField, Typography |
|
||||
| [src/components/EmailProviderConnections.tsx:1](../../../job-tracker-ui/src/components/EmailProviderConnections.tsx#L1) | 247 | reachable | ConnectedAccountsPage.tsx | Box, Button, CheckCircleIcon, Checkbox, Chip, Divider, FormControlLabel, Paper, ProviderRow, Stack, TextField, Typography |
|
||||
| [src/components/ErrorBoundary.tsx:1](../../../job-tracker-ui/src/components/ErrorBoundary.tsx#L1) | 63 | reachable | ClientApp.tsx | ErrorBoundaryInner |
|
||||
| [src/components/GoogleAuthCard.tsx:1](../../../job-tracker-ui/src/components/GoogleAuthCard.tsx#L1) | 301 | reachable | LoginPage.tsx, ProfilePage.tsx | Box, Button, Chip, Paper, TwoFactorChallenge, Typography |
|
||||
| [src/components/GradientButton.tsx:1](../../../job-tracker-ui/src/components/GradientButton.tsx#L1) | 42 | reachable | LandingPage.tsx | Button |
|
||||
| [src/components/ImportExportJobs.tsx:1](../../../job-tracker-ui/src/components/ImportExportJobs.tsx#L1) | 282 | reachable | SettingsView.tsx | Box, Button, Paper, Typography |
|
||||
| [src/components/InterviewPrep.tsx:1](../../../job-tracker-ui/src/components/InterviewPrep.tsx#L1) | 512 | reachable | ApplicationWorkspacePage.tsx | Alert, ApplicationFollowUp, AutoFixHighIcon, Box, Button, Checkbox, Chip, DeleteOutlineIcon, Divider, IconButton, InterviewAiAssistant, LinearProgress, MenuItem, Paper, PrepRow, Shell, Skeleton, Stack, TextField, Tooltip, Typography |
|
||||
| [src/components/JobDetailsPanels.tsx:1](../../../job-tracker-ui/src/components/JobDetailsPanels.tsx#L1) | 59 | reachable | ApplicationWorkflowAssist.tsx | Box, Button, ListCard, TextField, Typography |
|
||||
| [src/components/JobFlowBar.tsx:1](../../../job-tracker-ui/src/components/JobFlowBar.tsx#L1) | 186 | outside graph | — | Box, Chip, CloseIcon, EmojiEventsIcon, ForumIcon, MarkEmailReadIcon, React.Fragment, Typography, WorkOutlineIcon |
|
||||
| [src/components/JobTable.tsx:1](../../../job-tracker-ui/src/components/JobTable.tsx#L1) | 866 | reachable | App.tsx, SettingsView.tsx | Box, Button, Checkbox, Chip, DeleteOutlineIcon, EditJobDialog, EditOutlinedIcon, EmptyJobsState, FormControl, FormControlLabel, IconButton, InputAdornment, InputLabel, Menu, MenuItem, MoreHorizIcon, Paper, RestoreFromTrashOutlinedIcon, SavedViewsMenu, SearchIcon, Select, Stack, Table, TableBody, TableCell, TableHead, TablePagination, TableRow, TableSortLabel, TextField, Tooltip, Typography, ViewColumnIcon, ViewStateNotice, WorkOutlineIcon |
|
||||
| [src/components/KanbanBoard.tsx:1](../../../job-tracker-ui/src/components/KanbanBoard.tsx#L1) | 428 | reachable | App.tsx | Alert, Box, Card, CardContent, Chip, IconButton, ListSubheader, Menu, MenuItem, MoreHorizIcon, Paper, Typography, ViewStateNotice |
|
||||
| [src/components/Markdown.tsx:1](../../../job-tracker-ui/src/components/Markdown.tsx#L1) | 68 | outside graph | — | Box, Link, Typography |
|
||||
| [src/components/MicrosoftAuthCard.tsx:1](../../../job-tracker-ui/src/components/MicrosoftAuthCard.tsx#L1) | 247 | reachable | LoginPage.tsx, MicrosoftLegacyRelinkPage.tsx, ProfilePage.tsx | Box, Button, Chip, Paper, TextField, TwoFactorChallenge, Typography |
|
||||
| [src/components/NotificationsPopover.tsx:1](../../../job-tracker-ui/src/components/NotificationsPopover.tsx#L1) | 168 | reachable | App.tsx | Alert, Box, Button, CircularProgress, CloseIcon, DeleteOutlineIcon, Divider, DoneIcon, IconButton, Popover, Stack, Tooltip, Typography |
|
||||
| [src/components/OnboardingChecklist.tsx:1](../../../job-tracker-ui/src/components/OnboardingChecklist.tsx#L1) | 127 | reachable | DashboardView.tsx | Box, Button, CheckCircleIcon, CloseIcon, IconButton, LinearProgress, Paper, RadioButtonUncheckedIcon, Stack, Typography |
|
||||
| [src/components/ProFeatureNotice.tsx:1](../../../job-tracker-ui/src/components/ProFeatureNotice.tsx#L1) | 55 | reachable | AiSectionAssistant.tsx, CareerProfilePage.tsx, CvBuilderEditor.tsx | Alert, Button, CloseIcon, IconButton, Stack, Typography |
|
||||
| [src/components/QuickCaptureCard.tsx:1](../../../job-tracker-ui/src/components/QuickCaptureCard.tsx#L1) | 70 | reachable | SettingsView.tsx | Box, Paper, TextField, Typography |
|
||||
| [src/components/QuickCommandDialog.tsx:1](../../../job-tracker-ui/src/components/QuickCommandDialog.tsx#L1) | 159 | reachable | App.tsx | Box, Dialog, DialogContent, List, ListItemButton, ListItemText, SearchIcon, TextField, Typography |
|
||||
| [src/components/RemindersView.tsx:1](../../../job-tracker-ui/src/components/RemindersView.tsx#L1) | 139 | reachable | App.tsx | Box, Button, Chip, Divider, Paper, ReminderSection, Typography, ViewStateNotice |
|
||||
| [src/components/RichTextField.tsx:1](../../../job-tracker-ui/src/components/RichTextField.tsx#L1) | 79 | reachable | ApplicationAssets.tsx, CvBuilderEditor.tsx, CareerProfileSections.tsx | Box, FormatBoldIcon, FormatItalicIcon, FormatUnderlinedIcon, IconButton, LinkIcon, TextField, Tooltip |
|
||||
| [src/components/RulesSettingsCard.tsx:1](../../../job-tracker-ui/src/components/RulesSettingsCard.tsx#L1) | 99 | reachable | SettingsView.tsx | Alert, Box, Button, Paper, TextField, Typography |
|
||||
| [src/components/SavedViewsMenu.tsx:1](../../../job-tracker-ui/src/components/SavedViewsMenu.tsx#L1) | 183 | reachable | JobTable.tsx | BookmarkBorderIcon, Button, DeleteOutlineIcon, Divider, IconButton, ListItemText, Menu, MenuItem, TextField, Tooltip |
|
||||
| [src/components/SessionsSettingsCard.tsx:1](../../../job-tracker-ui/src/components/SessionsSettingsCard.tsx#L1) | 148 | reachable | ProfilePage.tsx | Alert, Button, DeleteIcon, Dialog, DialogActions, DialogContent, DialogTitle, IconButton, List, ListItem, ListItemSecondaryAction, ListItemText, Paper, Typography |
|
||||
| [src/components/SettingsView.tsx:1](../../../job-tracker-ui/src/components/SettingsView.tsx#L1) | 244 | reachable | App.tsx | AiPrivacySettingsCard, AiUsageCard, Alert, BackupCard, Box, Button, Checkbox, FormControl, FormControlLabel, ImportExportJobs, InputLabel, MenuItem, Paper, QuickCaptureCard, RulesSettingsCard, SectionCard, Select, Skeleton, Tab, TabPanel, Tabs, Typography |
|
||||
| [src/components/TagsInput.tsx:1](../../../job-tracker-ui/src/components/TagsInput.tsx#L1) | 37 | reachable | AddJobModal.tsx, EditJobDialog.tsx | Autocomplete, Chip, TextField |
|
||||
| [src/components/TurnstileWidget.tsx:1](../../../job-tracker-ui/src/components/TurnstileWidget.tsx#L1) | 37 | reachable | LoginPage.tsx | Box, Typography |
|
||||
| [src/components/TwoFactorChallenge.tsx:1](../../../job-tracker-ui/src/components/TwoFactorChallenge.tsx#L1) | 84 | reachable | GoogleAuthCard.tsx, MicrosoftAuthCard.tsx, LoginPage.tsx | Alert, Box, Button, Checkbox, FormControlLabel, TextField, Typography |
|
||||
| [src/components/TwoFactorSettingsCard.tsx:1](../../../job-tracker-ui/src/components/TwoFactorSettingsCard.tsx#L1) | 391 | reachable | ProfilePage.tsx | Alert, Box, Button, Checkbox, DeleteIcon, Dialog, DialogActions, DialogContent, DialogTitle, Divider, FormControlLabel, IconButton, List, ListItem, ListItemSecondaryAction, ListItemText, Paper, TextField, Typography |
|
||||
| [src/components/UserManagementCard.tsx:1](../../../job-tracker-ui/src/components/UserManagementCard.tsx#L1) | 184 | outside graph | — | Box, Button, Paper, TextField, Typography |
|
||||
| [src/components/ViewStateNotice.tsx:1](../../../job-tracker-ui/src/components/ViewStateNotice.tsx#L1) | 51 | reachable | CompaniesTable.tsx, DashboardView.tsx, JobTable.tsx, KanbanBoard.tsx, RemindersView.tsx, AdminAuditPage.tsx | Alert, Box, Button, CircularProgress, Typography |
|
||||
| [src/components/cv/AiSectionAssistant.tsx:1](../../../job-tracker-ui/src/components/cv/AiSectionAssistant.tsx#L1) | 113 | reachable | CvBuilderEditor.tsx | Alert, AutoFixHighIcon, Box, Button, MenuItem, Paper, ProFeatureNotice, Stack, TextField, Typography |
|
||||
| [src/components/cv/CvCustomizeTab.tsx:1](../../../job-tracker-ui/src/components/cv/CvCustomizeTab.tsx#L1) | 103 | reachable | CvBuilderEditor.tsx | Box, Button, ControlSlider, CvTemplateGallery, Divider, FormControl, FormControlLabel, IconButton, InputLabel, MenuItem, Paper, Select, Slider, Stack, Switch, TextField, Typography |
|
||||
| [src/confirm.tsx:1](../../../job-tracker-ui/src/confirm.tsx#L1) | 88 | reachable | App.tsx, InterviewPrep.tsx, dialogs.ts, ApplicationWorkspacePage.tsx, CorrespondenceInboxPage.tsx, CareerEvidenceBank.tsx | Alert, Button, ConfirmContext.Provider, Dialog, DialogActions, DialogContent, DialogTitle, Typography |
|
||||
| [src/i18n/I18nProvider.tsx:1](../../../job-tracker-ui/src/i18n/I18nProvider.tsx#L1) | 67 | reachable | App.tsx, ClientApp.tsx, AddJobModal.tsx, AiPrivacySettingsCard.tsx, AiUsageCard.tsx, ApplicationAssets.tsx, ApplicationCalendarActions.tsx, ApplicationChecklist.tsx, ApplicationIntelligence.tsx, ApplicationWorkflowAssist.tsx, Attachments.tsx, AuthStatusCard.tsx, BackupCard.tsx, CompaniesTable.tsx, Correspondence.tsx, CropImageDialog.tsx, CvTemplateGallery.tsx, DashboardView.tsx, EditJobDialog.tsx, EmailProviderConnections.tsx, ErrorBoundary.tsx, GoogleAuthCard.tsx, ImportExportJobs.tsx, InterviewPrep.tsx, JobDetailsPanels.tsx, JobTable.tsx, KanbanBoard.tsx, MicrosoftAuthCard.tsx, NotificationsPopover.tsx, OnboardingChecklist.tsx, ProFeatureNotice.tsx, QuickCaptureCard.tsx, QuickCommandDialog.tsx, RemindersView.tsx, RichTextField.tsx, RulesSettingsCard.tsx, SavedViewsMenu.tsx, SessionsSettingsCard.tsx, SettingsView.tsx, TagsInput.tsx, TurnstileWidget.tsx, TwoFactorChallenge.tsx, TwoFactorSettingsCard.tsx, UserManagementCard.tsx, ViewStateNotice.tsx, AiSectionAssistant.tsx, CvCustomizeTab.tsx, confirm.tsx, AppShell.tsx, prompt.tsx, AdminAuditPage.tsx, AdminSystemPage.tsx, AdminUsersPage.tsx, ApplicationWorkspacePage.tsx, CareerProfilePage.tsx, ConnectedAccountsPage.tsx, CorrespondenceInboxPage.tsx, CvBuilderEditor.tsx, CvBuilderPage.tsx, ForgotPasswordPage.tsx, GmailReviewPage.tsx, JobDiscoveryPage.tsx, LandingPage.tsx, LoginPage.tsx, MicrosoftLegacyRelinkPage.tsx, NotFoundPage.tsx, OfferComparisonPage.tsx, OperationsPage.tsx, ProfilePage.tsx, ResetPasswordPage.tsx, RouteErrorPage.tsx, VerifyEmailPage.tsx, CareerEvidenceBank.tsx, CareerProfileSections.tsx, CareerWorkspaceOverview.tsx, ProfileCompleteness.tsx | I18nContext.Provider |
|
||||
| [src/layout/AppShell.tsx:1](../../../job-tracker-ui/src/layout/AppShell.tsx#L1) | 578 | reachable | App.tsx, LandingPage.tsx | AppBar, Avatar, Badge, Box, Breadcrumbs, Button, ButtonGroup, Chip, Divider, Drawer, IconButton, JobbjaktMark, List, ListItem, ListItemButton, ListItemIcon, ListItemText, Menu, MenuIcon, MenuItem, MenuOpenIcon, NotificationsNoneIcon, SettingsOutlinedIcon, StackLanguageToggle, Toolbar, Tooltip, Typography |
|
||||
| [src/prompt.tsx:1](../../../job-tracker-ui/src/prompt.tsx#L1) | 84 | reachable | App.tsx, BackupCard.tsx, dialogs.ts | Button, Dialog, DialogActions, DialogContent, DialogTitle, PromptContext.Provider, TextField, Typography |
|
||||
| [src/toast.tsx:1](../../../job-tracker-ui/src/toast.tsx#L1) | 84 | reachable | App.tsx, AddJobModal.tsx, AiPrivacySettingsCard.tsx, ApplicationWorkflowAssist.tsx, Attachments.tsx, AuthStatusCard.tsx, BackupCard.tsx, CompaniesTable.tsx, Correspondence.tsx, EditJobDialog.tsx, EmailProviderConnections.tsx, GoogleAuthCard.tsx, ImportExportJobs.tsx, JobTable.tsx, MicrosoftAuthCard.tsx, QuickCaptureCard.tsx, RemindersView.tsx, RulesSettingsCard.tsx, SessionsSettingsCard.tsx, SettingsView.tsx, TwoFactorSettingsCard.tsx, UserManagementCard.tsx, AiSectionAssistant.tsx, AdminAuditPage.tsx, AdminUsersPage.tsx, CareerProfilePage.tsx, CorrespondenceInboxPage.tsx, CvBuilderEditor.tsx, CvBuilderPage.tsx, ForgotPasswordPage.tsx, GmailReviewPage.tsx, LoginPage.tsx, ProfilePage.tsx, ResetPasswordPage.tsx | Alert, Button, Snackbar, ToastContext.Provider |
|
||||
| [src/views/AdminAuditPage.tsx:1](../../../job-tracker-ui/src/views/AdminAuditPage.tsx#L1) | 184 | reachable | App.tsx | Box, Button, Chip, Paper, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, Typography, ViewStateNotice |
|
||||
| [src/views/AdminSystemPage.tsx:1](../../../job-tracker-ui/src/views/AdminSystemPage.tsx#L1) | 676 | reachable | App.tsx | Alert, Box, Button, Checkbox, Chip, DetailRow, FormControlLabel, Paper, Stack, SummaryCard, Tab, Tabs, TextField, Typography |
|
||||
| [src/views/AdminUsersPage.tsx:1](../../../job-tracker-ui/src/views/AdminUsersPage.tsx#L1) | 308 | reachable | App.tsx | Alert, Box, Button, Checkbox, Chip, DataGrid, FormControlLabel, Paper, Stack, TextField, Tooltip, Typography |
|
||||
| [src/views/ApplicationWorkspacePage.tsx:1](../../../job-tracker-ui/src/views/ApplicationWorkspacePage.tsx#L1) | 491 | reachable | App.tsx | Accordion, AccordionDetails, AccordionSummary, Alert, ApplicationAnalysis, ApplicationCalendarActions, ApplicationChecklist, ApplicationCoverLetterSection, ApplicationCvSection, ApplicationInterviewPrep, ApplicationMatch, ApplicationPackageDraftsSection, ApplicationProgress, ApplicationStatusSuggestion, ApplicationStrategySnapshot, ApplicationTimeline, ApplicationWorkspace, ArrowBackIcon, ArrowForwardIcon, Attachments, AutoFixHighIcon, Box, Button, ChecklistIcon, Chip, Correspondence, DescriptionOutlinedIcon, Divider, EditJobDialog, EditOutlinedIcon, ExpandMoreIcon, FolderOutlinedIcon, IconButton, JobDetailsSection, MailOutlineIcon, OpenInNewIcon, OverviewDetails, OverviewSection, Paper, Skeleton, Stack, Tab, Tabs, Tooltip, Typography, WorkspaceHeader |
|
||||
| [src/views/CareerProfilePage.tsx:1](../../../job-tracker-ui/src/views/CareerProfilePage.tsx#L1) | 879 | reachable | CareerWorkspacePage.tsx | Accordion, AccordionDetails, AccordionSummary, Alert, Avatar, Box, Button, CareerEvidenceBank, CareerWorkspaceOverview, Checkbox, Chip, CropImageDialog, DeleteOutlineIcon, EducationSection, ExpandMoreIcon, FormControl, FormControlLabel, InputLabel, InterestsSection, LanguagesSection, LinearProgress, LongTailSections, MenuItem, OtherSectionsSection, Paper, PersonalInformationSection, PhotoCameraOutlinedIcon, ProFeatureNotice, ProfessionalSummarySection, ProfileCompleteness, Select, SkillsSection, Tab, Tabs, TextField, Typography, UploadFileOutlinedIcon, WorkExperienceSection |
|
||||
| [src/views/CareerWorkspacePage.tsx:1](../../../job-tracker-ui/src/views/CareerWorkspacePage.tsx#L1) | 8 | reachable | App.tsx | CareerProfilePage |
|
||||
| [src/views/ConnectedAccountsPage.tsx:1](../../../job-tracker-ui/src/views/ConnectedAccountsPage.tsx#L1) | 26 | reachable | App.tsx | Box, Button, EmailProviderConnections, Paper, Typography |
|
||||
| [src/views/CorrespondenceInboxPage.tsx:1](../../../job-tracker-ui/src/views/CorrespondenceInboxPage.tsx#L1) | 750 | reachable | App.tsx | Alert, Autocomplete, Box, Button, Chip, CircularProgress, FormControl, GmailReviewPage, InputLabel, MailOutlineIcon, MenuItem, Pagination, Paper, Select, Stack, TextField, Typography |
|
||||
| [src/views/CvBuilderEditor.tsx:1](../../../job-tracker-ui/src/views/CvBuilderEditor.tsx#L1) | 1131 | reachable | App.tsx | AddContentCard, AddIcon, AiSectionAssistant, Alert, ArrowBackIcon, ArrowDownwardIcon, ArrowUpwardIcon, AutoFixHighIcon, Box, Button, Chip, Collapse, ContentCopyIcon, ContentTab, CvCustomizeTab, DeleteOutlineIcon, Dialog, DialogActions, DialogContent, DialogTitle, Divider, DocumentAiTab, DragIndicatorIcon, EditorSkeleton, EntryEditor, ExpandMoreIcon, HistoryTab, IconButton, MenuItem, Paper, PictureAsPdfIcon, ProFeatureNotice, PublicIcon, RedoIcon, RichTextField, SaveBadge, SectionRow, Skeleton, SpellcheckIcon, Stack, Tab, Tabs, TextField, Tooltip, TranslateIcon, Typography, UndoIcon, VisibilityIcon, VisibilityOffIcon |
|
||||
| [src/views/CvBuilderPage.tsx:1](../../../job-tracker-ui/src/views/CvBuilderPage.tsx#L1) | 203 | reachable | App.tsx | AddIcon, Alert, Box, Button, Chip, CvTemplateGallery, CvTemplateThumbnail, DescriptionOutlinedIcon, Dialog, DialogActions, DialogContent, DialogTitle, IconButton, Menu, MenuItem, MoreVertIcon, Paper, PublicIcon, Stack, TextField, Typography |
|
||||
| [src/views/ForgotPasswordPage.tsx:1](../../../job-tracker-ui/src/views/ForgotPasswordPage.tsx#L1) | 101 | reachable | App.tsx | Alert, Box, Button, Paper, TextField, Typography |
|
||||
| [src/views/GmailReviewPage.tsx:1](../../../job-tracker-ui/src/views/GmailReviewPage.tsx#L1) | 290 | reachable | CorrespondenceInboxPage.tsx | Box, Button, Chip, CircularProgress, Paper, Stack, TextField, Typography |
|
||||
| [src/views/JobDiscoveryPage.tsx:1](../../../job-tracker-ui/src/views/JobDiscoveryPage.tsx#L1) | 220 | reachable | App.tsx | AddIcon, Alert, BookmarkAddOutlinedIcon, Box, Button, Card, CardActions, CardContent, Chip, CircularProgress, DeleteOutlineIcon, FormControlLabel, MenuItem, RefreshIcon, SearchIcon, Stack, Switch, TextField, Typography |
|
||||
| [src/views/LandingPage.tsx:1](../../../job-tracker-ui/src/views/LandingPage.tsx#L1) | 282 | reachable | App.tsx | AlarmIcon, AttachIcon, Box, Button, Container, DashboardIcon, GradientButton, InsightsIcon, MailIcon, MatchIcon, Stack, StackLanguageToggle, Typography |
|
||||
| [src/views/LoginPage.tsx:1](../../../job-tracker-ui/src/views/LoginPage.tsx#L1) | 279 | reachable | App.tsx | Alert, Box, Button, Checkbox, Divider, FormControlLabel, GoogleAuthCard, MicrosoftAuthCard, Paper, TextField, TurnstileWidget, TwoFactorChallenge, Typography |
|
||||
| [src/views/MicrosoftLegacyRelinkPage.tsx:1](../../../job-tracker-ui/src/views/MicrosoftLegacyRelinkPage.tsx#L1) | 67 | reachable | App.tsx | Alert, Box, Button, CircularProgress, Paper, Typography |
|
||||
| [src/views/NotFoundPage.tsx:1](../../../job-tracker-ui/src/views/NotFoundPage.tsx#L1) | 41 | reachable | App.tsx | Box, Button, Paper, Typography |
|
||||
| [src/views/OfferComparisonPage.tsx:1](../../../job-tracker-ui/src/views/OfferComparisonPage.tsx#L1) | 67 | reachable | App.tsx | Alert, Box, Button, Chip, Fact, Paper, Skeleton, Stack, Typography |
|
||||
| [src/views/OperationsPage.tsx:1](../../../job-tracker-ui/src/views/OperationsPage.tsx#L1) | 148 | reachable | App.tsx | Alert, Box, Button, Chip, LinearProgress, Paper, Stack, Typography |
|
||||
| [src/views/ProfilePage.tsx:1](../../../job-tracker-ui/src/views/ProfilePage.tsx#L1) | 373 | reachable | App.tsx | Alert, AuthStatusCard, Avatar, Box, Button, Chip, CropImageDialog, DeleteOutlineIcon, Divider, GoogleAuthCard, MicrosoftAuthCard, Paper, PhotoCameraOutlinedIcon, SessionsSettingsCard, TextField, TwoFactorSettingsCard, Typography |
|
||||
| [src/views/PublicCvPage.tsx:1](../../../job-tracker-ui/src/views/PublicCvPage.tsx#L1) | 87 | reachable | App.tsx | |
|
||||
| [src/views/ResetPasswordPage.tsx:1](../../../job-tracker-ui/src/views/ResetPasswordPage.tsx#L1) | 115 | reachable | App.tsx | Alert, Box, Button, Paper, TextField, Typography |
|
||||
| [src/views/RouteErrorPage.tsx:1](../../../job-tracker-ui/src/views/RouteErrorPage.tsx#L1) | 53 | reachable | App.tsx | Box, Button, Paper, Typography |
|
||||
| [src/views/VerifyEmailPage.tsx:1](../../../job-tracker-ui/src/views/VerifyEmailPage.tsx#L1) | 77 | reachable | App.tsx | Alert, Box, Button, CircularProgress, Paper, Typography |
|
||||
| [src/views/career/CareerEvidenceBank.tsx:1](../../../job-tracker-ui/src/views/career/CareerEvidenceBank.tsx#L1) | 39 | reachable | CareerProfilePage.tsx | Alert, Box, Button, Chip, DeleteOutlineIcon, MenuItem, Paper, Stack, TextField, Typography |
|
||||
| [src/views/career/CareerProfileSections.tsx:1](../../../job-tracker-ui/src/views/career/CareerProfileSections.tsx#L1) | 252 | reachable | CareerProfilePage.tsx | AddIcon, Box, Button, Chip, DeleteOutlineIcon, FieldReviewNote, IconButton, LinesField, RichTextField, Stack, TextField, Typography |
|
||||
| [src/views/career/CareerWorkspaceOverview.tsx:1](../../../job-tracker-ui/src/views/career/CareerWorkspaceOverview.tsx#L1) | 178 | reachable | CareerProfilePage.tsx | ActionCard, Alert, Box, Button, Chip, DescriptionOutlinedIcon, LinearProgress, Paper, PersonOutlineIcon, Skeleton, Stack, Typography, UploadFileOutlinedIcon, WorkOutlineIcon |
|
||||
| [src/views/career/ProfileCompleteness.tsx:1](../../../job-tracker-ui/src/views/career/ProfileCompleteness.tsx#L1) | 91 | reachable | CareerProfilePage.tsx | Accordion, AccordionDetails, AccordionSummary, Box, Button, Chip, ExpandMoreIcon, LinearProgress, Typography |
|
||||
@@ -0,0 +1,102 @@
|
||||
# Protected functionality ledger
|
||||
|
||||
This is the current source-backed preservation baseline. **All rows are Preserve** until a later approved migration explicitly records relocation, merger or deprecation. Target-only ideas are separated at the end. A component's existence proves implemented behavior, not live provider availability or usability quality. The [API census](API_CALL_SITES.md) and [component census](COMPONENT_INVENTORY.md) supply the individual call sites/consumers behind these groups.
|
||||
|
||||
There are **57 protected capability groups**. The [completion summary](PHASE1_SUMMARY.md) classifies existing behavior, UX improvements, new requirements and known defects separately. A preserve-and-improve classification never removes a capability from this contract; known defective behavior is not the intended behavior to preserve.
|
||||
|
||||
## Entry, navigation and account
|
||||
|
||||
| ID | Current user-facing function | Source / important contract |
|
||||
|---|---|---|
|
||||
| E01 | Public product explanation, Free/Pro feature comparison, honest unavailable-billing states and sign-in/register links | `LandingPage`, `planCatalog`; do not invent prices or purchasable capabilities |
|
||||
| E02 | Local sign-in/registration, remember-me, configured Turnstile, resend verification, reset password, verify email | `LoginPage`, reset/verification views, `TurnstileWidget`; server configuration controls availability |
|
||||
| E03 | Google/Microsoft sign-in/link flows, Microsoft legacy relink, provider popup success/error handling | `GoogleAuthCard`, `MicrosoftAuthCard`, `MicrosoftLegacyRelinkPage`; OAuth/state/origin rules remain |
|
||||
| E04 | 2FA challenge, recovery codes, setup/verify/disable/regenerate, trusted devices and revocation | `TwoFactorChallenge`, `TwoFactorSettingsCard`; preserve sensitive confirmation and recovery presentation |
|
||||
| E05 | Active session list, current-device distinction, single/other-session revocation, logout | `SessionsSettingsCard`, `AppShell`, `auth`, `sessionBoundary`; invalidation must clear private UI |
|
||||
| E06 | Avatar upload/remove, name/display name/headline, account email-change request/cancel/confirm, local password change | `ProfilePage`; account identity is distinct from canonical career data |
|
||||
| E07 | Collapsible desktop sidebar, mobile drawer, active subsection matching, keyboard command launcher | `AppShell`, `QuickCommandDialog`; job/company search and fixed navigation/add commands |
|
||||
| E08 | Theme light/dark/system, EN/Bokmål preference with server hydration, toast/confirm/prompt and error routes | `themePrefs`, `I18nProvider`, `toast`, `confirm`, `prompt`, route error/not-found views |
|
||||
| E09 | Cross-tab account switch, stale mutation/read rejection, BFCache/focus revalidation and reauthentication | `api`, `sessionBoundary`, `App`; B02 server precondition and client unmount/abort must survive extraction |
|
||||
|
||||
## Discover and manage jobs
|
||||
|
||||
| ID | Current user-facing function | Source / important contract |
|
||||
|---|---|---|
|
||||
| J01 | Search NAV listings by role/company/municipality, sorting, source/freshness/deadline/work-arrangement details, external listing links | `JobDiscoveryPage`; provider failures and empty results distinguishable |
|
||||
| J02 | Named saved searches, run/delete, automatic alert preference, new/updated results and dismissal | `JobDiscoveryPage`, saved-search APIs; retain owner scope and alert metadata |
|
||||
| J03 | Add job manually or from URL/pasted listing, reviewed preview, company lookup/create, tags and dates, duplicate warning | `AddJobModal`; preview is not a write; keep B01 outbound boundary and existing review step |
|
||||
| J04 | Capture via browser extension, bookmarklet and share/capture query; optional job attachment and tailored CV draft after create | `QuickCaptureCard`, `captureUrl`, `App`, `browser-extension`; preserve payload/URL contracts |
|
||||
| J05 | Job table search/filter/sort/pagination, visible columns/page size, saved named views, row/card actions | `JobTable`, `SavedViewsMenu`; mobile cards and accessible full-workspace entry already exist |
|
||||
| J06 | Selection/bulk status/delete/restore, soft-delete/trash and restore, edit job/company association | `JobTable`, `EditJobDialog`; preserve partial-error handling and confirmation semantics |
|
||||
| J07 | Kanban grouped status views, drag move, keyboard pickup/drop/cancel, direct status selection, job opening | `KanbanBoard`, `pipeline`; same records as table, not a second job model |
|
||||
| J08 | Company directory, recruiter/contact/source/location and pipeline/contact-date editing; company relationship job history | `CompaniesTable`; company creation is integrated with job/import flows, not assumed standalone CRUD |
|
||||
| J09 | Offer comparison including recorded compensation, decision deadline and notes, source-job links | `OfferComparisonPage`; no new compensation calculator implied |
|
||||
| J10 | JSON/CSV job export; JSON import; CSV-to-import-JSON conversion and import counts | `ImportExportJobs`; preserve B05 blob bytes/BOM, spreadsheet safety and typed formats; do not claim full account restore from job import |
|
||||
|
||||
## Job workspace and application artifacts
|
||||
|
||||
| ID | Current user-facing function | Source / important contract |
|
||||
|---|---|---|
|
||||
| W01 | Dedicated job workspace and reusable quick overlay; overview/analysis/CV/cover-letter/interview sections; old deep-link mapping | `ApplicationWorkspacePage`, `ApplicationWorkspace`, `jobWorkspaceRoute`; retain full-page/overlay parity |
|
||||
| W02 | Job detail/status/progression, next actions, dates, description, recruiter/contact and timeline | `JobDetailsSection`, `ApplicationProgress`, `ApplicationTimeline`; do not redefine pipeline enums or history |
|
||||
| W03 | Analysis, profile match/gaps, strategy snapshot, workflow assistance and status suggestions | `ApplicationAnalysis`, `ApplicationMatch`, `ApplicationStatusSuggestion`, `ApplicationStrategySnapshot`; show provenance/loading/failure and confirmation |
|
||||
| W04 | Checklist create/edit/complete/delete/order, progress and recommended next action | `ApplicationChecklist`; keep keyboard reorder and persisted item identity |
|
||||
| W05 | Select/attach a CV, create/tailor document variants, review tailoring guidance and selected-source context | `ApplicationCvSection`, `ApplicationTailoringSection`, `applicationWorkspace`; owner+job association enforced by API |
|
||||
| W06 | Cover-letter editing, AI suggestion review/reject/apply, saved versions and restore, unsaved navigation protection | `ApplicationAssets`, `CoverLetterAiAssistant`; document override must not mutate master profile |
|
||||
| W07 | Application draft fields, submission/package preparation, frozen submitted-package versions and archived attachment download | `ApplicationAssets`, `SubmittedPackagesSection`, `applicationWorkspace`; historical package is distinct from current draft |
|
||||
| W08 | Attachments upload/drag-drop, category/purpose, rename, AI-use flag, preview/download/delete | `Attachments`; owner-scoped binary retrieval, consent selection and size/error handling |
|
||||
| W09 | Interview questions/categories, manual items, answers, ready status, remove, contextual AI review/add-to-prep | `InterviewPrep`, `applicationWorkspace`; generated facts/answers require review |
|
||||
| W10 | Interview debrief template and follow-up content/date, readiness and next-action state | `InterviewPrep`; neutral prompts must not seed invented company/achievement facts |
|
||||
| W11 | Follow-up/reminder workflow, rule-driven dates/stages, notifications, deadline/follow-up ICS and Google/Outlook calendar event actions | `RemindersView`, `RulesSettingsCard`, `ApplicationCalendarActions`; calendar integration already exists, date-only/timestamp semantics must survive |
|
||||
| W12 | Async operation progress, pending/failed/retry/cancel states and associated notifications | `OperationsPage`, profile processing components; preserve operation IDs and server state, avoid duplicate starts on rerender |
|
||||
|
||||
## Career Profile and CVs
|
||||
|
||||
| ID | Current user-facing function | Source / important contract |
|
||||
|---|---|---|
|
||||
| C01 | Canonical structured career profile: personal details, summary, skills, work, education, languages, interests and long-tail/custom sections | `CareerProfilePage`, `views/career/*`; profile save is an account singleton, CV is an identified derived resource |
|
||||
| C02 | Profile overview, completeness/import guidance, manual edit, copy raw CV text, avatar and version history/restore | `CareerWorkspacePage`, `CareerProfilePage`; preserve projection/history behavior without redesigning schema |
|
||||
| C03 | CV/file upload and extraction, rebuild/improve/reprocess, progress/history, diff, explicit low-confidence selection, accept/discard, retry/cancel | `CareerProfilePage`, `/profile-cv/*`; uploaded proposal is not silently canonical truth |
|
||||
| C04 | Evidence bank: sourced statement/title/category/tags, add/list/delete and truthfulness guidance | `CareerEvidenceBank`; evidence belongs to profile owner and is not AI-invented experience |
|
||||
| C05 | CV library create/name/copy/delete, template gallery and real sample thumbnails, publish/unpublish/share and PDF download | `CvBuilderPage`, `cvBuilder`; separate public slug from numeric private ID |
|
||||
| C06 | Studio Template/Content/Customize/AI Tools/History navigation; debounced preview, page boundaries/automatic fit and save state | `CvBuilderEditor`; existing mobile edit/preview and desktop split must retain parity; automatic fit is not a manual zoom control |
|
||||
| C07 | Built-in item visibility/order, custom sections, preset/free-text section catalog, item/content overrides, hide/remove, native section drag and keyboard reorder | `CvBuilderEditor`, `components/cv/*`, `useDragReorder`; selected profile source and overrides remain distinguishable |
|
||||
| C08 | Template-capability-aware typography/colors/header/headings, skills presentation/groups, one/two-column and sidebar settings, photo/icons | `CvCustomizeTab`, theme registry; retain unsupported-setting handling and server render equivalence |
|
||||
| C09 | A4/Letter, density, margins/spacing, date format, document language, per-section rows/grid/compact/bubbles and columns | `cvBuilder`, `CvCustomizeTab`, content controls; do not drop advanced controls merely to simplify layout |
|
||||
| C10 | Undo/redo of editor settings, save queue, unsaved guard, history/version restore and preview lifecycle | `CvBuilderEditor`; current queue serializes one mounted editor only, not concurrent browsers |
|
||||
| C11 | Per-section AI improve/translate/custom instruction and whole-document translate/improve/spelling review | `AiSectionAssistant`, builder AI tab; source/target language, current/suggestion comparison, apply/reject |
|
||||
| C12 | Authenticated PDF export and anonymous published-CV preview/PDF; long searchable multipage documents | `PublicCvPage`, `cvBuilder`, server renderer; B03 shared resource admission and public ownership/publication checks |
|
||||
|
||||
## Correspondence and external accounts
|
||||
|
||||
| ID | Current user-facing function | Source / important contract |
|
||||
|---|---|---|
|
||||
| M01 | Job-scoped manual note/email log, search, delete, paste/import message; current job/company context | `Correspondence`; manual record is different from sending a real email |
|
||||
| M02 | Gmail candidate search/import, linked-thread refresh/sync history, import thread, unlink/relink/move with job selection | `Correspondence`; cross-job relationship changes remain owner-bound |
|
||||
| M03 | Global correspondence inbox with pagination/search/filter/direction/provider/link state, full message and attachment information, source-job links | `CorrespondenceInboxPage`; provider read failure distinct from no messages |
|
||||
| M04 | Compose/reply, choose connected provider and job/recipient, save/resume/delete drafts, review/confirm send, delivery states and explicit new attempt | `CorrespondenceInboxPage`; persisted draft revision and send idempotency/uncertain delivery controls must survive navigation changes |
|
||||
| M05 | Gmail review candidates, proposed jobs, link/reject/keep decisions, notes, manual sync and create suggested job | `GmailReviewPage` embedded in inbox; retain legacy review redirect |
|
||||
| M06 | Gmail/Google and Microsoft Graph connection/status/disconnect, IMAP connection/status/disconnect | `EmailProviderConnections`, `ConnectedAccountsPage`; login identity and mail-provider identity are separate capabilities |
|
||||
|
||||
## Dashboard, settings and administration
|
||||
|
||||
| ID | Current user-facing function | Source / important contract |
|
||||
|---|---|---|
|
||||
| S01 | Personal dashboard counts, activity/funnel/company/skill trends, stage/salary insights and actionable next steps | `DashboardView`, analytics APIs; chart transformations and empty/error states preserved |
|
||||
| S02 | Five per-owner local visibility preferences; onboarding progress and dismiss; notification bell/unread/read/dismiss | `DashboardView`, `OnboardingChecklist`, `NotificationsPopover`; no server widget-order/resize contract exists yet |
|
||||
| S03 | Follow-up/ghosting rule settings, email reminder preference, table preferences, theme/language and connected-account links | `SettingsView`, `RulesSettingsCard`; preferences have different scopes, do not replace with one unsafely shared object |
|
||||
| S04 | AI privacy settings, usage/limits, honest Free/Pro lock states, billing status, conditional checkout and portal | `AiPrivacySettingsCard`, `AiUsageCard`, `AccountPlanProvider`; server entitlement authoritative, not client context defaults |
|
||||
| S05 | Account export, encrypted backup download, lifecycle status and deliberate deletion confirmation | `BackupCard`; recent-auth/password/provider restrictions remain; a downloadable backup is not proven restore readiness |
|
||||
| A01 | Admin user list/create, role promotion/demotion guards, reset link and delete with last-admin/self warnings | `AdminUsersPage`, `/users`; API roles enforce privilege regardless of navigation |
|
||||
| A02 | Admin audit event list/detail, supported undo and job restore | `AdminAuditPage`; preserve event/ownership semantics rather than generic mutation buttons |
|
||||
| A03 | Separate system health/build/database/AI/email status, SMTP settings/test, AI probe, CV corpus benchmark and maintenance run controls | `AdminSystemPage`; sensitive config values/logs must not move into personal dashboard |
|
||||
|
||||
## Relationship to the target brief
|
||||
|
||||
The target grouped navigation would reorganize an existing collapsible sidebar. Jobs/Table and Kanban can become view choices over the existing APIs. Dedicated Analytics/Documents/Notifications destinations would relocate embedded capabilities. They are not permission to delete offers, reminders, correspondence review, operations or legacy URLs.
|
||||
|
||||
Dashboard drag/order/resize/reset are new work beyond the five local booleans; cross-device persistence is an unresolved scope choice. Optional goals, a new logo and expanded contextual access are separately identified additions; a general chat platform, product rename or additional providers are not implied by existing AI/integrations. Retain implemented Google/Outlook calendar actions even though the product brief describes broader calendar ambitions as future work.
|
||||
|
||||
The [new-requirements matrix](PHASE1_SUMMARY.md#3-genuinely-new-requirements-and-conditional-additions) distinguishes required versus suggested/future additions: expanded command search/actions, application density, configurable status mappings, per-task scheduling, intent-led onboarding, contextual-assistant access and aggregate admin metrics. Existing raw custom status values, job reminders, onboarding checklist, operational admin dashboard and CV document density must remain intact; none alone establishes the corresponding new contract. The brief asks for contextual assistant access, not necessarily a general chat platform; a new logo is required, while a name change remains undecided.
|
||||
|
||||
During migration, extend this table with destination, regression test and review evidence per ID. Do not treat a screen screenshot or a count of passing tests as proof that all rows survived.
|
||||
@@ -0,0 +1,117 @@
|
||||
# Phase 1 completion summary
|
||||
|
||||
Completed **2026-09-10** on `codex/ui-redesign-audit`. This is an audit checkpoint, not a verified production release. **No visual redesign, component-library migration, route restructuring, Phase 2 implementation or B07 work has begun. B06/F04 closure remains open.**
|
||||
|
||||
The detailed [architecture assessment](README.md), [functionality ledger](FUNCTIONALITY_INVENTORY.md), [route/workflow map](ROUTE_MAP.md), [API/identity map](API_AND_IDENTITY.md), [component inventory](COMPONENT_INVENTORY.md), [test assessment](TEST_INVENTORY.md) and [migration risks](RISKS_AND_MIGRATION.md) form this completed audit. The source census remains valid: the only `src`/`app` change since its checkpoint is the separately documented interview test assertion. This final completion commit changes documentation only.
|
||||
|
||||
## 1. Existing functionality to preserve
|
||||
|
||||
All **57 preservation IDs** in the full ledger remain protected, including features that already satisfy the brief. These are capability groups, not a claim of 57 individual controls or complete runtime test coverage.
|
||||
|
||||
| Ledger IDs | Complete functional coverage by area |
|
||||
|---|---|
|
||||
| E01–E09 | Public plans; local and provider authentication; registration/verification/reset/email change; 2FA/recovery/trusted devices; sessions/logout; account identity/avatar/password; sidebar/command search; theme/localisation/shared feedback; cross-account state isolation |
|
||||
| J01–J10 | NAV discovery and saved alerts; reviewed manual/URL/paste capture; extension/bookmarklet capture; table/mobile cards/filter/sort/pagination/columns/saved views; edit/bulk actions/trash/restore; Kanban; companies/contacts; offer comparison; JSON/CSV export and import/conversion |
|
||||
| W01–W12 | Full job workspace and quick overlay/deep links; progression/timeline; analysis/match/strategy; checklist; CV selection/tailoring; cover letters/review/version restore; application drafts/frozen submitted packages; attachments; interview prep/debrief; reminders/calendar/notifications; asynchronous operations |
|
||||
| C01–C12 | Canonical Career Profile and long-tail sections; overview/completeness/history; file extraction/diff/accept/discard; evidence bank; CV library/copy/publish/share; responsive editor/preview; item and section visibility/order/overrides; template-aware customization; page/document settings; undo/redo/save/history; section/document AI review; private/public PDF |
|
||||
| M01–M06 | Job correspondence records; Gmail search/import/thread linking; global inbox/filter/attachment information; provider compose/reply/drafts/review/send/delivery uncertainty; Gmail review decisions; Google/Microsoft/IMAP connections |
|
||||
| S01–S05 | Personal analytics/next actions; owner-local widget visibility/onboarding/notifications; follow-up/email/table/theme/language preferences; AI privacy/usage/Free–Pro/billing capabilities; account export/encrypted backup/lifecycle/deletion |
|
||||
| A01–A03 | Admin user/role/reset/deletion guards; audit/undo/restore; separate operational diagnostics/email settings/AI probes/CV benchmarks/maintenance |
|
||||
|
||||
The route map accounts for **31 concrete patterns: 29 page patterns and two compatibility aliases**. It also records not-found/error boundaries and non-route entry points: capture query strings, command search, notification links, workspace section/tab aliases, OAuth popups and public CV slugs. Embedded Documents, Analytics and Notifications functionality is inventoried even though these are not current standalone routes.
|
||||
|
||||
Protected workflows include discover/capture → review/create → analyse/match → select/tailor CV → prepare material → submit/freeze package → track/remind → interview/outcome; import CV → review canonical profile → create/edit/publish/export variant; inbox/import → link → draft/review/send; and account/session/admin lifecycle. Exact sub-actions and source owners are in the ledger; a redesigned page must not silently omit its nested functions.
|
||||
|
||||
## 2. Existing functionality needing UX improvement
|
||||
|
||||
These are **preserve-and-improve**, not missing features or authorization to implement them now. Assessment concerns coherence/discoverability unless a defect is explicitly identified in section 4. No new user study or visual/manual accessibility session is claimed.
|
||||
|
||||
| Existing capability / IDs | Already satisfies the brief | Remaining UX work |
|
||||
|---|---|---|
|
||||
| Navigation and command launcher, E07 | Collapsible sidebar, section support, mobile drawer, active child-route matching, Ctrl/Cmd+K job/company search | Main features currently share one broad Manage group. Improve grouping and discoverability; retain account/admin separation. Extra command capabilities are new scope below |
|
||||
| Job views and workspace, J05–J07, W01–W12 | Desktop table/mobile cards, saved views, keyboard Kanban, full job page and shared quick overlay, next actions and artifact relationships | Present table/board as views of one workspace; clarify section hierarchy and full-page/preview transitions. Preserve every alias, status and nested workflow |
|
||||
| Career and CV, C01–C12 | Canonical profile, document overrides, real template previews, page boundaries/automatic fit, drag and keyboard reorder, undo/history, desktop split/mobile edit-preview, PDF | Improve source-versus-override clarity, advanced-control discovery and preview focus. Keep advanced existing controls despite the brief's shorter suggested list |
|
||||
| Contextual AI, W03/W05/W06/W09, C03/C11 | Context selection, source material, preview/diff and accept/reject already exist | Standardize current/suggested/evidence presentation and locked/error states. Presence of review UI is not proof that every AI output is factually correct |
|
||||
| Dashboard/onboarding, S01–S02 | Metrics/trends, next actions, five owner-local visibility switches, guided checklist/dismissal | Improve hierarchy and empty/loaded states; additional widget/layout contracts are new work |
|
||||
| Mail/documents/notifications, M01–M06, W06–W08/W11–W12 | Drafts, explicit send/review, delivery states, job-linked files, inbox/review, reminders and operations | Make destinations and status distinctions easier to find. Unified navigation relocates these capabilities; it does not replace their persistence contracts |
|
||||
| Calendar and account/settings/admin, W11, E02–E06, S03–S05, A01–A03 | Follow-up/deadline Google/Outlook event creation and ICS; account controls; separate admin tools | Preserve provider availability/permission handling and clarify account versus career versus provider identity; keep operational diagnostics separate from personal data |
|
||||
| Themes/localisation/responsiveness, E08 and all flows | MUI theme, light/dark/system, EN/Bokmål and existing workflow adaptations | Standardize tokens, layout/focus/error states and test populated EN/NB/mobile surfaces. New density and brand choices are separate requirements |
|
||||
|
||||
## 3. Genuinely new requirements and conditional additions
|
||||
|
||||
These distinctions come from comparing the brief to the current handlers/models, not from treating every desired noun as a missing page. Proposed defaults are not permission to change stored data.
|
||||
|
||||
| Brief requirement | Current boundary / incremental work | Scope decision |
|
||||
|---|---|---|
|
||||
| Dashboard ordering, resizing, reset and remembered layout | [DashboardView](../../../job-tracker-ui/src/components/DashboardView.tsx) persists only `cards/activity/funnel/companies/skills` visibility flags | New layout behavior. Decide device-local versus cross-device persistence before backend/schema work; Phase 8 |
|
||||
| Expanded command palette | [QuickCommandDialog](../../../job-tracker-ui/src/components/QuickCommandDialog.tsx) searches jobs/companies and has six static commands | Add CV/document search, CV creation, Career Profile, appearance/language and contextual commands where specified; reuse existing actions |
|
||||
| Comfortable/compact application density | Theme/table display preferences exist; CV **document** density exists | New application preference affecting chosen dense surfaces; do not confuse it with document density |
|
||||
| Configurable statuses and category mapping | [JobPipeline](../../../JobTrackerApi/Services/JobPipeline.cs) preserves free-text/custom values; [pipeline.ts](../../../job-tracker-ui/src/pipeline.ts) defines fixed groups and an Other fallback | New configuration/mapping workflow. Preserve Waiting/Ghosted, raw values, analytics and rules; never replace statuses with the brief's suggested list |
|
||||
| Per-task due dates and notifications | [Checklist model](../../../JobTrackerApi/Models/ApplicationChecklistItem.cs) stores status/order/completion; follow-up and deadline dates belong to the job | New task scheduling contract if adopted. Existing job reminders do not prove per-task scheduling |
|
||||
| Optional editable weekly goals | Existing analytics and onboarding do not store a weekly target | New optional capability with supportive defaults; no universal application quota |
|
||||
| Intent-led onboarding and suggested profile preferences | [OnboardingChecklist](../../../job-tracker-ui/src/components/OnboardingChecklist.tsx) links existing account/profile/job/provider steps; it does not collect the brief's initial job-seeker intent | New intake/personalization scope if adopted. Suggested desired-role/remote/salary preferences need an explicit data contract; do not infer them from job salary/location |
|
||||
| Shared contextual-assistant entry and command actions | Domain-specific AI controls/wrappers exist; no persistent shell assistant is wired into the current route graph | New interaction surface/reuse work. A general-purpose chat platform is not required to satisfy contextual access |
|
||||
| Aggregate product/admin dashboard and candidate metrics | [AdminSystemPage](../../../job-tracker-ui/src/views/AdminSystemPage.tsx) is operational; personal dashboard analytics already exist | Cohort/adoption/conversion and CV-performance additions require defined measures, permitted aggregate data and availability. Candidate widget lists are not a promise every metric exists or must ship; correlation is not causation |
|
||||
| Manual CV preview zoom | [CvBuilderEditor](../../../job-tracker-ui/src/views/CvBuilderEditor.tsx) automatically fits page width; drag reorder, pagination and history already exist | Manual zoom is a recommended incremental control, not a reason to rebuild the preview or declare all builder capabilities missing |
|
||||
| Logo and brand treatment | Existing mark/theme remain functional | New logo is required by the brief; exact accent/name decisions are deferred. No automatic product rename |
|
||||
| Broader event/provider support, priority/favourite and optional quick-drawer form | Existing calendar actions cover follow-up/deadline; current quick overlay is functional; suggested fields/providers are not all current contracts | Conditional product additions or presentation choices. Broader calendar is explicitly future scope. Do not remove existing integration or introduce schema merely to match illustrative examples |
|
||||
|
||||
Grouped navigation, a Jobs view switch, and clearer Documents/Analytics/Notifications destinations belong to **section 2's reorganization of existing behavior**; they do not automatically require new APIs or a new resource model. New analytical measures or cross-job queries must be scoped separately from relocating existing views.
|
||||
|
||||
## 4. Known baseline defects and technical/UX debt
|
||||
|
||||
The existing validated queue owns these defects; this final audit pass records them and does not fix or re-audit them. Prior independently reproduced findings remain distinct from source-only risks and unresolved production evidence.
|
||||
|
||||
| Status / owner | Known issue relevant to redesign | Required treatment |
|
||||
|---|---|---|
|
||||
| Open, F01 / D01–D03 | CV/profile/job lost-update and atomic-history/projection contracts | Preserve existing history; a mounted editor's save queue does not solve concurrent writers. Coordinate before changing persistence/conflict UX |
|
||||
| Open, D04–D07 | Duplicate creation intent, concurrent/omitted byte accounting and export/history growth | Preserve current warnings/quotas; do not silently add idempotency, retention or storage redesign |
|
||||
| Open, F14 / A01–A03 | Previously validated shared contrast and preview keyboard-scroll defect; thin loaded-state accessibility coverage | Track structural fixes separately; no compliance claim from shell-only axe samples |
|
||||
| Open, W01/W05 | Calendar date/timezone correctness and private unsaved-draft recovery scope | Preserve current semantics while documenting defects. Do not persist private drafts across accounts or invent new storage |
|
||||
| Open, R01b/R02/R03/R05 and paid-lifecycle queue | Production DB fallback validation, consistent backup/restore/rollback proof, worker survival, billing entitlement/deletion | Separate owners and release gates. No schema work before recovery proof; billing UI cannot establish entitlement correctness |
|
||||
| Historical error fixed; R01a partly open | `interview-prep.test.tsx:120` TS2571 reproduced, then corrected with a test-only matcher during authorized release repair | Standalone tsc now passes. Explicit standalone CI gate is still missing; do not continue calling TS2571 an active failure |
|
||||
| Current CI block | Run 743 browser worker SIGSEGV, then timeout/closed-browser errors; deploy skipped | Obtain runner diagnostics before another release attempt. Do not weaken gates or label the crash an application assertion failure |
|
||||
| Source-backed maintainability/UX debt | Two router layers; large stateful forms; mixed breakpoints; hardcoded shell colors alongside theme; hardcoded English mobile accessible name | Small, tested future changes. Module size is not performance evidence; cleanup candidates are not approved deletions |
|
||||
| Unverified risk | Router identity changes with display preferences; optional labels/select patterns; loaded EN/NB/theme/mobile/manual accessibility gaps | Targeted regression/manual checks needed. Do not promote these to reproduced security defects |
|
||||
| Open, B06/F04 | Actual deployed ingress trust and bypass protection remain unproven | Separate production verification below; repository checks cannot close it |
|
||||
|
||||
See [validated ticket boundaries](../../audits/2026-09-07/validation/implementation-queue.md), [test limitations](TEST_INVENTORY.md) and [regression-risk evidence](RISKS_AND_MIGRATION.md). Identity isolation, record concurrency and creation idempotency remain three different contracts.
|
||||
|
||||
## Functionality preservation contract
|
||||
|
||||
Every ledger ID defaults to **preserve**. Before migrating a workflow, record its destination, retained actions/state transitions, exact API/data meaning, permission/session boundary and regression evidence. Relocation/merger must retain capability; deprecation requires an explicit documented decision. No deletion is approved in this audit.
|
||||
|
||||
Preserve direct links/aliases/capture/OAuth flows; owner/session/role/entitlement checks; canonical Career Profile versus document overrides; AI preview/reject/apply and factual provenance; dirty/conflict recovery and private-state disposal; server drafts/history/frozen packages and uncertain-send behavior; publication versus private downloads; B01–B05 safety contracts; typed date/JSON/CSV/PDF formats and Norwegian text. Existing backend defects are recorded for their own tickets, not redefined as acceptable intended behavior.
|
||||
|
||||
A migrated workflow needs same-account success and cross-account denial, populated/empty/error/locked states, EN/NB, light/dark, desktop/mobile, keyboard/focus and artifact checks appropriate to its behavior. A screenshot, module presence or aggregate test count alone is insufficient preservation evidence.
|
||||
|
||||
## Frontend architecture and stack assessment
|
||||
|
||||
**Retain the current stack.** React 19/strict TypeScript is a client-rendered SPA packaged by Next 16 static export, with React Router 7 owning application routes and nginx handling static/deep-link/API routing. MUI 7, MUI X, Emotion and MUI icons already provide the foundation; styling combines central theme/global CSS and substantial local `sx`. TanStack Table is used, not TanStack Query. No Redux/Zustand/cache-platform replacement is warranted.
|
||||
|
||||
Local React state and focused contexts own forms/auth/theme/language/plan/feedback; shared Axios owns cookies/XSRF/B02 request provenance/cancellation and blob downloads. The API census contains **266 call expressions**; ownership is server-enforced. Improve shared component contracts and existing tokens before page migration. No demonstrated limitation justifies replacing the component library, routing or backend.
|
||||
|
||||
## Test and CI baseline
|
||||
|
||||
| Evidence | Result / limit |
|
||||
|---|---|
|
||||
| Latest completed CI, run 743 at main `27657f3` | **916/916 backend**, zero skips; **67 suites / 298 frontend tests**; lint, dependency audits, Compose/safety/parser and production builds passed |
|
||||
| Same CI browser gate | **6 passed / 12 failed**; initial worker SIGSEGV, retry timeout and closed-browser errors. Deploy skipped; not a green release |
|
||||
| Local release checks at `a861ca4`, unchanged source since | **18/18 browser**, standalone tsc, lint and static build passed; installed npm audit zero vulnerabilities. Local success does not replace CI |
|
||||
| Prior B06 focused tests | **166 backend, 3 Compose, 6 native Production ingress, 9 native B04 HTTP** checks passed; no actual Traefik/nginx topology |
|
||||
| Accessibility/test census | 76 frontend test files, 106 backend test-source files; general axe samples eight routes in English, not all loaded interactions/manual accessibility |
|
||||
| Final audit-only validation | **621 local links valid, 57 unique ledger IDs, 31 route patterns, 266 API rows, all 120 production source files match the census**; only intervening `src` change is the documented test assertion. Secret scan and diff checks pass; application suites were not rerun for prose edits |
|
||||
|
||||
Exact commands, historical failures and classification are preserved in [TEST_INVENTORY](TEST_INVENTORY.md) and the [release report](../../audits/2026-09-07/validation/b06-completion.md#final-release-retry--2026-09-10). Previous CI/build changes were separate authorized release/baseline repairs: Compose availability, Linux fixture/runtime/TLS-test portability, blocking dependency advisories and the test-only TS2571 correction. They are not redesign implementation, and this final pass adds none.
|
||||
|
||||
## Unresolved production validation
|
||||
|
||||
**B06 implementation complete, but B06 runtime/production closure and F04 remain open.** Intended path: Traefik → host 3000 → nginx → internal API 8080. Docker Desktop's inference-socket failure prevented the real local container chain. No verified production SSH alias was available; no credentials or CI secrets were obtained.
|
||||
|
||||
Actual nginx startup/fail-closed configuration/restart, Traefik socket peer and trusted CIDR, backend client IP/scheme, real-chain B02/B04 behavior, host/container listeners, firewall/NAT/IPv6 and independent direct-port denial still need evidence. Public DNS/valid HTTPS were observed, but intended-address inventory and the HTTP redirect to unreachable HTTPS port 444 remain unresolved. Live `/health` last observed **2026-09-10 05:07 UTC**, version **299**, ready; this is not deployment of the pushed audit repairs. Exact safe operator commands and expected evidence remain in the [B06 report](../../audits/2026-09-07/validation/b06-completion.md).
|
||||
|
||||
## Recommended Phase 2 starting point
|
||||
|
||||
Start only when separately authorized: **a bounded functional-baseline session, with no layout or route changes**. Add the explicit standalone type-check script/CI step while coordinating with runner repair; map the 57 preservation IDs to existing assertions and add the highest-risk missing tests for route/overlay/capture parity, populated CV/profile overrides and AI accept/reject. Preserve real API/B02 identity tests and B01/B03/B04/B05 suites.
|
||||
|
||||
Exit requires a documented pass/fail/unavailable baseline, meaningful loaded-workflow proof and a preservation checklist tied to tests. Existing defects stay separately ticketed; failed tests are not made green by weakening expectations. Record-integrity and recovery prerequisites govern later persistence/schema work. Phase 3 navigation proposals and Phase 4 visuals follow their own checkpoints; no stable/live tag or F04 closure is warranted while their evidence is missing. Branding, product additions and cross-device layout decisions do not block this audit's completion.
|
||||
@@ -0,0 +1,99 @@
|
||||
# Phase 1 — Frontend audit
|
||||
|
||||
Successor checkpoint: [Phase 2 preservation matrix, regression results and traceability](../phase-2/README.md), based on the completed Phase 1 commit `2d0f131`. The historical audit below is retained; no visual redesign has started.
|
||||
|
||||
Date: 2026-09-09. Source checkpoint: `97fce89c002fe86e737ca7387170321d464b80d7` (application changes through `0954876`; subsequent commits contain the brief and CI prerequisite corrections). Audit branch: `codex/ui-redesign-audit`. Test-only packaging correction `23cc250` leaves the frontend census unchanged; its fresh 22 PDF and 915 broader backend passes are recorded in the test assessment.
|
||||
|
||||
**Phase 1 source audit complete; redesign implementation has not started.** The repository already implements much of the proposed workflow. Retain its MUI/React foundation and domain contracts, improve shared semantics, then migrate representative screens in small steps. A replacement frontend framework or component library is not justified by the observed problems.
|
||||
|
||||
Finalized 2026-09-10: [Phase 1 completion summary](PHASE1_SUMMARY.md) explicitly separates existing functionality to preserve, existing functionality needing UX improvement, new/conditional requirements and baseline defects/debt. It includes the preservation contract, validation limits and bounded Phase 2 starting point. All 57 ledger IDs remain protected; this is a separate documentation checkpoint, not a stable/live release tag.
|
||||
|
||||
This is a source and regression-evidence audit, not a new visual usability study, production certification, or execution of security ticket B07. The existing security queue still governs unresolved integrity/recovery work. F04 remains open. A pushed revision is not proof of a successful deployment; see [release evidence](../../audits/2026-09-07/validation/b06-completion.md#authorized-release-attempt-and-ci-prerequisite-correction).
|
||||
|
||||
Release status, 2026-09-10: main `27657f3` is pushed. Run 743 passed 916 backend and 298 frontend tests, lint, audits and production builds, but the browser gate failed with a worker SIGSEGV and subsequent browser-closed errors; deployment was skipped. Further release attempts await runner diagnostics. The live endpoint still reports version 299. This does not block delivering the Phase 1 findings, but it does block claiming a verified live checkpoint for subsequent work.
|
||||
|
||||
## Deliverables and method
|
||||
|
||||
| Handoff requirement | Evidence |
|
||||
|---|---|
|
||||
| Final summary and four-way scope classification | [Phase 1 completion summary](PHASE1_SUMMARY.md) |
|
||||
| Architecture, styling, state | This document |
|
||||
| Every declared route/page | [Route map](ROUTE_MAP.md): 31 concrete route patterns, including two redirect aliases; 29 page patterns |
|
||||
| Existing user functionality | [Preservation ledger](FUNCTIONALITY_INVENTORY.md), including nested screens and non-navigation entry points |
|
||||
| Components | [All production TSX modules](COMPONENT_INVENTORY.md), shared responsibility map below |
|
||||
| Tests and gaps | [Test assessment](TEST_INVENTORY.md), [test-file census](TEST_FILES.md) |
|
||||
| API integration | [Contract map](API_AND_IDENTITY.md), [266 call expressions](API_CALL_SITES.md) |
|
||||
| Auth, authorization, storage | [Identity and storage boundaries](API_AND_IDENTITY.md) |
|
||||
| Localisation, mobile, accessibility | Assessment below and [regression risks](RISKS_AND_MIGRATION.md) |
|
||||
| Migration and decisions | [Proposed sequence and gates](RISKS_AND_MIGRATION.md) |
|
||||
|
||||
Read the ten documents required by [ASTRA_HANDOFF](../ASTRA_HANDOFF.md), then traced route elements, their local import graph, event handlers, API wrappers, server controllers/services, security middleware, test configuration and existing validation evidence. The [machine-readable census](source-inventory.json) records 120 production TS/TSX files under `src`/`app`, 76 frontend test files, 266 direct shared-client call expressions, and 34 route declarations. Declarations include a duplicated root and two wildcard routing boundaries; they are not 34 pages. File and expression counts are inventory aids, not coverage percentages.
|
||||
|
||||
Regenerate source tables from the repository root after installing the locked frontend dependencies:
|
||||
|
||||
```sh
|
||||
node docs/redesign/audit/generate-inventory.cjs
|
||||
```
|
||||
|
||||
The generator reads tracked source through the installed TypeScript AST. It does not execute application code. API paths supplied through variables are retained as expressions; resolve them with the contract map. CSS, static assets, OAuth SDK traffic and browser-extension entry points require the accompanying manual inspection. Runtime behavior is marked as prior evidence or unverified where this pass did not execute it.
|
||||
|
||||
## Architecture and technology
|
||||
|
||||
The application is a client-rendered React SPA packaged by Next, not a server-rendered Next route application. [Next configuration](../../../job-tracker-ui/next.config.js) exports static production assets and rewrites development deep links to the root. `app/page.tsx` and `app/[[...slug]]/ClientShell.tsx` load `ClientApp` with SSR disabled; `App.tsx` owns React Router. Nginx supplies the production SPA fallback and `/api` proxy. Preserve direct URL entry, refresh, history navigation and static deployment together.
|
||||
|
||||
| Concern | Current implementation | Redesign implication |
|
||||
|---|---|---|
|
||||
| Framework | Next 16, React 19, TypeScript strict; exact installed versions in lockfile | Keep build/output model unless a measured requirement needs a change |
|
||||
| Navigation | React Router 7, outer public routes plus authenticated shell's inner `Routes`, lazy feature imports | Consolidate navigation metadata deliberately; do not accidentally introduce a second active route authority |
|
||||
| UI | MUI 7, MUI X Data Grid/Date Pickers, Emotion, MUI icons; TanStack **Table**, not Query | Shared tokens and component composition can implement the brief without a library migration |
|
||||
| Styling | [theme.ts](../../../job-tracker-ui/src/theme.ts), global CSS, MUI overrides, extensive inline `sx`, page-local styling | Theme exists, but duplicated shell/page color and surface choices weaken consistency |
|
||||
| Typography | Archivo, loaded by app layout; theme typography variants | Preserve fallbacks and loading behavior; font self-hosting is a separate deployment/privacy choice |
|
||||
| Data transport | One Axios instance in [api.ts](../../../job-tracker-ui/src/api.ts), thin typed domain wrappers and direct page calls | Keep the B02 interceptors and blob download behavior when extracting components |
|
||||
| State | Local React state/effects; auth, account plan, theme, i18n, toast, prompt and confirm contexts | No Redux, Zustand or React Query store to replace; introduce a cache only for a demonstrated need |
|
||||
| Async UI | `useViewResource`, resource-specific effects, operation polling, custom refresh events, component-local workspace tab cache | Preserve request sequencing, loading/error/refresh distinction and identity invalidation |
|
||||
| Forms | Controlled MUI fields; many page-local draft states | Retain dirty guards and validation while extracting forms; do not add persistent private drafts implicitly |
|
||||
| Documents | Canonical Career Profile, persisted CV settings/overrides, server HTML/PDF renderer, iframe preview | The editor is a domain application, not merely a form to restyle |
|
||||
| Charts | Dashboard custom SVG/chart presentation | Preserve data meaning and add accessible alternatives where needed; no automatic chart-library replacement |
|
||||
|
||||
Dependencies and scripts: [package.json](../../../job-tracker-ui/package.json). Rendering entry and route wiring: [App.tsx](../../../job-tracker-ui/src/App.tsx). No frontend Next API route/server-action backend was found in the audited entry graph; application APIs live in .NET.
|
||||
|
||||
## Shared component responsibilities
|
||||
|
||||
| Existing foundation | Responsibility to retain / improve |
|
||||
|---|---|
|
||||
| `layout/AppShell` navigation model, `QuickCommandDialog` | Collapse/drawer navigation, active-route matching, capture actions, notifications, command search, logout and identity display |
|
||||
| `theme`, `themePrefs`, `themeBootstrap`, global CSS | Light/dark/system preference, initial paint, semantic colors, spacing and responsive surfaces |
|
||||
| `ViewStateNotice`, `useViewResource`, toast/confirm/prompt | Loaded, empty, failure, retry and confirmation behavior; give them consistent focus and announcement contracts |
|
||||
| `RichTextField`, `TagsInput`, date controls | Plain text/markdown storage and form naming; avoid introducing HTML editing semantics during visual work |
|
||||
| `ApplicationWorkspace`, `ApplicationAssets`, `jobWorkspaceRoute` | Full-page and overlay entry, section/deep-link aliases, derived artifacts and unsaved navigation |
|
||||
| `components/cv/*`, `CvTemplateGallery`, `CvTemplateThumbnail`, `cvBuilder` | Capability-aware design settings, content editing, sample rendering, ordering and public/export contracts |
|
||||
| `AiSectionAssistant`, cover-letter AI, interview AI, `aiWorkspace` | Context selection, entitlement states, current/suggested review, explicit acceptance and provenance |
|
||||
| Account security/settings cards | OAuth links, mail connections, 2FA, sessions, exports, deletion and billing capabilities |
|
||||
|
||||
The complete component table includes four production modules outside the current ClientApp import graph: `Attachment`, `JobFlowBar`, `Markdown`, `UserManagementCard`. These are cleanup candidates only after checking non-app consumers and preservation coverage. App entry modules are naturally outside that graph. **Nothing is approved for deletion in Phase 1.**
|
||||
|
||||
## Localisation
|
||||
|
||||
[I18nProvider](../../../job-tracker-ui/src/i18n/I18nProvider.tsx) uses a typed EN/Bokmål dictionary, string interpolation, English fallback and `uiLanguage` local preference. Default is English unless a stored Norwegian code is present; authenticated `/auth/me` hydration restores the server preference. Changes PUT `/auth/preferences/language`; the document language becomes `en` or `nb-NO`. UI language and CV/AI document language are distinct controls and must remain so.
|
||||
|
||||
Most screen text is translated. There are real consistency gaps, for example the mobile job-card accessible name is hardcoded English at [JobTable:661](../../../job-tracker-ui/src/components/JobTable.tsx#L661). API errors and user-generated content are not automatically translated. Reusing translated labels, date/status formatters and long-string layouts should precede per-page copy changes. Browser accessibility tests currently force English; they do not prove Bokmål fit, pronunciation, errors or document formatting. Existing data and language preferences do not require Connor to decide a new default during this audit.
|
||||
|
||||
## Responsive behavior and accessibility
|
||||
|
||||
The code already adapts several workflows. `AppShell` changes to a drawer below 768 px; desktop sidebar has expanded/collapsed states. `JobTable` uses mobile cards and a separate filter arrangement. Kanban permits narrow-screen horizontal scrolling, keyboard pick-up/drop and a live region. Settings switch from tabs to a labelled select on small screens. The CV editor uses edit/preview panes below 900 px rather than shrinking the desktop split. These are preservation requirements, not missing features.
|
||||
|
||||
Responsiveness remains locally defined through mixed media queries and MUI breakpoints. Drawer/dialog nesting, long job titles, tables, email threads and document preview require a common layout contract plus real mobile testing. No new screenshot study or screen-reader session was performed in this Phase 1 pass.
|
||||
|
||||
The prior [validated accessibility evidence](../../audits/2026-09-07/validation/README.md#accessibility-conclusions) is **F14**, not draft-recovery F09. It measured shared contrast failures and a preview scroll-region focus failure after a viewport transition. Those are not evidence that every custom control is inaccessible. Current builder rich-text call sites supply labels and Kanban has keyboard support. `RichTextField` still permits unnamed usages in its optional props, and some Customize select patterns need an explicit accessible-name check. These are targeted verification points, not newly reproduced violations.
|
||||
|
||||
The automated axe suite samples eight routes and largely waits for `body`/`main`. It can miss loaded content, expanded editors, dialogs and errors. Before migration approval, use populated states, EN/NB, both themes, desktop/mobile; manually test keyboard and NVDA/Narrator, focus restoration, reauthentication, 200%/400% zoom, 320 CSS-pixel reflow, text spacing, forced colors, reduced motion and touch alternatives. Fix shared contrast semantics and the actual preview focus boundary before duplicating new page layouts.
|
||||
|
||||
## Principal conclusions
|
||||
|
||||
1. Preserve the working domain model and security boundaries; new page organization must not redefine ownership, CV persistence, AI acceptance or export formats.
|
||||
2. The full job workspace, canonical Career Profile, contextual AI review, CV history and adaptive mobile surfaces already exist. The redesign should improve coherence and discoverability.
|
||||
3. Shared visual semantics and loaded-state accessibility coverage are weaker than the broad test count suggests. Fix foundations before whole-page migration.
|
||||
4. Five local dashboard visibility switches are not the proposed configurable dashboard. Ordering, resizing and cross-device persistence are future scope requiring a bounded contract.
|
||||
5. The known standalone TypeScript failure reproduced during the audit; subsequent authorized release repair `a861ca4` corrected its test-only assertion after patched Next made it build-blocking. Standalone tsc now passes; an explicit standalone CI gate remains outstanding. Release/production and unresolved integrity gates must not be called green by implication.
|
||||
|
||||
Next work is **Phase 2 — Functional baseline**, subject to the gates in [RISKS_AND_MIGRATION](RISKS_AND_MIGRATION.md). No Phase 2 code, B07, or redesign component changes were made here.
|
||||
@@ -0,0 +1,73 @@
|
||||
# Risks and proposed migration approach
|
||||
|
||||
This report proposes the next steps; it does not authorize or implement Phase 2, B07 or a backend overhaul. Original security findings keep their IDs and queue ownership. [Validated implementation queue](../../audits/2026-09-07/validation/implementation-queue.md) remains the source for those fixes.
|
||||
|
||||
## Prioritized regression risks
|
||||
|
||||
| Risk | Source evidence | Smallest appropriate response / gate |
|
||||
|---|---|---|
|
||||
| Lost identity boundary during a new shell/cache/client | `App`, `api`, `sessionBoundary`; B02 server session preconditions | Keep private provider unmount, immutable provenance, abort/late-response checks and real A→B tests. Session headers never become rate-limit owner identity |
|
||||
| Editor redesign hides existing lost-update behavior | CV save payload lacks expected revision; page-local save queue is not cross-editor concurrency | Coordinate existing D01/D02 integrity tickets before changing save/conflict UX. Do not call history/undo optimistic concurrency or add a generic concurrency framework |
|
||||
| Creation replay becomes easier through capture/dialog rerenders | Preview/duplicate warning and subsequent create are separate requests | Preserve current review flow; existing D04 creation-intent ticket is distinct from identity and record revision. No idempotency work in this audit |
|
||||
| Route consolidation loses entry contracts | Two router layers, legacy `/applications/:id`, old section mappings, `?add`, notification/company deep links | Route manifest + redirect tests before moving navigation; keep capture and OAuth callbacks functional |
|
||||
| New styling repeats existing contrast/focus defects | Validated F14 theme pair contrast; builder preview scroll-region transition | A01 semantic foreground/background/state colors, A02 preview structure, A03 loaded/manual verification before broad page work |
|
||||
| Thin initial-state tests create false confidence | Eight-route axe sample; component API mocks; historical TS2571 now corrected during release repair | Phase 2 loaded-interaction and explicit standalone type gate; require feature-ledger evidence per migrated workflow |
|
||||
| CV/profile semantics blur in a unified editor | Canonical profile save versus CV override/settings and published slug | Clearly display source versus document override; test master unchanged, source selection, history, publish/unpublish and long PDF |
|
||||
| AI presentation implies facts or silently applies changes | Contextual suggestions and extraction accept/discard already exist | Preserve preview/reject/apply and source/context metadata; prompt instructions alone do not prove factual accuracy |
|
||||
| Draft/history/mail/package behavior lost in visual simplification | `ApplicationAssets` 769 lines, inbox 750, correspondence 747; separate persistence/attempt APIs | Extract by cohesive responsibility, not one generic form; keep conflict/send uncertainty, frozen package and unsaved guards |
|
||||
| Broad navigation has competing labels/destinations | Eleven main nav entries plus account/admin; profile vs career; table/board separate; embedded operations/review | Design grouped IA with an explicit preservation/redirect map; improve discoverability without deleting capabilities |
|
||||
| Styling consistency remains expensive | Central theme plus hardcoded shell navy/indigo, page-local shadows and large `sx` compositions | Retain MUI; establish tokens and a few layout/field/state primitives, then validate representative surfaces |
|
||||
| Large editor/page modules couple visual and persistence work | Editor 1,131 lines; career 879; job table 866 | One cohesive extraction at a time, bracketed by real workflow tests; file length alone is not a rewrite justification |
|
||||
| Dashboard customization mistaken for an existing server contract | Only five visibility booleans in owner-local storage | Keep current prefs until Phase 8; separately decide device-only vs cross-device contract before persistence/schema work |
|
||||
| International/mobile regressions | Mixed 768/900/MUI breakpoints; hardcoded English mobile accessible name; EN-only axe | Preserve current mobile adaptations; long Bokmål labels and locale/date semantics in the representative matrix |
|
||||
| App route identity churn | Router memo depends on job display preferences in `App` | Potential state-reset risk, not reproduced here; test changing preferences during meaningful workflow before refactoring router ownership |
|
||||
| Unstable release proof or assumed production ingress | New CI prerequisite/build failures; Docker unavailable; F04 open; HTTP redirect observed to unreachable port 444 | No stable/live claim from push alone. Keep required gates and complete operator evidence; no trust-all fallback or invented proxy CIDR |
|
||||
|
||||
Line counts come from the [source census](source-inventory.json); they are prioritization context, not measured performance. No new authorization bypass is alleged by this redesign audit.
|
||||
|
||||
## Recommended order
|
||||
|
||||
1. **Finish the authorized release attempt and retain the checkpoint.** Required CI and deploy preflight must pass before calling the changes live. Production F04 runtime/firewall/DNS/peer checks remain a separate closure gate. Audit/planning can continue without claiming Phase 0 stability.
|
||||
2. **Phase 2 — Functional baseline.** Add explicit standalone CI type checking (the TS2571 assertion was corrected separately in release repair `a861ca4`); trace preservation IDs to existing tests; add missing loaded workflow/route/AI-review tests. Preserve B01–B06 regression suites. Coordinate outstanding record-integrity and recovery tickets through their existing queue, without silently starting them here.
|
||||
3. **Phase 3 — UX architecture.** Define grouped navigation, Jobs view switch, destination for embedded Documents/Analytics/Notifications, full-page/preview contract, contextual AI pattern and mobile workflow rules. Map every current route/feature to its destination and backward-compatible entry. Keep API contracts.
|
||||
4. **Phase 4 — Shared foundations.** Use existing MUI/Emotion/theme system. Resolve proven semantic contrast and focus defects, then unify tokens, page/layout/state primitives, labelled fields, buttons, dialogs/drawers and tables. Avoid a speculative new design-system framework.
|
||||
5. **Phase 5 — Representative migration.** Shell → Jobs table/board entry → full job workspace → CV editor; dashboard presentation can join after shell tokens stabilize. Preserve a data-rich populated example, empty/error/locked case, mobile and keyboard path for each. Do not modify editor save semantics concurrently with integrity work.
|
||||
6. **Phase 6 — Review checkpoint.** Inspect EN/NB × light/dark × desktop/mobile, loaded accessibility, long data, CSV/PDF downloads, identity switching and history. Correct shared defects before repeating the design.
|
||||
7. **Phase 7 — Remaining routes and nested functions.** Auth/account, career/import/evidence, CV library/public CV, correspondence, reminders/operations, settings and admin; update every preservation row with evidence.
|
||||
8. **Phase 8 — Dashboard customization.** Add ordering/reset/resizing only with defined layout and persistence semantics. Coordinate any needed migration with restore/rollback readiness. Keep aggregate admin reporting separate.
|
||||
9. **Phase 9 — Cross-product QA and release evidence.** Full regression, manual access/mobile, document pagination and provider/production checks. Feature parity, not only appearance, determines readiness.
|
||||
10. **Phase 10 — Cleanup.** Delete legacy modules only after route/import checks and preservation-ledger completion. Do not use static unreachability alone as approval.
|
||||
|
||||
No router/state/styling dependency replacement is currently recommended. If a later prototype demonstrates a concrete limitation, compare the smallest alternative against MUI reuse and measure migration/keyboard/theme cost before choosing it.
|
||||
|
||||
## Session-sized next scopes and coordination
|
||||
|
||||
| Suggested future scope | Boundaries / expected proof | Parallel constraints |
|
||||
|---|---|---|
|
||||
| Type and baseline bookkeeping | Preserve the corrected test assertion; explicit type script/CI gate, known-failure classification | Independent of visual work; coordinate CI edits with deployment repair |
|
||||
| Route/preservation regression | Existing route helpers, full/overlay/capture navigation tests | Do not change router structure at the same time |
|
||||
| Theme semantic contract | Central theme/MUI states + representative loaded contrast fixtures | Separate from backend; integrate before page styling |
|
||||
| Builder focus contract | Actual preview container, viewport transition and keyboard scroll tests | Must not run concurrently with D01/editor save refactor |
|
||||
| Shell/navigation proposal | Documentation/interactive prototype and route map only until Phase 3 approved | Can be prepared while tests run; no new production routing yet |
|
||||
| First screen migration | One agreed shell or Jobs surface with existing handlers/contracts retained | Use settled tokens; no simultaneous edits of its route/state owner |
|
||||
|
||||
These are future implementation boundaries, not work started by Phase 1. Mail delivery, billing, schema, restore and infrastructure changes remain outside redesign sessions unless explicitly authorized and independently gated.
|
||||
|
||||
## Decisions required from Connor
|
||||
|
||||
No answer is required to complete this audit. Ask only when the dependent phase starts:
|
||||
|
||||
- **Brand/name/accent:** the brief intentionally leaves them unsettled. Keep current naming and theme until Phase 3/4 chooses them; do not block baseline tests on branding.
|
||||
- **Dashboard persistence:** whether layout should follow the account across devices materially changes Phase 8's persistence scope. Existing implementation is device-local and owner-keyed.
|
||||
- **Private local draft recovery:** preserve in-memory drafts and existing server drafts now. New persistent device-local career/CV content needs the privacy/retention choice already recorded under W05.
|
||||
- **Product additions/deprecations:** new goals/general assistant/providers or removing an existing preservation row need explicit product scope. Navigation relocation alone need not become a series of permission questions.
|
||||
|
||||
The repository already answers the current framework, default language, existing calendar support, account/profile distinction and owned-resource semantics. Do not ask Connor to supply those facts.
|
||||
|
||||
## Stop conditions
|
||||
|
||||
- No large visual migration until loaded regression baseline and shared UI contracts are defined. No stable release label while required CI is red or deployment state unknown.
|
||||
- No editor persistence redesign before resolving its record conflict contract; B02 must remain green throughout. No schema change before recovery/rollback prerequisites.
|
||||
- No implicit feature deletion, master-profile mutation from CV overrides, silent AI application, unknown email resend, or private cache reuse across identities.
|
||||
- No public accessibility claim without manual primary-flow evidence. No F04 closure without actual deployed/runtime evidence; direct-port failure from one workstation is insufficient.
|
||||
- Stop at Phase 1's audit deliverables in this session. Phase 2 and B07 have not begun.
|
||||
@@ -0,0 +1,63 @@
|
||||
# Current route and page map
|
||||
|
||||
Source: [App.tsx shell routes](../../../job-tracker-ui/src/App.tsx#L379) and [outer router](../../../job-tracker-ui/src/App.tsx#L472). All paths below are current behavior, not target architecture. Preserve or redirect every path intentionally. `:id` and `:slug` represent route patterns, not single records.
|
||||
|
||||
## Public entry routes
|
||||
|
||||
| Route | Component | Contract |
|
||||
|---|---|---|
|
||||
| `/` | LandingPage | Public product/plans and sign-in entry; authenticated redirection behavior has its own test |
|
||||
| `/login` | LoginPage | Local/Google/Microsoft sign-in, verification/2FA branches |
|
||||
| `/register` | LoginPage (`initialMode=register`) | Configuration-gated account creation |
|
||||
| `/forgot-password` | ForgotPasswordPage | Request reset without disclosing account existence |
|
||||
| `/reset-password` | ResetPasswordPage | Token-bearing reset flow |
|
||||
| `/verify-email` | VerifyEmailPage | Verification-token flow |
|
||||
| `/confirm-email-change` | VerifyEmailPage (`emailChange`) | Pending email-change confirmation |
|
||||
| `/microsoft-legacy-relink` | MicrosoftLegacyRelinkPage | Existing-account recovery/relink flow |
|
||||
| `/cv/:slug` | PublicCvPage | Explicitly published CV preview/PDF; no general private CV access |
|
||||
|
||||
## Application routes
|
||||
|
||||
The shell resolves auth/config and `/auth/me`; deployed server authentication remains authoritative. Admin navigation is hidden for ordinary accounts, while API role checks enforce access even for crafted deep links. A routed component is not itself an authorization boundary.
|
||||
|
||||
| Route | Component | Entry / preservation requirement |
|
||||
|---|---|---|
|
||||
| `/dashboard` | DashboardView | Personal metrics, next actions, onboarding and local widget visibility |
|
||||
| `/discover` | JobDiscoveryPage | NAV search, saved searches/alerts, review before tracker import |
|
||||
| `/jobs` | JobTable (`jobs`) | Filters, search, sort, pagination, saved views, columns, selection/bulk actions, full/preview workspace |
|
||||
| `/jobs/:id` | ApplicationWorkspacePage | Full job workspace; section/follow-mode query semantics |
|
||||
| `/reminders` | RemindersView | Follow-ups, deadlines and progression actions |
|
||||
| `/operations` | OperationsPage | Background progress/retry/cancel and notifications; reachable outside main nav |
|
||||
| `/kanban` | KanbanBoard | Alternate view of the same jobs; grouped statuses, keyboard and pointer moves |
|
||||
| `/companies` | CompaniesTable | Company/contact editing and relationship detail; `?edit=id` command entry |
|
||||
| `/offers` | OfferComparisonPage | Comparison of recorded offers and links to source workspaces |
|
||||
| `/correspondence` | CorrespondenceInboxPage | Inbox, provider messages, drafts, reply/send and review mode |
|
||||
| `/correspondence/review` | Redirect | Existing alias to `/correspondence?view=review` |
|
||||
| `/profile` | ProfilePage | Account identity/avatar/password/email; security cards and account links |
|
||||
| `/applications/:id` | LegacyApplicationRedirect | Existing alias to `/jobs/:id`; preserves search and router state |
|
||||
| `/career` | CareerWorkspacePage → CareerProfilePage | Canonical profile, import/review, history and evidence bank |
|
||||
| `/career/builder` | CvBuilderPage | CV library, templates, creation/copy/manage/share/export |
|
||||
| `/career/builder/:id` | CvBuilderEditor | Persisted CV variant studio, preview, history and AI review |
|
||||
| `/admin/audit` | AdminAuditPage | Admin event history and permitted undo/restore |
|
||||
| `/admin/users` | AdminUsersPage | Admin account/role/reset/deletion controls |
|
||||
| `/admin/system` | AdminSystemPage | Admin service/configuration/maintenance/benchmark diagnostics |
|
||||
| `/trash` | JobTable (`trash`) | Deleted-job listing and restoration; retain distinct mode semantics |
|
||||
| `/settings` | SettingsView | General, follow-up rules, notifications and backup/account lifecycle tabs |
|
||||
| `/settings/connected-accounts` | ConnectedAccountsPage | Gmail, Microsoft Graph and IMAP provider connections |
|
||||
|
||||
There are **31 concrete patterns: 29 page patterns and two redirect aliases**. The remaining three declarations in the AST census are the outer `/*` shell, inner `*` NotFoundPage and inner root redirect. The explicit outer `/` landing route takes precedence over the shell's root redirect. RouteErrorPage is an error boundary, not a separate URL. Do not manufacture an `/analytics`, `/documents` or `/notifications` page from the target brief: their current functions are embedded in dashboard/job/operations/settings surfaces.
|
||||
|
||||
## Non-route entries and state contracts
|
||||
|
||||
| Entry | Current effect |
|
||||
|---|---|
|
||||
| Shell add button; command action; capture URL `?add=` / `?addtext=` | Opens AddJobModal and consumes the query. Browser extension/bookmarklet/share entry depends on this |
|
||||
| Job row/card; preview action | Full job page or reused ApplicationWorkspace overlay; preserve close/focus, dirty protection and full-page handoff |
|
||||
| `/jobs/:id?section=...` | Overview, analysis, CV, cover-letter or interview; older `match`, job-details/checklist/documents/timeline/communication values map through existing route helpers |
|
||||
| `/applications/:id` and older tab contracts | Legacy redirects and numeric tab-to-section mapping; covered by route-contract tests |
|
||||
| Command search `Ctrl/Cmd+K` | Jobs and companies plus six static commands; company result uses `/companies?edit=id` |
|
||||
| Notifications/reminders/operations | Deep links can enter a job subsection or processing result without first visiting Jobs |
|
||||
| Builder public/share/PDF | Public slug lifecycle is separate from authenticated numeric CV identity |
|
||||
| OAuth popups/callbacks | Provider/backend callback and postMessage flow; not additional React pages. Preserve exact origin/state contracts |
|
||||
|
||||
Sources: [jobWorkspaceRoute](../../../job-tracker-ui/src/jobWorkspaceRoute.ts), [AppShell](../../../job-tracker-ui/src/layout/AppShell.tsx), [QuickCommandDialog](../../../job-tracker-ui/src/components/QuickCommandDialog.tsx), [workspace component](../../../job-tracker-ui/src/views/ApplicationWorkspacePage.tsx). Phase 3 may propose navigation merges; this inventory does not authorize deleting aliases or current functionality.
|
||||
@@ -0,0 +1,194 @@
|
||||
# Test file inventory
|
||||
|
||||
Tracked test files at the audit baseline. Presence is not proof that a feature or every interaction is covered. See TEST_INVENTORY.md for scope and execution evidence.
|
||||
|
||||
| Layer | File |
|
||||
|---|---|
|
||||
| Backend | [JobTrackerApi.Tests/AccountDataExportTests.cs:1](../../../JobTrackerApi.Tests/AccountDataExportTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/AccountDeletionTests.cs:1](../../../JobTrackerApi.Tests/AccountDeletionTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/AccountPlansTests.cs:1](../../../JobTrackerApi.Tests/AccountPlansTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/AiEvaluationFixtureTests.cs:1](../../../JobTrackerApi.Tests/AiEvaluationFixtureTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/AiOperationQueueTests.cs:1](../../../JobTrackerApi.Tests/AiOperationQueueTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/AiPrivacyPolicyTests.cs:1](../../../JobTrackerApi.Tests/AiPrivacyPolicyTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/AiUsageMeterTests.cs:1](../../../JobTrackerApi.Tests/AiUsageMeterTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/AiWorkspaceNotePersistenceTests.cs:1](../../../JobTrackerApi.Tests/AiWorkspaceNotePersistenceTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/AiWorkspaceTests.cs:1](../../../JobTrackerApi.Tests/AiWorkspaceTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/AnalyticsSalaryTests.cs:1](../../../JobTrackerApi.Tests/AnalyticsSalaryTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/ApiAuthorizationPostureTests.cs:1](../../../JobTrackerApi.Tests/ApiAuthorizationPostureTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/ApplicationAssetsTests.cs:1](../../../JobTrackerApi.Tests/ApplicationAssetsTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/ApplicationCalendarControllerTests.cs:1](../../../JobTrackerApi.Tests/ApplicationCalendarControllerTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/ApplicationChecklistTests.cs:1](../../../JobTrackerApi.Tests/ApplicationChecklistTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/ApplicationIntelligenceTests.cs:1](../../../JobTrackerApi.Tests/ApplicationIntelligenceTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/ApplicationWorkspaceTests.cs:1](../../../JobTrackerApi.Tests/ApplicationWorkspaceTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/AttachmentConsistencyTests.cs:1](../../../JobTrackerApi.Tests/AttachmentConsistencyTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/AttachmentFlagsRecomputeTests.cs:1](../../../JobTrackerApi.Tests/AttachmentFlagsRecomputeTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/AttachmentsControllerTests.cs:1](../../../JobTrackerApi.Tests/AttachmentsControllerTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/AuthAndSystemControllerTests.cs:1](../../../JobTrackerApi.Tests/AuthAndSystemControllerTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/AuthSessionRevocationTests.cs:1](../../../JobTrackerApi.Tests/AuthSessionRevocationTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/AvatarStorageTests.cs:1](../../../JobTrackerApi.Tests/AvatarStorageTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/BackgroundWorkerTenantTests.cs:1](../../../JobTrackerApi.Tests/BackgroundWorkerTenantTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/BackupControllerTests.cs:1](../../../JobTrackerApi.Tests/BackupControllerTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/BillingControllerTests.cs:1](../../../JobTrackerApi.Tests/BillingControllerTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/BuildMetadataTests.cs:1](../../../JobTrackerApi.Tests/BuildMetadataTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/CareerEvidenceControllerTests.cs:1](../../../JobTrackerApi.Tests/CareerEvidenceControllerTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/CareerProfileControllerTests.cs:1](../../../JobTrackerApi.Tests/CareerProfileControllerTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/CareerProfileServiceTests.cs:1](../../../JobTrackerApi.Tests/CareerProfileServiceTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/ClientErrorsControllerTests.cs:1](../../../JobTrackerApi.Tests/ClientErrorsControllerTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/CompanyRelationshipTests.cs:1](../../../JobTrackerApi.Tests/CompanyRelationshipTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/CorrespondenceControllerTests.cs:1](../../../JobTrackerApi.Tests/CorrespondenceControllerTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/CsvExportTests.cs:1](../../../JobTrackerApi.Tests/CsvExportTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/CurrentUserIdLiveEvaluationTests.cs:1](../../../JobTrackerApi.Tests/CurrentUserIdLiveEvaluationTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/CvBuilderTests.cs:1](../../../JobTrackerApi.Tests/CvBuilderTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/CvCorpusHarnessTests.cs:1](../../../JobTrackerApi.Tests/CvCorpusHarnessTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/CvExportRetentionTests.cs:1](../../../JobTrackerApi.Tests/CvExportRetentionTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/CvExtractionCoverageTests.cs:1](../../../JobTrackerApi.Tests/CvExtractionCoverageTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/CvProcessingOperationTests.cs:1](../../../JobTrackerApi.Tests/CvProcessingOperationTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/CvProfileDiffServiceTests.cs:1](../../../JobTrackerApi.Tests/CvProfileDiffServiceTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/DatabaseBackupRunnerTests.cs:1](../../../JobTrackerApi.Tests/DatabaseBackupRunnerTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/EmailControllerTests.cs:1](../../../JobTrackerApi.Tests/EmailControllerTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/EmailDraftPersistenceTests.cs:1](../../../JobTrackerApi.Tests/EmailDraftPersistenceTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/EmailDraftsControllerTests.cs:1](../../../JobTrackerApi.Tests/EmailDraftsControllerTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/EmailOwnershipIntegrationTests.cs:1](../../../JobTrackerApi.Tests/EmailOwnershipIntegrationTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/EmailProviderDeliveryTests.cs:1](../../../JobTrackerApi.Tests/EmailProviderDeliveryTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/EmailSendAttemptStoreTests.cs:1](../../../JobTrackerApi.Tests/EmailSendAttemptStoreTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/EmailSendControllerTests.cs:1](../../../JobTrackerApi.Tests/EmailSendControllerTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/EmailStatusClassifierTests.cs:1](../../../JobTrackerApi.Tests/EmailStatusClassifierTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/ExternalOriginTests.cs:1](../../../JobTrackerApi.Tests/ExternalOriginTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/ForwardedIngressTests.cs:1](../../../JobTrackerApi.Tests/ForwardedIngressTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/GmailControllerTests.cs:1](../../../JobTrackerApi.Tests/GmailControllerTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/GoogleTokenValidatorTests.cs:1](../../../JobTrackerApi.Tests/GoogleTokenValidatorTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/HumanLanguageCatalogTests.cs:1](../../../JobTrackerApi.Tests/HumanLanguageCatalogTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/ImapControllerTests.cs:1](../../../JobTrackerApi.Tests/ImapControllerTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/ImapProviderTests.cs:1](../../../JobTrackerApi.Tests/ImapProviderTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/ImapServiceSsrfGuardTests.cs:1](../../../JobTrackerApi.Tests/ImapServiceSsrfGuardTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/InterviewAiContextTests.cs:1](../../../JobTrackerApi.Tests/InterviewAiContextTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/InterviewPrepPersistenceTests.cs:1](../../../JobTrackerApi.Tests/InterviewPrepPersistenceTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/InterviewPrepTests.cs:1](../../../JobTrackerApi.Tests/InterviewPrepTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/JobApplicationsApplicationPackageTests.cs:1](../../../JobTrackerApi.Tests/JobApplicationsApplicationPackageTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/JobApplicationsAppliedDateHistoryTests.cs:1](../../../JobTrackerApi.Tests/JobApplicationsAppliedDateHistoryTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/JobApplicationsAuthorizationTests.cs:1](../../../JobTrackerApi.Tests/JobApplicationsAuthorizationTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/JobApplicationsControllerTests.cs:1](../../../JobTrackerApi.Tests/JobApplicationsControllerTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/JobApplicationsEndpointBehaviorTests.cs:1](../../../JobTrackerApi.Tests/JobApplicationsEndpointBehaviorTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/JobApplicationsFollowUpDraftTests.cs:1](../../../JobTrackerApi.Tests/JobApplicationsFollowUpDraftTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/JobApplicationsMariaDraftTests.cs:1](../../../JobTrackerApi.Tests/JobApplicationsMariaDraftTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/JobApplicationsWorkflowSignalsTests.cs:1](../../../JobTrackerApi.Tests/JobApplicationsWorkflowSignalsTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/JobCvMatchServiceTests.cs:1](../../../JobTrackerApi.Tests/JobCvMatchServiceTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/JobDiscoveryControllerTests.cs:1](../../../JobTrackerApi.Tests/JobDiscoveryControllerTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/JobImportHttpTransportTests.cs:1](../../../JobTrackerApi.Tests/JobImportHttpTransportTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/JobImportServiceTests.cs:1](../../../JobTrackerApi.Tests/JobImportServiceTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/JobLifecycleEventTests.cs:1](../../../JobTrackerApi.Tests/JobLifecycleEventTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/JobOpportunitySyncTests.cs:1](../../../JobTrackerApi.Tests/JobOpportunitySyncTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/JobPipelineTests.cs:1](../../../JobTrackerApi.Tests/JobPipelineTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/LocalAuthIdentityTests.cs:1](../../../JobTrackerApi.Tests/LocalAuthIdentityTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/MaintenanceJobDispatcherTests.cs:1](../../../JobTrackerApi.Tests/MaintenanceJobDispatcherTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/MeteredSummarizerServiceTests.cs:1](../../../JobTrackerApi.Tests/MeteredSummarizerServiceTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/MicrosoftGraphControllerTests.cs:1](../../../JobTrackerApi.Tests/MicrosoftGraphControllerTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/MicrosoftGraphProviderTests.cs:1](../../../JobTrackerApi.Tests/MicrosoftGraphProviderTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/MicrosoftTokenValidatorTests.cs:1](../../../JobTrackerApi.Tests/MicrosoftTokenValidatorTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/MigrationChainTests.cs:1](../../../JobTrackerApi.Tests/MigrationChainTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/NotificationSettingsControllerTests.cs:1](../../../JobTrackerApi.Tests/NotificationSettingsControllerTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/OperationsControllerTests.cs:1](../../../JobTrackerApi.Tests/OperationsControllerTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/OwnershipGuardTests.cs:1](../../../JobTrackerApi.Tests/OwnershipGuardTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/PdfRenderAdmissionTests.cs:1](../../../JobTrackerApi.Tests/PdfRenderAdmissionTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/ProEntitlementAuthorizationTests.cs:1](../../../JobTrackerApi.Tests/ProEntitlementAuthorizationTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/ProductionConfigTests.cs:1](../../../JobTrackerApi.Tests/ProductionConfigTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/ProfileCvControllerTests.cs:1](../../../JobTrackerApi.Tests/ProfileCvControllerTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/PublicCvControllerTests.cs:1](../../../JobTrackerApi.Tests/PublicCvControllerTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/RateLimitPartitionKeysTests.cs:1](../../../JobTrackerApi.Tests/RateLimitPartitionKeysTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/RouteUniquenessTests.cs:1](../../../JobTrackerApi.Tests/RouteUniquenessTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/RulesEngineProspectTests.cs:1](../../../JobTrackerApi.Tests/RulesEngineProspectTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/SavedJobSearchAlertTests.cs:1](../../../JobTrackerApi.Tests/SavedJobSearchAlertTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/SessionContextMiddlewareTests.cs:1](../../../JobTrackerApi.Tests/SessionContextMiddlewareTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/SessionsControllerTests.cs:1](../../../JobTrackerApi.Tests/SessionsControllerTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/SqliteDateTimeOffsetCompatibilityTests.cs:1](../../../JobTrackerApi.Tests/SqliteDateTimeOffsetCompatibilityTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/SqliteMigrationHelperTests.cs:1](../../../JobTrackerApi.Tests/SqliteMigrationHelperTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/StageAnalyticsTests.cs:1](../../../JobTrackerApi.Tests/StageAnalyticsTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/StartupSchemaOwnershipTests.cs:1](../../../JobTrackerApi.Tests/StartupSchemaOwnershipTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/StrategySnapshotOperationTests.cs:1](../../../JobTrackerApi.Tests/StrategySnapshotOperationTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/SubmittedApplicationPackageTests.cs:1](../../../JobTrackerApi.Tests/SubmittedApplicationPackageTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/SummarizerServiceTests.cs:1](../../../JobTrackerApi.Tests/SummarizerServiceTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/TwoFactorControllerTests.cs:1](../../../JobTrackerApi.Tests/TwoFactorControllerTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/UserOperationStoreTests.cs:1](../../../JobTrackerApi.Tests/UserOperationStoreTests.cs#L1) |
|
||||
| Backend | [JobTrackerApi.Tests/UsersControllerTests.cs:1](../../../JobTrackerApi.Tests/UsersControllerTests.cs#L1) |
|
||||
| Browser extension | [browser-extension/tests/capture-url.test.mjs:1](../../../browser-extension/tests/capture-url.test.mjs#L1) |
|
||||
| Browser / HTTP | [e2e/accessibility.spec.ts:1](../../../job-tracker-ui/e2e/accessibility.spec.ts#L1) |
|
||||
| Browser / HTTP | [e2e/account-isolation.spec.ts:1](../../../job-tracker-ui/e2e/account-isolation.spec.ts#L1) |
|
||||
| Browser / HTTP | [e2e/csv-export.spec.ts:1](../../../job-tracker-ui/e2e/csv-export.spec.ts#L1) |
|
||||
| Browser / HTTP | [e2e/job-discovery.spec.ts:1](../../../job-tracker-ui/e2e/job-discovery.spec.ts#L1) |
|
||||
| Browser / HTTP | [e2e/kanban-theme.spec.ts:1](../../../job-tracker-ui/e2e/kanban-theme.spec.ts#L1) |
|
||||
| Browser / HTTP | [e2e/smoke.spec.ts:1](../../../job-tracker-ui/e2e/smoke.spec.ts#L1) |
|
||||
| Browser / HTTP | [ingress-tests/production.spec.ts:1](../../../job-tracker-ui/ingress-tests/production.spec.ts#L1) |
|
||||
| Browser / HTTP | [pdf-tests/admission.spec.ts:1](../../../job-tracker-ui/pdf-tests/admission.spec.ts#L1) |
|
||||
| Browser / HTTP | [rate-limit-tests/isolation.spec.ts:1](../../../job-tracker-ui/rate-limit-tests/isolation.spec.ts#L1) |
|
||||
| Frontend unit/component | [src/App.test.tsx:1](../../../job-tracker-ui/src/App.test.tsx#L1) |
|
||||
| Frontend unit/component | [src/LandingPage.authRedirect.test.tsx:1](../../../job-tracker-ui/src/LandingPage.authRedirect.test.tsx#L1) |
|
||||
| Frontend unit/component | [src/account-data-export.test.tsx:1](../../../job-tracker-ui/src/account-data-export.test.tsx#L1) |
|
||||
| Frontend unit/component | [src/account-scoped-state.test.tsx:1](../../../job-tracker-ui/src/account-scoped-state.test.tsx#L1) |
|
||||
| Frontend unit/component | [src/admin-audit-page.test.tsx:1](../../../job-tracker-ui/src/admin-audit-page.test.tsx#L1) |
|
||||
| Frontend unit/component | [src/admin-system-page.test.tsx:1](../../../job-tracker-ui/src/admin-system-page.test.tsx#L1) |
|
||||
| Frontend unit/component | [src/admin-users-page.test.tsx:1](../../../job-tracker-ui/src/admin-users-page.test.tsx#L1) |
|
||||
| Frontend unit/component | [src/ai-section-assistant.test.tsx:1](../../../job-tracker-ui/src/ai-section-assistant.test.tsx#L1) |
|
||||
| Frontend unit/component | [src/ai-usage-card.test.tsx:1](../../../job-tracker-ui/src/ai-usage-card.test.tsx#L1) |
|
||||
| Frontend unit/component | [src/app-shell-notifications.test.tsx:1](../../../job-tracker-ui/src/app-shell-notifications.test.tsx#L1) |
|
||||
| Frontend unit/component | [src/application-assets.test.tsx:1](../../../job-tracker-ui/src/application-assets.test.tsx#L1) |
|
||||
| Frontend unit/component | [src/application-calendar.test.tsx:1](../../../job-tracker-ui/src/application-calendar.test.tsx#L1) |
|
||||
| Frontend unit/component | [src/application-checklist.test.tsx:1](../../../job-tracker-ui/src/application-checklist.test.tsx#L1) |
|
||||
| Frontend unit/component | [src/application-drafts.test.ts:1](../../../job-tracker-ui/src/application-drafts.test.ts#L1) |
|
||||
| Frontend unit/component | [src/application-intelligence.test.tsx:1](../../../job-tracker-ui/src/application-intelligence.test.tsx#L1) |
|
||||
| Frontend unit/component | [src/application-route-contracts.test.ts:1](../../../job-tracker-ui/src/application-route-contracts.test.ts#L1) |
|
||||
| Frontend unit/component | [src/application-workflow-assist.test.tsx:1](../../../job-tracker-ui/src/application-workflow-assist.test.tsx#L1) |
|
||||
| Frontend unit/component | [src/application-workspace-overlay.test.tsx:1](../../../job-tracker-ui/src/application-workspace-overlay.test.tsx#L1) |
|
||||
| Frontend unit/component | [src/applicationWorkspace.test.ts:1](../../../job-tracker-ui/src/applicationWorkspace.test.ts#L1) |
|
||||
| Frontend unit/component | [src/attachments.test.tsx:1](../../../job-tracker-ui/src/attachments.test.tsx#L1) |
|
||||
| Frontend unit/component | [src/captureUrl.test.ts:1](../../../job-tracker-ui/src/captureUrl.test.ts#L1) |
|
||||
| Frontend unit/component | [src/career-evidence.test.tsx:1](../../../job-tracker-ui/src/career-evidence.test.tsx#L1) |
|
||||
| Frontend unit/component | [src/career-workspace-page.test.tsx:1](../../../job-tracker-ui/src/career-workspace-page.test.tsx#L1) |
|
||||
| Frontend unit/component | [src/companies-relationship.test.tsx:1](../../../job-tracker-ui/src/companies-relationship.test.tsx#L1) |
|
||||
| Frontend unit/component | [src/confirm-flow.test.tsx:1](../../../job-tracker-ui/src/confirm-flow.test.tsx#L1) |
|
||||
| Frontend unit/component | [src/confirm.test.tsx:1](../../../job-tracker-ui/src/confirm.test.tsx#L1) |
|
||||
| Frontend unit/component | [src/correspondence-gmail-import.test.tsx:1](../../../job-tracker-ui/src/correspondence-gmail-import.test.tsx#L1) |
|
||||
| Frontend unit/component | [src/correspondence-inbox-page.test.tsx:1](../../../job-tracker-ui/src/correspondence-inbox-page.test.tsx#L1) |
|
||||
| Frontend unit/component | [src/correspondence-search-context.test.ts:1](../../../job-tracker-ui/src/correspondence-search-context.test.ts#L1) |
|
||||
| Frontend unit/component | [src/csv-export.test.tsx:1](../../../job-tracker-ui/src/csv-export.test.tsx#L1) |
|
||||
| Frontend unit/component | [src/cv-builder-deep-link.test.tsx:1](../../../job-tracker-ui/src/cv-builder-deep-link.test.tsx#L1) |
|
||||
| Frontend unit/component | [src/cv-builder-page.test.tsx:1](../../../job-tracker-ui/src/cv-builder-page.test.tsx#L1) |
|
||||
| Frontend unit/component | [src/cv-template-thumbnail.test.tsx:1](../../../job-tracker-ui/src/cv-template-thumbnail.test.tsx#L1) |
|
||||
| Frontend unit/component | [src/cvBuilder.test.ts:1](../../../job-tracker-ui/src/cvBuilder.test.ts#L1) |
|
||||
| Frontend unit/component | [src/daily-control-loop.test.tsx:1](../../../job-tracker-ui/src/daily-control-loop.test.tsx#L1) |
|
||||
| Frontend unit/component | [src/dialog-flow.test.tsx:1](../../../job-tracker-ui/src/dialog-flow.test.tsx#L1) |
|
||||
| Frontend unit/component | [src/email-provider-connections.test.tsx:1](../../../job-tracker-ui/src/email-provider-connections.test.tsx#L1) |
|
||||
| Frontend unit/component | [src/end-to-end-trust-loop.test.tsx:1](../../../job-tracker-ui/src/end-to-end-trust-loop.test.tsx#L1) |
|
||||
| Frontend unit/component | [src/gmail-review-page.test.tsx:1](../../../job-tracker-ui/src/gmail-review-page.test.tsx#L1) |
|
||||
| Frontend unit/component | [src/i18n-provider.test.tsx:1](../../../job-tracker-ui/src/i18n-provider.test.tsx#L1) |
|
||||
| Frontend unit/component | [src/interview-prep.test.tsx:1](../../../job-tracker-ui/src/interview-prep.test.tsx#L1) |
|
||||
| Frontend unit/component | [src/job-discovery.test.tsx:1](../../../job-tracker-ui/src/job-discovery.test.tsx#L1) |
|
||||
| Frontend unit/component | [src/kanban-grouped-board.test.tsx:1](../../../job-tracker-ui/src/kanban-grouped-board.test.tsx#L1) |
|
||||
| Frontend unit/component | [src/landing-page.test.tsx:1](../../../job-tracker-ui/src/landing-page.test.tsx#L1) |
|
||||
| Frontend unit/component | [src/login-page.test.tsx:1](../../../job-tracker-ui/src/login-page.test.tsx#L1) |
|
||||
| Frontend unit/component | [src/microsoft-legacy-relink-page.test.tsx:1](../../../job-tracker-ui/src/microsoft-legacy-relink-page.test.tsx#L1) |
|
||||
| Frontend unit/component | [src/nginx-config.test.ts:1](../../../job-tracker-ui/src/nginx-config.test.ts#L1) |
|
||||
| Frontend unit/component | [src/notifications-popover.test.tsx:1](../../../job-tracker-ui/src/notifications-popover.test.tsx#L1) |
|
||||
| Frontend unit/component | [src/offer-comparison.test.tsx:1](../../../job-tracker-ui/src/offer-comparison.test.tsx#L1) |
|
||||
| Frontend unit/component | [src/onboarding-checklist.test.tsx:1](../../../job-tracker-ui/src/onboarding-checklist.test.tsx#L1) |
|
||||
| Frontend unit/component | [src/operations-page.test.tsx:1](../../../job-tracker-ui/src/operations-page.test.tsx#L1) |
|
||||
| Frontend unit/component | [src/pipeline.test.ts:1](../../../job-tracker-ui/src/pipeline.test.ts#L1) |
|
||||
| Frontend unit/component | [src/planCatalog.test.ts:1](../../../job-tracker-ui/src/planCatalog.test.ts#L1) |
|
||||
| Frontend unit/component | [src/pro-feature-notice.test.tsx:1](../../../job-tracker-ui/src/pro-feature-notice.test.tsx#L1) |
|
||||
| Frontend unit/component | [src/profile-page.test.tsx:1](../../../job-tracker-ui/src/profile-page.test.tsx#L1) |
|
||||
| Frontend unit/component | [src/prompt.test.tsx:1](../../../job-tracker-ui/src/prompt.test.tsx#L1) |
|
||||
| Frontend unit/component | [src/public-cv-page.test.tsx:1](../../../job-tracker-ui/src/public-cv-page.test.tsx#L1) |
|
||||
| Frontend unit/component | [src/quick-capture.test.tsx:1](../../../job-tracker-ui/src/quick-capture.test.tsx#L1) |
|
||||
| Frontend unit/component | [src/session-boundary.test.ts:1](../../../job-tracker-ui/src/session-boundary.test.ts#L1) |
|
||||
| Frontend unit/component | [src/settings-view.test.tsx:1](../../../job-tracker-ui/src/settings-view.test.tsx#L1) |
|
||||
| Frontend unit/component | [src/sidebar-active-nav.test.ts:1](../../../job-tracker-ui/src/sidebar-active-nav.test.ts#L1) |
|
||||
| Frontend unit/component | [src/theme-state.test.tsx:1](../../../job-tracker-ui/src/theme-state.test.tsx#L1) |
|
||||
| Frontend unit/component | [src/turnstile-widget.test.tsx:1](../../../job-tracker-ui/src/turnstile-widget.test.tsx#L1) |
|
||||
| Frontend unit/component | [src/two-factor-settings.test.tsx:1](../../../job-tracker-ui/src/two-factor-settings.test.tsx#L1) |
|
||||
| Frontend unit/component | [src/use-view-resource.test.tsx:1](../../../job-tracker-ui/src/use-view-resource.test.tsx#L1) |
|
||||
| Frontend unit/component | [src/verify-email-page.test.tsx:1](../../../job-tracker-ui/src/verify-email-page.test.tsx#L1) |
|
||||
| Frontend unit/component | [src/workflow-trust-signals.test.tsx:1](../../../job-tracker-ui/src/workflow-trust-signals.test.tsx#L1) |
|
||||
| Repository safety | [scripts/test-ingress-topology.py:1](../../../scripts/test-ingress-topology.py#L1) |
|
||||
| Repository safety | [scripts/test-ingress.py:1](../../../scripts/test-ingress.py#L1) |
|
||||
| Repository safety | [scripts/test-ollama-evaluation.py:1](../../../scripts/test-ollama-evaluation.py#L1) |
|
||||
| Repository safety | [scripts/test-supply-chain.py:1](../../../scripts/test-supply-chain.py#L1) |
|
||||
| Parser / AI service | [tools/summarizer/tests/test_app.py:1](../../../tools/summarizer/tests/test_app.py#L1) |
|
||||
@@ -0,0 +1,61 @@
|
||||
# Test baseline and gaps
|
||||
|
||||
The [test-file census](TEST_FILES.md) lists every discovered tracked frontend test file (76: 67 unit/component and nine browser/HTTP specification files), 106 backend test-source files, plus extension/parser/repository safety files. Counts describe files, not executed cases or coverage percentages. The source snapshot is `97fce89`; subsequent bounded CI packaging corrections are tracked in the B06 release log, not redesign work.
|
||||
|
||||
## Test layers
|
||||
|
||||
| Layer | Setup and demonstrated scope | Limit |
|
||||
|---|---|---|
|
||||
| Frontend unit/component | Jest, jsdom, Testing Library; [config](../../../job-tracker-ui/jest.config.cjs). Account switching, routes, workspace/drafts, CV, mail, admin, theme, translations, dialogs and imports/exports | Babel strips TypeScript; mocks do not prove server ownership or real provider behavior |
|
||||
| General browser | [Playwright config](../../../job-tracker-ui/playwright.config.ts): one Chromium project, isolated .NET API/SQLite data and local frontend; production static build supported | Chromium only; no actual Traefik/nginx; no cross-browser or screen-reader certification |
|
||||
| Browser smoke | Nine cases in `e2e/smoke.spec.ts`: health/readiness, public plans, login, Free restrictions, reviewed job create, deep-linked job workspace/dirty guard, career, public CV/PDF, long searchable Code PDF | Selected journeys, not exhaustive editor states or all providers |
|
||||
| Account isolation | Four `e2e/account-isolation.spec.ts` cases: original A→B replay, missing/forged context, crafted CV ownership/association, two-tab private-state disposal/Back | Retain API and database assertions when UI changes |
|
||||
| Accessibility | Two axe cases over eight sampled routes | See missing state matrix below |
|
||||
| Other general browser | One each discovery responsive review, dark Kanban, CSV security/UTF-8 byte preservation | Does not establish whole-app mobile/theme parity |
|
||||
| PDF HTTP probe | `pdf-tests/admission.spec.ts` and dedicated config | Synthetic bounded concurrent requests; admission differs from time-window limiting |
|
||||
| Rate-limit HTTP probe | `rate-limit-tests/isolation.spec.ts` and dedicated config | Nine account/session/IP/auth/forwarding scenarios; native API topology |
|
||||
| Production ingress HTTP | `ingress-tests/production.spec.ts` and dedicated config | Six native production-mode origin/proxy checks; not running nginx or Traefik |
|
||||
| Backend | xUnit/Moq, SQLite/InMemory and HTTP test seams; API/service ownership, auth, parser, CV/history/PDF, queue, billing, export, migration and rate/proxy tests | Provider-host tests and fault/probe tests have separate runtime requirements; SQLite is not full MariaDB proof |
|
||||
| Repository/dependencies | Compose rendering + 17 hostile mutations, secret scan/SBOM, evaluation and supply-chain scripts | Config validation cannot prove host firewall or actual socket peers |
|
||||
| Adjacent clients/services | Browser-extension node tests and Python parser/service tests | Preserve capture/document boundary; avoid pretending these are covered by React Jest |
|
||||
|
||||
The default browser suite currently contains **18 tests**. The nine special browser/HTTP files count includes six general e2e files and three separately configured suites. Special configurations must be invoked explicitly; a plain general Playwright run does not run all of them.
|
||||
|
||||
## Existing evidence versus fresh execution
|
||||
|
||||
| Evidence | Result and classification |
|
||||
|---|---|
|
||||
| Previous B06 unchanged application regression | 166 focused backend, 3 Compose, 6 production HTTP, 9 rate-limit HTTP passed; [exact prior commands/results](../../audits/2026-09-07/validation/b06-completion.md) |
|
||||
| Previous B06 broader baseline | 915 selected backend tests, 298 frontend tests/67 suites, 18 browser tests, Release builds and targeted lint passed; external-DNS-dependent IMAP test excluded locally and explicitly documented |
|
||||
| Fresh Phase 1 type reproduction | From `job-tracker-ui`: `node node_modules/typescript/bin/tsc --noEmit --incremental false` → exit 1, `src/interview-prep.test.tsx(120,10): error TS2571: Object is of type 'unknown'.` |
|
||||
| Fresh release-prerequisite checks | `python scripts/test-ingress.py` → 3/3 pass; real standalone Compose path also tested; official downloaded Linux binary digest verified; tracked-secret scan and diff check pass |
|
||||
| Fresh packaging regression at `23cc250` | Explicit installed .NET 9.0.317 SDK: Release build + **22/22 PDF admission tests** pass, then **915/915 selected backend tests** pass (same documented external-DNS IMAP exclusion). Results: `.artifacts/redesign-audit/backend.trx` |
|
||||
| Actual CI push verification | Runs 736/737 exposed missing Compose/token prerequisites; corrected. Run 738 passed Compose/safety/extension/parser/API build, then exposed Linux PDF test-fixture packaging collision. Follow-up result belongs in [release log](../../audits/2026-09-07/validation/b06-completion.md#authorized-release-attempt-and-ci-prerequisite-correction) |
|
||||
| Fresh Linux backend confirmation | Run 741, `2024c37`, passed **916/916 backend cases**, zero skipped, after test packaging/runtime-location and TLS EOF-or-no-read portability corrections. This includes the locally excluded DNS-dependent case. Application behavior and all security assertions remain protected; deployment has separate gates |
|
||||
| Subsequent authorized release correction, `a861ca4` | Next 16.3.4/sharp 0.35.4/js-yaml 3.15.2 resolve three blocking dependency entries: installed npm audit **zero vulnerabilities**. Fresh **298 frontend / 18 browser** pass, plus **13 interview tests** after the test-only TS2571 correction. Standalone tsc, lint and production static build pass. Exact commands and failed-before evidence remain in the B06 release log; this is release repair, not a redesign implementation |
|
||||
| Final CI retry, run 743 at `27657f3` | **916 backend / 298 frontend** pass, zero backend skips; Compose/safety/parser, dependency audits, lint and production builds pass. Browser gate: **6 passed / 12 failed**, beginning with worker SIGSEGV, then retry timeout and closed-browser navigation errors. Deploy skipped. Repeated process crashes reached the bounded stop condition; runner-host evidence is required before another release attempt. See [exact result and diagnostic handoff](../../audits/2026-09-07/validation/b06-completion.md#final-release-retry--2026-09-10). Local browser success is not a replacement for CI |
|
||||
| Live/container boundary | Local Docker engine unavailable; F04 production evidence remains incomplete. No audit claim of real proxy-chain success |
|
||||
|
||||
The selected broader backend suite was repeated for the PDF fixture packaging change; the frontend suite was not repeated merely for documentation edits. Preserve the distinction between prior unchanged-source evidence, newly executed checks and actual CI failures. Run 739 subsequently failed earlier in the unchanged API compiler with `csc.dll` exit 139 on both existing attempts; it did not reach the corrected test-project build. No test gate was bypassed.
|
||||
|
||||
## Exact TypeScript root cause
|
||||
|
||||
[Interview test:120](../../../job-tracker-ui/src/interview-prep.test.tsx#L120) originally read `.content` from `mockedApi.post.mock.calls[0][1]`. Axios's mocked generic request-data argument is typed `unknown`; the test dereferenced it without narrowing. The runtime assertion passed because Babel removes types. The production component's emitted behavior was not shown broken by this test diagnostic.
|
||||
|
||||
[tsconfig](../../../job-tracker-ui/tsconfig.json) includes `src`, including tests. CI runs Jest/lint/Next build but has no explicit full standalone `tsc --noEmit` gate. The patched Next 16.3.4 release build independently failed on this exact diagnostic. Authorized release repair `a861ca4` replaced the property dereference with an object matcher, retaining both positive prompt and negative invented-content assertions. Fresh standalone tsc, 13 interview tests and Next build now pass. The original Phase 1 reproduction above remains historical evidence. Phase 2 still needs a deliberate standalone CI gate; no strictness or test coverage was weakened.
|
||||
|
||||
## Missing regression proof before redesign
|
||||
|
||||
| Priority | Add or preserve evidence |
|
||||
|---|---|
|
||||
| First | Stable type-check gate; B02 stale/queued mutation + cross-tab unmount as shared-client acceptance; legacy URL/overlay/full-page parity |
|
||||
| First | Populated CV editor/profile/job detail plus open menus/dialogs/error/conflict states in EN/NB, light/dark, mobile/desktop; preserve underlying draft/history assertions |
|
||||
| First | AI suggestions rejected/accepted distinctly; actual profile values unchanged when editing a CV/cover letter; unsupported/locked capabilities behave honestly |
|
||||
| First | Typed date values, JSON import/export and B05 CSV bytes; attachments/private/public download distinctions; package history remains frozen |
|
||||
| Before mail migration | Draft revisions, uncertain send/new attempt, provider reconnect/read failure, ownership and explicit send confirmation; use synthetic/sandbox providers |
|
||||
| Before admin migration | Non-admin denied APIs, role revocation and last-admin guards, sensitive diagnostics excluded from personal pages |
|
||||
| Before public release | Keyboard + NVDA/Narrator, preview scroll/focus, zoom/reflow, forced colors, real mobile keyboard and touch; provider/production gates separately |
|
||||
|
||||
Axe currently scans `/`, `/login`, `/dashboard`, `/jobs`, `/kanban`, `/career`, `/settings`, `/admin/system`, mostly after `body`/`main` appears, in English. Missing builder library/editor, job detail, discovery, offers, mail/review, public CV, account security, registration/reset/verification, operations, admin users/audit and interactive loaded states must be selected intentionally. Do not convert a green shell sample into a WCAG compliance claim.
|
||||
|
||||
Recommended later commands: locked dependency restore; Release backend build/test; full Jest; standalone tsc; lint; production frontend build; general Playwright static-export suite; explicit PDF/rate/ingress configurations; Compose validator and `git diff --check`. Use the environment/setup commands from the completion reports rather than guessing SDK paths or running probes against production.
|
||||
@@ -0,0 +1,76 @@
|
||||
// Read-only source census for the Phase 1 audit. Uses the repository's installed TypeScript parser.
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const cp = require('child_process');
|
||||
const root = process.cwd();
|
||||
const ts = require(path.join(root, 'job-tracker-ui/node_modules/typescript'));
|
||||
const out = path.join(root, 'docs/redesign/audit');
|
||||
fs.mkdirSync(out, {recursive:true});
|
||||
const paths = cp.execFileSync('git', ['ls-files'], {encoding:'utf8'}).trim().split('\n');
|
||||
const files = paths.filter(p => /^job-tracker-ui\/(src|app)\/.+\.[jt]sx?$/.test(p) && !/\.test\./.test(p) && !p.endsWith('setupTests.ts'));
|
||||
const tests = paths.filter(p => /(?:\.test\.[jt]sx?$|\.spec\.tsx?$)/.test(p) && p.startsWith('job-tracker-ui/'));
|
||||
const sources = {};
|
||||
const routes = [];
|
||||
function loc(sf,n) { return sf.getLineAndCharacterOfPosition(n.getStart(sf)).line + 1; }
|
||||
function resolve(from, dep) {
|
||||
if (!dep.startsWith('.')) return null;
|
||||
const base = path.posix.normalize(path.posix.join(path.posix.dirname(from),dep));
|
||||
return [base, ...['.ts','.tsx','.js','.jsx','/index.ts','/index.tsx'].map(s=>base+s)].find(p=>files.includes(p)) || null;
|
||||
}
|
||||
for (const file of files) {
|
||||
const content = fs.readFileSync(path.join(root,file),'utf8');
|
||||
const sf = ts.createSourceFile(file,content,ts.ScriptTarget.Latest,true,file.endsWith('tsx')?ts.ScriptKind.TSX:ts.ScriptKind.TS);
|
||||
const info = {lines:content.split(/\r?\n/).length, imports:[], api:[], labels:[], components:[], storage:[], functions:[]};
|
||||
function visit(n) {
|
||||
if(ts.isImportDeclaration(n) && ts.isStringLiteral(n.moduleSpecifier)) info.imports.push(n.moduleSpecifier.text);
|
||||
if(ts.isCallExpression(n)) {
|
||||
if(n.expression.kind===ts.SyntaxKind.ImportKeyword && n.arguments[0] && ts.isStringLiteral(n.arguments[0])) info.imports.push(n.arguments[0].text);
|
||||
if(ts.isPropertyAccessExpression(n.expression) && /^(api|axios)$/.test(n.expression.expression.getText(sf)) && /^(get|post|put|patch|delete|request)$/.test(n.expression.name.text)) info.api.push({line:loc(sf,n),method:n.expression.name.text.toUpperCase(),url:n.arguments[0]?.getText(sf)||''});
|
||||
if(n.expression.getText(sf)==='t' && n.arguments[0] && ts.isStringLiteral(n.arguments[0])) info.labels.push(n.arguments[0].text);
|
||||
if(/(?:localStorage|sessionStorage)\.(?:getItem|setItem|removeItem)$/.test(n.expression.getText(sf))) info.storage.push({line:loc(sf,n),call:n.getText(sf).replace(/\s+/g,' ').slice(0,220)});
|
||||
}
|
||||
if((ts.isFunctionDeclaration(n)||ts.isVariableDeclaration(n)) && n.name && /^[A-Z]/.test(n.name.getText(sf))) info.functions.push({name:n.name.getText(sf),line:loc(sf,n)});
|
||||
if(ts.isJsxOpeningElement(n)||ts.isJsxSelfClosingElement(n)) {
|
||||
const tag=n.tagName.getText(sf);
|
||||
if(/^[A-Z]/.test(tag)) info.components.push(tag);
|
||||
if(tag==='Route') {
|
||||
const attrs=Object.fromEntries(n.attributes.properties.filter(ts.isJsxAttribute).map(a=>[a.name.getText(sf),a.initializer?.getText(sf)||'']));
|
||||
routes.push({file,line:loc(sf,n),...attrs});
|
||||
}
|
||||
}
|
||||
if(file.endsWith('/App.tsx') && ts.isPropertyAssignment(n) && n.name.getText(sf)==='path' && ts.isStringLiteral(n.initializer)) routes.push({file,line:loc(sf,n),path:n.initializer.text,element:n.parent.getText(sf).replace(/\s+/g,' ')});
|
||||
ts.forEachChild(n,visit);
|
||||
}
|
||||
visit(sf);
|
||||
for(const key of ['imports','labels','components']) info[key]=[...new Set(info[key])].sort();
|
||||
info.dependencies=info.imports.map(i=>resolve(file,i)).filter(Boolean);
|
||||
sources[file]=info;
|
||||
}
|
||||
const reachable = new Set();
|
||||
function walk(file) { if(reachable.has(file)||!sources[file])return; reachable.add(file); sources[file].dependencies.forEach(walk); }
|
||||
walk('job-tracker-ui/src/ClientApp.tsx');
|
||||
for (const [file,info] of Object.entries(sources)) {
|
||||
info.reachableFromClientApp=reachable.has(file);
|
||||
info.importedBy=Object.keys(sources).filter(f=>sources[f].dependencies.includes(file));
|
||||
}
|
||||
const summary = {baseline:cp.execFileSync('git',['rev-parse','HEAD'],{encoding:'utf8'}).trim(),sourceFiles:files.length,frontendTestFiles:tests.length,apiCallSites:Object.values(sources).reduce((n,s)=>n+s.api.length,0),routeDeclarations:routes.length};
|
||||
fs.writeFileSync(path.join(out,'source-inventory.json'),JSON.stringify({summary,routes,tests,sources},null,2)+'\n');
|
||||
const cell=s=>String(s).replaceAll('|','\\|').replaceAll('\n',' ');
|
||||
const link=(file,line)=>`[${file.replace('job-tracker-ui/','')}:${line}](../../../${file}#L${line})`;
|
||||
// docs/redesign/audit is three directories below the repository root.
|
||||
const sourceLink=link;
|
||||
let api='# API call-site inventory\n\nGenerated with the installed TypeScript AST from the committed baseline. Each row is an actual `api`/`axios` call expression, not a claim of runtime test coverage. Paths are relative to the shared `/api` base; interpolations are preserved. Domain wrappers are included. Companion technical audit maps contracts and authorization.\n\n| Source | Method | URL expression |\n|---|---|---|\n';
|
||||
for(const [file,info] of Object.entries(sources)) for(const call of info.api) api+=`| ${sourceLink(file,call.line)} | ${call.method} | ${cell(call.url)} |\n`;
|
||||
fs.writeFileSync(path.join(out,'API_CALL_SITES.md'),api);
|
||||
let components='# Component and source inventory\n\nEvery production TSX module under `src` and `app` is listed. Lines are size indicators, not complexity scores. Reachability is relative-import reachability from ClientApp; app entry files are expected to be outside that graph. MUI primitives are dependencies, not missing local components. No unreachable file is approved for deletion.\n\n| Module | Lines | Client graph | Local direct consumers | Rendered component names |\n|---|---|---|---|---|\n';
|
||||
for(const [file,info] of Object.entries(sources)) if(file.endsWith('.tsx')) components+=`| ${sourceLink(file,1)} | ${info.lines} | ${info.reachableFromClientApp?'reachable':'outside graph'} | ${cell(info.importedBy.map(f=>path.basename(f)).join(', ')||'—')} | ${cell(info.components.join(', '))} |\n`;
|
||||
fs.writeFileSync(path.join(out,'COMPONENT_INVENTORY.md'),components);
|
||||
let testList='# Test file inventory\n\nTracked test files at the audit baseline. Presence is not proof that a feature or every interaction is covered. See TEST_INVENTORY.md for scope and execution evidence.\n\n| Layer | File |\n|---|---|\n';
|
||||
for(const file of paths.filter(p => tests.includes(p) || /^JobTrackerApi.Tests\/.+\.cs$/.test(p) && /Tests?\.cs$/.test(p) || /^browser-extension\/tests\/.+\.test\.mjs$/.test(p) || /^tools\/summarizer\/tests\/.+\.py$/.test(p) || /^scripts\/test-.+\.py$/.test(p))) {
|
||||
const layer=file.startsWith('job-tracker-ui/src/')?'Frontend unit/component':file.startsWith('job-tracker-ui/')?'Browser / HTTP':file.startsWith('JobTrackerApi.Tests/')?'Backend':file.startsWith('tools/')?'Parser / AI service':file.startsWith('browser-extension/')?'Browser extension':'Repository safety';
|
||||
testList+=`| ${layer} | ${sourceLink(file,1)} |\n`;
|
||||
}
|
||||
fs.writeFileSync(path.join(out,'TEST_FILES.md'),testList);
|
||||
console.log(JSON.stringify(summary));
|
||||
console.log('Largest:',Object.entries(sources).sort((a,b)=>b[1].lines-a[1].lines).slice(0,12).map(([f,s])=>[f,s.lines]));
|
||||
console.log('Outside graph:',Object.entries(sources).filter(([f,s])=>f.endsWith('.tsx')&&!s.reachableFromClientApp).map(([f])=>f));
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,68 @@
|
||||
# Phase 2 baseline defects and intentional gaps
|
||||
|
||||
Application source is unchanged from `2d0f131`. No defect below was fixed as part of the preservation checkpoint.
|
||||
|
||||
## P2-D01 — SQLite tailoring and submitted-package capture fail
|
||||
|
||||
**Confirmed in the disposable running local API, not production.** The new [browser regression](../../../job-tracker-ui/e2e/redesign-preservation.spec.ts) creates a real non-admin account, canonical profile, company/job, attached CV, cover letter and UTF-8 attachment. It opens the existing legacy job URL, then clicks **Freeze current package** in the **CV section** (not the cover-letter section).
|
||||
|
||||
Observed:
|
||||
|
||||
| Request | Result |
|
||||
|---|---|
|
||||
| Profile/job/CV/letter/file fixture writes | Successful, including B02 session preconditions |
|
||||
| GET job submitted-packages | 200, empty list |
|
||||
| GET job tailoring | 500 |
|
||||
| POST job submitted-packages | 500; no successful package row shown |
|
||||
| API exception | `System.NotSupportedException: SQLite does not support expressions of type 'DateTimeOffset' in ORDER BY clauses.` |
|
||||
|
||||
Stack evidence points to [ApplicationAssetsService](../../../JobTrackerApi/Services/ApplicationAssetsService.cs), `GetTailoringPlanAsync` (ordering verified CareerEvidence by UpdatedAtUtc at line 182), and [SubmittedApplicationPackageService](../../../JobTrackerApi/Services/SubmittedApplicationPackageService.cs), `CaptureAsync` (ordering variants by UpdatedAtUtc at line 67). These paths do not use the SQLite-safe ordering used in some nearby services. Existing [SqliteDateTimeOffsetCompatibilityTests](../../../JobTrackerApi.Tests/SqliteDateTimeOffsetCompatibilityTests.cs) exercise other ordering paths; they do not certify these two. Existing [SubmittedApplicationPackageTests](../../../JobTrackerApi.Tests/SubmittedApplicationPackageTests.cs) prove snapshot/owner/failure contracts with test seams, not successful native SQLite execution of this query.
|
||||
|
||||
The new desired success test is retained with an explicit `test.fail` annotation activated **after fixture and navigation setup, only for the observed capture HTTP 500**. Playwright reports an expected failure as expected execution; this is **not** a successfully frozen package. Post-capture checks for unchanged archived HTML/letter and exact attachment bytes after deleting the current source are present but **not reached**. Do not count them as achieved coverage. A successful capture runs all later assertions as ordinary tests; setup, non-500 responses and subsequent archive failures cannot be masked by the annotation. Remove the narrow annotation and update this classification only with the separately scoped fix and successful native evidence.
|
||||
|
||||
Initial fixture error: the first draft looked for package capture under the cover-letter tab; inspection showed the existing capture lives under CV, and the test was corrected. That locator error is not an application defect.
|
||||
|
||||
Scope/next owner: a separate bounded provider-compatibility repair and native storage verification before any package/tailoring redesign. Preserve timestamps/ownership and MariaDB behavior; no schema change is presumed. Phase 2 neither changes ordering nor claims the same failure in MariaDB/production. All original 916 backend and 298 frontend cases remain retained.
|
||||
|
||||
Local diagnostic command (PowerShell, from job-tracker-ui, after the documented build):
|
||||
|
||||
```powershell
|
||||
$env:DEBUG='pw:webserver'
|
||||
node node_modules/@playwright/test/cli.js test e2e/redesign-preservation.spec.ts
|
||||
```
|
||||
|
||||
The full debug log is local ignored evidence, `.artifacts/phase2-browser-focused.log`; it is not committed wholesale. It contains synthetic accounts only. The exception/request facts above are the durable evidence. Use the normal commands in [README](README.md) to reproduce with an isolated data directory; never substitute production URLs.
|
||||
|
||||
## P2-O01 — one rules-save HTTP 500, not independently reproduced
|
||||
|
||||
The first browser run sent the valid rule object with appliedFollowUpDays=5 and received 500; a subsequent GET still returned 14. The visible draft retained 5. Response contained a generic problem and trace ID; that run did not retain the server exception, so the cause is unknown.
|
||||
|
||||
An isolated authenticated direct PUT then returned 204 and persisted 5. A repeat of the original mobile→desktop browser save with explicit response observation also returned 204 and persisted 5. **No rules regression is expected-failed or skipped.** Both successful browser/direct saves remain ordinary permanent tests. Do not claim a confirmed root cause from one transient result. If it recurs, collect the server exception/trace before proposing a fix. Failed notification-save UI retention is independently covered with a deterministic transport failure.
|
||||
|
||||
## Audit precision: workspace overlay and admin route
|
||||
|
||||
The current source has a reusable ApplicationWorkspace with override/onClose/full-page-link props, but the production call-site search finds only its normal full-page wrapper. Preserve the reusable contract; do not claim the test named application-workspace-overlay exercises a mounted production quick-preview host. A future overlay needs its own close/dirty/focus tests before adoption.
|
||||
|
||||
Non-admin navigation hides admin destinations. A crafted admin URL can render the page shell; the API returns 403 and no user list is exposed. The browser test verifies this established boundary, not a nonexistent frontend route-denial screen.
|
||||
|
||||
## Existing debt remains separate
|
||||
|
||||
The [Phase 1 risks](../audit/RISKS_AND_MIGRATION.md), [baseline summary](../audit/PHASE1_SUMMARY.md) and [validated queue](../../audits/2026-09-07/validation/implementation-queue.md) remain authoritative for prior findings:
|
||||
|
||||
- Stale CV revision overwrites, creation idempotency, calendar/timezone correctness, restore/rollback readiness, billing/deletion and operational failures are not resolved by redesign tests.
|
||||
- Shared contrast/focus, builder preview scrolling and incomplete loaded accessibility/manual testing remain open. No axe/WCAG certification is added.
|
||||
- Historical TS2571 was corrected before Phase 2; it does not recur. This checkpoint adds the explicit full src/app standalone type gate without relaxing strictness. Its existing tsconfig scope includes Jest files, not all Playwright/config files.
|
||||
- Main CI run 743's worker SIGSEGV/retry timeout/closed-browser failures and skipped deployment remain unresolved runner evidence. A local passing suite and feature-branch push do not close this release blocker.
|
||||
- **B06 implementation complete; F04 remains open.** Actual Traefik peer, nginx/API runtime listeners, firewall/NAT/IPv6 exposure, forwarded client IP, DNS/TLS/redirect and restart behavior still need the [operator handoff](../../audits/2026-09-07/validation/b06-completion.md). No production credentials accessed or live tests run here.
|
||||
|
||||
## Intentional gaps by deadline
|
||||
|
||||
| Deadline | Evidence still required |
|
||||
|---|---|
|
||||
| Before shell/Jobs restructuring | Mobile drawer focus/return, saved view CRUD, bulk partial failures/trash restore, capture/legacy entries; matrix E07/J04/J05/J06/W01 |
|
||||
| Before CV/package restructuring | Native capture/tailoring success, item-specific override reset/source edit, browser history restore, loaded preview keyboard and template/control combinations; C07–C10/W05/W07 |
|
||||
| Before career import restructuring | Low-confidence selection/discard, real unusual document extraction and error recovery; C03 |
|
||||
| Before mail/settings/admin restructuring | Provider sandbox reconnect/delivery; persisted draft conflict/uncertain-send retained; session list UI, audit undo/restore, dashboard toggle ownership; E05/M01–M06/S02/A02 |
|
||||
| Before public release | Manual assistive technology/touch/zoom/reflow; EN/NB and both themes; provider/DNS/TLS/firewall/backup restoration; original CI runner remediation |
|
||||
|
||||
These gaps permit Phase 3 planning, not unguarded production restructuring of the affected capability.
|
||||
@@ -0,0 +1,223 @@
|
||||
# Phase 2 preservation matrix
|
||||
|
||||
Baseline: **`2d0f131`**, the [57-group Phase 1 ledger](../audit/FUNCTIONALITY_INVENTORY.md). `PRES-E01` is a stable alias of `E01`, and similarly for all rows; original IDs retain their meaning. No capability has been removed, merged or deprecated. Each row includes all sub-capabilities in its linked baseline group, even when its short label abbreviates them.
|
||||
|
||||
## Measurement and limits
|
||||
|
||||
**57/57 groups (100%) have mapped automated behavioral evidence for at least one core behavior.** This is group-level preservation coverage, not statement coverage, exhaustive sub-workflow coverage, visual parity or 100% readiness. Every row's remaining gaps are explicit. A passing mock/service test does not prove a real provider, actual browser persistence, or production. In particular W07 retains passing service/component evidence but its new native SQLite capture test is an **expected failure**, not successful end-to-end preservation proof. See [baseline defects](BASELINE_DEFECTS.md).
|
||||
|
||||
`H` = identity/data loss/irreversible action or a high-risk shared navigation/editing surface; `M` = important reversible presentation/workflow risk. These are redesign regression risks, not security severity labels. “Touch” is expected later-phase impact, not implementation authorization. `F` links are Jest component/helper tests; `B` links are backend tests. Named behaviors describe assertions, not mere file existence. The existing App.test harness is deliberately not counted as route proof.
|
||||
|
||||
New browser tests live in [redesign-preservation.spec.ts](../../../job-tracker-ui/e2e/redesign-preservation.spec.ts); original [smoke](../../../job-tracker-ui/e2e/smoke.spec.ts), [account isolation](../../../job-tracker-ui/e2e/account-isolation.spec.ts), and special B01–B06 suites stay unchanged. The [full original test census](../audit/TEST_FILES.md) supplies secondary coverage beyond the representative tests mapped below.
|
||||
|
||||
| Preservation ID / capability | Route / entry | Frontend; backend owners | Automated tests and protected behavior | Missing evidence / deliberate gap | Risk | Expected redesign touch |
|
||||
|---|---|---|---|---|---|---|
|
||||
| **PRES-E01** — Public product/plans; honest unavailable billing | `/` | LandingPage, planCatalog; AccountPlans | [F:landing-page][F:landing-page.test.tsx], [F:planCatalog][F:planCatalog.test.ts], [F:LandingPage.authRedirect][F:LandingPage.authRedirect.test.tsx]; [B:AccountPlans][B:AccountPlansTests]. Free/Pro copy, unavailable checkout and authenticated redirect | Actual checkout/provider availability; public mobile/keyboard review | M | Yes — public entry |
|
||||
| **PRES-E02** — Local login/register/remember, challenge, reset and verification | `/login; /register; /forgot-password; /reset-password; /verify-email` | LoginPage, TurnstileWidget, VerifyEmailPage; AuthController | [F:login-page][F:login-page.test.tsx], [F:verify-email-page][F:verify-email-page.test.tsx], [F:turnstile-widget][F:turnstile-widget.test.tsx]; [B:LocalAuthIdentity][B:LocalAuthIdentityTests], [B:AuthAndSystemController][B:AuthAndSystemControllerTests]. Configuration gates, two-factor branch, safe redirect, verification outcomes; smoke login | Real reset-email delivery, Turnstile widget/browser configuration, complete reset submit journey | H | Yes — entry forms |
|
||||
| **PRES-E03** — Google/Microsoft login, link, legacy relink and popup results | `/login; /microsoft-legacy-relink; provider callbacks` | GoogleAuthCard, MicrosoftAuthCard, MicrosoftLegacyRelinkPage; token validators | [F:login-page][F:login-page.test.tsx], [F:microsoft-legacy-relink-page][F:microsoft-legacy-relink-page.test.tsx]; [B:GoogleTokenValidator][B:GoogleTokenValidatorTests], [B:MicrosoftTokenValidator][B:MicrosoftTokenValidatorTests], [B:LocalAuthIdentity][B:LocalAuthIdentityTests]. Provider failure/relink presentation and token validation | Real consent/popups, revoked grants, provider sandbox; callbacks must retain origin/state | H | Yes — account entry |
|
||||
| **PRES-E04** — 2FA challenge/recovery/setup/disable; trusted-device revocation | `/profile; /login` | TwoFactorSettingsCard, TwoFactorChallenge; TwoFactorController | [F:two-factor-settings][F:two-factor-settings.test.tsx], [F:login-page][F:login-page.test.tsx]; [B:TwoFactorController][B:TwoFactorControllerTests], [B:SessionsController][B:SessionsControllerTests]. Setup/recovery presentation, disable failure/retry; server trusted-device/session checks | Real mobile authenticator/recovery and trusted-device UI traversal | H | Yes — security forms |
|
||||
| **PRES-E05** — Session list/current device, revoke one/others, logout | `/profile; shell user menu` | SessionsSettingsCard, AppShell, auth; SessionsController | [F:session-boundary][F:session-boundary.test.ts], [F:account-scoped-state][F:account-scoped-state.test.tsx]; [B:SessionsController][B:SessionsControllerTests], [B:AuthSessionRevocation][B:AuthSessionRevocationTests]. Own active sessions only, revoke ownership and next-request invalidation; B02 browser logout | Session-list card UI selection/refresh after remote revoke | H | Yes — shell/account |
|
||||
| **PRES-E06** — Avatar, account names/headline, email change and password | `/profile; /confirm-email-change` | ProfilePage; AuthController, AvatarStorage | [F:profile-page][F:profile-page.test.tsx], [F:verify-email-page][F:verify-email-page.test.tsx]; [B:AvatarStorage][B:AvatarStorageTests], [B:LocalAuthIdentity][B:LocalAuthIdentityTests]. Email change password/confirmation contract; safe avatar storage and identity endpoints | Avatar upload/remove and password-change browser journeys | H | Yes — account/profile separation |
|
||||
| **PRES-E07** — Desktop collapse/mobile drawer, active nav and command search/actions | `Shell; Ctrl/Cmd+K` | AppShell, QuickCommandDialog; job/company search APIs | [F:sidebar-active-nav][F:sidebar-active-nav.test.ts], [F:app-shell-notifications][F:app-shell-notifications.test.tsx], [F:quick-command-preservation][F:quick-command-preservation.test.tsx]; [B:JobApplicationsAuthorization][B:JobApplicationsAuthorizationTests], [B:CompanyRelationship][B:CompanyRelationshipTests]. Active matching and notification entry; NEW job/company destinations, reviewed add and search reset; NEW browser keyboard navigation | Drawer focus return and collapse persistence in EN/NB; all command destinations before shell migration | H | Yes — first restructuring target |
|
||||
| **PRES-E08** — Theme/system, EN/NB hydration, toast/confirm/prompt and errors | `Shell; preferences; fallback routes` | themePrefs, I18nProvider, confirm, prompt; auth preference | [F:theme-state][F:theme-state.test.tsx], [F:i18n-provider][F:i18n-provider.test.tsx], [F:confirm][F:confirm.test.tsx], [F:prompt][F:prompt.test.tsx], [F:dialog-flow][F:dialog-flow.test.tsx]; [B:LocalAuthIdentity][B:LocalAuthIdentityTests]. Hydrated theme/language, confirmation/cancellation and dialog behavior | Not-found/error-boundary retry route; full locale/theme/state cross-product and OS theme changes | M | Yes — shared foundations |
|
||||
| **PRES-E09** — Account-switch private-state disposal and stale request rejection | `All private routes; tabs/BFCache/focus` | App, api, sessionBoundary; SessionContextMiddleware | [F:session-boundary][F:session-boundary.test.ts], [F:account-scoped-state][F:account-scoped-state.test.tsx]; [B:SessionContextMiddleware][B:SessionContextMiddlewareTests], [B:CurrentUserIdLiveEvaluation][B:CurrentUserIdLiveEvaluationTests], [B:OwnershipGuard][B:OwnershipGuardTests]. B02 stale mutation/read abort, account-scoped state; four real-API/two-tab account-isolation browser cases | Real BFCache variations and long offline retry; preserve server precondition even with old bundles | H | Yes — all shared state |
|
||||
| **PRES-J01** — NAV discovery/filter/sort/source/deadline/details | `/discover` | JobDiscoveryPage; JobDiscoveryController | [F:job-discovery][F:job-discovery.test.tsx]; [B:JobDiscoveryController][B:JobDiscoveryControllerTests], [B:JobOpportunitySync][B:JobOpportunitySyncTests]. Search/sort/source details, empty versus failure/retry; responsive discovery browser | Live NAV freshness/API availability; long result keyboard traversal | M | Yes — jobs entry |
|
||||
| **PRES-J02** — Saved searches, run/delete/alerts, result changes/dismissal | `/discover` | JobDiscoveryPage; saved-search/alert services | [F:job-discovery][F:job-discovery.test.tsx]; [B:SavedJobSearchAlert][B:SavedJobSearchAlertTests], [B:JobDiscoveryController][B:JobDiscoveryControllerTests]. Saved search run, pause and dismiss controls; owner-scoped alert results | Scheduler-to-notification browser journey, delete confirmation on mobile | M | Yes — discovery workspace |
|
||||
| **PRES-J03** — Reviewed manual/URL/paste job create, company/tags/dates, duplicates | `Shell AddJobModal` | AddJobModal; JobImportService, JobApplicationsController | [F:quick-capture][F:quick-capture.test.tsx]; [B:JobImportService][B:JobImportServiceTests], [B:JobImportHttpTransport][B:JobImportHttpTransportTests], [B:JobApplicationsController][B:JobApplicationsControllerTests]. Preview before write, URL prefills, manual browser create; B01 real connection boundary | Duplicate warning and company/date edge UI; creation-intent idempotency remains separate unresolved contract | H | Yes — capture/navigation |
|
||||
| **PRES-J04** — Extension/bookmarklet/share capture; post-create attachment/CV | `?add= / ?addtext=; QuickCaptureCard; browser-extension` | App, captureUrl, QuickCaptureCard; job/CV APIs | [F:captureUrl][F:captureUrl.test.ts], [F:quick-capture][F:quick-capture.test.tsx], [F:quick-command-preservation][F:quick-command-preservation.test.tsx]; [B:JobImportService][B:JobImportServiceTests], [B:CvBuilder][B:CvBuilderTests]. Capture URL encoding/review/no auto-create, post-create CV; extension node tests; NEW keyboard add opens review only | Installed extension permissions/browser-share flow and optional attachment failure recovery | H | Yes — entry contracts |
|
||||
| **PRES-J05** — Job filters/sort/page/columns, saved views and row/mobile actions | `/jobs` | JobTable, SavedViewsMenu; JobApplicationsController | [F:application-workspace-overlay][F:application-workspace-overlay.test.tsx], [F:account-scoped-state][F:account-scoped-state.test.tsx], [F:daily-control-loop][F:daily-control-loop.test.tsx]; [B:JobApplicationsController][B:JobApplicationsControllerTests], [B:JobApplicationsEndpointBehavior][B:JobApplicationsEndpointBehaviorTests]. URL hydration, selection does not open row, list return/focus; browser dirty/full workspace | Saved-view CRUD browser journey, page-size changes with dirty nested state | H | Yes — jobs view consolidation |
|
||||
| **PRES-J06** — Bulk status/delete, soft delete/trash/restore and job edit | `/jobs; /trash; EditJobDialog` | JobTable, EditJobDialog; job lifecycle/controller | [F:application-workspace-overlay][F:application-workspace-overlay.test.tsx], [F:confirm-flow][F:confirm-flow.test.tsx]; [B:JobApplicationsController][B:JobApplicationsControllerTests], [B:JobApplicationsAuthorization][B:JobApplicationsAuthorizationTests], [B:JobLifecycleEvent][B:JobLifecycleEventTests]. Selection and destructive confirmation primitive; server CRUD/ownership/lifecycle behavior | Bulk partial failure and trash→restore UI journey; shared confirm tests alone do not prove bulk flow | H | Yes — table/actions |
|
||||
| **PRES-J07** — Kanban grouped statuses, pointer/keyboard move and precise menu | `/kanban` | KanbanBoard, pipeline; status endpoint | [F:kanban-grouped-board][F:kanban-grouped-board.test.tsx], [F:pipeline][F:pipeline.test.ts]; [B:JobPipeline][B:JobPipelineTests]. Keyboard pickup/drop, failed move keeps card, custom group and precise stages; dark browser test | Touch drag, keyboard cancel/focus after responsive view switching | H | Yes — table/board unified entry |
|
||||
| **PRES-J08** — Company/contact edits, pipeline dates and linked-job history | `/companies; ?edit=id` | CompaniesTable; company relationship APIs | [F:companies-relationship][F:companies-relationship.test.tsx], [F:quick-command-preservation][F:quick-command-preservation.test.tsx]; [B:CompanyRelationship][B:CompanyRelationshipTests]. Job history/context and NEW command company destination | All contact/date edits persisted through browser; source URL safety | M | Yes — directory/navigation |
|
||||
| **PRES-J09** — Recorded offers/compensation/deadlines/notes and source links | `/offers` | OfferComparisonPage; job API | [F:offer-comparison][F:offer-comparison.test.tsx]; [B:AnalyticsSalary][B:AnalyticsSalaryTests], [B:JobApplicationsEndpointBehavior][B:JobApplicationsEndpointBehaviorTests]. Recorded values/empty states and salary normalization | Mobile many-offer comparison; localized currency/date visual review | M | Yes — jobs/analytics placement |
|
||||
| **PRES-J10** — CSV/JSON export, JSON import and CSV-to-JSON conversion | `/settings; ImportExportJobs` | ImportExportJobs; CSV/job controller | [F:csv-export][F:csv-export.test.tsx]; [B:CsvExport][B:CsvExportTests], [B:JobApplicationsController][B:JobApplicationsControllerTests]. B05 formula/quotes/UTF-8/typed dates and numeric behavior; CSV browser byte download | Full browser JSON import/count/roundtrip; Excel/LibreOffice compatibility remains separately bounded | H | Yes — settings/download placement |
|
||||
| **PRES-W01** — Full workspace, sections, legacy links and optional reusable overlay | `/jobs/:id; /applications/:id; section/followMode` | ApplicationWorkspacePage, jobWorkspaceRoute; workspace API | [F:application-workspace-overlay][F:application-workspace-overlay.test.tsx], [F:applicationWorkspace][F:applicationWorkspace.test.ts], [F:application-route-contracts][F:application-route-contracts.test.ts]; [B:ApplicationWorkspace][B:ApplicationWorkspaceTests]. Deep links, dirty cancel/discard, missing job and back/filter/focus; NEW actual App command/legacy entry | No active production caller of override/onClose overlay props found; full overlay parity needs a host fixture before adoption | H | Yes — primary workflow |
|
||||
| **PRES-W02** — Detail/status/progression, recruiter/dates/description/timeline | `/jobs/:id overview` | WorkspaceHeader, JobDetailsSection, ApplicationProgress/Timeline; workflow APIs | [F:application-intelligence][F:application-intelligence.test.tsx], [F:workflow-trust-signals][F:workflow-trust-signals.test.tsx], [F:daily-control-loop][F:daily-control-loop.test.tsx]; [B:JobApplicationsWorkflowSignals][B:JobApplicationsWorkflowSignalsTests], [B:JobApplicationsAppliedDateHistory][B:JobApplicationsAppliedDateHistoryTests]. Timeline source labels, next-action routing and applied-date history | Edit/status→timeline real-browser sequence across locale/timezone | H | Yes — workspace |
|
||||
| **PRES-W03** — Analysis, match/gaps, strategy and reviewed status/workflow suggestions | `/jobs/:id?section=analysis` | ApplicationAnalysis/Match/StrategySnapshot/StatusSuggestion; intelligence/AI services | [F:application-intelligence][F:application-intelligence.test.tsx], [F:application-workflow-assist][F:application-workflow-assist.test.tsx]; [B:ApplicationIntelligence][B:ApplicationIntelligenceTests], [B:StrategySnapshotOperation][B:StrategySnapshotOperationTests], [B:JobApplicationsEndpointBehavior][B:JobApplicationsEndpointBehaviorTests]. Loading/error/empty states, match and explicit suggested action behavior | Actual model truthfulness/evaluation; source freshness and cancelled generation browser states | H | Yes — contextual AI |
|
||||
| **PRES-W04** — Checklist create/edit/complete/delete/order and next action | `Job overview/checklist entry` | ApplicationChecklist; checklist service/controller | [F:application-checklist][F:application-checklist.test.tsx]; [B:ApplicationChecklist][B:ApplicationChecklistTests]. Persisted item actions and keyboard reorder; localized readiness | Real-API mobile edit/delete and interruption recovery | H | Yes — workspace detail |
|
||||
| **PRES-W05** — Attach/select/create/tailor CV and source context | `Job CV section; builder deep link` | ApplicationCvSection/TailoringSection; ApplicationAssetsService | [F:application-assets][F:application-assets.test.tsx], [F:cv-builder-deep-link][F:cv-builder-deep-link.test.tsx]; [B:ApplicationAssets][B:ApplicationAssetsTests], [B:CvBuilder][B:CvBuilderTests]. Attach re-points only, detach keeps variant, foreign owner denied, tailoring never writes source; B02 API browser | P2-D01 SQLite tailoring ordering blocks successful real-browser guidance; actual attach→render relationship beyond existing service coverage | H | Yes — document relationships |
|
||||
| **PRES-W06** — Cover-letter manual/AI review, versions/restore and dirty protection | `Job cover-letter section` | ApplicationCoverLetterSection, CoverLetterAiAssistant; assets/history | [F:application-assets][F:application-assets.test.tsx], [F:application-workspace-overlay][F:application-workspace-overlay.test.tsx]; [B:ApplicationAssets][B:ApplicationAssetsTests]. Manual save/discard/restore, linked-CV AI explicit apply then save, version history owner scope | Whole-browser AI reject/provider success and cover-letter history persistence; no actual model called here | H | Yes — workspace/content editing |
|
||||
| **PRES-W07** — Application drafts and immutable submitted package/archived binaries | `Job cover-letter drafts; CV section package history` | ApplicationPackageDraftsSection, SubmittedPackagesSection; package service/storage | [F:application-assets][F:application-assets.test.tsx], [F:application-drafts][F:application-drafts.test.ts]; [B:SubmittedApplicationPackage][B:SubmittedApplicationPackageTests], [B:JobApplicationsApplicationPackage][B:JobApplicationsApplicationPackageTests], [B:JobApplicationsEndpointBehavior][B:JobApplicationsEndpointBehaviorTests]. Drafts keep ordinary notes; service frozen document/hash/copy-failure/tenant assertions; NEW native browser capture oracle | P2-D01 expected failure: SQLite capture 500. NEW post-capture mutation/archive assertions are NOT reached. Require real storage/provider success before package restructuring | H | Yes — documents/submission |
|
||||
| **PRES-W08** — Attachments upload/category/name/AI consent/preview/download/delete | `Job overview Documents accordion; package archive` | Attachments; AttachmentsController, AttachmentStorage | [F:attachments][F:attachments.test.tsx]; [B:AttachmentsController][B:AttachmentsControllerTests], [B:AttachmentConsistency][B:AttachmentConsistencyTests], [B:AttachmentFlagsRecompute][B:AttachmentFlagsRecomputeTests]. Empty/flag controls, storage consistency and owned download; NEW browser fixture uploads real UTF-8 binary | Archived bytes after live delete blocked by W07; drag/drop, rename and download browser journey | H | Yes — document surfaces |
|
||||
| **PRES-W09** — Interview questions/answers/ready/manual removal/AI review | `Job interview section` | InterviewPrep; interview persistence/AI endpoints | [F:interview-prep][F:interview-prep.test.tsx]; [B:InterviewPrepPersistence][B:InterviewPrepPersistenceTests], [B:InterviewAiContext][B:InterviewAiContextTests], [B:InterviewPrep][B:InterviewPrepTests]. Explicit AI add, manual/answer readiness, relevant prompt context without invented defaults | Populated mobile keyboard editing and real reload; actual model quality separate | H | Yes — workspace AI |
|
||||
| **PRES-W10** — Debrief/follow-up draft/date/readiness | `Job interview section; follow-up entry` | InterviewPrep; follow-up draft services | [F:interview-prep][F:interview-prep.test.tsx]; [B:JobApplicationsFollowUpDraft][B:JobApplicationsFollowUpDraftTests], [B:InterviewPrep][B:InterviewPrepTests]. Neutral debrief template and follow-up content handling | Timezone/reload browser chain; calendar defects remain outside Phase 2 | M | Yes — workspace workflow |
|
||||
| **PRES-W11** — Reminder rules/actions/notifications, ICS and Google/Outlook events | `/reminders; job calendar actions; /settings` | RemindersView, RulesSettingsCard, ApplicationCalendarActions; calendar/rules APIs | [F:daily-control-loop][F:daily-control-loop.test.tsx], [F:application-calendar][F:application-calendar.test.tsx], [F:settings-view][F:settings-view.test.tsx]; [B:ApplicationCalendarController][B:ApplicationCalendarControllerTests], [B:RulesEngineProspect][B:RulesEngineProspectTests], [B:NotificationSettingsController][B:NotificationSettingsControllerTests]. Provider write confirmation, ICS and permission; NEW per-scenario rule payload and real saved preference checks | DST/date-only correctness and real provider timezone must be resolved separately; initial rules 500 not reproduced | H | Yes — scheduling/navigation |
|
||||
| **PRES-W12** — Async progress/failure/retry/cancel/notifications | `/operations; profile processing` | OperationsPage, CareerProfilePage; operation store/worker | [F:operations-page][F:operations-page.test.tsx], [F:profile-page][F:profile-page.test.tsx]; [B:OperationsController][B:OperationsControllerTests], [B:UserOperationStore][B:UserOperationStoreTests], [B:CvProcessingOperation][B:CvProcessingOperationTests]. Failure retry/cancel, durable operation IDs; extraction polling keeps dirty fields | Long-lived disconnect/reconnect and worker fault/recovery operational gaps | H | Yes — notifications/import |
|
||||
| **PRES-C01** — Canonical structured career data and long-tail/custom sections | `/career?section=profile` | CareerProfilePage, career views; CareerProfileService/controller | [F:profile-page][F:profile-page.test.tsx]; [B:CareerProfileController][B:CareerProfileControllerTests], [B:CareerProfileService][B:CareerProfileServiceTests]. Structured field changes propagate to saved singleton, stable child IDs and relational roundtrip; NEW CV edit leaves master unchanged | Every long-tail section/mobile keyboard save; concurrent canonical saves outside scope | H | Yes — career workflow |
|
||||
| **PRES-C02** — Profile overview/completeness/import guidance/raw copy/history/restore | `/career` | CareerWorkspacePage, CareerProfilePage; version service | [F:career-workspace-page][F:career-workspace-page.test.tsx], [F:profile-page][F:profile-page.test.tsx]; [B:CareerProfileService][B:CareerProfileServiceTests]. First/returning state, completeness/errors and non-destructive history restore | Clipboard/avatar and full browser historical restore; real extraction quality separate | M | Yes — career entry |
|
||||
| **PRES-C03** — Upload/extract/rebuild/reprocess/diff/select/accept/discard/retry/cancel | `Career import/review` | CareerProfilePage; profile-CV/processing/diff services | [F:profile-page][F:profile-page.test.tsx]; [B:ProfileCvController][B:ProfileCvControllerTests], [B:CvProfileDiffService][B:CvProfileDiffServiceTests], [B:CvProcessingOperation][B:CvProcessingOperationTests], [B:CvExtractionCoverage][B:CvExtractionCoverageTests]. Queue/upload, operation retry, pending explicit apply and polling retains edits; extraction/diff service checks | Manual low-confidence deselection/discard UI; OCR/scanned/unusual real-document quality | H | Yes — career import |
|
||||
| **PRES-C04** — Evidence bank source/category/tags/add/list/delete | `Career overview/evidence` | CareerEvidenceBank; CareerEvidenceController | [F:career-evidence][F:career-evidence.test.tsx]; [B:CareerEvidenceController][B:CareerEvidenceControllerTests]. Sourced evidence creation, tags and owner boundaries | Delete and many-item search/keyboard browser flow | M | Yes — canonical evidence |
|
||||
| **PRES-C05** — CV library create/copy/delete/templates/share/publication/PDF | `/career/builder` | CvBuilderPage, thumbnails; CvVariantService/PublicCvController | [F:cv-builder-page][F:cv-builder-page.test.tsx], [F:cv-template-thumbnail][F:cv-template-thumbnail.test.tsx]; [B:CvBuilder][B:CvBuilderTests], [B:PublicCvController][B:PublicCvControllerTests]. General/job-specific creation, delete cancel, duplication fresh slug, public revoke/republish; existing public browser PDF | Complete library→publish/unpublish/revoke old link browser chain | H | Yes — CV workspace |
|
||||
| **PRES-C06** — Studio tabs, preview debounce/pages/fit/save state/mobile panes | `/career/builder/:id` | CvBuilderEditor, CvPreviewPane; preview API | [F:cv-builder-deep-link][F:cv-builder-deep-link.test.tsx]; [B:CvBuilder][B:CvBuilderTests]. Five tabs, deep-link/error/save state and preview retry; NEW real mobile Edit/Preview, reload and desktop split | Keyboard preview scroll/focus (known F15), all theme/page interactions | H | Yes — representative editor |
|
||||
| **PRES-C07** — Visibility/order/custom sections and item/document overrides | `Builder Content` | CvBuilderEditor, CV controls/useDragReorder; resolver/settings | [F:cv-builder-deep-link][F:cv-builder-deep-link.test.tsx], [F:cvBuilder][F:cvBuilder.test.ts]; [B:CvBuilder][B:CvBuilderTests]. Add/edit/delete/order/hide, override renders without master change; NEW persisted headline and sibling isolation | Native drag/touch and override reset-to-source browser flow; item-level real saved overrides beyond headline | H | Yes — editing controls |
|
||||
| **PRES-C08** — Capability-aware templates/typography/color/header/skills/sidebar/photo | `Builder Template/Customize` | CvCustomizeTab, registry; ThemedCvRenderer | [F:cv-builder-deep-link][F:cv-builder-deep-link.test.tsx], [F:cvBuilder][F:cvBuilder.test.ts], [F:cv-template-thumbnail][F:cv-template-thumbnail.test.tsx]; [B:CvBuilder][B:CvBuilderTests]. Template change clears incompatible defaults; normalized overrides reach shared renderer, safe color/markdown | Complete template×advanced control visual/render parity; inaccessible color controls baseline | M | Yes — component foundation |
|
||||
| **PRES-C09** — A4/Letter/density/margins/dates/document locale/section presentation | `Builder Customize/Content` | cvBuilder, CvCustomizeTab; settings normalization/render model | [F:cvBuilder][F:cvBuilder.test.ts], [F:cv-builder-deep-link][F:cv-builder-deep-link.test.tsx]; [B:CvBuilder][B:CvBuilderTests]. Page sizes, NB controls/default labels/dates, section grid/columns; existing long searchable PDF browser | All advanced combinations, print margins and long localized text manual review | M | Yes — controls must remain |
|
||||
| **PRES-C10** — Undo/redo, save queue, dirty guard, history restore/preview lifecycle | `Builder editor/history` | CvBuilderEditor; CvVariantService version history | [F:cv-builder-deep-link][F:cv-builder-deep-link.test.tsx]; [B:CvBuilder][B:CvBuilderTests]. Undo/redo, failed save retry, guard cancel, restore-as-new-version; NEW saved override reload | Multi-browser stale-CV revision remains separate defect; history-tab browser restore and offline navigation | H | Yes — do not alter persistence contract |
|
||||
| **PRES-C11** — Section and whole-document AI improve/translate/instruction/grammar review | `Builder AI Tools/content assistant` | AiSectionAssistant, DocumentAiTab; CV AI assist | [F:ai-section-assistant][F:ai-section-assistant.test.tsx], [F:cv-builder-deep-link][F:cv-builder-deep-link.test.tsx]; [B:AiWorkspace][B:AiWorkspaceTests], [B:AiPrivacyPolicy][B:AiPrivacyPolicyTests], [B:ProEntitlementAuthorization][B:ProEntitlementAuthorizationTests]. NEW reject/close, edited acceptance, failure retry, whole-document apply/reject and malformed/missing/duplicate IDs rejected; no implicit PUT | Real model factual validation and section→whole review interactions; Free-disabled controls proven in new browser | H | Yes — AI interaction pattern |
|
||||
| **PRES-C12** — Private PDF and published anonymous CV/PDF, long searchable pages | `Builder export; /cv/:slug` | PublicCvPage, cvBuilder; HtmlPdfGenerator/shared admission | [F:public-cv-page][F:public-cv-page.test.tsx]; [B:PublicCvController][B:PublicCvControllerTests], [B:PdfRenderAdmission][B:PdfRenderAdmissionTests], [B:CvBuilder][B:CvBuilderTests]. Anonymous public rendering versus private refusal; B03 shared capacity/failure; real multipage/public PDF browser | All templates/print engines; deployed Chromium/font behavior and PDF manual inspection | H | Yes — export/preview links |
|
||||
| **PRES-M01** — Job-scoped manual correspondence/search/delete/paste import | `Workspace communication accordion` | Correspondence; CorrespondenceController | [F:end-to-end-trust-loop][F:end-to-end-trust-loop.test.tsx], [F:correspondence-search-context][F:correspondence-search-context.test.ts]; [B:CorrespondenceController][B:CorrespondenceControllerTests]. Canonical communication entry does not send mail, job context and CRUD ownership | Manual note edit/delete/paste-import real browser; preserve send/log distinction | H | Yes — workspace/correspondence |
|
||||
| **PRES-M02** — Gmail candidate/thread import/refresh/sync/unlink/relink/move | `Job communication Gmail tools` | Correspondence; GmailController/provider | [F:correspondence-gmail-import][F:correspondence-gmail-import.test.tsx]; [B:GmailController][B:GmailControllerTests], [B:EmailOwnershipIntegration][B:EmailOwnershipIntegrationTests]. Candidates, selected thread import/move/refresh and diagnostics; owner-bound relationships | Real provider/sandbox threads, multi-account reconnect and sync failure | H | Yes — relationship controls |
|
||||
| **PRES-M03** — Global inbox filters/page/provider/link/direction/detail/source links | `/correspondence` | CorrespondenceInboxPage; EmailController | [F:correspondence-inbox-page][F:correspondence-inbox-page.test.tsx]; [B:EmailController][B:EmailControllerTests], [B:EmailOwnershipIntegration][B:EmailOwnershipIntegrationTests]. Filter/pagination/detail/provider read failure distinct from empty | Real large mailbox and attachment binary browser flows | H | Yes — inbox/navigation |
|
||||
| **PRES-M04** — Compose/reply/provider/recipient/job; draft conflicts; confirm send/uncertainty | `/correspondence drafts/compose` | CorrespondenceInboxPage; EmailDrafts/Send/attempt store | [F:correspondence-inbox-page][F:correspondence-inbox-page.test.tsx]; [B:EmailDraftsController][B:EmailDraftsControllerTests], [B:EmailDraftPersistence][B:EmailDraftPersistenceTests], [B:EmailSendController][B:EmailSendControllerTests], [B:EmailSendAttemptStore][B:EmailSendAttemptStoreTests], [B:EmailProviderDelivery][B:EmailProviderDeliveryTests]. Revision conflicts, explicit send confirm, uncertain state has no automatic retry, deliberate new attempt | Actual sandbox mail delivery and browser reconnect; do not loosen idempotency/conflict guards | H | Yes — high-risk drafts/navigation |
|
||||
| **PRES-M05** — Review proposed jobs/link/reject/keep/notes/sync/create | `/correspondence?view=review; /correspondence/review` | GmailReviewPage, CorrespondenceInboxPage; Gmail review | [F:gmail-review-page][F:gmail-review-page.test.tsx], [F:correspondence-inbox-page][F:correspondence-inbox-page.test.tsx]; [B:GmailController][B:GmailControllerTests]. Review decisions/localization and inbox review entry | Real App legacy alias and suggested-job create browser workflow | H | Yes — merged review entry |
|
||||
| **PRES-M06** — Gmail/Graph/IMAP connect/status/disconnect separate from sign-in | `/settings/connected-accounts` | EmailProviderConnections; Google/Graph/IMAP controllers | [F:email-provider-connections][F:email-provider-connections.test.tsx]; [B:MicrosoftGraphController][B:MicrosoftGraphControllerTests], [B:ImapController][B:ImapControllerTests], [B:ImapServiceSsrfGuard][B:ImapServiceSsrfGuardTests], [B:GmailController][B:GmailControllerTests]. Connection state/NB/IMAP form and provider ownership; outbound IMAP safety | Actual provider authorization/revocation/token expiry; no credentials contacted in new tests | H | Yes — settings/account |
|
||||
| **PRES-S01** — Personal metrics/activity/funnel/company/skills/stage/salary/next actions | `/dashboard` | DashboardView; analytics/controllers | [F:daily-control-loop][F:daily-control-loop.test.tsx], [F:workflow-trust-signals][F:workflow-trust-signals.test.tsx]; [B:StageAnalytics][B:StageAnalyticsTests], [B:AnalyticsSalary][B:AnalyticsSalaryTests]. Empty dashboard prioritizes onboarding, attention deep links; server analytics facts | Chart transformation fidelity and full populated/timezone browser dashboard | M | Yes — dashboard/analytics |
|
||||
| **PRES-S02** — Owner-local five visibility preferences/onboarding dismiss/notifications | `/dashboard; notification bell` | DashboardView, OnboardingChecklist, NotificationsPopover; notification APIs | [F:onboarding-checklist][F:onboarding-checklist.test.tsx], [F:notifications-popover][F:notifications-popover.test.tsx], [F:app-shell-notifications][F:app-shell-notifications.test.tsx], [F:account-scoped-state][F:account-scoped-state.test.tsx]; [B:JobLifecycleEvent][B:JobLifecycleEventTests]. Onboarding actions; unread/read/dismiss UI and owner-scoped storage primitive | Five dashboard toggles/reload per account not fully tested at mounted dashboard; ordering/resizing is new work | M | Yes — preserve current preferences |
|
||||
| **PRES-S03** — Rules/reminder/table/theme/language preferences and account links | `/settings tabs/mobile selector` | SettingsView, RulesSettingsCard, theme/i18n; rules/notification/auth | [F:settings-view][F:settings-view.test.tsx], [F:theme-state][F:theme-state.test.tsx], [F:i18n-provider][F:i18n-provider.test.tsx]; [B:NotificationSettingsController][B:NotificationSettingsControllerTests], [B:RulesEngineProspect][B:RulesEngineProspectTests]. NEW failed notification retry, rule scenario preservation; NEW mobile→desktop edit + native notification/rule persistence | Initial single rules 500 unconfirmed; unsaved rule drafts are not persisted across tab unmount; table preference/router identity change gap | H | Yes — shared preferences/navigation |
|
||||
| **PRES-S04** — AI privacy/usage/limits/Free-Pro/server entitlement/conditional billing | `/settings; locked AI controls; plans` | AiPrivacySettingsCard, AiUsageCard, AccountPlanProvider; billing/entitlement | [F:settings-view][F:settings-view.test.tsx], [F:ai-usage-card][F:ai-usage-card.test.tsx], [F:pro-feature-notice][F:pro-feature-notice.test.tsx], [F:planCatalog][F:planCatalog.test.ts]; [B:AiPrivacyPolicy][B:AiPrivacyPolicyTests], [B:AiUsageMeter][B:AiUsageMeterTests], [B:ProEntitlementAuthorization][B:ProEntitlementAuthorizationTests], [B:BillingController][B:BillingControllerTests]. Local-only AI setting, limits/locks, billing readiness; NEW real Free CV AI denied UI and API | Real checkout/webhooks/portal; billing/account deletion issues retain independent queue | H | Yes — settings/AI states |
|
||||
| **PRES-S05** — Account ZIP/encrypted backup/lifecycle/deletion confirmations | `Settings Backup tab` | BackupCard; export/backup/deletion services | [F:account-data-export][F:account-data-export.test.tsx]; [B:AccountDataExport][B:AccountDataExportTests], [B:BackupController][B:BackupControllerTests], [B:AccountDeletion][B:AccountDeletionTests], [B:DatabaseBackupRunner][B:DatabaseBackupRunnerTests]. ZIP protected download, recent auth, server enable flag and exact deletion phrase; service deletion/backup | Real restored account/database and paid deletion/provider cancellation operational proof; not safe to run against production | H | Yes — account lifecycle |
|
||||
| **PRES-A01** — Admin user/create/roles/reset/delete/last-admin/self guards | `/admin/users` | AdminUsersPage; UsersController | [F:admin-users-page][F:admin-users-page.test.tsx]; [B:UsersController][B:UsersControllerTests], [B:ApiAuthorizationPosture][B:ApiAuthorizationPostureTests]. Demotion/delete confirm, last-admin server checks; NEW non-admin crafted create/read denied | Role revocation during mounted admin view and actual reset delivery; route can render empty shell without granting privilege | H | Yes — separate admin area |
|
||||
| **PRES-A02** — Audit list/detail/supported undo/job restore | `/admin/audit` | AdminAuditPage; AdminAuditController | [F:admin-audit-page][F:admin-audit-page.test.tsx]; [B:AuthAndSystemController][B:AuthAndSystemControllerTests], [B:ApiAuthorizationPosture][B:ApiAuthorizationPostureTests]. Unavailable-versus-empty retry and NB stored event text; server admin-only posture | Successful undo and job-restore UI against persisted API; role posture test is not undo behavior proof | H | Yes — admin navigation |
|
||||
| **PRES-A03** — System/build/DB/AI/mail/SMTP/test/probe/benchmark/maintenance | `/admin/system` | AdminSystemPage; AdminSystemController/maintenance/benchmark | [F:admin-system-page][F:admin-system-page.test.tsx]; [B:AuthAndSystemController][B:AuthAndSystemControllerTests], [B:MaintenanceJobDispatcher][B:MaintenanceJobDispatcherTests], [B:CvCorpusHarness][B:CvCorpusHarnessTests]. Health/error/NB, SMTP submit and benchmark controls; dispatcher authorization and modes | Production SMTP/model/corpus/maintenance and sensitive-data presentation; no live jobs launched | H | Yes — admin hierarchy |
|
||||
|
||||
## Before changing a mapped surface
|
||||
|
||||
Run its mapped tests and the shared identity/route regressions. Add the missing behavioral fixture needed by the proposed change before editing that workflow. A gap can remain intentional for Phase 3 documentation; it cannot silently become permission to restructure an unprotected workflow. Keep [traceability](TRACEABILITY.md) updated per preservation ID and require real EN/NB, light/dark, desktop/mobile and keyboard evidence for each later migration.
|
||||
|
||||
[F:landing-page.test.tsx]: ../../../job-tracker-ui/src/landing-page.test.tsx
|
||||
[F:planCatalog.test.ts]: ../../../job-tracker-ui/src/planCatalog.test.ts
|
||||
[F:LandingPage.authRedirect.test.tsx]: ../../../job-tracker-ui/src/LandingPage.authRedirect.test.tsx
|
||||
[B:AccountPlansTests]: ../../../JobTrackerApi.Tests/AccountPlansTests.cs
|
||||
[F:login-page.test.tsx]: ../../../job-tracker-ui/src/login-page.test.tsx
|
||||
[F:verify-email-page.test.tsx]: ../../../job-tracker-ui/src/verify-email-page.test.tsx
|
||||
[F:turnstile-widget.test.tsx]: ../../../job-tracker-ui/src/turnstile-widget.test.tsx
|
||||
[B:LocalAuthIdentityTests]: ../../../JobTrackerApi.Tests/LocalAuthIdentityTests.cs
|
||||
[B:AuthAndSystemControllerTests]: ../../../JobTrackerApi.Tests/AuthAndSystemControllerTests.cs
|
||||
[F:microsoft-legacy-relink-page.test.tsx]: ../../../job-tracker-ui/src/microsoft-legacy-relink-page.test.tsx
|
||||
[B:GoogleTokenValidatorTests]: ../../../JobTrackerApi.Tests/GoogleTokenValidatorTests.cs
|
||||
[B:MicrosoftTokenValidatorTests]: ../../../JobTrackerApi.Tests/MicrosoftTokenValidatorTests.cs
|
||||
[F:two-factor-settings.test.tsx]: ../../../job-tracker-ui/src/two-factor-settings.test.tsx
|
||||
[B:TwoFactorControllerTests]: ../../../JobTrackerApi.Tests/TwoFactorControllerTests.cs
|
||||
[B:SessionsControllerTests]: ../../../JobTrackerApi.Tests/SessionsControllerTests.cs
|
||||
[F:session-boundary.test.ts]: ../../../job-tracker-ui/src/session-boundary.test.ts
|
||||
[F:account-scoped-state.test.tsx]: ../../../job-tracker-ui/src/account-scoped-state.test.tsx
|
||||
[B:AuthSessionRevocationTests]: ../../../JobTrackerApi.Tests/AuthSessionRevocationTests.cs
|
||||
[F:profile-page.test.tsx]: ../../../job-tracker-ui/src/profile-page.test.tsx
|
||||
[B:AvatarStorageTests]: ../../../JobTrackerApi.Tests/AvatarStorageTests.cs
|
||||
[F:sidebar-active-nav.test.ts]: ../../../job-tracker-ui/src/sidebar-active-nav.test.ts
|
||||
[F:app-shell-notifications.test.tsx]: ../../../job-tracker-ui/src/app-shell-notifications.test.tsx
|
||||
[F:quick-command-preservation.test.tsx]: ../../../job-tracker-ui/src/quick-command-preservation.test.tsx
|
||||
[B:JobApplicationsAuthorizationTests]: ../../../JobTrackerApi.Tests/JobApplicationsAuthorizationTests.cs
|
||||
[B:CompanyRelationshipTests]: ../../../JobTrackerApi.Tests/CompanyRelationshipTests.cs
|
||||
[F:theme-state.test.tsx]: ../../../job-tracker-ui/src/theme-state.test.tsx
|
||||
[F:i18n-provider.test.tsx]: ../../../job-tracker-ui/src/i18n-provider.test.tsx
|
||||
[F:confirm.test.tsx]: ../../../job-tracker-ui/src/confirm.test.tsx
|
||||
[F:prompt.test.tsx]: ../../../job-tracker-ui/src/prompt.test.tsx
|
||||
[F:dialog-flow.test.tsx]: ../../../job-tracker-ui/src/dialog-flow.test.tsx
|
||||
[B:SessionContextMiddlewareTests]: ../../../JobTrackerApi.Tests/SessionContextMiddlewareTests.cs
|
||||
[B:CurrentUserIdLiveEvaluationTests]: ../../../JobTrackerApi.Tests/CurrentUserIdLiveEvaluationTests.cs
|
||||
[B:OwnershipGuardTests]: ../../../JobTrackerApi.Tests/OwnershipGuardTests.cs
|
||||
[F:job-discovery.test.tsx]: ../../../job-tracker-ui/src/job-discovery.test.tsx
|
||||
[B:JobDiscoveryControllerTests]: ../../../JobTrackerApi.Tests/JobDiscoveryControllerTests.cs
|
||||
[B:JobOpportunitySyncTests]: ../../../JobTrackerApi.Tests/JobOpportunitySyncTests.cs
|
||||
[B:SavedJobSearchAlertTests]: ../../../JobTrackerApi.Tests/SavedJobSearchAlertTests.cs
|
||||
[F:quick-capture.test.tsx]: ../../../job-tracker-ui/src/quick-capture.test.tsx
|
||||
[B:JobImportServiceTests]: ../../../JobTrackerApi.Tests/JobImportServiceTests.cs
|
||||
[B:JobImportHttpTransportTests]: ../../../JobTrackerApi.Tests/JobImportHttpTransportTests.cs
|
||||
[B:JobApplicationsControllerTests]: ../../../JobTrackerApi.Tests/JobApplicationsControllerTests.cs
|
||||
[F:captureUrl.test.ts]: ../../../job-tracker-ui/src/captureUrl.test.ts
|
||||
[B:CvBuilderTests]: ../../../JobTrackerApi.Tests/CvBuilderTests.cs
|
||||
[F:application-workspace-overlay.test.tsx]: ../../../job-tracker-ui/src/application-workspace-overlay.test.tsx
|
||||
[F:daily-control-loop.test.tsx]: ../../../job-tracker-ui/src/daily-control-loop.test.tsx
|
||||
[B:JobApplicationsEndpointBehaviorTests]: ../../../JobTrackerApi.Tests/JobApplicationsEndpointBehaviorTests.cs
|
||||
[F:confirm-flow.test.tsx]: ../../../job-tracker-ui/src/confirm-flow.test.tsx
|
||||
[B:JobLifecycleEventTests]: ../../../JobTrackerApi.Tests/JobLifecycleEventTests.cs
|
||||
[F:kanban-grouped-board.test.tsx]: ../../../job-tracker-ui/src/kanban-grouped-board.test.tsx
|
||||
[F:pipeline.test.ts]: ../../../job-tracker-ui/src/pipeline.test.ts
|
||||
[B:JobPipelineTests]: ../../../JobTrackerApi.Tests/JobPipelineTests.cs
|
||||
[F:companies-relationship.test.tsx]: ../../../job-tracker-ui/src/companies-relationship.test.tsx
|
||||
[F:offer-comparison.test.tsx]: ../../../job-tracker-ui/src/offer-comparison.test.tsx
|
||||
[B:AnalyticsSalaryTests]: ../../../JobTrackerApi.Tests/AnalyticsSalaryTests.cs
|
||||
[F:csv-export.test.tsx]: ../../../job-tracker-ui/src/csv-export.test.tsx
|
||||
[B:CsvExportTests]: ../../../JobTrackerApi.Tests/CsvExportTests.cs
|
||||
[F:applicationWorkspace.test.ts]: ../../../job-tracker-ui/src/applicationWorkspace.test.ts
|
||||
[F:application-route-contracts.test.ts]: ../../../job-tracker-ui/src/application-route-contracts.test.ts
|
||||
[B:ApplicationWorkspaceTests]: ../../../JobTrackerApi.Tests/ApplicationWorkspaceTests.cs
|
||||
[F:application-intelligence.test.tsx]: ../../../job-tracker-ui/src/application-intelligence.test.tsx
|
||||
[F:workflow-trust-signals.test.tsx]: ../../../job-tracker-ui/src/workflow-trust-signals.test.tsx
|
||||
[B:JobApplicationsWorkflowSignalsTests]: ../../../JobTrackerApi.Tests/JobApplicationsWorkflowSignalsTests.cs
|
||||
[B:JobApplicationsAppliedDateHistoryTests]: ../../../JobTrackerApi.Tests/JobApplicationsAppliedDateHistoryTests.cs
|
||||
[F:application-workflow-assist.test.tsx]: ../../../job-tracker-ui/src/application-workflow-assist.test.tsx
|
||||
[B:ApplicationIntelligenceTests]: ../../../JobTrackerApi.Tests/ApplicationIntelligenceTests.cs
|
||||
[B:StrategySnapshotOperationTests]: ../../../JobTrackerApi.Tests/StrategySnapshotOperationTests.cs
|
||||
[F:application-checklist.test.tsx]: ../../../job-tracker-ui/src/application-checklist.test.tsx
|
||||
[B:ApplicationChecklistTests]: ../../../JobTrackerApi.Tests/ApplicationChecklistTests.cs
|
||||
[F:application-assets.test.tsx]: ../../../job-tracker-ui/src/application-assets.test.tsx
|
||||
[F:cv-builder-deep-link.test.tsx]: ../../../job-tracker-ui/src/cv-builder-deep-link.test.tsx
|
||||
[B:ApplicationAssetsTests]: ../../../JobTrackerApi.Tests/ApplicationAssetsTests.cs
|
||||
[F:application-drafts.test.ts]: ../../../job-tracker-ui/src/application-drafts.test.ts
|
||||
[B:SubmittedApplicationPackageTests]: ../../../JobTrackerApi.Tests/SubmittedApplicationPackageTests.cs
|
||||
[B:JobApplicationsApplicationPackageTests]: ../../../JobTrackerApi.Tests/JobApplicationsApplicationPackageTests.cs
|
||||
[F:attachments.test.tsx]: ../../../job-tracker-ui/src/attachments.test.tsx
|
||||
[B:AttachmentsControllerTests]: ../../../JobTrackerApi.Tests/AttachmentsControllerTests.cs
|
||||
[B:AttachmentConsistencyTests]: ../../../JobTrackerApi.Tests/AttachmentConsistencyTests.cs
|
||||
[B:AttachmentFlagsRecomputeTests]: ../../../JobTrackerApi.Tests/AttachmentFlagsRecomputeTests.cs
|
||||
[F:interview-prep.test.tsx]: ../../../job-tracker-ui/src/interview-prep.test.tsx
|
||||
[B:InterviewPrepPersistenceTests]: ../../../JobTrackerApi.Tests/InterviewPrepPersistenceTests.cs
|
||||
[B:InterviewAiContextTests]: ../../../JobTrackerApi.Tests/InterviewAiContextTests.cs
|
||||
[B:InterviewPrepTests]: ../../../JobTrackerApi.Tests/InterviewPrepTests.cs
|
||||
[B:JobApplicationsFollowUpDraftTests]: ../../../JobTrackerApi.Tests/JobApplicationsFollowUpDraftTests.cs
|
||||
[F:application-calendar.test.tsx]: ../../../job-tracker-ui/src/application-calendar.test.tsx
|
||||
[F:settings-view.test.tsx]: ../../../job-tracker-ui/src/settings-view.test.tsx
|
||||
[B:ApplicationCalendarControllerTests]: ../../../JobTrackerApi.Tests/ApplicationCalendarControllerTests.cs
|
||||
[B:RulesEngineProspectTests]: ../../../JobTrackerApi.Tests/RulesEngineProspectTests.cs
|
||||
[B:NotificationSettingsControllerTests]: ../../../JobTrackerApi.Tests/NotificationSettingsControllerTests.cs
|
||||
[F:operations-page.test.tsx]: ../../../job-tracker-ui/src/operations-page.test.tsx
|
||||
[B:OperationsControllerTests]: ../../../JobTrackerApi.Tests/OperationsControllerTests.cs
|
||||
[B:UserOperationStoreTests]: ../../../JobTrackerApi.Tests/UserOperationStoreTests.cs
|
||||
[B:CvProcessingOperationTests]: ../../../JobTrackerApi.Tests/CvProcessingOperationTests.cs
|
||||
[B:CareerProfileControllerTests]: ../../../JobTrackerApi.Tests/CareerProfileControllerTests.cs
|
||||
[B:CareerProfileServiceTests]: ../../../JobTrackerApi.Tests/CareerProfileServiceTests.cs
|
||||
[F:career-workspace-page.test.tsx]: ../../../job-tracker-ui/src/career-workspace-page.test.tsx
|
||||
[B:ProfileCvControllerTests]: ../../../JobTrackerApi.Tests/ProfileCvControllerTests.cs
|
||||
[B:CvProfileDiffServiceTests]: ../../../JobTrackerApi.Tests/CvProfileDiffServiceTests.cs
|
||||
[B:CvExtractionCoverageTests]: ../../../JobTrackerApi.Tests/CvExtractionCoverageTests.cs
|
||||
[F:career-evidence.test.tsx]: ../../../job-tracker-ui/src/career-evidence.test.tsx
|
||||
[B:CareerEvidenceControllerTests]: ../../../JobTrackerApi.Tests/CareerEvidenceControllerTests.cs
|
||||
[F:cv-builder-page.test.tsx]: ../../../job-tracker-ui/src/cv-builder-page.test.tsx
|
||||
[F:cv-template-thumbnail.test.tsx]: ../../../job-tracker-ui/src/cv-template-thumbnail.test.tsx
|
||||
[B:PublicCvControllerTests]: ../../../JobTrackerApi.Tests/PublicCvControllerTests.cs
|
||||
[F:cvBuilder.test.ts]: ../../../job-tracker-ui/src/cvBuilder.test.ts
|
||||
[F:ai-section-assistant.test.tsx]: ../../../job-tracker-ui/src/ai-section-assistant.test.tsx
|
||||
[B:AiWorkspaceTests]: ../../../JobTrackerApi.Tests/AiWorkspaceTests.cs
|
||||
[B:AiPrivacyPolicyTests]: ../../../JobTrackerApi.Tests/AiPrivacyPolicyTests.cs
|
||||
[B:ProEntitlementAuthorizationTests]: ../../../JobTrackerApi.Tests/ProEntitlementAuthorizationTests.cs
|
||||
[F:public-cv-page.test.tsx]: ../../../job-tracker-ui/src/public-cv-page.test.tsx
|
||||
[B:PdfRenderAdmissionTests]: ../../../JobTrackerApi.Tests/PdfRenderAdmissionTests.cs
|
||||
[F:end-to-end-trust-loop.test.tsx]: ../../../job-tracker-ui/src/end-to-end-trust-loop.test.tsx
|
||||
[F:correspondence-search-context.test.ts]: ../../../job-tracker-ui/src/correspondence-search-context.test.ts
|
||||
[B:CorrespondenceControllerTests]: ../../../JobTrackerApi.Tests/CorrespondenceControllerTests.cs
|
||||
[F:correspondence-gmail-import.test.tsx]: ../../../job-tracker-ui/src/correspondence-gmail-import.test.tsx
|
||||
[B:GmailControllerTests]: ../../../JobTrackerApi.Tests/GmailControllerTests.cs
|
||||
[B:EmailOwnershipIntegrationTests]: ../../../JobTrackerApi.Tests/EmailOwnershipIntegrationTests.cs
|
||||
[F:correspondence-inbox-page.test.tsx]: ../../../job-tracker-ui/src/correspondence-inbox-page.test.tsx
|
||||
[B:EmailControllerTests]: ../../../JobTrackerApi.Tests/EmailControllerTests.cs
|
||||
[B:EmailDraftsControllerTests]: ../../../JobTrackerApi.Tests/EmailDraftsControllerTests.cs
|
||||
[B:EmailDraftPersistenceTests]: ../../../JobTrackerApi.Tests/EmailDraftPersistenceTests.cs
|
||||
[B:EmailSendControllerTests]: ../../../JobTrackerApi.Tests/EmailSendControllerTests.cs
|
||||
[B:EmailSendAttemptStoreTests]: ../../../JobTrackerApi.Tests/EmailSendAttemptStoreTests.cs
|
||||
[B:EmailProviderDeliveryTests]: ../../../JobTrackerApi.Tests/EmailProviderDeliveryTests.cs
|
||||
[F:gmail-review-page.test.tsx]: ../../../job-tracker-ui/src/gmail-review-page.test.tsx
|
||||
[F:email-provider-connections.test.tsx]: ../../../job-tracker-ui/src/email-provider-connections.test.tsx
|
||||
[B:MicrosoftGraphControllerTests]: ../../../JobTrackerApi.Tests/MicrosoftGraphControllerTests.cs
|
||||
[B:ImapControllerTests]: ../../../JobTrackerApi.Tests/ImapControllerTests.cs
|
||||
[B:ImapServiceSsrfGuardTests]: ../../../JobTrackerApi.Tests/ImapServiceSsrfGuardTests.cs
|
||||
[B:StageAnalyticsTests]: ../../../JobTrackerApi.Tests/StageAnalyticsTests.cs
|
||||
[F:onboarding-checklist.test.tsx]: ../../../job-tracker-ui/src/onboarding-checklist.test.tsx
|
||||
[F:notifications-popover.test.tsx]: ../../../job-tracker-ui/src/notifications-popover.test.tsx
|
||||
[F:ai-usage-card.test.tsx]: ../../../job-tracker-ui/src/ai-usage-card.test.tsx
|
||||
[F:pro-feature-notice.test.tsx]: ../../../job-tracker-ui/src/pro-feature-notice.test.tsx
|
||||
[B:AiUsageMeterTests]: ../../../JobTrackerApi.Tests/AiUsageMeterTests.cs
|
||||
[B:BillingControllerTests]: ../../../JobTrackerApi.Tests/BillingControllerTests.cs
|
||||
[F:account-data-export.test.tsx]: ../../../job-tracker-ui/src/account-data-export.test.tsx
|
||||
[B:AccountDataExportTests]: ../../../JobTrackerApi.Tests/AccountDataExportTests.cs
|
||||
[B:BackupControllerTests]: ../../../JobTrackerApi.Tests/BackupControllerTests.cs
|
||||
[B:AccountDeletionTests]: ../../../JobTrackerApi.Tests/AccountDeletionTests.cs
|
||||
[B:DatabaseBackupRunnerTests]: ../../../JobTrackerApi.Tests/DatabaseBackupRunnerTests.cs
|
||||
[F:admin-users-page.test.tsx]: ../../../job-tracker-ui/src/admin-users-page.test.tsx
|
||||
[B:UsersControllerTests]: ../../../JobTrackerApi.Tests/UsersControllerTests.cs
|
||||
[B:ApiAuthorizationPostureTests]: ../../../JobTrackerApi.Tests/ApiAuthorizationPostureTests.cs
|
||||
[F:admin-audit-page.test.tsx]: ../../../job-tracker-ui/src/admin-audit-page.test.tsx
|
||||
[F:admin-system-page.test.tsx]: ../../../job-tracker-ui/src/admin-system-page.test.tsx
|
||||
[B:MaintenanceJobDispatcherTests]: ../../../JobTrackerApi.Tests/MaintenanceJobDispatcherTests.cs
|
||||
[B:CvCorpusHarnessTests]: ../../../JobTrackerApi.Tests/CvCorpusHarnessTests.cs
|
||||
@@ -0,0 +1,107 @@
|
||||
# Phase 2 — Preservation regression checkpoint
|
||||
|
||||
**Completed 2026-09-10 from `2d0f131` on `codex/ui-redesign-phase2`.** This checkpoint adds regression protection and an explicit TypeScript CI gate. **No visual redesign, application behavior change, route/layout/component restructuring, schema change or B07 work.** The Phase 1 source/audit remains intact.
|
||||
|
||||
## Preservation coverage
|
||||
|
||||
- **57/57 protected capability groups mapped (100%)**, with stable `PRES-` aliases of the original E/J/W/C/M/S/A IDs.
|
||||
- **44 high-risk groups**, all mapped to existing behavioral assertions. High risk includes private identity, data edits/history/relationships, irreversible actions and shared navigation likely to be restructured.
|
||||
- **147 referenced existing/new automated test files** across frontend and backend. Each row records route/entry, component/service owners, protected behavior, missing proof and expected later redesign impact.
|
||||
- This percentage means **at least one core automated behavioral assertion per capability group**. It is not exhaustive sub-workflow, code, provider, mobile/accessibility or visual coverage. The matrix explicitly retains partial coverage and one native package-capture blocker.
|
||||
|
||||
Read the [complete preservation matrix](PRESERVATION_MATRIX.md), [traceability contract and Phase 2 change ledger](TRACEABILITY.md), and [baseline defects/intentional gaps](BASELINE_DEFECTS.md).
|
||||
|
||||
## High-risk additions and preserved evidence
|
||||
|
||||
| Workflow | New meaningful regression |
|
||||
|---|---|
|
||||
| Command/navigation/capture | Three component cases: job/company destination, closing clears query, add opens review without creating; real browser Ctrl+K search, legacy match link, reviewed add |
|
||||
| CV section AI | Four cases: Reject/Close clear proposal without applying, edited proposal applies only on acceptance, failure retry leaves content untouched |
|
||||
| Whole-CV AI | Five cases: explicit Apply/Reject; invalid JSON, missing IDs and duplicate IDs cannot become edits; save targets only selected CV |
|
||||
| Cover-letter AI | One case: Reject preserves saved draft, disabled Save/no dirty state/no PUT |
|
||||
| Settings | Two component cases: retry failed notification save with retained draft; changing one rule keeps other scenarios |
|
||||
| Canonical profile → derived CV | Browser mobile edit/preview → reload → desktop preview; stored headline changes only selected CV, complete canonical profile and sibling remain unchanged |
|
||||
| Documents/submission | Native profile/job/CV/letter/file fixture + legacy entry and capture; **expected failure P2-D01**, with desired immutable HTML/letter/archive-byte checks retained but not reached |
|
||||
| Responsive preferences | Browser mobile→desktop rule draft stays, native save persists; notification false survives reload; separate direct API rule save |
|
||||
| Permissions | Non-admin admin-menu entries absent, direct users GET/POST forbidden, normal CV create allowed, Free AI actions disabled and API 403 |
|
||||
|
||||
Existing B01 connection-time outbound protections, B02 ownership/session/cache checks, B03 rendering admission, B04 trusted principal/IP limits, B05 CSV bytes, and B06 application/configuration tests are unchanged. Mail draft revisions/uncertain sends, profile extraction acceptance, CV history, long PDF, job dirty navigation and other existing tests were retained.
|
||||
|
||||
## Baseline and fresh verification
|
||||
|
||||
Fresh local execution uses installed .NET **9.0.317**, Node dependencies already locked in the checkout, Windows/Chromium and Playwright's disposable SQLite API. There were no production requests or provider credentials. Test fixtures use only synthetic accounts and local URLs.
|
||||
|
||||
| Gate | Recorded Phase 1 baseline | Fresh Phase 2 result |
|
||||
|---|---|---|
|
||||
| Backend Release suite | 916 passing | **916 passed, 0 failed, 0 skipped**; no exclusions, count unchanged |
|
||||
| Frontend Jest | 298 tests / 67 suites | **313 passed / 68 suites**; +15 cases, +1 test file; no existing test removed |
|
||||
| General browser | 18 passing | **23 actually passed + 1 expected failure**, 0 unexpected failures/skips/flaky; original 18 still pass, +6 cases |
|
||||
| B04 real HTTP | 9 cases | **9 passed** |
|
||||
| B06 production-mode API/forwarding HTTP | 6 cases | **6 passed**; not nginx/Traefik runtime proof |
|
||||
| B03 PDF burst/admission | 1 case | **1 passed**, multi-route capacity/recovery oracle unchanged |
|
||||
| Compose ingress/configuration | 3 tests/17 hostile mutations | **3 passed** |
|
||||
| Backend build | Release | **Pass, 0 warnings/errors**, warnings-as-errors |
|
||||
| Frontend build | Next production static export | **Pass** |
|
||||
| Standalone type check | Historical TS2571 repaired before Phase 2 | **Pass**; new explicit CI command, unchanged strictness |
|
||||
| New browser-spec TypeScript | Previously no whole-Playwright type gate | **Pass** with temporary Node-aware config for this new spec only |
|
||||
| ESLint | Zero-warning gate | **Pass**, no suppressions added |
|
||||
| Tracked secret scan; diff whitespace | Required | **Pass** |
|
||||
|
||||
Playwright's list reporter says “24 passed” because expected failures count as expected outcomes. Its JSON records **23 `expectedStatus=passed/actual=passed` and one `expectedStatus=failed/actual=failed`**. Do not present this as 24 successful workflows. The failed native package test is not a regression introduced by application changes: there are no such changes in this branch. See P2-D01.
|
||||
|
||||
Intermediate test-authoring corrections: one missing test-fixture brace, Testing Library's unsupported `exact` role options, a Playwright callback named `use` misidentified as a React hook, and package capture's actual CV-tab location. Corrected test code rather than weakening lint/types or changing UI. The temporary isolated TypeScript config initially lacked Node type roots; setting the existing installed Node declarations resolved Buffer. No new dependency installed.
|
||||
|
||||
One initial rules-save 500 did not reproduce in the direct or repeated browser path; retained as observation P2-O01, not asserted as a diagnosed defect. The reproducible SQLite package/tailoring failure is recorded separately. No blanket skipping or gate bypass was added.
|
||||
|
||||
## Exact commands
|
||||
|
||||
From the repository root (PowerShell):
|
||||
|
||||
```powershell
|
||||
$env:DOTNET_ROOT='C:/Users/Connor/.dotnet'
|
||||
& C:/Users/Connor/.dotnet/dotnet.exe test JobTrackerApi.Tests/JobTrackerApi.Tests.csproj --configuration Release --no-restore --logger 'trx;LogFileName=phase2.trx' --results-directory .artifacts/redesign-phase2 -- xUnit.parallelizeTestCollections=false xUnit.maxParallelThreads=1
|
||||
& C:/Users/Connor/.dotnet/dotnet.exe build JobTrackerApi/JobTrackerApi.csproj --configuration Release --no-restore -warnaserror
|
||||
python scripts/test-ingress.py
|
||||
python scripts/supply-chain.py scan-secrets
|
||||
git diff --check
|
||||
```
|
||||
|
||||
From `job-tracker-ui`:
|
||||
|
||||
```powershell
|
||||
npm test -- --watchAll=false --runInBand
|
||||
npm run typecheck
|
||||
npm run lint
|
||||
$env:NEXT_PUBLIC_API_BASE_URL='http://localhost:5302/api'
|
||||
$env:NEXT_TELEMETRY_DISABLED='1'
|
||||
npm run build
|
||||
$env:DOTNET_ROOT='C:/Users/Connor/.dotnet'
|
||||
$env:DOTNET_HOST_PATH='C:/Users/Connor/.dotnet/dotnet.exe'
|
||||
$env:PLAYWRIGHT_STATIC_EXPORT='true'
|
||||
$env:PLAYWRIGHT_PREBUILT_EXPORT='true'
|
||||
$env:PLAYWRIGHT_JSON_OUTPUT_FILE='../.artifacts/phase2-browser-results.json'
|
||||
node node_modules/@playwright/test/cli.js test --reporter=list,json
|
||||
node node_modules/@playwright/test/cli.js test --config=playwright.rate-limits.config.ts
|
||||
node node_modules/@playwright/test/cli.js test --config=playwright.ingress.config.ts
|
||||
node node_modules/@playwright/test/cli.js test --config=playwright.pdf.config.ts
|
||||
```
|
||||
|
||||
Initial focused command: `npm test -- --watchAll=false --runInBand --runTestsByPath src/quick-command-preservation.test.tsx src/ai-section-assistant.test.tsx src/cv-builder-deep-link.test.tsx src/settings-view.test.tsx` → 31 passed. Final full Jest includes the additional cover-letter reject case.
|
||||
|
||||
Final browser rerun after narrowing the known-failure annotation: `node node_modules/@playwright/test/cli.js test e2e/redesign-preservation.spec.ts` → five actual passes and the same one expected HTTP-500 capture failure (23.8s); no unexpected failures. Final lint and isolated browser-spec typing also pass.
|
||||
|
||||
Additional isolated browser-spec typing used `node node_modules/typescript/bin/tsc --project ../.artifacts/phase2-browser-tsconfig.json --noEmit`. Temporary config extends the frontend tsconfig, includes only `../job-tracker-ui/e2e/redesign-preservation.spec.ts`, sets `incremental:false`, `typeRoots:["../job-tracker-ui/node_modules/@types"]`, `types:["node"]`. This is a local check, not a claim that every pre-existing Playwright/config source is covered by the new CI type gate.
|
||||
|
||||
Local logs: `.artifacts/phase2-{backend,jest,build,api-build,typecheck,browser-typecheck,lint,browser,rate-limits,ingress,pdf,compose,secrets}.log`; backend TRX under `.artifacts/redesign-phase2`; browser JSON and sanitized outcome summary under `.artifacts`. Durable conclusions and reproduction remain in these committed documents/tests; raw traces with synthetic session material are not published.
|
||||
|
||||
The matrix was checked against the original ledger: 57 rows, 57 unique IDs, no missing/extra IDs, and all 147 test-reference paths exist. No production source file changed. Full baseline execution was not repeated for documentation-only edits.
|
||||
|
||||
## Remaining gaps and next phase
|
||||
|
||||
**Phase 2 is complete as a regression/planning checkpoint, with its documented baseline failure.** Phase 3 may define UX architecture: grouped sidebar, page/route destination and backwards compatibility, one Jobs table/board entry, canonical Career Profile/derived-document workflows, contextual AI review, and explicit mobile task rules. Keep the existing MUI/Emotion/React Router/API stack while planning. Do not implement those layouts or components in this checkpoint.
|
||||
|
||||
Before a later code migration, close that surface's matrix gaps or establish a deliberately scoped additional fixture. W07 package/tailoring needs native-provider success first; shared shell needs drawer/focus and bulk/saved-view cases; career/mail/admin need their specified interactive and provider proof. Screen-reader/touch/reflow and the full EN/NB × light/dark × desktop/mobile matrix remain visual/UX gates.
|
||||
|
||||
**F04 remains open.** The B06 repository/application changes are complete, but actual deployed proxy peer/listeners, firewall/NAT/IPv6, client IP, DNS/TLS/redirect and restart behavior remain unverified; follow the existing [operator commands](../../audits/2026-09-07/validation/b06-completion.md). Prior main CI run 743 failed in the browser worker and did not deploy; this branch's local success does not repair or certify that runner. Pushing this feature branch is a preservation checkpoint, not a production deployment or a release-readiness claim.
|
||||
|
||||
No Phase 3 implementation, visual redesign, component-library migration, B07 or later security ticket has begun.
|
||||
@@ -0,0 +1,58 @@
|
||||
# Redesign traceability
|
||||
|
||||
Successor planning checkpoint: [Phase 3 impact matrix and architecture](../phase-3/PRESERVATION_IMPACT_MATRIX.md). It maps all 57 contracts to proposed destinations/validation, without changing their requirements or claiming implemented/visual parity. Phase 4 has not started.
|
||||
|
||||
Phase 2 starts at `2d0f131`. The [preservation matrix](PRESERVATION_MATRIX.md) is the stable baseline: **PRES-E01…PRES-A03**, in the original ledger's groups (not one contiguous numeric range). Old references such as C07 remain valid aliases. Subdivide a later workflow with a suffix, for example `PRES-C07.override-reset`; do not reuse an ID or silently change its meaning.
|
||||
|
||||
## Required chain for each later change
|
||||
|
||||
`PRES-ID → requirement/source contract → implementation commit + changed owner → regression test + executed result → visual/UX evidence`
|
||||
|
||||
The requirement for every current PRES-ID is its **complete original row** in [FUNCTIONALITY_INVENTORY.md](../audit/FUNCTIONALITY_INVENTORY.md), including the source contract. A proposed new requirement uses a separate `NEW-xxx` ID referencing the product brief; it cannot overwrite a PRES requirement. Defects use their original audit IDs or the separate P2-D IDs below, not “preserve the broken behavior.”
|
||||
|
||||
For every implementation PR/session, add a row to this file (or link its scoped trace sheet here) with:
|
||||
|
||||
| Field | Required evidence |
|
||||
|---|---|
|
||||
| PRES-ID / requirement | Original ledger group plus the precise sub-workflow being changed; target-brief requirement if applicable |
|
||||
| Baseline → destination | Existing route, capture/query/alias/keyboard entry and intended location; preserved/relocated/merged/explicitly deprecated |
|
||||
| Implementation | Commit SHA, affected components/API owners, reason for the change; “unchanged” is valid but not “done” |
|
||||
| Automated regression | Named behavioral test, file, command/result and provider/test layer; include negative/dirty/error cases |
|
||||
| Visual/UX validation | Screenshot/video or review artifact with route, populated/empty/error/locked state, viewport, theme, locale, keyboard/focus result and date |
|
||||
| Gaps / decision / reviewer | Known baseline defects and intentional missing proof; approval reference only when semantics/deprecation need it |
|
||||
|
||||
No screenshot alone proves data preservation. No mocked API test alone proves ownership. No successful localhost run proves F04. A shifted feature remains protected after moving to another page.
|
||||
|
||||
## Phase 2 changes (test/CI checkpoint only)
|
||||
|
||||
All application implementations and routes remain at their Phase 1 behavior. The commit containing this document identifies the Phase 2 implementation of **regression protection**, not a product migration. Use `git log -- docs/redesign/phase-2` to resolve its SHA without a self-referential commit placeholder.
|
||||
|
||||
| PRES IDs → existing requirement | Phase 2 change → automated oracle | Visual/UX evidence |
|
||||
|---|---|---|
|
||||
| E07, J04 → command job/company/add entry and reviewed capture | [quick-command-preservation](../../../job-tracker-ui/src/quick-command-preservation.test.tsx): three cases; destinations, callback and no-create boundary | No visual redesign. Native browser keyboard search/add evidence below |
|
||||
| C11 → per-section review never implicitly applies | [ai-section-assistant](../../../job-tracker-ui/src/ai-section-assistant.test.tsx): reject/close, edit then accept, failed suggestion retry | jsdom interaction only; no model or visual certification |
|
||||
| C07, C11 → whole-document review changes only accepted CV settings | [cv-builder-deep-link](../../../job-tracker-ui/src/cv-builder-deep-link.test.tsx): apply/reject and invalid/missing/duplicate block responses | jsdom current/proposed comparison; real canonical isolation below |
|
||||
| W06 → rejected letter does not replace saved content | [application-assets](../../../job-tracker-ui/src/application-assets.test.tsx): reject clears proposal, original remains, no dirty/save | No provider contacted; real delivery remains outside this workflow |
|
||||
| S03, W11 → scenario-specific rules and retryable preferences | [settings-view](../../../job-tracker-ui/src/settings-view.test.tsx): unchanged scenarios in payload; failed notification save retains draft and retries | Responsive browser preference evidence below |
|
||||
| C01, C06, C07, C10 → canonical/sibling isolation and responsive editor persistence | [redesign-preservation browser](../../../job-tracker-ui/e2e/redesign-preservation.spec.ts): mobile override, preview/edit, reload, desktop preview; API compares master and sibling before/after | Current Chromium UI at 390×844 and 1440×900, EN/default theme. Behavioral viewport check, not visual approval |
|
||||
| W01, W05–W08 → source relationships and frozen submission | Same browser spec: fixture creates actual job/CV/letter/file; captures via legacy CV workspace entry | **Expected failure P2-D01:** capture fails on SQLite. Later source-mutation/archive checks not reached; do not mark this migration ready |
|
||||
| E07, J04, W01 → real command launcher and old links | Same browser spec: Ctrl+K job selection, actual App legacy match link, reviewed add leaves job count unchanged | Current desktop route/keyboard behavior only |
|
||||
| S03, W11 → responsive rules/notification controls | Same browser spec: edit rule mobile→desktop, save, native API check; notification false survives reload; independent direct rule save | 390×844/1440×900 EN/default theme; no screenshot-based redesign |
|
||||
| A01, S04, E07 → hidden admin navigation is not authorization; Free AI locks | Same browser spec: hidden admin menu, direct users read/create 403; ordinary CV creation succeeds, all three AI actions disabled, API AI 403 | Current non-admin browser + real API. Admin route shell may render, but cannot retrieve privileged data |
|
||||
| All 57 → reliable test type baseline | [package script](../../../job-tracker-ui/package.json), [CI gate](../../../.gitea/workflows/ci-deploy.yml): `npm run typecheck` before Jest | Static TypeScript for existing src/app scope; no visual or app change |
|
||||
|
||||
## Phase 3 handoff and later migration gates
|
||||
|
||||
Phase 3 is **UX architecture documentation**, not a new implementation batch: grouped sidebar/page hierarchy; Jobs table/board view entry; canonical Career Profile versus derived CV/document relationships; full-page versus optional preview; contextual AI review pattern; mobile task flows. Map all 31 existing routes, both aliases, capture/notification/provider entries and all 57 PRES rows to intended destinations.
|
||||
|
||||
Keep current MUI/Emotion/React Router/API and ownership contracts while designing. Brand/name/accent and cross-device dashboard customization remain genuine product decisions for the dependent work; do not ask about facts already in the audit.
|
||||
|
||||
Before Phase 4/5 code, require an approved route/preservation plan, targeted regression fixtures for that surface's gaps, a shared accessibility/state contract, and coordinated integrity/recovery dependencies. In particular:
|
||||
|
||||
- Do not restructure package/tailoring workflows while P2-D01 blocks native success proof.
|
||||
- Do not conflate B02 identity, stale-CV revision and create-intent idempotency. Those queue items remain distinct; B07 was not started.
|
||||
- Do not drop mail draft revisions, explicit send/new-attempt controls or owner-bound references.
|
||||
- Before migrating shared controls, validate EN/NB × light/dark × desktop/mobile, keyboard focus, error/empty/dirty states and representative loaded data. Real screen reader/touch/reflow tests remain manual.
|
||||
- Do not label a release healthy until the previously failed CI browser runner is diagnosed and the required pipeline passes. F04 requires actual production ingress evidence.
|
||||
|
||||
Future trace sheets must link here. There are **no Phase 3+ implementation commits or visual sign-offs** in this checkpoint.
|
||||
@@ -0,0 +1,54 @@
|
||||
# Accessibility plan
|
||||
|
||||
ARC-16; all PRES groups, with structural priorities E07/E08, J05/J07, W01/W06, C03/C06–C11 and M04. Accessibility is a release criterion per migrated capability, not a final theme polish pass.
|
||||
|
||||
## Existing evidence and first priorities
|
||||
|
||||
Phase 1 carries F14 shared contrast failures and builder preview keyboard-scroll failure after viewport transition. Phase 2 adds behavioral viewport tests, but does not close F14. The original axe suite samples eight routes and can finish before full data/controls load. A green shell scan is not proof of loaded builder/import/send/admin accessibility.
|
||||
|
||||
Priority order: (1) semantic foreground/background/focus pairs; (2) named fields/landmarks and modal focus; (3) loaded preview keyboard access and reorder alternatives; (4) complete responsive task traversal and chart alternatives. Fix each at the responsible shared component before multiplying it across screens.
|
||||
|
||||
## Contracts by interaction
|
||||
|
||||
| Area | Required behavior | Evidence required |
|
||||
|---|---|---|
|
||||
| Landmarks and headings | Skip link, labelled primary/secondary nav, one main, one h1; sensible heading hierarchy | Real App route transitions, keyboard and accessibility tree |
|
||||
| Navigation | Active current page, collapsed link names, menu grouping, route-heading focus | Expanded/rail/mobile; close vs navigate focus behavior |
|
||||
| Fields/errors | Persistent label, help/error association, required status; summary focuses first invalid field; draft retained | RichTextField/Customize selects, date/autocomplete, server validation failure |
|
||||
| Tables | Table headers/sort state, named row selection/actions, noninteractive text plus explicit links | Keyboard sort/select/bulk; no whole-row click hijacking controls |
|
||||
| Mobile cards | Same information/actions with translated role/company accessible names | Bokmål long titles, touch/keyboard, no English-only label inheritance |
|
||||
| Drag/reorder | Move earlier/later or exact status menu for every drag action; pickup/move/drop/cancel announced | CV, checklist, Kanban; focus stays on moved item |
|
||||
| Dialog/drawer | Labelled purpose, initial focus, trap only when modal, Escape policy, trigger return, dirty confirm | Nested confirmation and mobile software keyboard; no stacked invisible traps |
|
||||
| Preview | Named scroll region/iframe, meaningful title, keyboard entry/scroll/exit, actual document pagination | Loaded multipage CV, switch desktop→mobile→desktop and zoom |
|
||||
| AI review | Current/proposed/source names, insert/delete semantics not color alone, deliberate Accept/Reject | Keyboard edited proposal and no-write Reject; long diff/mobile |
|
||||
| Async states | Busy/loading status, polite completion, actionable error; no repeated announcement per keystroke | Operations, render, import, failed save, delayed read after account switch |
|
||||
| Charts | Text summary, exact data values table/list, period/denominator/sample, non-color legend | Keyboard access without pointer tooltip; no unreadable SVG-only values |
|
||||
| Notifications | Accessible unread count, names/timestamps and action names; read ≠ task complete | Popover/full centre, update announcements without focus theft |
|
||||
| Menus/commands | Search name, result groups/count, selected result, arrow/Enter/Escape behavior | IME, focus return, denied/unavailable commands and no private results after logout |
|
||||
| Motion | Honor reduced motion; no information relies on animation | Reorder/view/dialog completion under reduced motion |
|
||||
| Contrast/focus | Internal target: ordinary text ≥4.5:1, large text ≥3:1, essential controls/focus ≥3:1 against adjacent surface | Both modes and states; computed rendered pairs, not swatch-only test |
|
||||
| Touch/reflow | Aim 44 px touch hit area, visible focus, ordinary content usable at 320 CSS px | 200%/400% zoom, text spacing, forced colors, touch/virtual keyboard |
|
||||
|
||||
The numeric thresholds above are project validation targets, not a compliance certification. Disabled controls also need readable adjacent explanations; active important labels cannot use low contrast merely because the design looks quiet.
|
||||
|
||||
## Loaded-state automated matrix
|
||||
|
||||
Use seeded synthetic owner data. Wait for a meaningful named loaded field/row/action, **not just body/main**. Scan and interact after expanding:
|
||||
- Career sections, extraction review including low-confidence choices and history.
|
||||
- CV Content, Customize, AI review, History and long loaded preview.
|
||||
- Job application drafts, documents, checklist, correspondence, interview and dirty confirmation.
|
||||
- Inbox draft/send review/uncertain result; Settings security/provider/deletion controls.
|
||||
- Populated Jobs table/board/cards, saved filters, bulk/trash results.
|
||||
- Admin data/denial/confirmation and aggregate charts when implemented.
|
||||
|
||||
For each migrated shared primitive: EN/NB × light/dark × desktop/phone plus relevant loading/error/locked states. Existing representative browser coverage is a floor, not an instruction to assert only initial pages. Retain all Phase 2 behavior tests and add focused user outcomes rather than CSS snapshots.
|
||||
|
||||
## Manual validation still required
|
||||
|
||||
Use NVDA or Narrator on Windows for primary tasks; record tool/browser, exact route/state, actions and outcome. Verify complete keyboard flow, focus restoration, reading order, data-table alternatives, live-region repetition and escape from iframe/scroll regions. Real touch/virtual keyboard and at least one mobile screen reader require separate access if unavailable locally.
|
||||
|
||||
Test a long Norwegian form, many custom statuses, extreme company/title strings, multipage CV, reduced motion, forced colors, browser zoom and increased text spacing. An emulator screenshot does not prove these interactions. Any unavailable manual evidence stays an explicit gap on its PRES trace row.
|
||||
|
||||
## Stop conditions
|
||||
|
||||
No broad component/page propagation while shared naming/focus/contrast regression remains. Do not disable axe rules or hide failed controls to get a green test. P2-D01 must not be disguised by an “empty” loaded-state fixture; its package workflow remains blocked. No public accessibility claim until manual primary-flow evidence exists. F04 production ingress and CI runner gates remain separate.
|
||||
@@ -0,0 +1,57 @@
|
||||
# AI interaction model
|
||||
|
||||
ARC-11; PRES-W03, PRES-W05, PRES-W06, PRES-W09, PRES-W10, PRES-C03, PRES-C04, PRES-C11, PRES-S04, PRES-E09. NEW-07 is shared contextual access, not a new autonomous agent platform.
|
||||
|
||||
## Grounding contract
|
||||
|
||||
AI must never invent experience, skills, qualifications, education, employment, achievements/metrics, certifications, languages, dates or personal facts. Job requirements describe the employer's needs, not the user's abilities. Unknown is distinct from absent.
|
||||
|
||||
Source context is explicit: current canonical items, selected CV overrides, known job text, user-supplied notes or specifically included attachments. Show source identity/last-known revision where available. Existing models do not supply universal claim-level provenance; a source attachment chip is not proof that every generated sentence is grounded. New structured claim references/unsupported-claim validation need a separately scoped API/evaluation contract.
|
||||
|
||||
When source support is missing: omit the claim, mark it unverified for review or ask the user to add truthful information. Never invent numeric achievements or add a job keyword as an asserted skill. Treat imported job/document content as data, not instructions to change permissions, transmit private files or bypass review. Keep existing server AI privacy/entitlement and source ownership controls.
|
||||
|
||||
## Three entry surfaces, one domain action
|
||||
|
||||
1. Inline action beside a summary, letter, answer or section.
|
||||
2. Contextual panel listing the small set of actions valid for the current job/CV/profile.
|
||||
3. Command entry routing to that same action/review surface.
|
||||
|
||||
The panel uses explicit context and selected source; it does not scrape unrelated routes or retain A's context after switching accounts. No hidden conversation memory, automatic generation on navigation, broad tools or background writes. Commands can open review but cannot silently apply changes.
|
||||
|
||||
## Suggestion states
|
||||
|
||||
| State | Presentation / available actions | Mutation boundary |
|
||||
|---|---|---|
|
||||
| Ready | Target scope and selected sources, action controls | No change |
|
||||
| Generating | Cancel when supported, bounded progress, request identity | No accepted content written |
|
||||
| Review | Current / Proposed / Sources; readable diff and edit proposal | Original retained |
|
||||
| Accepted into draft | “Applied to this draft/CV” with resulting save state | Existing domain action only; distinguish explicit Save vs builder autosave |
|
||||
| Rejected / closed | Proposal cleared; original editor restored | No PUT/save implied |
|
||||
| Regenerating | Keep original and prior proposal until replacement succeeds; clear prior acceptance | No automatic acceptance of replacement |
|
||||
| Invalid/unsupported | Explain structure/source validation error; retry or edit manually | Cannot Apply malformed/missing/duplicate block IDs |
|
||||
| Failed/timeout/busy | Safe message, retry when allowed, retain original | No stale/partial silent apply |
|
||||
| Provider unavailable / privacy disabled / Free locked | Explain supported reason; manual workflow and settings/plan link as applicable | No new request/charge, no fake output |
|
||||
| Context changed / account expired | Discard or require a fresh review against current content | Cannot apply response to different owner/resource |
|
||||
|
||||
An accepted suggestion is not proof of factual accuracy; meaningful support/review checks remain mandatory. Generation failure and legitimate “no supported improvement” are different states.
|
||||
|
||||
## Review interaction
|
||||
|
||||
Use the existing current-versus-suggested UI first. Text diff has insert/delete labels and screen-readable order, not color alone. Never force acceptance of all changes when only part is supported; per-change acceptance is a future extension where the existing action supports whole output only. Manual edit before Accept remains.
|
||||
|
||||
Target label is beside Apply: “Apply to this CV,” “Use in this letter draft,” or “Review changes to Career Profile.” No ambiguous universal “Save everything.” Translate changes language without changing facts; show target document language separately from UI language.
|
||||
|
||||
If source/editor changes during generation, require refreshed review against the current value. Do not rebase proposals silently or imply this solves server optimistic concurrency. API payload IDs/source selection must remain owner-scoped; stale-response rejection follows B02. Regenerate/Retry is user-initiated and respects limits; no retry loop on every render.
|
||||
|
||||
## Domain specifics
|
||||
|
||||
- Match: distinguish deterministic requirement comparison from optional AI explanation. Show scoring method/sample/freshness limits, not a promise of hiring probability.
|
||||
- CV: preserve per-section and whole-document flows and strict original block matching; accepted edits update only variant settings.
|
||||
- Profile extraction: explicit low-confidence selection and discard; don't convert extracted guesses into canonical facts.
|
||||
- Letter/answers: retain original until accepted into job draft; no auto-send.
|
||||
- Interview: generated questions/prep are proposals; Add to prep is deliberate. Suggested answers cannot invent personal examples.
|
||||
- Correspondence: AI draft preparation is distinct from provider send confirmation/attempt identity. Never auto-resend uncertain delivery.
|
||||
|
||||
## Evidence before consolidation
|
||||
|
||||
Phase 2 AI tests cover reject/close/no-write, edit then accept, invalid/missing/duplicate block IDs and CV-only mutation. Preserve those oracles when extracting presentation. Add delayed-response/context-change, cancelled generation, unavailable provider, keyboard diff and two-account scenarios. Model factuality needs controlled known-source evaluations; mocked suggestions prove UI boundaries only. No “hallucination-proof” certification is claimed.
|
||||
@@ -0,0 +1,51 @@
|
||||
# Application shell
|
||||
|
||||
ARC-03; PRES-E01–PRES-E09, PRES-S02–PRES-S04, PRES-A01–PRES-A03. Reuse [AppShell](../../../job-tracker-ui/src/layout/AppShell.tsx) and [App](../../../job-tracker-ui/src/App.tsx). They already support grouped items, longest-path active matching, collapse persistence, mobile drawer, breadcrumbs, account actions and notification access.
|
||||
|
||||
## Structure
|
||||
|
||||
```text
|
||||
Desktop
|
||||
┌ Sidebar (expanded / rail) ┬ Search/commands Notifications Account ┐
|
||||
│ Dashboard │ Breadcrumb if useful │
|
||||
│ JOB SEARCH │ Page title / context Primary action │
|
||||
│ CAREER │ Secondary workspace navigation │
|
||||
│ INSIGHTS │ Main content [AI panel] │
|
||||
│ ACCOUNT / ADMIN │ │
|
||||
└──────────────────────────┴────────────────────────────────────────────┘
|
||||
Mobile: compact top bar → contextual title/actions → content
|
||||
Menu opens a modal navigation drawer; search, bell and account stay reachable.
|
||||
```
|
||||
|
||||
Global bar owns navigation utilities, not a repeated second job/CV header. Contextual page header owns title, description, scope and one primary action plus explicit secondary/overflow actions. Detail WorkspaceHeader owns role/company/status/deadline or CV name/save/publication state.
|
||||
|
||||
## Reuse and responsibility
|
||||
|
||||
Retain the current collapse preference (device-local, non-private), 254/84 px starting widths and <768 px drawer breakpoint until content testing demonstrates a change. Do not reset collapse just because group labels change. Collapsed links have persistent accessible names and tooltips; the active child determines one primary owner through explicit metadata for /discover, /companies and other non-nested paths.
|
||||
|
||||
Keep navigation metadata near existing App route/title ownership; extend it with logical parent/title information only as needed. Do not create a router framework, move to Next routing or recreate the router on theme/density changes. Route identity churn is a source-backed risk requiring a regression test before touching that owner.
|
||||
|
||||
## Global controls
|
||||
|
||||
- Search opens the existing command dialog. Keyboard and visible button share one action.
|
||||
- Notification bell has count and accessible unread summary; no CV/mail body in its collapsed label.
|
||||
- Account menu: Account & security, Settings, Connections, theme, language, sign out. Existing provider-link/security controls remain on their pages; appearance actions do not create another settings store.
|
||||
- A contextual “AI help” entry appears only where supported actions/context exist. It opens a narrow action panel, not automatic chat or generation. Without an eligible context, omit this entry rather than showing a global disabled assistant.
|
||||
- Build metadata remains admin-only. No operational configuration in a personal header.
|
||||
- Keep current light/dark/system and EN/Bokmål hydration; UI language and document language remain distinct.
|
||||
|
||||
## Identity and navigation lifetimes
|
||||
|
||||
Preserve the B02 authenticated-tree unmount, pending-request abort and stale response/mutation rejection on identity/session transition. A new panel, command result, notification list or preview may not outlive that tree. No second Axios client, persisted private command history or unscoped draft store.
|
||||
|
||||
Navigation goes through existing dirty protection. Cancel keeps the document and focus; Discard is deliberate. An account boundary clears private content even if unsaved: do not ask B to retain A's draft. Logout retains existing server invalidation/local cleanup. Role-hidden navigation supplements, never replaces, server authorization. Denied admin/API pages show safe unavailable/permission states without fetching hidden individual data.
|
||||
|
||||
## Interaction requirements
|
||||
|
||||
Skip-to-main link, labelled navigation landmark, one main region, logical h1 and route-change focus on the new page title. Closing mobile drawer/command/bell returns focus to its trigger; selecting a route closes the surface and focuses the new heading. Avoid multiple active modal focus traps. A persistent desktop sidebar is not a modal dialog.
|
||||
|
||||
Sticky headers must not obscure focused controls at zoom or with wrapped Bokmål text. Reserve no permanent AI column when it is closed. On phone use full-height dialogs for long tasks and preserve scroll/draft state through viewport changes.
|
||||
|
||||
## Gate before shell migration
|
||||
|
||||
Extend existing sidebar-active-nav, app-shell-notifications, quick-command-preservation, account-isolation and redesign-preservation tests with collapse persistence, mobile focus return, all logical parent routes, theme/language changes during dirty editing and two-account command/notification disposal. Loaded EN/NB × light/dark × phone/desktop evidence is required. Implement this only after Phase 4 foundations; no shell rewrite is part of Phase 3.
|
||||
@@ -0,0 +1,44 @@
|
||||
# Career Profile UX
|
||||
|
||||
ARC-08; PRES-C01–PRES-C04, PRES-C07, PRES-C10, PRES-C11, PRES-E06, PRES-E09. Sources: [CareerProfilePage](../../../job-tracker-ui/src/views/CareerProfilePage.tsx), [career sections](../../../job-tracker-ui/src/views/career/CareerProfileSections.tsx), [CvVariant](../../../JobTrackerApi/Models/CvVariant.cs), Phase 2 canonical/sibling isolation regression.
|
||||
|
||||
## One canonical source
|
||||
|
||||
Career Profile is the account-owned structured career singleton. Account /profile owns login identity/email/security; CVs own identified settings and item overrides. Label every edit destination before editing, including AI review:
|
||||
|
||||
| Scope label | What changes | What must not change |
|
||||
|---|---|---|
|
||||
| Career Profile — shared source | Canonical selected profile fields after explicit profile save/import acceptance | Account login/email/security; existing CV overrides; frozen submitted packages |
|
||||
| This CV only — [name] | Selected variant settings/item override | Canonical profile, sibling CVs, other owners |
|
||||
| This application — [role/company] | Job draft, letter, answers or existing job-specific artifact | Canonical data; unrelated application drafts |
|
||||
| Suggestion — not saved | Review proposal only | Any stored user-owned content until deliberate acceptance/save |
|
||||
|
||||
Live CVs can reflect later canonical edits where they inherit the source. Never promise that editing Career Profile leaves all rendered live CVs unchanged. Existing overridden fields remain overridden; frozen submissions remain historical. Show this consequence near profile Save and the variant's source label. An existing published live CV also needs clear explanation; it is not an immutable submitted snapshot.
|
||||
|
||||
## Page organization
|
||||
|
||||
Overview gives completeness and actionable missing information, recent import/history and CV links. Edit offers personal information, summary, work, education, skills, languages, interests and **all existing long-tail/custom sections**. Evidence has its existing sourced statement/title/category/tags list/add/delete. Import review/history stays a distinct activity with operation status.
|
||||
|
||||
Do not force onboarding before access. Profile completeness is guidance, not a public employability score or requirement to invent content. No “missing skill” autofill from job requirements. Desired role/location/remote/salary preferences are NEW-13 only where not currently represented; current job fields do not satisfy career intent preferences.
|
||||
|
||||
Preserve avatar editing, raw CV text copy, manual edit, history restore, extraction rebuild/improve/reprocess, retry/cancel and low-confidence selections. Do not reduce the canonical schema to the shorter illustrative section list.
|
||||
|
||||
## Import and AI review
|
||||
|
||||
Choose/upload source → processing with operation ID → compare extracted proposal with current profile → select supported changes (including low-confidence controls) → accept or discard. Display source document/field/confidence evidence when returned. Confidence alone does not verify truth.
|
||||
|
||||
Discard leaves canonical data intact. Failed extraction retains original and recovery actions; cancellation is not success. Low-confidence or conflicting personal claims require explicit user verification. User edits the proposal before acceptance where supported. No silent replacing of profile or automatic propagation from an AI assistant.
|
||||
|
||||
Privacy/entitlement/provider availability explains disabled actions and offers manual editing. Never send unselected attachment content or retain private extracted text in global shell state.
|
||||
|
||||
## Source and override interaction
|
||||
|
||||
Variant item references retain stable source keys. “Edit Career Profile” navigates to source; “Customize in this CV” creates/edits the existing override. “Use profile value” resets only that override, showing current source value first. Profile item removal can leave stale selections/overrides: surface an unresolved source link with repair choices, not silent reassignment to a similarly named item. Exact missing-source behavior needs a focused fixture before changing it.
|
||||
|
||||
Do not add one-click “promote all CV changes to profile.” If later requested, it needs an explicit field-level review and same-account server contract. No generic synchronization framework or schema move.
|
||||
|
||||
## Preservation and migration gate
|
||||
|
||||
Current save/history/projection behavior remains, but F01 record concurrency is unresolved. Plan source/override wording independently; coordinate any save/history/conflict owner edits with D01–D03 and recovery gates. B02 alone is not optimistic concurrency.
|
||||
|
||||
Before restructuring: native A→B stale-state denial; canonical/sibling unchanged after variant override; profile inherited-field propagation and override reset; extraction accept/discard/low-confidence/no-provider states; history restore; long sections and loaded keyboard names. Phase 2 already proves a headline override path, not every item/restore/import combination.
|
||||
@@ -0,0 +1,59 @@
|
||||
# Commands, notifications and tasks
|
||||
|
||||
ARC-12; PRES-E07–PRES-E09, PRES-J01–PRES-J04, PRES-J08, PRES-W04, PRES-W11, PRES-W12, PRES-M01–PRES-M06, PRES-S02–PRES-S04.
|
||||
|
||||
## Command palette
|
||||
|
||||
Extend [QuickCommandDialog](../../../job-tracker-ui/src/components/QuickCommandDialog.tsx), which already has six static commands and job/company search. NEW-02 expands it; no competing command platform.
|
||||
|
||||
| Group | Results / actions | Scope and implementation limit |
|
||||
|---|---|---|
|
||||
| Context | Open Match, tailor current CV, review current letter, interview prep | Only supported actions for current resource; opens existing review/action, never auto-applies |
|
||||
| Navigate | Dashboard, Jobs, Discover, Follow-ups, Companies, Offers, Career Profile, CVs, Documents, Analytics, Correspondence, Settings | Existing pages first; new routes only after they have meaningful content |
|
||||
| Find | Jobs, companies, CV names, supported document metadata | Owner-scoped reads, labelled entity kind; CV/library read reuse, document search awaits bounded projection |
|
||||
| Create | Add/import job; create CV | Opens review/template/name flow; not a background POST or auto-generated content |
|
||||
| Preferences | Light/dark/system, EN/Bokmål, density when implemented | Existing preference handlers and clear current selection; no new account identity |
|
||||
| Administrative | Authorized page navigation | Server still gates every action; no reset/delete/run-maintenance shortcut |
|
||||
|
||||
Blank query shows a short useful command list, not every button in the product. Preserve current job/company result destinations and Add's no-write behavior. Context actions may rank ahead of generic navigation; don't hide ordinary manual commands when AI is unavailable.
|
||||
|
||||
Ctrl+K / Cmd+K and visible search button open the same modal. Focus goes to a **labelled** search input (placeholder alone insufficient). Arrow keys move among results, Enter activates, Home/End reach boundaries, Escape clears a nested scope or closes, Tab remains inside the modal. Do not intercept these keys while IME composition is active; don't steal unrelated editor shortcuts. When closing without navigation restore the trigger; after navigation focus destination heading.
|
||||
|
||||
Group headings/result count and loading/error/no-match states are announced. Existing search catches map errors to empty arrays; improve that deliberately during command migration, not with an assertion that current search distinguishes outage. Debounce, cancel stale results, cap result sets and never generate PDFs/AI during search. No private recent searches in localStorage. Clear query/results on close and identity change.
|
||||
|
||||
## Notification centre
|
||||
|
||||
Reuse NotificationsPopover, UserNotification read/unread/dismiss and operation links. Bell opens a recent list; optional /notifications is an expanded view of the same store. Show unread/all, clear timestamps, safe summaries, mark read and dismiss. Opening a link marks/read behavior must be explicit and must not execute the underlying job action.
|
||||
|
||||
| Event | Current source / availability | Planned behavior |
|
||||
|---|---|---|
|
||||
| Background work progress/success/failure | UserOperation + notifications exist | Link /operations or actual result; preserve retry/cancel identity and failure |
|
||||
| Saved NAV search changes | Saved-search alert results exist | Retain existing alert/dismiss flow; unified centre producer only after end-to-end evidence |
|
||||
| Job follow-up/deadline | Rules/reminders feed and email preference exist | Centre event producer/dedup needs verification/addition; don't equate reminder rows with stored notifications |
|
||||
| Upcoming interview | No general scheduled interview resource | NEW-06 event data first; no inference from stage |
|
||||
| Task due | Checklist has no due date | NEW-04 scheduling contract first |
|
||||
| Document/application state | Existing workspace/artifact events | Link only persisted event/result; avoid one notification per keystroke/preview |
|
||||
| System message | Existing operation/config failures where relevant | Actionable owner-safe messages, no infrastructure secrets |
|
||||
|
||||
Future producers need event identity/dedup, ownership, expiry/read behavior and navigation tests. Do not introduce a general event bus as a presentation task. Muting/hiding a notification is not completing a checklist item or deleting a job.
|
||||
|
||||
## Distinct lifecycles
|
||||
|
||||
- **Task:** actionable job checklist item; pending/done/dismissed, manual or system/auto-signal, persistent ID/order. Completing it changes task state. Due date/time/reminder and cross-job tasks view are new contracts, not already implemented.
|
||||
- **Notification:** notice about a recorded event or work needing attention. Read/dismiss affects the notice only.
|
||||
- **Calendar event:** scheduled time/date, provider identity and timezone; current job deadline/follow-up creation and ICS are existing features. It is not a generic task completion store.
|
||||
- **Email:** external message with actual delivery, provider and consent constraints. Draft/edit/review/send/uncertain attempt remain distinct from notifications and manual correspondence logs.
|
||||
|
||||
Retain Google/Outlook writable-permission/status actions and ICS fallback from [ApplicationCalendarActions](../../../job-tracker-ui/src/components/ApplicationCalendarActions.tsx). Current kinds are follow-up and deadline; no general scheduled interview/calendar sync is proven. A future interview event needs date/time/timezone, explicit calendar link and duplicate-creation semantics. Calendar/timezone baseline defects keep their original owner; no event schema work here.
|
||||
|
||||
## Mail architecture and integrations
|
||||
|
||||
Transactional account/reminder email already has server SMTP configuration and admin test/settings. Gmail, Microsoft Graph and IMAP connection capabilities also exist, with differing read/send capabilities. Preserve those; the original brief's “choose an email provider” is a future deployment/product decision, not evidence that no email architecture exists.
|
||||
|
||||
Correspondence primary entry keeps inbox filters, read details/attachment metadata, provider compose/reply, persisted draft versions, explicit review/send, uncertain-delivery handling, thread import/link/unlink/move and review candidates. A note/pasted message does not send an email. Retry after unknown delivery must use the existing explicit attempt controls.
|
||||
|
||||
LinkedIn recruiter links or imported listing URLs are **not** a LinkedIn integration. No unsupported scraping, auto-apply or synchronization is planned. NAV remains the supported discovery mechanism; additional jobs/calendars/providers need explicit future contracts and authorization.
|
||||
|
||||
## Tests before migration
|
||||
|
||||
Command job/company/Add behavior, keyboard active result/focus, dirty navigation, account switch and stale result rejection; notification read/dismiss does not complete task; link to operation/job section survives aliases; task auto/manual/dismissed rules and keyboard ordering retained. Mail requires real sandbox delivery/reconnect/uncertainty evidence before changing provider flows; Phase 2 mocks are not provider certification.
|
||||
@@ -0,0 +1,47 @@
|
||||
# Component architecture
|
||||
|
||||
ARC-15; affected contracts are in each row and the full [impact matrix](PRESERVATION_IMPACT_MATRIX.md). Names describe responsibilities, not a mandate for one file per name. Retain existing implementations until a scoped consumer needs the shared behavior.
|
||||
|
||||
## Retain, refactor, consolidate or add
|
||||
|
||||
| Target responsibility | Existing owner | Disposition and boundary | PRES |
|
||||
|---|---|---|---|
|
||||
| AppShell / Sidebar | layout/AppShell; App nav/title helpers | **Refactor** existing grouped/collapsed/drawer presentation; preserve auth tree, collapse and active matching. Extract Sidebar only if it simplifies this owner | E07–E09 |
|
||||
| PageHeader | AppShell title/breadcrumb/rightActions plus local page headers | **Consolidate** heading/action hierarchy; keep one h1 and no duplicated global/detail header | E07, E08, W01, C06 |
|
||||
| WorkspaceHeader | local WorkspaceHeader in ApplicationWorkspacePage; CV header | **Retain/refactor** per-domain content; share layout only, not save/status semantics | W02, C06, C10 |
|
||||
| MetricCard | DashboardView metricCards/Card | **Refactor** as data-labelled presentation; no fetching/AI in a card | S01 |
|
||||
| DashboardWidget | DashboardView SectionCard + visibility menu | **Add later** catalogue/order/size wrapper in Phase 8; existing visibility migrated | S02 |
|
||||
| DataTable / MobileDataCard | JobTable and CompaniesTable use MUI Table; admin uses MUI X grid | **Retain** engines; share toolbar/state/cell styles where proven. TanStack Table is installed but no current src import was found; do not introduce it as a migration prerequisite | J05, J06, J08, A01 |
|
||||
| StatusBadge | pipeline statusLabel/statusTone and inline Chips | **Consolidate** presentation using existing status semantics; custom values retained | J07, W02, S01 |
|
||||
| FilterBar | JobTable inline/mobile controls; SavedViewsMenu | **Refactor** presentation around same filters/saved-view owner | J05, S03 |
|
||||
| EmptyState / ErrorState / LoadingSkeleton | ViewStateNotice, useViewResource, local skeletons | **Consolidate** semantics/retry/focus; existing owner still decides data/error states. Do not create three wrappers when one composition suffices | E08 and all loaded pages |
|
||||
| QuickPreviewDrawer | ApplicationWorkspace optional props; no active host | **Add only after parity fixture**, read-only summary/navigation. Do not mount second full editor | W01, J05 |
|
||||
| CommandPalette | QuickCommandDialog + App keyboard trigger | **Refactor/extend** existing action callbacks and owner-scoped result reads | E07, J04, J08 |
|
||||
| NotificationCentre | NotificationsPopover + Notifications API | **Refactor/extend** same store; new full route only when needed | S02, W11, W12 |
|
||||
| AIAction | AiSectionAssistant, CoverLetterAiAssistant, InterviewAiAssistant, document AI tab, aiWorkspace | **Consolidate** state/entry presentation, retaining each API/context and entitlements | C11, W03, W06, W09, S04 |
|
||||
| AIReviewDiff | current/proposed builder, extraction and letter views | **Consolidate** readable comparison/reject/apply UI; domain validations and payload identity remain local | C03, C11, W06 |
|
||||
| DocumentCard / row | CvBuilderPage, Attachments, SubmittedPackagesSection | **Add index projection view**, reuse domain open/download handlers; never merge resource persistence | C05, W07, W08 |
|
||||
| WorkspaceTabs | ApplicationWorkspace tabs; CV studio tabs; settings TabPanel | **Refactor** semantics/responsive selector where repeated; preserve dirty guards and active resource | W01, C06, S03 |
|
||||
| SettingsSection | SettingsView SectionCard; security/provider cards in ProfilePage | **Consolidate** layout/help/errors only; server/local settings scopes stay separate | E04–E06, M06, S03–S05 |
|
||||
| Form fields and dates | MUI TextField/Select/Autocomplete/DatePicker; RichTextField, TagsInput | **Retain + targeted accessibility refactor** for names/help/errors. No form framework or HTML storage migration | C01, C07, W06, J03 |
|
||||
| Dialog / Drawer / confirmation | MUI, confirm, prompt, toast | **Retain** mechanics; standardize focus/name/dirty behavior and mode styling | E08, W01, S05 |
|
||||
| Task row / Timeline | ApplicationChecklist, ApplicationTimeline, InterviewPrep | **Retain/refactor** domain structures; no universal task/event object | W02, W04, W09, W10 |
|
||||
| Chart container | DashboardView SVG helpers | **Add small composition only as needed** for title/period/values table; keep calculations in owner/service | S01 |
|
||||
| Theme providers/tokens | theme, ThemeProvider, themePrefs, themeBootstrap, I18nProvider | **Retain/refine**; no second theme, locale or preference authority | E08 |
|
||||
| Auth/session/API boundary | App, api, sessionBoundary, accountPlan | **Retain unchanged** unless a separately evidenced defect; server identity/permissions remain authoritative | E02–E09, S04 |
|
||||
|
||||
Unreachable Attachment, JobFlowBar, Markdown and UserManagementCard in the earlier census are not authorized deletions. Shared component extraction must prove all existing consumers, not assume file size or duplicate names require a new abstraction.
|
||||
|
||||
## Data and state ownership
|
||||
|
||||
Presentation primitives accept current state/callbacks, not raw account IDs with their own API clients. Form draft, persistence revision, selected source and send attempt remain in the relevant domain owner. A UI “scope” label is not authorization. No generic hook that conflates account profile singleton, identified CV, package capture and mail send.
|
||||
|
||||
Keep API cancellation/provenance and private-tree lifetime. Existing resource loads may be split only with latest-response and owner boundary tests. Avoid duplicating network loads between a header, sidebar panel and content. Newly consolidated Documents/Analytics need explicit bounded reads, not hidden browser crawls.
|
||||
|
||||
Reusable components should emerge from actual repeated behavior. E.g. MUI already implements Button/Dialog semantics; standardizing its theme is enough. AI review can share a comparison frame while retaining different extraction/block/letter acceptance validators. Do not flatten them into one unsafe Apply callback.
|
||||
|
||||
## File and implementation discipline
|
||||
|
||||
Keep current folder layout initially. Add small shared modules next to existing components/theme only when first consumers migrate. No bulk rename, barrel re-export tree, new package/workspace or Storybook dependency in Phase 4. Use existing test/browser fixtures for representative states.
|
||||
|
||||
Do not move App's provider order, router creation and navigation presentation in the same change. Do not change CV save owner while extracting visual fields. Do not run mail draft/send restructuring alongside generic document work. Components listed “add later” are not Phase 4 scope just because their names appear in this document.
|
||||
@@ -0,0 +1,54 @@
|
||||
# CV workspace
|
||||
|
||||
ARC-09; PRES-C05–PRES-C12, PRES-W05, PRES-W07, PRES-E08, PRES-E09. Retain [CvBuilderEditor](../../../job-tracker-ui/src/views/CvBuilderEditor.tsx), [customization](../../../job-tracker-ui/src/components/cv/CvCustomizeTab.tsx), [cvBuilder client](../../../job-tracker-ui/src/cvBuilder.ts) and the shared server preview/PDF renderer.
|
||||
|
||||
## Library and editor
|
||||
|
||||
The /career/builder library is labelled CVs. Preserve name/create/copy/delete, template gallery with actual rendered thumbnails, publish/unpublish/share and PDF download. A public slug is separate from private numeric identity. Documents links here rather than exposing a second CV editor.
|
||||
|
||||
```text
|
||||
CV name · This CV only · optional linked job Save state · Undo Redo · Export
|
||||
[Content | Template | Customize | AI tools | History]
|
||||
[Section navigation + selected editor] [Live paginated preview]
|
||||
[Page n of m · Fit width]
|
||||
```
|
||||
|
||||
Existing studio capabilities are preserved regardless of tab order. Recommend Content as the primary editing task; template choice stays obvious for creation. A tab-order change is presentation only and requires keyboard/route regression. Reuse current editor state/save owner rather than remounting it on every tab, theme or viewport change.
|
||||
|
||||
## Content and scope
|
||||
|
||||
Each section: localized title, visibility, position, source/inherited/override marker, edit and contextual AI. Expand one or more sections; users are not forced to close previous work. Retain built-in item visibility/order, custom/preset/free-text sections, stable keys, hide/remove and manual content. Reorder uses existing native drag plus Move up/down controls; mobile and keyboard have equal outcomes.
|
||||
|
||||
“Inherited from Career Profile” links to the source. “Customized in this CV” offers comparison/reset. Reset changes only the override; it does not delete the career item. Job-specific variants show the linked job without implying ownership of that job or automatic submission. Restoring source or an old version must never bypass the correct server-owned resource lookup.
|
||||
|
||||
## Bounded customization with advanced parity
|
||||
|
||||
Default Customize shows template, accent, supported typography, font size, margins and spacing. Advanced disclosure retains every existing setting:
|
||||
|
||||
- Template-capability-aware heading/header styles, colors, photo and icons.
|
||||
- Skills presentation/groups, one/two columns, sidebar and section columns.
|
||||
- A4/Letter, document density, date format and document language.
|
||||
- Per-section rows/grid/compact/bubbles and section spacing/order/visibility.
|
||||
- Unsupported-setting handling and the server's normalization/fallback behavior.
|
||||
|
||||
This is intentionally constrained to CV layout, not a word processor. Do not drop current advanced settings because the brief lists fewer. No arbitrary absolute positioning, HTML editor or new font/template engine. UI Comfortable/Compact density is **not** CV document density.
|
||||
|
||||
Template switches retain compatible settings and make unsupported options unavailable with explanation. Existing saved template/settings IDs remain renderable. Any destructive reset is explicit; do not silently reset a CV when changing application theme.
|
||||
|
||||
## Preview and persistence
|
||||
|
||||
Keep debounced server preview, actual page boundaries, automatic fit and shared HTML/PDF engine. Preview is render-only; no implicit AI or profile writes. Maintain latest-result sequencing and dispose old preview/blob work on unmount/account switch.
|
||||
|
||||
Desktop split needs independent labelled, keyboard-scrollable panes. Mobile has Edit / Preview, remembers section/scroll, keeps save/export accessible and never compresses both into a phone. Manual zoom (NEW-12) is optional incremental work; Fit width already exists. Browser page zoom/reflow remains accessible even if manual preview zoom is deferred.
|
||||
|
||||
Save states: loading, editing/unsaved, saving, saved, failed with retry, reauthentication required. Do not show Saved before acknowledgement. Retain current local save queue and dirty navigation guard; it only serializes one editor and cannot resolve concurrent writers. Undo/redo is editor settings history; persisted version restore creates a new version rather than erasing history. Do not label either as conflict protection.
|
||||
|
||||
PDF failures/busy admission use existing B03 contract with understandable retry; never start parallel renders for preview thumbnails or bypass the shared generator. Private export/public published export retain separate authorization/publication checks.
|
||||
|
||||
## AI
|
||||
|
||||
Per-section improve/translate/custom instruction and whole-document translate/improve/spelling retain source/target language, current/proposed comparison, edit/accept/reject and strict block identity validation. Reject/close/error does not modify settings. Accept targets this variant; if a separate Save follows, say so. Do not place “apply to Career Profile” beside CV Accept.
|
||||
|
||||
## Required evidence before migration
|
||||
|
||||
Phase 2's mobile headline override and AI tests are a starting point. Add source reset/inherited edit, custom item reorder/hide, history/undo/reload, public unpublish, long searchable multipage PDF and unsupported template settings. Exercise selected-source context and native package relationships after P2-D01 is repaired separately. Loaded preview keyboard scroll after viewport transition is an F14 gate. Do not edit this stateful owner concurrently with stale-revision work; no F01 fix is claimed by this UX plan.
|
||||
@@ -0,0 +1,106 @@
|
||||
# Dashboard architecture
|
||||
|
||||
ARC-04 / ARC-05; PRES-S01, PRES-S02, PRES-W02–PRES-W12, PRES-C05, PRES-A01–PRES-A03. New work: NEW-01, NEW-05, NEW-08, NEW-11 in [Open decisions](OPEN_DECISIONS.md).
|
||||
|
||||
## Existing implementation and honest metric meanings
|
||||
|
||||
[DashboardView](../../../job-tracker-ui/src/components/DashboardView.tsx) calls stats, analytics, analytics-overview, tags, tag-trends and reminders under /jobapplications. It persists five owner-local visibility flags: cards, activity, funnel, companies, skills. No order, resize, goal or server layout contract exists.
|
||||
|
||||
[AnalyticsService](../../../JobTrackerApi/Services/AnalyticsService.cs), [analytics controller](../../../JobTrackerApi/Controllers/JobApplicationAnalyticsController.cs) and [DTOs](../../../JobTrackerApi/Models/AnalyticsDtos.cs) establish these constraints:
|
||||
|
||||
- stats.active and overview.totalActive count **non-deleted tracked jobs**, including closed and pre-application statuses. They do not measure in-flight applications. total includes deleted rows.
|
||||
- The funnel counts jobs **currently** in each canonical stage. It is not a historical cohort conversion funnel. Unknown custom values may be absent from that canonical-stage series; byStatus retains raw values.
|
||||
- Monthly analytics selects jobs applied within a month-aligned window, then counts their dated responses in the displayed months. It is not all responses received in that period, and not an eventual response probability.
|
||||
- Source breakdown uses current Company.Source and includes prospects. Top-source/top-company lists are truncated; do not treat their sum as the whole population.
|
||||
- Time in stage is median **current occupancy age**, with fallback to DateApplied or SavedAt when a transition is unavailable. It is not completed-stage duration.
|
||||
- Median first response uses rows with both dates; averageDaysSinceApplied is application age, not response speed. Current calculations clamp negative durations; data-quality labeling must not pretend dates were verified.
|
||||
- Upcoming interview captions derived from “interview-prep” signals identify preparation work, **not scheduled interviews**.
|
||||
|
||||
These are source-backed semantic limitations (P3-O01 in Open decisions). Preserve access to current reports, but label them accurately during their scoped migration. No analytics implementation or retrospective data repair occurs here.
|
||||
|
||||
## Default personal layout
|
||||
|
||||
Useful immediately: tracked-job summary; recorded response summary with denominator; current interview/offer counts; attention/follow-ups/deadlines; application activity; current pipeline distribution; companies and tags; onboarding when incomplete. Keep existing salary/stage/source details reachable from Analytics. New data-dependent widgets are absent until supported, never placeholder zeroes.
|
||||
|
||||
Suggested visual order: “Next actions” and key counts first, activity/pipeline next, companies/tags and details below. A new user sees real empty states with Add/import job and Review Career Profile. No sample jobs or fake success metrics. Users may hide any widget; if all are hidden show an intentional layout-empty state with Customize/Restore defaults.
|
||||
|
||||
## Layout contract for later Phase 8
|
||||
|
||||
A small catalogue defines widget ID, localized title, supported sizes and data owner. Use CSS grid with discrete small/medium/wide spans; do not start with a dashboard framework or free-positioned canvas. Plain ordered IDs plus visibility and size metadata suffice; no stored HTML or career content.
|
||||
|
||||
Customize mode supports show/hide, drag/reorder plus Move earlier/later, size choices on suitable charts/lists, and Restore defaults with a clear confirmation. Changes give immediate preview; storage failure leaves usable content and a retry/message, never silently claims persistence. Unknown IDs are ignored safely without losing known entries; invalid sizes fall back to defaults. Preserve existing five hidden/visible choices when migrating; do not silently re-enable hidden widgets.
|
||||
|
||||
Use one logical order across breakpoints. Mobile has a single-column feed and move/hide controls; desktop width preferences remain saved but inactive on mobile. Charts have supported size ranges, metrics fixed height with wrapped labels. Reset affects only dashboard layout, not jobs, account preferences, goals or canonical profile.
|
||||
|
||||
**Persistence decision OD-04:** recommend owner-keyed device-local metadata first, matching current scope, clearly labelled “Saved for this account on this device.” Cross-device layout requires a separately tested server preference contract before Phase 8; do not claim localStorage syncs devices. Either option must validate owner from the active identity, dispose A's state on switch, handle corrupt/stale data and avoid storing content. Theme/density settings are separate; no generic preference blob that merges unrelated security controls.
|
||||
|
||||
## Widget catalogue and data requirements
|
||||
|
||||
Availability: **Existing** = current endpoint/behavior with stated meaning; **Derivable** = fields exist, but a bounded owner-scoped aggregate or read composition is still needed; **New data** = required facts/contracts do not currently exist. No client N+1 fetch across every job to build global metrics.
|
||||
|
||||
| ID / widget | Availability and data source | Definition / boundary / empty behavior |
|
||||
|---|---|---|
|
||||
| DW-01 Tracked jobs | Existing: stats.active | Non-deleted records; rename display accordingly, not in-flight applications |
|
||||
| DW-02 Active applications | Derivable: Status, DateApplied, IsDeleted | Explicit applied active-stage mapping; exclude prospects/closed, unknown separate. Do not reuse DW-01 |
|
||||
| DW-03 Total recorded jobs / trash | Existing: total/deleted | Label deleted inclusion; trash link retains restore semantics |
|
||||
| DW-04 Applications this week | Derivable: DateApplied | Distinct submissions in agreed local week; no SavedAt substitution; period/timezone gate |
|
||||
| DW-05 Applications this month | Existing monthly applied point | Calendar month label; not rolling appliedLast30Days |
|
||||
| DW-06 Applications last 30 days | Existing: appliedLast30Days | Rolling server-time period, labelled as such until timezone contract is settled |
|
||||
| DW-07 Interviews | Existing: current Interview stage | “Jobs currently at interview”; not number of meetings or all-time interviews |
|
||||
| DW-08 Upcoming interviews | New data: scheduled interview start/end/timezone | Do not infer appointment from Interview status, deadline or follow-up |
|
||||
| DW-09 Offers | Existing: current Offer stage | Current recorded offers, link /offers; historical offer count needs events |
|
||||
| DW-10 Rejections | Existing: normalized current Rejected stage | Current recorded outcome; neutral language, no failure badge |
|
||||
| DW-11 Response rate | Existing period series or source rates, different denominators | Display exact cohort/window and responses/applied sample; no mixing overview and period denominators. No sample = “Not enough recorded data” |
|
||||
| DW-12 Interview conversion | Derivable with data-quality qualification: status events + application cohort | Distinct applied jobs that reached Interview / submitted cohort; current Interview count is not this numerator |
|
||||
| DW-13 Offer conversion | Derivable with same historical cohort | Distinct jobs reaching Offer / submitted cohort (explicit label); missing history shown, not fabricated |
|
||||
| DW-14 Application funnel | Existing canonical distribution | Label “Current pipeline”; include Unknown/custom bucket when migrating. Historical conversion is separate NEW work |
|
||||
| DW-15 Applications over time | Existing analytics monthly points | Applied counts, period/source subtitle and accessible values table |
|
||||
| DW-16 Applications by status | Existing byStatus | Preserve custom/raw values, aliases and unknown labels |
|
||||
| DW-17 Applications by sector | New normalized sector coverage | No reliable tracked-job sector dimension found; NAV listing data alone does not cover tracked history |
|
||||
| DW-18 Applications by location | Derivable JobApplication.Location | Unknown explicit; raw text first, normalized municipality grouping needs a mapping contract |
|
||||
| DW-19 Applications by source | Existing source response summary; complete count needs aggregate | Company.Source is mutable company-level provenance, not original job capture source; top six is not full breakdown |
|
||||
| DW-20 Applications by role | Derivable JobTitle | Raw role/title distribution; normalized role families require taxonomy, not automatic AI classification |
|
||||
| DW-21 Average response time | Derivable DateApplied/ResponseDate | Arithmetic mean over valid paired responses, show n; existing median offered separately, do not relabel it average |
|
||||
| DW-22 Median first recorded response | Existing overview median | Available dated sample; no implication missing dates mean no response |
|
||||
| DW-23 Time in pipeline stages | Existing current occupancy medians | Label current-stage age and fallback; completed durations/history trends need new aggregation |
|
||||
| DW-24 AI match statistics | Derivable per-job deterministic match output, aggregate missing | Require score definition, profile/job version and scored sample; do not trigger generation on dashboard view |
|
||||
| DW-25 Recent activity | Job-scoped recentActivity/timeline exists; global read missing | Bounded owner event feed, source links and safe summaries; monthly applications chart is not event history |
|
||||
| DW-26 Deadlines | Existing job deadlines/reminders | Upcoming/overdue date-only semantics; full period read coverage must be declared |
|
||||
| DW-27 Tasks | Existing per-job checklist; cross-job aggregate missing | Pending/done/dismissed, stable IDs; “Tasks due” awaits per-task due dates |
|
||||
| DW-28 Follow-ups / attention | Existing reminders/workflowSignal | Keep existing reason/action links; feed includes more than overdue follow-ups |
|
||||
| DW-29 CV usage | Derivable CvVariant job link + package metadata, aggregate missing | Current linkage separate from recorded submitted version. A download alone is not “used to apply” |
|
||||
| DW-30 CV performance | Future qualified historical join | Submission cohort + frozen CV/version + outcomes, n and missing-data rate; correlation only; P2-D01 gate |
|
||||
| DW-31 Recently edited CVs | Existing library UpdatedAtUtc | Bound list and safe editor links; not performance |
|
||||
| DW-32 Weekly goals | New optional goal preference | Disabled until chosen, editable/disable/pause; no default quota. Completion uses valid DateApplied, never encouraging unsuitable applications |
|
||||
| DW-33 AI recommendations | Existing deterministic next steps; new AI aggregation/panel optional | Label rule-based guidance honestly; user-invoked AI only, grounded reasons, hide/ignore, no implicit edits |
|
||||
| DW-34 Companies | Existing topCompanies | Recorded activity and responses; top eight and unknowns disclosed |
|
||||
| DW-35 Skills/tags and trends | Existing tags/tag-trends | Job tags, not the user's verified skills. tags does not consume a months parameter; label its actual period |
|
||||
| DW-36 Salary insights | Existing currency/period groups | Preserve separate currencies/periods; no inferred FX conversion |
|
||||
| DW-37 Work arrangement | New consistent tracked dimension | Imported description text alone is not a reliable remote/hybrid aggregate |
|
||||
| DW-38 Interview/offer trends | Derivable historical events with completeness limits | Not current-stage counts plotted as historical outcomes; no synthetic transition dates |
|
||||
| DW-39 Response/match trends | Partial existing monthly responses; match history missing | Define cohort vs events, scoring version and missing history before trend claims |
|
||||
|
||||
Period controls must explain which widgets are all-time, current snapshot or selected-period. Do not put a global “this month” above unfiltered all-time data. Zero, unknown, loading, unauthorized/provider-unavailable and failed are distinct. No automatic polling/generation per widget beyond current service needs. Keyboard/data-table alternatives accompany every chart.
|
||||
|
||||
## Separate aggregate admin dashboard
|
||||
|
||||
Propose /admin overview; retain /admin/users, /admin/audit and /admin/system. Current system page is operational diagnostics, not product analytics. Never compute product metrics by downloading individual profiles or the existing admin user table into a dashboard.
|
||||
|
||||
| Candidate | Current evidence | Required implementation / honest limit |
|
||||
|---|---|---|
|
||||
| Registered users | Identity users exist | New authorized server count; define inclusion of pending/deleting accounts |
|
||||
| New users by period | ApplicationUser has no registration timestamp | Reliable creation event/timestamp contract and historical coverage; do not infer from OAuth-linked dates |
|
||||
| Active users | Sessions exist, no agreed activity metric | Define meaningful activity/time window/retention; active sessions are not active people |
|
||||
| Total jobs tracked | Admin system exposes operational counts | Verify scope and deletion inclusion; new explicit aggregate endpoint for product meaning, not reuse diagnostic counts blindly |
|
||||
| Interviews/offers recorded | Job statuses/events exist | Server aggregate with distinct job/event meaning and missing-history qualification |
|
||||
| CVs created | CvVariant timestamps exist | Authorized count/time buckets; owner-scoped personal library is not admin aggregate |
|
||||
| Documents generated | Several artifact types/render paths exist | Define successful persisted artifact vs PDF render/download/retry; no unified document counter today |
|
||||
| AI usage | Usage metering exists | Aggregate actions/tokens/period/failures as supported; no prompts, responses or career facts |
|
||||
| Common sectors | No reliable shared sector dimension | Future normalized dimension and privacy-safe grouping |
|
||||
| Common locations/roles/technologies | Job location/title/tags exist | New bounded aggregate; use job metadata, not individual profile skill extraction |
|
||||
| Subscription/trial metrics | Stripe status fields/entitlements exist; no generic trial contract proven | Define paid/active/cancelled periods from billing authority; never infer trials from Free accounts |
|
||||
| Onboarding/adoption/churn/conversion | Current checklist/UI alone does not supply product event funnel | Future event/consent/retention definitions; no fabricated denominator |
|
||||
|
||||
Privacy: aggregate-only responses, no owner IDs/names/emails/job descriptions/CV content. For sensitive breakdowns propose fixed coarse cohorts, minimum cell size 10 with complementary suppression, no arbitrary filters/drill-down that reconstruct smaller groups. This is a review proposal, **not an anonymity guarantee**. Totals/period differences and rare roles can still identify; evaluate release scope/retention before implementation. Support access remains separately authorized/audited and cannot be reached by clicking an analytics bar. Denial must be server-enforced even when admin navigation is hidden.
|
||||
|
||||
Admin dashboard work waits for OD-09's metric/privacy definition. Personal layout settings never affect aggregate scopes or admin operational controls.
|
||||
@@ -0,0 +1,52 @@
|
||||
# Design-system architecture
|
||||
|
||||
ARC-14; PRES-E08 and all consumers, especially PRES-J05, PRES-C06–PRES-C09 and PRES-S03. **Retain MUI/Emotion, MUI icons and existing theme providers.** No evidence justifies a competing library or state/router replacement.
|
||||
|
||||
## Foundation and ownership
|
||||
|
||||
[theme.ts](../../../job-tracker-ui/src/theme.ts) already supplies palettes, typography, custom shadows, CSS variables, breakpoints and MUI overrides. themePrefs/themeBootstrap/ThemeProvider preserve initial paint and light/dark/system selection. AppShell currently has separate hardcoded navy/indigo constants; migrate them to semantic theme roles deliberately after shared-state contrast proof.
|
||||
|
||||
Use the existing theme as the token authority; page CSS variables may alias it. No parallel JSON token compiler, token package or generic design-system runtime. Typed augmentation only for small roles the current theme cannot express. Existing MUI Button/TextField/Select/Dialog APIs remain; do not wrap every primitive merely to rename it.
|
||||
|
||||
## Token proposal
|
||||
|
||||
Values below are **working architecture proposals**, to validate in P4 fixtures. Final brand hue/logo is not selected.
|
||||
|
||||
| Family | Planned roles / scale | Usage |
|
||||
|---|---|---|
|
||||
| Brand | primary main/on-primary, interaction hover/selected/focus | One hue; keep current #5B5BD6 as provisional continuity, not final approved identity |
|
||||
| Text | primary, secondary, muted, disabled, inverse, link | Explicit foreground/background pairs; avoid arbitrary opacity for required copy |
|
||||
| Surfaces | canvas, content, subtle, raised, overlay, navigation | Light neutral canvas with content layer; dark layered neutral surfaces rather than inversion |
|
||||
| Borders | subtle separator, control boundary, strong/selected, error, focus | Separator need not meet same contrast as an essential form boundary |
|
||||
| Status | info/success/warning/error plus neutral; each background/text/icon/border | Pipeline text and icon alongside color; no identical semantic mapping assumed across unrelated domains |
|
||||
| Typography | body 14/20, dense/support 13/18, caption 12/16; section 18/24, page 28/34 desktop and 24/30 phone; weights 400/600/700 | Preserve Archivo/system stack and æ/ø/å; numerals tabular where useful; rem sizing, no fixed-height clipping |
|
||||
| Spacing | 4, 8, 12, 16, 24, 32, 48 px equivalents | Related controls vs sections; reuse existing page/card/gap variables |
|
||||
| Radii | control 8, panel 12, overlay 16; pill only for small status/tag | Subtle distinction without every surface being a nested rounded card |
|
||||
| Elevation | flat/base, subtle raised, overlay | Borders/surface contrast carry dark-mode depth; no glow/glass dependency |
|
||||
| Motion | immediate feedback, ~120–180 ms disclosure, ≤220 ms overlays as proposal | No animated metric counters; reduced-motion removes nonessential movement |
|
||||
| Layout | existing responsive breakpoints; page max-width by reading/data/editor mode | Reading content bounded, tables/editor can use width; no one-width-fits-all |
|
||||
| Density | Comfortable/Compact spacing variants | Application surfaces only; separate from CV document density |
|
||||
|
||||
Retain existing heading semantics separately from style variants: a page can be h1 with page-title styling rather than inheriting a marketing hero size. Do not change font loading/hosting or add a typeface dependency in foundations.
|
||||
|
||||
## Light and dark
|
||||
|
||||
Choose and test both schemes as first-class palettes. Navigation can retain a distinct tonal surface while honoring mode and contrast; no assumption that current fixed navy is inaccessible everywhere. Dark hover/selected/focus layers need visible borders/outlines without washing out content. Avoid gradients and glass in core product surfaces; any existing public marketing treatment can wait for its own migration.
|
||||
|
||||
One semantic role may need different foregrounds in different modes. Never compute on-primary/alert text by blindly lightening/darkening one color and assume it meets contrast. P4 explicitly verifies actual rendered pairs, including disabled-adjacent explanations and focus indicators. Prefer outline + offset for focus over low-opacity shadow alone.
|
||||
|
||||
## Component variants and state contract
|
||||
|
||||
Use contained primary for one main action, outlined secondary and text tertiary. Destructive confirm is explicit and labelled; red is not the default presentation of ordinary rejection outcomes. Pro/AI unavailable controls need visible reasons and manual alternatives, not only disabled opacity.
|
||||
|
||||
Buttons, inputs, selects/comboboxes, checkboxes, switches, tabs, table headers/rows, badges, alerts, menus, dialogs and drawers need default/hover/focus/selected/disabled/error/loading definitions. Focus is distinguishable from selected. Form label/error/help IDs stay linked. Placeholder is a hint, not a label.
|
||||
|
||||
Empty/error/loading states use the existing ViewStateNotice and MUI primitives with consistent semantics. Keep old content while refreshing where the owner currently supports it, but clearly mark a refresh failure; never replace an error with fake zero metrics.
|
||||
|
||||
## Page primitives
|
||||
|
||||
Minimal set: PageHeader (heading/description/actions), WorkspaceHeader (identified resource/scope/state), PageSection (optional heading, no mandatory card), and a small data/reading/editor width contract. Reuse existing Shell/SectionCard patterns only where source repetition warrants extraction. See [Component architecture](COMPONENT_ARCHITECTURE.md) for owners and explicit deferred additions.
|
||||
|
||||
## Phase 4 acceptance
|
||||
|
||||
First establish semantic state pairs and visible focus with a loaded representative form/table/alert fixture in both schemes and locales. Keep current brand and route tree. Later adjust typography/layout primitives only after responsive checks. Test meaningful interaction/contrast, not snapshots of CSS or token names. No visual identity sign-off, accessibility certification or production release is implied by this planning package.
|
||||
@@ -0,0 +1,48 @@
|
||||
# Documents UX
|
||||
|
||||
ARC-10; PRES-C05–PRES-C12, PRES-W05–PRES-W08, PRES-M04, PRES-S05, PRES-E09. NEW-11 covers missing cross-job read projections, not a replacement storage model.
|
||||
|
||||
## Purpose and existing resources
|
||||
|
||||
Documents is an owner-scoped **index of existing resources** with links to the editor that owns each one. It does not make every artifact a new generic Document record.
|
||||
|
||||
| Category | Existing backing / destination | State and relationship rule |
|
||||
|---|---|---|
|
||||
| CVs | CvVariant / CV library and editor | Current settings + canonical inherited content; optional job link; private/public independent of submission |
|
||||
| Cover letters | Job letter text/history and ApplicationAssets | One job's draft/versions; edit at job Application → Letter |
|
||||
| Application answers / recruiter draft | Existing package draft fields | Job-owned text; not a new standalone response library |
|
||||
| Uploaded attachments | Attachments owned through job | Purpose/type/size/name/date; current upload/rename/AI-use/download/delete retained |
|
||||
| References / certificates | Attachment categories where the existing purpose supports them | Display as uploaded evidence; no new verification/credential issuer system |
|
||||
| Generated materials | Existing variant/letter/tailored output paths | Generated preview/PDF may be transient; do not claim every download is stored |
|
||||
| Submitted packages | Submitted package records, frozen content and archived files | Immutable historical evidence; version/time/job and captured source identifiers |
|
||||
| Email drafts | Existing correspondence draft service | Context link to Correspondence; not a file or proof of delivery |
|
||||
|
||||
A certificate uploaded to a job is not automatically a verified Career Profile qualification. A public CV is not automatically submitted. A frozen package is a recorded snapshot, not confirmation that an employer received it.
|
||||
|
||||
## Index and data availability
|
||||
|
||||
Proposed /documents has text search, type, linked job and Draft / Submitted filters; sort by updated/captured date where present. CV public/private state is a separate badge/filter. Row/card: name, kind, linked role/company, source/version when recorded, draft/frozen state and open/download actions. Mobile uses structured cards; desktop may use a compact table/list.
|
||||
|
||||
CV library reads exist. Attachments, letters and package history are mainly job-scoped. A coherent cross-job index/search needs a bounded server projection with authenticated-owner queries, pagination, supported sort and metadata-only responses. Do not download every job/body/attachment to synthesize an index in the browser. Until that projection exists, provide honest links to CVs and selected-job documents rather than pretending a complete All documents result.
|
||||
|
||||
No binary relocation, schema unification or new upload-to-account bucket is presumed. General account-level reference storage would be a separately approved NEW requirement. Preserve job attachment limits and safe response headers; never render file content in search snippets.
|
||||
|
||||
## Relationships and actions
|
||||
|
||||
Open routes to the existing owner editor. Show “Used for” only when a relationship/submission actually records it. Current attachment/variant relation and submitted version are separate; changing current material cannot relabel history as the newest version.
|
||||
|
||||
Retain current CV association cardinality: CvVariant has an optional JobApplicationId and attaching may re-point it. Do not offer “attach everywhere” as though a many-to-many relation exists. Detaching does not delete the variant. Deleting a job does not imply deleting independent CVs. Preserve all ownership/reference guards.
|
||||
|
||||
Download current PDF renders current CV; archived package download uses captured content/files. Distinct labels prevent confusing the two. No regenerate button overwrites frozen packages. Capturing again creates a new package via the existing operation. Live attachment deletion never implies erasing its historical archived copy; source-copy failure stays an error.
|
||||
|
||||
AI-use toggle is explicit context inclusion permission for that attachment; do not broaden it when moving its card to Documents. Upload is not permission to use all files with every AI action.
|
||||
|
||||
## Error, dirty and authorization behavior
|
||||
|
||||
Missing/deleted/foreign resource yields existing safe API result and no private metadata. An index entry may become stale: refresh and explain unavailable; never attach another matching-name item. Navigation from a dirty letter/CV honors its guard; account switching disposes results/drafts. No owner IDs from filters select an API tenant.
|
||||
|
||||
P2-D01 blocks native SQLite package capture/tailoring. Existing mocked/service proofs are not successful archive-preservation evidence. Freeze/history presentation migration waits for its separate repair and actual unchanged HTML/letter/binary checks. A unified Documents page must not conceal that baseline defect.
|
||||
|
||||
## Validation
|
||||
|
||||
List/search isolation with A/B, draft vs frozen vs public labels, source editor links, foreign attach denied, detach keeps variant, live source mutation does not alter captured content, archived bytes survive live deletion, no extra render on listing, correct download filenames/encoding. Extend Phase 2 W07/W08 evidence before claiming complete document parity.
|
||||
@@ -0,0 +1,46 @@
|
||||
# Information architecture
|
||||
|
||||
Affected: all 57 PRES contracts; [per-group destinations](PRESERVATION_IMPACT_MATRIX.md). Recommendation ARC-01.
|
||||
|
||||
## Navigation tree
|
||||
|
||||
Use understandable labels with EN/Bokmål translations; English below specifies meaning, not final translation strings.
|
||||
|
||||
| Primary group | Primary destination | Secondary navigation / contextual entries |
|
||||
|---|---|---|
|
||||
| Home | Dashboard | Customize; links to Analytics, due follow-ups and source jobs |
|
||||
| Job search | Jobs | Tracked jobs (Table / Board), Discover, Follow-ups, Companies, Offers; Trash in workspace utilities |
|
||||
| Job search | Correspondence | Inbox, Drafts, Review; linked job and connected-provider entry |
|
||||
| Career | Career Profile | Overview, Edit, Import review/history, Evidence |
|
||||
| Career | CVs | Library/templates; selected CV editor |
|
||||
| Career | Documents | All supported artifacts; type/job/state filters; links to CV library and job editors |
|
||||
| Insights | Analytics | Overview, trends, breakdowns, stage/salary insights; source-job filters |
|
||||
| Account | Settings | Account, Security, Appearance/language, Notifications/rules, Connections, AI/plan, Data/privacy |
|
||||
| Admin, authorized only | Admin dashboard | Aggregate product overview (new); Users, Audit, System as distinct secondary pages |
|
||||
|
||||
Correspondence earns a primary entry because it has independent inbox, drafts, provider review and real-send workflows (PRES-M01–M06). Hiding it in Documents would blur messages and files. The Jobs secondary navigation keeps discovery, follow-ups, companies and offers visible without separate duplicate top-level menus. A direct child URL selects Jobs as its primary parent even when the path is not prefixed /jobs.
|
||||
|
||||
Notifications remain globally reachable through the bell; an expanded centre can have a utility route. Background operations remain reachable from the centre and Settings/System activity link for the user. Admin operations remain separate from the user's operations. No empty Admin dashboard is shipped before its aggregation exists.
|
||||
|
||||
## Page and workspace hierarchy
|
||||
|
||||
- Dashboard answers “what needs attention?” Analytics answers “what happened, over which period and sample?” A metric deep-links to its source/filter, not a second unrelated dataset.
|
||||
- Jobs is the work index. Table/Board is presentation, not pipeline identity. Discover is a separate search source within Jobs; importing still requires review.
|
||||
- Job workspace owns ongoing work on one job: Overview, Match, Application, Documents, Timeline, Notes & tasks, Interview. Correspondence is contextual within that job and also accessible through the inbox.
|
||||
- Career Profile owns canonical facts. CVs selects/renders those facts with document overrides. Documents helps find the resulting resources without creating a second CV database/editor.
|
||||
- Account means login identity, sessions, privacy and preferences. “Career Profile” never means login/email settings.
|
||||
- Admin has privileged aggregate/operational work; personal Dashboard always remains personal, even for an administrator.
|
||||
|
||||
## Secondary navigation rules
|
||||
|
||||
Keep a stable Jobs workspace navigation row on list/discovery/company/offer/follow-up surfaces. On /jobs/:id use Jobs breadcrumb/back plus document-specific tabs; do not stack both full navigation rows and seven more tabs on a phone. Secondary items live in a labelled select on narrow layouts when they cannot fit.
|
||||
|
||||
Use a simple Settings section list with links to existing /profile and /settings/connected-accounts until their content is safely relocated. Their primary owner is Settings; there are no duplicate Account/Profile menu destinations. Existing tab content, import/export, capture setup, billing availability and deletion controls remain reachable.
|
||||
|
||||
Avoid a separate “AI” route. Contextual AI actions open relevant review/assistant surfaces. Avoid top-level Cover letters, Applications, Board or Templates entries when the existing workspaces already own them.
|
||||
|
||||
## Classification and sequencing
|
||||
|
||||
Grouped navigation and Jobs consolidation are UX improvements to existing behavior. Analytics and Documents are new **destinations**, built from existing functions plus explicitly qualified read requirements. Global cross-job document search and product/admin aggregation are new API work when no suitable endpoint exists. Dashboard customization is Phase 8 work, not a prerequisite to naming its destination.
|
||||
|
||||
No capability is deprecated. MUI/Emotion and existing route/data owners remain. [Route plan](ROUTE_PLAN.md) is the authoritative migration table; [Component architecture](COMPONENT_ARCHITECTURE.md) identifies reuse. Navigation simplification must not remove advanced, provider, privileged or low-frequency functionality.
|
||||
@@ -0,0 +1,51 @@
|
||||
# Jobs workspace
|
||||
|
||||
ARC-06; PRES-J01–PRES-J10, PRES-W01, PRES-E07–PRES-E09, PRES-S03. [Current JobTable](../../../job-tracker-ui/src/components/JobTable.tsx), [KanbanBoard](../../../job-tracker-ui/src/components/KanbanBoard.tsx), SavedViewsMenu and EditJobDialog remain the owners.
|
||||
|
||||
## Structure and defaults
|
||||
|
||||
Page header: Jobs, result count and Add/import job. Secondary row: Tracked, Discover, Follow-ups, Companies, Offers; Trash in visible utilities menu. Inside Tracked: **Table / Board** switch, Search, Filters, Saved views, Columns, Comfortable/Compact. Desktop defaults to Table; phone Table choice means structured cards. The view switch is not another primary nav destination.
|
||||
|
||||
Explicit view query wins over an optional remembered owner-local choice. First use stays table; saved preference must not override a shared URL. Existing /kanban remains functional until matching query semantics and keyboard behavior work in the unified owner. Do not recreate AddJobModal or create jobs as a side effect of route mounting.
|
||||
|
||||
## Table information
|
||||
|
||||
| Default visible | Optional but preserved | Conditional new columns |
|
||||
|---|---|---|
|
||||
| Selection, company, role with next-action cue, status, location, deadline, applied date, row actions | Existing days-since and external source link toggles; page size 15/20/25; all current toggle behavior | Follow-up date, package readiness, recorded match with provenance, favourite/priority after its explicit contract |
|
||||
|
||||
Show “Not applied” for null DateApplied, not today or zero days. Long text wraps to a controlled second line with full accessible text and full page access. A row action must not also navigate; selection/status menu/external link remain independent. Do not hide existing information under hover-only interaction. Salary, contacts, descriptions and full timeline belong in preview/detail rather than making every row tall.
|
||||
|
||||
New optional match cells need bounded list data, not a request or AI call for every row. Unknown/insufficient match shows no invented percentage.
|
||||
|
||||
## Filter and saved-view contract
|
||||
|
||||
Retain server q, status (including raw custom values), companyId, location, needsFollowUp, readiness, deleted/includeDeleted, sortBy, sortDir and pagination. Filter changes reset pagination as today. Search retains debounce/error behavior. Active filters are visible chips with Clear all; clear does not alter column/density settings or stored named views.
|
||||
|
||||
Saved views already exist and are justified by repeat searches. Preserve create/select/delete and owner isolation. Do not introduce a second “saved filter” store. View/layout/density may be added only through versioned compatible presentation metadata; existing saved views must still load. Board must apply the same supported filters and result scope before consolidation; retain existing large-list limits explicitly, never silently omit jobs.
|
||||
|
||||
Selection is explicit and page-scoped unless a later “all matching” server contract is added. Show selected count and scope. On view/filter/result changes clear invalid selections deliberately, with no action on invisible stale IDs.
|
||||
|
||||
## Bulk and row actions
|
||||
|
||||
Keep edit, exact status choice, delete-to-trash, restore and existing confirmation/partial failure behavior. After partial failure report successes/failures and keep failed items actionable; never announce all succeeded. Confirmation names count and action. Trash retains deleted-only behavior and restoration, not permanent deletion.
|
||||
|
||||
JSON/CSV import/export stays in Settings → Data with a contextual Jobs utility link to that existing function. Preserve B05 bytes/formula safety; do not export only the visible page without explicit labeling.
|
||||
|
||||
## Status semantics and customization
|
||||
|
||||
Current statuses: Saved, Interested, Preparing, Applied, Waiting, Interview, Offer, Rejected, Ghosted, Withdrawn. Keep aliases, raw/custom values, unknown Other fallback and server rules. Board groups are NotApplied, Active and Closed; group-drop destinations Saved, Applied and Rejected remain distinct from precise status choices. Offer is active on the board despite its success analytics category.
|
||||
|
||||
NEW-03 proposes an owner status configuration screen under Settings, reached from Jobs: label, order and explicit mapping to existing system semantics. Until a backend contract exists, keep current free-text/custom behavior and Other group. Do not replace defaults with illustrative Screening/Final Interview/Accepted or auto-map unknown statuses. Rules, analytics and history mapping require a dedicated cross-layer change and regression before UI adoption.
|
||||
|
||||
Favourite/priority is NEW-09: no current JobApplication field was found. Propose a simple owner flag later, default off, independently filterable; do not repurpose Tags, NextAction, operation Priority or status. Defer its control until persistence/ownership and product choice are settled.
|
||||
|
||||
## Quick preview
|
||||
|
||||
NEW-10 creates an optional **read-only** drawer for scanning: role/company, status/dates, short description, next action, CV/document summary and Open full job. Do not embed another full editable seven-tab workspace or a second save queue. Existing ApplicationWorkspace overlay props remain protected but are not evidence of a currently mounted preview.
|
||||
|
||||
Open via explicit row Preview action; role/title link still opens full page. Escape closes and returns focus; opening full page preserves list filters/page/focus context. Mobile may use a full-height preview or go straight to full page if testing shows no benefit; full functionality remains in the full page. No sending, destructive mutation or AI apply inside preview.
|
||||
|
||||
## Validation before migration
|
||||
|
||||
Use PRES-J05/J06/J07 gaps from Phase 2: saved-view CRUD, partial bulk failure, trash restore, identical table/board filtered records, precise versus coarse stage movement, keyboard pickup/drop/cancel and mobile no-drag alternative. Add real App query/back/capture tests, ownership switching and long Bokmål data. Route and table/board owner changes must land together or keep the old entry; never redirect before parity.
|
||||
@@ -0,0 +1,56 @@
|
||||
# Job workspace and guided journey
|
||||
|
||||
ARC-07; PRES-W01–PRES-W12, PRES-J03–PRES-J09, PRES-M01–PRES-M05, PRES-C01, PRES-C05–PRES-C12. Reuse ApplicationWorkspacePage, ApplicationAssets, Attachments, Correspondence, InterviewPrep and the typed applicationWorkspace client.
|
||||
|
||||
## Persistent header
|
||||
|
||||
Show role, company, current status with precise edit action, location, deadline and next action. Secondary metadata: source link, saved/applied date and recorded match summary with source/freshness when available. Main CTA follows the supported next step; Edit job and overflow retain existing controls. Do not automatically start analysis/rendering when opening a page.
|
||||
|
||||
A “Tailored for this job” or “Current CV” label must link to the exact variant. No header claim that a package was submitted because a PDF was downloaded. Favourite awaits NEW-09. On mobile keep title/status/action compact; remaining metadata expands without disappearing.
|
||||
|
||||
## Section ownership
|
||||
|
||||
| Target section | Existing capabilities consolidated | Display and progressive disclosure |
|
||||
|---|---|---|
|
||||
| Overview | JobDetailsSection, ApplicationProgress, nextStep, description/translation, dates and contacts | Summary and one suggested action; expand full source description and contact details |
|
||||
| Match | ApplicationAnalysis, ApplicationMatch, strategy snapshot, reviewed workflow/status suggestions | Source requirements, supported/unknown gaps, explanation; optional AI narrative with review. “Analysis” old key maps here |
|
||||
| Application | ApplicationCvSection/TailoringSection, CoverLetterSection, package drafts and SubmittedPackagesSection | Steps within one section: CV → Letter → Answers → Submission; independent entry, explicit scope and save state |
|
||||
| Documents | Attachments plus references to current CV/letter and immutable packages | Upload/category/AI-use/rename/preview/download/delete; do not copy content into a new store |
|
||||
| Timeline | ApplicationTimeline plus correspondence and follow-up context | Activity / Correspondence subview; filters and compose/import thread actions. Sending remains its explicit review flow |
|
||||
| Notes & tasks | Existing Notes, ApplicationChecklist | Manual/system/dismissed distinction, move controls and progress. Application answers remain in Application, not overwritten into Notes |
|
||||
| Interview | InterviewPrep manual/questions/AI review/readiness/debrief/follow-up | Prep / Debrief disclosure; preserve generated question review, manual edits and source context |
|
||||
|
||||
Keep all seven reachable even before an interview; “Not yet applicable” explains missing context without hiding existing edits. Old CV/cover-letter/checklist/document/timeline/communication links translate per [Route plan](ROUTE_PLAN.md). A logical Timeline merge does not imply all emails and job events already share a backend timeline endpoint.
|
||||
|
||||
Application has four subsection controls, not an enforced wizard or duplicated route tree. Keep per-domain save/dirty boundaries and guards across tab/query changes. Choosing a new CV may reassign a relationship; explain it before performing the existing attach operation. Do not invent many-to-many CV associations.
|
||||
|
||||
## Guided journey without a forced wizard
|
||||
|
||||
| Situation observed | Suggested action | Required guard / alternative |
|
||||
|---|---|---|
|
||||
| Discover/import listing | Review captured fields; then Save job | Preview never creates. URL import retains B01. Manual entry stays available |
|
||||
| Saved job lacks usable description | Add/review source description | No invented facts or auto-submit |
|
||||
| Source available, profile missing/incomplete | Review Career Profile; optionally analyse job independently | Missing information is unknown, not proof of unfitness |
|
||||
| Match available | Inspect supported strengths/gaps, choose/tailor CV | No job requirement silently added to profile |
|
||||
| CV chosen | Prepare letter/answers | User can use existing materials or skip tailoring |
|
||||
| Material prepared | Review and freeze current package; record application | Explicit capture operation; not proof of external delivery |
|
||||
| Applied with date | Set/review follow-up and current reminders | Existing date/rule meanings retained; no automatic real email |
|
||||
| Interview stage | Open preparation and optionally add supported calendar action | Current calendar supports deadline/follow-up; scheduled interview needs NEW data |
|
||||
| Interview completed | Record debrief/outcome and optional follow-up | Do not seed achievements or company facts |
|
||||
| Offer / rejected / withdrawn / other | Review offer/record outcome, retain artifacts/history | Neutral tone; no mandatory goal or stage path |
|
||||
|
||||
Use the existing server nextStep/checklist/workflowSignal as initial source. Do not build a competing client progression engine. A dismissed/manual checklist item stays user-controlled; rerenders and layout changes must not reseed or repeat operations.
|
||||
|
||||
Status change and package capture are separate existing operations. Preserve both; never claim an atomic “Applied + immutable package + external send” transaction exists. Show partial completion and explicit retry without creating duplicate sends/packages. Any new atomic submission behavior needs a separate backend design.
|
||||
|
||||
## Full page versus preview and state
|
||||
|
||||
Full page owns writing, AI review, correspondence send, history restore, package capture and multi-step work. Preview is a summary/navigation aid. Dirty text survives ordinary viewport changes and cancelled navigation. Accepted AI suggestions change only the intended local draft/settings; saving uses its existing API contract. Account switch clears private state and invalidates stale requests even if a dialog was open.
|
||||
|
||||
Existing job IDs are application-read-model identifiers; internal Job linkage does not authorize a route/schema rewrite. Ownership, association and resource missing results stay server-enforced.
|
||||
|
||||
## Migration blockers and proof
|
||||
|
||||
**P2-D01** blocks successful native SQLite tailoring/package capture; fix under its separate ticket before restructuring those sections. Keep its expected-failure oracle visible. F01 lost-update semantics remain separate from B02 identity and creation idempotency; no new autosave/conflict system in this plan.
|
||||
|
||||
Require loaded tests covering all section aliases, draft Cancel/Discard, preserved Notes versus answer drafts, CV attach/detach, immutable historical HTML/letter/binary after live mutation, timeline/status updates, correspondence uncertain-send behavior and interview review. Test keyboard/mobile and both locales/themes; missing/foreign job stays safe. No removal of low-frequency interview/debrief/version/attachment actions for a cleaner overview.
|
||||
@@ -0,0 +1,72 @@
|
||||
# Open decisions and requirement boundaries
|
||||
|
||||
No answer is required to complete this documentation checkpoint. This register makes future dependent work reviewable; proposals are not silent product decisions.
|
||||
|
||||
## Decisions required from Connor
|
||||
|
||||
| ID / decision | Proposal / alternatives | When it affects implementation |
|
||||
|---|---|---|
|
||||
| OD-01 Product name | Keep Jobjakt until chosen. Explore Norwegian-first descriptive vs Scandinavian professional/supportive internationally readable naming; no proposed name is trademark/domain-vetted | Before brand/public-copy rollout; does not block state/focus foundations |
|
||||
| OD-02 Logo/identity | New wordmark + compact mark + favicon in both modes; retain existing mark meanwhile. Avoid generic AI-sparkle identity | Before branded shell assets; no logo generated/selected here |
|
||||
| OD-03 Final primary color | One hue. Current indigo #5B5BD6 is continuity only; muted Nordic blue/teal direction is an untested alternative | Before palette identity sign-off; semantic contrast fixes can use current hue |
|
||||
| OD-04 Dashboard persistence | Recommend owner-scoped device-local layout first; alternative account-synced layout. Both satisfy per-user isolation, only latter follows devices | Before Phase 8 persistence; server option needs API/recovery/migration assessment |
|
||||
| OD-05 Email delivery provider | Existing SMTP architecture remains. Managed transactional provider choice can change deployment/config, not the redesign's ownership of notification/mail flows | Before new email rollout or infrastructure change |
|
||||
| OD-06 External jobs/calendar scope | Preserve NAV and existing Google/Outlook deadline/follow-up + ICS. Decide separately on scheduled interviews, wider event sync or additional providers | Before NEW-06; no LinkedIn integration or auto-apply presumed |
|
||||
| OD-07 Monetisation-specific UX | Retain server Free/Pro capabilities and honest unavailable billing; no invented prices/trial countdown/checkout promises | Before new trial, paywall, pricing or aggregate subscription UI |
|
||||
| OD-08 Favourite vs priority | Recommend optional favourite only initially; richer priority levels need clear user need and persistence semantics | Before NEW-09 schema/API/UI, not a phase-4 prerequisite |
|
||||
| OD-09 Aggregate admin/privacy scope | Start with a small approved total/period set; fixed coarse breakdowns, proposed suppression threshold 10, no individual drill-down | Before NEW-08 API/retention and privacy review; threshold alone does not guarantee anonymity |
|
||||
| OD-10 Job-seeker intent intake | Optional, skippable desired role/work preferences vs retaining only current onboarding checklist | Before NEW-13 profile/personalization storage |
|
||||
| OD-11 Weekly-goal measurement | Recommend optional user-chosen application target, disabled until set; broader effort goals need their own recorded facts | Before NEW-05. No universal default number or punitive state |
|
||||
|
||||
Ordinary route grouping, MUI reuse, current language defaults and current providers are answered by the repository and this scope; they do not require more questions. Current English fallback with EN/NB authenticated preference stays. No implicit change to Norwegian-first market focus or requirement for new users to speak English.
|
||||
|
||||
## Stable new-requirement registry
|
||||
|
||||
NEW IDs do not replace or renumber PRES IDs. “Required” means part of the target brief, not approved to implement in Phase 3.
|
||||
|
||||
| ID | Requirement / classification | Existing boundary / target implementation owner |
|
||||
|---|---|---|
|
||||
| NEW-01 | Dashboard order/resize/reset and persisted layout — required | Five visibility flags already exist; Phase 8, OD-04 |
|
||||
| NEW-02 | Expanded command entities/actions — required, staged by data availability | Jobs/companies + six commands exist; command owner |
|
||||
| NEW-03 | User-configured status definitions/mapping — required target | Raw custom values exist; new server/UI semantics needed before configuration |
|
||||
| NEW-04 | Per-task due dates/reminders — required target | Checklist has no due dates; separate scheduling contract |
|
||||
| NEW-05 | Optional editable/disableable weekly goals — required target | No goal preference exists; OD-11 and period definition |
|
||||
| NEW-06 | Scheduled interviews / broader calendar events — conditional future | Only job follow-up/deadline event actions and ICS proven |
|
||||
| NEW-07 | Shared contextual AI panel/access — required | Existing domain actions/review retained, no general chat engine |
|
||||
| NEW-08 | Aggregate admin product dashboard — required target, metric-qualified | Operational admin exists; new authorized aggregation/definitions, OD-09 |
|
||||
| NEW-09 | Favourite/priority — conditional proposal | No job-level field found; do not reuse operation Priority |
|
||||
| NEW-10 | Active quick-preview host — optional UX improvement | Reusable workspace props exist, production host absent; read-only proposal |
|
||||
| NEW-11 | Bounded cross-job document/task/activity/metric reads — required where target needs them | Existing job-scoped functions are not global query endpoints; separate read projections |
|
||||
| NEW-12 | Manual CV preview zoom — optional incremental control | Automatic fit/page boundaries already exist |
|
||||
| NEW-13 | Intent-led onboarding and unsupported career preferences — conditional proposal | Existing onboarding/completeness protected; no inferred personal facts |
|
||||
| NEW-14 | Comfortable/Compact application density — required | Distinct from current CV document density and table page size |
|
||||
| NEW-15 | New logo/final identity — required logo; rename/color undecided | Keep existing assets/name until OD-01–03 |
|
||||
| NEW-16 | Consistent AI source explanation and unsupported-claim checks — required safety target | Existing review/prompt context is partial evidence, not universal factual grounding/provenance |
|
||||
|
||||
## Contradictions resolved against source
|
||||
|
||||
| Original suggestion / ambiguity | Source-backed resolution |
|
||||
|---|---|
|
||||
| Calendar integration described as wholly future | ApplicationCalendarActions already creates Google/Outlook follow-up/deadline events and ICS. Preserve these; broader scheduled events are new |
|
||||
| Choose email provider as though mail does not exist | SMTP account/reminder delivery plus Gmail/Graph/IMAP connections exist; deployment provider remains open, no replacement here |
|
||||
| “Active applications” and conversion funnel | stats.active = non-deleted jobs; current-stage counts ≠ historical conversions. Use precise catalogue definitions |
|
||||
| Upcoming interviews widget | Interview-prep task/status is not a dated appointment; new schedule data required |
|
||||
| Shared quick overlay already in production | Phase 2 call-site check finds only full-page host. Preserve reusable props; add/test real preview deliberately |
|
||||
| Short CV customization list | Full C08/C09 controls stay under Advanced; no silent removal |
|
||||
| Suggested stages include Accepted/Screening/Final Interview | Preserve ten current stages, Waiting/Ghosted and raw custom values; new mapping needs explicit contract |
|
||||
| Career Profile versus account Profile | /career owns canonical facts; /profile owns identity/security, organized under Settings |
|
||||
| Simplified menu omits mail, companies, offers, reminders/operations | Each has an explicit primary/secondary/utility home; none deleted |
|
||||
| CV “used by jobs” suggests arbitrary many-to-many reuse | Current CvVariant has optional single job reference; attach may repoint, detach preserves CV |
|
||||
| API metadata implies native package success | P2-D01 capture/tailoring fails on SQLite; expected failure is not frozen-package proof |
|
||||
| Earlier Phase 1 docs say TypeScript gate missing | Phase 2 added gate; TS2571 corrected earlier. Remote gate execution still unverified |
|
||||
| Phase 2 C06 gap row calls the preview issue F15 | The validated accessibility finding and queue A01–A03 are F14; use F14 here. Preserve the historical row rather than renumbering audit findings |
|
||||
| Phase 1 describes TanStack Table as used | Dependency exists, but current src search finds no @tanstack/useReactTable/useTable import or call. JobTable uses MUI Table directly; preserve its actual owner, not an assumed engine |
|
||||
| Latest brief mentions pre-Phase-4 layout work | Phase 4 remains shared foundations; broad shell/screens are Phase 5 after contracts/gates |
|
||||
|
||||
## Separately recorded baseline debt
|
||||
|
||||
**P3-O01 — analytics presentation semantics, source-confirmed limitations.** Dashboard currently labels non-deleted tracked count as active applications, an interview-prep count as upcoming, and uses datasets with different windows. Source: DashboardView, AnalyticsService, JobApplicationAnalyticsController. Future chart migration must use definitions in [Dashboard architecture](DASHBOARD_ARCHITECTURE.md), explicitly separate snapshots/cohorts/periods and avoid summing truncated breakdowns. This is not a new production incident or a fix performed in Phase 3. Aggregate data quality and timezone corrections remain separately scoped.
|
||||
|
||||
**P3-O02 — navigation metadata retention, source observation.** ApplicationWorkspace go() replaces search with only section, so followMode may be lost; QuickCommandDialog maps fetch failure to empty results. Preserve current successful workflows but target explicit query retention and error states when those owners migrate. No incident, severity escalation or implementation is claimed.
|
||||
|
||||
Keep [P2-D01/P2-O01 and intentional gaps](../phase-2/BASELINE_DEFECTS.md) separate. F01/data concurrency, D04 creation idempotency, timezone, storage/recovery, billing and worker/release issues retain the [validated queue](../../audits/2026-09-07/validation/implementation-queue.md) owners. No schema before restore/rollback readiness. **F04 remains open** pending actual ingress runtime/production evidence; no operator/credential action was taken in this phase.
|
||||
@@ -0,0 +1,88 @@
|
||||
# Phase 4 implementation plan
|
||||
|
||||
**Planning only. Phase 4 has not started.** This package is a documentation checkpoint from d6d3dd9. The next phase establishes shared foundations; broad navigation/layout migration stays Phase 5 under the original redesign sequence. No B07 work is included.
|
||||
|
||||
## Entry conditions
|
||||
|
||||
- Phase 3 route/impact plan is the reviewed working contract; retain all 57 PRES IDs and 31 existing patterns.
|
||||
- Current baseline is available: 916 backend, 313 frontend; general browser **23 pass + 1 documented expected failure**. No tests/gates removed.
|
||||
- Select the exact shared consumer/state fixture before editing. Gaps in [Phase 2](../phase-2/BASELINE_DEFECTS.md) remain explicit.
|
||||
- Brand, providers and cross-device layout decisions do not block the first semantic accessibility slice. Keep current identity and storage behavior.
|
||||
- No schema or persistence redesign here. R03 restore/rollback proof gates schema; D01–D03 govern editor/job revision semantics. B02 identity and creation idempotency remain distinct.
|
||||
- Local foundation work can proceed without production access. Production rollout still needs required CI and F04 ingress evidence; a feature-branch push is not deployment approval.
|
||||
|
||||
## Session-sized foundation queue
|
||||
|
||||
| Ticket / scope | Owned surface and dependencies | Regression/UX evidence | Risk / rollback |
|
||||
|---|---|---|---|
|
||||
| **P4-01 Semantic state and focus foundation** | Existing theme.ts; narrowly required theme typing/shared MUI overrides; existing browser/Jest fixtures. Coordinate with validated A01, do not duplicate it | Loaded button/chip/tab/field/alert state pairs in both modes; visible keyboard focus; preserve filled-button contrast and EN/NB labels | Broad visual reach, no schema. Revert this isolated theme/test slice if necessary; no data migration |
|
||||
| **P4-02 Named controls and modal state** | RichTextField/actual unlabelled Select call sites plus confirm/prompt/ViewStateNotice as evidenced. After P4-01; inventory callers first | Labels/helper/error links, draft retained on error, dialog focus/return, loading announcements; no mass form rewrite | Small caller changes; revert per shared primitive, retain all behavior tests |
|
||||
| **P4-03 Builder preview keyboard boundary** | Actual preview region in CvBuilderEditor and focused browser fixture; coordinate validated A02. Serialize with D01; no editor save refactor | Loaded one/multipage preview, keyboard entry/scroll/exit and mobile→desktop transition; edit/preview/undo state retained | Touches stateful editor; stop if change requires persistence redesign. No schema |
|
||||
| **P4-04 Page layout and typography primitives** | Theme/page variables plus smallest PageHeader/PageSection composition needed by existing consumers. After P4-01/02; use current hue/mark | One h1, long NB copy, 320 px reflow, reading/data/editor width, no hidden actions | Limit to one representative existing consumer, not all pages; revert primitive/consumer together |
|
||||
| **P4-05 Table/list density contract** | Theme Table styles and a bounded existing table consumer; typed Comfortable/Compact preference scoped explicitly. After P4-04 | Row selection/link/action isolation; readable values at both densities; touch hit regions; existing column/page prefs retained | No replacement table library. If preference API/schema is needed, separate that decision instead of broadening this slice |
|
||||
| **P4-06 Shared validation checkpoint** | Tests/docs only plus separately ticketed defects; after combined foundations | Loaded EN/NB × light/dark × mobile/desktop, keyboard/manual preview/form, full suites/type/lint/build and PRES traces | Do not mask failures. Foundation accepted before Phase 5 shell/pages |
|
||||
|
||||
Do not scaffold every component in COMPONENT_ARCHITECTURE.md during Phase 4. Command expansion, aggregate dashboards, document query APIs, status settings, scheduled tasks and full AI panel are later feature slices, not required to establish tokens/names/focus. A shared AI diff frame can be extracted when the first domain migrates and its validation remains intact.
|
||||
|
||||
## First implementation handoff: P4-01 only
|
||||
|
||||
Problem: F14 identified semantic small-text contrast and focus debt across shared states. Current theme and shell/page literals can produce inconsistent pairings; replacing MUI is not required.
|
||||
|
||||
Objective: make the chosen existing MUI state pairs readable and keyboard focus clear in light/dark, using the current provisional brand hue. Inspect actual rendered default/hover/focus/selected states for Chips, outlined/contained Buttons, Tabs and Alerts. If a failure lives in one local hardcoded style, include only that directly responsible override with caller evidence.
|
||||
|
||||
Start with theme.ts and existing theme/accessibility test owners. Add a populated fixture through the existing test app/environment; no new public test route, component gallery framework or production data. Verify real foreground/background/icon/focus pairs. Preserve labels, action outcomes and enabled/disabled behavior. Do not merely test that token names exist or choose new arbitrary global colors.
|
||||
|
||||
**Exclude:** App route/provider changes, navigation grouping, sidebar rewrite, dashboard cards/layout, CV save/preview extraction (separate P4-03), final logo/color/font choice, API/schema, dependencies, billing, B07. Do not silently bundle all of A01–A03.
|
||||
|
||||
Acceptance:
|
||||
1. Original targeted contrast/focus failure reproduced or explicitly matched to prior evidence; fixed at the responsible shared layer.
|
||||
2. Representative loaded fixtures verify both themes and active states, including no regression to filled-button foregrounds.
|
||||
3. Keyboard focus visible; names/semantics/actions preserved in EN/Bokmål.
|
||||
4. Related Jest/browser checks, standalone TypeScript, lint and production build pass; full frontend suite remains at least the unchanged 313 plus intentional additions.
|
||||
5. Backend/B01–B05 contracts unchanged; execute relevant suite at checkpoint if shared tooling/API changes occur. Do not claim fresh full backend run without executing it.
|
||||
6. Document actual PRES-E08 and affected consumer evidence, source/contrast measurement, changed files and any remaining manual gaps. Commit separately, review before P4-02/next surface.
|
||||
|
||||
## Foundation stop conditions
|
||||
|
||||
Stop the affected slice if it requires a new component library, HTTP/auth change, schema, broad state rewrite or unresolved product semantics. Do not expand a theme fix into routing or all page styling.
|
||||
|
||||
Do not proceed to broad representative migration with failing named/focus/contrast states. P2-D01 must be independently repaired and successful native package/archive tests obtained before package/tailoring restructuring. Do not co-edit CvBuilderEditor with D01, even if one change seems visual. A mounted save queue remains insufficient against stale browsers.
|
||||
|
||||
No hiding functions, test exclusions, CSS overflow clipping or broad axe suppression to create apparent parity. Unexpected known-baseline recurrence is recorded separately, with affected work paused as necessary.
|
||||
|
||||
## Later sequencing, without starting it
|
||||
|
||||
1. Review P4 foundation evidence.
|
||||
2. Phase 5 shell/navigation grouping, retaining existing routes/provider tree; add drawer/account/command gap tests first.
|
||||
3. Jobs Table/Board owner and /kanban alias in one scoped migration after filter/query parity. Keep discovery/companies/offers/reminders reachable.
|
||||
4. Existing-data dashboard/Analytics presentation with accurate definitions. New metrics do not block relocating already available charts.
|
||||
5. Job workspace sections **after** package/native and relevant integrity gates; CV editor **after** revision/focus gates. Keep mail-send owner isolated.
|
||||
6. Phase 6 review, then Phase 7 remaining routes using each PRES trace.
|
||||
7. Phase 8 customization/new data requirements only after relevant product decisions and backend/recovery readiness.
|
||||
8. Full QA/release evidence; cleanup only with proven unreferenced code and explicit preservation sign-off.
|
||||
|
||||
## Safe coordination
|
||||
|
||||
Theme fixture planning and backend baseline defect investigation can be independent if separately authorized and owned. Source/metric definition documentation can proceed independently from UI primitives. Do not concurrently mutate theme.ts or App.tsx from several visual sessions.
|
||||
|
||||
Serialize route helper/App/private-tree changes; serialize table/board integration at its shared owner. Serialize D01 with preview/editor extraction. Serialize document index work with package persistence changes only after their contracts settle. Mail send/draft state is never a generic document-form refactor.
|
||||
|
||||
No backend security queue is automatically authorized by this planning sequence. F04 and B07 retain their existing scope/approval boundaries.
|
||||
|
||||
## Phase 3 checkpoint validation
|
||||
|
||||
Method: inspect the current application source and model/controller contracts; compare against Phase 1 full ledger/routes and Phase 2 assertion/gap mapping. Read the product specification package. Validate local Markdown/source links, complete ID/route coverage, new-requirement catalogue, representative existing test names and documentation-only Git diff. No rendered visual prototype or production visit was necessary to validate a documentation-only plan.
|
||||
|
||||
Commands were run from repository root using PowerShell/Python/Git. Full application suites were not rerun solely for prose changes; their unchanged Phase 2 evidence is reused with that limitation. Source-backed observations P3-O01/P3-O02 are recorded, not repaired.
|
||||
|
||||
| Executed command / review | Result |
|
||||
|---|---|
|
||||
| git switch -c codex/ui-redesign-phase3 d6d3dd9 | Dedicated branch created at the requested clean checkpoint |
|
||||
| python - (inline document/PRES/route/link validator) | 19 required deliverables; 57 unique PRES rows exactly match the original ledger; 31 routes exactly match current App.tsx; 16 ARC and 16 NEW definitions; 39 widget IDs; all 73 local package links resolve |
|
||||
| python - (inline Markdown table and named-test checks) | Balanced fences and consistent table columns; named existing regression references checked against Phase 2/current test files |
|
||||
| rg / Get-Content current source and model review | Confirmed route/query/provider boundaries, current statuses, canonical/variant relationship, widget field availability and source-backed limitations; corrected draft references before completion |
|
||||
| git diff --check and git diff --cached --check | Passed; no whitespace errors |
|
||||
| git diff --cached --name-only / --stat | 22 Markdown files only: 19 package files, root index, traceability link and TODO; no app/test/build/CI/schema changes |
|
||||
| python scripts/supply-chain.py scan-secrets | Passed on the staged documentation; no high-confidence credential patterns found |
|
||||
|
||||
This proves architecture-document completeness and consistency with inspected source. It does not prove a future migration, manual usability, AI factual quality, provider operation, remote CI or production ingress. Commit/push status is reported with the final checkpoint SHA; no deployment is performed.
|
||||
@@ -0,0 +1,106 @@
|
||||
# Preservation impact and traceability
|
||||
|
||||
Every row preserves the **complete original capability group**, not just the shorthand below. Authority: [Phase 1 ledger](../audit/FUNCTIONALITY_INVENTORY.md), [Phase 2 tests/gaps](../phase-2/PRESERVATION_MATRIX.md) and [traceability contract](../phase-2/TRACEABILITY.md). All 57 IDs retain meaning. No deprecation is proposed.
|
||||
|
||||
## Architectural recommendation index
|
||||
|
||||
| Recommendation | Planned change / existing behavior retained | Affected groups | Phase 4 or later validation |
|
||||
|---|---|---|---|
|
||||
| ARC-01 [Information architecture](INFORMATION_ARCHITECTURE.md) | Group current capabilities, retain every low-frequency destination | All 57 | Full destination ledger and role visibility; no removed function |
|
||||
| ARC-02 [Routes](ROUTE_PLAN.md) | Keep 31 patterns; staged Board alias and precise old section mapping | E01–E09, J01–J10, W01–W12, C01–C12, M01–M06, S01–S05, A01–A03 | Real App entries/back/refresh/capture/dirty/account switch |
|
||||
| ARC-03 [Shell](APP_SHELL.md) | Reuse collapse/drawer/commands/private-tree lifetime | E07–E09, S02–S04, A01–A03 | Focus, current parent, identity disposal, theme/locale |
|
||||
| ARC-04 [Personal dashboard](DASHBOARD_ARCHITECTURE.md) | Preserve current charts/visibility; data-qualified expansion | S01, S02, W02–W12, C05 | Exact metric denominators/source; owner layout and nonavailable states |
|
||||
| ARC-05 [Admin dashboard](DASHBOARD_ARCHITECTURE.md) | Separate new aggregate view from current admin actions | A01–A03, S04 | Server authorization/aggregate-only response; no individual drill-down |
|
||||
| ARC-06 [Jobs](JOBS_WORKSPACE.md) | Table/Board one view owner; existing filters/statuses/bulk retained | J01–J10, W01, S03 | Saved views, filtered parity, partial failure, restore, keyboard |
|
||||
| ARC-07 [Job workspace](JOB_WORKSPACE.md) | Consolidate sections without changing artifact/send ownership | W01–W12, M01–M05, C05–C12 | Native package success, source relationships, dirty/send/history |
|
||||
| ARC-08 [Career](CAREER_PROFILE_UX.md) | Explicit canonical source and import review | C01–C04, C07, C10, C11, E06, E09 | Source/override/reset, import accept/discard, owner denial |
|
||||
| ARC-09 [CV](CV_WORKSPACE.md) | Clean editor/preview and advanced disclosure, full controls retained | C05–C12, W05, W07 | Pagination/preview focus, history, AI no-write, canonical isolation |
|
||||
| ARC-10 [Documents](DOCUMENTS_UX.md) | Index existing artifacts; no replacement store | W05–W08, C05–C12, M04, S05 | Owner list/download/attach, current vs frozen, immutable bytes |
|
||||
| ARC-11 [AI](AI_INTERACTION_MODEL.md) | Contextual reviewed suggestions with explicit target | W03, W05, W06, W09, W10, C03, C04, C11, S04, E09 | Accept/reject, stale/malformed/context switch, source eval |
|
||||
| ARC-12 [Commands/notices/tasks](COMMANDS_NOTIFICATIONS_TASKS.md) | Retain command, reminder, inbox and provider distinctions | E07–E09, J01–J04, J08, W04, W11, W12, M01–M06, S02–S04 | No-write command, task vs notice, real send safeguards |
|
||||
| ARC-13 [Responsive](RESPONSIVE_STRATEGY.md) | Adapt existing mobile workflows without losing features | All 57 | Breakpoint dirty/focus preservation; real touch/zoom |
|
||||
| ARC-14 [Design system](DESIGN_SYSTEM_ARCHITECTURE.md) | Refine MUI tokens/states, keep stack/identity | All 57; E08 shared owner | Loaded mode/locale/contrast/state fixtures |
|
||||
| ARC-15 [Components](COMPONENT_ARCHITECTURE.md) | Small extractions with domain state kept intact | All 57 | Consumer regression; no duplicated network/save owner |
|
||||
| ARC-16 [Accessibility](ACCESSIBILITY_PLAN.md) | Structural focus/naming/contrast first | All 57 | Loaded axe + keyboard + manual assistive technology |
|
||||
|
||||
Ranges above use existing PRES aliases (e.g. E07 = PRES-E07); they never define new IDs. New capabilities use NEW-01–NEW-16 in Open decisions. Their presence does not count as implemented preservation.
|
||||
|
||||
## 57-group change-impact matrix
|
||||
|
||||
“Retained” = **yes, required by this plan**, not a claim that a migration has executed. Baseline test names below identify representative Phase 2 evidence; the linked Phase 2 row includes exact paths, other tests and known limits. New verification described here is work for the phase touching that surface. Phase 4 only exercises relevant shared foundations; it does not restructure all these workflows.
|
||||
|
||||
| PRES ID | Existing behavior → proposed UX / destination | Retained? / impact | Representative existing evidence → required migration proof |
|
||||
|---|---|---|---|
|
||||
| PRES-E01 | Product/Free–Pro/unavailable billing → public /, later brand presentation | Yes; public copy later | landing-page, planCatalog → EN/NB/mobile/locked availability, no invented purchase flow |
|
||||
| PRES-E02 | Login/register/remember/reset/verify → same public routes, shared form states | Yes; shared field impact | login-page, verify-email-page, LocalAuthIdentityTests → loaded errors, token submit, safe redirect/provider config |
|
||||
| PRES-E03 | Provider login/link/relink → same routes under account entry | Yes; no provider redesign | microsoft-legacy-relink-page, GoogleTokenValidatorTests, MicrosoftTokenValidatorTests → real sandbox popup/origin/revoke/relink |
|
||||
| PRES-E04 | 2FA/recovery/trusted device → Settings security at existing /profile | Yes; nested form preservation | two-factor-settings, TwoFactorControllerTests → focus and full recovery/revoke traversal |
|
||||
| PRES-E05 | Sessions/revoke/logout → Settings security and account menu | Yes; high-risk shell lifetime | SessionsControllerTests, account-isolation browser → actual session card/revocation + private disposal |
|
||||
| PRES-E06 | Avatar/name/email/password → /profile under Settings Account | Yes; distinct from Career Profile | profile-page, AvatarStorageTests → upload/remove/password/email-change and cross-account denial |
|
||||
| PRES-E07 | Collapse/mobile/active nav/commands → grouped shell, expanded existing palette | Yes; first shell migration | sidebar-active-nav, quick-command-preservation, browser keyboard → every logical parent, drawer focus, collapse/dirty preferences |
|
||||
| PRES-E08 | Theme/EN–NB/feedback/error routes → semantic tokens and common states | Yes; Phase 4 direct impact | theme-state, i18n-provider, confirm/prompt → loaded schemes/locales/reduced motion/error route |
|
||||
| PRES-E09 | Account-switch/stale read/write/BFCache handling → unchanged boundary for all new surfaces | Yes; mandatory regression | session-boundary, SessionContextMiddlewareTests, account-isolation → stale command/panel/draft/results cannot cross identity |
|
||||
| PRES-J01 | NAV discovery/filter/source details → Jobs Discover at /discover | Yes; relocate navigation | job-discovery, JobDiscoveryControllerTests → loaded search/error/mobile; NAV production availability separate |
|
||||
| PRES-J02 | Saved search/run/delete/alerts/dismiss → same Discover workspace | Yes; no new store | job-discovery, SavedJobSearchAlertTests → delete/alert-to-centre/reload and owner isolation |
|
||||
| PRES-J03 | Manual/URL/paste preview/company/tags/date create → shared Add/import action | Yes; reviewed create unchanged | quick-capture, JobImportHttpTransportTests → preview no write, duplicate/company/date failure; B01 intact |
|
||||
| PRES-J04 | Extension/bookmarklet/share + post-create artifacts → same capture contract | Yes; route sensitivity | captureUrl, quick-command-preservation, extension tests → real capture/review/optional attachment failure and no replay create |
|
||||
| PRES-J05 | Table/cards/filter/sort/page/columns/saved views → Tracked Table/Board | Yes; high-risk view owner | daily-control-loop, account-scoped-state, workspace browser → saved-view CRUD, filter parity/back/focus, long NB cards |
|
||||
| PRES-J06 | Edit/bulk status/delete/trash/restore → same actions and /trash utility | Yes; no hard-delete substitution | JobApplicationsAuthorizationTests, confirm-flow → native partial bulk failure, restore, selected scope |
|
||||
| PRES-J07 | Grouped pointer/keyboard board + exact status → /jobs?view=board after parity | Yes; delayed alias | kanban-grouped-board, pipeline → same filtered IDs, keyboard cancel and unknown custom group |
|
||||
| PRES-J08 | Company/contact/pipeline/date/related jobs → Jobs Companies, /companies?edit=id | Yes; no lost directory | companies-relationship, CompanyRelationshipTests → full contact persistence and command entry |
|
||||
| PRES-J09 | Offer compensation/decision/notes/source links → Jobs Offers at /offers | Yes; no calculator rewrite | offer-comparison, AnalyticsSalaryTests → many-offer mobile, currency/period and source links |
|
||||
| PRES-J10 | CSV/JSON export/import/conversion/counts → Settings Data plus Jobs link | Yes; formats unchanged | CsvExportTests, csv-export/browser → BOM/typed dates/formulas and native import counts, no full-restore claim |
|
||||
| PRES-W01 | Full workspace/aliases/optional props → new section map + optional read-only host | Yes; no active preview assumed | application-route-contracts, application-workspace-overlay, actual App browser → every alias, followMode, dirty/back/focus and new host |
|
||||
| PRES-W02 | Details/progression/next action/dates/contacts/history → header/Overview/Timeline | Yes; same records/stages | workflow-trust-signals, JobApplicationsAppliedDateHistoryTests → native edit/status/history, null applied date |
|
||||
| PRES-W03 | Analysis/match/strategy/reviewed status suggestions → Match section | Yes; context explained | application-intelligence, application-workflow-assist → loaded provider/error/source/freshness and explicit apply |
|
||||
| PRES-W04 | Checklist CRUD/dismiss/complete/order/next step → Notes & tasks | Yes; due date remains new | application-checklist, ApplicationChecklistTests → keyboard/mobile persisted actions, manual vs auto |
|
||||
| PRES-W05 | Select/attach/create/tailor CV → Application → CV | Yes; relationship unchanged | application-assets, ApplicationAssetsTests → source context, foreign attach denied; native tailoring blocked by P2-D01 |
|
||||
| PRES-W06 | Letter/manual/AI review/history/dirty guard → Application → Letter | Yes; independent draft/save | application-assets AI reject, ApplicationAssetsTests → whole-browser accept/reject/restore/Cancel/Discard |
|
||||
| PRES-W07 | Answers/drafts/frozen packages/archived downloads → Application → Submission + Documents history | Yes; migration blocked | SubmittedApplicationPackageTests; browser expected failure → **P2-D01 native capture and post-source-mutation archived bytes required** |
|
||||
| PRES-W08 | Upload/drop/purpose/name/AI use/preview/download/delete → Documents section/index | Yes; owner-bound binary | attachments, AttachmentConsistencyTests → native rename/drop/download/delete; archived survival after W07 fix |
|
||||
| PRES-W09 | Manual/AI interview questions/answers/ready/remove → Interview Prep | Yes; explicit Add/review | interview-prep, InterviewPrepPersistenceTests → mobile keyboard/reload and no invented answer facts |
|
||||
| PRES-W10 | Debrief/follow-up draft/date/readiness → Interview Debrief | Yes; no scheduled meeting inferred | interview-prep, JobApplicationsFollowUpDraftTests → persisted debrief and followMode navigation |
|
||||
| PRES-W11 | Rules/reminders/notifications/ICS/Google/Outlook → Follow-ups + job calendar controls | Yes; supported providers retained | application-calendar, settings-view, ApplicationCalendarControllerTests → permission/ICS/timezone + provider sandbox; new event scope separate |
|
||||
| PRES-W12 | Operation pending/progress/fail/retry/cancel and notices → /operations utility + centre | Yes; operation identity retained | operations-page, OperationsControllerTests → no duplicate starts, result links/cancel/late response |
|
||||
| PRES-C01 | Full canonical profile and long-tail sections → Career Profile Edit | Yes; global scope explained | profile-page, CareerProfileControllerTests, account-isolation/browser → same-account save, A/B replay denial, all sections |
|
||||
| PRES-C02 | Overview/completeness/raw text/avatar/history/restore → Career overview/history | Yes; not a new score | career-workspace-page, CareerProfileServiceTests → native restore/projection, copy/raw accessibility |
|
||||
| PRES-C03 | Extract/rebuild/improve/reprocess/diff/low confidence/accept/discard | Yes; Import review workspace | profile-page, CvProfileDiffServiceTests, CvExtractionCoverageTests → loaded low-confidence/discard/cancel/provider failure; actual extraction corpus |
|
||||
| PRES-C04 | Sourced evidence CRUD/tags/truthfulness → Career Evidence | Yes; no invented skill bank | career-evidence, CareerEvidenceControllerTests → edit context/delete/owner isolation and source navigation |
|
||||
| PRES-C05 | CV library/create/copy/delete/gallery/publish/share/PDF → CVs | Yes; same IDs/slugs | cv-builder-page, CvBuilderTests/PublicCvControllerTests → template controls, publication lifecycle and owned links |
|
||||
| PRES-C06 | Studio tabs/live preview/fit/page/save/mobile modes → focused editor/preview | Yes; high-risk visual owner | cv-builder-deep-link, redesign-preservation browser → loaded preview focus/scroll, modes without remount |
|
||||
| PRES-C07 | Item/section selection/order/override/custom content → scoped section editor | Yes; all controls | cv-builder-deep-link, cvBuilder, canonical/sibling browser → reset/inherit/missing-source/reorder/hide |
|
||||
| PRES-C08 | Template-aware typography/header/skills/layout/photo/icons → Customize + Advanced | Yes; no control dropped | cv-builder-deep-link, cv-template-thumbnail, CvBuilderTests → supported/unsupported values and preview/PDF equivalence |
|
||||
| PRES-C09 | A4/Letter/density/margins/dates/language/presentations/columns → Advanced document controls | Yes; app density separate | cvBuilder, CvBuilderTests, existing multipage PDF browser → real multipage/grid/bubble cases in both document locales |
|
||||
| PRES-C10 | Undo/redo/save queue/dirty/history/restore → persistent editor state | Yes; F01 remains separate | cv-builder-deep-link, CvBuilderTests → browser history/undo/reload/failed save; no concurrency overclaim |
|
||||
| PRES-C11 | Section/document AI translate/improve/spelling/review → shared comparison frame | Yes; accept/reject retained | ai-section-assistant, cv-builder-deep-link → malformed IDs, changed source, rejected suggestion no mutation |
|
||||
| PRES-C12 | Private and published PDF/preview/searchable long docs → same export/public path | Yes; B03 preserved | PublicCvControllerTests, PdfRenderAdmissionTests → private denial, unpublish, all-render paths/busy recovery |
|
||||
| PRES-M01 | Job manual correspondence/search/delete/paste/import → Timeline Correspondence | Yes; log ≠ send | end-to-end-trust-loop, CorrespondenceControllerTests → current-job context and manual record mutation |
|
||||
| PRES-M02 | Gmail search/import/refresh/thread link/unlink/move → job Correspondence/inbox | Yes; relationship checks | correspondence-gmail-import, EmailOwnershipIntegrationTests → foreign thread/job denial and provider reconnect |
|
||||
| PRES-M03 | Global inbox/filter/paging/message/attachment info/job links → primary Correspondence | Yes; not hidden in Documents | correspondence-inbox-page, EmailControllerTests → native paging/detail/empty vs provider failure |
|
||||
| PRES-M04 | Compose/reply/provider/drafts/review/send/attempt uncertainty → same inbox domain | Yes; high-risk send boundary | EmailDraftPersistenceTests, EmailSendAttemptStoreTests → draft conflict, explicit send, unknown delivery, no auto-resend |
|
||||
| PRES-M05 | Review candidates/proposals/link/reject/keep/notes/sync/create → inbox Review | Yes; legacy alias retained | gmail-review-page, GmailControllerTests → old route/result/link/create confirmation and owner scope |
|
||||
| PRES-M06 | Google/Graph/IMAP status/connect/disconnect → Settings Connections | Yes; login vs mailbox distinction | email-provider-connections, provider tests → actual grants/reconnect/disabled capability; no unsupported send |
|
||||
| PRES-S01 | Counts/activity/funnel/company/tag/stage/salary/next steps → Dashboard + Analytics | Yes; precise labels, no fabricated metrics | daily-control-loop, StageAnalyticsTests → P3-O01 definitions, period/unknown/sample/error and data table |
|
||||
| PRES-S02 | Five owner visibility flags/onboarding/dismiss/bell/read/dismiss → layout migration + centre | Yes; new layout additive | onboarding-checklist, notifications-popover → preference migration/A-B/reset/corrupt storage before Phase 8 |
|
||||
| PRES-S03 | Rules/email/table/theme/language/preferences → Settings sections + context links | Yes; scopes retained | settings-view, browser responsive save → scenario fields/error retry/resize and separate document density |
|
||||
| PRES-S04 | AI privacy/usage/limits/Free–Pro/billing status/checkout/portal → Settings AI/plan | Yes; server authority | ai-usage-card, ProEntitlementAuthorizationTests, BillingControllerTests → honest unavailable/provider/entitlement, no invented trial |
|
||||
| PRES-S05 | Account export/encrypted backup/lifecycle/delete confirmation → Settings Data/privacy | Yes; recent auth retained | account-data-export, AccountDeletionTests, BackupControllerTests → safe confirmation/download and restore-readiness gap explicit |
|
||||
| PRES-A01 | Admin users/create/roles/reset/delete/last-admin protections → Admin Users | Yes; aggregate view does not replace tools | admin-users-page, UsersControllerTests, nonadmin browser → role revocation/denial/confirmation and no user-data analytics |
|
||||
| PRES-A02 | Audit list/detail/supported undo/job restore → Admin Audit | Yes; exact supported actions | admin-audit-page → native undo/restore/owner and confirmation; not generic mutation |
|
||||
| PRES-A03 | System health/build/database/AI/email/config/test/benchmark/maintenance → Admin System | Yes; separate from product counts | admin-system-page, MaintenanceJobDispatcherTests → privileged actions retained; secrets not in dashboard |
|
||||
|
||||
## Trace chain and readiness
|
||||
|
||||
For every subsequent change record:
|
||||
|
||||
**PRES-ID + original sub-workflow → ARC/NEW requirement → implementation commit and owner → named executed regression → visual/UX evidence → remaining gaps.**
|
||||
|
||||
Use a scoped trace sheet linked from [Phase 2 traceability](../phase-2/TRACEABILITY.md); the table above is planning impact only. Example, not completed work:
|
||||
|
||||
| PRES / requirement | Architecture | Implementation | Regression | Visual/UX |
|
||||
|---|---|---|---|---|
|
||||
| PRES-E08 accessible semantic states | ARC-14/16; P4-01 | Not started | Existing theme/feedback tests plus planned loaded contrast/focus fixture | Not performed |
|
||||
| PRES-J07 unified Board entry | ARC-02/06 | Not started, Phase 5 | Filtered parity, exact status/keyboard cancel and /kanban alias | EN/NB, themes, phone/desktop/touch pending |
|
||||
| PRES-W07 frozen package | ARC-07/10 | Blocked by P2-D01 | Native capture currently expected failure; post-capture assertions unproven | No migration approval |
|
||||
|
||||
**57/57 groups mapped, zero approved removals.** This is 100% architecture traceability, not 100% automated workflow coverage or implementation readiness. Phase 2 contains explicit provider, production, accessibility, visual and native SQLite gaps. Preserve them until evidence closes them.
|
||||
@@ -0,0 +1,53 @@
|
||||
# Phase 3 — UX architecture checkpoint
|
||||
|
||||
Based on **d6d3dd9**, on **codex/ui-redesign-phase3**, 2026-09-10. This package defines the target architecture; it does not implement it. No application code, routes, styling, APIs, schema, dependencies or CI configuration change in this phase. Phase 4 and B07 have not started. **B06 implementation complete; F04 remains open.**
|
||||
|
||||
## Authority and reading order
|
||||
|
||||
The [product brief](../ASTRA_HANDOFF.md), [original design direction](../MASTER_REDESIGN_SPEC.md), [Phase 1 full functionality ledger](../audit/FUNCTIONALITY_INVENTORY.md) and [Phase 2 preservation matrix](../phase-2/PRESERVATION_MATRIX.md) remain authoritative. This package resolves presentation choices using current source. It never replaces a PRES contract with its shorter summary. Later user instructions govern any conflict with the original illustrative brief.
|
||||
|
||||
| Document | Decision it supplies |
|
||||
|---|---|
|
||||
| [Information architecture](INFORMATION_ARCHITECTURE.md) | Grouped navigation and where every less-visible capability lives |
|
||||
| [Route plan](ROUTE_PLAN.md) | All 31 existing patterns, aliases, query/state contracts and staged additions |
|
||||
| [Application shell](APP_SHELL.md) | Existing shell reuse, account boundaries, headers and navigation behavior |
|
||||
| [Dashboards](DASHBOARD_ARCHITECTURE.md) | Personal layouts, data-qualified widget catalogue and separate aggregate admin |
|
||||
| [Jobs workspace](JOBS_WORKSPACE.md) | One table/board workspace, filters, statuses, bulk operations and preview |
|
||||
| [Job workspace](JOB_WORKSPACE.md) | Section ownership and optional guided application journey |
|
||||
| [Career Profile](CAREER_PROFILE_UX.md) | Canonical editing, extraction review and source propagation |
|
||||
| [CV workspace](CV_WORKSPACE.md) | Editor/preview, source overrides, advanced controls and history |
|
||||
| [Documents](DOCUMENTS_UX.md) | Federated discovery of existing artifacts and frozen relationships |
|
||||
| [AI interaction](AI_INTERACTION_MODEL.md) | Grounding, explicit review, destination scope and provider failures |
|
||||
| [Commands, notifications and tasks](COMMANDS_NOTIFICATIONS_TASKS.md) | Contextual commands and distinct task/event/message lifecycles |
|
||||
| [Responsive strategy](RESPONSIVE_STRATEGY.md) | Mobile task behavior and density |
|
||||
| [Design system](DESIGN_SYSTEM_ARCHITECTURE.md) | MUI tokens, states and bounded visual proposals |
|
||||
| [Component architecture](COMPONENT_ARCHITECTURE.md) | Retain/refactor/consolidate/add map |
|
||||
| [Accessibility](ACCESSIBILITY_PLAN.md) | Loaded-state and manual gates before repeating shared defects |
|
||||
| [Preservation impact](PRESERVATION_IMPACT_MATRIX.md) | All 57 contracts, recommendations and implementation evidence chain |
|
||||
| [Open decisions](OPEN_DECISIONS.md) | Genuine product choices, contradictions and unavailable data |
|
||||
| [Phase 4 plan](PHASE4_IMPLEMENTATION_PLAN.md) | Small foundation tickets, dependencies and first slice |
|
||||
|
||||
## Scope classification
|
||||
|
||||
- **Existing / preserve:** all 57 PRES groups, including advanced CV controls, NAV alerts, correspondence delivery safeguards, owner-bound artifacts, Google/Outlook calendar actions, public CVs, account security and admin operations.
|
||||
- **Existing / improve UX:** grouped navigation, one Jobs view switch, clear job sections, source/override labels, coherent document entry, discoverable settings, shared error/focus presentation.
|
||||
- **New:** explicitly numbered NEW requirements in [Open decisions](OPEN_DECISIONS.md), such as dashboard layout order/size, broader commands, density, aggregate admin analytics and due-dated tasks. Availability and implementation phases differ; a candidate metric is not an available feature.
|
||||
- **Baseline defects / debt:** P2-D01 SQLite package/tailoring failure; F14 loaded accessibility; separate concurrency, idempotency, calendar and paid-lifecycle queues; failed remote CI browser runner; F04 runtime ingress evidence. Do not fix these incidentally or preserve their defective results as desired behavior.
|
||||
|
||||
## Selected architecture
|
||||
|
||||
Retain React Router inside the Next static SPA, MUI/Emotion, the existing auth/private-tree lifecycle and shared Axios client. Reorganize navigation without changing most paths. Keep a full job page and a focused optional read-only preview. Career Profile remains canonical; CV settings override one derived document; job drafts and frozen packages remain separate resources.
|
||||
|
||||
Use one main brand hue through semantic light/dark tokens; final identity is undecided. Prefer the current mobile drawer over adding a second navigation system. Personal dashboard customization starts with owner-scoped layout metadata; cross-device persistence needs an explicit choice before implementation. New aggregate admin metrics must be server-computed and privacy-bounded.
|
||||
|
||||
## Evidence and limits
|
||||
|
||||
Source validation includes App/AppShell, route helpers, Jobs/table/board, workspace/asset models, Career/CV owners, analytics controller/service/DTOs, account/provider/notification owners and the Phase 1/2 test evidence. [Open decisions](OPEN_DECISIONS.md) records corrections to illustrative requirements and source-backed metric limitations. No user study, fresh visual prototype, provider evaluation or production inspection is claimed.
|
||||
|
||||
The unchanged **Phase 2 executed baseline** is 916 backend and 313 frontend tests passing (Phase 1: 916 / 298). General browser results are **23 actual passes + 1 expected failure**, not 24 successful workflows. B04 HTTP 9, B06 native HTTP 6, PDF admission 1 and Compose validation 3 passed. Standalone TypeScript, lint and Release builds passed there. Those are reused results for unchanged application code, not freshly executed Phase 3 suites. Historical TS2571 is corrected; remote CI/production certification remains outstanding.
|
||||
|
||||
Completion checks and exact commands are recorded in [Phase 4 plan](PHASE4_IMPLEMENTATION_PLAN.md#phase-3-checkpoint-validation). These check document completeness, source/route/PRES references and documentation-only scope. No visual implementation is approved by a document passing validation.
|
||||
|
||||
## Next boundary
|
||||
|
||||
Recommended first implementation slice: **P4-01 semantic state and focus foundation** in the existing theme, with loaded regression fixtures. Keep existing brand identity, routes and page composition. Follow the bounded plan and stop for review before expanding to the shell or representative pages. Production release remains separately gated by CI and F04 evidence.
|
||||
@@ -0,0 +1,44 @@
|
||||
# Responsive strategy and density
|
||||
|
||||
ARC-13; all PRES contracts, particularly PRES-E07, PRES-E08, PRES-J05, PRES-J07, PRES-W01, PRES-C06–PRES-C10, PRES-M04, PRES-S03, PRES-A01. This is an interaction contract, not screenshots or completed visual validation.
|
||||
|
||||
## Breakpoints and content
|
||||
|
||||
Retain current MUI breakpoints (0, 768, 1024, 1266, 1440) as starting tokens. The existing CV two-pane boundary at 900 has a content reason; do not force it to match global navigation without testing. Mobile shell currently changes below 768. Document these two responsibilities rather than changing every media query at once.
|
||||
|
||||
Required viewport samples: 320 CSS px reflow, 390×844 phone, 430 px large phone, 768×1024 portrait tablet, 1024×768 landscape, 1280×800 laptop, 1440×900 desktop, 1920 wide. Also test just below/above 768 and 900. EN/Bokmål, light/dark, long names, missing data, many jobs/custom statuses and multipage CVs matter more than device labels.
|
||||
|
||||
## Task-specific behavior
|
||||
|
||||
| Surface | Narrow / touch | Wider layouts | Preservation condition |
|
||||
|---|---|---|---|
|
||||
| Navigation | Existing modal menu drawer; grouped sections, visible search/bell/account | Collapsible sidebar/rail | Same destinations and active owner; focus return; no duplicate bottom nav by default |
|
||||
| Jobs | Structured cards, essential role/company/status/date/action; filter sheet | Primary table, optional board | Same filters/records/actions; selection never opens row; no functionality removed by density |
|
||||
| Board | Scrollable grouped columns with explicit status menu and keyboard alternative | Same grouping/drag semantics | No mandatory drag or hidden custom/Other status |
|
||||
| Dashboard | Single-column user order; move/hide; no meaningless horizontal resize | Discrete spans and supported resize | Keep saved desktop sizes when phone ignores them |
|
||||
| Job workspace | Header wraps; labelled section selector if tabs overflow; actions easy to reach | Full page, readable tabs and section content | Preserve current section, dirty draft and return context through resize |
|
||||
| CV editor | Edit/Preview toggle; touch/keyboard reorder controls | Editor + scrollable paginated preview | No remount/data loss at 900 boundary; preview keyboard scroll works after transition |
|
||||
| Dialogs/drawers | Long forms full-height with reachable Save/Cancel above virtual keyboard | Bounded modal or side panel | One modal focus trap; no fixed-height clipping of errors |
|
||||
| Commands | Full-width dialog, labelled search and grouped list; keyboard doesn't cover selected result | Bounded dialog | Same command scope, owner clearing and activation behavior |
|
||||
| Filters | Draft filter sheet with Apply/Cancel; existing committed filters retained on cancel | Inline common filters, advanced disclosure | Show active filter count, never change result data before intended apply |
|
||||
| Charts | Short summary + accessible values table, limited visible labels | Chart with legend and comparison | Same period/denominator; no hover-only values |
|
||||
| Correspondence | Message/draft panes as stepwise views with clear Back | List/detail when usable | Unsaved/draft/revision/send attempt unchanged; full content readable |
|
||||
| Settings | Labelled section select/list and single-column fields | Secondary section navigation | Save failure retains draft, account/security/provider separation |
|
||||
| Admin | Label/value rows or labelled horizontal table region, actions explicit | Table and aggregate panels | No omitted permission/last-admin warnings, no leaked private analytics |
|
||||
| Offers | One comparison at a time with accessible labelled alternatives | Recorded offers side-by-side | Same currency/period/source, no invented normalized comparison |
|
||||
|
||||
A read-only job preview may become full height on phone or be omitted in favor of the full page, subject to validation. Existing full job functionality remains. Stacked tabs or tiny icon-only controls are not acceptable substitutes for navigation.
|
||||
|
||||
## Comfortable and Compact
|
||||
|
||||
NEW-14. Comfortable is default. Density changes row/field spacing and secondary metadata layout, never feature visibility, source semantics or font legibility. Propose table body vertical padding 12 px / 8 px, with sufficient control hit area. Compact on touch must retain at least the same target hit regions as Comfortable; it is not permission to shrink every icon.
|
||||
|
||||
Apply to Jobs/company/admin tables and suitable lists first. Do not compress CV document layout, long-form editor text, dialogs or charts globally. Keep a distinct application preference with the existing identity/preference model; scope and migration must be explicit. Old page-size/column preferences and owner-keyed saved filters continue to work.
|
||||
|
||||
## State, scrolling and validation
|
||||
|
||||
Resizing a viewport changes presentation, not component/resource identity. Keep draft text, selected object, filter state, review proposal and save operation. Never keep private state after B02 invalidates the session. Preserve document and page scroll independently; return focus to a meaningful element that is visible in the new mode.
|
||||
|
||||
Avoid global overflow hiding as a “fix.” Data tables/boards/doc previews may have labelled intentional internal scroll; the page at 320 CSS px must not require sideways scrolling to reach ordinary actions. Sticky headers/footers and software keyboards must not obscure focused input/error messages.
|
||||
|
||||
Each migrated surface needs populated/empty/loading/error/locked/dirty checks and manual keyboard, touch, 200%/400% zoom and screen reader evidence. Browser viewport emulation is useful but does not prove touch usability, virtual keyboard behavior or assistive technology compatibility.
|
||||
@@ -0,0 +1,91 @@
|
||||
# Route plan
|
||||
|
||||
ARC-02. This is a future migration plan, not live route configuration. Preserve the SPA/static deployment and current React Router authority. Most current URLs stay canonical: meaningful labels do not require renaming stable paths.
|
||||
|
||||
## All existing route patterns
|
||||
|
||||
“Keep” includes refresh, direct entry, back/forward and owner/role behavior. No existing public or callback URL is removed.
|
||||
|
||||
| Current pattern | Target and disposition | Navigation owner | PRES contracts |
|
||||
|---|---|---|---|
|
||||
| / | Keep public landing and authenticated redirect behavior | Public | PRES-E01 |
|
||||
| /login | Keep | Public/account entry | PRES-E02, PRES-E03, PRES-E04 |
|
||||
| /register | Keep configured registration | Public | PRES-E02 |
|
||||
| /forgot-password | Keep | Public | PRES-E02 |
|
||||
| /reset-password | Keep token query contract | Public | PRES-E02 |
|
||||
| /verify-email | Keep token query contract | Public | PRES-E02 |
|
||||
| /confirm-email-change | Keep pending identity confirmation | Public | PRES-E06 |
|
||||
| /microsoft-legacy-relink | Keep recovery/relink | Public/account | PRES-E03 |
|
||||
| /cv/:slug | Keep published-only page/PDF; never redirect to numeric ID | Public share | PRES-C05, PRES-C12 |
|
||||
| /dashboard | Keep personal dashboard | Dashboard | PRES-S01, PRES-S02 |
|
||||
| /discover | Keep, present as Jobs → Discover | Jobs | PRES-J01, PRES-J02 |
|
||||
| /jobs | Keep; add presentation query view=table or view=board | Jobs → Tracked | PRES-J03, PRES-J05, PRES-J06, PRES-J07 |
|
||||
| /jobs/:id | Keep; staged section plan below | Jobs → job | PRES-W01–PRES-W12, PRES-M01, PRES-M02 |
|
||||
| /reminders | Keep as Jobs → Follow-ups | Jobs | PRES-W11 |
|
||||
| /operations | Keep progress/retry/cancel; utility link | Notifications / user activity | PRES-W12 |
|
||||
| /kanban | Future replace redirect to /jobs?view=board after unified board parity | Jobs → Tracked | PRES-J07 |
|
||||
| /companies | Keep including ?edit=id | Jobs → Companies | PRES-J08, PRES-E07 |
|
||||
| /offers | Keep | Jobs → Offers | PRES-J09 |
|
||||
| /correspondence | Keep inbox/drafts/review workspace | Correspondence | PRES-M01–PRES-M06 |
|
||||
| /correspondence/review | Keep existing alias to /correspondence?view=review | Correspondence | PRES-M05 |
|
||||
| /profile | Keep account identity/security under Settings; no rename to Career Profile | Settings → Account/Security | PRES-E04, PRES-E05, PRES-E06 |
|
||||
| /applications/:id | Keep existing alias to /jobs/:id with query and router state | Jobs → job | PRES-W01 |
|
||||
| /career | Keep | Career Profile | PRES-C01–PRES-C04 |
|
||||
| /career/builder | Keep; label CVs | CVs | PRES-C05 |
|
||||
| /career/builder/:id | Keep identified CV editor | CVs → CV | PRES-C06–PRES-C12 |
|
||||
| /admin/audit | Keep privileged audit/undo | Admin → Audit | PRES-A02 |
|
||||
| /admin/users | Keep privileged user management | Admin → Users | PRES-A01 |
|
||||
| /admin/system | Keep operational diagnostics, configuration and maintenance | Admin → System | PRES-A03 |
|
||||
| /trash | Keep deleted-only semantics as Jobs utility | Jobs → Trash | PRES-J06 |
|
||||
| /settings | Keep; later add validated section query for shared account navigation | Settings | PRES-S03, PRES-S04, PRES-S05, PRES-J10 |
|
||||
| /settings/connected-accounts | Keep; separate login identity from mail/calendar authorization | Settings → Connections | PRES-M06, PRES-E03 |
|
||||
|
||||
31 existing patterns mapped: 29 pages and two existing aliases. /kanban is currently a page, not already an alias. Outer /*, inner * and the inner root redirect stay as routing/error infrastructure. Do not confuse them with user pages.
|
||||
|
||||
## New destinations and readiness
|
||||
|
||||
| Proposed path | Minimum meaningful content / prerequisite |
|
||||
|---|---|
|
||||
| /analytics | Existing analytics charts and definitions; source/sample/period shown. New metrics only after their data contract |
|
||||
| /documents | Owner-scoped artifact index with job/type/state links; cross-job listing/query availability gated in Documents plan |
|
||||
| /notifications | Expanded existing notification centre; read/dismiss actions and operation links, not a new notification store |
|
||||
| /admin | Aggregate dashboard after authorized count endpoints exist; until then keep existing admin children visible with no empty dashboard |
|
||||
|
||||
These routes are not introduced in Phase 4 foundations. No /cvs rename, /account rename or nested Jobs child-URL rewrite is necessary. A future desire for prettier URLs needs its own compatibility proposal.
|
||||
|
||||
## Job section compatibility
|
||||
|
||||
Current accepted keys are overview, analysis, cv, cover-letter, interview. Current legacy keys collapse to analysis or overview in applicationWorkspace.ts. Future mappings must reveal the intended content more precisely without losing current capabilities.
|
||||
|
||||
| Incoming key | Current resolution | Future destination |
|
||||
|---|---|---|
|
||||
| absent, overview, job-details | overview | Overview; details anchor for job-details |
|
||||
| analysis, match | analysis | Match (analysis + profile comparison + strategy) |
|
||||
| cv | cv | Application → CV |
|
||||
| cover-letter | cover-letter | Application → Cover letter |
|
||||
| interview | interview | Interview |
|
||||
| documents | overview | Documents |
|
||||
| timeline | overview | Timeline |
|
||||
| checklist | overview | Notes & tasks → checklist |
|
||||
| communication | overview | Timeline → correspondence/follow-up context |
|
||||
|
||||
Future canonical top sections: overview, match, application, documents, timeline, tasks, interview. application may use sub=cv, letter, answers, submission; timeline may use sub=activity or correspondence. This is a small navigation adapter; do not change API section strings or persistent checklist links in bulk.
|
||||
|
||||
Keep all numeric tab helper inputs: 0 overview; 1 correspondence; 2 attachments; 3 tailored CV; 4 follow-up; 5 candidate fit; 6 focus plan; 7 interview prep; 8 readiness; 9 history. Translate their existing section outputs through the table. Preserve followMode and open its actual compose/follow-up mode; do not silently drop it as current tab switching can. Unknown keys fall back safely to Overview without submitting or mutating data.
|
||||
|
||||
## Query, state and compatibility contract
|
||||
|
||||
- Preserve Jobs q, status, companyId, location, needsFollowUp, readiness, includeDeleted, sortBy, sortDir and page. Keep existing page-number interpretation. view changes only presentation. Board query parity must be tested before /kanban redirects.
|
||||
- Preserve return state from/focusJobId; return to exact filter/list context, restore focus or a safe nearby list heading if the row no longer exists. Only local safe return paths are accepted.
|
||||
- Preserve capture ?add= and ?addtext=, encoding, consumed-once review and optional post-create attachment/CV work. Do not put private draft bodies in new URLs or command history.
|
||||
- Preserve /companies?edit=id and correspondence ?view=review. Draft identity/revision stays with its service; adding a draft deep link must not create/resend a draft.
|
||||
- Preserve /career?section=overview, profile, evidence or import; missing/unknown selects Overview as today. Canonical editing and import-review entry must remain distinct. No CV editor query contract is invented from a test filename.
|
||||
- Keep public token URLs and backend OAuth callback/postMessage origin/state behavior. Do not migrate them through a generic route redirect.
|
||||
- Prefer React Router replace for compatibility aliases so Back does not loop. Preserve relevant query/hash/state; never trust URL account identifiers as authorization.
|
||||
- Keep aliases throughout migration and at least the first completed full rollout. There is **no approved removal date**. Removal requires explicit decision, observed old-link usage without private data, updated extension/bookmarklet/notifications and regression evidence. No redirect chains: every alias targets the canonical resolver directly.
|
||||
|
||||
## Titles, breadcrumbs and route validation
|
||||
|
||||
One h1 per page. Collection title: Jobs / Career Profile / CVs. Detail: role at company or CV name, safely text-rendered; browser title adds section and current Jobjakt name. Public token pages must not copy tokens into titles. Breadcrumbs on detail/Settings/Admin only when they provide navigation, e.g. Jobs → Role, CVs → CV name; status and tabs are not extra breadcrumb ancestors.
|
||||
|
||||
Phase 5 route changes require an actual App browser fixture for every migrated canonical/alias entry, loaded and missing/foreign objects, query retention, capture no-write, browser Back/refresh, dirty Cancel/Discard, focus return and account switch. Existing route-helper tests alone are insufficient. Test real static nginx fallback separately before deployment; F04 remains open.
|
||||
+12
-1
@@ -1,6 +1,17 @@
|
||||
# Redesign — 2026-09-09
|
||||
|
||||
- [x] Phase 2 from `2d0f131`, on `codex/ui-redesign-phase2`: [preservation checkpoint](../redesign/phase-2/README.md), 57 stable PRES groups mapped, 15 added component cases, six added browser cases, explicit standalone CI type gate and traceability. Fresh 916 backend / 313 frontend pass; browser 23 actual passes + one documented SQLite expected failure, B04/B06/PDF/config regressions pass. No application/visual redesign, B07 or F04 closure.
|
||||
- [ ] P2-D01 separate baseline repair before package/tailoring restructuring: SQLite DateTimeOffset ordering fails in native tailoring/capture. Retain the expected-failure browser oracle; prove native capture and unchanged historical binaries after source edits/deletion before removing annotation. P2-O01 initial rules 500 remains unconfirmed; direct and browser save controls pass. See [baseline evidence](../redesign/phase-2/BASELINE_DEFECTS.md).
|
||||
- [x] Read the Astra handoff and required product documents; complete Phase 1 source audit with 31 concrete route patterns, functionality preservation ledger, 266 API call sites, component/test inventory, identity/storage map, responsive/localisation assessment and migration gates. See `docs/redesign/audit/README.md`. No redesign implementation or B07 started.
|
||||
- [x] Finalize Phase 1 separately, 2026-09-10: classify all 57 preservation groups, UX improvements, genuinely new/conditional requirements and baseline defects; retain already-implemented CV drag/fit, custom status values, calendar and other protected workflows. Final summary/contract/test limits/Phase 2 starting point: `docs/redesign/audit/PHASE1_SUMMARY.md`. Documentation only; B06/F04 closure remains open.
|
||||
- [ ] Complete the authorized `main` release through required CI/deploy gates. Main `27657f3` is pushed; run 743 passed 916 backend/298 frontend tests, lint, dependency audits and production builds, then the browser gate failed (worker SIGSEGV, retry timeout and closed-browser errors; 6 passed/12 failed). Deploy skipped; live remains version 299. Stop further release retries pending runner-host diagnostics; exact read-only handoff and evidence are in `docs/audits/2026-09-07/validation/b06-completion.md`. No gate was bypassed.
|
||||
- [x] Phase 3 from `d6d3dd9` on `codex/ui-redesign-phase3`: [UX architecture package](../redesign/phase-3/README.md), 31 existing route patterns, 57 PRES impact rows, data-qualified personal/admin dashboards, canonical/CV/job/frozen-document distinctions, contextual AI review, responsive/accessibility/component plans and explicit decisions. Documentation only; no Phase 4/visual implementation or B07. F04 remains open.
|
||||
- [ ] Next redesign work, not started: [P4-01 semantic state/focus foundation](../redesign/phase-3/PHASE4_IMPLEMENTATION_PLAN.md) using existing MUI/theme and loaded regression fixtures. Keep current brand/routes/providers; coordinate validated A01. Do not begin broad shell/pages before foundation proof.
|
||||
- [ ] P3-O01/P3-O02 separately scoped presentation debt: accurate analytics population/period labels and navigation/query/error retention when those surfaces migrate. [Source observations and boundaries](../redesign/phase-3/OPEN_DECISIONS.md); no analytics, route or command behavior fixed during Phase 3.
|
||||
|
||||
# Expanded production audit — 2026-09-07
|
||||
|
||||
- [x] Authorized release repair, 2026-09-10: patch Next/sharp/js-yaml dependency advisories; preserve deliberate React Router full navigation and correct the historical TS2571 test assertion when the patched Next build makes it release-blocking. Local standalone tsc/build and 13 interview tests pass. R01a's explicit standalone CI command remains outstanding; deployment and F04 closure are separate gates in the B06 report.
|
||||
- [x] Authorized release repair, 2026-09-10: patch Next/sharp/js-yaml dependency advisories; preserve deliberate React Router full navigation and correct the historical TS2571 test assertion when the patched Next build makes it release-blocking. Local standalone tsc/build and 13 interview tests pass. R01a's explicit standalone CI command is now added in the separate Phase 2 checkpoint; deployed CI execution and F04 closure remain separate gates in the B06 report.
|
||||
|
||||
- [ ] B06 implementation complete; **F04 open, runtime/production verification incomplete**. Fresh 166 focused backend, 3 Compose, 6 Production HTTP and 9 B04 HTTP checks pass; prior broader/build evidence retained. Public DNS/valid HTTPS observed; ports 3000/5202/8080 unreachable from this workstation. HTTP redirects to unreachable HTTPS port 444. Docker inference-socket failure blocks real Compose/Traefik/nginx tests; actual peers/backend client IP and production host/firewall evidence require operator handoff (no documented SSH alias). See `docs/audits/2026-09-07/validation/b06-completion.md`. B01–B05 preserved; B07 not started.
|
||||
|
||||
|
||||
@@ -0,0 +1,188 @@
|
||||
import { expect, test as base, type APIRequestContext } from "@playwright/test";
|
||||
|
||||
const apiUrl = "http://localhost:5302/api";
|
||||
const password = "E2ePassword123!";
|
||||
const canonical = {
|
||||
version: "1", contact: { fullName: "Åse Eksempel", headline: "Canonical engineer" },
|
||||
jobs: [{ title: "Engineer", company: "Nordlys AS", bullets: ["Built reliable APIs"] }],
|
||||
skills: ["C#", "Norsk"],
|
||||
};
|
||||
|
||||
async function login(api: APIRequestContext, email: string) {
|
||||
expect((await api.post(`${apiUrl}/auth/login`, { data: { email, password } })).status()).toBe(200);
|
||||
const me = await (await api.get(`${apiUrl}/auth/me`)).json();
|
||||
const csrf = (await api.storageState()).cookies.find(cookie => cookie.name === "XSRF-TOKEN")!.value;
|
||||
return { "X-CSRF-TOKEN": csrf, "X-Session-Context": me.sessionContext as string };
|
||||
}
|
||||
|
||||
// Every case gets a real non-admin account in Playwright's disposable local API database.
|
||||
// No API responses are mocked; browser traffic cannot leave the local test environment.
|
||||
const test = base.extend<{ headers: Record<string, string> }>({
|
||||
headers: async ({ playwright, page, context }, provide) => {
|
||||
const admin = await playwright.request.newContext();
|
||||
try {
|
||||
const adminHeaders = await login(admin, "e2e@example.test");
|
||||
const email = `pres-${Date.now()}-${Math.random().toString(36).slice(2)}@example.test`;
|
||||
expect((await admin.post(`${apiUrl}/users`, { headers: adminHeaders, data: { email, password, roles: [] } })).status()).toBe(200);
|
||||
const headers = await login(page.request, email);
|
||||
await context.route("**/*", route => {
|
||||
const url = new URL(route.request().url());
|
||||
return ["localhost", "127.0.0.1"].includes(url.hostname) || ["data:", "blob:", "about:"].includes(url.protocol)
|
||||
? route.continue() : route.abort();
|
||||
});
|
||||
await context.addInitScript(() => localStorage.setItem("uiLanguage", "en"));
|
||||
expect((await page.request.put(`${apiUrl}/career/profile`, { headers, data: { profile: canonical } })).status()).toBe(200);
|
||||
await provide(headers);
|
||||
} finally { await admin.dispose(); }
|
||||
},
|
||||
});
|
||||
|
||||
test("[PRES-C01 PRES-C06 PRES-C07 PRES-C10] mobile CV override persists without changing the canonical profile or sibling CV", async ({ page, headers }) => {
|
||||
const create = async (name: string) => {
|
||||
const response = await page.request.post(`${apiUrl}/cv/variants`, { headers, data: { name } });
|
||||
expect(response.status()).toBe(200);
|
||||
return response.json();
|
||||
};
|
||||
const selected = await create("Selected CV");
|
||||
const sibling = await create("Sibling CV");
|
||||
const profileBefore = await (await page.request.get(`${apiUrl}/career/profile`)).json();
|
||||
await page.setViewportSize({ width: 390, height: 844 });
|
||||
await page.goto(`/career/builder/${selected.id}`);
|
||||
await page.getByRole("tab", { name: "Content", exact: true }).click();
|
||||
await page.getByLabel("Headline override", { exact: true }).fill("Reviewed CV-only headline");
|
||||
await page.getByRole("button", { name: "Save now", exact: true }).click();
|
||||
await expect(page.getByText("Saved", { exact: true })).toBeVisible();
|
||||
await page.getByRole("button", { name: "Preview", exact: true }).click();
|
||||
await expect(page.getByTitle("CV preview page 1")).toBeVisible();
|
||||
await page.getByRole("button", { name: "Edit", exact: true }).click();
|
||||
await expect(page.getByLabel("Headline override", { exact: true })).toHaveValue("Reviewed CV-only headline");
|
||||
await page.reload();
|
||||
await page.getByRole("tab", { name: "Content", exact: true }).click();
|
||||
await expect(page.getByLabel("Headline override", { exact: true })).toHaveValue("Reviewed CV-only headline");
|
||||
await page.setViewportSize({ width: 1440, height: 900 });
|
||||
await expect(page.getByTitle("CV preview page 1")).toBeVisible();
|
||||
expect((await (await page.request.get(`${apiUrl}/cv/variants/${selected.id}`)).json()).settings.headline).toBe("Reviewed CV-only headline");
|
||||
expect(await (await page.request.get(`${apiUrl}/cv/variants/${sibling.id}`)).json()).toEqual(sibling);
|
||||
expect((await (await page.request.get(`${apiUrl}/career/profile`)).json()).profile).toEqual(profileBefore.profile);
|
||||
});
|
||||
|
||||
test("[PRES-W01 PRES-W05 PRES-W06 PRES-W07 PRES-W08] legacy workspace preserves frozen documents after current sources change", async ({ page, headers }) => {
|
||||
const companyResponse = await page.request.post(`${apiUrl}/companies`, { headers, data: { name: "Preservation AS" } });
|
||||
expect(companyResponse.status()).toBe(201);
|
||||
const company = await companyResponse.json();
|
||||
const jobResponse = await page.request.post(`${apiUrl}/jobapplications`, { headers, data: { companyId: company.id, jobTitle: "Preserved workspace", status: "Saved" } });
|
||||
expect(jobResponse.ok()).toBe(true);
|
||||
const job = await jobResponse.json();
|
||||
const variantResponse = await page.request.post(`${apiUrl}/cv/variants`, { headers, data: { name: "Submitted CV", jobApplicationId: job.id } });
|
||||
expect(variantResponse.status()).toBe(200);
|
||||
const variant = await variantResponse.json();
|
||||
const jobUrl = `${apiUrl}/jobapplications/${job.id}`;
|
||||
expect((await page.request.put(`${jobUrl}/cv`, { headers, data: { variantId: variant.id } })).status()).toBe(200);
|
||||
expect((await page.request.put(`${jobUrl}/cover-letter`, { headers, data: { text: "Original reviewed letter", source: "manual" } })).status()).toBe(200);
|
||||
const bytes = Buffer.from("Original attachment — æøå\n", "utf8");
|
||||
const upload = await page.request.post(`${apiUrl}/attachments`, { headers, multipart: { jobId: String(job.id), files: { name: "evidence.txt", mimeType: "text/plain", buffer: bytes } } });
|
||||
expect(upload.ok()).toBe(true);
|
||||
const [attachment] = await (await page.request.get(`${apiUrl}/attachments/${job.id}`)).json();
|
||||
await page.goto(`/applications/${job.id}?section=cv`);
|
||||
await expect(page).toHaveURL(new RegExp(`/jobs/${job.id}\\?section=cv$`));
|
||||
const captured = page.waitForResponse(response => response.url() === `${jobUrl}/submitted-packages` && response.request().method() === 'POST');
|
||||
await page.getByRole("button", { name: "Freeze current package", exact: true }).click();
|
||||
const captureResponse = await captured;
|
||||
// P2-D01: native SQLite rejects the service's DateTimeOffset ORDER BY. Only
|
||||
// this known response is expected; setup failures and later archive failures
|
||||
// after a successful capture must remain ordinary failing assertions.
|
||||
if (captureResponse.status() === 500) {
|
||||
test.fail(true, 'P2-D01: SQLite submitted-package capture; phase-2/BASELINE_DEFECTS.md');
|
||||
}
|
||||
expect(captureResponse.status()).toBe(200);
|
||||
await expect(page.getByText("Submission package v1", { exact: true })).toBeVisible();
|
||||
const [snapshot] = await (await page.request.get(`${jobUrl}/submitted-packages`)).json();
|
||||
const before = await (await page.request.get(`${jobUrl}/submitted-packages/${snapshot.id}`)).json();
|
||||
expect(before.coverLetterText).toBe("Original reviewed letter");
|
||||
expect(before.renderedCvHtml).toContain("Canonical engineer");
|
||||
expect(snapshot.cvVariantId).toBe(variant.id);
|
||||
expect(snapshot.attachments).toHaveLength(1);
|
||||
expect((await page.request.put(`${jobUrl}/cover-letter`, { headers, data: { text: "Later letter", source: "manual" } })).status()).toBe(200);
|
||||
expect((await page.request.put(`${apiUrl}/cv/variants/${variant.id}`, { headers, data: { name: "Later CV", settings: { ...variant.settings, headline: "Later headline" } } })).status()).toBe(200);
|
||||
expect((await page.request.delete(`${apiUrl}/attachments/${attachment.id}`, { headers })).status()).toBe(204);
|
||||
expect(await (await page.request.get(`${jobUrl}/submitted-packages/${snapshot.id}`)).json()).toEqual(before);
|
||||
const archived = await page.request.get(`${jobUrl}/submitted-packages/${snapshot.id}/attachments/${snapshot.attachments[0].id}`);
|
||||
expect(archived.status()).toBe(200);
|
||||
expect(await archived.body()).toEqual(bytes);
|
||||
await page.reload();
|
||||
await page.getByRole("button", { name: "View preserved package", exact: true }).click();
|
||||
await expect(page.getByRole("dialog").getByText("Original reviewed letter", { exact: true })).toBeVisible();
|
||||
});
|
||||
|
||||
test("[PRES-S03 PRES-W11] responsive settings navigation keeps rule edits and persists notification preferences", async ({ page, headers }) => {
|
||||
expect((await page.request.get(`${apiUrl}/auth/me`, { headers })).status()).toBe(200);
|
||||
await page.setViewportSize({ width: 390, height: 844 });
|
||||
await page.goto("/settings");
|
||||
await page.getByLabel("Settings section", { exact: true }).click();
|
||||
await page.getByRole("option", { name: "Follow-ups", exact: true }).click();
|
||||
const field = page.getByLabel("Applied: follow-up days", { exact: true });
|
||||
await field.fill("5");
|
||||
await page.setViewportSize({ width: 1440, height: 900 });
|
||||
await expect(field).toHaveValue("5");
|
||||
const saveRules = page.waitForResponse(response => response.url() === `${apiUrl}/rules` && response.request().method() === 'PUT');
|
||||
await page.getByRole("button", { name: "Save Rules", exact: true }).click();
|
||||
expect((await saveRules).status()).toBe(204);
|
||||
await expect.poll(async () => (await (await page.request.get(`${apiUrl}/rules`)).json()).appliedFollowUpDays).toBe(5);
|
||||
await page.getByRole("tab", { name: "Notifications", exact: true }).click();
|
||||
await page.getByLabel("Email reminders for follow-ups", { exact: true }).uncheck();
|
||||
await page.getByRole("button", { name: "Save notification settings", exact: true }).click();
|
||||
await expect.poll(async () => (await (await page.request.get(`${apiUrl}/notification-settings`)).json()).emailFollowUpRemindersEnabled).toBe(false);
|
||||
await page.reload();
|
||||
await page.getByRole("tab", { name: "Notifications", exact: true }).click();
|
||||
await expect(page.getByLabel("Email reminders for follow-ups", { exact: true })).not.toBeChecked();
|
||||
});
|
||||
|
||||
test("[PRES-S03] rule changes persist through the real API", async ({ page, headers }) => {
|
||||
const settings = await (await page.request.get(`${apiUrl}/rules`)).json();
|
||||
const saved = await page.request.put(`${apiUrl}/rules`, { headers, data: { ...settings, appliedFollowUpDays: 5 } });
|
||||
expect(saved.status()).toBe(204);
|
||||
expect((await (await page.request.get(`${apiUrl}/rules`)).json()).appliedFollowUpDays).toBe(5);
|
||||
});
|
||||
|
||||
test("[PRES-E07 PRES-W01 PRES-J04] keyboard command navigation and legacy workspace entry preserve the selected job", async ({ page, headers }) => {
|
||||
const companyResponse = await page.request.post(`${apiUrl}/companies`, { headers, data: { name: "Command AS" } });
|
||||
expect(companyResponse.status()).toBe(201);
|
||||
const jobResponse = await page.request.post(`${apiUrl}/jobapplications`, { headers,
|
||||
data: { companyId: (await companyResponse.json()).id, jobTitle: "Command engineer", status: "Saved" } });
|
||||
expect(jobResponse.ok()).toBe(true);
|
||||
const job = await jobResponse.json();
|
||||
await page.goto("/dashboard");
|
||||
await expect(page.getByRole("heading", { name: "Dashboard", exact: true })).toBeVisible();
|
||||
await page.keyboard.press("Control+k");
|
||||
await page.getByRole("dialog").getByRole("textbox").fill("Command engineer");
|
||||
await page.getByText("Command AS - Command engineer", { exact: true }).click();
|
||||
await expect(page).toHaveURL(new RegExp(`/jobs/${job.id}$`));
|
||||
await expect(page.getByRole("heading", { name: "Command engineer", exact: true })).toBeVisible();
|
||||
await page.goto(`/applications/${job.id}?section=match`);
|
||||
await expect(page).toHaveURL(new RegExp(`/jobs/${job.id}\\?section=match$`));
|
||||
await expect(page.getByRole("tab", { name: "Analysis", exact: true })).toHaveAttribute("aria-selected", "true");
|
||||
await page.keyboard.press("Control+k");
|
||||
await page.getByRole("dialog").getByText("Add new job", { exact: true }).click();
|
||||
await expect(page.getByRole("dialog")).toBeVisible();
|
||||
expect((await (await page.request.get(`${apiUrl}/jobapplications`)).json()).total).toBe(1);
|
||||
});
|
||||
|
||||
test("[PRES-A01 PRES-S04 PRES-E07] non-admin navigation cannot grant API privileges or unlock CV AI", async ({ page, headers }) => {
|
||||
await page.goto("/dashboard");
|
||||
await expect(page.getByRole("heading", { name: "Dashboard", exact: true })).toBeVisible();
|
||||
for (const name of ["Users", "Audit log", "System"]) await expect(page.getByRole("button", { name, exact: true })).toHaveCount(0);
|
||||
const denied = page.waitForResponse(response => response.url() === `${apiUrl}/users`);
|
||||
await page.goto("/admin/users");
|
||||
expect((await denied).status()).toBe(403);
|
||||
await expect(page.getByText("e2e@example.test", { exact: true })).toHaveCount(0);
|
||||
expect((await page.request.post(`${apiUrl}/users`, { headers, data: { email: "must-not-exist@example.test", password, roles: ["Admin"] } })).status()).toBe(403);
|
||||
const created = await page.request.post(`${apiUrl}/cv/variants`, { headers, data: { name: "Free CV" } });
|
||||
expect(created.status()).toBe(200);
|
||||
await page.goto(`/career/builder/${(await created.json()).id}`);
|
||||
await page.getByRole("tab", { name: "AI Tools", exact: true }).click();
|
||||
const actions = page.getByRole("button", { name: "Review suggestion", exact: true });
|
||||
await expect(actions).toHaveCount(3);
|
||||
for (const action of await actions.all()) await expect(action).toBeDisabled();
|
||||
expect((await page.request.post(`${apiUrl}/cv/ai/assist`, { headers, data: { action: "improve", text: "Safe test text" } })).status()).toBe(403);
|
||||
expect((await (await page.request.get(`${apiUrl}/auth/me`)).json()).roles).toEqual([]);
|
||||
});
|
||||
@@ -35,6 +35,7 @@
|
||||
"build": "node --max-old-space-size=4096 ./node_modules/next/dist/bin/next build",
|
||||
"lint": "eslint . --max-warnings=0",
|
||||
"lint:fix": "eslint . --fix",
|
||||
"typecheck": "tsc --noEmit --incremental false",
|
||||
"test": "jest",
|
||||
"test:pdf-admission": "playwright test --config=playwright.pdf.config.ts",
|
||||
"test:rate-limits": "playwright test --config=playwright.rate-limits.config.ts",
|
||||
|
||||
@@ -69,3 +69,44 @@ test("section AI controls render in Bokmål without changing the document langua
|
||||
expect(screen.getByLabelText("Språk for resultat")).toHaveTextContent("Norsk bokmål");
|
||||
expect(screen.getByRole("button", { name: "Foreslå" })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test.each(["Reject", "Close"])("[PRES-C11] %s leaves the source unchanged and removes the suggestion", async (action) => {
|
||||
mockedApi.post.mockResolvedValue({ data: { result: "Unaccepted draft" } } as never);
|
||||
const onApply = renderAssistant();
|
||||
fireEvent.click(screen.getByRole("button", { name: "AI suggestions" }));
|
||||
fireEvent.click(screen.getByRole("button", { name: "Suggest" }));
|
||||
expect(await screen.findByDisplayValue("Unaccepted draft")).toBeInTheDocument();
|
||||
fireEvent.click(screen.getByRole("button", { name: action }));
|
||||
expect(screen.queryByDisplayValue("Unaccepted draft")).not.toBeInTheDocument();
|
||||
expect(onApply).not.toHaveBeenCalled();
|
||||
expect(mockedApi.put).not.toHaveBeenCalled();
|
||||
if (action === "Close") fireEvent.click(screen.getByRole("button", { name: "AI suggestions" }));
|
||||
expect(screen.queryByRole("button", { name: "Apply" })).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
test("[PRES-C11] applies the user's edited suggestion only after acceptance", async () => {
|
||||
mockedApi.post.mockResolvedValue({ data: { result: "Proposed wording" } } as never);
|
||||
const onApply = renderAssistant();
|
||||
fireEvent.click(screen.getByRole("button", { name: "AI suggestions" }));
|
||||
fireEvent.click(screen.getByRole("button", { name: "Suggest" }));
|
||||
fireEvent.change(await screen.findByDisplayValue("Proposed wording"), { target: { value: "My reviewed wording" } });
|
||||
expect(onApply).not.toHaveBeenCalled();
|
||||
fireEvent.click(screen.getByRole("button", { name: "Apply" }));
|
||||
expect(onApply).toHaveBeenCalledTimes(1);
|
||||
expect(onApply).toHaveBeenCalledWith("My reviewed wording");
|
||||
});
|
||||
|
||||
test("[PRES-C11] a failed suggestion can be retried without applying or saving content", async () => {
|
||||
mockedApi.post.mockRejectedValueOnce(new Error("offline"))
|
||||
.mockResolvedValueOnce({ data: { result: "Recovered suggestion" } } as never);
|
||||
const onApply = renderAssistant();
|
||||
fireEvent.click(screen.getByRole("button", { name: "AI suggestions" }));
|
||||
fireEvent.click(screen.getByRole("button", { name: "Suggest" }));
|
||||
expect(await screen.findByRole("alert")).toBeInTheDocument();
|
||||
await waitFor(() => expect(screen.getByRole("button", { name: "Suggest" })).toBeEnabled());
|
||||
expect(onApply).not.toHaveBeenCalled();
|
||||
fireEvent.click(screen.getByRole("button", { name: "Suggest" }));
|
||||
expect(await screen.findByDisplayValue("Recovered suggestion")).toBeInTheDocument();
|
||||
expect(onApply).not.toHaveBeenCalled();
|
||||
expect(mockedApi.put).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@@ -261,6 +261,23 @@ test("AI cover letter suggestions use the linked CV and require explicit apply",
|
||||
));
|
||||
});
|
||||
|
||||
test("[PRES-W06] rejecting an AI cover letter preserves the saved draft without a write", async () => {
|
||||
routeGet();
|
||||
mockedApi.post.mockResolvedValue({ data: { result: { text: "Unaccepted letter" } } } as never);
|
||||
const onDirtyChange = jest.fn();
|
||||
render(<ApplicationCoverLetterSection jobId={7} onDirtyChange={onDirtyChange} />);
|
||||
await screen.findByText(/application's full job advert and analysis/i);
|
||||
fireEvent.click(screen.getByRole("button", { name: "Generate" }));
|
||||
await screen.findByText("Unaccepted letter");
|
||||
expect(screen.getByLabelText("Cover letter")).toHaveValue("Dear team");
|
||||
fireEvent.click(screen.getByRole("button", { name: "Reject" }));
|
||||
expect(screen.queryByText("Unaccepted letter")).not.toBeInTheDocument();
|
||||
expect(screen.getByLabelText("Cover letter")).toHaveValue("Dear team");
|
||||
expect(screen.getByRole("button", { name: "Save" })).toBeDisabled();
|
||||
expect(onDirtyChange).not.toHaveBeenCalledWith(true);
|
||||
expect(mockedApi.put).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test("a failed load surfaces an error", async () => {
|
||||
mockedApi.get.mockRejectedValue(new Error("boom"));
|
||||
|
||||
|
||||
@@ -345,3 +345,52 @@ test('preview failure is visible and retryable without leaving the editor', asyn
|
||||
expect(screen.getByTitle('CV preview')).toHaveAttribute('sandbox', 'allow-same-origin allow-scripts');
|
||||
await waitFor(() => expect(screen.queryByText('Preview unavailable')).not.toBeInTheDocument());
|
||||
});
|
||||
|
||||
async function reviewDocumentSuggestion(result: string) {
|
||||
routeGet(() => Promise.resolve({ data: variant }), { headline: 'Canonical headline', sections: [] });
|
||||
mockedApi.post.mockImplementation((url: string) => Promise.resolve({ data: url === '/cv/ai/assist'
|
||||
? { result } : { html: '<p>Preview</p>' } } as never));
|
||||
mockedApi.put.mockImplementation((_url, body) => Promise.resolve({ data: { ...variant, ...(body as object) } } as never));
|
||||
renderAt(3);
|
||||
await screen.findByDisplayValue('Backend CV');
|
||||
fireEvent.click(screen.getByRole('tab', { name: 'AI Tools' }));
|
||||
fireEvent.click(screen.getAllByRole('button', { name: 'Review suggestion' })[0]);
|
||||
await waitFor(() => expect(mockedApi.post).toHaveBeenCalledWith('/cv/ai/assist', expect.objectContaining({
|
||||
action: 'document-translate', text: JSON.stringify([{ id: 'headline', items: ['Canonical headline'] }]),
|
||||
})));
|
||||
}
|
||||
|
||||
test.each(['Apply', 'Reject'])('[PRES-C11 PRES-C07] whole-document AI %s changes only an explicitly accepted CV override', async action => {
|
||||
await reviewDocumentSuggestion(JSON.stringify([{ id: 'headline', items: ['Reviewed headline'] }]));
|
||||
expect(await screen.findByText('Review 1 proposed change(s)')).toBeInTheDocument();
|
||||
expect(screen.getByText('Canonical headline')).toBeInTheDocument();
|
||||
expect(screen.getByText('Reviewed headline')).toBeInTheDocument();
|
||||
expect(mockedApi.put).not.toHaveBeenCalled();
|
||||
fireEvent.click(screen.getByRole('button', { name: action }));
|
||||
expect(screen.queryByText('Review 1 proposed change(s)')).not.toBeInTheDocument();
|
||||
fireEvent.click(screen.getByRole('tab', { name: 'Content' }));
|
||||
expect(await screen.findByLabelText('Headline override')).toHaveValue(action === 'Apply' ? 'Reviewed headline' : '');
|
||||
if (action === 'Apply') {
|
||||
fireEvent.click(screen.getByRole('button', { name: 'Save now' }));
|
||||
await waitFor(() => expect(mockedApi.put).toHaveBeenCalledWith('/cv/variants/3', expect.objectContaining({
|
||||
settings: expect.objectContaining({ headline: 'Reviewed headline' }),
|
||||
})));
|
||||
expect(mockedApi.put.mock.calls.every(([url]) => url === '/cv/variants/3')).toBe(true);
|
||||
} else {
|
||||
expect(screen.getByText('Saved')).toBeInTheDocument();
|
||||
expect(mockedApi.put).not.toHaveBeenCalled();
|
||||
}
|
||||
});
|
||||
|
||||
test.each([
|
||||
['invalid JSON', 'not JSON'],
|
||||
['missing original ID', JSON.stringify([{ id: 'unknown', items: ['Wrong document'] }])],
|
||||
['duplicate original ID', JSON.stringify([{ id: 'headline', items: ['First'] }, { id: 'headline', items: ['Second'] }])],
|
||||
])('[PRES-C11] unsafe whole-document response (%s) cannot be applied', async (_case, result) => {
|
||||
await reviewDocumentSuggestion(result);
|
||||
expect(await screen.findByText('AI suggestion failed.')).toBeInTheDocument();
|
||||
expect(screen.queryByRole('button', { name: 'Apply' })).not.toBeInTheDocument();
|
||||
fireEvent.click(screen.getByRole('tab', { name: 'Content' }));
|
||||
expect(await screen.findByLabelText('Headline override')).toHaveValue('');
|
||||
expect(mockedApi.put).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
import React from "react";
|
||||
import "@testing-library/jest-dom";
|
||||
import { fireEvent, render, screen, waitFor } from "@testing-library/react";
|
||||
import { api } from "./api";
|
||||
import { I18nProvider } from "./i18n/I18nProvider";
|
||||
import QuickCommandDialog from "./components/QuickCommandDialog";
|
||||
|
||||
const mockedApi = api as jest.Mocked<typeof api>;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
localStorage.removeItem("uiLanguage");
|
||||
mockedApi.get.mockImplementation((url: string) => Promise.resolve({ data: url === "/companies"
|
||||
? [{ id: 7, name: "Nordlys AS" }]
|
||||
: { items: [{ id: 42, jobTitle: "Platform engineer", company: { name: "Nordlys AS" } }] } } as never));
|
||||
});
|
||||
|
||||
test.each([
|
||||
["Nordlys AS - Platform engineer", "/jobs/42"],
|
||||
["Nordlys AS", "/companies?edit=7"],
|
||||
])("[PRES-E07] command search opens %s at its existing destination", async (label, destination) => {
|
||||
const onNavigate = jest.fn();
|
||||
const onClose = jest.fn();
|
||||
render(<I18nProvider><QuickCommandDialog open onClose={onClose} onNavigate={onNavigate} onOpenAddJob={jest.fn()} /></I18nProvider>);
|
||||
fireEvent.change(screen.getByRole("textbox"), { target: { value: "Nordlys" } });
|
||||
fireEvent.click(await screen.findByText(label, { exact: true }));
|
||||
expect(onNavigate).toHaveBeenCalledWith(destination);
|
||||
expect(onClose).toHaveBeenCalledTimes(1);
|
||||
expect(mockedApi.post).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test("[PRES-E07 PRES-J04] the capture command opens review without creating a job, and closing clears search", async () => {
|
||||
const onOpenAddJob = jest.fn();
|
||||
const props = { onClose: jest.fn(), onNavigate: jest.fn(), onOpenAddJob };
|
||||
const view = (open: boolean) => <I18nProvider><QuickCommandDialog open={open} {...props} /></I18nProvider>;
|
||||
const { rerender } = render(view(true));
|
||||
fireEvent.click(screen.getByRole("button", { name: /add new job/i }));
|
||||
expect(onOpenAddJob).toHaveBeenCalledTimes(1);
|
||||
expect(mockedApi.post).not.toHaveBeenCalled();
|
||||
fireEvent.change(screen.getByRole("textbox"), { target: { value: "Nordlys" } });
|
||||
await screen.findByText("Nordlys AS - Platform engineer");
|
||||
rerender(view(false));
|
||||
rerender(view(true));
|
||||
await waitFor(() => expect(screen.getByRole("textbox")).toHaveValue(""));
|
||||
expect(screen.queryByText("Nordlys AS - Platform engineer")).not.toBeInTheDocument();
|
||||
});
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import '@testing-library/jest-dom';
|
||||
import { fireEvent, render, screen } from '@testing-library/react';
|
||||
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
|
||||
import SettingsView from './components/SettingsView';
|
||||
@@ -136,3 +136,30 @@ test('AI privacy settings are server-backed and external processing is local-onl
|
||||
externalProcessingAllowed: false,
|
||||
});
|
||||
});
|
||||
|
||||
test('[PRES-S03] failed notification save preserves the draft for an explicit retry', async () => {
|
||||
mockedApi.put.mockRejectedValueOnce(new Error('offline'));
|
||||
renderView();
|
||||
fireEvent.click(screen.getByRole('tab', { name: /notifications/i }));
|
||||
const preference = await screen.findByLabelText(/email reminders for follow-ups/i);
|
||||
await waitFor(() => expect(preference).toBeChecked());
|
||||
fireEvent.click(preference);
|
||||
fireEvent.click(screen.getByRole('button', { name: /save notification settings/i }));
|
||||
expect(await screen.findByRole('alert')).toHaveTextContent(/could not|failed/i);
|
||||
expect(preference).not.toBeChecked();
|
||||
fireEvent.click(screen.getByRole('button', { name: /save notification settings/i }));
|
||||
await waitFor(() => expect(mockedApi.put).toHaveBeenCalledTimes(2));
|
||||
expect(mockedApi.put).toHaveBeenLastCalledWith('/notification-settings', { emailFollowUpRemindersEnabled: false });
|
||||
await waitFor(() => expect(screen.queryByRole('alert')).not.toBeInTheDocument());
|
||||
});
|
||||
|
||||
test('[PRES-S03 PRES-W11] saving one follow-up rule preserves the other scenarios', async () => {
|
||||
renderView();
|
||||
fireEvent.click(screen.getByRole('tab', { name: /follow-ups/i }));
|
||||
fireEvent.change(await screen.findByLabelText('Applied: follow-up days'), { target: { value: '5' } });
|
||||
fireEvent.click(screen.getByRole('button', { name: /save rules/i }));
|
||||
await waitFor(() => expect(mockedApi.put).toHaveBeenCalledWith('/rules', {
|
||||
id: 1, appliedFollowUpDays: 5, appliedGhostDays: 30,
|
||||
offerFollowUpDays: 7, offerGhostDays: 14, feedbackFollowUpDays: 7, feedbackGhostDays: 14,
|
||||
}));
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user