Merge pull request #171 from Tria-plc/freight_feature/priority

Freight feature/priority
This commit is contained in:
marshal
2026-06-16 13:34:18 +03:00
committed by GitHub
28 changed files with 495 additions and 333 deletions

View File

@@ -179,10 +179,10 @@ const buildSidebarSections = (demoItems: SidebarItem[]): SidebarSection[] => [
icon: <Boxes />,
children: [
...getCategorySidebarChildren("configuration"),
{
label: "Train scheduling rules",
href: "/dashboard/configuration/train-scheduling-rules",
},
// {
// label: "Train scheduling rules",
// href: "/dashboard/configuration/train-scheduling-rules",
// },
],
},
{
@@ -320,7 +320,7 @@ const App = () => {
<Route
path="rules"
element={<Navigate to="/dashboard/rules/priority-rules" replace />}
element={<Navigate to="/dashboard/rules/priority-configs" replace />}
/>
<Route path="rules/:resource" element={<RuleEngineResourcePage />} />

View File

@@ -165,7 +165,17 @@ const RuleEngineFormDialog = ({
}
}, [open, fields, initialRecord]);
const formRows = useMemo(() => buildFormRows(fields), [fields]);
const visibleFields = useMemo(
() =>
fields.filter(
(field) =>
!field.hideWhen ||
!field.hideWhen.equals.includes(String(values[field.hideWhen.field] ?? "")),
),
[fields, values],
);
const formRows = useMemo(() => buildFormRows(visibleFields), [visibleFields]);
const setField = (name: string, value: unknown) => {
setValues((current) => ({ ...current, [name]: value }));
@@ -175,7 +185,7 @@ const RuleEngineFormDialog = ({
event.preventDefault();
const payload: Record<string, unknown> = {};
for (const field of fields) {
for (const field of visibleFields) {
const raw = values[field.name];
if (field.type === "number") {
if (raw === "" || raw === undefined) continue;

View File

@@ -212,8 +212,8 @@ export const URL_CONSTANTS = {
WAGON_TYPES: "/wagon-types",
WAGON_TYPE_BY_ID: (id: string) => `/wagon-types/${id}`,
PRIORITY_RULES: "/priority-rules",
PRIORITY_RULE_BY_ID: (id: string) => `/priority-rules/${id}`,
PRIORITY_CONFIGS: "/priority-configs",
PRIORITY_CONFIG_BY_ID: (id: string) => `/priority-configs/${id}`,
SERVICE_TYPES: "/service-types",
SERVICE_TYPE_BY_ID: (id: string) => `/service-types/${id}`,

View File

@@ -296,8 +296,13 @@ const RuleEngineResourcePage = () => {
};
const handleFormSubmit = (values: Record<string, unknown>) => {
const payload =
config.slug === "rates" ? { ...values, currency: "USD" } : values;
let payload = values;
if (config.slug === "rates") {
payload = { ...values, currency: "USD" };
} else if (config.slug === "priority-configs") {
// Label is required by the backend but hidden in the UI for now.
payload = { ...values, label: String(Date.now()) };
}
if (editing?.id) {
update.mutate(

View File

@@ -34,6 +34,8 @@ export interface FormFieldDef {
optional?: boolean;
options?: { label: string; value: string }[];
placeholder?: string;
/** Hide this field when another field currently equals one of these values. */
hideWhen?: { field: string; equals: string[] };
}
export interface RuleEngineOrderConfig {
@@ -110,7 +112,15 @@ const RATE_UNITS = ["PER_WAGON", "PER_TON", "PER_CONTAINER", "PER_KM", "FLAT"].m
value: v,
}));
const CURRENCIES = [{ label: "USD", value: "USD" }];
const CURRENCIES = [
{ label: "USD", value: "USD" },
{ label: "ETB", value: "ETB" },
];
const PRIORITY_CONFIG_TYPES = [
{ label: "Wagon count", value: "WAGON" },
{ label: "Payment currency", value: "CURRENCY" },
];
const codeColumn = (key: string, header = "Code"): ResourceColumn => ({
id: key,
@@ -226,29 +236,41 @@ export const RULE_ENGINE_RESOURCES: RuleEngineResourceConfig[] = [
],
},
{
slug: "priority-rules",
slug: "priority-configs",
label: "Priority Rules",
category: "rules",
subtitle: "Booking priority scoring rules",
subtitle: "Wagon-count and payment-currency scoring rules",
searchPlaceholder: "Search priority rules...",
orderConfig: { field: "displayOrder", label: "Display order" },
columns: [
codeColumn("code"),
{ id: "label", header: "Label", accessorKey: "label" },
{ id: "score", header: "Score", accessorKey: "score", format: "number" },
{ id: "conditionCurrency", header: "Currency", accessorKey: "conditionCurrency" },
{ id: "type", header: "Type", accessorKey: "type" },
{ id: "currency", header: "Currency", accessorKey: "currency" },
{ id: "minWagonCount", header: "Min wagons", accessorKey: "minWagonCount", format: "number" },
{ id: "maxWagonCount", header: "Max wagons", accessorKey: "maxWagonCount", format: "number" },
{ id: "scorePoints", header: "Points", accessorKey: "scorePoints", format: "number" },
activeColumn,
],
formFields: [
{ name: "label", label: "Label", type: "text", required: true },
{ name: "score", label: "Score", type: "number", required: true },
{
name: "conditionCurrency",
label: "Condition currency",
name: "type",
label: "Type",
type: "select",
required: true,
options: PRIORITY_CONFIG_TYPES,
placeholder: "Wagon count or payment currency",
},
{
name: "currency",
label: "Currency",
type: "select",
optional: true,
options: [{ label: "Any", value: RULE_ENGINE_SELECT_NONE }, ...CURRENCIES],
placeholder: "Any currency (optional)",
options: [{ label: "None", value: RULE_ENGINE_SELECT_NONE }, ...CURRENCIES],
placeholder: "Select a currency",
hideWhen: { field: "type", equals: ["WAGON"] },
},
{ name: "minWagonCount", label: "Min wagon count", type: "number", required: true },
{ name: "maxWagonCount", label: "Max wagon count", type: "number", required: true },
{ name: "scorePoints", label: "Score points", type: "number", required: true },
{ name: "isActive", label: "Active", type: "boolean" },
],
},
@@ -500,7 +522,7 @@ export const getCategorySidebarChildren = (
}));
export const DEFAULT_CONFIGURATION_SLUG: RuleEngineResourceSlug = "cargo-types";
export const DEFAULT_RULES_SLUG: RuleEngineResourceSlug = "priority-rules";
export const DEFAULT_RULES_SLUG: RuleEngineResourceSlug = "priority-configs";
/** @deprecated Use DEFAULT_CONFIGURATION_SLUG */
export const DEFAULT_RULE_ENGINE_SLUG = DEFAULT_CONFIGURATION_SLUG;

View File

@@ -28,7 +28,7 @@ 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-rules": URL_CONSTANTS.RULE_ENGINE.PRIORITY_RULES,
"priority-configs": URL_CONSTANTS.RULE_ENGINE.PRIORITY_CONFIGS,
"service-types": URL_CONSTANTS.RULE_ENGINE.SERVICE_TYPES,
"surcharge-types": URL_CONSTANTS.RULE_ENGINE.SURCHARGE_TYPES,
"weight-limit-rules": URL_CONSTANTS.RULE_ENGINE.WEIGHT_LIMIT_RULES,
@@ -46,8 +46,8 @@ const byIdPath = (resource: RuleEngineResourceSlug, id: string): string => {
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-rules":
return URL_CONSTANTS.RULE_ENGINE.PRIORITY_RULE_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 "surcharge-types":

View File

@@ -2,7 +2,7 @@ export type RuleEngineResourceSlug =
| "cargo-types"
| "container-types"
| "wagon-types"
| "priority-rules"
| "priority-configs"
| "service-types"
| "surcharge-types"
| "weight-limit-rules"