From 576e594b8570a344fdbf217706fc93d0d62c1cc4 Mon Sep 17 00:00:00 2001 From: David Pietrocola Date: Sat, 14 Sep 2024 12:22:13 -0400 Subject: [PATCH 1/6] feat: adds survery --- .../HackerForm/HackathonProfileForm.tsx | 66 +++++++ web/app/hacker/layout.tsx | 64 +++++++ web/app/hacker/page.tsx | 6 + web/components/ui/button.tsx | 26 +-- web/components/ui/form.tsx | 178 ++++++++++++++++++ web/components/ui/label.tsx | 26 +++ web/components/ui/radio-group.tsx | 44 +++++ web/package.json | 7 +- web/pnpm-lock.yaml | 113 ++++++++++- 9 files changed, 513 insertions(+), 17 deletions(-) create mode 100644 web/app/hacker/HackerForm/HackathonProfileForm.tsx create mode 100644 web/app/hacker/layout.tsx create mode 100644 web/app/hacker/page.tsx create mode 100644 web/components/ui/form.tsx create mode 100644 web/components/ui/label.tsx create mode 100644 web/components/ui/radio-group.tsx diff --git a/web/app/hacker/HackerForm/HackathonProfileForm.tsx b/web/app/hacker/HackerForm/HackathonProfileForm.tsx new file mode 100644 index 00000000..3796f3c1 --- /dev/null +++ b/web/app/hacker/HackerForm/HackathonProfileForm.tsx @@ -0,0 +1,66 @@ +"use client"; +import { Button } from '@/components/ui/button'; +import { Form, FormItem } from '@/components/ui/form'; +import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group'; +import { useState } from 'react'; + +const SurveyForm = () => { + const [formData, setFormData] = useState(Array(20).fill(3)); // Default score is 3 for each question + + const questions = [ + "I prefer to lead team projects.", + "I am excited about attending multiple workshops and events at the hackathon.", + "I enjoy brainstorming and collaborating on ideas with others.", + "I like when my teammates have skills that complement mine.", + "Winning the hackathon is my top priority.", + "I am open to learning new technologies during the event.", + "I work best under tight deadlines.", + "I prefer working with teammates who share similar strengths.", + "I appreciate constructive feedback on my work.", + "I am confident in my coding abilities.", + "I enjoy taking on challenging problems.", + "I prefer a well-structured plan before starting a project.", + "I am comfortable adapting to changes during the project.", + "I like to focus on one task at a time.", + "I am eager to help teammates who are struggling.", + "I value creativity over functionality in projects.", + "I believe communication is key to a successful team.", + "I am motivated by learning rather than winning.", + "I have experience with version control systems like GitHub.", + "I enjoy working late hours to meet project goals." + ]; + + const handleInputChange = (index: number, value: number) => { + const newFormData = [...formData]; + newFormData[index] = value; + setFormData(newFormData); + }; + + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault(); + console.log(formData); // Submit logic here + }; + + return ( +
+

Survey

+ {questions.map((question, index) => ( + + + handleInputChange(index, parseInt(e.target.value))} + > + {[1, 2, 3, 4, 5].map((value) => ( + + + ))} + + + ))} + +
+ ); +}; + +export default SurveyForm; \ No newline at end of file diff --git a/web/app/hacker/layout.tsx b/web/app/hacker/layout.tsx new file mode 100644 index 00000000..0373c309 --- /dev/null +++ b/web/app/hacker/layout.tsx @@ -0,0 +1,64 @@ +import { Button } from "@/components/ui/button"; +import Image from "next/image"; +import Link from "next/link"; +import { ReactNode } from "react"; + +export default function SplashPageLayout({ + children, +}: { + children: ReactNode; +}) { + return ( +
+
+ +
+
{children}
+
+
+ Built with ❤️ at{" "} + HackTheNorth2024. + Powered by Convex,{" "} + Next.js and{" "} + shadcn/ui. +
+
+
+ ); +} + +function FooterLink({ href, children }: { href: string; children: ReactNode }) { + return ( + + {children} + + ); +} + +function SplashPageNav() { + return ( + <> + + + + + + ); +} diff --git a/web/app/hacker/page.tsx b/web/app/hacker/page.tsx new file mode 100644 index 00000000..7f989885 --- /dev/null +++ b/web/app/hacker/page.tsx @@ -0,0 +1,6 @@ +import { GetStarted } from "@/app/(splash)/GetStarted/GetStarted"; +import SurveyForm from "./HackerForm/HackathonProfileForm"; + +export default function HomePage() { + return ; +} diff --git a/web/components/ui/button.tsx b/web/components/ui/button.tsx index 225e1138..0270f644 100644 --- a/web/components/ui/button.tsx +++ b/web/components/ui/button.tsx @@ -1,8 +1,8 @@ -import * as React from "react"; -import { Slot } from "@radix-ui/react-slot"; -import { cva, type VariantProps } from "class-variance-authority"; +import * as React from "react" +import { Slot } from "@radix-ui/react-slot" +import { cva, type VariantProps } from "class-variance-authority" -import { cn } from "@/lib/utils"; +import { cn } from "@/lib/utils" const buttonVariants = cva( "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50", @@ -31,27 +31,27 @@ const buttonVariants = cva( variant: "default", size: "default", }, - }, -); + } +) export interface ButtonProps extends React.ButtonHTMLAttributes, VariantProps { - asChild?: boolean; + asChild?: boolean } const Button = React.forwardRef( ({ className, variant, size, asChild = false, ...props }, ref) => { - const Comp = asChild ? Slot : "button"; + const Comp = asChild ? Slot : "button" return ( - ); - }, -); -Button.displayName = "Button"; + ) + } +) +Button.displayName = "Button" -export { Button, buttonVariants }; +export { Button, buttonVariants } diff --git a/web/components/ui/form.tsx b/web/components/ui/form.tsx new file mode 100644 index 00000000..b6daa654 --- /dev/null +++ b/web/components/ui/form.tsx @@ -0,0 +1,178 @@ +"use client" + +import * as React from "react" +import * as LabelPrimitive from "@radix-ui/react-label" +import { Slot } from "@radix-ui/react-slot" +import { + Controller, + ControllerProps, + FieldPath, + FieldValues, + FormProvider, + useFormContext, +} from "react-hook-form" + +import { cn } from "@/lib/utils" +import { Label } from "@/components/ui/label" + +const Form = FormProvider + +type FormFieldContextValue< + TFieldValues extends FieldValues = FieldValues, + TName extends FieldPath = FieldPath +> = { + name: TName +} + +const FormFieldContext = React.createContext( + {} as FormFieldContextValue +) + +const FormField = < + TFieldValues extends FieldValues = FieldValues, + TName extends FieldPath = FieldPath +>({ + ...props +}: ControllerProps) => { + return ( + + + + ) +} + +const useFormField = () => { + const fieldContext = React.useContext(FormFieldContext) + const itemContext = React.useContext(FormItemContext) + const { getFieldState, formState } = useFormContext() + + const fieldState = getFieldState(fieldContext.name, formState) + + if (!fieldContext) { + throw new Error("useFormField should be used within ") + } + + const { id } = itemContext + + return { + id, + name: fieldContext.name, + formItemId: `${id}-form-item`, + formDescriptionId: `${id}-form-item-description`, + formMessageId: `${id}-form-item-message`, + ...fieldState, + } +} + +type FormItemContextValue = { + id: string +} + +const FormItemContext = React.createContext( + {} as FormItemContextValue +) + +const FormItem = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => { + const id = React.useId() + + return ( + +
+ + ) +}) +FormItem.displayName = "FormItem" + +const FormLabel = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => { + const { error, formItemId } = useFormField() + + return ( +
- - ); -}; - -export default SurveyForm; \ No newline at end of file + + + Personality Form + Please answer the following questions on a scale of 1 to 5, where 1 is the lowest and 5 is the highest. + +
+ + {questions.map((question, index) => ( +
+ + setAnswers(prev => ({ ...prev, [index]: value }))} + className="flex space-x-4 justify-center" + > + {[1, 2, 3, 4, 5].map((value) => ( +
+ + +
+ ))} +
+
+ ))} +
+ + + +
+
+ ) +} \ No newline at end of file diff --git a/web/app/hacker/page.tsx b/web/app/hacker/page.tsx index 7f989885..e0a6e718 100644 --- a/web/app/hacker/page.tsx +++ b/web/app/hacker/page.tsx @@ -1,6 +1,5 @@ -import { GetStarted } from "@/app/(splash)/GetStarted/GetStarted"; import SurveyForm from "./HackerForm/HackathonProfileForm"; export default function HomePage() { return ; -} +} \ No newline at end of file From 309db54d9bea452ae6a8efdba43c59af40437f48 Mon Sep 17 00:00:00 2001 From: David Pietrocola Date: Sat, 14 Sep 2024 20:05:40 -0400 Subject: [PATCH 3/6] feat: make multistep form --- .../HackerForm/HackathonProfileForm.tsx | 159 +++++++++++++++--- web/components/ui/progress.tsx | 28 +++ web/package.json | 1 + web/pnpm-lock.yaml | 26 +++ 4 files changed, 194 insertions(+), 20 deletions(-) create mode 100644 web/components/ui/progress.tsx diff --git a/web/app/hacker/HackerForm/HackathonProfileForm.tsx b/web/app/hacker/HackerForm/HackathonProfileForm.tsx index 21472f73..1b458801 100644 --- a/web/app/hacker/HackerForm/HackathonProfileForm.tsx +++ b/web/app/hacker/HackerForm/HackathonProfileForm.tsx @@ -4,52 +4,171 @@ import { Button } from "@/components/ui/button" import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card" import { Label } from "@/components/ui/label" import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group" +import { Progress } from "@/components/ui/progress" const questions = [ - "How satisfied are you with our product?", - "How likely are you to recommend our product to others?", - "How would you rate the ease of use of our product?", - "How would you rate our customer support?", - "How well does our product meet your needs?" + { + "id": 1, + "text": "I prefer to lead team projects.", + "category": "Motivation and Goals" + }, + { + "id": 2, + "text": "I am excited about attending multiple workshops and events at the hackathon.", + "category": "Commitment Level" + }, + { + "id": 3, + "text": "I enjoy brainstorming and collaborating on ideas with others.", + "category": "Team Dynamics" + }, + { + "id": 4, + "text": "I like when my teammates have skills that complement mine.", + "category": "Team Dynamics" + }, + { + "id": 5, + "text": "Winning the hackathon is my top priority.", + "category": "Commitment Level" + }, + { + "id": 6, + "text": "I am open to learning new technologies during the event.", + "category": "Skills and Experience" + }, + { + "id": 7, + "text": "I work best under tight deadlines.", + "category": "Work Style" + }, + { + "id": 8, + "text": "I prefer working with teammates who share similar strengths.", + "category": "Team Dynamics" + }, + { + "id": 9, + "text": "I appreciate constructive feedback on my work.", + "category": "Skills and Experience" + }, + { + "id": 10, + "text": "I am confident in my coding abilities.", + "category": "Skills and Experience" + }, + { + "id": 11, + "text": "I enjoy taking on challenging problems.", + "category": "Skills and Experience" + }, + { + "id": 12, + "text": "I prefer a well-structured plan before starting a project.", + "category": "Work Style" + }, + { + "id": 13, + "text": "I am comfortable adapting to changes during the project.", + "category": "Work Style" + }, + { + "id": 14, + "text": "I like to focus on one task at a time.", + "category": "Work Style" + }, + { + "id": 15, + "text": "I am eager to help teammates who are struggling.", + "category": "Team Dynamics" + }, + { + "id": 16, + "text": "I value creativity over functionality in projects.", + "category": "Motivation and Goals" + }, + { + "id": 17, + "text": "I believe communication is key to a successful team.", + "category": "Team Dynamics" + }, + { + "id": 18, + "text": "I am motivated by learning rather than winning.", + "category": "Motivation and Goals" + }, + { + "id": 19, + "text": "I have experience with version control systems like GitHub.", + "category": "Skills and Experience" + }, + { + "id": 20, + "text": "I enjoy working late hours to meet project goals.", + "category": "Commitment Level" + } ] +const questionsPerPage = 5 +const totalPages = Math.ceil(questions.length / questionsPerPage) + export default function Component() { const [answers, setAnswers] = useState>({}) + const [currentPage, setCurrentPage] = useState(1) const handleSubmit = (e: React.FormEvent) => { e.preventDefault() - console.log("Submitted answers:", answers) - // Here you would typically send the answers to your backend + if (currentPage < totalPages) { + setCurrentPage(currentPage + 1) + } else { + console.log("Submitted answers:", answers) + // Here you would typically send the answers to your backend + } } + const handlePrevious = () => { + setCurrentPage(currentPage - 1) + } + + const startIndex = (currentPage - 1) * questionsPerPage + const endIndex = startIndex + questionsPerPage + const currentQuestions = questions.slice(startIndex, endIndex) + return ( - + - Personality Form - Please answer the following questions on a scale of 1 to 5, where 1 is the lowest and 5 is the highest. + Hackathon Participant Survey + Please answer the following questions on a scale of 1 to 5, where 1 is strongly disagree and 5 is strongly agree.
- {questions.map((question, index) => ( -
- + + {currentQuestions.map((question) => ( +
+ setAnswers(prev => ({ ...prev, [index]: value }))} - className="flex space-x-4 justify-center" + id={`question-${question.id}`} + onValueChange={(value) => setAnswers(prev => ({ ...prev, [question.id]: value }))} + className="flex space-x-2 justify-center" > {[1, 2, 3, 4, 5].map((value) => (
- - + +
))}
+

{question.category}

))} - - + + {currentPage > 1 && ( + + )} + diff --git a/web/components/ui/progress.tsx b/web/components/ui/progress.tsx new file mode 100644 index 00000000..4fc3b473 --- /dev/null +++ b/web/components/ui/progress.tsx @@ -0,0 +1,28 @@ +"use client" + +import * as React from "react" +import * as ProgressPrimitive from "@radix-ui/react-progress" + +import { cn } from "@/lib/utils" + +const Progress = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, value, ...props }, ref) => ( + + + +)) +Progress.displayName = ProgressPrimitive.Root.displayName + +export { Progress } diff --git a/web/package.json b/web/package.json index 486475b3..793bae8e 100644 --- a/web/package.json +++ b/web/package.json @@ -18,6 +18,7 @@ "@radix-ui/react-dropdown-menu": "^2.1.1", "@radix-ui/react-icons": "^1.3.0", "@radix-ui/react-label": "^2.1.0", + "@radix-ui/react-progress": "^1.1.0", "@radix-ui/react-radio-group": "^1.2.0", "@radix-ui/react-slot": "^1.1.0", "@radix-ui/react-toast": "^1.2.1", diff --git a/web/pnpm-lock.yaml b/web/pnpm-lock.yaml index 7b3699b2..50f3dfed 100644 --- a/web/pnpm-lock.yaml +++ b/web/pnpm-lock.yaml @@ -26,6 +26,9 @@ importers: '@radix-ui/react-label': specifier: ^2.1.0 version: 2.1.0(@types/react-dom@18.3.0)(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-progress': + specifier: ^1.1.0 + version: 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-radio-group': specifier: ^1.2.0 version: 1.2.0(@types/react-dom@18.3.0)(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -832,6 +835,19 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-progress@1.1.0': + resolution: {integrity: sha512-aSzvnYpP725CROcxAOEBVZZSIQVQdHgBr2QQFKySsaD14u8dNT0batuXI+AAGDdAHfXH8rbnHmjYFqVJ21KkRg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-radio-group@1.2.0': resolution: {integrity: sha512-yv+oiLaicYMBpqgfpSPw6q+RyXlLdIpQWDHZbUKURxe+nEh53hFXPPlfhfQQtYkS5MMK/5IWIa76SksleQZSzw==} peerDependencies: @@ -3225,6 +3241,16 @@ snapshots: '@types/react': 18.3.5 '@types/react-dom': 18.3.0 + '@radix-ui/react-progress@1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/react-context': 1.1.0(@types/react@18.3.5)(react@18.3.1) + '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.5 + '@types/react-dom': 18.3.0 + '@radix-ui/react-radio-group@1.2.0(@types/react-dom@18.3.0)(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@radix-ui/primitive': 1.1.0 From 5fb10ddf5d96ded11dabaa373fc64997eb66e1b6 Mon Sep 17 00:00:00 2001 From: David Pietrocola Date: Sat, 14 Sep 2024 20:37:53 -0400 Subject: [PATCH 4/6] feat: complete forms --- .../HackerForm/HackathonProfileForm.tsx | 54 ++++++++++++++----- web/app/layout.tsx | 4 +- 2 files changed, 42 insertions(+), 16 deletions(-) diff --git a/web/app/hacker/HackerForm/HackathonProfileForm.tsx b/web/app/hacker/HackerForm/HackathonProfileForm.tsx index 1b458801..c68ff9ac 100644 --- a/web/app/hacker/HackerForm/HackathonProfileForm.tsx +++ b/web/app/hacker/HackerForm/HackathonProfileForm.tsx @@ -1,5 +1,5 @@ "use client" -import { useState } from "react" +import { useState, useEffect, useRef } from "react" import { Button } from "@/components/ui/button" import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card" import { Label } from "@/components/ui/label" @@ -115,11 +115,14 @@ const totalPages = Math.ceil(questions.length / questionsPerPage) export default function Component() { const [answers, setAnswers] = useState>({}) const [currentPage, setCurrentPage] = useState(1) + const [currentQuestionIndex, setCurrentQuestionIndex] = useState(0) + const formRef = useRef(null) const handleSubmit = (e: React.FormEvent) => { e.preventDefault() if (currentPage < totalPages) { setCurrentPage(currentPage + 1) + setCurrentQuestionIndex(0) } else { console.log("Submitted answers:", answers) // Here you would typically send the answers to your backend @@ -128,41 +131,64 @@ export default function Component() { const handlePrevious = () => { setCurrentPage(currentPage - 1) + setCurrentQuestionIndex(questionsPerPage - 1) } + const handleAnswer = (questionId: number, value: string) => { + setAnswers(prev => ({ ...prev, [questionId]: value })) + if (currentQuestionIndex < questionsPerPage - 1) { + setCurrentQuestionIndex(currentQuestionIndex + 1) + } + } + + useEffect(() => { + const currentQuestionElement = formRef.current?.querySelector(`#question-${currentQuestionIndex}`) + if (currentQuestionElement) { + currentQuestionElement.scrollIntoView({ behavior: 'smooth', block: 'nearest' }) + } + }, [currentQuestionIndex]) + const startIndex = (currentPage - 1) * questionsPerPage const endIndex = startIndex + questionsPerPage const currentQuestions = questions.slice(startIndex, endIndex) return ( - - + + Hackathon Participant Survey Please answer the following questions on a scale of 1 to 5, where 1 is strongly disagree and 5 is strongly agree. -
+ - {currentQuestions.map((question) => ( -
- + {currentQuestions.map((question, index) => ( +
+ setAnswers(prev => ({ ...prev, [question.id]: value }))} - className="flex space-x-2 justify-center" + onValueChange={(value) => handleAnswer(question.id, value)} + className="flex justify-center items-center space-x-4" > {[1, 2, 3, 4, 5].map((value) => ( -
- - +
+ = 4 ? 'hover:bg-[#baf2df] border-[#8ee9c2]' : + 'hover:bg-gray-200 border-gray-400' + }`} + /> +
))} -

{question.category}

+

{question.category}

))} - + {currentPage > 1 && ( )} diff --git a/web/app/layout.tsx b/web/app/layout.tsx index 3e31abe6..25c306d8 100644 --- a/web/app/layout.tsx +++ b/web/app/layout.tsx @@ -7,8 +7,8 @@ import { ConvexAuthNextjsServerProvider } from "@convex-dev/auth/nextjs/server"; const inter = Inter({ subsets: ["latin"] }); export const metadata: Metadata = { - title: "Convex + Next.js + Convex Auth", - description: "Generated by npm create convex", + title: "Hackd", + description: "Find your next big team with Hackd.", icons: { icon: "/convex.svg", }, From 16cde8f3b532834e181264b38c6c3ee7dea9cb3a Mon Sep 17 00:00:00 2001 From: David Pietrocola Date: Sat, 14 Sep 2024 20:40:35 -0400 Subject: [PATCH 5/6] feat: improves style form --- .../HackerForm/HackathonProfileForm.tsx | 49 ++++++++++--------- 1 file changed, 26 insertions(+), 23 deletions(-) diff --git a/web/app/hacker/HackerForm/HackathonProfileForm.tsx b/web/app/hacker/HackerForm/HackathonProfileForm.tsx index c68ff9ac..13e7017a 100644 --- a/web/app/hacker/HackerForm/HackathonProfileForm.tsx +++ b/web/app/hacker/HackerForm/HackathonProfileForm.tsx @@ -153,46 +153,49 @@ export default function Component() { const currentQuestions = questions.slice(startIndex, endIndex) return ( - + - Hackathon Participant Survey - Please answer the following questions on a scale of 1 to 5, where 1 is strongly disagree and 5 is strongly agree. + Hackathon Participant Survey + Please answer the following questions on a scale from strongly disagree to strongly agree. - - + + {currentQuestions.map((question, index) => ( -
- - handleAnswer(question.id, value)} - className="flex justify-center items-center space-x-4" - > - {[1, 2, 3, 4, 5].map((value) => ( -
+
+ +
+ Disagree + handleAnswer(question.id, value)} + className="flex justify-center items-center space-x-6" + > + {[1, 2, 3, 4, 5].map((value) => ( +
= 4 ? 'hover:bg-[#baf2df] border-[#8ee9c2]' : 'hover:bg-gray-200 border-gray-400' }`} - /> - -
- ))} -
-

{question.category}

+ />
+ ))} + + Agree +
+

{question.category}

))} {currentPage > 1 && ( - + )} - From 046dc6de6a73a4de500933949615b9d7ed80d24c Mon Sep 17 00:00:00 2001 From: MoonGlade-create Date: Sat, 14 Sep 2024 22:01:36 -0400 Subject: [PATCH 6/6] fix: profile form --- .../HackerForm/HackathonProfileForm.tsx | 57 ++++---- web/package-lock.json | 129 +++++++++++++++++- 2 files changed, 161 insertions(+), 25 deletions(-) diff --git a/web/app/hacker/HackerForm/HackathonProfileForm.tsx b/web/app/hacker/HackerForm/HackathonProfileForm.tsx index 13e7017a..b2f3256a 100644 --- a/web/app/hacker/HackerForm/HackathonProfileForm.tsx +++ b/web/app/hacker/HackerForm/HackathonProfileForm.tsx @@ -125,7 +125,6 @@ export default function Component() { setCurrentQuestionIndex(0) } else { console.log("Submitted answers:", answers) - // Here you would typically send the answers to your backend } } @@ -153,53 +152,63 @@ export default function Component() { const currentQuestions = questions.slice(startIndex, endIndex) return ( - + - Hackathon Participant Survey - Please answer the following questions on a scale from strongly disagree to strongly agree. + Hackathon Participant Survey + Please answer the following questions on a scale from strongly disagree to strongly agree. - + {currentQuestions.map((question, index) => ( -
- -
- Disagree +
+ +
+ Disagree handleAnswer(question.id, value)} className="flex justify-center items-center space-x-6" > {[1, 2, 3, 4, 5].map((value) => ( -
- = 4 ? 'hover:bg-[#baf2df] border-[#8ee9c2]' : - 'hover:bg-gray-200 border-gray-400' +
+ > + = 4 ? 'hover:bg-[#baf2df] border-[#8ee9c2]' : + 'hover:bg-gray-200 border-gray-400' + }`} + /> +
))}
- Agree + + Agree
-

{question.category}

+

{question.category}

))} {currentPage > 1 && ( - + )} - ) -} \ No newline at end of file +} diff --git a/web/package-lock.json b/web/package-lock.json index b7e10763..c399910d 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -10,8 +10,12 @@ "dependencies": { "@auth/core": "^0.34.2", "@convex-dev/auth": "^0.0.61", + "@hookform/resolvers": "^3.9.0", "@radix-ui/react-dropdown-menu": "^2.1.1", "@radix-ui/react-icons": "^1.3.0", + "@radix-ui/react-label": "^2.1.0", + "@radix-ui/react-progress": "^1.1.0", + "@radix-ui/react-radio-group": "^1.2.0", "@radix-ui/react-slot": "^1.1.0", "@radix-ui/react-toast": "^1.2.1", "@radix-ui/react-toggle": "^1.1.0", @@ -23,8 +27,10 @@ "next-themes": "^0.3.0", "react": "^18", "react-dom": "^18", + "react-hook-form": "^7.53.0", "tailwind-merge": "^2.4.0", - "tailwindcss-animate": "^1.0.7" + "tailwindcss-animate": "^1.0.7", + "zod": "^3.23.8" }, "devDependencies": { "@types/node": "^20", @@ -624,6 +630,14 @@ "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.7.tgz", "integrity": "sha512-X8R8Oj771YRl/w+c1HqAC1szL8zWQRwFvgDwT129k9ACdBoud/+/rX9V0qiMl6LWUdP9voC2nDVZYPMQQsb6eA==" }, + "node_modules/@hookform/resolvers": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-3.9.0.tgz", + "integrity": "sha512-bU0Gr4EepJ/EQsH/IwEzYLsT/PEj5C0ynLQ4m+GSHS+xKH4TfSelhluTgOaoc4kA5s7eCsQbM4wvZLzELmWzUg==", + "peerDependencies": { + "react-hook-form": "^7.0.0" + } + }, "node_modules/@humanwhocodes/config-array": { "version": "0.11.14", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", @@ -1639,6 +1653,28 @@ } } }, + "node_modules/@radix-ui/react-label": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.1.0.tgz", + "integrity": "sha512-peLblDlFw/ngk3UWq0VnYaOLy6agTZZ+MUO/WhVfm14vJGML+xH4FAl2XQGLqdefjNb7ApRg6Yn7U42ZhmYXdw==", + "dependencies": { + "@radix-ui/react-primitive": "2.0.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-menu": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.1.1.tgz", @@ -1777,6 +1813,60 @@ } } }, + "node_modules/@radix-ui/react-progress": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-progress/-/react-progress-1.1.0.tgz", + "integrity": "sha512-aSzvnYpP725CROcxAOEBVZZSIQVQdHgBr2QQFKySsaD14u8dNT0batuXI+AAGDdAHfXH8rbnHmjYFqVJ21KkRg==", + "dependencies": { + "@radix-ui/react-context": "1.1.0", + "@radix-ui/react-primitive": "2.0.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-radio-group": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-radio-group/-/react-radio-group-1.2.0.tgz", + "integrity": "sha512-yv+oiLaicYMBpqgfpSPw6q+RyXlLdIpQWDHZbUKURxe+nEh53hFXPPlfhfQQtYkS5MMK/5IWIa76SksleQZSzw==", + "dependencies": { + "@radix-ui/primitive": "1.1.0", + "@radix-ui/react-compose-refs": "1.1.0", + "@radix-ui/react-context": "1.1.0", + "@radix-ui/react-direction": "1.1.0", + "@radix-ui/react-presence": "1.1.0", + "@radix-ui/react-primitive": "2.0.0", + "@radix-ui/react-roving-focus": "1.1.0", + "@radix-ui/react-use-controllable-state": "1.1.0", + "@radix-ui/react-use-previous": "1.1.0", + "@radix-ui/react-use-size": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-roving-focus": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.0.tgz", @@ -1971,6 +2061,20 @@ } } }, + "node_modules/@radix-ui/react-use-previous": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.1.0.tgz", + "integrity": "sha512-Z/e78qg2YFnnXcW88A4JmTtm4ADckLno6F7OXotmkQfeuCVaKuYzqAATPhVzl3delXE7CxIV8shofPn3jPc5Og==", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-use-rect": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.0.tgz", @@ -5964,6 +6068,21 @@ "react": "^18.3.1" } }, + "node_modules/react-hook-form": { + "version": "7.53.0", + "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.53.0.tgz", + "integrity": "sha512-M1n3HhqCww6S2hxLxciEXy2oISPnAzxY7gvwVPrtlczTM/1dDadXgUxDpHMrMTblDOcm/AXtXxHwZ3jpg1mqKQ==", + "engines": { + "node": ">=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/react-hook-form" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17 || ^18 || ^19" + } + }, "node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", @@ -7277,6 +7396,14 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/zod": { + "version": "3.23.8", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz", + "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } } } }