From 1eef12c369eb62646b0d9132ce4dddeafcfcefab Mon Sep 17 00:00:00 2001 From: Tanushree-RD <217297962+Tanushree-RD@users.noreply.github.com> Date: Sun, 13 Sep 2026 23:15:37 +0530 Subject: [PATCH 1/5] refactor(student): adopt shadcn/ui components for onboarding --- apps/web/app/(onboarding)/student/page.tsx | 110 +++++++++++---------- 1 file changed, 60 insertions(+), 50 deletions(-) diff --git a/apps/web/app/(onboarding)/student/page.tsx b/apps/web/app/(onboarding)/student/page.tsx index 29f7b3e..91a53ab 100644 --- a/apps/web/app/(onboarding)/student/page.tsx +++ b/apps/web/app/(onboarding)/student/page.tsx @@ -7,6 +7,9 @@ import { useRouter } from 'next/navigation' import { useEffect, useRef, useState, type MouseEvent } from 'react' import { useForm } from 'react-hook-form' +import { Button } from '@/components/ui/button' +import { Input } from '@/components/ui/input' +import { Badge } from '@/components/ui/badge' import { useSaveStudentProfile } from '@/lib/hooks/use-onboarding' import { studentCompleteOnboardingSchema, @@ -602,11 +605,11 @@ export default function StudentOnboardingPage() { - @@ -620,12 +623,12 @@ export default function StudentOnboardingPage() { {headline.length}/80 - @@ -683,11 +686,11 @@ export default function StudentOnboardingPage() { - @@ -696,11 +699,11 @@ export default function StudentOnboardingPage() { - @@ -726,11 +729,11 @@ export default function StudentOnboardingPage() { - @@ -738,11 +741,11 @@ export default function StudentOnboardingPage() { - @@ -777,7 +780,7 @@ export default function StudentOnboardingPage() { Technical Skills
- setNewSkillInput(e.target.value)} @@ -788,34 +791,38 @@ export default function StudentOnboardingPage() { } }} placeholder="Type a skill and press Enter..." - className="flex-1 px-3.5 py-2 rounded-xl border border-border-subtle focus:border-brand focus:ring-2 focus:ring-brand/15 outline-none text-xs sm:text-sm transition bg-card" + className="flex-1 px-3.5 py-2 rounded-xl border border-border-subtle focus-visible:border-brand focus-visible:ring-brand/15 text-xs sm:text-sm bg-card h-auto" /> - +
{/* Added Skills Badges */}
{skills.map((s) => ( - - {s} - - + + ))}
@@ -828,14 +835,16 @@ export default function StudentOnboardingPage() { .filter((s) => !skills.includes(s)) .slice(0, 5) .map((suggestion) => ( - + ))} @@ -847,25 +856,25 @@ export default function StudentOnboardingPage() {
- -
- @@ -899,12 +908,12 @@ export default function StudentOnboardingPage() { GitHub Profile URL
- - +
{errors.githubUrl && ( @@ -920,12 +929,12 @@ export default function StudentOnboardingPage() { LinkedIn Profile URL
- - +
{errors.linkedinUrl && ( @@ -941,12 +950,12 @@ export default function StudentOnboardingPage() { Portfolio / Website (Optional)
- - +
{errors.portfolioUrl && ( @@ -962,12 +971,12 @@ export default function StudentOnboardingPage() { Resume / CV Link (Optional)
- - +
{errors.resumeUrl && ( @@ -985,21 +994,22 @@ export default function StudentOnboardingPage() { {/* Bottom Actions Bar (Fixed at bottom of right column) */}
{/* Back Button */} - + {/* Next / Complete Button */} - +
From e30685a2ab023add128aed255ec01e8b6d87eb13 Mon Sep 17 00:00:00 2001 From: Chaithra S Date: Wed, 16 Sep 2026 01:17:18 +0530 Subject: [PATCH 2/5] Update icons for GitHub and LinkedIn profile inputs Replaced GitHub and LinkedIn icons with Code and Link icons respectively. --- apps/web/app/(onboarding)/student/page.tsx | 26 +++------------------- 1 file changed, 3 insertions(+), 23 deletions(-) diff --git a/apps/web/app/(onboarding)/student/page.tsx b/apps/web/app/(onboarding)/student/page.tsx index 91a53ab..f90a612 100644 --- a/apps/web/app/(onboarding)/student/page.tsx +++ b/apps/web/app/(onboarding)/student/page.tsx @@ -2,7 +2,7 @@ import { zodResolver } from '@hookform/resolvers/zod' import { motion, AnimatePresence } from 'motion/react' -import { ArrowLeft, ArrowRight, Check, FileText, Globe, Plus, Sparkles, X } from 'lucide-react' +import { ArrowLeft, ArrowRight, Check, Code, FileText, Globe, Link, Plus, Sparkles, X } from 'lucide-react' import { useRouter } from 'next/navigation' import { useEffect, useRef, useState, type MouseEvent } from 'react' import { useForm } from 'react-hook-form' @@ -17,26 +17,6 @@ import { } from '@/lib/schemas/student-onboarding.schema' import { showConfetti, ONBOARDING_REDIRECT_DELAY_MS } from '@/lib/utils/confetti' -function GithubIcon({ className = 'w-4 h-4' }: { className?: string }) { - return ( - - - - ) -} - -function LinkedinIcon({ className = 'w-4 h-4' }: { className?: string }) { - return ( - - - - ) -} - const DEFAULT_STUDENT_VALUES: StudentOnboardingInput = { fullName: '', headline: '', @@ -908,7 +888,7 @@ export default function StudentOnboardingPage() { GitHub Profile URL
- +
- + Date: Wed, 16 Sep 2026 01:32:37 +0530 Subject: [PATCH 3/5] Format student onboarding imports --- apps/web/app/(onboarding)/student/page.tsx | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/apps/web/app/(onboarding)/student/page.tsx b/apps/web/app/(onboarding)/student/page.tsx index f90a612..935c1dd 100644 --- a/apps/web/app/(onboarding)/student/page.tsx +++ b/apps/web/app/(onboarding)/student/page.tsx @@ -2,7 +2,18 @@ import { zodResolver } from '@hookform/resolvers/zod' import { motion, AnimatePresence } from 'motion/react' -import { ArrowLeft, ArrowRight, Check, Code, FileText, Globe, Link, Plus, Sparkles, X } from 'lucide-react' +import { + ArrowLeft, + ArrowRight, + Check, + Code, + FileText, + Globe, + Link, + Plus, + Sparkles, + X, +} from 'lucide-react' import { useRouter } from 'next/navigation' import { useEffect, useRef, useState, type MouseEvent } from 'react' import { useForm } from 'react-hook-form' From f17bae2a3b5f064405c6fe275f749ce975550427 Mon Sep 17 00:00:00 2001 From: Chaithra S Date: Fri, 18 Sep 2026 21:37:23 +0530 Subject: [PATCH 4/5] Refactor student onboarding UI and add components --- apps/web/app/(onboarding)/student/page.tsx | 932 ++++++------------ .../onboarding/student-mascot-hero.tsx | 207 ++++ .../onboarding/student-onboarding-stepper.tsx | 89 ++ 3 files changed, 580 insertions(+), 648 deletions(-) create mode 100644 apps/web/components/onboarding/student-mascot-hero.tsx create mode 100644 apps/web/components/onboarding/student-onboarding-stepper.tsx diff --git a/apps/web/app/(onboarding)/student/page.tsx b/apps/web/app/(onboarding)/student/page.tsx index 935c1dd..a9a8708 100644 --- a/apps/web/app/(onboarding)/student/page.tsx +++ b/apps/web/app/(onboarding)/student/page.tsx @@ -2,25 +2,19 @@ import { zodResolver } from '@hookform/resolvers/zod' import { motion, AnimatePresence } from 'motion/react' -import { - ArrowLeft, - ArrowRight, - Check, - Code, - FileText, - Globe, - Link, - Plus, - Sparkles, - X, -} from 'lucide-react' +import { ArrowLeft, ArrowRight, Code, FileText, Globe, Link, Plus, Sparkles, X } from 'lucide-react' import { useRouter } from 'next/navigation' import { useEffect, useRef, useState, type MouseEvent } from 'react' -import { useForm } from 'react-hook-form' +import { useForm, useWatch } from 'react-hook-form' import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' import { Badge } from '@/components/ui/badge' +import { StudentMascotHero } from '@/components/onboarding/student-mascot-hero' +import { + StudentOnboardingStepper, + type StepNumber, +} from '@/components/onboarding/student-onboarding-stepper' import { useSaveStudentProfile } from '@/lib/hooks/use-onboarding' import { studentCompleteOnboardingSchema, @@ -47,13 +41,22 @@ const DEFAULT_STUDENT_VALUES: StudentOnboardingInput = { resumeUrl: '', } +const GRADUATION_YEAR_OPTIONS = ['2024', '2025', '2026', '2027', '2028', '2029', '2030'] +const POPULAR_SKILL_SUGGESTIONS = [ + 'Python', + 'JavaScript', + 'Node.js', + 'SQL', + 'Docker', + 'Git', + 'Java', +] + export default function StudentOnboardingPage() { const router = useRouter() - const [step, setStep] = useState<1 | 2 | 3 | 4>(1) + const [step, setStep] = useState(1) const [isCompleted, setIsCompleted] = useState(false) const [newSkillInput, setNewSkillInput] = useState('') - const [step1Error, setStep1Error] = useState('') - const [step2Error, setStep2Error] = useState('') const saveStudentMutation = useSaveStudentProfile() @@ -61,8 +64,9 @@ export default function StudentOnboardingPage() { register, handleSubmit, setValue, - watch, trigger, + control, + getValues, formState: { errors }, } = useForm({ resolver: zodResolver(studentCompleteOnboardingSchema), @@ -70,12 +74,8 @@ export default function StudentOnboardingPage() { defaultValues: DEFAULT_STUDENT_VALUES, }) - const fullName = watch('fullName') || '' - const headline = watch('headline') || '' - const bio = watch('bio') || '' - const school = watch('school') || '' - const degree = watch('degree') || '' - const skills = watch('skills') || [] + // Watch skills array specifically for rendering skill badges in Step 3 + const skills = useWatch({ control, name: 'skills' }) || [] // Interactive Character Eye Tracking & Blinking const [mousePos, setMousePos] = useState({ x: 0, y: 0 }) @@ -94,30 +94,38 @@ export default function StudentOnboardingPage() { } useEffect(() => { - const interval = setInterval( + let timeoutId: NodeJS.Timeout + + const intervalId = setInterval( () => { setBlink(true) - setTimeout(() => setBlink(false), 140) + timeoutId = setTimeout(() => setBlink(false), 140) }, 3200 + Math.random() * 2600, ) - return () => clearInterval(interval) + + return () => { + clearInterval(intervalId) + if (timeoutId) clearTimeout(timeoutId) + } }, []) - // Skills handlers + // Skills management handlers const handleAddSkill = (skillToAdd?: string) => { const target = (skillToAdd || newSkillInput).trim() if (!target) return - if (!skills.includes(target)) { - setValue('skills', [...skills, target], { shouldValidate: true }) + const currentSkills = getValues('skills') || [] + if (!currentSkills.includes(target)) { + setValue('skills', [...currentSkills, target], { shouldValidate: true }) } setNewSkillInput('') } const handleRemoveSkill = (skillToRemove: string) => { + const currentSkills = getValues('skills') || [] setValue( 'skills', - skills.filter((s) => s !== skillToRemove), + currentSkills.filter((s) => s !== skillToRemove), { shouldValidate: true }, ) } @@ -132,30 +140,21 @@ export default function StudentOnboardingPage() { router.push('/landing') }, ONBOARDING_REDIRECT_DELAY_MS) } catch { - // Error handled in mutation + // Error handled by mutation hook } } - // Stepper navigation + // Stepper navigation with Zod schema validation const handleNext = async () => { if (step === 1) { const valid = await trigger(['fullName', 'headline']) - if (!fullName.trim() || !headline.trim() || !valid) { - setStep1Error('Please fill in your full name and headline.') - return - } - setStep1Error('') - setStep(2) + if (valid) setStep(2) } else if (step === 2) { const valid = await trigger(['school', 'degree']) - if (!school.trim() || !degree.trim() || !valid) { - setStep2Error('Please provide your School/University and Degree.') - return - } - setStep2Error('') - setStep(3) + if (valid) setStep(3) } else if (step === 3) { - setStep(4) + const valid = await trigger('skills') + if (valid) setStep(4) } else if (step === 4) { void handleSubmit(onFinalSubmit)() } @@ -165,530 +164,165 @@ export default function StudentOnboardingPage() { if (step === 1) { router.push('/role-select') } else { - setStep((prev) => (prev - 1) as 1 | 2 | 3 | 4) + setStep((prev) => (prev - 1) as StepNumber) } } - const gradYearOptions = ['2024', '2025', '2026', '2027', '2028', '2029', '2030'] - const popularSkills = ['Python', 'JavaScript', 'Node.js', 'SQL', 'Docker', 'Git', 'Java'] - return (
{/* Top Navbar */}
-
+
{/* Logo */} -
router.push('/')} > DK24 CareerLink -
+
{/* Main Container Fixed-Height Card */}
-
- {/* ========================================================================= */} - {/* LEFT COLUMN: HERO & 3D STUDENT WITH LAPTOP (Fixed Height) */} - {/* ========================================================================= */} -
- {/* Top Content */} -
- {/* Title & Subtitle */} -
-

- Let's build your
- - career profile - -

-

- A complete profile helps recruiters discover you and gives you better - opportunities. -

-
-
- - {/* Bottom 3D Scene: Student Character with Laptop */} -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - {/* Ground Shadow */} - - - {/* 3D Student Character */} - - - - {/* Graduation Cap */} - - - - - - - - - - - {/* Left Eye */} - - - - {!blink && ( - - )} - - - {/* Right Eye */} - - - - {!blink && ( - - )} - - - {/* Smile */} - - - - - - {/* 3D Laptop */} - - - - - - - - - - -
-
+
+ {/* LEFT COLUMN: HERO & 3D MASCOT */} + - {/* ========================================================================= */} - {/* RIGHT COLUMN: MULTI-STEP WIZARD FORM (Fixed Height, No Scrolling) */} - {/* ========================================================================= */} + {/* RIGHT COLUMN: MULTI-STEP WIZARD FORM */}
{/* Top Stepper Indicator */} -
-
- {/* Step 1: Basic Info */} -
setStep(1)} - > -
1 - ? 'bg-brand text-white shadow-xs' - : step === 1 - ? 'bg-brand text-white shadow-[0_0_0_5px_var(--brand-green-glow)]' - : 'bg-card border-2 border-border-subtle text-text-muted group-hover:border-slate-300' - }`} - > - {step > 1 ? : '1'} -
- 1 - ? 'font-semibold text-slate-700' - : 'font-medium text-text-muted' - }`} - > - Basic Info - -
- - {/* Connector Line 1 -> 2 */} -
-
1 ? '100%' : '0%' }} - /> -
- - {/* Step 2: Education */} -
step > 1 && setStep(2)} - > -
2 - ? 'bg-brand text-white shadow-xs' - : step === 2 - ? 'bg-brand text-white shadow-[0_0_0_5px_var(--brand-green-glow)]' - : 'bg-card border-2 border-border-subtle text-text-muted group-hover:border-slate-300' - }`} - > - {step > 2 ? : '2'} -
- 2 - ? 'font-semibold text-slate-700' - : 'font-medium text-text-muted' - }`} - > - Education - -
- - {/* Connector Line 2 -> 3 */} -
-
2 ? '100%' : '0%' }} - /> -
- - {/* Step 3: Skills & Exp */} -
step > 2 && setStep(3)} - > -
3 - ? 'bg-brand text-white shadow-xs' - : step === 3 - ? 'bg-brand text-white shadow-[0_0_0_5px_var(--brand-green-glow)]' - : 'bg-card border-2 border-border-subtle text-text-muted group-hover:border-slate-300' - }`} - > - {step > 3 ? : '3'} -
- 3 - ? 'font-semibold text-slate-700' - : 'font-medium text-text-muted' - }`} - > - Skills & Exp - -
- - {/* Connector Line 3 -> 4 */} -
-
3 ? '100%' : '0%' }} - /> -
- - {/* Step 4: Links & Finish */} -
step > 3 && setStep(4)} - > -
- 4 -
- - Links & Finish - -
-
-
+ - {/* Step Form Content Body (Fixed Area, perfectly fitted) */} -
+ {/* Scrollable Form Content */} +
- {/* ------------------------------------------------------------- */} - {/* STEP 1: BASIC DETAILS */} - {/* ------------------------------------------------------------- */} + {/* STEP 1: BASIC INFO */} {step === 1 && (
-

- Basic Details +

+ Personal Information

-

- Start with the basics. You can update these anytime. +

+ Tell us your name and how you want recruiters to see you.

- {(step1Error || errors.fullName || errors.headline) && ( -
- {step1Error || errors.fullName?.message || errors.headline?.message} +
+ {/* Full Name */} +
+ + + {errors.fullName && ( +

+ {errors.fullName.message} +

+ )}
- )} - {/* Full Name */} -
- - -
- - {/* Headline */} -
-
-
- {/* Bio / Summary */} -
-
- - - {bio.length}/300 - + {/* Bio */} +
+ +