Refactor clearance terminology to document approval across services and UI components

This commit is contained in:
Marshal
2026-07-20 07:46:38 +00:00
committed by Hagernesh
parent b67aa813f9
commit 0727de0f4d
10 changed files with 40 additions and 27 deletions

View File

@@ -157,13 +157,13 @@ export class BookingLifecycleNotifierService {
});
}
/** Clearance finalized → customer can proceed to request operation. */
/** Document approval finalized → customer can proceed to request operation. */
clearanceReady(b: Booking): void {
const msg =
`Clearance for booking ${b.reference} is complete. ` +
`Document approval for booking ${b.reference} is finalized. ` +
`You can now proceed to request operation from the portal.`;
void this.notifyContact(b, msg, 'CLEARANCE READY');
this.inApp(b, 'Clearance complete', msg, {
void this.notifyContact(b, msg, 'DOCUMENT APPROVAL FINALIZED');
this.inApp(b, 'Document approval finalized', msg, {
type: NotificationType.CLEARANCE_DECISION,
});
}

View File

@@ -456,7 +456,7 @@ export class ContractClearanceService {
const allowed = ['CLEARANCE_UNDER_REVIEW', 'AWAITING_CLEARANCE_DOCUMENTS'];
if (!allowed.includes(contract.status)) {
throw new ConflictException(
`Cannot finalize clearance on status "${contract.status}".`,
`Cannot finalize document approval on status "${contract.status}".`,
);
}
}

View File

@@ -135,12 +135,14 @@ export function ClearanceReviewSection({
const finalizeMutation = useMutation({
mutationFn: () => bookingsService.finalizeClearance(bookingId),
onSuccess: () => {
toast.success("Clearance finalized");
toast.success("Document approval finalized");
refresh();
},
onError: (e) =>
toast.error(
e instanceof Error ? e.message : "Could not finalize clearance",
e instanceof Error
? e.message
: "Could not finalize document approval",
),
});
@@ -366,7 +368,7 @@ export function ClearanceReviewSection({
<Alert color="red" radius="md" icon={<AlertCircle size={16} />}>
{finalizeMutation.error instanceof Error
? finalizeMutation.error.message
: "Could not finalize clearance."}
: "Could not finalize document approval."}
</Alert>
)}
@@ -445,7 +447,7 @@ export function ClearanceReviewSection({
loading={finalizeMutation.isPending}
onClick={() => finalizeMutation.mutate()}
>
Finalize clearance
Finalize document approval
</Button>
</Group>
</Paper>

View File

@@ -68,7 +68,7 @@ export interface ContractClearanceReviewSectionProps {
queriesLocked?: boolean;
/**
* ONE_TIME customs contracts use the phased milestone workflow. Hides the
* legacy "Finalize clearance" shortcut; booking readiness follows delivery
* legacy "Finalize document approval" shortcut; booking readiness follows delivery
* order (import) or export release.
*/
phasedCustoms?: boolean;
@@ -393,7 +393,7 @@ export function ContractClearanceReviewSection({
<Alert color="red" radius="md" icon={<AlertCircle size={16} />}>
{finalizeClearance.error instanceof Error
? finalizeClearance.error.message
: "Could not finalize clearance."}
: "Could not finalize document approval."}
</Alert>
)}
@@ -482,7 +482,7 @@ export function ContractClearanceReviewSection({
})
}
>
Finalize clearance
Finalize document approval
</Button>
</Group>
</Paper>

View File

@@ -160,7 +160,8 @@ export default function BuildTrainModal({ opened, onClose, onBuilt }: BuildTrain
onChange={(value) => setImportTrainNumber(value ?? "")}
searchable
clearable
nothingFoundMessage="No free run numbers — add more in Dropdown Settings"
nothingFoundMessage={importNumbers.emptyMessage}
error={importNumbers.settingMissing ? importNumbers.emptyMessage : undefined}
/>
</Group>
<Select

View File

@@ -105,7 +105,8 @@ const EditTrainDetailsModal = ({ train, onClose }: EditTrainDetailsModalProps) =
}}
searchable
clearable
nothingFoundMessage="No free run numbers — add more in Dropdown Settings"
nothingFoundMessage={importNumbers.emptyMessage}
error={importNumbers.settingMissing ? importNumbers.emptyMessage : undefined}
radius="md"
/>
<TextInput

View File

@@ -348,14 +348,16 @@ export function useContractClearanceMutations(
onSuccess: () => {
toast.success(
selfClear
? "Clearance approved — customer can now book"
: "Clearance finalized — ready for booking",
? "Document approval finalized — customer can now book"
: "Document approval finalized — ready for booking",
);
refresh();
},
onError: (e) =>
toast.error(
e instanceof Error ? e.message : "Could not finalize clearance",
e instanceof Error
? e.message
: "Could not finalize document approval",
),
});

View File

@@ -1,7 +1,6 @@
import { useQuery } from "@tanstack/react-query";
import { useMemo } from "react";
import { IMPORT_TRAIN_OPTIONS } from "@/constants/trainRuns";
import { api } from "@/services/api";
/** Dropdown-settings code holding the admin-managed IMPORT run numbers. */
@@ -14,10 +13,11 @@ export interface ImportTrainNumberOption {
}
/**
* Selectable IMPORT run numbers for the Train Builder, sourced from the
* admin-managed `import_train_numbers` dropdown setting (admins add new runs
* from the Dropdown Settings editor). Falls back to the legacy hardcoded run
* list while the setting is missing or has no options.
* Selectable IMPORT run numbers for the Train Builder, sourced solely from the
* admin-managed `import_train_numbers` dropdown setting admins add and remove
* runs from /dashboard/dropdown-settings and the pickers follow. There is no
* hardcoded fallback on purpose: a missing setting must be visible (see
* `settingMissing`) rather than masked by stale defaults.
*
* Numbers already claimed by an existing train are kept in the list but
* disabled and tagged "in use". Pass `currentNumber` when editing a train so
@@ -37,14 +37,13 @@ export function useImportTrainNumberOptions(currentNumber?: string | null) {
);
const options = useMemo<ImportTrainNumberOption[]>(() => {
const configured = [...(settingQuery.data?.children ?? [])]
const base = [...(settingQuery.data?.children ?? [])]
.filter((option) => !option.disabled)
.sort((a, b) => (a.order ?? 0) - (b.order ?? 0))
.map((option) => ({
value: option.value,
label: option.label || option.value,
}));
const base = configured.length ? configured : IMPORT_TRAIN_OPTIONS;
const used = new Set(usedQuery.data?.importTrainNumbers ?? []);
if (currentNumber) used.delete(currentNumber);
@@ -60,8 +59,16 @@ export function useImportTrainNumberOptions(currentNumber?: string | null) {
return items;
}, [settingQuery.data, usedQuery.data, currentNumber]);
const settingMissing = settingQuery.isError;
return {
options,
isLoading: settingQuery.isLoading || usedQuery.isLoading,
/** True when the dropdown setting is absent — surfaced instead of silently
* falling back, so a broken config is visible rather than looking normal. */
settingMissing,
emptyMessage: settingMissing
? `Dropdown setting "${IMPORT_TRAIN_NUMBERS_CODE}" is missing — create it in Dropdown Settings`
: "No free run numbers — add more in Dropdown Settings",
};
}

View File

@@ -325,7 +325,7 @@ export default function DocumentClearanceListPage({
subtitle={
opsMode
? "Review the customer's own clearance documents per shipment booking, raise queries, and finalize."
: "Review customer documents, raise queries, and finalize clearance for each booking."
: "Review customer documents, raise queries, and finalize document approval for each booking."
}
meta={statusBadge}
action={

View File

@@ -256,7 +256,7 @@ function StatusBadge({ row }: { row: ClearanceRow }) {
if (row.ready) {
return (
<Tooltip
label="Clearance finalized — the customer creates the booking in the portal"
label="Document approval finalized — the customer creates the booking in the portal"
withArrow
>
<Badge
@@ -266,7 +266,7 @@ function StatusBadge({ row }: { row: ClearanceRow }) {
radius="sm"
leftSection={<PackageCheck size={12} />}
>
Clearance finalized
Documents approved
</Badge>
</Tooltip>
);