From 498dc5aa94a6c20ba023f39c6b3c91c3ff3aaa15 Mon Sep 17 00:00:00 2001 From: Om Lanke Date: Mon, 7 Jul 2025 01:07:35 +0530 Subject: [PATCH] signup form --- apps/student/app/signup/action.ts | 74 ++- apps/student/app/signup/page.tsx | 560 +++--------------- .../app/signup/steps/AcademicDetailsStep.tsx | 158 +++++ .../signup/steps/AdditionalDetailsStep.tsx | 74 +++ .../app/signup/steps/InternshipStep.tsx | 176 ++++++ .../app/signup/steps/PersonalDetailsStep.tsx | 140 +++++ apps/student/app/signup/steps/ResumeStep.tsx | 113 ++++ .../app/signup/steps/SemesterGradesStep.tsx | 78 +++ 8 files changed, 882 insertions(+), 491 deletions(-) create mode 100644 apps/student/app/signup/steps/AcademicDetailsStep.tsx create mode 100644 apps/student/app/signup/steps/AdditionalDetailsStep.tsx create mode 100644 apps/student/app/signup/steps/InternshipStep.tsx create mode 100644 apps/student/app/signup/steps/PersonalDetailsStep.tsx create mode 100644 apps/student/app/signup/steps/ResumeStep.tsx create mode 100644 apps/student/app/signup/steps/SemesterGradesStep.tsx diff --git a/apps/student/app/signup/action.ts b/apps/student/app/signup/action.ts index 4ca5171..b47603b 100644 --- a/apps/student/app/signup/action.ts +++ b/apps/student/app/signup/action.ts @@ -1,5 +1,5 @@ 'use server' -import { db, students } from '@workspace/db'; +import { db, students, grades, internships as internshipsTable, resumes as resumesTable } from '@workspace/db'; import { eq } from '@workspace/db/drizzle'; import { studentSignupSchema } from './schema'; import { auth } from '@/auth'; @@ -12,6 +12,12 @@ export async function signupAction(data: FormData) { } const formData = Object.fromEntries(data.entries()); + // Parse arrays/objects from formData if sent as JSON strings + if (typeof formData.skills === 'string') formData.skills = JSON.parse(formData.skills); + if (typeof formData.sgpi === 'string') formData.sgpi = JSON.parse(formData.sgpi); + if (typeof formData.internships === 'string') formData.internships = JSON.parse(formData.internships); + if (typeof formData.resume === 'string') formData.resume = JSON.parse(formData.resume); + const parsedData = await studentSignupSchema.safeParseAsync(formData); if (!parsedData.success) { @@ -20,6 +26,7 @@ export async function signupAction(data: FormData) { const student = parsedData.data; + // Update student table await db.update(students).set({ rollNumber: student.rollNumber, firstName: student.firstName, @@ -34,7 +41,7 @@ export async function signupAction(data: FormData) { degree: student.degree, branch: student.branch, year: student.year, - skills: student.skills, + skills: student.skills, // store as array linkedin: student.linkedin, github: student.github, ssc: String(student.ssc), @@ -42,5 +49,68 @@ export async function signupAction(data: FormData) { isDiploma: student.isDiploma, }).where(eq(students.id, studentId)); + // Upsert grades (sgpi) + if (Array.isArray(student.sgpi)) { + for (const grade of student.sgpi) { + await db.insert(grades).values({ + studentId: studentId, + sem: grade.sem, + sgpi: String(grade.sgpi), + isKT: grade.kt, + deadKT: grade.ktDead, + }).onConflictDoUpdate({ + target: [grades.studentId, grades.sem], + set: { + sgpi: String(grade.sgpi), + isKT: grade.kt, + deadKT: grade.ktDead, + updatedAt: new Date(), + }, + }); + } + } + + // Upsert internships + if (Array.isArray(student.internships)) { + for (const internship of student.internships) { + await db.insert(internshipsTable).values({ + studentId, + title: internship.title, + company: internship.company, + description: internship.description, + location: internship.location, + startDate: internship.startDate, + endDate: internship.endDate, + }).onConflictDoUpdate({ + target: [internshipsTable.studentId, internshipsTable.title, internshipsTable.company], + set: { + description: internship.description, + location: internship.location, + startDate: internship.startDate, + endDate: internship.endDate, + updatedAt: new Date(), + }, + }); + } + } + + // Upsert resumes + if (Array.isArray(student.resume)) { + for (const resume of student.resume) { + await db.insert(resumesTable).values({ + studentId, + title: resume.title, + link: resume.link, + }).onConflictDoUpdate({ + target: [resumesTable.studentId, resumesTable.title], + set: { + link: resume.link, + updatedAt: new Date(), + }, + }); + } + } + + return { success: true }; } diff --git a/apps/student/app/signup/page.tsx b/apps/student/app/signup/page.tsx index 8ad6594..42437c9 100644 --- a/apps/student/app/signup/page.tsx +++ b/apps/student/app/signup/page.tsx @@ -1,51 +1,34 @@ +// Due to the length and complexity of the complete updated form, the full implementation is provided modularly. +// This file only includes the top-level form layout and updated schema logic. Other components (InternshipModal, ResumeModal, etc.) +// should be created as separate files or extracted for cleanliness. + 'use client'; import { useState } from 'react'; import { useForm } from 'react-hook-form'; import { zodResolver } from '@hookform/resolvers/zod'; -import * as z from 'zod'; +import { z } from 'zod'; +// Only import used UI components import { Button } from '@workspace/ui/components/button'; -import { Input } from '@workspace/ui/components/input'; -import { Textarea } from '@workspace/ui/components/textarea'; import { Progress } from '@workspace/ui/components/progress'; -import { Separator } from '@workspace/ui/components/separator'; -import { - Form, - FormControl, - FormField, - FormItem, - FormLabel, - FormMessage, - FormDescription, -} from '@workspace/ui/components/form'; -import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from '@workspace/ui/components/select'; import { Card, CardContent, CardHeader, CardTitle } from '@workspace/ui/components/card'; +import { Form } from '@workspace/ui/components/form'; + import { studentSignupSchema, StudentSignup } from './schema'; +import PersonalDetailsStep from './steps/PersonalDetailsStep'; +import AcademicDetailsStep from './steps/AcademicDetailsStep'; +import SemesterGradesStep from './steps/SemesterGradesStep'; +import AdditionalDetailsStep from './steps/AdditionalDetailsStep'; +import InternshipStep from './steps/InternshipStep'; +import ResumeStep from './steps/ResumeStep'; const steps = [ - { - id: 1, - title: 'Personal Details', - fields: [ - 'firstName', - 'lastName', - 'fathersName', - 'mothersName', - 'email', - 'rollNumber', - 'phoneNumber', - 'address', - ], - }, - { id: 2, title: 'Academic Details', fields: ['degree', 'year', 'branch', 'ssc', 'hsc'] }, - { id: 3, title: 'Semester Grades', fields: ['sem1', 'sem1KT', 'sem2', 'sem2KT'] }, + { id: 1, title: 'Personal Details', fields: ['firstName', 'lastName', 'mothersName', 'rollNumber', 'phoneNumber', 'address', 'gender', 'dob', 'personalGmail'] }, + { id: 2, title: 'Academic Details', fields: ['degree', 'year', 'branch', 'ssc', 'hsc', 'isDiploma'] }, + { id: 3, title: 'Semester Grades', fields: ['sgpi'] }, { id: 4, title: 'Additional Details', fields: ['linkedin', 'github', 'skills'] }, + { id: 5, title: 'Internships', fields: ['internships'] }, + { id: 6, title: 'Resumes', fields: ['resume'] }, ]; export default function StudentRegistrationForm() { @@ -56,59 +39,59 @@ export default function StudentRegistrationForm() { resolver: zodResolver(studentSignupSchema), defaultValues: { firstName: '', + middleName: '', lastName: '', - fathersName: '', mothersName: '', - email: '', rollNumber: '', phoneNumber: '', address: '', + gender: '', + dob: new Date(), + personalGmail: '', degree: '', - year: '', branch: '', - ssc: '', - hsc: '', - sem1: '', - sem1KT: '', - sem2: '', - sem2KT: '', + year: '', + skills: [], linkedin: '', github: '', - skills: '', + ssc: 0, + hsc: 0, + isDiploma: false, + sgpi: Array.from({ length: 8 }, (_, i) => ({ sem: i + 1, sgpi: 0, kt: false, ktDead: false })), + internships: [], + resume: [], }, }); - const validateCurrentStep = async () => { - const currentStepData = steps.find((step) => step.id === currentStep); - if (!currentStepData) return false; + // console.log(form.formState.errors) - const result = await form.trigger(currentStepData.fields as any); - return result; + const validateCurrentStep = async () => { + const current = steps.find((s) => s.id === currentStep); + if (!current) return false; + // Cast fields to the correct type for react-hook-form + return await form.trigger(current.fields as Parameters[0]); }; const nextStep = async () => { const isValid = await validateCurrentStep(); - if (isValid && currentStep < steps.length) { - setCurrentStep(currentStep + 1); - } + if (isValid && currentStep < steps.length) setCurrentStep((prev) => prev + 1); }; const prevStep = () => { - if (currentStep > 1) { - setCurrentStep(currentStep - 1); - } + if (currentStep > 1) setCurrentStep((prev) => prev - 1); }; - const onSubmit = async (data: FormData) => { + const onSubmit = async (data: StudentSignup) => { + // Only submit if on the last step + if (currentStep !== steps.length) return; setIsSubmitting(true); try { - // Simulate API call - await new Promise((resolve) => setTimeout(resolve, 2000)); + await new Promise((res) => setTimeout(res, 2000)); console.log('Form submitted:', data); alert('Form submitted successfully!'); - } catch (error) { - console.error('Submission error:', error); - alert('Submission failed. Please try again.'); + } catch (err) { + console.error(err); + alert('Submission failed. Try again.'); } finally { setIsSubmitting(false); } @@ -124,6 +107,10 @@ export default function StudentRegistrationForm() { return ; case 4: return ; + case 5: + return ; + case 6: + return ; default: return null; } @@ -133,7 +120,7 @@ export default function StudentRegistrationForm() { return (
-
+
@@ -141,9 +128,7 @@ export default function StudentRegistrationForm() {
- - Step {currentStep} of {steps.length} - + Step {currentStep} of {steps.length} {steps[currentStep - 1]?.title}
@@ -151,21 +136,30 @@ export default function StudentRegistrationForm() {
- + { + if ( + e.key === 'Enter' && + e.target instanceof HTMLElement && + e.target.tagName !== 'TEXTAREA' + ) { + e.preventDefault(); + } + }} + > {renderStep()} -
- - {currentStep === steps.length ? ( - ) : ( @@ -182,415 +176,3 @@ export default function StudentRegistrationForm() {
); } - -function PersonalDetailsStep({ form }: { form: any }) { - return ( -
-

Personal Details

- -
- ( - - First Name * - - - - - - )} - /> - ( - - Last Name * - - - - - - )} - /> -
- -
- ( - - Father's Name - - - - - - )} - /> - ( - - Mother's Name - - - - - - )} - /> -
- - - -
- ( - - Email * - - - - - - )} - /> - ( - - Roll Number * - - - - - - )} - /> -
- - ( - - Phone Number * - - - - Without country code - - - )} - /> - - ( - - Address * - -