@ginia/ui 0.1.14 → 0.1.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -39,6 +39,7 @@ function ResumeEditor({
39
39
  hideFields = [],
40
40
  hideSections = [],
41
41
  menuSlot = [],
42
+ customMenuOnly = false,
42
43
  theme
43
44
  }) {
44
45
  const handleSectionChange = (partial) => {
@@ -117,13 +118,14 @@ function ResumeEditor({
117
118
  ] })
118
119
  }
119
120
  ];
120
- const allSections = [
121
+ const slotSections = menuSlot.map((slot, idx) => ({
122
+ id: `slot-${idx}`,
123
+ title: slot.title,
124
+ content: slot.node
125
+ }));
126
+ const allSections = customMenuOnly ? slotSections : [
121
127
  ...coreSections.filter((section) => !hideSections.includes(section.title)),
122
- ...menuSlot.map((slot, idx) => ({
123
- id: `slot-${idx}`,
124
- title: slot.title,
125
- content: slot.node
126
- }))
128
+ ...slotSections
127
129
  ];
128
130
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "flex w-full flex-col items-start gap-4", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_accordion.Accordion, { type: "single", collapsible: true, className: "w-full", children: allSections.map((section) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
129
131
  import_accordion.AccordionItem,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../src/components/domain/resume/resume-editor/resume-editor.tsx"],"sourcesContent":["import { CandidateProfile, ResumeMenuSlot } from \"../types\";\nimport {\n Accordion,\n AccordionItem,\n AccordionTrigger,\n AccordionContent,\n} from \"../../../ui/accordion\";\nimport { PersonalInfo } from \"./sections/personal-info\";\nimport { AcademicInfo } from \"./sections/academic-info\";\nimport { EducationSection } from \"./sections/education\";\nimport { ExperienceSection } from \"./sections/experience\";\nimport { ActivitiesSection } from \"./sections/activities\";\nimport { SkillsSection } from \"./sections/skills\";\nimport { CertificationsSection } from \"./sections/certifications\";\nimport { RequirementsSection } from \"./sections/requirements\";\nimport { PreferencesSection } from \"./sections/preferences\";\nimport { Label, Textarea } from \"../../../ui\";\n\nexport interface ResumeEditorProps {\n data: CandidateProfile;\n onChange?: (data: CandidateProfile) => void;\n hideFields?: string[];\n hideSections?: string[];\n menuSlot?: ResumeMenuSlot[];\n theme?: string;\n}\n\nexport function ResumeEditor({\n data,\n onChange,\n hideFields = [],\n hideSections = [],\n menuSlot = [],\n theme,\n}: ResumeEditorProps) {\n const handleSectionChange = (partial: Partial<CandidateProfile>) => {\n if (onChange) {\n onChange({ ...data, ...partial });\n }\n };\n\n const coreSections = [\n {\n id: \"personal-info\",\n title: \"Información personal\",\n content: <PersonalInfo data={data} onChange={handleSectionChange} hideFields={hideFields} />,\n },\n {\n id: \"academic-info\",\n title: \"Información Académica\",\n content: <AcademicInfo data={data} onChange={handleSectionChange} hideFields={hideFields} />,\n },\n {\n id: \"education\",\n title: \"Educación\",\n content: (\n <EducationSection data={data} onChange={handleSectionChange} hideFields={hideFields} />\n ),\n },\n {\n id: \"experience\",\n title: \"Experiencia laboral\",\n content: (\n <ExperienceSection data={data} onChange={handleSectionChange} hideFields={hideFields} />\n ),\n },\n {\n id: \"activities\",\n title: \"Actividades y Logros\",\n content: (\n <ActivitiesSection data={data} onChange={handleSectionChange} hideFields={hideFields} />\n ),\n },\n {\n id: \"skills\",\n title: \"Habilidades\",\n content: <SkillsSection data={data} onChange={handleSectionChange} hideFields={hideFields} />,\n },\n {\n id: \"certifications\",\n title: \"Certificaciones\",\n content: (\n <CertificationsSection\n data={data}\n onChange={handleSectionChange}\n hideFields={hideFields}\n theme={theme}\n />\n ),\n },\n {\n id: \"requirements\",\n title: \"Requisitos laborales\",\n content: (\n <RequirementsSection data={data} onChange={handleSectionChange} hideFields={hideFields} />\n ),\n },\n {\n id: \"preferences\",\n title: \"Preferencias\",\n content: (\n <PreferencesSection data={data} onChange={handleSectionChange} hideFields={hideFields} />\n ),\n },\n {\n id: \"notes\",\n title: \"Notas\",\n content: (\n <div className=\"space-y-2\">\n <Label>Notas internas</Label>\n <Textarea\n rows={5}\n placeholder=\"Notas visibles solo para administradores...\"\n value={data.advanced?.notes || \"\"}\n onChange={(e) =>\n handleSectionChange({ advanced: { ...data.advanced, notes: e.target.value } })\n }\n />\n </div>\n ),\n },\n ];\n\n const allSections = [\n ...coreSections.filter((section) => !hideSections.includes(section.title)),\n ...menuSlot.map((slot, idx) => ({\n id: `slot-${idx}`,\n title: slot.title,\n content: slot.node,\n })),\n ];\n\n return (\n <div className=\"flex w-full flex-col items-start gap-4\">\n <Accordion type=\"single\" collapsible className=\"w-full\">\n {allSections.map((section) => (\n <AccordionItem\n key={section.id}\n value={section.id}\n className=\"border-b border-border/40 px-2 transition-all data-[state=open]:border-l-2 data-[state=open]:border-l-secondary data-[state=open]:pl-3\"\n >\n <AccordionTrigger className=\"text-md font-medium transition-all data-[state=open]:font-semibold data-[state=open]:text-primary\">\n {section.title}\n </AccordionTrigger>\n <AccordionContent className=\"pb-3 pt-2\">\n <div className=\"bg-card px-2\">{section.content}</div>\n </AccordionContent>\n </AccordionItem>\n ))}\n </Accordion>\n </div>\n );\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../../../src/components/domain/resume/resume-editor/resume-editor.tsx"],"sourcesContent":["import { CandidateProfile, ResumeMenuSlot } from \"../types\";\nimport {\n Accordion,\n AccordionItem,\n AccordionTrigger,\n AccordionContent,\n} from \"../../../ui/accordion\";\nimport { PersonalInfo } from \"./sections/personal-info\";\nimport { AcademicInfo } from \"./sections/academic-info\";\nimport { EducationSection } from \"./sections/education\";\nimport { ExperienceSection } from \"./sections/experience\";\nimport { ActivitiesSection } from \"./sections/activities\";\nimport { SkillsSection } from \"./sections/skills\";\nimport { CertificationsSection } from \"./sections/certifications\";\nimport { RequirementsSection } from \"./sections/requirements\";\nimport { PreferencesSection } from \"./sections/preferences\";\nimport { Label, Textarea } from \"../../../ui\";\n\nexport interface ResumeEditorProps {\n data: CandidateProfile;\n onChange?: (data: CandidateProfile) => void;\n hideFields?: string[];\n hideSections?: string[];\n menuSlot?: ResumeMenuSlot[];\n customMenuOnly?: boolean;\n theme?: string;\n}\n\nexport function ResumeEditor({\n data,\n onChange,\n hideFields = [],\n hideSections = [],\n menuSlot = [],\n customMenuOnly = false,\n theme,\n}: ResumeEditorProps) {\n const handleSectionChange = (partial: Partial<CandidateProfile>) => {\n if (onChange) {\n onChange({ ...data, ...partial });\n }\n };\n\n const coreSections = [\n {\n id: \"personal-info\",\n title: \"Información personal\",\n content: <PersonalInfo data={data} onChange={handleSectionChange} hideFields={hideFields} />,\n },\n {\n id: \"academic-info\",\n title: \"Información Académica\",\n content: <AcademicInfo data={data} onChange={handleSectionChange} hideFields={hideFields} />,\n },\n {\n id: \"education\",\n title: \"Educación\",\n content: (\n <EducationSection data={data} onChange={handleSectionChange} hideFields={hideFields} />\n ),\n },\n {\n id: \"experience\",\n title: \"Experiencia laboral\",\n content: (\n <ExperienceSection data={data} onChange={handleSectionChange} hideFields={hideFields} />\n ),\n },\n {\n id: \"activities\",\n title: \"Actividades y Logros\",\n content: (\n <ActivitiesSection data={data} onChange={handleSectionChange} hideFields={hideFields} />\n ),\n },\n {\n id: \"skills\",\n title: \"Habilidades\",\n content: <SkillsSection data={data} onChange={handleSectionChange} hideFields={hideFields} />,\n },\n {\n id: \"certifications\",\n title: \"Certificaciones\",\n content: (\n <CertificationsSection\n data={data}\n onChange={handleSectionChange}\n hideFields={hideFields}\n theme={theme}\n />\n ),\n },\n {\n id: \"requirements\",\n title: \"Requisitos laborales\",\n content: (\n <RequirementsSection data={data} onChange={handleSectionChange} hideFields={hideFields} />\n ),\n },\n {\n id: \"preferences\",\n title: \"Preferencias\",\n content: (\n <PreferencesSection data={data} onChange={handleSectionChange} hideFields={hideFields} />\n ),\n },\n {\n id: \"notes\",\n title: \"Notas\",\n content: (\n <div className=\"space-y-2\">\n <Label>Notas internas</Label>\n <Textarea\n rows={5}\n placeholder=\"Notas visibles solo para administradores...\"\n value={data.advanced?.notes || \"\"}\n onChange={(e) =>\n handleSectionChange({ advanced: { ...data.advanced, notes: e.target.value } })\n }\n />\n </div>\n ),\n },\n ];\n\n const slotSections = menuSlot.map((slot, idx) => ({\n id: `slot-${idx}`,\n title: slot.title,\n content: slot.node,\n }));\n\n const allSections = customMenuOnly\n ? slotSections\n : [\n ...coreSections.filter((section) => !hideSections.includes(section.title)),\n ...slotSections,\n ];\n\n return (\n <div className=\"flex w-full flex-col items-start gap-4\">\n <Accordion type=\"single\" collapsible className=\"w-full\">\n {allSections.map((section) => (\n <AccordionItem\n key={section.id}\n value={section.id}\n className=\"border-b border-border/40 px-2 transition-all data-[state=open]:border-l-2 data-[state=open]:border-l-secondary data-[state=open]:pl-3\"\n >\n <AccordionTrigger className=\"text-md font-medium transition-all data-[state=open]:font-semibold data-[state=open]:text-primary\">\n {section.title}\n </AccordionTrigger>\n <AccordionContent className=\"pb-3 pt-2\">\n <div className=\"bg-card px-2\">{section.content}</div>\n </AccordionContent>\n </AccordionItem>\n ))}\n </Accordion>\n </div>\n );\n}\n"],"mappings":"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","names":[]}
@@ -8,8 +8,9 @@ interface ResumeEditorProps {
8
8
  hideFields?: string[];
9
9
  hideSections?: string[];
10
10
  menuSlot?: ResumeMenuSlot[];
11
+ customMenuOnly?: boolean;
11
12
  theme?: string;
12
13
  }
13
- declare function ResumeEditor({ data, onChange, hideFields, hideSections, menuSlot, theme, }: ResumeEditorProps): react_jsx_runtime.JSX.Element;
14
+ declare function ResumeEditor({ data, onChange, hideFields, hideSections, menuSlot, customMenuOnly, theme, }: ResumeEditorProps): react_jsx_runtime.JSX.Element;
14
15
 
15
16
  export { ResumeEditor, type ResumeEditorProps };
@@ -8,8 +8,9 @@ interface ResumeEditorProps {
8
8
  hideFields?: string[];
9
9
  hideSections?: string[];
10
10
  menuSlot?: ResumeMenuSlot[];
11
+ customMenuOnly?: boolean;
11
12
  theme?: string;
12
13
  }
13
- declare function ResumeEditor({ data, onChange, hideFields, hideSections, menuSlot, theme, }: ResumeEditorProps): react_jsx_runtime.JSX.Element;
14
+ declare function ResumeEditor({ data, onChange, hideFields, hideSections, menuSlot, customMenuOnly, theme, }: ResumeEditorProps): react_jsx_runtime.JSX.Element;
14
15
 
15
16
  export { ResumeEditor, type ResumeEditorProps };
@@ -21,6 +21,7 @@ function ResumeEditor({
21
21
  hideFields = [],
22
22
  hideSections = [],
23
23
  menuSlot = [],
24
+ customMenuOnly = false,
24
25
  theme
25
26
  }) {
26
27
  const handleSectionChange = (partial) => {
@@ -99,13 +100,14 @@ function ResumeEditor({
99
100
  ] })
100
101
  }
101
102
  ];
102
- const allSections = [
103
+ const slotSections = menuSlot.map((slot, idx) => ({
104
+ id: `slot-${idx}`,
105
+ title: slot.title,
106
+ content: slot.node
107
+ }));
108
+ const allSections = customMenuOnly ? slotSections : [
103
109
  ...coreSections.filter((section) => !hideSections.includes(section.title)),
104
- ...menuSlot.map((slot, idx) => ({
105
- id: `slot-${idx}`,
106
- title: slot.title,
107
- content: slot.node
108
- }))
110
+ ...slotSections
109
111
  ];
110
112
  return /* @__PURE__ */ jsx("div", { className: "flex w-full flex-col items-start gap-4", children: /* @__PURE__ */ jsx(Accordion, { type: "single", collapsible: true, className: "w-full", children: allSections.map((section) => /* @__PURE__ */ jsxs(
111
113
  AccordionItem,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../src/components/domain/resume/resume-editor/resume-editor.tsx"],"sourcesContent":["import { CandidateProfile, ResumeMenuSlot } from \"../types\";\nimport {\n Accordion,\n AccordionItem,\n AccordionTrigger,\n AccordionContent,\n} from \"../../../ui/accordion\";\nimport { PersonalInfo } from \"./sections/personal-info\";\nimport { AcademicInfo } from \"./sections/academic-info\";\nimport { EducationSection } from \"./sections/education\";\nimport { ExperienceSection } from \"./sections/experience\";\nimport { ActivitiesSection } from \"./sections/activities\";\nimport { SkillsSection } from \"./sections/skills\";\nimport { CertificationsSection } from \"./sections/certifications\";\nimport { RequirementsSection } from \"./sections/requirements\";\nimport { PreferencesSection } from \"./sections/preferences\";\nimport { Label, Textarea } from \"../../../ui\";\n\nexport interface ResumeEditorProps {\n data: CandidateProfile;\n onChange?: (data: CandidateProfile) => void;\n hideFields?: string[];\n hideSections?: string[];\n menuSlot?: ResumeMenuSlot[];\n theme?: string;\n}\n\nexport function ResumeEditor({\n data,\n onChange,\n hideFields = [],\n hideSections = [],\n menuSlot = [],\n theme,\n}: ResumeEditorProps) {\n const handleSectionChange = (partial: Partial<CandidateProfile>) => {\n if (onChange) {\n onChange({ ...data, ...partial });\n }\n };\n\n const coreSections = [\n {\n id: \"personal-info\",\n title: \"Información personal\",\n content: <PersonalInfo data={data} onChange={handleSectionChange} hideFields={hideFields} />,\n },\n {\n id: \"academic-info\",\n title: \"Información Académica\",\n content: <AcademicInfo data={data} onChange={handleSectionChange} hideFields={hideFields} />,\n },\n {\n id: \"education\",\n title: \"Educación\",\n content: (\n <EducationSection data={data} onChange={handleSectionChange} hideFields={hideFields} />\n ),\n },\n {\n id: \"experience\",\n title: \"Experiencia laboral\",\n content: (\n <ExperienceSection data={data} onChange={handleSectionChange} hideFields={hideFields} />\n ),\n },\n {\n id: \"activities\",\n title: \"Actividades y Logros\",\n content: (\n <ActivitiesSection data={data} onChange={handleSectionChange} hideFields={hideFields} />\n ),\n },\n {\n id: \"skills\",\n title: \"Habilidades\",\n content: <SkillsSection data={data} onChange={handleSectionChange} hideFields={hideFields} />,\n },\n {\n id: \"certifications\",\n title: \"Certificaciones\",\n content: (\n <CertificationsSection\n data={data}\n onChange={handleSectionChange}\n hideFields={hideFields}\n theme={theme}\n />\n ),\n },\n {\n id: \"requirements\",\n title: \"Requisitos laborales\",\n content: (\n <RequirementsSection data={data} onChange={handleSectionChange} hideFields={hideFields} />\n ),\n },\n {\n id: \"preferences\",\n title: \"Preferencias\",\n content: (\n <PreferencesSection data={data} onChange={handleSectionChange} hideFields={hideFields} />\n ),\n },\n {\n id: \"notes\",\n title: \"Notas\",\n content: (\n <div className=\"space-y-2\">\n <Label>Notas internas</Label>\n <Textarea\n rows={5}\n placeholder=\"Notas visibles solo para administradores...\"\n value={data.advanced?.notes || \"\"}\n onChange={(e) =>\n handleSectionChange({ advanced: { ...data.advanced, notes: e.target.value } })\n }\n />\n </div>\n ),\n },\n ];\n\n const allSections = [\n ...coreSections.filter((section) => !hideSections.includes(section.title)),\n ...menuSlot.map((slot, idx) => ({\n id: `slot-${idx}`,\n title: slot.title,\n content: slot.node,\n })),\n ];\n\n return (\n <div className=\"flex w-full flex-col items-start gap-4\">\n <Accordion type=\"single\" collapsible className=\"w-full\">\n {allSections.map((section) => (\n <AccordionItem\n key={section.id}\n value={section.id}\n className=\"border-b border-border/40 px-2 transition-all data-[state=open]:border-l-2 data-[state=open]:border-l-secondary data-[state=open]:pl-3\"\n >\n <AccordionTrigger className=\"text-md font-medium transition-all data-[state=open]:font-semibold data-[state=open]:text-primary\">\n {section.title}\n </AccordionTrigger>\n <AccordionContent className=\"pb-3 pt-2\">\n <div className=\"bg-card px-2\">{section.content}</div>\n </AccordionContent>\n </AccordionItem>\n ))}\n </Accordion>\n </div>\n );\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../../../src/components/domain/resume/resume-editor/resume-editor.tsx"],"sourcesContent":["import { CandidateProfile, ResumeMenuSlot } from \"../types\";\nimport {\n Accordion,\n AccordionItem,\n AccordionTrigger,\n AccordionContent,\n} from \"../../../ui/accordion\";\nimport { PersonalInfo } from \"./sections/personal-info\";\nimport { AcademicInfo } from \"./sections/academic-info\";\nimport { EducationSection } from \"./sections/education\";\nimport { ExperienceSection } from \"./sections/experience\";\nimport { ActivitiesSection } from \"./sections/activities\";\nimport { SkillsSection } from \"./sections/skills\";\nimport { CertificationsSection } from \"./sections/certifications\";\nimport { RequirementsSection } from \"./sections/requirements\";\nimport { PreferencesSection } from \"./sections/preferences\";\nimport { Label, Textarea } from \"../../../ui\";\n\nexport interface ResumeEditorProps {\n data: CandidateProfile;\n onChange?: (data: CandidateProfile) => void;\n hideFields?: string[];\n hideSections?: string[];\n menuSlot?: ResumeMenuSlot[];\n customMenuOnly?: boolean;\n theme?: string;\n}\n\nexport function ResumeEditor({\n data,\n onChange,\n hideFields = [],\n hideSections = [],\n menuSlot = [],\n customMenuOnly = false,\n theme,\n}: ResumeEditorProps) {\n const handleSectionChange = (partial: Partial<CandidateProfile>) => {\n if (onChange) {\n onChange({ ...data, ...partial });\n }\n };\n\n const coreSections = [\n {\n id: \"personal-info\",\n title: \"Información personal\",\n content: <PersonalInfo data={data} onChange={handleSectionChange} hideFields={hideFields} />,\n },\n {\n id: \"academic-info\",\n title: \"Información Académica\",\n content: <AcademicInfo data={data} onChange={handleSectionChange} hideFields={hideFields} />,\n },\n {\n id: \"education\",\n title: \"Educación\",\n content: (\n <EducationSection data={data} onChange={handleSectionChange} hideFields={hideFields} />\n ),\n },\n {\n id: \"experience\",\n title: \"Experiencia laboral\",\n content: (\n <ExperienceSection data={data} onChange={handleSectionChange} hideFields={hideFields} />\n ),\n },\n {\n id: \"activities\",\n title: \"Actividades y Logros\",\n content: (\n <ActivitiesSection data={data} onChange={handleSectionChange} hideFields={hideFields} />\n ),\n },\n {\n id: \"skills\",\n title: \"Habilidades\",\n content: <SkillsSection data={data} onChange={handleSectionChange} hideFields={hideFields} />,\n },\n {\n id: \"certifications\",\n title: \"Certificaciones\",\n content: (\n <CertificationsSection\n data={data}\n onChange={handleSectionChange}\n hideFields={hideFields}\n theme={theme}\n />\n ),\n },\n {\n id: \"requirements\",\n title: \"Requisitos laborales\",\n content: (\n <RequirementsSection data={data} onChange={handleSectionChange} hideFields={hideFields} />\n ),\n },\n {\n id: \"preferences\",\n title: \"Preferencias\",\n content: (\n <PreferencesSection data={data} onChange={handleSectionChange} hideFields={hideFields} />\n ),\n },\n {\n id: \"notes\",\n title: \"Notas\",\n content: (\n <div className=\"space-y-2\">\n <Label>Notas internas</Label>\n <Textarea\n rows={5}\n placeholder=\"Notas visibles solo para administradores...\"\n value={data.advanced?.notes || \"\"}\n onChange={(e) =>\n handleSectionChange({ advanced: { ...data.advanced, notes: e.target.value } })\n }\n />\n </div>\n ),\n },\n ];\n\n const slotSections = menuSlot.map((slot, idx) => ({\n id: `slot-${idx}`,\n title: slot.title,\n content: slot.node,\n }));\n\n const allSections = customMenuOnly\n ? slotSections\n : [\n ...coreSections.filter((section) => !hideSections.includes(section.title)),\n ...slotSections,\n ];\n\n return (\n <div className=\"flex w-full flex-col items-start gap-4\">\n <Accordion type=\"single\" collapsible className=\"w-full\">\n {allSections.map((section) => (\n <AccordionItem\n key={section.id}\n value={section.id}\n className=\"border-b border-border/40 px-2 transition-all data-[state=open]:border-l-2 data-[state=open]:border-l-secondary data-[state=open]:pl-3\"\n >\n <AccordionTrigger className=\"text-md font-medium transition-all data-[state=open]:font-semibold data-[state=open]:text-primary\">\n {section.title}\n </AccordionTrigger>\n <AccordionContent className=\"pb-3 pt-2\">\n <div className=\"bg-card px-2\">{section.content}</div>\n </AccordionContent>\n </AccordionItem>\n ))}\n </Accordion>\n </div>\n );\n}\n"],"mappings":"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","names":[]}
@@ -23,27 +23,44 @@ __export(experience_education_exports, {
23
23
  module.exports = __toCommonJS(experience_education_exports);
24
24
  var import_jsx_runtime = require("react/jsx-runtime");
25
25
  var import_lucide_react = require("lucide-react");
26
+ var import_date_fns = require("date-fns");
27
+ var import_locale = require("date-fns/locale");
26
28
  function parseDateToTimestamp(dateStr) {
27
29
  if (!dateStr) return 0;
28
30
  const isoMatch = dateStr.match(/^(\d{4})-(\d{1,2})(?:-(\d{1,2}))?/);
29
31
  if (isoMatch) {
30
32
  const [, y, m, d] = isoMatch;
31
- const date = /* @__PURE__ */ new Date(`${y}-${m.padStart(2, "0")}-${(d || "01").padStart(2, "0")}T00:00:00.000Z`);
32
- return date.getTime();
33
+ const year2 = Number(y);
34
+ const monthIndex = Number(m) - 1;
35
+ const day = Number(d || "1");
36
+ return new Date(year2, monthIndex, day).getTime();
33
37
  }
34
38
  const mmYyyy = dateStr.match(/^(\d{1,2})\/(\d{4})$/);
35
39
  if (mmYyyy) {
36
40
  const [, m, y] = mmYyyy;
37
- const date = /* @__PURE__ */ new Date(`${y}-${m.padStart(2, "0")}-01T00:00:00.000Z`);
38
- return date.getTime();
41
+ const year2 = Number(y);
42
+ const monthIndex = Number(m) - 1;
43
+ return new Date(year2, monthIndex, 1).getTime();
39
44
  }
40
45
  const year = dateStr.match(/\d{4}/);
41
46
  if (year) {
42
- const date = /* @__PURE__ */ new Date(`${year[0]}-01-01T00:00:00.000Z`);
43
- return date.getTime();
47
+ return new Date(Number(year[0]), 0, 1).getTime();
44
48
  }
45
49
  return 0;
46
50
  }
51
+ function formatDisplayDate(dateStr) {
52
+ if (!dateStr) return "";
53
+ if (!/\d{4}/.test(dateStr)) return dateStr;
54
+ const ts = parseDateToTimestamp(dateStr);
55
+ if (!ts) return dateStr;
56
+ return (0, import_date_fns.format)(new Date(ts), "MMMM yyyy", { locale: import_locale.es });
57
+ }
58
+ function formatDateRange(opts) {
59
+ const startText = opts.hideStart ? "" : formatDisplayDate(opts.start);
60
+ const endText = opts.hideEnd ? "" : formatDisplayDate(opts.end);
61
+ if (startText && endText) return `${startText} - ${endText}`;
62
+ return startText || endText || "";
63
+ }
47
64
  function sortByDateDesc(items) {
48
65
  return [...items].sort((a, b) => {
49
66
  const dateA = a.end_date || a.start_date;
@@ -93,10 +110,12 @@ function ExperienceEducation({
93
110
  !hideFields.includes("experience.title") && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h4", { className: "text-lg font-bold text-foreground", children: exp.title }),
94
111
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "mb-2 flex flex-col sm:flex-row sm:items-center sm:justify-between", children: [
95
112
  !hideFields.includes("experience.institution_name") && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "text-base font-semibold text-primary", children: exp.institution_name }),
96
- (!hideFields.includes("experience.start_date") || !hideFields.includes("experience.end_date")) && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "mt-1 w-fit rounded-md bg-muted/30 px-2 py-0.5 text-sm font-medium text-muted-foreground sm:mt-0 pdf-badge", children: [
97
- !hideFields.includes("experience.start_date") && exp.start_date,
98
- !hideFields.includes("experience.end_date") && exp.end_date && exp.end_date.toLowerCase() !== "presente" ? `- ${exp.end_date}` : null
99
- ].filter(Boolean).join(" ") })
113
+ (!hideFields.includes("experience.start_date") || !hideFields.includes("experience.end_date")) && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "mt-1 w-fit rounded-md bg-muted/30 px-2 py-0.5 text-sm font-medium text-muted-foreground sm:mt-0 pdf-badge", children: formatDateRange({
114
+ start: exp.start_date,
115
+ end: exp.end_date,
116
+ hideStart: hideFields.includes("experience.start_date"),
117
+ hideEnd: hideFields.includes("experience.end_date")
118
+ }) })
100
119
  ] }),
101
120
  exp.description && !hideFields.includes("experience.description") && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "mt-3 whitespace-pre-wrap text-base leading-relaxed text-foreground/80", children: exp.description })
102
121
  ]
@@ -124,10 +143,12 @@ function ExperienceEducation({
124
143
  !hideFields.includes("education.title") && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h4", { className: "text-lg font-bold text-foreground", children: edu.title }),
125
144
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "mb-2 flex flex-col sm:flex-row sm:items-center sm:justify-between", children: [
126
145
  !hideFields.includes("education.institution_name") && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "text-base font-semibold text-primary", children: edu.institution_name }),
127
- (!hideFields.includes("education.start_date") || !hideFields.includes("education.end_date")) && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "mt-1 w-fit rounded-md bg-muted/30 px-2 py-0.5 text-sm font-medium text-muted-foreground sm:mt-0 pdf-badge", children: [
128
- !hideFields.includes("education.start_date") && edu.start_date,
129
- !hideFields.includes("education.end_date") && edu.end_date && edu.end_date.toLowerCase() !== "presente" ? `- ${edu.end_date}` : null
130
- ].filter(Boolean).join(" ") })
146
+ (!hideFields.includes("education.start_date") || !hideFields.includes("education.end_date")) && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "mt-1 w-fit rounded-md bg-muted/30 px-2 py-0.5 text-sm font-medium text-muted-foreground sm:mt-0 pdf-badge", children: formatDateRange({
147
+ start: edu.start_date,
148
+ end: edu.end_date,
149
+ hideStart: hideFields.includes("education.start_date"),
150
+ hideEnd: hideFields.includes("education.end_date")
151
+ }) })
131
152
  ] }),
132
153
  edu.description && !hideFields.includes("education.description") && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "mt-3 whitespace-pre-wrap text-base leading-relaxed text-foreground/80", children: edu.description })
133
154
  ]
@@ -155,10 +176,12 @@ function ExperienceEducation({
155
176
  !hideFields.includes("activities.title") && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h4", { className: "text-lg font-bold text-foreground", children: act.title }),
156
177
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "mb-2 flex flex-col sm:flex-row sm:items-center sm:justify-between", children: [
157
178
  !hideFields.includes("activities.institution_name") && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "text-base font-semibold text-primary", children: act.institution_name }),
158
- (!hideFields.includes("activities.start_date") || !hideFields.includes("activities.end_date")) && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "mt-1 w-fit rounded-md bg-muted/30 px-2 py-0.5 text-sm font-medium text-muted-foreground sm:mt-0 pdf-badge", children: [
159
- !hideFields.includes("activities.start_date") && act.start_date,
160
- !hideFields.includes("activities.end_date") && act.end_date && act.end_date.toLowerCase() !== "presente" ? `- ${act.end_date}` : null
161
- ].filter(Boolean).join(" ") })
179
+ (!hideFields.includes("activities.start_date") || !hideFields.includes("activities.end_date")) && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "mt-1 w-fit rounded-md bg-muted/30 px-2 py-0.5 text-sm font-medium text-muted-foreground sm:mt-0 pdf-badge", children: formatDateRange({
180
+ start: act.start_date,
181
+ end: act.end_date,
182
+ hideStart: hideFields.includes("activities.start_date"),
183
+ hideEnd: hideFields.includes("activities.end_date")
184
+ }) })
162
185
  ] }),
163
186
  act.description && !hideFields.includes("activities.description") && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "mt-3 whitespace-pre-wrap text-base leading-relaxed text-foreground/80", children: act.description })
164
187
  ]
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../src/components/domain/resume/resume-preview/sections/experience-education.tsx"],"sourcesContent":["import { CandidateProfile } from \"../../types\";\nimport type { CVExperienceBaseItem, CVCertificationItem } from \"../../types\";\nimport { GraduationCap, Briefcase, Trophy, Award } from \"lucide-react\";\n\n/** Parsea una fecha a timestamp. Soporta ISO (YYYY-MM-DD), MM/YYYY, YYYY-MM, YYYY. */\nfunction parseDateToTimestamp(dateStr: string | undefined): number {\n if (!dateStr) return 0;\n const isoMatch = dateStr.match(/^(\\d{4})-(\\d{1,2})(?:-(\\d{1,2}))?/);\n if (isoMatch) {\n const [, y, m, d] = isoMatch;\n const date = new Date(`${y}-${m.padStart(2, \"0\")}-${(d || \"01\").padStart(2, \"0\")}T00:00:00.000Z`);\n return date.getTime();\n }\n const mmYyyy = dateStr.match(/^(\\d{1,2})\\/(\\d{4})$/);\n if (mmYyyy) {\n const [, m, y] = mmYyyy;\n const date = new Date(`${y}-${m.padStart(2, \"0\")}-01T00:00:00.000Z`);\n return date.getTime();\n }\n const year = dateStr.match(/\\d{4}/);\n if (year) {\n const date = new Date(`${year[0]}-01-01T00:00:00.000Z`);\n return date.getTime();\n }\n return 0;\n}\n\n/** Ordena descendente por fecha (más reciente primero). Usa end_date o start_date si no hay end. */\nfunction sortByDateDesc<T extends CVExperienceBaseItem>(items: T[]): T[] {\n return [...items].sort((a, b) => {\n const dateA = a.end_date || a.start_date;\n const dateB = b.end_date || b.start_date;\n if (!dateA && !dateB) return 0;\n if (!dateA) return 1;\n if (!dateB) return -1;\n return parseDateToTimestamp(dateB) - parseDateToTimestamp(dateA);\n });\n}\n\n/** Ordena certificaciones descendente por date_awarded */\nfunction sortCertificationsByDateDesc(items: CVCertificationItem[]): CVCertificationItem[] {\n return [...items].sort((a, b) => {\n const tsA = parseDateToTimestamp(a.date_awarded);\n const tsB = parseDateToTimestamp(b.date_awarded);\n return tsB - tsA;\n });\n}\n\ninterface ExperienceEducationProps {\n data: CandidateProfile;\n theme?: string;\n hideFields?: string[];\n hideSections?: string[];\n}\n\nexport function ExperienceEducation({\n data,\n theme,\n hideFields = [],\n hideSections = [],\n}: ExperienceEducationProps) {\n const experiences = sortByDateDesc(data.work_experience || []);\n const education = sortByDateDesc(data.education || []);\n const activities = sortByDateDesc(data.activities || []);\n const certifications = sortCertificationsByDateDesc(data.certifications || []);\n\n const themeStyle = theme ? { backgroundColor: theme } : undefined;\n\n return (\n <div className=\"space-y-12\">\n {/* Experience */}\n {experiences.length > 0 && !hideSections.includes(\"Experiencia laboral\") && (\n <div className=\"space-y-6\">\n <h3 className=\"flex items-center gap-2 border-b-2 border-primary/20 pb-2 text-2xl font-bold text-primary\">\n <Briefcase className=\"h-6 w-6 text-primary\" />\n Experiencia Laboral\n </h3>\n <div className=\"space-y-8\">\n {experiences.map((exp) => (\n <div\n key={exp.id}\n className=\"relative pl-6 after:absolute after:bottom-[-24px] after:left-[3px] after:top-4 after:w-[2px] after:bg-border last:after:hidden\"\n >\n <div\n className=\"pdf-line-v absolute left-0 top-2 h-2 w-2 rounded-full bg-primary\"\n style={themeStyle}\n />\n {!hideFields.includes(\"experience.title\") && (\n <h4 className=\"text-lg font-bold text-foreground\">{exp.title}</h4>\n )}\n <div className=\"mb-2 flex flex-col sm:flex-row sm:items-center sm:justify-between\">\n {!hideFields.includes(\"experience.institution_name\") && (\n <span className=\"text-base font-semibold text-primary\">\n {exp.institution_name}\n </span>\n )}\n {(!hideFields.includes(\"experience.start_date\") ||\n !hideFields.includes(\"experience.end_date\")) && (\n <span className=\"mt-1 w-fit rounded-md bg-muted/30 px-2 py-0.5 text-sm font-medium text-muted-foreground sm:mt-0 pdf-badge\">\n {[\n !hideFields.includes(\"experience.start_date\") && exp.start_date,\n !hideFields.includes(\"experience.end_date\") &&\n exp.end_date &&\n exp.end_date.toLowerCase() !== \"presente\"\n ? `- ${exp.end_date}`\n : null,\n ]\n .filter(Boolean)\n .join(\" \")}\n </span>\n )}\n </div>\n {exp.description && !hideFields.includes(\"experience.description\") && (\n <p className=\"mt-3 whitespace-pre-wrap text-base leading-relaxed text-foreground/80\">\n {exp.description}\n </p>\n )}\n </div>\n ))}\n </div>\n </div>\n )}\n\n {/* Education */}\n {education.length > 0 && !hideSections.includes(\"Educación\") && (\n <div className=\"space-y-6\">\n <h3 className=\"flex items-center gap-2 border-b-2 border-primary/20 pb-2 text-2xl font-bold text-primary\">\n <GraduationCap className=\"h-6 w-6 text-primary\" />\n Educación\n </h3>\n <div className=\"space-y-8\">\n {education.map((edu) => (\n <div\n key={edu.id}\n className=\"relative pl-6 after:absolute after:bottom-[-24px] after:left-[3px] after:top-4 after:w-[2px] after:bg-border last:after:hidden\"\n >\n <div\n className=\"pdf-line-v absolute left-0 top-2 h-2 w-2 rounded-full bg-primary\"\n style={themeStyle}\n />\n {!hideFields.includes(\"education.title\") && (\n <h4 className=\"text-lg font-bold text-foreground\">{edu.title}</h4>\n )}\n <div className=\"mb-2 flex flex-col sm:flex-row sm:items-center sm:justify-between\">\n {!hideFields.includes(\"education.institution_name\") && (\n <span className=\"text-base font-semibold text-primary\">\n {edu.institution_name}\n </span>\n )}\n {(!hideFields.includes(\"education.start_date\") ||\n !hideFields.includes(\"education.end_date\")) && (\n <span className=\"mt-1 w-fit rounded-md bg-muted/30 px-2 py-0.5 text-sm font-medium text-muted-foreground sm:mt-0 pdf-badge\">\n {[\n !hideFields.includes(\"education.start_date\") && edu.start_date,\n !hideFields.includes(\"education.end_date\") &&\n edu.end_date &&\n edu.end_date.toLowerCase() !== \"presente\"\n ? `- ${edu.end_date}`\n : null,\n ]\n .filter(Boolean)\n .join(\" \")}\n </span>\n )}\n </div>\n {edu.description && !hideFields.includes(\"education.description\") && (\n <p className=\"mt-3 whitespace-pre-wrap text-base leading-relaxed text-foreground/80\">\n {edu.description}\n </p>\n )}\n </div>\n ))}\n </div>\n </div>\n )}\n\n {/* Activities */}\n {activities.length > 0 && !hideSections.includes(\"Actividades y Logros\") && (\n <div className=\"space-y-6\">\n <h3 className=\"flex items-center gap-2 border-b-2 border-primary/20 pb-2 text-2xl font-bold text-primary\">\n <Trophy className=\"h-6 w-6 text-primary\" />\n Actividades y Logros\n </h3>\n <div className=\"space-y-8\">\n {activities.map((act) => (\n <div\n key={act.id}\n className=\"relative pl-6 after:absolute after:bottom-[-24px] after:left-[3px] after:top-4 after:w-[2px] after:bg-border last:after:hidden\"\n >\n <div\n className=\"pdf-line-v absolute left-0 top-2 h-2 w-2 rounded-full bg-primary\"\n style={themeStyle}\n />\n {!hideFields.includes(\"activities.title\") && (\n <h4 className=\"text-lg font-bold text-foreground\">{act.title}</h4>\n )}\n <div className=\"mb-2 flex flex-col sm:flex-row sm:items-center sm:justify-between\">\n {!hideFields.includes(\"activities.institution_name\") && (\n <span className=\"text-base font-semibold text-primary\">\n {act.institution_name}\n </span>\n )}\n {(!hideFields.includes(\"activities.start_date\") ||\n !hideFields.includes(\"activities.end_date\")) && (\n <span className=\"mt-1 w-fit rounded-md bg-muted/30 px-2 py-0.5 text-sm font-medium text-muted-foreground sm:mt-0 pdf-badge\">\n {[\n !hideFields.includes(\"activities.start_date\") && act.start_date,\n !hideFields.includes(\"activities.end_date\") &&\n act.end_date &&\n act.end_date.toLowerCase() !== \"presente\"\n ? `- ${act.end_date}`\n : null,\n ]\n .filter(Boolean)\n .join(\" \")}\n </span>\n )}\n </div>\n {act.description && !hideFields.includes(\"activities.description\") && (\n <p className=\"mt-3 whitespace-pre-wrap text-base leading-relaxed text-foreground/80\">\n {act.description}\n </p>\n )}\n </div>\n ))}\n </div>\n </div>\n )}\n\n {/* Certifications */}\n {certifications.length > 0 && !hideSections.includes(\"Certificaciones\") && (\n <div className=\"space-y-6\">\n <h3 className=\"flex items-center gap-2 border-b-2 border-primary/20 pb-2 text-2xl font-bold text-primary\">\n <Award className=\"h-6 w-6 text-primary\" />\n Certificaciones\n </h3>\n <div className=\"grid grid-cols-1 gap-4\">\n {certifications.map((cert) => (\n <div\n key={cert.id}\n className=\"rounded-lg border border-border/60 bg-card p-4 shadow-sm transition-colors hover:border-primary/30 hover:shadow-md\"\n >\n {!hideFields.includes(\"certifications.title\") && (\n <h4 className=\"break-words text-base font-bold text-foreground\">\n {cert.title}\n </h4>\n )}\n {!hideFields.includes(\"certifications.institution_name\") && (\n <p className=\"mt-1 line-clamp-1 text-sm text-muted-foreground\">\n {cert.institution_name}\n </p>\n )}\n <div className=\"mt-4 flex items-center justify-between\">\n {!hideFields.includes(\"certifications.date_awarded\") && (\n <span className=\"rounded-md bg-muted/50 px-2 py-1 text-xs font-semibold uppercase tracking-wider text-muted-foreground pdf-badge\">\n {cert.date_awarded}\n </span>\n )}\n {cert.verified && !hideFields.includes(\"certifications.verified\") && (\n <span\n className=\"pdf-badge rounded-full border px-2.5 py-1 text-xs font-bold transition-colors\"\n style={{\n backgroundColor: theme?.startsWith(\"#\") ? `${theme}15` : undefined,\n color: theme,\n borderColor: theme?.startsWith(\"#\") ? `${theme}30` : theme,\n }}\n >\n Verificado\n </span>\n )}\n </div>\n </div>\n ))}\n </div>\n </div>\n )}\n </div>\n );\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../../../../src/components/domain/resume/resume-preview/sections/experience-education.tsx"],"sourcesContent":["import { CandidateProfile } from \"../../types\";\nimport type { CVExperienceBaseItem, CVCertificationItem } from \"../../types\";\nimport { GraduationCap, Briefcase, Trophy, Award } from \"lucide-react\";\nimport { format } from \"date-fns\";\nimport { es } from \"date-fns/locale\";\n\n/** Parsea una fecha a timestamp. Soporta ISO (YYYY-MM-DD), MM/YYYY, YYYY-MM, YYYY. */\nfunction parseDateToTimestamp(dateStr: string | undefined): number {\n if (!dateStr) return 0;\n const isoMatch = dateStr.match(/^(\\d{4})-(\\d{1,2})(?:-(\\d{1,2}))?/);\n if (isoMatch) {\n const [, y, m, d] = isoMatch;\n // Importante: interpretar como fecha \"local\" para evitar corrimientos por zona horaria\n // (p.ej. 2024-08-01 en UTC puede formatearse como julio en zonas negativas).\n const year = Number(y);\n const monthIndex = Number(m) - 1; // JS Date usa meses 0-based\n const day = Number(d || \"1\");\n return new Date(year, monthIndex, day).getTime();\n }\n const mmYyyy = dateStr.match(/^(\\d{1,2})\\/(\\d{4})$/);\n if (mmYyyy) {\n const [, m, y] = mmYyyy;\n const year = Number(y);\n const monthIndex = Number(m) - 1;\n return new Date(year, monthIndex, 1).getTime();\n }\n const year = dateStr.match(/\\d{4}/);\n if (year) {\n return new Date(Number(year[0]), 0, 1).getTime();\n }\n return 0;\n}\n\n/** Formatea una fecha ISO/string a \"mes año\" en español, ej: \"agosto 2020\". */\nfunction formatDisplayDate(dateStr: string | undefined): string {\n if (!dateStr) return \"\";\n // Si es un valor semántico como \"Presente\", lo retorna tal cual\n if (!/\\d{4}/.test(dateStr)) return dateStr;\n const ts = parseDateToTimestamp(dateStr);\n if (!ts) return dateStr;\n return format(new Date(ts), \"MMMM yyyy\", { locale: es });\n}\n\nfunction formatDateRange(opts: {\n start?: string;\n end?: string;\n hideStart?: boolean;\n hideEnd?: boolean;\n}): string {\n const startText = opts.hideStart ? \"\" : formatDisplayDate(opts.start);\n const endText = opts.hideEnd ? \"\" : formatDisplayDate(opts.end);\n\n if (startText && endText) return `${startText} - ${endText}`;\n return startText || endText || \"\";\n}\n\n/** Ordena descendente por fecha (más reciente primero). Usa end_date o start_date si no hay end. */\nfunction sortByDateDesc<T extends CVExperienceBaseItem>(items: T[]): T[] {\n return [...items].sort((a, b) => {\n const dateA = a.end_date || a.start_date;\n const dateB = b.end_date || b.start_date;\n if (!dateA && !dateB) return 0;\n if (!dateA) return 1;\n if (!dateB) return -1;\n return parseDateToTimestamp(dateB) - parseDateToTimestamp(dateA);\n });\n}\n\n/** Ordena certificaciones descendente por date_awarded */\nfunction sortCertificationsByDateDesc(items: CVCertificationItem[]): CVCertificationItem[] {\n return [...items].sort((a, b) => {\n const tsA = parseDateToTimestamp(a.date_awarded);\n const tsB = parseDateToTimestamp(b.date_awarded);\n return tsB - tsA;\n });\n}\n\ninterface ExperienceEducationProps {\n data: CandidateProfile;\n theme?: string;\n hideFields?: string[];\n hideSections?: string[];\n}\n\nexport function ExperienceEducation({\n data,\n theme,\n hideFields = [],\n hideSections = [],\n}: ExperienceEducationProps) {\n const experiences = sortByDateDesc(data.work_experience || []);\n const education = sortByDateDesc(data.education || []);\n const activities = sortByDateDesc(data.activities || []);\n const certifications = sortCertificationsByDateDesc(data.certifications || []);\n\n const themeStyle = theme ? { backgroundColor: theme } : undefined;\n\n return (\n <div className=\"space-y-12\">\n {/* Experience */}\n {experiences.length > 0 && !hideSections.includes(\"Experiencia laboral\") && (\n <div className=\"space-y-6\">\n <h3 className=\"flex items-center gap-2 border-b-2 border-primary/20 pb-2 text-2xl font-bold text-primary\">\n <Briefcase className=\"h-6 w-6 text-primary\" />\n Experiencia Laboral\n </h3>\n <div className=\"space-y-8\">\n {experiences.map((exp) => (\n <div\n key={exp.id}\n className=\"relative pl-6 after:absolute after:bottom-[-24px] after:left-[3px] after:top-4 after:w-[2px] after:bg-border last:after:hidden\"\n >\n <div\n className=\"pdf-line-v absolute left-0 top-2 h-2 w-2 rounded-full bg-primary\"\n style={themeStyle}\n />\n {!hideFields.includes(\"experience.title\") && (\n <h4 className=\"text-lg font-bold text-foreground\">{exp.title}</h4>\n )}\n <div className=\"mb-2 flex flex-col sm:flex-row sm:items-center sm:justify-between\">\n {!hideFields.includes(\"experience.institution_name\") && (\n <span className=\"text-base font-semibold text-primary\">\n {exp.institution_name}\n </span>\n )}\n {(!hideFields.includes(\"experience.start_date\") ||\n !hideFields.includes(\"experience.end_date\")) && (\n <span className=\"mt-1 w-fit rounded-md bg-muted/30 px-2 py-0.5 text-sm font-medium text-muted-foreground sm:mt-0 pdf-badge\">\n {formatDateRange({\n start: exp.start_date,\n end: exp.end_date,\n hideStart: hideFields.includes(\"experience.start_date\"),\n hideEnd: hideFields.includes(\"experience.end_date\"),\n })}\n </span>\n )}\n </div>\n {exp.description && !hideFields.includes(\"experience.description\") && (\n <p className=\"mt-3 whitespace-pre-wrap text-base leading-relaxed text-foreground/80\">\n {exp.description}\n </p>\n )}\n </div>\n ))}\n </div>\n </div>\n )}\n\n {/* Education */}\n {education.length > 0 && !hideSections.includes(\"Educación\") && (\n <div className=\"space-y-6\">\n <h3 className=\"flex items-center gap-2 border-b-2 border-primary/20 pb-2 text-2xl font-bold text-primary\">\n <GraduationCap className=\"h-6 w-6 text-primary\" />\n Educación\n </h3>\n <div className=\"space-y-8\">\n {education.map((edu) => (\n <div\n key={edu.id}\n className=\"relative pl-6 after:absolute after:bottom-[-24px] after:left-[3px] after:top-4 after:w-[2px] after:bg-border last:after:hidden\"\n >\n <div\n className=\"pdf-line-v absolute left-0 top-2 h-2 w-2 rounded-full bg-primary\"\n style={themeStyle}\n />\n {!hideFields.includes(\"education.title\") && (\n <h4 className=\"text-lg font-bold text-foreground\">{edu.title}</h4>\n )}\n <div className=\"mb-2 flex flex-col sm:flex-row sm:items-center sm:justify-between\">\n {!hideFields.includes(\"education.institution_name\") && (\n <span className=\"text-base font-semibold text-primary\">\n {edu.institution_name}\n </span>\n )}\n {(!hideFields.includes(\"education.start_date\") ||\n !hideFields.includes(\"education.end_date\")) && (\n <span className=\"mt-1 w-fit rounded-md bg-muted/30 px-2 py-0.5 text-sm font-medium text-muted-foreground sm:mt-0 pdf-badge\">\n {formatDateRange({\n start: edu.start_date,\n end: edu.end_date,\n hideStart: hideFields.includes(\"education.start_date\"),\n hideEnd: hideFields.includes(\"education.end_date\"),\n })}\n </span>\n )}\n </div>\n {edu.description && !hideFields.includes(\"education.description\") && (\n <p className=\"mt-3 whitespace-pre-wrap text-base leading-relaxed text-foreground/80\">\n {edu.description}\n </p>\n )}\n </div>\n ))}\n </div>\n </div>\n )}\n\n {/* Activities */}\n {activities.length > 0 && !hideSections.includes(\"Actividades y Logros\") && (\n <div className=\"space-y-6\">\n <h3 className=\"flex items-center gap-2 border-b-2 border-primary/20 pb-2 text-2xl font-bold text-primary\">\n <Trophy className=\"h-6 w-6 text-primary\" />\n Actividades y Logros\n </h3>\n <div className=\"space-y-8\">\n {activities.map((act) => (\n <div\n key={act.id}\n className=\"relative pl-6 after:absolute after:bottom-[-24px] after:left-[3px] after:top-4 after:w-[2px] after:bg-border last:after:hidden\"\n >\n <div\n className=\"pdf-line-v absolute left-0 top-2 h-2 w-2 rounded-full bg-primary\"\n style={themeStyle}\n />\n {!hideFields.includes(\"activities.title\") && (\n <h4 className=\"text-lg font-bold text-foreground\">{act.title}</h4>\n )}\n <div className=\"mb-2 flex flex-col sm:flex-row sm:items-center sm:justify-between\">\n {!hideFields.includes(\"activities.institution_name\") && (\n <span className=\"text-base font-semibold text-primary\">\n {act.institution_name}\n </span>\n )}\n {(!hideFields.includes(\"activities.start_date\") ||\n !hideFields.includes(\"activities.end_date\")) && (\n <span className=\"mt-1 w-fit rounded-md bg-muted/30 px-2 py-0.5 text-sm font-medium text-muted-foreground sm:mt-0 pdf-badge\">\n {formatDateRange({\n start: act.start_date,\n end: act.end_date,\n hideStart: hideFields.includes(\"activities.start_date\"),\n hideEnd: hideFields.includes(\"activities.end_date\"),\n })}\n </span>\n )}\n </div>\n {act.description && !hideFields.includes(\"activities.description\") && (\n <p className=\"mt-3 whitespace-pre-wrap text-base leading-relaxed text-foreground/80\">\n {act.description}\n </p>\n )}\n </div>\n ))}\n </div>\n </div>\n )}\n\n {/* Certifications */}\n {certifications.length > 0 && !hideSections.includes(\"Certificaciones\") && (\n <div className=\"space-y-6\">\n <h3 className=\"flex items-center gap-2 border-b-2 border-primary/20 pb-2 text-2xl font-bold text-primary\">\n <Award className=\"h-6 w-6 text-primary\" />\n Certificaciones\n </h3>\n <div className=\"grid grid-cols-1 gap-4\">\n {certifications.map((cert) => (\n <div\n key={cert.id}\n className=\"rounded-lg border border-border/60 bg-card p-4 shadow-sm transition-colors hover:border-primary/30 hover:shadow-md\"\n >\n {!hideFields.includes(\"certifications.title\") && (\n <h4 className=\"break-words text-base font-bold text-foreground\">\n {cert.title}\n </h4>\n )}\n {!hideFields.includes(\"certifications.institution_name\") && (\n <p className=\"mt-1 line-clamp-1 text-sm text-muted-foreground\">\n {cert.institution_name}\n </p>\n )}\n <div className=\"mt-4 flex items-center justify-between\">\n {!hideFields.includes(\"certifications.date_awarded\") && (\n <span className=\"rounded-md bg-muted/50 px-2 py-1 text-xs font-semibold uppercase tracking-wider text-muted-foreground pdf-badge\">\n {cert.date_awarded}\n </span>\n )}\n {cert.verified && !hideFields.includes(\"certifications.verified\") && (\n <span\n className=\"pdf-badge rounded-full border px-2.5 py-1 text-xs font-bold transition-colors\"\n style={{\n backgroundColor: theme?.startsWith(\"#\") ? `${theme}15` : undefined,\n color: theme,\n borderColor: theme?.startsWith(\"#\") ? `${theme}30` : theme,\n }}\n >\n Verificado\n </span>\n )}\n </div>\n </div>\n ))}\n </div>\n </div>\n )}\n </div>\n );\n}\n"],"mappings":"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","names":["year"]}
@@ -1,26 +1,43 @@
1
1
  import { jsx, jsxs } from "react/jsx-runtime";
2
2
  import { GraduationCap, Briefcase, Trophy, Award } from "lucide-react";
3
+ import { format } from "date-fns";
4
+ import { es } from "date-fns/locale";
3
5
  function parseDateToTimestamp(dateStr) {
4
6
  if (!dateStr) return 0;
5
7
  const isoMatch = dateStr.match(/^(\d{4})-(\d{1,2})(?:-(\d{1,2}))?/);
6
8
  if (isoMatch) {
7
9
  const [, y, m, d] = isoMatch;
8
- const date = /* @__PURE__ */ new Date(`${y}-${m.padStart(2, "0")}-${(d || "01").padStart(2, "0")}T00:00:00.000Z`);
9
- return date.getTime();
10
+ const year2 = Number(y);
11
+ const monthIndex = Number(m) - 1;
12
+ const day = Number(d || "1");
13
+ return new Date(year2, monthIndex, day).getTime();
10
14
  }
11
15
  const mmYyyy = dateStr.match(/^(\d{1,2})\/(\d{4})$/);
12
16
  if (mmYyyy) {
13
17
  const [, m, y] = mmYyyy;
14
- const date = /* @__PURE__ */ new Date(`${y}-${m.padStart(2, "0")}-01T00:00:00.000Z`);
15
- return date.getTime();
18
+ const year2 = Number(y);
19
+ const monthIndex = Number(m) - 1;
20
+ return new Date(year2, monthIndex, 1).getTime();
16
21
  }
17
22
  const year = dateStr.match(/\d{4}/);
18
23
  if (year) {
19
- const date = /* @__PURE__ */ new Date(`${year[0]}-01-01T00:00:00.000Z`);
20
- return date.getTime();
24
+ return new Date(Number(year[0]), 0, 1).getTime();
21
25
  }
22
26
  return 0;
23
27
  }
28
+ function formatDisplayDate(dateStr) {
29
+ if (!dateStr) return "";
30
+ if (!/\d{4}/.test(dateStr)) return dateStr;
31
+ const ts = parseDateToTimestamp(dateStr);
32
+ if (!ts) return dateStr;
33
+ return format(new Date(ts), "MMMM yyyy", { locale: es });
34
+ }
35
+ function formatDateRange(opts) {
36
+ const startText = opts.hideStart ? "" : formatDisplayDate(opts.start);
37
+ const endText = opts.hideEnd ? "" : formatDisplayDate(opts.end);
38
+ if (startText && endText) return `${startText} - ${endText}`;
39
+ return startText || endText || "";
40
+ }
24
41
  function sortByDateDesc(items) {
25
42
  return [...items].sort((a, b) => {
26
43
  const dateA = a.end_date || a.start_date;
@@ -70,10 +87,12 @@ function ExperienceEducation({
70
87
  !hideFields.includes("experience.title") && /* @__PURE__ */ jsx("h4", { className: "text-lg font-bold text-foreground", children: exp.title }),
71
88
  /* @__PURE__ */ jsxs("div", { className: "mb-2 flex flex-col sm:flex-row sm:items-center sm:justify-between", children: [
72
89
  !hideFields.includes("experience.institution_name") && /* @__PURE__ */ jsx("span", { className: "text-base font-semibold text-primary", children: exp.institution_name }),
73
- (!hideFields.includes("experience.start_date") || !hideFields.includes("experience.end_date")) && /* @__PURE__ */ jsx("span", { className: "mt-1 w-fit rounded-md bg-muted/30 px-2 py-0.5 text-sm font-medium text-muted-foreground sm:mt-0 pdf-badge", children: [
74
- !hideFields.includes("experience.start_date") && exp.start_date,
75
- !hideFields.includes("experience.end_date") && exp.end_date && exp.end_date.toLowerCase() !== "presente" ? `- ${exp.end_date}` : null
76
- ].filter(Boolean).join(" ") })
90
+ (!hideFields.includes("experience.start_date") || !hideFields.includes("experience.end_date")) && /* @__PURE__ */ jsx("span", { className: "mt-1 w-fit rounded-md bg-muted/30 px-2 py-0.5 text-sm font-medium text-muted-foreground sm:mt-0 pdf-badge", children: formatDateRange({
91
+ start: exp.start_date,
92
+ end: exp.end_date,
93
+ hideStart: hideFields.includes("experience.start_date"),
94
+ hideEnd: hideFields.includes("experience.end_date")
95
+ }) })
77
96
  ] }),
78
97
  exp.description && !hideFields.includes("experience.description") && /* @__PURE__ */ jsx("p", { className: "mt-3 whitespace-pre-wrap text-base leading-relaxed text-foreground/80", children: exp.description })
79
98
  ]
@@ -101,10 +120,12 @@ function ExperienceEducation({
101
120
  !hideFields.includes("education.title") && /* @__PURE__ */ jsx("h4", { className: "text-lg font-bold text-foreground", children: edu.title }),
102
121
  /* @__PURE__ */ jsxs("div", { className: "mb-2 flex flex-col sm:flex-row sm:items-center sm:justify-between", children: [
103
122
  !hideFields.includes("education.institution_name") && /* @__PURE__ */ jsx("span", { className: "text-base font-semibold text-primary", children: edu.institution_name }),
104
- (!hideFields.includes("education.start_date") || !hideFields.includes("education.end_date")) && /* @__PURE__ */ jsx("span", { className: "mt-1 w-fit rounded-md bg-muted/30 px-2 py-0.5 text-sm font-medium text-muted-foreground sm:mt-0 pdf-badge", children: [
105
- !hideFields.includes("education.start_date") && edu.start_date,
106
- !hideFields.includes("education.end_date") && edu.end_date && edu.end_date.toLowerCase() !== "presente" ? `- ${edu.end_date}` : null
107
- ].filter(Boolean).join(" ") })
123
+ (!hideFields.includes("education.start_date") || !hideFields.includes("education.end_date")) && /* @__PURE__ */ jsx("span", { className: "mt-1 w-fit rounded-md bg-muted/30 px-2 py-0.5 text-sm font-medium text-muted-foreground sm:mt-0 pdf-badge", children: formatDateRange({
124
+ start: edu.start_date,
125
+ end: edu.end_date,
126
+ hideStart: hideFields.includes("education.start_date"),
127
+ hideEnd: hideFields.includes("education.end_date")
128
+ }) })
108
129
  ] }),
109
130
  edu.description && !hideFields.includes("education.description") && /* @__PURE__ */ jsx("p", { className: "mt-3 whitespace-pre-wrap text-base leading-relaxed text-foreground/80", children: edu.description })
110
131
  ]
@@ -132,10 +153,12 @@ function ExperienceEducation({
132
153
  !hideFields.includes("activities.title") && /* @__PURE__ */ jsx("h4", { className: "text-lg font-bold text-foreground", children: act.title }),
133
154
  /* @__PURE__ */ jsxs("div", { className: "mb-2 flex flex-col sm:flex-row sm:items-center sm:justify-between", children: [
134
155
  !hideFields.includes("activities.institution_name") && /* @__PURE__ */ jsx("span", { className: "text-base font-semibold text-primary", children: act.institution_name }),
135
- (!hideFields.includes("activities.start_date") || !hideFields.includes("activities.end_date")) && /* @__PURE__ */ jsx("span", { className: "mt-1 w-fit rounded-md bg-muted/30 px-2 py-0.5 text-sm font-medium text-muted-foreground sm:mt-0 pdf-badge", children: [
136
- !hideFields.includes("activities.start_date") && act.start_date,
137
- !hideFields.includes("activities.end_date") && act.end_date && act.end_date.toLowerCase() !== "presente" ? `- ${act.end_date}` : null
138
- ].filter(Boolean).join(" ") })
156
+ (!hideFields.includes("activities.start_date") || !hideFields.includes("activities.end_date")) && /* @__PURE__ */ jsx("span", { className: "mt-1 w-fit rounded-md bg-muted/30 px-2 py-0.5 text-sm font-medium text-muted-foreground sm:mt-0 pdf-badge", children: formatDateRange({
157
+ start: act.start_date,
158
+ end: act.end_date,
159
+ hideStart: hideFields.includes("activities.start_date"),
160
+ hideEnd: hideFields.includes("activities.end_date")
161
+ }) })
139
162
  ] }),
140
163
  act.description && !hideFields.includes("activities.description") && /* @__PURE__ */ jsx("p", { className: "mt-3 whitespace-pre-wrap text-base leading-relaxed text-foreground/80", children: act.description })
141
164
  ]
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../src/components/domain/resume/resume-preview/sections/experience-education.tsx"],"sourcesContent":["import { CandidateProfile } from \"../../types\";\nimport type { CVExperienceBaseItem, CVCertificationItem } from \"../../types\";\nimport { GraduationCap, Briefcase, Trophy, Award } from \"lucide-react\";\n\n/** Parsea una fecha a timestamp. Soporta ISO (YYYY-MM-DD), MM/YYYY, YYYY-MM, YYYY. */\nfunction parseDateToTimestamp(dateStr: string | undefined): number {\n if (!dateStr) return 0;\n const isoMatch = dateStr.match(/^(\\d{4})-(\\d{1,2})(?:-(\\d{1,2}))?/);\n if (isoMatch) {\n const [, y, m, d] = isoMatch;\n const date = new Date(`${y}-${m.padStart(2, \"0\")}-${(d || \"01\").padStart(2, \"0\")}T00:00:00.000Z`);\n return date.getTime();\n }\n const mmYyyy = dateStr.match(/^(\\d{1,2})\\/(\\d{4})$/);\n if (mmYyyy) {\n const [, m, y] = mmYyyy;\n const date = new Date(`${y}-${m.padStart(2, \"0\")}-01T00:00:00.000Z`);\n return date.getTime();\n }\n const year = dateStr.match(/\\d{4}/);\n if (year) {\n const date = new Date(`${year[0]}-01-01T00:00:00.000Z`);\n return date.getTime();\n }\n return 0;\n}\n\n/** Ordena descendente por fecha (más reciente primero). Usa end_date o start_date si no hay end. */\nfunction sortByDateDesc<T extends CVExperienceBaseItem>(items: T[]): T[] {\n return [...items].sort((a, b) => {\n const dateA = a.end_date || a.start_date;\n const dateB = b.end_date || b.start_date;\n if (!dateA && !dateB) return 0;\n if (!dateA) return 1;\n if (!dateB) return -1;\n return parseDateToTimestamp(dateB) - parseDateToTimestamp(dateA);\n });\n}\n\n/** Ordena certificaciones descendente por date_awarded */\nfunction sortCertificationsByDateDesc(items: CVCertificationItem[]): CVCertificationItem[] {\n return [...items].sort((a, b) => {\n const tsA = parseDateToTimestamp(a.date_awarded);\n const tsB = parseDateToTimestamp(b.date_awarded);\n return tsB - tsA;\n });\n}\n\ninterface ExperienceEducationProps {\n data: CandidateProfile;\n theme?: string;\n hideFields?: string[];\n hideSections?: string[];\n}\n\nexport function ExperienceEducation({\n data,\n theme,\n hideFields = [],\n hideSections = [],\n}: ExperienceEducationProps) {\n const experiences = sortByDateDesc(data.work_experience || []);\n const education = sortByDateDesc(data.education || []);\n const activities = sortByDateDesc(data.activities || []);\n const certifications = sortCertificationsByDateDesc(data.certifications || []);\n\n const themeStyle = theme ? { backgroundColor: theme } : undefined;\n\n return (\n <div className=\"space-y-12\">\n {/* Experience */}\n {experiences.length > 0 && !hideSections.includes(\"Experiencia laboral\") && (\n <div className=\"space-y-6\">\n <h3 className=\"flex items-center gap-2 border-b-2 border-primary/20 pb-2 text-2xl font-bold text-primary\">\n <Briefcase className=\"h-6 w-6 text-primary\" />\n Experiencia Laboral\n </h3>\n <div className=\"space-y-8\">\n {experiences.map((exp) => (\n <div\n key={exp.id}\n className=\"relative pl-6 after:absolute after:bottom-[-24px] after:left-[3px] after:top-4 after:w-[2px] after:bg-border last:after:hidden\"\n >\n <div\n className=\"pdf-line-v absolute left-0 top-2 h-2 w-2 rounded-full bg-primary\"\n style={themeStyle}\n />\n {!hideFields.includes(\"experience.title\") && (\n <h4 className=\"text-lg font-bold text-foreground\">{exp.title}</h4>\n )}\n <div className=\"mb-2 flex flex-col sm:flex-row sm:items-center sm:justify-between\">\n {!hideFields.includes(\"experience.institution_name\") && (\n <span className=\"text-base font-semibold text-primary\">\n {exp.institution_name}\n </span>\n )}\n {(!hideFields.includes(\"experience.start_date\") ||\n !hideFields.includes(\"experience.end_date\")) && (\n <span className=\"mt-1 w-fit rounded-md bg-muted/30 px-2 py-0.5 text-sm font-medium text-muted-foreground sm:mt-0 pdf-badge\">\n {[\n !hideFields.includes(\"experience.start_date\") && exp.start_date,\n !hideFields.includes(\"experience.end_date\") &&\n exp.end_date &&\n exp.end_date.toLowerCase() !== \"presente\"\n ? `- ${exp.end_date}`\n : null,\n ]\n .filter(Boolean)\n .join(\" \")}\n </span>\n )}\n </div>\n {exp.description && !hideFields.includes(\"experience.description\") && (\n <p className=\"mt-3 whitespace-pre-wrap text-base leading-relaxed text-foreground/80\">\n {exp.description}\n </p>\n )}\n </div>\n ))}\n </div>\n </div>\n )}\n\n {/* Education */}\n {education.length > 0 && !hideSections.includes(\"Educación\") && (\n <div className=\"space-y-6\">\n <h3 className=\"flex items-center gap-2 border-b-2 border-primary/20 pb-2 text-2xl font-bold text-primary\">\n <GraduationCap className=\"h-6 w-6 text-primary\" />\n Educación\n </h3>\n <div className=\"space-y-8\">\n {education.map((edu) => (\n <div\n key={edu.id}\n className=\"relative pl-6 after:absolute after:bottom-[-24px] after:left-[3px] after:top-4 after:w-[2px] after:bg-border last:after:hidden\"\n >\n <div\n className=\"pdf-line-v absolute left-0 top-2 h-2 w-2 rounded-full bg-primary\"\n style={themeStyle}\n />\n {!hideFields.includes(\"education.title\") && (\n <h4 className=\"text-lg font-bold text-foreground\">{edu.title}</h4>\n )}\n <div className=\"mb-2 flex flex-col sm:flex-row sm:items-center sm:justify-between\">\n {!hideFields.includes(\"education.institution_name\") && (\n <span className=\"text-base font-semibold text-primary\">\n {edu.institution_name}\n </span>\n )}\n {(!hideFields.includes(\"education.start_date\") ||\n !hideFields.includes(\"education.end_date\")) && (\n <span className=\"mt-1 w-fit rounded-md bg-muted/30 px-2 py-0.5 text-sm font-medium text-muted-foreground sm:mt-0 pdf-badge\">\n {[\n !hideFields.includes(\"education.start_date\") && edu.start_date,\n !hideFields.includes(\"education.end_date\") &&\n edu.end_date &&\n edu.end_date.toLowerCase() !== \"presente\"\n ? `- ${edu.end_date}`\n : null,\n ]\n .filter(Boolean)\n .join(\" \")}\n </span>\n )}\n </div>\n {edu.description && !hideFields.includes(\"education.description\") && (\n <p className=\"mt-3 whitespace-pre-wrap text-base leading-relaxed text-foreground/80\">\n {edu.description}\n </p>\n )}\n </div>\n ))}\n </div>\n </div>\n )}\n\n {/* Activities */}\n {activities.length > 0 && !hideSections.includes(\"Actividades y Logros\") && (\n <div className=\"space-y-6\">\n <h3 className=\"flex items-center gap-2 border-b-2 border-primary/20 pb-2 text-2xl font-bold text-primary\">\n <Trophy className=\"h-6 w-6 text-primary\" />\n Actividades y Logros\n </h3>\n <div className=\"space-y-8\">\n {activities.map((act) => (\n <div\n key={act.id}\n className=\"relative pl-6 after:absolute after:bottom-[-24px] after:left-[3px] after:top-4 after:w-[2px] after:bg-border last:after:hidden\"\n >\n <div\n className=\"pdf-line-v absolute left-0 top-2 h-2 w-2 rounded-full bg-primary\"\n style={themeStyle}\n />\n {!hideFields.includes(\"activities.title\") && (\n <h4 className=\"text-lg font-bold text-foreground\">{act.title}</h4>\n )}\n <div className=\"mb-2 flex flex-col sm:flex-row sm:items-center sm:justify-between\">\n {!hideFields.includes(\"activities.institution_name\") && (\n <span className=\"text-base font-semibold text-primary\">\n {act.institution_name}\n </span>\n )}\n {(!hideFields.includes(\"activities.start_date\") ||\n !hideFields.includes(\"activities.end_date\")) && (\n <span className=\"mt-1 w-fit rounded-md bg-muted/30 px-2 py-0.5 text-sm font-medium text-muted-foreground sm:mt-0 pdf-badge\">\n {[\n !hideFields.includes(\"activities.start_date\") && act.start_date,\n !hideFields.includes(\"activities.end_date\") &&\n act.end_date &&\n act.end_date.toLowerCase() !== \"presente\"\n ? `- ${act.end_date}`\n : null,\n ]\n .filter(Boolean)\n .join(\" \")}\n </span>\n )}\n </div>\n {act.description && !hideFields.includes(\"activities.description\") && (\n <p className=\"mt-3 whitespace-pre-wrap text-base leading-relaxed text-foreground/80\">\n {act.description}\n </p>\n )}\n </div>\n ))}\n </div>\n </div>\n )}\n\n {/* Certifications */}\n {certifications.length > 0 && !hideSections.includes(\"Certificaciones\") && (\n <div className=\"space-y-6\">\n <h3 className=\"flex items-center gap-2 border-b-2 border-primary/20 pb-2 text-2xl font-bold text-primary\">\n <Award className=\"h-6 w-6 text-primary\" />\n Certificaciones\n </h3>\n <div className=\"grid grid-cols-1 gap-4\">\n {certifications.map((cert) => (\n <div\n key={cert.id}\n className=\"rounded-lg border border-border/60 bg-card p-4 shadow-sm transition-colors hover:border-primary/30 hover:shadow-md\"\n >\n {!hideFields.includes(\"certifications.title\") && (\n <h4 className=\"break-words text-base font-bold text-foreground\">\n {cert.title}\n </h4>\n )}\n {!hideFields.includes(\"certifications.institution_name\") && (\n <p className=\"mt-1 line-clamp-1 text-sm text-muted-foreground\">\n {cert.institution_name}\n </p>\n )}\n <div className=\"mt-4 flex items-center justify-between\">\n {!hideFields.includes(\"certifications.date_awarded\") && (\n <span className=\"rounded-md bg-muted/50 px-2 py-1 text-xs font-semibold uppercase tracking-wider text-muted-foreground pdf-badge\">\n {cert.date_awarded}\n </span>\n )}\n {cert.verified && !hideFields.includes(\"certifications.verified\") && (\n <span\n className=\"pdf-badge rounded-full border px-2.5 py-1 text-xs font-bold transition-colors\"\n style={{\n backgroundColor: theme?.startsWith(\"#\") ? `${theme}15` : undefined,\n color: theme,\n borderColor: theme?.startsWith(\"#\") ? `${theme}30` : theme,\n }}\n >\n Verificado\n </span>\n )}\n </div>\n </div>\n ))}\n </div>\n </div>\n )}\n </div>\n );\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../../../../src/components/domain/resume/resume-preview/sections/experience-education.tsx"],"sourcesContent":["import { CandidateProfile } from \"../../types\";\nimport type { CVExperienceBaseItem, CVCertificationItem } from \"../../types\";\nimport { GraduationCap, Briefcase, Trophy, Award } from \"lucide-react\";\nimport { format } from \"date-fns\";\nimport { es } from \"date-fns/locale\";\n\n/** Parsea una fecha a timestamp. Soporta ISO (YYYY-MM-DD), MM/YYYY, YYYY-MM, YYYY. */\nfunction parseDateToTimestamp(dateStr: string | undefined): number {\n if (!dateStr) return 0;\n const isoMatch = dateStr.match(/^(\\d{4})-(\\d{1,2})(?:-(\\d{1,2}))?/);\n if (isoMatch) {\n const [, y, m, d] = isoMatch;\n // Importante: interpretar como fecha \"local\" para evitar corrimientos por zona horaria\n // (p.ej. 2024-08-01 en UTC puede formatearse como julio en zonas negativas).\n const year = Number(y);\n const monthIndex = Number(m) - 1; // JS Date usa meses 0-based\n const day = Number(d || \"1\");\n return new Date(year, monthIndex, day).getTime();\n }\n const mmYyyy = dateStr.match(/^(\\d{1,2})\\/(\\d{4})$/);\n if (mmYyyy) {\n const [, m, y] = mmYyyy;\n const year = Number(y);\n const monthIndex = Number(m) - 1;\n return new Date(year, monthIndex, 1).getTime();\n }\n const year = dateStr.match(/\\d{4}/);\n if (year) {\n return new Date(Number(year[0]), 0, 1).getTime();\n }\n return 0;\n}\n\n/** Formatea una fecha ISO/string a \"mes año\" en español, ej: \"agosto 2020\". */\nfunction formatDisplayDate(dateStr: string | undefined): string {\n if (!dateStr) return \"\";\n // Si es un valor semántico como \"Presente\", lo retorna tal cual\n if (!/\\d{4}/.test(dateStr)) return dateStr;\n const ts = parseDateToTimestamp(dateStr);\n if (!ts) return dateStr;\n return format(new Date(ts), \"MMMM yyyy\", { locale: es });\n}\n\nfunction formatDateRange(opts: {\n start?: string;\n end?: string;\n hideStart?: boolean;\n hideEnd?: boolean;\n}): string {\n const startText = opts.hideStart ? \"\" : formatDisplayDate(opts.start);\n const endText = opts.hideEnd ? \"\" : formatDisplayDate(opts.end);\n\n if (startText && endText) return `${startText} - ${endText}`;\n return startText || endText || \"\";\n}\n\n/** Ordena descendente por fecha (más reciente primero). Usa end_date o start_date si no hay end. */\nfunction sortByDateDesc<T extends CVExperienceBaseItem>(items: T[]): T[] {\n return [...items].sort((a, b) => {\n const dateA = a.end_date || a.start_date;\n const dateB = b.end_date || b.start_date;\n if (!dateA && !dateB) return 0;\n if (!dateA) return 1;\n if (!dateB) return -1;\n return parseDateToTimestamp(dateB) - parseDateToTimestamp(dateA);\n });\n}\n\n/** Ordena certificaciones descendente por date_awarded */\nfunction sortCertificationsByDateDesc(items: CVCertificationItem[]): CVCertificationItem[] {\n return [...items].sort((a, b) => {\n const tsA = parseDateToTimestamp(a.date_awarded);\n const tsB = parseDateToTimestamp(b.date_awarded);\n return tsB - tsA;\n });\n}\n\ninterface ExperienceEducationProps {\n data: CandidateProfile;\n theme?: string;\n hideFields?: string[];\n hideSections?: string[];\n}\n\nexport function ExperienceEducation({\n data,\n theme,\n hideFields = [],\n hideSections = [],\n}: ExperienceEducationProps) {\n const experiences = sortByDateDesc(data.work_experience || []);\n const education = sortByDateDesc(data.education || []);\n const activities = sortByDateDesc(data.activities || []);\n const certifications = sortCertificationsByDateDesc(data.certifications || []);\n\n const themeStyle = theme ? { backgroundColor: theme } : undefined;\n\n return (\n <div className=\"space-y-12\">\n {/* Experience */}\n {experiences.length > 0 && !hideSections.includes(\"Experiencia laboral\") && (\n <div className=\"space-y-6\">\n <h3 className=\"flex items-center gap-2 border-b-2 border-primary/20 pb-2 text-2xl font-bold text-primary\">\n <Briefcase className=\"h-6 w-6 text-primary\" />\n Experiencia Laboral\n </h3>\n <div className=\"space-y-8\">\n {experiences.map((exp) => (\n <div\n key={exp.id}\n className=\"relative pl-6 after:absolute after:bottom-[-24px] after:left-[3px] after:top-4 after:w-[2px] after:bg-border last:after:hidden\"\n >\n <div\n className=\"pdf-line-v absolute left-0 top-2 h-2 w-2 rounded-full bg-primary\"\n style={themeStyle}\n />\n {!hideFields.includes(\"experience.title\") && (\n <h4 className=\"text-lg font-bold text-foreground\">{exp.title}</h4>\n )}\n <div className=\"mb-2 flex flex-col sm:flex-row sm:items-center sm:justify-between\">\n {!hideFields.includes(\"experience.institution_name\") && (\n <span className=\"text-base font-semibold text-primary\">\n {exp.institution_name}\n </span>\n )}\n {(!hideFields.includes(\"experience.start_date\") ||\n !hideFields.includes(\"experience.end_date\")) && (\n <span className=\"mt-1 w-fit rounded-md bg-muted/30 px-2 py-0.5 text-sm font-medium text-muted-foreground sm:mt-0 pdf-badge\">\n {formatDateRange({\n start: exp.start_date,\n end: exp.end_date,\n hideStart: hideFields.includes(\"experience.start_date\"),\n hideEnd: hideFields.includes(\"experience.end_date\"),\n })}\n </span>\n )}\n </div>\n {exp.description && !hideFields.includes(\"experience.description\") && (\n <p className=\"mt-3 whitespace-pre-wrap text-base leading-relaxed text-foreground/80\">\n {exp.description}\n </p>\n )}\n </div>\n ))}\n </div>\n </div>\n )}\n\n {/* Education */}\n {education.length > 0 && !hideSections.includes(\"Educación\") && (\n <div className=\"space-y-6\">\n <h3 className=\"flex items-center gap-2 border-b-2 border-primary/20 pb-2 text-2xl font-bold text-primary\">\n <GraduationCap className=\"h-6 w-6 text-primary\" />\n Educación\n </h3>\n <div className=\"space-y-8\">\n {education.map((edu) => (\n <div\n key={edu.id}\n className=\"relative pl-6 after:absolute after:bottom-[-24px] after:left-[3px] after:top-4 after:w-[2px] after:bg-border last:after:hidden\"\n >\n <div\n className=\"pdf-line-v absolute left-0 top-2 h-2 w-2 rounded-full bg-primary\"\n style={themeStyle}\n />\n {!hideFields.includes(\"education.title\") && (\n <h4 className=\"text-lg font-bold text-foreground\">{edu.title}</h4>\n )}\n <div className=\"mb-2 flex flex-col sm:flex-row sm:items-center sm:justify-between\">\n {!hideFields.includes(\"education.institution_name\") && (\n <span className=\"text-base font-semibold text-primary\">\n {edu.institution_name}\n </span>\n )}\n {(!hideFields.includes(\"education.start_date\") ||\n !hideFields.includes(\"education.end_date\")) && (\n <span className=\"mt-1 w-fit rounded-md bg-muted/30 px-2 py-0.5 text-sm font-medium text-muted-foreground sm:mt-0 pdf-badge\">\n {formatDateRange({\n start: edu.start_date,\n end: edu.end_date,\n hideStart: hideFields.includes(\"education.start_date\"),\n hideEnd: hideFields.includes(\"education.end_date\"),\n })}\n </span>\n )}\n </div>\n {edu.description && !hideFields.includes(\"education.description\") && (\n <p className=\"mt-3 whitespace-pre-wrap text-base leading-relaxed text-foreground/80\">\n {edu.description}\n </p>\n )}\n </div>\n ))}\n </div>\n </div>\n )}\n\n {/* Activities */}\n {activities.length > 0 && !hideSections.includes(\"Actividades y Logros\") && (\n <div className=\"space-y-6\">\n <h3 className=\"flex items-center gap-2 border-b-2 border-primary/20 pb-2 text-2xl font-bold text-primary\">\n <Trophy className=\"h-6 w-6 text-primary\" />\n Actividades y Logros\n </h3>\n <div className=\"space-y-8\">\n {activities.map((act) => (\n <div\n key={act.id}\n className=\"relative pl-6 after:absolute after:bottom-[-24px] after:left-[3px] after:top-4 after:w-[2px] after:bg-border last:after:hidden\"\n >\n <div\n className=\"pdf-line-v absolute left-0 top-2 h-2 w-2 rounded-full bg-primary\"\n style={themeStyle}\n />\n {!hideFields.includes(\"activities.title\") && (\n <h4 className=\"text-lg font-bold text-foreground\">{act.title}</h4>\n )}\n <div className=\"mb-2 flex flex-col sm:flex-row sm:items-center sm:justify-between\">\n {!hideFields.includes(\"activities.institution_name\") && (\n <span className=\"text-base font-semibold text-primary\">\n {act.institution_name}\n </span>\n )}\n {(!hideFields.includes(\"activities.start_date\") ||\n !hideFields.includes(\"activities.end_date\")) && (\n <span className=\"mt-1 w-fit rounded-md bg-muted/30 px-2 py-0.5 text-sm font-medium text-muted-foreground sm:mt-0 pdf-badge\">\n {formatDateRange({\n start: act.start_date,\n end: act.end_date,\n hideStart: hideFields.includes(\"activities.start_date\"),\n hideEnd: hideFields.includes(\"activities.end_date\"),\n })}\n </span>\n )}\n </div>\n {act.description && !hideFields.includes(\"activities.description\") && (\n <p className=\"mt-3 whitespace-pre-wrap text-base leading-relaxed text-foreground/80\">\n {act.description}\n </p>\n )}\n </div>\n ))}\n </div>\n </div>\n )}\n\n {/* Certifications */}\n {certifications.length > 0 && !hideSections.includes(\"Certificaciones\") && (\n <div className=\"space-y-6\">\n <h3 className=\"flex items-center gap-2 border-b-2 border-primary/20 pb-2 text-2xl font-bold text-primary\">\n <Award className=\"h-6 w-6 text-primary\" />\n Certificaciones\n </h3>\n <div className=\"grid grid-cols-1 gap-4\">\n {certifications.map((cert) => (\n <div\n key={cert.id}\n className=\"rounded-lg border border-border/60 bg-card p-4 shadow-sm transition-colors hover:border-primary/30 hover:shadow-md\"\n >\n {!hideFields.includes(\"certifications.title\") && (\n <h4 className=\"break-words text-base font-bold text-foreground\">\n {cert.title}\n </h4>\n )}\n {!hideFields.includes(\"certifications.institution_name\") && (\n <p className=\"mt-1 line-clamp-1 text-sm text-muted-foreground\">\n {cert.institution_name}\n </p>\n )}\n <div className=\"mt-4 flex items-center justify-between\">\n {!hideFields.includes(\"certifications.date_awarded\") && (\n <span className=\"rounded-md bg-muted/50 px-2 py-1 text-xs font-semibold uppercase tracking-wider text-muted-foreground pdf-badge\">\n {cert.date_awarded}\n </span>\n )}\n {cert.verified && !hideFields.includes(\"certifications.verified\") && (\n <span\n className=\"pdf-badge rounded-full border px-2.5 py-1 text-xs font-bold transition-colors\"\n style={{\n backgroundColor: theme?.startsWith(\"#\") ? `${theme}15` : undefined,\n color: theme,\n borderColor: theme?.startsWith(\"#\") ? `${theme}30` : theme,\n }}\n >\n Verificado\n </span>\n )}\n </div>\n </div>\n ))}\n </div>\n </div>\n )}\n </div>\n );\n}\n"],"mappings":"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","names":["year"]}
@@ -128,6 +128,7 @@ function Resume({
128
128
  hideFields = [],
129
129
  hideSections = [],
130
130
  menuSlot = [],
131
+ customMenuOnly = false,
131
132
  onSave,
132
133
  showTopBar = true,
133
134
  className
@@ -387,7 +388,8 @@ function Resume({
387
388
  theme,
388
389
  hideFields,
389
390
  hideSections,
390
- menuSlot
391
+ menuSlot,
392
+ customMenuOnly
391
393
  }
392
394
  ) }),
393
395
  onSave && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex justify-end gap-3 p-4", children: [
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/components/domain/resume/resume.tsx"],"sourcesContent":["import { useRef, useState } from \"react\";\nimport html2canvas from \"html2canvas\";\nimport { jsPDF } from \"jspdf\";\nimport { CandidateProfile, ResumeProps, ResumeSavePayload, Action } from \"./types\";\nimport { ResumeEditor } from \"./resume-editor/resume-editor\";\nimport { ResumePreview } from \"./resume-preview/resume-preview\";\nimport { Button } from \"../../ui/button\";\nimport { Download, Edit, Loader2, Save, X } from \"lucide-react\";\nimport { Sheet, SheetTrigger, SheetContent, SheetTitle, SheetClose } from \"../../ui/sheet\";\n\nfunction calculateDiffs(original: CandidateProfile, current: CandidateProfile): ResumeSavePayload {\n // Determine diff for candidate table (academic info etc)\n const candidateData: Partial<CandidateProfile> = {};\n if (original.student_education_institution_id !== current.student_education_institution_id)\n candidateData.student_education_institution_id = current.student_education_institution_id;\n if (original.education_institution_id !== current.education_institution_id)\n candidateData.education_institution_id = current.education_institution_id;\n if (original.program_name !== current.program_name)\n candidateData.program_name = current.program_name;\n if (JSON.stringify(original.preferences) !== JSON.stringify(current.preferences))\n candidateData.preferences = current.preferences;\n if (JSON.stringify(original.advanced) !== JSON.stringify(current.advanced))\n candidateData.advanced = current.advanced;\n\n // Determine diff for user table (personal info)\n const userData: Partial<CandidateProfile> = {};\n const userFields: (keyof CandidateProfile)[] = [\n \"first_name\",\n \"middle_name\",\n \"last_name\",\n \"second_last_name\",\n \"phone_number\",\n \"phone_number_2\",\n \"email\",\n \"city\",\n \"address\",\n \"postal_code\",\n \"birth_date\",\n \"photo_url\",\n ];\n userFields.forEach((field) => {\n if (original[field] !== current[field]) {\n // @ts-expect-error - jspdf types are a bit funky\n userData[field] = current[field];\n }\n });\n\n // Helper for array diffs\n const calculateArrayDiff = (origArray: any[] = [], currArray: any[] = []) => {\n const changes: any[] = [];\n const origMap = new Map(origArray.map((item) => [item.id, item]));\n\n currArray.forEach((currItem) => {\n const origItem = origMap.get(currItem.id);\n if (!origItem) {\n // New item\n changes.push({ action: \"create\" as Action, data: currItem });\n } else if (JSON.stringify(origItem) !== JSON.stringify(currItem)) {\n // Updated item\n changes.push({ action: \"update\" as Action, data: currItem, id: currItem.id });\n }\n });\n\n const currMap = new Map(currArray.map((item) => [item.id, item]));\n origArray.forEach((origItem) => {\n if (!currMap.has(origItem.id)) {\n // Deleted item\n changes.push({ action: \"delete\" as Action, id: origItem.id });\n }\n });\n\n return changes;\n };\n\n const experiencesDiff = [\n ...calculateArrayDiff(original.work_experience, current.work_experience),\n ...calculateArrayDiff(original.education, current.education),\n ...calculateArrayDiff(original.activities, current.activities),\n ];\n\n const hwOrig = original.skills?.hard || [];\n const hwCurr = current.skills?.hard || [];\n const swOrig = original.skills?.soft || [];\n const swCurr = current.skills?.soft || [];\n\n const skillsDiff = [...calculateArrayDiff(hwOrig, hwCurr), ...calculateArrayDiff(swOrig, swCurr)];\n\n return {\n candidate: {\n action: Object.keys(candidateData).length > 0 ? \"update\" : \"none\",\n data: candidateData,\n },\n user: {\n action: Object.keys(userData).length > 0 ? \"update\" : \"none\",\n data: userData,\n },\n curriculum: {\n action: original.summary !== current.summary ? \"update\" : \"none\",\n data: { summary: current.summary },\n },\n experiences: experiencesDiff,\n skills: skillsDiff,\n certifications: calculateArrayDiff(original.certifications, current.certifications),\n };\n}\n\nexport function Resume({\n data,\n editable = false,\n headerSlot,\n withDownload = false,\n theme = \"hsl(var(--secondary))\",\n hideFields = [],\n hideSections = [],\n menuSlot = [],\n onSave,\n showTopBar = true,\n className,\n}: ResumeProps) {\n const themeStyle = theme ? ({ \"--cv-theme\": theme } as React.CSSProperties) : undefined;\n const [draftData, setDraftData] = useState<CandidateProfile>(data);\n const [isOpen, setIsOpen] = useState(false);\n const [isDownloading, setIsDownloading] = useState(false);\n const previewRef = useRef<HTMLDivElement>(null);\n\n const handleOpenChange = (open: boolean) => {\n if (open) {\n setDraftData(data);\n }\n setIsOpen(open);\n };\n\n const handleSave = () => {\n if (onSave) {\n const payload = calculateDiffs(data, draftData);\n onSave(payload, draftData);\n }\n setIsOpen(false);\n };\n\n const handleDownload = async () => {\n if (!previewRef.current || isDownloading) return;\n setIsDownloading(true);\n\n try {\n const element = previewRef.current;\n const canvas = await html2canvas(element, {\n scale: 2,\n useCORS: true,\n backgroundColor: \"#ffffff\",\n logging: false,\n windowWidth: 1200, // Force desktop layout breakpoint\n onclone: (clonedDoc) => {\n const clonedElement = clonedDoc.getElementById(\"resume-capture-area\");\n if (clonedElement instanceof HTMLElement) {\n clonedElement.style.height = \"auto\";\n clonedElement.style.overflow = \"visible\";\n clonedElement.style.width = \"1200px\";\n\n // Remove border radius for elements with pdf-border-line class in PDF\n const borderLineElements = Array.from(clonedElement.querySelectorAll(\".pdf-border-line\"));\n if (clonedElement.classList.contains(\"pdf-border-line\")) {\n borderLineElements.push(clonedElement);\n }\n borderLineElements.forEach((el) => {\n if (el instanceof HTMLElement) {\n el.style.setProperty(\"border-radius\", \"0\", \"important\");\n }\n });\n\n // Fix Background: html2canvas doesn't support mask-image. Render gradient to canvas\n // and use as background-image for pixel-perfect PDF output.\n const backgroundGradients = clonedElement.querySelectorAll(\"[data-pdf-background-gradient]\");\n const w = 1200;\n const h = 160;\n const canvas = clonedDoc.createElement(\"canvas\");\n canvas.width = w;\n canvas.height = h;\n const ctx = canvas.getContext(\"2d\");\n if (ctx) {\n const hGrad = ctx.createLinearGradient(0, 0, w, 0);\n hGrad.addColorStop(0, \"#ffffff\");\n hGrad.addColorStop(0.5, \"#f3e8ff\");\n hGrad.addColorStop(1, \"#bfdbfe\");\n ctx.fillStyle = hGrad;\n ctx.fillRect(0, 0, w, h);\n ctx.globalCompositeOperation = \"destination-in\";\n const vGrad = ctx.createLinearGradient(0, 0, 0, h);\n vGrad.addColorStop(0, \"rgba(0,0,0,1)\");\n vGrad.addColorStop(1, \"rgba(0,0,0,0)\");\n ctx.fillStyle = vGrad;\n ctx.fillRect(0, 0, w, h);\n ctx.globalCompositeOperation = \"source-over\";\n }\n const dataUrl = canvas.toDataURL(\"image/png\");\n backgroundGradients.forEach((el) => {\n if (el instanceof HTMLElement) {\n el.style.maskImage = \"none\";\n el.style.background = \"none\";\n el.style.backgroundImage = `url(${dataUrl})`;\n el.style.backgroundSize = \"100% 100%\";\n el.style.backgroundPosition = \"0 0\";\n }\n });\n\n // Fix for images with object-fit: cover not being respected by html2canvas\n const objectCoverImages = clonedElement.querySelectorAll(\"img.object-cover\");\n objectCoverImages.forEach((img) => {\n if (img instanceof HTMLImageElement) {\n const parent = img.parentElement;\n if (parent) {\n // Replace img with a div that has background-image: cover\n const div = clonedDoc.createElement(\"div\");\n div.style.width = \"100%\";\n div.style.height = \"100%\";\n div.style.backgroundImage = `url(${img.src})`;\n div.style.backgroundSize = \"cover\";\n div.style.backgroundPosition = \"center\";\n div.style.borderRadius = window.getComputedStyle(img).borderRadius;\n div.className = img.className;\n parent.replaceChild(div, img);\n }\n }\n });\n\n // Fix Avatar alignment in PDF: apply consistent margin for both image and fallback\n const headerAvatars = clonedElement.querySelectorAll(\".pdf-header-avatar\");\n headerAvatars.forEach((el) => {\n if (el instanceof HTMLElement) {\n el.style.marginTop = \"1.33rem\";\n }\n });\n\n // Hide elements marked as hidden for PDF\n const hiddenElements = clonedElement.querySelectorAll(\".pdf-hidden\");\n hiddenElements.forEach((el) => {\n if (el instanceof HTMLElement) {\n el.style.display = \"none\";\n }\n });\n\n // Fix SVG vertical alignment for html2canvas\n // html2canvas doesn't properly handle flex align-items with SVGs\n const svgs = clonedElement.querySelectorAll(\"svg\");\n svgs.forEach((svg) => {\n svg.style.display = \"inline-block\";\n svg.style.verticalAlign = \"middle\";\n svg.style.flexShrink = \"0\";\n svg.style.marginRight = \"0.4rem\";\n svg.style.marginBottom = \"-1.2rem\";\n });\n\n // Fix line-clamp which causes text vertical cut-offs in html2canvas\n const clampElements = clonedElement.querySelectorAll(\n \".line-clamp-1, .line-clamp-2, .line-clamp-3\",\n );\n clampElements.forEach((el) => {\n if (el instanceof HTMLElement) {\n el.style.display = \"block\";\n el.style.webkitLineClamp = \"unset\";\n el.style.webkitBoxOrient = \"unset\";\n el.style.overflow = \"visible\";\n el.style.whiteSpace = \"normal\";\n el.style.lineHeight = \"1.5\";\n }\n });\n\n // Replace generic HTML elements like custom badges with simple spans\n // because html2canvas struggles with flexbox and strict borders inside round pills\n const badges = clonedElement.querySelectorAll(\".pdf-badge\");\n badges.forEach((el) => {\n if (el instanceof HTMLElement) {\n el.style.paddingTop = \"-10px\";\n el.style.display = \"block\";\n el.style.paddingTop = \"0px\";\n el.style.paddingBottom = \"0.8rem\";\n }\n });\n\n // Fix headers with bottom borders losing padding/margin\n const headersWithBorders = clonedElement.querySelectorAll(\"h1, h2, h3, h4\");\n headersWithBorders.forEach((el) => {\n if (el instanceof HTMLElement) {\n const hasBorder =\n el.className.includes(\"border-b\") || el.style.borderBottomWidth !== \"\";\n if (hasBorder) {\n el.style.paddingBottom = \"1.2rem\";\n }\n }\n });\n\n // Add margin top to pdf-line-v elements\n const verticalLines = clonedElement.querySelectorAll(\".pdf-line-v\");\n verticalLines.forEach((el) => {\n if (el instanceof HTMLElement) {\n el.style.marginTop = \"0.5rem\";\n el.style.zIndex = \"5\";\n }\n });\n\n // Add margin bottom to initials in header for better alignment in PDF\n const headerInitials = clonedElement.querySelectorAll(\".pdf-header-initials\");\n headerInitials.forEach((el) => {\n if (el instanceof HTMLElement) {\n el.style.paddingBottom = \"2rem\";\n }\n });\n\n // Add styles to summary for PDF\n const summaries = clonedElement.querySelectorAll(\".pdf-summary\");\n summaries.forEach((el) => {\n if (el instanceof HTMLElement) {\n el.style.width = \"100%\";\n el.style.maxWidth = \"100%\";\n el.style.marginTop = \"1.5rem\";\n }\n });\n\n // Show footer in PDF and force grayscale for the logo\n const pdfFooters = clonedElement.querySelectorAll(\".pdf-footer\");\n pdfFooters.forEach((el) => {\n if (el instanceof HTMLElement) {\n el.style.display = \"flex\";\n }\n });\n\n // Also make sure parent containers don't restrict height\n let parent = clonedElement.parentElement;\n while (parent) {\n parent.style.height = \"auto\";\n parent.style.overflow = \"visible\";\n parent = parent.parentElement;\n }\n }\n },\n });\n\n const imgData = canvas.toDataURL(\"image/jpeg\", 1);\n const imgWidthPx = canvas.width;\n const imgHeightPx = canvas.height;\n\n // Convert px to mm (at 96 DPI base * scale factor)\n const pxToMm = 25.4 / (96 * 2);\n const pdfWidth = imgWidthPx * pxToMm;\n const pdfHeight = imgHeightPx * pxToMm;\n\n const pdf = new jsPDF({\n orientation: pdfWidth > pdfHeight ? \"landscape\" : \"portrait\",\n unit: \"mm\",\n format: [pdfWidth, pdfHeight],\n });\n\n pdf.addImage(imgData, \"JPEG\", 0, 0, pdfWidth, pdfHeight, undefined, \"FAST\");\n\n const fileName = `CV_${data.first_name}_${data.last_name}`.replace(/\\s+/g, \"_\");\n pdf.save(`${fileName}.pdf`);\n } catch (error) {\n console.error(\"Error generating PDF:\", error);\n } finally {\n setIsDownloading(false);\n }\n };\n\n return (\n <div\n className=\"relative h-full w-full overflow-y-auto bg-muted/20 px-4 py-4 sm:px-8\"\n style={themeStyle}\n >\n {/* Preview Panel - Takes full width, centered like a document */}\n <div className=\"mx-auto h-full w-full max-w-5xl\">\n <ResumePreview\n ref={previewRef}\n data={draftData}\n headerSlot={headerSlot}\n theme={theme}\n hideFields={hideFields}\n hideSections={hideSections}\n showTopBar={showTopBar}\n className={className}\n />\n </div>\n\n {/* Floating Action Buttons */}\n <div className=\"fixed bottom-6 right-6 z-50 flex flex-col gap-3 sm:flex-row\">\n {editable && (\n <Sheet open={isOpen} onOpenChange={handleOpenChange}>\n <SheetTrigger asChild>\n <Button\n size=\"lg\"\n className=\"flex items-center gap-2 rounded-full bg-primary px-4 text-primary-foreground shadow-xl hover:bg-primary/90\"\n >\n <Edit className=\"h-5 w-5\" />\n <span className=\"hidden font-semibold sm:inline\">Editar</span>\n </Button>\n </SheetTrigger>\n {/* Drawer opens from the right side, taking good width */}\n <SheetContent\n side=\"right\"\n showCloseButton={false}\n className=\"flex w-full flex-col overflow-hidden bg-card p-0 sm:max-w-md md:max-w-2xl\"\n >\n {/* Header */}\n <div className=\"flex items-center justify-between p-6 pb-2\">\n <SheetTitle className=\"text-2xl font-bold text-foreground\">Editar</SheetTitle>\n <SheetClose className=\"rounded-full p-2 opacity-70 transition-colors hover:bg-muted hover:opacity-100\">\n <X className=\"h-6 w-6 text-foreground\" />\n <span className=\"sr-only\">Cerrar</span>\n </SheetClose>\n </div>\n\n {/* Scrollable editor content */}\n <div className=\"flex-1 overflow-y-auto px-6 pb-6 pt-2\">\n <ResumeEditor\n data={draftData}\n onChange={setDraftData}\n theme={theme}\n hideFields={hideFields}\n hideSections={hideSections}\n menuSlot={menuSlot}\n />\n </div>\n\n {/* Sticky save footer */}\n {onSave && (\n <div className=\"flex justify-end gap-3 p-4\">\n <SheetClose asChild>\n <Button variant=\"outline\">Cancelar</Button>\n </SheetClose>\n <Button onClick={handleSave} className=\"gap-2\">\n <Save className=\"h-4 w-4\" />\n Guardar\n </Button>\n </div>\n )}\n </SheetContent>\n </Sheet>\n )}\n\n {withDownload && (\n <Button\n size=\"lg\"\n variant=\"ghost\"\n disabled={isDownloading}\n onClick={handleDownload}\n className=\"flex items-center gap-2 rounded-full bg-purple-200 px-4 text-purple-100 text-primary shadow-xl\"\n >\n {isDownloading ? (\n <Loader2 className=\"h-5 w-5 animate-spin\" />\n ) : (\n <Download className=\"h-5 w-5\" />\n )}\n <span className=\"hidden font-semibold sm:inline\">\n {isDownloading ? \"Generando...\" : \"Descargar\"}\n </span>\n </Button>\n )}\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":["html2canvas","canvas","parent"]}
1
+ {"version":3,"sources":["../../../../src/components/domain/resume/resume.tsx"],"sourcesContent":["import { useRef, useState } from \"react\";\nimport html2canvas from \"html2canvas\";\nimport { jsPDF } from \"jspdf\";\nimport { CandidateProfile, ResumeProps, ResumeSavePayload, Action } from \"./types\";\nimport { ResumeEditor } from \"./resume-editor/resume-editor\";\nimport { ResumePreview } from \"./resume-preview/resume-preview\";\nimport { Button } from \"../../ui/button\";\nimport { Download, Edit, Loader2, Save, X } from \"lucide-react\";\nimport { Sheet, SheetTrigger, SheetContent, SheetTitle, SheetClose } from \"../../ui/sheet\";\n\nfunction calculateDiffs(original: CandidateProfile, current: CandidateProfile): ResumeSavePayload {\n // Determine diff for candidate table (academic info etc)\n const candidateData: Partial<CandidateProfile> = {};\n if (original.student_education_institution_id !== current.student_education_institution_id)\n candidateData.student_education_institution_id = current.student_education_institution_id;\n if (original.education_institution_id !== current.education_institution_id)\n candidateData.education_institution_id = current.education_institution_id;\n if (original.program_name !== current.program_name)\n candidateData.program_name = current.program_name;\n if (JSON.stringify(original.preferences) !== JSON.stringify(current.preferences))\n candidateData.preferences = current.preferences;\n if (JSON.stringify(original.advanced) !== JSON.stringify(current.advanced))\n candidateData.advanced = current.advanced;\n\n // Determine diff for user table (personal info)\n const userData: Partial<CandidateProfile> = {};\n const userFields: (keyof CandidateProfile)[] = [\n \"first_name\",\n \"middle_name\",\n \"last_name\",\n \"second_last_name\",\n \"phone_number\",\n \"phone_number_2\",\n \"email\",\n \"city\",\n \"address\",\n \"postal_code\",\n \"birth_date\",\n \"photo_url\",\n ];\n userFields.forEach((field) => {\n if (original[field] !== current[field]) {\n // @ts-expect-error - jspdf types are a bit funky\n userData[field] = current[field];\n }\n });\n\n // Helper for array diffs\n const calculateArrayDiff = (origArray: any[] = [], currArray: any[] = []) => {\n const changes: any[] = [];\n const origMap = new Map(origArray.map((item) => [item.id, item]));\n\n currArray.forEach((currItem) => {\n const origItem = origMap.get(currItem.id);\n if (!origItem) {\n // New item\n changes.push({ action: \"create\" as Action, data: currItem });\n } else if (JSON.stringify(origItem) !== JSON.stringify(currItem)) {\n // Updated item\n changes.push({ action: \"update\" as Action, data: currItem, id: currItem.id });\n }\n });\n\n const currMap = new Map(currArray.map((item) => [item.id, item]));\n origArray.forEach((origItem) => {\n if (!currMap.has(origItem.id)) {\n // Deleted item\n changes.push({ action: \"delete\" as Action, id: origItem.id });\n }\n });\n\n return changes;\n };\n\n const experiencesDiff = [\n ...calculateArrayDiff(original.work_experience, current.work_experience),\n ...calculateArrayDiff(original.education, current.education),\n ...calculateArrayDiff(original.activities, current.activities),\n ];\n\n const hwOrig = original.skills?.hard || [];\n const hwCurr = current.skills?.hard || [];\n const swOrig = original.skills?.soft || [];\n const swCurr = current.skills?.soft || [];\n\n const skillsDiff = [...calculateArrayDiff(hwOrig, hwCurr), ...calculateArrayDiff(swOrig, swCurr)];\n\n return {\n candidate: {\n action: Object.keys(candidateData).length > 0 ? \"update\" : \"none\",\n data: candidateData,\n },\n user: {\n action: Object.keys(userData).length > 0 ? \"update\" : \"none\",\n data: userData,\n },\n curriculum: {\n action: original.summary !== current.summary ? \"update\" : \"none\",\n data: { summary: current.summary },\n },\n experiences: experiencesDiff,\n skills: skillsDiff,\n certifications: calculateArrayDiff(original.certifications, current.certifications),\n };\n}\n\nexport function Resume({\n data,\n editable = false,\n headerSlot,\n withDownload = false,\n theme = \"hsl(var(--secondary))\",\n hideFields = [],\n hideSections = [],\n menuSlot = [],\n customMenuOnly = false,\n onSave,\n showTopBar = true,\n className,\n}: ResumeProps) {\n const themeStyle = theme ? ({ \"--cv-theme\": theme } as React.CSSProperties) : undefined;\n const [draftData, setDraftData] = useState<CandidateProfile>(data);\n const [isOpen, setIsOpen] = useState(false);\n const [isDownloading, setIsDownloading] = useState(false);\n const previewRef = useRef<HTMLDivElement>(null);\n\n const handleOpenChange = (open: boolean) => {\n if (open) {\n setDraftData(data);\n }\n setIsOpen(open);\n };\n\n const handleSave = () => {\n if (onSave) {\n const payload = calculateDiffs(data, draftData);\n onSave(payload, draftData);\n }\n setIsOpen(false);\n };\n\n const handleDownload = async () => {\n if (!previewRef.current || isDownloading) return;\n setIsDownloading(true);\n\n try {\n const element = previewRef.current;\n const canvas = await html2canvas(element, {\n scale: 2,\n useCORS: true,\n backgroundColor: \"#ffffff\",\n logging: false,\n windowWidth: 1200, // Force desktop layout breakpoint\n onclone: (clonedDoc) => {\n const clonedElement = clonedDoc.getElementById(\"resume-capture-area\");\n if (clonedElement instanceof HTMLElement) {\n clonedElement.style.height = \"auto\";\n clonedElement.style.overflow = \"visible\";\n clonedElement.style.width = \"1200px\";\n\n // Remove border radius for elements with pdf-border-line class in PDF\n const borderLineElements = Array.from(clonedElement.querySelectorAll(\".pdf-border-line\"));\n if (clonedElement.classList.contains(\"pdf-border-line\")) {\n borderLineElements.push(clonedElement);\n }\n borderLineElements.forEach((el) => {\n if (el instanceof HTMLElement) {\n el.style.setProperty(\"border-radius\", \"0\", \"important\");\n }\n });\n\n // Fix Background: html2canvas doesn't support mask-image. Render gradient to canvas\n // and use as background-image for pixel-perfect PDF output.\n const backgroundGradients = clonedElement.querySelectorAll(\"[data-pdf-background-gradient]\");\n const w = 1200;\n const h = 160;\n const canvas = clonedDoc.createElement(\"canvas\");\n canvas.width = w;\n canvas.height = h;\n const ctx = canvas.getContext(\"2d\");\n if (ctx) {\n const hGrad = ctx.createLinearGradient(0, 0, w, 0);\n hGrad.addColorStop(0, \"#ffffff\");\n hGrad.addColorStop(0.5, \"#f3e8ff\");\n hGrad.addColorStop(1, \"#bfdbfe\");\n ctx.fillStyle = hGrad;\n ctx.fillRect(0, 0, w, h);\n ctx.globalCompositeOperation = \"destination-in\";\n const vGrad = ctx.createLinearGradient(0, 0, 0, h);\n vGrad.addColorStop(0, \"rgba(0,0,0,1)\");\n vGrad.addColorStop(1, \"rgba(0,0,0,0)\");\n ctx.fillStyle = vGrad;\n ctx.fillRect(0, 0, w, h);\n ctx.globalCompositeOperation = \"source-over\";\n }\n const dataUrl = canvas.toDataURL(\"image/png\");\n backgroundGradients.forEach((el) => {\n if (el instanceof HTMLElement) {\n el.style.maskImage = \"none\";\n el.style.background = \"none\";\n el.style.backgroundImage = `url(${dataUrl})`;\n el.style.backgroundSize = \"100% 100%\";\n el.style.backgroundPosition = \"0 0\";\n }\n });\n\n // Fix for images with object-fit: cover not being respected by html2canvas\n const objectCoverImages = clonedElement.querySelectorAll(\"img.object-cover\");\n objectCoverImages.forEach((img) => {\n if (img instanceof HTMLImageElement) {\n const parent = img.parentElement;\n if (parent) {\n // Replace img with a div that has background-image: cover\n const div = clonedDoc.createElement(\"div\");\n div.style.width = \"100%\";\n div.style.height = \"100%\";\n div.style.backgroundImage = `url(${img.src})`;\n div.style.backgroundSize = \"cover\";\n div.style.backgroundPosition = \"center\";\n div.style.borderRadius = window.getComputedStyle(img).borderRadius;\n div.className = img.className;\n parent.replaceChild(div, img);\n }\n }\n });\n\n // Fix Avatar alignment in PDF: apply consistent margin for both image and fallback\n const headerAvatars = clonedElement.querySelectorAll(\".pdf-header-avatar\");\n headerAvatars.forEach((el) => {\n if (el instanceof HTMLElement) {\n el.style.marginTop = \"1.33rem\";\n }\n });\n\n // Hide elements marked as hidden for PDF\n const hiddenElements = clonedElement.querySelectorAll(\".pdf-hidden\");\n hiddenElements.forEach((el) => {\n if (el instanceof HTMLElement) {\n el.style.display = \"none\";\n }\n });\n\n // Fix SVG vertical alignment for html2canvas\n // html2canvas doesn't properly handle flex align-items with SVGs\n const svgs = clonedElement.querySelectorAll(\"svg\");\n svgs.forEach((svg) => {\n svg.style.display = \"inline-block\";\n svg.style.verticalAlign = \"middle\";\n svg.style.flexShrink = \"0\";\n svg.style.marginRight = \"0.4rem\";\n svg.style.marginBottom = \"-1.2rem\";\n });\n\n // Fix line-clamp which causes text vertical cut-offs in html2canvas\n const clampElements = clonedElement.querySelectorAll(\n \".line-clamp-1, .line-clamp-2, .line-clamp-3\",\n );\n clampElements.forEach((el) => {\n if (el instanceof HTMLElement) {\n el.style.display = \"block\";\n el.style.webkitLineClamp = \"unset\";\n el.style.webkitBoxOrient = \"unset\";\n el.style.overflow = \"visible\";\n el.style.whiteSpace = \"normal\";\n el.style.lineHeight = \"1.5\";\n }\n });\n\n // Replace generic HTML elements like custom badges with simple spans\n // because html2canvas struggles with flexbox and strict borders inside round pills\n const badges = clonedElement.querySelectorAll(\".pdf-badge\");\n badges.forEach((el) => {\n if (el instanceof HTMLElement) {\n el.style.paddingTop = \"-10px\";\n el.style.display = \"block\";\n el.style.paddingTop = \"0px\";\n el.style.paddingBottom = \"0.8rem\";\n }\n });\n\n // Fix headers with bottom borders losing padding/margin\n const headersWithBorders = clonedElement.querySelectorAll(\"h1, h2, h3, h4\");\n headersWithBorders.forEach((el) => {\n if (el instanceof HTMLElement) {\n const hasBorder =\n el.className.includes(\"border-b\") || el.style.borderBottomWidth !== \"\";\n if (hasBorder) {\n el.style.paddingBottom = \"1.2rem\";\n }\n }\n });\n\n // Add margin top to pdf-line-v elements\n const verticalLines = clonedElement.querySelectorAll(\".pdf-line-v\");\n verticalLines.forEach((el) => {\n if (el instanceof HTMLElement) {\n el.style.marginTop = \"0.5rem\";\n el.style.zIndex = \"5\";\n }\n });\n\n // Add margin bottom to initials in header for better alignment in PDF\n const headerInitials = clonedElement.querySelectorAll(\".pdf-header-initials\");\n headerInitials.forEach((el) => {\n if (el instanceof HTMLElement) {\n el.style.paddingBottom = \"2rem\";\n }\n });\n\n // Add styles to summary for PDF\n const summaries = clonedElement.querySelectorAll(\".pdf-summary\");\n summaries.forEach((el) => {\n if (el instanceof HTMLElement) {\n el.style.width = \"100%\";\n el.style.maxWidth = \"100%\";\n el.style.marginTop = \"1.5rem\";\n }\n });\n\n // Show footer in PDF and force grayscale for the logo\n const pdfFooters = clonedElement.querySelectorAll(\".pdf-footer\");\n pdfFooters.forEach((el) => {\n if (el instanceof HTMLElement) {\n el.style.display = \"flex\";\n }\n });\n\n // Also make sure parent containers don't restrict height\n let parent = clonedElement.parentElement;\n while (parent) {\n parent.style.height = \"auto\";\n parent.style.overflow = \"visible\";\n parent = parent.parentElement;\n }\n }\n },\n });\n\n const imgData = canvas.toDataURL(\"image/jpeg\", 1);\n const imgWidthPx = canvas.width;\n const imgHeightPx = canvas.height;\n\n // Convert px to mm (at 96 DPI base * scale factor)\n const pxToMm = 25.4 / (96 * 2);\n const pdfWidth = imgWidthPx * pxToMm;\n const pdfHeight = imgHeightPx * pxToMm;\n\n const pdf = new jsPDF({\n orientation: pdfWidth > pdfHeight ? \"landscape\" : \"portrait\",\n unit: \"mm\",\n format: [pdfWidth, pdfHeight],\n });\n\n pdf.addImage(imgData, \"JPEG\", 0, 0, pdfWidth, pdfHeight, undefined, \"FAST\");\n\n const fileName = `CV_${data.first_name}_${data.last_name}`.replace(/\\s+/g, \"_\");\n pdf.save(`${fileName}.pdf`);\n } catch (error) {\n console.error(\"Error generating PDF:\", error);\n } finally {\n setIsDownloading(false);\n }\n };\n\n return (\n <div\n className=\"relative h-full w-full overflow-y-auto bg-muted/20 px-4 py-4 sm:px-8\"\n style={themeStyle}\n >\n {/* Preview Panel - Takes full width, centered like a document */}\n <div className=\"mx-auto h-full w-full max-w-5xl\">\n <ResumePreview\n ref={previewRef}\n data={draftData}\n headerSlot={headerSlot}\n theme={theme}\n hideFields={hideFields}\n hideSections={hideSections}\n showTopBar={showTopBar}\n className={className}\n />\n </div>\n\n {/* Floating Action Buttons */}\n <div className=\"fixed bottom-6 right-6 z-50 flex flex-col gap-3 sm:flex-row\">\n {editable && (\n <Sheet open={isOpen} onOpenChange={handleOpenChange}>\n <SheetTrigger asChild>\n <Button\n size=\"lg\"\n className=\"flex items-center gap-2 rounded-full bg-primary px-4 text-primary-foreground shadow-xl hover:bg-primary/90\"\n >\n <Edit className=\"h-5 w-5\" />\n <span className=\"hidden font-semibold sm:inline\">Editar</span>\n </Button>\n </SheetTrigger>\n {/* Drawer opens from the right side, taking good width */}\n <SheetContent\n side=\"right\"\n showCloseButton={false}\n className=\"flex w-full flex-col overflow-hidden bg-card p-0 sm:max-w-md md:max-w-2xl\"\n >\n {/* Header */}\n <div className=\"flex items-center justify-between p-6 pb-2\">\n <SheetTitle className=\"text-2xl font-bold text-foreground\">Editar</SheetTitle>\n <SheetClose className=\"rounded-full p-2 opacity-70 transition-colors hover:bg-muted hover:opacity-100\">\n <X className=\"h-6 w-6 text-foreground\" />\n <span className=\"sr-only\">Cerrar</span>\n </SheetClose>\n </div>\n\n {/* Scrollable editor content */}\n <div className=\"flex-1 overflow-y-auto px-6 pb-6 pt-2\">\n <ResumeEditor\n data={draftData}\n onChange={setDraftData}\n theme={theme}\n hideFields={hideFields}\n hideSections={hideSections}\n menuSlot={menuSlot}\n customMenuOnly={customMenuOnly}\n />\n </div>\n\n {/* Sticky save footer */}\n {onSave && (\n <div className=\"flex justify-end gap-3 p-4\">\n <SheetClose asChild>\n <Button variant=\"outline\">Cancelar</Button>\n </SheetClose>\n <Button onClick={handleSave} className=\"gap-2\">\n <Save className=\"h-4 w-4\" />\n Guardar\n </Button>\n </div>\n )}\n </SheetContent>\n </Sheet>\n )}\n\n {withDownload && (\n <Button\n size=\"lg\"\n variant=\"ghost\"\n disabled={isDownloading}\n onClick={handleDownload}\n className=\"flex items-center gap-2 rounded-full bg-purple-200 px-4 text-purple-100 text-primary shadow-xl\"\n >\n {isDownloading ? (\n <Loader2 className=\"h-5 w-5 animate-spin\" />\n ) : (\n <Download className=\"h-5 w-5\" />\n )}\n <span className=\"hidden font-semibold sm:inline\">\n {isDownloading ? \"Generando...\" : \"Descargar\"}\n </span>\n </Button>\n )}\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":["html2canvas","canvas","parent"]}
@@ -2,6 +2,6 @@ import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { ResumeProps } from './types.cjs';
3
3
  import 'react';
4
4
 
5
- declare function Resume({ data, editable, headerSlot, withDownload, theme, hideFields, hideSections, menuSlot, onSave, showTopBar, className, }: ResumeProps): react_jsx_runtime.JSX.Element;
5
+ declare function Resume({ data, editable, headerSlot, withDownload, theme, hideFields, hideSections, menuSlot, customMenuOnly, onSave, showTopBar, className, }: ResumeProps): react_jsx_runtime.JSX.Element;
6
6
 
7
7
  export { Resume };
@@ -2,6 +2,6 @@ import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { ResumeProps } from './types.js';
3
3
  import 'react';
4
4
 
5
- declare function Resume({ data, editable, headerSlot, withDownload, theme, hideFields, hideSections, menuSlot, onSave, showTopBar, className, }: ResumeProps): react_jsx_runtime.JSX.Element;
5
+ declare function Resume({ data, editable, headerSlot, withDownload, theme, hideFields, hideSections, menuSlot, customMenuOnly, onSave, showTopBar, className, }: ResumeProps): react_jsx_runtime.JSX.Element;
6
6
 
7
7
  export { Resume };
@@ -95,6 +95,7 @@ function Resume({
95
95
  hideFields = [],
96
96
  hideSections = [],
97
97
  menuSlot = [],
98
+ customMenuOnly = false,
98
99
  onSave,
99
100
  showTopBar = true,
100
101
  className
@@ -354,7 +355,8 @@ function Resume({
354
355
  theme,
355
356
  hideFields,
356
357
  hideSections,
357
- menuSlot
358
+ menuSlot,
359
+ customMenuOnly
358
360
  }
359
361
  ) }),
360
362
  onSave && /* @__PURE__ */ jsxs("div", { className: "flex justify-end gap-3 p-4", children: [
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/components/domain/resume/resume.tsx"],"sourcesContent":["import { useRef, useState } from \"react\";\nimport html2canvas from \"html2canvas\";\nimport { jsPDF } from \"jspdf\";\nimport { CandidateProfile, ResumeProps, ResumeSavePayload, Action } from \"./types\";\nimport { ResumeEditor } from \"./resume-editor/resume-editor\";\nimport { ResumePreview } from \"./resume-preview/resume-preview\";\nimport { Button } from \"../../ui/button\";\nimport { Download, Edit, Loader2, Save, X } from \"lucide-react\";\nimport { Sheet, SheetTrigger, SheetContent, SheetTitle, SheetClose } from \"../../ui/sheet\";\n\nfunction calculateDiffs(original: CandidateProfile, current: CandidateProfile): ResumeSavePayload {\n // Determine diff for candidate table (academic info etc)\n const candidateData: Partial<CandidateProfile> = {};\n if (original.student_education_institution_id !== current.student_education_institution_id)\n candidateData.student_education_institution_id = current.student_education_institution_id;\n if (original.education_institution_id !== current.education_institution_id)\n candidateData.education_institution_id = current.education_institution_id;\n if (original.program_name !== current.program_name)\n candidateData.program_name = current.program_name;\n if (JSON.stringify(original.preferences) !== JSON.stringify(current.preferences))\n candidateData.preferences = current.preferences;\n if (JSON.stringify(original.advanced) !== JSON.stringify(current.advanced))\n candidateData.advanced = current.advanced;\n\n // Determine diff for user table (personal info)\n const userData: Partial<CandidateProfile> = {};\n const userFields: (keyof CandidateProfile)[] = [\n \"first_name\",\n \"middle_name\",\n \"last_name\",\n \"second_last_name\",\n \"phone_number\",\n \"phone_number_2\",\n \"email\",\n \"city\",\n \"address\",\n \"postal_code\",\n \"birth_date\",\n \"photo_url\",\n ];\n userFields.forEach((field) => {\n if (original[field] !== current[field]) {\n // @ts-expect-error - jspdf types are a bit funky\n userData[field] = current[field];\n }\n });\n\n // Helper for array diffs\n const calculateArrayDiff = (origArray: any[] = [], currArray: any[] = []) => {\n const changes: any[] = [];\n const origMap = new Map(origArray.map((item) => [item.id, item]));\n\n currArray.forEach((currItem) => {\n const origItem = origMap.get(currItem.id);\n if (!origItem) {\n // New item\n changes.push({ action: \"create\" as Action, data: currItem });\n } else if (JSON.stringify(origItem) !== JSON.stringify(currItem)) {\n // Updated item\n changes.push({ action: \"update\" as Action, data: currItem, id: currItem.id });\n }\n });\n\n const currMap = new Map(currArray.map((item) => [item.id, item]));\n origArray.forEach((origItem) => {\n if (!currMap.has(origItem.id)) {\n // Deleted item\n changes.push({ action: \"delete\" as Action, id: origItem.id });\n }\n });\n\n return changes;\n };\n\n const experiencesDiff = [\n ...calculateArrayDiff(original.work_experience, current.work_experience),\n ...calculateArrayDiff(original.education, current.education),\n ...calculateArrayDiff(original.activities, current.activities),\n ];\n\n const hwOrig = original.skills?.hard || [];\n const hwCurr = current.skills?.hard || [];\n const swOrig = original.skills?.soft || [];\n const swCurr = current.skills?.soft || [];\n\n const skillsDiff = [...calculateArrayDiff(hwOrig, hwCurr), ...calculateArrayDiff(swOrig, swCurr)];\n\n return {\n candidate: {\n action: Object.keys(candidateData).length > 0 ? \"update\" : \"none\",\n data: candidateData,\n },\n user: {\n action: Object.keys(userData).length > 0 ? \"update\" : \"none\",\n data: userData,\n },\n curriculum: {\n action: original.summary !== current.summary ? \"update\" : \"none\",\n data: { summary: current.summary },\n },\n experiences: experiencesDiff,\n skills: skillsDiff,\n certifications: calculateArrayDiff(original.certifications, current.certifications),\n };\n}\n\nexport function Resume({\n data,\n editable = false,\n headerSlot,\n withDownload = false,\n theme = \"hsl(var(--secondary))\",\n hideFields = [],\n hideSections = [],\n menuSlot = [],\n onSave,\n showTopBar = true,\n className,\n}: ResumeProps) {\n const themeStyle = theme ? ({ \"--cv-theme\": theme } as React.CSSProperties) : undefined;\n const [draftData, setDraftData] = useState<CandidateProfile>(data);\n const [isOpen, setIsOpen] = useState(false);\n const [isDownloading, setIsDownloading] = useState(false);\n const previewRef = useRef<HTMLDivElement>(null);\n\n const handleOpenChange = (open: boolean) => {\n if (open) {\n setDraftData(data);\n }\n setIsOpen(open);\n };\n\n const handleSave = () => {\n if (onSave) {\n const payload = calculateDiffs(data, draftData);\n onSave(payload, draftData);\n }\n setIsOpen(false);\n };\n\n const handleDownload = async () => {\n if (!previewRef.current || isDownloading) return;\n setIsDownloading(true);\n\n try {\n const element = previewRef.current;\n const canvas = await html2canvas(element, {\n scale: 2,\n useCORS: true,\n backgroundColor: \"#ffffff\",\n logging: false,\n windowWidth: 1200, // Force desktop layout breakpoint\n onclone: (clonedDoc) => {\n const clonedElement = clonedDoc.getElementById(\"resume-capture-area\");\n if (clonedElement instanceof HTMLElement) {\n clonedElement.style.height = \"auto\";\n clonedElement.style.overflow = \"visible\";\n clonedElement.style.width = \"1200px\";\n\n // Remove border radius for elements with pdf-border-line class in PDF\n const borderLineElements = Array.from(clonedElement.querySelectorAll(\".pdf-border-line\"));\n if (clonedElement.classList.contains(\"pdf-border-line\")) {\n borderLineElements.push(clonedElement);\n }\n borderLineElements.forEach((el) => {\n if (el instanceof HTMLElement) {\n el.style.setProperty(\"border-radius\", \"0\", \"important\");\n }\n });\n\n // Fix Background: html2canvas doesn't support mask-image. Render gradient to canvas\n // and use as background-image for pixel-perfect PDF output.\n const backgroundGradients = clonedElement.querySelectorAll(\"[data-pdf-background-gradient]\");\n const w = 1200;\n const h = 160;\n const canvas = clonedDoc.createElement(\"canvas\");\n canvas.width = w;\n canvas.height = h;\n const ctx = canvas.getContext(\"2d\");\n if (ctx) {\n const hGrad = ctx.createLinearGradient(0, 0, w, 0);\n hGrad.addColorStop(0, \"#ffffff\");\n hGrad.addColorStop(0.5, \"#f3e8ff\");\n hGrad.addColorStop(1, \"#bfdbfe\");\n ctx.fillStyle = hGrad;\n ctx.fillRect(0, 0, w, h);\n ctx.globalCompositeOperation = \"destination-in\";\n const vGrad = ctx.createLinearGradient(0, 0, 0, h);\n vGrad.addColorStop(0, \"rgba(0,0,0,1)\");\n vGrad.addColorStop(1, \"rgba(0,0,0,0)\");\n ctx.fillStyle = vGrad;\n ctx.fillRect(0, 0, w, h);\n ctx.globalCompositeOperation = \"source-over\";\n }\n const dataUrl = canvas.toDataURL(\"image/png\");\n backgroundGradients.forEach((el) => {\n if (el instanceof HTMLElement) {\n el.style.maskImage = \"none\";\n el.style.background = \"none\";\n el.style.backgroundImage = `url(${dataUrl})`;\n el.style.backgroundSize = \"100% 100%\";\n el.style.backgroundPosition = \"0 0\";\n }\n });\n\n // Fix for images with object-fit: cover not being respected by html2canvas\n const objectCoverImages = clonedElement.querySelectorAll(\"img.object-cover\");\n objectCoverImages.forEach((img) => {\n if (img instanceof HTMLImageElement) {\n const parent = img.parentElement;\n if (parent) {\n // Replace img with a div that has background-image: cover\n const div = clonedDoc.createElement(\"div\");\n div.style.width = \"100%\";\n div.style.height = \"100%\";\n div.style.backgroundImage = `url(${img.src})`;\n div.style.backgroundSize = \"cover\";\n div.style.backgroundPosition = \"center\";\n div.style.borderRadius = window.getComputedStyle(img).borderRadius;\n div.className = img.className;\n parent.replaceChild(div, img);\n }\n }\n });\n\n // Fix Avatar alignment in PDF: apply consistent margin for both image and fallback\n const headerAvatars = clonedElement.querySelectorAll(\".pdf-header-avatar\");\n headerAvatars.forEach((el) => {\n if (el instanceof HTMLElement) {\n el.style.marginTop = \"1.33rem\";\n }\n });\n\n // Hide elements marked as hidden for PDF\n const hiddenElements = clonedElement.querySelectorAll(\".pdf-hidden\");\n hiddenElements.forEach((el) => {\n if (el instanceof HTMLElement) {\n el.style.display = \"none\";\n }\n });\n\n // Fix SVG vertical alignment for html2canvas\n // html2canvas doesn't properly handle flex align-items with SVGs\n const svgs = clonedElement.querySelectorAll(\"svg\");\n svgs.forEach((svg) => {\n svg.style.display = \"inline-block\";\n svg.style.verticalAlign = \"middle\";\n svg.style.flexShrink = \"0\";\n svg.style.marginRight = \"0.4rem\";\n svg.style.marginBottom = \"-1.2rem\";\n });\n\n // Fix line-clamp which causes text vertical cut-offs in html2canvas\n const clampElements = clonedElement.querySelectorAll(\n \".line-clamp-1, .line-clamp-2, .line-clamp-3\",\n );\n clampElements.forEach((el) => {\n if (el instanceof HTMLElement) {\n el.style.display = \"block\";\n el.style.webkitLineClamp = \"unset\";\n el.style.webkitBoxOrient = \"unset\";\n el.style.overflow = \"visible\";\n el.style.whiteSpace = \"normal\";\n el.style.lineHeight = \"1.5\";\n }\n });\n\n // Replace generic HTML elements like custom badges with simple spans\n // because html2canvas struggles with flexbox and strict borders inside round pills\n const badges = clonedElement.querySelectorAll(\".pdf-badge\");\n badges.forEach((el) => {\n if (el instanceof HTMLElement) {\n el.style.paddingTop = \"-10px\";\n el.style.display = \"block\";\n el.style.paddingTop = \"0px\";\n el.style.paddingBottom = \"0.8rem\";\n }\n });\n\n // Fix headers with bottom borders losing padding/margin\n const headersWithBorders = clonedElement.querySelectorAll(\"h1, h2, h3, h4\");\n headersWithBorders.forEach((el) => {\n if (el instanceof HTMLElement) {\n const hasBorder =\n el.className.includes(\"border-b\") || el.style.borderBottomWidth !== \"\";\n if (hasBorder) {\n el.style.paddingBottom = \"1.2rem\";\n }\n }\n });\n\n // Add margin top to pdf-line-v elements\n const verticalLines = clonedElement.querySelectorAll(\".pdf-line-v\");\n verticalLines.forEach((el) => {\n if (el instanceof HTMLElement) {\n el.style.marginTop = \"0.5rem\";\n el.style.zIndex = \"5\";\n }\n });\n\n // Add margin bottom to initials in header for better alignment in PDF\n const headerInitials = clonedElement.querySelectorAll(\".pdf-header-initials\");\n headerInitials.forEach((el) => {\n if (el instanceof HTMLElement) {\n el.style.paddingBottom = \"2rem\";\n }\n });\n\n // Add styles to summary for PDF\n const summaries = clonedElement.querySelectorAll(\".pdf-summary\");\n summaries.forEach((el) => {\n if (el instanceof HTMLElement) {\n el.style.width = \"100%\";\n el.style.maxWidth = \"100%\";\n el.style.marginTop = \"1.5rem\";\n }\n });\n\n // Show footer in PDF and force grayscale for the logo\n const pdfFooters = clonedElement.querySelectorAll(\".pdf-footer\");\n pdfFooters.forEach((el) => {\n if (el instanceof HTMLElement) {\n el.style.display = \"flex\";\n }\n });\n\n // Also make sure parent containers don't restrict height\n let parent = clonedElement.parentElement;\n while (parent) {\n parent.style.height = \"auto\";\n parent.style.overflow = \"visible\";\n parent = parent.parentElement;\n }\n }\n },\n });\n\n const imgData = canvas.toDataURL(\"image/jpeg\", 1);\n const imgWidthPx = canvas.width;\n const imgHeightPx = canvas.height;\n\n // Convert px to mm (at 96 DPI base * scale factor)\n const pxToMm = 25.4 / (96 * 2);\n const pdfWidth = imgWidthPx * pxToMm;\n const pdfHeight = imgHeightPx * pxToMm;\n\n const pdf = new jsPDF({\n orientation: pdfWidth > pdfHeight ? \"landscape\" : \"portrait\",\n unit: \"mm\",\n format: [pdfWidth, pdfHeight],\n });\n\n pdf.addImage(imgData, \"JPEG\", 0, 0, pdfWidth, pdfHeight, undefined, \"FAST\");\n\n const fileName = `CV_${data.first_name}_${data.last_name}`.replace(/\\s+/g, \"_\");\n pdf.save(`${fileName}.pdf`);\n } catch (error) {\n console.error(\"Error generating PDF:\", error);\n } finally {\n setIsDownloading(false);\n }\n };\n\n return (\n <div\n className=\"relative h-full w-full overflow-y-auto bg-muted/20 px-4 py-4 sm:px-8\"\n style={themeStyle}\n >\n {/* Preview Panel - Takes full width, centered like a document */}\n <div className=\"mx-auto h-full w-full max-w-5xl\">\n <ResumePreview\n ref={previewRef}\n data={draftData}\n headerSlot={headerSlot}\n theme={theme}\n hideFields={hideFields}\n hideSections={hideSections}\n showTopBar={showTopBar}\n className={className}\n />\n </div>\n\n {/* Floating Action Buttons */}\n <div className=\"fixed bottom-6 right-6 z-50 flex flex-col gap-3 sm:flex-row\">\n {editable && (\n <Sheet open={isOpen} onOpenChange={handleOpenChange}>\n <SheetTrigger asChild>\n <Button\n size=\"lg\"\n className=\"flex items-center gap-2 rounded-full bg-primary px-4 text-primary-foreground shadow-xl hover:bg-primary/90\"\n >\n <Edit className=\"h-5 w-5\" />\n <span className=\"hidden font-semibold sm:inline\">Editar</span>\n </Button>\n </SheetTrigger>\n {/* Drawer opens from the right side, taking good width */}\n <SheetContent\n side=\"right\"\n showCloseButton={false}\n className=\"flex w-full flex-col overflow-hidden bg-card p-0 sm:max-w-md md:max-w-2xl\"\n >\n {/* Header */}\n <div className=\"flex items-center justify-between p-6 pb-2\">\n <SheetTitle className=\"text-2xl font-bold text-foreground\">Editar</SheetTitle>\n <SheetClose className=\"rounded-full p-2 opacity-70 transition-colors hover:bg-muted hover:opacity-100\">\n <X className=\"h-6 w-6 text-foreground\" />\n <span className=\"sr-only\">Cerrar</span>\n </SheetClose>\n </div>\n\n {/* Scrollable editor content */}\n <div className=\"flex-1 overflow-y-auto px-6 pb-6 pt-2\">\n <ResumeEditor\n data={draftData}\n onChange={setDraftData}\n theme={theme}\n hideFields={hideFields}\n hideSections={hideSections}\n menuSlot={menuSlot}\n />\n </div>\n\n {/* Sticky save footer */}\n {onSave && (\n <div className=\"flex justify-end gap-3 p-4\">\n <SheetClose asChild>\n <Button variant=\"outline\">Cancelar</Button>\n </SheetClose>\n <Button onClick={handleSave} className=\"gap-2\">\n <Save className=\"h-4 w-4\" />\n Guardar\n </Button>\n </div>\n )}\n </SheetContent>\n </Sheet>\n )}\n\n {withDownload && (\n <Button\n size=\"lg\"\n variant=\"ghost\"\n disabled={isDownloading}\n onClick={handleDownload}\n className=\"flex items-center gap-2 rounded-full bg-purple-200 px-4 text-purple-100 text-primary shadow-xl\"\n >\n {isDownloading ? (\n <Loader2 className=\"h-5 w-5 animate-spin\" />\n ) : (\n <Download className=\"h-5 w-5\" />\n )}\n <span className=\"hidden font-semibold sm:inline\">\n {isDownloading ? \"Generando...\" : \"Descargar\"}\n </span>\n </Button>\n )}\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":["canvas","parent"]}
1
+ {"version":3,"sources":["../../../../src/components/domain/resume/resume.tsx"],"sourcesContent":["import { useRef, useState } from \"react\";\nimport html2canvas from \"html2canvas\";\nimport { jsPDF } from \"jspdf\";\nimport { CandidateProfile, ResumeProps, ResumeSavePayload, Action } from \"./types\";\nimport { ResumeEditor } from \"./resume-editor/resume-editor\";\nimport { ResumePreview } from \"./resume-preview/resume-preview\";\nimport { Button } from \"../../ui/button\";\nimport { Download, Edit, Loader2, Save, X } from \"lucide-react\";\nimport { Sheet, SheetTrigger, SheetContent, SheetTitle, SheetClose } from \"../../ui/sheet\";\n\nfunction calculateDiffs(original: CandidateProfile, current: CandidateProfile): ResumeSavePayload {\n // Determine diff for candidate table (academic info etc)\n const candidateData: Partial<CandidateProfile> = {};\n if (original.student_education_institution_id !== current.student_education_institution_id)\n candidateData.student_education_institution_id = current.student_education_institution_id;\n if (original.education_institution_id !== current.education_institution_id)\n candidateData.education_institution_id = current.education_institution_id;\n if (original.program_name !== current.program_name)\n candidateData.program_name = current.program_name;\n if (JSON.stringify(original.preferences) !== JSON.stringify(current.preferences))\n candidateData.preferences = current.preferences;\n if (JSON.stringify(original.advanced) !== JSON.stringify(current.advanced))\n candidateData.advanced = current.advanced;\n\n // Determine diff for user table (personal info)\n const userData: Partial<CandidateProfile> = {};\n const userFields: (keyof CandidateProfile)[] = [\n \"first_name\",\n \"middle_name\",\n \"last_name\",\n \"second_last_name\",\n \"phone_number\",\n \"phone_number_2\",\n \"email\",\n \"city\",\n \"address\",\n \"postal_code\",\n \"birth_date\",\n \"photo_url\",\n ];\n userFields.forEach((field) => {\n if (original[field] !== current[field]) {\n // @ts-expect-error - jspdf types are a bit funky\n userData[field] = current[field];\n }\n });\n\n // Helper for array diffs\n const calculateArrayDiff = (origArray: any[] = [], currArray: any[] = []) => {\n const changes: any[] = [];\n const origMap = new Map(origArray.map((item) => [item.id, item]));\n\n currArray.forEach((currItem) => {\n const origItem = origMap.get(currItem.id);\n if (!origItem) {\n // New item\n changes.push({ action: \"create\" as Action, data: currItem });\n } else if (JSON.stringify(origItem) !== JSON.stringify(currItem)) {\n // Updated item\n changes.push({ action: \"update\" as Action, data: currItem, id: currItem.id });\n }\n });\n\n const currMap = new Map(currArray.map((item) => [item.id, item]));\n origArray.forEach((origItem) => {\n if (!currMap.has(origItem.id)) {\n // Deleted item\n changes.push({ action: \"delete\" as Action, id: origItem.id });\n }\n });\n\n return changes;\n };\n\n const experiencesDiff = [\n ...calculateArrayDiff(original.work_experience, current.work_experience),\n ...calculateArrayDiff(original.education, current.education),\n ...calculateArrayDiff(original.activities, current.activities),\n ];\n\n const hwOrig = original.skills?.hard || [];\n const hwCurr = current.skills?.hard || [];\n const swOrig = original.skills?.soft || [];\n const swCurr = current.skills?.soft || [];\n\n const skillsDiff = [...calculateArrayDiff(hwOrig, hwCurr), ...calculateArrayDiff(swOrig, swCurr)];\n\n return {\n candidate: {\n action: Object.keys(candidateData).length > 0 ? \"update\" : \"none\",\n data: candidateData,\n },\n user: {\n action: Object.keys(userData).length > 0 ? \"update\" : \"none\",\n data: userData,\n },\n curriculum: {\n action: original.summary !== current.summary ? \"update\" : \"none\",\n data: { summary: current.summary },\n },\n experiences: experiencesDiff,\n skills: skillsDiff,\n certifications: calculateArrayDiff(original.certifications, current.certifications),\n };\n}\n\nexport function Resume({\n data,\n editable = false,\n headerSlot,\n withDownload = false,\n theme = \"hsl(var(--secondary))\",\n hideFields = [],\n hideSections = [],\n menuSlot = [],\n customMenuOnly = false,\n onSave,\n showTopBar = true,\n className,\n}: ResumeProps) {\n const themeStyle = theme ? ({ \"--cv-theme\": theme } as React.CSSProperties) : undefined;\n const [draftData, setDraftData] = useState<CandidateProfile>(data);\n const [isOpen, setIsOpen] = useState(false);\n const [isDownloading, setIsDownloading] = useState(false);\n const previewRef = useRef<HTMLDivElement>(null);\n\n const handleOpenChange = (open: boolean) => {\n if (open) {\n setDraftData(data);\n }\n setIsOpen(open);\n };\n\n const handleSave = () => {\n if (onSave) {\n const payload = calculateDiffs(data, draftData);\n onSave(payload, draftData);\n }\n setIsOpen(false);\n };\n\n const handleDownload = async () => {\n if (!previewRef.current || isDownloading) return;\n setIsDownloading(true);\n\n try {\n const element = previewRef.current;\n const canvas = await html2canvas(element, {\n scale: 2,\n useCORS: true,\n backgroundColor: \"#ffffff\",\n logging: false,\n windowWidth: 1200, // Force desktop layout breakpoint\n onclone: (clonedDoc) => {\n const clonedElement = clonedDoc.getElementById(\"resume-capture-area\");\n if (clonedElement instanceof HTMLElement) {\n clonedElement.style.height = \"auto\";\n clonedElement.style.overflow = \"visible\";\n clonedElement.style.width = \"1200px\";\n\n // Remove border radius for elements with pdf-border-line class in PDF\n const borderLineElements = Array.from(clonedElement.querySelectorAll(\".pdf-border-line\"));\n if (clonedElement.classList.contains(\"pdf-border-line\")) {\n borderLineElements.push(clonedElement);\n }\n borderLineElements.forEach((el) => {\n if (el instanceof HTMLElement) {\n el.style.setProperty(\"border-radius\", \"0\", \"important\");\n }\n });\n\n // Fix Background: html2canvas doesn't support mask-image. Render gradient to canvas\n // and use as background-image for pixel-perfect PDF output.\n const backgroundGradients = clonedElement.querySelectorAll(\"[data-pdf-background-gradient]\");\n const w = 1200;\n const h = 160;\n const canvas = clonedDoc.createElement(\"canvas\");\n canvas.width = w;\n canvas.height = h;\n const ctx = canvas.getContext(\"2d\");\n if (ctx) {\n const hGrad = ctx.createLinearGradient(0, 0, w, 0);\n hGrad.addColorStop(0, \"#ffffff\");\n hGrad.addColorStop(0.5, \"#f3e8ff\");\n hGrad.addColorStop(1, \"#bfdbfe\");\n ctx.fillStyle = hGrad;\n ctx.fillRect(0, 0, w, h);\n ctx.globalCompositeOperation = \"destination-in\";\n const vGrad = ctx.createLinearGradient(0, 0, 0, h);\n vGrad.addColorStop(0, \"rgba(0,0,0,1)\");\n vGrad.addColorStop(1, \"rgba(0,0,0,0)\");\n ctx.fillStyle = vGrad;\n ctx.fillRect(0, 0, w, h);\n ctx.globalCompositeOperation = \"source-over\";\n }\n const dataUrl = canvas.toDataURL(\"image/png\");\n backgroundGradients.forEach((el) => {\n if (el instanceof HTMLElement) {\n el.style.maskImage = \"none\";\n el.style.background = \"none\";\n el.style.backgroundImage = `url(${dataUrl})`;\n el.style.backgroundSize = \"100% 100%\";\n el.style.backgroundPosition = \"0 0\";\n }\n });\n\n // Fix for images with object-fit: cover not being respected by html2canvas\n const objectCoverImages = clonedElement.querySelectorAll(\"img.object-cover\");\n objectCoverImages.forEach((img) => {\n if (img instanceof HTMLImageElement) {\n const parent = img.parentElement;\n if (parent) {\n // Replace img with a div that has background-image: cover\n const div = clonedDoc.createElement(\"div\");\n div.style.width = \"100%\";\n div.style.height = \"100%\";\n div.style.backgroundImage = `url(${img.src})`;\n div.style.backgroundSize = \"cover\";\n div.style.backgroundPosition = \"center\";\n div.style.borderRadius = window.getComputedStyle(img).borderRadius;\n div.className = img.className;\n parent.replaceChild(div, img);\n }\n }\n });\n\n // Fix Avatar alignment in PDF: apply consistent margin for both image and fallback\n const headerAvatars = clonedElement.querySelectorAll(\".pdf-header-avatar\");\n headerAvatars.forEach((el) => {\n if (el instanceof HTMLElement) {\n el.style.marginTop = \"1.33rem\";\n }\n });\n\n // Hide elements marked as hidden for PDF\n const hiddenElements = clonedElement.querySelectorAll(\".pdf-hidden\");\n hiddenElements.forEach((el) => {\n if (el instanceof HTMLElement) {\n el.style.display = \"none\";\n }\n });\n\n // Fix SVG vertical alignment for html2canvas\n // html2canvas doesn't properly handle flex align-items with SVGs\n const svgs = clonedElement.querySelectorAll(\"svg\");\n svgs.forEach((svg) => {\n svg.style.display = \"inline-block\";\n svg.style.verticalAlign = \"middle\";\n svg.style.flexShrink = \"0\";\n svg.style.marginRight = \"0.4rem\";\n svg.style.marginBottom = \"-1.2rem\";\n });\n\n // Fix line-clamp which causes text vertical cut-offs in html2canvas\n const clampElements = clonedElement.querySelectorAll(\n \".line-clamp-1, .line-clamp-2, .line-clamp-3\",\n );\n clampElements.forEach((el) => {\n if (el instanceof HTMLElement) {\n el.style.display = \"block\";\n el.style.webkitLineClamp = \"unset\";\n el.style.webkitBoxOrient = \"unset\";\n el.style.overflow = \"visible\";\n el.style.whiteSpace = \"normal\";\n el.style.lineHeight = \"1.5\";\n }\n });\n\n // Replace generic HTML elements like custom badges with simple spans\n // because html2canvas struggles with flexbox and strict borders inside round pills\n const badges = clonedElement.querySelectorAll(\".pdf-badge\");\n badges.forEach((el) => {\n if (el instanceof HTMLElement) {\n el.style.paddingTop = \"-10px\";\n el.style.display = \"block\";\n el.style.paddingTop = \"0px\";\n el.style.paddingBottom = \"0.8rem\";\n }\n });\n\n // Fix headers with bottom borders losing padding/margin\n const headersWithBorders = clonedElement.querySelectorAll(\"h1, h2, h3, h4\");\n headersWithBorders.forEach((el) => {\n if (el instanceof HTMLElement) {\n const hasBorder =\n el.className.includes(\"border-b\") || el.style.borderBottomWidth !== \"\";\n if (hasBorder) {\n el.style.paddingBottom = \"1.2rem\";\n }\n }\n });\n\n // Add margin top to pdf-line-v elements\n const verticalLines = clonedElement.querySelectorAll(\".pdf-line-v\");\n verticalLines.forEach((el) => {\n if (el instanceof HTMLElement) {\n el.style.marginTop = \"0.5rem\";\n el.style.zIndex = \"5\";\n }\n });\n\n // Add margin bottom to initials in header for better alignment in PDF\n const headerInitials = clonedElement.querySelectorAll(\".pdf-header-initials\");\n headerInitials.forEach((el) => {\n if (el instanceof HTMLElement) {\n el.style.paddingBottom = \"2rem\";\n }\n });\n\n // Add styles to summary for PDF\n const summaries = clonedElement.querySelectorAll(\".pdf-summary\");\n summaries.forEach((el) => {\n if (el instanceof HTMLElement) {\n el.style.width = \"100%\";\n el.style.maxWidth = \"100%\";\n el.style.marginTop = \"1.5rem\";\n }\n });\n\n // Show footer in PDF and force grayscale for the logo\n const pdfFooters = clonedElement.querySelectorAll(\".pdf-footer\");\n pdfFooters.forEach((el) => {\n if (el instanceof HTMLElement) {\n el.style.display = \"flex\";\n }\n });\n\n // Also make sure parent containers don't restrict height\n let parent = clonedElement.parentElement;\n while (parent) {\n parent.style.height = \"auto\";\n parent.style.overflow = \"visible\";\n parent = parent.parentElement;\n }\n }\n },\n });\n\n const imgData = canvas.toDataURL(\"image/jpeg\", 1);\n const imgWidthPx = canvas.width;\n const imgHeightPx = canvas.height;\n\n // Convert px to mm (at 96 DPI base * scale factor)\n const pxToMm = 25.4 / (96 * 2);\n const pdfWidth = imgWidthPx * pxToMm;\n const pdfHeight = imgHeightPx * pxToMm;\n\n const pdf = new jsPDF({\n orientation: pdfWidth > pdfHeight ? \"landscape\" : \"portrait\",\n unit: \"mm\",\n format: [pdfWidth, pdfHeight],\n });\n\n pdf.addImage(imgData, \"JPEG\", 0, 0, pdfWidth, pdfHeight, undefined, \"FAST\");\n\n const fileName = `CV_${data.first_name}_${data.last_name}`.replace(/\\s+/g, \"_\");\n pdf.save(`${fileName}.pdf`);\n } catch (error) {\n console.error(\"Error generating PDF:\", error);\n } finally {\n setIsDownloading(false);\n }\n };\n\n return (\n <div\n className=\"relative h-full w-full overflow-y-auto bg-muted/20 px-4 py-4 sm:px-8\"\n style={themeStyle}\n >\n {/* Preview Panel - Takes full width, centered like a document */}\n <div className=\"mx-auto h-full w-full max-w-5xl\">\n <ResumePreview\n ref={previewRef}\n data={draftData}\n headerSlot={headerSlot}\n theme={theme}\n hideFields={hideFields}\n hideSections={hideSections}\n showTopBar={showTopBar}\n className={className}\n />\n </div>\n\n {/* Floating Action Buttons */}\n <div className=\"fixed bottom-6 right-6 z-50 flex flex-col gap-3 sm:flex-row\">\n {editable && (\n <Sheet open={isOpen} onOpenChange={handleOpenChange}>\n <SheetTrigger asChild>\n <Button\n size=\"lg\"\n className=\"flex items-center gap-2 rounded-full bg-primary px-4 text-primary-foreground shadow-xl hover:bg-primary/90\"\n >\n <Edit className=\"h-5 w-5\" />\n <span className=\"hidden font-semibold sm:inline\">Editar</span>\n </Button>\n </SheetTrigger>\n {/* Drawer opens from the right side, taking good width */}\n <SheetContent\n side=\"right\"\n showCloseButton={false}\n className=\"flex w-full flex-col overflow-hidden bg-card p-0 sm:max-w-md md:max-w-2xl\"\n >\n {/* Header */}\n <div className=\"flex items-center justify-between p-6 pb-2\">\n <SheetTitle className=\"text-2xl font-bold text-foreground\">Editar</SheetTitle>\n <SheetClose className=\"rounded-full p-2 opacity-70 transition-colors hover:bg-muted hover:opacity-100\">\n <X className=\"h-6 w-6 text-foreground\" />\n <span className=\"sr-only\">Cerrar</span>\n </SheetClose>\n </div>\n\n {/* Scrollable editor content */}\n <div className=\"flex-1 overflow-y-auto px-6 pb-6 pt-2\">\n <ResumeEditor\n data={draftData}\n onChange={setDraftData}\n theme={theme}\n hideFields={hideFields}\n hideSections={hideSections}\n menuSlot={menuSlot}\n customMenuOnly={customMenuOnly}\n />\n </div>\n\n {/* Sticky save footer */}\n {onSave && (\n <div className=\"flex justify-end gap-3 p-4\">\n <SheetClose asChild>\n <Button variant=\"outline\">Cancelar</Button>\n </SheetClose>\n <Button onClick={handleSave} className=\"gap-2\">\n <Save className=\"h-4 w-4\" />\n Guardar\n </Button>\n </div>\n )}\n </SheetContent>\n </Sheet>\n )}\n\n {withDownload && (\n <Button\n size=\"lg\"\n variant=\"ghost\"\n disabled={isDownloading}\n onClick={handleDownload}\n className=\"flex items-center gap-2 rounded-full bg-purple-200 px-4 text-purple-100 text-primary shadow-xl\"\n >\n {isDownloading ? (\n <Loader2 className=\"h-5 w-5 animate-spin\" />\n ) : (\n <Download className=\"h-5 w-5\" />\n )}\n <span className=\"hidden font-semibold sm:inline\">\n {isDownloading ? \"Generando...\" : \"Descargar\"}\n </span>\n </Button>\n )}\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":["canvas","parent"]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/components/domain/resume/types.ts"],"sourcesContent":["import { ReactNode } from \"react\";\n\nexport type Action = \"create\" | \"update\" | \"delete\" | \"none\";\n\nexport interface CVExperienceBaseItem {\n id: string;\n title: string;\n type: string;\n institution_name?: string;\n institution_id?: string;\n start_date: string;\n end_date?: string;\n description?: string;\n}\n\nexport interface CVEducationItem extends CVExperienceBaseItem {\n _type?: \"education\";\n}\nexport interface CVExperienceItem extends CVExperienceBaseItem {\n _type?: \"experience\";\n}\nexport interface CVActivityItem extends CVExperienceBaseItem {\n _type?: \"activity\";\n}\n\nexport interface ExperienceWithAction extends CVExperienceBaseItem {\n action?: Action;\n}\n\nexport interface CVSkillItem {\n id: string;\n name: string;\n verified?: boolean;\n type?: string;\n}\n\nexport interface SkillWithAction extends CVSkillItem {\n action?: Action;\n}\n\nexport interface CVCertificationItem {\n id: string;\n title: string;\n institution_name?: string;\n institution_id?: string;\n date_awarded?: string;\n verified?: boolean;\n url?: string;\n}\n\nexport interface CertificationWithAction extends CVCertificationItem {\n action?: Action;\n}\n\nexport interface CVDocumentItem {\n id: string;\n document_type?: string;\n document_status?: string;\n document_url?: string;\n appointment_date?: string;\n comments?: string;\n}\n\nexport interface DocumentWithAction extends CVDocumentItem {\n action?: Action;\n}\n\nexport interface CandidateProfile {\n // Información personal (from user table)\n photo_url?: string;\n first_name: string;\n middle_name?: string;\n last_name: string;\n second_last_name?: string;\n phone_number?: string;\n phone_number_2?: string;\n email?: string;\n city?: string;\n address?: string;\n postal_code?: string;\n birth_date?: string;\n\n // Curriculum (from curriculum table)\n summary?: string;\n last_updated?: string; // ISO date or formatted string, e.g. \"2025-02-26\"\n\n // Información Académica (from candidate table)\n student_education_institution_id?: string;\n education_institution_id?: string; // added this too for autocomplete logic\n program_name?: string;\n\n // Listados estructurados (from experience table, filtered by type)\n education?: CVEducationItem[];\n work_experience?: CVExperienceItem[];\n activities?: CVActivityItem[];\n\n // Habilidades (from skill table, grouped by type: hard/soft)\n skills?: {\n hard?: CVSkillItem[];\n soft?: CVSkillItem[];\n };\n\n // Certificaciones (from certification table)\n certifications?: CVCertificationItem[];\n\n // Requisitos laborales (from document table)\n work_requirements?: {\n documents: CVDocumentItem[];\n };\n\n // Preferencias (from candidate table fields)\n preferences?: {\n candidate_state?: string;\n employment_preference?: string;\n work_study_preference?: string;\n min_expected_salary?: number;\n max_expected_salary?: number;\n desired_role?: string;\n dream_job?: string;\n english_job_interest?: boolean;\n };\n\n // Avanzado\n advanced?: {\n notes?: string;\n };\n}\n\nexport interface ResumeSkill {\n id: string;\n name: string;\n type: \"Técnica\" | \"Interpersonal\";\n verified: boolean;\n}\nexport interface ResumeExperience {\n id: string;\n}\nexport interface ResumeEducation {\n id: string;\n}\nexport interface ResumeAward {\n id: string;\n}\n\nexport interface ResumeMenuSlot {\n title: string;\n node: ReactNode;\n}\n\nexport interface ResumeSavePayload {\n candidate: {\n action: Action;\n data: Partial<CandidateProfile>;\n };\n user: {\n action: Action;\n data: Partial<CandidateProfile>;\n };\n curriculum: {\n action: Action;\n data: {\n summary?: string;\n };\n };\n experiences: {\n action: Action;\n data: Partial<CVExperienceBaseItem>;\n id?: string;\n }[];\n skills: { action: Action; data: Partial<CVSkillItem>; id?: string }[];\n certifications: {\n action: Action;\n data: Partial<CVCertificationItem>;\n id?: string;\n }[];\n}\n\nexport interface ResumeProps {\n /**\n * Data del CV del candidato.\n */\n data: CandidateProfile;\n /**\n * Determina si el panel de edición (botón y drawer) es visible.\n * @default false\n */\n editable?: boolean;\n /**\n * Opciones u acciones extra a renderizar en la cabecera del preview.\n */\n headerSlot?: ReactNode;\n /**\n * Activa el botón flotante de descargar PDF.\n * @default false\n */\n withDownload?: boolean;\n /**\n * Color del tema (Hex, RGB o CSS color) usado para destacar el header, detalles y borde del avatar.\n * @default undefined (usa el primario de ginia-ui)\n */\n theme?: string;\n /**\n * Arreglo con la ruta de los campos a ocultar del editor y preview.\n * Por ejemplo: [\"first_name\", \"education.title\", \"skills.hard.verified\"]\n */\n hideFields?: string[];\n /**\n * Arreglo con los nombres de las secciones a ocultar completamente del editor y preview.\n * Por ejemplo: [\"Avanzado\", \"Preferencias\", \"Educación\"]\n */\n hideSections?: string[];\n /**\n * Slots adicionales para el menú del editor (Accordion).\n */\n menuSlot?: ResumeMenuSlot[];\n /**\n * Callback al guardar los cambios del editor.\n * Recibe el CandidateProfile actualizado y el array estructurado guardado por tipo de items\n */\n onSave?: (payload: ResumeSavePayload, fullProfile: CandidateProfile) => void;\n /**\n * Muestra u oculta la raya superior del preview.\n * @default true\n */\n showTopBar?: boolean;\n /**\n * Clases CSS adicionales para el contenedor del preview.\n */\n className?: string;\n}\n"],"mappings":";;;;;;;;;;;;;;AAAA;AAAA;","names":[]}
1
+ {"version":3,"sources":["../../../../src/components/domain/resume/types.ts"],"sourcesContent":["import { ReactNode } from \"react\";\n\nexport type Action = \"create\" | \"update\" | \"delete\" | \"none\";\n\nexport interface CVExperienceBaseItem {\n id: string;\n title: string;\n type: string;\n institution_name?: string;\n institution_id?: string;\n start_date: string;\n end_date?: string;\n description?: string;\n}\n\nexport interface CVEducationItem extends CVExperienceBaseItem {\n _type?: \"education\";\n}\nexport interface CVExperienceItem extends CVExperienceBaseItem {\n _type?: \"experience\";\n}\nexport interface CVActivityItem extends CVExperienceBaseItem {\n _type?: \"activity\";\n}\n\nexport interface ExperienceWithAction extends CVExperienceBaseItem {\n action?: Action;\n}\n\nexport interface CVSkillItem {\n id: string;\n name: string;\n verified?: boolean;\n type?: string;\n}\n\nexport interface SkillWithAction extends CVSkillItem {\n action?: Action;\n}\n\nexport interface CVCertificationItem {\n id: string;\n title: string;\n institution_name?: string;\n institution_id?: string;\n date_awarded?: string;\n verified?: boolean;\n url?: string;\n}\n\nexport interface CertificationWithAction extends CVCertificationItem {\n action?: Action;\n}\n\nexport interface CVDocumentItem {\n id: string;\n document_type?: string;\n document_status?: string;\n document_url?: string;\n appointment_date?: string;\n comments?: string;\n}\n\nexport interface DocumentWithAction extends CVDocumentItem {\n action?: Action;\n}\n\nexport interface CandidateProfile {\n // Información personal (from user table)\n photo_url?: string;\n first_name: string;\n middle_name?: string;\n last_name: string;\n second_last_name?: string;\n phone_number?: string;\n phone_number_2?: string;\n email?: string;\n city?: string;\n address?: string;\n postal_code?: string;\n birth_date?: string;\n\n // Curriculum (from curriculum table)\n summary?: string;\n last_updated?: string; // ISO date or formatted string, e.g. \"2025-02-26\"\n\n // Información Académica (from candidate table)\n student_education_institution_id?: string;\n education_institution_id?: string; // added this too for autocomplete logic\n program_name?: string;\n\n // Listados estructurados (from experience table, filtered by type)\n education?: CVEducationItem[];\n work_experience?: CVExperienceItem[];\n activities?: CVActivityItem[];\n\n // Habilidades (from skill table, grouped by type: hard/soft)\n skills?: {\n hard?: CVSkillItem[];\n soft?: CVSkillItem[];\n };\n\n // Certificaciones (from certification table)\n certifications?: CVCertificationItem[];\n\n // Requisitos laborales (from document table)\n work_requirements?: {\n documents: CVDocumentItem[];\n };\n\n // Preferencias (from candidate table fields)\n preferences?: {\n candidate_state?: string;\n employment_preference?: string;\n work_study_preference?: string;\n min_expected_salary?: number;\n max_expected_salary?: number;\n desired_role?: string;\n dream_job?: string;\n english_job_interest?: boolean;\n };\n\n // Avanzado\n advanced?: {\n notes?: string;\n };\n}\n\nexport interface ResumeSkill {\n id: string;\n name: string;\n type: \"Técnica\" | \"Interpersonal\";\n verified: boolean;\n}\nexport interface ResumeExperience {\n id: string;\n}\nexport interface ResumeEducation {\n id: string;\n}\nexport interface ResumeAward {\n id: string;\n}\n\nexport interface ResumeMenuSlot {\n title: string;\n node: ReactNode;\n}\n\nexport interface ResumeSavePayload {\n candidate: {\n action: Action;\n data: Partial<CandidateProfile>;\n };\n user: {\n action: Action;\n data: Partial<CandidateProfile>;\n };\n curriculum: {\n action: Action;\n data: {\n summary?: string;\n };\n };\n experiences: {\n action: Action;\n data: Partial<CVExperienceBaseItem>;\n id?: string;\n }[];\n skills: { action: Action; data: Partial<CVSkillItem>; id?: string }[];\n certifications: {\n action: Action;\n data: Partial<CVCertificationItem>;\n id?: string;\n }[];\n}\n\nexport interface ResumeProps {\n /**\n * Data del CV del candidato.\n */\n data: CandidateProfile;\n /**\n * Determina si el panel de edición (botón y drawer) es visible.\n * @default false\n */\n editable?: boolean;\n /**\n * Opciones u acciones extra a renderizar en la cabecera del preview.\n */\n headerSlot?: ReactNode;\n /**\n * Activa el botón flotante de descargar PDF.\n * @default false\n */\n withDownload?: boolean;\n /**\n * Color del tema (Hex, RGB o CSS color) usado para destacar el header, detalles y borde del avatar.\n * @default undefined (usa el primario de ginia-ui)\n */\n theme?: string;\n /**\n * Arreglo con la ruta de los campos a ocultar del editor y preview.\n * Por ejemplo: [\"first_name\", \"education.title\", \"skills.hard.verified\"]\n */\n hideFields?: string[];\n /**\n * Arreglo con los nombres de las secciones a ocultar completamente del editor y preview.\n * Por ejemplo: [\"Avanzado\", \"Preferencias\", \"Educación\"]\n */\n hideSections?: string[];\n /**\n * Slots adicionales para el menú del editor (Accordion).\n */\n menuSlot?: ResumeMenuSlot[];\n /**\n * Si es true, el editor muestra solo los slots de menuSlot y omite las secciones por defecto.\n * @default false\n */\n customMenuOnly?: boolean;\n /**\n * Callback al guardar los cambios del editor.\n * Recibe el CandidateProfile actualizado y el array estructurado guardado por tipo de items\n */\n onSave?: (payload: ResumeSavePayload, fullProfile: CandidateProfile) => void;\n /**\n * Muestra u oculta la raya superior del preview.\n * @default true\n */\n showTopBar?: boolean;\n /**\n * Clases CSS adicionales para el contenedor del preview.\n */\n className?: string;\n}\n"],"mappings":";;;;;;;;;;;;;;AAAA;AAAA;","names":[]}
@@ -186,6 +186,11 @@ interface ResumeProps {
186
186
  * Slots adicionales para el menú del editor (Accordion).
187
187
  */
188
188
  menuSlot?: ResumeMenuSlot[];
189
+ /**
190
+ * Si es true, el editor muestra solo los slots de menuSlot y omite las secciones por defecto.
191
+ * @default false
192
+ */
193
+ customMenuOnly?: boolean;
189
194
  /**
190
195
  * Callback al guardar los cambios del editor.
191
196
  * Recibe el CandidateProfile actualizado y el array estructurado guardado por tipo de items
@@ -186,6 +186,11 @@ interface ResumeProps {
186
186
  * Slots adicionales para el menú del editor (Accordion).
187
187
  */
188
188
  menuSlot?: ResumeMenuSlot[];
189
+ /**
190
+ * Si es true, el editor muestra solo los slots de menuSlot y omite las secciones por defecto.
191
+ * @default false
192
+ */
193
+ customMenuOnly?: boolean;
189
194
  /**
190
195
  * Callback al guardar los cambios del editor.
191
196
  * Recibe el CandidateProfile actualizado y el array estructurado guardado por tipo de items
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ginia/ui",
3
- "version": "0.1.14",
3
+ "version": "0.1.17",
4
4
  "description": "Ginia Design System — Unified UI component library for all Ginia hubs",
5
5
  "license": "MIT",
6
6
  "private": false,