@fleetbase/fleetops-engine 0.6.38 → 0.6.40
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 +43 -38
- package/addon/components/layout/fleet-ops-sidebar.js +103 -24
- 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 +1088 -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 +52 -0
- package/addon/components/vehicle/form.hbs +51 -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 +79 -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 +4 -2
- 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 +53 -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 +38 -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/migrations/2026_04_14_000001_drop_redundant_capacity_columns_from_payloads_table.php +42 -0
- package/server/migrations/2026_04_14_000002_rename_capacity_columns_on_vehicles_table.php +48 -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/Api/v1/OrderController.php +15 -2
- package/server/src/Http/Controllers/Api/v1/VehicleController.php +18 -2
- 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/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 +18 -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 +380 -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,342 @@
|
|
|
1
|
+
import Component from '@glimmer/component';
|
|
2
|
+
import { tracked } from '@glimmer/tracking';
|
|
3
|
+
import { action } from '@ember/object';
|
|
4
|
+
import { inject as service } from '@ember/service';
|
|
5
|
+
import { addMinutes } from 'date-fns';
|
|
6
|
+
import toCalendarDate from '../../utils/to-calendar-date';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Orchestrator::PlanViewer
|
|
10
|
+
*
|
|
11
|
+
* Displays the proposed plan after a run completes. Shows per-vehicle
|
|
12
|
+
* route cards with ordered stop rows. Supports drag-and-drop manual
|
|
13
|
+
* overrides and expand/collapse per route card.
|
|
14
|
+
*
|
|
15
|
+
* Tabs:
|
|
16
|
+
* - Routes — per-vehicle route cards with ordered stop list
|
|
17
|
+
* - Timeline — EventCalendar resourceTimelineDay view (same library as Scheduler)
|
|
18
|
+
*
|
|
19
|
+
* @arg planByVehicle - Array of { vehicle, driver, orders, routeColor, summary }
|
|
20
|
+
* @arg unassignedAfterRun - Array of orders the engine could not assign
|
|
21
|
+
* @arg runMessage - Optional informational message from the engine
|
|
22
|
+
* @arg onDropOnVehicle - Action(vehicleId, driverId, event)
|
|
23
|
+
* @arg onDragOver - Action(event)
|
|
24
|
+
* @arg onDismissMessage - Action — dismiss the run message banner
|
|
25
|
+
* @arg formatDuration - Helper(seconds) → string
|
|
26
|
+
* @arg formatDistance - Helper(metres) → string
|
|
27
|
+
* @arg formatUnixTime - Helper(unix) → string
|
|
28
|
+
*/
|
|
29
|
+
export default class OrchestratorPlanViewerComponent extends Component {
|
|
30
|
+
@service currentUser;
|
|
31
|
+
|
|
32
|
+
@tracked expandedCards = new Set();
|
|
33
|
+
@tracked activeTab = 'routes'; // 'routes' | 'timeline'
|
|
34
|
+
@tracked _calendar = null;
|
|
35
|
+
|
|
36
|
+
// ── Tab / card actions ────────────────────────────────────────────────────
|
|
37
|
+
|
|
38
|
+
@action setTab(tab) {
|
|
39
|
+
this.activeTab = tab;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
@action toggleCard(vehicleId) {
|
|
43
|
+
const expanded = new Set(this.expandedCards);
|
|
44
|
+
if (expanded.has(vehicleId)) {
|
|
45
|
+
expanded.delete(vehicleId);
|
|
46
|
+
} else {
|
|
47
|
+
expanded.add(vehicleId);
|
|
48
|
+
}
|
|
49
|
+
this.expandedCards = expanded;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
@action expandAll() {
|
|
53
|
+
const ids = (this.args.planByVehicle ?? []).map((g) => g.vehicle?.public_id).filter(Boolean);
|
|
54
|
+
this.expandedCards = new Set(ids);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
@action collapseAll() {
|
|
58
|
+
this.expandedCards = new Set();
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Returns true when every vehicle card is expanded.
|
|
63
|
+
* Used to show the correct label on the single toggle button.
|
|
64
|
+
*/
|
|
65
|
+
get allExpanded() {
|
|
66
|
+
const ids = (this.args.planByVehicle ?? []).map((g) => g.vehicle?.public_id).filter(Boolean);
|
|
67
|
+
return ids.length > 0 && ids.every((id) => this.expandedCards.has(id));
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
@action toggleExpandAll() {
|
|
71
|
+
if (this.allExpanded) {
|
|
72
|
+
this.collapseAll();
|
|
73
|
+
} else {
|
|
74
|
+
this.expandAll();
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* isExpanded — decorated as @action so Glimmer allows it to be invoked
|
|
80
|
+
* with an argument from HBS: (this.isExpanded vehicleId)
|
|
81
|
+
*/
|
|
82
|
+
@action isExpanded(vehicleId) {
|
|
83
|
+
return this.expandedCards.has(vehicleId);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
get hasUnassigned() {
|
|
87
|
+
return (this.args.unassignedAfterRun ?? []).length > 0;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
get totalOrders() {
|
|
91
|
+
return (this.args.planByVehicle ?? []).reduce((sum, g) => sum + (g.orders?.length ?? 0), 0);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
get totalVehicles() {
|
|
95
|
+
return (this.args.planByVehicle ?? []).length;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// ── EventCalendar ─────────────────────────────────────────────────────────
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Returns the IANA timezone string for the current organisation.
|
|
102
|
+
* Falls back to the browser's local timezone.
|
|
103
|
+
*/
|
|
104
|
+
get companyTimezone() {
|
|
105
|
+
return this.currentUser?.company?.timezone ?? Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Receives the EventCalendar instance once it is mounted.
|
|
110
|
+
*/
|
|
111
|
+
@action setCalendarApi(calendar) {
|
|
112
|
+
this._calendar = calendar;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* The date to display on the timeline — derived from the earliest scheduled
|
|
117
|
+
* order in the plan, so the calendar opens on the right day automatically.
|
|
118
|
+
*/
|
|
119
|
+
get timelineDate() {
|
|
120
|
+
const plan = this.args.planByVehicle ?? [];
|
|
121
|
+
let earliest = null;
|
|
122
|
+
for (const group of plan) {
|
|
123
|
+
for (const item of group.orders ?? []) {
|
|
124
|
+
const t = this._resolveStopTime(item);
|
|
125
|
+
if (t !== null) {
|
|
126
|
+
if (earliest === null || t < earliest) earliest = t;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
if (earliest === null) return new Date();
|
|
131
|
+
// Convert the UTC unix timestamp to a wall-clock Date in the company timezone.
|
|
132
|
+
return toCalendarDate(new Date(earliest * 1000), this.companyTimezone);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Resources for EventCalendar — one row per vehicle/driver group.
|
|
137
|
+
* When a driver phase ran (@hasDriverPhase), the row title uses the driver
|
|
138
|
+
* name as the primary identifier; otherwise the vehicle name is used.
|
|
139
|
+
*/
|
|
140
|
+
get calendarResources() {
|
|
141
|
+
const hasDriverPhase = this.args.hasDriverPhase ?? false;
|
|
142
|
+
return (this.args.planByVehicle ?? []).map((group) => {
|
|
143
|
+
const primaryTitle =
|
|
144
|
+
hasDriverPhase && group.driver ? (group.driver.name ?? group.driver.display_name ?? 'Driver') : (group.vehicle?.display_name ?? group.vehicle?.name ?? 'Vehicle');
|
|
145
|
+
return {
|
|
146
|
+
id: group.vehicle?.public_id ?? group.vehicle?.id ?? String(Math.random()),
|
|
147
|
+
title: primaryTitle,
|
|
148
|
+
extendedProps: { group, hasDriverPhase },
|
|
149
|
+
};
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Events for EventCalendar — one event per planned stop.
|
|
155
|
+
* Uses the vehicle's routeColor for consistent color coding.
|
|
156
|
+
*/
|
|
157
|
+
get calendarEvents() {
|
|
158
|
+
const tz = this.companyTimezone;
|
|
159
|
+
const events = [];
|
|
160
|
+
const DURATION_MIN = 30; // default slot duration when no estimate available
|
|
161
|
+
|
|
162
|
+
for (const group of this.args.planByVehicle ?? []) {
|
|
163
|
+
const resourceId = group.vehicle?.public_id ?? group.vehicle?.id;
|
|
164
|
+
const color = group.routeColor ?? '#6366f1';
|
|
165
|
+
|
|
166
|
+
// Cursor used when no arrival time is known — starts at 08:00 on the plan date
|
|
167
|
+
let cursorMs = null;
|
|
168
|
+
|
|
169
|
+
for (const item of group.orders ?? []) {
|
|
170
|
+
const stopTimeSec = this._resolveStopTime(item);
|
|
171
|
+
let startMs;
|
|
172
|
+
|
|
173
|
+
if (stopTimeSec !== null) {
|
|
174
|
+
startMs = stopTimeSec * 1000;
|
|
175
|
+
cursorMs = startMs + DURATION_MIN * 60 * 1000;
|
|
176
|
+
} else {
|
|
177
|
+
// No time known — use cursor or default to 08:00 today
|
|
178
|
+
if (cursorMs === null) {
|
|
179
|
+
const today = new Date();
|
|
180
|
+
today.setHours(8, 0, 0, 0);
|
|
181
|
+
cursorMs = today.getTime();
|
|
182
|
+
}
|
|
183
|
+
startMs = cursorMs;
|
|
184
|
+
cursorMs = startMs + DURATION_MIN * 60 * 1000;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const startUtc = new Date(startMs);
|
|
188
|
+
const endUtc = addMinutes(startUtc, DURATION_MIN);
|
|
189
|
+
|
|
190
|
+
// Convert to "fake local" dates for EventCalendar timezone handling
|
|
191
|
+
const start = toCalendarDate(startUtc, tz);
|
|
192
|
+
const end = toCalendarDate(endUtc, tz);
|
|
193
|
+
|
|
194
|
+
const order = item.order;
|
|
195
|
+
const tracking = order?.tracking ?? order?.public_id ?? '—';
|
|
196
|
+
const customerName = order?.customer_name ?? null;
|
|
197
|
+
const pickupAddress = order?.payload?.pickup?.address ?? order?.pickup_name ?? null;
|
|
198
|
+
const dropoffAddress = order?.payload?.dropoff?.address ?? order?.dropoff_name ?? null;
|
|
199
|
+
const sequence = item.sequence ?? null;
|
|
200
|
+
|
|
201
|
+
events.push({
|
|
202
|
+
id: `plan-${resourceId}-${order?.public_id ?? Math.random()}`,
|
|
203
|
+
resourceId,
|
|
204
|
+
title: tracking,
|
|
205
|
+
start,
|
|
206
|
+
end,
|
|
207
|
+
display: 'block',
|
|
208
|
+
backgroundColor: color,
|
|
209
|
+
borderColor: color,
|
|
210
|
+
textColor: '#ffffff',
|
|
211
|
+
extendedProps: {
|
|
212
|
+
order,
|
|
213
|
+
tracking,
|
|
214
|
+
sequence,
|
|
215
|
+
customerName,
|
|
216
|
+
pickupAddress,
|
|
217
|
+
dropoffAddress,
|
|
218
|
+
startUtc,
|
|
219
|
+
endUtc,
|
|
220
|
+
},
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
return events;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Renders the resource label cell for each timeline row.
|
|
230
|
+
* When a driver phase ran (hasDriverPhase), the driver name is the primary
|
|
231
|
+
* label and the vehicle name is the secondary sub-label. Otherwise the
|
|
232
|
+
* vehicle name is primary and the driver name (if any) is secondary.
|
|
233
|
+
*/
|
|
234
|
+
@action renderResourceLabel({ resource }) {
|
|
235
|
+
const { group, hasDriverPhase } = resource.extendedProps ?? {};
|
|
236
|
+
const vehicleName = group?.vehicle?.display_name ?? group?.vehicle?.name ?? 'Vehicle';
|
|
237
|
+
const driverName = group?.driver?.name ?? '';
|
|
238
|
+
const stopCount = group?.orders?.length ?? 0;
|
|
239
|
+
const color = group?.routeColor ?? '#6366f1';
|
|
240
|
+
|
|
241
|
+
const primaryLabel = hasDriverPhase && driverName ? driverName : vehicleName;
|
|
242
|
+
const secondaryLabel = hasDriverPhase && driverName ? vehicleName : driverName;
|
|
243
|
+
|
|
244
|
+
return {
|
|
245
|
+
html: `<div style="width:100%;box-sizing:border-box;padding:4px 8px;border-left:3px solid ${color};">
|
|
246
|
+
<div style="font-size:0.72rem;font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;width:100%;">${primaryLabel}</div>
|
|
247
|
+
${secondaryLabel ? `<div style="font-size:0.65rem;color:#9ca3af;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;">${secondaryLabel}</div>` : ''}
|
|
248
|
+
<div style="font-size:0.62rem;color:#6b7280;margin-top:1px;">${stopCount} stop${stopCount !== 1 ? 's' : ''}</div>
|
|
249
|
+
</div>`,
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Renders the event tile content inside the timeline.
|
|
255
|
+
* Shows: sequence + tracking number, scheduled time, pickup → dropoff address, customer.
|
|
256
|
+
*/
|
|
257
|
+
@action renderEventContent({ event }) {
|
|
258
|
+
const { tracking, sequence, customerName, pickupAddress, dropoffAddress, startUtc, endUtc } = event.extendedProps ?? {};
|
|
259
|
+
|
|
260
|
+
const fmt = (d) => {
|
|
261
|
+
if (!d) return '';
|
|
262
|
+
return d.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit', hour12: false });
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
const title = sequence != null ? `${sequence}. ${tracking}` : tracking;
|
|
266
|
+
const timeRange = startUtc ? `${fmt(startUtc)} – ${fmt(endUtc)}` : '';
|
|
267
|
+
const routeLine = [pickupAddress, dropoffAddress].filter(Boolean).join(' > ');
|
|
268
|
+
|
|
269
|
+
return {
|
|
270
|
+
html: `<div style="display:flex;flex-direction:column;gap:2px;padding:3px 0;overflow:hidden;height:100%;">
|
|
271
|
+
<div style="display:flex;align-items:center;gap:4px;flex-wrap:nowrap;">
|
|
272
|
+
<span style="width:7px;height:7px;border-radius:50%;background:#ffffff;opacity:0.9;flex-shrink:0;"></span>
|
|
273
|
+
<span style="font-size:0.72rem;font-weight:700;color:#ffffff;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1;min-width:0;">${title}</span>
|
|
274
|
+
</div>
|
|
275
|
+
${timeRange ? `<div style="font-size:0.65rem;color:rgba(255,255,255,0.9);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;line-height:1.3;">${timeRange}</div>` : ''}
|
|
276
|
+
${
|
|
277
|
+
routeLine
|
|
278
|
+
? `<div style="font-size:0.62rem;color:rgba(255,255,255,0.8);overflow:hidden;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;line-height:1.3;word-break:break-word;">${routeLine}</div>`
|
|
279
|
+
: ''
|
|
280
|
+
}
|
|
281
|
+
${
|
|
282
|
+
customerName
|
|
283
|
+
? `<div style="font-size:0.62rem;color:rgba(255,255,255,0.7);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;line-height:1.3;">${customerName}</div>`
|
|
284
|
+
: ''
|
|
285
|
+
}
|
|
286
|
+
</div>`,
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Calendar display options — 24h time format, no header toolbar
|
|
292
|
+
* (navigation is not needed for a read-only plan review).
|
|
293
|
+
*/
|
|
294
|
+
get calendarOptions() {
|
|
295
|
+
return {
|
|
296
|
+
slotLabelFormat: { hour: '2-digit', minute: '2-digit', hour12: false },
|
|
297
|
+
eventTimeFormat: { hour: '2-digit', minute: '2-digit', hour12: false },
|
|
298
|
+
dayHeaderFormat: { weekday: 'short', month: 'numeric', day: 'numeric' },
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
get calendarHeaderToolbar() {
|
|
303
|
+
return { start: '', center: 'title', end: '' };
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// ── Internal helpers ──────────────────────────────────────────────────────
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Resolve the best available Unix timestamp (seconds) for a stop item.
|
|
310
|
+
*
|
|
311
|
+
* Priority:
|
|
312
|
+
* 1. VROOM arrival time (item.arrival — Unix seconds)
|
|
313
|
+
* 2. order.scheduled_at (JS Date object from Ember Data or ISO string from plain JSON)
|
|
314
|
+
* 3. null — caller must use a fallback cursor
|
|
315
|
+
*/
|
|
316
|
+
_resolveStopTime(item) {
|
|
317
|
+
// 1. VROOM arrival time (Unix seconds integer)
|
|
318
|
+
if (item.arrival && typeof item.arrival === 'number') {
|
|
319
|
+
return item.arrival;
|
|
320
|
+
}
|
|
321
|
+
// 2. order.scheduled_at — may be a Date object (Ember Data) or an ISO
|
|
322
|
+
// string (plain JSON from orchestrator/orders endpoint)
|
|
323
|
+
const scheduledAt = item.order?.scheduled_at;
|
|
324
|
+
if (scheduledAt) {
|
|
325
|
+
const d = scheduledAt instanceof Date ? scheduledAt : new Date(scheduledAt);
|
|
326
|
+
if (!isNaN(d.getTime())) {
|
|
327
|
+
return Math.floor(d.getTime() / 1000);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
return null;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* The date range covered by the plan, formatted for the header.
|
|
335
|
+
* e.g. "Thu Apr 10, 2026"
|
|
336
|
+
*/
|
|
337
|
+
get timelineDateRange() {
|
|
338
|
+
const date = this.timelineDate;
|
|
339
|
+
if (!date) return null;
|
|
340
|
+
return date.toLocaleDateString('en-US', { weekday: 'short', month: 'short', day: 'numeric', year: 'numeric' });
|
|
341
|
+
}
|
|
342
|
+
}
|
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
{{! Orchestrator::ResourcePanel — right panel: Vehicles + Drivers tabs }}
|
|
2
|
+
<div class="resource-panel flex flex-col h-full overflow-hidden">
|
|
3
|
+
|
|
4
|
+
{{! Tab bar — matches original border-bottom underline style }}
|
|
5
|
+
<div class="flex border-b dark:border-gray-700 flex-shrink-0">
|
|
6
|
+
<button
|
|
7
|
+
type="button"
|
|
8
|
+
class="flex-1 py-2 pl-2 text-xs font-medium transition-colors
|
|
9
|
+
{{if
|
|
10
|
+
(eq this.activeTab 'vehicles')
|
|
11
|
+
'text-indigo-500 border-b-2 border-indigo-500 dark:text-indigo-400 dark:border-indigo-400'
|
|
12
|
+
'text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-300'
|
|
13
|
+
}}"
|
|
14
|
+
{{on "click" (fn this.setTab "vehicles")}}
|
|
15
|
+
>
|
|
16
|
+
<FaIcon @icon="truck" @size="xs" class="mr-1" />
|
|
17
|
+
{{t "orchestrator.available-vehicles"}}
|
|
18
|
+
<span class="ml-1 text-gray-400 tabular-nums">({{this.filteredVehicles.length}})</span>
|
|
19
|
+
</button>
|
|
20
|
+
<button
|
|
21
|
+
type="button"
|
|
22
|
+
class="flex-1 py-2 text-xs font-medium transition-colors
|
|
23
|
+
{{if
|
|
24
|
+
(eq this.activeTab 'drivers')
|
|
25
|
+
'text-indigo-500 border-b-2 border-indigo-500 dark:text-indigo-400 dark:border-indigo-400'
|
|
26
|
+
'text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-300'
|
|
27
|
+
}}"
|
|
28
|
+
{{on "click" (fn this.setTab "drivers")}}
|
|
29
|
+
>
|
|
30
|
+
<FaIcon @icon="id-card-alt" @size="xs" class="mr-1" />
|
|
31
|
+
{{t "orchestrator.available-drivers"}}
|
|
32
|
+
<span class="ml-1 text-gray-400 tabular-nums">({{this.filteredDrivers.length}})</span>
|
|
33
|
+
</button>
|
|
34
|
+
</div>
|
|
35
|
+
|
|
36
|
+
{{! Search + filter strip — shared, sits above the scrollable list }}
|
|
37
|
+
<div class="px-2 py-1.5 border-b dark:border-gray-700 flex-shrink-0 space-y-1">
|
|
38
|
+
{{! Search input }}
|
|
39
|
+
<div class="relative">
|
|
40
|
+
<FaIcon @icon="search" @size="xs" class="absolute left-2 top-1/2 -translate-y-1/2 text-gray-400 pointer-events-none" />
|
|
41
|
+
{{#if (eq this.activeTab "vehicles")}}
|
|
42
|
+
<input
|
|
43
|
+
type="text"
|
|
44
|
+
class="form-input form-input-sm w-full pl-7i pr-2 py-1 text-xs"
|
|
45
|
+
placeholder={{t "orchestrator.search-vehicles"}}
|
|
46
|
+
value={{this.vehicleSearch}}
|
|
47
|
+
{{on "input" this.onVehicleSearchInput}}
|
|
48
|
+
/>
|
|
49
|
+
{{else}}
|
|
50
|
+
<input
|
|
51
|
+
type="text"
|
|
52
|
+
class="form-input form-input-sm w-full pl-7i pr-2 py-1 text-xs"
|
|
53
|
+
placeholder={{t "orchestrator.search-drivers"}}
|
|
54
|
+
value={{this.driverSearch}}
|
|
55
|
+
{{on "input" this.onDriverSearchInput}}
|
|
56
|
+
/>
|
|
57
|
+
{{/if}}
|
|
58
|
+
</div>
|
|
59
|
+
{{! Filter chips }}
|
|
60
|
+
{{#if (eq this.activeTab "vehicles")}}
|
|
61
|
+
<div class="flex gap-1 flex-wrap">
|
|
62
|
+
<button
|
|
63
|
+
type="button"
|
|
64
|
+
class="px-2 py-0.5 rounded text-xs transition-colors
|
|
65
|
+
{{if
|
|
66
|
+
(eq this.vehicleFilter 'all')
|
|
67
|
+
'bg-indigo-500 text-white'
|
|
68
|
+
'bg-gray-100 dark:bg-gray-700 text-gray-600 dark:text-gray-300 hover:bg-gray-200 dark:hover:bg-gray-600'
|
|
69
|
+
}}"
|
|
70
|
+
{{on "click" (fn this.setVehicleFilter "all")}}
|
|
71
|
+
>{{t "orchestrator.filter-all"}}</button>
|
|
72
|
+
<button
|
|
73
|
+
type="button"
|
|
74
|
+
class="px-2 py-0.5 rounded text-xs transition-colors
|
|
75
|
+
{{if
|
|
76
|
+
(eq this.vehicleFilter 'active')
|
|
77
|
+
'bg-indigo-500 text-white'
|
|
78
|
+
'bg-gray-100 dark:bg-gray-700 text-gray-600 dark:text-gray-300 hover:bg-gray-200 dark:hover:bg-gray-600'
|
|
79
|
+
}}"
|
|
80
|
+
{{on "click" (fn this.setVehicleFilter "active")}}
|
|
81
|
+
>{{t "orchestrator.filter-active"}}</button>
|
|
82
|
+
<button
|
|
83
|
+
type="button"
|
|
84
|
+
class="px-2 py-0.5 rounded text-xs transition-colors
|
|
85
|
+
{{if
|
|
86
|
+
(eq this.vehicleFilter 'available')
|
|
87
|
+
'bg-indigo-500 text-white'
|
|
88
|
+
'bg-gray-100 dark:bg-gray-700 text-gray-600 dark:text-gray-300 hover:bg-gray-200 dark:hover:bg-gray-600'
|
|
89
|
+
}}"
|
|
90
|
+
{{on "click" (fn this.setVehicleFilter "available")}}
|
|
91
|
+
>{{t "orchestrator.filter-available"}}</button>
|
|
92
|
+
<button
|
|
93
|
+
type="button"
|
|
94
|
+
class="px-2 py-0.5 rounded text-xs transition-colors
|
|
95
|
+
{{if
|
|
96
|
+
(eq this.vehicleFilter 'no-driver')
|
|
97
|
+
'bg-indigo-500 text-white'
|
|
98
|
+
'bg-gray-100 dark:bg-gray-700 text-gray-600 dark:text-gray-300 hover:bg-gray-200 dark:hover:bg-gray-600'
|
|
99
|
+
}}"
|
|
100
|
+
{{on "click" (fn this.setVehicleFilter "no-driver")}}
|
|
101
|
+
>{{t "orchestrator.filter-no-driver"}}</button>
|
|
102
|
+
</div>
|
|
103
|
+
{{else}}
|
|
104
|
+
<div class="flex gap-1 flex-wrap">
|
|
105
|
+
<button
|
|
106
|
+
type="button"
|
|
107
|
+
class="px-2 py-0.5 rounded text-xs transition-colors
|
|
108
|
+
{{if
|
|
109
|
+
(eq this.driverFilter 'all')
|
|
110
|
+
'bg-indigo-500 text-white'
|
|
111
|
+
'bg-gray-100 dark:bg-gray-700 text-gray-600 dark:text-gray-300 hover:bg-gray-200 dark:hover:bg-gray-600'
|
|
112
|
+
}}"
|
|
113
|
+
{{on "click" (fn this.setDriverFilter "all")}}
|
|
114
|
+
>{{t "orchestrator.filter-all"}}</button>
|
|
115
|
+
<button
|
|
116
|
+
type="button"
|
|
117
|
+
class="px-2 py-0.5 rounded text-xs transition-colors
|
|
118
|
+
{{if
|
|
119
|
+
(eq this.driverFilter 'online')
|
|
120
|
+
'bg-indigo-500 text-white'
|
|
121
|
+
'bg-gray-100 dark:bg-gray-700 text-gray-600 dark:text-gray-300 hover:bg-gray-200 dark:hover:bg-gray-600'
|
|
122
|
+
}}"
|
|
123
|
+
{{on "click" (fn this.setDriverFilter "online")}}
|
|
124
|
+
>{{t "orchestrator.filter-online"}}</button>
|
|
125
|
+
<button
|
|
126
|
+
type="button"
|
|
127
|
+
class="px-2 py-0.5 rounded text-xs transition-colors
|
|
128
|
+
{{if
|
|
129
|
+
(eq this.driverFilter 'offline')
|
|
130
|
+
'bg-indigo-500 text-white'
|
|
131
|
+
'bg-gray-100 dark:bg-gray-700 text-gray-600 dark:text-gray-300 hover:bg-gray-200 dark:hover:bg-gray-600'
|
|
132
|
+
}}"
|
|
133
|
+
{{on "click" (fn this.setDriverFilter "offline")}}
|
|
134
|
+
>{{t "orchestrator.filter-offline"}}</button>
|
|
135
|
+
<button
|
|
136
|
+
type="button"
|
|
137
|
+
class="px-2 py-0.5 rounded text-xs transition-colors
|
|
138
|
+
{{if
|
|
139
|
+
(eq this.driverFilter 'on-shift')
|
|
140
|
+
'bg-indigo-500 text-white'
|
|
141
|
+
'bg-gray-100 dark:bg-gray-700 text-gray-600 dark:text-gray-300 hover:bg-gray-200 dark:hover:bg-gray-600'
|
|
142
|
+
}}"
|
|
143
|
+
{{on "click" (fn this.setDriverFilter "on-shift")}}
|
|
144
|
+
>{{t "orchestrator.filter-on-job"}}</button>
|
|
145
|
+
</div>
|
|
146
|
+
{{/if}}
|
|
147
|
+
</div>
|
|
148
|
+
|
|
149
|
+
{{! Tab content }}
|
|
150
|
+
<div class="flex-1 overflow-y-auto">
|
|
151
|
+
{{#if @isLoading}}
|
|
152
|
+
<div class="flex justify-center py-8"><Spinner /></div>
|
|
153
|
+
|
|
154
|
+
{{! ── Vehicles tab ── }}
|
|
155
|
+
{{else if (eq this.activeTab "vehicles")}}
|
|
156
|
+
{{#if this.filteredVehicles.length}}
|
|
157
|
+
{{#each this.filteredVehicles as |vehicle|}}
|
|
158
|
+
<button
|
|
159
|
+
type="button"
|
|
160
|
+
class="w-full text-left flex items-start gap-3 px-3 py-2.5 border-b dark:border-gray-700/60 cursor-pointer select-none transition-colors
|
|
161
|
+
{{if
|
|
162
|
+
(includes vehicle.public_id this.selectedVehicleIdsArray)
|
|
163
|
+
'bg-indigo-100 dark:bg-indigo-900/60 hover:bg-indigo-100 dark:hover:bg-indigo-900/60'
|
|
164
|
+
'hover:bg-gray-50 dark:hover:bg-gray-800/60'
|
|
165
|
+
}}"
|
|
166
|
+
{{on "click" (fn @onToggleVehicle vehicle)}}
|
|
167
|
+
>
|
|
168
|
+
{{! Avatar (photo_url) — driver online dot top-right }}
|
|
169
|
+
<div class="relative flex-shrink-0 mt-0.5">
|
|
170
|
+
<img
|
|
171
|
+
src={{vehicle.photo_url}}
|
|
172
|
+
alt={{or vehicle.display_name "Vehicle"}}
|
|
173
|
+
class="w-9 h-9 rounded object-cover bg-gray-200 dark:bg-gray-700"
|
|
174
|
+
{{fallback-img-src (config "defaultValues.vehicleAvatar")}}
|
|
175
|
+
/>
|
|
176
|
+
<span
|
|
177
|
+
class="absolute top-0 left-0 block w-2.5 h-2.5 rounded-full border-2 border-white dark:border-gray-900
|
|
178
|
+
{{if vehicle.driver.online 'bg-green-500' 'bg-gray-400'}}"
|
|
179
|
+
></span>
|
|
180
|
+
</div>
|
|
181
|
+
{{! All fields — always shown, "-" when absent }}
|
|
182
|
+
<div class="flex-1 min-w-0 space-y-0.5">
|
|
183
|
+
{{! Name + vehicle status badge }}
|
|
184
|
+
<div class="flex items-center justify-between gap-1.5">
|
|
185
|
+
<span class="text-xs font-semibold truncate dark:text-white">{{or vehicle.display_name "-"}}</span>
|
|
186
|
+
{{#if vehicle.status}}
|
|
187
|
+
<Badge @status={{vehicle.status}}>{{smart-humanize vehicle.status}}</Badge>
|
|
188
|
+
{{/if}}
|
|
189
|
+
</div>
|
|
190
|
+
{{! Assigned driver }}
|
|
191
|
+
<div class="flex items-center gap-1 text-xs text-gray-500 dark:text-gray-400">
|
|
192
|
+
<FaIcon @icon="id-card-alt" @size="xs" class="w-3 flex-shrink-0" />
|
|
193
|
+
<span class="truncate">{{or vehicle.driver.name (t "orchestrator.no-driver-assigned")}}</span>
|
|
194
|
+
</div>
|
|
195
|
+
{{! Plate number }}
|
|
196
|
+
<div class="flex items-center gap-1 text-xs text-gray-500 dark:text-gray-400">
|
|
197
|
+
<FaIcon @icon="hashtag" @size="xs" class="w-3 flex-shrink-0" />
|
|
198
|
+
<span class="truncate">{{or vehicle.plate_number "-"}}</span>
|
|
199
|
+
</div>
|
|
200
|
+
{{! Call sign }}
|
|
201
|
+
<div class="flex items-center gap-1 text-xs text-gray-500 dark:text-gray-400">
|
|
202
|
+
<FaIcon @icon="broadcast-tower" @size="xs" class="w-3 flex-shrink-0" />
|
|
203
|
+
<span class="truncate">{{or vehicle.call_sign "-"}}</span>
|
|
204
|
+
</div>
|
|
205
|
+
</div>
|
|
206
|
+
</button>
|
|
207
|
+
{{/each}}
|
|
208
|
+
{{else}}
|
|
209
|
+
<div class="flex flex-col items-center justify-center py-12 text-gray-400 text-xs">
|
|
210
|
+
<FaIcon @icon="truck" @size="2x" class="mb-2 opacity-40" />
|
|
211
|
+
{{#if this.vehicleSearch}}
|
|
212
|
+
{{t "orchestrator.no-search-results"}}
|
|
213
|
+
{{else}}
|
|
214
|
+
{{t "orchestrator.no-available-vehicles"}}
|
|
215
|
+
{{/if}}
|
|
216
|
+
</div>
|
|
217
|
+
{{/if}}
|
|
218
|
+
|
|
219
|
+
{{! ── Drivers tab ── }}
|
|
220
|
+
{{else}}
|
|
221
|
+
{{#if this.filteredDrivers.length}}
|
|
222
|
+
{{#each this.filteredDrivers as |driver|}}
|
|
223
|
+
<button
|
|
224
|
+
type="button"
|
|
225
|
+
class="w-full text-left flex items-start gap-3 px-3 py-2.5 border-b dark:border-gray-700/60 cursor-pointer select-none transition-colors
|
|
226
|
+
{{if
|
|
227
|
+
(includes driver.public_id this.selectedDriverIdsArray)
|
|
228
|
+
'bg-indigo-100 dark:bg-indigo-900/60 hover:bg-indigo-100 dark:hover:bg-indigo-900/60'
|
|
229
|
+
'hover:bg-gray-50 dark:hover:bg-gray-800/60'
|
|
230
|
+
}}"
|
|
231
|
+
{{on "click" (fn @onToggleDriver driver)}}
|
|
232
|
+
>
|
|
233
|
+
{{! Avatar — online dot top-right }}
|
|
234
|
+
<div class="relative flex-shrink-0 mt-0.5">
|
|
235
|
+
<img
|
|
236
|
+
src={{driver.photo_url}}
|
|
237
|
+
alt={{or driver.name "Driver"}}
|
|
238
|
+
class="w-9 h-9 rounded-full object-cover bg-gray-200 dark:bg-gray-700"
|
|
239
|
+
{{fallback-img-src (config "defaultValues.driverImage")}}
|
|
240
|
+
/>
|
|
241
|
+
<span
|
|
242
|
+
class="absolute top-0 left-0 block w-2.5 h-2.5 rounded-full border-2 border-white dark:border-gray-900 {{if driver.online 'bg-green-500' 'bg-gray-400'}}"
|
|
243
|
+
></span>
|
|
244
|
+
</div>
|
|
245
|
+
{{! All fields — always shown, "-" when absent }}
|
|
246
|
+
<div class="flex-1 min-w-0 space-y-0.5">
|
|
247
|
+
<div class="text-xs font-semibold truncate dark:text-white">{{or driver.name "-"}}</div>
|
|
248
|
+
{{! Vehicle }}
|
|
249
|
+
<div class="flex items-center gap-1 text-xs text-gray-500 dark:text-gray-400">
|
|
250
|
+
<FaIcon @icon="truck" @size="xs" class="w-3 flex-shrink-0" />
|
|
251
|
+
<span class="truncate">{{or driver.vehicle.display_name (t "orchestrator.no-vehicle-assigned")}}</span>
|
|
252
|
+
</div>
|
|
253
|
+
{{! Phone }}
|
|
254
|
+
<div class="flex items-center gap-1 text-xs text-gray-500 dark:text-gray-400">
|
|
255
|
+
<FaIcon @icon="phone" @size="xs" class="w-3 flex-shrink-0" />
|
|
256
|
+
<span class="truncate">{{or driver.phone "-"}}</span>
|
|
257
|
+
</div>
|
|
258
|
+
{{! Email }}
|
|
259
|
+
<div class="flex items-center gap-1 text-xs text-gray-500 dark:text-gray-400">
|
|
260
|
+
<FaIcon @icon="envelope" @size="xs" class="w-3 flex-shrink-0" />
|
|
261
|
+
<span class="truncate">{{or driver.email "-"}}</span>
|
|
262
|
+
</div>
|
|
263
|
+
</div>
|
|
264
|
+
</button>
|
|
265
|
+
{{/each}}
|
|
266
|
+
{{else}}
|
|
267
|
+
<div class="flex flex-col items-center justify-center py-12 text-gray-400 text-xs">
|
|
268
|
+
<FaIcon @icon="id-card-alt" @size="2x" class="mb-2 opacity-40" />
|
|
269
|
+
{{#if this.driverSearch}}
|
|
270
|
+
{{t "orchestrator.no-search-results"}}
|
|
271
|
+
{{else}}
|
|
272
|
+
{{t "orchestrator.no-available-drivers"}}
|
|
273
|
+
{{/if}}
|
|
274
|
+
</div>
|
|
275
|
+
{{/if}}
|
|
276
|
+
{{/if}}
|
|
277
|
+
</div>
|
|
278
|
+
|
|
279
|
+
{{! Selection info bar }}
|
|
280
|
+
{{#if (or this.selectedDriverIdsArray.length this.selectedVehicleIdsArray.length)}}
|
|
281
|
+
<div class="px-3 py-2 border-t dark:border-gray-700 bg-indigo-900/20 flex items-center justify-between flex-shrink-0">
|
|
282
|
+
<span class="text-xs text-indigo-400">
|
|
283
|
+
{{#if this.selectedVehicleIdsArray.length}}
|
|
284
|
+
{{this.selectedVehicleIdsArray.length}}
|
|
285
|
+
vehicles selected
|
|
286
|
+
{{/if}}
|
|
287
|
+
{{#if (and this.selectedVehicleIdsArray.length this.selectedDriverIdsArray.length)}}
|
|
288
|
+
·
|
|
289
|
+
{{/if}}
|
|
290
|
+
{{#if this.selectedDriverIdsArray.length}}
|
|
291
|
+
{{this.selectedDriverIdsArray.length}}
|
|
292
|
+
{{t "orchestrator.drivers-selected"}}
|
|
293
|
+
{{/if}}
|
|
294
|
+
</span>
|
|
295
|
+
<button type="button" class="text-xs text-gray-400 hover:text-white" {{on "click" this.clearAllSelections}}>
|
|
296
|
+
{{t "orchestrator.clear-selection"}}
|
|
297
|
+
</button>
|
|
298
|
+
</div>
|
|
299
|
+
{{/if}}
|
|
300
|
+
|
|
301
|
+
</div>
|