diff --git a/apps/backoffice/src/app/features/exam/pages/ExamDetailPage.tsx b/apps/backoffice/src/app/features/exam/pages/ExamDetailPage.tsx index e77e95b85..11a87bacb 100644 --- a/apps/backoffice/src/app/features/exam/pages/ExamDetailPage.tsx +++ b/apps/backoffice/src/app/features/exam/pages/ExamDetailPage.tsx @@ -117,13 +117,18 @@ export function ExamDetailPage() { // Only approved bank items may go on a paper (US-EXAM-003), so the picker // must not offer drafts or retired questions either. + // + // BOTH describes a mixed paper — a question itself is never "BOTH" (see + // QuestionForm), so an equality check against it would match nothing and + // silently offer zero questions. Same skip-condition as the backend's own + // random draw (ExamService.selectRandomQuestions). const eligibleQuestions = useMemo(() => { if (!exam) return []; return allQuestions .filter( (q) => q.certificationId === exam.certificationId && - q.form === exam.form && + (exam.form === 'BOTH' || q.form === exam.form) && q.status === 'APPROVED', ) .map((q) => ({ id: q.id, title: q.title, form: q.form, points: q.points })); diff --git a/apps/backoffice/src/app/features/exam/types/exam.ts b/apps/backoffice/src/app/features/exam/types/exam.ts index 1c25cfd7d..5e3e3d4df 100644 --- a/apps/backoffice/src/app/features/exam/types/exam.ts +++ b/apps/backoffice/src/app/features/exam/types/exam.ts @@ -3,6 +3,13 @@ import type { EstimatedTime } from "../../question/types/question"; import type { QuestionForm } from "../../question/types/question"; export type { QuestionForm }; +/** + * The paper's own form — unlike a question's (QuestionForm), an exam may mix + * both: BOTH means a mixed paper, resolved per-question against each + * question's own ESSAY/CHOICE form. Mirrors backend EExamForm. + */ +export type ExamForm = QuestionForm | "BOTH"; + export type ExamType = "WRITTEN" | "ORAL"; export type ExamAdministrationMethod = "OFFLINE" | "ONLINE"; export type ExamEvaluationMethod = "SUM" | "AVERAGE" | "PERCENTAGE"; @@ -39,7 +46,7 @@ export interface Exam { date: string; givenTime: EstimatedTime | null; type: ExamType; - form: QuestionForm; + form: ExamForm; venue: string; administrationMethod: ExamAdministrationMethod; evaluationMethod: ExamEvaluationMethod; @@ -63,7 +70,7 @@ export interface CreateExamPayload { date: string; givenTime: EstimatedTime; type: ExamType; - form: QuestionForm; + form: ExamForm; venue: string; administrationMethod: ExamAdministrationMethod; evaluationMethod: ExamEvaluationMethod; @@ -79,7 +86,7 @@ export interface UpdateExamPayload { date?: string; givenTime?: EstimatedTime; type?: ExamType; - form?: QuestionForm; + form?: ExamForm; venue?: string; administrationMethod?: ExamAdministrationMethod; evaluationMethod?: ExamEvaluationMethod; diff --git a/apps/backoffice/src/app/features/license-review/config/actions.ts b/apps/backoffice/src/app/features/license-review/config/actions.ts index 8653938e7..2ce63ea65 100644 --- a/apps/backoffice/src/app/features/license-review/config/actions.ts +++ b/apps/backoffice/src/app/features/license-review/config/actions.ts @@ -65,16 +65,9 @@ export interface ActionDefinition { */ export const ACTIONS: ActionDefinition[] = [ // ------------------------------------------------------------- workflow - { - id: 'claim', - tier: 'workflow', - labelKey: 'review.actions.claim', - // Mirrors the CLAIM transition's `from` list: an examined cert (CoC/CoP) - // sits in ELIGIBILITY_PAID once its eligibility fee clears, not SUBMITTED. - from: ['SUBMITTED', 'ELIGIBILITY_PAID'], - permissions: ['can:claim:license-application'], - emphasis: 'light', - }, + // Claim is deliberately absent here: an officer claims from the queue + // (LicenseQueuePage), not from this detail page. That implementation is + // separate — see LicenseQueuePage/actions.tsx — and is unaffected by this. { id: 'assign', tier: 'workflow',