Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- "use client";
- import { useState } from "react";
- import { useForm, useFieldArray, Controller } from "react-hook-form";
- import { zodResolver } from "@hookform/resolvers/zod";
- import { z } from "zod";
- import { PlusCircle, X } from "lucide-react";
- import { Button } from "@/components/ui/button";
- import { Input } from "@/components/ui/input";
- import { Textarea } from "@/components/ui/textarea";
- import { Label } from "@/components/ui/label";
- import { Checkbox } from "@/components/ui/checkbox";
- import { useToast } from "@/hooks/use-toast";
- import { createTest } from "@/app/actions";
- import type { Test } from "@/types/test";
- const AnswerSchema = z.object({
- text: z.string().trim().min(1, "Answer text is required"),
- isCorrect: z.boolean(),
- });
- const QuestionSchema = z.object({
- text: z.string().min(1, "Question text is required"),
- answers: z.array(AnswerSchema).min(1, "At least one answer is required"),
- });
- const TestSchema = z.object({
- testName: z.string().min(1, "Test name is required"),
- testDescription: z.string().min(1, "Test description is required"),
- questions: z
- .array(QuestionSchema)
- .min(1, "At least one question is required"),
- });
- export default function TestCreationForm() {
- const { toast } = useToast();
- const [isSubmitting, setIsSubmitting] = useState(false);
- const {
- control,
- handleSubmit,
- formState: { errors },
- } = useForm<Test>({
- resolver: zodResolver(TestSchema),
- defaultValues: {
- testName: "",
- testDescription: "",
- questions: [{ text: "", answers: [{ text: "", isCorrect: false }] }],
- },
- });
- const {
- fields: questionFields,
- append: appendQuestion,
- remove: removeQuestion,
- } = useFieldArray({
- control,
- name: "questions",
- });
- const onFormSubmit = async (data: Test) => {
- setIsSubmitting(true);
- const formData = new FormData();
- formData.append("testData", JSON.stringify(data));
- try {
- const result = await createTest(formData);
- if (result.success) {
- toast({
- title: "Success",
- description: result.message,
- });
- } else {
- toast({
- title: "Error",
- description: result.message,
- variant: "destructive",
- });
- }
- } catch (error) {
- toast({
- title: "Error",
- description: "An unexpected error occurred",
- variant: "destructive",
- });
- } finally {
- setIsSubmitting(false);
- }
- };
- return (
- <form
- onSubmit={handleSubmit(onFormSubmit)}
- className="space-y-6 max-w-2xl mx-auto p-6"
- >
- <div className="space-y-2">
- <Label htmlFor="testName">Test Name</Label>
- <Controller
- name="testName"
- control={control}
- render={({ field }) => <Input {...field} id="testName" />}
- />
- {errors.testName && (
- <p className="text-red-500 text-sm">{errors.testName.message}</p>
- )}
- </div>
- <div className="space-y-2">
- <Label htmlFor="testDescription">Test Description</Label>
- <Controller
- name="testDescription"
- control={control}
- render={({ field }) => <Textarea {...field} id="testDescription" />}
- />
- {errors.testDescription && (
- <p className="text-red-500 text-sm">
- {errors.testDescription.message}
- </p>
- )}
- </div>
- <div className="space-y-4">
- {questionFields.map((questionField, questionIndex) => (
- <div key={questionIndex} className="border p-4 rounded-md space-y-2">
- <div className="flex items-center space-x-2">
- <Controller
- name={`questions.${questionIndex}.text`}
- control={control}
- render={({ field }) => (
- <Input {...field} placeholder="Enter question" />
- )}
- />
- <Button
- type="button"
- variant="ghost"
- size="icon"
- onClick={() => removeQuestion(questionIndex)}
- >
- <X className="h-4 w-4" />
- <span className="sr-only">Remove question</span>
- </Button>
- </div>
- {errors.questions?.[questionIndex]?.text && (
- <p className="text-red-500 text-sm">
- {errors.questions[questionIndex]?.text?.message}
- </p>
- )}
- <Controller
- name={`questions.${questionIndex}.answers`}
- control={control}
- render={({ field }) => (
- <div className="space-y-2">
- {field.value.map((answer, answerIndex) => (
- <div
- key={answerIndex}
- className="flex items-center space-x-2 ml-4"
- >
- <Controller
- name={`questions.${questionIndex}.answers.${answerIndex}.isCorrect`}
- control={control}
- render={({ field: checkboxField }) => (
- <Checkbox
- id={`question-${questionIndex}-answer-${answerIndex}-correct`}
- checked={checkboxField.value}
- onCheckedChange={checkboxField.onChange}
- />
- )}
- />
- <Controller
- name={`questions.${questionIndex}.answers.${answerIndex}.text`}
- control={control}
- render={({ field: inputField }) => (
- <Input {...inputField} placeholder="Enter answer" />
- )}
- />
- <Button
- type="button"
- variant="ghost"
- size="icon"
- onClick={() => {
- const newAnswers = [...field.value];
- newAnswers.splice(answerIndex, 1);
- field.onChange(newAnswers);
- }}
- >
- <X className="h-4 w-4" />
- <span className="sr-only">Remove answer</span>
- </Button>
- </div>
- ))}
- <Button
- type="button"
- variant="outline"
- size="sm"
- onClick={() =>
- field.onChange([
- ...field.value,
- { text: "", isCorrect: false },
- ])
- }
- className="mt-2"
- >
- <PlusCircle className="h-4 w-4 mr-2" />
- Add Answer
- </Button>
- </div>
- )}
- />
- {errors.questions?.[questionIndex]?.answers && (
- <p className="text-red-500 text-sm">
- {errors.questions[questionIndex]?.answers?.message}
- </p>
- )}
- </div>
- ))}
- </div>
- <Button
- type="button"
- onClick={() =>
- appendQuestion({
- text: "",
- answers: [{ text: "", isCorrect: false }],
- })
- }
- variant="outline"
- className="w-full"
- >
- <PlusCircle className="h-4 w-4 mr-2" />
- Add Question
- </Button>
- <Button type="submit" className="w-full" disabled={isSubmitting}>
- {isSubmitting ? "Submitting..." : "Submit Test"}
- </Button>
- </form>
- );
- }
Advertisement
Add Comment
Please, Sign In to add comment