@fleetbase/fleetops-engine 0.6.24 → 0.6.26

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 (51) 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/DriverController.php +14 -4
  44. package/server/src/Http/Controllers/Api/v1/OrderController.php +1 -1
  45. package/server/src/Http/Controllers/Api/v1/VehicleController.php +2 -2
  46. package/server/src/Http/Controllers/Internal/v1/OrderController.php +8 -3
  47. package/server/src/Http/Resources/v1/Driver.php +1 -1
  48. package/server/src/Http/Resources/v1/Vehicle.php +197 -19
  49. package/server/src/Http/Resources/v1/VehicleWithoutDriver.php +211 -28
  50. package/server/src/Models/Driver.php +15 -8
  51. package/server/src/Models/Vehicle.php +101 -15
@@ -1,8 +1,16 @@
1
1
  <div class="form-wrapper" ...attributes>
2
+ {{! DETAILS / IDENTIFICATION }}
2
3
  <ContentPanel @title={{t "common.details"}} @open={{true}} @wrapperClass="bordered-top">
3
4
  <div class="flex flex-row space-x-3 mb-3">
4
5
  <div class="flex items-center justify-start rounded-full">
5
- <Image src={{@resource.photo_url}} @fallbackSrc={{config "defaultValues.vehicleImage"}} alt={{@resource.name}} height="48" width="48" class="h-14 w-14 rounded-full shadow-sm" />
6
+ <Image
7
+ src={{@resource.photo_url}}
8
+ @fallbackSrc={{config "defaultValues.vehicleImage"}}
9
+ alt={{@resource.name}}
10
+ height="48"
11
+ width="48"
12
+ class="h-14 w-14 rounded-full shadow-sm"
13
+ />
6
14
  <Attach::Tooltip @class="clean" @animation="scale" @placement="top">
7
15
  <InputInfo @text={{t "vehicle.fields.upload-new-photo"}} />
8
16
  </Attach::Tooltip>
@@ -22,61 +30,51 @@
22
30
  </div>
23
31
  </div>
24
32
  </div>
33
+
25
34
  <div class="grid grid-cols-1 gap-2 lg:grid-cols-2 lg:gap-2 no-input-group-padding text-xs">
35
+ {{! Vehicle Identification }}
36
+ <div class="col-span-1 lg:col-span-2 text-[11px] uppercase tracking-wide text-gray-500 font-semibold mt-1">
37
+ Vehicle Identification
38
+ </div>
39
+
40
+ <InputGroup @name={{t "common.name"}}>
41
+ <Input @value={{@resource.name}} @type="text" class="w-full form-input" placeholder={{t "common.name"}} disabled={{cannot-write @resource}} />
42
+ </InputGroup>
43
+
26
44
  <InputGroup @name={{t "common.internal-id"}}>
27
45
  <Input @value={{@resource.internal_id}} @type="text" class="w-full form-input" placeholder={{t "common.internal-id"}} disabled={{cannot-write @resource}} />
28
46
  </InputGroup>
29
47
 
30
48
  <InputGroup @name={{t "vehicle.fields.plate-number"}}>
31
- <Input
32
- @value={{@resource.plate_number}}
33
- @type="text"
34
- class="w-full form-input"
35
- placeholder={{t "vehicle.fields.plate-number"}}
36
- disabled={{cannot-write @resource}}
37
- />
49
+ <Input @value={{@resource.plate_number}} @type="text" class="w-full form-input" placeholder={{t "vehicle.fields.plate-number"}} disabled={{cannot-write @resource}} />
38
50
  </InputGroup>
39
51
 
40
52
  <InputGroup @name={{t "vehicle.fields.vin-number"}}>
41
- <Input
42
- @value={{@resource.vin}}
43
- @type="text"
44
- class="w-full form-input"
45
- placeholder={{t "vehicle.fields.vin-number"}}
46
- disabled={{cannot-write @resource}}
47
- />
53
+ <Input @value={{@resource.vin}} @type="text" class="w-full form-input" placeholder={{t "vehicle.fields.vin-number"}} disabled={{cannot-write @resource}} />
48
54
  </InputGroup>
49
55
 
50
56
  <InputGroup @name={{t "vehicle.fields.make"}}>
51
- <Input
52
- @value={{@resource.make}}
53
- @type="text"
54
- class="w-full form-input"
55
- placeholder={{t "vehicle.fields.vehicle-make"}}
56
- disabled={{cannot-write @resource}}
57
- />
57
+ <Input @value={{@resource.make}} @type="text" class="w-full form-input" placeholder={{t "vehicle.fields.vehicle-make"}} disabled={{cannot-write @resource}} />
58
58
  </InputGroup>
59
59
 
60
60
  <InputGroup @name={{t "vehicle.fields.model"}}>
61
- <Input
62
- @value={{@resource.model}}
63
- @type="text"
64
- class="w-full form-input"
65
- placeholder={{t "vehicle.fields.vehicle-model"}}
66
- disabled={{cannot-write @resource}}
67
- />
61
+ <Input @value={{@resource.model}} @type="text" class="w-full form-input" placeholder={{t "vehicle.fields.vehicle-model"}} disabled={{cannot-write @resource}} />
68
62
  </InputGroup>
69
63
 
70
64
  <InputGroup @name={{t "vehicle.fields.year"}}>
71
- <Input
72
- @value={{@resource.year}}
73
- @type="text"
74
- class="w-full form-input"
75
- placeholder={{t "vehicle.fields.vehicle-year"}}
76
- disabled={{cannot-write @resource}}
77
- />
65
+ <Input @value={{@resource.year}} @type="text" class="w-full form-input" placeholder={{t "vehicle.fields.vehicle-year"}} disabled={{cannot-write @resource}} />
78
66
  </InputGroup>
79
67
 
68
+ <InputGroup @value={{@resource.trim}} @name="Trim" @placeholder="Trim" />
69
+ <InputGroup @value={{@resource.color}} @name="Color" @placeholder="Vehicle Color" />
70
+ <InputGroup @value={{@resource.serial_number}} @name="Serial Number" @placeholder="Serial Number" />
71
+ <InputGroup @value={{@resource.class}} @name="Class" @placeholder="Class" />
72
+
73
+ {{! Assignment & Status }}
74
+ <div class="col-span-1 lg:col-span-2 text-[11px] uppercase tracking-wide text-gray-500 font-semibold mt-3">
75
+ Assignment & Status
76
+ </div>
77
+
80
78
  <InputGroup @name={{t "vehicle.fields.driver-assigned"}}>
81
79
  <ModelSelect
82
80
  @modelName="driver"
@@ -96,27 +94,455 @@
96
94
  <InputGroup @name={{t "common.status"}}>
97
95
  <div class="fleetbase-model-select fleetbase-power-select ember-model-select">
98
96
  <PowerSelect
99
- @options={{this.statusOptions}}
100
- @selected={{@resource.status}}
101
- @onChange={{fn (mut @resource.status)}}
97
+ @options={{get-fleet-ops-options "vehicleStatuses"}}
98
+ @selected={{find-by "value" @resource.status (get-fleet-ops-options "vehicleStatuses")}}
99
+ @onChange={{set-model-attr @resource "status"}}
102
100
  @placeholder={{t "common.status"}}
103
101
  @triggerClass="form-select form-input"
104
102
  @disabled={{cannot-write @resource}}
105
- as |status|
103
+ as |option|
106
104
  >
107
- {{smart-humanize status}}
105
+ <div class="text-sm">
106
+ <div class="font-semibold normalize-in-trigger">{{option.label}}</div>
107
+ <div class="hide-from-trigger">{{option.description}}</div>
108
+ </div>
108
109
  </PowerSelect>
109
110
  </div>
110
111
  </InputGroup>
112
+
113
+ <InputGroup @value={{@resource.call_sign}} @name="Call Sign" @placeholder="Call Sign" />
114
+
115
+ {{! Location }}
116
+ <div class="col-span-1 lg:col-span-2 text-[11px] uppercase tracking-wide text-gray-500 font-semibold mt-3">
117
+ Location
118
+ </div>
119
+
111
120
  <InputGroup @name={{t "common.coordinates"}} @wrapperClass="col-span-2">
112
121
  <ModelCoordinatesInput @model={{@resource}} @disabled={{cannot-write @resource}} @autocomplete="location" />
113
122
  </InputGroup>
114
123
  </div>
124
+
115
125
  <RegistryYield @registry="fleet-ops:component:vehicle:form:details" as |RegistryComponent|>
116
126
  <RegistryComponent @resource={{@resource}} @controller={{this.controller}} @permission={{get-write-permission @resource}} />
117
127
  </RegistryYield>
118
128
  </ContentPanel>
119
129
 
130
+ {{! MEASUREMENT & UNITS / ODOMETER }}
131
+ <ContentPanel @title="Measurement & Units" @open={{true}} @wrapperClass="bordered-top">
132
+ <div class="grid grid-cols-1 gap-2 lg:grid-cols-2 lg:gap-2 no-input-group-padding text-xs">
133
+ <div class="col-span-1 lg:col-span-2 text-[11px] uppercase tracking-wide text-gray-500 font-semibold mt-1">
134
+ Measurement & Units
135
+ </div>
136
+
137
+ <InputGroup @name="System of Measurement">
138
+ <div class="fleetbase-model-select fleetbase-power-select ember-model-select">
139
+ <PowerSelect
140
+ @options={{get-fleet-ops-options "measurementSystems"}}
141
+ @selected={{find-by "value" @resource.measurement_system (get-fleet-ops-options "measurementSystems")}}
142
+ @onChange={{set-model-attr @resource "measurement_system"}}
143
+ @placeholder="Select System of Measurement"
144
+ @triggerClass="form-select form-input"
145
+ @disabled={{cannot-write @resource}}
146
+ as |option|
147
+ >
148
+ <div class="text-sm">
149
+ <div class="font-semibold normalize-in-trigger">{{option.label}}</div>
150
+ <div class="hide-from-trigger">{{option.description}}</div>
151
+ </div>
152
+ </PowerSelect>
153
+ </div>
154
+ </InputGroup>
155
+
156
+ <InputGroup @name="Fuel Volume Unit">
157
+ <div class="fleetbase-model-select fleetbase-power-select ember-model-select">
158
+ <PowerSelect
159
+ @options={{get-fleet-ops-options "fuelVolumeUnits"}}
160
+ @selected={{find-by "value" @resource.fuel_volume_unit (get-fleet-ops-options "fuelVolumeUnits")}}
161
+ @onChange={{set-model-attr @resource "fuel_volume_unit"}}
162
+ @placeholder="Select Fuel Volume Unit"
163
+ @triggerClass="form-select form-input"
164
+ @disabled={{cannot-write @resource}}
165
+ as |option|
166
+ >
167
+ <div class="text-sm">
168
+ <div class="font-semibold normalize-in-trigger">{{option.label}}</div>
169
+ <div class="hide-from-trigger">{{option.description}}</div>
170
+ </div>
171
+ </PowerSelect>
172
+ </div>
173
+ </InputGroup>
174
+
175
+ <div class="col-span-1 lg:col-span-2 text-[11px] uppercase tracking-wide text-gray-500 font-semibold mt-3">
176
+ Odometer & Usage
177
+ </div>
178
+
179
+ <InputGroup @value={{@resource.odometer}} @name={{concat "Current Odometer (" @resource.odometer_unit ")"}} @type="number" @placeholder="Current Odometer Reading" />
180
+
181
+ <InputGroup @name="Odometer Unit">
182
+ <div class="fleetbase-model-select fleetbase-power-select ember-model-select">
183
+ <PowerSelect
184
+ @options={{get-fleet-ops-options "odometerUnits"}}
185
+ @selected={{find-by "value" @resource.odometer_unit (get-fleet-ops-options "odometerUnits")}}
186
+ @onChange={{set-model-attr @resource "odometer_unit"}}
187
+ @placeholder="Select unit"
188
+ @triggerClass="form-select form-input"
189
+ @disabled={{cannot-write @resource}}
190
+ as |option|
191
+ >
192
+ <div class="text-sm">
193
+ <div class="font-semibold normalize-in-trigger">{{option.label}}</div>
194
+ <div class="hide-from-trigger">{{option.description}}</div>
195
+ </div>
196
+ </PowerSelect>
197
+ </div>
198
+ </InputGroup>
199
+
200
+ <InputGroup @value={{@resource.odometer_at_purchase}} @name="Odometer at Purchase" @type="number" @placeholder="Odometer reading at purchase" />
201
+ </div>
202
+ </ContentPanel>
203
+
204
+ {{! BODY & USAGE }}
205
+ <ContentPanel @title="Body & Usage" @open={{true}} @wrapperClass="bordered-top">
206
+ <div class="grid grid-cols-1 gap-2 lg:grid-cols-2 lg:gap-2 no-input-group-padding text-xs">
207
+ <div class="col-span-1 lg:col-span-2 text-[11px] uppercase tracking-wide text-gray-500 font-semibold mt-1">
208
+ Body & Usage
209
+ </div>
210
+
211
+ <InputGroup @name="Body Type">
212
+ <div class="fleetbase-model-select fleetbase-power-select ember-model-select">
213
+ <PowerSelect
214
+ @options={{get-fleet-ops-options "vehicleBodyTypes"}}
215
+ @selected={{find-by "value" @resource.body_type (get-fleet-ops-options "vehicleBodyTypes")}}
216
+ @onChange={{set-model-attr @resource "body_type"}}
217
+ @placeholder="Select body type"
218
+ @triggerClass="form-select form-input"
219
+ @disabled={{cannot-write @resource}}
220
+ as |option|
221
+ >
222
+ <div class="text-sm">
223
+ <div class="font-semibold normalize-in-trigger">{{option.label}}</div>
224
+ <div class="hide-from-trigger">{{option.description}}</div>
225
+ </div>
226
+ </PowerSelect>
227
+ </div>
228
+ </InputGroup>
229
+
230
+ <InputGroup @name="Body Subtype">
231
+ <div class="fleetbase-model-select fleetbase-power-select ember-model-select">
232
+ <PowerSelect
233
+ @options={{get-fleet-ops-options "vehicleBodySubTypes"}}
234
+ @selected={{find-by "value" @resource.body_sub_type (get-fleet-ops-options "vehicleBodySubTypes")}}
235
+ @onChange={{set-model-attr @resource "body_sub_type"}}
236
+ @placeholder="Select body sub-type"
237
+ @triggerClass="form-select form-input"
238
+ @disabled={{cannot-write @resource}}
239
+ as |option|
240
+ >
241
+ <div class="text-sm">
242
+ <div class="font-semibold normalize-in-trigger">{{option.label}}</div>
243
+ <div class="hide-from-trigger">{{option.description}}</div>
244
+ </div>
245
+ </PowerSelect>
246
+ </div>
247
+ </InputGroup>
248
+
249
+ <InputGroup @name="Usage Type">
250
+ <div class="fleetbase-model-select fleetbase-power-select ember-model-select">
251
+ <PowerSelect
252
+ @options={{get-fleet-ops-options "vehicleUsageTypes"}}
253
+ @selected={{find-by "value" @resource.usage_type (get-fleet-ops-options "vehicleUsageTypes")}}
254
+ @onChange={{set-model-attr @resource "usage_type"}}
255
+ @placeholder="Select usage type"
256
+ @triggerClass="form-select form-input"
257
+ @disabled={{cannot-write @resource}}
258
+ as |option|
259
+ >
260
+ <div class="text-sm">
261
+ <div class="font-semibold normalize-in-trigger">{{option.label}}</div>
262
+ <div class="hide-from-trigger">{{option.description}}</div>
263
+ </div>
264
+ </PowerSelect>
265
+ </div>
266
+ </InputGroup>
267
+
268
+ <InputGroup @name="Ownership Type">
269
+ <div class="fleetbase-model-select fleetbase-power-select ember-model-select">
270
+ <PowerSelect
271
+ @options={{get-fleet-ops-options "vehicleOwnershipTypes"}}
272
+ @selected={{find-by "value" @resource.ownership_type (get-fleet-ops-options "vehicleOwnershipTypes")}}
273
+ @onChange={{set-model-attr @resource "ownership_type"}}
274
+ @placeholder="Select ownership type"
275
+ @triggerClass="form-select form-input"
276
+ @disabled={{cannot-write @resource}}
277
+ as |option|
278
+ >
279
+ <div class="text-sm">
280
+ <div class="font-semibold normalize-in-trigger">{{option.label}}</div>
281
+ <div class="hide-from-trigger">{{option.description}}</div>
282
+ </div>
283
+ </PowerSelect>
284
+ </div>
285
+ </InputGroup>
286
+
287
+ <InputGroup @name="Fuel Type">
288
+ <div class="fleetbase-model-select fleetbase-power-select ember-model-select">
289
+ <PowerSelect
290
+ @options={{get-fleet-ops-options "fuelTypes"}}
291
+ @selected={{find-by "value" @resource.fuel_type (get-fleet-ops-options "fuelTypes")}}
292
+ @onChange={{set-model-attr @resource "fuel_type"}}
293
+ @placeholder="Select fuel type"
294
+ @triggerClass="form-select form-input"
295
+ @disabled={{cannot-write @resource}}
296
+ as |option|
297
+ >
298
+ <div class="text-sm">
299
+ <div class="font-semibold normalize-in-trigger">{{option.label}}</div>
300
+ <div class="hide-from-trigger">{{option.description}}</div>
301
+ </div>
302
+ </PowerSelect>
303
+ </div>
304
+ </InputGroup>
305
+
306
+ <InputGroup @name="Transmission">
307
+ <div class="fleetbase-model-select fleetbase-power-select ember-model-select">
308
+ <PowerSelect
309
+ @options={{get-fleet-ops-options "transmissionTypes"}}
310
+ @selected={{find-by "value" @resource.transmission (get-fleet-ops-options "transmissionTypes")}}
311
+ @onChange={{set-model-attr @resource "transmission"}}
312
+ @placeholder="Select transmission"
313
+ @triggerClass="form-select form-input"
314
+ @disabled={{cannot-write @resource}}
315
+ as |option|
316
+ >
317
+ <div class="text-sm">
318
+ <div class="font-semibold normalize-in-trigger">{{option.label}}</div>
319
+ <div class="hide-from-trigger">{{option.description}}</div>
320
+ </div>
321
+ </PowerSelect>
322
+ </div>
323
+ </InputGroup>
324
+ </div>
325
+ </ContentPanel>
326
+
327
+ {{! TECHNICAL SPECIFICATIONS (ENGINE, CAPACITY, REGULATORY) }}
328
+ <ContentPanel @title="Technical Specifications" @open={{true}} @wrapperClass="bordered-top">
329
+ <div class="grid grid-cols-1 lg:grid-cols-2 gap-2 no-input-group-padding text-xs">
330
+
331
+ {{! Powertrain & Engine }}
332
+ <div class="col-span-1 lg:col-span-2 text-[11px] uppercase tracking-wide text-gray-500 font-semibold mt-1">
333
+ Powertrain & Engine
334
+ </div>
335
+
336
+ <InputGroup @value={{@resource.engine_number}} @name="Engine Number" @placeholder="Engine Number" />
337
+ <InputGroup @value={{@resource.engine_make}} @name="Engine Make" @placeholder="Engine Manufacturer" />
338
+ <InputGroup @value={{@resource.engine_model}} @name="Engine Model" @placeholder="Engine Model" />
339
+ <InputGroup @value={{@resource.engine_family}} @name="Engine Family" @placeholder="Engine Family" />
340
+ <InputGroup @value={{@resource.engine_configuration}} @name="Engine Configuration" @placeholder="e.g., Inline-4, V6" />
341
+ <InputGroup @value={{@resource.cylinder_arrangement}} @name="Cylinder Arrangement" @placeholder="e.g., I, V, H" />
342
+ <InputGroup @value={{@resource.number_of_cylinders}} @name="Number of Cylinders" @type="number" @placeholder="e.g., 4, 6" />
343
+
344
+ <InputGroup @name="Engine Size">
345
+ <UnitInput class="w-full" @value={{@resource.engine_size}} @unit="L" @type="number" @step="0.1" @placeholder="Engine Size" />
346
+ </InputGroup>
347
+
348
+ <InputGroup @name="Engine Displacement">
349
+ <UnitInput class="w-full" @value={{@resource.engine_displacement}} @unit="cc" @type="number" @placeholder="Engine Displacement" />
350
+ </InputGroup>
351
+
352
+ <InputGroup @name="Horsepower">
353
+ <UnitInput class="w-full" @value={{@resource.horsepower}} @unit="hp" @type="number" @placeholder="Horsepower" />
354
+ </InputGroup>
355
+
356
+ <InputGroup @value={{@resource.horsepower_rpm}} @name="Horsepower RPM" @type="number" @placeholder="RPM at Max Horsepower" />
357
+
358
+ <InputGroup @name="Torque">
359
+ <UnitInput class="w-full" @value={{@resource.torque}} @unit="Nm" @type="number" @placeholder="Torque" />
360
+ </InputGroup>
361
+
362
+ <InputGroup @value={{@resource.torque_rpm}} @name="Torque RPM" @type="number" @placeholder="RPM at Max Torque" />
363
+
364
+ {{! Capacity & Dimensions }}
365
+ <div class="col-span-1 lg:col-span-2 text-[11px] uppercase tracking-wide text-gray-500 font-semibold mt-3">
366
+ Capacity & Dimensions
367
+ </div>
368
+
369
+ <div class="grid grid-cols-1 lg:grid-cols-3 col-span-1 lg:col-span-2 gap-2">
370
+ <InputGroup @name="Fuel Capacity">
371
+ <UnitInput class="w-full" @value={{@resource.fuel_capacity}} @unit="L" @placeholder="Fuel Tank Capacity" />
372
+ </InputGroup>
373
+
374
+ <InputGroup @name="Payload Capacity">
375
+ <UnitInput class="w-full" @value={{@resource.payload_capacity}} @unit="kg" @placeholder="Maximum Payload" />
376
+ </InputGroup>
377
+
378
+ <InputGroup @name="Towing Capacity">
379
+ <UnitInput class="w-full" @value={{@resource.towing_capacity}} @unit="kg" @placeholder="Towing Capacity" />
380
+ </InputGroup>
381
+
382
+ <InputGroup @name="Seating Capacity">
383
+ <Input @value={{@resource.seating_capacity}} @type="number" class="form-input w-full" placeholder="Number of Seats" />
384
+ </InputGroup>
385
+
386
+ <InputGroup @name="Vehicle Weight">
387
+ <UnitInput class="w-full" @value={{@resource.weight}} @unit="kg" @placeholder="Vehicle Weight" />
388
+ </InputGroup>
389
+
390
+ <InputGroup @name="Length">
391
+ <UnitInput class="w-full" @value={{@resource.length}} @unit="cm" @placeholder="Length" />
392
+ </InputGroup>
393
+
394
+ <InputGroup @name="Width">
395
+ <UnitInput class="w-full" @value={{@resource.width}} @unit="cm" @placeholder="Width" />
396
+ </InputGroup>
397
+
398
+ <InputGroup @name="Height">
399
+ <UnitInput class="w-full" @value={{@resource.height}} @unit="cm" @placeholder="Height" />
400
+ </InputGroup>
401
+
402
+ <InputGroup @name="Cargo Volume">
403
+ <UnitInput class="w-full" @value={{@resource.cargo_volume}} @unit="L" @placeholder="Cargo Volume" />
404
+ </InputGroup>
405
+
406
+ <InputGroup @name="Passenger Volume">
407
+ <UnitInput class="w-full" @value={{@resource.passenger_volume}} @unit="L" @placeholder="Passenger Volume" />
408
+ </InputGroup>
409
+
410
+ <InputGroup @name="Interior Volume">
411
+ <UnitInput class="w-full" @value={{@resource.interior_volume}} @unit="L" @placeholder="Interior Volume" />
412
+ </InputGroup>
413
+
414
+ <InputGroup @name="Ground Clearance">
415
+ <UnitInput class="w-full" @value={{@resource.ground_clearance}} @unit="cm" @placeholder="Ground Clearance" />
416
+ </InputGroup>
417
+
418
+ <InputGroup @name="Bed Length">
419
+ <UnitInput class="w-full" @value={{@resource.bed_length}} @unit="cm" @placeholder="Bed Length" />
420
+ </InputGroup>
421
+ </div>
422
+
423
+ {{! Regulatory & Compliance }}
424
+ <div class="col-span-1 lg:col-span-2 text-[11px] uppercase tracking-wide text-gray-500 font-semibold mt-3">
425
+ Regulatory & Compliance
426
+ </div>
427
+
428
+ <InputGroup @value={{@resource.emission_standard}} @name="Emission Standard" @placeholder="e.g., Euro 6, EPA Tier 3" />
429
+
430
+ <div></div>
431
+
432
+ <InputGroup @name="GVWR (Gross Vehicle Weight Rating)">
433
+ <UnitInput class="w-full" @value={{@resource.gvwr}} @unit="kg" @placeholder="GVWR" />
434
+ </InputGroup>
435
+
436
+ <InputGroup @name="GCWR (Gross Combined Weight Rating)">
437
+ <UnitInput class="w-full" @value={{@resource.gcwr}} @unit="kg" @placeholder="GCWR" />
438
+ </InputGroup>
439
+
440
+ <InputGroup @wrapperClass="mt-1 space-y-2">
441
+ <Checkbox @value={{@resource.dpf_equipped}} @label="DPF Equipped" @onToggle={{fn (mut @resource.dpf_equipped)}} />
442
+ <Checkbox @value={{@resource.scr_equipped}} @label="SCR Equipped" @onToggle={{fn (mut @resource.scr_equipped)}} />
443
+ </InputGroup>
444
+ </div>
445
+ </ContentPanel>
446
+
447
+ {{! FINANCIAL & LIFECYCLE }}
448
+ <ContentPanel @title="Financial & Lifecycle" @open={{true}} @wrapperClass="bordered-top">
449
+ <div class="grid grid-cols-1 gap-2 lg:grid-cols-2 lg:gap-2 no-input-group-padding text-xs">
450
+ <div class="col-span-1 lg:col-span-2 text-[11px] uppercase tracking-wide text-gray-500 font-semibold mt-1">
451
+ Asset Values
452
+ </div>
453
+
454
+ <InputGroup @name="Currency" @value={{@resource.currency}}>
455
+ <CurrencySelect @currency={{@resource.currency}} @onCurrencyChange={{fn (mut @resource.currency)}} @triggerClass="w-full form-select" />
456
+ </InputGroup>
457
+ <div></div>
458
+
459
+ <InputGroup @name="Acquisition Cost">
460
+ <MoneyInput @value={{@resource.acquisition_cost}} @currency={{@resource.currency}} />
461
+ </InputGroup>
462
+
463
+ <InputGroup @name="Current Value">
464
+ <MoneyInput @value={{@resource.current_value}} @currency={{@resource.currency}} />
465
+ </InputGroup>
466
+
467
+ <InputGroup @name="Insurance Value">
468
+ <MoneyInput @value={{@resource.insurance_value}} @currency={{@resource.currency}} />
469
+ </InputGroup>
470
+
471
+ <InputGroup @name="Depreciation Rate (%)">
472
+ <Input @value={{@resource.depreciation_rate}} @type="number" step="0.01" class="form-input w-full" />
473
+ </InputGroup>
474
+
475
+ <div class="col-span-1 lg:col-span-2 text-[11px] uppercase tracking-wide text-gray-500 font-semibold mt-3">
476
+ Lifecycle & Service Life
477
+ </div>
478
+
479
+ <InputGroup @name="Estimated Service Life (Distance)">
480
+ <Input @value={{@resource.estimated_service_life_distance}} @type="number" class="form-input w-full" placeholder="Total distance" />
481
+ </InputGroup>
482
+
483
+ <InputGroup @name="Service Life Distance Unit">
484
+ <div class="fleetbase-model-select fleetbase-power-select ember-model-select">
485
+ <PowerSelect
486
+ @options={{get-fleet-ops-options "odometerUnits"}}
487
+ @selected={{find-by "value" @resource.estimated_service_life_distance_unit (get-fleet-ops-options "odometerUnits")}}
488
+ @onChange={{set-model-attr @resource "estimated_service_life_distance_unit"}}
489
+ @placeholder="Select unit"
490
+ @triggerClass="form-select form-input"
491
+ @disabled={{cannot-write @resource}}
492
+ as |option|
493
+ >
494
+ <div class="text-sm">
495
+ <div class="font-semibold normalize-in-trigger">{{option.label}}</div>
496
+ <div class="hide-from-trigger">{{option.description}}</div>
497
+ </div>
498
+ </PowerSelect>
499
+ </div>
500
+ </InputGroup>
501
+
502
+ <InputGroup @name="Estimated Service Life (Months)">
503
+ <Input @value={{@resource.estimated_service_life_months}} @type="number" class="form-input w-full" />
504
+ </InputGroup>
505
+
506
+ <InputGroup @name="Purchase Date">
507
+ <DatePicker
508
+ @value={{or (format-date-fns @resource.purchased_at "dd-MM-yyyy") null}}
509
+ @onChange={{fn (mut @resource.purchased_at)}}
510
+ @placeholder="DD-MM-YYYY"
511
+ class="filter-date-input w-full flex-1"
512
+ />
513
+ </InputGroup>
514
+
515
+ <InputGroup @name="Lease Expiry Date">
516
+ <DatePicker
517
+ @value={{or (format-date-fns @resource.lease_expires_at "dd-MM-yyyy") null}}
518
+ @onChange={{fn (mut @resource.lease_expires_at)}}
519
+ @placeholder="DD-MM-YYYY"
520
+ class="filter-date-input w-full flex-1"
521
+ />
522
+ </InputGroup>
523
+
524
+ <div class="col-span-1 lg:col-span-2 text-[11px] uppercase tracking-wide text-gray-500 font-semibold mt-3">
525
+ Financing (Loan)
526
+ </div>
527
+
528
+ <InputGroup @name="Loan Amount">
529
+ <MoneyInput @value={{@resource.loan_amount}} @currency={{@resource.currency}} />
530
+ </InputGroup>
531
+
532
+ <InputGroup @value={{@resource.loan_number_of_payments}} @name="Number of Payments" @type="number" @placeholder="Number of payments" />
533
+
534
+ <InputGroup @name="First Payment Date">
535
+ <DatePicker
536
+ @value={{or (format-date-fns @resource.loan_first_payment "dd-MM-yyyy") null}}
537
+ @onChange={{fn (mut @resource.loan_first_payment)}}
538
+ @placeholder="DD-MM-YYYY"
539
+ class="filter-date-input w-full flex-1"
540
+ />
541
+ </InputGroup>
542
+ </div>
543
+ </ContentPanel>
544
+
545
+ {{! CUSTOM FIELDS, REGISTRY, AVATAR, METADATA }}
120
546
  <CustomField::Yield @subject={{@resource}} @wrapperClass="bordered-top" />
121
547
 
122
548
  <RegistryYield @registry="fleet-ops:component:vehicle:form" as |RegistryComponent|>
@@ -46,6 +46,7 @@ export default class AnalyticsReportsIndexController extends Controller {
46
46
  get columns() {
47
47
  return [
48
48
  {
49
+ sticky: true,
49
50
  label: 'Title',
50
51
  valuePath: 'title',
51
52
  cellComponent: 'table/cell/anchor',
@@ -58,7 +59,6 @@ export default class AnalyticsReportsIndexController extends Controller {
58
59
  {
59
60
  label: 'ID',
60
61
  valuePath: 'public_id',
61
- width: '130px',
62
62
  cellComponent: 'click-to-copy',
63
63
  resizable: true,
64
64
  sortable: true,
@@ -75,7 +75,8 @@ export default class AnalyticsReportsIndexController extends Controller {
75
75
  ddMenuLabel: this.intl.t('common.resource-actions', { resource: this.intl.t('resource.Driver') }),
76
76
  cellClassNames: 'overflow-visible',
77
77
  wrapperClass: 'flex items-center justify-end mx-2',
78
- width: '10%',
78
+ sticky: 'right',
79
+ width: 60,
79
80
  actions: [
80
81
  {
81
82
  label: 'View report...',
@@ -56,6 +56,7 @@ export default class ConnectivityDevicesIndexController extends Controller {
56
56
  /** columns */
57
57
  @tracked columns = [
58
58
  {
59
+ sticky: true,
59
60
  label: this.intl.t('column.name'),
60
61
  valuePath: 'displayName',
61
62
  cellComponent: 'table/cell/anchor',
@@ -104,7 +105,6 @@ export default class ConnectivityDevicesIndexController extends Controller {
104
105
  label: this.intl.t('column.status'),
105
106
  valuePath: 'status',
106
107
  cellComponent: 'table/cell/status',
107
- width: '10%',
108
108
  resizable: true,
109
109
  sortable: true,
110
110
  filterable: true,
@@ -124,7 +124,6 @@ export default class ConnectivityDevicesIndexController extends Controller {
124
124
  label: this.intl.t('column.updated-at'),
125
125
  valuePath: 'updatedAt',
126
126
  sortParam: 'updated_at',
127
- width: '10%',
128
127
  resizable: true,
129
128
  sortable: true,
130
129
  hidden: true,
@@ -141,7 +140,8 @@ export default class ConnectivityDevicesIndexController extends Controller {
141
140
  ddMenuLabel: this.intl.t('common.resource-actions', { resource: this.intl.t('resource.device') }),
142
141
  cellClassNames: 'overflow-visible',
143
142
  wrapperClass: 'flex items-center justify-end mx-2',
144
- width: '10%',
143
+ sticky: 'right',
144
+ width: 60,
145
145
  actions: [
146
146
  {
147
147
  label: this.intl.t('common.view-resource', { resource: this.intl.t('resource.device') }),
@@ -30,6 +30,7 @@ export default class ConnectivityEventsIndexController extends Controller {
30
30
  /** columns */
31
31
  @tracked columns = [
32
32
  {
33
+ sticky: true,
33
34
  label: 'Event',
34
35
  valuePath: 'event_type',
35
36
  cellComponent: 'table/cell/anchor',
@@ -107,7 +108,6 @@ export default class ConnectivityEventsIndexController extends Controller {
107
108
  label: this.intl.t('column.created-at'),
108
109
  valuePath: 'createdAt',
109
110
  sortParam: 'created_at',
110
- width: '10%',
111
111
  resizable: true,
112
112
  sortable: true,
113
113
  filterable: true,
@@ -122,7 +122,8 @@ export default class ConnectivityEventsIndexController extends Controller {
122
122
  ddMenuLabel: this.intl.t('common.resource-actions', { resource: this.intl.t('resource.device-event') }),
123
123
  cellClassNames: 'overflow-visible',
124
124
  wrapperClass: 'flex items-center justify-end mx-2',
125
- width: '10%',
125
+ sticky: 'right',
126
+ width: 60,
126
127
  actions: [
127
128
  {
128
129
  label: this.intl.t('common.view-resource', { resource: this.intl.t('resource.device-event') }),