feat(career): CV Builder UI — 3-tab builder, live preview, variants, public CV
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:
@@ -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]);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user