Files
jobtrackingapp/docs/career-workspace-ux-refactor.md
T
cesnimda 63473bae85 refactor(career): Phase 1 increment — user-facing terminology + component split
UI-only restructuring of the Career Profile surface. No change to
database models, CareerProfiles schema, CvVariants, extraction APIs, AI
services, CV rendering, or public CV.

Terminology -> user-facing (i18n strings):
- "Structured CV editor"     -> "Career information"
- "CV structure overview"    -> "Profile sections"
- "Summary bullets"          -> "Professional summary"
- "Core skills"              -> "Skills"
- "Analyze sections"         -> "Read sections"
- "Original extraction"      -> "Original import"
- hardcoded "Master career profile" -> "Career profile"
Help text de-jargoned; the Career information help now frames it as the
source the CV Builder consumes.

Component split (first step): extract ProfileCompleteness (completeness
meter + missing chips + version history) into src/views/career/. Display
only, props in, no state or API.

Save path untouched: api.put("/career/profile", { profile, cvText }). A
new test pins that exact call as the refactor invariant so the remaining
section extraction cannot silently change save behaviour. Existing
profile-page tests re-pointed to the new labels; every behavioural
assertion (save, parse, field values) kept.

Verified: tsc clean, production build clean, 136 frontend tests pass
(135 + 1 invariant). Sidebar fix from the previous task still passes.
Backend untouched.

The remaining Phase 1 work (per-section editor components, hiding the
template-driven builder and structure-overview blocks, actionable
per-section empty states) is staged in docs/career-workspace-ux-refactor.md
because it touches the live extraction test surface and is best verified
by driving the authenticated UI. This increment is a clean, non-regressing
checkpoint.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 02:50:52 +02:00

9.3 KiB
Raw Blame History

Career Workspace + CV Builder UX refactor

2026-07-20. UX/product restructuring against v1.0.0. Frontend-only — no change to CareerProfiles, CvVariants, the CV generation pipeline, extraction APIs, AI services, permissions, or tenant isolation.

This document is the plan. It is delivered staged: Phase 0 (the sidebar bug and the framing copy) is implemented and tested now; Phases 14 are scoped for incremental delivery because they require surgery on the 1376-line CareerProfilePage and touch the production CV/extraction flow, where a single large rewrite would risk the "do not break" list. Each later phase is independently shippable and verifiable.

Current problems

  1. Sidebar double-highlight. On /career/builder/{id}, both "Career Workspace" and "CV Builder" light up. Root cause: AppShell used pathname === to || pathname.startsWith(to + "/") per item, so /career matched every /career/... child. No "most specific wins" rule. (Fixed — Phase 0.)
  2. Too many competing concepts on one page. CareerWorkspacePage is a thin shell around CareerProfilePage (1376 lines), which bundles: a structured-CV editor, extraction-run history, a "CV structure overview", rewrite templates with a PDF carousel (a second, template-driven CV builder), and field-level review metadata. A user cannot tell which artifact is "their CV".
  3. Internal vocabulary leaks to users — "structured CV", "JSON structure", "extraction schema", "structure overview". These are implementation concepts.
  4. Extraction is opaque. Upload runs, the profile changes, but the user never sees what changed and cannot approve it. History is shown as a primary section instead of the result of an import.
  5. Two CV builders. The template-driven rewrite/PDF flow inside the profile page overlaps the real CV Builder (/career/builder), which already owns templates, layout, styling, variants and PDF.

New information architecture

Product rule: Career Profile holds your information; CV Builder creates documents from it.

Surface Owns Does NOT own
Career Profile (/career) Personal info, professional summary, work experience, education, skills, projects, certifications, languages. The facts. Templates, layout, styling, PDF, variants
CV Builder (/career/builder, /career/builder/{id}) Templates, layout, styling, section order/visibility, variants, PDF generation Career facts (it reads the profile)

Career Profile page structure (target):

  • Header — "Career Profile", subtitle "This information powers your CVs, applications, cover letters and AI assistance.", profile completeness %, last updated, quick action → CV Builder.
  • Sections (user-facing labels only): Personal information · Professional summary · Work experience · Education · Skills · Projects · Certifications · Languages.
  • Import CV — current source (filename, date, status) + [Upload new CV]; after extraction a review screen (below). History moves to Settings → Advanced → Import history.

Removed / relocated concepts

Concept Disposition
"Structured CV Editor" Renamed and reframed to Career Profile editor (same fields, user vocabulary)
"CV Structure Overview" Removed from the user surface; if needed for debugging, move under admin/developer tools
"Template-driven CV Builder" (rewrite templates + PDF carousel inside the profile page) Removed — the CV Builder already provides templates, layouts, styling, sections, customization and PDF. One CV Builder only
Extraction run history as a primary section Relocated to Settings → Advanced → Import history
Internal terms ("structured CV", "JSON", "schema") in labels/help text Replaced with plain language

Route ownership (authoritative)

/career                 → Career Profile        (Career Workspace nav item)
/career/builder         → CV Builder            (CV Builder nav item)
/career/builder/{id}    → CV Builder            (child of CV Builder, NOT Career Profile)

Rule: the sidebar item whose to is the longest prefix the current path is at or under wins; all others are inactive. A child route never activates a parent nav item.

Implementation plan

Phase 0 — Sidebar bug + framing (DONE, this change)

  • AppShell.activeNavTo(pathname, tos) — exported pure function; longest-owning to wins. selected now compares against the single computed activeTo across both nav lists.
  • Breadcrumb/title in App.tsx: explicit /career/builder → "CV Builder" ownership before the /career fallback (previously /career/builder showed "Career Workspace").
  • Career Workspace header reframed to the "Career Profile" product framing.
  • Tests: sidebar-active-nav.test.ts — asserts exactly one active item for /career, /career/builder, /career/builder/{id}, and that no item double-highlights.

Phase 1 — terminology + first component split (IN PROGRESS)

Delivered 2026-07-20 (increment 1):

Terminology → user-facing (src/i18n/translations.ts, no structural change):

Internal term (before) User-facing (after)
"Structured CV editor" "Career information"
"CV structure overview" "Profile sections"
"Summary bullets" "Professional summary"
"Core skills" "Skills"
"Analyze sections" "Read sections"
"Original extraction" "Original import"
hardcoded "Master career profile" "Career profile"
Help text de-jargoned; the "Career information" help now says *"The CV Builder uses this information
to create documents."*

Component extracted: src/views/career/ProfileCompleteness.tsx — the completeness meter + missing chips + version-history accordion, pulled out of CareerProfilePage. Display-only, props in, no state or API — the first step of the split.

No API / data / model change. The save path is untouched: api.put("/career/profile", { profile: structuredCv, cvText }). A new test (profile-page.test.tsx → "saving the career profile PUTs … unchanged (Phase 1 refactor invariant)") pins exactly that call so the remaining extraction can't silently change it. Existing profile-page tests were re-pointed to the new labels; all behavioural assertions (save, parse, field values) kept.

Verified: tsc clean, production build clean, 136 frontend tests pass (was 135; +1 invariant test). Sidebar fix from the previous task still passes.

Remaining in Phase 1 (staged, needs the app running to click-verify each section's save round-trip):

  • Extract the editing sections into PersonalInformationSection … LanguagesSection components and a CareerProfileHeader, keeping structuredCv + setStructuredCv + the save handler in the parent (so behaviour stays identical). This is voluminous mechanical JSX movement through a 1376-line file.
  • Hide the template-driven CV builder (rewrite templates + PDF carousel) and the structure overview parse block from the user surface. Both are tested against live extraction behaviour, so each removal must move its test coverage, not delete it — done incrementally with verification.
  • Per-section actionable empty states ("No work experience added yet" → [Add experience]); the Add affordances already exist, so this is copy + wiring.

Rationale for staging: these touch the live CV/extraction test surface and are best verified by driving the authenticated UI. Increment 1 is a clean, non-regressing checkpoint per the "reviewed and verified before Phase 2" instruction.

Phase 2 — Import CV review screen

Add a post-upload "New information found" review (Experience / Skills / Languages / Education) with [Accept all] · [Review individually] · [Discard], diffing the extracted profile against the current one client-side (no backend change — the extraction API already returns the structured result and field metadata). Nothing is written until the user accepts. Relocate run history to Settings → Advanced → Import history.

Phase 3 — Remove the second CV builder

Delete the rewrite-template + PDF-carousel flow from CareerProfilePage. Confirm the CV Builder (/career/builder) covers templates/layout/styling/variants/PDF first (it does). Verify public CV and PDF generation still work end to end.

Phase 4 — WYSIWYG for long-form fields

Rich editor (bold, lists, links, undo/redo) for professional summary, work descriptions, achievements, projects, cover letters. Store clean HTML or Markdown only — no editor-specific state — and the renderer consumes the same format. Choose a small dependency already compatible with the stack, or a minimal contentEditable wrapper; decide at Phase 4 to avoid a premature dependency.

Preserved (verified not touched)

CareerProfiles, CvVariants, CV generation, public CV pages, extraction APIs, AI services, permissions, tenant isolation. Phases 14 are frontend-only; any that appears to need a backend change is a signal to re-scope, not to change the model.

Why staged

The removals and the review/WYSIWYG surfaces all require editing the 1376-line CareerProfilePage and the live extraction/generation path. Delivering them as one change would put the v1.0.0 CV pipeline at risk with no incremental verification. Each phase above is small enough to ship and verify on its own.