@fleetbase/fleetops-engine 0.6.38 → 0.6.39
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/addon/components/activity/form.hbs +2 -12
- package/addon/components/activity/logic-builder.hbs +1 -1
- package/addon/components/custom-entity/form.hbs +4 -20
- package/addon/components/device/form.hbs +3 -15
- package/addon/components/device/panel-header.hbs +13 -2
- package/addon/components/driver/details.hbs +33 -2
- package/addon/components/driver/form.hbs +42 -0
- package/addon/components/driver/panel-header.hbs +8 -1
- package/addon/components/driver/schedule.hbs +115 -76
- package/addon/components/driver/schedule.js +349 -157
- package/addon/components/driver-onboard-settings.hbs +2 -8
- package/addon/components/entity-field-editing-settings.hbs +2 -5
- package/addon/components/equipment/card.hbs +49 -0
- package/addon/components/equipment/card.js +6 -0
- package/addon/components/equipment/details.hbs +83 -44
- package/addon/components/equipment/form.hbs +111 -41
- package/addon/components/equipment/form.js +78 -10
- package/addon/components/equipment/panel-header.hbs +36 -0
- package/addon/components/equipment/panel-header.js +2 -0
- package/addon/components/fleet/driver-listing.hbs +3 -1
- package/addon/components/fleet/vehicle-listing.hbs +4 -7
- package/addon/components/fleet-panel/vehicle-listing.hbs +1 -6
- package/addon/components/fuel-report/form.hbs +1 -5
- package/addon/components/layout/fleet-ops-sidebar.hbs +40 -36
- package/addon/components/layout/fleet-ops-sidebar.js +61 -10
- package/addon/components/maintenance/cost-panel.hbs +176 -0
- package/addon/components/maintenance/cost-panel.js +241 -0
- package/addon/components/maintenance/details.hbs +123 -60
- package/addon/components/maintenance/form.hbs +138 -78
- package/addon/components/maintenance/form.js +131 -6
- package/addon/components/maintenance/panel-header.hbs +31 -0
- package/addon/components/maintenance/panel-header.js +2 -0
- package/addon/components/maintenance-schedule/details.hbs +260 -0
- package/addon/components/maintenance-schedule/details.js +158 -0
- package/addon/components/maintenance-schedule/form.hbs +287 -0
- package/addon/components/maintenance-schedule/form.js +199 -0
- package/addon/components/map/container.hbs +1 -1
- package/addon/components/map/drawer/device-event-listing.hbs +1 -1
- package/addon/components/map/drawer/driver-listing.hbs +1 -6
- package/addon/components/map/drawer/place-listing.hbs +1 -6
- package/addon/components/map/drawer/vehicle-listing.hbs +1 -6
- package/addon/components/map/drawer.hbs +8 -1
- package/addon/components/map/leaflet-live-map.hbs +2 -1
- package/addon/components/map/toolbar/visibility-control-panel.hbs +1 -1
- package/addon/components/map/toolbar.hbs +4 -29
- package/addon/components/modals/add-driver-shift.hbs +155 -0
- package/addon/components/modals/add-driver-shift.js +210 -0
- package/addon/components/modals/bulk-assign-orders.hbs +67 -0
- package/addon/components/modals/driver-shift.hbs +43 -0
- package/addon/components/modals/driver-shift.js +56 -0
- package/addon/components/modals/entity-form.hbs +5 -28
- package/addon/components/modals/orchestrator-import.hbs +351 -0
- package/addon/components/modals/orchestrator-import.js +807 -0
- package/addon/components/modals/order-config-new-status.hbs +1 -5
- package/addon/components/modals/scheduling-conflict.hbs +47 -0
- package/addon/components/modals/scheduling-conflict.js +53 -0
- package/addon/components/modals/send-work-order.hbs +91 -0
- package/addon/components/modals/send-work-order.js +3 -0
- package/addon/components/modals/service-area-form.hbs +1 -6
- package/addon/components/modals/set-driver-availability.hbs +50 -0
- package/addon/components/modals/set-driver-availability.js +57 -0
- package/addon/components/modals/update-order-activity.hbs +13 -9
- package/addon/components/modals/user-form.hbs +1 -5
- package/addon/components/modals/vehicle-form.hbs +17 -102
- package/addon/components/modals/vendor-form.hbs +15 -82
- package/addon/components/orchestrator/card-fields-settings.hbs +76 -0
- package/addon/components/orchestrator/card-fields-settings.js +134 -0
- package/addon/components/orchestrator/order-pool.hbs +264 -0
- package/addon/components/orchestrator/order-pool.js +394 -0
- package/addon/components/orchestrator/phase-builder.hbs +162 -0
- package/addon/components/orchestrator/phase-builder.js +162 -0
- package/addon/components/orchestrator/plan-viewer.hbs +278 -0
- package/addon/components/orchestrator/plan-viewer.js +342 -0
- package/addon/components/orchestrator/resource-panel.hbs +301 -0
- package/addon/components/orchestrator/resource-panel.js +106 -0
- package/addon/components/orchestrator-workbench.hbs +318 -0
- package/addon/components/orchestrator-workbench.js +1087 -0
- package/addon/components/order/details/custom-fields.hbs +10 -1
- package/addon/components/order/details/detail.hbs +37 -2
- package/addon/components/order/details/detail.js +0 -1
- package/addon/components/order/details/integrated-vendor-details.hbs +4 -4
- package/addon/components/order/details/notes.hbs +1 -7
- package/addon/components/order/details/route.hbs +1 -5
- package/addon/components/order/form/details.hbs +44 -10
- package/addon/components/order/form/details.js +57 -0
- package/addon/components/order/form/notes.hbs +1 -7
- package/addon/components/order/form/payload.hbs +1 -7
- package/addon/components/order/form/route.hbs +3 -15
- package/addon/components/order/header.hbs +1 -7
- package/addon/components/order/route-editor.hbs +4 -25
- package/addon/components/order/schedule-card.hbs +102 -95
- package/addon/components/order/schedule-card.js +8 -3
- package/addon/components/order-config-manager/details.hbs +2 -10
- package/addon/components/order-config-manager/entities.hbs +1 -7
- package/addon/components/order-config-manager.hbs +1 -6
- package/addon/components/part/card.hbs +49 -0
- package/addon/components/part/card.js +6 -0
- package/addon/components/part/details.hbs +102 -56
- package/addon/components/part/form.hbs +131 -56
- package/addon/components/part/form.js +78 -11
- package/addon/components/part/panel-header.hbs +36 -0
- package/addon/components/part/panel-header.js +2 -0
- package/addon/components/place/form.hbs +1 -7
- package/addon/components/sensor/details.hbs +1 -1
- package/addon/components/sensor/form.hbs +5 -3
- package/addon/components/sensor/panel-header.hbs +8 -1
- package/addon/components/service-area/form.hbs +1 -6
- package/addon/components/service-rate/form.hbs +12 -60
- package/addon/components/telematic/form.hbs +6 -2
- package/addon/components/vehicle/details/maintenance-history.hbs +42 -0
- package/addon/components/vehicle/details/maintenance-history.js +32 -0
- package/addon/components/vehicle/details/schedules.hbs +40 -0
- package/addon/components/vehicle/details/schedules.js +32 -0
- package/addon/components/vehicle/details/work-orders.hbs +42 -0
- package/addon/components/vehicle/details/work-orders.js +32 -0
- package/addon/components/vehicle/details.hbs +30 -0
- package/addon/components/vehicle/form.hbs +39 -0
- package/addon/components/vehicle/panel-header.hbs +19 -49
- package/addon/components/warranty/details.hbs +3 -2
- package/addon/components/warranty/form.hbs +3 -17
- package/addon/components/work-order/details.hbs +135 -40
- package/addon/components/work-order/form.hbs +178 -45
- package/addon/components/work-order/form.js +197 -4
- package/addon/components/work-order/panel-header.hbs +31 -0
- package/addon/components/work-order/panel-header.js +2 -0
- package/addon/controllers/connectivity/devices/index/details.js +1 -1
- package/addon/controllers/maintenance/equipment/index/details/index.js +0 -1
- package/addon/controllers/maintenance/equipment/index/details.js +36 -1
- package/addon/controllers/maintenance/equipment/index/edit.js +56 -1
- package/addon/controllers/maintenance/equipment/index/new.js +32 -1
- package/addon/controllers/maintenance/equipment/index.js +127 -113
- package/addon/controllers/maintenance/maintenances/index/details/index.js +3 -0
- package/addon/controllers/maintenance/maintenances/index/details.js +54 -0
- package/addon/controllers/maintenance/maintenances/index/edit.js +68 -0
- package/addon/controllers/maintenance/maintenances/index/new.js +34 -0
- package/addon/controllers/maintenance/maintenances/index.js +191 -0
- package/addon/controllers/maintenance/parts/index/details/index.js +0 -1
- package/addon/controllers/maintenance/parts/index/details.js +36 -1
- package/addon/controllers/maintenance/parts/index/edit.js +56 -1
- package/addon/controllers/maintenance/parts/index/new.js +32 -1
- package/addon/controllers/maintenance/parts/index.js +135 -113
- package/addon/controllers/maintenance/schedules/index/details.js +115 -0
- package/addon/controllers/maintenance/schedules/index/edit.js +41 -0
- package/addon/controllers/maintenance/schedules/index/new.js +33 -0
- package/addon/controllers/maintenance/schedules/index.js +280 -0
- package/addon/controllers/maintenance/work-orders/index/details.js +41 -1
- package/addon/controllers/maintenance/work-orders/index/edit.js +67 -1
- package/addon/controllers/maintenance/work-orders/index/new.js +43 -1
- package/addon/controllers/maintenance/work-orders/index.js +105 -113
- package/addon/controllers/management/drivers/index/details.js +6 -1
- package/addon/controllers/management/vehicles/index/details.js +65 -0
- package/addon/controllers/management/vehicles/index.js +18 -0
- package/addon/controllers/operations/orchestrator.js +10 -0
- package/addon/controllers/operations/orders/index.js +6 -0
- package/addon/controllers/operations/scheduler/fleet-schedule.js +341 -0
- package/addon/controllers/operations/scheduler/index.js +799 -275
- package/addon/controllers/operations/scheduler.js +21 -0
- package/addon/controllers/settings/orchestrator.js +70 -0
- package/addon/controllers/settings/scheduling.js +155 -0
- package/addon/extension.js +19 -0
- package/addon/instance-initializers/register-vroom-allocation.js +27 -0
- package/addon/models/maintenance-schedule.js +61 -0
- package/addon/routes/maintenance/equipment/index/details.js +27 -1
- package/addon/routes/maintenance/equipment/index/edit.js +27 -1
- package/addon/routes/maintenance/maintenances/index/details/index.js +3 -0
- package/addon/routes/maintenance/maintenances/index/details.js +29 -0
- package/addon/routes/maintenance/maintenances/index/edit.js +29 -0
- package/addon/routes/maintenance/maintenances/index/new.js +3 -0
- package/addon/routes/maintenance/maintenances/index.js +23 -0
- package/addon/routes/maintenance/maintenances.js +3 -0
- package/addon/routes/maintenance/parts/index/details.js +27 -1
- package/addon/routes/maintenance/parts/index/edit.js +27 -1
- package/addon/routes/maintenance/schedules/index/details/index.js +2 -0
- package/addon/routes/maintenance/schedules/index/details/work-orders.js +11 -0
- package/addon/routes/maintenance/schedules/index/details.js +25 -0
- package/addon/routes/maintenance/schedules/index/edit.js +25 -0
- package/addon/routes/maintenance/schedules/index/new.js +2 -0
- package/addon/routes/maintenance/schedules/index.js +21 -0
- package/addon/routes/maintenance/schedules.js +2 -0
- package/addon/routes/maintenance/work-orders/index/details.js +27 -1
- package/addon/routes/maintenance/work-orders/index/edit.js +27 -1
- package/addon/routes/management/vehicles/index/details/maintenance-history.js +3 -0
- package/addon/routes/management/vehicles/index/details/schedules.js +3 -0
- package/addon/routes/management/vehicles/index/details/work-orders.js +3 -0
- package/addon/routes/operations/orchestrator.js +23 -0
- package/addon/routes/operations/scheduler/fleet-schedule.js +28 -0
- package/addon/routes/operations/scheduler/index.js +48 -26
- package/addon/routes/operations/scheduler.js +14 -1
- package/addon/routes/settings/orchestrator.js +27 -0
- package/addon/routes/settings/scheduling.js +3 -0
- package/addon/routes.js +31 -1
- package/addon/services/driver-actions.js +40 -7
- package/addon/services/driver-scheduling.js +4 -1
- package/addon/services/equipment-actions.js +15 -5
- package/addon/services/leaflet-map-manager.js +14 -6
- package/addon/services/maintenance-actions.js +17 -14
- package/addon/services/maintenance-schedule-actions.js +118 -0
- package/addon/services/orchestration-engine-interface.js +49 -0
- package/addon/services/orchestration-engine.js +74 -0
- package/addon/services/order-actions.js +15 -0
- package/addon/services/order-allocation.js +116 -0
- package/addon/services/part-actions.js +12 -2
- package/addon/services/scheduling.js +316 -0
- package/addon/services/vehicle-actions.js +70 -7
- package/addon/services/vroom-allocation-engine.js +45 -0
- package/addon/services/work-order-actions.js +80 -0
- package/addon/styles/fleetops-engine.css +1658 -0
- package/addon/templates/analytics/reports/index/edit.hbs +1 -1
- package/addon/templates/analytics/reports/index/new.hbs +1 -1
- package/addon/templates/application.hbs +6 -1
- package/addon/templates/connectivity/devices/index/details/events.hbs +0 -1
- package/addon/templates/connectivity/devices.hbs +0 -1
- package/addon/templates/connectivity/events/index/details.hbs +0 -1
- package/addon/templates/connectivity/events.hbs +0 -1
- package/addon/templates/connectivity/sensors.hbs +0 -1
- package/addon/templates/connectivity/telematics/index/details/devices.hbs +0 -1
- package/addon/templates/connectivity/telematics/index/details/events.hbs +0 -1
- package/addon/templates/connectivity/telematics/index/details/sensors.hbs +0 -1
- package/addon/templates/connectivity/telematics.hbs +0 -1
- package/addon/templates/connectivity/tracking.hbs +0 -1
- package/addon/templates/connectivity.hbs +0 -1
- package/addon/templates/maintenance/equipment/index/details/index.hbs +1 -2
- package/addon/templates/maintenance/equipment/index/details.hbs +15 -2
- package/addon/templates/maintenance/equipment/index/edit.hbs +12 -2
- package/addon/templates/maintenance/equipment/index/new.hbs +1 -2
- package/addon/templates/maintenance/equipment/index.hbs +48 -13
- package/addon/templates/maintenance/equipment.hbs +0 -1
- package/addon/templates/maintenance/maintenances/index/details/index.hbs +1 -0
- package/addon/templates/maintenance/maintenances/index/details.hbs +15 -0
- package/addon/templates/maintenance/maintenances/index/edit.hbs +12 -0
- package/addon/templates/maintenance/maintenances/index/new.hbs +11 -0
- package/addon/templates/maintenance/maintenances/index.hbs +14 -0
- package/addon/templates/maintenance/maintenances.hbs +1 -0
- package/addon/templates/maintenance/parts/index/details/index.hbs +1 -2
- package/addon/templates/maintenance/parts/index/details.hbs +15 -2
- package/addon/templates/maintenance/parts/index/edit.hbs +12 -2
- package/addon/templates/maintenance/parts/index/new.hbs +1 -2
- package/addon/templates/maintenance/parts/index.hbs +48 -13
- package/addon/templates/maintenance/parts.hbs +0 -1
- package/addon/templates/maintenance/schedules/index/details/index.hbs +1 -0
- package/addon/templates/maintenance/schedules/index/details/work-orders.hbs +39 -0
- package/addon/templates/maintenance/schedules/index/details.hbs +14 -0
- package/addon/templates/maintenance/schedules/index/edit.hbs +12 -0
- package/addon/templates/maintenance/schedules/index/new.hbs +11 -0
- package/addon/templates/maintenance/schedules/index.hbs +40 -0
- package/addon/templates/maintenance/schedules.hbs +1 -0
- package/addon/templates/maintenance/work-orders/index/details.hbs +2 -1
- package/addon/templates/maintenance/work-orders/index/edit.hbs +2 -4
- package/addon/templates/maintenance/work-orders/index/new.hbs +1 -2
- package/addon/templates/maintenance/work-orders.hbs +0 -1
- package/addon/templates/management/contacts/customers/edit.hbs +1 -2
- package/addon/templates/management/contacts/customers/new.hbs +1 -2
- package/addon/templates/management/contacts/customers.hbs +1 -1
- package/addon/templates/management/contacts/index/edit.hbs +1 -2
- package/addon/templates/management/contacts/index/new.hbs +1 -2
- package/addon/templates/management/drivers/index/details/orders.hbs +0 -1
- package/addon/templates/management/drivers/index/edit.hbs +1 -2
- package/addon/templates/management/drivers/index/new.hbs +1 -2
- package/addon/templates/management/fleets/index/edit.hbs +1 -2
- package/addon/templates/management/fleets/index/new.hbs +1 -2
- package/addon/templates/management/fleets/index.hbs +1 -2
- package/addon/templates/management/fuel-reports/index/edit.hbs +1 -2
- package/addon/templates/management/fuel-reports/index/new.hbs +1 -2
- package/addon/templates/management/fuel-reports/index.hbs +1 -2
- package/addon/templates/management/issues/index/edit.hbs +1 -2
- package/addon/templates/management/issues/index/new.hbs +1 -2
- package/addon/templates/management/issues/index.hbs +1 -2
- package/addon/templates/management/places/index/details/activity.hbs +0 -1
- package/addon/templates/management/places/index/details/comments.hbs +0 -1
- package/addon/templates/management/places/index/details/documents.hbs +0 -1
- package/addon/templates/management/places/index/details/map.hbs +0 -1
- package/addon/templates/management/places/index/details/operations.hbs +0 -1
- package/addon/templates/management/places/index/details/performance.hbs +0 -1
- package/addon/templates/management/places/index/details/rules.hbs +0 -1
- package/addon/templates/management/vehicles/index/details/equipment.hbs +0 -1
- package/addon/templates/management/vehicles/index/details/maintenance-history.hbs +2 -0
- package/addon/templates/management/vehicles/index/details/schedules.hbs +2 -0
- package/addon/templates/management/vehicles/index/details/work-orders.hbs +2 -0
- package/addon/templates/management/vehicles/index/details.hbs +1 -1
- package/addon/templates/management/vehicles/index/edit.hbs +1 -2
- package/addon/templates/management/vehicles/index/new.hbs +1 -2
- package/addon/templates/management/vendors/index/edit.hbs +1 -2
- package/addon/templates/management/vendors/index/new.hbs +1 -2
- package/addon/templates/management/vendors/index.hbs +1 -2
- package/addon/templates/management/vendors/integrated.hbs +1 -2
- package/addon/templates/operations/orchestrator.hbs +1 -0
- package/addon/templates/operations/orders/index.hbs +6 -1
- package/addon/templates/operations/scheduler/fleet-schedule.hbs +41 -0
- package/addon/templates/operations/scheduler/index.hbs +147 -88
- package/addon/templates/operations/scheduler.hbs +7 -1
- package/addon/templates/settings/avatars.hbs +1 -1
- package/addon/templates/settings/orchestrator.hbs +65 -0
- package/addon/templates/settings/payments/index.hbs +1 -5
- package/addon/templates/settings/scheduling.hbs +82 -0
- package/addon/utils/create-full-calendar-event-from-order.js +52 -14
- package/addon/utils/create-full-calendar-event-from-schedule-item.js +50 -0
- package/addon/utils/fleet-ops-options.js +254 -0
- package/addon/utils/route-colors.js +99 -0
- package/addon/utils/to-calendar-date.js +70 -0
- package/app/components/driver/schedule.js +1 -0
- package/app/components/maintenance/cost-panel.js +1 -0
- package/app/components/maintenance/panel-header.js +1 -0
- package/app/components/maintenance-schedule/details.js +1 -0
- package/app/components/maintenance-schedule/form.js +1 -0
- package/app/components/modals/add-driver-shift.js +1 -0
- package/app/components/modals/bulk-assign-orders.js +1 -0
- package/app/components/modals/driver-shift.js +1 -0
- package/app/components/modals/orchestrator-import.js +1 -0
- package/app/components/modals/scheduling-conflict.js +1 -0
- package/app/components/modals/send-work-order.js +1 -0
- package/app/components/modals/set-driver-availability.js +1 -0
- package/app/components/orchestrator/card-fields-settings.js +1 -0
- package/app/components/orchestrator/order-pool.js +1 -0
- package/app/components/orchestrator/phase-builder.js +1 -0
- package/app/components/orchestrator/plan-viewer.js +1 -0
- package/app/components/orchestrator/resource-panel.js +1 -0
- package/app/components/orchestrator-workbench.js +1 -0
- package/app/components/vehicle/details/maintenance-history.js +1 -0
- package/app/components/vehicle/details/schedules.js +1 -0
- package/app/components/vehicle/details/work-orders.js +1 -0
- package/app/controllers/operations/orchestrator.js +1 -0
- package/app/controllers/settings/orchestrator.js +1 -0
- package/app/controllers/settings/scheduling.js +1 -0
- package/app/routes/operations/orchestrator.js +1 -0
- package/app/routes/settings/orchestrator.js +1 -0
- package/app/routes/settings/scheduling.js +1 -0
- package/app/services/maintenance-schedule-actions.js +1 -0
- package/app/services/orchestration-engine-interface.js +1 -0
- package/app/services/orchestration-engine.js +1 -0
- package/app/services/order-allocation.js +1 -0
- package/app/services/scheduling.js +1 -0
- package/app/services/vroom-allocation-engine.js +1 -0
- package/app/templates/settings/scheduling.js +1 -0
- package/app/utils/create-full-calendar-event-from-schedule-item.js +1 -0
- package/app/utils/route-colors.js +1 -0
- package/composer.json +5 -3
- package/extension.json +1 -1
- package/package.json +6 -5
- package/server/config/fleetops.php +20 -1
- package/server/migrations/2025_08_28_054927_create_parts_table.php +2 -2
- package/server/migrations/2025_08_28_054932_add_public_id_to_maintenance_tables.php +45 -0
- package/server/migrations/2025_09_01_000001_create_maintenance_schedules_table.php +88 -0
- package/server/migrations/2026_04_01_000001_fix_monetary_columns_in_parts_table.php +48 -0
- package/server/migrations/2026_04_01_000003_add_photo_uuid_to_equipment_and_parts_tables.php +61 -0
- package/server/migrations/2026_04_01_000004_add_public_id_to_equipments_table.php +38 -0
- package/server/migrations/2026_04_01_000005_add_missing_columns_to_parts_table.php +67 -0
- package/server/migrations/2026_04_04_000001_add_reminder_offsets_to_maintenance_schedules.php +44 -0
- package/server/migrations/2026_04_08_000001_add_orchestrator_columns_to_vehicles_table.php +58 -0
- package/server/migrations/2026_04_08_000002_add_orchestrator_columns_to_drivers_table.php +41 -0
- package/server/migrations/2026_04_08_000003_add_orchestrator_columns_to_orders_table.php +38 -0
- package/server/migrations/2026_04_08_000004_add_orchestrator_columns_to_payloads_table.php +41 -0
- package/server/migrations/2026_04_08_000005_add_orchestrator_columns_to_waypoints_table.php +38 -0
- package/server/migrations/2026_04_09_000001_create_manifests_table.php +48 -0
- package/server/migrations/2026_04_09_000002_create_manifest_stops_table.php +48 -0
- package/server/migrations/2026_04_09_000003_add_manifest_uuid_to_orders_table.php +28 -0
- package/server/migrations/2026_04_13_000001_add_pod_notes_columns_to_waypoints_table.php +39 -0
- package/server/resources/views/mail/maintenance-schedule-reminder.blade.php +59 -0
- package/server/resources/views/mail/work-order-dispatched.blade.php +67 -0
- package/server/src/Auth/Schemas/FleetOps.php +44 -0
- package/server/src/Console/Commands/ProcessMaintenanceTriggers.php +150 -0
- package/server/src/Console/Commands/SendMaintenanceReminders.php +128 -0
- package/server/src/Http/Controllers/Internal/v1/DriverController.php +1 -0
- package/server/src/Http/Controllers/Internal/v1/EquipmentController.php +27 -0
- package/server/src/Http/Controllers/Internal/v1/LiveController.php +9 -2
- package/server/src/Http/Controllers/Internal/v1/MaintenanceController.php +165 -0
- package/server/src/Http/Controllers/Internal/v1/MaintenanceScheduleController.php +304 -0
- package/server/src/Http/Controllers/Internal/v1/ManifestController.php +138 -0
- package/server/src/Http/Controllers/Internal/v1/OrchestrationController.php +975 -0
- package/server/src/Http/Controllers/Internal/v1/OrderController.php +42 -0
- package/server/src/Http/Controllers/Internal/v1/PartController.php +27 -0
- package/server/src/Http/Controllers/Internal/v1/SettingController.php +118 -0
- package/server/src/Http/Controllers/Internal/v1/Traits/DriverSchedulingTrait.php +214 -0
- package/server/src/Http/Controllers/Internal/v1/WorkOrderController.php +68 -0
- package/server/src/Http/Resources/v1/Driver.php +1 -0
- package/server/src/Http/Resources/v1/Maintenance.php +138 -0
- package/server/src/Http/Resources/v1/MaintenanceSchedule.php +137 -0
- package/server/src/Http/Resources/v1/Orchestrator/Order.php +116 -0
- package/server/src/Http/Resources/v1/Order.php +7 -4
- package/server/src/Http/Resources/v1/Waypoint.php +7 -0
- package/server/src/Http/Resources/v1/WorkOrder.php +136 -0
- package/server/src/Imports/EquipmentImport.php +32 -0
- package/server/src/Imports/MaintenanceImport.php +32 -0
- package/server/src/Imports/MaintenanceScheduleImport.php +32 -0
- package/server/src/Imports/PartImport.php +32 -0
- package/server/src/Imports/WorkOrderImport.php +32 -0
- package/server/src/Jobs/ProcessAllocationJob.php +119 -0
- package/server/src/Listeners/HandleDeliveryCompletion.php +47 -0
- package/server/src/Listeners/NotifyDriverOnShiftChange.php +63 -0
- package/server/src/Mail/MaintenanceScheduleReminder.php +68 -0
- package/server/src/Mail/WorkOrderDispatched.php +58 -0
- package/server/src/Models/Asset.php +2 -2
- package/server/src/Models/Device.php +1 -1
- package/server/src/Models/Driver.php +82 -4
- package/server/src/Models/Equipment.php +62 -2
- package/server/src/Models/Maintenance.php +127 -9
- package/server/src/Models/MaintenanceSchedule.php +353 -0
- package/server/src/Models/Manifest.php +214 -0
- package/server/src/Models/ManifestStop.php +162 -0
- package/server/src/Models/Order.php +70 -0
- package/server/src/Models/OrderConfig.php +5 -2
- package/server/src/Models/Part.php +69 -3
- package/server/src/Models/Payload.php +7 -2
- package/server/src/Models/Place.php +1 -1
- package/server/src/Models/Sensor.php +1 -1
- package/server/src/Models/ServiceQuote.php +1 -1
- package/server/src/Models/Vehicle.php +20 -1
- package/server/src/Models/Warranty.php +1 -1
- package/server/src/Models/Waypoint.php +7 -1
- package/server/src/Models/WorkOrder.php +122 -12
- package/server/src/Notifications/DriverShiftChanged.php +110 -0
- package/server/src/Observers/WorkOrderObserver.php +107 -0
- package/server/src/Orchestration/Contracts/OrchestrationEngineInterface.php +63 -0
- package/server/src/Orchestration/Engines/DriverAssignmentEngine.php +265 -0
- package/server/src/Orchestration/Engines/GreedyOrchestrationEngine.php +155 -0
- package/server/src/Orchestration/Engines/RouteSequencingEngine.php +272 -0
- package/server/src/Orchestration/Engines/VroomOrchestrationEngine.php +192 -0
- package/server/src/Orchestration/OrchestrationEngineRegistry.php +83 -0
- package/server/src/Orchestration/Support/OrchestrationPayloadBuilder.php +290 -0
- package/server/src/Providers/EventServiceProvider.php +7 -1
- package/server/src/Providers/FleetOpsServiceProvider.php +42 -15
- package/server/src/routes.php +65 -4
- package/translations/ar-ae.yml +44 -12
- package/translations/bg-bg.yaml +51 -10
- package/translations/en-us.yaml +444 -1
- package/translations/fr-fr.yaml +51 -10
- package/translations/mn-mn.yaml +51 -10
- package/translations/pt-br.yaml +51 -10
- package/translations/ru-ru.yaml +51 -10
- package/translations/vi-vn.yaml +48 -12
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
<Layout::Section::Header @title={{t "settings.scheduling.scheduling-settings"}}>
|
|
2
|
+
<Button
|
|
3
|
+
@type="primary"
|
|
4
|
+
@size="sm"
|
|
5
|
+
@icon="save"
|
|
6
|
+
@text={{t "common.save-changes"}}
|
|
7
|
+
@onClick={{perform this.saveSettings}}
|
|
8
|
+
@disabled={{this.saveSettings.isRunning}}
|
|
9
|
+
@isLoading={{or this.saveSettings.isRunning this.getSettings.isRunning}}
|
|
10
|
+
/>
|
|
11
|
+
</Layout::Section::Header>
|
|
12
|
+
<Layout::Section::Body class="overflow-y-scroll h-full">
|
|
13
|
+
<div class="container mx-auto h-screen">
|
|
14
|
+
<div class="max-w-3xl my-10 mx-auto space-y-6">
|
|
15
|
+
|
|
16
|
+
{{! ── Materialisation ──────────────────────────────────────────── }}
|
|
17
|
+
<ContentPanel @title={{t "settings.scheduling.materialisation"}} @open={{true}} @wrapperClass="bordered-classic">
|
|
18
|
+
<InputGroup @name={{t "settings.scheduling.horizon-days"}} @helpText={{t "settings.scheduling.horizon-days-help"}}>
|
|
19
|
+
<Input @type="number" @value={{this.horizonDays}} min="7" max="365" class="w-full form-input" />
|
|
20
|
+
</InputGroup>
|
|
21
|
+
<InputGroup @name={{t "settings.scheduling.default-shift-duration"}} @helpText={{t "settings.scheduling.default-shift-duration-help"}}>
|
|
22
|
+
<Input @type="number" @value={{this.defaultShiftDuration}} min="1" max="24" class="w-full form-input" />
|
|
23
|
+
</InputGroup>
|
|
24
|
+
</ContentPanel>
|
|
25
|
+
|
|
26
|
+
{{! ── Hours of Service Limits ──────────────────────────────────── }}
|
|
27
|
+
<ContentPanel @title={{t "settings.scheduling.hours-of-service"}} @open={{true}} @wrapperClass="bordered-classic">
|
|
28
|
+
<InputGroup @name={{t "settings.scheduling.hos-daily-limit"}} @helpText={{t "settings.scheduling.hos-daily-limit-help"}}>
|
|
29
|
+
<Input @type="number" @value={{this.hosDailyLimit}} min="1" max="24" class="w-full form-input" />
|
|
30
|
+
</InputGroup>
|
|
31
|
+
<InputGroup @name={{t "settings.scheduling.hos-weekly-limit"}} @helpText={{t "settings.scheduling.hos-weekly-limit-help"}}>
|
|
32
|
+
<Input @type="number" @value={{this.hosWeeklyLimit}} min="1" max="168" class="w-full form-input" />
|
|
33
|
+
</InputGroup>
|
|
34
|
+
</ContentPanel>
|
|
35
|
+
|
|
36
|
+
{{! ── Behaviour ────────────────────────────────────────────────── }}
|
|
37
|
+
<ContentPanel @title={{t "settings.scheduling.behaviour"}} @open={{true}} @wrapperClass="bordered-classic">
|
|
38
|
+
<InputGroup @name={{t "settings.scheduling.auto-activate-schedule"}} @helpText={{t "settings.scheduling.auto-activate-schedule-help"}}>
|
|
39
|
+
<Toggle @isToggled={{this.autoActivateSchedule}} @onToggle={{fn (mut this.autoActivateSchedule)}} />
|
|
40
|
+
</InputGroup>
|
|
41
|
+
<InputGroup @name={{t "settings.scheduling.notify-drivers"}} @helpText={{t "settings.scheduling.notify-drivers-help"}}>
|
|
42
|
+
<Toggle @isToggled={{this.notifyDriversOnShiftChange}} @onToggle={{fn (mut this.notifyDriversOnShiftChange)}} />
|
|
43
|
+
</InputGroup>
|
|
44
|
+
</ContentPanel>
|
|
45
|
+
|
|
46
|
+
{{! ── Reusable Schedule Templates ─────────────────────────────── }}
|
|
47
|
+
<ContentPanel @title={{t "settings.scheduling.schedule-templates"}} @open={{true}} @wrapperClass="bordered-classic" @actionButtons={{this.templateActionButtons}}>
|
|
48
|
+
{{#if this.loadTemplates.isRunning}}
|
|
49
|
+
<div class="flex items-center justify-center py-8">
|
|
50
|
+
<Spinner />
|
|
51
|
+
</div>
|
|
52
|
+
{{else if this.scheduleTemplates.length}}
|
|
53
|
+
<div class="divide-y divide-gray-100 dark:divide-gray-700">
|
|
54
|
+
{{#each this.scheduleTemplates as |template|}}
|
|
55
|
+
<div class="flex items-center justify-between py-2.5 px-1">
|
|
56
|
+
<div class="flex items-center gap-2 min-w-0">
|
|
57
|
+
<span class="inline-block w-2.5 h-2.5 rounded-full flex-shrink-0" style={{html-safe (concat "background-color: " template.color)}}></span>
|
|
58
|
+
<span class="text-sm font-medium text-gray-800 dark:text-gray-100 truncate">
|
|
59
|
+
{{template.name}}
|
|
60
|
+
</span>
|
|
61
|
+
<span class="text-xs text-gray-400 dark:text-gray-500 truncate hidden sm:block">
|
|
62
|
+
{{template.rrule}}
|
|
63
|
+
</span>
|
|
64
|
+
</div>
|
|
65
|
+
<div class="flex items-center gap-1 flex-shrink-0">
|
|
66
|
+
<Button @icon="pencil" @size="xs" @type="link" @onClick={{fn this.editTemplate template}} />
|
|
67
|
+
<Button @icon="trash" @size="xs" @type="link" class="text-red-400 hover:text-red-600" @onClick={{fn this.deleteTemplate template}} />
|
|
68
|
+
</div>
|
|
69
|
+
</div>
|
|
70
|
+
{{/each}}
|
|
71
|
+
</div>
|
|
72
|
+
{{else}}
|
|
73
|
+
<div class="text-center py-8 text-sm text-gray-400 dark:text-gray-500">
|
|
74
|
+
{{t "settings.scheduling.no-templates"}}
|
|
75
|
+
</div>
|
|
76
|
+
{{/if}}
|
|
77
|
+
</ContentPanel>
|
|
78
|
+
|
|
79
|
+
</div>
|
|
80
|
+
</div>
|
|
81
|
+
<Spacer @height="600px" />
|
|
82
|
+
</Layout::Section::Body>
|
|
@@ -1,4 +1,15 @@
|
|
|
1
1
|
import { get } from '@ember/object';
|
|
2
|
+
import { addMinutes } from 'date-fns';
|
|
3
|
+
import toCalendarDate from './to-calendar-date';
|
|
4
|
+
|
|
5
|
+
const STATUS_COLORS = {
|
|
6
|
+
created: '#6366f1',
|
|
7
|
+
dispatched: '#3b82f6',
|
|
8
|
+
active: '#22c55e',
|
|
9
|
+
completed: '#9ca3af',
|
|
10
|
+
cancelled: '#ef4444',
|
|
11
|
+
expired: '#f97316',
|
|
12
|
+
};
|
|
2
13
|
|
|
3
14
|
export function createOrderEventTitle(order) {
|
|
4
15
|
return get(order, 'tracking') ?? get(order, 'public_id');
|
|
@@ -9,28 +20,55 @@ export function createOrderEventDescription(order) {
|
|
|
9
20
|
const driverAssignedName = get(order, 'driver_assigned.name');
|
|
10
21
|
const vehicleAssignedName = get(order, 'driver_assigned.vehicle_name');
|
|
11
22
|
const destination = get(order, 'pickupName');
|
|
12
|
-
|
|
13
|
-
let title = [];
|
|
23
|
+
const parts = [];
|
|
14
24
|
if (driverAssignedName) {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
25
|
+
parts.push(driverAssignedName + ' @ ' + scheduledAtTime);
|
|
26
|
+
if (vehicleAssignedName) parts.push(vehicleAssignedName);
|
|
27
|
+
if (destination) parts.push('to ' + destination);
|
|
18
28
|
} else {
|
|
19
|
-
|
|
20
|
-
|
|
29
|
+
if (scheduledAtTime) parts.push(scheduledAtTime);
|
|
30
|
+
if (destination) parts.push('to ' + destination);
|
|
31
|
+
if (vehicleAssignedName) parts.push(vehicleAssignedName);
|
|
21
32
|
}
|
|
22
|
-
|
|
23
|
-
return title.filter(Boolean).join('\n');
|
|
33
|
+
return parts.filter(Boolean).join('\n');
|
|
24
34
|
}
|
|
25
35
|
|
|
26
|
-
|
|
36
|
+
/**
|
|
37
|
+
* Converts an order Ember Data record into a calendar event object for
|
|
38
|
+
* @event-calendar/core.
|
|
39
|
+
*
|
|
40
|
+
* @event-calendar/core has no timezone support — it reads the browser-local
|
|
41
|
+
* fields of any Date passed as `start`/`end` and positions the event at that
|
|
42
|
+
* wall-clock time. To display events in the company timezone regardless of
|
|
43
|
+
* the browser's timezone, we pass a "fake local" Date whose local fields equal
|
|
44
|
+
* the company wall-clock time. See `to-calendar-date.js` for details.
|
|
45
|
+
*
|
|
46
|
+
* @param {Model} order Ember Data `order` record.
|
|
47
|
+
* @param {string} timezone IANA timezone string for the company, e.g. 'Asia/Singapore'.
|
|
48
|
+
* @returns {Object} Calendar event object.
|
|
49
|
+
*/
|
|
50
|
+
export default function createFullCalendarEventFromOrder(order, timezone) {
|
|
51
|
+
const scheduledAt = get(order, 'scheduled_at');
|
|
52
|
+
const duration = get(order, 'estimated_duration') ?? 60;
|
|
53
|
+
|
|
54
|
+
// Convert the UTC scheduled_at to a wall-clock Date in the company timezone.
|
|
55
|
+
const startUtc = scheduledAt ? new Date(scheduledAt) : null;
|
|
56
|
+
const start = startUtc ? toCalendarDate(startUtc, timezone) : null;
|
|
57
|
+
const end = start ? toCalendarDate(addMinutes(startUtc, duration), timezone) : null;
|
|
58
|
+
|
|
59
|
+
const status = get(order, 'status') ?? 'created';
|
|
60
|
+
const color = STATUS_COLORS[status] ?? STATUS_COLORS.created;
|
|
27
61
|
return {
|
|
28
|
-
id: order
|
|
62
|
+
id: get(order, 'id'),
|
|
63
|
+
resourceId: get(order, 'driver_assigned_uuid') ?? null,
|
|
29
64
|
title: createOrderEventTitle(order),
|
|
30
65
|
description: createOrderEventDescription(order),
|
|
31
|
-
start
|
|
32
|
-
|
|
33
|
-
allDay: true,
|
|
66
|
+
start,
|
|
67
|
+
end,
|
|
34
68
|
display: 'block',
|
|
69
|
+
backgroundColor: color,
|
|
70
|
+
borderColor: color,
|
|
71
|
+
textColor: '#ffffff',
|
|
72
|
+
extendedProps: { order, status, type: 'order' },
|
|
35
73
|
};
|
|
36
74
|
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { get } from '@ember/object';
|
|
2
|
+
import toCalendarDate from './to-calendar-date';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* createFullCalendarEventFromScheduleItem
|
|
6
|
+
*
|
|
7
|
+
* Transforms a ScheduleItem Ember Data record into a calendar event object for
|
|
8
|
+
* @event-calendar/core. Used to render driver shift windows as background
|
|
9
|
+
* blocks on the resource timeline.
|
|
10
|
+
*
|
|
11
|
+
* @event-calendar/core has no timezone support — it reads the browser-local
|
|
12
|
+
* fields of any Date passed as `start`/`end` and positions the event at that
|
|
13
|
+
* wall-clock time. To display shifts in the company timezone regardless of
|
|
14
|
+
* the browser's timezone, we pass a "fake local" Date whose local fields equal
|
|
15
|
+
* the company wall-clock time. See `to-calendar-date.js` for details.
|
|
16
|
+
*
|
|
17
|
+
* @param {Model} scheduleItem A `schedule-item` Ember Data record.
|
|
18
|
+
* @param {Model} driver The associated `driver` Ember Data record.
|
|
19
|
+
* @param {string} timezone IANA timezone string for the company, e.g. 'Asia/Singapore'.
|
|
20
|
+
* @param {Object} [overrides] Optional calendar property overrides (e.g. display, backgroundColor).
|
|
21
|
+
* @returns {Object} Calendar event object.
|
|
22
|
+
*/
|
|
23
|
+
export default function createFullCalendarEventFromScheduleItem(scheduleItem, driver, timezone, overrides = {}) {
|
|
24
|
+
const driverName = get(driver, 'name') ?? 'Unknown Driver';
|
|
25
|
+
const title = get(scheduleItem, 'title') || `${driverName} — Shift`;
|
|
26
|
+
|
|
27
|
+
const startRaw = get(scheduleItem, 'start_at');
|
|
28
|
+
const endRaw = get(scheduleItem, 'end_at');
|
|
29
|
+
|
|
30
|
+
// Convert the UTC start/end to wall-clock Dates in the company timezone.
|
|
31
|
+
const start = startRaw ? toCalendarDate(new Date(startRaw), timezone) : null;
|
|
32
|
+
const end = endRaw ? toCalendarDate(new Date(endRaw), timezone) : null;
|
|
33
|
+
|
|
34
|
+
return {
|
|
35
|
+
id: `shift-${get(scheduleItem, 'id')}`,
|
|
36
|
+
resourceId: get(scheduleItem, 'assignee_uuid') ?? get(driver, 'id'),
|
|
37
|
+
title,
|
|
38
|
+
start,
|
|
39
|
+
end,
|
|
40
|
+
display: 'background',
|
|
41
|
+
backgroundColor: 'rgba(99, 102, 241, 0.08)',
|
|
42
|
+
borderColor: 'rgba(99, 102, 241, 0.25)',
|
|
43
|
+
extendedProps: {
|
|
44
|
+
scheduleItem,
|
|
45
|
+
driver,
|
|
46
|
+
type: 'shift',
|
|
47
|
+
},
|
|
48
|
+
...overrides,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
@@ -569,6 +569,247 @@ export const transmissionTypes = [
|
|
|
569
569
|
|
|
570
570
|
export const odometerUnits = [...distanceUnits, { label: 'Hours', value: 'hours', description: 'Unit of time measurement, commonly used worldwide.' }];
|
|
571
571
|
|
|
572
|
+
// ─── Orchestrator: Driver & Vehicle Skills ────────────────────────────────────
|
|
573
|
+
|
|
574
|
+
/**
|
|
575
|
+
* driverSkills
|
|
576
|
+
* Comprehensive list of driver qualifications and certifications used by the
|
|
577
|
+
* Orchestrator to match orders to appropriately skilled drivers.
|
|
578
|
+
*/
|
|
579
|
+
export const driverSkills = [
|
|
580
|
+
// Licences & Certifications
|
|
581
|
+
{ label: 'Car Licence (B)', value: 'licence_b', description: 'Standard passenger car licence.', group: 'licence' },
|
|
582
|
+
{ label: 'Light Truck Licence (C)', value: 'licence_c', description: 'Licence for rigid trucks up to 8 tonnes GVM.', group: 'licence' },
|
|
583
|
+
{ label: 'Medium Rigid (MR)', value: 'licence_mr', description: 'Two-axle rigid vehicle up to 8 tonnes.', group: 'licence' },
|
|
584
|
+
{ label: 'Heavy Rigid (HR)', value: 'licence_hr', description: 'Three or more axle rigid vehicle.', group: 'licence' },
|
|
585
|
+
{ label: 'Heavy Combination (HC)', value: 'licence_hc', description: 'Prime mover + semi-trailer combination.', group: 'licence' },
|
|
586
|
+
{ label: 'Multi-Combination (MC)', value: 'licence_mc', description: 'B-double, road train, or multi-trailer combination.', group: 'licence' },
|
|
587
|
+
{ label: 'Motorcycle Licence (A)', value: 'licence_a', description: 'Motorcycle or scooter delivery licence.', group: 'licence' },
|
|
588
|
+
{ label: 'Forklift Licence', value: 'licence_forklift', description: 'Certified forklift operator.', group: 'licence' },
|
|
589
|
+
{ label: 'Dangerous Goods (DG)', value: 'cert_dg', description: 'Certified to transport dangerous goods under ADG/IMDG codes.', group: 'certification' },
|
|
590
|
+
{ label: 'Hazmat (US DOT)', value: 'cert_hazmat', description: 'US DOT hazardous materials endorsement.', group: 'certification' },
|
|
591
|
+
{ label: 'ADR Hazmat (EU)', value: 'cert_adr', description: 'European ADR dangerous goods driver certificate.', group: 'certification' },
|
|
592
|
+
{ label: 'Refrigerated Transport', value: 'cert_reefer', description: 'Trained in cold-chain and temperature-controlled transport.', group: 'certification' },
|
|
593
|
+
{ label: 'Oversize / Overweight', value: 'cert_oversize', description: 'Certified for oversize or overweight load operations.', group: 'certification' },
|
|
594
|
+
{ label: 'Passenger Transport', value: 'cert_passenger', description: 'Authorised to carry fare-paying passengers.', group: 'certification' },
|
|
595
|
+
{ label: 'First Aid', value: 'cert_first_aid', description: 'Current first aid certificate.', group: 'certification' },
|
|
596
|
+
{ label: 'Defensive Driving', value: 'cert_defensive_driving', description: 'Advanced defensive driving qualification.', group: 'certification' },
|
|
597
|
+
// Operational Skills
|
|
598
|
+
{ label: 'Hand-truck / Trolley', value: 'skill_hand_truck', description: 'Proficient with hand-truck and trolley equipment.', group: 'operational' },
|
|
599
|
+
{ label: 'Tail-lift Operation', value: 'skill_tail_lift', description: 'Trained to operate hydraulic tail-lift.', group: 'operational' },
|
|
600
|
+
{ label: 'Crane / HIAB', value: 'skill_hiab', description: 'Certified to operate truck-mounted crane/HIAB.', group: 'operational' },
|
|
601
|
+
{ label: 'Pallet Jack', value: 'skill_pallet_jack', description: 'Manual or electric pallet jack operation.', group: 'operational' },
|
|
602
|
+
{ label: 'Pump-out / Tanker', value: 'skill_tanker', description: 'Trained in tanker loading, unloading, and pump operation.', group: 'operational' },
|
|
603
|
+
{ label: 'Livestock Handling', value: 'skill_livestock', description: 'Experienced with live animal transport and handling.', group: 'operational' },
|
|
604
|
+
{ label: 'Fragile / White Goods', value: 'skill_fragile', description: 'Trained in handling fragile or high-value goods.', group: 'operational' },
|
|
605
|
+
{ label: 'Pharmaceutical / Medical', value: 'skill_pharma', description: 'Experienced with pharmaceutical and medical supply chain requirements.', group: 'operational' },
|
|
606
|
+
{ label: 'Cash-in-Transit', value: 'skill_cit', description: 'Cleared and trained for cash-in-transit operations.', group: 'operational' },
|
|
607
|
+
{ label: 'Contactless / Unattended Delivery', value: 'skill_contactless', description: 'Proficient in unattended or safe-drop delivery procedures.', group: 'operational' },
|
|
608
|
+
// Language & Customer Service
|
|
609
|
+
{ label: 'Customer-facing', value: 'skill_customer_facing', description: 'Experienced in direct customer interaction and service.', group: 'soft_skills' },
|
|
610
|
+
{ label: 'Multilingual', value: 'skill_multilingual', description: 'Fluent in more than one language.', group: 'soft_skills' },
|
|
611
|
+
{ label: 'Digital / App Proficient', value: 'skill_digital', description: 'Comfortable using mobile apps and digital POD tools.', group: 'soft_skills' },
|
|
612
|
+
];
|
|
613
|
+
|
|
614
|
+
/**
|
|
615
|
+
* vehicleSkills
|
|
616
|
+
* Equipment and capability flags for vehicles used by the Orchestrator to
|
|
617
|
+
* match vehicles to orders requiring specific handling.
|
|
618
|
+
*/
|
|
619
|
+
export const vehicleSkills = [
|
|
620
|
+
// Cargo Handling Equipment
|
|
621
|
+
{ label: 'Tail Lift', value: 'tail_lift', description: 'Hydraulic tail-lift for loading/unloading without a dock.', group: 'equipment' },
|
|
622
|
+
{ label: 'Crane / HIAB', value: 'hiab', description: 'Truck-mounted crane for self-loading.', group: 'equipment' },
|
|
623
|
+
{ label: 'Pallet Racking', value: 'pallet_racking', description: 'Internal pallet racking system.', group: 'equipment' },
|
|
624
|
+
{ label: 'Roller Bed / Conveyor Floor', value: 'roller_bed', description: 'Motorised roller floor for easy cargo movement.', group: 'equipment' },
|
|
625
|
+
{ label: 'Curtainsider', value: 'curtainsider', description: 'Curtain-sided trailer for easy side-loading.', group: 'equipment' },
|
|
626
|
+
{ label: 'Flatbed / Step-deck', value: 'flatbed', description: 'Open flatbed or step-deck for oversized loads.', group: 'equipment' },
|
|
627
|
+
{ label: 'Refrigerated (Reefer)', value: 'refrigerated', description: 'Active refrigeration unit for cold-chain cargo.', group: 'temperature' },
|
|
628
|
+
{ label: 'Chilled (2–8 °C)', value: 'chilled', description: 'Maintains chilled temperature range 2–8 °C.', group: 'temperature' },
|
|
629
|
+
{ label: 'Frozen (< −18 °C)', value: 'frozen', description: 'Maintains frozen temperature below −18 °C.', group: 'temperature' },
|
|
630
|
+
{ label: 'Ambient / Insulated', value: 'insulated', description: 'Insulated body without active cooling.', group: 'temperature' },
|
|
631
|
+
{ label: 'Heated / Warm', value: 'heated', description: 'Heated cargo space for temperature-sensitive goods in cold climates.', group: 'temperature' },
|
|
632
|
+
// Compliance & Certification
|
|
633
|
+
{ label: 'Dangerous Goods Approved', value: 'dg_approved', description: 'Vehicle certified and placarded for dangerous goods.', group: 'compliance' },
|
|
634
|
+
{ label: 'ADR Certified (EU)', value: 'adr_certified', description: 'European ADR dangerous goods vehicle certification.', group: 'compliance' },
|
|
635
|
+
{ label: 'Oversize Permit', value: 'oversize_permit', description: 'Holds current oversize/overweight load permit.', group: 'compliance' },
|
|
636
|
+
{ label: 'Food Grade', value: 'food_grade', description: 'Cargo area meets food safety/hygiene standards.', group: 'compliance' },
|
|
637
|
+
{ label: 'Pharmaceutical Grade', value: 'pharma_grade', description: 'Meets GDP/pharmaceutical transport standards.', group: 'compliance' },
|
|
638
|
+
{ label: 'Cash-in-Transit Secure', value: 'cit_secure', description: 'Armoured or security-fitted for cash-in-transit.', group: 'compliance' },
|
|
639
|
+
// Connectivity & Telematics
|
|
640
|
+
{ label: 'GPS / Live Tracking', value: 'gps_tracking', description: 'Fitted with live GPS tracking device.', group: 'telematics' },
|
|
641
|
+
{ label: 'Temperature Logger', value: 'temp_logger', description: 'Continuous temperature data logging.', group: 'telematics' },
|
|
642
|
+
{ label: 'Dash Camera', value: 'dash_cam', description: 'Forward-facing dash camera installed.', group: 'telematics' },
|
|
643
|
+
{ label: 'ELD / Tachograph', value: 'eld', description: 'Electronic logging device or digital tachograph fitted.', group: 'telematics' },
|
|
644
|
+
];
|
|
645
|
+
|
|
646
|
+
// ─── Orchestrator: Capacity & Dimension Options ───────────────────────────────
|
|
647
|
+
|
|
648
|
+
/**
|
|
649
|
+
* capacityDimensions
|
|
650
|
+
* The multi-dimensional capacity axes supported by the Orchestrator and VROOM.
|
|
651
|
+
* Each dimension can be independently constrained on vehicles and required by orders.
|
|
652
|
+
*/
|
|
653
|
+
export const capacityDimensions = [
|
|
654
|
+
{ label: 'Weight (kg)', value: 'weight_kg', unit: 'kg', description: 'Gross cargo weight in kilograms.', group: 'weight' },
|
|
655
|
+
{ label: 'Weight (lb)', value: 'weight_lb', unit: 'lb', description: 'Gross cargo weight in pounds.', group: 'weight' },
|
|
656
|
+
{ label: 'Volume (m³)', value: 'volume_m3', unit: 'm³', description: 'Total cargo volume in cubic metres.', group: 'volume' },
|
|
657
|
+
{ label: 'Volume (ft³)', value: 'volume_ft3', unit: 'ft³', description: 'Total cargo volume in cubic feet.', group: 'volume' },
|
|
658
|
+
{ label: 'Pallets', value: 'pallets', unit: 'pallets', description: 'Number of standard EUR/AU pallets.', group: 'units' },
|
|
659
|
+
{ label: 'Parcels / Packages', value: 'parcels', unit: 'parcels', description: 'Count of individual parcels or packages.', group: 'units' },
|
|
660
|
+
{ label: 'Cartons / Cases', value: 'cartons', unit: 'cartons', description: 'Count of cartons or cases.', group: 'units' },
|
|
661
|
+
{ label: 'Linear Metres (LDM)', value: 'ldm', unit: 'LDM', description: 'Loading metres — floor space occupied by cargo.', group: 'length' },
|
|
662
|
+
{ label: 'Floor Area (m²)', value: 'floor_area_m2', unit: 'm²', description: 'Cargo floor area in square metres.', group: 'length' },
|
|
663
|
+
{ label: 'Seats / Passengers', value: 'seats', unit: 'seats', description: 'Passenger seat capacity (for passenger transport).', group: 'units' },
|
|
664
|
+
];
|
|
665
|
+
|
|
666
|
+
/**
|
|
667
|
+
* vehicleCapacityProfiles
|
|
668
|
+
* Common pre-defined capacity profiles for quick assignment to vehicles.
|
|
669
|
+
*/
|
|
670
|
+
export const vehicleCapacityProfiles = [
|
|
671
|
+
{ label: 'Motorbike / Scooter', value: 'moto', description: 'Up to 20 kg, 0.05 m³, 5 parcels.', weight_kg: 20, volume_m3: 0.05, parcels: 5 },
|
|
672
|
+
{ label: 'Small Car / Hatchback', value: 'small_car', description: 'Up to 150 kg, 0.3 m³, 20 parcels.', weight_kg: 150, volume_m3: 0.3, parcels: 20 },
|
|
673
|
+
{ label: 'Cargo Van (Small)', value: 'small_van', description: 'Up to 800 kg, 5 m³, 100 parcels.', weight_kg: 800, volume_m3: 5, parcels: 100 },
|
|
674
|
+
{ label: 'Cargo Van (Large)', value: 'large_van', description: 'Up to 1,500 kg, 10 m³, 200 parcels.', weight_kg: 1500, volume_m3: 10, parcels: 200 },
|
|
675
|
+
{ label: 'Light Truck (3.5t)', value: 'light_truck', description: 'Up to 3,500 kg, 20 m³, 6 pallets.', weight_kg: 3500, volume_m3: 20, pallets: 6 },
|
|
676
|
+
{ label: 'Medium Truck (7.5t)', value: 'medium_truck', description: 'Up to 7,500 kg, 40 m³, 14 pallets.', weight_kg: 7500, volume_m3: 40, pallets: 14 },
|
|
677
|
+
{ label: 'Heavy Truck (13.5t)', value: 'heavy_truck', description: 'Up to 13,500 kg, 60 m³, 22 pallets.', weight_kg: 13500, volume_m3: 60, pallets: 22 },
|
|
678
|
+
{ label: 'Semi-Trailer (26t)', value: 'semi_trailer', description: 'Up to 26,000 kg, 90 m³, 33 pallets.', weight_kg: 26000, volume_m3: 90, pallets: 33 },
|
|
679
|
+
{ label: 'B-Double (42.5t)', value: 'b_double', description: 'Up to 42,500 kg, 150 m³, 54 pallets.', weight_kg: 42500, volume_m3: 150, pallets: 54 },
|
|
680
|
+
{ label: 'Refrigerated Van', value: 'reefer_van', description: 'Up to 1,200 kg, 8 m³, cold-chain.', weight_kg: 1200, volume_m3: 8 },
|
|
681
|
+
{ label: 'Refrigerated Truck (10t)', value: 'reefer_truck', description: 'Up to 10,000 kg, 50 m³, cold-chain.', weight_kg: 10000, volume_m3: 50 },
|
|
682
|
+
];
|
|
683
|
+
|
|
684
|
+
// ─── Orchestrator: Order Priority Options ─────────────────────────────────────
|
|
685
|
+
|
|
686
|
+
/**
|
|
687
|
+
* orderPriorityLevels
|
|
688
|
+
* Routing priority levels for orders. Higher priority orders are served first
|
|
689
|
+
* by the optimization engine when time windows conflict.
|
|
690
|
+
*/
|
|
691
|
+
export const orderPriorityLevels = [
|
|
692
|
+
{ label: 'Critical', value: 100, description: 'Must be served first — SLA breach imminent or emergency delivery.', color: 'red' },
|
|
693
|
+
{ label: 'High', value: 75, description: 'High-priority order; serve before standard orders.', color: 'orange' },
|
|
694
|
+
{ label: 'Standard', value: 50, description: 'Normal priority — default for all orders.', color: 'blue' },
|
|
695
|
+
{ label: 'Low', value: 25, description: 'Low-priority order; serve after higher-priority stops.', color: 'gray' },
|
|
696
|
+
{ label: 'Flexible', value: 10, description: 'No strict time requirement; fill remaining capacity.', color: 'green' },
|
|
697
|
+
];
|
|
698
|
+
|
|
699
|
+
// ─── Orchestrator: Time Window Presets ────────────────────────────────────────
|
|
700
|
+
|
|
701
|
+
/**
|
|
702
|
+
* timeWindowPresets
|
|
703
|
+
* Common delivery/pickup time window presets for quick assignment.
|
|
704
|
+
* Times are stored as HH:MM strings; the backend converts to Unix timestamps.
|
|
705
|
+
*/
|
|
706
|
+
export const timeWindowPresets = [
|
|
707
|
+
{ label: 'Early Morning (06:00–09:00)', value: 'early_morning', start: '06:00', end: '09:00', description: 'Pre-business-hours delivery window.' },
|
|
708
|
+
{ label: 'Morning (08:00–12:00)', value: 'morning', start: '08:00', end: '12:00', description: 'Standard morning delivery window.' },
|
|
709
|
+
{ label: 'Midday (11:00–14:00)', value: 'midday', start: '11:00', end: '14:00', description: 'Lunchtime delivery window.' },
|
|
710
|
+
{ label: 'Afternoon (12:00–17:00)', value: 'afternoon', start: '12:00', end: '17:00', description: 'Standard afternoon delivery window.' },
|
|
711
|
+
{ label: 'Business Hours (09:00–17:00)', value: 'business_hours', start: '09:00', end: '17:00', description: 'Full business-hours window.' },
|
|
712
|
+
{ label: 'Evening (17:00–21:00)', value: 'evening', start: '17:00', end: '21:00', description: 'After-hours residential delivery.' },
|
|
713
|
+
{ label: 'Night (21:00–06:00)', value: 'night', start: '21:00', end: '06:00', description: 'Overnight or graveyard-shift delivery.' },
|
|
714
|
+
{ label: 'AM Only (Before 12:00)', value: 'am_only', start: '00:00', end: '12:00', description: 'Must be delivered before noon.' },
|
|
715
|
+
{ label: 'PM Only (After 12:00)', value: 'pm_only', start: '12:00', end: '23:59', description: 'Must be delivered after noon.' },
|
|
716
|
+
{ label: 'Anytime', value: 'anytime', start: '00:00', end: '23:59', description: 'No time restriction — deliver any time.' },
|
|
717
|
+
];
|
|
718
|
+
|
|
719
|
+
// ─── Orchestrator: Route Optimization Constraint Options ──────────────────────
|
|
720
|
+
|
|
721
|
+
/**
|
|
722
|
+
* optimizationObjectives
|
|
723
|
+
* The primary objective the optimization engine should minimise/maximise.
|
|
724
|
+
*/
|
|
725
|
+
export const optimizationObjectives = [
|
|
726
|
+
{ label: 'Minimize Total Distance', value: 'min_distance', description: 'Produce routes with the shortest total travel distance.', icon: 'route' },
|
|
727
|
+
{ label: 'Minimize Total Duration', value: 'min_duration', description: 'Produce routes with the shortest total travel time.', icon: 'clock' },
|
|
728
|
+
{ label: 'Minimize Vehicles Used', value: 'min_vehicles', description: 'Use as few vehicles as possible while serving all orders.', icon: 'truck' },
|
|
729
|
+
{ label: 'Balance Workload', value: 'balance_workload', description: 'Distribute stops evenly across all available drivers.', icon: 'scale' },
|
|
730
|
+
{ label: 'Maximize On-Time Delivery', value: 'max_on_time', description: 'Prioritise serving stops within their time windows.', icon: 'check-circle' },
|
|
731
|
+
{ label: 'Minimize Overtime', value: 'min_overtime', description: 'Keep driver working hours within contracted limits.', icon: 'user-clock' },
|
|
732
|
+
{ label: 'Minimize Fuel / Emissions', value: 'min_emissions', description: 'Optimize for lowest fuel consumption and CO₂ output.', icon: 'leaf' },
|
|
733
|
+
];
|
|
734
|
+
|
|
735
|
+
/**
|
|
736
|
+
* routingConstraintOptions
|
|
737
|
+
* Boolean constraint toggles exposed in the Orchestrator settings and run panel.
|
|
738
|
+
*/
|
|
739
|
+
export const routingConstraintOptions = [
|
|
740
|
+
{ label: 'Respect Time Windows', value: 'respect_time_windows', description: 'Enforce delivery/pickup time windows as hard constraints.' },
|
|
741
|
+
{ label: 'Respect Vehicle Capacity', value: 'respect_capacity', description: 'Do not exceed vehicle weight, volume, or unit capacity.' },
|
|
742
|
+
{ label: 'Respect Driver Skills', value: 'respect_skills', description: 'Only assign orders to drivers/vehicles with matching skills.' },
|
|
743
|
+
{ label: 'Respect Max Tasks per Route', value: 'respect_max_tasks', description: 'Do not exceed the maximum number of stops per vehicle.' },
|
|
744
|
+
{ label: 'Respect Max Travel Time', value: 'respect_max_travel_time', description: 'Do not exceed the maximum driving time per vehicle/driver.' },
|
|
745
|
+
{ label: 'Respect Max Distance', value: 'respect_max_distance', description: 'Do not exceed the maximum distance per vehicle/driver.' },
|
|
746
|
+
{ label: 'Allow Unassigned Orders', value: 'allow_unassigned', description: 'Permit orders to remain unassigned if no feasible vehicle exists.' },
|
|
747
|
+
{ label: 'Pickup Before Delivery', value: 'pickup_before_delivery', description: 'Enforce pickup-before-dropoff sequencing for P&D orders.' },
|
|
748
|
+
{ label: 'Return to Depot', value: 'return_to_depot', description: 'Require vehicles to return to their start depot after the last stop.' },
|
|
749
|
+
{ label: 'Avoid Highways', value: 'avoid_highways', description: 'Route via non-highway roads where possible.' },
|
|
750
|
+
{ label: 'Avoid Tolls', value: 'avoid_tolls', description: 'Route to avoid toll roads where possible.' },
|
|
751
|
+
{ label: 'Avoid Ferries', value: 'avoid_ferries', description: 'Route to avoid ferry crossings where possible.' },
|
|
752
|
+
];
|
|
753
|
+
|
|
754
|
+
/**
|
|
755
|
+
* serviceTimePresets
|
|
756
|
+
* Common service time (dwell time) presets for waypoints.
|
|
757
|
+
* Represents the expected time in seconds to complete the task at a stop.
|
|
758
|
+
*/
|
|
759
|
+
export const serviceTimePresets = [
|
|
760
|
+
{ label: '2 minutes', value: 120, description: 'Quick drop — parcel left at door.' },
|
|
761
|
+
{ label: '5 minutes', value: 300, description: 'Standard residential drop-off.' },
|
|
762
|
+
{ label: '10 minutes', value: 600, description: 'Standard commercial delivery.' },
|
|
763
|
+
{ label: '15 minutes', value: 900, description: 'Delivery with signature/POD.' },
|
|
764
|
+
{ label: '20 minutes', value: 1200, description: 'Delivery with unloading assistance.' },
|
|
765
|
+
{ label: '30 minutes', value: 1800, description: 'Larger delivery or installation.' },
|
|
766
|
+
{ label: '45 minutes', value: 2700, description: 'Complex delivery or site inspection.' },
|
|
767
|
+
{ label: '1 hour', value: 3600, description: 'Full hour on-site service.' },
|
|
768
|
+
{ label: '2 hours', value: 7200, description: 'Extended on-site service or installation.' },
|
|
769
|
+
];
|
|
770
|
+
|
|
771
|
+
// ─── Orchestrator: Import Column Mapping Options ──────────────────────────────
|
|
772
|
+
|
|
773
|
+
/**
|
|
774
|
+
* importColumnMappings
|
|
775
|
+
* Canonical column names used by the Orchestrator order import flow.
|
|
776
|
+
* Each entry defines the internal field name, a human-readable label, whether
|
|
777
|
+
* it is required, and example values to guide column mapping.
|
|
778
|
+
*/
|
|
779
|
+
export const importColumnMappings = [
|
|
780
|
+
// Order Identity
|
|
781
|
+
{ field: 'reference', label: 'Order Reference / ID', required: false, example: 'ORD-001', group: 'order' },
|
|
782
|
+
{ field: 'type', label: 'Order Type', required: false, example: 'delivery', group: 'order' },
|
|
783
|
+
{ field: 'notes', label: 'Notes / Instructions', required: false, example: 'Leave at back door', group: 'order' },
|
|
784
|
+
{ field: 'priority', label: 'Priority', required: false, example: '75', group: 'order' },
|
|
785
|
+
// Pickup
|
|
786
|
+
{ field: 'pickup_name', label: 'Pickup Contact Name', required: false, example: 'Warehouse A', group: 'pickup' },
|
|
787
|
+
{ field: 'pickup_address', label: 'Pickup Address', required: true, example: '123 Main St, Sydney NSW 2000', group: 'pickup' },
|
|
788
|
+
{ field: 'pickup_lat', label: 'Pickup Latitude', required: false, example: '-33.8688', group: 'pickup' },
|
|
789
|
+
{ field: 'pickup_lng', label: 'Pickup Longitude', required: false, example: '151.2093', group: 'pickup' },
|
|
790
|
+
{ field: 'pickup_time_window_start', label: 'Pickup Window Start', required: false, example: '08:00', group: 'pickup' },
|
|
791
|
+
{ field: 'pickup_time_window_end', label: 'Pickup Window End', required: false, example: '12:00', group: 'pickup' },
|
|
792
|
+
{ field: 'pickup_service_time', label: 'Pickup Service Time (min)', required: false, example: '10', group: 'pickup' },
|
|
793
|
+
// Dropoff
|
|
794
|
+
{ field: 'dropoff_name', label: 'Dropoff Contact Name', required: false, example: 'John Smith', group: 'dropoff' },
|
|
795
|
+
{ field: 'dropoff_address', label: 'Dropoff Address', required: true, example: '456 Queen St, Melbourne VIC 3000', group: 'dropoff' },
|
|
796
|
+
{ field: 'dropoff_lat', label: 'Dropoff Latitude', required: false, example: '-37.8136', group: 'dropoff' },
|
|
797
|
+
{ field: 'dropoff_lng', label: 'Dropoff Longitude', required: false, example: '144.9631', group: 'dropoff' },
|
|
798
|
+
{ field: 'dropoff_time_window_start', label: 'Delivery Window Start', required: false, example: '12:00', group: 'dropoff' },
|
|
799
|
+
{ field: 'dropoff_time_window_end', label: 'Delivery Window End', required: false, example: '17:00', group: 'dropoff' },
|
|
800
|
+
{ field: 'dropoff_service_time', label: 'Delivery Service Time (min)', required: false, example: '5', group: 'dropoff' },
|
|
801
|
+
// Cargo / Capacity
|
|
802
|
+
{ field: 'weight_kg', label: 'Weight (kg)', required: false, example: '25', group: 'cargo' },
|
|
803
|
+
{ field: 'volume_m3', label: 'Volume (m³)', required: false, example: '0.5', group: 'cargo' },
|
|
804
|
+
{ field: 'pallets', label: 'Pallets', required: false, example: '2', group: 'cargo' },
|
|
805
|
+
{ field: 'parcels', label: 'Parcels / Packages', required: false, example: '10', group: 'cargo' },
|
|
806
|
+
{ field: 'required_skills', label: 'Required Skills (comma-separated)', required: false, example: 'refrigerated,tail_lift', group: 'cargo' },
|
|
807
|
+
// Customer
|
|
808
|
+
{ field: 'customer_name', label: 'Customer Name', required: false, example: 'Acme Corp', group: 'customer' },
|
|
809
|
+
{ field: 'customer_phone', label: 'Customer Phone', required: false, example: '+61400000000', group: 'customer' },
|
|
810
|
+
{ field: 'customer_email', label: 'Customer Email', required: false, example: 'customer@example.com', group: 'customer' },
|
|
811
|
+
];
|
|
812
|
+
|
|
572
813
|
export default function fleetOpsOptions(key) {
|
|
573
814
|
const allOptions = {
|
|
574
815
|
driverTypes,
|
|
@@ -615,6 +856,19 @@ export default function fleetOpsOptions(key) {
|
|
|
615
856
|
transmissionTypes,
|
|
616
857
|
odometerUnits,
|
|
617
858
|
measurementSystems,
|
|
859
|
+
// Orchestrator options
|
|
860
|
+
driverSkills,
|
|
861
|
+
driverSkillsPowerGroups: toPowerSelectGroups(driverSkills),
|
|
862
|
+
vehicleSkills,
|
|
863
|
+
vehicleSkillsPowerGroups: toPowerSelectGroups(vehicleSkills),
|
|
864
|
+
capacityDimensions,
|
|
865
|
+
vehicleCapacityProfiles,
|
|
866
|
+
orderPriorityLevels,
|
|
867
|
+
timeWindowPresets,
|
|
868
|
+
optimizationObjectives,
|
|
869
|
+
routingConstraintOptions,
|
|
870
|
+
serviceTimePresets,
|
|
871
|
+
importColumnMappings,
|
|
618
872
|
};
|
|
619
873
|
|
|
620
874
|
return allOptions[key] ?? [];
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Route Colors Utility
|
|
3
|
+
*
|
|
4
|
+
* Provides deterministic color assignment, color manipulation helpers,
|
|
5
|
+
* and status-based route line styling for the FleetOps map visualization.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* The canonical route color palette used across the live map and order detail views.
|
|
10
|
+
* Colors are chosen for high contrast against both light and dark CartoDB tile layers.
|
|
11
|
+
*/
|
|
12
|
+
export const ROUTE_COLOR_PALETTE = ['#0EA5E9', '#8B5CF6', '#F59E0B', '#10B981', '#F97316', '#EC4899', '#06B6D4', '#EF4444', '#84CC16', '#6366F1'];
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Darken a hex color by a given integer amount (0-255 per channel).
|
|
16
|
+
*
|
|
17
|
+
* @param {string} hex - A CSS hex color string, e.g. "#0EA5E9"
|
|
18
|
+
* @param {number} amount - How much to subtract from each RGB channel
|
|
19
|
+
* @returns {string} Darkened hex color string
|
|
20
|
+
*/
|
|
21
|
+
export function darkenColor(hex, amount = 40) {
|
|
22
|
+
const clean = hex.replace('#', '');
|
|
23
|
+
const num = parseInt(clean, 16);
|
|
24
|
+
const r = Math.max(0, (num >> 16) - amount);
|
|
25
|
+
const g = Math.max(0, ((num >> 8) & 0xff) - amount);
|
|
26
|
+
const b = Math.max(0, (num & 0xff) - amount);
|
|
27
|
+
return `#${[r, g, b].map((v) => v.toString(16).padStart(2, '0')).join('')}`;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Derive a deterministic color from a string identifier (e.g. order public_id).
|
|
32
|
+
* The same ID will always produce the same color from the palette.
|
|
33
|
+
*
|
|
34
|
+
* @param {string} id - Any string identifier
|
|
35
|
+
* @returns {string} A hex color string from ROUTE_COLOR_PALETTE
|
|
36
|
+
*/
|
|
37
|
+
export function colorForId(id = '') {
|
|
38
|
+
let hash = 0;
|
|
39
|
+
for (let i = 0; i < id.length; i++) {
|
|
40
|
+
hash = (hash << 5) - hash + id.charCodeAt(i);
|
|
41
|
+
hash |= 0; // Convert to 32-bit integer
|
|
42
|
+
}
|
|
43
|
+
return ROUTE_COLOR_PALETTE[Math.abs(hash) % ROUTE_COLOR_PALETTE.length];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Return a Leaflet Path options `styles` array for a route polyline
|
|
48
|
+
* based on the order status. Uses a two-layer "cased" approach:
|
|
49
|
+
* a darker, heavier outline beneath a brighter, thinner main line.
|
|
50
|
+
*
|
|
51
|
+
* @param {string} status - The order status string
|
|
52
|
+
* @param {string} color - The base hex color for this route
|
|
53
|
+
* @returns {Array<Object>} Array of Leaflet Path options objects
|
|
54
|
+
*/
|
|
55
|
+
export function routeStyleForStatus(status, color) {
|
|
56
|
+
const darker = darkenColor(color, 40);
|
|
57
|
+
|
|
58
|
+
switch (status) {
|
|
59
|
+
case 'pending':
|
|
60
|
+
return [{ color: '#9CA3AF', weight: 5, opacity: 0.65, dashArray: '8 14', lineCap: 'round', lineJoin: 'round' }];
|
|
61
|
+
|
|
62
|
+
case 'canceled':
|
|
63
|
+
return [{ color: '#EF4444', weight: 4, opacity: 0.4, dashArray: '6 10', lineCap: 'round', lineJoin: 'round' }];
|
|
64
|
+
|
|
65
|
+
case 'completed':
|
|
66
|
+
return [
|
|
67
|
+
{ color: darkenColor('#14B8A6', 30), weight: 7, opacity: 0.5, lineCap: 'round', lineJoin: 'round' },
|
|
68
|
+
{ color: '#14B8A6', weight: 4, opacity: 0.5, lineCap: 'round', lineJoin: 'round' },
|
|
69
|
+
];
|
|
70
|
+
|
|
71
|
+
case 'in_progress':
|
|
72
|
+
case 'dispatched':
|
|
73
|
+
default:
|
|
74
|
+
return [
|
|
75
|
+
{ color: darker, weight: 9, opacity: 0.9, lineCap: 'round', lineJoin: 'round' },
|
|
76
|
+
{ color: color, weight: 5, opacity: 1.0, lineCap: 'round', lineJoin: 'round' },
|
|
77
|
+
];
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Build a Leaflet `L.divIcon` HTML string for a numbered/typed waypoint marker.
|
|
83
|
+
*
|
|
84
|
+
* @param {string|number} label - The label to display inside the circle (e.g. "P", "D", "2")
|
|
85
|
+
* @param {string} bgColor - CSS background color for the circle
|
|
86
|
+
* @returns {string} HTML string suitable for use in `L.divIcon({ html: ... })`
|
|
87
|
+
*/
|
|
88
|
+
export function waypointIconHtml(label, bgColor) {
|
|
89
|
+
return `<div style="
|
|
90
|
+
width:32px;height:32px;
|
|
91
|
+
background:${bgColor};
|
|
92
|
+
border:2.5px solid #fff;
|
|
93
|
+
border-radius:50%;
|
|
94
|
+
display:flex;align-items:center;justify-content:center;
|
|
95
|
+
color:#fff;font-weight:700;font-size:13px;
|
|
96
|
+
box-shadow:0 2px 8px rgba(0,0,0,0.45);
|
|
97
|
+
font-family:ui-sans-serif,system-ui,sans-serif;
|
|
98
|
+
">${label}</div>`;
|
|
99
|
+
}
|