From 46e1f245f8c8dcc35cc595f5df06abc4939f8cf4 Mon Sep 17 00:00:00 2001 From: geulah Date: Sun, 5 Oct 2025 23:26:33 +0100 Subject: [PATCH] feat(editors): implement work, education and skills editors Add new editor components for work experience, education and skills sections Add validation logic for these sections in the store Replace placeholder content with actual editor implementations --- cv-engine/src/App.tsx | 9 +- cv-engine/src/editors/EducationEditor.tsx | 130 ++++++++++++++ cv-engine/src/editors/SkillsEditor.tsx | 92 ++++++++++ cv-engine/src/editors/WorkEditor.tsx | 198 ++++++++++++++++++++++ cv-engine/src/store/cvStore.ts | 44 ++++- 5 files changed, 468 insertions(+), 5 deletions(-) create mode 100644 cv-engine/src/editors/EducationEditor.tsx create mode 100644 cv-engine/src/editors/SkillsEditor.tsx create mode 100644 cv-engine/src/editors/WorkEditor.tsx diff --git a/cv-engine/src/App.tsx b/cv-engine/src/App.tsx index e626af4..0986189 100644 --- a/cv-engine/src/App.tsx +++ b/cv-engine/src/App.tsx @@ -1,6 +1,9 @@ import React from 'react'; import Stepper from './components/Stepper'; import PersonalEditor from './editors/PersonalEditor'; +import WorkEditor from './editors/WorkEditor'; +import EducationEditor from './editors/EducationEditor'; +import SkillsEditor from './editors/SkillsEditor'; import PreviewPanel from './components/PreviewPanel'; import { useActiveStep } from './store/cvStore'; @@ -13,11 +16,11 @@ const App: React.FC = () => { case 'personal': return ; case 'work': - return
Work Experience editor will be implemented in M2
; + return ; case 'education': - return
Education editor will be implemented in M2
; + return ; case 'skills': - return
Skills editor will be implemented in M2
; + return ; case 'summary': return
Summary editor will be implemented in M3
; case 'finalize': diff --git a/cv-engine/src/editors/EducationEditor.tsx b/cv-engine/src/editors/EducationEditor.tsx new file mode 100644 index 0000000..e3d924b --- /dev/null +++ b/cv-engine/src/editors/EducationEditor.tsx @@ -0,0 +1,130 @@ +import React from 'react'; +import { useEducationData, useCvStore } from '../store/cvStore'; + +const EducationEditor: React.FC = () => { + const education = useEducationData(); + const { addEducationItem, updateEducationItem, removeEducationItem, reorderEducationItems } = useCvStore(); + + const handleFieldChange = (id: string, field: string, value: string) => { + updateEducationItem(id, { [field]: value } as any); + }; + + return ( +
+
+

Education

+ +
+ + {education.length === 0 && ( +

No education items yet. Click "Add Education" to start.

+ )} + +
+ {education.map((ed, idx) => { + const requiredError = { + degree: !ed.degree, + school: !ed.school, + startDate: !ed.startDate, + }; + return ( +
+
+
Education #{idx + 1}
+
+ + + +
+
+ +
+
+ + handleFieldChange(ed.id, 'degree', e.target.value)} + className={`w-full px-3 py-2 border rounded-md ${requiredError.degree ? 'border-red-500' : 'border-gray-300'}`} + /> + {requiredError.degree &&

Degree is required

} +
+
+ + handleFieldChange(ed.id, 'school', e.target.value)} + className={`w-full px-3 py-2 border rounded-md ${requiredError.school ? 'border-red-500' : 'border-gray-300'}`} + /> + {requiredError.school &&

School name is required

} +
+
+
+ + handleFieldChange(ed.id, 'startDate', e.target.value)} + className={`w-full px-3 py-2 border rounded-md ${requiredError.startDate ? 'border-red-500' : 'border-gray-300'}`} + /> + {requiredError.startDate &&

Start date is required

} +
+
+ + handleFieldChange(ed.id, 'endDate', e.target.value)} + className="w-full px-3 py-2 border rounded-md border-gray-300" + /> +

Leave empty if ongoing

+
+
+
+ +
+ +