feat(biometric-enrollment): add biometric enrollment feature with API integration and UI components

This commit is contained in:
Nati
2026-08-28 06:45:47 +00:00
parent c3db978a1f
commit 822abe4de4
10 changed files with 340 additions and 0 deletions

View File

@@ -6,6 +6,7 @@ export * from './lib/features/location';
export * from './lib/features/seafarer';
export * from './lib/features/seafarer-registration';
export * from './lib/features/seafarer-document';
export * from './lib/features/biometric-enrollment';
export * from './lib/features/vessel';
export { baseQueryWithReauth, configureTokenRefresh } from './lib/base-api/base-query-with-reauth';
export { openAuthedDocument, downloadAuthedFile } from './lib/base-api/download';

View File

@@ -0,0 +1,44 @@
import { baseApi } from '../../base-api';
import type { BiometricEnrollment, EnrollBiometric } from './biometric-enrollment.types';
const TAG = 'BiometricEnrollment' as const;
const forProfile = (profileId: string) => ({ type: TAG, id: profileId }) as const;
/**
* Scanner capture (fingerprint, face) stored per profile. No applicant-facing
* endpoint — enrollment happens at a counter with a scanner.
*/
export const biometricEnrollmentApi = baseApi
.enhanceEndpoints({ addTagTypes: [TAG] })
.injectEndpoints({
endpoints: (builder) => ({
enrollBiometric: builder.mutation<BiometricEnrollment, EnrollBiometric>({
query: (body) => ({ url: '/biometric-enrollments', method: 'POST', body }),
invalidatesTags: (r, error) => (error || !r ? [] : [forProfile(r.profileId)]),
}),
getBiometricEnrollments: builder.query<BiometricEnrollment[], string>({
query: (profileId) => ({ url: `/biometric-enrollments/profile/${profileId}` }),
providesTags: (_r, _e, profileId) => [forProfile(profileId)],
}),
revokeBiometricEnrollment: builder.mutation<
BiometricEnrollment,
{ id: string; profileId: string; reason: string }
>({
query: ({ id, reason }) => ({
url: `/biometric-enrollments/${id}/revoke`,
method: 'POST',
body: { reason },
}),
invalidatesTags: (_r, error, { profileId }) => (error ? [] : [forProfile(profileId)]),
}),
}),
overrideExisting: false,
});
export const {
useEnrollBiometricMutation,
useGetBiometricEnrollmentsQuery,
useRevokeBiometricEnrollmentMutation,
} = biometricEnrollmentApi;

View File

@@ -0,0 +1,32 @@
export type BiometricModality = 'FINGERPRINT' | 'FACE';
export type BiometricEnrollmentStatus = 'ACTIVE' | 'REVOKED';
export interface BiometricEnrollment {
id: string;
profileId: string;
modality: BiometricModality;
templateFormat: string;
qualityScore: number | null;
deviceId: string | null;
status: BiometricEnrollmentStatus;
enrolledById: string;
enrolledAt: string;
consentAt: string;
revokedReason: string | null;
revokedById: string | null;
revokedAt: string | null;
createdAt: string;
updatedAt: string;
}
export interface EnrollBiometric {
profileId: string;
modality: BiometricModality;
/** Vendor SDK template, base64. Never the raw scan image. */
template: string;
templateFormat: string;
qualityScore?: number;
deviceId?: string;
/** ISO 8601 — when the subject consented to capture. */
consentAt: string;
}

View File

@@ -0,0 +1,2 @@
export * from './biometric-enrollment.types';
export * from './biometric-enrollment-api';