mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-09-07 16:35:42 +00:00
fix issue
This commit is contained in:
@@ -313,7 +313,9 @@ const RuleEngineResourcePage = () => {
|
||||
(f) =>
|
||||
f.name === "originYardId" ||
|
||||
f.name === "fromYardId" ||
|
||||
f.name === "toYardId",
|
||||
f.name === "toYardId" ||
|
||||
// Operational targets pick a station by yard code.
|
||||
f.name === "dimensionKey",
|
||||
),
|
||||
);
|
||||
const { data: yardOptions, isLoading: yardOptionsLoading } =
|
||||
@@ -475,6 +477,20 @@ const RuleEngineResourcePage = () => {
|
||||
.map(({ label, value }) => ({ label, value })),
|
||||
};
|
||||
}
|
||||
// An operational target's key is a category, a container class, or a
|
||||
// station's YARD CODE — never a yard id, because the reports match it
|
||||
// against what their classification CASE emits.
|
||||
if (field.name === "dimensionKey") {
|
||||
const staticOptions = field.optionsFromValues;
|
||||
return {
|
||||
...field,
|
||||
type: "select" as const,
|
||||
optionsFromValues: (values: Record<string, unknown>) =>
|
||||
String(values.dimension ?? "") === "station"
|
||||
? (yardOptions ?? []).map(({ label, code }) => ({ label, value: code }))
|
||||
: (staticOptions?.(values) ?? []),
|
||||
};
|
||||
}
|
||||
if (field.name === "originYardId" || field.name === "destinationYardId") {
|
||||
const end = field.name === "originYardId" ? "origin" : "destination";
|
||||
return {
|
||||
|
||||
@@ -142,6 +142,37 @@ const TRADE_DIRECTIONS = [
|
||||
{ label: "Both", value: "BOTH" },
|
||||
];
|
||||
|
||||
/**
|
||||
* The cargo categories and container classes an operational target may be
|
||||
* keyed on.
|
||||
*
|
||||
* Mirrors CARGO_CATEGORIES / CONTAINER_CLASSES in the API's
|
||||
* `modules/reports/operations-classification.ts`, which is the source of truth:
|
||||
* a report matches a target by this exact key, so a value here that the API
|
||||
* does not emit is a plan the report will never find. The API spec
|
||||
* `operations-classification.spec.ts` guards the API side of the pair.
|
||||
*/
|
||||
export const OPERATIONS_CARGO_CATEGORIES = [
|
||||
{ label: "Multimodal container import", value: "CONTAINER_IMPORT_MULTIMODAL" },
|
||||
{ label: "Unimodal container import", value: "CONTAINER_IMPORT_UNIMODAL" },
|
||||
{ label: "Export container", value: "CONTAINER_EXPORT" },
|
||||
{ label: "Empty container", value: "EMPTY_CONTAINER" },
|
||||
{ label: "Fertilizer", value: "FERTILIZER" },
|
||||
{ label: "RoRo", value: "RORO" },
|
||||
{ label: "Break bulk", value: "BREAK_BULK" },
|
||||
{ label: "Sand", value: "SAND" },
|
||||
{ label: "Bulk", value: "BULK" },
|
||||
{ label: "Other imports", value: "OTHER_IMPORT" },
|
||||
{ label: "Other export cargo", value: "OTHER_EXPORT" },
|
||||
];
|
||||
|
||||
export const OPERATIONS_CONTAINER_CLASSES = [
|
||||
{ label: "Multimodal container import", value: "CONTAINER_IMPORT_MULTIMODAL" },
|
||||
{ label: "Unimodal container import", value: "CONTAINER_IMPORT_UNIMODAL" },
|
||||
{ label: "Full export container", value: "CONTAINER_EXPORT" },
|
||||
{ label: "Empty container return", value: "EMPTY_CONTAINER_RETURN" },
|
||||
];
|
||||
|
||||
// Mirrors the YardCountry enum in @edr/types — the only two countries on the line.
|
||||
const YARD_COUNTRIES = [
|
||||
{ label: "Ethiopia", value: "Ethiopia" },
|
||||
@@ -478,6 +509,14 @@ export const RULE_ENGINE_RESOURCES: RuleEngineResourceConfig[] = [
|
||||
optional: true,
|
||||
placeholder: "Select parent cargo type (optional)",
|
||||
},
|
||||
{
|
||||
name: "fullTrainsetWagons",
|
||||
label: "Wagons in a full trainset",
|
||||
type: "number",
|
||||
optional: true,
|
||||
description:
|
||||
"What the Trainset Performance report divides loaded wagons by — 37 for vehicles, 22 for sand. Leave blank to use the default in Operating standards.",
|
||||
},
|
||||
{ name: "requiresDirectorApproval", label: "Requires director approval", type: "boolean" },
|
||||
{ name: "hasLashing", label: "Charge lashing fee", type: "boolean" },
|
||||
{
|
||||
@@ -639,6 +678,108 @@ export const RULE_ENGINE_RESOURCES: RuleEngineResourceConfig[] = [
|
||||
{ name: "isActive", label: "Active", type: "boolean", description: "Off suspends the officer regardless of the validity window" },
|
||||
],
|
||||
},
|
||||
{
|
||||
slug: "operations-targets",
|
||||
label: "Operational Targets",
|
||||
category: "configuration",
|
||||
subtitle:
|
||||
"Planned TEU, trainsets and tonnage per period — the Plan column in the operations reports",
|
||||
searchPlaceholder: "Search by category, station or note...",
|
||||
supportsSearch: true,
|
||||
cardTitleKey: "appliesToLabel",
|
||||
cardSubtitleKey: "periodStart",
|
||||
columns: [
|
||||
// The *Label columns are readable twins the API sends alongside the stored
|
||||
// codes (see OperationsTargetsService.toRow) — the codes themselves are
|
||||
// enums the reports join on and stay out of the grid.
|
||||
{ id: "periodStart", header: "Period start", accessorKey: "periodStart", format: "date" },
|
||||
{ id: "periodLabel", header: "Period", accessorKey: "periodLabel" },
|
||||
{ id: "metricLabel", header: "Metric", accessorKey: "metricLabel" },
|
||||
{ id: "dimensionLabel", header: "Plan by", accessorKey: "dimensionLabel" },
|
||||
{ id: "appliesToLabel", header: "Applies to", accessorKey: "appliesToLabel" },
|
||||
{ id: "cargoCategoryLabel", header: "Cargo category", accessorKey: "cargoCategoryLabel" },
|
||||
{ id: "plannedValue", header: "Plan", accessorKey: "plannedValue", format: "number" },
|
||||
],
|
||||
formFields: [
|
||||
{
|
||||
name: "metric",
|
||||
label: "Metric",
|
||||
type: "select",
|
||||
required: true,
|
||||
options: [
|
||||
{ label: "TEU", value: "TEU" },
|
||||
{ label: "Trainsets", value: "TRAINSET" },
|
||||
{ label: "Volume (tons)", value: "VOLUME_TONS" },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "periodType",
|
||||
label: "Period",
|
||||
type: "select",
|
||||
required: true,
|
||||
options: [
|
||||
{ label: "Weekly", value: "week" },
|
||||
{ label: "Monthly", value: "month" },
|
||||
{ label: "Quarterly", value: "quarter" },
|
||||
{ label: "Yearly", value: "year" },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "periodStart",
|
||||
label: "Period start",
|
||||
type: "date",
|
||||
required: true,
|
||||
description: "Any date inside the period — snapped to its start on save.",
|
||||
},
|
||||
{
|
||||
name: "dimension",
|
||||
label: "Plan by",
|
||||
type: "select",
|
||||
required: true,
|
||||
options: [
|
||||
{ label: "Cargo category", value: "cargo_category" },
|
||||
{ label: "Station", value: "station" },
|
||||
{ label: "Container class", value: "container_class" },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "dimensionKey",
|
||||
label: "Applies to",
|
||||
type: "select",
|
||||
required: true,
|
||||
placeholder: "Select",
|
||||
// The valid keys depend on the chosen dimension, and must match what the
|
||||
// reports emit exactly — a mismatch here is a target the report never
|
||||
// finds. Station options are the live yard codes, injected by
|
||||
// RuleEngineResourcePage.
|
||||
optionsFromValues: (values) => {
|
||||
const dimension = String(values.dimension ?? "");
|
||||
if (dimension === "container_class") return OPERATIONS_CONTAINER_CLASSES;
|
||||
if (dimension === "station") return [];
|
||||
return OPERATIONS_CARGO_CATEGORIES;
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "cargoCategory",
|
||||
label: "Cargo category",
|
||||
type: "select",
|
||||
required: true,
|
||||
// A station's plan is per station AND per cargo type — the OCC report
|
||||
// plans Nagad-Mojo container and Nagad-Mojo fertilizer separately. The
|
||||
// other two dimensions already carry the category in the key above.
|
||||
showWhen: { field: "dimension", equals: ["station"] },
|
||||
options: OPERATIONS_CARGO_CATEGORIES,
|
||||
},
|
||||
{
|
||||
name: "plannedValue",
|
||||
label: "Planned value",
|
||||
type: "number",
|
||||
required: true,
|
||||
description: "TEU, trainsets or tonnes — whichever the metric above is.",
|
||||
},
|
||||
{ name: "note", label: "Note", type: "text", optional: true },
|
||||
],
|
||||
},
|
||||
{
|
||||
slug: "yard-distances",
|
||||
label: "Yard Distances",
|
||||
@@ -652,6 +793,7 @@ export const RULE_ENGINE_RESOURCES: RuleEngineResourceConfig[] = [
|
||||
{ id: "fromYardLabel", header: "From yard", accessorKey: "fromYardLabel" },
|
||||
{ id: "toYardLabel", header: "To yard", accessorKey: "toYardLabel" },
|
||||
{ id: "distanceKm", header: "Distance (km)", accessorKey: "distanceKm", format: "number" },
|
||||
{ id: "standardHours", header: "Standard (hrs)", accessorKey: "standardHours", format: "number" },
|
||||
],
|
||||
formFields: [
|
||||
// Options injected at render from useYardOptions (RuleEngineResourcePage).
|
||||
@@ -665,6 +807,14 @@ export const RULE_ENGINE_RESOURCES: RuleEngineResourceConfig[] = [
|
||||
description:
|
||||
"Symmetric — one entry covers both directions. Route segments between these yards use this value.",
|
||||
},
|
||||
{
|
||||
name: "standardHours",
|
||||
label: "Standard running time (hrs)",
|
||||
type: "number",
|
||||
optional: true,
|
||||
description:
|
||||
"What the Train Delays report judges this leg against — 21h Negad to GMP, 20h to Adama, 20.5h to Modjo, 22h to Sebeta. Leave blank to use the default in Operating standards.",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user