mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-09-08 01:55:41 +00:00
Merge branch 'dev' into dj-franc
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import type { Freight, PaginatedResponse } from "@edr/types";
|
||||
import type { Freight, PaginatedResponse, Publication } from "@edr/types";
|
||||
|
||||
import { QUERY_KEYS } from "@/constants/QUERY_KEYS";
|
||||
import type { FleetResourceSlug } from "@/pages/fleet/config/resources";
|
||||
@@ -191,6 +191,7 @@ import type { EimsInvoiceStatusView, EimsModeOfPayment, EimsReceiptView, EimsVer
|
||||
import { invoicesService } from "./invoices.service";
|
||||
import { dropdownSettingsService } from "./dropdownSettings.service";
|
||||
import { fileUploadSettingsService } from "./fileUploadSettings.service";
|
||||
import { publicationsService, type UpdatePublicationPayload } from "./publications.service";
|
||||
import {
|
||||
fleetService,
|
||||
type FleetListFilters,
|
||||
@@ -2934,6 +2935,52 @@ export const api = {
|
||||
),
|
||||
},
|
||||
|
||||
publications: {
|
||||
list: endpoint<void, Publication[]>(
|
||||
"publications",
|
||||
"list",
|
||||
publicationsService.list,
|
||||
),
|
||||
|
||||
create: endpoint<
|
||||
{ file: File; meta: UpdatePublicationPayload & { title: string }; onProgress?: (percent: number | null) => void },
|
||||
Publication
|
||||
>(
|
||||
"publications",
|
||||
"create",
|
||||
({ file, meta, onProgress }) => publicationsService.create(file, meta, onProgress),
|
||||
undefined,
|
||||
() => [["publications"]],
|
||||
),
|
||||
|
||||
update: endpoint<{ id: string; dto: UpdatePublicationPayload }, Publication>(
|
||||
"publications",
|
||||
"update",
|
||||
({ id, dto }) => publicationsService.update(id, dto),
|
||||
undefined,
|
||||
() => [["publications"]],
|
||||
),
|
||||
|
||||
replaceFile: endpoint<
|
||||
{ id: string; file: File; onProgress?: (percent: number | null) => void },
|
||||
Publication
|
||||
>(
|
||||
"publications",
|
||||
"replaceFile",
|
||||
({ id, file, onProgress }) => publicationsService.replaceFile(id, file, onProgress),
|
||||
undefined,
|
||||
() => [["publications"]],
|
||||
),
|
||||
|
||||
remove: endpoint<{ id: string }, void>(
|
||||
"publications",
|
||||
"remove",
|
||||
({ id }) => publicationsService.remove(id),
|
||||
undefined,
|
||||
() => [["publications"]],
|
||||
),
|
||||
},
|
||||
|
||||
dropdownSettings: {
|
||||
list: endpoint<void, DropdownSetting[]>(
|
||||
"dropdown-settings",
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
import type { Publication } from "@edr/types";
|
||||
|
||||
import { api as client } from "../auth/http";
|
||||
|
||||
const BASE = "/publications";
|
||||
|
||||
export interface UpdatePublicationPayload {
|
||||
title?: string;
|
||||
description?: string;
|
||||
category?: string;
|
||||
sortOrder?: number;
|
||||
published?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* The freight portal's public document library (/publications), managed here.
|
||||
* Every write is multipart because create/replaceFile carry a real file — the
|
||||
* client's response interceptor already unwraps the `{ success, data }`
|
||||
* envelope, so each method stays a one-liner.
|
||||
*/
|
||||
export const publicationsService = {
|
||||
async list(): Promise<Publication[]> {
|
||||
const { data } = await client.get<Publication[]>(`${BASE}/admin`);
|
||||
return data;
|
||||
},
|
||||
|
||||
async create(
|
||||
file: File,
|
||||
meta: UpdatePublicationPayload & { title: string },
|
||||
onProgress?: (percent: number | null) => void,
|
||||
): Promise<Publication> {
|
||||
const form = new FormData();
|
||||
form.append("file", file);
|
||||
Object.entries(meta).forEach(([key, value]) => {
|
||||
if (value !== undefined) form.append(key, String(value));
|
||||
});
|
||||
|
||||
const { data } = await client.post<Publication>(BASE, form, {
|
||||
timeout: 2 * 60 * 1000,
|
||||
onUploadProgress: (event) =>
|
||||
onProgress?.(
|
||||
event.total ? Math.round((event.loaded / event.total) * 100) : null,
|
||||
),
|
||||
});
|
||||
return data;
|
||||
},
|
||||
|
||||
async update(id: string, dto: UpdatePublicationPayload): Promise<Publication> {
|
||||
const { data } = await client.patch<Publication>(`${BASE}/${id}`, dto);
|
||||
return data;
|
||||
},
|
||||
|
||||
async replaceFile(
|
||||
id: string,
|
||||
file: File,
|
||||
onProgress?: (percent: number | null) => void,
|
||||
): Promise<Publication> {
|
||||
const form = new FormData();
|
||||
form.append("file", file);
|
||||
|
||||
const { data } = await client.post<Publication>(`${BASE}/${id}/file`, form, {
|
||||
timeout: 2 * 60 * 1000,
|
||||
onUploadProgress: (event) =>
|
||||
onProgress?.(
|
||||
event.total ? Math.round((event.loaded / event.total) * 100) : null,
|
||||
),
|
||||
});
|
||||
return data;
|
||||
},
|
||||
|
||||
async remove(id: string): Promise<void> {
|
||||
await client.delete(`${BASE}/${id}`);
|
||||
},
|
||||
};
|
||||
@@ -756,6 +756,15 @@ export const trainSchedulingService = {
|
||||
return response.data;
|
||||
},
|
||||
|
||||
/** The schedule detail page's wagon-list Excel export. */
|
||||
downloadScheduleWagonsWorkbook: async (scheduleId: string): Promise<Blob> => {
|
||||
const response = await client.get(
|
||||
URL_CONSTANTS.TRAIN_SCHEDULING.SCHEDULE_WAGONS_EXPORT(scheduleId),
|
||||
{ responseType: "blob" },
|
||||
);
|
||||
return response.data as Blob;
|
||||
},
|
||||
|
||||
downloadIntercityMarshallingDocument: async (
|
||||
scheduleId: string,
|
||||
): Promise<Blob> => {
|
||||
|
||||
@@ -29,6 +29,12 @@ export interface Wagon {
|
||||
/** Latest status-log flip to MAINTENANCE / to AVAILABLE (list endpoint only). */
|
||||
lastMaintenanceAt?: string | null;
|
||||
lastAvailableAt?: string | null;
|
||||
/** Newest wagon_movements arrival — the idle clock's start (list endpoint only). */
|
||||
lastMovedAt?: string | null;
|
||||
/** Loaded / empty / total moves inside `statsWindowDays` (list endpoint only). */
|
||||
loadsInWindow?: number;
|
||||
movesInWindow?: number;
|
||||
emptyMovesInWindow?: number;
|
||||
currentYardId: string | null;
|
||||
currentYard?: { id: string; label?: string; code?: string } | null;
|
||||
/** Odd EXPORT run (Ethiopia → Djibouti); null when the wagon is not on a run. */
|
||||
@@ -55,6 +61,8 @@ export interface WagonListFilters {
|
||||
* the latest status-log flip to MAINTENANCE, not a stored column. */
|
||||
maintenanceFrom?: string;
|
||||
maintenanceTo?: string;
|
||||
/** Window (days) the per-row load/move counts cover. Does not filter rows. */
|
||||
statsWindowDays?: number;
|
||||
/** Only read by `getPaged`. */
|
||||
page?: number;
|
||||
pageSize?: number;
|
||||
@@ -73,6 +81,8 @@ const wagonListQuery = (filters: WagonListFilters): string => {
|
||||
if (filters.createdTo) params.set('createdTo', filters.createdTo);
|
||||
if (filters.maintenanceFrom) params.set('maintenanceFrom', filters.maintenanceFrom);
|
||||
if (filters.maintenanceTo) params.set('maintenanceTo', filters.maintenanceTo);
|
||||
if (filters.statsWindowDays)
|
||||
params.set('statsWindowDays', String(filters.statsWindowDays));
|
||||
if (filters.page) params.set('page', String(filters.page));
|
||||
if (filters.pageSize) params.set('pageSize', String(filters.pageSize));
|
||||
const qs = params.toString();
|
||||
@@ -101,6 +111,8 @@ export interface WagonMovementRecord {
|
||||
note: string | null;
|
||||
createdAt: string;
|
||||
wagon?: { id: string; wagonNumber?: string } | null;
|
||||
/** The booking's human reference, joined at read time. Null when unloaded. */
|
||||
bookingReference?: string | null;
|
||||
}
|
||||
|
||||
/** One row of the wagon status audit trail. Returned newest first by the API. */
|
||||
|
||||
Reference in New Issue
Block a user