diff --git a/docs/redesign/phase-7/COMMITS.md b/docs/redesign/phase-7/COMMITS.md index 0681836..8d0a3a4 100644 --- a/docs/redesign/phase-7/COMMITS.md +++ b/docs/redesign/phase-7/COMMITS.md @@ -45,4 +45,8 @@ Peer review found no product defect. The native geometry test now additionally a Observed normal public push2026-10-12 22:24:44.035140UTC proves localHEAD=origin=live bfa0ab6, clean worktree and unchanged local/origin/live main27657f3. P7-04 implemented CONDITIONAL GO; source/output/units/native/fidelity/static proof and all iframe/manual/release conditions remain. P7-05 successor records exact receipt and13 committed source baselines; no product evidence replaced. -| P7-05 Discovery | Actual hash follows observed normal push | [Results](p7-05/evidence/discovery-results.json):21/1,new8+existing4native,backend14,app/browser types/lint/build PASS;18/31images actually reviewed;new18scans0/81+existing9scans0/52 retained. Final hygiene/normal push pending. +9/0 frontend682/93 and browser257 derived only; other P7-05 domains pending; main unchanged | +| P7-05 Discovery | `34a4d6dc6b4bbe546ef01be91e98bd922b99444f` | [Results](p7-05/evidence/discovery-results.json):21/1,new8+existing4native,backend14,app/browser types/lint/build PASS;18/31images actually reviewed;new18scans0/81+existing9scans0/52 retained. Final hygiene/normal push pending. +9/0 frontend682/93 and browser257 derived only; other P7-05 domains pending; main unchanged | + +Observed Discovery normal push2026-10-12 22:49:31.085724UTC proves localHEAD=origin=live34a4d6d and clean worktree, with local/origin/live main27657f3 unchanged. Companies successor records exact receipt and committed baseline; no runtime bytes replaced. + +| P7-05 Companies | Actual hash follows observed normal push | [Results](p7-05/evidence/companies-results.json):30/4,new6+existing2+named4native,backend2,app/browser types/lint/build PASS;20/50images reviewed;new20scans0/91+named20WCAG0/163 qualified. Final hygiene/normal push pending. +7/0frontend689/93 and browser263 derived only; Offers/Reminders next, main unchanged | diff --git a/docs/redesign/phase-7/DEBT.md b/docs/redesign/phase-7/DEBT.md index 9e836e1..e7e96b7 100644 --- a/docs/redesign/phase-7/DEBT.md +++ b/docs/redesign/phase-7/DEBT.md @@ -138,3 +138,7 @@ Public18 top-document all-enabled scans0/12 retain sandbox frame-tested occurren ### P7-05 Discovery evidence [Discovery results](p7-05/evidence/discovery-results.json) repair reproduced saved-to-manual context, held old rows, unrelated Retry, edited-filter loss, duplicate mutations, unnamed spinner and Cancel/removed-control focus in the local owner. New18 all-enabled scans0/81 retain18 badge,57 overlapped contrast and6 modal-focus incompletes; existing9scans0/52 remain. Keyboard trap/return/next Tab and moved-focus tests are automated Chromium evidence, not M01-M12 human/device/zoom acceptance. NAV/provider delivery/model calls were withheld; UI-only intercepted dismiss204 is not persisted-run/history proof. No inherited debt or whole PRES group is closed by this slice. + +### P7-05 Companies evidence + +[Companies results](p7-05/evidence/companies-results.json) repair demonstrated duplicate/held-save draft ownership, stale relation reads, deep-link hash/state loss, locale refetch, incorrect arbitrary-stage translation, stale response title and BODY focus after dialog exit. Local post-exit fallback preserves valid MUI/moved focus. New20 all-enabled scans0/91 and named20 WCAG0/163 retain complete unresolved overlap/occlusion/modal/contrast/wrapper vectors; finite keyboard/reflow proof does not close M01–M12. Date readbacks compare full calendar seconds without claiming SQLite Kind/DST equivalence. Existing shared cached consumers remain; no inherited debt or whole PRES group is closed. diff --git a/docs/redesign/phase-7/PRESERVATION.md b/docs/redesign/phase-7/PRESERVATION.md index bff14d8..5f2d973 100644 --- a/docs/redesign/phase-7/PRESERVATION.md +++ b/docs/redesign/phase-7/PRESERVATION.md @@ -156,3 +156,7 @@ Implemented P7-03 scope is **CONDITIONAL GO**. The normal import/history push `5 ### P7-05 Discovery finite disposition [Discovery results](p7-05/evidence/discovery-results.json): J01/J02/J08/J09/W11 exact query/saved-ID Retry, protected draft/mutation ownership, SQLite CRUD/owner denial, reviewed-import navigation and backend provider-double runner/dismiss safeguards. Fresh21/1/new8+existing4/native/backend14/types/lint/build pass;18 actual new image reviews. New18scans0/81 and existing9scans0/52 remain qualified. No capability/route/API/schema/AI safety change; all57 remain56 PARTIAL/C11 DEFERRED and32 routes. Derived682/93/browser257 are not integrated totals. Companies/Offers/Reminders/manual/provider/release obligations remain; final hygiene/normal push pending. + +### P7-05 Companies finite disposition + +[Companies results](p7-05/evidence/companies-results.json): J01/J08/E08/W11 original nine-field payload/date/source/free-stage semantics, current response title, owner/tenant relation/latest20 projection, protected draft recovery, stale reads, query/hash/state/Back and post-exit focus. Fresh30/4/new6+existing2+named4native/backend2/types/lint/build pass;20/50 images actually reviewed. New20scans0/91 and named20WCAG0/163 remain qualified. No capability/route/API/schema/AI/output change; all57 remain56 PARTIAL/C11 DEFERRED and32 routes. Derived689/93/browser263 are not integrated totals. Cached consumers/manual/release obligations and Offers/Reminders remain; final hygiene/normal push pending. diff --git a/docs/redesign/phase-7/README.md b/docs/redesign/phase-7/README.md index 4ee2b02..ac3924d 100644 --- a/docs/redesign/phase-7/README.md +++ b/docs/redesign/phase-7/README.md @@ -13,7 +13,7 @@ The entry commit is `b5319981ad3d1bd635daff861453d23c0de5320b` on `codex/ui-rede | [P7-02](p7-02/README.md) | Onboarding and Career overview/evidence | Entryad7b419 pushed/verified; bounded onboarding CONDITIONAL GO32/13/4+types/lint/build/13 actual PNG inspections, hygiene PASS and d21501aa push verified23:55:12UTC. Separate SQLite evidence-list repair91 guards/HTTP1/Release passed, pushed01281f52/direct receipt00:06:47UTC with unfinished Career work preserved. Career affected46/3/native14/preservation2/types/lint/build and actual22-image review pass; implemented checkpoint CONDITIONAL GO with99 incompletes/manual gates. Career final hygiene PASS (four commands0,9docs347links0errors); normally pushed8b4d5c2/direct localHEAD=origin=live receipt05:51:04UTC, clean worktree/main unchanged | | [P7-03](p7-03/README.md) | Career structured editing and import review | Source-ID and structured-editor commits pushed; final import/history runtime/design CONDITIONAL GO: affected76/6, native35, backend98/seeder24, types/lint/build PASS;33/54 final images inspected. Import17 scans0/302, editor13 scans0/377, Career22 scans0/99 retained. Final staged hygiene PASS; normal push5d6e3235 directly verified17:28:54UTC with clean worktree/main unchanged. RD21/human/provider/output gates remain;57/32 unchanged | | [P7-04](p7-04/README.md) | CV library and public entry | Implemented CONDITIONAL GO; library fcacae7 push verified. Public affected38/3/native8/types/lint/build and fresh fidelity16/36+8/24 PASS;18 images actually reviewed, frame-tested12/manual gates retained. Public final hygiene PASS/normal push bfa0ab6 directly verified22:24:44UTC, clean main unchanged | -| [P7-05](p7-05/README.md) | Discovery, Companies, Offers and Reminders | Entry bfa0ab6 verified; Discovery21/1,new8+existing4native/backend14/types/lint/build pass;18 actual PNG reviews/qualified81+52incompletes. Discovery implemented CONDITIONAL GO, hygiene PASS/normal push pending; Companies/Offers/Reminders next as separate commits | +| [P7-05](p7-05/README.md) | Discovery, Companies, Offers and Reminders | Entry bfa0ab6 verified; Discovery21/1,new8+existing4native/backend14/types/lint/build pass;18 actual PNG reviews/qualified81+52incompletes. Discovery implemented CONDITIONAL GO, hygiene PASS/normal push34a4d6d verified; Companies30/4,new6+old2+named4native/backend2/types/lint/build pass;20actual PNG reviews/qualified91+163incompletes. Companies implemented CONDITIONAL GO, hygiene PASS/normal push pending; Offers/Reminders next as separate commits | | P7-06 | Remaining Application forms and global correspondence | Existing seven-section workspace, immutable packages and draft/send owners; planned | | P7-06F | Deferred `followMode` reviewed composer | Optional separately reviewed mail slice; **DEFERRED** until mode→draft/review action and fallback are explicitly decided | | P7-07 | Documents owner-scoped index | Authenticated metadata-only paginated backend projection first; source destinations from P7-04/06; planned | diff --git a/docs/redesign/phase-7/p7-05/README.md b/docs/redesign/phase-7/p7-05/README.md index 21fc965..8f6a35b 100644 --- a/docs/redesign/phase-7/p7-05/README.md +++ b/docs/redesign/phase-7/p7-05/README.md @@ -1,6 +1,6 @@ # P7-05 — Discovery, Companies, Offers and Reminders -**Discovery implemented scope CONDITIONAL GO; hygiene PASS/normal push pending. Companies, Offers and Reminders remain pending.** Follow the exact [approved handoff](../../phase-6/p6-05/PHASE7_HANDOFF.md#migration-order-and-commit-boundaries): Discovery, Companies, Offers, then Reminders as four separate normal development commits. +**Discovery implemented scope CONDITIONAL GO; hygiene PASS/normal push34a4d6d verified. Companies, Offers and Reminders remain pending.** Follow the exact [approved handoff](../../phase-6/p6-05/PHASE7_HANDOFF.md#migration-order-and-commit-boundaries): Discovery, Companies, Offers, then Reminders as four separate normal development commits. [Entry evidence](evidence/entry.json) records the actual bfa0ab6 public push, clean direct development equality/main unchanged and13 exact committed source baselines. P7-04 implemented scope is CONDITIONAL GO: affected38/3/public native8/fidelity16/36+8/24/types/lint/build/static checks pass;18 public PNGs actually inspected. Sandbox frame-tested12 and separate settings incompletes223 remain. Whole frontend573/89/backend1,033/sidecar49 are inherited; frontend673/93/browser249/backend1,034 are derived expectations, not integrated reruns. @@ -15,7 +15,7 @@ PRES-J01/J02/J08/J09/W11 and all57 groups remain binding;32 routes stay. No stan ## Discovery implemented result -**Discovery implemented scope CONDITIONAL GO; hygiene PASS/normal push pending. Companies, Offers and Reminders remain pending.** [Finite results](evidence/discovery-results.json) retain exact final source/build/raw outcomes and failed attempts. Fresh affected **21/1**, new ordinary browser **8**, existing ordinary browser **4**, backend **14**, app/browser TypeScript, lint and production build pass. The original12 unit cases remain; +9/0 gives **682/93 frontend derived only**. Browser+8 gives **257 derived only**; no fresh integrated totals until P7-12. +**Discovery implemented scope CONDITIONAL GO; hygiene PASS/normal push34a4d6d verified. Companies, Offers and Reminders remain pending.** [Finite results](evidence/discovery-results.json) retain exact final source/build/raw outcomes and failed attempts. Fresh affected **21/1**, new ordinary browser **8**, existing ordinary browser **4**, backend **14**, app/browser TypeScript, lint and production build pass. The original12 unit cases remain; +9/0 gives **682/93 frontend derived only**. Browser+8 gives **257 derived only**; no fresh integrated totals until P7-12. Shell retains its H1 and intro. Saved searches use the established section title/tokens; results use a shared H2, feedback and named focusable region. Job cards are named articles with H3 headings and wrapping actions. Manual search clears saved context; switching saved sources clears old rows before the pending response. One synchronous operation guard prevents duplicate requests and locks the three draft fields and dependent mutations. Read Retry uses the exact submitted GET or bodyless saved-ID POST while preserving both later unsaved filter drafts. Mutation failures preserve their drafts/current rows and require the original action, without an unrelated manual-search Retry. Semantic fallback keys retranslate without refetching. @@ -26,3 +26,21 @@ All **18 new final PNGs were displayed and reviewed**;13 existing-regression cap Real owner SQLite list/create/toggle/delete and reload, crafted foreign/anonymous denial, saved-list failure recovery and provider-double runner/history/dismiss tests pass. Browser vacancy search/run are explicit fixtures and never call NAV; real missing-result Dismiss404 and two intercepted204 UI recovery paths are distinguished. Stored native counts remain unchanged for those UI-only204 responses; successful provider/history/dismiss persistence or alert delivery is not claimed from browser fixtures. Meaningful initial unit RED11/10, peer Retry RED20/1, first native3/5 and focus RED6/2 are frozen. Exact fixture corrections account for legitimate UpdatedAt order and MUI label-plus-value accessible naming; timeout remains unchanged. Final21/1 and8+4 pass with no skipped/expected-failure/retry/relaxed assertions. All57 whole-group statuses remain56 PARTIAL/C11 DEFERRED and32 routes retained. Main stays27657f3/production NO-GO; model review remains withheld and Phase8 separate. + +## Companies implemented result + +**Companies implemented scope CONDITIONAL GO; hygiene PASS/normal push pending. Offers and Reminders remain pending.** [Companies entry](evidence/companies-entry.json) verifies the preceding Discovery push34a4d6d and six committed baselines. [Finite results](evidence/companies-results.json) retain exact source/export, failures, contextual triage and all raw outcomes. Fresh affected **30/4**, new ordinary browser **6**, existing company recovery **2**, named-controls **4**, backend **2**, app/browser TypeScript, lint and production build pass. +7 cases/0 suites gives **689/93 frontend derived only**; browser+6 gives **263 derived only**. Latest whole frontend573/89/backend1,033/sidecar49 remain inherited; P7-12 integrated acceptance is pending. + +Shared tokens and semantic mobile articles/headings replace one-off surfaces. Desktop uses a named keyboard-scrollable table with row headers and predictable source/pipeline/date/action columns; its840px canvas scrolls within the container without app-chrome overflow. Existing nine-field PUT, trim/null/date/source semantics, existing routes and arbitrary company pipeline text stay intact. Canonical related application statuses use existing localisation. Loaded relationship title prefers fresh response metadata; selected name remains available during loading. + +A synchronous save guard locks all nine draft fields and Cancel/Escape while one submitted snapshot is pending. Save error keeps the editable draft and recovers through the original Save. Named pending feedback receives focus before disabling fields; an idle empty save notice is omitted to preserve the established live-status contract. Relationship reads use selected identity and request generation; late A success/error and closed reads cannot replace B. Explicit Retry reads B without list reload; permission denial does not offer Retry. Locale changes translate fallback errors/status without fetching Companies again. Edit deep-link replacement removes onlyedit and preserves remaining query/hash/router state and Back. + +Native reproduced BODY focus after dialog exit, including removal of the old invoking action across767/768. A local React effect runs after MUI removes its exiting modal and restores the current company's action only if focus is still body. MUI's valid focus return and deliberately moved focus are retained. Native evidence covers deep-link767→768 and manually opened768→767 drafts, normal relationship Close,320px Edit keyboard wrapping,1024px landscape relationship scrolling/trap, held Save and Retry focus. No shared modal framework change. + +All **20 latest new PNGs were actually displayed and reviewed**;30 existing-regression captures are retained without a claimed review. Eight balanced320/360/390/767/768/1024 landscape/1440/1920 EN/Bokmål/light/dark long-record samples have root overflow0. Table/dialog scrolling is recorded separately. Earlier reviewed long-record images exposed split Source/date/header text; bounded column corrections were rebuilt and these latest images reviewed. New20 all-enabled scans retain **0 violations/91 incompletes**:20 invisible-zero badge,51 overlapping contrast,9 partial occlusion and11 open-modal focus. Existing named20 WCAG-tagged scans retain **0/163**:8 badge,124 overlap,15 partial,4 modal,8 disabled Undo/Redo wrapper labels and4 aria-labelledby shadow IDs. Old company recovery has no axe scan. Complete contexts remain; uncomputed ratio0 is not measured contrast and keyboard proof does not establish human reader/browse-mode acceptance. Sticky-shell landscape painting, full reflow/zoom, physical touch/software keyboard, fonts/photos and M01–M12 remain qualified. + +Real isolated SQLite save/reload, source normalization, latest20 active contacts excluding deleted jobs, relationship projection, exact owner/foreign/anonymous denials and linked Job navigation pass. Two unchanged-binary backend Facts pass; broader filter names do not imply broader matched coverage. Readback comparisons preserve all calendar seconds despite SQLite Kind/Z formatting differences and do not claim UTC/DST qualification. Server details/free text and existing cached useCompanies consumers remain untouched. API fixture creation is distinguished from an absent standalone creation screen. + +Baseline6/2, corrected RED6/7, expanded RED26/4, raw-stage/title RED11/1 and native4/2 plus focused0/1 are frozen. No skipped/expected-failure/retried tests, relaxed original assertions or disabled axe rules. All57 statuses remain56 PARTIAL/C11 DEFERRED;32 routes. Main stays27657f3/production NO-GO, P5-13 withheld and Phase8 separate. + +[Final finite hygiene](evidence/companies-static-checks.json) passed four raw commands at2026-10-13 06:58:01–05UTC,12documents/425links0errors,57groups/32routes; final staged whitespace is checked after staging. Existing named-controls12 dialog scans assert all WCAG-tagged violations,8 full-page scans gate five historical rule IDs; all retained raw scanned violations are independently0. These scopes do not replace the new20 all-enabled Companies scans. Normal push receipt follows observation in the next slice. diff --git a/docs/redesign/phase-7/p7-05/evidence/companies-entry.json b/docs/redesign/phase-7/p7-05/evidence/companies-entry.json new file mode 100644 index 0000000..fcc22e9 --- /dev/null +++ b/docs/redesign/phase-7/p7-05/evidence/companies-entry.json @@ -0,0 +1,140 @@ +{ + "checkpoint": "P7-05", + "slice": "Companies", + "recordedUtc": "2026-10-12T22:51:43.208402+00:00", + "entry": "CONDITIONAL_GO", + "previousDiscoveryPush": { + "observedUtc": "2026-10-12T22:49:31.085724+00:00", + "branch": "codex/ui-redesign-phase4", + "head": "34a4d6dc6b4bbe546ef01be91e98bd922b99444f", + "originDevelopment": "34a4d6dc6b4bbe546ef01be91e98bd922b99444f", + "liveDevelopment": "34a4d6dc6b4bbe546ef01be91e98bd922b99444f", + "main": "27657f31131cd95d42f0e1290853ec9c9b3959a4", + "originMain": "27657f31131cd95d42f0e1290853ec9c9b3959a4", + "liveMain": "27657f31131cd95d42f0e1290853ec9c9b3959a4", + "workingTreeClean": true, + "pendingPaths": [], + "commands": [ + { + "command": [ + "rtk", + "proxy", + "git", + "branch", + "--show-current" + ], + "exitCode": 0, + "stdout": "codex/ui-redesign-phase4\n", + "stderr": "[rtk] /!\\ No hook installed \u2014 run `rtk init -g` for automatic token savings\n" + }, + { + "command": [ + "rtk", + "proxy", + "git", + "rev-parse", + "HEAD" + ], + "exitCode": 0, + "stdout": "34a4d6dc6b4bbe546ef01be91e98bd922b99444f\n", + "stderr": "[rtk] /!\\ No hook installed \u2014 run `rtk init -g` for automatic token savings\n" + }, + { + "command": [ + "rtk", + "proxy", + "git", + "rev-parse", + "origin/codex/ui-redesign-phase4" + ], + "exitCode": 0, + "stdout": "34a4d6dc6b4bbe546ef01be91e98bd922b99444f\n", + "stderr": "[rtk] /!\\ No hook installed \u2014 run `rtk init -g` for automatic token savings\n" + }, + { + "command": [ + "rtk", + "proxy", + "git", + "rev-parse", + "main" + ], + "exitCode": 0, + "stdout": "27657f31131cd95d42f0e1290853ec9c9b3959a4\n", + "stderr": "[rtk] /!\\ No hook installed \u2014 run `rtk init -g` for automatic token savings\n" + }, + { + "command": [ + "rtk", + "proxy", + "git", + "rev-parse", + "origin/main" + ], + "exitCode": 0, + "stdout": "27657f31131cd95d42f0e1290853ec9c9b3959a4\n", + "stderr": "[rtk] /!\\ No hook installed \u2014 run `rtk init -g` for automatic token savings\n" + }, + { + "command": [ + "rtk", + "proxy", + "git", + "ls-remote", + "origin", + "refs/heads/codex/ui-redesign-phase4", + "refs/heads/main" + ], + "exitCode": 0, + "stdout": "34a4d6dc6b4bbe546ef01be91e98bd922b99444f\trefs/heads/codex/ui-redesign-phase4\n27657f31131cd95d42f0e1290853ec9c9b3959a4\trefs/heads/main\n", + "stderr": "[rtk] /!\\ No hook installed \u2014 run `rtk init -g` for automatic token savings\n" + }, + { + "command": [ + "rtk", + "proxy", + "git", + "status", + "--porcelain=v1" + ], + "exitCode": 0, + "stdout": "", + "stderr": "[rtk] /!\\ No hook installed \u2014 run `rtk init -g` for automatic token savings\n" + } + ] + }, + "baselineSources": [ + { + "path": "job-tracker-ui/src/components/CompaniesTable.tsx", + "gitBlobSha256": "f95c5a8aff51cf5d75ae40227c1d07e3277cfdf5eabab3cb6d386cfc3425c72b", + "bytes": 16512 + }, + { + "path": "job-tracker-ui/src/companies-relationship.test.tsx", + "gitBlobSha256": "df2e0f95ea289c38e0aca47e3c043753397ef764d95962084b24b61872388d1b", + "bytes": 4471 + }, + { + "path": "job-tracker-ui/src/locale-fetch-stability.test.tsx", + "gitBlobSha256": "9bb7d13ca31e135d5d5324a9778fb923ab8498494c727193711b8b507d837627", + "bytes": 1536 + }, + { + "path": "job-tracker-ui/src/hooks/useCompanies.ts", + "gitBlobSha256": "a48e2ec2abf50c89b9a983bed93bc3c8b91fc537af6673eb9245cc4bdd5fac10", + "bytes": 2344 + }, + { + "path": "JobTrackerApi/Controllers/CompaniesController.cs", + "gitBlobSha256": "c7be79cc92ad781a7d5fbf52e32dfe24db8915e591fa694a1ffdd6c56861feca", + "bytes": 8464 + }, + { + "path": "JobTrackerApi.Tests/CompanyRelationshipTests.cs", + "gitBlobSha256": "9f4ebce5eca006ed363a613b5fd4492f47ee60fe5ab942cfd3e9329dae233239", + "bytes": 3006 + } + ], + "baseline": "Affected6/2 frontendPASS; company relation backend2PASS. Wholefrontend573/89 inherited;682/93 and257browser derived only. Existing useCompanies consumers unchanged.", + "scope": "Existing nine-field PUT, company edit query/hash/state, owned relationship projections/latest20 contacts, no standalone company-create UI. No backend/schema/production/Phase8/AI change." +} diff --git a/docs/redesign/phase-7/p7-05/evidence/companies-results.json b/docs/redesign/phase-7/p7-05/evidence/companies-results.json new file mode 100644 index 0000000..171c182 --- /dev/null +++ b/docs/redesign/phase-7/p7-05/evidence/companies-results.json @@ -0,0 +1,4513 @@ +{ + "schemaVersion": 1, + "checkpoint": "P7-05", + "slice": "Companies", + "recordedUtc": "2026-10-13T06:57:23.030262+00:00", + "acceptance": "COMPANIES_IMPLEMENTED_SCOPE_CONDITIONAL_GO_HYGIENE_PASS_NORMAL_PUSH_PENDING_OFFERS_REMINDERS_PENDING", + "entryReceipt": { + "checkpoint": "P7-05", + "slice": "Companies", + "recordedUtc": "2026-10-12T22:51:43.208402+00:00", + "entry": "CONDITIONAL_GO", + "previousDiscoveryPush": { + "observedUtc": "2026-10-12T22:49:31.085724+00:00", + "branch": "codex/ui-redesign-phase4", + "head": "34a4d6dc6b4bbe546ef01be91e98bd922b99444f", + "originDevelopment": "34a4d6dc6b4bbe546ef01be91e98bd922b99444f", + "liveDevelopment": "34a4d6dc6b4bbe546ef01be91e98bd922b99444f", + "main": "27657f31131cd95d42f0e1290853ec9c9b3959a4", + "originMain": "27657f31131cd95d42f0e1290853ec9c9b3959a4", + "liveMain": "27657f31131cd95d42f0e1290853ec9c9b3959a4", + "workingTreeClean": true, + "pendingPaths": [], + "commands": [ + { + "command": [ + "rtk", + "proxy", + "git", + "branch", + "--show-current" + ], + "exitCode": 0, + "stdout": "codex/ui-redesign-phase4\n", + "stderr": "[rtk] /!\\ No hook installed — run `rtk init -g` for automatic token savings\n" + }, + { + "command": [ + "rtk", + "proxy", + "git", + "rev-parse", + "HEAD" + ], + "exitCode": 0, + "stdout": "34a4d6dc6b4bbe546ef01be91e98bd922b99444f\n", + "stderr": "[rtk] /!\\ No hook installed — run `rtk init -g` for automatic token savings\n" + }, + { + "command": [ + "rtk", + "proxy", + "git", + "rev-parse", + "origin/codex/ui-redesign-phase4" + ], + "exitCode": 0, + "stdout": "34a4d6dc6b4bbe546ef01be91e98bd922b99444f\n", + "stderr": "[rtk] /!\\ No hook installed — run `rtk init -g` for automatic token savings\n" + }, + { + "command": [ + "rtk", + "proxy", + "git", + "rev-parse", + "main" + ], + "exitCode": 0, + "stdout": "27657f31131cd95d42f0e1290853ec9c9b3959a4\n", + "stderr": "[rtk] /!\\ No hook installed — run `rtk init -g` for automatic token savings\n" + }, + { + "command": [ + "rtk", + "proxy", + "git", + "rev-parse", + "origin/main" + ], + "exitCode": 0, + "stdout": "27657f31131cd95d42f0e1290853ec9c9b3959a4\n", + "stderr": "[rtk] /!\\ No hook installed — run `rtk init -g` for automatic token savings\n" + }, + { + "command": [ + "rtk", + "proxy", + "git", + "ls-remote", + "origin", + "refs/heads/codex/ui-redesign-phase4", + "refs/heads/main" + ], + "exitCode": 0, + "stdout": "34a4d6dc6b4bbe546ef01be91e98bd922b99444f\trefs/heads/codex/ui-redesign-phase4\n27657f31131cd95d42f0e1290853ec9c9b3959a4\trefs/heads/main\n", + "stderr": "[rtk] /!\\ No hook installed — run `rtk init -g` for automatic token savings\n" + }, + { + "command": [ + "rtk", + "proxy", + "git", + "status", + "--porcelain=v1" + ], + "exitCode": 0, + "stdout": "", + "stderr": "[rtk] /!\\ No hook installed — run `rtk init -g` for automatic token savings\n" + } + ] + }, + "baselineSources": [ + { + "path": "job-tracker-ui/src/components/CompaniesTable.tsx", + "gitBlobSha256": "f95c5a8aff51cf5d75ae40227c1d07e3277cfdf5eabab3cb6d386cfc3425c72b", + "bytes": 16512 + }, + { + "path": "job-tracker-ui/src/companies-relationship.test.tsx", + "gitBlobSha256": "df2e0f95ea289c38e0aca47e3c043753397ef764d95962084b24b61872388d1b", + "bytes": 4471 + }, + { + "path": "job-tracker-ui/src/locale-fetch-stability.test.tsx", + "gitBlobSha256": "9bb7d13ca31e135d5d5324a9778fb923ab8498494c727193711b8b507d837627", + "bytes": 1536 + }, + { + "path": "job-tracker-ui/src/hooks/useCompanies.ts", + "gitBlobSha256": "a48e2ec2abf50c89b9a983bed93bc3c8b91fc537af6673eb9245cc4bdd5fac10", + "bytes": 2344 + }, + { + "path": "JobTrackerApi/Controllers/CompaniesController.cs", + "gitBlobSha256": "c7be79cc92ad781a7d5fbf52e32dfe24db8915e591fa694a1ffdd6c56861feca", + "bytes": 8464 + }, + { + "path": "JobTrackerApi.Tests/CompanyRelationshipTests.cs", + "gitBlobSha256": "9f4ebce5eca006ed363a613b5fd4492f47ee60fe5ab942cfd3e9329dae233239", + "bytes": 3006 + } + ], + "baseline": "Affected6/2 frontendPASS; company relation backend2PASS. Wholefrontend573/89 inherited;682/93 and257browser derived only. Existing useCompanies consumers unchanged.", + "scope": "Existing nine-field PUT, company edit query/hash/state, owned relationship projections/latest20 contacts, no standalone company-create UI. No backend/schema/production/Phase8/AI change." + }, + "immutableFinalCapsule": { + "path": ".artifacts/phase7/p7-05/companies/final-columns/results.json", + "sha256": "35c6a44fd5ca2d75fca0dd0b362ff1b7aaad5ed35a93c18b8e7d8eb98d75d0f0", + "bytes": 239706, + "hashPolicy": "Exact raw bytes" + }, + "verifiedFileReferences": 293, + "sourceAndExportMatch": true, + "unitPreRunSourceReceipt": { + "path": ".artifacts/phase7/p7-05/companies/unit-final-columns-green/pre-run-source-receipt.json", + "sha256": "aec27ec50af3839500ddd4391914f4d84b70a6fd759288fdef158dd0e8cfa22d", + "bytes": 1718, + "hashPolicy": "Exact raw bytes" + }, + "sourceQualification": "Unit pre-run receipt captured06:46:39UTC, during final native execution, not a retroactive pre-native receipt. Final product styles were applied before06:45:30 gate start; no product/native source edit occurred through final build/native collection and these raw bytes still match. Earlier attempt pre-run receipts are separately retained.", + "units": [ + { + "raw": { + "path": ".artifacts/phase7/p7-05/companies/final-columns/unit/p705-companies-final-columns-green-20261013-20261013T064639Z.json", + "sha256": "9f5396da12d6f7cf2beaf7bb22380bc43c58d60b950102d443b6f26b0dda2a93", + "bytes": 12880, + "hashPolicy": "Exact measured raw bytes" + }, + "startedUnixMs": 1791874000763, + "passed": 30, + "failed": 0, + "pending": 0, + "total": 30, + "suites": 4, + "cases": [ + { + "name": "opens one company relationship view with applications and recent contact", + "status": "passed" + }, + { + "name": "company contact dates follow the en UI locale", + "status": "passed" + }, + { + "name": "company contact dates follow the nb UI locale", + "status": "passed" + }, + { + "name": "[PRES-J08 E08] en company save failure remains inside the dialog with the draft available for retry", + "status": "passed" + }, + { + "name": "[PRES-J08 E08] nb company save failure remains inside the dialog with the draft available for retry", + "status": "passed" + }, + { + "name": "a held company save locks all nine fields and cancellation, submits one exact snapshot, and retains a failed draft", + "status": "passed" + }, + { + "name": "a locale change translates an existing company-save failure without refetching or replacing its draft", + "status": "passed" + }, + { + "name": "retrying a failed relationship read loads the same company without reloading the company list", + "status": "passed" + }, + { + "name": "a late success for relationship A cannot replace the opened relationship B", + "status": "passed" + }, + { + "name": "a late failure for relationship A cannot replace the opened relationship B", + "status": "passed" + }, + { + "name": "a relationship response received after closing stays closed and does not populate another held relationship", + "status": "passed" + }, + { + "name": "the company edit deep link removes only edit while preserving query, hash, state and replace navigation", + "status": "passed" + }, + { + "name": "light loaded company workflow loading → populated remains understandable and usable", + "status": "passed" + }, + { + "name": "light loaded company workflow loading → empty remains understandable and usable", + "status": "passed" + }, + { + "name": "light loaded company workflow loading → error remains understandable and usable", + "status": "passed" + }, + { + "name": "dark loaded company workflow loading → populated remains understandable and usable", + "status": "passed" + }, + { + "name": "dark loaded company workflow loading → empty remains understandable and usable", + "status": "passed" + }, + { + "name": "dark loaded company workflow loading → error remains understandable and usable", + "status": "passed" + }, + { + "name": "feedback and locale changes translate company presentation without reloading company data", + "status": "passed" + }, + { + "name": "pipeline normalizeStatus canonicalizes casing and synonyms", + "status": "passed" + }, + { + "name": "pipeline normalizeStatus preserves unknown as Other and empty as Applied", + "status": "passed" + }, + { + "name": "pipeline statusTone maps stages to palette keys", + "status": "passed" + }, + { + "name": "pipeline statusLabel localizes canonical and passes through custom", + "status": "passed" + }, + { + "name": "pipeline canonical stage list is stable and ordered", + "status": "passed" + }, + { + "name": "pipeline normalizeStatus canonicalizes the new stage synonyms", + "status": "passed" + }, + { + "name": "pipeline board groups match the agreed layout", + "status": "passed" + }, + { + "name": "pipeline every stage belongs to exactly one group, and groups agree with the backend", + "status": "passed" + }, + { + "name": "pipeline groupOf and isProspect classify stages", + "status": "passed" + }, + { + "name": "pipeline drag entry stages never infer Ghosted or Withdrawn", + "status": "passed" + }, + { + "name": "pipeline statusTone covers every stage", + "status": "passed" + } + ], + "commandRaw": { + "path": ".artifacts/phase7/p7-05/companies/final-columns/unit/p705-companies-final-columns-green-20261013-20261013T064639Z-command.json", + "sha256": "ca2141fe16f365fa4b7ae819e3916d42dc1eeafb824c771bf4252aea1e08b6e6", + "bytes": 787, + "hashPolicy": "Exact measured raw bytes" + }, + "logRaw": { + "path": ".artifacts/phase7/p7-05/companies/final-columns/unit/p705-companies-final-columns-green-20261013-20261013T064639Z.log", + "sha256": "6b78e4684bbde187a84ef0ef7c2b6a03fc578abcd2f1403caf04624ee296de6d", + "bytes": 829, + "hashPolicy": "Exact measured raw bytes" + }, + "qualification": "Raw executed outcome; source bytes are not inferred retroactively. Passing subset is not slice acceptance." + } + ], + "native": { + "companies": { + "command": { + "name": "companies", + "command": [ + "rtk", + "proxy", + "node", + "node_modules/@playwright/test/cli.js", + "test", + "e2e/phase7-companies.spec.ts", + "--retries=0", + "--reporter=list,json", + "--output=E:\\Storage\\Projects\\Active\\Job tracker\\.artifacts\\phase7\\current\\ui\\companies-results" + ], + "startedUtc": "2026-10-13T06:46:07.925156+00:00", + "finishedUtc": "2026-10-13T06:46:53.717647+00:00", + "exitCode": 0 + }, + "commandRaw": { + "path": ".artifacts/phase7/p7-05/companies/final-columns/companies-command.json", + "sha256": "8ee59e87f397144980332f738399d8da5922837f93d19a21387f8fb75837340e", + "bytes": 470, + "hashPolicy": "Exact measured raw bytes" + }, + "reportRaw": { + "path": ".artifacts/phase7/p7-05/companies/final-columns/companies.json", + "sha256": "33b99b07dd6285380c201faf629785fdabfa8f736325a06335864c770b95b67a", + "bytes": 1486659, + "hashPolicy": "Exact measured raw bytes" + }, + "logRaw": { + "path": ".artifacts/phase7/p7-05/companies/final-columns/companies.log", + "sha256": "c0367b687b8b241b3b46241a772653f42d2d26becee174647eaaef7e4e892f84", + "bytes": 1548, + "hashPolicy": "Exact measured raw bytes" + }, + "stats": { + "startTime": "2026-10-13T06:46:08.429Z", + "duration": 45245.55699999999, + "expected": 6, + "skipped": 0, + "unexpected": 0, + "flaky": 0 + }, + "cases": [ + { + "title": "[P7-05 Companies] nine-field save persists and owned relationship projects latest20 excluding deleted jobs", + "file": "phase7-companies.spec.ts", + "line": 198, + "expectedStatus": "passed", + "project": "chromium", + "resultStatuses": [ + "passed" + ], + "retries": [ + 0 + ], + "errors": [ + [] + ], + "durationMs": 6032 + }, + { + "title": "[P7-05 Companies] held save locks one snapshot, retains a failed draft and recovers through its original Save", + "file": "phase7-companies.spec.ts", + "line": 289, + "expectedStatus": "passed", + "project": "chromium", + "resultStatuses": [ + "passed" + ], + "retries": [ + 0 + ], + "errors": [ + [] + ], + "durationMs": 5063 + }, + { + "title": "[P7-05 Companies] late relationship A cannot replace B and Retry reads the same company with stable focus", + "file": "phase7-companies.spec.ts", + "line": 333, + "expectedStatus": "passed", + "project": "chromium", + "resultStatuses": [ + "passed" + ], + "retries": [ + 0 + ], + "errors": [ + [] + ], + "durationMs": 3793 + }, + { + "title": "[P7-05 Companies] edit deep link preserves query hash state and Back; open drafts survive767/768 transition", + "file": "phase7-companies.spec.ts", + "line": 396, + "expectedStatus": "passed", + "project": "chromium", + "resultStatuses": [ + "passed" + ], + "retries": [ + 0 + ], + "errors": [ + [] + ], + "durationMs": 5093 + }, + { + "title": "[P7-05 Companies] live locale changes translate failures and relationship status without refetching or losing drafts", + "file": "phase7-companies.spec.ts", + "line": 451, + "expectedStatus": "passed", + "project": "chromium", + "resultStatuses": [ + "passed" + ], + "retries": [ + 0 + ], + "errors": [ + [] + ], + "durationMs": 3445 + }, + { + "title": "[P7-05 Companies] eight finite language/theme widths reflow with named table scrolling and keyboard dialogs", + "file": "phase7-companies.spec.ts", + "line": 490, + "expectedStatus": "passed", + "project": "chromium", + "resultStatuses": [ + "passed" + ], + "retries": [ + 0 + ], + "errors": [ + [] + ], + "durationMs": 14722 + } + ], + "reportErrors": [], + "configuration": { + "version": "1.62.1", + "workers": 1, + "projects": [ + { + "name": "chromium", + "retries": 0 + } + ] + }, + "audit": { + "scans": [ + { + "test": "[P7-05 Companies] nine-field save persists and owned relationship projects latest20 excluding deleted jobs", + "attachment": "p7-companies-en-light-1440-saved-reloaded-audit", + "geometry": { + "width": 1440, + "height": 900, + "rootOverflow": 0, + "visibleHorizontalBounds": [], + "scrollRegions": [ + { + "label": "Companies", + "tabIndex": 0, + "clientWidth": 1120, + "scrollWidth": 1120, + "clientHeight": 96, + "scrollHeight": 96, + "left": 286, + "right": 1408, + "overflowX": "auto", + "overflowY": "auto" + } + ] + }, + "engine": null, + "toolOptions": null, + "violationNodes": 0, + "incompleteNodes": 1, + "raw": { + "path": ".artifacts/phase7/p7-05/companies/final-columns/embedded/companies/001-p7-companies-en-light-1440-saved-reloaded-audit.json", + "sha256": "dd330c376a3bc2976cd4b71b36ffb363d75efced41d2d5fd57922cc33f826fef", + "bytes": 7705, + "hashPolicy": "Exact measured raw bytes" + }, + "originalEmbeddedReport": { + "path": ".artifacts/phase7/p7-05/companies/final-columns/companies.json", + "sha256": "33b99b07dd6285380c201faf629785fdabfa8f736325a06335864c770b95b67a", + "bytes": 1486659, + "hashPolicy": "Exact measured raw bytes" + }, + "context": { + "sample": { + "language": "en", + "theme": "light", + "width": 1440, + "height": 900 + }, + "state": "saved-reloaded", + "boundary": "Company creation is an API fixture: the existing Companies page has no standalone create interface. Company list/detail/nine-field PUT, job creation/soft deletion and manual correspondence use the actual isolated owner HTTP API and disposable SQLite. Route-held requests continue to that API when released; explicitly controlled failures establish presentation and recovery only. No provider/model/external service is invoked. Browser request records contain only Companies method/path/query/JSON payload; direct fixture/readback requests are verified separately and no session or CSRF headers are recorded.", + "qualifications": [ + "Finite Chromium states, not a Cartesian width/language/theme/state claim.", + "A named bounded desktop company table may legitimately scroll in two dimensions; its geometry is recorded separately from app-root overflow.", + "Axe incomplete findings require contextual triage; automated keyboard/focus checks do not establish human screen-reader acceptance.", + "Real zoom, physical touch/software keyboard, deployed fonts/photos and production acceptance remain unverified." + ] + }, + "rules": [ + { + "kind": "incomplete", + "id": "color-contrast", + "nodes": 1 + } + ] + }, + { + "test": "[P7-05 Companies] nine-field save persists and owned relationship projects latest20 excluding deleted jobs", + "attachment": "p7-companies-en-light-1440-latest20-owned-relationship-audit", + "geometry": { + "width": 1440, + "height": 900, + "rootOverflow": 0, + "visibleHorizontalBounds": [], + "scrollRegions": [ + { + "label": "Companies", + "tabIndex": 0, + "clientWidth": 1120, + "scrollWidth": 1120, + "clientHeight": 96, + "scrollHeight": 96, + "left": 286, + "right": 1408, + "overflowX": "auto", + "overflowY": "auto" + }, + { + "label": "Relationship history", + "tabIndex": -1, + "clientWidth": 1024, + "scrollWidth": 1024, + "clientHeight": 732, + "scrollHeight": 1311, + "left": 208, + "right": 1232, + "overflowX": "auto", + "overflowY": "auto" + } + ] + }, + "engine": null, + "toolOptions": null, + "violationNodes": 0, + "incompleteNodes": 4, + "raw": { + "path": ".artifacts/phase7/p7-05/companies/final-columns/embedded/companies/003-p7-companies-en-light-1440-latest20-owned-relationship-audit.json", + "sha256": "1738838ac08cf424cff7454c5e7a56ca3595011df6e372071378c880e2222e0f", + "bytes": 20010, + "hashPolicy": "Exact measured raw bytes" + }, + "originalEmbeddedReport": { + "path": ".artifacts/phase7/p7-05/companies/final-columns/companies.json", + "sha256": "33b99b07dd6285380c201faf629785fdabfa8f736325a06335864c770b95b67a", + "bytes": 1486659, + "hashPolicy": "Exact measured raw bytes" + }, + "context": { + "sample": { + "language": "en", + "theme": "light", + "width": 1440, + "height": 900 + }, + "state": "latest20-owned-relationship", + "boundary": "Company creation is an API fixture: the existing Companies page has no standalone create interface. Company list/detail/nine-field PUT, job creation/soft deletion and manual correspondence use the actual isolated owner HTTP API and disposable SQLite. Route-held requests continue to that API when released; explicitly controlled failures establish presentation and recovery only. No provider/model/external service is invoked. Browser request records contain only Companies method/path/query/JSON payload; direct fixture/readback requests are verified separately and no session or CSRF headers are recorded.", + "qualifications": [ + "Finite Chromium states, not a Cartesian width/language/theme/state claim.", + "A named bounded desktop company table may legitimately scroll in two dimensions; its geometry is recorded separately from app-root overflow.", + "Axe incomplete findings require contextual triage; automated keyboard/focus checks do not establish human screen-reader acceptance.", + "Real zoom, physical touch/software keyboard, deployed fonts/photos and production acceptance remain unverified." + ] + }, + "rules": [ + { + "kind": "incomplete", + "id": "aria-hidden-focus", + "nodes": 1 + }, + { + "kind": "incomplete", + "id": "color-contrast", + "nodes": 3 + } + ] + }, + { + "test": "[P7-05 Companies] held save locks one snapshot, retains a failed draft and recovers through its original Save", + "attachment": "p7-companies-nb-dark-360-held-save-audit", + "geometry": { + "width": 360, + "height": 844, + "rootOverflow": 0, + "visibleHorizontalBounds": [], + "scrollRegions": [ + { + "label": "Rediger selskap", + "tabIndex": -1, + "clientWidth": 360, + "scrollWidth": 360, + "clientHeight": 679, + "scrollHeight": 679, + "left": 0, + "right": 360, + "overflowX": "auto", + "overflowY": "auto" + } + ] + }, + "engine": null, + "toolOptions": null, + "violationNodes": 0, + "incompleteNodes": 2, + "raw": { + "path": ".artifacts/phase7/p7-05/companies/final-columns/embedded/companies/008-p7-companies-nb-dark-360-held-save-audit.json", + "sha256": "94f8d2914a0b5bcfb1cac9250e0bb3cee38498caf3aa6204f2026f4f940ac6bf", + "bytes": 8216, + "hashPolicy": "Exact measured raw bytes" + }, + "originalEmbeddedReport": { + "path": ".artifacts/phase7/p7-05/companies/final-columns/companies.json", + "sha256": "33b99b07dd6285380c201faf629785fdabfa8f736325a06335864c770b95b67a", + "bytes": 1486659, + "hashPolicy": "Exact measured raw bytes" + }, + "context": { + "sample": { + "language": "nb", + "theme": "dark", + "width": 360, + "height": 844 + }, + "state": "held-save", + "boundary": "Company creation is an API fixture: the existing Companies page has no standalone create interface. Company list/detail/nine-field PUT, job creation/soft deletion and manual correspondence use the actual isolated owner HTTP API and disposable SQLite. Route-held requests continue to that API when released; explicitly controlled failures establish presentation and recovery only. No provider/model/external service is invoked. Browser request records contain only Companies method/path/query/JSON payload; direct fixture/readback requests are verified separately and no session or CSRF headers are recorded.", + "qualifications": [ + "Finite Chromium states, not a Cartesian width/language/theme/state claim.", + "A named bounded desktop company table may legitimately scroll in two dimensions; its geometry is recorded separately from app-root overflow.", + "Axe incomplete findings require contextual triage; automated keyboard/focus checks do not establish human screen-reader acceptance.", + "Real zoom, physical touch/software keyboard, deployed fonts/photos and production acceptance remain unverified." + ] + }, + "rules": [ + { + "kind": "incomplete", + "id": "aria-hidden-focus", + "nodes": 1 + }, + { + "kind": "incomplete", + "id": "color-contrast", + "nodes": 1 + } + ] + }, + { + "test": "[P7-05 Companies] held save locks one snapshot, retains a failed draft and recovers through its original Save", + "attachment": "p7-companies-nb-dark-360-failed-save-durable-draft-audit", + "geometry": { + "width": 360, + "height": 844, + "rootOverflow": 0, + "visibleHorizontalBounds": [], + "scrollRegions": [ + { + "label": "Rediger selskap", + "tabIndex": -1, + "clientWidth": 360, + "scrollWidth": 360, + "clientHeight": 679, + "scrollHeight": 679, + "left": 0, + "right": 360, + "overflowX": "auto", + "overflowY": "auto" + } + ] + }, + "engine": null, + "toolOptions": null, + "violationNodes": 0, + "incompleteNodes": 12, + "raw": { + "path": ".artifacts/phase7/p7-05/companies/final-columns/embedded/companies/010-p7-companies-nb-dark-360-failed-save-durable-draft-audit.json", + "sha256": "e14e0b7deb25c040979f3072e712a2c6be695c0e80146ca254afca9759be16b8", + "bytes": 15114, + "hashPolicy": "Exact measured raw bytes" + }, + "originalEmbeddedReport": { + "path": ".artifacts/phase7/p7-05/companies/final-columns/companies.json", + "sha256": "33b99b07dd6285380c201faf629785fdabfa8f736325a06335864c770b95b67a", + "bytes": 1486659, + "hashPolicy": "Exact measured raw bytes" + }, + "context": { + "sample": { + "language": "nb", + "theme": "dark", + "width": 360, + "height": 844 + }, + "state": "failed-save-durable-draft", + "boundary": "Company creation is an API fixture: the existing Companies page has no standalone create interface. Company list/detail/nine-field PUT, job creation/soft deletion and manual correspondence use the actual isolated owner HTTP API and disposable SQLite. Route-held requests continue to that API when released; explicitly controlled failures establish presentation and recovery only. No provider/model/external service is invoked. Browser request records contain only Companies method/path/query/JSON payload; direct fixture/readback requests are verified separately and no session or CSRF headers are recorded.", + "qualifications": [ + "Finite Chromium states, not a Cartesian width/language/theme/state claim.", + "A named bounded desktop company table may legitimately scroll in two dimensions; its geometry is recorded separately from app-root overflow.", + "Axe incomplete findings require contextual triage; automated keyboard/focus checks do not establish human screen-reader acceptance.", + "Real zoom, physical touch/software keyboard, deployed fonts/photos and production acceptance remain unverified." + ] + }, + "rules": [ + { + "kind": "incomplete", + "id": "aria-hidden-focus", + "nodes": 1 + }, + { + "kind": "incomplete", + "id": "color-contrast", + "nodes": 11 + } + ] + }, + { + "test": "[P7-05 Companies] held save locks one snapshot, retains a failed draft and recovers through its original Save", + "attachment": "p7-companies-nb-dark-360-recovery-save-held-audit", + "geometry": { + "width": 360, + "height": 844, + "rootOverflow": 0, + "visibleHorizontalBounds": [], + "scrollRegions": [ + { + "label": "Rediger selskap", + "tabIndex": -1, + "clientWidth": 360, + "scrollWidth": 360, + "clientHeight": 679, + "scrollHeight": 679, + "left": 0, + "right": 360, + "overflowX": "auto", + "overflowY": "auto" + } + ] + }, + "engine": null, + "toolOptions": null, + "violationNodes": 0, + "incompleteNodes": 2, + "raw": { + "path": ".artifacts/phase7/p7-05/companies/final-columns/embedded/companies/012-p7-companies-nb-dark-360-recovery-save-held-audit.json", + "sha256": "876cc70effd09ef553fd9e1e9f44919048b692fe58ac656781385915d561c82c", + "bytes": 8226, + "hashPolicy": "Exact measured raw bytes" + }, + "originalEmbeddedReport": { + "path": ".artifacts/phase7/p7-05/companies/final-columns/companies.json", + "sha256": "33b99b07dd6285380c201faf629785fdabfa8f736325a06335864c770b95b67a", + "bytes": 1486659, + "hashPolicy": "Exact measured raw bytes" + }, + "context": { + "sample": { + "language": "nb", + "theme": "dark", + "width": 360, + "height": 844 + }, + "state": "recovery-save-held", + "boundary": "Company creation is an API fixture: the existing Companies page has no standalone create interface. Company list/detail/nine-field PUT, job creation/soft deletion and manual correspondence use the actual isolated owner HTTP API and disposable SQLite. Route-held requests continue to that API when released; explicitly controlled failures establish presentation and recovery only. No provider/model/external service is invoked. Browser request records contain only Companies method/path/query/JSON payload; direct fixture/readback requests are verified separately and no session or CSRF headers are recorded.", + "qualifications": [ + "Finite Chromium states, not a Cartesian width/language/theme/state claim.", + "A named bounded desktop company table may legitimately scroll in two dimensions; its geometry is recorded separately from app-root overflow.", + "Axe incomplete findings require contextual triage; automated keyboard/focus checks do not establish human screen-reader acceptance.", + "Real zoom, physical touch/software keyboard, deployed fonts/photos and production acceptance remain unverified." + ] + }, + "rules": [ + { + "kind": "incomplete", + "id": "aria-hidden-focus", + "nodes": 1 + }, + { + "kind": "incomplete", + "id": "color-contrast", + "nodes": 1 + } + ] + }, + { + "test": "[P7-05 Companies] late relationship A cannot replace B and Retry reads the same company with stable focus", + "attachment": "p7-companies-en-dark-390-relationship-retry-held-audit", + "geometry": { + "width": 390, + "height": 844, + "rootOverflow": 0, + "visibleHorizontalBounds": [], + "scrollRegions": [ + { + "label": "Relationship history", + "tabIndex": -1, + "clientWidth": 390, + "scrollWidth": 390, + "clientHeight": 740, + "scrollHeight": 740, + "left": 0, + "right": 390, + "overflowX": "auto", + "overflowY": "auto" + } + ] + }, + "engine": null, + "toolOptions": null, + "violationNodes": 0, + "incompleteNodes": 2, + "raw": { + "path": ".artifacts/phase7/p7-05/companies/final-columns/embedded/companies/017-p7-companies-en-dark-390-relationship-retry-held-audit.json", + "sha256": "30caafd56b8c3a068176b0fec76107d8917faf495fe683f895c2d8c1cfd25c5e", + "bytes": 7833, + "hashPolicy": "Exact measured raw bytes" + }, + "originalEmbeddedReport": { + "path": ".artifacts/phase7/p7-05/companies/final-columns/companies.json", + "sha256": "33b99b07dd6285380c201faf629785fdabfa8f736325a06335864c770b95b67a", + "bytes": 1486659, + "hashPolicy": "Exact measured raw bytes" + }, + "context": { + "sample": { + "language": "en", + "theme": "dark", + "width": 390, + "height": 844 + }, + "state": "relationship-retry-held", + "boundary": "Company creation is an API fixture: the existing Companies page has no standalone create interface. Company list/detail/nine-field PUT, job creation/soft deletion and manual correspondence use the actual isolated owner HTTP API and disposable SQLite. Route-held requests continue to that API when released; explicitly controlled failures establish presentation and recovery only. No provider/model/external service is invoked. Browser request records contain only Companies method/path/query/JSON payload; direct fixture/readback requests are verified separately and no session or CSRF headers are recorded.", + "qualifications": [ + "Finite Chromium states, not a Cartesian width/language/theme/state claim.", + "A named bounded desktop company table may legitimately scroll in two dimensions; its geometry is recorded separately from app-root overflow.", + "Axe incomplete findings require contextual triage; automated keyboard/focus checks do not establish human screen-reader acceptance.", + "Real zoom, physical touch/software keyboard, deployed fonts/photos and production acceptance remain unverified." + ] + }, + "rules": [ + { + "kind": "incomplete", + "id": "aria-hidden-focus", + "nodes": 1 + }, + { + "kind": "incomplete", + "id": "color-contrast", + "nodes": 1 + } + ] + }, + { + "test": "[P7-05 Companies] late relationship A cannot replace B and Retry reads the same company with stable focus", + "attachment": "p7-companies-en-dark-390-relationship-recovered-audit", + "geometry": { + "width": 390, + "height": 844, + "rootOverflow": 0, + "visibleHorizontalBounds": [], + "scrollRegions": [ + { + "label": "Relationship history", + "tabIndex": -1, + "clientWidth": 390, + "scrollWidth": 390, + "clientHeight": 740, + "scrollHeight": 740, + "left": 0, + "right": 390, + "overflowX": "auto", + "overflowY": "auto" + } + ] + }, + "engine": null, + "toolOptions": null, + "violationNodes": 0, + "incompleteNodes": 2, + "raw": { + "path": ".artifacts/phase7/p7-05/companies/final-columns/embedded/companies/019-p7-companies-en-dark-390-relationship-recovered-audit.json", + "sha256": "fd512f875c80669b07c2214dfcaf60c6f8415c7d35509f75c83f554b547e80fd", + "bytes": 8165, + "hashPolicy": "Exact measured raw bytes" + }, + "originalEmbeddedReport": { + "path": ".artifacts/phase7/p7-05/companies/final-columns/companies.json", + "sha256": "33b99b07dd6285380c201faf629785fdabfa8f736325a06335864c770b95b67a", + "bytes": 1486659, + "hashPolicy": "Exact measured raw bytes" + }, + "context": { + "sample": { + "language": "en", + "theme": "dark", + "width": 390, + "height": 844 + }, + "state": "relationship-recovered", + "boundary": "Company creation is an API fixture: the existing Companies page has no standalone create interface. Company list/detail/nine-field PUT, job creation/soft deletion and manual correspondence use the actual isolated owner HTTP API and disposable SQLite. Route-held requests continue to that API when released; explicitly controlled failures establish presentation and recovery only. No provider/model/external service is invoked. Browser request records contain only Companies method/path/query/JSON payload; direct fixture/readback requests are verified separately and no session or CSRF headers are recorded.", + "qualifications": [ + "Finite Chromium states, not a Cartesian width/language/theme/state claim.", + "A named bounded desktop company table may legitimately scroll in two dimensions; its geometry is recorded separately from app-root overflow.", + "Axe incomplete findings require contextual triage; automated keyboard/focus checks do not establish human screen-reader acceptance.", + "Real zoom, physical touch/software keyboard, deployed fonts/photos and production acceptance remain unverified." + ] + }, + "rules": [ + { + "kind": "incomplete", + "id": "aria-hidden-focus", + "nodes": 1 + }, + { + "kind": "incomplete", + "id": "color-contrast", + "nodes": 1 + } + ] + }, + { + "test": "[P7-05 Companies] edit deep link preserves query hash state and Back; open drafts survive767/768 transition", + "attachment": "p7-companies-nb-light-768-open-draft-breakpoint-transition-audit", + "geometry": { + "width": 768, + "height": 900, + "rootOverflow": 0, + "visibleHorizontalBounds": [], + "scrollRegions": [ + { + "label": "Selskaper", + "tabIndex": 0, + "clientWidth": 718, + "scrollWidth": 840, + "clientHeight": 96, + "scrollHeight": 96, + "left": 24, + "right": 744, + "overflowX": "auto", + "overflowY": "auto" + }, + { + "label": "Rediger selskap", + "tabIndex": -1, + "clientWidth": 704, + "scrollWidth": 704, + "clientHeight": 534, + "scrollHeight": 534, + "left": 32, + "right": 736, + "overflowX": "auto", + "overflowY": "auto" + } + ] + }, + "engine": null, + "toolOptions": null, + "violationNodes": 0, + "incompleteNodes": 11, + "raw": { + "path": ".artifacts/phase7/p7-05/companies/final-columns/embedded/companies/023-p7-companies-nb-light-768-open-draft-breakpoint-transition-audit.json", + "sha256": "b87b33e4b86722c24dee2fed29da35788a47e2f2336359cb4c0a9beb0e709124", + "bytes": 14422, + "hashPolicy": "Exact measured raw bytes" + }, + "originalEmbeddedReport": { + "path": ".artifacts/phase7/p7-05/companies/final-columns/companies.json", + "sha256": "33b99b07dd6285380c201faf629785fdabfa8f736325a06335864c770b95b67a", + "bytes": 1486659, + "hashPolicy": "Exact measured raw bytes" + }, + "context": { + "sample": { + "language": "nb", + "theme": "light", + "width": 768, + "height": 900 + }, + "state": "open-draft-breakpoint-transition", + "boundary": "Company creation is an API fixture: the existing Companies page has no standalone create interface. Company list/detail/nine-field PUT, job creation/soft deletion and manual correspondence use the actual isolated owner HTTP API and disposable SQLite. Route-held requests continue to that API when released; explicitly controlled failures establish presentation and recovery only. No provider/model/external service is invoked. Browser request records contain only Companies method/path/query/JSON payload; direct fixture/readback requests are verified separately and no session or CSRF headers are recorded.", + "qualifications": [ + "Finite Chromium states, not a Cartesian width/language/theme/state claim.", + "A named bounded desktop company table may legitimately scroll in two dimensions; its geometry is recorded separately from app-root overflow.", + "Axe incomplete findings require contextual triage; automated keyboard/focus checks do not establish human screen-reader acceptance.", + "Real zoom, physical touch/software keyboard, deployed fonts/photos and production acceptance remain unverified." + ] + }, + "rules": [ + { + "kind": "incomplete", + "id": "aria-hidden-focus", + "nodes": 1 + }, + { + "kind": "incomplete", + "id": "color-contrast", + "nodes": 10 + } + ] + }, + { + "test": "[P7-05 Companies] edit deep link preserves query hash state and Back; open drafts survive767/768 transition", + "attachment": "p7-companies-nb-light-767-manual-open-draft-breakpoint-transition-audit", + "geometry": { + "width": 767, + "height": 900, + "rootOverflow": 0, + "visibleHorizontalBounds": [], + "scrollRegions": [ + { + "label": "Rediger selskap", + "tabIndex": -1, + "clientWidth": 767, + "scrollWidth": 767, + "clientHeight": 735, + "scrollHeight": 735, + "left": 0, + "right": 767, + "overflowX": "auto", + "overflowY": "auto" + } + ] + }, + "engine": null, + "toolOptions": null, + "violationNodes": 0, + "incompleteNodes": 11, + "raw": { + "path": ".artifacts/phase7/p7-05/companies/final-columns/embedded/companies/026-p7-companies-nb-light-767-manual-open-draft-breakpoint-transition-audit.json", + "sha256": "5fbb78d0407bbcd3afc20c16ab6b63cace69688150cdf19b7eead729f187a31e", + "bytes": 14210, + "hashPolicy": "Exact measured raw bytes" + }, + "originalEmbeddedReport": { + "path": ".artifacts/phase7/p7-05/companies/final-columns/companies.json", + "sha256": "33b99b07dd6285380c201faf629785fdabfa8f736325a06335864c770b95b67a", + "bytes": 1486659, + "hashPolicy": "Exact measured raw bytes" + }, + "context": { + "sample": { + "language": "nb", + "theme": "light", + "width": 767, + "height": 900 + }, + "state": "manual-open-draft-breakpoint-transition", + "boundary": "Company creation is an API fixture: the existing Companies page has no standalone create interface. Company list/detail/nine-field PUT, job creation/soft deletion and manual correspondence use the actual isolated owner HTTP API and disposable SQLite. Route-held requests continue to that API when released; explicitly controlled failures establish presentation and recovery only. No provider/model/external service is invoked. Browser request records contain only Companies method/path/query/JSON payload; direct fixture/readback requests are verified separately and no session or CSRF headers are recorded.", + "qualifications": [ + "Finite Chromium states, not a Cartesian width/language/theme/state claim.", + "A named bounded desktop company table may legitimately scroll in two dimensions; its geometry is recorded separately from app-root overflow.", + "Axe incomplete findings require contextual triage; automated keyboard/focus checks do not establish human screen-reader acceptance.", + "Real zoom, physical touch/software keyboard, deployed fonts/photos and production acceptance remain unverified." + ] + }, + "rules": [ + { + "kind": "incomplete", + "id": "aria-hidden-focus", + "nodes": 1 + }, + { + "kind": "incomplete", + "id": "color-contrast", + "nodes": 10 + } + ] + }, + { + "test": "[P7-05 Companies] live locale changes translate failures and relationship status without refetching or losing drafts", + "attachment": "p7-companies-en-light-1440-live-cross-tab-locale-retained-failure-audit", + "geometry": { + "width": 1440, + "height": 900, + "rootOverflow": 0, + "visibleHorizontalBounds": [], + "scrollRegions": [ + { + "label": "Companies", + "tabIndex": 0, + "clientWidth": 1120, + "scrollWidth": 1120, + "clientHeight": 96, + "scrollHeight": 96, + "left": 286, + "right": 1408, + "overflowX": "auto", + "overflowY": "auto" + }, + { + "label": "Edit Company", + "tabIndex": -1, + "clientWidth": 768, + "scrollWidth": 768, + "clientHeight": 418, + "scrollHeight": 418, + "left": 336, + "right": 1104, + "overflowX": "auto", + "overflowY": "auto" + } + ] + }, + "engine": null, + "toolOptions": null, + "violationNodes": 0, + "incompleteNodes": 13, + "raw": { + "path": ".artifacts/phase7/p7-05/companies/final-columns/embedded/companies/031-p7-companies-en-light-1440-live-cross-tab-locale-retained-failure-audit.json", + "sha256": "c6860ade8725c1e5726a539f4d8e30141e231b2014b22cd8f986b89e0c7bb134", + "bytes": 24060, + "hashPolicy": "Exact measured raw bytes" + }, + "originalEmbeddedReport": { + "path": ".artifacts/phase7/p7-05/companies/final-columns/companies.json", + "sha256": "33b99b07dd6285380c201faf629785fdabfa8f736325a06335864c770b95b67a", + "bytes": 1486659, + "hashPolicy": "Exact measured raw bytes" + }, + "context": { + "sample": { + "language": "en", + "theme": "light", + "width": 1440, + "height": 900 + }, + "state": "live-cross-tab-locale-retained-failure", + "boundary": "Company creation is an API fixture: the existing Companies page has no standalone create interface. Company list/detail/nine-field PUT, job creation/soft deletion and manual correspondence use the actual isolated owner HTTP API and disposable SQLite. Route-held requests continue to that API when released; explicitly controlled failures establish presentation and recovery only. No provider/model/external service is invoked. Browser request records contain only Companies method/path/query/JSON payload; direct fixture/readback requests are verified separately and no session or CSRF headers are recorded.", + "qualifications": [ + "Finite Chromium states, not a Cartesian width/language/theme/state claim.", + "A named bounded desktop company table may legitimately scroll in two dimensions; its geometry is recorded separately from app-root overflow.", + "Axe incomplete findings require contextual triage; automated keyboard/focus checks do not establish human screen-reader acceptance.", + "Real zoom, physical touch/software keyboard, deployed fonts/photos and production acceptance remain unverified." + ] + }, + "rules": [ + { + "kind": "incomplete", + "id": "aria-hidden-focus", + "nodes": 1 + }, + { + "kind": "incomplete", + "id": "color-contrast", + "nodes": 12 + } + ] + }, + { + "test": "[P7-05 Companies] eight finite language/theme widths reflow with named table scrolling and keyboard dialogs", + "attachment": "p7-companies-en-light-320-loaded-finite-sample-audit", + "geometry": { + "width": 320, + "height": 740, + "rootOverflow": 0, + "visibleHorizontalBounds": [], + "scrollRegions": [] + }, + "engine": null, + "toolOptions": null, + "violationNodes": 0, + "incompleteNodes": 1, + "raw": { + "path": ".artifacts/phase7/p7-05/companies/final-columns/embedded/companies/035-p7-companies-en-light-320-loaded-finite-sample-audit.json", + "sha256": "61a31aa609bf4351113af6d8af4ae33a3988d25c8df2be60b7048f146fecf8f6", + "bytes": 5484, + "hashPolicy": "Exact measured raw bytes" + }, + "originalEmbeddedReport": { + "path": ".artifacts/phase7/p7-05/companies/final-columns/companies.json", + "sha256": "33b99b07dd6285380c201faf629785fdabfa8f736325a06335864c770b95b67a", + "bytes": 1486659, + "hashPolicy": "Exact measured raw bytes" + }, + "context": { + "sample": { + "language": "en", + "theme": "light", + "width": 320, + "height": 740 + }, + "state": "loaded-finite-sample", + "boundary": "Company creation is an API fixture: the existing Companies page has no standalone create interface. Company list/detail/nine-field PUT, job creation/soft deletion and manual correspondence use the actual isolated owner HTTP API and disposable SQLite. Route-held requests continue to that API when released; explicitly controlled failures establish presentation and recovery only. No provider/model/external service is invoked. Browser request records contain only Companies method/path/query/JSON payload; direct fixture/readback requests are verified separately and no session or CSRF headers are recorded.", + "qualifications": [ + "Finite Chromium states, not a Cartesian width/language/theme/state claim.", + "A named bounded desktop company table may legitimately scroll in two dimensions; its geometry is recorded separately from app-root overflow.", + "Axe incomplete findings require contextual triage; automated keyboard/focus checks do not establish human screen-reader acceptance.", + "Real zoom, physical touch/software keyboard, deployed fonts/photos and production acceptance remain unverified." + ] + }, + "rules": [ + { + "kind": "incomplete", + "id": "color-contrast", + "nodes": 1 + } + ] + }, + { + "test": "[P7-05 Companies] eight finite language/theme widths reflow with named table scrolling and keyboard dialogs", + "attachment": "p7-companies-en-light-320-mobile-edit-keyboard-trap-audit", + "geometry": { + "width": 320, + "height": 740, + "rootOverflow": 0, + "visibleHorizontalBounds": [], + "scrollRegions": [ + { + "label": "Edit Company", + "tabIndex": -1, + "clientWidth": 320, + "scrollWidth": 320, + "clientHeight": 575, + "scrollHeight": 575, + "left": 0, + "right": 320, + "overflowX": "auto", + "overflowY": "auto" + } + ] + }, + "engine": null, + "toolOptions": null, + "violationNodes": 0, + "incompleteNodes": 12, + "raw": { + "path": ".artifacts/phase7/p7-05/companies/final-columns/embedded/companies/037-p7-companies-en-light-320-mobile-edit-keyboard-trap-audit.json", + "sha256": "7032716dafc7aeaa3042393221a79ac205fa49f4e197009d2a6d037cf2a955a9", + "bytes": 15542, + "hashPolicy": "Exact measured raw bytes" + }, + "originalEmbeddedReport": { + "path": ".artifacts/phase7/p7-05/companies/final-columns/companies.json", + "sha256": "33b99b07dd6285380c201faf629785fdabfa8f736325a06335864c770b95b67a", + "bytes": 1486659, + "hashPolicy": "Exact measured raw bytes" + }, + "context": { + "sample": { + "language": "en", + "theme": "light", + "width": 320, + "height": 740 + }, + "state": "mobile-edit-keyboard-trap", + "boundary": "Company creation is an API fixture: the existing Companies page has no standalone create interface. Company list/detail/nine-field PUT, job creation/soft deletion and manual correspondence use the actual isolated owner HTTP API and disposable SQLite. Route-held requests continue to that API when released; explicitly controlled failures establish presentation and recovery only. No provider/model/external service is invoked. Browser request records contain only Companies method/path/query/JSON payload; direct fixture/readback requests are verified separately and no session or CSRF headers are recorded.", + "qualifications": [ + "Finite Chromium states, not a Cartesian width/language/theme/state claim.", + "A named bounded desktop company table may legitimately scroll in two dimensions; its geometry is recorded separately from app-root overflow.", + "Axe incomplete findings require contextual triage; automated keyboard/focus checks do not establish human screen-reader acceptance.", + "Real zoom, physical touch/software keyboard, deployed fonts/photos and production acceptance remain unverified." + ] + }, + "rules": [ + { + "kind": "incomplete", + "id": "aria-hidden-focus", + "nodes": 1 + }, + { + "kind": "incomplete", + "id": "color-contrast", + "nodes": 11 + } + ] + }, + { + "test": "[P7-05 Companies] eight finite language/theme widths reflow with named table scrolling and keyboard dialogs", + "attachment": "p7-companies-nb-dark-360-loaded-finite-sample-audit", + "geometry": { + "width": 360, + "height": 844, + "rootOverflow": 0, + "visibleHorizontalBounds": [], + "scrollRegions": [] + }, + "engine": null, + "toolOptions": null, + "violationNodes": 0, + "incompleteNodes": 1, + "raw": { + "path": ".artifacts/phase7/p7-05/companies/final-columns/embedded/companies/039-p7-companies-nb-dark-360-loaded-finite-sample-audit.json", + "sha256": "af6bae813d995c9d81c8967afd1c4eca50b6ab91e0ff9b3f607a990c9cf44a4b", + "bytes": 5481, + "hashPolicy": "Exact measured raw bytes" + }, + "originalEmbeddedReport": { + "path": ".artifacts/phase7/p7-05/companies/final-columns/companies.json", + "sha256": "33b99b07dd6285380c201faf629785fdabfa8f736325a06335864c770b95b67a", + "bytes": 1486659, + "hashPolicy": "Exact measured raw bytes" + }, + "context": { + "sample": { + "language": "nb", + "theme": "dark", + "width": 360, + "height": 844 + }, + "state": "loaded-finite-sample", + "boundary": "Company creation is an API fixture: the existing Companies page has no standalone create interface. Company list/detail/nine-field PUT, job creation/soft deletion and manual correspondence use the actual isolated owner HTTP API and disposable SQLite. Route-held requests continue to that API when released; explicitly controlled failures establish presentation and recovery only. No provider/model/external service is invoked. Browser request records contain only Companies method/path/query/JSON payload; direct fixture/readback requests are verified separately and no session or CSRF headers are recorded.", + "qualifications": [ + "Finite Chromium states, not a Cartesian width/language/theme/state claim.", + "A named bounded desktop company table may legitimately scroll in two dimensions; its geometry is recorded separately from app-root overflow.", + "Axe incomplete findings require contextual triage; automated keyboard/focus checks do not establish human screen-reader acceptance.", + "Real zoom, physical touch/software keyboard, deployed fonts/photos and production acceptance remain unverified." + ] + }, + "rules": [ + { + "kind": "incomplete", + "id": "color-contrast", + "nodes": 1 + } + ] + }, + { + "test": "[P7-05 Companies] eight finite language/theme widths reflow with named table scrolling and keyboard dialogs", + "attachment": "p7-companies-en-dark-390-loaded-finite-sample-audit", + "geometry": { + "width": 390, + "height": 844, + "rootOverflow": 0, + "visibleHorizontalBounds": [], + "scrollRegions": [] + }, + "engine": null, + "toolOptions": null, + "violationNodes": 0, + "incompleteNodes": 1, + "raw": { + "path": ".artifacts/phase7/p7-05/companies/final-columns/embedded/companies/041-p7-companies-en-dark-390-loaded-finite-sample-audit.json", + "sha256": "5ebb37f8064089339f69d207626063a5688c11d08adb6e8beaeb7c2347b0f6c2", + "bytes": 5483, + "hashPolicy": "Exact measured raw bytes" + }, + "originalEmbeddedReport": { + "path": ".artifacts/phase7/p7-05/companies/final-columns/companies.json", + "sha256": "33b99b07dd6285380c201faf629785fdabfa8f736325a06335864c770b95b67a", + "bytes": 1486659, + "hashPolicy": "Exact measured raw bytes" + }, + "context": { + "sample": { + "language": "en", + "theme": "dark", + "width": 390, + "height": 844 + }, + "state": "loaded-finite-sample", + "boundary": "Company creation is an API fixture: the existing Companies page has no standalone create interface. Company list/detail/nine-field PUT, job creation/soft deletion and manual correspondence use the actual isolated owner HTTP API and disposable SQLite. Route-held requests continue to that API when released; explicitly controlled failures establish presentation and recovery only. No provider/model/external service is invoked. Browser request records contain only Companies method/path/query/JSON payload; direct fixture/readback requests are verified separately and no session or CSRF headers are recorded.", + "qualifications": [ + "Finite Chromium states, not a Cartesian width/language/theme/state claim.", + "A named bounded desktop company table may legitimately scroll in two dimensions; its geometry is recorded separately from app-root overflow.", + "Axe incomplete findings require contextual triage; automated keyboard/focus checks do not establish human screen-reader acceptance.", + "Real zoom, physical touch/software keyboard, deployed fonts/photos and production acceptance remain unverified." + ] + }, + "rules": [ + { + "kind": "incomplete", + "id": "color-contrast", + "nodes": 1 + } + ] + }, + { + "test": "[P7-05 Companies] eight finite language/theme widths reflow with named table scrolling and keyboard dialogs", + "attachment": "p7-companies-nb-light-767-loaded-finite-sample-audit", + "geometry": { + "width": 767, + "height": 900, + "rootOverflow": 0, + "visibleHorizontalBounds": [], + "scrollRegions": [] + }, + "engine": null, + "toolOptions": null, + "violationNodes": 0, + "incompleteNodes": 1, + "raw": { + "path": ".artifacts/phase7/p7-05/companies/final-columns/embedded/companies/043-p7-companies-nb-light-767-loaded-finite-sample-audit.json", + "sha256": "0ede689227845495f4355561287ef24a189c775e811aef711129d750b625798c", + "bytes": 5482, + "hashPolicy": "Exact measured raw bytes" + }, + "originalEmbeddedReport": { + "path": ".artifacts/phase7/p7-05/companies/final-columns/companies.json", + "sha256": "33b99b07dd6285380c201faf629785fdabfa8f736325a06335864c770b95b67a", + "bytes": 1486659, + "hashPolicy": "Exact measured raw bytes" + }, + "context": { + "sample": { + "language": "nb", + "theme": "light", + "width": 767, + "height": 900 + }, + "state": "loaded-finite-sample", + "boundary": "Company creation is an API fixture: the existing Companies page has no standalone create interface. Company list/detail/nine-field PUT, job creation/soft deletion and manual correspondence use the actual isolated owner HTTP API and disposable SQLite. Route-held requests continue to that API when released; explicitly controlled failures establish presentation and recovery only. No provider/model/external service is invoked. Browser request records contain only Companies method/path/query/JSON payload; direct fixture/readback requests are verified separately and no session or CSRF headers are recorded.", + "qualifications": [ + "Finite Chromium states, not a Cartesian width/language/theme/state claim.", + "A named bounded desktop company table may legitimately scroll in two dimensions; its geometry is recorded separately from app-root overflow.", + "Axe incomplete findings require contextual triage; automated keyboard/focus checks do not establish human screen-reader acceptance.", + "Real zoom, physical touch/software keyboard, deployed fonts/photos and production acceptance remain unverified." + ] + }, + "rules": [ + { + "kind": "incomplete", + "id": "color-contrast", + "nodes": 1 + } + ] + }, + { + "test": "[P7-05 Companies] eight finite language/theme widths reflow with named table scrolling and keyboard dialogs", + "attachment": "p7-companies-en-dark-768-loaded-finite-sample-audit", + "geometry": { + "width": 768, + "height": 1024, + "rootOverflow": 0, + "visibleHorizontalBounds": [ + { + "tag": "TABLE", + "role": null, + "label": "Companies", + "text": "NameLocationSourcePipelineRecruiterNext ContactActionsSamfunnskritiske systemer og rekruttering for ", + "left": 24, + "right": 864, + "width": 840, + "insideNamedScrollRegion": "Companies" + }, + { + "tag": "THEAD", + "role": null, + "label": null, + "text": "NameLocationSourcePipelineRecruiterNext ContactActions", + "left": 24, + "right": 864, + "width": 840, + "insideNamedScrollRegion": "Companies" + }, + { + "tag": "TR", + "role": null, + "label": null, + "text": "NameLocationSourcePipelineRecruiterNext ContactActions", + "left": 24, + "right": 864, + "width": 840, + "insideNamedScrollRegion": "Companies" + }, + { + "tag": "TH", + "role": null, + "label": null, + "text": "Next Contact", + "left": 662.578125, + "right": 772, + "width": 109.421875, + "insideNamedScrollRegion": "Companies" + }, + { + "tag": "TH", + "role": null, + "label": null, + "text": "Actions", + "left": 772, + "right": 864, + "width": 92, + "insideNamedScrollRegion": "Companies" + }, + { + "tag": "TBODY", + "role": null, + "label": null, + "text": "Samfunnskritiske systemer og rekruttering for nordnorske virksomheter Æ Ø Å — langsiktig samarbeidTr", + "left": 24, + "right": 864, + "width": 840, + "insideNamedScrollRegion": "Companies" + }, + { + "tag": "TR", + "role": null, + "label": null, + "text": "Samfunnskritiske systemer og rekruttering for nordnorske virksomheter Æ Ø Å — langsiktig samarbeidTr", + "left": 24, + "right": 864, + "width": 840, + "insideNamedScrollRegion": "Companies" + }, + { + "tag": "TD", + "role": null, + "label": null, + "text": "12/1/2026", + "left": 662.578125, + "right": 772, + "width": 109.421875, + "insideNamedScrollRegion": "Companies" + }, + { + "tag": "TD", + "role": null, + "label": null, + "text": "", + "left": 772, + "right": 864, + "width": 92, + "insideNamedScrollRegion": "Companies" + }, + { + "tag": "BUTTON", + "role": null, + "label": "Relationship history: Samfunnskritiske systemer og rekruttering for nordnorske virksomheter Æ Ø Å — langsiktig samarbeid", + "text": "", + "left": 788, + "right": 818, + "width": 30, + "insideNamedScrollRegion": "Companies" + }, + { + "tag": "BUTTON", + "role": null, + "label": "Edit: Samfunnskritiske systemer og rekruttering for nordnorske virksomheter Æ Ø Å — langsiktig samarbeid", + "text": "", + "left": 818, + "right": 848, + "width": 30, + "insideNamedScrollRegion": "Companies" + } + ], + "scrollRegions": [ + { + "label": "Companies", + "tabIndex": 0, + "clientWidth": 718, + "scrollWidth": 840, + "clientHeight": 202, + "scrollHeight": 202, + "left": 24, + "right": 744, + "overflowX": "auto", + "overflowY": "auto" + } + ] + }, + "engine": null, + "toolOptions": null, + "violationNodes": 0, + "incompleteNodes": 1, + "raw": { + "path": ".artifacts/phase7/p7-05/companies/final-columns/embedded/companies/045-p7-companies-en-dark-768-loaded-finite-sample-audit.json", + "sha256": "02956f379ccdc34b87e8f4ec31764afed658951cfede17f2fee24284290e0c30", + "bytes": 9049, + "hashPolicy": "Exact measured raw bytes" + }, + "originalEmbeddedReport": { + "path": ".artifacts/phase7/p7-05/companies/final-columns/companies.json", + "sha256": "33b99b07dd6285380c201faf629785fdabfa8f736325a06335864c770b95b67a", + "bytes": 1486659, + "hashPolicy": "Exact measured raw bytes" + }, + "context": { + "sample": { + "language": "en", + "theme": "dark", + "width": 768, + "height": 1024 + }, + "state": "loaded-finite-sample", + "boundary": "Company creation is an API fixture: the existing Companies page has no standalone create interface. Company list/detail/nine-field PUT, job creation/soft deletion and manual correspondence use the actual isolated owner HTTP API and disposable SQLite. Route-held requests continue to that API when released; explicitly controlled failures establish presentation and recovery only. No provider/model/external service is invoked. Browser request records contain only Companies method/path/query/JSON payload; direct fixture/readback requests are verified separately and no session or CSRF headers are recorded.", + "qualifications": [ + "Finite Chromium states, not a Cartesian width/language/theme/state claim.", + "A named bounded desktop company table may legitimately scroll in two dimensions; its geometry is recorded separately from app-root overflow.", + "Axe incomplete findings require contextual triage; automated keyboard/focus checks do not establish human screen-reader acceptance.", + "Real zoom, physical touch/software keyboard, deployed fonts/photos and production acceptance remain unverified." + ] + }, + "rules": [ + { + "kind": "incomplete", + "id": "color-contrast", + "nodes": 1 + } + ] + }, + { + "test": "[P7-05 Companies] eight finite language/theme widths reflow with named table scrolling and keyboard dialogs", + "attachment": "p7-companies-nb-light-1024-loaded-finite-sample-audit", + "geometry": { + "width": 1024, + "height": 320, + "rootOverflow": 0, + "visibleHorizontalBounds": [ + { + "tag": "TABLE", + "role": null, + "label": "Selskaper", + "text": "NavnStedKildePipelineRekruttererNeste kontaktHandlingerSamfunnskritiske systemer og rekruttering for", + "left": 278, + "right": 1118, + "width": 840, + "insideNamedScrollRegion": "Selskaper" + }, + { + "tag": "THEAD", + "role": null, + "label": null, + "text": "NavnStedKildePipelineRekruttererNeste kontaktHandlinger", + "left": 278, + "right": 1118, + "width": 840, + "insideNamedScrollRegion": "Selskaper" + }, + { + "tag": "TR", + "role": null, + "label": null, + "text": "NavnStedKildePipelineRekruttererNeste kontaktHandlinger", + "left": 278, + "right": 1118, + "width": 840, + "insideNamedScrollRegion": "Selskaper" + }, + { + "tag": "TH", + "role": null, + "label": null, + "text": "Handlinger", + "left": 1020.296875, + "right": 1118, + "width": 97.703125, + "insideNamedScrollRegion": "Selskaper" + }, + { + "tag": "TBODY", + "role": null, + "label": null, + "text": "Samfunnskritiske systemer og rekruttering for nordnorske virksomheter Æ Ø Å — langsiktig samarbeidTr", + "left": 278, + "right": 1118, + "width": 840, + "insideNamedScrollRegion": "Selskaper" + }, + { + "tag": "TR", + "role": null, + "label": null, + "text": "Samfunnskritiske systemer og rekruttering for nordnorske virksomheter Æ Ø Å — langsiktig samarbeidTr", + "left": 278, + "right": 1118, + "width": 840, + "insideNamedScrollRegion": "Selskaper" + }, + { + "tag": "TD", + "role": null, + "label": null, + "text": "", + "left": 1020.296875, + "right": 1118, + "width": 97.703125, + "insideNamedScrollRegion": "Selskaper" + }, + { + "tag": "BUTTON", + "role": null, + "label": "Relasjonshistorikk: Samfunnskritiske systemer og rekruttering for nordnorske virksomheter Æ Ø Å — langsiktig samarbeid", + "text": "", + "left": 1042, + "right": 1072, + "width": 30, + "insideNamedScrollRegion": "Selskaper" + }, + { + "tag": "BUTTON", + "role": null, + "label": "Rediger: Samfunnskritiske systemer og rekruttering for nordnorske virksomheter Æ Ø Å — langsiktig samarbeid", + "text": "", + "left": 1072, + "right": 1102, + "width": 30, + "insideNamedScrollRegion": "Selskaper" + } + ], + "scrollRegions": [ + { + "label": "Selskaper", + "tabIndex": 0, + "clientWidth": 720, + "scrollWidth": 840, + "clientHeight": 202, + "scrollHeight": 202, + "left": 278, + "right": 1000, + "overflowX": "auto", + "overflowY": "auto" + } + ] + }, + "engine": null, + "toolOptions": null, + "violationNodes": 0, + "incompleteNodes": 1, + "raw": { + "path": ".artifacts/phase7/p7-05/companies/final-columns/embedded/companies/047-p7-companies-nb-light-1024-loaded-finite-sample-audit.json", + "sha256": "cf255e337877f9d76bfd63caaf78ffb960a74ce54992efc954ec36c575a8c31b", + "bytes": 10329, + "hashPolicy": "Exact measured raw bytes" + }, + "originalEmbeddedReport": { + "path": ".artifacts/phase7/p7-05/companies/final-columns/companies.json", + "sha256": "33b99b07dd6285380c201faf629785fdabfa8f736325a06335864c770b95b67a", + "bytes": 1486659, + "hashPolicy": "Exact measured raw bytes" + }, + "context": { + "sample": { + "language": "nb", + "theme": "light", + "width": 1024, + "height": 320 + }, + "state": "loaded-finite-sample", + "boundary": "Company creation is an API fixture: the existing Companies page has no standalone create interface. Company list/detail/nine-field PUT, job creation/soft deletion and manual correspondence use the actual isolated owner HTTP API and disposable SQLite. Route-held requests continue to that API when released; explicitly controlled failures establish presentation and recovery only. No provider/model/external service is invoked. Browser request records contain only Companies method/path/query/JSON payload; direct fixture/readback requests are verified separately and no session or CSRF headers are recorded.", + "qualifications": [ + "Finite Chromium states, not a Cartesian width/language/theme/state claim.", + "A named bounded desktop company table may legitimately scroll in two dimensions; its geometry is recorded separately from app-root overflow.", + "Axe incomplete findings require contextual triage; automated keyboard/focus checks do not establish human screen-reader acceptance.", + "Real zoom, physical touch/software keyboard, deployed fonts/photos and production acceptance remain unverified." + ] + }, + "rules": [ + { + "kind": "incomplete", + "id": "color-contrast", + "nodes": 1 + } + ] + }, + { + "test": "[P7-05 Companies] eight finite language/theme widths reflow with named table scrolling and keyboard dialogs", + "attachment": "p7-companies-nb-light-1024-landscape-relationship-scroll-trap-audit", + "geometry": { + "width": 1024, + "height": 320, + "rootOverflow": 0, + "visibleHorizontalBounds": [], + "scrollRegions": [ + { + "label": "Selskaper", + "tabIndex": 0, + "clientWidth": 720, + "scrollWidth": 840, + "clientHeight": 202, + "scrollHeight": 202, + "left": 278, + "right": 1000, + "overflowX": "auto", + "overflowY": "auto" + }, + { + "label": "Relasjonshistorikk", + "tabIndex": -1, + "clientWidth": 960, + "scrollWidth": 960, + "clientHeight": 152, + "scrollHeight": 291, + "left": 32, + "right": 992, + "overflowX": "auto", + "overflowY": "auto" + } + ] + }, + "engine": null, + "toolOptions": null, + "violationNodes": 0, + "incompleteNodes": 11, + "raw": { + "path": ".artifacts/phase7/p7-05/companies/final-columns/embedded/companies/049-p7-companies-nb-light-1024-landscape-relationship-scroll-trap-audit.json", + "sha256": "f0fd2d1ffd4a82b102476caa460910c51cac471307d87e640b69ff4ba70a43d2", + "bytes": 24222, + "hashPolicy": "Exact measured raw bytes" + }, + "originalEmbeddedReport": { + "path": ".artifacts/phase7/p7-05/companies/final-columns/companies.json", + "sha256": "33b99b07dd6285380c201faf629785fdabfa8f736325a06335864c770b95b67a", + "bytes": 1486659, + "hashPolicy": "Exact measured raw bytes" + }, + "context": { + "sample": { + "language": "nb", + "theme": "light", + "width": 1024, + "height": 320 + }, + "state": "landscape-relationship-scroll-trap", + "boundary": "Company creation is an API fixture: the existing Companies page has no standalone create interface. Company list/detail/nine-field PUT, job creation/soft deletion and manual correspondence use the actual isolated owner HTTP API and disposable SQLite. Route-held requests continue to that API when released; explicitly controlled failures establish presentation and recovery only. No provider/model/external service is invoked. Browser request records contain only Companies method/path/query/JSON payload; direct fixture/readback requests are verified separately and no session or CSRF headers are recorded.", + "qualifications": [ + "Finite Chromium states, not a Cartesian width/language/theme/state claim.", + "A named bounded desktop company table may legitimately scroll in two dimensions; its geometry is recorded separately from app-root overflow.", + "Axe incomplete findings require contextual triage; automated keyboard/focus checks do not establish human screen-reader acceptance.", + "Real zoom, physical touch/software keyboard, deployed fonts/photos and production acceptance remain unverified." + ] + }, + "rules": [ + { + "kind": "incomplete", + "id": "aria-hidden-focus", + "nodes": 1 + }, + { + "kind": "incomplete", + "id": "color-contrast", + "nodes": 10 + } + ] + }, + { + "test": "[P7-05 Companies] eight finite language/theme widths reflow with named table scrolling and keyboard dialogs", + "attachment": "p7-companies-en-light-1440-loaded-finite-sample-audit", + "geometry": { + "width": 1440, + "height": 900, + "rootOverflow": 0, + "visibleHorizontalBounds": [], + "scrollRegions": [ + { + "label": "Companies", + "tabIndex": 0, + "clientWidth": 1120, + "scrollWidth": 1120, + "clientHeight": 134, + "scrollHeight": 134, + "left": 286, + "right": 1408, + "overflowX": "auto", + "overflowY": "auto" + } + ] + }, + "engine": null, + "toolOptions": null, + "violationNodes": 0, + "incompleteNodes": 1, + "raw": { + "path": ".artifacts/phase7/p7-05/companies/final-columns/embedded/companies/052-p7-companies-en-light-1440-loaded-finite-sample-audit.json", + "sha256": "07f48caf4f9e7b3447ece746c7e68d3d78606a8d84bca6a98b50f99412c55aa8", + "bytes": 8492, + "hashPolicy": "Exact measured raw bytes" + }, + "originalEmbeddedReport": { + "path": ".artifacts/phase7/p7-05/companies/final-columns/companies.json", + "sha256": "33b99b07dd6285380c201faf629785fdabfa8f736325a06335864c770b95b67a", + "bytes": 1486659, + "hashPolicy": "Exact measured raw bytes" + }, + "context": { + "sample": { + "language": "en", + "theme": "light", + "width": 1440, + "height": 900 + }, + "state": "loaded-finite-sample", + "boundary": "Company creation is an API fixture: the existing Companies page has no standalone create interface. Company list/detail/nine-field PUT, job creation/soft deletion and manual correspondence use the actual isolated owner HTTP API and disposable SQLite. Route-held requests continue to that API when released; explicitly controlled failures establish presentation and recovery only. No provider/model/external service is invoked. Browser request records contain only Companies method/path/query/JSON payload; direct fixture/readback requests are verified separately and no session or CSRF headers are recorded.", + "qualifications": [ + "Finite Chromium states, not a Cartesian width/language/theme/state claim.", + "A named bounded desktop company table may legitimately scroll in two dimensions; its geometry is recorded separately from app-root overflow.", + "Axe incomplete findings require contextual triage; automated keyboard/focus checks do not establish human screen-reader acceptance.", + "Real zoom, physical touch/software keyboard, deployed fonts/photos and production acceptance remain unverified." + ] + }, + "rules": [ + { + "kind": "incomplete", + "id": "color-contrast", + "nodes": 1 + } + ] + }, + { + "test": "[P7-05 Companies] eight finite language/theme widths reflow with named table scrolling and keyboard dialogs", + "attachment": "p7-companies-nb-dark-1920-loaded-finite-sample-audit", + "geometry": { + "width": 1920, + "height": 1080, + "rootOverflow": 0, + "visibleHorizontalBounds": [], + "scrollRegions": [ + { + "label": "Selskaper", + "tabIndex": 0, + "clientWidth": 1318, + "scrollWidth": 1318, + "clientHeight": 117, + "scrollHeight": 117, + "left": 427, + "right": 1747, + "overflowX": "auto", + "overflowY": "auto" + } + ] + }, + "engine": null, + "toolOptions": null, + "violationNodes": 0, + "incompleteNodes": 1, + "raw": { + "path": ".artifacts/phase7/p7-05/companies/final-columns/embedded/companies/054-p7-companies-nb-dark-1920-loaded-finite-sample-audit.json", + "sha256": "99bc0b9c9735cf7d75df98b9cf5d1409f20d7a1b4631db83873c0df59bdbda89", + "bytes": 8475, + "hashPolicy": "Exact measured raw bytes" + }, + "originalEmbeddedReport": { + "path": ".artifacts/phase7/p7-05/companies/final-columns/companies.json", + "sha256": "33b99b07dd6285380c201faf629785fdabfa8f736325a06335864c770b95b67a", + "bytes": 1486659, + "hashPolicy": "Exact measured raw bytes" + }, + "context": { + "sample": { + "language": "nb", + "theme": "dark", + "width": 1920, + "height": 1080 + }, + "state": "loaded-finite-sample", + "boundary": "Company creation is an API fixture: the existing Companies page has no standalone create interface. Company list/detail/nine-field PUT, job creation/soft deletion and manual correspondence use the actual isolated owner HTTP API and disposable SQLite. Route-held requests continue to that API when released; explicitly controlled failures establish presentation and recovery only. No provider/model/external service is invoked. Browser request records contain only Companies method/path/query/JSON payload; direct fixture/readback requests are verified separately and no session or CSRF headers are recorded.", + "qualifications": [ + "Finite Chromium states, not a Cartesian width/language/theme/state claim.", + "A named bounded desktop company table may legitimately scroll in two dimensions; its geometry is recorded separately from app-root overflow.", + "Axe incomplete findings require contextual triage; automated keyboard/focus checks do not establish human screen-reader acceptance.", + "Real zoom, physical touch/software keyboard, deployed fonts/photos and production acceptance remain unverified." + ] + }, + "rules": [ + { + "kind": "incomplete", + "id": "color-contrast", + "nodes": 1 + } + ] + } + ], + "violationNodes": 0, + "incompleteNodes": 91, + "checkContexts": [ + { + "kind": "incomplete", + "rule": "color-contrast", + "check": "color-contrast", + "message": "Element content is too short to determine if it is actual text content", + "occurrences": 20, + "representativeContexts": [ + { + "attachment": "p7-companies-en-light-1440-saved-reloaded-audit", + "target": [ + ".MuiBadge-invisible" + ], + "data": { + "contrastRatio": 0, + "fontSize": "9.0pt (12px)", + "fontWeight": "normal", + "messageKey": "shortTextContent", + "expectedContrastRatio": "4.5:1" + }, + "relatedNodes": [] + }, + { + "attachment": "p7-companies-en-light-1440-latest20-owned-relationship-audit", + "target": [ + ".MuiBadge-invisible" + ], + "data": { + "contrastRatio": 0, + "fontSize": "9.0pt (12px)", + "fontWeight": "normal", + "messageKey": "shortTextContent", + "expectedContrastRatio": "4.5:1" + }, + "relatedNodes": [] + } + ] + }, + { + "kind": "incomplete", + "rule": "aria-hidden-focus", + "check": "focusable-modal-open", + "message": "Check that focusable elements are not tabbable in the current state", + "occurrences": 11, + "representativeContexts": [ + { + "attachment": "p7-companies-en-light-1440-latest20-owned-relationship-audit", + "target": [ + "#root" + ], + "data": null, + "relatedNodes": [ + { + "html": "Skip to main content", + "target": [ + "a" + ] + }, + { + "html": "", + "target": [ + ".css-11kar8v" + ] + }, + { + "html": "
", + "target": [ + ".MuiDrawer-paperAnchorDockedLeft > .css-1vpgucn > .css-184269b.MuiBox-root > .css-6zugs3.MuiBox-root > .css-r91acf.MuiBox-root:nth-child(1) > .MuiList-padding.css-k1qkeu[aria-label=\"Navigation\"] > .css-hapf9j.MuiListItem-root.MuiListItem-gutters > .css-j56oom.MuiListItemButton-root[aria-label=\"Dashboard\"]" + ] + }, + { + "html": "
", + "target": [ + ".MuiDrawer-paperAnchorDockedLeft > .css-1vpgucn > .css-184269b.MuiBox-root > .css-6zugs3.MuiBox-root > .css-r91acf.MuiBox-root:nth-child(2) > .MuiList-padding.css-k1qkeu[aria-label=\"Job search\"] > .css-hapf9j.MuiListItem-root.MuiListItem-gutters:nth-child(1) > .Mui-selected[aria-current=\"location\"][aria-label=\"Job Applications\"]" + ] + }, + { + "html": "
", + "target": [ + ".MuiDrawer-paperAnchorDockedLeft > .css-1vpgucn > .css-184269b.MuiBox-root > .css-6zugs3.MuiBox-root > .css-r91acf.MuiBox-root:nth-child(2) > .MuiList-padding.css-k1qkeu[aria-label=\"Job search\"] > .css-hapf9j.MuiListItem-root.MuiListItem-gutters:nth-child(1) > .css-1rv7atp[aria-label=\"Job Applications\"] > .css-1ohqk82.MuiListItem-root.MuiListItem-gutters:nth-child(1) > .css-m9zpd7.MuiListItemButton-root.MuiListItemButton-gutters" + ] + }, + { + "html": "
", + "target": [ + ".MuiDrawer-paperAnchorDockedLeft > .css-1vpgucn > .css-184269b.MuiBox-root > .css-6zugs3.MuiBox-root > .css-r91acf.MuiBox-root:nth-child(2) > .MuiList-padding.css-k1qkeu[aria-label=\"Job search\"] > .css-hapf9j.MuiListItem-root.MuiListItem-gutters:nth-child(1) > .css-1rv7atp[aria-label=\"Job Applications\"] > .css-1ohqk82.MuiListItem-root.MuiListItem-gutters:nth-child(2) > .css-m9zpd7.MuiListItemButton-root.MuiListItemButton-gutters" + ] + }, + { + "html": "
", + "target": [ + ".MuiDrawer-paperAnchorDockedLeft > .css-1vpgucn > .css-184269b.MuiBox-root > .css-6zugs3.MuiBox-root > .css-r91acf.MuiBox-root:nth-child(2) > .MuiList-padding.css-k1qkeu[aria-label=\"Job search\"] > .css-hapf9j.MuiListItem-root.MuiListItem-gutters:nth-child(1) > .css-1rv7atp[aria-label=\"Job Applications\"] > .css-1ohqk82.MuiListItem-root.MuiListItem-gutters:nth-child(3) > .Mui-selected.css-m9zpd7[aria-current=\"page\"]" + ] + }, + { + "html": "
", + "target": [ + ".MuiDrawer-paperAnchorDockedLeft > .css-1vpgucn > .css-184269b.MuiBox-root > .css-6zugs3.MuiBox-root > .css-r91acf.MuiBox-root:nth-child(2) > .MuiList-padding.css-k1qkeu[aria-label=\"Job search\"] > .css-hapf9j.MuiListItem-root.MuiListItem-gutters:nth-child(1) > .css-1rv7atp[aria-label=\"Job Applications\"] > .css-1ohqk82.MuiListItem-root.MuiListItem-gutters:nth-child(4) > .css-m9zpd7.MuiListItemButton-root.MuiListItemButton-gutters" + ] + }, + { + "html": "
", + "target": [ + ".MuiDrawer-paperAnchorDockedLeft > .css-1vpgucn > .css-184269b.MuiBox-root > .css-6zugs3.MuiBox-root > .css-r91acf.MuiBox-root:nth-child(2) > .MuiList-padding.css-k1qkeu[aria-label=\"Job search\"] > .css-hapf9j.MuiListItem-root.MuiListItem-gutters:nth-child(1) > .css-1rv7atp[aria-label=\"Job Applications\"] > .css-1ohqk82.MuiListItem-root.MuiListItem-gutters:nth-child(5) > .css-m9zpd7.MuiListItemButton-root.MuiListItemButton-gutters" + ] + }, + { + "html": "
", + "target": [ + ".MuiDrawer-paperAnchorDockedLeft > .css-1vpgucn > .css-184269b.MuiBox-root > .css-6zugs3.MuiBox-root > .css-r91acf.MuiBox-root:nth-child(2) > .MuiList-padding.css-k1qkeu[aria-label=\"Job search\"] > .css-hapf9j.MuiListItem-root.MuiListItem-gutters:nth-child(2) > .css-j56oom.MuiListItemButton-root[aria-label=\"Job email\"]" + ] + }, + { + "html": "
", + "target": [ + ".MuiDrawer-paperAnchorDockedLeft > .css-1vpgucn > .css-184269b.MuiBox-root > .css-6zugs3.MuiBox-root > .css-r91acf.MuiBox-root:nth-child(3) > .MuiList-padding.css-k1qkeu[aria-label=\"Career\"] > .css-hapf9j.MuiListItem-root.MuiListItem-gutters:nth-child(1) > .css-j56oom.MuiListItemButton-root[aria-label=\"Career profile\"]" + ] + }, + { + "html": "
", + "target": [ + ".MuiDrawer-paperAnchorDockedLeft > .css-1vpgucn > .css-184269b.MuiBox-root > .css-6zugs3.MuiBox-root > .css-r91acf.MuiBox-root:nth-child(3) > .MuiList-padding.css-k1qkeu[aria-label=\"Career\"] > .css-hapf9j.MuiListItem-root.MuiListItem-gutters:nth-child(2) > .css-j56oom.MuiListItemButton-root[aria-label=\"CV Builder\"]" + ] + }, + { + "html": "
", + "target": [ + ".MuiDrawer-paperAnchorDockedLeft > .css-1vpgucn > .css-184269b.MuiBox-root > .css-6zugs3.MuiBox-root > .css-r91acf.MuiBox-root:nth-child(4) > .MuiList-padding.css-k1qkeu[aria-label=\"Insights\"] > .css-hapf9j.MuiListItem-root.MuiListItem-gutters > .css-j56oom.MuiListItemButton-root[aria-label=\"Analytics\"]" + ] + }, + { + "html": "
", + "target": [ + ".MuiDrawer-paperAnchorDockedLeft > .css-1vpgucn > .css-6zugs3.MuiBox-root > .css-r91acf.MuiBox-root > .MuiList-padding.css-k1qkeu[aria-label=\"Account\"] > .css-hapf9j.MuiListItem-root.MuiListItem-gutters > .css-j56oom.MuiListItemButton-root[aria-label=\"Settings\"]" + ] + }, + { + "html": "
", + "target": [ + ".MuiDrawer-paperAnchorDockedLeft > .css-1vpgucn > .css-6zugs3.MuiBox-root > .css-r91acf.MuiBox-root > .MuiList-padding.css-k1qkeu[aria-label=\"Account\"] > .css-hapf9j.MuiListItem-root.MuiListItem-gutters > .css-1rv7atp[aria-label=\"Settings\"] > .css-1ohqk82.MuiListItem-root.MuiListItem-gutters:nth-child(1) > .css-m9zpd7.MuiListItemButton-root.MuiListItemButton-gutters" + ] + }, + { + "html": "
", + "target": [ + ".MuiDrawer-paperAnchorDockedLeft > .css-1vpgucn > .css-6zugs3.MuiBox-root > .css-r91acf.MuiBox-root > .MuiList-padding.css-k1qkeu[aria-label=\"Account\"] > .css-hapf9j.MuiListItem-root.MuiListItem-gutters > .css-1rv7atp[aria-label=\"Settings\"] > .css-1ohqk82.MuiListItem-root.MuiListItem-gutters:nth-child(2) > .css-m9zpd7.MuiListItemButton-root.MuiListItemButton-gutters" + ] + }, + { + "html": "
", + "target": [ + ".MuiTableContainer-root" + ] + }, + { + "html": "", + "target": [ + ".css-1u962su" + ] + }, + { + "html": "; +} + +function renderPage(initialEntries: any[] = ["/companies"]) { + return render(); +} + +function deferred() { + let resolve!: (value: T) => void; + let reject!: (reason: unknown) => void; + const promise = new Promise((yes, no) => { resolve = yes; reject = no; }); + return { promise, resolve, reject }; +} + +test("a held company save locks all nine fields and cancellation, submits one exact snapshot, and retains a failed draft", async () => { + const write = deferred(); + mockedApi.get.mockResolvedValue({ data: [companyA] } as any); + mockedApi.put.mockImplementationOnce(() => write.promise); + renderPage(); + fireEvent.click(await screen.findByRole("button", { name: "Edit: Acme" })); + const dialog = await screen.findByRole("dialog", { name: "Edit Company" }); + const draft = { + Name: "Acme updated", Location: "Bergen", Source: "User", "Pipeline stage": " Interview ", + "Recruiter name": " Updated recruiter ", "Recruiter email": " updated@example.test ", + "Recruiter LinkedIn": " https://example.test/recruiter ", "Last contacted": "2026-11-21", "Next contact": "2026-11-23", + }; + Object.entries(draft).forEach(([label, value]) => fireEvent.change(within(dialog).getByLabelText(label), { target: { value } })); + const save = within(dialog).getByRole("button", { name: "Save" }); + fireEvent.click(save); + expect(mockedApi.put).toHaveBeenCalledWith("/companies/3", { + name: "Acme updated", location: "Bergen", source: "User", recruiterName: "Updated recruiter", + recruiterEmail: "updated@example.test", recruiterLinkedIn: "https://example.test/recruiter", + pipelineStage: "Interview", lastContactedAt: "2026-11-21", nextContactAt: "2026-11-23", + }); + expect(save).toBeDisabled(); + Object.keys(draft).forEach((label) => expect(within(dialog).getByLabelText(label)).toBeDisabled()); + expect(within(dialog).getByRole("button", { name: "Cancel" })).toBeDisabled(); + fireEvent.click(save); + fireEvent.click(within(dialog).getByRole("button", { name: "Cancel" })); + expect(mockedApi.put).toHaveBeenCalledTimes(1); + expect(dialog).toBeVisible(); + await act(async () => { write.reject(new Error("synthetic save failure")); }); + expect(await within(dialog).findByRole("alert")).toHaveTextContent("Failed to update company."); + Object.entries(draft).forEach(([label, value]) => expect(within(dialog).getByLabelText(label)).toHaveValue(value)); + expect(save).toBeEnabled(); + expect(within(dialog).getByRole("button", { name: "Cancel" })).toBeEnabled(); + expect(mockedApi.put).toHaveBeenCalledTimes(1); +}); + +test("a locale change translates an existing company-save failure without refetching or replacing its draft", async () => { + mockedApi.get.mockResolvedValue({ data: [companyA] } as any); + mockedApi.put.mockRejectedValueOnce(new Error("synthetic save failure")); + renderPage(); + fireEvent.click(await screen.findByRole("button", { name: "Edit: Acme" })); + const dialog = await screen.findByRole("dialog"); + fireEvent.change(within(dialog).getByRole("textbox", { name: "Name" }), { target: { value: "Acme unsaved" } }); + fireEvent.change(within(dialog).getByRole("textbox", { name: "Recruiter name" }), { target: { value: "Unsaved recruiter" } }); + fireEvent.click(within(dialog).getByRole("button", { name: "Save" })); + expect(await within(dialog).findByRole("alert")).toHaveTextContent("Failed to update company."); + const reads = mockedApi.get.mock.calls.length; + act(() => { window.dispatchEvent(new StorageEvent("storage", { key: "uiLanguage", newValue: "nb" })); }); + expect(within(dialog).getByRole("alert")).toHaveTextContent("Kunne ikke oppdatere selskap."); + expect(within(dialog).getByRole("textbox", { name: "Navn" })).toHaveValue("Acme unsaved"); + expect(within(dialog).getByRole("textbox", { name: "Navn på rekrutterer" })).toHaveValue("Unsaved recruiter"); + expect(mockedApi.get).toHaveBeenCalledTimes(reads); + expect(mockedApi.put).toHaveBeenCalledTimes(1); +}); + +test("retrying a failed relationship read loads the same company without reloading the company list", async () => { + const retry = deferred(); + let reads = 0; + mockedApi.get.mockImplementation((url) => url === "/companies/3/relationship" + ? ++reads === 1 ? Promise.reject(new Error("synthetic relationship failure")) : retry.promise + : Promise.resolve({ data: [companyA] }) as any); + renderPage(); + fireEvent.click(await screen.findByRole("button", { name: "Relationship history: Acme" })); + const dialog = await screen.findByRole("dialog"); + expect(await within(dialog).findByRole("alert")).toHaveTextContent("Could not load the relationship history."); + fireEvent.click(within(dialog).getByRole("button", { name: "Retry" })); + expect(reads).toBe(2); + expect(mockedApi.get.mock.calls.filter(([url]) => url === "/companies")).toHaveLength(1); + expect(within(dialog).queryByText("Acme role")).not.toBeInTheDocument(); + await act(async () => { retry.resolve(relationship()); }); + expect(await within(dialog).findByText("Acme role")).toBeInTheDocument(); + expect(within(dialog).queryByRole("alert")).not.toBeInTheDocument(); + expect(reads).toBe(2); +}); + +test.each(["success", "failure"])("a late %s for relationship A cannot replace the opened relationship B", async (outcome) => { + const previous = deferred(); + mockedApi.get.mockImplementation((url) => url === "/companies/3/relationship" ? previous.promise + : Promise.resolve(url === "/companies/4/relationship" ? relationship(companyB) : { data: [companyA, companyB] }) as any); + renderPage(); + fireEvent.click(await screen.findByRole("button", { name: "Relationship history: Acme" })); + fireEvent.click(await screen.findByRole("button", { name: "Close" })); + await waitFor(() => expect(screen.queryByRole("dialog")).not.toBeInTheDocument()); + fireEvent.click(screen.getByRole("button", { name: "Relationship history: Northstar" })); + const dialog = await screen.findByRole("dialog"); + expect(await within(dialog).findByText("Northstar role")).toBeInTheDocument(); + await act(async () => { + if (outcome === "success") previous.resolve(relationship()); + else previous.reject(new Error("late A relationship failure")); + }); + expect(within(dialog).getByText("Northstar role")).toBeInTheDocument(); + expect(within(dialog).queryByText("Acme role")).not.toBeInTheDocument(); + expect(within(dialog).queryByRole("alert")).not.toBeInTheDocument(); + expect(mockedApi.get.mock.calls.filter(([url]) => String(url).endsWith("/relationship"))).toHaveLength(2); +}); + +test("a relationship response received after closing stays closed and does not populate another held relationship", async () => { + const previous = deferred(); + const next = deferred(); + mockedApi.get.mockImplementation((url) => url === "/companies/3/relationship" ? previous.promise + : url === "/companies/4/relationship" ? next.promise : Promise.resolve({ data: [companyA, companyB] }) as any); + renderPage(); + fireEvent.click(await screen.findByRole("button", { name: "Relationship history: Acme" })); + fireEvent.click(await screen.findByRole("button", { name: "Close" })); + await waitFor(() => expect(screen.queryByRole("dialog")).not.toBeInTheDocument()); + await act(async () => { previous.resolve(relationship()); }); + expect(screen.queryByRole("dialog")).not.toBeInTheDocument(); + fireEvent.click(screen.getByRole("button", { name: "Relationship history: Northstar" })); + const dialog = await screen.findByRole("dialog"); + expect(within(dialog).queryByText("Acme role")).not.toBeInTheDocument(); + expect(within(dialog).queryByText("Northstar role")).not.toBeInTheDocument(); + await act(async () => { next.resolve(relationship(companyB)); }); + expect(await within(dialog).findByText("Northstar role")).toBeInTheDocument(); +}); + +test("the company edit deep link removes only edit while preserving query, hash, state and replace navigation", async () => { + mockedApi.get.mockResolvedValue({ data: [companyA] } as any); + renderPage([ + { pathname: "/jobs/9", search: "?keep=true", hash: "#source", state: { previous: true } }, + { pathname: "/companies", search: "?edit=3&return=jobs%2F9", hash: "#activity", state: { returnTo: "/jobs/9" } }, + ]); + const route = screen.getByLabelText("Route context"); + expect(await screen.findByRole("dialog", { name: "Edit Company" })).toBeInTheDocument(); + await waitFor(() => expect(JSON.parse(route.textContent!).search).toBe("?return=jobs%2F9")); + expect(JSON.parse(route.textContent!)).toEqual({ pathname: "/companies", search: "?return=jobs%2F9", hash: "#activity", state: { returnTo: "/jobs/9" } }); + fireEvent.click(screen.getByRole("button", { name: "Cancel" })); + await waitFor(() => expect(screen.queryByRole("dialog")).not.toBeInTheDocument()); + fireEvent.click(screen.getByRole("button", { name: "Previous route" })); + expect(JSON.parse(route.textContent!)).toEqual({ pathname: "/jobs/9", search: "?keep=true", hash: "#source", state: { previous: true } }); + expect(mockedApi.get).toHaveBeenCalledTimes(1); +}); diff --git a/job-tracker-ui/src/components/CompaniesTable.tsx b/job-tracker-ui/src/components/CompaniesTable.tsx index dabf22a..e7b3967 100644 --- a/job-tracker-ui/src/components/CompaniesTable.tsx +++ b/job-tracker-ui/src/components/CompaniesTable.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useMemo, useState } from "react"; +import React, { useEffect, useMemo, useRef, useState } from "react"; import { useLocation, useNavigate } from "react-router-dom"; import { @@ -30,7 +30,8 @@ import EditOutlinedIcon from "@mui/icons-material/EditOutlined"; import PeopleAltOutlinedIcon from "@mui/icons-material/PeopleAltOutlined"; import { useToast } from "../toast"; import { useI18n } from "../i18n/I18nProvider"; -import { useViewResource } from "../hooks/useViewResource"; +import { useViewResource, type ViewResourceError } from "../hooks/useViewResource"; +import { statusLabel } from "../pipeline"; export default function CompaniesTable() { const isMobile = useMediaQuery("(max-width:767.95px)"); @@ -39,12 +40,22 @@ export default function CompaniesTable() { const location = useLocation(); const navigate = useNavigate(); const [editOpen, setEditOpen] = useState(false); - const [editError, setEditError] = useState(""); + const [editError, setEditError] = useState(null); const [editing, setEditing] = useState(null); + const [saving, setSaving] = useState(false); + const savePending = useRef(false); + const editContentRef = useRef(null); + const editCompanyIdRef = useRef(null); + const containerRef = useRef(null); + const [focusReturn, setFocusReturn] = useState<{ action: "edit" | "relationship"; id?: number | null } | null>(null); const [relationshipOpen, setRelationshipOpen] = useState(false); + const [relationshipCompany, setRelationshipCompany] = useState(null); const [relationshipLoading, setRelationshipLoading] = useState(false); - const [relationshipError, setRelationshipError] = useState(""); + const [relationshipError, setRelationshipError] = useState(null); const [relationship, setRelationship] = useState(null); + const relationshipSequence = useRef(0); + const relationshipPending = useRef(false); + const relationshipContentRef = useRef(null); const [recruiterName, setRecruiterName] = useState(""); const [recruiterEmail, setRecruiterEmail] = useState(""); @@ -60,14 +71,14 @@ export default function CompaniesTable() { }, { initialData: [], - errorMessage: t("companiesLoadFailed"), - deps: [t], + errorMessage: "", }, ); const companies = companiesResource.data; useEffect(() => { + if (savePending.current) return; const params = new URLSearchParams(location.search); const editId = Number(params.get("edit") || 0); if (!editId || companies.length === 0) return; @@ -75,11 +86,22 @@ export default function CompaniesTable() { if (!company) return; openEdit(company); params.delete("edit"); - navigate({ pathname: location.pathname, search: params.toString() ? `?${params.toString()}` : "" }, { replace: true }); - }, [companies, location.pathname, location.search, navigate]); + navigate({ pathname: location.pathname, search: params.toString() ? `?${params.toString()}` : "", hash: location.hash }, { replace: true, state: location.state }); + }, [companies, location.pathname, location.search, location.hash, location.state, navigate, saving]); + + useEffect(() => () => { relationshipSequence.current += 1; }, []); + + useEffect(() => { + // Restore after MUI has removed the exiting modal. + if (focusReturn?.id && document.activeElement === document.body) { + containerRef.current?.querySelector(`[data-company-${focusReturn.action}="${focusReturn.id}"]`)?.focus(); + } + }, [focusReturn]); const openEdit = (c: Company) => { - setEditError(""); + if (savePending.current) return; + editCompanyIdRef.current = c.id; + setEditError(null); setEditing(c); setRecruiterName(c.recruiterName ?? ""); setRecruiterEmail(c.recruiterEmail ?? ""); @@ -91,22 +113,40 @@ export default function CompaniesTable() { }; const openRelationship = async (company: Company) => { - setRelationshipOpen(true); setRelationshipLoading(true); setRelationshipError(""); setRelationship(null); + const request = ++relationshipSequence.current; + relationshipPending.current = true; + setRelationshipCompany(company); + setRelationshipOpen(true); setRelationshipLoading(true); setRelationshipError(null); setRelationship(null); try { const response = await api.get(`/companies/${company.id}/relationship`); - setRelationship(response.data); + if (request === relationshipSequence.current) setRelationship(response.data); } catch (error) { - setRelationshipError(getApiErrorMessage(error, t("companiesRelationshipLoadFailed"))); + if (request !== relationshipSequence.current) return; + const status = (error as { response?: { status?: number } })?.response?.status; + const unauthorized = status === 401 || status === 403; + setRelationshipError({ kind: unauthorized ? "unauthorized" : "unavailable", message: getApiErrorMessage(error, ""), retryable: !unauthorized, status }); } finally { - setRelationshipLoading(false); + if (request === relationshipSequence.current) { + relationshipPending.current = false; + setRelationshipLoading(false); + } } }; + const closeRelationship = () => { + relationshipSequence.current += 1; + relationshipPending.current = false; + setRelationshipOpen(false); + }; + const canSave = useMemo(() => !!editing?.id, [editing]); const save = async () => { - if (!editing?.id) return; - setEditError(""); + if (!editing?.id || savePending.current) return; + savePending.current = true; + editContentRef.current?.focus(); + setSaving(true); + setEditError(null); try { const res = await api.put(`/companies/${editing.id}`, { name: editing.name, @@ -125,7 +165,10 @@ export default function CompaniesTable() { setEditOpen(false); setEditing(null); } catch (error) { - setEditError(getApiErrorMessage(error, t("companiesUpdateFailed"))); + setEditError(getApiErrorMessage(error, "")); + } finally { + savePending.current = false; + setSaving(false); } }; @@ -137,10 +180,10 @@ export default function CompaniesTable() { ); return ( - + {companies.map((c) => ( - + - - {c.name} + + {c.name} {c.location || t("companiesLocation")} - openEdit(c)}> + openEdit(c)}> - void openRelationship(c)}> + void openRelationship(c)}> @@ -181,8 +224,8 @@ export default function CompaniesTable() { ) : null} ) : ( - - + +
{t("companiesName")} @@ -191,26 +234,26 @@ export default function CompaniesTable() { {t("companiesPipeline")} {t("companiesRecruiter")} {t("companiesNextContact")} - + {t("jobTableActions")} {companies.map((c) => ( - {c.name} + {c.name} {c.location ?? ""} - {c.source ?? ""} - {c.pipelineStage ?? ""} + {c.source ?? ""} + {c.pipelineStage ?? ""} {c.recruiterName ?? ""} {c.recruiterEmail ? ` (${c.recruiterEmail})` : ""} - {c.nextContactAt ? new Date(c.nextContactAt).toLocaleDateString(language === "nb" ? "nb-NO" : "en") : ""} - - void openRelationship(c)}> + {c.nextContactAt ? new Date(c.nextContactAt).toLocaleDateString(language === "nb" ? "nb-NO" : "en") : ""} + + void openRelationship(c)}> - openEdit(c)}> + openEdit(c)}> @@ -231,45 +274,54 @@ export default function CompaniesTable() { ) ) : null} - setEditOpen(false)} fullWidth fullScreen={isMobile} maxWidth="sm"> + { if (!savePending.current) setEditOpen(false); }} fullWidth fullScreen={isMobile} maxWidth="sm" + slotProps={{ transition: { onExited: () => setFocusReturn({ action: "edit", id: editCompanyIdRef.current }) } }}> {t("companiesEdit")} - - {editError ? {editError} : null} + + {saving ? : null} + {editError !== null ? {editError || t("companiesUpdateFailed")} : null} setEditing((p) => (p ? { ...p, name: e.target.value } : p))} sx={{ gridColumn: { xs: "1 / -1", md: "1 / -1" } }} /> setEditing((p) => (p ? { ...p, location: e.target.value } : p))} /> setEditing((p) => (p ? { ...p, source: e.target.value } : p))} /> setPipelineStage(e.target.value)} /> setRecruiterName(e.target.value)} /> setRecruiterEmail(e.target.value)} /> setRecruiterLinkedIn(e.target.value)} sx={{ gridColumn: { xs: "1 / -1", md: "1 / -1" } }} @@ -277,6 +329,7 @@ export default function CompaniesTable() { setLastContactedAt(e.target.value)} @@ -284,6 +337,7 @@ export default function CompaniesTable() { /> setNextContactAt(e.target.value)} @@ -292,18 +346,24 @@ export default function CompaniesTable() { - - + - setRelationshipOpen(false)} fullWidth fullScreen={isMobile} maxWidth="md"> - {relationship?.company.name ?? t("companiesRelationship")} - - {relationshipLoading ? {t("loading")} : null} - {relationshipError ? {relationshipError} : null} + setFocusReturn({ action: "relationship", id: relationshipCompany?.id }) } }}> + {relationship?.company.name ?? relationshipCompany?.name ?? t("companiesRelationship")} + + { + if (!relationshipCompany || relationshipPending.current) return; + relationshipContentRef.current?.focus(); + void openRelationship(relationshipCompany); + }} /> {relationship ? ( @@ -312,21 +372,21 @@ export default function CompaniesTable() { {renderCompanyMeta(t("companiesNextContact"), relationship.company.nextContactAt ? new Date(relationship.company.nextContactAt).toLocaleDateString(language === "nb" ? "nb-NO" : "en") : null)} - {t("companiesApplications")} + {t("companiesApplications")} {relationship.applications.length === 0 ? {t("companiesNoApplications")} : ( {relationship.applications.map((application) => ( - {application.jobTitle}{application.status} · {t("companiesMessageCount", { count: application.messageCount })} - + {application.jobTitle}{statusLabel(t, application.status)} · {t("companiesMessageCount", { count: application.messageCount })} + ))} )} - {t("companiesRecentContact")} + {t("companiesRecentContact")} {relationship.recentContacts.length === 0 ? {t("companiesNoContact")} : ( {relationship.recentContacts.map((contact) => ( - + {contact.subject || contact.channel || t("companiesContact")} {contact.jobTitle} · {new Date(contact.date).toLocaleDateString(language === "nb" ? "nb-NO" : "en")} · {contact.from} @@ -336,9 +396,9 @@ export default function CompaniesTable() { ) : null} - + - + ); } diff --git a/job-tracker-ui/src/locale-fetch-stability.test.tsx b/job-tracker-ui/src/locale-fetch-stability.test.tsx index 58b44ca..c7a5e92 100644 --- a/job-tracker-ui/src/locale-fetch-stability.test.tsx +++ b/job-tracker-ui/src/locale-fetch-stability.test.tsx @@ -13,7 +13,7 @@ function Actions() { return <>; } -test("feedback does not reload company data; changing locale still reloads translated states", async () => { +test("feedback and locale changes translate company presentation without reloading company data", async () => { localStorage.clear(); (api.get as jest.Mock).mockResolvedValue({ data: [{ id: 3, name: "Acme" }] }); render(); @@ -25,5 +25,5 @@ test("feedback does not reload company data; changing locale still reloads trans expect(reads()).toBe(1); fireEvent.click(screen.getByRole("button", { name: "Change language" })); await screen.findByRole("button", { name: "Rediger: Acme" }); - await waitFor(() => expect(reads()).toBe(2)); + await waitFor(() => expect(reads()).toBe(1)); });