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>
This commit is contained in:
cesnimda
2026-07-20 02:50:52 +02:00
parent 4f69d395be
commit 63473bae85
5 changed files with 170 additions and 72 deletions
+25 -7
View File
@@ -205,13 +205,13 @@ test('profile page loads persisted structured cv and can re-parse it', async ()
renderPage();
expect(await screen.findByText(/cv ready/i)).toBeInTheDocument();
expect(screen.getByText(/cv structure overview/i)).toBeInTheDocument();
expect(screen.getByText(/structured cv editor/i)).toBeInTheDocument();
expect(screen.getByText(/profile sections/i)).toBeInTheDocument();
expect(screen.getAllByText(/career information/i).length).toBeGreaterThan(0);
expect(screen.getByText(/extraction history/i)).toBeInTheDocument();
expect(screen.getByText(/resume.pdf/i)).toBeInTheDocument();
expect(screen.getByText(/current run/i)).toBeInTheDocument();
expect(screen.getAllByText(/original extraction/i).length).toBeGreaterThan(0);
const originalExtractionToggle = screen.getByRole('button', { name: /original extraction/i });
expect(screen.getAllByText(/original import/i).length).toBeGreaterThan(0);
const originalExtractionToggle = screen.getByRole('button', { name: /original import/i });
expect(originalExtractionToggle).toHaveAttribute('aria-expanded', 'false');
expect(screen.getAllByText(/professional summary/i).length).toBeGreaterThan(0);
expect(screen.getByLabelText(/full name/i)).toHaveValue('Demo User');
@@ -222,7 +222,7 @@ test('profile page loads persisted structured cv and can re-parse it', async ()
expect(originalExtractionToggle).toHaveAttribute('aria-expanded', 'true');
expect(await screen.findByLabelText(/profile cv \/ master resume text/i)).toHaveValue('Professional Summary\nBuilt backend systems');
const analyzeButton = screen.getByRole('button', { name: /analyze sections/i });
const analyzeButton = screen.getByRole('button', { name: /read sections/i });
await waitFor(() => expect(analyzeButton).toBeEnabled());
fireEvent.click(analyzeButton);
@@ -233,6 +233,24 @@ test('profile page loads persisted structured cv and can re-parse it', async ()
expect(screen.getAllByText(/core skills/i).length).toBeGreaterThan(0);
});
test('saving the career profile PUTs the structured profile and cv text unchanged (Phase 1 refactor invariant)', async () => {
renderPage();
const saveButton = await screen.findByRole('button', { name: /save changes/i });
await waitFor(() => expect(saveButton).toBeEnabled());
fireEvent.click(saveButton);
await waitFor(() => {
expect(mockedApi.put).toHaveBeenCalledWith(
'/career/profile',
expect.objectContaining({
profile: expect.objectContaining({ contact: expect.objectContaining({ fullName: 'Demo User' }) }),
cvText: 'Professional Summary\nBuilt backend systems',
}),
);
});
});
test('profile page can reprocess from stored artifact history', async () => {
renderPage();
@@ -249,9 +267,9 @@ test('profile page keeps raw extraction collapsed until expanded', async () => {
renderPage();
expect(await screen.findByText(/cv ready/i)).toBeInTheDocument();
expect(screen.getByText(/the structured cv stays front and center/i)).toBeInTheDocument();
expect(screen.getByText(/your career information stays front and center/i)).toBeInTheDocument();
const originalExtractionToggle = screen.getByRole('button', { name: /original extraction/i });
const originalExtractionToggle = screen.getByRole('button', { name: /original import/i });
expect(originalExtractionToggle).toHaveAttribute('aria-expanded', 'false');
const copyButton = screen.getByRole('button', { name: /copy cv text/i });
expect(copyButton).toBeDisabled();