Files
edr-platform/apps/edr-freight-web/backoffice/src/services/ruleEngine/ruleEngine.service.ts
Marshal 9d81a2e1ee integrate global logistics staff user into seeder
refactor pricing data seeder to fold surcharge types into rates
update route meta subtitle to remove surcharge types
enhance RuleEngineFormDialog to support conditional field visibility
 remove surcharge types from URL constants and related services
add cargo leaf options query for bulk cargo type selection
update RuleEngineResourcePage to utilize cargo leaf options
modify resources configuration to remove surcharge types
implement migration to fold surcharge types into rates
create utility to derive legacy rate types from new rate structure
2026-06-23 23:15:32 +00:00

204 lines
6.5 KiB
TypeScript

import { api as client } from "@/auth/http";
import { URL_CONSTANTS } from "@/constants/URLS";
import type {
RuleEngineListMeta,
RuleEngineListResult,
RuleEngineRecord,
RuleEngineResourceSlug,
} from "@/types/rule-engine";
import { unwrap } from "@/utils/endpoint";
export interface RuleEngineListParams {
search?: string;
page?: number;
pageSize?: number;
isActive?: boolean;
status?: string;
sortBy?: string;
sortOrder?: "ASC" | "DESC";
requiresDirectorApproval?: boolean;
}
export interface RuleEngineReorderPayload {
ids: string[];
requiresDirectorApproval?: boolean;
}
const RESOURCE_BASE: Record<RuleEngineResourceSlug, string> = {
"cargo-types": URL_CONSTANTS.RULE_ENGINE.CARGO_TYPES,
"container-types": URL_CONSTANTS.RULE_ENGINE.CONTAINER_TYPES,
"wagon-types": URL_CONSTANTS.RULE_ENGINE.WAGON_TYPES,
"priority-configs": URL_CONSTANTS.RULE_ENGINE.PRIORITY_CONFIGS,
"service-types": URL_CONSTANTS.RULE_ENGINE.SERVICE_TYPES,
"weight-limit-rules": URL_CONSTANTS.RULE_ENGINE.WEIGHT_LIMIT_RULES,
yards: URL_CONSTANTS.RULE_ENGINE.YARDS,
"shipping-lines": URL_CONSTANTS.RULE_ENGINE.SHIPPING_LINES,
rates: URL_CONSTANTS.RULE_ENGINE.RATES,
"approval-rules": URL_CONSTANTS.RULE_ENGINE.APPROVAL_RULES,
};
const byIdPath = (resource: RuleEngineResourceSlug, id: string): string => {
switch (resource) {
case "cargo-types":
return URL_CONSTANTS.RULE_ENGINE.CARGO_TYPE_BY_ID(id);
case "container-types":
return URL_CONSTANTS.RULE_ENGINE.CONTAINER_TYPE_BY_ID(id);
case "wagon-types":
return URL_CONSTANTS.RULE_ENGINE.WAGON_TYPE_BY_ID(id);
case "priority-configs":
return URL_CONSTANTS.RULE_ENGINE.PRIORITY_CONFIG_BY_ID(id);
case "service-types":
return URL_CONSTANTS.RULE_ENGINE.SERVICE_TYPE_BY_ID(id);
case "weight-limit-rules":
return URL_CONSTANTS.RULE_ENGINE.WEIGHT_LIMIT_RULE_BY_ID(id);
case "yards":
return URL_CONSTANTS.RULE_ENGINE.YARD_BY_ID(id);
case "shipping-lines":
return URL_CONSTANTS.RULE_ENGINE.SHIPPING_LINE_BY_ID(id);
case "rates":
return URL_CONSTANTS.RULE_ENGINE.RATE_BY_ID(id);
case "approval-rules":
return URL_CONSTANTS.RULE_ENGINE.APPROVAL_RULE_BY_ID(id);
default:
return `${RESOURCE_BASE[resource]}/${id}`;
}
};
const defaultMeta = (dataLength: number, page = 1, pageSize = 10): RuleEngineListMeta => ({
total: dataLength,
page,
pageSize,
totalPages: Math.max(1, Math.ceil(dataLength / pageSize)),
});
const isPaginatedListResult = <T extends RuleEngineRecord>(
value: unknown,
): value is RuleEngineListResult<T> =>
Boolean(value) &&
typeof value === "object" &&
"data" in value &&
Array.isArray((value as RuleEngineListResult<T>).data);
const normalizeList = <T extends RuleEngineRecord>(
payload: unknown,
page = 1,
pageSize = 10,
): RuleEngineListResult<T> => {
if (isPaginatedListResult<T>(payload)) {
return {
data: payload.data,
meta: payload.meta ?? defaultMeta(payload.data.length, page, pageSize),
};
}
const body = unwrap(payload as { data: unknown }) as unknown;
if (isPaginatedListResult<T>(body)) {
return {
data: body.data,
meta: body.meta ?? defaultMeta(body.data.length, page, pageSize),
};
}
if (Array.isArray(body)) {
return { data: body as T[], meta: defaultMeta(body.length, page, pageSize) };
}
return { data: [], meta: defaultMeta(0, page, pageSize) };
};
const normalizeEntity = <T extends RuleEngineRecord>(payload: unknown): T => {
return unwrap(payload as { data: T }) as T;
};
export const ruleEngineService = {
list: async <T extends RuleEngineRecord>(
resource: RuleEngineResourceSlug,
params?: RuleEngineListParams,
): Promise<RuleEngineListResult<T>> => {
const page = params?.page ?? 1;
const pageSize = params?.pageSize ?? 10;
const response = await client.get(RESOURCE_BASE[resource], {
params: {
page,
pageSize,
search: params?.search,
isActive: params?.isActive,
status: params?.status,
sortBy: params?.sortBy,
sortOrder: params?.sortOrder,
requiresDirectorApproval: params?.requiresDirectorApproval,
},
});
return normalizeList<T>(response.data, page, pageSize);
},
getById: async <T extends RuleEngineRecord>(
resource: RuleEngineResourceSlug,
id: string,
): Promise<T> => {
const response = await client.get(byIdPath(resource, id));
return normalizeEntity<T>(response.data);
},
create: async <T extends RuleEngineRecord>(
resource: RuleEngineResourceSlug,
payload: Record<string, unknown>,
): Promise<T> => {
const response = await client.post(RESOURCE_BASE[resource], payload);
return normalizeEntity<T>(response.data);
},
update: async <T extends RuleEngineRecord>(
resource: RuleEngineResourceSlug,
id: string,
payload: Record<string, unknown>,
): Promise<T> => {
const response = await client.patch(byIdPath(resource, id), payload);
return normalizeEntity<T>(response.data);
},
remove: async (resource: RuleEngineResourceSlug, id: string): Promise<void> => {
await client.delete(byIdPath(resource, id));
},
reorder: async (
resource: RuleEngineResourceSlug,
payload: RuleEngineReorderPayload,
): Promise<void> => {
await client.post(`${RESOURCE_BASE[resource]}/reorder`, payload);
},
moveOrder: async (
resource: RuleEngineResourceSlug,
id: string,
direction: "up" | "down",
): Promise<void> => {
await client.post(`${byIdPath(resource, id)}/move-order`, { direction });
},
submitRate: async <T extends RuleEngineRecord>(id: string): Promise<T> => {
const response = await client.post(URL_CONSTANTS.RULE_ENGINE.RATE_SUBMIT(id));
return normalizeEntity<T>(response.data);
},
approveRate: async <T extends RuleEngineRecord>(id: string): Promise<T> => {
const response = await client.post(URL_CONSTANTS.RULE_ENGINE.RATE_APPROVE(id));
return normalizeEntity<T>(response.data);
},
getApprovalChain: async (
requiresDirectorApproval = true,
): Promise<RuleEngineRecord[]> => {
const response = await client.get(URL_CONSTANTS.RULE_ENGINE.APPROVAL_RULES_CHAIN, {
params: { requiresDirectorApproval },
});
const body = unwrap(response.data) as unknown;
if (Array.isArray(body)) return body as RuleEngineRecord[];
if (body && typeof body === "object" && "data" in body && Array.isArray((body as { data: unknown }).data)) {
return (body as { data: RuleEngineRecord[] }).data;
}
return [];
},
};