Skip to content

feat(scheduler): unified resource timeline dispatch board#218

Merged
roncodes merged 6 commits intodev-v0.6.39from
feat/unified-scheduler-refactor
Apr 6, 2026
Merged

feat(scheduler): unified resource timeline dispatch board#218
roncodes merged 6 commits intodev-v0.6.39from
feat/unified-scheduler-refactor

Conversation

@roncodes
Copy link
Copy Markdown
Member

@roncodes roncodes commented Apr 5, 2026

Overview

This PR implements the full scheduler refactor outlined in the technical implementation plan. The order scheduler is transformed from a disconnected month-grid into a unified, industry-standard resource timeline dispatch board — with every driver on their own row, orders positioned at their exact scheduled time, and a collapsible sidebar for unscheduled orders.


Files Changed

File Type Description
addon/services/scheduling.js New Central SchedulingService (undo/redo, conflict detection, optimistic assign, bulk assign, best-fit)
addon/routes/operations/scheduler/index.js Modified Parallel model load (orders + drivers + scheduleItems) + socket lifecycle
addon/controllers/operations/scheduler/index.js Modified Full refactor: reactive getters, Ember Concurrency search, bulk selection, undo/redo, socket handlers
addon/templates/operations/scheduler/index.hbs Modified Two-column layout: collapsible sidebar + resource timeline
addon/utils/create-full-calendar-event-from-order.js Modified Removes allDay, adds resourceId + end time + status colours
addon/utils/create-full-calendar-event-from-schedule-item.js New Shift background events for driver rows
addon/components/order/schedule-card.hbs Modified Checkbox multi-select + selected ring highlight
addon/components/modals/scheduling-conflict.hbs New Conflict resolution modal (auto-adjust / assign-anyway)
addon/components/modals/bulk-assign-orders.hbs New Bulk assignment modal
translations/en-us.yaml Modified 22 new scheduler i18n keys

Key Changes in Detail

New: SchedulingService

  • assignOrder(order, driver, start) — optimistic update with automatic rollback on API failure
  • unscheduleOrder(order) — removes driver/time assignment, moves order back to sidebar
  • bulkAssign(orderIds, driver, start) — assigns multiple orders sequentially with gap spacing
  • checkConflicts(driver, start, end) — scans existing events for overlaps before committing
  • findBestFit(order, driver) — calls /best-fit API with heuristic fallback
  • Undo/redo stack (50-entry history); canUndo/canRedo tracked properties

Modified: create-full-calendar-event-from-order.js

Before After
allDay: true Removed — events placed at exact scheduled_at time
No resourceId resourceId: driver_uuid — event renders in correct driver row
No end end derived from estimated_duration (default 60 min)
Single colour Status-based colour (indigo/blue/green/gray/red/orange)

Backend Requirements (follow-up)

  • B1GET /fleet-ops/orders multi-status filter
  • B2Order model: estimated_duration field (integer, minutes)
  • B3Driver model: max_daily_orders field (integer)
  • B4POST /fleet-ops/orders/bulk-schedule endpoint
  • B5GET /fleet-ops/orders/:id/best-fit?driver_uuid= endpoint
  • B6 — Org-wide SocketCluster channel fleet-ops.{org_uuid}

Testing Checklist

  • Drag unscheduled order onto driver row — event appears at correct time in correct row
  • Drag to conflicting slot — conflict modal surfaces with conflicting orders listed
  • Bulk select + assign — all orders assigned, sidebar empties
  • Undo after assignment — order returns to sidebar, calendar event removed
  • Search sidebar — filters with 300 ms debounce
  • Collapse sidebar — timeline expands to full width
  • Add driver shift — shift background block appears on driver row

Breaking Changes

None. SchedulingService is additive. Route model shape is extended but backward-compatible.

## Summary

Complete refactor of the order scheduler from a disconnected month-grid
into a unified, industry-standard resource timeline dispatch board.

## New Files

- addon/services/scheduling.js
  Central SchedulingService with undo/redo stack, conflict detection,
  optimistic assign/unschedule, bulk assign, and best-fit placement.

- addon/utils/create-full-calendar-event-from-schedule-item.js
  Transforms a ScheduleItem record into a FullCalendar background event
  for rendering driver shift windows on the resource timeline.

- addon/components/modals/scheduling-conflict.hbs
  Modal that surfaces detected scheduling conflicts with auto-adjust
  and assign-anyway resolution options.

- addon/components/modals/bulk-assign-orders.hbs
  Modal for assigning multiple selected sidebar orders to a driver
  at a chosen date/time in a single operation.

## Modified Files

- addon/controllers/operations/scheduler/index.js
  - Injects new SchedulingService
  - Reactive computed getters: unscheduledOrders (multi-status, search
    filtered), calendarResources (drivers + capacity), allCalendarEvents
    (order events + shift background blocks)
  - Ember Concurrency debounced search task
  - Bulk selection state (selectedOrderIds Set, toggleOrderSelection,
    selectAllOrders, clearSelection, isOrderSelected, hasSelection)
  - Undo/redo actions delegating to SchedulingService
  - Real-time socket event handlers (order:updated, driver:updated)
  - setViewRange, goToPrev, goToNext, goToToday toolbar actions
  - openBulkAssignModal, addDriverShift actions
  - Replaces hostRouter.refresh() with targeted store updates

- addon/routes/operations/scheduler/index.js
  - Parallel model loading: orders (created + dispatched + active),
    drivers, and schedule items in a single RSVP.hash
  - Socket channel subscription/teardown lifecycle

- addon/templates/operations/scheduler/index.hbs
  - Two-column layout: collapsible sidebar + full-height timeline
  - Toolbar: day/week toggle, prev/today/next, undo/redo, add-shift,
    bulk-assign (contextual), clear-selection
  - Sidebar: debounced search input, select-all, per-order checkboxes,
    FullCalendar::Draggable wrappers, empty state
  - Timeline: FullCalendar resourceTimelineDay with time-precise events,
    resource rows per driver, open-source license key

- addon/utils/create-full-calendar-event-from-order.js
  - Removes allDay: true — events now positioned at exact scheduled time
  - Adds resourceId from driver_uuid for correct driver row placement
  - Derives end time from estimated_duration (default 60 min)
  - Status-based backgroundColor (created=indigo, dispatched=blue,
    active=green, completed=gray, cancelled=red, expired=orange)

- addon/components/order/schedule-card.hbs
  - Adds optional checkbox for multi-select bulk scheduling
  - Applies ring highlight when @selected=true

- translations/en-us.yaml
  - Adds 22 new scheduler i18n keys (view-day, view-week, undo, redo,
    search-orders, select-all, all-scheduled, bulk-assign, conflict-*,
    assign-anyway, auto-adjust, clear-selection, etc.)

## Breaking Changes

None. The new SchedulingService is additive. The route model shape is
extended (adds drivers and scheduleItems) but is backward-compatible.

## Testing Notes

- Unit tests needed for SchedulingService.checkConflicts,
  assignOrder optimistic rollback, and bulkAssign
- Integration tests needed for unscheduledOrders computed getter
  (search filter, status filter)
- E2E: drag unscheduled order onto driver row, verify event appears
  at correct time; trigger conflict, verify modal; undo, verify revert
@roncodes roncodes force-pushed the feat/unified-scheduler-refactor branch from 9fd8376 to 922db3a Compare April 5, 2026 07:17
@roncodes
Copy link
Copy Markdown
Member Author

roncodes commented Apr 5, 2026

Fix: Modal implementations, app re-exports, and callback signatures

This force-push (922db3ab) amends the original commit with three categories of fixes:


1. Modal templates — correct Modal::Default pattern

Both new modal templates were rewritten to match the established pattern used throughout FleetOps (e.g. modals/assign-driver, modals/driver-shift, modals/order-event):

Before (wrong) After (correct)
`<ModalDefault @modal={{@modal}} as modal
<modal.header>, <modal.body>, <modal.footer> block slots <div class="modal-body-container"> inside the component body
@modal.options.foo @options.foo
@modal.title Rendered automatically by Modal::Default from @options.title
Custom <modal.footer> with modal.confirm / modal.decline Footer buttons controlled via @options properties (acceptButtonText, hideDeclineButton, etc.)

modals/bulk-assign-orders.hbs — now uses @options.driver, @options.date, @options.orders with ModelSelect and DateTimeInput matching the assign-driver pattern.

modals/scheduling-conflict.hbs — now uses @options.conflicts, @options.driver, @options.order. Custom resolution buttons (Auto-Adjust, Assign Anyway) are placed inside the body and delegate to the new JS component actions.


2. New JS component — modals/scheduling-conflict.js

The conflict modal requires a backing Glimmer component to expose the autoAdjust and assignAnyway actions (which call @options.autoAdjust / @options.assignAnyway with the correct (modalsManager, done) signature). Added:

addon/components/modals/scheduling-conflict.js

3. Controller callback signatures corrected

All confirm, unschedule, delete, and autoAdjust/assignAnyway callbacks in the controller were updated from async (modal) => to async (modalsManager, done) =>, matching the actual modalsManager.onClickConfirmWithDone signature:

// modalsManager service calls:
confirm(this, done)  // this = modalsManager service, done = close function

4. App re-exports added (the missing piece)

All new addon/ files now have corresponding app/ re-export shims:

New re-export Points to
app/services/scheduling.js @fleetbase/fleetops-engine/services/scheduling
app/utils/create-full-calendar-event-from-schedule-item.js @fleetbase/fleetops-engine/utils/create-full-calendar-event-from-schedule-item
app/components/modals/scheduling-conflict.js @fleetbase/fleetops-engine/components/modals/scheduling-conflict
app/components/modals/bulk-assign-orders.js @fleetbase/fleetops-engine/components/modals/bulk-assign-orders

Total files in this commit: 15 (10 original + 5 new from this fix push)

Base automatically changed from feat/driver-scheduling-integration to dev-v0.6.39 April 6, 2026 06:57
roncodes and others added 2 commits April 6, 2026 14:57
## Summary

Complete refactor of the order scheduler from a disconnected month-grid
into a unified, industry-standard resource timeline dispatch board.

## New Files

- addon/services/scheduling.js
  Central SchedulingService with undo/redo stack, conflict detection,
  optimistic assign/unschedule, bulk assign, and best-fit placement.

- addon/utils/create-full-calendar-event-from-schedule-item.js
  Transforms a ScheduleItem record into a FullCalendar background event
  for rendering driver shift windows on the resource timeline.

- addon/components/modals/scheduling-conflict.hbs
  Modal that surfaces detected scheduling conflicts with auto-adjust
  and assign-anyway resolution options.

- addon/components/modals/bulk-assign-orders.hbs
  Modal for assigning multiple selected sidebar orders to a driver
  at a chosen date/time in a single operation.

## Modified Files

- addon/controllers/operations/scheduler/index.js
  - Injects new SchedulingService
  - Reactive computed getters: unscheduledOrders (multi-status, search
    filtered), calendarResources (drivers + capacity), allCalendarEvents
    (order events + shift background blocks)
  - Ember Concurrency debounced search task
  - Bulk selection state (selectedOrderIds Set, toggleOrderSelection,
    selectAllOrders, clearSelection, isOrderSelected, hasSelection)
  - Undo/redo actions delegating to SchedulingService
  - Real-time socket event handlers (order:updated, driver:updated)
  - setViewRange, goToPrev, goToNext, goToToday toolbar actions
  - openBulkAssignModal, addDriverShift actions
  - Replaces hostRouter.refresh() with targeted store updates

- addon/routes/operations/scheduler/index.js
  - Parallel model loading: orders (created + dispatched + active),
    drivers, and schedule items in a single RSVP.hash
  - Socket channel subscription/teardown lifecycle

- addon/templates/operations/scheduler/index.hbs
  - Two-column layout: collapsible sidebar + full-height timeline
  - Toolbar: day/week toggle, prev/today/next, undo/redo, add-shift,
    bulk-assign (contextual), clear-selection
  - Sidebar: debounced search input, select-all, per-order checkboxes,
    FullCalendar::Draggable wrappers, empty state
  - Timeline: FullCalendar resourceTimelineDay with time-precise events,
    resource rows per driver, open-source license key

- addon/utils/create-full-calendar-event-from-order.js
  - Removes allDay: true — events now positioned at exact scheduled time
  - Adds resourceId from driver_uuid for correct driver row placement
  - Derives end time from estimated_duration (default 60 min)
  - Status-based backgroundColor (created=indigo, dispatched=blue,
    active=green, completed=gray, cancelled=red, expired=orange)

- addon/components/order/schedule-card.hbs
  - Adds optional checkbox for multi-select bulk scheduling
  - Applies ring highlight when @selected=true

- translations/en-us.yaml
  - Adds 22 new scheduler i18n keys (view-day, view-week, undo, redo,
    search-orders, select-all, all-scheduled, bulk-assign, conflict-*,
    assign-anyway, auto-adjust, clear-selection, etc.)

## Breaking Changes

None. The new SchedulingService is additive. The route model shape is
extended (adds drivers and scheduleItems) but is backward-compatible.

## Testing Notes

- Unit tests needed for SchedulingService.checkConflicts,
  assignOrder optimistic rollback, and bulkAssign
- Integration tests needed for unscheduledOrders computed getter
  (search filter, status filter)
- E2E: drag unscheduled order onto driver row, verify event appears
  at correct time; trigger conflict, verify modal; undo, verify revert
@roncodes roncodes force-pushed the feat/unified-scheduler-refactor branch from 57be129 to 0196a96 Compare April 6, 2026 07:02
roncodes and others added 2 commits April 6, 2026 15:03
## Summary

Complete refactor of the order scheduler from a disconnected month-grid
into a unified, industry-standard resource timeline dispatch board.

## New Files

- addon/services/scheduling.js
  Central SchedulingService with undo/redo stack, conflict detection,
  optimistic assign/unschedule, bulk assign, and best-fit placement.

- addon/utils/create-full-calendar-event-from-schedule-item.js
  Transforms a ScheduleItem record into a FullCalendar background event
  for rendering driver shift windows on the resource timeline.

- addon/components/modals/scheduling-conflict.hbs
  Modal that surfaces detected scheduling conflicts with auto-adjust
  and assign-anyway resolution options.

- addon/components/modals/bulk-assign-orders.hbs
  Modal for assigning multiple selected sidebar orders to a driver
  at a chosen date/time in a single operation.

## Modified Files

- addon/controllers/operations/scheduler/index.js
  - Injects new SchedulingService
  - Reactive computed getters: unscheduledOrders (multi-status, search
    filtered), calendarResources (drivers + capacity), allCalendarEvents
    (order events + shift background blocks)
  - Ember Concurrency debounced search task
  - Bulk selection state (selectedOrderIds Set, toggleOrderSelection,
    selectAllOrders, clearSelection, isOrderSelected, hasSelection)
  - Undo/redo actions delegating to SchedulingService
  - Real-time socket event handlers (order:updated, driver:updated)
  - setViewRange, goToPrev, goToNext, goToToday toolbar actions
  - openBulkAssignModal, addDriverShift actions
  - Replaces hostRouter.refresh() with targeted store updates

- addon/routes/operations/scheduler/index.js
  - Parallel model loading: orders (created + dispatched + active),
    drivers, and schedule items in a single RSVP.hash
  - Socket channel subscription/teardown lifecycle

- addon/templates/operations/scheduler/index.hbs
  - Two-column layout: collapsible sidebar + full-height timeline
  - Toolbar: day/week toggle, prev/today/next, undo/redo, add-shift,
    bulk-assign (contextual), clear-selection
  - Sidebar: debounced search input, select-all, per-order checkboxes,
    FullCalendar::Draggable wrappers, empty state
  - Timeline: FullCalendar resourceTimelineDay with time-precise events,
    resource rows per driver, open-source license key

- addon/utils/create-full-calendar-event-from-order.js
  - Removes allDay: true — events now positioned at exact scheduled time
  - Adds resourceId from driver_uuid for correct driver row placement
  - Derives end time from estimated_duration (default 60 min)
  - Status-based backgroundColor (created=indigo, dispatched=blue,
    active=green, completed=gray, cancelled=red, expired=orange)

- addon/components/order/schedule-card.hbs
  - Adds optional checkbox for multi-select bulk scheduling
  - Applies ring highlight when @selected=true

- translations/en-us.yaml
  - Adds 22 new scheduler i18n keys (view-day, view-week, undo, redo,
    search-orders, select-all, all-scheduled, bulk-assign, conflict-*,
    assign-anyway, auto-adjust, clear-selection, etc.)

## Breaking Changes

None. The new SchedulingService is additive. The route model shape is
extended (adds drivers and scheduleItems) but is backward-compatible.

## Testing Notes

- Unit tests needed for SchedulingService.checkConflicts,
  assignOrder optimistic rollback, and bulkAssign
- Integration tests needed for unscheduledOrders computed getter
  (search filter, status filter)
- E2E: drag unscheduled order onto driver row, verify event appears
  at correct time; trigger conflict, verify modal; undo, verify revert
@roncodes roncodes force-pushed the feat/unified-scheduler-refactor branch from 324956a to 4be6ad4 Compare April 6, 2026 07:09
…e/fleetops into feat/unified-scheduler-refactor
@roncodes roncodes merged commit 6e1286f into dev-v0.6.39 Apr 6, 2026
@roncodes roncodes deleted the feat/unified-scheduler-refactor branch April 6, 2026 09:01
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