diff --git a/src/actions/custom_quiz_actions.ts b/src/actions/custom_quiz_actions.ts new file mode 100644 index 0000000..7f4e6d9 --- /dev/null +++ b/src/actions/custom_quiz_actions.ts @@ -0,0 +1,141 @@ +"use server" + +import { getCookie } from "@/actions/cookie_actions"; + +const API_BASE_URL = process.env.NEXT_PUBLIC_STUDENT_API_BASE_URL; + +export const createCustomQuiz = async (quizData) => { + try { + const token = await getCookie("token"); + const res = await fetch( + `${API_BASE_URL}/api/quiz/custom/create`, + { + method: "POST", + headers: { + "Content-Type": "application/json", + Cookie: `token=${token}`, + }, + body: JSON.stringify(quizData), + credentials: "include", + } + ); + + if (!res.ok) { + throw new Error(`HTTP error! status: ${res.status}`); + } + + return await res.json(); + } catch (error) { + if (error instanceof Error) { + throw new Error( + `Error in creating custom quiz: ${error.message}` + ); + } else { + throw new Error( + "An unknown error occurred while creating custom quiz!" + ); + } + } +} + +export const getCustomQuizzes = async () => { + try { + const token = await getCookie("token"); + const res = await fetch( + `${API_BASE_URL}/api/quiz/custom/get`, + { + method: "GET", + headers: { + "Content-Type": "application/json", + Cookie: `token=${token}`, + }, + credentials: "include", + cache: "force-cache", + } + ); + + if (!res.ok) { + throw new Error(`HTTP error! status: ${res.status}`); + } + + return await res.json(); + } catch (error) { + if (error instanceof Error) { + throw new Error( + `Error in fetching custom quizzes: ${error.message}` + ); + } else { + throw new Error( + "An unknown error occurred while fetching custom quizzes!" + ); + } + } +} + +export const getCustomQuizQuestions = async (quizId) => { + try { + const token = await getCookie("token"); + const res = await fetch( + `${API_BASE_URL}/api/quiz/custom/${quizId}/questions`, + { + method: "GET", + headers: { + "Content-Type": "application/json", + Cookie: `token=${token}`, + }, + credentials: "include", + cache: "force-cache", + } + ); + + if (!res.ok) { + throw new Error(`HTTP error! status: ${res.status}`); + } + + return await res.json(); + } catch (error) { + if (error instanceof Error) { + throw new Error( + `Error in fetching custom quiz questions: ${error.message}` + ); + } else { + throw new Error( + "An unknown error occurred while fetching custom quiz questions!" + ); + } + } +} + +export const saveCustomQuizAnswers = async (answerData : any) => { + try { + const token = await getCookie("token"); + const res = await fetch( + `${API_BASE_URL}/api/quiz/custom/question/save`, + { + method: "POST", + headers: { + "Content-Type": "application/json", + Cookie: `token=${token}`, + }, + body: JSON.stringify(answerData), + credentials: "include", + } + ); + + if (!res.ok) { + throw new Error(`HTTP error! status: ${res.status}`); + } + + return await res.json(); + } catch (error) { + if (error instanceof Error) { + throw new Error( + `Error in saving custom quiz answers: ${error.message}` + ); + } else { + throw new Error( + "An unknown error occurred while saving custom quiz answers!" + ); + } + } +} \ No newline at end of file diff --git a/src/app/(root)/quizzes/_components/CustomizedQuiz.tsx b/src/app/(root)/quizzes/_components/CustomizedQuiz.tsx index 4e0af38..86460bf 100644 --- a/src/app/(root)/quizzes/_components/CustomizedQuiz.tsx +++ b/src/app/(root)/quizzes/_components/CustomizedQuiz.tsx @@ -1,123 +1,202 @@ -import React, { useState } from "react"; -import CustomInput from "./CustomInput"; +import React, { useEffect, useState } from "react"; import Image from "next/image"; import icon from "../../../../../public/assets/images/customizequiz.png"; import { Button } from "@/components/ui/button"; +import { MultiSelect } from "@/components/ui/multi-select"; +import CustomInput from "./CustomInput"; +import { toast } from "sonner"; +import { getSubjectChapters, getChapterTopics } from "@/actions/question_actions"; +import { useAppSelector } from "@/redux/hooks"; +import { createCustomQuiz } from "@/actions/custom_quiz_actions"; +import { IAcademic, subjectChaptersProps, Topic } from "../types"; +import { TCustomQuizProps } from "@/helpers/types/index"; -const subjects = ["Math", "Science"]; -const chapters = { - Math: ["Algebra", "Geometry"], - Science: ["Physics", "Chemistry"], -}; -const topics = { - Algebra: ["Linear Equations", "Quadratic Equations"], - Geometry: ["Triangles", "Circles"], - Physics: ["Kinematics", "Dynamics"], - Chemistry: ["Organic Chemistry", "Inorganic Chemistry"], -}; +const CustomizedQuiz = () => { + const userAcademic = useAppSelector((state) => state.user.user?.academic as IAcademic); + const [quizState, setQuizState] = useState({ + subject: "", + chapters: [], + topics: [], + numberOfQuestions: "", + }); + const [chaptersData, setChaptersData] = useState([]); + const [topics, setTopics] = useState([]); + const [isLoading, setIsLoading] = useState(false); + const [creatingQuiz, setCreatingQuiz] = useState(false); + const [draftCount, setDraftCount] = useState(0); -const CustomizedQuiz: React.FC = () => { - const [subjectName, setSubjectName] = useState(""); - const [chapterName, setChapterName] = useState(""); - const [topicName, setTopicName] = useState(""); - const [numberOfQuestions, setNumberOfQuestions] = useState(""); - const [difficulty, setDifficulty] = useState(""); - - const handleCreateNow = () => { - // Logic for creating the quiz - console.log({ - subjectName, - chapterName, - topicName, - numberOfQuestions, - difficulty, - }); - }; + useEffect(() => { + const savedDraftCount = localStorage.getItem("draftCount"); + if (savedDraftCount) { + setDraftCount(parseInt(savedDraftCount, 10)); + } + }, []); + + useEffect(() => { + const fetchChapters = async () => { + // if (!quizState.subject || !userAcademic?.standard) return; + // setIsLoading(true); + try { + const data = await getSubjectChapters(quizState.subject, userAcademic.standard); + if (data?.chapters) { + setChaptersData(data.chapters); + } + } catch (error) { + toast.error("Unable to fetch chapters!"); + } finally { + setIsLoading(false); + } + }; + + fetchChapters(); + }, [quizState.subject, userAcademic?.standard]); + + useEffect(() => { + const fetchTopics = async () => { + if (!quizState.subject || quizState.chapters.length === 0 || !userAcademic?.standard) return; + setIsLoading(true); + try { + const data = await getChapterTopics( + quizState.subject, + quizState.chapters[0], + userAcademic.standard + ); + setTopics(data.topics); + } catch (error) { + toast.error("Unable to fetch topics!"); + } finally { + setIsLoading(false); + } + }; - const handleSubjectChange = (e: React.ChangeEvent) => { - setSubjectName(e.target.value); - setChapterName(""); - setTopicName(""); + fetchTopics(); + }, [quizState.subject, quizState.chapters, userAcademic?.standard]); + + const handleSaveAsDraft = () => { + const newDraftCount = draftCount + 1; + setDraftCount(newDraftCount); + localStorage.setItem("draftCount", newDraftCount.toString()); + toast.success("Draft saved successfully!"); }; - const handleChapterChange = (e: React.ChangeEvent) => { - setChapterName(e.target.value); - setTopicName(""); + const handleCreateQuiz = async () => { + if (!quizState.subject || quizState.chapters.length === 0 || quizState.topics.length === 0) { + toast.error("Please select subject, chapters, and topics."); + return; + } + setCreatingQuiz(true); + try { + const response = await createCustomQuiz({ + ...quizState, + subjects: [quizState.subject], + numberOfQuestions: parseInt(quizState.numberOfQuestions) || undefined, + }); + if (response.success) { + toast.success("Custom quiz created successfully!"); + } else { + toast.error(response.message || "Failed to create custom quiz."); + } + } catch (error) { + toast.error(`Error creating quiz: ${error instanceof Error ? error.message : "An unknown error occurred"}`); + } finally { + setCreatingQuiz(false); + } }; return (

Customized Quiz

-

Draft(0)

+

Draft({draftCount})

- - + + +
+ + ({ + _id: chapter._id, + name: chapter.name, + }))} + onValueChange={(selectedChapters) => + setQuizState((prev) => ({ + ...prev, + chapters: selectedChapters, + topics: [], + })) } - value={chapterName} - onChange={handleChapterChange} - label="Chapter Name" - placeholder="Select Chapter" + defaultValue={quizState.chapters} + placeholder="Select Chapters" + maxCount={3} /> - setTopicName(e.target.value)} - label="Topic Name" - placeholder="Select Topic" + + ({ + _id: topic._id, + name: topic.name, + }))} + onValueChange={(selectedTopics) => + setQuizState((prev) => ({ + ...prev, + topics: selectedTopics, + })) + } + defaultValue={quizState.topics} + placeholder="Select Topics" + maxCount={5} /> + setNumberOfQuestions(e.target.value)} + value={quizState.numberOfQuestions} + onChange={(e) => + setQuizState((prev) => ({ + ...prev, + numberOfQuestions: e.target.value, + })) + } label="Number of Questions" placeholder="Enter Number of Questions" /> -
- setDifficulty("Easy")} - label="Easy" - /> - setDifficulty("Moderate")} - label="Moderate" - /> - setDifficulty("Hard")} - label="Hard" - /> -
-
- -
-

- {/* Create your own personalized quiz by giving the information related to - the quiz and practice! */} - Creating quiz coming soon... + +

+ Click 'Create Now' to generate your custom quiz!

+ customize quiz ); diff --git a/src/helpers/types/index.ts b/src/helpers/types/index.ts index c8aa986..b386e72 100644 --- a/src/helpers/types/index.ts +++ b/src/helpers/types/index.ts @@ -79,6 +79,13 @@ export type TMoodEmojisProps = { moodImg: string; }; +export type TCustomQuizProps = { + subject: string; + chapters: string[]; + topics: string[]; + numberOfQuestions: string; +} + export type TQuizQuestionOptionsProps = { name: string; tag: string; diff --git a/src/redux/store.ts b/src/redux/store.ts index dce0c8e..d2b1762 100644 --- a/src/redux/store.ts +++ b/src/redux/store.ts @@ -1,5 +1,5 @@ -import { configureStore } from "@reduxjs/toolkit"; +import { configureStore } from "@reduxjs/toolkit"; import userReducer from "@/redux/slices/userSlice"; import weeklyReportReducer from "@/redux/slices/weeklyReportSlice"; import monthlyReportReducer from "@/redux/slices/monthlyReportSlice";