From 4b733d4dfb6e9771eea597bb500b3c7e19d494c2 Mon Sep 17 00:00:00 2001 From: Nati Date: Fri, 28 Aug 2026 09:40:13 +0000 Subject: [PATCH] feat(biometric-enrollment): add BSID generation functionality and update translations --- .../pages/BiometricEnrollmentPage.tsx | 59 ++++++++++++++++++- apps/backoffice/src/app/i18n/locales/am.ts | 1 + apps/backoffice/src/app/i18n/locales/en.ts | 1 + .../biometric-enrollment-api.ts | 10 ++++ .../features/licensing/licensing.helpers.ts | 3 + .../lib/features/licensing/licensing.types.ts | 3 + libs/auth/src/lib/types/auth.types.ts | 2 + 7 files changed, 77 insertions(+), 2 deletions(-) diff --git a/apps/backoffice/src/app/features/biometric-enrollment/pages/BiometricEnrollmentPage.tsx b/apps/backoffice/src/app/features/biometric-enrollment/pages/BiometricEnrollmentPage.tsx index 4b6e1e5e1..5ab70f38e 100644 --- a/apps/backoffice/src/app/features/biometric-enrollment/pages/BiometricEnrollmentPage.tsx +++ b/apps/backoffice/src/app/features/biometric-enrollment/pages/BiometricEnrollmentPage.tsx @@ -20,6 +20,7 @@ import { extractErrorMessage, openAuthedDocument, useEnrollBiometricMutation, + useGenerateBsidMutation, useGetBiometricEnrollmentsQuery, useGetBiometricSimulateCapabilitiesQuery, useListSeafarerRegistrationsQuery, @@ -108,6 +109,11 @@ export function BiometricEnrollmentPage() { // ALLOW_BIOMETRIC_SIMULATION=true, same shortcut the payment bypass uses. const { data: capabilities } = useGetBiometricSimulateCapabilitiesQuery(); const simulateEnabled = capabilities?.simulateEnabled ?? false; + const [generateBsid, { isLoading: generatingBsid }] = useGenerateBsidMutation(); + // Seeded from the seafarer registration list (which does not carry BSID + // yet) and updated locally once generated — this screen's only source of + // truth for it until the registry surfaces the profile's BSID directly. + const [bsid, setBsid] = useState(null); const [enroll, { isLoading: enrolling }] = useEnrollBiometricMutation(); const [revoke, { isLoading: revoking }] = useRevokeBiometricEnrollmentMutation(); const [printing, setPrinting] = useState(false); @@ -134,6 +140,17 @@ export function BiometricEnrollmentPage() { } } + async function handleGenerateBsid() { + if (!profileId) return; + try { + const result = await generateBsid(profileId).unwrap(); + setBsid(result.bsid); + notify.success(`BSID ${result.bsid} generated.`); + } catch (err) { + notify.error(extractErrorMessage(err, 'Could not generate BSID.')); + } + } + async function handleRevoke(id: string) { if (!profileId) return; try { @@ -167,7 +184,12 @@ export function BiometricEnrollmentPage() { /> {!selected ? ( - + { + setSelected(r); + setBsid(null); + }} + /> ) : ( @@ -176,7 +198,15 @@ export function BiometricEnrollmentPage() { {applicantName(selected)} {selected.seafarerNumber} - @@ -204,6 +234,31 @@ export function BiometricEnrollmentPage() { )} + + Biometric Subject ID (BSID) + + Required before this registration can be approved. Generating it is final — + confirm the capture is good first. + + + {bsid ? ( + + ) : ( + Not generated + )} + {!bsid && ( + + )} + + + On file diff --git a/apps/backoffice/src/app/i18n/locales/am.ts b/apps/backoffice/src/app/i18n/locales/am.ts index e83e9929a..3f03fe1c8 100644 --- a/apps/backoffice/src/app/i18n/locales/am.ts +++ b/apps/backoffice/src/app/i18n/locales/am.ts @@ -868,6 +868,7 @@ export const am: Translations = { DRAFT: "ረቂቅ", SUBMITTED: "ቀርቧል", UNDER_REVIEW: "በግምገማ ላይ", + AWAITING_BIOMETRICS: "ባዮሜትሪክ በመጠባበቅ ላይ", UNDER_EVALUATION: "በምዘና ላይ", RESUBMIT_REQUIRED: "እንደገና ማቅረብ ያስፈልጋል", INSPECTION_PENDING: "ምርመራ በመጠባበቅ ላይ", diff --git a/apps/backoffice/src/app/i18n/locales/en.ts b/apps/backoffice/src/app/i18n/locales/en.ts index ff4fc4f15..748d1a9ff 100644 --- a/apps/backoffice/src/app/i18n/locales/en.ts +++ b/apps/backoffice/src/app/i18n/locales/en.ts @@ -875,6 +875,7 @@ export const en = { DRAFT: 'Draft', SUBMITTED: 'Submitted', UNDER_REVIEW: 'Under Review', + AWAITING_BIOMETRICS: 'Awaiting Biometrics', UNDER_EVALUATION: 'Under Evaluation', RESUBMIT_REQUIRED: 'Resubmit Required', INSPECTION_PENDING: 'Inspection Pending', diff --git a/libs/api/src/lib/features/biometric-enrollment/biometric-enrollment-api.ts b/libs/api/src/lib/features/biometric-enrollment/biometric-enrollment-api.ts index d6117dd25..db42102fa 100644 --- a/libs/api/src/lib/features/biometric-enrollment/biometric-enrollment-api.ts +++ b/libs/api/src/lib/features/biometric-enrollment/biometric-enrollment-api.ts @@ -44,6 +44,15 @@ export const biometricEnrollmentApi = baseApi }), invalidatesTags: (_r, error, { profileId }) => (error ? [] : [forProfile(profileId)]), }), + + /** Stamps the profile's BSID once enrollment is confirmed. Requires an active enrollment. */ + generateBsid: builder.mutation<{ id: string; bsid: string | null }, string>({ + query: (profileId) => ({ + url: `/biometric-enrollments/profile/${profileId}/generate-bsid`, + method: 'POST', + }), + invalidatesTags: (_r, error, profileId) => (error ? [] : [forProfile(profileId)]), + }), }), overrideExisting: false, }); @@ -54,4 +63,5 @@ export const { useGetMyBiometricEnrollmentsQuery, useGetBiometricSimulateCapabilitiesQuery, useRevokeBiometricEnrollmentMutation, + useGenerateBsidMutation, } = biometricEnrollmentApi; diff --git a/libs/api/src/lib/features/licensing/licensing.helpers.ts b/libs/api/src/lib/features/licensing/licensing.helpers.ts index 888b57be9..390ee330b 100644 --- a/libs/api/src/lib/features/licensing/licensing.helpers.ts +++ b/libs/api/src/lib/features/licensing/licensing.helpers.ts @@ -63,6 +63,7 @@ export const STATUS_LABELS: Record = { DRAFT: 'Draft', SUBMITTED: 'Submitted', UNDER_REVIEW: 'Under Review', + AWAITING_BIOMETRICS: 'Awaiting Biometrics', UNDER_EVALUATION: 'Under Evaluation', RESUBMIT_REQUIRED: 'Resubmit Required', INSPECTION_PENDING: 'Inspection Pending', @@ -93,6 +94,7 @@ export const STATUS_COLORS: Record = { DRAFT: 'gray', SUBMITTED: 'blue', UNDER_REVIEW: 'indigo', + AWAITING_BIOMETRICS: 'indigo', UNDER_EVALUATION: 'indigo', RESUBMIT_REQUIRED: 'orange', INSPECTION_PENDING: 'cyan', @@ -132,6 +134,7 @@ export const STATUS_PROGRESS: Record = { DRAFT: 5, SUBMITTED: 15, UNDER_REVIEW: 30, + AWAITING_BIOMETRICS: 30, UNDER_EVALUATION: 45, RESUBMIT_REQUIRED: 30, INSPECTION_PENDING: 55, diff --git a/libs/api/src/lib/features/licensing/licensing.types.ts b/libs/api/src/lib/features/licensing/licensing.types.ts index 0fac20c6e..e14bae67e 100644 --- a/libs/api/src/lib/features/licensing/licensing.types.ts +++ b/libs/api/src/lib/features/licensing/licensing.types.ts @@ -25,6 +25,9 @@ export type LicenseStatus = | "DRAFT" | "SUBMITTED" | "UNDER_REVIEW" + // Seafarer registration only: same slot UNDER_REVIEW occupies elsewhere, + // but approval is blocked until the applicant's profile has a BSID. + | "AWAITING_BIOMETRICS" | "UNDER_EVALUATION" // Employee filed their review; parked with the team leader for a decision. | "REVIEW_REPORTED" diff --git a/libs/auth/src/lib/types/auth.types.ts b/libs/auth/src/lib/types/auth.types.ts index 056e574e5..ae7066a46 100644 --- a/libs/auth/src/lib/types/auth.types.ts +++ b/libs/auth/src/lib/types/auth.types.ts @@ -84,6 +84,8 @@ export interface CurrentProfile { * registration is approved, null before that. */ seafarerNumber?: string | null; + /** Biometric Subject ID — stamped by staff once biometric enrollment is confirmed. */ + bsid?: string | null; seafarerStatus?: 'ACTIVE' | 'INACTIVE' | 'PENDING' | 'SUSPENDED' | null; seafarerDepartment?: 'DECK' | 'ENGINE' | 'CATERING' | null; seafarerStatusReason?: string | null;