@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.
- package/dist/components/cv/TemplatePicker.d.ts +14 -0
- package/dist/components/cv/TemplatePicker.d.ts.map +1 -0
- package/dist/components/cv/TemplatePicker.js +161 -0
- package/dist/components/cv/TemplateRenderer.d.ts +20 -0
- package/dist/components/cv/TemplateRenderer.d.ts.map +1 -0
- package/dist/components/cv/TemplateRenderer.js +14 -0
- package/dist/hooks/useEasyApplyFormHook.d.ts +5 -5
- package/dist/hooks/useEasyApplyFormHook.d.ts.map +1 -1
- package/dist/hooks/useEasyApplyFormHook.js +5 -2
- package/dist/index.d.ts +15 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -0
- package/dist/stores/cvDrafts.store.d.ts +44 -0
- package/dist/stores/cvDrafts.store.d.ts.map +1 -0
- package/dist/stores/cvDrafts.store.js +86 -0
- package/dist/stores/cvLayout.store.d.ts +24 -0
- package/dist/stores/cvLayout.store.d.ts.map +1 -0
- package/dist/stores/cvLayout.store.js +116 -0
- package/dist/stores/cvPreview.store.d.ts +29 -0
- package/dist/stores/cvPreview.store.d.ts.map +1 -0
- package/dist/stores/cvPreview.store.js +80 -0
- package/dist/types/cv-blocks.types.d.ts +44 -0
- package/dist/types/cv-blocks.types.d.ts.map +1 -0
- package/dist/types/cv-blocks.types.js +1 -0
- package/dist/types/cv-draft.types.d.ts +32 -0
- package/dist/types/cv-draft.types.d.ts.map +1 -0
- package/dist/types/cv-draft.types.js +1 -0
- package/dist/types/easy-apply.d.ts +8 -0
- package/dist/types/easy-apply.d.ts.map +1 -1
- package/dist/types/easy-apply.js +35 -1
- package/dist/types/template.types.d.ts +122 -0
- package/dist/types/template.types.d.ts.map +1 -0
- package/dist/types/template.types.js +1 -0
- package/dist/utils/cv/pdf/printReset.d.ts +3 -0
- package/dist/utils/cv/pdf/printReset.d.ts.map +1 -0
- package/dist/utils/cv/pdf/printReset.js +41 -0
- package/dist/utils/cv/pdf/styles/base.d.ts +2 -0
- package/dist/utils/cv/pdf/styles/base.d.ts.map +1 -0
- package/dist/utils/cv/pdf/styles/base.js +44 -0
- package/dist/utils/cv/pdf/styles/preprocessCssClient.d.ts +3 -0
- package/dist/utils/cv/pdf/styles/preprocessCssClient.d.ts.map +1 -0
- package/dist/utils/cv/pdf/styles/preprocessCssClient.js +33 -0
- package/dist/utils/cv-block-converter.d.ts +11 -0
- package/dist/utils/cv-block-converter.d.ts.map +1 -0
- package/dist/utils/cv-block-converter.js +83 -0
- 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.
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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;
|
|
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.
|
|
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
|
|
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
|
package/dist/index.d.ts.map
CHANGED
|
@@ -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":"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"}
|
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
|
+
}));
|