@recruitnepal/shared-packages 1.4.0 → 1.6.0

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.
Files changed (46) hide show
  1. package/dist/components/cv/TemplatePicker.d.ts +14 -0
  2. package/dist/components/cv/TemplatePicker.d.ts.map +1 -0
  3. package/dist/components/cv/TemplatePicker.js +161 -0
  4. package/dist/components/cv/TemplateRenderer.d.ts +20 -0
  5. package/dist/components/cv/TemplateRenderer.d.ts.map +1 -0
  6. package/dist/components/cv/TemplateRenderer.js +14 -0
  7. package/dist/hooks/useEasyApplyFormHook.d.ts +5 -5
  8. package/dist/hooks/useEasyApplyFormHook.d.ts.map +1 -1
  9. package/dist/hooks/useEasyApplyFormHook.js +5 -2
  10. package/dist/index.d.ts +15 -1
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +12 -0
  13. package/dist/stores/cvDrafts.store.d.ts +44 -0
  14. package/dist/stores/cvDrafts.store.d.ts.map +1 -0
  15. package/dist/stores/cvDrafts.store.js +86 -0
  16. package/dist/stores/cvLayout.store.d.ts +24 -0
  17. package/dist/stores/cvLayout.store.d.ts.map +1 -0
  18. package/dist/stores/cvLayout.store.js +116 -0
  19. package/dist/stores/cvPreview.store.d.ts +29 -0
  20. package/dist/stores/cvPreview.store.d.ts.map +1 -0
  21. package/dist/stores/cvPreview.store.js +80 -0
  22. package/dist/types/cv-blocks.types.d.ts +44 -0
  23. package/dist/types/cv-blocks.types.d.ts.map +1 -0
  24. package/dist/types/cv-blocks.types.js +1 -0
  25. package/dist/types/cv-draft.types.d.ts +32 -0
  26. package/dist/types/cv-draft.types.d.ts.map +1 -0
  27. package/dist/types/cv-draft.types.js +1 -0
  28. package/dist/types/easy-apply.d.ts +8 -0
  29. package/dist/types/easy-apply.d.ts.map +1 -1
  30. package/dist/types/easy-apply.js +35 -1
  31. package/dist/types/template.types.d.ts +122 -0
  32. package/dist/types/template.types.d.ts.map +1 -0
  33. package/dist/types/template.types.js +1 -0
  34. package/dist/utils/cv/pdf/printReset.d.ts +3 -0
  35. package/dist/utils/cv/pdf/printReset.d.ts.map +1 -0
  36. package/dist/utils/cv/pdf/printReset.js +41 -0
  37. package/dist/utils/cv/pdf/styles/base.d.ts +2 -0
  38. package/dist/utils/cv/pdf/styles/base.d.ts.map +1 -0
  39. package/dist/utils/cv/pdf/styles/base.js +44 -0
  40. package/dist/utils/cv/pdf/styles/preprocessCssClient.d.ts +3 -0
  41. package/dist/utils/cv/pdf/styles/preprocessCssClient.d.ts.map +1 -0
  42. package/dist/utils/cv/pdf/styles/preprocessCssClient.js +33 -0
  43. package/dist/utils/cv-block-converter.d.ts +11 -0
  44. package/dist/utils/cv-block-converter.d.ts.map +1 -0
  45. package/dist/utils/cv-block-converter.js +83 -0
  46. package/package.json +1 -44
@@ -0,0 +1,14 @@
1
+ import type { TemplateData } from '../../types/template.types';
2
+ import type { CvTemplateRegistry } from './TemplateRenderer';
3
+ export declare function generateSampleTemplateData(seed: string): TemplateData;
4
+ export type TemplatePickerProps = {
5
+ /** Template registry from the host app */
6
+ registry: CvTemplateRegistry;
7
+ /** Optional list of slugs to show; defaults to Object.keys(registry) */
8
+ allowedSlugs?: string[];
9
+ /** Optional display names: slug -> title */
10
+ slugTitles?: Record<string, string>;
11
+ onPicked: (slug: string) => void;
12
+ };
13
+ export default function TemplatePicker({ registry, allowedSlugs, slugTitles, onPicked, }: TemplatePickerProps): import("react/jsx-runtime").JSX.Element;
14
+ //# sourceMappingURL=TemplatePicker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TemplatePicker.d.ts","sourceRoot":"","sources":["../../../src/components/cv/TemplatePicker.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC/D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AA0B7D,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,CAqFrE;AAuED,MAAM,MAAM,mBAAmB,GAAG;IAChC,0CAA0C;IAC1C,QAAQ,EAAE,kBAAkB,CAAC;IAC7B,wEAAwE;IACxE,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,4CAA4C;IAC5C,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CAClC,CAAC;AA+BF,MAAM,CAAC,OAAO,UAAU,cAAc,CAAC,EACrC,QAAQ,EACR,YAAY,EACZ,UAAe,EACf,QAAQ,GACT,EAAE,mBAAmB,2CAwBrB"}
@@ -0,0 +1,161 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useLayoutEffect, useRef, useState } from 'react';
4
+ import TemplateRenderer from './TemplateRenderer';
5
+ const FIRST_NAMES = ['Prakash', 'Bikash', 'Nabin', 'Suresh', 'Raj', 'Amit', 'Kumar', 'Sita', 'Gita', 'Pooja'];
6
+ const LAST_NAMES = ['Shrestha', 'Pradhan', 'Sharma', 'Pandey', 'Thapa', 'Karki', 'Gurung', 'Rai', 'Tamang', 'Lama'];
7
+ const TITLES = ['Software Engineer', 'Data Scientist', 'Product Manager', 'Frontend Developer', 'Full Stack Engineer'];
8
+ const COMPANIES = ['TechCorp', 'InnovateLab', 'Digital Solutions', 'Cloud Systems', 'Smart Solutions'];
9
+ const CITIES = ['Kathmandu', 'Pokhara', 'Lalitpur', 'Bhaktapur', 'Biratnagar'];
10
+ const INSTITUTIONS = ['Kathmandu University', 'Tribhuvan University', 'Pokhara University', 'NEC'];
11
+ const DEGREES = ['B.E.', 'B.Sc.', 'M.Sc.', 'M.B.A.'];
12
+ const FIELDS = ['Computer Engineering', 'Computer Science', 'Information Technology', 'Data Science'];
13
+ const PROJECT_NAMES = ['E-Commerce Platform', 'Task Management System', 'Analytics Dashboard', 'Mobile Banking App'];
14
+ const TECH_STACKS = [['React', 'Next.js', 'TypeScript'], ['Python', 'Django', 'PostgreSQL'], ['Node.js', 'Express', 'MongoDB']];
15
+ function seededRandom(seed) {
16
+ let h = 0;
17
+ for (let i = 0; i < seed.length; i++) {
18
+ h = ((h << 5) - h + seed.charCodeAt(i)) | 0;
19
+ }
20
+ return function next() {
21
+ h = (Math.imul(1664525, h) + 1013904223) >>> 0;
22
+ return h / 0x100000000;
23
+ };
24
+ }
25
+ const pick = (rng, arr) => arr[Math.floor(rng() * arr.length)];
26
+ export function generateSampleTemplateData(seed) {
27
+ const rng = seededRandom(seed);
28
+ const randomItem = (arr) => pick(rng, arr);
29
+ const firstName = randomItem(FIRST_NAMES);
30
+ const lastName = randomItem(LAST_NAMES);
31
+ const title = randomItem(TITLES);
32
+ const company = randomItem(COMPANIES);
33
+ const city = randomItem(CITIES);
34
+ const institution = randomItem(INSTITUTIONS);
35
+ const degree = randomItem(DEGREES);
36
+ const field = randomItem(FIELDS);
37
+ const projectName = randomItem(PROJECT_NAMES);
38
+ const techStack = randomItem(TECH_STACKS);
39
+ const currentYear = new Date().getFullYear();
40
+ const startYear = currentYear - Math.floor(rng() * 5) - 1;
41
+ const endYear = currentYear - Math.floor(rng() * 2);
42
+ const eduStartYear = startYear - 4;
43
+ const eduEndYear = startYear;
44
+ const gpa = (3.0 + rng() * 1.0).toFixed(1);
45
+ return {
46
+ personalInfo: {
47
+ firstName,
48
+ lastName,
49
+ title,
50
+ email: `${firstName.toLowerCase()}.${lastName.toLowerCase()}@example.com`,
51
+ phone: `+977-98${Math.floor(rng() * 10000000).toString().padStart(7, '0')}`,
52
+ location: { city },
53
+ summary: `${title} specializing in modern technologies.`,
54
+ links: [
55
+ { type: 'LinkedIn', label: `linkedin.com/in/${firstName.toLowerCase()}`, url: `https://linkedin.com/in/${firstName.toLowerCase()}` },
56
+ ],
57
+ },
58
+ sections: {
59
+ experience: [
60
+ {
61
+ id: 'exp1',
62
+ position: title,
63
+ company,
64
+ location: city,
65
+ startDate: `${startYear}-01-01`,
66
+ endDate: `${endYear}-12-31`,
67
+ current: rng() > 0.5,
68
+ description: `Responsible for developing solutions.`,
69
+ highlights: ['Improved performance.', 'Led implementation.'],
70
+ },
71
+ ],
72
+ education: [
73
+ {
74
+ id: 'edu1',
75
+ degree,
76
+ field,
77
+ institution,
78
+ location: city,
79
+ startDate: `${eduStartYear}-01-01`,
80
+ endDate: `${eduEndYear}-12-31`,
81
+ current: false,
82
+ gpa: `${gpa}/4.0`,
83
+ },
84
+ ],
85
+ projects: [
86
+ {
87
+ id: 'prj1',
88
+ name: projectName,
89
+ description: `A comprehensive ${projectName.toLowerCase()} built with modern technologies.`,
90
+ technologies: techStack,
91
+ url: `https://example.com`,
92
+ startDate: `${endYear - 1}-01-01`,
93
+ endDate: `${endYear - 1}-10-01`,
94
+ highlights: ['Implemented core features.', 'Deployed to production.'],
95
+ },
96
+ ],
97
+ skills: [
98
+ { id: 's1', category: 'Languages', skills: techStack.slice(0, 3).map((name) => ({ name })) },
99
+ { id: 's2', category: 'Tools', skills: ['Git', 'Docker', 'AWS'].map((name) => ({ name })) },
100
+ ],
101
+ certifications: [
102
+ { id: 'c1', name: 'AWS Certified Cloud Practitioner', issuer: 'AWS', date: `${endYear - 1}-06-01` },
103
+ ],
104
+ languages: [
105
+ { id: 'l1', language: 'English', proficiency: 'professional' },
106
+ { id: 'l2', language: 'Nepali', proficiency: 'native' },
107
+ ],
108
+ },
109
+ };
110
+ }
111
+ const MM_PER_IN = 25.4;
112
+ const DPI = 96;
113
+ const PDF_MARGINS_MM = { top: 10, bottom: 10, left: 10, right: 10 };
114
+ const mmToPx = (mm) => (mm / MM_PER_IN) * DPI;
115
+ const A4_W_PX = mmToPx(210);
116
+ const A4_H_PX = mmToPx(297);
117
+ const CONTENT_W_PX = A4_W_PX - mmToPx(PDF_MARGINS_MM.left + PDF_MARGINS_MM.right);
118
+ const CONTENT_H_PX = A4_H_PX - mmToPx(PDF_MARGINS_MM.top + PDF_MARGINS_MM.bottom);
119
+ function FitPreview({ registry, slug, data, }) {
120
+ const wrapRef = useRef(null);
121
+ const [scale, setScale] = useState(0.25);
122
+ useLayoutEffect(() => {
123
+ const update = () => {
124
+ const el = wrapRef.current;
125
+ if (!el)
126
+ return;
127
+ setScale(Math.max(0.1, el.clientWidth / CONTENT_W_PX));
128
+ };
129
+ update();
130
+ const ro = new ResizeObserver(update);
131
+ if (wrapRef.current)
132
+ ro.observe(wrapRef.current);
133
+ return () => ro.disconnect();
134
+ }, []);
135
+ return (_jsx("div", { ref: wrapRef, className: "relative w-full overflow-hidden rounded-xl bg-white", style: { aspectRatio: `${CONTENT_W_PX} / ${CONTENT_H_PX}` }, children: _jsx("div", { className: "absolute top-0 left-0", style: {
136
+ width: A4_W_PX,
137
+ height: A4_H_PX,
138
+ transform: `scale(${scale})`,
139
+ transformOrigin: 'top left',
140
+ pointerEvents: 'none',
141
+ }, children: _jsx("div", { style: {
142
+ position: 'relative',
143
+ width: '100%',
144
+ height: '100%',
145
+ paddingTop: mmToPx(PDF_MARGINS_MM.top),
146
+ paddingBottom: mmToPx(PDF_MARGINS_MM.bottom),
147
+ paddingLeft: mmToPx(PDF_MARGINS_MM.left),
148
+ paddingRight: mmToPx(PDF_MARGINS_MM.right),
149
+ boxSizing: 'border-box',
150
+ overflow: 'hidden',
151
+ }, children: _jsx(TemplateRenderer, { registry: registry, slug: slug, data: data, printMode: true }) }) }) }));
152
+ }
153
+ function TemplateCard({ registry, slug, title, onPick, }) {
154
+ const [templateData] = useState(() => generateSampleTemplateData(slug));
155
+ return (_jsxs("button", { type: "button", onClick: () => onPick(slug), className: "w-full text-left rounded-2xl border bg-white hover:shadow-lg transition-shadow focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary", children: [_jsxs("div", { className: "p-4 border-b rounded-t-2xl", children: [_jsx("div", { className: "text-sm font-medium", children: title }), _jsx("div", { className: "text-xs text-muted-foreground", children: "Click to select" })] }), _jsx("div", { className: "p-4", children: _jsx(FitPreview, { registry: registry, slug: slug, data: templateData }) })] }));
156
+ }
157
+ export default function TemplatePicker({ registry, allowedSlugs, slugTitles = {}, onPicked, }) {
158
+ const slugs = allowedSlugs ?? Object.keys(registry);
159
+ const titleFor = (slug) => slugTitles[slug] ?? slug.replace(/-/g, ' ').replace(/\b\w/g, (c) => c.toUpperCase());
160
+ return (_jsxs("div", { className: "custom-container py-8", children: [_jsxs("div", { className: "mb-6", children: [_jsx("h1", { className: "text-2xl font-semibold", children: "Select a CV Template" }), _jsx("p", { className: "text-sm text-muted-foreground", children: "Preview templates with sample data and pick one to start." })] }), _jsx("div", { className: "grid grid-cols-1 md:grid-cols-2 xl:grid-cols-4 gap-6", children: slugs.map((slug) => (_jsx(TemplateCard, { registry: registry, slug: slug, title: titleFor(slug), onPick: onPicked }, slug))) })] }));
161
+ }
@@ -0,0 +1,20 @@
1
+ import React from 'react';
2
+ import type { TemplateData, TemplateProps } from '../../types/template.types';
3
+ /** Registry map: template slug -> component that receives TemplateProps */
4
+ export type CvTemplateRegistry = Record<string, React.ComponentType<TemplateProps>>;
5
+ export type TemplateRendererProps = {
6
+ /** Template registry provided by the host app (each project defines its own templates) */
7
+ registry: CvTemplateRegistry;
8
+ /** Template slug (e.g. 'modern', 'professional') */
9
+ slug: string;
10
+ /** CV data to render */
11
+ data: TemplateData;
12
+ printMode?: boolean;
13
+ className?: string;
14
+ };
15
+ /**
16
+ * Renders a CV template by slug using the registry provided by the host app.
17
+ * Host app must pass its own TEMPLATE_REGISTRY (no templates are shipped in the package).
18
+ */
19
+ export default function TemplateRenderer({ registry, slug, data, printMode, className, }: TemplateRendererProps): import("react/jsx-runtime").JSX.Element | null;
20
+ //# sourceMappingURL=TemplateRenderer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TemplateRenderer.d.ts","sourceRoot":"","sources":["../../../src/components/cv/TemplateRenderer.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAE9E,2EAA2E;AAC3E,MAAM,MAAM,kBAAkB,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,aAAa,CAAC,aAAa,CAAC,CAAC,CAAC;AAEpF,MAAM,MAAM,qBAAqB,GAAG;IAClC,0FAA0F;IAC1F,QAAQ,EAAE,kBAAkB,CAAC;IAC7B,oDAAoD;IACpD,IAAI,EAAE,MAAM,CAAC;IACb,wBAAwB;IACxB,IAAI,EAAE,YAAY,CAAC;IACnB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF;;;GAGG;AACH,MAAM,CAAC,OAAO,UAAU,gBAAgB,CAAC,EACvC,QAAQ,EACR,IAAI,EACJ,IAAI,EACJ,SAAiB,EACjB,SAAc,GACf,EAAE,qBAAqB,kDAOvB"}
@@ -0,0 +1,14 @@
1
+ 'use client';
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ /**
4
+ * Renders a CV template by slug using the registry provided by the host app.
5
+ * Host app must pass its own TEMPLATE_REGISTRY (no templates are shipped in the package).
6
+ */
7
+ export default function TemplateRenderer({ registry, slug, data, printMode = false, className = '', }) {
8
+ const fallbackSlug = Object.keys(registry)[0] ?? 'modern';
9
+ const Comp = registry[slug] ?? registry[fallbackSlug];
10
+ if (!Comp) {
11
+ return null;
12
+ }
13
+ return _jsx(Comp, { data: data, printMode: printMode, className: className });
14
+ }
@@ -26,7 +26,7 @@ export declare const easyApplyUserSchema: z.ZodObject<{
26
26
  email: z.ZodString;
27
27
  phone_no: z.ZodString;
28
28
  current_address: z.ZodString;
29
- current_status: z.ZodString;
29
+ current_status: z.ZodEnum<["open to work", "open to switch", "part time", "freelance", "not looking"]>;
30
30
  two_wheeler: z.ZodDefault<z.ZodBoolean>;
31
31
  driving_license: z.ZodDefault<z.ZodBoolean>;
32
32
  salary_expectation: z.ZodEffects<z.ZodObject<{
@@ -62,7 +62,7 @@ export declare const easyApplyUserSchema: z.ZodObject<{
62
62
  email: string;
63
63
  phone_no: string;
64
64
  current_address: string;
65
- current_status: string;
65
+ current_status: "open to work" | "open to switch" | "part time" | "freelance" | "not looking";
66
66
  two_wheeler: boolean;
67
67
  driving_license: boolean;
68
68
  salary_expectation: {
@@ -80,7 +80,7 @@ export declare const easyApplyUserSchema: z.ZodObject<{
80
80
  email: string;
81
81
  phone_no: string;
82
82
  current_address: string;
83
- current_status: string;
83
+ current_status: "open to work" | "open to switch" | "part time" | "freelance" | "not looking";
84
84
  salary_expectation: {
85
85
  min: number;
86
86
  max: number;
@@ -111,7 +111,7 @@ export declare const useEasyApplyFormHook: ({ vacancyId, isIntern, token, onSucc
111
111
  email: string;
112
112
  phone_no: string;
113
113
  current_address: string;
114
- current_status: string;
114
+ current_status: "open to work" | "open to switch" | "part time" | "freelance" | "not looking";
115
115
  two_wheeler: boolean;
116
116
  driving_license: boolean;
117
117
  salary_expectation: {
@@ -129,7 +129,7 @@ export declare const useEasyApplyFormHook: ({ vacancyId, isIntern, token, onSucc
129
129
  email: string;
130
130
  phone_no: string;
131
131
  current_address: string;
132
- current_status: string;
132
+ current_status: "open to work" | "open to switch" | "part time" | "freelance" | "not looking";
133
133
  two_wheeler: boolean;
134
134
  driving_license: boolean;
135
135
  salary_expectation: {
@@ -1 +1 @@
1
- {"version":3,"file":"useEasyApplyFormHook.d.ts","sourceRoot":"","sources":["../../src/hooks/useEasyApplyFormHook.tsx"],"names":[],"mappings":"AAKA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAcxB,eAAO,MAAM,qBAAqB;;;;;;;;;EAGhC,CAAC;AAEH,eAAO,MAAM,sBAAsB;;;;;;;;;WAAiC,CAAC;AAErE,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAsB9B,CAAC;AAEH,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AACtE,MAAM,MAAM,wBAAwB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAC7E,MAAM,MAAM,yBAAyB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AA+B/E,MAAM,MAAM,0BAA0B,GAAG;IACvC,SAAS,EAAE,MAAM,CAAC;IAClB,0HAA0H;IAC1H,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IACtD,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACrC,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAAI,qDAMlC,0BAA0B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;uBAqDV,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoErC,CAAC;AAEF,eAAe,oBAAoB,CAAC"}
1
+ {"version":3,"file":"useEasyApplyFormHook.d.ts","sourceRoot":"","sources":["../../src/hooks/useEasyApplyFormHook.tsx"],"names":[],"mappings":"AAKA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAexB,eAAO,MAAM,qBAAqB;;;;;;;;;EAGhC,CAAC;AAEH,eAAO,MAAM,sBAAsB;;;;;;;;;WAAiC,CAAC;AAErE,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAwB9B,CAAC;AAEH,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AACtE,MAAM,MAAM,wBAAwB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAC7E,MAAM,MAAM,yBAAyB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AA+B/E,MAAM,MAAM,0BAA0B,GAAG;IACvC,SAAS,EAAE,MAAM,CAAC;IAClB,0HAA0H;IAC1H,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IACtD,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACrC,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAAI,qDAMlC,0BAA0B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;uBAqDV,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoErC,CAAC;AAEF,eAAe,oBAAoB,CAAC"}
@@ -4,6 +4,7 @@ import { useForm } from 'react-hook-form';
4
4
  import { zodResolver } from '@hookform/resolvers/zod';
5
5
  import { z } from 'zod';
6
6
  import { useEasyApplyMutations } from './useEasyApplyMutations';
7
+ import { WORK_STATUS_VALUES } from '../types/easy-apply';
7
8
  // Schemas
8
9
  const salaryExpectationSchema = z
9
10
  .object({
@@ -36,7 +37,9 @@ export const easyApplyUserSchema = z.object({
36
37
  current_address: z
37
38
  .string()
38
39
  .min(1, { message: 'Current address is required' }),
39
- current_status: z.string().min(1, { message: 'Current status is required' }),
40
+ current_status: z.enum(WORK_STATUS_VALUES, {
41
+ errorMap: () => ({ message: 'Please select a valid current status' }),
42
+ }),
40
43
  two_wheeler: z.boolean().default(false),
41
44
  driving_license: z.boolean().default(false),
42
45
  salary_expectation: salaryExpectationSchema,
@@ -82,7 +85,7 @@ export const useEasyApplyFormHook = ({ vacancyId, isIntern = false, token, onSuc
82
85
  email: '',
83
86
  phone_no: '',
84
87
  current_address: '',
85
- current_status: '',
88
+ current_status: 'open to work',
86
89
  two_wheeler: false,
87
90
  driving_license: false,
88
91
  salary_expectation: { min: 10000, max: 20000 },
package/dist/index.d.ts CHANGED
@@ -3,7 +3,8 @@ export type { SingleVacancyRes, VacancyApiRes, Pagination, ExperienceLevel, } fr
3
3
  export type { ApplicantItem, ApplicantApiResponse, ApplicantPayload, } from './types/applicant';
4
4
  export type { SavedVacancyItem, SavedVacancyApiResponse, SavedVacancyPayload, } from './types/saved-vacancy';
5
5
  export type { Question, AnswerOption, VacancyQuestionsResponse, } from './types/questions';
6
- export type { QuestionsAPIRes, ScreeningAnswersApiResNew, } from './types/easy-apply';
6
+ export { WORKSTATUS, WORK_STATUS_VALUES, } from './types/easy-apply';
7
+ export type { WorkStatusValue, QuestionsAPIRes, ScreeningAnswersApiResNew, } from './types/easy-apply';
7
8
  export { apiRequest } from './utils/api-request';
8
9
  export type { ApiResponse } from './utils/api-request';
9
10
  export { useGetAllVacancy } from './hooks/useGetAllVacancy';
@@ -22,4 +23,17 @@ export { default as JobsClient } from './components/JobsClient';
22
23
  export type { JobsClientProps } from './components/JobsClient';
23
24
  export { default as JobDescriptionClient } from './components/JobDescriptionClient';
24
25
  export type { JobDescriptionClientProps } from './components/JobDescriptionClient';
26
+ export type { TemplateData, TemplatePersonalInfo, TemplateSections, TemplateExperience, TemplateEducation, TemplateProject, TemplateSkill, TemplateSkillCategory, TemplateCertification, TemplateLanguage, TemplateReference, TemplateTraining, TemplateLink, TemplateProps, } from './types/template.types';
27
+ export type { CvBlock, CvBlockType, CvBlockStyle, CvLayout, } from './types/cv-blocks.types';
28
+ export type { CvDraft, CreateCvDraftPayload, UpdateCvDraftPayload, CvDraftListResponse, } from './types/cv-draft.types';
29
+ export { convertTemplateToBlocks, getDefaultColumns, getDefaultStyles, } from './utils/cv-block-converter';
30
+ export { preprocessTemplateCssClient, clearCssCacheClient, } from './utils/cv/pdf/styles/preprocessCssClient';
31
+ export { useCvPreview } from './stores/cvPreview.store';
32
+ export { useCvLayout } from './stores/cvLayout.store';
33
+ export { useCvDraftsStore } from './stores/cvDrafts.store';
34
+ export { default as TemplateRenderer } from './components/cv/TemplateRenderer';
35
+ export type { CvTemplateRegistry, TemplateRendererProps } from './components/cv/TemplateRenderer';
36
+ export { default as TemplatePicker } from './components/cv/TemplatePicker';
37
+ export type { TemplatePickerProps } from './components/cv/TemplatePicker';
38
+ export { generateSampleTemplateData } from './components/cv/TemplatePicker';
25
39
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,cAAc,CAAC;AAGxD,YAAY,EACV,gBAAgB,EAChB,aAAa,EACb,UAAU,EACV,eAAe,GAChB,MAAM,SAAS,CAAC;AACjB,YAAY,EACV,aAAa,EACb,oBAAoB,EACpB,gBAAgB,GACjB,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EACV,gBAAgB,EAChB,uBAAuB,EACvB,mBAAmB,GACpB,MAAM,uBAAuB,CAAC;AAC/B,YAAY,EACV,QAAQ,EACR,YAAY,EACZ,wBAAwB,GACzB,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EACV,eAAe,EACf,yBAAyB,GAC1B,MAAM,oBAAoB,CAAC;AAG5B,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACjD,YAAY,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAGvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,EAAE,OAAO,IAAI,uBAAuB,EAAE,MAAM,0BAA0B,CAAC;AAC9E,OAAO,EAAE,wBAAwB,EAAE,MAAM,kCAAkC,CAAC;AAC5E,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAClE,OAAO,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AACtE,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAClE,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AACtE,YAAY,EAAE,2BAA2B,EAAE,MAAM,+BAA+B,CAAC;AACjF,OAAO,EACL,oBAAoB,EACpB,mBAAmB,EACnB,qBAAqB,EACrB,sBAAsB,GACvB,MAAM,8BAA8B,CAAC;AACtC,YAAY,EACV,mBAAmB,EACnB,wBAAwB,EACxB,yBAAyB,EACzB,0BAA0B,GAC3B,MAAM,8BAA8B,CAAC;AAGtC,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAChE,YAAY,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC/D,OAAO,EAAE,OAAO,IAAI,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;AACpF,YAAY,EAAE,yBAAyB,EAAE,MAAM,mCAAmC,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,cAAc,CAAC;AAGxD,YAAY,EACV,gBAAgB,EAChB,aAAa,EACb,UAAU,EACV,eAAe,GAChB,MAAM,SAAS,CAAC;AACjB,YAAY,EACV,aAAa,EACb,oBAAoB,EACpB,gBAAgB,GACjB,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EACV,gBAAgB,EAChB,uBAAuB,EACvB,mBAAmB,GACpB,MAAM,uBAAuB,CAAC;AAC/B,YAAY,EACV,QAAQ,EACR,YAAY,EACZ,wBAAwB,GACzB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,UAAU,EACV,kBAAkB,GACnB,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EACV,eAAe,EACf,eAAe,EACf,yBAAyB,GAC1B,MAAM,oBAAoB,CAAC;AAG5B,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACjD,YAAY,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAGvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,EAAE,OAAO,IAAI,uBAAuB,EAAE,MAAM,0BAA0B,CAAC;AAC9E,OAAO,EAAE,wBAAwB,EAAE,MAAM,kCAAkC,CAAC;AAC5E,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAClE,OAAO,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AACtE,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAClE,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AACtE,YAAY,EAAE,2BAA2B,EAAE,MAAM,+BAA+B,CAAC;AACjF,OAAO,EACL,oBAAoB,EACpB,mBAAmB,EACnB,qBAAqB,EACrB,sBAAsB,GACvB,MAAM,8BAA8B,CAAC;AACtC,YAAY,EACV,mBAAmB,EACnB,wBAAwB,EACxB,yBAAyB,EACzB,0BAA0B,GAC3B,MAAM,8BAA8B,CAAC;AAGtC,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAChE,YAAY,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC/D,OAAO,EAAE,OAAO,IAAI,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;AACpF,YAAY,EAAE,yBAAyB,EAAE,MAAM,mCAAmC,CAAC;AAGnF,YAAY,EACV,YAAY,EACZ,oBAAoB,EACpB,gBAAgB,EAChB,kBAAkB,EAClB,iBAAiB,EACjB,eAAe,EACf,aAAa,EACb,qBAAqB,EACrB,qBAAqB,EACrB,gBAAgB,EAChB,iBAAiB,EACjB,gBAAgB,EAChB,YAAY,EACZ,aAAa,GACd,MAAM,wBAAwB,CAAC;AAChC,YAAY,EACV,OAAO,EACP,WAAW,EACX,YAAY,EACZ,QAAQ,GACT,MAAM,yBAAyB,CAAC;AACjC,YAAY,EACV,OAAO,EACP,oBAAoB,EACpB,oBAAoB,EACpB,mBAAmB,GACpB,MAAM,wBAAwB,CAAC;AAGhC,OAAO,EACL,uBAAuB,EACvB,iBAAiB,EACjB,gBAAgB,GACjB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EACL,2BAA2B,EAC3B,mBAAmB,GACpB,MAAM,2CAA2C,CAAC;AAGnD,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AACxD,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AACtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAG3D,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AAC/E,YAAY,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,MAAM,kCAAkC,CAAC;AAClG,OAAO,EAAE,OAAO,IAAI,cAAc,EAAE,MAAM,gCAAgC,CAAC;AAC3E,YAAY,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;AAC1E,OAAO,EAAE,0BAA0B,EAAE,MAAM,gCAAgC,CAAC"}
package/dist/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  // API Client
2
2
  export { initApi, getBaseURL, API } from './api-client';
3
+ export { WORKSTATUS, WORK_STATUS_VALUES, } from './types/easy-apply';
3
4
  // Utils
4
5
  export { apiRequest } from './utils/api-request';
5
6
  // Hooks
@@ -16,3 +17,14 @@ export { useEasyApplyFormHook, easyApplyUserSchema, screeningAnswerSchema, scree
16
17
  // Components
17
18
  export { default as JobsClient } from './components/JobsClient';
18
19
  export { default as JobDescriptionClient } from './components/JobDescriptionClient';
20
+ // CV Builder: utils
21
+ export { convertTemplateToBlocks, getDefaultColumns, getDefaultStyles, } from './utils/cv-block-converter';
22
+ export { preprocessTemplateCssClient, clearCssCacheClient, } from './utils/cv/pdf/styles/preprocessCssClient';
23
+ // CV Builder: stores
24
+ export { useCvPreview } from './stores/cvPreview.store';
25
+ export { useCvLayout } from './stores/cvLayout.store';
26
+ export { useCvDraftsStore } from './stores/cvDrafts.store';
27
+ // CV Builder: components (registry-based; no templates in package)
28
+ export { default as TemplateRenderer } from './components/cv/TemplateRenderer';
29
+ export { default as TemplatePicker } from './components/cv/TemplatePicker';
30
+ export { generateSampleTemplateData } from './components/cv/TemplatePicker';
@@ -0,0 +1,44 @@
1
+ import type { TemplateData } from '../types/template.types';
2
+ import type { CvDraft } from '../types/cv-draft.types';
3
+ type LocalCvDraft = {
4
+ id: string;
5
+ name: string;
6
+ template: string;
7
+ cv_data: TemplateData | null;
8
+ isAnonymous: boolean;
9
+ backendId?: string;
10
+ createdAt: string;
11
+ updatedAt: string;
12
+ };
13
+ type CvDraftsState = {
14
+ drafts: LocalCvDraft[];
15
+ currentDraftId: string | null;
16
+ setCurrentDraft: (id: string | null) => void;
17
+ addDraft: (draft: Omit<LocalCvDraft, 'id' | 'createdAt' | 'updatedAt'>) => string;
18
+ updateDraft: (id: string, updates: Partial<LocalCvDraft>) => void;
19
+ deleteDraft: (id: string) => void;
20
+ getCurrentDraft: () => LocalCvDraft | null;
21
+ getDraftById: (id: string) => LocalCvDraft | null;
22
+ syncDraftToBackend: (localId: string, backendId: string) => void;
23
+ loadDraftsFromBackend: (backendDrafts: CvDraft[]) => void;
24
+ clearDrafts: () => void;
25
+ };
26
+ export declare const useCvDraftsStore: import("zustand").UseBoundStore<Omit<import("zustand").StoreApi<CvDraftsState>, "persist"> & {
27
+ persist: {
28
+ setOptions: (options: Partial<import("zustand/middleware").PersistOptions<CvDraftsState, {
29
+ drafts: LocalCvDraft[];
30
+ currentDraftId: string | null;
31
+ }>>) => void;
32
+ clearStorage: () => void;
33
+ rehydrate: () => Promise<void> | void;
34
+ hasHydrated: () => boolean;
35
+ onHydrate: (fn: (state: CvDraftsState) => void) => () => void;
36
+ onFinishHydration: (fn: (state: CvDraftsState) => void) => () => void;
37
+ getOptions: () => Partial<import("zustand/middleware").PersistOptions<CvDraftsState, {
38
+ drafts: LocalCvDraft[];
39
+ currentDraftId: string | null;
40
+ }>>;
41
+ };
42
+ }>;
43
+ export {};
44
+ //# sourceMappingURL=cvDrafts.store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cvDrafts.store.d.ts","sourceRoot":"","sources":["../../src/stores/cvDrafts.store.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAC;AAEvD,KAAK,YAAY,GAAG;IAClB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,YAAY,GAAG,IAAI,CAAC;IAC7B,WAAW,EAAE,OAAO,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,KAAK,aAAa,GAAG;IACnB,MAAM,EAAE,YAAY,EAAE,CAAC;IACvB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,eAAe,EAAE,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IAC7C,QAAQ,EAAE,CAAC,KAAK,EAAE,IAAI,CAAC,YAAY,EAAE,IAAI,GAAG,WAAW,GAAG,WAAW,CAAC,KAAK,MAAM,CAAC;IAClF,WAAW,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,YAAY,CAAC,KAAK,IAAI,CAAC;IAClE,WAAW,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,eAAe,EAAE,MAAM,YAAY,GAAG,IAAI,CAAC;IAC3C,YAAY,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,YAAY,GAAG,IAAI,CAAC;IAClD,kBAAkB,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IACjE,qBAAqB,EAAE,CAAC,aAAa,EAAE,OAAO,EAAE,KAAK,IAAI,CAAC;IAC1D,WAAW,EAAE,MAAM,IAAI,CAAC;CACzB,CAAC;AAEF,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;EAyG5B,CAAC"}
@@ -0,0 +1,86 @@
1
+ import { create } from 'zustand';
2
+ import { persist } from 'zustand/middleware';
3
+ export const useCvDraftsStore = create()(persist((set, get) => ({
4
+ drafts: [],
5
+ currentDraftId: null,
6
+ setCurrentDraft: (id) => set({ currentDraftId: id }),
7
+ addDraft: (draft) => {
8
+ const id = `local_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
9
+ const newDraft = {
10
+ ...draft,
11
+ id,
12
+ createdAt: new Date().toISOString(),
13
+ updatedAt: new Date().toISOString(),
14
+ };
15
+ set((state) => ({
16
+ drafts: [...state.drafts, newDraft],
17
+ currentDraftId: id,
18
+ }));
19
+ return id;
20
+ },
21
+ updateDraft: (id, updates) => {
22
+ set((state) => ({
23
+ drafts: state.drafts.map((d) => d.id === id ? { ...d, ...updates, updatedAt: new Date().toISOString() } : d),
24
+ }));
25
+ },
26
+ deleteDraft: (id) => {
27
+ set((state) => ({
28
+ drafts: state.drafts.filter((d) => d.id !== id),
29
+ currentDraftId: state.currentDraftId === id ? null : state.currentDraftId,
30
+ }));
31
+ },
32
+ getCurrentDraft: () => {
33
+ const { drafts, currentDraftId } = get();
34
+ return drafts.find((d) => d.id === currentDraftId) || null;
35
+ },
36
+ getDraftById: (id) => {
37
+ const { drafts } = get();
38
+ return drafts.find((d) => d.id === id) || null;
39
+ },
40
+ syncDraftToBackend: (localId, backendId) => {
41
+ set((state) => ({
42
+ drafts: state.drafts.map((d) => d.id === localId ? { ...d, backendId } : d),
43
+ }));
44
+ },
45
+ loadDraftsFromBackend: (backendDrafts) => {
46
+ const { drafts: existingDrafts } = get();
47
+ const activeDrafts = backendDrafts.filter((draft) => draft.is_active !== false);
48
+ const syncedDrafts = activeDrafts.map((backendDraft) => {
49
+ const existing = existingDrafts.find((d) => d.backendId === backendDraft.id);
50
+ if (existing) {
51
+ return {
52
+ ...existing,
53
+ name: backendDraft.name,
54
+ template: backendDraft.template,
55
+ cv_data: backendDraft.cv_data,
56
+ updatedAt: backendDraft.updated_at,
57
+ isAnonymous: false,
58
+ backendId: backendDraft.id,
59
+ };
60
+ }
61
+ return {
62
+ id: `backend_${backendDraft.id}`,
63
+ name: backendDraft.name,
64
+ template: backendDraft.template,
65
+ cv_data: backendDraft.cv_data,
66
+ isAnonymous: false,
67
+ backendId: backendDraft.id,
68
+ createdAt: backendDraft.created_at,
69
+ updatedAt: backendDraft.updated_at,
70
+ };
71
+ });
72
+ const anonymousDrafts = existingDrafts.filter((d) => d.isAnonymous);
73
+ const backendIds = new Set(activeDrafts.map((d) => d.id));
74
+ const validSyncedDrafts = existingDrafts.filter((d) => !d.isAnonymous && d.backendId && backendIds.has(d.backendId));
75
+ const newSyncedDrafts = syncedDrafts.filter((d) => !validSyncedDrafts.some((e) => e.backendId === d.backendId));
76
+ const mergedDrafts = [...anonymousDrafts, ...validSyncedDrafts, ...newSyncedDrafts];
77
+ set({ drafts: mergedDrafts });
78
+ },
79
+ clearDrafts: () => set({ drafts: [], currentDraftId: null }),
80
+ }), {
81
+ name: 'cv-drafts-storage',
82
+ partialize: (state) => ({
83
+ drafts: state.drafts.filter((d) => d.isAnonymous),
84
+ currentDraftId: state.currentDraftId,
85
+ }),
86
+ }));
@@ -0,0 +1,24 @@
1
+ import type { CvLayout, CvBlock } from '../types/cv-blocks.types';
2
+ import type { TemplateData } from '../types/template.types';
3
+ type CvLayoutState = {
4
+ layout: CvLayout | null;
5
+ isEditMode: boolean;
6
+ loadTemplate: (templateSlug: string, data: TemplateData) => void;
7
+ addBlock: (block: CvBlock) => void;
8
+ updateBlock: (id: string, updates: Partial<CvBlock>) => void;
9
+ deleteBlock: (id: string) => void;
10
+ moveBlock: (id: string, newPosition: {
11
+ column?: number;
12
+ order?: number;
13
+ }) => void;
14
+ reorderBlocks: (ids: string[]) => void;
15
+ setGridColumns: (columns: number) => void;
16
+ setGlobalStyle: (key: string, value: unknown) => void;
17
+ toggleEditMode: () => void;
18
+ saveLayout: () => Promise<void>;
19
+ loadSavedLayout: (layoutId: string) => Promise<void>;
20
+ reset: () => void;
21
+ };
22
+ export declare const useCvLayout: import("zustand").UseBoundStore<import("zustand").StoreApi<CvLayoutState>>;
23
+ export {};
24
+ //# sourceMappingURL=cvLayout.store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cvLayout.store.d.ts","sourceRoot":"","sources":["../../src/stores/cvLayout.store.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAO5D,KAAK,aAAa,GAAG;IACnB,MAAM,EAAE,QAAQ,GAAG,IAAI,CAAC;IACxB,UAAU,EAAE,OAAO,CAAC;IACpB,YAAY,EAAE,CAAC,YAAY,EAAE,MAAM,EAAE,IAAI,EAAE,YAAY,KAAK,IAAI,CAAC;IACjE,QAAQ,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IACnC,WAAW,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,KAAK,IAAI,CAAC;IAC7D,WAAW,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,SAAS,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,WAAW,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IAClF,aAAa,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IACvC,cAAc,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1C,cAAc,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IACtD,cAAc,EAAE,MAAM,IAAI,CAAC;IAC3B,UAAU,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAChC,eAAe,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACrD,KAAK,EAAE,MAAM,IAAI,CAAC;CACnB,CAAC;AAEF,eAAO,MAAM,WAAW,4EA+HrB,CAAC"}
@@ -0,0 +1,116 @@
1
+ import { create } from 'zustand';
2
+ import { convertTemplateToBlocks, getDefaultColumns, getDefaultStyles, } from '../utils/cv-block-converter';
3
+ export const useCvLayout = create((set, get) => ({
4
+ layout: null,
5
+ isEditMode: false,
6
+ loadTemplate: (templateSlug, data) => {
7
+ const blocks = convertTemplateToBlocks(templateSlug, data);
8
+ const layout = {
9
+ id: `layout-${Date.now()}`,
10
+ name: `${templateSlug} - Custom`,
11
+ template: templateSlug,
12
+ grid: {
13
+ columns: getDefaultColumns(templateSlug),
14
+ gap: 16,
15
+ width: 210,
16
+ },
17
+ blocks,
18
+ globalStyles: getDefaultStyles(templateSlug),
19
+ };
20
+ set({ layout });
21
+ },
22
+ addBlock: (block) => set((state) => ({
23
+ layout: state.layout
24
+ ? { ...state.layout, blocks: [...state.layout.blocks, block] }
25
+ : null,
26
+ })),
27
+ updateBlock: (id, updates) => set((state) => ({
28
+ layout: state.layout
29
+ ? {
30
+ ...state.layout,
31
+ blocks: state.layout.blocks.map((b) => b.id === id ? { ...b, ...updates } : b),
32
+ }
33
+ : null,
34
+ })),
35
+ deleteBlock: (id) => set((state) => ({
36
+ layout: state.layout
37
+ ? {
38
+ ...state.layout,
39
+ blocks: state.layout.blocks.filter((b) => b.id !== id),
40
+ }
41
+ : null,
42
+ })),
43
+ moveBlock: (id, { column, order }) => set((state) => {
44
+ if (!state.layout)
45
+ return state;
46
+ return {
47
+ layout: {
48
+ ...state.layout,
49
+ blocks: state.layout.blocks.map((b) => {
50
+ if (b.id === id) {
51
+ return {
52
+ ...b,
53
+ style: {
54
+ ...b.style,
55
+ gridColumn: column !== undefined ? column : b.style.gridColumn,
56
+ order: order !== undefined ? order : b.style.order,
57
+ },
58
+ };
59
+ }
60
+ return b;
61
+ }),
62
+ },
63
+ };
64
+ }),
65
+ reorderBlocks: (ids) => set((state) => {
66
+ if (!state.layout)
67
+ return state;
68
+ const blockMap = new Map(state.layout.blocks.map((b) => [b.id, b]));
69
+ const reordered = ids
70
+ .map((id) => blockMap.get(id))
71
+ .filter(Boolean);
72
+ return {
73
+ layout: { ...state.layout, blocks: reordered },
74
+ };
75
+ }),
76
+ setGridColumns: (columns) => set((state) => ({
77
+ layout: state.layout
78
+ ? { ...state.layout, grid: { ...state.layout.grid, columns } }
79
+ : null,
80
+ })),
81
+ setGlobalStyle: (key, value) => set((state) => ({
82
+ layout: state.layout
83
+ ? {
84
+ ...state.layout,
85
+ globalStyles: { ...state.layout.globalStyles, [key]: value },
86
+ }
87
+ : null,
88
+ })),
89
+ toggleEditMode: () => set((state) => ({ isEditMode: !state.isEditMode })),
90
+ saveLayout: async () => {
91
+ const { layout } = get();
92
+ if (!layout)
93
+ return;
94
+ try {
95
+ if (typeof localStorage !== 'undefined') {
96
+ localStorage.setItem('cv-layout-draft', JSON.stringify(layout));
97
+ }
98
+ }
99
+ catch (_err) {
100
+ /* noop */
101
+ }
102
+ },
103
+ loadSavedLayout: async (_layoutId) => {
104
+ try {
105
+ if (typeof localStorage === 'undefined')
106
+ return;
107
+ const saved = localStorage.getItem('cv-layout-draft');
108
+ if (saved)
109
+ set({ layout: JSON.parse(saved) });
110
+ }
111
+ catch (_err) {
112
+ /* noop */
113
+ }
114
+ },
115
+ reset: () => set({ layout: null }),
116
+ }));