feat: implement retake exam functionality and update translations for exam status
Test / backend (push) Successful in 12s
Test / frontend (push) Successful in 23s

This commit is contained in:
LaurensBartelds committed 2026-04-27 11:28:01 +02:00
1 parent f794dacd2d
commit 4935251125
4 files changed
+31 -2

No files matched your search

+28 -1
View File
@@ -2,7 +2,9 @@ import { useState, useEffect, useCallback } from 'react'
import { useNavigate } from 'react-router-dom'
import { useTranslation } from 'react-i18next'
import * as resultApi from '../api/resultApi'
import * as examApi from '../api/examApi'
import { getExamUrlId } from '../utils/slug'
import type { Exam } from '../types'
interface ResultItem {
id: number
@@ -17,7 +19,11 @@ interface ResultItem {
exam_id: number | null
}
export default function MyResults() {
interface Props {
onLoadExam: (exam: Exam, saveToWorkspace?: boolean, examId?: number | null) => void
}
export default function MyResults({ onLoadExam }: Props) {
const { t } = useTranslation()
const navigate = useNavigate()
const [results, setResults] = useState<ResultItem[]>([])
@@ -25,6 +31,7 @@ export default function MyResults() {
const [loading, setLoading] = useState(true)
const [error, setError] = useState<string | null>(null)
const [deleteConfirm, setDeleteConfirm] = useState<number | null>(null)
const [retakingId, setRetakingId] = useState<number | null>(null)
const loadResults = useCallback(async () => {
setLoading(true)
@@ -54,6 +61,18 @@ export default function MyResults() {
}
}
const retakeExam = async (item: ResultItem) => {
if (!item.exam_id) return
setRetakingId(item.id)
try {
const exam = await examApi.getExam(item.exam_id)
onLoadExam(exam, false, item.exam_id)
} catch (e) {
setError((e as Error).message)
setRetakingId(null)
}
}
const reviewResult = async (item: ResultItem) => {
try {
const data = await resultApi.getResult(item.id)
@@ -129,6 +148,14 @@ export default function MyResults() {
</div>
<div className="workspace-card-actions" style={{ gap: 8 }}>
<div className={`score-pill ${passed ? 'pass' : 'fail'}`}>{pct}%</div>
<button
className="btn btn-primary btn-sm"
onClick={() => void retakeExam(r)}
disabled={retakingId === r.id || !r.exam_id}
title={r.exam_id ? undefined : t('results.cantRetake')}
>
{retakingId === r.id ? t('common.loading') : t('results.retakeExam')}
</button>
<button className="btn btn-sm" onClick={() => void reviewResult(r)}>
{t('common.edit')}
</button>
+1 -1
View File
@@ -83,7 +83,7 @@ export default function Workspace({ onLoadExam }: Props) {
<main className="workspace-main">
{activeTab === 'exams' && <MyExams onLoadExam={onLoadExam} />}
{activeTab === 'results' && <MyResults />}
{activeTab === 'results' && <MyResults onLoadExam={onLoadExam} />}
{activeTab === 'settings' && <Settings />}
</main>
</div>
+1
View File
@@ -124,6 +124,7 @@
"explanation": "Explanation",
"rubric": "Rubric (self-grade)",
"retakeExam": "Retake exam",
"cantRetake": "Exam no longer saved in workspace",
"backToHome": "Back to home"
},
"tweaks": {
+1
View File
@@ -124,6 +124,7 @@
"explanation": "Uitleg",
"rubric": "Rubric (zelfbeoordeling)",
"retakeExam": "Examen opnieuw maken",
"cantRetake": "Examen niet meer opgeslagen in werkruimte",
"backToHome": "Terug naar home"
},
"tweaks": {