Merge branch 'feature/exam-attempt-domain' of github.com:Tria-plc/emaui into feature/exam-attempt-domain

This commit is contained in:
mihretue
2026-08-17 14:43:45 +03:00
79 changed files with 11034 additions and 2376 deletions

View File

@@ -7,7 +7,7 @@ import type { AuthUser } from "../types/auth.types";
const BASE_API_URL =
(import.meta as { env?: Record<string, string> }).env?.[
"VITE_BASE_API_URL"
] ?? "http://localhost:3000/api";
] ?? "http://localhost:3001/api";
/**
* Restores the signed-in session before the router renders.

View File

@@ -10,6 +10,9 @@ interface ProtectedRouteProps {
export function ProtectedRoute({ children, loginPath = '/login' }: ProtectedRouteProps) {
const location = useLocation();
// `authStorage` is already scoped to this app; the bare key is only the
// legacy pre-prefix session. Never read a sibling app's token — that is how
// a backoffice tab ends up authenticated as a portal applicant.
const token = authStorage.getToken() ?? Cookies.get('auth-token');
if (!token) {

View File

@@ -91,6 +91,13 @@ export interface ProfileMeResponse {
* `usePermissions()`.
*/
permissions?: string[];
/**
* Whether the profile can apply for a CoC/CoP right now: seafarer
* registration approved, plus a verified sea service record and a
* verified medical certificate. Computed server-side so the "Apply"
* button and the API's own eligibility check can never disagree.
*/
eligibleForCoc: boolean;
}
const profileApi = baseApi
@@ -109,7 +116,7 @@ const profileApi = baseApi
unknown,
{ id: string; body: Record<string, unknown> }
>({
query: ({ id, body }) => ({ url: `/profiles/${id}`, method: 'PATCH', body }),
query: ({ id, body }) => ({ url: `/profiles/${id}`, method: 'PUT', body }),
invalidatesTags: ['CurrentProfile'],
}),
updateMyAddress: builder.mutation<
@@ -167,6 +174,7 @@ export function useCurrentProfile() {
refetch,
completeness: data?.completeness ?? 0,
missing,
eligibleForCoc: data?.eligibleForCoc ?? false,
/**
* True when nothing the requirement asks for is still blank. Unknown
* profile (still loading) reads as not-ready, so a caller never submits

View File

@@ -35,6 +35,8 @@ export interface CurrentProfileAddress {
id: string;
idType: string;
idNumber: string;
passportNumber: string | null;
passportExpiry: string | null;
nationality: string;
regionId: string | null;
cityId: string | null;
@@ -42,6 +44,7 @@ export interface CurrentProfileAddress {
woredaId: string | null;
kebeleId: string | null;
streetAddress: string | null;
currentAddress: string | null;
houseNumber: string | null;
primaryPhoneNumber: string;
secondaryPhoneNumber: string | null;