mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-29 18:48:11 +00:00
fix
This commit is contained in:
@@ -84,6 +84,7 @@ import type {
|
||||
InventoryInquiryFilter,
|
||||
InventoryInquiryResult,
|
||||
InventoryMovement,
|
||||
InitiateWarehouseInvoicePaymentPayload,
|
||||
LoadableWagon,
|
||||
LoadInventoryPayload,
|
||||
LoadPassedExportResult,
|
||||
@@ -102,6 +103,7 @@ import type {
|
||||
WarehouseActivityLog,
|
||||
WarehouseDashboard,
|
||||
WarehouseFeeInvoice,
|
||||
WarehouseInvoicePaymentResponse,
|
||||
WarehouseFilter,
|
||||
WarehouseInventoryItem,
|
||||
WarehouseInvoiceFilter,
|
||||
@@ -943,12 +945,19 @@ export const api = {
|
||||
() => INVENTORY_INVALIDATIONS,
|
||||
),
|
||||
|
||||
autoUnloadArrivedBookings: endpoint<string, AutoUnloadArrivedResult>(
|
||||
autoUnloadArrivedBookings: endpoint<
|
||||
{
|
||||
scheduleId: string;
|
||||
warehouseId?: string;
|
||||
assignments?: { bookingId: string; warehouseId: string; yardId: string; zoneId: string }[];
|
||||
},
|
||||
AutoUnloadArrivedResult
|
||||
>(
|
||||
"warehouse-inventory",
|
||||
"auto-unload-arrived-bookings",
|
||||
(scheduleId) =>
|
||||
({ scheduleId, warehouseId, assignments }) =>
|
||||
warehouseService
|
||||
.autoUnloadArrivedBookings(scheduleId)
|
||||
.autoUnloadArrivedBookings({ scheduleId, warehouseId, assignments })
|
||||
.then((r) => r.data),
|
||||
undefined,
|
||||
() => INVENTORY_INVALIDATIONS,
|
||||
@@ -1111,6 +1120,18 @@ export const api = {
|
||||
() => [["warehouse-fee-invoices"], ["warehouse-inventory"]],
|
||||
),
|
||||
|
||||
payInvoiceOnline: endpoint<
|
||||
{ id: string; payload: InitiateWarehouseInvoicePaymentPayload },
|
||||
WarehouseInvoicePaymentResponse
|
||||
>(
|
||||
"warehouse-fee-invoices",
|
||||
"pay-online",
|
||||
({ id, payload }) =>
|
||||
warehouseService.payInvoiceOnline(id, payload).then((r) => r.data),
|
||||
undefined,
|
||||
() => [["warehouse-fee-invoices"], ["warehouse-inventory"]],
|
||||
),
|
||||
|
||||
gateClearance: endpoint<string, WarehouseInventoryItem>(
|
||||
"warehouse-fee-invoices",
|
||||
"gate-clearance",
|
||||
@@ -1122,8 +1143,14 @@ export const api = {
|
||||
},
|
||||
|
||||
routes: {
|
||||
list: endpoint<void, RouteRecord[]>("routes", "list", () =>
|
||||
routesService.getAll().then((r) => r.data),
|
||||
list: endpoint<{ status?: import("./routes.service").RouteStatus } | void, RouteRecord[]>(
|
||||
"routes",
|
||||
"list",
|
||||
(input) =>
|
||||
routesService
|
||||
.getAll(input?.status ? { status: input.status } : undefined)
|
||||
.then((r) => r.data),
|
||||
(input) => ["routes", input?.status ?? "all"],
|
||||
),
|
||||
|
||||
yards: endpoint<void, YardRef[]>(
|
||||
|
||||
@@ -301,6 +301,105 @@ export const bookingsService = {
|
||||
|
||||
governmentExpedite: (id: string) =>
|
||||
postBooking<BookingDetail>(B.GOVERNMENT_EXPEDITE(id)),
|
||||
|
||||
getClearance: async (id: string): Promise<Freight.ClearanceView> => {
|
||||
const response = await client.get(B.CLEARANCE(id));
|
||||
return unwrap(response.data) as Freight.ClearanceView;
|
||||
},
|
||||
|
||||
uploadDeclaration: async (
|
||||
id: string,
|
||||
files: Record<string, File | null>,
|
||||
): Promise<BookingDetail> => {
|
||||
const form = new FormData();
|
||||
for (const [key, file] of Object.entries(files)) {
|
||||
if (file) form.append(key, file);
|
||||
}
|
||||
const response = await client.post(B.CLEARANCE_DECLARATION(id), form, {
|
||||
headers: { "Content-Type": "multipart/form-data" },
|
||||
});
|
||||
return unwrap(response.data) as BookingDetail;
|
||||
},
|
||||
|
||||
adviseDuty: async (
|
||||
id: string,
|
||||
payload: {
|
||||
dutyRequired: boolean;
|
||||
amount?: number;
|
||||
currency?: string;
|
||||
declarationSerial?: string;
|
||||
attachment?: File | null;
|
||||
},
|
||||
): Promise<BookingDetail> => {
|
||||
const form = new FormData();
|
||||
form.append("dutyRequired", String(payload.dutyRequired));
|
||||
if (payload.amount != null) form.append("amount", String(payload.amount));
|
||||
if (payload.currency) form.append("currency", payload.currency);
|
||||
if (payload.declarationSerial) {
|
||||
form.append("declarationSerial", payload.declarationSerial);
|
||||
}
|
||||
if (payload.attachment) form.append("attachment", payload.attachment);
|
||||
const response = await client.post(B.CLEARANCE_DUTY(id), form, {
|
||||
headers: { "Content-Type": "multipart/form-data" },
|
||||
});
|
||||
return unwrap(response.data) as BookingDetail;
|
||||
},
|
||||
|
||||
finalizePreClearance: (id: string) =>
|
||||
postBooking<BookingDetail>(B.CLEARANCE_FINALIZE_PRE(id)),
|
||||
|
||||
uploadTransitPermit: async (
|
||||
id: string,
|
||||
files: Record<string, File | null>,
|
||||
): Promise<BookingDetail> => {
|
||||
const form = new FormData();
|
||||
for (const [key, file] of Object.entries(files)) {
|
||||
if (file) form.append(key, file);
|
||||
}
|
||||
const response = await client.post(B.CLEARANCE_TRANSIT_PERMIT(id), form, {
|
||||
headers: { "Content-Type": "multipart/form-data" },
|
||||
});
|
||||
return unwrap(response.data) as BookingDetail;
|
||||
},
|
||||
|
||||
uploadDeliveryOrder: async (id: string, file: File): Promise<BookingDetail> => {
|
||||
const form = new FormData();
|
||||
form.append("file", file);
|
||||
const response = await client.post(B.CLEARANCE_DELIVERY_ORDER(id), form, {
|
||||
headers: { "Content-Type": "multipart/form-data" },
|
||||
});
|
||||
return unwrap(response.data) as BookingDetail;
|
||||
},
|
||||
|
||||
uploadReleaseOrder: async (
|
||||
id: string,
|
||||
file: File,
|
||||
vesselDepartureDate: string,
|
||||
): Promise<{ hold?: boolean; holdReason?: string }> => {
|
||||
const form = new FormData();
|
||||
form.append("file", file);
|
||||
form.append("vesselDepartureDate", vesselDepartureDate);
|
||||
const response = await client.post(B.CLEARANCE_RELEASE_ORDER(id), form, {
|
||||
headers: { "Content-Type": "multipart/form-data" },
|
||||
});
|
||||
return unwrap(response.data) as { hold?: boolean; holdReason?: string };
|
||||
},
|
||||
|
||||
requestRoAmendment: (id: string, note?: string) =>
|
||||
postBooking<BookingDetail>(B.CLEARANCE_RO_AMENDMENT(id), { note }),
|
||||
|
||||
confirmExportRelease: (id: string) =>
|
||||
postBooking<BookingDetail>(B.CLEARANCE_EXPORT_RELEASE(id), {}),
|
||||
|
||||
getEtClearanceQueue: async (): Promise<BookingDetail[]> => {
|
||||
const response = await client.get(B.CLEARANCE_ET_QUEUE);
|
||||
return (unwrap(response.data) ?? []) as BookingDetail[];
|
||||
},
|
||||
|
||||
getDjClearanceQueue: async (): Promise<BookingDetail[]> => {
|
||||
const response = await client.get(B.CLEARANCE_DJ_QUEUE);
|
||||
return (unwrap(response.data) ?? []) as BookingDetail[];
|
||||
},
|
||||
};
|
||||
|
||||
async function ensurePdfBlob(blob: Blob): Promise<Blob> {
|
||||
|
||||
@@ -159,6 +159,13 @@ export const contractsService = {
|
||||
return unwrap(response.data) as ContractView;
|
||||
},
|
||||
|
||||
downloadContractDocument: async (id: string): Promise<Blob> => {
|
||||
const response = await client.get(C.CONTRACT_DOCUMENT(id), {
|
||||
responseType: "blob",
|
||||
});
|
||||
return response.data as Blob;
|
||||
},
|
||||
|
||||
signContract: (id: string, payload: SignContractPayload) =>
|
||||
postContract<Freight.IContract>(C.CONTRACT_SIGN(id), payload),
|
||||
|
||||
@@ -200,6 +207,153 @@ export const contractsService = {
|
||||
finalizeClearance: (id: string) =>
|
||||
postContract<Freight.IContract>(C.CLEARANCE_FINALIZE(id)),
|
||||
|
||||
getEtClearanceQueue: async (): Promise<PaginatedContracts> => {
|
||||
const response = await client.get<PaginatedContracts>(C.CLEARANCE_ET_QUEUE);
|
||||
const data = unwrap(response.data);
|
||||
return {
|
||||
items: (data.items ?? []) as Freight.IContract[],
|
||||
total: data.total ?? 0,
|
||||
};
|
||||
},
|
||||
|
||||
getDjClearanceQueue: async (): Promise<PaginatedContracts> => {
|
||||
const response = await client.get<PaginatedContracts>(C.CLEARANCE_DJ_QUEUE);
|
||||
const data = unwrap(response.data);
|
||||
return {
|
||||
items: (data.items ?? []) as Freight.IContract[],
|
||||
total: data.total ?? 0,
|
||||
};
|
||||
},
|
||||
|
||||
uploadDeclaration: async (
|
||||
id: string,
|
||||
files: Record<string, File | null>,
|
||||
): Promise<Freight.IContract> => {
|
||||
const form = new FormData();
|
||||
for (const [key, file] of Object.entries(files)) {
|
||||
if (file) form.append(key, file);
|
||||
}
|
||||
const response = await client.post(C.CLEARANCE_DECLARATION(id), form, {
|
||||
headers: { "Content-Type": "multipart/form-data" },
|
||||
});
|
||||
return unwrap(response.data) as Freight.IContract;
|
||||
},
|
||||
|
||||
adviseContractDuty: async (
|
||||
id: string,
|
||||
payload: {
|
||||
dutyRequired: boolean;
|
||||
amount?: number;
|
||||
currency?: string;
|
||||
declarationSerial?: string;
|
||||
attachment?: File | null;
|
||||
},
|
||||
): Promise<Freight.IContract> => {
|
||||
const form = new FormData();
|
||||
form.append("dutyRequired", String(payload.dutyRequired));
|
||||
if (payload.amount != null) form.append("amount", String(payload.amount));
|
||||
if (payload.currency) form.append("currency", payload.currency);
|
||||
if (payload.declarationSerial) {
|
||||
form.append("declarationSerial", payload.declarationSerial);
|
||||
}
|
||||
if (payload.attachment) form.append("attachment", payload.attachment);
|
||||
const response = await client.post(C.CLEARANCE_DUTY(id), form, {
|
||||
headers: { "Content-Type": "multipart/form-data" },
|
||||
});
|
||||
return unwrap(response.data) as Freight.IContract;
|
||||
},
|
||||
|
||||
finalizePreClearance: (id: string) =>
|
||||
postContract<Freight.IContract>(C.CLEARANCE_FINALIZE_PRE(id)),
|
||||
|
||||
uploadContractTransitPermit: async (
|
||||
id: string,
|
||||
files: Record<string, File | null>,
|
||||
): Promise<Freight.IContract> => {
|
||||
const form = new FormData();
|
||||
for (const [key, file] of Object.entries(files)) {
|
||||
if (file) form.append(key, file);
|
||||
}
|
||||
const response = await client.post(C.CLEARANCE_TRANSIT_PERMIT(id), form, {
|
||||
headers: { "Content-Type": "multipart/form-data" },
|
||||
});
|
||||
return unwrap(response.data) as Freight.IContract;
|
||||
},
|
||||
|
||||
uploadDeliveryOrder: async (
|
||||
id: string,
|
||||
file: File,
|
||||
): Promise<Freight.IContract> => {
|
||||
const form = new FormData();
|
||||
form.append("file", file);
|
||||
const response = await client.post(C.CLEARANCE_DELIVERY_ORDER(id), form, {
|
||||
headers: { "Content-Type": "multipart/form-data" },
|
||||
});
|
||||
return unwrap(response.data) as Freight.IContract;
|
||||
},
|
||||
|
||||
uploadReleaseOrder: async (
|
||||
id: string,
|
||||
file: File,
|
||||
vesselDepartureDate: string,
|
||||
): Promise<{ contract: Freight.IContract; hold: boolean; holdReason?: string }> => {
|
||||
const form = new FormData();
|
||||
form.append("file", file);
|
||||
form.append("vesselDepartureDate", vesselDepartureDate);
|
||||
const response = await client.post(C.CLEARANCE_RELEASE_ORDER(id), form, {
|
||||
headers: { "Content-Type": "multipart/form-data" },
|
||||
});
|
||||
return unwrap(response.data) as {
|
||||
contract: Freight.IContract;
|
||||
hold: boolean;
|
||||
holdReason?: string;
|
||||
};
|
||||
},
|
||||
|
||||
requestRoAmendment: (id: string, note?: string) =>
|
||||
postContract<Freight.IContract>(C.CLEARANCE_RO_AMENDMENT(id), { note }),
|
||||
|
||||
confirmExportRelease: (id: string) =>
|
||||
postContract<Freight.IContract>(C.CLEARANCE_EXPORT_RELEASE(id)),
|
||||
|
||||
finalizeExportClearance: (id: string) =>
|
||||
postContract<Freight.IContract>(C.CLEARANCE_FINALIZE_EXPORT(id)),
|
||||
|
||||
uploadTransportDocument: async (
|
||||
bookingId: string,
|
||||
files: Record<string, File | null>,
|
||||
) => {
|
||||
const form = new FormData();
|
||||
for (const [key, file] of Object.entries(files)) {
|
||||
if (file) form.append(key, file);
|
||||
}
|
||||
const response = await client.post(C.BOOKING_TRANSPORT_DOCUMENT(bookingId), form, {
|
||||
headers: { "Content-Type": "multipart/form-data" },
|
||||
});
|
||||
return unwrap(response.data);
|
||||
},
|
||||
|
||||
/** GL Djibouti uploads T1 transit documents (multi-file, post wagon allocation). */
|
||||
uploadT1Documents: async (
|
||||
bookingId: string,
|
||||
files: Record<string, File | null>,
|
||||
) => {
|
||||
const form = new FormData();
|
||||
for (const [key, file] of Object.entries(files)) {
|
||||
if (file) form.append(key, file);
|
||||
}
|
||||
const response = await client.post(C.BOOKING_T1_DOCUMENTS(bookingId), form, {
|
||||
headers: { "Content-Type": "multipart/form-data" },
|
||||
});
|
||||
return unwrap(response.data);
|
||||
},
|
||||
|
||||
/** GL Ethiopia closes (accepts) the T1 document set after the train arrives. */
|
||||
closeT1: async (bookingId: string): Promise<Freight.ClearanceT1State> => {
|
||||
const response = await client.post(C.BOOKING_T1_CLOSE(bookingId));
|
||||
return unwrap(response.data) as Freight.ClearanceT1State;
|
||||
},
|
||||
|
||||
// ── Path A self-clearance (Operations review) ──
|
||||
getOpsClearanceQueue: async (): Promise<PaginatedContracts> => {
|
||||
const response = await client.get<PaginatedContracts>(
|
||||
|
||||
@@ -29,9 +29,12 @@ export interface LastMileVehicle {
|
||||
plateNumber: string;
|
||||
manufacturer: string;
|
||||
model: string;
|
||||
vehicleType?: string | null;
|
||||
code?: string | null;
|
||||
powerPlateNo?: string | null;
|
||||
trailerPlateNo?: string | null;
|
||||
assignedDriverId?: string | null;
|
||||
assignedDriverName?: string | null;
|
||||
}
|
||||
|
||||
export interface LastMileRecord {
|
||||
|
||||
@@ -2,6 +2,8 @@ import { api as apiClient } from '../auth/http';
|
||||
|
||||
import { URL_CONSTANTS } from '@/constants/URLS';
|
||||
|
||||
export type RouteStatus = 'AVAILABLE' | 'MAINTENANCE' | 'DAMAGED' | 'STOP_WORKING';
|
||||
|
||||
export interface YardRef {
|
||||
id: string;
|
||||
code: string;
|
||||
@@ -14,32 +16,54 @@ export interface RouteMilestone {
|
||||
routeId: string;
|
||||
yardId: string;
|
||||
sequenceNo: number;
|
||||
distanceKm?: number | null;
|
||||
yard?: YardRef | null;
|
||||
}
|
||||
|
||||
export interface RouteRecord {
|
||||
id: string;
|
||||
name: string;
|
||||
status: RouteStatus;
|
||||
originYardId: string;
|
||||
destinationYardId: string;
|
||||
isActive: boolean;
|
||||
originYard?: YardRef | null;
|
||||
destinationYard?: YardRef | null;
|
||||
milestones?: RouteMilestone[];
|
||||
}
|
||||
|
||||
export interface SaveRoutePayload {
|
||||
name: string;
|
||||
milestones: Array<{ yardId: string }>;
|
||||
isActive?: boolean;
|
||||
milestones: Array<{ yardId: string; distanceKm?: number }>;
|
||||
status?: RouteStatus;
|
||||
}
|
||||
|
||||
export function formatRouteLabel(route: RouteRecord): string {
|
||||
const origin =
|
||||
route.originYard?.code ?? route.originYard?.label ?? 'Origin';
|
||||
const dest =
|
||||
route.destinationYard?.code ?? route.destinationYard?.label ?? 'Destination';
|
||||
return `${origin} → ${dest}`;
|
||||
}
|
||||
|
||||
export function totalRouteDistanceKm(route: RouteRecord): number {
|
||||
return (route.milestones ?? []).reduce(
|
||||
(sum, m) => sum + Number(m.distanceKm ?? 0),
|
||||
0,
|
||||
);
|
||||
}
|
||||
|
||||
export const ROUTE_STATUS_OPTIONS: Array<{ value: RouteStatus; label: string }> = [
|
||||
{ value: 'AVAILABLE', label: 'Available' },
|
||||
{ value: 'MAINTENANCE', label: 'Maintenance' },
|
||||
{ value: 'DAMAGED', label: 'Damaged' },
|
||||
{ value: 'STOP_WORKING', label: 'Stop working' },
|
||||
];
|
||||
|
||||
interface YardListResponse {
|
||||
data: YardRef[];
|
||||
}
|
||||
|
||||
export const routesService = {
|
||||
getAll: () => apiClient.get<RouteRecord[]>(URL_CONSTANTS.ROUTES.BASE),
|
||||
getAll: (params?: { status?: RouteStatus; search?: string }) =>
|
||||
apiClient.get<RouteRecord[]>(URL_CONSTANTS.ROUTES.BASE, { params }),
|
||||
getById: (id: string) => apiClient.get<RouteRecord>(URL_CONSTANTS.ROUTES.BY_ID(id)),
|
||||
create: (data: SaveRoutePayload) => apiClient.post(URL_CONSTANTS.ROUTES.BASE, data),
|
||||
update: (id: string, data: Partial<SaveRoutePayload>) =>
|
||||
|
||||
@@ -29,6 +29,8 @@ export interface Vehicle {
|
||||
status: VehicleStatus;
|
||||
availability: VehicleAvailability;
|
||||
description?: string | null;
|
||||
assignedDriverId?: string | null;
|
||||
assignedDriverName?: string | null;
|
||||
code?: string | null;
|
||||
powerPlateNo?: string | null;
|
||||
trailerPlateNo?: string | null;
|
||||
|
||||
@@ -18,6 +18,8 @@ import type {
|
||||
WarehouseFeeInvoice,
|
||||
WarehouseInvoiceFilter,
|
||||
PayInvoicePayload,
|
||||
InitiateWarehouseInvoicePaymentPayload,
|
||||
WarehouseInvoicePaymentResponse,
|
||||
BookingScheduleView,
|
||||
InventoryFilter,
|
||||
InventoryInquiryFilter,
|
||||
@@ -171,10 +173,14 @@ export const warehouseService = {
|
||||
apiClient.get<ImportTrain[]>(URL_CONSTANTS.WAREHOUSE_INVENTORY.IMPORT_ARRIVE_QUEUE),
|
||||
importTrainItems: (scheduleId: string) =>
|
||||
apiClient.get<ImportTrainItem[]>(URL_CONSTANTS.WAREHOUSE_INVENTORY.IMPORT_TRAIN_ITEMS(scheduleId)),
|
||||
autoUnloadArrivedBookings: (scheduleId: string) =>
|
||||
autoUnloadArrivedBookings: (payload: {
|
||||
scheduleId: string;
|
||||
warehouseId?: string;
|
||||
assignments?: { bookingId: string; warehouseId: string; yardId: string; zoneId: string }[];
|
||||
}) =>
|
||||
apiClient.post<AutoUnloadArrivedResult>(
|
||||
URL_CONSTANTS.WAREHOUSE_INVENTORY.IMPORT_AUTO_UNLOAD_ARRIVED,
|
||||
{ scheduleId },
|
||||
payload,
|
||||
),
|
||||
importUnloadedQueue: () =>
|
||||
apiClient.get<ImportUnloadedItem[]>(URL_CONSTANTS.WAREHOUSE_INVENTORY.IMPORT_UNLOADED_QUEUE),
|
||||
@@ -294,6 +300,8 @@ export const warehouseService = {
|
||||
apiClient.patch<WarehouseFeeInvoice>(URL_CONSTANTS.WAREHOUSE_INVOICES.CANCEL(id)),
|
||||
payInvoice: (id: string, payload: PayInvoicePayload) =>
|
||||
apiClient.post<WarehouseFeeInvoice>(URL_CONSTANTS.WAREHOUSE_INVOICES.PAY(id), payload),
|
||||
payInvoiceOnline: (id: string, payload: InitiateWarehouseInvoicePaymentPayload) =>
|
||||
apiClient.post<WarehouseInvoicePaymentResponse>(URL_CONSTANTS.WAREHOUSE_INVOICES.PAY_ONLINE(id), payload),
|
||||
gateClearance: (inventoryId: string) =>
|
||||
apiClient.post<WarehouseInventoryItem>(URL_CONSTANTS.WAREHOUSE_INVOICES.GATE_CLEARANCE(inventoryId), {}),
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user