feat(career): CV Builder UI — 3-tab builder, live preview, variants, public CV
CI and Deploy / test (push) Failing after 1m57s
CI and Deploy / deploy (push) Has been skipped

Frontend for Phase 4. /career/builder lists CV variants; the editor has the
three spec tabs (Content / Customize / AI Tools, plus History) beside an
always-on live preview that re-renders through the server theme engine on a
debounce. Content: reorder/hide/rename sections, headline override, custom
sections. Customize: 8-theme picker, accent colour, fonts, density, page size,
photo/icons/page-number toggles. AI Tools: suggestion-only assistance (never
auto-applied). Autosave with version history + restore, public on/off with a
copyable /cv/{slug} link, PDF export. Public read-only page at /cv/:slug.

- cvBuilder.ts (types + API), CvBuilderPage, CvBuilderEditor, PublicCvPage
- routes + nav wired in App.tsx; "Open CV Builder" entry on Career Workspace
- 2 component tests; tsc + production build clean

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
cesnimda
2026-07-18 10:05:21 +02:00
parent a3e18e4b44
commit 158dd02b00
7 changed files with 883 additions and 6 deletions
+7
View File
@@ -49,6 +49,9 @@ const RemindersView = lazy(() => import("./components/RemindersView"));
const QuickCommandDialog = lazy(() => import("./components/QuickCommandDialog"));
const ProfilePage = lazy(() => import("./views/ProfilePage"));
const CareerWorkspacePage = lazy(() => import("./views/CareerWorkspacePage"));
const CvBuilderPage = lazy(() => import("./views/CvBuilderPage"));
const CvBuilderEditor = lazy(() => import("./views/CvBuilderEditor"));
const PublicCvPage = lazy(() => import("./views/PublicCvPage"));
const ConnectedAccountsPage = lazy(() => import("./views/ConnectedAccountsPage"));
const AdminAuditPage = lazy(() => import("./views/AdminAuditPage"));
const AdminUsersPage = lazy(() => import("./views/AdminUsersPage"));
@@ -242,6 +245,7 @@ function Shell({ jobPageSize, setJobPageSize, jobColumns, setJobColumns, themeMo
{ to: "/correspondence", label: "Correspondence", icon: <MailOutlineIcon fontSize="small" />, section: t("manage") },
{ to: "/correspondence/review", label: "Gmail review", icon: <MailOutlineIcon fontSize="small" />, section: t("manage") },
{ to: "/career", label: "Career Workspace", icon: <DescriptionOutlinedIcon fontSize="small" />, section: t("manage") },
{ to: "/career/builder", label: "CV Builder", icon: <DescriptionOutlinedIcon fontSize="small" />, section: t("manage") },
{ to: "/trash", label: t("trash"), icon: <DeleteOutlineIcon fontSize="small" />, section: t("manage") },
];
@@ -323,6 +327,8 @@ function Shell({ jobPageSize, setJobPageSize, jobColumns, setJobColumns, themeMo
<Route path="/correspondence/review" element={<GmailReviewPage />} />
<Route path="/profile" element={<ProfilePage />} />
<Route path="/career" element={<CareerWorkspacePage />} />
<Route path="/career/builder" element={<CvBuilderPage />} />
<Route path="/career/builder/:id" element={<CvBuilderEditor />} />
<Route path="/admin/audit" element={<AdminAuditPage />} />
<Route path="/admin/users" element={<AdminUsersPage />} />
<Route path="/admin/system" element={<AdminSystemPage />} />
@@ -378,6 +384,7 @@ export default function App() {
{ path: "/forgot-password", element: <ForgotPasswordPage />, errorElement: <RouteErrorPage /> },
{ path: "/reset-password", element: <ResetPasswordPage />, errorElement: <RouteErrorPage /> },
{ path: "/verify-email", element: <VerifyEmailPage />, errorElement: <RouteErrorPage /> },
{ path: "/cv/:slug", element: <PublicCvPage />, errorElement: <RouteErrorPage /> },
{ path: "/*", element: <Shell jobPageSize={jobPageSize} setJobPageSize={setJobPageSize} jobColumns={jobColumns} setJobColumns={setJobColumns} themeMode={themeMode} onThemeModeChange={onThemeModeChange} />, errorElement: <RouteErrorPage /> },
], { future: { v7_relativeSplatPath: true } }), [jobColumns, jobPageSize, themeMode]);