From 4253d33dfdd4d1aebe2224e2729aabac8ed12753 Mon Sep 17 00:00:00 2001 From: cesnimda Date: Sun, 29 Mar 2026 00:58:05 +0100 Subject: [PATCH] Fix frontend build for CV profile and draft types --- job-tracker-ui/src/i18n/translations.ts | 2 + job-tracker-ui/src/tailoredCvDraft.ts | 52 +++++++++++++++++-------- 2 files changed, 38 insertions(+), 16 deletions(-) diff --git a/job-tracker-ui/src/i18n/translations.ts b/job-tracker-ui/src/i18n/translations.ts index 8b5ffaf..995ef8b 100644 --- a/job-tracker-ui/src/i18n/translations.ts +++ b/job-tracker-ui/src/i18n/translations.ts @@ -213,6 +213,7 @@ export const translations = { profileCvStructuredEditorHelp: "Edit reusable CV data directly so generators and matching can work from stable fields instead of raw text alone.", profileCvExtractionHistory: "Extraction history", profileCvExtractionHistoryHelp: "See which parser run produced the current structured profile and reprocess from the stored source artifact when needed.", + profileCvExtractionHistoryEmpty: "No extraction runs yet.", profileCvProfileVersion: "Profile v{count}", profileCvCurrentRun: "Current run", profileCvNoStoredArtifact: "No stored artifact", @@ -1113,6 +1114,7 @@ export const translations = { profileCvStructuredEditorHelp: "Rediger gjenbrukbare CV-data direkte slik at generatorer og matching kan jobbe fra stabile felt i stedet for bare råtekst.", profileCvExtractionHistory: "Ekstraksjonshistorikk", profileCvExtractionHistoryHelp: "Se hvilken parserkjøring som produserte den nåværende strukturerte profilen, og kjør på nytt fra det lagrede kildeartefaktet ved behov.", + profileCvExtractionHistoryEmpty: "Ingen ekstraksjonskjøringer ennå.", profileCvProfileVersion: "Profil v{count}", profileCvCurrentRun: "Gjeldende kjøring", profileCvNoStoredArtifact: "Ingen lagret kildefil", diff --git a/job-tracker-ui/src/tailoredCvDraft.ts b/job-tracker-ui/src/tailoredCvDraft.ts index 9411550..32558bf 100644 --- a/job-tracker-ui/src/tailoredCvDraft.ts +++ b/job-tracker-ui/src/tailoredCvDraft.ts @@ -35,14 +35,26 @@ function formatDateRange(start?: string | null, end?: string | null, isCurrent?: export function renderTailoredCvDraftText(source?: Partial | null) { const draft = emptyTailoredCvDraft(); + const sourceSummary = source?.summary; + const sourceSelectedSkills = source?.selectedSkills; + const sourceExperience = source?.experience; + const sourceEducation = source?.education; + const sourceCustomSections = source?.customSections; + + const summary = Array.isArray(sourceSummary) ? sourceSummary.filter(Boolean) : []; + const selectedSkills = Array.isArray(sourceSelectedSkills) ? sourceSelectedSkills.filter(Boolean) : []; + const experience = Array.isArray(sourceExperience) ? sourceExperience.filter(Boolean) : []; + const education = Array.isArray(sourceEducation) ? sourceEducation.filter(Boolean) : []; + const customSections = Array.isArray(sourceCustomSections) ? sourceCustomSections.filter(Boolean) : []; + const normalized = { ...draft, ...source, - summary: Array.isArray(source?.summary) ? source.summary.filter(Boolean) : [], - selectedSkills: Array.isArray(source?.selectedSkills) ? source.selectedSkills.filter(Boolean) : [], - experience: Array.isArray(source?.experience) ? source.experience.filter(Boolean) : [], - education: Array.isArray(source?.education) ? source.education.filter(Boolean) : [], - customSections: Array.isArray(source?.customSections) ? source.customSections.filter(Boolean) : [], + summary, + selectedSkills, + experience, + education, + customSections, }; const sections: string[] = []; @@ -88,39 +100,47 @@ export function renderTailoredCvDraftText(source?: Partial | nu export function normalizeTailoredCvDraft(source?: Partial | null): TailoredCvDraft { const empty = emptyTailoredCvDraft(); + const sourceSummary = source?.summary; + const sourceSelectedSkills = source?.selectedSkills; + const sourceExperience = source?.experience; + const sourceEducation = source?.education; + const sourceCustomSections = source?.customSections; + const sourceRenderOptions = source?.renderOptions; + const sourceSectionOrder = sourceRenderOptions?.sectionOrder; + const normalized: TailoredCvDraft = { ...empty, ...source, templateId: source?.templateId?.trim() || empty.templateId, headline: source?.headline ?? "", - summary: Array.isArray(source?.summary) ? source!.summary.filter(Boolean) : [], - selectedSkills: Array.isArray(source?.selectedSkills) ? source!.selectedSkills.filter(Boolean) : [], - experience: Array.isArray(source?.experience) ? source!.experience.map((item) => ({ + summary: Array.isArray(sourceSummary) ? sourceSummary.filter(Boolean) : [], + selectedSkills: Array.isArray(sourceSelectedSkills) ? sourceSelectedSkills.filter(Boolean) : [], + experience: Array.isArray(sourceExperience) ? sourceExperience.map((item) => ({ title: item?.title ?? "", company: item?.company ?? "", location: item?.location ?? "", start: item?.start ?? "", end: item?.end ?? "", isCurrent: Boolean(item?.isCurrent), - bullets: Array.isArray(item?.bullets) ? item!.bullets.filter(Boolean) : [], + bullets: Array.isArray(item?.bullets) ? item.bullets.filter(Boolean) : [], })) : [], - education: Array.isArray(source?.education) ? source!.education.map((item) => ({ + education: Array.isArray(sourceEducation) ? sourceEducation.map((item) => ({ qualification: item?.qualification ?? "", institution: item?.institution ?? "", location: item?.location ?? "", start: item?.start ?? "", end: item?.end ?? "", - details: Array.isArray(item?.details) ? item!.details.filter(Boolean) : [], + details: Array.isArray(item?.details) ? item.details.filter(Boolean) : [], })) : [], - customSections: Array.isArray(source?.customSections) ? source!.customSections.map((item) => ({ + customSections: Array.isArray(sourceCustomSections) ? sourceCustomSections.map((item) => ({ title: item?.title ?? "", - items: Array.isArray(item?.items) ? item!.items.filter(Boolean) : [], + items: Array.isArray(item?.items) ? item.items.filter(Boolean) : [], })) : [], renderOptions: { ...empty.renderOptions, - ...source?.renderOptions, - sectionOrder: Array.isArray(source?.renderOptions?.sectionOrder) && source.renderOptions.sectionOrder.length > 0 - ? source.renderOptions.sectionOrder.filter(Boolean) + ...sourceRenderOptions, + sectionOrder: Array.isArray(sourceSectionOrder) && sourceSectionOrder.length > 0 + ? sourceSectionOrder.filter(Boolean) : DEFAULT_SECTION_ORDER, }, status: source?.status?.trim() || empty.status,