mirror of
https://github.com/Tria-plc/emaui.git
synced 2026-09-07 15:25:47 +00:00
Merge origin/WorkflowChange into logestic_chnage
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.
This commit is contained in:
@@ -866,19 +866,6 @@ export const licensingApi = baseApi
|
||||
* flight; these two *start* a stage, because under the push model
|
||||
* assignment is how work begins — nothing is claimed from a queue.
|
||||
*/
|
||||
assignReviewer: builder.mutation<
|
||||
LicenseApplication,
|
||||
{ id: string; officerId: string; remark?: string }
|
||||
>({
|
||||
query: ({ id, ...body }) => ({
|
||||
url: `/license-application-review/${id}/assign-reviewer`,
|
||||
method: 'POST',
|
||||
body,
|
||||
}),
|
||||
invalidatesTags: (_r, error, { id }) =>
|
||||
error ? [] : [itemTag('LicenseApplication', id), listTag('ApplicationQueue')],
|
||||
}),
|
||||
|
||||
assignInspector: builder.mutation<
|
||||
LicenseApplication,
|
||||
{ id: string; inspectorId: string; remark?: string }
|
||||
@@ -1132,6 +1119,30 @@ export const licensingApi = baseApi
|
||||
error ? [] : [itemTag('LicenseApplication', applicationId), listTag('Inspection')],
|
||||
}),
|
||||
|
||||
/** Moves a booked visit: another day, slot, inspector or place. */
|
||||
rescheduleInspection: builder.mutation<
|
||||
Inspection,
|
||||
{
|
||||
inspectionId: string;
|
||||
applicationId: string;
|
||||
scheduledDate: string;
|
||||
timeSlot: 'MORNING' | 'AFTERNOON';
|
||||
inspectorId?: string;
|
||||
location?: string;
|
||||
reason?: string;
|
||||
}
|
||||
>({
|
||||
query: ({ inspectionId, scheduledDate, timeSlot, inspectorId, location, reason }) => ({
|
||||
url: `/inspections/${inspectionId}/schedule`,
|
||||
method: 'PATCH',
|
||||
// applicationId is for cache invalidation only; the visit knows its
|
||||
// own application.
|
||||
body: { scheduledDate, timeSlot, inspectorId, location, reason },
|
||||
}),
|
||||
invalidatesTags: (_r, error, { applicationId }) =>
|
||||
error ? [] : [itemTag('LicenseApplication', applicationId), listTag('Inspection')],
|
||||
}),
|
||||
|
||||
getInspections: builder.query<Inspection[], string>({
|
||||
query: (applicationId) => ({ url: `/inspections/application/${applicationId}` }),
|
||||
providesTags: () => [listTag('Inspection')],
|
||||
@@ -1280,6 +1291,7 @@ export const {
|
||||
useCreatePickupOfficeMutation,
|
||||
useUpdatePickupOfficeMutation,
|
||||
useScheduleInspectionMutation,
|
||||
useRescheduleInspectionMutation,
|
||||
useGetInspectionsQuery,
|
||||
useRecordInspectionResultMutation,
|
||||
useGetNotificationsQuery,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { isValidPhoneNumber } from 'libphonenumber-js';
|
||||
import { resolveTokenFromStorage } from '../../session';
|
||||
import { BASE_API_URL } from '../../base-api/base-query-with-reauth';
|
||||
import type {
|
||||
ApplicationKind,
|
||||
Bilingual,
|
||||
@@ -17,10 +18,6 @@ function sectionAppliesToKind(section: FormSectionConfig, kind: ApplicationKind)
|
||||
return !section.applicationKinds?.length || section.applicationKinds.includes(kind);
|
||||
}
|
||||
|
||||
const BASE_API_URL =
|
||||
(import.meta as { env?: Record<string, string> }).env?.['VITE_BASE_API_URL'] ??
|
||||
'http://localhost:3000/api';
|
||||
|
||||
/**
|
||||
* Uploads a document straight to the API.
|
||||
*
|
||||
@@ -69,6 +66,7 @@ export const STATUS_LABELS: Record<LicenseStatus, string> = {
|
||||
DRAFT: 'Draft',
|
||||
SUBMITTED: 'Submitted',
|
||||
UNDER_REVIEW: 'Under Review',
|
||||
AWAITING_BIOMETRICS: 'Awaiting Biometrics',
|
||||
UNDER_EVALUATION: 'Under Evaluation',
|
||||
RESUBMIT_REQUIRED: 'Resubmit Required',
|
||||
INSPECTION_PENDING: 'Inspection Pending',
|
||||
@@ -99,6 +97,7 @@ export const STATUS_COLORS: Record<LicenseStatus, string> = {
|
||||
DRAFT: 'gray',
|
||||
SUBMITTED: 'blue',
|
||||
UNDER_REVIEW: 'indigo',
|
||||
AWAITING_BIOMETRICS: 'indigo',
|
||||
UNDER_EVALUATION: 'indigo',
|
||||
RESUBMIT_REQUIRED: 'orange',
|
||||
INSPECTION_PENDING: 'cyan',
|
||||
@@ -138,6 +137,7 @@ export const STATUS_PROGRESS: Record<LicenseStatus, number> = {
|
||||
DRAFT: 5,
|
||||
SUBMITTED: 15,
|
||||
UNDER_REVIEW: 30,
|
||||
AWAITING_BIOMETRICS: 30,
|
||||
UNDER_EVALUATION: 45,
|
||||
RESUBMIT_REQUIRED: 30,
|
||||
INSPECTION_PENDING: 55,
|
||||
@@ -619,6 +619,49 @@ interface ConditionLike {
|
||||
anyOf?: ConditionLike[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Section keys a condition reads, recursing `anyOf`.
|
||||
*
|
||||
* `FieldCondition.field` is a `sectionKey.fieldKey` path, so the prefix names
|
||||
* the section whose answer decides the condition.
|
||||
*/
|
||||
export function conditionSections(
|
||||
condition: FieldCondition | undefined | null,
|
||||
): string[] {
|
||||
if (!condition) return [];
|
||||
if (condition.anyOf) return condition.anyOf.flatMap(conditionSections);
|
||||
if (!condition.field) return [];
|
||||
const [sectionKey] = condition.field.split('.');
|
||||
return sectionKey ? [sectionKey] : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Sections whose visibility hangs on an answer in one of `flagged`.
|
||||
*
|
||||
* Mirrors the server's `sectionsDependingOn`: an officer flagging the section
|
||||
* that holds the vessel category is asking for an answer that decides which
|
||||
* fields in other sections are required, so those sections have to open too —
|
||||
* otherwise the applicant sees newly-required fields they cannot edit and
|
||||
* cannot resubmit.
|
||||
*/
|
||||
export function sectionsDependingOn(
|
||||
sections: FormSectionConfig[],
|
||||
flagged: Set<string>,
|
||||
): Set<string> {
|
||||
const dependent = new Set<string>();
|
||||
if (flagged.size === 0) return dependent;
|
||||
|
||||
for (const section of sections) {
|
||||
if (flagged.has(section.key)) continue;
|
||||
const reads = [
|
||||
section.showWhen,
|
||||
...(section.fields ?? []).map((field) => field.showWhen),
|
||||
].flatMap(conditionSections);
|
||||
if (reads.some((key) => flagged.has(key))) dependent.add(section.key);
|
||||
}
|
||||
return dependent;
|
||||
}
|
||||
|
||||
export function conditionHolds(
|
||||
condition: FieldCondition | undefined | null,
|
||||
formData: Record<string, Record<string, unknown>>,
|
||||
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user