feat(cv): rebuild professional resume studio

This commit is contained in:
cesnimda
2026-08-24 20:21:23 +02:00
parent dca5daa1a2
commit 367b70681a
27 changed files with 827 additions and 187 deletions
+18 -6
View File
@@ -29,7 +29,9 @@ The master profile is never written by the builder. A variant references career
`SettingsJson` blob (`CvVariantSettings`, `JobTrackerApi/Models/CvVariantSettings.cs`) because it is edited and saved
as a unit — never queried field-by-field. A variant stores:
- `ThemeId` + overrides: accent, heading/body font, density, page size, photo/icons/page-numbers.
- `ThemeId` + normalized overrides: printable colours, curated heading/body fonts, typography,
density, A4/Letter, margins/gaps, one/two-column layout, sidebar assignment, heading/header and
skills treatments, photo/icons and the retained page-number extension point.
- `Sections`: ordered list with per-section `Hidden` + optional renamed `Title`.
- `Overrides`: keyed by `ItemKey``{ Hidden, Title, Subtitle, Bullets }` (per-item, job-specific).
- `CustomSections`: variant-only sections not in the master profile.
@@ -46,7 +48,8 @@ as a unit — never queried field-by-field. A variant stores:
## API
Authenticated (`/api/cv`, `CvVariantController`): `GET themes`; `GET/POST variants`;
Authenticated (`/api/cv`, `CvVariantController`): `GET themes`; `GET themes/{id}/preview`;
`GET/POST variants`;
`GET/PUT/DELETE variants/{id}`; `POST variants/{id}/duplicate`; `PUT variants/{id}/public`;
`GET variants/{id}/versions`, `POST …/versions/{v}/restore`; `GET variants/{id}/preview` and
`POST preview` (live preview of unsaved settings); `POST variants/{id}/export-pdf`; `POST ai/assist`.
@@ -61,10 +64,16 @@ The endpoint applies the identical public/private slug check; unknown, revoked,
404 without invoking the exporter. Anonymous PDF generation is limited to three requests per minute
per public link because it launches Chromium. `PublicCvPage` exposes it as a native **Download PDF** link.
CV/job associations are authorized server-side during creation. A caller cannot attach a CV to
another user's job merely by supplying its numeric ID. List cards resolve an associated job's title
and company only through the owner-filtered relationship.
## Builder workflow (frontend)
`/career/builder` lists variants (`CvBuilderPage`); the editor (`CvBuilderEditor`) is three tabs —
**Content**, **Customize**, **AI Tools** (plus **History**) — beside an always-on live preview that
`/career/builder` lists variants (`CvBuilderPage`) with real rendered template thumbnails, language,
modified/version state and associated-job context. Creation asks only for a name and template. The
editor (`CvBuilderEditor`) separates **Content**, **Template**, **Design**, **Layout**, **AI** and
**History** beside an always-on desktop preview that
re-renders through `POST /api/cv/preview` on a 300 ms debounce. Edits autosave on an 800 ms debounce
(`source: autosave`), appending a version each save; the header shows Unsaved / Saving / Saved.
`/cv/:slug` (`PublicCvPage`) renders a public CV in a sandboxed iframe.
@@ -75,9 +84,12 @@ buttons (the keyboard-accessible path); entry order is stored per section as `It
variant, never on the profile. Each entry exposes hide, title/subtitle override, and rich-text bullet
editing (`RichTextField` — a markdown toolbar over a textarea; storage stays plain text, the server
renderer converts the `**bold** *italic* __underline__ [text](url)` whitelist to safe HTML).
Contact details and career history remain master data; the Content tab explains this boundary and
links to the master editor while variant-specific wording, order, headings and visibility stay local.
**Preview** has zoom presets (±, slider, measured Fit), physical A4/Letter dimensions, a ceiling-based
page count with prev/next navigation and page-break indicators, and an "updating…" chip. Three-page
**Preview** displays separate labelled A4/Letter page sheets rather than one infinite document. It has
zoom controls, measured Fit, a ceiling-based page count with prev/next navigation, and an "updating…"
chip. Three-page
and longer documents receive content-focus guidance rather than automatic font shrinking. Preview
requests and autosaves are ordered so stale responses cannot replace newer edits; PDF/public actions
save the current variant before consuming the stored render. **Customize** badges ATS-friendly themes.
+8 -4
View File
@@ -24,15 +24,19 @@ which is retained only for the legacy tailored-draft flow and is not used by the
| Spacing | `PageMarginMm`, `SectionGapMm`, `EntryGapMm` |
| Styling | `HeaderStyle` (`plain`\|`band`\|`centered`\|`kicker`), `HeadingStyle` (`caps-rule`\|`underline`\|`plain`\|`bar`), `PhotoShape` (`none`\|`square`\|`rounded`\|`circle`), `DefaultIcons` |
The renderer computes CSS variables from these plus the variant's runtime overrides (accent, fonts,
density, page size, photo, icons) and picks one of the layout wrappers. `SidebarSections` decides which
section keys move to the sidebar for the two-column layouts.
The renderer computes CSS from these plus normalized variant overrides: printable palette, curated
fonts, body/heading size, line height, spacing, page size, layout, sidebar width/content, header and
heading treatments, skills presentation, photo and icons. `SidebarSections` decides which section
keys move to the sidebar for the two-column layouts. Changing a template or override never transforms
the structured career content.
## Adding a theme
1. Append one `CvTheme { … }` to `CvThemeCatalog.Themes` (`JobTrackerApi/Models/CvTheme.cs`). Only override the
fields that differ from the defaults.
2. Nothing else. It appears in `GET /api/cv/themes`, the Customize tab picker, and renders.
2. Nothing else. It appears in `GET /api/cv/themes`, the Template tab and dashboard create flow.
`GET /api/cv/themes/{id}/preview` renders isolated in-memory sample data for a real visual thumbnail;
preview data is never persisted.
Add a `CvBuilderTests.Every_catalog_theme_renders_valid_html` already loops the whole catalog, so a new
theme is smoke-tested automatically.
+21 -1
View File
@@ -1,9 +1,29 @@
# CAREER-002 CV Builder redesign
Updated: 2026-08-15
Updated: 2026-08-24
Status: `IMPLEMENTED — NOT VERIFIED`. Repository, automated and pathological Chromium/PDF gates are complete; authenticated application-browser and production gates remain.
## 2026-08-24 professional-builder increment
- Split the editor into Content, Template, Design, Layout, AI and History workspaces under a compact
document toolbar with editable name, autosave/retry state, undo/redo, duplicate, public link and PDF.
- Added normalized print-safe colour, typography, spacing, A4/Letter, language/date, skills and
one/two-column controls. The same settings and renderer drive live preview, public output and PDF.
- Replaced the infinite preview with labelled measured page sheets, mobile Edit/Preview switching,
fit/zoom/page navigation and horizontal-overflow reporting.
- Added real rendered demo thumbnails for all eight templates. Demo content is constructed in memory
by a read-only endpoint and is never stored in a user's master profile or CV.
- Expanded structured contacts with GitHub and validated custom links; added Norwegian default labels,
`nå` and configurable date formats.
- Hardened CV/job association so a foreign tenant's numeric job ID is rejected by the service and API.
Dashboard cards now show the authorized associated role/company instead of only an internal ID.
- Added explicit Original/Suggested AI review with Accept/Reject/Edit and a measurable-impact action
that asks for placeholders rather than inventing metrics.
- Frontend gates after this increment: TypeScript pass, optimized production build pass, 61/61 Jest
suites and 246/246 tests. Backend/migration/PDF reruns are blocked locally by the missing .NET 9 SDK;
the new backend paths have focused tests ready for the normal SDK/CI gate.
## Revalidated capability matrix
| Requirement | Existing implementation | Current status |