New Transit Agents admin table (name, valid-from/to, active/suspended, validity badge) — DJ assign step now picks from it, active+valid only.

This commit is contained in:
Marshal
2026-07-29 05:38:35 +00:00
parent 891311d861
commit b17a72c280
59 changed files with 2903 additions and 451 deletions

View File

@@ -338,10 +338,10 @@ export const bookingsService = {
note,
}),
/** GL Djibouti names (or changes) that officer — unblocks the declaration. */
assignTransitAssignee: (id: string, assignee: string) =>
/** GL Djibouti picks (or changes) that officer — unblocks the declaration. */
assignTransitAssignee: (id: string, transitAgentId: string) =>
postBooking<BookingDetail>(B.CLEARANCE_TRANSIT_ASSIGNEE_ASSIGN(id), {
assignee,
transitAgentId,
}),
uploadDeclaration: async (
@@ -382,6 +382,22 @@ export const bookingsService = {
return unwrap(response.data) as BookingDetail;
},
uploadDraftDeclaration: async (
id: string,
files: File[],
price: number,
currency: string,
): Promise<BookingDetail> => {
const form = new FormData();
files.forEach((file, index) => form.append(`draft_declaration_${index}`, file));
form.append("price", String(price));
form.append("currency", currency);
const response = await client.post(B.CLEARANCE_DRAFT_DECLARATION(id), form, {
headers: { "Content-Type": "multipart/form-data" },
});
return unwrap(response.data) as BookingDetail;
},
finalizePreClearance: (id: string) =>
postBooking<BookingDetail>(B.CLEARANCE_FINALIZE_PRE(id)),

View File

@@ -331,10 +331,10 @@ export const contractsService = {
{ note },
),
/** GL Djibouti names (or changes) that officer — unblocks the declaration. */
assignTransitAssignee: (id: string, assignee: string) =>
/** GL Djibouti picks (or changes) that officer — unblocks the declaration. */
assignTransitAssignee: (id: string, transitAgentId: string) =>
postContract<Freight.IContract>(C.CLEARANCE_TRANSIT_ASSIGNEE_ASSIGN(id), {
assignee,
transitAgentId,
}),
/**

View File

@@ -0,0 +1,60 @@
import type { Freight } from "@edr/types";
import { api as client } from "../auth/http";
import { unwrap } from "@/utils/endpoint";
import { URL_CONSTANTS } from "@/constants/URLS";
const X = URL_CONSTANTS.GL_EXCHANGE;
export interface GlExchangeUpload {
title: string;
visibleToCustomer: boolean;
file: File;
}
export interface GlExchangeEdit {
title?: string;
visibleToCustomer?: boolean;
/** Optional replacement bytes — omit to keep the stored file. */
file?: File | null;
}
const multipart = { headers: { "Content-Type": "multipart/form-data" } };
/** GL Ethiopia ↔ GL Djibouti shared documents for one booking or contract. */
export const glExchangeService = {
list: async (entityId: string): Promise<Freight.GlExchangeDocument[]> => {
const response = await client.get(X.FOR_ENTITY(entityId));
return (unwrap(response.data) ?? []) as Freight.GlExchangeDocument[];
},
upload: async (
entityId: string,
input: GlExchangeUpload,
): Promise<Freight.GlExchangeDocument> => {
const form = new FormData();
form.append("file", input.file);
form.append("title", input.title);
form.append("visibleToCustomer", String(input.visibleToCustomer));
const response = await client.post(X.FOR_ENTITY(entityId), form, multipart);
return unwrap(response.data) as Freight.GlExchangeDocument;
},
update: async (
documentId: string,
input: GlExchangeEdit,
): Promise<Freight.GlExchangeDocument> => {
const form = new FormData();
if (input.file) form.append("file", input.file);
if (input.title != null) form.append("title", input.title);
if (input.visibleToCustomer != null) {
form.append("visibleToCustomer", String(input.visibleToCustomer));
}
const response = await client.patch(X.DOCUMENT(documentId), form, multipart);
return unwrap(response.data) as Freight.GlExchangeDocument;
},
remove: async (documentId: string): Promise<void> => {
await client.delete(X.DOCUMENT(documentId));
},
};

View File

@@ -94,6 +94,7 @@ const RESOURCE_BASE: Record<RuleEngineResourceSlug, string> = {
"shipping-lines": URL_CONSTANTS.RULE_ENGINE.SHIPPING_LINES,
rates: URL_CONSTANTS.RULE_ENGINE.RATES,
"approval-rules": URL_CONSTANTS.RULE_ENGINE.APPROVAL_RULES,
"transit-agents": URL_CONSTANTS.RULE_ENGINE.TRANSIT_AGENTS,
};
const byIdPath = (resource: RuleEngineResourceSlug, id: string): string => {

View File

@@ -0,0 +1,20 @@
import { api } from "../auth/http";
import { URL_CONSTANTS } from "../constants/URLS";
export interface TransitAgent {
id: string;
name: string;
validFrom: string;
validTo: string;
isActive: boolean;
}
export const transitAgentsService = {
/** Active + currently inside its validity window — the assignment dropdown. */
async listAssignable() {
const response = await api.get<TransitAgent[]>(
URL_CONSTANTS.RULE_ENGINE.TRANSIT_AGENTS_ASSIGNABLE,
);
return response.data;
},
};