Skip to content

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

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

v0.6.39 ~ added engine registry yield + use correct default asset url…#213
roncodes wants to merge 184 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 }
Ronald A Richardson and others added 30 commits April 8, 2026 00:48
…ver/vehicle panel

- Map: initialise mapCenter from location service (lat/lng) so the map
  opens on the company/user location instead of the ocean (0,0).
  Falls back to Singapore (1.369, 103.8864) matching the live map.

- Order cards: redesigned to match Order::ScheduleCard pattern —
  header row shows tracking number (order.tracking) + status badge +
  priority badge; body uses a 2-col dl grid with pickup, dropoff,
  scheduled date, and customer; skills tags shown below.

- Right panel (pre-run): replaced the broken 'Available Drivers'
  vehicle list with a proper tabbed UI:
    • Drivers tab — queries the driver model directly, shows avatar,
      online/offline badge (green success / grey default — no more
      red Error), vehicle, phone, shift status, skills.
    • Vehicles tab — shows vehicle name, assigned driver name,
      plate number, skills; online badge only shown when driver exists.
  Both tabs use the same resource-card design (header + dl body).

- clearDriverSelection now also clears selectedVehicleIds so the
  combined selection bar clears both tabs at once.

- Translation keys added: show-orders, hide-orders, show-drivers,
  hide-drivers, no-available-drivers, vehicles-selected; duplicate
  available-vehicles / no-available-vehicles keys removed.
…banner, driver+vehicle selection

Map:
- Call location.getUserLocation() async in constructor; once resolved
  call map.setView() imperatively (tracked @lat/@lng changes are
  ignored by ember-leaflet after initial render).
- onMapLoad also calls setView with current mapCenter so the map
  is positioned correctly on first paint.
- _centerMapOnOrders also calls setView imperatively.

Driver/Vehicle cards:
- Replaced dl-grid panel cards with compact avatar rows:
  32px avatar with online-dot indicator, name + secondary line
  (vehicle or phone for drivers; driver name or plate for vehicles),
  status badge on the right.
- No separate panel header — clean list-item design.
- All empty values fall back to '-' not '_'.

Run message banner:
- Store result.message from backend in @Tracked orchestratorRunMessage.
- Show dismissable amber alert banner below toolbar when message present.
- Toolbar badge label fixed: 'Unassigned: N' (was showing 'Warning').
- dismissRunMessage action clears the banner.

Driver+Vehicle selection logic:
- Drivers tab selection resolves to driver.vehicle.public_id.
- Vehicle IDs from both tabs are unioned (deduplicated Set).
- Falls back to all available vehicles if nothing selected.
- driver_ids sent as hint in payload when drivers explicitly selected.
…next-map-container wrapper, hover-proof selected highlight in dark mode

- Order cards now always render all 6 fields (pickup, dropoff, scheduled, customer, driver assigned, vehicle assigned) with '-' when absent — no conditional hiding
- LeafletMap wrapped in <div class="next-map-container"> for correct tile/styling
- Driver and vehicle card selected state uses bg-indigo-100 dark:bg-indigo-400/20 with matching hover classes so hover can never override the selected highlight
- Badge @Label usages replaced with block content (yield) per ember-ui API
- Added missing orchestrator translation keys: pickup, dropoff, scheduled, customer, driver-assigned, vehicle-assigned, no-vehicle-assigned, no-driver-assigned
…de selected highlight to indigo-900/60

- Added orchestrator.pickup, dropoff, scheduled, customer, driver-assigned,
  vehicle-assigned, no-vehicle-assigned, no-driver-assigned inside the
  orchestrator: YAML section (removed orphaned lines that were outside the section)
- Selected highlight in dark mode changed from indigo-400/20 (too washed out)
  to indigo-900/60 — clearly visible without being overwhelming, one level
  above the default dark card background
…apReady guard and seeding mapCenter synchronously

Root cause: the {{#if this.mapReady}} guard caused the LeafletMap to mount
*after* loadUnassignedOrders set mapReady=true. At that point _centerMapOnOrders
had already run with this.leafletMap=null, so its setView call was silently
skipped. Leaflet then used @lat/@lng from the initial tracked property value
(0,0) because ember-leaflet ignores @lat/@lng changes after first render.

Fix:
- Remove the {{#if this.mapReady}} guard entirely — LeafletMap always renders
- Seed mapCenter synchronously in the constructor via location.getLatitude() /
  location.getLongitude() (returns Singapore 1.369,103.8864 synchronously)
- onMapLoad now always fires before any data tasks complete, so setView has a
  valid map reference and positions correctly to Singapore (or real location)
- _centerMapOnOrders still calls setView after orders load to re-centre on data
- getUserLocation() async call still updates to real location when resolved
…d access

- Add console.log in constructor showing exact lat/lng returned by location
  service and the resulting mapCenter value
- Add console.log in onMapLoad showing the coordinates passed to setView
  and whether map.setView is available
- Fix falsy guard: change 'if (lat && lng)' to 'if (lat != null && lng != null)'
  so coordinate 0 is not treated as falsy
- Fix Ember proxy error in runOrchestration: use o.get('public_id') pattern
  for unassignedOrders, availableVehicles, and availableDrivers to handle
  PromiseObject proxies that require .get() for property access
…le tabs

- Add driverSearch, driverFilter, vehicleSearch, vehicleFilter tracked state
- Add onDriverSearchInput, setDriverFilter, onVehicleSearchInput, setVehicleFilter actions
- Add filteredDrivers getter: searches name, phone, email, vehicle name;
  filters by online/offline/on-shift
- Add filteredVehicles getter: searches display_name, plate_number, call_sign,
  driver name; filters by active status / no-driver
- Add shared search input + filter chip strip above the scrollable list in the
  right panel (sits between tab bar and overflow-y-auto list)
- Switch {{#each}} loops to use filteredDrivers / filteredVehicles
- Tab count badges now show filtered count
- Empty states show 'No results match your search' when a search is active
- Add 10 new translation keys: search-drivers, search-vehicles, no-search-results,
  filter-online, filter-offline, filter-on-shift, filter-active, filter-no-driver
- All 54 orchestrator.* keys now present in en-us.yaml (zero missing)
The mapReady guard removal left a dangling {{else}}...{{/if}} block after
the closing </LeafletMap> tag, causing a Glimmer parse error:
'Expecting EOF, got INVERSE' at line 354.
- onMapLoad now destructures { target: map } from the Leaflet event object,
  matching the live map's didLoad({ target: map }) pattern. Previously the
  whole event object was passed as 'map', so map.setView was undefined and
  setView was never called — leaving the map at 0,0 (the ocean).
- tileSourceUrl now uses CartoCDN (dark_all / light_all) matching the live
  map, instead of OpenStreetMap tiles.
- Remove debug console.log from constructor.
…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.
…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
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