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 }
…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'
…builder styling, proxy error, Optimize spelling
- Add _buildRoutePolyline() to planByVehicle — threads pickup→dropoff
coordinates for each stop in sequence order to draw a straight-line
route polyline per vehicle on the map
- Hide unassigned P/D markers once a plan is proposed; replace with
numbered stop markers (sequence number + vehicle route color) at each
dropoff with a tooltip showing tracking number, address, and arrival
- Add Routes/Timeline tab switcher to PlanViewer right panel
- Routes tab: existing vehicle route cards (unchanged)
- Timeline tab: Gantt-style schedule with hour-tick axis, per-vehicle
rows, and color-coded stop blocks; uses VROOM arrival times when
available, falls back to 30-min slots for greedy engine results
- Phase builder: replace bare <input> with <InputGroup> for the phase
label field; tighten order status pill sizing from rounded-full px-2
text-2xs to rounded px-1.5 text-xs
- Fix proxy .get() error: driver.vehicle is an async belongsTo proxy;
resolve vehicle from availableVehicles by vehicle_id attr instead
- Fix _groupByVehicle: resolve driver from availableDrivers by
driver_id or vehicle_id instead of accessing vehicle.driver proxy
- Rename Optimise→Optimize / Optimisation→Optimization throughout
(phase-builder.js, fleet-ops-options.js, en-us.yaml)
- Add tab-routes and tab-timeline translation keys
…ilder.js and fleet-ops-options.js
- Add _getOrderStops(order) helper that normalises both order shapes:
- Standard: pickup + dropoff → [{label:'P',...}, {label:'D',...}]
- Multi-drop: payload.waypoints sorted by .order → [{label:'1',...}, ...]
using waypoint.place.latitude/longitude
- Add @action getOrderStops(order) wrapper so HBS can call it inline
- Unassigned markers: replace hardcoded pickup/dropoff {{#if}} blocks with
{{#each (this.getOrderStops order) as |stop|}} — works for both shapes
Multi-drop waypoint markers use numeric labels and red colour
- Planned stop markers: same — {{#each (this.getOrderStops item.order)}}
so each waypoint in a multi-drop order gets its own numbered marker
- _buildRoutePolyline: iterate _getOrderStops per stop item so polylines
thread through all waypoints in sequence for multi-drop orders
- _centerMapOnOrders: use _getOrderStops flatMap so map centres correctly
when the order list contains only multi-drop orders
…MAT_ERROR, multi-waypoint support
- plan-viewer.js: full timeline rewrite with real date/time axis, correct
hour-tick positioning, per-block rich tooltips (tracking, time window,
date, customer, pickup/dropoff address), timelineDateRange header
- plan-viewer.hbs: rewritten timeline tab — sticky axis header with date
boundary labels at midnight, per-vehicle Gantt rows with hover tooltips
showing full order details, vertical grid lines aligned to axis ticks
- orchestrator-workbench.js: add _fitMapToPlan() using window.L.latLngBounds
to fitBounds() after proposedPlan is set (deferred 200ms via later());
import { later } from '@ember/runloop'
- orchestrator-workbench.hbs: fix committed badge to use orchestrator.committed-badge
key (no {count} variable) — resolves FORMAT_ERROR on commit
- translations/en-us.yaml: add orchestrator.vehicle key for timeline label column
- Multi-waypoint: _getOrderStops already handles waypoints array; plan-viewer
dropoffAddress falls back to last waypoint place address for multi-drop orders
…GeoJSON coordinate extraction
- Replace straight-line polylines with OSRM routing control via leafletMapManager
- _buildRoutePolyline renamed to _buildRouteWaypoints (returns [[lat,lng],...])
- _fitMapToPlan replaced with _drawRoutingControls (one routing control per vehicle)
- _clearRoutingControls clears controls on discard and before each new run
- Map fitting is handled automatically by leafletMapManager.positionWaypoints
- Removed <layers.polyline> from HBS (routing control draws route itself)
- Fix GeoJSON coordinate extraction in _getOrderStops
- location.coordinates is [lng, lat] (GeoJSON) not {latitude, longitude}
- Read coordinates[1] for lat, coordinates[0] for lng
- Fix isMultiDrop check for plain JSON orders (no Ember Data computed property)
- Check waypoints?.length > 0 instead of payload.isMultiDrop
- Fix formatUnixTime to use en-US locale (prevent Arabic date rendering)
- Fix plan-viewer _resolveStopTime to parse ISO string scheduled_at
- new Date(scheduledAt) handles both Date objects and ISO strings
…imeline in plan-viewer
Use the same @event-calendar/core component already powering the Scheduler
(resourceTimelineDay view) for the orchestrator plan timeline tab.
- Each vehicle group becomes a resource row with color-coded left border,
vehicle name, driver name, and stop count in the label cell
- Each planned stop is an event block showing:
* Sequence number + tracking number (bold)
* Scheduled time range (HH:MM - HH:MM)
* Pickup -> dropoff address route line
* Customer name (when available)
- Event colors match the per-vehicle routeColor used on the map
- Timezone handling via toCalendarDate() (same approach as Scheduler)
- VROOM arrival times used when available; falls back to order.scheduled_at
then to an 08:00 cursor for unscheduled stops
- timelineDate computed getter auto-navigates the calendar to the correct
day based on the earliest stop time in the plan
- Read-only (editable=false) — no drag-drop reschedule in plan review mode
- Removes all custom leftPct/widthPct Gantt math and group-hover tooltip
workarounds; EventCalendar handles layout, overflow, and tooltips natively
…emoval
1. EventCalendar CSS (plan-viewer timeline tab)
- Added id='fleet-ops-orchestrator-plan-timeline' to the timeline wrapper div
- Added matching CSS block in fleetops-engine.css scoped to that ID,
mirroring all scheduler timeline styles (light + dark mode variables,
toolbar, sidebar, col-head, ec-day, ec-event, now-indicator, scrollbar)
- Without the ID the existing CSS scoped to #fleet-ops-scheduler-timeline
had no effect on the plan-viewer EventCalendar instance
2. Route plotting (orchestrator-workbench.js)
- Added this.leafletMapManager.setMap(map) call inside onMapLoad()
- Previously the map was stored only in this.leafletMap (local tracked
property) but never registered with the leafletMapManager service
- addRoutingControl() calls ensureInteractive() -> waitForMap() which
awaits the promise resolved by setMap(); without this call the promise
never resolved and routing controls silently timed out
3. Emoji removal (plan-viewer.js renderEventContent)
- Removed emoji characters (stopwatch, pin, person) from the event tile
HTML strings in renderEventContent
- Time range, route line, and customer name now render as plain text
without any emoji prefixes
1. Timeline: switch EventCalendar view from resourceTimelineDay to resourceTimelineWeek and increase slot width from 80 to 120px so event cards have enough room to display their content without clipping. 2. Event cards: allow address line to wrap up to 2 lines (-webkit-line-clamp:2) and increase gap/padding slightly so all fields are readable. 3. Map center: getUserLocation() (browser/IP geolocation) was overwriting the correct map center set by _centerMapOnOrders() due to a race condition. Now only applies the geolocation fallback when no orders or plan are loaded. 4. Routing waypoints: _buildRouteWaypoints() was prepending the driver/vehicle current GPS location as the first OSRM waypoint, producing 3 coordinates for a 1-order plan with 2 stops. Removed the start-location prepend so only actual order stop coordinates are sent to OSRM. 5. Routing markers: addRoutingControl() now accepts a createMarker option. _drawRoutingControls() passes createMarker: () => null to suppress the routing control's default plain Leaflet markers — the orchestrator renders its own custom numbered div-icon markers via planByVehicle in the HBS. 6. Location marker context: added address fallbacks in _getOrderStops() — if place.address is empty the tooltip/popup falls back to order.pickup_name / order.dropoff_name / payload.pickup.name so markers always show context.
…tion race - Add [Orchestrator] console.log at each point where mapCenter is set: constructor initial coords, getUserLocation resolution, _centerMapOnOrders computed center, and onMapLoad mount-time center. - Introduce _mapCenteredOnOrders boolean flag (set in _centerMapOnOrders) so getUserLocation() cannot override the order-based center even when it resolves before loadOrders completes (the previous array-length guard had a race condition where getUserLocation resolved before unassignedOrders was populated). - onMapLoad now re-applies mapCenter if _mapCenteredOnOrders is already true (handles the case where orders load before the map mounts).
1. Marker labels: planned stop markers were using item.sequence (the order's position in the vehicle route, always 1 for a single-order plan) as the icon label. Changed to stop.label which is 'P' for pickup and 'D' for dropoff (set by _getOrderStops), so each stop is correctly differentiated. 2. Tooltip binding: all three marker blocks (unassigned order stops, driver location, planned route stops) were using <layers.tooltip> and <layers.popup> as standalone children without a block param on the parent <layers.marker>. In ember-leaflet, tooltip and popup must be used as contextual components yielded by the marker block (as |marker|), otherwise they never bind to the marker and do not render. Fixed all three blocks to use 'as |marker|' on <layers.marker> and <marker.tooltip> / <marker.popup> as contextual components — consistent with the pattern used in leaflet-live-map.hbs and place/details.hbs.
…ns, tooltip CSS
1. Run error display: when a run returns zero assignments the right panel
previously stayed blank with no feedback (PlanViewer only renders when
hasProposedPlan is true). Now:
- A warning notification toast fires immediately with the server message
or a fallback i18n string.
- A new @Tracked runError property drives a dedicated empty-state panel
in the right panel (warning icon, message, Try Again button) so the
user always knows what happened.
- runError is cleared at the start of each new run.
2. Map fitBounds centering: _centerMapOnOrders() was computing a raw
arithmetic centroid of all stop coordinates. With stops in Sydney,
Singapore and Mongolia the centroid lands in Brunei — outside the
viewport of any individual cluster. Replaced with fitBounds() using the
bounding box of all stop lats/lngs with 40px padding and maxZoom:14.
Single-point fallback uses setView at zoom 14. onMapLoad now calls
_centerMapOnOrders() directly when orders are already loaded so the
same fitBounds logic applies on map mount. Removed diagnostic console
logs that were added for debugging.
3. Driver avatar icons: the custom orch-marker-driver div-icon (a small
coloured dot) has been replaced with the same icon pattern used by the
live-map: @ICON={{icon iconUrl=driver.vehicle_avatar iconSize=(array 24 24)}}.
The popup now shows the driver photo, name, vehicle name, and online
status in the same layout as the live-map popup. The tooltip shows
name and online/offline status inline.
4. Tooltip CSS: the orchestrator map wrapper div now includes the
next-map-container class alongside fleetbase-leaflet-map-container.
This activates the .next-map-container .leaflet-tooltip rules in
ember-ui/next.css (dark rounded pill, gray-900 bg, gray-100 text,
shadow) — the same styling that the live-map tooltips use.
When a run returns no assignments the right panel now shows two buttons: - Try Again: re-runs the orchestrator with the current resource selection - Change Resources: calls clearRunError() which resets runError, proposedPlan, unassignedAfterRun and orchestratorRunMessage — returning the right panel to the ResourcePanel so the user can re-select drivers/vehicles before running again. Also adds the 'change-resources' i18n key to en-us.yaml.
The frontend phase builder sends mode='optimize_routes' for the 'Optimize Routes' phase. The backend controller only handled 'assign_vehicles', 'assign_drivers', 'optimize', and 'allocate' — so 'optimize_routes' fell through to the legacy else branch which filters out any vehicle without an online driver, producing 'No available vehicles found.' even when vehicles are explicitly selected. Two changes: 1. Order filter: add an explicit optimize_routes branch that applies no vehicle-assignment constraint (the user selects orders manually). 2. Vehicle filter: include optimize_routes in the modes that do not require an online driver — use all explicitly selected vehicles as-is. Also update the controller docblock to document the new mode.
…modal sizing Order pool filters: - Rename 'Unplanned' to 'Unassigned' (no vehicle/driver assigned) — clearer intent - Add 'Today' quick filter (orders scheduled for today's date) - Keep 'Urgent' (priority >= 75), 'Scheduled', 'Imported' filters - Add Advanced filter toggle with: Country (CountrySelect), Order Type, Order Status, Scheduled Date — all client-side filtering - Active advanced filters indicated by yellow dot on toggle button - Clear Filters link shown when any advanced filter is active Import modal: - Use flb--modal-xl for wider modal (1140px at >= 1200px viewport) - Hide footer accept button (hideAcceptButton: true) — navigation is handled entirely by per-step Next/Back buttons inside the modal body - Replace old dot-divider step indicator with tab-style step indicator (border-bottom highlight, numbered circle, transitions) - Fix Browse File button overlap: wrap label in block div so the 'Accepted: csv, xlsx, xls' text renders below it, not behind it - Step 2 forward button now reads 'Next' instead of 'Preview & Import' for consistent step progression language
…n, modal-xl class, flush tabs - Add onFileQueued() action to orchestrator-import.js to handle ember-file-upload FileDropzone/UploadButton pattern (extracts native File from UploadFile wrapper, removes from queue since we handle parsing client-side) - Fix modalClass from 'flb--modal-xl' to 'modal-xl' (correct unprefixed alias) - HBS already uses flush tab nav (no modal-body-container wrapper), FileDropzone with file-queue helper, UploadButton, and body-level navigation buttons - hideAcceptButton: true so only the per-step navigation buttons are shown
… and improved UI
- Redesign orchestrator-import.js with full field schema:
* 7 grouped sections: Order, Pickup, Dropoff, Payload, Customer,
Facilitator, Vehicle/Driver (48 fields total)
* Smart auto-mapping with extensive alias dictionary
* Downloadable CSV template with sample data
* Footer buttons injected via modalsManager.setOption('actionButtons')
so all pagination/step controls render in the modal footer, not content
- Redesign orchestrator-import.hbs:
* Step tabs rendered in a <nav> above content (not inside scrollable area)
* Column mapping grouped by section with collapsible cards
* Preview table shows pickup, dropoff, scheduled, customer, status columns
* Validation errors highlighted in red with row-level error messages
- Update CSS (fleetops-engine.css):
* Larger step tabs: padding 12px 22px, font-size 0.8rem, font-weight 600,
border-bottom 3px, step number badge 22x22px
* New .import-mapping-section and .import-mapping-section-title styles
- Update backend importOrders() in OrchestrationController:
* Groups multi_waypoint rows by order_ref into a single order
* Full entity resolution: Contact, Vendor (customer & facilitator),
Vehicle (by plate number), Driver (by email/phone/name)
* Creates new Contact/Vendor if no existing record found
* Builds Place from structured street/city/state/postal/country/lat/lng fields
* Attaches Payload with pickup/dropoff (pickup_dropoff) or setWaypoints
(multi_waypoint) via existing Payload helper methods
* Wraps each order group in DB::beginTransaction / rollBack on failure
* Resolves OrderConfig by slug/namespace/public_id
- Add col-customer translation key and update missing-dropoff message
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