diff --git a/frontend/src/components/InProgressQuizzes.jsx b/frontend/src/components/InProgressQuizzes.jsx new file mode 100644 index 0000000..68e3943 --- /dev/null +++ b/frontend/src/components/InProgressQuizzes.jsx @@ -0,0 +1,50 @@ +import { useEffect, useState } from 'react' +import { useNavigate } from 'react-router-dom' +import api from '../api/client' +import ConfirmButton from './ConfirmButton' + +export default function InProgressQuizzes() { + const [inProgress, setInProgress] = useState([]) + const navigate = useNavigate() + + useEffect(() => { + api.get('/attempts/in-progress').then(res => setInProgress(res.data)).catch(() => {}) + }, []) + + const deleteAttempt = async (attemptId) => { + await api.delete(`/attempts/${attemptId}`) + setInProgress(prev => prev.filter(a => a.attempt_id !== attemptId)) + } + + if (inProgress.length === 0) return null + + return ( +
+

+ ⏸ In Progress ({inProgress.length}) +

+
+ {inProgress.map(a => ( +
+
+
{a.quiz_title}
+
+ Started {new Date(a.started_at).toLocaleDateString()} · {a.total_questions} questions +
+
+
+ + deleteAttempt(a.attempt_id)} + /> +
+
+ ))} +
+
+ ) +} diff --git a/frontend/src/pages/DashboardPage.jsx b/frontend/src/pages/DashboardPage.jsx index 325a8aa..b9a50a5 100644 --- a/frontend/src/pages/DashboardPage.jsx +++ b/frontend/src/pages/DashboardPage.jsx @@ -2,6 +2,7 @@ import { useState, useEffect } from 'react' import { Link } from 'react-router-dom' import api from '../api/client' import LineChart from '../components/LineChart' +import InProgressQuizzes from '../components/InProgressQuizzes' import { useAuth } from '../context/AuthContext' function greeting(name) { @@ -74,6 +75,8 @@ export default function DashboardPage() { )} + + {/* Performance graph with dropdown */} {history.length > 0 && (
diff --git a/frontend/src/pages/QuizPage.jsx b/frontend/src/pages/QuizPage.jsx index f5e3300..338b3d6 100644 --- a/frontend/src/pages/QuizPage.jsx +++ b/frontend/src/pages/QuizPage.jsx @@ -417,6 +417,10 @@ export default function QuizPage() { toastRef.current = setTimeout(() => setToast(''), 3000) } + const adjustImageZoom = (delta) => { + setImageZoom(z => Math.max(1, Math.min(4, z + delta))) + } + useEffect(() => { try { const saved = localStorage.getItem(`quiz-highlights:${id}`) @@ -1061,10 +1065,12 @@ const timerStarted = timeLeft !== null {expandedImagePath && (
setExpandedImagePath('')}>
e.stopPropagation()}> - + {Math.round(imageZoom * 100)}% - - + + {[1, 2, 3, 4].map(zoom => ( + + ))}
e.stopPropagation()}> diff --git a/frontend/src/pages/QuizzesPage.jsx b/frontend/src/pages/QuizzesPage.jsx index e4164c8..a174ec7 100644 --- a/frontend/src/pages/QuizzesPage.jsx +++ b/frontend/src/pages/QuizzesPage.jsx @@ -4,56 +4,11 @@ import { useAuth } from '../context/AuthContext' import api from '../api/client' import ConfirmButton from '../components/ConfirmButton' import Dialog from '../components/Dialog' +import InProgressQuizzes from '../components/InProgressQuizzes' import { useDialog } from '../hooks/useDialog' const TeachChat = lazy(() => import('../components/TeachChat')) -function InProgressSection() { - const [inProgress, setInProgress] = useState([]) - const navigate = useNavigate() - - useEffect(() => { - api.get('/attempts/in-progress').then(res => setInProgress(res.data)).catch(() => {}) - }, []) - - const deleteAttempt = async (attemptId) => { - await api.delete(`/attempts/${attemptId}`) - setInProgress(prev => prev.filter(a => a.attempt_id !== attemptId)) - } - - if (inProgress.length === 0) return null - - return ( -
-

- ⏸ In Progress ({inProgress.length}) -

-
- {inProgress.map(a => ( -
-
-
{a.quiz_title}
-
- Started {new Date(a.started_at).toLocaleDateString()} · {a.total_questions} questions -
-
-
- - deleteAttempt(a.attempt_id)} - /> -
-
- ))} -
-
- ) -} - function PastAttemptsSection() { const [open, setOpen] = useState(false) const [history, setHistory] = useState(null) @@ -506,7 +461,7 @@ export default function QuizzesPage() { )} {/* In-progress quizzes */} - {!isSearching && } + {!isSearching && } {/* Past attempts (loads on demand) */} {!isSearching && }