From 2aeecc905026b574e6fd4bb0743f19ad73aafefb Mon Sep 17 00:00:00 2001 From: Om Lanke Date: Mon, 7 Jul 2025 02:04:10 +0530 Subject: [PATCH] untested pushhhhh --- apps/student/app/signup/action.ts | 178 +++++++++--------- apps/student/app/signup/page.tsx | 90 +++++++-- apps/student/app/signup/schema.ts | 63 ++++--- .../app/signup/steps/AcademicDetailsStep.tsx | 26 ++- .../signup/steps/AdditionalDetailsStep.tsx | 22 ++- .../app/signup/steps/PersonalDetailsStep.tsx | 73 +++++++ apps/student/auth.ts | 80 ++++---- apps/student/package.json | 1 + pnpm-lock.yaml | 3 + 9 files changed, 350 insertions(+), 186 deletions(-) diff --git a/apps/student/app/signup/action.ts b/apps/student/app/signup/action.ts index b47603b..9a05f55 100644 --- a/apps/student/app/signup/action.ts +++ b/apps/student/app/signup/action.ts @@ -1,116 +1,114 @@ -'use server' -import { db, students, grades, internships as internshipsTable, resumes as resumesTable } from '@workspace/db'; +'use server'; +import { + db, + students, + grades, + internships as internshipsTable, + resumes as resumesTable, +} from '@workspace/db'; import { eq } from '@workspace/db/drizzle'; -import { studentSignupSchema } from './schema'; +import { studentSignupSchema, StudentSignup } from './schema'; import { auth } from '@/auth'; -export async function signupAction(data: FormData) { +export async function signupAction(data: StudentSignup) { + try { const session = await auth(); const studentId = session?.user?.studentId; if (!studentId) { - return { error: 'Student ID not found in session.' }; + return { error: 'Student ID not found in session.' }; } - 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); + // Validate data using schema + const parsedData = await studentSignupSchema.safeParseAsync(data); if (!parsedData.success) { - return { error: parsedData.error.issues }; + return { error: parsedData.error.issues }; } const student = parsedData.data; - // Update student table - await db.update(students).set({ - rollNumber: student.rollNumber, - firstName: student.firstName, - middleName: student.middleName, - lastName: student.lastName, - mothersName: student.mothersName, - gender: student.gender, - dob: student.dob, - personalGmail: student.personalGmail, - phoneNumber: student.phoneNumber, - address: student.address, - degree: student.degree, - branch: student.branch, - year: student.year, - skills: student.skills, // store as array - linkedin: student.linkedin, - github: student.github, - ssc: String(student.ssc), - hsc: String(student.hsc), - isDiploma: student.isDiploma, - }).where(eq(students.id, studentId)); + // Use a transaction to ensure all operations succeed or fail together + await db.transaction(async (tx) => { + // Update student table + await tx + .update(students) + .set({ + rollNumber: student.rollNumber, + firstName: student.firstName, + middleName: student.middleName, + lastName: student.lastName, + mothersName: student.mothersName, + gender: student.gender, + dob: student.dob, + personalGmail: student.personalGmail, + phoneNumber: student.phoneNumber, + address: student.address, + degree: student.degree, + branch: student.branch, + year: student.year, + skills: student.skills, // store as array + linkedin: student.linkedin, + github: student.github, + ssc: String(student.ssc), + hsc: String(student.hsc), + isDiploma: student.isDiploma, + }) + .where(eq(students.id, studentId)); - // Upsert grades (sgpi) - if (Array.isArray(student.sgpi)) { + // Clear existing grades for this student + await tx.delete(grades).where(eq(grades.studentId, studentId)); + + // Insert 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(), - }, - }); + await tx.insert(grades).values({ + studentId: studentId, + sem: grade.sem, + sgpi: String(grade.sgpi), + isKT: grade.kt, + deadKT: grade.ktDead, + }); } - } + } - // Upsert internships - if (Array.isArray(student.internships)) { + // Clear existing internships for this student + await tx.delete(internshipsTable).where(eq(internshipsTable.studentId, studentId)); + + // Insert 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(), - }, - }); + await tx.insert(internshipsTable).values({ + studentId, + title: internship.title, + company: internship.company, + description: internship.description, + location: internship.location, + startDate: internship.startDate, + endDate: internship.endDate, + }); } - } + } - // Upsert resumes - if (Array.isArray(student.resume)) { + // Clear existing resumes for this student + await tx.delete(resumesTable).where(eq(resumesTable.studentId, studentId)); + + // Insert 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(), - }, - }); + await tx.insert(resumesTable).values({ + studentId, + title: resume.title, + link: resume.link, + }); } - } + } + }); return { success: true }; + } catch (error) { + console.error('Signup action error:', error); + return { + error: error instanceof Error ? error.message : 'An unexpected error occurred during signup.', + }; + } } - diff --git a/apps/student/app/signup/page.tsx b/apps/student/app/signup/page.tsx index 42437c9..7751d66 100644 --- a/apps/student/app/signup/page.tsx +++ b/apps/student/app/signup/page.tsx @@ -4,7 +4,7 @@ 'use client'; -import { useState } from 'react'; +import { useState, useTransition } from 'react'; import { useForm } from 'react-hook-form'; import { zodResolver } from '@hookform/resolvers/zod'; import { z } from 'zod'; @@ -13,6 +13,7 @@ import { Button } from '@workspace/ui/components/button'; import { Progress } from '@workspace/ui/components/progress'; import { Card, CardContent, CardHeader, CardTitle } from '@workspace/ui/components/card'; import { Form } from '@workspace/ui/components/form'; +import { useRouter } from 'next/navigation'; import { studentSignupSchema, StudentSignup } from './schema'; import PersonalDetailsStep from './steps/PersonalDetailsStep'; @@ -22,9 +23,29 @@ import AdditionalDetailsStep from './steps/AdditionalDetailsStep'; import InternshipStep from './steps/InternshipStep'; import ResumeStep from './steps/ResumeStep'; +import { signupAction } from './action'; + const steps = [ - { 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: 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'] }, @@ -34,6 +55,8 @@ const steps = [ export default function StudentRegistrationForm() { const [currentStep, setCurrentStep] = useState(1); const [isSubmitting, setIsSubmitting] = useState(false); + const [isPending, startTransition] = useTransition(); + const router = useRouter(); const form = useForm({ resolver: zodResolver(studentSignupSchema), @@ -57,7 +80,12 @@ export default function StudentRegistrationForm() { ssc: 0, hsc: 0, isDiploma: false, - sgpi: Array.from({ length: 8 }, (_, i) => ({ sem: i + 1, sgpi: 0, kt: false, ktDead: false })), + sgpi: Array.from({ length: 8 }, (_, i) => ({ + sem: i + 1, + sgpi: 0, + kt: false, + ktDead: false, + })), internships: [], resume: [], }, @@ -68,13 +96,21 @@ export default function StudentRegistrationForm() { 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]); + + try { + const result = await form.trigger(current.fields as (keyof StudentSignup)[]); + return result; + } catch (error) { + console.error('Validation error:', error); + return false; + } }; const nextStep = async () => { const isValid = await validateCurrentStep(); - if (isValid && currentStep < steps.length) setCurrentStep((prev) => prev + 1); + if (isValid && currentStep < steps.length) { + setCurrentStep((prev) => prev + 1); + } }; const prevStep = () => { @@ -84,13 +120,24 @@ export default function StudentRegistrationForm() { const onSubmit = async (data: StudentSignup) => { // Only submit if on the last step if (currentStep !== steps.length) return; + setIsSubmitting(true); try { - await new Promise((res) => setTimeout(res, 2000)); - console.log('Form submitted:', data); - alert('Form submitted successfully!'); + const result = await signupAction(data); + if (result && result.success) { + router.push('/'); + return; + } + if (result && result.error) { + const errorMessage = Array.isArray(result.error) + ? result.error.map((e) => e.message || e).join(', ') + : result.error; + alert('Submission failed: ' + errorMessage); + } else { + alert('Submission failed. Try again.'); + } } catch (err) { - console.error(err); + console.error('Submission error:', err); alert('Submission failed. Try again.'); } finally { setIsSubmitting(false); @@ -128,7 +175,9 @@ export default function StudentRegistrationForm() {
- Step {currentStep} of {steps.length} + + Step {currentStep} of {steps.length} + {steps[currentStep - 1]?.title}
@@ -139,7 +188,7 @@ export default function StudentRegistrationForm() {
{ + onKeyDown={(e) => { if ( e.key === 'Enter' && e.target instanceof HTMLElement && @@ -151,16 +200,17 @@ export default function StudentRegistrationForm() { > {renderStep()}
- {currentStep === steps.length ? ( - ) : (