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() {
Full Name *
-
@@ -620,12 +623,12 @@ export default function StudentOnboardingPage() {
{headline.length}/80
-
@@ -683,11 +686,11 @@ export default function StudentOnboardingPage() {
School / University *
-
@@ -696,11 +699,11 @@ export default function StudentOnboardingPage() {
Degree & Major *
-
@@ -726,11 +729,11 @@ export default function StudentOnboardingPage() {
GPA (Optional)
-
@@ -738,11 +741,11 @@ export default function StudentOnboardingPage() {
Specialization (Optional)
-
@@ -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"
/>
-
handleAddSkill()}
- className="px-3.5 py-2 bg-brand hover:bg-brand-hover text-white font-semibold rounded-xl text-xs sm:text-sm transition cursor-pointer flex items-center gap-1 shrink-0"
+ className="px-3.5 py-2 bg-brand hover:bg-brand-hover text-white font-semibold rounded-xl text-xs sm:text-sm flex items-center gap-1 shrink-0 h-auto"
>
Add
-
+
{/* Added Skills Badges */}
{skills.map((s) => (
-
- {s}
- {s}
+ handleRemoveSkill(s)}
- className="hover:text-red-500 cursor-pointer"
+ className="h-3.5 w-3.5 p-0 hover:bg-transparent hover:text-red-500 text-brand-dark"
+ aria-label={`Remove skill ${s}`}
>
-
-
+
+
))}
@@ -828,14 +835,16 @@ export default function StudentOnboardingPage() {
.filter((s) => !skills.includes(s))
.slice(0, 5)
.map((suggestion) => (
- handleAddSkill(suggestion)}
- className="text-[10px] font-medium bg-slate-100 hover:bg-brand-light hover:text-brand-hover text-slate-600 px-2 py-0.5 rounded-md transition cursor-pointer"
+ className="text-[10px] font-medium bg-slate-100 hover:bg-brand-light hover:text-brand-hover text-slate-600 px-2 py-0.5 rounded-md h-auto"
>
+ {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 */}
-
Back
-
+
{/* Next / Complete Button */}
-
{step < 4 ? (
<>
@@ -1012,7 +1022,7 @@ export default function StudentOnboardingPage() {
>
)}
-
+
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 (