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:
fitse-yotor
2026-08-28 16:27:36 +03:00
58 changed files with 2348 additions and 412 deletions

View File

@@ -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>>,