mirror of
https://github.com/Tria-plc/emaui.git
synced 2026-08-26 19:12:50 +00:00
New portal feature apps/portal/src/app/features/exam-attempt/, following the repo's existing folder convention (pages/<Page>/index.tsx, components/, types/, hooks/ — matches the payments feature's hooks/ precedent): - types/exam-attempt.ts — response/local-state shapes - hooks/useExamAttempt.ts — all API orchestration: load (registration + attempt), start/resume, per-question autosave (immediate on MCQ select, debounced+flushed-on-navigation for essay text), local countdown seeded from the server's serverTime/remainingSeconds, submit, and resync from the server whenever a write is refused as expired/already-submitted - components/ExamInstructions, ExamTimer, ExamQuestionNav, ExamQuestionDisplay, ExamCompletion — one concern per file, not a single page dump - pages/ExamAttemptPage — thin view layer over the hook Flow: /exams (existing) gets a 'Take exam' action on eligible registration rows → /exams/:examId/take, which shows instructions before an attempt exists, the live exam screen while IN_PROGRESS, and a no-score completion screen once SUBMITTED/EXPIRED (never fabricates a result — grading doesn't exist yet). Security note: RequirePermission on the route and disabled inputs after local timeout are UI conveniences only. Every save/submit is independently re-checked by the backend's ownership + applyExpiry() on each call: a client that skipped the UI entirely and hit the API directly would be bound by exactly the same rules. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>