[200~feat: add CustomsPaymentsCard and PaymentsTab components for handling customs payments and payment summaries

This commit is contained in:
Marshal
2026-08-20 15:45:42 +00:00
committed by Hagernesh
parent 2e28b10610
commit c5909b8ec7
57 changed files with 3255 additions and 787 deletions

View File

@@ -232,6 +232,9 @@ import {
type BuiltTrainListFilters,
type BuiltTrainListResponse,
type ScheduleConsist,
type ScheduleWagonYards,
type UpdateScheduleWagonYardsPayload,
type UpdateScheduleWagonYardsResult,
type ScheduleHistoryEntry,
type TrainComposition,
type UpdateTrainDetailsPayload,
@@ -416,6 +419,30 @@ export const api = {
],
),
scheduleWagonYards: endpoint<{ scheduleId: string }, ScheduleWagonYards>(
"train-scheduling",
"schedule-wagon-yards",
({ scheduleId }) =>
trainBuilderService.scheduleWagonYards(scheduleId).then((r) => r.data),
({ scheduleId }) => [
...QUERY_KEYS.TRAIN_SCHEDULING.ROOT,
"wagon-yards",
scheduleId,
],
),
updateScheduleWagonYards: endpoint<
{ scheduleId: string; payload: UpdateScheduleWagonYardsPayload },
UpdateScheduleWagonYardsResult
>(
"train-scheduling",
"update-schedule-wagon-yards",
({ scheduleId, payload }) =>
trainBuilderService.updateScheduleWagonYards(scheduleId, payload).then((r) => r.data),
undefined,
() => TRAIN_SCHEDULING_INVALIDATIONS,
),
adjustConsist: endpoint<
{ scheduleId: string; payload: AdjustConsistPayload },
AdjustConsistResult

View File

@@ -466,11 +466,11 @@ export const bookingsService = {
return unwrap(response.data) as Freight.ClearanceCharge[];
},
/** GL Ethiopia sets or revises a charge's amount + currency. */
/** GL Ethiopia sets or revises a charge's amount, currency and description. */
billClearanceCharge: async (
id: string,
chargeId: string,
payload: { amount: number; currency: string },
payload: { amount: number; currency: string; description?: string },
): Promise<Freight.ClearanceCharge[]> => {
const response = await client.patch(
`/bookings/${id}/clearance/charges/${chargeId}/bill`,
@@ -479,7 +479,7 @@ export const bookingsService = {
return unwrap(response.data) as Freight.ClearanceCharge[];
},
/** GL Ethiopia issues the charge's payable invoice to the customer. */
/** GL Ethiopia sends the priced charge to the customer for approval. */
sendClearanceCharge: async (
id: string,
chargeId: string,
@@ -490,16 +490,17 @@ export const bookingsService = {
return unwrap(response.data) as Freight.ClearanceCharge[];
},
/** GL Ethiopia creates the miscellaneous charge (document + amount + currency). */
/** GL Ethiopia creates a miscellaneous charge (document + amount + currency + description). */
createMiscellaneousCharge: async (
id: string,
file: File,
payload: { amount: number; currency: string },
payload: { amount: number; currency: string; description: string },
): Promise<Freight.ClearanceCharge[]> => {
const form = new FormData();
form.append("file", file);
form.append("amount", String(payload.amount));
form.append("currency", payload.currency);
form.append("description", payload.description);
const response = await client.post(
`/bookings/${id}/clearance/charges/miscellaneous`,
form,

View File

@@ -300,6 +300,45 @@ export interface ScheduleHistoryEntry {
/** Adjust response: fresh consist + schedule-impact warnings to surface. */
export type AdjustConsistResult = ScheduleConsist & { warnings: string[] };
/** One consist wagon in the schedule-yards tab: where this departure plans it vs where it stands. */
export interface ScheduleWagonYardRow {
id: string;
wagonNumber: string;
sequenceNumber: number | null;
wagonType: { id: string; code: string; name: string };
physicalYardId: string | null;
physicalYardLabel: string | null;
plannedYardId: string | null;
plannedYardLabel: string | null;
aligned: boolean;
locked: boolean;
lockReason: string | null;
}
export interface ScheduleWagonYardStop {
yardId: string;
label: string;
/** Origin or intermediate stop — wagons can board here. The destination cannot. */
pickup: boolean;
planned: number;
physical: number;
}
export interface ScheduleWagonYards {
scheduleId: string;
train: { id: string; code: string };
editable: boolean;
stops: ScheduleWagonYardStop[];
wagons: ScheduleWagonYardRow[];
misaligned: number;
}
export interface UpdateScheduleWagonYardsPayload {
moves: Array<{ wagonId: string; yardId: string }>;
}
export type UpdateScheduleWagonYardsResult = ScheduleWagonYards & { warnings: string[] };
export const trainBuilderService = {
list: (filters: BuiltTrainListFilters = {}) =>
apiClient.get<BuiltTrainListResponse>(`${BASE}${toQuery(filters)}`),
@@ -350,6 +389,15 @@ export const trainBuilderService = {
`/train-scheduling/schedules/${scheduleId}/adjust-consist`,
payload,
),
/** Schedule-only wagon yard plan (where THIS departure boards each wagon). */
scheduleWagonYards: (scheduleId: string) =>
apiClient.get<ScheduleWagonYards>(`/train-scheduling/schedules/${scheduleId}/wagon-yards`),
/** Re-plan boarding yards for this schedule; physical wagon yards untouched. */
updateScheduleWagonYards: (scheduleId: string, payload: UpdateScheduleWagonYardsPayload) =>
apiClient.patch<UpdateScheduleWagonYardsResult>(
`/train-scheduling/schedules/${scheduleId}/wagon-yards`,
payload,
),
/** Unified wagon/booking change history for the schedule's History tab. */
scheduleHistory: (scheduleId: string) =>
apiClient.get<ScheduleHistoryEntry[]>(