@fleetbase/fleetops-engine 0.6.25 → 0.6.27

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/DRIVER_SCHEDULING.md +186 -0
  2. package/addon/components/driver/schedule.hbs +100 -0
  3. package/addon/components/driver/schedule.js +267 -0
  4. package/addon/components/order/kanban-card.hbs +2 -2
  5. package/addon/components/vehicle/details.hbs +594 -4
  6. package/addon/components/vehicle/form.hbs +467 -41
  7. package/addon/controllers/analytics/reports/index.js +3 -2
  8. package/addon/controllers/connectivity/devices/index.js +3 -3
  9. package/addon/controllers/connectivity/events/index.js +3 -2
  10. package/addon/controllers/connectivity/sensors/index.js +3 -5
  11. package/addon/controllers/connectivity/telematics/index.js +3 -1
  12. package/addon/controllers/maintenance/equipment/index.js +4 -4
  13. package/addon/controllers/maintenance/parts/index.js +4 -4
  14. package/addon/controllers/maintenance/work-orders/index.js +4 -4
  15. package/addon/controllers/management/contacts/customers.js +12 -10
  16. package/addon/controllers/management/contacts/index.js +3 -10
  17. package/addon/controllers/management/drivers/index/details.js +26 -13
  18. package/addon/controllers/management/drivers/index.js +4 -16
  19. package/addon/controllers/management/fleets/index.js +3 -13
  20. package/addon/controllers/management/fuel-reports/index.js +3 -10
  21. package/addon/controllers/management/issues/index.js +3 -12
  22. package/addon/controllers/management/places/index.js +4 -12
  23. package/addon/controllers/management/vehicles/index.js +3 -13
  24. package/addon/controllers/management/vendors/index.js +3 -13
  25. package/addon/controllers/operations/orders/index.js +5 -22
  26. package/addon/controllers/operations/scheduler/index.js +195 -6
  27. package/addon/controllers/operations/service-rates/index.js +34 -34
  28. package/addon/controllers/settings/payments/index.js +0 -6
  29. package/addon/routes.js +1 -0
  30. package/addon/services/driver-scheduling.js +171 -0
  31. package/addon/services/leaflet-layer-visibility-manager.js +4 -1
  32. package/addon/services/service-rate-actions.js +5 -1
  33. package/addon/templates/management/drivers/index/details/positions.hbs +1 -2
  34. package/addon/templates/management/drivers/index/details/schedule.hbs +1 -2
  35. package/addon/templates/operations/scheduler/index.hbs +48 -10
  36. package/addon/utils/fleet-ops-options.js +86 -0
  37. package/app/services/driver-scheduling.js +1 -0
  38. package/composer.json +1 -1
  39. package/extension.json +1 -1
  40. package/package.json +3 -3
  41. package/server/migrations/2025_11_17_033648_add_additional_columns_to_vehicles_table.php +142 -0
  42. package/server/src/Constraints/HOSConstraint.php +244 -0
  43. package/server/src/Http/Controllers/Api/v1/OrderController.php +1 -1
  44. package/server/src/Http/Controllers/Internal/v1/OrderController.php +8 -3
  45. package/server/src/Http/Resources/v1/Vehicle.php +197 -19
  46. package/server/src/Http/Resources/v1/VehicleWithoutDriver.php +211 -28
  47. package/server/src/Models/Driver.php +12 -8
  48. package/server/src/Models/Place.php +2 -2
  49. package/server/src/Models/Vehicle.php +101 -15
@@ -0,0 +1,186 @@
1
+ # FleetOps Driver Scheduling Module
2
+
3
+ This module implements driver scheduling with Hours of Service (HOS) compliance for FleetOps.
4
+
5
+ ## Features
6
+
7
+ - **Fleet-Wide Scheduler**: Manage all drivers' schedules in a single calendar view
8
+ - **Driver Schedule Management**: Create, edit, and manage driver shifts
9
+ - **HOS Compliance**: Real-time validation against FMCSA regulations
10
+ - **Calendar View**: Visual schedule management with drag-and-drop
11
+ - **Resource Timeline**: View multiple drivers' schedules simultaneously
12
+ - **Individual Driver Schedules**: Dedicated schedule view on driver detail pages
13
+ - **Availability Management**: Track driver availability and time-off requests
14
+ - **Conflict Detection**: Automatic detection of scheduling conflicts
15
+ - **Dual View Modes**: Toggle between order scheduling and driver scheduling
16
+
17
+ ## Components
18
+
19
+ ### Operations/Scheduler (Fleet-Wide View)
20
+
21
+ The main scheduler view at `operations/scheduler` now supports both order scheduling and driver scheduling.
22
+
23
+ **Location**: `addon/controllers/operations/scheduler/index.js`
24
+
25
+ **Features**:
26
+ - **View Mode Toggle**: Switch between "Orders" and "Driver Schedules" modes
27
+ - **Resource Timeline**: In driver mode, shows all drivers as resources with their shifts
28
+ - **Drag-and-Drop**: Drag shifts between drivers or reschedule by dragging
29
+ - **Add Shift**: Quick action button to create new driver shifts
30
+ - **Real-Time Updates**: Calendar updates automatically when shifts are modified
31
+ - **Status Colors**: Visual indicators for shift status (pending, confirmed, etc.)
32
+
33
+ **View Modes**:
34
+ 1. **Orders Mode** (default):
35
+ - Month calendar view
36
+ - Drag unscheduled orders to calendar
37
+ - Manage order scheduling
38
+
39
+ 2. **Driver Schedules Mode**:
40
+ - Resource timeline view (week view with drivers as resources)
41
+ - View all drivers' shifts simultaneously
42
+ - Drag shifts between drivers
43
+ - Reschedule shifts by dragging
44
+ - Click shifts to view/edit details
45
+
46
+ **Usage**:
47
+ Navigate to Operations → Scheduler, then toggle between "Orders" and "Driver Schedules" using the header buttons.
48
+
49
+ ### Driver::Schedule
50
+
51
+ Displays and manages a driver's schedule from their detail page.
52
+
53
+ **Location**: `addon/components/driver/schedule.js`
54
+
55
+ **Features**:
56
+ - HOS compliance dashboard with visual indicators
57
+ - Weekly calendar view of driver shifts
58
+ - Upcoming shifts list (next 5 shifts)
59
+ - Availability and time-off management
60
+ - Quick actions for adding shifts and requesting time off
61
+
62
+ **Usage**:
63
+ ```handlebars
64
+ <Driver::Schedule @driver={{@model}} />
65
+ ```
66
+
67
+ **Integration**: Add a "Schedule" tab to the driver detail page.
68
+
69
+ ## Backend Components
70
+
71
+ ### HOSConstraint
72
+
73
+ Validates schedule items against FMCSA Hours of Service regulations.
74
+
75
+ **Location**: `server/src/Constraints/HOSConstraint.php`
76
+
77
+ **Regulations Enforced**:
78
+ 1. **11-Hour Driving Limit**: Maximum 11 hours driving after 10 consecutive hours off duty
79
+ 2. **14-Hour Duty Window**: Cannot drive beyond 14th hour after coming on duty
80
+ 3. **60/70-Hour Weekly Limit**: Cannot drive after 60/70 hours in 7/8 consecutive days
81
+ 4. **30-Minute Break**: Required after 8 cumulative hours of driving
82
+
83
+ **Violation Severity**:
84
+ - `critical`: Immediate compliance issue, shift cannot be scheduled
85
+ - `warning`: Approaching limit, requires attention
86
+
87
+ **Registration**:
88
+ ```php
89
+ // In FleetOps ServiceProvider boot() method
90
+ $constraintService = app(\Fleetbase\Services\Scheduling\ConstraintService::class);
91
+ $constraintService->register('driver', \Fleetbase\FleetOps\Constraints\HOSConstraint::class);
92
+ ```
93
+
94
+ ## HOS Compliance Dashboard
95
+
96
+ The HOS dashboard displays:
97
+ - **Daily Driving Hours**: X/11 hours with circular progress indicator
98
+ - **Weekly Hours**: X/70 hours with circular progress indicator
99
+ - **Compliance Status**: Badge indicating compliance level
100
+ - Green: Compliant
101
+ - Yellow: Approaching Limit
102
+ - Red: At Limit
103
+
104
+ ## API Endpoints
105
+
106
+ FleetOps extends the core scheduling endpoints with driver-specific functionality:
107
+
108
+ - `GET /drivers/{id}/hos-status` - Get HOS compliance status for a driver
109
+ - `GET /drivers/{id}/schedule` - Get schedule items for a driver
110
+ - `POST /drivers/{id}/schedule` - Create a new shift for a driver
111
+ - `PUT /schedule-items/{id}` - Update a shift (with HOS validation)
112
+ - `DELETE /schedule-items/{id}` - Delete a shift
113
+
114
+ ## Workflow
115
+
116
+ ### Creating a Driver Shift
117
+
118
+ 1. User clicks "Add Shift" button
119
+ 2. Modal opens with shift form
120
+ 3. User selects date, time, vehicle, and other details
121
+ 4. System validates against HOS constraints
122
+ 5. If violations found, display warnings/errors
123
+ 6. If valid, create schedule item
124
+ 7. Update driver schedule view and HOS dashboard
125
+
126
+ ### HOS Validation
127
+
128
+ 1. When a schedule item is created/updated
129
+ 2. `HOSConstraint::validate()` is called
130
+ 3. Checks all four HOS regulations
131
+ 4. Returns violations array if any
132
+ 5. Frontend displays violations to user
133
+ 6. User can adjust shift or override (with proper permissions)
134
+
135
+ ## Integration with Core Scheduling
136
+
137
+ FleetOps uses the core scheduling module with driver-specific customizations:
138
+
139
+ **Core Components Used**:
140
+ - `Schedule` model (subject_type: 'fleet', subject_uuid: fleet.id)
141
+ - `ScheduleItem` model (assignee_type: 'driver', assignee_uuid: driver.id)
142
+ - `ScheduleAvailability` model (subject_type: 'driver')
143
+ - `ScheduleConstraint` model (subject_type: 'driver')
144
+
145
+ **FleetOps Extensions**:
146
+ - `HOSConstraint` for compliance validation
147
+ - `Driver::Schedule` component for driver-specific UI
148
+ - HOS status API endpoint
149
+ - Driver-specific scheduling logic
150
+
151
+ ## Future Enhancements
152
+
153
+ - **Automatic Schedule Generation**: AI-powered schedule optimization
154
+ - **ELD Integration**: Sync with Electronic Logging Devices
155
+ - **Predictive HOS**: Forecast HOS availability for future dates
156
+ - **Mobile App**: Driver-facing mobile app for schedule viewing
157
+ - **Notifications**: Real-time alerts for schedule changes and HOS warnings
158
+ - **Reporting**: HOS compliance reports and analytics
159
+
160
+ ## Testing
161
+
162
+ ### HOS Constraint Tests
163
+
164
+ Test cases should cover:
165
+ - 11-hour driving limit enforcement
166
+ - 14-hour duty window enforcement
167
+ - 60/70-hour weekly limit enforcement
168
+ - 30-minute break requirement
169
+ - Edge cases (consecutive shifts, split shifts, etc.)
170
+
171
+ ### Integration Tests
172
+
173
+ - Create driver shift via API
174
+ - Validate HOS constraints are enforced
175
+ - Update shift and verify re-validation
176
+ - Delete shift and verify HOS recalculation
177
+ - Test driver schedule view rendering
178
+
179
+ ## Compliance Notes
180
+
181
+ This implementation follows FMCSA Hours of Service regulations as of 2025. Regulations may vary by:
182
+ - Jurisdiction (US Federal, state-specific, Canada, etc.)
183
+ - Vehicle type (property-carrying vs. passenger-carrying)
184
+ - Industry (short-haul vs. long-haul)
185
+
186
+ **Important**: This is a software implementation and should not be the sole method of HOS compliance. Proper driver training, ELD integration, and regular audits are essential for full compliance.
@@ -0,0 +1,100 @@
1
+ <div class="driver-schedule-container" ...attributes>
2
+ <ContentPanel @title="Hours of Service Status" @open={{true}} @isLoading={{this.loadHOSStatus.isRunning}} @wrapperClass="bordered-top">
3
+ {{#if this.hosStatus}}
4
+ <div class="grid grid-cols-1 md:grid-cols-3 gap-4">
5
+ <div class="flex items-center justify-between p-3 bg-gray-50 dark:bg-gray-700 rounded">
6
+ <div>
7
+ <div class="text-sm text-gray-500 dark:text-gray-400">Daily Driving Hours</div>
8
+ <div class="text-2xl font-bold">{{this.hosStatus.daily_hours}}/11</div>
9
+ </div>
10
+ <div class="w-16 h-16">
11
+ <CircularProgress @value={{this.hosStatus.daily_hours}} @max={{11}} />
12
+ </div>
13
+ </div>
14
+
15
+ <div class="flex items-center justify-between p-3 bg-gray-50 dark:bg-gray-700 rounded">
16
+ <div>
17
+ <div class="text-sm text-gray-500 dark:text-gray-400">Weekly Hours</div>
18
+ <div class="text-2xl font-bold">{{this.hosStatus.weekly_hours}}/70</div>
19
+ </div>
20
+ <div class="w-16 h-16">
21
+ <CircularProgress @value={{this.hosStatus.weekly_hours}} @max={{70}} />
22
+ </div>
23
+ </div>
24
+
25
+ <div class="flex items-center justify-between p-3 bg-gray-50 dark:bg-gray-700 rounded">
26
+ <div>
27
+ <div class="text-sm text-gray-500 dark:text-gray-400">Compliance Status</div>
28
+ <span
29
+ class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-{{this.hosComplianceBadge.color}}-100 text-{{this.hosComplianceBadge.color}}-800"
30
+ >
31
+ {{this.hosComplianceBadge.text}}
32
+ </span>
33
+ </div>
34
+ </div>
35
+ </div>
36
+ {{/if}}
37
+ </ContentPanel>
38
+ <ContentPanel @title="Schedule Calendar" @open={{true}} @isLoading={{this.loadDriverSchedule.isRunning}} @actionButtons={{this.scheduleActionButtons}} @wrapperClass="bordered-top">
39
+ <ScheduleCalendar @items={{this.scheduleItems}} @view="timeGridWeek" @onItemClick={{this.handleItemClick}}>
40
+ <:item as |ctx|>
41
+ <div class="schedule-item-content">
42
+ <div class="font-medium">{{ctx.item.title}}</div>
43
+ <div class="text-xs">{{ctx.item.duration}} min</div>
44
+ </div>
45
+ </:item>
46
+ </ScheduleCalendar>
47
+ </ContentPanel>
48
+
49
+ <ContentPanel @title="Upcoming Shifts" @open={{true}} @actionButtons={{this.shiftActionButtons}} @wrapperClass="bordered-top">
50
+ {{#if this.upcomingShifts.length}}
51
+ <div class="space-y-2">
52
+ {{#each this.upcomingShifts as |shift|}}
53
+ <ScheduleItemCard @item={{shift}} @onClick={{this.handleItemClick}}>
54
+ <:actions>
55
+ <button type="button" class="text-blue-600 hover:text-blue-800 text-sm" {{on "click" (fn this.editScheduleItem shift)}}>
56
+ Edit
57
+ </button>
58
+ </:actions>
59
+ </ScheduleItemCard>
60
+ {{/each}}
61
+ </div>
62
+ {{else}}
63
+ <div class="text-center py-4 text-gray-500 dark:text-gray-400">
64
+ No upcoming shifts scheduled
65
+ </div>
66
+ {{/if}}
67
+ </ContentPanel>
68
+
69
+ <ContentPanel @title="Availability & Time Off" @open={{true}} @actionButtons={{this.availabilityActionButtons}} @wrapperClass="bordered-top">
70
+ {{#if this.availability.length}}
71
+ <div class="space-y-2">
72
+ {{#each this.availability as |avail|}}
73
+ <div class="flex items-center justify-between p-2 bg-gray-50 dark:bg-gray-700 rounded">
74
+ <div>
75
+ <div class="font-medium">
76
+ {{#if avail.is_available}}
77
+ <span class="text-green-600">Available</span>
78
+ {{else}}
79
+ <span class="text-red-600">Unavailable</span>
80
+ {{/if}}
81
+ </div>
82
+ <div class="text-sm text-gray-500">
83
+ {{format-date-fns avail.start_at "MMM DD, YYYY"}}
84
+ -
85
+ {{format-date-fns avail.end_at "MMM DD, YYYY"}}
86
+ </div>
87
+ {{#if avail.reason}}
88
+ <div class="text-xs text-gray-400">{{avail.reason}}</div>
89
+ {{/if}}
90
+ </div>
91
+ </div>
92
+ {{/each}}
93
+ </div>
94
+ {{else}}
95
+ <div class="text-center py-4 text-gray-500 dark:text-gray-400">
96
+ No availability restrictions set
97
+ </div>
98
+ {{/if}}
99
+ </ContentPanel>
100
+ </div>
@@ -0,0 +1,267 @@
1
+ import Component from '@glimmer/component';
2
+ import { tracked } from '@glimmer/tracking';
3
+ import { inject as service } from '@ember/service';
4
+ import { action } from '@ember/object';
5
+ import { task } from 'ember-concurrency';
6
+
7
+ /**
8
+ * Driver::Schedule Component
9
+ *
10
+ * Displays and manages a driver's schedule from their detail page.
11
+ * Includes HOS compliance tracking, upcoming shifts, and availability management.
12
+ *
13
+ * @example
14
+ * <Driver::Schedule @resource={{@model}} />
15
+ */
16
+ export default class DriverScheduleComponent extends Component {
17
+ @service driverScheduling;
18
+ @service notifications;
19
+ @service modalsManager;
20
+ @service store;
21
+ @tracked scheduleItems = [];
22
+ @tracked upcomingShifts = [];
23
+ @tracked hosStatus = null;
24
+ @tracked availability = [];
25
+ @tracked timeOffRequests = [];
26
+ @tracked selectedItem = null;
27
+
28
+ get scheduleActionButtons() {
29
+ return [
30
+ {
31
+ type: 'default',
32
+ text: 'Request Time Off',
33
+ icon: 'calendar-times',
34
+ iconPrefix: 'fas',
35
+ permission: 'fleet-ops update driver',
36
+ onClick: this.requestTimeOff,
37
+ },
38
+ ];
39
+ }
40
+
41
+ get shiftActionButtons() {
42
+ return [
43
+ {
44
+ type: 'default',
45
+ text: 'Add Shift',
46
+ icon: 'plus',
47
+ iconPrefix: 'fas',
48
+ permission: 'fleet-ops update driver',
49
+ onClick: this.addShift,
50
+ },
51
+ ];
52
+ }
53
+
54
+ get availabilityActionButtons() {
55
+ return [
56
+ {
57
+ type: 'default',
58
+ text: 'Set Availability',
59
+ icon: 'clock',
60
+ iconPrefix: 'fas',
61
+ permission: 'fleet-ops update driver',
62
+ onClick: this.setAvailability,
63
+ },
64
+ ];
65
+ }
66
+
67
+ constructor() {
68
+ super(...arguments);
69
+ this.loadDriverSchedule.perform();
70
+ this.loadHOSStatus.perform();
71
+ this.loadAvailability.perform();
72
+ }
73
+
74
+ /**
75
+ * Load driver schedule items
76
+ */
77
+ @task *loadDriverSchedule() {
78
+ try {
79
+ const items = yield this.driverScheduling.getScheduleItemsForAssignee.perform('driver', this.args.resource.id, {
80
+ start_at: this.startDate,
81
+ end_at: this.endDate,
82
+ });
83
+
84
+ this.scheduleItems = items.toArray();
85
+ this.upcomingShifts = this.scheduleItems.filter((item) => new Date(item.start_at) > new Date()).slice(0, 5);
86
+ } catch (error) {
87
+ console.error('Failed to load driver schedule:', error);
88
+ }
89
+ }
90
+
91
+ /**
92
+ * Load HOS status for the driver
93
+ */
94
+ @task *loadHOSStatus() {
95
+ try {
96
+ const response = yield this.fetch.get(`drivers/${this.args.resource.id}/hos-status`);
97
+ this.hosStatus = response;
98
+ } catch (error) {
99
+ console.error('Failed to load HOS status:', error);
100
+ }
101
+ }
102
+
103
+ /**
104
+ * Load driver availability
105
+ */
106
+ @task *loadAvailability() {
107
+ try {
108
+ const availability = yield this.store.query('schedule-availability', {
109
+ subject_type: 'driver',
110
+ subject_uuid: this.args.resource.id,
111
+ start_at: this.startDate,
112
+ end_at: this.endDate,
113
+ });
114
+
115
+ this.availability = availability.toArray();
116
+ } catch (error) {
117
+ console.error('Failed to load availability:', error);
118
+ }
119
+ }
120
+
121
+ /**
122
+ * Get start date for schedule query (current week)
123
+ */
124
+ get startDate() {
125
+ const now = new Date();
126
+ const dayOfWeek = now.getDay();
127
+ const diff = now.getDate() - dayOfWeek;
128
+ return new Date(now.setDate(diff)).toISOString();
129
+ }
130
+
131
+ /**
132
+ * Get end date for schedule query (4 weeks out)
133
+ */
134
+ get endDate() {
135
+ const now = new Date();
136
+ return new Date(now.setDate(now.getDate() + 28)).toISOString();
137
+ }
138
+
139
+ /**
140
+ * Get HOS compliance badge color
141
+ */
142
+ get hosComplianceBadge() {
143
+ if (!this.hosStatus) {
144
+ return { color: 'gray', text: 'Unknown' };
145
+ }
146
+
147
+ const { daily_hours, weekly_hours } = this.hosStatus;
148
+
149
+ if (daily_hours >= 11 || weekly_hours >= 70) {
150
+ return { color: 'red', text: 'At Limit' };
151
+ }
152
+
153
+ if (daily_hours >= 9 || weekly_hours >= 60) {
154
+ return { color: 'yellow', text: 'Approaching Limit' };
155
+ }
156
+
157
+ return { color: 'green', text: 'Compliant' };
158
+ }
159
+
160
+ /**
161
+ * Handle item click
162
+ */
163
+ @action
164
+ handleItemClick(item) {
165
+ this.selectedItem = item;
166
+ this.modalsManager.show('modals/schedule-item-details', {
167
+ item,
168
+ onEdit: this.editScheduleItem,
169
+ onDelete: this.deleteScheduleItem,
170
+ });
171
+ }
172
+
173
+ /**
174
+ * Add new shift
175
+ */
176
+ @action
177
+ addShift() {
178
+ this.modalsManager.show('modals/add-shift', {
179
+ driver: this.args.resource,
180
+ onSave: this.handleShiftAdded,
181
+ });
182
+ }
183
+
184
+ /**
185
+ * Edit schedule item
186
+ */
187
+ @action
188
+ async editScheduleItem(item) {
189
+ this.modalsManager.show('modals/edit-shift', {
190
+ item,
191
+ driver: this.args.resource,
192
+ onSave: this.handleShiftUpdated,
193
+ });
194
+ }
195
+
196
+ /**
197
+ * Delete schedule item
198
+ */
199
+ @action
200
+ async deleteScheduleItem(item) {
201
+ if (confirm('Are you sure you want to delete this shift?')) {
202
+ try {
203
+ await this.driverScheduling.deleteScheduleItem.perform(item);
204
+ await this.loadDriverSchedule.perform();
205
+ } catch (error) {
206
+ console.error('Failed to delete shift:', error);
207
+ }
208
+ }
209
+ }
210
+
211
+ /**
212
+ * Handle shift added
213
+ */
214
+ @action
215
+ async handleShiftAdded() {
216
+ await this.loadDriverSchedule.perform();
217
+ await this.loadHOSStatus.perform();
218
+ }
219
+
220
+ /**
221
+ * Handle shift updated
222
+ */
223
+ @action
224
+ async handleShiftUpdated() {
225
+ await this.loadDriverSchedule.perform();
226
+ await this.loadHOSStatus.perform();
227
+ }
228
+
229
+ /**
230
+ * Request time off
231
+ */
232
+ @action
233
+ requestTimeOff() {
234
+ this.modalsManager.show('modals/request-time-off', {
235
+ driver: this.args.resource,
236
+ onSave: this.handleTimeOffRequested,
237
+ });
238
+ }
239
+
240
+ /**
241
+ * Handle time off requested
242
+ */
243
+ @action
244
+ async handleTimeOffRequested() {
245
+ await this.loadAvailability.perform();
246
+ await this.loadDriverSchedule.perform();
247
+ }
248
+
249
+ /**
250
+ * Set availability
251
+ */
252
+ @action
253
+ setAvailability() {
254
+ this.modalsManager.show('modals/set-availability', {
255
+ driver: this.args.resource,
256
+ onSave: this.handleAvailabilitySet,
257
+ });
258
+ }
259
+
260
+ /**
261
+ * Handle availability set
262
+ */
263
+ @action
264
+ async handleAvailabilitySet() {
265
+ await this.loadAvailability.perform();
266
+ }
267
+ }
@@ -47,7 +47,7 @@
47
47
  <div class="flex flex-row">
48
48
  <div class="flex-1">
49
49
  <div class="flex flex-row space-x-2">
50
- <div class="resource-assigned-photo">
50
+ <div class="resource-assigned-photo relative">
51
51
  <Image
52
52
  src={{avatar-url @card.customer.photo_url}}
53
53
  @fallbackSrc={{config "defaultValues.contactImage"}}
@@ -64,7 +64,7 @@
64
64
  </div>
65
65
  <div class="flex-1">
66
66
  <div class="flex flex-row space-x-2">
67
- <div class="resource-assigned-photo">
67
+ <div class="resource-assigned-photo relative">
68
68
  <Image
69
69
  src={{avatar-url @card.driver_assigned.photo_url}}
70
70
  @fallbackSrc={{config "defaultValues.driverImage"}}