From ccc2dae493ec47ada4407935efeade6709677fa0 Mon Sep 17 00:00:00 2001
From: BruArtist <60303299@udst.edu.qa>
Date: Sat, 11 Apr 2026 20:25:21 +0300
Subject: [PATCH 1/2] Add logout button to landing page
---
src/pages/LandingPage.jsx | 22 +++++++++++++++++++---
1 file changed, 19 insertions(+), 3 deletions(-)
diff --git a/src/pages/LandingPage.jsx b/src/pages/LandingPage.jsx
index fbc2b53..e798f06 100644
--- a/src/pages/LandingPage.jsx
+++ b/src/pages/LandingPage.jsx
@@ -3,6 +3,11 @@ import { useNavigate } from "react-router-dom";
function LandingPage() {
const navigate = useNavigate();
+ const handleLogout = () => {
+ localStorage.removeItem("quizeyUser");
+ navigate("/");
+ };
+
return (
@@ -15,7 +20,13 @@ function LandingPage() {
AI Chatbot
- Profile
+
+ Logout
+
@@ -34,7 +45,10 @@ function LandingPage() {
-
navigate("/generate")}>
+ navigate("/generate")}
+ >
Start Quiz
How it works
@@ -45,7 +59,9 @@ function LandingPage() {
- 98% success boosting quiz mastery
+
+ 98% success boosting quiz mastery
+
From a0ddce1149e66dfd7c03f1846066c3448f33fb41 Mon Sep 17 00:00:00 2001
From: BruArtist <60303299@udst.edu.qa>
Date: Sat, 11 Apr 2026 21:11:54 +0300
Subject: [PATCH 2/2] Fixed the dashboard and useless buttons that aint workind
and fixed the hashing and the salt to make it more secure for our security
implentation
---
backend/models/Result.js | 11 +-
backend/models/User.js | 6 +-
backend/routes/authRoutes.js | 38 ++++--
backend/routes/quizRoutes.js | 116 +++++++++++------
src/pages/DashboardPage.jsx | 226 +++++++++++++++++++++++++++++----
src/pages/FeedbackPage.jsx | 21 ++-
src/pages/GenerateQuizPage.jsx | 31 ++---
src/pages/LandingPage.jsx | 18 ++-
src/pages/QuizPage.jsx | 60 ++++++---
src/services/api.js | 44 ++++++-
10 files changed, 445 insertions(+), 126 deletions(-)
diff --git a/backend/models/Result.js b/backend/models/Result.js
index 335e321..b583076 100644
--- a/backend/models/Result.js
+++ b/backend/models/Result.js
@@ -1,10 +1,15 @@
-const mongoose = require('mongoose');
+const mongoose = require("mongoose");
const resultSchema = new mongoose.Schema(
{
+ userId: {
+ type: mongoose.Schema.Types.ObjectId,
+ ref: "User",
+ required: true,
+ },
quizId: {
type: mongoose.Schema.Types.ObjectId,
- ref: 'Quiz',
+ ref: "Quiz",
default: null,
},
score: {
@@ -31,4 +36,4 @@ const resultSchema = new mongoose.Schema(
{ timestamps: true }
);
-module.exports = mongoose.model('Result', resultSchema);
\ No newline at end of file
+module.exports = mongoose.model("Result", resultSchema);
\ No newline at end of file
diff --git a/backend/models/User.js b/backend/models/User.js
index 867d708..73e1fc5 100644
--- a/backend/models/User.js
+++ b/backend/models/User.js
@@ -15,11 +15,7 @@ const userSchema = new mongoose.Schema(
trim: true,
lowercase: true,
},
- passwordHash: {
- type: String,
- required: true,
- },
- passwordSalt: {
+ password: {
type: String,
required: true,
},
diff --git a/backend/routes/authRoutes.js b/backend/routes/authRoutes.js
index ce8df14..9417415 100644
--- a/backend/routes/authRoutes.js
+++ b/backend/routes/authRoutes.js
@@ -8,6 +8,32 @@ function hashPassword(password, salt) {
return crypto.scryptSync(password, salt, 64).toString("hex");
}
+function createStoredPassword(password) {
+ const salt = crypto.randomBytes(16).toString("hex");
+ const hash = hashPassword(password, salt);
+ return `${salt}:${hash}`;
+}
+
+function verifyStoredPassword(password, storedPassword) {
+ if (!storedPassword || typeof storedPassword !== "string") {
+ return false;
+ }
+
+ const parts = storedPassword.split(":");
+
+ if (parts.length !== 2) {
+ return false;
+ }
+
+ const [salt, originalHash] = parts;
+ const attemptHash = hashPassword(password, salt);
+
+ return crypto.timingSafeEqual(
+ Buffer.from(originalHash, "hex"),
+ Buffer.from(attemptHash, "hex")
+ );
+}
+
function sanitizeUser(user) {
return {
id: user._id,
@@ -16,7 +42,6 @@ function sanitizeUser(user) {
};
}
-// POST /api/auth/register
router.post("/register", async (req, res) => {
try {
const { fullName, email, password, confirmPassword } = req.body;
@@ -67,14 +92,12 @@ router.post("/register", async (req, res) => {
});
}
- const salt = crypto.randomBytes(16).toString("hex");
- const passwordHash = hashPassword(password, salt);
+ const storedPassword = createStoredPassword(password);
const newUser = await User.create({
fullName: fullName.trim(),
email: normalizedEmail,
- passwordHash,
- passwordSalt: salt,
+ password: storedPassword,
});
return res.status(201).json({
@@ -89,7 +112,6 @@ router.post("/register", async (req, res) => {
}
});
-// POST /api/auth/login
router.post("/login", async (req, res) => {
try {
const { email, password } = req.body;
@@ -112,9 +134,9 @@ router.post("/login", async (req, res) => {
});
}
- const attemptedHash = hashPassword(password, user.passwordSalt);
+ const isValidPassword = verifyStoredPassword(password, user.password);
- if (attemptedHash !== user.passwordHash) {
+ if (!isValidPassword) {
return res.status(401).json({
error: "Authentication Error",
details: "Invalid email or password.",
diff --git a/backend/routes/quizRoutes.js b/backend/routes/quizRoutes.js
index 270923a..a613fa8 100644
--- a/backend/routes/quizRoutes.js
+++ b/backend/routes/quizRoutes.js
@@ -1,70 +1,97 @@
-const express = require('express');
-const { generateQuiz } = require('../services/geminiService');
-const { calculateScore } = require('../services/scoreService');
-const Quiz = require('../models/Quiz');
-const Result = require('../models/Result');
+const express = require("express");
+const { generateQuiz } = require("../services/geminiService");
+const { calculateScore } = require("../services/scoreService");
+const Quiz = require("../models/Quiz");
+const Result = require("../models/Result");
const router = express.Router();
-router.post('/generate', async (req, res) => {
+router.post("/generate", async (req, res) => {
try {
const { topic, text } = req.body;
if (!topic && !text) {
- return res.status(400).json({ error: 'Bad Request: Either "topic" or "text" must be provided' });
+ return res
+ .status(400)
+ .json({ error: 'Bad Request: Either "topic" or "text" must be provided' });
}
- if (topic && typeof topic !== 'string') {
+
+ if (topic && typeof topic !== "string") {
return res.status(400).json({ error: 'Bad Request: "topic" must be a string' });
}
- if (text && typeof text !== 'string') {
+
+ if (text && typeof text !== "string") {
return res.status(400).json({ error: 'Bad Request: "text" must be a string' });
}
const trimmedTopic = topic ? topic.trim() : null;
const trimmedText = text ? text.trim() : null;
- if ((!trimmedTopic || trimmedTopic.length === 0) && (!trimmedText || trimmedText.length === 0)) {
- return res.status(400).json({ error: 'Bad Request: "topic" or "text" cannot be empty' });
+ if (
+ (!trimmedTopic || trimmedTopic.length === 0) &&
+ (!trimmedText || trimmedText.length === 0)
+ ) {
+ return res
+ .status(400)
+ .json({ error: 'Bad Request: "topic" or "text" cannot be empty' });
}
const questions = await generateQuiz({ topic: trimmedTopic, text: trimmedText });
- // Save quiz to MongoDB
const quiz = await Quiz.create({
topic: trimmedTopic,
sourceText: trimmedText,
questions,
});
- return res.status(200).json({ quizId: quiz._id, questions: quiz.questions });
+ return res.status(200).json({
+ quizId: quiz._id,
+ questions: quiz.questions,
+ });
} catch (error) {
- return res.status(500).json({ error: 'Failed to generate quiz', details: error.message });
+ return res.status(500).json({
+ error: "Failed to generate quiz",
+ details: error.message,
+ });
}
});
-router.post('/submit', async (req, res) => {
+router.post("/submit", async (req, res) => {
try {
- const { quizId, questions, answers } = req.body;
+ const { userId, quizId, questions, answers } = req.body;
+
+ if (!userId) {
+ return res.status(400).json({
+ error: 'Bad Request: "userId" is required',
+ });
+ }
if (!questions || !Array.isArray(questions)) {
- return res.status(400).json({ error: 'Bad Request: "questions" must be an array' });
+ return res.status(400).json({
+ error: 'Bad Request: "questions" must be an array',
+ });
}
if (!answers || !Array.isArray(answers)) {
- return res.status(400).json({ error: 'Bad Request: "answers" must be an array' });
+ return res.status(400).json({
+ error: 'Bad Request: "answers" must be an array',
+ });
}
if (questions.length === 0) {
- return res.status(400).json({ error: 'Bad Request: "questions" cannot be empty' });
+ return res.status(400).json({
+ error: 'Bad Request: "questions" cannot be empty',
+ });
}
if (questions.length !== answers.length) {
- return res.status(400).json({ error: 'Mismatch between questions and answers length' });
+ return res.status(400).json({
+ error: "Mismatch between questions and answers length",
+ });
}
const scoreResult = calculateScore(questions, answers);
- // Ensure all required fields exist
const normalizedResult = {
score: scoreResult.score,
total: scoreResult.total,
@@ -72,8 +99,8 @@ router.post('/submit', async (req, res) => {
wrongAnswers: scoreResult.wrongAnswers,
};
- // Save result to MongoDB
const result = await Result.create({
+ userId,
quizId: quizId || null,
...normalizedResult,
answers,
@@ -83,47 +110,64 @@ router.post('/submit', async (req, res) => {
resultId: result._id,
...normalizedResult,
});
-
} catch (error) {
return res.status(500).json({
- error: 'Failed to calculate score',
+ error: "Failed to calculate score",
details: error.message,
});
}
});
-router.get('/results', async (req, res) => {
+router.get("/results", async (req, res) => {
try {
- const results = await Result.find()
- .populate('quizId')
+ const { userId } = req.query;
+
+ if (!userId) {
+ return res.status(400).json({
+ error: 'Bad Request: "userId" is required',
+ });
+ }
+
+ const results = await Result.find({ userId })
+ .populate("quizId")
.sort({ createdAt: -1 });
return res.status(200).json(results);
} catch (error) {
return res.status(500).json({
- error: 'Failed to fetch results',
+ error: "Failed to fetch results",
details: error.message,
});
}
});
-router.get('/dashboard', async (req, res) => {
+router.get("/dashboard", async (req, res) => {
try {
- const results = await Result.find()
- .populate('quizId')
+ const { userId } = req.query;
+
+ if (!userId) {
+ return res.status(400).json({
+ error: 'Bad Request: "userId" is required',
+ });
+ }
+
+ const results = await Result.find({ userId })
+ .populate("quizId")
.sort({ createdAt: -1 });
const completed = results.length;
const avgScore = completed
? Math.round(
- results.reduce((sum, item) => sum + (item.total ? (item.score / item.total) * 100 : 0), 0) / completed
+ results.reduce((sum, item) => {
+ return sum + (item.total ? (item.score / item.total) * 100 : 0);
+ }, 0) / completed
)
: 0;
const recentQuizzes = results.slice(0, 5).map((item) => ({
id: item._id,
- topic: item.quizId?.topic || 'Untitled Quiz',
+ topic: item.quizId?.topic || "Untitled Quiz",
score: item.score,
total: item.total,
percentage: item.total ? Math.round((item.score / item.total) * 100) : 0,
@@ -147,14 +191,14 @@ router.get('/dashboard', async (req, res) => {
});
} catch (error) {
return res.status(500).json({
- error: 'Failed to fetch dashboard data',
+ error: "Failed to fetch dashboard data",
details: error.message,
});
}
});
-router.get('/health', (req, res) => {
- res.status(200).json({ status: 'ok' });
+router.get("/health", (req, res) => {
+ res.status(200).json({ status: "ok" });
});
module.exports = router;
\ No newline at end of file
diff --git a/src/pages/DashboardPage.jsx b/src/pages/DashboardPage.jsx
index bd7ed36..a9aa61e 100644
--- a/src/pages/DashboardPage.jsx
+++ b/src/pages/DashboardPage.jsx
@@ -1,8 +1,18 @@
-import { useEffect, useState } from "react";
+import { useEffect, useMemo, useRef, useState } from "react";
import { useNavigate } from "react-router-dom";
+import {
+ clearStoredUser,
+ fetchDashboardData,
+ getStoredUser,
+ getUserInitials,
+} from "../services/api";
function DashboardPage() {
const navigate = useNavigate();
+ const currentUser = useMemo(() => getStoredUser(), []);
+ const firstName = currentUser?.fullName?.trim()?.split(" ")[0] || "Learner";
+ const settingsRef = useRef(null);
+
const [dashboardData, setDashboardData] = useState({
completed: 0,
avgScore: 0,
@@ -10,22 +20,60 @@ function DashboardPage() {
chartData: [],
latestQuiz: null,
});
+
const [loading, setLoading] = useState(true);
const [error, setError] = useState("");
+ const [settings, setSettings] = useState(() => {
+ const saved = localStorage.getItem("quizeySettings");
+ if (saved) {
+ try {
+ return JSON.parse(saved);
+ } catch {
+ return {
+ preferredDifficulty: "medium",
+ preferredQuestions: 10,
+ studyGoal: 5,
+ emailTips: true,
+ };
+ }
+ }
+
+ return {
+ preferredDifficulty: "medium",
+ preferredQuestions: 10,
+ studyGoal: 5,
+ emailTips: true,
+ };
+ });
+
+ const handleLogout = () => {
+ clearStoredUser();
+ navigate("/");
+ };
+
+ const handleSettingChange = (field, value) => {
+ const updated = { ...settings, [field]: value };
+ setSettings(updated);
+ localStorage.setItem("quizeySettings", JSON.stringify(updated));
+ };
+
+ const scrollToSettings = () => {
+ settingsRef.current?.scrollIntoView({ behavior: "smooth", block: "start" });
+ };
+
useEffect(() => {
- const fetchDashboardData = async () => {
+ if (!currentUser?.id) {
+ navigate("/");
+ return;
+ }
+
+ const loadDashboard = async () => {
try {
setLoading(true);
setError("");
- const response = await fetch("http://localhost:5000/api/quiz/dashboard");
- const data = await response.json();
-
- if (!response.ok) {
- throw new Error(data.error || "Failed to fetch dashboard data.");
- }
-
+ const data = await fetchDashboardData(currentUser.id);
setDashboardData(data);
} catch (err) {
setError(err.message || "Failed to load dashboard.");
@@ -34,16 +82,22 @@ function DashboardPage() {
}
};
- fetchDashboardData();
- }, []);
+ loadDashboard();
+ }, [currentUser, navigate]);
- const { completed, avgScore, recentQuizzes, chartData, latestQuiz } = dashboardData;
+ const { completed, avgScore, recentQuizzes, chartData, latestQuiz } =
+ dashboardData;
const safeChartData = [...chartData];
while (safeChartData.length < 7) {
safeChartData.unshift({ percentage: 0 });
}
+ const highestPercentage = Math.max(
+ ...safeChartData.map((x) => x.percentage),
+ 0
+ );
+
if (loading) {
return (
@@ -80,11 +134,15 @@ function DashboardPage() {
Generate
History
-
AI Assistant
-
Settings
+
+ Settings
+
-
navigate("/generate")}>
+ navigate("/generate")}
+ >
+ New Quiz
@@ -92,14 +150,22 @@ function DashboardPage() {
-
Welcome back, Alex.
+
Welcome back, {firstName}.
{completed > 0
- ? `You have completed ${completed} quiz${completed > 1 ? "zes" : ""} so far.`
+ ? `You have completed ${completed} quiz${
+ completed > 1 ? "zes" : ""
+ } so far.`
: "Start your first quiz to see your progress here."}
-
Profile
+
+ Logout
+
@@ -114,10 +180,13 @@ function DashboardPage() {
{latestQuiz
? `You scored ${latestQuiz.score}/${latestQuiz.total} on your latest quiz.`
- : "Generate and complete a quiz to populate your dashboard with real data."}
+ : "Generate and complete a quiz to populate your dashboard with your own data."}
-
navigate("/generate")}>
+ navigate("/generate")}
+ >
{latestQuiz ? "Take Another Quiz" : "Start Quiz"}
@@ -128,9 +197,11 @@ function DashboardPage() {
-
A
-
Alex Thorne
-
Expert Polymath • Level 42
+
+ {getUserInitials(currentUser?.fullName)}
+
+
{currentUser?.fullName || "Learner"}
+
{currentUser?.email || "No email available"}
@@ -146,15 +217,14 @@ function DashboardPage() {
Score History
-
Performance over recent attempts
+
Performance over your recent attempts
{safeChartData.map((item, index) => (
x.percentage)) &&
- item.percentage > 0
+ item.percentage === highestPercentage && item.percentage > 0
? "active-bar"
: ""
}
@@ -176,7 +246,7 @@ function DashboardPage() {
No quizzes yet
-
Complete a quiz to see history here.
+
Complete a quiz to see your history here.
0/0
@@ -194,6 +264,108 @@ function DashboardPage() {
))
)}
+
+
+
Quick Settings
+
+ Personalize your learning flow. These settings are saved on your
+ device and used as your default study preferences.
+
+
+
+
+
+ Preferred Difficulty
+
+
+ handleSettingChange("preferredDifficulty", e.target.value)
+ }
+ style={{ width: "100%", padding: "12px", borderRadius: "12px" }}
+ >
+ Easy
+ Medium
+ Hard
+
+
+
+
+
+ Default Question Count
+
+
+ handleSettingChange(
+ "preferredQuestions",
+ Number(e.target.value)
+ )
+ }
+ style={{ width: "100%", padding: "12px", borderRadius: "12px" }}
+ >
+ 5
+ 10
+ 15
+ 20
+
+
+
+
+
+ Weekly Study Goal
+
+
+ handleSettingChange("studyGoal", Number(e.target.value))
+ }
+ style={{ width: "100%", padding: "12px", borderRadius: "12px" }}
+ >
+ 3 quizzes
+ 5 quizzes
+ 7 quizzes
+ 10 quizzes
+
+
+
+
+
+ Learning Tips
+
+
+ handleSettingChange("emailTips", !settings.emailTips)
+ }
+ style={{ width: "100%" }}
+ >
+ {settings.emailTips ? "Enabled" : "Disabled"}
+
+
+
+
+
+
Saved Preferences
+
+ Difficulty: {settings.preferredDifficulty} | Questions:{" "}
+ {settings.preferredQuestions} | Goal:{" "}
+ {settings.studyGoal} quizzes/week | Tips:{" "}
+ {settings.emailTips ? "On" : "Off"}
+
+
+
diff --git a/src/pages/FeedbackPage.jsx b/src/pages/FeedbackPage.jsx
index 5da508a..989c069 100644
--- a/src/pages/FeedbackPage.jsx
+++ b/src/pages/FeedbackPage.jsx
@@ -1,4 +1,5 @@
import { useLocation, useNavigate } from "react-router-dom";
+import { clearStoredUser } from "../services/api";
function FeedbackPage() {
const location = useLocation();
@@ -14,6 +15,11 @@ function FeedbackPage() {
const correctAnswers = location.state?.correctAnswers ?? score;
const wrongAnswers = location.state?.wrongAnswers ?? total - score;
+ const handleLogout = () => {
+ clearStoredUser();
+ navigate("/");
+ };
+
if (!questions.length) {
return (
@@ -40,9 +46,14 @@ function FeedbackPage() {
navigate("/landing")}>Home
Generate Quiz
navigate("/dashboard")}>My Quizzes
-
AI Chatbot
-
A
+
+ Logout
+
@@ -115,10 +126,12 @@ function FeedbackPage() {
- navigate("/dashboard")}>
+ navigate("/dashboard")}
+ >
Go to Dashboard →
- Ask AI More
diff --git a/src/pages/GenerateQuizPage.jsx b/src/pages/GenerateQuizPage.jsx
index d74cdbf..7d505e5 100644
--- a/src/pages/GenerateQuizPage.jsx
+++ b/src/pages/GenerateQuizPage.jsx
@@ -1,5 +1,6 @@
import { useState } from "react";
import { useNavigate } from "react-router-dom";
+import { clearStoredUser, generateQuiz } from "../services/api";
function GenerateQuizPage() {
const navigate = useNavigate();
@@ -11,7 +12,10 @@ function GenerateQuizPage() {
const [error, setError] = useState("");
const [errors, setErrors] = useState({});
- const API_URL = "http://localhost:5000/api/quiz/generate";
+ const handleLogout = () => {
+ clearStoredUser();
+ navigate("/");
+ };
const validateForm = () => {
const newErrors = {};
@@ -49,22 +53,10 @@ function GenerateQuizPage() {
setError("");
setErrors({});
- const response = await fetch(API_URL, {
- method: "POST",
- headers: {
- "Content-Type": "application/json",
- },
- body: JSON.stringify({
- topic: topic.trim(),
- }),
+ const data = await generateQuiz({
+ topic: topic.trim(),
});
- const data = await response.json();
-
- if (!response.ok) {
- throw new Error(data.error || "Failed to generate quiz.");
- }
-
const questions = data.questions || [];
if (!Array.isArray(questions) || questions.length === 0) {
@@ -96,10 +88,15 @@ function GenerateQuizPage() {
navigate("/landing")}>Home
Generate Quiz
navigate("/dashboard")}>My Quizzes
-
AI Chatbot
-
Profile
+
+ Logout
+
diff --git a/src/pages/LandingPage.jsx b/src/pages/LandingPage.jsx
index e798f06..bc3e0c6 100644
--- a/src/pages/LandingPage.jsx
+++ b/src/pages/LandingPage.jsx
@@ -1,10 +1,11 @@
import { useNavigate } from "react-router-dom";
+import { clearStoredUser } from "../services/api";
function LandingPage() {
const navigate = useNavigate();
const handleLogout = () => {
- localStorage.removeItem("quizeyUser");
+ clearStoredUser();
navigate("/");
};
@@ -17,7 +18,6 @@ function LandingPage() {
Home
navigate("/generate")}>Generate Quiz
navigate("/dashboard")}>My Quizzes
- AI Chatbot
Start Quiz
- How it works
+ navigate("/dashboard")}
+ >
+ View Progress
+
@@ -115,7 +120,12 @@ function LandingPage() {
Track your strengths and identify weak areas with deep AI-driven
insights.
-
View Analytics →
+
navigate("/dashboard")}
+ >
+ View Analytics
+
diff --git a/src/pages/QuizPage.jsx b/src/pages/QuizPage.jsx
index 7fcbbb7..3a2e0a2 100644
--- a/src/pages/QuizPage.jsx
+++ b/src/pages/QuizPage.jsx
@@ -1,5 +1,10 @@
import { useLocation, useNavigate } from "react-router-dom";
import { useState } from "react";
+import {
+ clearStoredUser,
+ getStoredUser,
+ submitQuiz,
+} from "../services/api";
function QuizPage() {
const location = useLocation();
@@ -8,12 +13,18 @@ function QuizPage() {
const questions = location.state?.questions || [];
const topic = location.state?.topic || "Quiz";
const quizId = location.state?.quizId || null;
+ const currentUser = getStoredUser();
const [currentIndex, setCurrentIndex] = useState(0);
const [selectedAnswers, setSelectedAnswers] = useState({});
const [loading, setLoading] = useState(false);
const [error, setError] = useState("");
+ const handleLogout = () => {
+ clearStoredUser();
+ navigate("/");
+ };
+
if (!questions.length) {
return (
@@ -55,25 +66,20 @@ function QuizPage() {
setLoading(true);
setError("");
- const answersArray = questions.map((_, index) => selectedAnswers[index] || null);
-
- const response = await fetch("http://localhost:5000/api/quiz/submit", {
- method: "POST",
- headers: {
- "Content-Type": "application/json",
- },
- body: JSON.stringify({
- quizId,
- questions,
- answers: answersArray,
- }),
- });
+ if (!currentUser?.id) {
+ throw new Error("Please log in again before submitting your quiz.");
+ }
- const data = await response.json();
+ const answersArray = questions.map(
+ (_, index) => selectedAnswers[index] || null
+ );
- if (!response.ok) {
- throw new Error(data.error || "Failed to submit quiz.");
- }
+ const data = await submitQuiz({
+ userId: currentUser.id,
+ quizId,
+ questions,
+ answers: answersArray,
+ });
navigate("/feedback", {
state: {
@@ -111,9 +117,14 @@ function QuizPage() {
navigate("/landing")}>Home
Generate Quiz
navigate("/dashboard")}>My Quizzes
-
AI Chatbot
-
A
+
+ Logout
+
@@ -128,7 +139,10 @@ function QuizPage() {
@@ -173,7 +187,11 @@ function QuizPage() {
{currentIndex < questions.length - 1 ? (
-
+
Next
) : (
diff --git a/src/services/api.js b/src/services/api.js
index f8288f4..7c2c198 100644
--- a/src/services/api.js
+++ b/src/services/api.js
@@ -4,12 +4,34 @@ async function handleResponse(response, fallbackMessage) {
const data = await response.json().catch(() => ({}));
if (!response.ok) {
- throw new Error(data.details || fallbackMessage);
+ throw new Error(data.details || data.error || fallbackMessage);
}
return data;
}
+export function getStoredUser() {
+ try {
+ const rawUser = localStorage.getItem("quizeyUser");
+ return rawUser ? JSON.parse(rawUser) : null;
+ } catch {
+ return null;
+ }
+}
+
+export function clearStoredUser() {
+ localStorage.removeItem("quizeyUser");
+}
+
+export function getUserInitials(fullName = "") {
+ const parts = fullName.trim().split(" ").filter(Boolean);
+
+ if (parts.length === 0) return "U";
+ if (parts.length === 1) return parts[0][0].toUpperCase();
+
+ return `${parts[0][0]}${parts[1][0]}`.toUpperCase();
+}
+
export async function registerUser(payload) {
const response = await fetch(`${API_BASE_URL}/api/auth/register`, {
method: "POST",
@@ -44,4 +66,24 @@ export async function generateQuiz(payload) {
});
return handleResponse(response, "Failed to generate quiz.");
+}
+
+export async function submitQuiz(payload) {
+ const response = await fetch(`${API_BASE_URL}/api/quiz/submit`, {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify(payload),
+ });
+
+ return handleResponse(response, "Failed to submit quiz.");
+}
+
+export async function fetchDashboardData(userId) {
+ const response = await fetch(
+ `${API_BASE_URL}/api/quiz/dashboard?userId=${encodeURIComponent(userId)}`
+ );
+
+ return handleResponse(response, "Failed to fetch dashboard data.");
}
\ No newline at end of file