feat(phase-2): separate /profile (identity) from /career (master profile)
Phase 2 — Career/Profile separation. The master career profile is the source of
truth; identity and career data are now saved independently so neither wipes the
other. CV Builder deliberately not built yet.
Backend — PUT /auth/profile is now a partial update:
- null/omitted field -> unchanged; "" -> cleared; value -> set (trimmed).
- Email/UserName never cleared to empty (login identifiers).
This lets /profile save identity fields and /career save the master-profile
fields through the same endpoint without one nulling the other. 4 new tests
cover the data-integrity guarantees (identity save keeps the CV, career save
keeps identity, empty clears, null leaves).
Frontend:
- ProfilePage save payload is now scoped by careerOnly: /career sends only
{ profileCvText, profileCvStructureJson }, /profile sends only identity.
- CareerWorkspacePage: removed the inert "CV Builder" tab (careerView) — Phase 2
establishes the master profile only; the builder is Phase 4.
- Dropped the dead careerView prop.
- Updated the CV-save test to render career mode and assert identity is excluded.
Source-of-truth flip (CareerProfileService authoritative) stays deferred to F5
per the branch design; CareerProfileService keeps mirroring via its dual-write.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -76,11 +76,11 @@ const structuredCv = {
|
||||
],
|
||||
};
|
||||
|
||||
function renderPage() {
|
||||
function renderPage(props: { careerOnly?: boolean } = {}) {
|
||||
return render(
|
||||
<ToastProvider>
|
||||
<I18nProvider>
|
||||
<ProfilePage />
|
||||
<ProfilePage {...props} />
|
||||
</I18nProvider>
|
||||
</ToastProvider>,
|
||||
);
|
||||
@@ -281,8 +281,10 @@ test('profile page rewrite tools use selected template and saved job context', a
|
||||
await waitFor(() => expect(createObjectURLMock).toHaveBeenCalledTimes(REWRITE_TEMPLATES_COUNT));
|
||||
});
|
||||
|
||||
test('saving profile persists structured cv json', async () => {
|
||||
renderPage();
|
||||
test('saving the master profile (career) persists structured cv json', async () => {
|
||||
// Phase 2: the master-profile save lives on /career (careerOnly). It sends only the CV fields —
|
||||
// identity is saved separately on /profile — so the payload carries profileCvStructureJson.
|
||||
renderPage({ careerOnly: true });
|
||||
|
||||
expect(await screen.findByText(/cv ready/i)).toBeInTheDocument();
|
||||
const fullNameInput = screen.getByLabelText(/full name/i);
|
||||
@@ -301,4 +303,7 @@ test('saving profile persists structured cv json', async () => {
|
||||
expect(parsed.contact.fullName).toBe('Updated Demo User');
|
||||
expect(parsed.skills).toEqual(['.NET', 'SQL']);
|
||||
expect(parsed.jobs[0].title).toBe('System Developer');
|
||||
// The career save must NOT carry identity fields (they belong to /profile).
|
||||
expect(payload.email).toBeUndefined();
|
||||
expect(payload.displayName).toBeUndefined();
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user