v0.6.39 ~ added engine registry yield + use correct default asset url…#213
Open
v0.6.39 ~ added engine registry yield + use correct default asset url…#213
Conversation
… for place, driver, vehicle icons
…k orders, equipment & parts Completes the existing but incomplete maintenance section in FleetOps, enabling full CRUD for all four maintenance resources: Maintenances, Work Orders, Equipment, and Parts. Frontend: - Uncomment and activate the maintenance sidebar panel (fleet-ops-sidebar.js) - Add Maintenances as first sidebar item with wrench icon - Add maintenances route block to routes.js - New route files for maintenances (index, new, edit, details, details/index) - Fix all work-orders/equipment/parts details+edit routes: add model hook and permission guard - New controllers for maintenances (index, new, edit, details, details/index) - Complete controllers for work-orders, equipment, parts (full columns, save tasks, tabs, action buttons) - New panel-header components for all four resources (HBS + JS) - Fix all new.hbs templates: correct @resource binding (was this.place bug) - Fix all details.hbs: add @headerComponent, TabNavigation with outlet - Fix all edit.hbs: add @headerTitle with resource name - New maintenances templates (maintenances.hbs, index, new, edit, details, details/index) - Add 12 new Universe registries in extension.js for maintenance/work-order/equipment/part - Fix maintenance-actions.js: use maintenance.summary instead of maintenance.name Backend: - Add maintenance, work-order, equipment, part resources to FleetOps auth schema - Add MaintenanceManager policy with full CRUD on all four resources - Update OperationsAdmin policy to include all four maintenance resources - Add Maintenance Technician role - New ProcessMaintenanceTriggers artisan command (time-based + odometer/engine-hour triggers) - Register command and daily schedule in FleetOpsServiceProvider
Connects the FleetOps Driver Eloquent model to the core-api scheduling system by adding polymorphic relationships: - schedules(): MorphMany<Schedule> — driver as subject - scheduleItems(): MorphMany<ScheduleItem> — driver as assignee - activeShiftFor(date): ?ScheduleItem — convenience method for the AllocationPayloadBuilder to retrieve the driver's active shift for a given date and inject start_at/end_at as VROOM time_window constraints - availabilities(): MorphMany<ScheduleAvailability> — time-off and preferred working hour records Also adds required use statements for Schedule, ScheduleItem, MorphMany, and ScheduleAvailability from the core-api namespace. Refs: #214
…i scheduling system
## Backend
### server/src/Models/Driver.php
- Add schedules() MorphMany — driver as polymorphic subject on Schedule
- Add scheduleItems() MorphMany — driver as polymorphic assignee on ScheduleItem
- Add activeShiftFor(date) — convenience method for AllocationPayloadBuilder
to retrieve the driver's active shift and inject time_window constraints
- Add availabilities() MorphMany — time-off and preferred working hours
- Add use statements for Schedule, ScheduleItem, ScheduleAvailability, MorphMany
### server/src/Http/Controllers/Internal/v1/Traits/DriverSchedulingTrait.php (new)
- scheduleItems(id, request) — GET /{id}/schedule-items with date range filter
- availabilities(id, request) — GET /{id}/availabilities with date range filter
- hosStatus(id) — GET /{id}/hos-status, computes daily/weekly hours from shifts
- activeShift(id) — GET /{id}/active-shift, used by AllocationPayloadBuilder
### server/src/Http/Controllers/Internal/v1/DriverController.php
- Use DriverSchedulingTrait to expose the four scheduling endpoints
### server/src/routes.php
- Register four scheduling sub-routes under the internal drivers fleetbaseRoutes group
## Frontend
### addon/components/driver/schedule.js (rewritten)
- Inject fetch, intl, store, driverScheduling, modalsManager, notifications
- loadDriverSchedule task: queries schedule-items via driverScheduling service
- loadAvailability task: queries schedule-availability via store
- loadHOSStatus task: calls /drivers/{id}/hos-status, suppresses 404 gracefully
- addShift, editShift, deleteShift actions using modals/add-driver-shift and
modals/driver-shift
- setAvailability, requestTimeOff, deleteAvailability actions using
modals/set-driver-availability
### addon/components/driver/schedule.hbs (rewritten)
- HOS Status panel (hidden when hosStatus is null)
- Upcoming Shifts panel with edit/delete per-shift buttons
- Availability & Time Off panel with add/delete availability buttons
- Fleetbase UI styling (minimal padding, Tailwind, ContentPanel, Badge, Button)
### addon/components/modals/add-driver-shift.{js,hbs} (new)
- Creates a ScheduleItem with assignee_type=driver
- Supports both single-driver (from driver panel) and multi-driver (from global
scheduler) modes via the hasManyDrivers computed property
### addon/components/modals/driver-shift.{js,hbs} (new)
- Edits an existing ScheduleItem, pre-populates fields from the passed item
### addon/components/modals/set-driver-availability.{js,hbs} (new)
- Shared modal for both Set Availability (is_available=true) and
Request Time Off (is_available=false) flows
### addon/services/driver-actions.js
- Add Schedule tab (component: 'driver/schedule') to the driver view panel tabs
### addon/controllers/operations/scheduler/index.js
- Add driverAvailabilities tracked property
- Enhance events computed to render unavailable availability records as
FullCalendar background events (red-300) so dispatchers see time-off blocks
- Add loadDriverAvailabilities task
- Call loadDriverAvailabilities when switching to 'drivers' view mode
- Use intl.t() for addDriverShift modal title and button text
Refs: #214
…etOps integration ## Backend ### Allocation Engine Architecture (server/src/Allocation/) - AllocationEngineInterface: defines the allocate()/getName()/getIdentifier() contract - AllocationEngineRegistry: singleton service-locator; engines register via resolving() hook - AllocationPayloadBuilder: engine-agnostic normalizer — builds jobs/vehicles arrays from Order/Vehicle models, reads custom fields for skill codes, injects driver shift time_windows from Driver::activeShiftFor() (prerequisite: PR #216 driver scheduling integration) - VroomAllocationEngine: default VROOM implementation; maps normalized payload to VROOM VRP wire format, handles integer ID mapping, parses routes/unassigned back to public_ids ### AllocationController (server/src/Http/Controllers/Internal/v1/) - POST fleet-ops/allocation/run — run engine against unassigned orders + online vehicles - POST fleet-ops/allocation/commit — commit assignments via Order::firstDispatchWithActivity() - GET fleet-ops/allocation/preview — preview without side effects - GET fleet-ops/allocation/engines — list registered engines (for settings dropdown) - GET fleet-ops/allocation/settings — get allocation settings - PATCH fleet-ops/allocation/settings — save allocation settings ### ProcessAllocationJob (server/src/Jobs/) - Queueable, idempotent background job for auto-allocation on order creation or re-allocation - Reads active engine from Setting::lookup('fleetops.allocation_engine', 'vroom') ### HandleDeliveryCompletion (server/src/Listeners/) - Listens on OrderCompleted; dispatches ProcessAllocationJob when auto_reallocate_on_complete is enabled — closes the re-allocation loop ### Provider/Route wiring - FleetOpsServiceProvider: registers AllocationEngineRegistry singleton + VroomAllocationEngine - EventServiceProvider: adds HandleDeliveryCompletion to OrderCompleted listeners - routes.php: adds /allocation group with 6 endpoints under internal v1 fleet-ops prefix ## Frontend ### Engine Registry Pattern (addon/services/) - allocation-engine-interface.js: abstract base class with allocate() contract - allocation-engine.js: registry service — register()/resolve()/has()/availableEngines - vroom-allocation-engine.js: VROOM adapter — delegates to backend AllocationController - order-allocation.js: orchestration service — run/commit/loadSettings/saveSettings tasks ### Instance Initializer (addon/instance-initializers/) - register-vroom-allocation.js: registers VroomAllocationEngine into the allocation-engine registry at app boot — identical pattern to register-osrm.js for route optimization ### Dispatcher Workbench (addon/components/) - order-allocation-workbench.js: three-panel workbench with Order Bucket, Proposed Plan view, Vehicle Bucket; runAllocation/commitPlan/discardPlan tasks; handleDrop for drag-and-drop override; planByVehicle computed groups assignments by vehicle for the plan view - order-allocation-workbench.hbs: full Handlebars template with toolbar, three panels, per-vehicle route cards, unassigned warning banner, override badges, empty states ### Settings UI (addon/controllers/settings/ + addon/templates/settings/) - order-allocation.js controller: loadSettings/saveSettings tasks, engineOptions from registry - order-allocation.hbs template: engine selector (PowerSelect from registry), auto-allocate toggles, max travel time input, balance workload toggle ### Route/Navigation wiring - routes.js: adds operations.allocation and settings.order-allocation routes - routes/operations/allocation.js: ability-guarded route - routes/settings/order-allocation.js: ability-guarded route with setupController hook - templates/operations/allocation.hbs: renders OrderAllocationWorkbench - extension.js: adds Allocation shortcut tile + fleet-ops:template:settings:order-allocation registry - layout/fleet-ops-sidebar.js: adds Allocation to operations nav, Order Allocation to settings nav Closes #214
… sections - work-order/form: split into Identification, Assignment (polymorphic target + assignee with type-driven ModelSelect), Scheduling, and Instructions panels. Added targetTypeOptions, assigneeTypeOptions, onTargetTypeChange, onAssigneeTypeChange, assignTarget, assignAssignee actions. Removed hardcoded 'user' model assumption. - maintenance/form: split into Identification, Asset & Work Order (polymorphic maintainable + performed-by), Scheduling & Readings (odometer, engine_hours, scheduled_at, started_at, completed_at), Costs (MoneyInput for labor_cost, parts_cost, tax, total_cost), and Notes panels. Added full polymorphic type handlers. - equipment/form: split into Photo, Identification (name, code, serial_number, manufacturer, model, type, status), Assignment (polymorphic equipable), and Purchase & Warranty panels. Fixed photo upload to use fetch.uploadFile.perform pattern. Added onEquipableTypeChange / assignEquipable actions. - part/form: split into Photo, Identification (name, sku, serial_number, barcode, manufacturer, model, type, status, description), Inventory (quantity_on_hand, unit_cost, msrp with MoneyInput), Compatibility (polymorphic asset), and Vendor & Warranty panels. Fixed photo upload to use fetch.uploadFile.perform pattern. Added onAssetTypeChange / assignAsset actions. All forms: added MetadataEditor panel, RegistryYield hooks, and CustomField::Yield. All option arrays cross-checked against PHP model fillable arrays and fleetops-data Ember models.
… equipment route name
… and part forms, fix all ContentPanel wrapperClass - equipment/form.hbs: remove standalone Photo ContentPanel; photo block (Image + UploadButton, matching vehicle/form structure) is now the first child of the Identification ContentPanel before the field grid. - part/form.hbs: same restructure as equipment. - All four forms (work-order, maintenance, equipment, part): every ContentPanel now carries @wrapperclass="bordered-top", including the first panel. Previously work-order and maintenance first panels had no wrapperClass at all. - equipment/form.js: equipableTypeOptions converted to { value, label } objects; added @Tracked selectedEquipableType; onEquipableTypeChange now receives option object and reads option.value. - part/form.js: assetTypeOptions converted to { value, label } objects; added @Tracked selectedAssetType; onAssetTypeChange updated similarly. - Both HBS files updated to bind @selected to the tracked option object and render {{option.label}} in the PowerSelect block.
…d-by type selectors
- maintainableTypeOptions: plain strings -> { value, label } objects
(Vehicle, Equipment)
- performedByTypeOptions: plain strings -> { value, label } objects
(Vendor, Driver, User) — added Vendor as a valid performer type
- Added selectedMaintainableType and selectedPerformedByType tracked
properties so the PowerSelect trigger shows the human-readable label
- Both onChange actions now receive the full option object and write
option.value to the model attribute
- Updated TYPE_TO_MODEL to include fleet-ops:vendor -> vendor
- HBS PowerSelect @selected bindings updated to use the tracked option
objects; block params renamed from |type| to |option| with {{option.label}}
…ems panel - Add migration to add public_id column to maintenances, work_orders, equipment, and parts tables (fixes SQLSTATE[42S22] unknown column error) - Replace flat cost ContentPanel with new Maintenance::CostPanel component - Invoice-style line items table with description, qty, unit cost, line total - Inline add/edit/remove rows with optimistic UI updates - Labour and Tax inputs remain as direct MoneyInput fields - Computed totals summary (Labour + Parts + Tax = Total) - All mutations hit dedicated API endpoints and reflect server-recomputed totals - Add addLineItem / updateLineItem / removeLineItem endpoints to MaintenanceController - Register POST/PUT/DELETE line-item sub-routes in routes.php
Resolves Glimmer reactivity assertion error caused by MoneyInput's autoNumerize modifier consuming and mutating @resource.currency in the same render cycle. Following the vehicle/form.hbs pattern, each form now has a single CurrencySelect input at the top of the cost/pricing section. All MoneyInput fields simply read @Currency without @canSelectCurrency or @onCurrencyChange. Files changed: - maintenance/cost-panel.hbs: added CurrencySelect before labour/tax inputs; removed @canSelectCurrency from all MoneyInput fields - equipment/form.hbs: added CurrencySelect before purchase_price; removed @canSelectCurrency/@onCurrencyChange from purchase_price - part/form.hbs: added CurrencySelect before unit_cost/msrp; removed @canSelectCurrency/@onCurrencyChange from both fields
…se/fleetops into feat/complete-maintenance-module
- Add Equipment::Card component (photo, type, status, year, quick actions) - Add Part::Card component (photo, type, qty, unit cost, quick actions) - Equipment index controller: inject appCache, add @Tracked layout, convert actionButtons/bulkActions to getters, add layout toggle dropdown - Parts index controller: same pattern as Equipment - Equipment index template: conditional table vs CardsGrid layout - Parts index template: conditional table vs CardsGrid layout - Layout preference persisted via appCache (fleetops:equipment:layout, fleetops:parts:layout)
Vehicle row dropdown additions: - Schedule Maintenance → opens schedule form pre-filled with vehicle - Create Work Order → opens work order form pre-filled with vehicle - Log Maintenance → opens maintenance form pre-filled with vehicle Vehicle details panel — 3 new tabs: - Schedules: lists active maintenance schedules for the vehicle, empty state with 'Add Schedule' CTA - Work Orders: lists work orders targeting the vehicle, empty state with 'Create Work Order' CTA - Maintenance History: lists completed maintenance records, empty state with 'Log Maintenance' CTA Supporting changes: - vehicle-actions.js: inject scheduleActions/workOrderActions/maintenanceActions, add scheduleMaintenance/createWorkOrder/logMaintenance @action methods - routes.js: add schedules/work-orders/maintenance-history sub-routes under vehicles.index.details; add maintenance.schedules top-level route - Translations: add vehicle.actions.schedule-maintenance/create-work-order/ log-maintenance; add menu.schedules/maintenance-history; add resource.maintenance-schedule(s)
…mmand rewrite Backend changes: - Migration: create maintenance_schedules table with interval fields (time/distance/engine-hours), next-due thresholds, default assignee, and add schedule_uuid FK to work_orders for traceability - MaintenanceSchedule model: isDue(), resetAfterCompletion(), pause(), resume(), complete() methods; polymorphic subject + defaultAssignee relationships; workOrders() hasMany - WorkOrderObserver: on status → 'closed', auto-creates a Maintenance history record from completion data stored in work_order.meta and calls schedule.resetAfterCompletion() to restart the interval cycle - ProcessMaintenanceTriggers rewrite: now reads MaintenanceSchedule instead of Maintenance; resolves vehicle odometer/engine-hours from the polymorphic subject; skips schedules with an existing open WO; auto-creates WorkOrder from schedule defaults on trigger - MaintenanceScheduleController: CRUD via FleetOpsController base + custom pause/resume/trigger endpoints - routes.php: register maintenance-schedules routes with pause/resume/ trigger sub-routes before work-orders - FleetOpsServiceProvider: register WorkOrderObserver
…r update, WO completion panel Frontend changes: - Sidebar: add 'Schedules' (calendar-alt icon) as first item in the Maintenance panel; rename 'Maintenances' entry to 'Maintenance History' (history icon) — order is now: Schedules, Work Orders, Maintenance History, Equipment, Parts - MaintenanceSchedule Ember model: full attr mapping for interval fields, next-due thresholds, default assignee, status, subject polymorphic - schedule-actions service: ResourceActionService subclass with transition/panel/modal patterns + pause(), resume(), triggerNow() actions - schedule/form.hbs + form.js: full create/edit form with Schedule Details, Asset (polymorphic subject), Maintenance Interval (time/distance/hours), and Work Order Defaults (priority, default assignee, instructions) panels - schedule/details.hbs + details.js: read-only details view component - Routes: maintenance.schedules.index (+ new/edit/details sub-routes) - Controllers: schedules/index (columns, actionButtons, bulkActions), schedules/index/details (tabs, actionButtons, edit/triggerNow/delete), schedules/index/new, schedules/index/edit - Templates: schedules index (Layout::Resource::Tabular), new overlay, edit overlay, details overlay - work-order/form.hbs: add Completion Details panel (odometer, engine hours, labour cost, parts cost, tax, notes) shown only when status is set to 'closed'; seeds the WorkOrderObserver auto-log creation - work-order/form.js: add isCompleting getter + six @Tracked completion state fields
… under MySQL 64-char limit The auto-generated name 'maintenance_schedules_default_assignee_type_default_assignee_uuid_index' is 73 characters, exceeding MySQL's 64-character identifier limit. Replaced with explicit short name 'ms_default_assignee_idx'.
…edit/details, fix TYPE_TO_MODEL keys, complete new/edit controllers with save task
…Select displayName, translation keys
…nce_schedules table
… is a real DB column not a computed accessor
…/cell/base across all maintenance controllers
…rvice, calendar, namespace 1. Rename scheduleActions → maintenanceScheduleActions - addon/services/schedule-actions.js → maintenance-schedule-actions.js - app/services/maintenance-schedule-actions.js re-export added - All @service injections and this.scheduleActions refs updated in schedules/index, schedules/index/details, vehicle-actions 2. Convert @Tracked actionButtons/bulkActions/columns → getters - All 5 maintenance index controllers now use get() instead of @Tracked - Prevents Glimmer reactivity assertion errors on render 3. Fix broken @service menuService injection - All 5 details controllers: @service menuService → @service('universe/menu-service') menuService 4. Rename schedule/ component namespace → maintenance-schedule/ - addon/components/schedule/ → addon/components/maintenance-schedule/ - app/components/maintenance-schedule/ re-exports added - Templates updated: Schedule::Form/Details → MaintenanceSchedule::Form/Details - Class names updated to MaintenanceScheduleFormComponent etc. 5. Add calendar visualization to MaintenanceSchedule::Details - details.js: computeOccurrences() + buildCalendarGrid() helpers - Navigable month calendar with scheduled dates highlighted in blue - Upcoming occurrences list (next 6 dates) - Only shown for time-based schedules (interval_method === 'time')
…-orders index getters The sed-based getter conversion left actionButtons and bulkActions getters without their closing } in two controllers: - maintenances/index.js: actionButtons and bulkActions both missing } - work-orders/index.js: bulkActions missing } schedules/index.js, equipment/index.js, and parts/index.js were unaffected.
…O tab, vehicle prefill, cost-panel re-export - ProcessMaintenanceTriggers: auto-generate WO code (WO-YYYYMMDD-XXXXX) and set opened_at on creation - WorkOrder::Details: full details component with overview, assignment, scheduling, and cost breakdown panels (cost breakdown reads from meta.completion_data, shown only when status is closed) - WorkOrder::Form: add prepareForSave action that packs completion tracked fields into meta before save - work-orders new/edit controllers: track formComponent and call prepareForSave before workOrder.save() - Schedules details: add Work Orders tab (route + template) showing all WOs created by this schedule - vehicle-actions: fix subject_type to use namespaced type strings (fleet-ops:vehicle etc) so schedule form pre-selects the correct asset type when opened from the vehicles index row dropdown - app/components/maintenance/cost-panel.js: add missing re-export shim - app/components/maintenance/panel-header.js: add missing re-export shim
…hip accessors
Replace all raw _type / _uuid attr reads and writes with proper
@belongsTo relationship accessors across the maintenance module.
Changes:
- addon/models/maintenance-schedule.js
• Replace subject_type/subject_uuid/subject_name attrs with
@belongsTo('maintenance-subject', {polymorphic:true}) subject
• Replace default_assignee_type/default_assignee_uuid attrs with
@belongsTo('facilitator', {polymorphic:true}) default_assignee
• Add interval_method attr (was missing)
• Remove obsolete raw type/uuid attrs
- addon/components/maintenance-schedule/form.js
• Add MODEL_TO_TYPE + ASSIGNEE_MODEL_TO_TYPE reverse-lookup maps
• Constructor now reads type from resource.subject.constructor.modelName
and resource.default_assignee.constructor.modelName instead of raw attrs
• onSubjectTypeChange / onAssigneeTypeChange clear the relationship
instead of writing _type/_uuid
• assignSubject / assignDefaultAssignee set the relationship only
- addon/components/maintenance-schedule/form.hbs
• @selectedModel binding updated from defaultAssignee → default_assignee
- addon/components/maintenance-schedule/details.hbs
• Asset field reads subject.displayName|name instead of subject_name
- addon/components/work-order/form.js
• Add TARGET_MODEL_TO_TYPE + ASSIGNEE_MODEL_TO_TYPE reverse-lookup maps
• Constructor reads type from target/assignee relationship model names
• onTargetTypeChange / onAssigneeTypeChange clear relationship only
• assignTarget / assignAssignee set relationship only
- addon/components/work-order/details.hbs
• Assignment panel uses target.displayName / assignee.displayName
• Schedule panel uses schedule.name instead of schedule_uuid
- addon/components/maintenance/form.js
• Add MAINTAINABLE_MODEL_TO_TYPE + PERFORMED_BY_MODEL_TO_TYPE maps
• Constructor reads type from maintainable/performed_by relationship
• onMaintainableTypeChange / onPerformedByTypeChange clear relationship
• assignMaintainable / assignPerformedBy set relationship only
- addon/components/maintenance/form.hbs
• @selectedModel binding updated from performedBy → performed_by
- addon/components/maintenance/details.hbs
• Maintainable / Performed By fields use relationship accessors
- addon/services/vehicle-actions.js
• scheduleMaintenance: pass { subject: vehicle } only
• createWorkOrder: pass { target: vehicle } only
• logMaintenance: pass { maintainable: vehicle } only
- addon/components/vehicle/details/schedules.js
• Fix service injection: @service scheduleActions → @service('maintenance-schedule-actions')
- addon/components/vehicle/details/schedules.hbs
• Add Schedule button passes { subject: @vehicle }
- addon/components/vehicle/details/work-orders.hbs
• Create Work Order button passes { target: @vehicle }
- addon/components/vehicle/details/maintenance-history.hbs
• Log Maintenance button passes { maintainable: @vehicle }
…point getCurrentDestinationLocation() returns new Point(0, 0) as a fallback when an order has no dropoff or waypoint set. These 0,0 values were being included in the coordinates response, causing the location service to resolve to the Gulf of Guinea (0,0) instead of a real location. Added the same null + 0,0 filter already used in the drivers, vehicles, and places endpoints — consistent with the existing pattern.
…ter, translation keys
…anslationReducer build error
…es, configurable card fields ## Backend - Add Manifest and ManifestStop models with full relationships (vehicle, driver, order, place, waypoint) - Add 3 migrations: create_manifests_table, create_manifest_stops_table, add_manifest_uuid_to_orders_table - Add ManifestController (index, show, showStop, updateStop, cancel, destroy) - Rewrite AllocationController: commit now generates Manifests with ordered ManifestStops - Add vehicle-only allocation mode (no driver required, uses vehicle.location as fallback) - Add DriverAssignmentEngine for greedy shift-aware driver-to-vehicle matching - Patch AllocationPayloadBuilder with buildVehiclesOnly() method - Add SettingController::getOrchestratorCardFields / saveOrchestratorCardFields - Register all new routes: manifests, manifest-stops, orchestrator-card-fields ## Frontend - Decompose OrchestratorWorkbench monolith into sub-components: - Orchestrator::OrderPool — filterable order list with drag support - Orchestrator::ResourcePanel — vehicles + drivers tabs with selection - Orchestrator::PhaseBuilder — compose multi-phase runs (mode, filters, constraints) - Orchestrator::PlanViewer — post-run route cards with ordered stop rows - Orchestrator::CardFieldsSettings — grouped card field config by order config - Rewrite orchestrator-workbench.js: phase state machine, planByVehicle grouping, manual drag-and-drop overrides, multi-phase execution with auto-commit option - Rewrite orchestrator-workbench.hbs: 3-panel layout with collapsible panels, phase builder panel, card fields panel, plan viewer / resource panel toggle - Add Orchestrator::CardFieldsSettings panel to Settings > Orchestrator page ## Translations - Add 60+ new keys: phase builder, allocation modes, card field labels, resource panel filters, plan viewer, general workbench labels - 169 unique keys in orchestrator section, zero duplicates
…rectory - app/components/orchestrator/order-pool.js - app/components/orchestrator/resource-panel.js - app/components/orchestrator/phase-builder.js - app/components/orchestrator/plan-viewer.js - app/components/orchestrator/card-fields-settings.js
…, leaflet container, card styling, toggle bar - Fix Glimmer render error: cardFieldsForOrder, priorityStatus, isExpanded were plain methods called as helpers in HBS; decorated as @action so they are callable with arguments from templates - Fix search inputs: pl-7 → pl-7i on all three search inputs (order-pool, resource-panel vehicles, resource-panel drivers) - Wrap LeafletMap with fleetbase-leaflet-map-container div in main workbench - Restore original vehicle/driver card styling: full avatar + status badge + all field rows (driver name, plate, call sign, phone, email, vehicle) matching pre-rewrite order-pool-card and vehicle-route-card patterns - Restore original long border-style toggle collapse bars (workbench-panel-toggle class, w-4 full-height bar with border) replacing the floating button style - Restore original plan-viewer vehicle-route-card layout with colour-coded left border, expand/collapse chevron, and assigned-stop drag rows
…rd fields, and leaflet map grey screen
- AllocationController::orderConfigFields: use ->with('customFields') relationship
instead of selecting non-existent 'custom_fields' column (fixes SQLSTATE[42S22])
- order-pool.hbs: remove hardcoded dl block; all card fields now driven exclusively
through resolvedCardFields() — eliminates the duplicate lowercase field rows
- order-pool.js: add DEFAULT_FIELDS fallback with icon/highlight metadata so cards
display correctly even when no card-field settings have been saved; update
resolvedCardFields to return {label, value, icon, iconClass, highlight} objects
- orchestrator-workbench.hbs: fix LeafletMap from named block syntax (<:layers>)
back to standard block syntax (as |layers|) — named blocks are not supported by
ember-leaflet and caused the grey map screen
… click-select, route viz markers/polylines - card-fields-settings: fix response key (configs not order_configs), fix config.fields (not config.custom_fields), decorate isStandardSelected/ isConfigFieldSelected/isMetaKeySelected as @action to avoid Glimmer method-as-helper errors - order-pool: replace order-filter-chip CSS class with explicit px-2 py-0.5 rounded text-xs transition-colors pattern matching vehicle/driver panel filter buttons; add hover:ring-1 outline on unselected cards so click-to- select is visually clear before clicking - orchestrator-workbench: import colorForId, routeStyleForStatus, waypointIconHtml from new route-colors utility (ported from feature/route-visualization-improvements); replace ROUTE_COLORS array; planByVehicle now uses colorForId for deterministic per-vehicle colors and routeStyleForStatus for two-layer cased polyline styles; map markers now use waypointIconHtml divIcon (green P = pickup, red D = dropoff) - route-colors.js: copy utility + app re-export from feature/route-visualization-improvements - fleetops-engine.css: append route visualization CSS from feature branch
… markers
Replace the plain {{hash className=... html=...}} @ICON pattern with the
correct ember-leaflet {{div-icon}} inline helper. The hash pattern passes a
POJO to Leaflet which rejects it because it lacks createIcon() — {{div-icon}}
constructs a proper L.DivIcon instance internally.
Also restore driver location markers (green/red dot) that were dropped in
the route-viz refactor, and add popup/tooltip to order dropoff markers.
…e handles, byConfig key mismatch
- AllocationController::orderConfigFields now returns 'uuid' alongside 'id'
so the byConfig key in saved settings matches order.order_config_uuid
- OrderConfig::customFields() fixed from wrong belongsTo to correct morphMany
so eager-loading via ->with('customFields') actually returns custom field rows
- card-fields-settings: use config.uuid (not config.id/public_id) as byConfig key
in both toggleConfigField and isConfigFieldSelected so selections persist correctly
- card-fields-settings: all ContentPanel blocks now have @OPEN={{true}} and
@wrapperclass='bordered-top' as requested
- settings/orchestrator.hbs: Card Fields ContentPanel changed to @OPEN={{true}}
- orchestrator-workbench.js: added @Tracked leftPanelWidth/rightPanelWidth (288/320px)
and @action startLeftResize / startRightResize with document mousemove/mouseup
drag handlers, min/max clamping, and cursor feedback
- orchestrator-workbench.hbs: resize handle divs already present from previous
commit; JS actions now back them up
…int; unify checkbox styling
- AllocationController::orderConfigFields now filters out any config whose
custom fields collection is empty after eager-load + fallback query, so
the Card Fields settings panel only shows configs that actually have fields
- card-fields-settings.hbs: removed the {{#if config.fields.length}} / {{else}}
block (no longer needed since backend filters) and replaced the two-line
div label (label + key subtext) with the same single-line label/span pattern
used by standard fields; field.key is preserved as a title= tooltip on hover
…ustom field value rendering AllocationPayloadBuilder: - Added static safeMeta($model, $key, $default) helper that wraps getMeta() in a try/catch to handle rows where the meta column is stored as a raw JSON string instead of a decoded array (causes TypeError: Return value must be of type array, string returned in HasMetaAttributes::getAllMeta()) - All getMeta() calls in buildVehicles() and buildVehiclesOnly() now go through safeMeta() so a single bad vehicle row does not abort the entire allocation run order-pool.js resolvedCardFields: - Custom field values are flattened onto the order payload by withCustomFields() using Str::snake(Str::lower(label)) as the top-level key — they are NOT nested under a custom_field_values array on the frontend object - Previous code called order.custom_field_values?.find(v => v.field_key === key) which returned a full CustomFieldValue model instance; Glimmer rendered it as [object Object] - New logic uses three strategies in order: 1. Direct flat property access: order[fieldKey] (covers most cases) 2. Scan the custom_field_values collection matching on customField.name or label 3. Scalar extraction with JSON.stringify fallback for object values - All resolved values are coerced to String before being pushed to the fields array
…custom fields, date locale
VroomAllocationEngine:
- Was calling POST {host}/ — the verso-optim.com API requires POST {base_uri}/solve
- Now reads base URI from config('vroom.base_uri') (set by fleetbase/vroom extension
when installed) → VROOM_HOST env → production default https://api.verso-optim.com/vrp/v1
- API key resolved in order: company Setting 'vroom.api_key' (written by the
fleetbase/vroom extension settings UI) → VROOM_API_KEY env → no key (self-hosted)
- Engine remains fully self-contained; fleetbase/vroom extension is NOT required
- Improved error message includes actionable hint about greedy fallback
GreedyAllocationEngine (new):
- Built-in nearest-vehicle greedy assignment — no external service required
- Registered as 'greedy' in AllocationEngineRegistry via FleetOpsServiceProvider
- Sorts orders by priority desc then scheduled_at asc before assigning
- Useful as a fallback when VROOM is unavailable or for simple deployments
AllocationController:
- Engine call wrapped in try/catch(\RuntimeException) returning structured 503 JSON
instead of an unhandled exception HTML page
- $engineId hoisted before the try block so it is always defined in the catch
fleetops.php config:
- Removed the vroom.host/timeout config block (the engine now reads vroom.base_uri
from the extension config, not a fleetops-specific key)
- Added documentation comment explaining API key resolution order
order-pool.js:
- _formatDate: use 'en-US' locale explicitly instead of undefined (browser/OS
locale was Arabic on some deployments)
- resolvedCardFields custom field lookup:
- Strategy 1: order[fieldKey] ?? order.get(fieldKey) — covers flat properties
pushed by withCustomFields() using Str::snake(Str::lower(label))
- Strategy 2: scan custom_field_values HasMany collection, match on cf.name or
cf.label, extract scalar .value — handles cases where the flat key differs
- Both Ember Data model proxy (.get()) and plain object access patterns used
- Default allocation engine changed from 'vroom' to 'greedy' everywhere:
* _legacyPhase() in orchestrator-workbench.js
* phase.engine fallback in _runSinglePhase
* loadEngines() fallback when API is unavailable
* Backend AllocationController::run() Setting::lookup fallback
- Fix custom field values not rendering on order cards:
* Change orders query from with=customFields to
with=customFieldValues.customField so the hasMany relation
is actually eager-loaded (customFields was the field definitions
relation, not the values)
* Add custom_field_values to Index/Order resource so the serialised
response includes the CFV array with nested custom_field object
(name, label, type) when the relation is loaded
* Simplify resolvedCardFields in order-pool.js — remove the dead
Strategy 1 flat-key approach (Ember Data drops unknown attrs) and
rely solely on scanning the custom_field_values collection matching
by custom_field.name (machine key) or custom_field.label
…dd OrchestrationController + OrchestratorOrderResource; update all frontend endpoints to fleet-ops/orchestrator/* - Revert Index/Order resource (no custom_field_values — keeps tabular view fast) - New Orchestration/ namespace: OrchestrationEngineRegistry, OrchestrationEngineInterface, VroomOrchestrationEngine, GreedyOrchestrationEngine, OrchestrationPayloadBuilder, DriverAssignmentEngine (namespace only) - New OrchestrationController replaces AllocationController with all existing methods plus a new GET orchestrator/orders endpoint using OrchestratorOrderResource - OrchestratorOrderResource: lightweight order shape + custom_field_values with nested custom_field definition — used exclusively by the workbench - Route prefix changed: allocation/ → orchestrator/ - ProcessAllocationJob updated to OrchestrationEngineRegistry + greedy default - Frontend: all fleet-ops/allocation/* calls → fleet-ops/orchestrator/* - loadOrders now calls orchestrator/orders (plain JSON) instead of store.query - order-allocation service: corrected default engine to greedy, updated all endpoints - vroom-allocation-engine: updated endpoint + passes engine: vroom in options
…ionController; clean up all stale references - Deleted server/src/Allocation/ entirely (AllocationEngineRegistry, AllocationEngineInterface, VroomAllocationEngine, GreedyAllocationEngine, AllocationPayloadBuilder, DriverAssignmentEngine) - Deleted AllocationController.php (replaced by OrchestrationController) - Renamed addon/services/allocation-engine-interface.js → orchestration-engine-interface.js - Renamed addon/services/allocation-engine.js → orchestration-engine.js - Updated all class names: AllocationEngineInterfaceService → OrchestrationEngineInterfaceService - Updated register-vroom-allocation initializer to use service:orchestration-engine - Updated orchestrator settings controller to inject service:orchestration-engine - Fixed all stale docblock comments referencing old Allocation class names - Zero remaining references to the old Allocation namespace anywhere in the codebase
…e filenames - app/services/allocation-engine-interface.js → orchestration-engine-interface.js - app/services/allocation-engine.js → orchestration-engine.js - Updated re-export targets to point at the renamed addon service files - vroom-allocation-engine and order-allocation shims unchanged (filenames kept)
…trationPayloadBuilder::buildVehicles Vehicles without an assigned driver (vehicle-only mode) caused an ErrorException when buildVehicles tried to read time_window_start, time_window_end, max_travel_time, skills, and custom_fields directly on a null $driver. Changed all bare $driver-> accesses to the null-safe $driver?-> operator so driverless vehicles are handled gracefully.
…l translation
The commitPlan success notification was calling intl.t('orchestrator.committed')
without the required {count} variable, causing a FORMAT_ERROR. Now passes
result?.committed?.length with a fallback to finalAssignments.length.
…fix 'result is not defined'
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
… for place, driver, vehicle icons