The COC detail page read the application status alone, so a candidate who had
registered, been marked present, sat the paper and had a PASSED mark published
still appeared to be awaiting an exam — and the officer was offered "Schedule
exam" and "Record exam result" for work already done.
ExamStatePanel reads GET /exams/applications/:id/state, which the server
derives from the registration, the attempt and the published mark. Session,
date and admission number while the exam is ahead; score, outcome and whether
the exam engine or an examiner produced the mark once it is settled. Polled,
because attendance is recorded and papers are marked while the officer has the
page open. Same derivation the applicant's portal reads, so the two cannot
disagree.
The eligible-exams query, the scheduling mutation and the outcome mutation are
gone from the RTK layer along with the endpoints behind them.
Four screens were telling the applicant things that were not true, and two
back-office actions were writing state nobody should be able to write by hand.
The exam form is a stepper. Basic Info and Settings each answer only for their
own fields, and a third step reviews the whole thing before anything is sent —
"Create Exam" exists on that step alone, so there is no path from an earlier
one into the API. Clicking it from Basic Info used to report the Settings
fields as missing: a correct error the user could not act on, since that step
had not been shown yet. The rules move to ./validation, which is what the
per-step check and the final whole-payload check both read, so the two cannot
drift apart. Back keeps everything entered — state lives in the component.
The portal derives the examination stage from the records rather than from the
application status alone. Registering, attendance and the sitting itself all
happen on the registration and the attempt, and the portal read none of them —
which is how an application still said "Awaiting Exam Date" after the back
office had already evaluated the paper. examStageFor() names what the records
add up to: eligible to register, registered, attendance confirmed, sitting,
under evaluation, passed, failed. Both the applications list and the
certificates page use it, and both poll the registrations they read it from,
because attendance is recorded by an invigilator while the candidate is
watching the screen.
EXAM_PAID is no longer a waiting state. Nobody assigns a date: the fee having
cleared is exactly what makes the candidate eligible to pick a published
sitting, so that row now offers Register rather than a disabled button.
Schedule Exam and Record Exam Outcome are gone from the COC queue — actions,
modals, RTK endpoints and strings. Their server endpoints are gone too, so
this is not a hidden button over a live route.
Resolves conflicts:
- LicenseReviewPage: dropped a duplicated schedule-issuance ActionIcon,
keeping the Tooltip-wrapped one and adding scheduledPeriod (required
by the scheduleIssuance mutation) to it.
- portal i18n (en.ts/am.ts): both sides added distinct keys under
licensing.card (reportDamaged/reissueFailed vs status/statusReason) —
kept both, additive.
- licensing.helpers.ts: kept sectionAppliesToKind (this branch) and
switched to the centralized BASE_API_URL import from
base-api/base-query-with-reauth (WorkflowChange), dropping the local
duplicate constant.
- Introduced new pickup appointment functionalities in the licensing API, including scheduling, rescheduling, and managing pickup offices.
- Added UI components for the pickup desk, allowing officers to check in, issue documents, and manage no-show appointments.
- Implemented a new page for managing pickup offices with CRUD operations.
- Enhanced internationalization support for new pickup-related terms and messages.
- Updated licensing types to include new application kinds and issuance periods.
- Created a read-only panel for displaying scheduled pickup appointments in the licensing component.
refactor(i18n): remove 'assignReviewer' translation key from Amharic and English locales
refactor(nav-config): clean up navigation configuration by removing redundant items
refactor(licensing-api): remove assignReviewer mutation from licensing API
feat(package): add build script for parallel building of portal and backoffice
- Introduced rankKey field in Certification interface and payloads.
- Updated CertificationForm to handle rankKey input.
- Enhanced ScheduleExamModal to filter exams based on rank.
- Added getEligibleExams API query to fetch exams relevant to the application's rank.
- Updated related components and types to accommodate new rankKey functionality.
- Implement DocumentRequirementsTab for managing document upload requirements.
- Create FieldEditorDrawer and SectionEditorDrawer for editing fields and sections in the form schema.
- Develop FormSchemaTab to handle sections and fields for a license type's form schema.
- Introduce CertificateRequirementsPage to serve as the main interface for configuring license type requirements.
- Add hooks for requirement actions to streamline mutation handling and notifications.
- Implement condition handling for fields and sections to manage visibility based on user input.
- Create schema-paths configuration to facilitate condition target collection.
- Refactor ExamStageActions component to handle eligibility payment and retake exam actions.
- Update MyApplicationsPage to use new retakeExam mutation instead of requestExamPayment.
- Modify licensing API to include retakeExam mutation for handling exam fee requests for failed candidates.
- Adjust mock data to reflect new application statuses and ensure consistency in eligibility and exam payment states.
- Update licensing helpers to include new status labels and colors for eligibility payment states.
- Revise licensing types to replace ELIGIBILITY_APPROVED with ELIGIBILITY_PAYMENT_PENDING and ELIGIBILITY_PAID for clarity in application status flow.
Seafarer and vessel registration were filtered out of the operations
step by `requiresOperatorMode !== false`, so someone registering as a
seafarer or vessel owner landed on an onboarding screen that did not
describe them. Both now appear, grouped apart from the company modes:
declaring "I am a seafarer" is a different kind of statement from "my
company forwards freight".
The designer's preview was gated on the Handlebars source alone, which a
canvas layout does not have until the server compiles it on save -- so
the button was dead for exactly the designs the canvas exists for.
Editing looked broken rather than deliberately read-only: every seeded
template is PUBLISHED, and a published design is immutable because
certificates were issued from it. Says so, and offers the new-version
action that is the way forward.
Reviewing officers saw company, capital and staff tabs on seafarer
certificate applications, because PRESENTATION is keyed by the generic
licence keys and the fifty-odd rank-specific CoC/CoP keys fell through
to the company default. Matched by prefix instead, so a certificate
configured tomorrow gets the right presentation without a code change.
Seaman book and BTC are wired to the newly seeded licence types and no
longer marked "soon".
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>