@recruitnepal/shared-packages 1.7.4 → 1.8.1
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/career-personality-test/components/CareerFitTestLanding.d.ts +10 -0
- package/dist/career-personality-test/components/CareerFitTestLanding.d.ts.map +1 -0
- package/dist/career-personality-test/components/CareerFitTestLanding.js +20 -0
- package/dist/career-personality-test/components/CareerTestDetailsForm.d.ts +28 -0
- package/dist/career-personality-test/components/CareerTestDetailsForm.d.ts.map +1 -0
- package/dist/career-personality-test/components/CareerTestDetailsForm.js +91 -0
- package/dist/career-personality-test/components/CareerTestResultsPage.d.ts +27 -0
- package/dist/career-personality-test/components/CareerTestResultsPage.d.ts.map +1 -0
- package/dist/career-personality-test/components/CareerTestResultsPage.js +121 -0
- package/dist/career-personality-test/components/CareerTestStart.d.ts +7 -0
- package/dist/career-personality-test/components/CareerTestStart.d.ts.map +1 -0
- package/dist/career-personality-test/components/CareerTestStart.js +87 -0
- package/dist/career-personality-test/constants/resultConstants.d.ts +29 -0
- package/dist/career-personality-test/constants/resultConstants.d.ts.map +1 -0
- package/dist/career-personality-test/constants/resultConstants.js +92 -0
- package/dist/career-personality-test/constants/storageKeys.d.ts +3 -0
- package/dist/career-personality-test/constants/storageKeys.d.ts.map +1 -0
- package/dist/career-personality-test/constants/storageKeys.js +2 -0
- package/dist/career-personality-test/data/overallBands.d.ts +13 -0
- package/dist/career-personality-test/data/overallBands.d.ts.map +1 -0
- package/dist/career-personality-test/data/overallBands.js +10 -0
- package/dist/career-personality-test/data/questions.d.ts +16 -0
- package/dist/career-personality-test/data/questions.d.ts.map +1 -0
- package/dist/career-personality-test/data/questions.js +51 -0
- package/dist/career-personality-test/data/traits.d.ts +7 -0
- package/dist/career-personality-test/data/traits.d.ts.map +1 -0
- package/dist/career-personality-test/data/traits.js +15 -0
- package/dist/career-personality-test/index.d.ts +20 -0
- package/dist/career-personality-test/index.d.ts.map +1 -0
- package/dist/career-personality-test/index.js +14 -0
- package/dist/career-personality-test/utils/scoring.d.ts +18 -0
- package/dist/career-personality-test/utils/scoring.d.ts.map +1 -0
- package/dist/career-personality-test/utils/scoring.js +69 -0
- package/dist/components/ui/Badge.d.ts +10 -0
- package/dist/components/ui/Badge.d.ts.map +1 -0
- package/dist/components/ui/Badge.js +20 -0
- package/dist/components/ui/Button.d.ts +12 -0
- package/dist/components/ui/Button.d.ts.map +1 -0
- package/dist/components/ui/Button.js +31 -0
- package/dist/components/ui/DesignationSelect.d.ts +2 -1
- package/dist/components/ui/DesignationSelect.d.ts.map +1 -1
- package/dist/components/ui/DesignationSelect.js +11 -16
- package/dist/components/ui/IndustrySelect.d.ts +3 -1
- package/dist/components/ui/IndustrySelect.d.ts.map +1 -1
- package/dist/components/ui/IndustrySelect.js +7 -8
- package/dist/components/ui/MultiSelectOptions.d.ts +4 -4
- package/dist/components/ui/MultiSelectOptions.d.ts.map +1 -1
- package/dist/components/ui/MultiSelectOptions.js +13 -24
- package/dist/hooks/useDebounce.d.ts +3 -0
- package/dist/hooks/useDebounce.d.ts.map +1 -1
- package/dist/hooks/useDebounce.js +3 -0
- package/dist/hooks/useDesignations.d.ts +1 -9
- package/dist/hooks/useDesignations.d.ts.map +1 -1
- package/dist/hooks/useDesignations.js +59 -68
- package/dist/hooks/useIndustries.d.ts +4 -10
- package/dist/hooks/useIndustries.d.ts.map +1 -1
- package/dist/hooks/useIndustries.js +60 -69
- package/dist/hooks/useSkills.d.ts +1 -2
- package/dist/hooks/useSkills.d.ts.map +1 -1
- package/dist/hooks/useSkills.js +7 -7
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +13 -1
- package/dist/utils/cn.d.ts +2 -1
- package/dist/utils/cn.d.ts.map +1 -1
- package/dist/utils/cn.js +4 -2
- package/package.json +21 -3
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export type CareerFitTestLandingProps = {
|
|
2
|
+
/** Base path for routes, e.g. "/career-personality-test" */
|
|
3
|
+
basePath: string;
|
|
4
|
+
/** Base path for images, e.g. "/images/careerFitPersonality" */
|
|
5
|
+
imageBasePath: string;
|
|
6
|
+
/** Optional brand name for title */
|
|
7
|
+
brandName?: string;
|
|
8
|
+
};
|
|
9
|
+
export declare function CareerFitTestLanding({ basePath, imageBasePath, brandName }: CareerFitTestLandingProps): import("react/jsx-runtime").JSX.Element;
|
|
10
|
+
//# sourceMappingURL=CareerFitTestLanding.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CareerFitTestLanding.d.ts","sourceRoot":"","sources":["../../../src/career-personality-test/components/CareerFitTestLanding.tsx"],"names":[],"mappings":"AAkBA,MAAM,MAAM,yBAAyB,GAAG;IACtC,4DAA4D;IAC5D,QAAQ,EAAE,MAAM,CAAC;IACjB,gEAAgE;IAChE,aAAa,EAAE,MAAM,CAAC;IACtB,oCAAoC;IACpC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAiBF,wBAAgB,oBAAoB,CAAC,EAAE,QAAQ,EAAE,aAAa,EAAE,SAA2B,EAAE,EAAE,yBAAyB,2CAyGvH"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { ArrowRight, BarChart3, BookText, CheckCircle2, ChevronRight, Compass, Layers, Percent, PieChart, Sparkles, ThumbsUp, } from 'lucide-react';
|
|
4
|
+
import { Button } from '../../components/ui/Button';
|
|
5
|
+
const CARD_ITEMS = [
|
|
6
|
+
{ icon: BarChart3, title: 'Trait Percentages', desc: 'Your exact scores for every Big Five and RIASEC trait, weighted and normalized for clarity.' },
|
|
7
|
+
{ icon: PieChart, title: 'Pie Charts', desc: 'Two visuals: Big Five distribution and RIASEC interests. Compare strengths side by side.' },
|
|
8
|
+
{ icon: BookText, title: 'Personality Narrative', desc: 'Readable summary of how you plan, collaborate, communicate, and handle pressure.' },
|
|
9
|
+
{ icon: Layers, title: 'Top Career Fields', desc: 'Fields mapped to your strongest RIASEC pattern so you focus your search where you\'ll thrive.' },
|
|
10
|
+
{ icon: Compass, title: 'Role Recommendations', desc: 'A curated list of role ideas aligned to your top traits and interests — practical, not vague.' },
|
|
11
|
+
{ icon: Percent, title: 'Combined Career-Fit', desc: 'One overall score blending both models to show readiness and fit scope at a glance.' },
|
|
12
|
+
];
|
|
13
|
+
const WHY_ITEMS = [
|
|
14
|
+
{ icon: Sparkles, title: 'Clarity you can use', desc: 'Everyday language, not just scores — spot your strengths, blind spots, and energizers.' },
|
|
15
|
+
{ icon: ThumbsUp, title: 'Confidence to choose', desc: 'Know which problems to tackle, how you collaborate, and where you\'re most resilient.' },
|
|
16
|
+
{ icon: CheckCircle2, title: 'Action you can take', desc: 'Target roles that match your natural interests. Align your resume with authentic talking points.' },
|
|
17
|
+
];
|
|
18
|
+
export function CareerFitTestLanding({ basePath, imageBasePath, brandName = 'Recruit Nepal' }) {
|
|
19
|
+
return (_jsxs("main", { className: "container mx-auto px-6 py-10", children: [_jsxs("section", { className: "flex flex-col items-center text-center max-w-3xl mx-auto", children: [_jsx("div", { className: "mb-6", children: _jsxs("span", { className: "inline-flex items-center gap-2 rounded-full border px-3 py-1 text-xs text-muted-foreground bg-white", children: [_jsx("span", { className: "h-1.5 w-1.5 rounded-full bg-blue-500" }), "Career Fit Test"] }) }), _jsx("h1", { className: "text-5xl font-semibold text-primary mb-4", children: "Career Fit Test" }), _jsx("p", { className: "mt-3 text-base text-muted-foreground max-w-xl", children: "Discover your work style, strengths, and ideal career paths \u2014 all in one test." })] }), _jsxs("section", { className: "grid lg:grid-cols-2 gap-10 items-start mt-20", children: [_jsxs("div", { children: [_jsx("h3", { className: "font-semibold mb-3 text-lg", children: "What is the Career Fit Test ?" }), _jsx("p", { className: "text-muted-foreground", children: "Our personality test is a short assessment that measures your work behaviors and career interests. You'll answer 40 short items. Quality results need focused attention." }), _jsx("p", { className: "mt-4 text-muted-foreground", children: "You'll respond on a 5-point scale and a few Yes/No items. Each response maps to a trait with a defined weight, so your final result reflects both what you chose and how strongly it signals that trait." }), _jsx("p", { className: "mt-4 text-muted-foreground", children: "Our test uses two proven models:" }), _jsxs("ul", { className: "list-disc ml-5 text-muted-foreground", children: [_jsxs("li", { children: [_jsx("span", { className: "font-medium", children: "BigFive (BIG-O):" }), " Openness, Conscientiousness, Extraversion, Agreeableness, and Neuroticism."] }), _jsxs("li", { children: [_jsx("span", { className: "font-medium", children: "RIASEC:" }), " Realistic, Investigative, Artistic, Social, Enterprising, and Conventional."] })] }), _jsx("p", { className: "mt-4 text-muted-foreground", children: "The result is a clear set of trait percentages that reflect both what you chose and how strongly each answer signals a trait, so we can match you to roles and work environments you're likely to enjoy and excel in." })] }), _jsx("div", { className: "relative aspect-[4/3] w-full overflow-hidden rounded-xl bg-white", children: _jsx("img", { src: `${imageBasePath}/careerfit-home.png`, alt: "Career Fit Test illustration", className: "w-full h-full object-contain" }) })] }), _jsxs("section", { className: "mt-20", children: [_jsx("h2", { className: "text-3xl font-semibold text-primary text-center mb-8", children: "What Results You'll Get" }), _jsx("div", { className: "grid md:grid-cols-3 gap-6", children: CARD_ITEMS.map((item, i) => (_jsx("div", { className: "rounded-lg border bg-white p-5 shadow-sm", children: _jsxs("div", { className: "flex items-start gap-3", children: [_jsx("div", { className: "mt-0.5 text-primary", children: _jsx(item.icon, { className: "h-5 w-5" }) }), _jsxs("div", { children: [_jsx("p", { className: "font-medium", children: item.title }), _jsx("p", { className: "text-sm text-muted-foreground", children: item.desc })] })] }) }, i))) })] }), _jsxs("section", { className: "mt-20", children: [_jsx("h2", { className: "text-3xl font-semibold text-primary text-center mb-8", children: "Why take the test?" }), _jsx("p", { className: "text-center text-sm text-muted-foreground mt-2", children: "In 10 minutes, this 40-question, weighted assessment turns your work style (BIG-O) and interests (RIASEC) into a plan you can use today." }), _jsx("div", { className: "mt-10 grid md:grid-cols-3 gap-6", children: WHY_ITEMS.map((f, i) => (_jsxs("div", { className: "rounded-xl border bg-white p-6 text-center", children: [_jsx("div", { className: "mx-auto mb-3 w-10 h-10 grid place-items-center rounded-full bg-blue-50 text-blue-600", children: _jsx(f.icon, { className: "h-6 w-6" }) }), _jsx("p", { className: "font-medium", children: f.title }), _jsx("p", { className: "text-sm text-muted-foreground", children: f.desc })] }, i))) }), _jsx("div", { className: "mt-8 rounded-xl border border-blue-100 bg-[#F5F9FF] p-5 shadow-sm", children: _jsxs("div", { className: "grid md:grid-cols-3 gap-4 items-center", children: [_jsxs("div", { children: [_jsx("p", { className: "text-xs font-semibold text-[#155177]", children: "BEFORE:" }), _jsx("p", { className: "text-sm text-gray-700", children: "Guessing, generic applications, mismatch fatigue." })] }), _jsx("div", { className: "hidden md:flex items-center justify-center text-[#155177]", children: _jsx(ChevronRight, { className: "h-5 w-5" }) }), _jsxs("div", { children: [_jsx("p", { className: "text-xs font-semibold text-[#155177]", children: "AFTER:" }), _jsx("p", { className: "text-sm text-gray-700", children: "Targeted roles, a clear story for interviews, momentum in the next 30\u201360 days." })] })] }) }), _jsx("div", { className: "mt-8 flex items-center justify-center", children: _jsx(Button, { asChild: true, size: "lg", className: "px-8", children: _jsxs("a", { href: `${basePath}/details`, className: "flex items-center gap-2", children: ["Start the Test", _jsx(ArrowRight, { className: "h-4 w-4" })] }) }) }), _jsx("p", { className: "mt-3 text-center text-xs text-muted-foreground", children: "Weighted scoring \u2022 BIG-O + RIASEC \u2022 Private by default" })] })] }));
|
|
20
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export type CareerTestDetailsFormInitial = {
|
|
2
|
+
firstName?: string;
|
|
3
|
+
lastName?: string;
|
|
4
|
+
email?: string;
|
|
5
|
+
phone?: string;
|
|
6
|
+
education?: string;
|
|
7
|
+
};
|
|
8
|
+
export type CareerTestDetailsFormProps = {
|
|
9
|
+
/** Base path, e.g. "/career-personality-test" */
|
|
10
|
+
basePath: string;
|
|
11
|
+
/** Callback to navigate (e.g. router.push) */
|
|
12
|
+
navigate: (path: string) => void;
|
|
13
|
+
/** Optional: load initial values (e.g. from API) */
|
|
14
|
+
getInitialData?: () => CareerTestDetailsFormInitial | Promise<CareerTestDetailsFormInitial>;
|
|
15
|
+
/** Optional: persist to backend before proceeding */
|
|
16
|
+
onSubmit?: (payload: CareerTestDetailsFormPayload) => Promise<void>;
|
|
17
|
+
/** Session storage key for user payload; default CAREER_TEST_USER_KEY */
|
|
18
|
+
storageKeyUser?: string;
|
|
19
|
+
};
|
|
20
|
+
export type CareerTestDetailsFormPayload = {
|
|
21
|
+
firstName: string;
|
|
22
|
+
lastName?: string;
|
|
23
|
+
email: string;
|
|
24
|
+
phone?: string;
|
|
25
|
+
education?: string;
|
|
26
|
+
};
|
|
27
|
+
export declare function CareerTestDetailsForm({ basePath, navigate, getInitialData, onSubmit, storageKeyUser, }: CareerTestDetailsFormProps): import("react/jsx-runtime").JSX.Element;
|
|
28
|
+
//# sourceMappingURL=CareerTestDetailsForm.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CareerTestDetailsForm.d.ts","sourceRoot":"","sources":["../../../src/career-personality-test/components/CareerTestDetailsForm.tsx"],"names":[],"mappings":"AAMA,MAAM,MAAM,4BAA4B,GAAG;IACzC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,0BAA0B,GAAG;IACvC,iDAAiD;IACjD,QAAQ,EAAE,MAAM,CAAC;IACjB,8CAA8C;IAC9C,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC,oDAAoD;IACpD,cAAc,CAAC,EAAE,MAAM,4BAA4B,GAAG,OAAO,CAAC,4BAA4B,CAAC,CAAC;IAC5F,qDAAqD;IACrD,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,4BAA4B,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACpE,yEAAyE;IACzE,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,4BAA4B,GAAG;IACzC,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAgBF,wBAAgB,qBAAqB,CAAC,EACpC,QAAQ,EACR,QAAQ,EACR,cAAc,EACd,QAAQ,EACR,cAAqC,GACtC,EAAE,0BAA0B,2CAoJ5B"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
import { cn } from '../../utils/cn';
|
|
5
|
+
import { CAREER_TEST_USER_KEY } from '../constants/storageKeys';
|
|
6
|
+
const EDUCATION_OPTIONS = [
|
|
7
|
+
{ value: 'below_slc_see', label: 'Below SLC/SEE' },
|
|
8
|
+
{ value: 'school', label: 'SLC/SEE' },
|
|
9
|
+
{ value: 'diploma', label: 'Diploma' },
|
|
10
|
+
{ value: 'intermediate', label: 'Intermediate (+2)' },
|
|
11
|
+
{ value: 'bachelor', label: "Bachelor's Degree" },
|
|
12
|
+
{ value: 'post_graduate_diploma', label: 'Post Graduate Diploma' },
|
|
13
|
+
{ value: 'master', label: "Master's Degree" },
|
|
14
|
+
{ value: 'mphil', label: 'MPhil' },
|
|
15
|
+
{ value: 'phd', label: 'PHD' },
|
|
16
|
+
];
|
|
17
|
+
export function CareerTestDetailsForm({ basePath, navigate, getInitialData, onSubmit, storageKeyUser = CAREER_TEST_USER_KEY, }) {
|
|
18
|
+
const [firstName, setFirstName] = React.useState('');
|
|
19
|
+
const [lastName, setLastName] = React.useState('');
|
|
20
|
+
const [email, setEmail] = React.useState('');
|
|
21
|
+
const [phone, setPhone] = React.useState('');
|
|
22
|
+
const [education, setEducation] = React.useState('');
|
|
23
|
+
const [submitting, setSubmitting] = React.useState(false);
|
|
24
|
+
const [errors, setErrors] = React.useState({});
|
|
25
|
+
React.useEffect(() => {
|
|
26
|
+
const load = async () => {
|
|
27
|
+
if (!getInitialData)
|
|
28
|
+
return;
|
|
29
|
+
const data = await getInitialData();
|
|
30
|
+
if (data?.firstName && !firstName)
|
|
31
|
+
setFirstName(data.firstName ?? '');
|
|
32
|
+
if (data?.lastName !== undefined && !lastName)
|
|
33
|
+
setLastName(data.lastName ?? '');
|
|
34
|
+
if (data?.email && !email)
|
|
35
|
+
setEmail(data.email ?? '');
|
|
36
|
+
if (data?.phone !== undefined && !phone)
|
|
37
|
+
setPhone(data.phone ?? '');
|
|
38
|
+
if (data?.education && !education)
|
|
39
|
+
setEducation(data.education ?? '');
|
|
40
|
+
};
|
|
41
|
+
load();
|
|
42
|
+
}, [getInitialData]);
|
|
43
|
+
const validate = () => {
|
|
44
|
+
const e = {};
|
|
45
|
+
if (!firstName.trim())
|
|
46
|
+
e.firstName = 'First name is required';
|
|
47
|
+
else if (firstName.trim().length > 50)
|
|
48
|
+
e.firstName = 'Max 50 characters';
|
|
49
|
+
if (lastName.trim().length > 50)
|
|
50
|
+
e.lastName = 'Max 50 characters';
|
|
51
|
+
const em = email.trim().toLowerCase();
|
|
52
|
+
if (!em)
|
|
53
|
+
e.email = 'Email is required';
|
|
54
|
+
else if (!/^[a-z0-9._%+-]+@gmail\.com$/i.test(em))
|
|
55
|
+
e.email = 'Please use a valid @gmail.com address';
|
|
56
|
+
if (phone.trim() && !/^[0-9+\-\s()]{6,20}$/.test(phone.trim()))
|
|
57
|
+
e.phone = 'Enter a valid phone (or leave blank)';
|
|
58
|
+
setErrors(e);
|
|
59
|
+
return Object.keys(e).length === 0;
|
|
60
|
+
};
|
|
61
|
+
const handleSubmit = async (ev) => {
|
|
62
|
+
ev.preventDefault();
|
|
63
|
+
if (!validate())
|
|
64
|
+
return;
|
|
65
|
+
setSubmitting(true);
|
|
66
|
+
const payload = {
|
|
67
|
+
firstName: firstName.trim(),
|
|
68
|
+
lastName: lastName.trim() || undefined,
|
|
69
|
+
email: email.trim().toLowerCase(),
|
|
70
|
+
phone: phone.trim() || undefined,
|
|
71
|
+
education: education.trim() || undefined,
|
|
72
|
+
};
|
|
73
|
+
try {
|
|
74
|
+
if (onSubmit)
|
|
75
|
+
await onSubmit(payload);
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
// ignore
|
|
79
|
+
}
|
|
80
|
+
try {
|
|
81
|
+
if (typeof window !== 'undefined')
|
|
82
|
+
window.sessionStorage.setItem(storageKeyUser, JSON.stringify(payload));
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
// ignore
|
|
86
|
+
}
|
|
87
|
+
setSubmitting(false);
|
|
88
|
+
navigate(`${basePath}/start`);
|
|
89
|
+
};
|
|
90
|
+
return (_jsx("main", { className: "container mx-auto px-4 md:px-6 py-10 md:py-14", children: _jsxs("div", { className: "mx-auto max-w-3xl rounded-2xl border shadow-sm bg-white p-6 md:p-10 relative", children: [_jsx("div", { className: "pointer-events-none absolute inset-0 rounded-2xl ring-2 ring-[#4DA0E6]/80" }), _jsx("h1", { className: "text-center text-3xl md:text-4xl font-semibold text-[#2E7DBE]", children: "Let's Get Started" }), _jsxs("p", { className: "mt-2 text-center text-sm text-slate-600", children: ["We'll use this to personalize your report and show", _jsx("br", { className: "hidden md:block" }), " your name on the results page."] }), _jsxs("form", { onSubmit: handleSubmit, className: "mt-8 space-y-5", children: [_jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4", children: [_jsxs("div", { children: [_jsx("label", { className: "block text-[12px] text-slate-600 mb-1", children: "First Name" }), _jsx("input", { type: "text", value: firstName, onChange: (e) => setFirstName(e.target.value), className: cn('w-full rounded-lg border px-3 py-2.5 text-sm outline-none', errors.firstName ? 'border-red-400 focus:ring-2 focus:ring-red-200' : 'border-slate-200 focus:ring-2 focus:ring-slate-200'), maxLength: 50, autoComplete: "given-name" }), errors.firstName && _jsx("p", { className: "mt-1 text-xs text-red-500", children: errors.firstName })] }), _jsxs("div", { children: [_jsx("label", { className: "block text-[12px] text-slate-600 mb-1", children: "Last Name" }), _jsx("input", { type: "text", value: lastName, onChange: (e) => setLastName(e.target.value), className: cn('w-full rounded-lg border px-3 py-2.5 text-sm outline-none', errors.lastName ? 'border-red-400 focus:ring-2 focus:ring-red-200' : 'border-slate-200 focus:ring-2 focus:ring-slate-200'), maxLength: 50, autoComplete: "family-name" }), errors.lastName && _jsx("p", { className: "mt-1 text-xs text-red-500", children: errors.lastName })] })] }), _jsxs("div", { children: [_jsx("label", { className: "block text-[12px] text-slate-600 mb-1", children: "Email" }), _jsx("input", { type: "email", value: email, onChange: (e) => setEmail(e.target.value), className: cn('w-full rounded-lg border px-3 py-2.5 text-sm outline-none', errors.email ? 'border-red-400 focus:ring-2 focus:ring-red-200' : 'border-slate-200 focus:ring-2 focus:ring-slate-200'), autoComplete: "email" }), errors.email && _jsx("p", { className: "mt-1 text-xs text-red-500", children: errors.email })] }), _jsxs("div", { children: [_jsx("label", { className: "block text-[12px] text-slate-600 mb-1", children: "Phone Number" }), _jsx("input", { type: "tel", value: phone, onChange: (e) => setPhone(e.target.value), className: cn('w-full rounded-lg border px-3 py-2.5 text-sm outline-none', errors.phone ? 'border-red-400 focus:ring-2 focus:ring-red-200' : 'border-slate-200 focus:ring-2 focus:ring-slate-200'), placeholder: "Optional", autoComplete: "tel" }), errors.phone && _jsx("p", { className: "mt-1 text-xs text-red-500", children: errors.phone })] }), _jsxs("div", { children: [_jsx("label", { className: "block text-[12px] text-slate-600 mb-1", children: "Education Level" }), _jsxs("select", { value: education, onChange: (e) => setEducation(e.target.value), className: cn('w-full rounded-lg border px-3 py-2.5 text-sm outline-none', errors.education ? 'border-red-400 focus:ring-2 focus:ring-red-200' : 'border-slate-200 focus:ring-2 focus:ring-slate-200'), children: [_jsx("option", { value: "", children: "Select education level" }), EDUCATION_OPTIONS.map((o) => (_jsx("option", { value: o.value, children: o.label }, o.value)))] }), errors.education && _jsx("p", { className: "mt-1 text-xs text-red-500", children: errors.education })] }), _jsx("p", { className: "text-center text-[11px] text-slate-500", children: "We won't share your details with employers without your permission." }), _jsx("div", { className: "flex justify-center pt-1", children: _jsx("button", { type: "submit", disabled: submitting, className: cn('w-full md:w-[280px] rounded-lg bg-[#4D97D9] px-5 py-3 text-white text-base font-semibold shadow-sm', submitting && 'opacity-70 cursor-not-allowed'), children: submitting ? 'Please wait…' : 'Start the Test' }) }), errors.form && _jsx("p", { className: "text-center text-sm text-red-500 mt-2", children: errors.form })] })] }) }));
|
|
91
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { TraitName } from '../utils/scoring';
|
|
2
|
+
export type StoredResults = {
|
|
3
|
+
overallPercentage: number;
|
|
4
|
+
frameworkPercentages?: {
|
|
5
|
+
BIGO: number;
|
|
6
|
+
RIASEC: number;
|
|
7
|
+
};
|
|
8
|
+
traitPercentages?: Partial<Record<TraitName, number>>;
|
|
9
|
+
};
|
|
10
|
+
export type CareerTestResultsPageProps = {
|
|
11
|
+
basePath: string;
|
|
12
|
+
imageBasePath: string;
|
|
13
|
+
/** Optional: save result to backend (e.g. POST /api/career-test) */
|
|
14
|
+
onSaveResult?: (payload: {
|
|
15
|
+
firstName?: string;
|
|
16
|
+
lastName?: string;
|
|
17
|
+
email?: string;
|
|
18
|
+
overallPct: number;
|
|
19
|
+
bigoPct: number;
|
|
20
|
+
riasecPct: number;
|
|
21
|
+
personaType: string;
|
|
22
|
+
}) => Promise<void>;
|
|
23
|
+
storageKeyResults?: string;
|
|
24
|
+
storageKeyUser?: string;
|
|
25
|
+
};
|
|
26
|
+
export declare function CareerTestResultsPage({ basePath, imageBasePath, onSaveResult, storageKeyResults, storageKeyUser, }: CareerTestResultsPageProps): import("react/jsx-runtime").JSX.Element;
|
|
27
|
+
//# sourceMappingURL=CareerTestResultsPage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CareerTestResultsPage.d.ts","sourceRoot":"","sources":["../../../src/career-personality-test/components/CareerTestResultsPage.tsx"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAGlD,MAAM,MAAM,aAAa,GAAG;IAC1B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,oBAAoB,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IACxD,gBAAgB,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC,CAAC;CACvD,CAAC;AAEF,MAAM,MAAM,0BAA0B,GAAG;IACvC,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,oEAAoE;IACpE,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE;QACvB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,UAAU,EAAE,MAAM,CAAC;QACnB,OAAO,EAAE,MAAM,CAAC;QAChB,SAAS,EAAE,MAAM,CAAC;QAClB,WAAW,EAAE,MAAM,CAAC;KACrB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACpB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,wBAAgB,qBAAqB,CAAC,EACpC,QAAQ,EACR,aAAa,EACb,YAAY,EACZ,iBAA2C,EAC3C,cAAqC,GACtC,EAAE,0BAA0B,2CAsR5B"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
import { BarChart, Bar, XAxis, YAxis, Tooltip, ResponsiveContainer, LabelList, CartesianGrid, } from 'recharts';
|
|
5
|
+
import { Button } from '../../components/ui/Button';
|
|
6
|
+
import { BIGO_LETTERS, RIASEC_LETTERS, BIGO_COMBOS, RIASEC_COMBOS, RIASEC_ROLES, pickPersonaBand, pickWorkStyleBand, } from '../constants/resultConstants';
|
|
7
|
+
import { CAREER_TEST_RESULTS_KEY, CAREER_TEST_USER_KEY } from '../constants/storageKeys';
|
|
8
|
+
export function CareerTestResultsPage({ basePath, imageBasePath, onSaveResult, storageKeyResults = CAREER_TEST_RESULTS_KEY, storageKeyUser = CAREER_TEST_USER_KEY, }) {
|
|
9
|
+
const [data, setData] = React.useState(null);
|
|
10
|
+
const [user, setUser] = React.useState(null);
|
|
11
|
+
const hasSavedRef = React.useRef(false);
|
|
12
|
+
React.useEffect(() => {
|
|
13
|
+
try {
|
|
14
|
+
const raw = typeof window !== 'undefined' ? window.sessionStorage.getItem(storageKeyResults) : null;
|
|
15
|
+
if (!raw)
|
|
16
|
+
return;
|
|
17
|
+
setData(JSON.parse(raw));
|
|
18
|
+
}
|
|
19
|
+
catch (e) {
|
|
20
|
+
console.error('Failed to load careerTestResults:', e);
|
|
21
|
+
}
|
|
22
|
+
}, [storageKeyResults]);
|
|
23
|
+
React.useEffect(() => {
|
|
24
|
+
try {
|
|
25
|
+
const raw = typeof window !== 'undefined' ? window.sessionStorage.getItem(storageKeyUser) : null;
|
|
26
|
+
if (raw)
|
|
27
|
+
setUser(JSON.parse(raw));
|
|
28
|
+
}
|
|
29
|
+
catch (e) {
|
|
30
|
+
console.error('Failed to load user from sessionStorage', e);
|
|
31
|
+
}
|
|
32
|
+
}, [storageKeyUser]);
|
|
33
|
+
const hasData = !!data;
|
|
34
|
+
const overall = Math.round(data?.overallPercentage ?? 0);
|
|
35
|
+
const persona = pickPersonaBand(overall);
|
|
36
|
+
const workStyleBand = pickWorkStyleBand(overall);
|
|
37
|
+
const bigoPct = Math.round(data?.frameworkPercentages?.BIGO ?? 0);
|
|
38
|
+
const riasecPct = Math.round(data?.frameworkPercentages?.RIASEC ?? 0);
|
|
39
|
+
const tp = data?.traitPercentages ?? {};
|
|
40
|
+
const bigoChartData = [
|
|
41
|
+
{ name: 'Openness', value: tp.Openness ?? 0 },
|
|
42
|
+
{ name: 'Conscientiousness', value: tp.Conscientiousness ?? 0 },
|
|
43
|
+
{ name: 'Extraversion', value: tp.Extraversion ?? 0 },
|
|
44
|
+
{ name: 'Agreeableness', value: tp.Agreeableness ?? 0 },
|
|
45
|
+
{ name: 'Neuroticism', value: tp.Neuroticism ?? 0 },
|
|
46
|
+
];
|
|
47
|
+
const riasecChartData = [
|
|
48
|
+
{ name: 'Realistic', value: tp.Realistic ?? 0 },
|
|
49
|
+
{ name: 'Investigative', value: tp.Investigative ?? 0 },
|
|
50
|
+
{ name: 'Artistic', value: tp.Artistic ?? 0 },
|
|
51
|
+
{ name: 'Social', value: tp.Social ?? 0 },
|
|
52
|
+
{ name: 'Enterprising', value: tp.Enterprising ?? 0 },
|
|
53
|
+
{ name: 'Conventional', value: tp.Conventional ?? 0 },
|
|
54
|
+
];
|
|
55
|
+
const top3Bigo = [...bigoChartData].sort((a, b) => b.value - a.value).slice(0, 3);
|
|
56
|
+
const top3BigoLetters = top3Bigo.map((t) => BIGO_LETTERS[t.name]).filter((x) => !!x);
|
|
57
|
+
const bigoComboDisplay = top3BigoLetters.length === 3 ? top3BigoLetters.join(' + ') : '—';
|
|
58
|
+
const bigoComboKey = top3BigoLetters.length === 3 ? [...top3BigoLetters].sort().join('') : '';
|
|
59
|
+
const bigoComboDescription = (bigoComboKey && BIGO_COMBOS[bigoComboKey]) || "Your top Big-O traits combine into a unique working style. As you complete more projects and reflect on what energizes you, this pattern will become even clearer.";
|
|
60
|
+
const top3Riasec = [...riasecChartData].sort((a, b) => b.value - a.value).slice(0, 3);
|
|
61
|
+
const top3RiasecLetters = top3Riasec.map((t) => RIASEC_LETTERS[t.name]).filter((x) => !!x);
|
|
62
|
+
const riasecComboDisplay = top3RiasecLetters.length === 3 ? top3RiasecLetters.join(' + ') : '—';
|
|
63
|
+
const riasecComboKey = top3RiasecLetters.length === 3 ? [...top3RiasecLetters].sort().join('') : '';
|
|
64
|
+
const riasecComboDescription = (riasecComboKey && RIASEC_COMBOS[riasecComboKey]) || "Your top RIASEC interests form a starting point for exploring roles and environments where you'll feel naturally engaged.";
|
|
65
|
+
const matchedRoles = (riasecComboKey && RIASEC_ROLES[riasecComboKey]) || [];
|
|
66
|
+
React.useEffect(() => {
|
|
67
|
+
if (!data || !user || hasSavedRef.current || !onSaveResult)
|
|
68
|
+
return;
|
|
69
|
+
const saveResult = async () => {
|
|
70
|
+
try {
|
|
71
|
+
await onSaveResult({
|
|
72
|
+
firstName: user.firstName,
|
|
73
|
+
lastName: user.lastName,
|
|
74
|
+
email: user.email,
|
|
75
|
+
overallPct: overall,
|
|
76
|
+
bigoPct,
|
|
77
|
+
riasecPct,
|
|
78
|
+
personaType: persona.title,
|
|
79
|
+
});
|
|
80
|
+
hasSavedRef.current = true;
|
|
81
|
+
}
|
|
82
|
+
catch (e) {
|
|
83
|
+
console.error('Failed to save test result:', e);
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
saveResult();
|
|
87
|
+
}, [data, user, overall, bigoPct, riasecPct, persona.title, onSaveResult]);
|
|
88
|
+
const handleDownloadPdf = () => {
|
|
89
|
+
if (typeof window !== 'undefined')
|
|
90
|
+
window.print();
|
|
91
|
+
};
|
|
92
|
+
const handleShareLink = async () => {
|
|
93
|
+
if (typeof window === 'undefined')
|
|
94
|
+
return;
|
|
95
|
+
const url = window.location.href;
|
|
96
|
+
if (navigator.share) {
|
|
97
|
+
try {
|
|
98
|
+
await navigator.share({ title: 'My Career Fit Results', text: 'Here are my BIG-O + RIASEC career fit results.', url });
|
|
99
|
+
}
|
|
100
|
+
catch (err) {
|
|
101
|
+
console.error('Share failed:', err);
|
|
102
|
+
}
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
if (navigator.clipboard) {
|
|
106
|
+
try {
|
|
107
|
+
await navigator.clipboard.writeText(url);
|
|
108
|
+
alert('Link copied to clipboard!');
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
alert(`Copy this link: ${url}`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
alert(`Copy this link: ${url}`);
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
const personaImageSrc = `${imageBasePath}/${persona.image}`;
|
|
119
|
+
const workStyleImageSrc = `${imageBasePath}/${workStyleBand.image}`;
|
|
120
|
+
return (_jsxs("main", { className: "container mx-auto px-4 md:px-6 py-6 md:py-10", children: [_jsxs("div", { className: "flex items-end justify-end gap-4 text-xs text-[#155177]", children: [_jsx("button", { type: "button", onClick: handleDownloadPdf, className: "hover:underline", children: "Download PDF" }), _jsx("button", { type: "button", onClick: handleShareLink, className: "hover:underline", children: "Share Link" })] }), !hasData ? (_jsxs("section", { className: "mt-6 max-w-3xl", children: [_jsx("h1", { className: "text-2xl font-semibold text-[#155177]", children: "Your Results" }), _jsx("p", { className: "mt-2 text-sm text-muted-foreground", children: "We couldn't find your test data in this session. Please take the test again." }), _jsx("div", { className: "mt-4", children: _jsx(Button, { asChild: true, children: _jsx("a", { href: `${basePath}/start`, children: "Retake the Test" }) }) })] })) : (_jsxs(_Fragment, { children: [_jsxs("section", { className: "mt-3 grid md:grid-cols-[1fr_180px] gap-4", children: [_jsxs("div", { className: "rounded-2xl border bg-white px-5 py-5 shadow-sm", children: [_jsxs("h1", { className: "text-2xl md:text-3xl font-semibold text-[#155177]", children: ["Hello, ", _jsx("span", { className: "opacity-70", children: user?.firstName ? user.firstName : 'there' })] }), _jsx("p", { className: "mt-1 text-[11px] text-muted-foreground", children: "Based on BIG-O + RIASEC, here's how you work and where you'll thrive." })] }), _jsxs("div", { className: "rounded-2xl border bg-white px-6 py-5 shadow-sm text-center", children: [_jsx("p", { className: "text-[11px] text-muted-foreground font-medium", children: "Overall Match" }), _jsxs("p", { className: "mt-1 text-2xl md:text-3xl font-bold", children: [overall, "%"] })] })] }), _jsxs("section", { className: "mt-6 rounded-2xl overflow-hidden border shadow-sm", children: [_jsxs("div", { className: "relative bg-[#24486a] text-white px-6 md:px-10 pt-10 pb-20", children: [_jsxs("div", { className: "grid md:grid-cols-5 gap-10 items-center", children: [_jsx("div", { className: "md:col-span-2 flex justify-end relative", children: _jsx("div", { className: "relative h-44 w-44 md:h-56 md:w-56 drop-shadow-2xl translate-x-10 md:translate-x-12", children: _jsx("img", { src: personaImageSrc, alt: persona.title, className: "w-full h-full object-contain" }) }) }), _jsxs("div", { className: "md:col-span-3", children: [_jsx("p", { className: "text-xs md:text-sm opacity-80", children: "Your Personality Type is:" }), _jsx("h2", { className: "mt-1 text-3xl md:text-4xl font-semibold text-[#2697D4]", children: persona.title }), _jsx("p", { className: "mt-3 text-sm md:text-base font-medium", children: persona.subtitle })] })] }), _jsx("div", { className: "pointer-events-none absolute inset-x-0 bottom-0 h-16 text-white", children: _jsx("svg", { viewBox: "0 0 1440 320", preserveAspectRatio: "none", className: "w-full h-full", children: _jsx("path", { fill: "#ffffff", d: "M0,224L48,213.3C96,203,192,181,288,165.3C384,149,480,139,576,149.3C672,160,768,192,864,208C960,224,1056,224,1152,213.3C1248,203,1344,181,1392,170.7L1440,160L1440,320L1392,320C1344,320,1248,320,1152,320C1056,320,960,320,864,320C768,320,672,320,576,320C480,320,384,320,288,320C192,320,96,320,48,320L0,320Z" }) }) })] }), _jsxs("div", { className: "bg-white px-6 md:px-10 pb-8 pt-2", children: [_jsx("p", { className: "text-sm md:text-base leading-6 text-slate-700 whitespace-pre-line", children: persona.description }), _jsxs("div", { className: "mt-5", children: [_jsx("h4", { className: "text-sm md:text-base font-semibold text-[#155177]", children: "Positive Attributes" }), _jsx("ul", { className: "mt-2 list-disc pl-5 text-sm md:text-base text-slate-700 space-y-1", children: persona.positiveAttributes.map((attr) => (_jsx("li", { children: attr }, attr))) })] })] })] }), _jsxs("section", { className: "mt-8", children: [_jsx("h3", { className: "text-center text-[#155177] text-lg md:text-xl font-semibold", children: "BIG-O Traits" }), _jsx("div", { className: "mt-3 rounded-xl border bg-white p-4 shadow-sm", children: _jsx("div", { className: "h-72 w-full", children: _jsx(ResponsiveContainer, { width: "100%", height: "100%", children: _jsxs(BarChart, { data: bigoChartData, margin: { top: 20, right: 20, left: 0, bottom: 40 }, children: [_jsx(CartesianGrid, { strokeDasharray: "3 3", vertical: false }), _jsx(XAxis, { dataKey: "name", interval: 0, tick: { fontSize: 11 } }), _jsx(YAxis, { tick: { fontSize: 11 }, domain: [0, 100], tickFormatter: (v) => `${v}%` }), _jsx(Tooltip, { formatter: (v) => `${v}%` }), _jsx(Bar, { dataKey: "value", radius: [6, 6, 0, 0], fill: "#4D7CF3", children: _jsx(LabelList, { dataKey: "value", position: "top", formatter: (label) => (label != null ? `${label}%` : ''), style: { fontSize: 11 } }) })] }) }) }) }), _jsxs("div", { className: "mt-3 grid md:grid-cols-2 gap-4", children: [_jsxs("div", { className: "rounded-xl border bg-white p-4 shadow-sm", children: [_jsxs("p", { className: "text-xs font-semibold text-[#155177]", children: ["Top 3 Big-O: ", _jsx("span", { className: "font-bold", children: bigoComboDisplay || '—' })] }), _jsx("ul", { className: "mt-2 ml-4 list-disc text-xs text-slate-700 space-y-1", children: top3Bigo.map((t) => (_jsx("li", { children: t.name }, t.name))) })] }), _jsx("div", { className: "rounded-xl border bg-white p-4 shadow-sm", children: _jsx("p", { className: "text-xs text-muted-foreground whitespace-pre-line", children: bigoComboDescription }) })] })] }), _jsxs("section", { className: "mt-8", children: [_jsx("h3", { className: "text-center text-[#155177] text-lg md:text-xl font-semibold", children: "RIASEC Traits" }), _jsx("div", { className: "mt-3 rounded-xl border bg-white p-4 shadow-sm", children: _jsx("div", { className: "h-72 w-full", children: _jsx(ResponsiveContainer, { width: "100%", height: "100%", children: _jsxs(BarChart, { data: riasecChartData, margin: { top: 20, right: 20, left: 0, bottom: 40 }, children: [_jsx(CartesianGrid, { strokeDasharray: "3 3", vertical: false }), _jsx(XAxis, { dataKey: "name", interval: 0, tick: { fontSize: 11 } }), _jsx(YAxis, { tick: { fontSize: 11 }, domain: [0, 100], tickFormatter: (v) => `${v}%` }), _jsx(Tooltip, { formatter: (v) => `${v}%` }), _jsx(Bar, { dataKey: "value", radius: [6, 6, 0, 0], fill: "#4D7CF3", children: _jsx(LabelList, { dataKey: "value", position: "top", formatter: (label) => (label != null ? `${label}%` : ''), style: { fontSize: 11 } }) })] }) }) }) }), _jsxs("div", { className: "mt-3 grid md:grid-cols-2 gap-4", children: [_jsxs("div", { className: "rounded-xl border bg-white p-4 shadow-sm", children: [_jsxs("p", { className: "text-xs font-semibold text-[#155177]", children: ["Top 3 RIASEC: ", _jsx("span", { className: "font-bold", children: riasecComboDisplay || '—' })] }), _jsx("ul", { className: "mt-2 ml-4 list-disc text-xs text-slate-700 space-y-1", children: top3Riasec.map((t) => (_jsx("li", { children: t.name }, t.name))) })] }), _jsx("div", { className: "rounded-xl border bg-white p-4 shadow-sm", children: _jsx("p", { className: "text-xs text-muted-foreground whitespace-pre-line", children: riasecComboDescription }) })] })] }), _jsx("section", { className: "mt-8 rounded-2xl border bg-white shadow-sm overflow-hidden", children: _jsxs("div", { className: "grid md:grid-cols-[220px_1fr] gap-0", children: [_jsx("div", { className: "p-6 md:p-8 flex items-center justify-center", children: _jsx("div", { className: "relative h-32 w-32 md:h-40 md:w-40", children: _jsx("img", { src: workStyleImageSrc, alt: "Work-style personality", className: "w-full h-full object-contain" }) }) }), _jsxs("div", { className: "p-6 md:p-8", children: [_jsx("h4", { className: "text-[#2697D4] text-xl md:text-2xl font-semibold", children: "Work-Style Personality" }), _jsx("p", { className: "mt-3 text-sm md:text-base text-slate-700 whitespace-pre-line leading-6", children: workStyleBand.text })] })] }) }), _jsxs("section", { className: "mt-8", children: [_jsxs("h4", { className: "text-[#155177] font-semibold text-lg md:text-xl", children: ["Matched Roles For ", riasecComboDisplay || '—'] }), _jsx("div", { className: "mt-3 grid sm:grid-cols-2 lg:grid-cols-3 gap-3", children: matchedRoles.length === 0 ? (_jsx("p", { className: "text-xs text-muted-foreground", children: "Once you complete the full assessment, you'll see matched role suggestions here." })) : (matchedRoles.map((role) => (_jsx("div", { className: "rounded-full border bg-white px-4 py-2 text-xs shadow-sm text-slate-700 text-center", children: role }, role)))) })] }), _jsx("div", { className: "mt-10 flex items-center justify-center gap-3", children: _jsx(Button, { variant: "outline", asChild: true, children: _jsx("a", { href: `${basePath}/start`, children: "Retake Test" }) }) })] }))] }));
|
|
121
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export type CareerTestStartProps = {
|
|
2
|
+
basePath: string;
|
|
3
|
+
navigate: (path: string) => void;
|
|
4
|
+
storageKeyResults?: string;
|
|
5
|
+
};
|
|
6
|
+
export declare function CareerTestStart({ basePath, navigate, storageKeyResults }: CareerTestStartProps): import("react/jsx-runtime").JSX.Element;
|
|
7
|
+
//# sourceMappingURL=CareerTestStart.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CareerTestStart.d.ts","sourceRoot":"","sources":["../../../src/career-personality-test/components/CareerTestStart.tsx"],"names":[],"mappings":"AA4BA,MAAM,MAAM,oBAAoB,GAAG;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B,CAAC;AAsDF,wBAAgB,eAAe,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,iBAA2C,EAAE,EAAE,oBAAoB,2CAmGxH"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
import { ChevronRight } from 'lucide-react';
|
|
5
|
+
import { Button } from '../../components/ui/Button';
|
|
6
|
+
import { QUESTIONS_BY_SECTION } from '../data/questions';
|
|
7
|
+
import { computeOverall } from '../utils/scoring';
|
|
8
|
+
import { CAREER_TEST_RESULTS_KEY } from '../constants/storageKeys';
|
|
9
|
+
import { QUESTIONS } from '../data/questions';
|
|
10
|
+
const TOTAL_SECTIONS = 6;
|
|
11
|
+
const NAV_OFFSET = 96;
|
|
12
|
+
const SCALE = [
|
|
13
|
+
{ key: 1, label: 'Strongly\nDisagree', ring: 'ring-red-500' },
|
|
14
|
+
{ key: 2, label: 'Disagree', ring: 'ring-red-400' },
|
|
15
|
+
{ key: 3, label: 'Neutral', ring: 'ring-blue-400' },
|
|
16
|
+
{ key: 4, label: 'Agree', ring: 'ring-green-400' },
|
|
17
|
+
{ key: 5, label: 'Strongly\nAgree', ring: 'ring-green-500' },
|
|
18
|
+
];
|
|
19
|
+
function smoothScrollToEl(el) {
|
|
20
|
+
if (!el)
|
|
21
|
+
return;
|
|
22
|
+
const top = el.getBoundingClientRect().top + window.pageYOffset - NAV_OFFSET;
|
|
23
|
+
window.scrollTo({ top, behavior: 'smooth' });
|
|
24
|
+
}
|
|
25
|
+
function QuestionRow({ q, value, onChange, setRef, }) {
|
|
26
|
+
return (_jsxs("div", { className: "text-center", ref: setRef, children: [_jsx("h3", { className: "text-xl md:text-2xl font-semibold", children: q.prompt }), q.type === 'likert' ? (_jsx("div", { className: "mt-6 grid grid-cols-5 gap-6 md:gap-8 place-items-center", children: SCALE.map((opt) => {
|
|
27
|
+
const active = value === opt.key;
|
|
28
|
+
return (_jsxs("button", { type: "button", onClick: () => onChange(opt.key), className: "group flex flex-col items-center gap-2 focus:outline-none", children: [_jsx("span", { className: ['h-9 w-9 rounded-full border-2 bg-white ring-2 transition', active ? 'border-current ring-current' : `border-transparent ${opt.ring}`].join(' ') }), _jsx("span", { className: "whitespace-pre-line text-[10px] text-muted-foreground text-center leading-tight", children: opt.label })] }, opt.key));
|
|
29
|
+
}) })) : (_jsx("div", { className: "mt-6 flex items-center justify-center gap-4", children: ['yes', 'no'].map((opt) => (_jsx(Button, { type: "button", variant: value === opt ? 'default' : 'outline', onClick: () => onChange(opt), className: "min-w-[90px]", children: opt.toUpperCase() }, opt))) }))] }));
|
|
30
|
+
}
|
|
31
|
+
export function CareerTestStart({ basePath, navigate, storageKeyResults = CAREER_TEST_RESULTS_KEY }) {
|
|
32
|
+
const [section, setSection] = React.useState(1);
|
|
33
|
+
const [answers, setAnswers] = React.useState({});
|
|
34
|
+
const questionRefs = React.useRef([]);
|
|
35
|
+
questionRefs.current = [];
|
|
36
|
+
const list = React.useMemo(() => QUESTIONS_BY_SECTION[section], [section]);
|
|
37
|
+
const progressPct = (section / TOTAL_SECTIONS) * 100;
|
|
38
|
+
React.useEffect(() => {
|
|
39
|
+
const t = setTimeout(() => smoothScrollToEl(questionRefs.current[0]), 100);
|
|
40
|
+
return () => clearTimeout(t);
|
|
41
|
+
}, [section]);
|
|
42
|
+
const selectAnswer = (qIndex, q, value) => {
|
|
43
|
+
setAnswers((prev) => ({ ...prev, [q.id]: value }));
|
|
44
|
+
const nextIdx = qIndex + 1;
|
|
45
|
+
if (nextIdx < list.length) {
|
|
46
|
+
requestAnimationFrame(() => smoothScrollToEl(questionRefs.current[nextIdx]));
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
const fillUnansweredWithDefaults = () => {
|
|
50
|
+
setAnswers((prev) => {
|
|
51
|
+
const updated = { ...prev };
|
|
52
|
+
for (const q of list) {
|
|
53
|
+
if (updated[q.id] === undefined) {
|
|
54
|
+
updated[q.id] = q.type === 'likert' ? 3 : 'no';
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return updated;
|
|
58
|
+
});
|
|
59
|
+
};
|
|
60
|
+
const goPrev = () => setSection((s) => Math.max(1, s - 1));
|
|
61
|
+
const goNext = () => {
|
|
62
|
+
fillUnansweredWithDefaults();
|
|
63
|
+
setTimeout(() => {
|
|
64
|
+
setSection((s) => Math.min(TOTAL_SECTIONS, s + 1));
|
|
65
|
+
window.scrollTo({ top: 0, behavior: 'smooth' });
|
|
66
|
+
}, 150);
|
|
67
|
+
};
|
|
68
|
+
const seeResults = () => {
|
|
69
|
+
const filled = { ...answers };
|
|
70
|
+
for (const q of list) {
|
|
71
|
+
if (filled[q.id] === undefined) {
|
|
72
|
+
filled[q.id] = q.type === 'likert' ? 3 : 'no';
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
setAnswers(filled);
|
|
76
|
+
const result = computeOverall(QUESTIONS, filled);
|
|
77
|
+
try {
|
|
78
|
+
if (typeof window !== 'undefined')
|
|
79
|
+
window.sessionStorage.setItem(storageKeyResults, JSON.stringify(result));
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
// ignore
|
|
83
|
+
}
|
|
84
|
+
navigate(`${basePath}/results`);
|
|
85
|
+
};
|
|
86
|
+
return (_jsxs("main", { className: "container mx-auto px-6 py-10", children: [_jsxs("div", { className: "mx-auto max-w-3xl rounded-xl border bg-blue-50/60 p-3 shadow-sm", children: [_jsxs("div", { className: "text-[11px] font-semibold text-center text-gray-700 tracking-wide", children: ["SECTION ", section, " OF ", TOTAL_SECTIONS] }), _jsx("div", { className: "mt-2 h-1.5 w-full rounded-full bg-blue-100", children: _jsx("div", { className: "h-1.5 rounded-full bg-[#155177] transition-all", style: { width: `${progressPct}%` } }) })] }), _jsx("div", { className: "mt-10 max-w-3xl mx-auto space-y-12", children: list.map((q, idx) => (_jsx(QuestionRow, { q: q, value: answers[q.id], onChange: (val) => selectAnswer(idx, q, val), setRef: (el) => (questionRefs.current[idx] = el) }, q.id))) }), _jsxs("div", { className: "mt-12 max-w-3xl mx-auto flex items-center justify-between", children: [_jsx(Button, { variant: "outline", className: "min-w-[150px]", onClick: goPrev, disabled: section <= 1, children: "Previous Step" }), section < TOTAL_SECTIONS ? (_jsxs(Button, { className: "min-w-[150px]", onClick: goNext, children: ["Next", _jsx(ChevronRight, { className: "ml-2 h-4 w-4" })] })) : (_jsxs(Button, { className: "min-w-[170px]", onClick: seeResults, children: ["See my result", _jsx(ChevronRight, { className: "ml-2 h-4 w-4" })] }))] })] }));
|
|
87
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Result content for career personality test: letter maps, combo descriptions, roles, bands.
|
|
3
|
+
* Image paths are filename-only; prepend imageBasePath in the Results component.
|
|
4
|
+
*/
|
|
5
|
+
import type { TraitName } from '../utils/scoring';
|
|
6
|
+
export declare const BIGO_LETTERS: Partial<Record<TraitName, 'O' | 'C' | 'E' | 'A' | 'N'>>;
|
|
7
|
+
export declare const RIASEC_LETTERS: Partial<Record<TraitName, 'R' | 'I' | 'A' | 'S' | 'E' | 'C'>>;
|
|
8
|
+
export declare const BIGO_COMBOS: Record<string, string>;
|
|
9
|
+
export declare const RIASEC_COMBOS: Record<string, string>;
|
|
10
|
+
export type WorkStyleBand = {
|
|
11
|
+
min: number;
|
|
12
|
+
max: number;
|
|
13
|
+
text: string;
|
|
14
|
+
image: string;
|
|
15
|
+
};
|
|
16
|
+
export declare const WORK_STYLE_BANDS: WorkStyleBand[];
|
|
17
|
+
export declare function pickWorkStyleBand(overallPct: number): WorkStyleBand;
|
|
18
|
+
export type PersonaBand = {
|
|
19
|
+
key: 'explorer' | 'builder' | 'achiever' | 'catalyst';
|
|
20
|
+
label: string;
|
|
21
|
+
title: string;
|
|
22
|
+
subtitle: string;
|
|
23
|
+
description: string;
|
|
24
|
+
image: string;
|
|
25
|
+
positiveAttributes: string[];
|
|
26
|
+
};
|
|
27
|
+
export declare function pickPersonaBand(overallPct: number): PersonaBand;
|
|
28
|
+
export declare const RIASEC_ROLES: Record<string, string[]>;
|
|
29
|
+
//# sourceMappingURL=resultConstants.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resultConstants.d.ts","sourceRoot":"","sources":["../../../src/career-personality-test/constants/resultConstants.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAElD,eAAO,MAAM,YAAY,EAAE,OAAO,CAAC,MAAM,CAAC,SAAS,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC,CAMhF,CAAC;AAEF,eAAO,MAAM,cAAc,EAAE,OAAO,CAAC,MAAM,CAAC,SAAS,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC,CAOxF,CAAC;AAEF,eAAO,MAAM,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAW9C,CAAC;AAEF,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAqBhD,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,eAAO,MAAM,gBAAgB,EAAE,aAAa,EAK3C,CAAC;AAEF,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,MAAM,GAAG,aAAa,CAGnE;AAED,MAAM,MAAM,WAAW,GAAG;IACxB,GAAG,EAAE,UAAU,GAAG,SAAS,GAAG,UAAU,GAAG,UAAU,CAAC;IACtD,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,kBAAkB,EAAE,MAAM,EAAE,CAAC;CAC9B,CAAC;AASF,wBAAgB,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,WAAW,CAI/D;AAED,eAAO,MAAM,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAqBjD,CAAC"}
|