The verify step now returns an encrypted verificationToken alongside the
readable identity. After the existing signup succeeds, the page posts it to
POST /profiles/me/fayda, which marks the phone verified when it is the one
Fayda vouched for and fills the still-empty profile fields. Best-effort: the
account already works if the call fails, and the token can be presented again.
When the server confirms the phone, the page goes straight to the portal
instead of /otp-verify — Fayda already verified that exact number with its own
OTP, and asking for a second one on the same number is theatre. An applicant
who edited the prefilled number still gets the normal OTP step.
The registrations table mirrored the server's old attendance deny-list —
ABSENT/WITHDRAWN/DISQUALIFIED — so the default REGISTERED fell through and
"Take exam" appeared before any invigilator had confirmed the candidate was
there. It is an allow-list now, PRESENT and LATE only, matching
ExamAttemptService.MAY_SIT.
A blocked candidate gets a reason rather than an empty cell: "Awaiting
attendance" with a tooltip while nobody has called the register, "Not sitting"
once attendance was taken and they are not. useExamAttempt does the same for
the direct-link path, and translates attendance_not_confirmed /
candidate_not_present instead of showing a raw error key on a Start button
that cannot work. The server refuses either way — this only makes the refusal
legible.
ScheduleExamModal no longer promises an admission number: scheduling makes a
sitting available, and the candidate registers for it themselves.
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
A license type configured without an inspection detail section (e.g. vessel
ownership transfer) would hide the "approval blocked" explanation entirely if
it ever reached INSPECTION_FAILED. The alert now renders beside the other
page-level status notices, so the reason is visible regardless of which detail
sections the type declares.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
VITE_BASE_API_URL with a localhost:3001/api fallback was re-derived in six
files across libs/api, libs/auth, the portal and the backoffice — and a seventh
site (UserManagementPage) read the env raw with no fallback at all, handing the
IAM user-management app an undefined apiUrl whenever no .env was present.
BASE_API_URL in base-query-with-reauth.ts is now the single definition,
exported from @ema-platform/api; every other site imports it. Trims the env
value and treats blank as unset, matching how the backend reads its own keys.
Submitting from Basic Info complained that required fields on the Settings
tab were empty — a correct error the user could not act on, since Settings
had not been shown yet.
The primary action now reads "Next" while Basic Info is open and validates
only that tab before moving on; Create/Update appears on Settings, the final
step, alongside a Back button. Submit still checks both tabs, since the tab
headers stay clickable and a user can reach Settings without going through
Next.
The Fayda redirect URI registered for local testing is
http://localhost:3000/callback, matched exactly by the provider, so the portal
has to be the thing listening there. Its dev and preview servers move from 4200
to 3000 and the API moves to 3001.
Every hardcoded fallback to http://localhost:3000/api follows — six copies of
the same default across libs/api, libs/auth, the portal and the backoffice —
otherwise a developer without a .env would have had the app calling itself.
e2e is unaffected: it binds its own ports (3011/4302/4303) explicitly.
README.md line 72 says `cp .env.example .env`, but no such file existed and
.gitignore's `.env.*` rule would have swallowed it — hence the explicit
negation alongside it.
Documents the two variables the code actually reads, VITE_BASE_API_URL and
VITE_USE_MOCKS, rather than the longer list in the README, none of which is
referenced anywhere in apps/ or libs/. Notes that Vite resolves this from the
workspace root via envDir, and that Fayda needs nothing here beyond the callback
path matching the API's FAYDA_REDIRECT_URI.