import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { useNavigate, useParams } from 'react-router-dom'; import { Alert, Box, Button, Chip, CircularProgress, Container, Paper, Stack, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, Typography, } from '@mui/material'; import ArrowBackIcon from '@mui/icons-material/ArrowBack'; import DownloadIcon from '@mui/icons-material/Download'; import EditIcon from '@mui/icons-material/Edit'; import { examRepository } from '../../services/exam/examRepository'; import type { BatchResultsResponse } from '../../types/exam.types'; function formatMark(value: number | null | undefined) { return value === null || value === undefined ? '' : String(value); } const ExamResultsPage: React.FC = () => { const { batchId } = useParams<{ batchId: string }>(); const navigate = useNavigate(); const [data, setData] = useState(null); const [loading, setLoading] = useState(true); const [downloading, setDownloading] = useState(false); const [error, setError] = useState(null); const load = useCallback(async () => { if (!batchId) return; setLoading(true); setError(null); try { setData(await examRepository.getBatchResults(batchId)); } catch (e) { setError(e instanceof Error ? e.message : String(e)); } finally { setLoading(false); } }, [batchId]); useEffect(() => { void load(); }, [load]); const summary = useMemo(() => { const rows = data?.results ?? []; const presentTotals = rows .map((r) => r.total) .filter((v): v is number => typeof v === 'number'); const average = presentTotals.length ? presentTotals.reduce((sum, v) => sum + v, 0) / presentTotals.length : null; return { total: rows.length, absent: rows.filter((r) => r.status === 'absent' && r.total === null).length, marked: rows.filter((r) => r.total !== null).length, average, }; }, [data]); const downloadCsv = async () => { if (!batchId) return; setDownloading(true); setError(null); try { const csv = await examRepository.getBatchCsv(batchId); const blob = new Blob([csv], { type: 'text/csv;charset=utf-8' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `batch-${batchId}.csv`; document.body.appendChild(a); a.click(); a.remove(); URL.revokeObjectURL(url); } catch (e) { setError(e instanceof Error ? e.message : String(e)); } finally { setDownloading(false); } }; if (loading) { return ; } if (error || !data) { return ( {error || 'Results not found'} ); } return ( {data.batch.title || 'Exam results'} Batch {data.batch.id} · created {new Date(data.batch.created_at).toLocaleDateString('en-GB')} Student Status {data.questions.map((q) => ( {q.label} / {q.max_marks} ))} Total {data.results.map((row) => ( {row.student_name || row.student_id || 'Unknown student'} {row.student_id && {row.student_id}} {data.questions.map((q) => ( {formatMark(row.marks[q.id])} ))} {formatMark(row.total)} ))}
); }; export default ExamResultsPage;