Public Access
feat: implement retake exam functionality and update translations for exam status
This commit is contained in:
1 parent
f794dacd2d
commit
4935251125
4 files changed
+31
-2
No files matched your search
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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": {
|
||||
|
||||
@@ -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": {
|
||||
|
||||
Reference in new issue
Block a user