feat(freight-backoffice): render reports from the server-driven catalog

Drop reportConfigs.ts (per-report FE config duplicating the backend) and
the chart-drawing ReportPage. Replace with ReportView: one engine that
renders any report the GET /reports catalog describes — filters, KPI
strip, sortable/paginated DataTable, xlsx/pdf export via blob download.
ReportSection embeds a report inline on any page, scoped by idKey, and
renders nothing if the caller lacks that report's permission.

Sidebar Reports submenu is now built from the live catalog
(sidebar-sections.tsx + App.tsx) instead of a hand-listed key — no FE
edit needed to add or hide a report.
This commit is contained in:
Nathnael
2026-08-13 07:52:29 +00:00
parent b7583df426
commit 081b9945cb
13 changed files with 586 additions and 1050 deletions

View File

@@ -174,7 +174,7 @@ import {
} from "./locomotives.service";
import { overviewService } from "./overview.service";
import { reportsService } from "./reports.service";
import type { ReportQueryInput, ReportResult } from "@/types/reports";
import type { ReportCatalogEntry, ReportRunParams, ReportRunResult } from "@/types/reports";
import {
paymentsService,
type PaginatedPayments,
@@ -3030,7 +3030,10 @@ export const api = {
},
reports: {
run: endpoint<ReportQueryInput, ReportResult>(
catalog: endpoint<void, ReportCatalogEntry[]>("reports", "catalog", () =>
reportsService.catalog(),
),
run: endpoint<ReportRunParams, ReportRunResult>(
"reports",
"run",
(input) => reportsService.run(input),

View File

@@ -1,14 +1,31 @@
import { api as client } from "../auth/http";
import { unwrap } from "@/utils/endpoint";
import { URL_CONSTANTS } from "@/constants/URLS";
import type { ReportQueryInput, ReportResult } from "@/types/reports";
import type { ReportCatalogEntry, ReportRunParams, ReportRunResult } from "@/types/reports";
const R = URL_CONSTANTS.REPORTS;
export const reportsService = {
run: async ({ key, ...params }: ReportQueryInput): Promise<ReportResult> => {
const response = await client.get<ReportResult>(
URL_CONSTANTS.REPORTS.RUN(key),
{ params },
);
catalog: async (): Promise<ReportCatalogEntry[]> => {
const response = await client.get(R.CATALOG);
return unwrap(response.data);
},
run: async ({ key, ...params }: ReportRunParams): Promise<ReportRunResult> => {
const response = await client.get(R.RUN(key), { params });
return unwrap(response.data);
},
/** Streams the export file as a blob — caller triggers the browser save. */
download: async (
key: string,
format: "xlsx" | "pdf",
params: Omit<ReportRunParams, "key" | "page" | "pageSize">,
): Promise<Blob> => {
const response = await client.get(R.EXPORT(key), {
params: { ...params, format },
responseType: "blob",
});
return response.data as Blob;
},
};