feat(exam): regrade action + stop inviting a re-take on a finished exam

Two fixes surfaced by actual candidate-flow testing:

- Portal 'My registrations' always showed 'Take exam' regardless of whether
  the candidate had already finished — clicking it just hit
  attempt_already_submitted server-side. The registrations/mine response
  now carries the attempt's status; the row shows a Completed/Time expired
  badge once finished, or 'Resume exam' while still in progress, and only
  invites a fresh start when there's genuinely nothing yet.
- Backoffice candidates panel: a Regrade button next to a SUBMITTED/EXPIRED
  sitting, calling the new POST /exam-attempts/:id/regrade — the officer's
  path to the same staff-triggered regrade, no SQL needed.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
mihretue
2026-08-17 12:00:45 +00:00
parent e97e12d979
commit 83d309248d
8 changed files with 116 additions and 19 deletions

View File

@@ -325,6 +325,10 @@ export const am: Translations = {
retake: "ድጋሚ {{n}}",
firstSitting: "የመጀመሪያ ሙከራ",
remarkRequired: "ለመውጣት ወይም ለመታገድ ምክንያት ያስፈልጋል።",
regrade: "እንደገና ደረጃ ስጥ",
regraded: "ውጤት ከተመዘገበው ሙከራ ተፈጥሯል።",
regradeNotEligible: "በራስ-ሰር ሊገመገም አይችልም፦ {{reason}}። ውጤት መዝግብ ተጠቀም።",
regradeError: "ይህን ሙከራ እንደገና መገምገም አልተቻለም።",
},
attendance: {
REGISTERED: "አልተጠራም",

View File

@@ -322,6 +322,10 @@ export const en = {
retake: 'Retake {{n}}',
firstSitting: 'First sitting',
remarkRequired: 'A reason is required for a withdrawal or a disqualification.',
regrade: 'Regrade',
regraded: 'Result created from the graded attempt.',
regradeNotEligible: 'Not auto-gradable: {{reason}}. Use Record Result instead.',
regradeError: 'Could not regrade this attempt.',
},
attendance: {
REGISTERED: 'Not called',