Merge pull request #1113 from Tria-plc/freight_feature/usermanagement

Freight feature/usermanagement
This commit is contained in:
marshal
2026-08-04 20:31:16 +03:00
committed by GitHub
34 changed files with 1322 additions and 243 deletions

View File

@@ -1605,6 +1605,15 @@ export const api = {
undefined,
() => [["routes"]],
),
/** Irreversible purge — refused while any train schedule uses the route. */
purge: endpoint<string, void>(
"routes",
"purge",
(id) => routesService.purge(id).then(() => undefined),
undefined,
() => [["routes"]],
),
},
stations: {
@@ -2194,6 +2203,15 @@ export const api = {
undefined,
({ slug }) => [QUERY_KEYS.FLEET.list(slug)],
),
/** Irreversible purge — locomotives and wagons only. */
purge: endpoint<{ slug: FleetResourceSlug; id: string }, unknown>(
"fleet",
"purge",
({ slug, id }) => fleetService.purge(slug, id),
undefined,
({ slug }) => [QUERY_KEYS.FLEET.list(slug)],
),
},
truckTypes: {

View File

@@ -5,8 +5,11 @@ import type { ApiResponse } from "@/types/apiResponse";
const BASE = URL_CONSTANTS.EXCHANGE_SETTINGS.BASE;
/** Where the rate the API last served came from. */
export type ExchangeRateSource = "live" | "cache" | "stored" | "default";
/**
* Where the rate the API last served came from. `live` means CBE answered;
* `stored` means it is failing and the fallback is in use.
*/
export type ExchangeRateSource = "live" | "stored";
/** Health of the CBE exchange-rate feed. */
export interface ExchangeFeedStatus {

View File

@@ -77,6 +77,21 @@ const removeHandlers: Record<FleetResourceSlug, (id: string) => Promise<unknown>
drivers: (id) => driversService.delete(id),
};
/**
* Permanent purge, only for the two slugs that expose it. Everything else stays
* soft-delete/decommission only, so there is deliberately no entry here.
*/
const purgeHandlers: Partial<
Record<FleetResourceSlug, (id: string) => Promise<unknown>>
> = {
locomotives: (id) => locomotivesService.purge(id),
wagons: (id) => wagonService.purge(id),
};
/** True when the slug supports an irreversible purge. */
export const isFleetPurgeable = (slug: FleetResourceSlug) =>
slug in purgeHandlers;
export const fleetService = {
list: (slug: FleetResourceSlug, filters?: FleetListFilters) => listHandlers[slug](filters),
/** Only for slugs in `pagedHandlers` — guard with `isFleetServerPaginated`. */
@@ -89,4 +104,11 @@ export const fleetService = {
update: (slug: FleetResourceSlug, id: string, data: Record<string, unknown>) =>
updateHandlers[slug](id, data),
remove: (slug: FleetResourceSlug, id: string) => removeHandlers[slug](id),
purge: (slug: FleetResourceSlug, id: string) => {
const handler = purgeHandlers[slug];
if (!handler) {
throw new Error(`Fleet resource "${slug}" cannot be permanently deleted`);
}
return handler(id);
},
};

View File

@@ -85,4 +85,7 @@ export const locomotivesService = {
update: (id: string, data: Partial<SaveLocomotivePayload>) =>
apiClient.patch(URL_CONSTANTS.LOCOMOTIVES.BY_ID(id), data),
decommission: (id: string) => apiClient.post(URL_CONSTANTS.LOCOMOTIVES.DECOMMISSION(id), {}),
/** Irreversible purge — the API refuses it while any train references the loco. */
purge: (id: string) =>
apiClient.delete(`${URL_CONSTANTS.LOCOMOTIVES.BY_ID(id)}/permanent`),
};

View File

@@ -101,6 +101,9 @@ export const routesService = {
update: (id: string, data: Partial<SaveRoutePayload>) =>
apiClient.patch(URL_CONSTANTS.ROUTES.BY_ID(id), data),
deactivate: (id: string) => apiClient.delete(URL_CONSTANTS.ROUTES.BY_ID(id)),
/** Irreversible purge — the API refuses it while any train schedule uses the route. */
purge: (id: string) =>
apiClient.delete(`${URL_CONSTANTS.ROUTES.BY_ID(id)}/permanent`),
/** All active yards (page-walked — the yards list API caps pageSize at 100). */
getYards: async (): Promise<YardRef[]> => {
const rows = await ruleEngineService.listAll("yards", { isActive: true });

View File

@@ -123,6 +123,8 @@ export const wagonService = {
create: (data: Partial<Wagon>) => apiClient.post('/wagons', data),
update: (id: string, data: Partial<Wagon>) => apiClient.patch(`/wagons/${id}`, data),
delete: (id: string) => apiClient.delete(`/wagons/${id}`),
/** Irreversible purge — the API refuses it when the wagon has any history. */
purge: (id: string) => apiClient.delete(`/wagons/${id}/permanent`),
/** Relocate many wagons to one yard in a single call (writes movement ledger). */
bulkTransfer: (wagonIds: string[], toYardId: string) =>
apiClient.post<{ moved: number }>('/wagons/bulk-transfer', { wagonIds, toYardId }),