Skip to content

v0.6.39 ~ added engine registry yield + use correct default asset url…#213

Open
roncodes wants to merge 224 commits intomainfrom
dev-v0.6.39
Open

v0.6.39 ~ added engine registry yield + use correct default asset url…#213
roncodes wants to merge 224 commits intomainfrom
dev-v0.6.39

Conversation

@roncodes
Copy link
Copy Markdown
Member

… for place, driver, vehicle icons

roncodes and others added 30 commits March 30, 2026 13:28
…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.
… 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
… 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 }
roncodes and others added 30 commits April 9, 2026 18:26
…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.
…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
- 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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant