@fleetbase/fleetops-engine 0.6.20 → 0.6.22

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 (162) hide show
  1. package/addon/components/custom-entity/form.hbs +14 -14
  2. package/addon/components/device/card.hbs +1 -0
  3. package/addon/components/device/card.js +3 -0
  4. package/addon/components/device/details.hbs +92 -43
  5. package/addon/components/device/form.hbs +108 -60
  6. package/addon/components/device/form.js +36 -8
  7. package/addon/components/device/manager.hbs +29 -0
  8. package/addon/components/device/manager.js +95 -0
  9. package/addon/components/device/panel-header.hbs +32 -0
  10. package/addon/components/device/panel-header.js +3 -0
  11. package/addon/components/device/pill.hbs +16 -0
  12. package/addon/components/device/pill.js +3 -0
  13. package/addon/components/driver/details.hbs +4 -0
  14. package/addon/components/driver/details.js +19 -1
  15. package/addon/components/driver/form.hbs +14 -3
  16. package/addon/components/driver/form.js +49 -47
  17. package/addon/components/driver/pill.hbs +17 -0
  18. package/addon/components/driver/pill.js +3 -0
  19. package/addon/components/entity/form.hbs +7 -5
  20. package/addon/components/layout/fleet-ops-sidebar.js +12 -12
  21. package/addon/components/map/drawer/device-event-listing.hbs +64 -0
  22. package/addon/components/map/drawer/device-event-listing.js +181 -0
  23. package/addon/components/map/drawer/position-listing.hbs +100 -0
  24. package/addon/components/map/drawer/position-listing.js +455 -0
  25. package/addon/components/map/drawer.js +2 -0
  26. package/addon/components/map/leaflet-live-map.hbs +7 -2
  27. package/addon/components/modals/attach-device.hbs +18 -0
  28. package/addon/components/modals/attach-device.js +3 -0
  29. package/addon/components/order/details/detail.hbs +2 -54
  30. package/addon/components/order/details/detail.js +1 -0
  31. package/addon/components/order/details/payload.hbs +6 -4
  32. package/addon/components/order/details/payload.js +2 -0
  33. package/addon/components/order/pill.hbs +34 -0
  34. package/addon/components/order/pill.js +3 -0
  35. package/addon/components/order-config-manager/custom-fields.js +1 -1
  36. package/addon/components/positions-replay.hbs +339 -0
  37. package/addon/components/positions-replay.js +409 -0
  38. package/addon/components/sensor/details.hbs +64 -38
  39. package/addon/components/sensor/form.hbs +112 -63
  40. package/addon/components/sensor/form.js +36 -24
  41. package/addon/components/sensor/panel-header.hbs +32 -0
  42. package/addon/components/sensor/panel-header.js +3 -0
  43. package/addon/components/telematic/details.hbs +40 -16
  44. package/addon/components/telematic/form.hbs +63 -64
  45. package/addon/components/telematic/form.js +73 -4
  46. package/addon/components/vehicle/card.hbs +2 -2
  47. package/addon/components/vehicle/details.hbs +4 -0
  48. package/addon/components/vehicle/details.js +19 -1
  49. package/addon/components/vehicle/form.hbs +4 -0
  50. package/addon/components/vehicle/pill.hbs +34 -0
  51. package/addon/components/vehicle/pill.js +3 -0
  52. package/addon/controllers/analytics/reports/index/edit.js +1 -1
  53. package/addon/controllers/connectivity/devices/index/details.js +22 -1
  54. package/addon/controllers/connectivity/devices/index/edit.js +66 -1
  55. package/addon/controllers/connectivity/devices/index.js +51 -9
  56. package/addon/controllers/connectivity/events/index.js +65 -16
  57. package/addon/controllers/connectivity/sensors/index/details.js +22 -1
  58. package/addon/controllers/connectivity/sensors/index/edit.js +66 -1
  59. package/addon/controllers/connectivity/sensors/index.js +66 -6
  60. package/addon/controllers/connectivity/telematics/index/details.js +22 -1
  61. package/addon/controllers/connectivity/telematics/index/edit.js +66 -1
  62. package/addon/controllers/connectivity/telematics/index.js +20 -11
  63. package/addon/controllers/management/fleets/index/details.js +26 -21
  64. package/addon/controllers/management/fleets/index/edit.js +9 -6
  65. package/addon/controllers/management/vehicles/index/details.js +26 -13
  66. package/addon/controllers/settings/custom-fields.js +6 -0
  67. package/addon/helpers/get-fleet-ops-option-label.js +11 -0
  68. package/addon/routes/connectivity/devices/index/details.js +27 -1
  69. package/addon/routes/connectivity/devices/index/edit.js +27 -1
  70. package/addon/routes/connectivity/sensors/index/details.js +27 -1
  71. package/addon/routes/connectivity/sensors/index/edit.js +27 -1
  72. package/addon/routes/connectivity/telematics/index/details.js +27 -1
  73. package/addon/routes/connectivity/telematics/index/edit.js +27 -1
  74. package/addon/routes/management/drivers/index/details/positions.js +3 -0
  75. package/addon/routes/management/vehicles/index/details/positions.js +3 -0
  76. package/addon/routes.js +4 -0
  77. package/addon/services/movement-tracker.js +81 -30
  78. package/addon/services/position-playback.js +486 -0
  79. package/addon/services/resource-metadata.js +46 -0
  80. package/addon/styles/fleetops-engine.css +157 -0
  81. package/addon/templates/connectivity/devices/index/details/index.hbs +2 -2
  82. package/addon/templates/connectivity/devices/index/details.hbs +15 -2
  83. package/addon/templates/connectivity/devices/index/edit.hbs +1 -1
  84. package/addon/templates/connectivity/events/index.hbs +1 -1
  85. package/addon/templates/connectivity/sensors/index/details/index.hbs +2 -2
  86. package/addon/templates/connectivity/sensors/index/details.hbs +15 -2
  87. package/addon/templates/connectivity/sensors/index/edit.hbs +1 -1
  88. package/addon/templates/connectivity/telematics/index/details/index.hbs +2 -2
  89. package/addon/templates/connectivity/telematics/index/details.hbs +14 -2
  90. package/addon/templates/connectivity/telematics/index/edit.hbs +1 -1
  91. package/addon/templates/management/drivers/index/details/positions.hbs +2 -0
  92. package/addon/templates/management/vehicles/index/details/devices.hbs +1 -2
  93. package/addon/templates/management/vehicles/index/details/positions.hbs +1 -0
  94. package/addon/utils/fleet-ops-options.js +95 -0
  95. package/app/components/device/card.js +1 -0
  96. package/app/components/device/manager.js +1 -0
  97. package/app/components/device/panel-header.js +1 -0
  98. package/app/components/device/pill.js +1 -0
  99. package/app/components/driver/pill.js +1 -0
  100. package/app/components/map/drawer/device-event-listing.js +1 -0
  101. package/app/components/map/drawer/position-listing.js +1 -0
  102. package/app/components/modals/attach-device.js +1 -0
  103. package/app/components/order/pill.js +1 -0
  104. package/app/components/positions-replay.js +1 -0
  105. package/app/components/sensor/panel-header.js +1 -0
  106. package/app/components/vehicle/pill.js +1 -0
  107. package/app/helpers/get-fleet-ops-option-label.js +1 -0
  108. package/app/routes/management/drivers/index/details/positions.js +1 -0
  109. package/app/routes/management/vehicles/index/details/positions.js +1 -0
  110. package/app/services/position-playback.js +1 -0
  111. package/app/services/resource-metadata.js +1 -0
  112. package/app/templates/management/drivers/index/details/positions.js +1 -0
  113. package/app/templates/management/vehicles/index/details/positions.js +1 -0
  114. package/composer.json +1 -1
  115. package/extension.json +1 -1
  116. package/package.json +4 -4
  117. package/server/config/telematics.php +111 -0
  118. package/server/migrations/2025_10_27_000001_add_telematics_integration_fields.php +70 -0
  119. package/server/migrations/2025_10_27_171322_fix_device_column_names.php +107 -0
  120. package/server/migrations/2025_10_27_203023_add_company_uuid_to_device_events_table.php +28 -0
  121. package/server/src/Console/Commands/ReplayVehicleLocations.php +225 -0
  122. package/server/src/Contracts/TelematicProviderDescriptor.php +72 -0
  123. package/server/src/Contracts/TelematicProviderInterface.php +119 -0
  124. package/server/src/Exceptions/TelematicProviderException.php +14 -0
  125. package/server/src/Exceptions/TelematicRateLimitExceededException.php +12 -0
  126. package/server/src/Http/Controllers/Api/v1/DriverController.php +24 -14
  127. package/server/src/Http/Controllers/Api/v1/VehicleController.php +27 -7
  128. package/server/src/Http/Controllers/Internal/v1/DeviceController.php +22 -0
  129. package/server/src/Http/Controllers/Internal/v1/PositionController.php +240 -0
  130. package/server/src/Http/Controllers/Internal/v1/SensorController.php +11 -0
  131. package/server/src/Http/Controllers/Internal/v1/TelematicController.php +141 -0
  132. package/server/src/Http/Controllers/TelematicWebhookController.php +169 -0
  133. package/server/src/Http/Filter/DeviceEventFilter.php +68 -0
  134. package/server/src/Http/Filter/PositionFilter.php +35 -0
  135. package/server/src/Http/Resources/v1/Position.php +44 -0
  136. package/server/src/Jobs/ReplayPositions.php +64 -0
  137. package/server/src/Jobs/SendPositionReplay.php +65 -0
  138. package/server/src/Jobs/SyncTelematicDevicesJob.php +106 -0
  139. package/server/src/Jobs/TestTelematicConnectionJob.php +102 -0
  140. package/server/src/Models/Asset.php +10 -8
  141. package/server/src/Models/Device.php +79 -12
  142. package/server/src/Models/DeviceEvent.php +33 -3
  143. package/server/src/Models/Driver.php +28 -1
  144. package/server/src/Models/Maintenance.php +15 -12
  145. package/server/src/Models/Part.php +2 -0
  146. package/server/src/Models/Payload.php +0 -1
  147. package/server/src/Models/Place.php +4 -1
  148. package/server/src/Models/Position.php +27 -17
  149. package/server/src/Models/Sensor.php +78 -13
  150. package/server/src/Models/Telematic.php +116 -6
  151. package/server/src/Models/TrackingNumber.php +3 -1
  152. package/server/src/Models/Vehicle.php +8 -11
  153. package/server/src/Models/WorkOrder.php +8 -5
  154. package/server/src/Providers/FleetOpsServiceProvider.php +2 -0
  155. package/server/src/Support/Telematics/Providers/AbstractProvider.php +151 -0
  156. package/server/src/Support/Telematics/Providers/FlespiProvider.php +182 -0
  157. package/server/src/Support/Telematics/Providers/GeotabProvider.php +181 -0
  158. package/server/src/Support/Telematics/Providers/SamsaraProvider.php +177 -0
  159. package/server/src/Support/Telematics/TelematicProviderRegistry.php +147 -0
  160. package/server/src/Support/Telematics/TelematicService.php +223 -0
  161. package/server/src/Support/Utils.php +1 -1
  162. package/server/src/routes.php +24 -1
@@ -1,12 +1,12 @@
1
1
  <div class="form-wrapper" ...attributes>
2
- <ContentPanel @title={{t "custom-entity.form.details"}} @open={{true}} @wrapperClass="bordered-top">
2
+ <ContentPanel @title={{t "custom-entity.fields.details"}} @open={{true}} @wrapperClass="bordered-top">
3
3
  <div class="space-y-2">
4
- <InputGroup @name={{t "custom-entity.form.name"}} @value={{@resource.name}} />
5
- <InputGroup @name={{t "custom-entity.form.description"}} @value={{@resource.description}} />
6
- <InputGroup @name={{t "custom-entity.form.type"}} @value={{@resource.type}} {{on "input" this.setCustomEntityType}} />
4
+ <InputGroup @name={{t "custom-entity.fields.name"}} @value={{@resource.name}} />
5
+ <InputGroup @name={{t "custom-entity.fields.description"}} @value={{@resource.description}} />
6
+ <InputGroup @name={{t "custom-entity.fields.type"}} @value={{@resource.type}} {{on "input" this.setCustomEntityType}} />
7
7
  </div>
8
8
  </ContentPanel>
9
- <ContentPanel @title={{t "custom-entity.form.entity-image"}} @open={{true}} @wrapperClass="bordered-top">
9
+ <ContentPanel @title={{t "custom-entity.fields.entity-image"}} @open={{true}} @wrapperClass="bordered-top">
10
10
  <div class="w-32 flex flex-col items-center">
11
11
  <Image src={{@resource.photo_url}} alt={{@resource.name}} height="128" width="128" class="h-32 w-32 rounded-md" />
12
12
  <UploadButton
@@ -19,32 +19,32 @@
19
19
  />
20
20
  </div>
21
21
  </ContentPanel>
22
- <ContentPanel @title={{t "custom-entity.form.measurements"}} @open={{true}} @wrapperClass="bordered-top">
22
+ <ContentPanel @title={{t "custom-entity.fields.measurements"}} @open={{true}} @wrapperClass="bordered-top">
23
23
  <div class="grid grid-cols-2 gap-2">
24
24
  <InputGroup
25
- @name={{t "custom-entity.form.length"}}
26
- @helpText={{t "custom-entity.form.length-text"}}
25
+ @name={{t "custom-entity.fields.length"}}
26
+ @helpText={{t "custom-entity.fields.length-text"}}
27
27
  @wrapperClass="mb-2"
28
28
  >
29
29
  <UnitInput class="w-full" @value={{@resource.length}} @unit={{@resource.dimensions_unit}} @canSelectUnit={{true}} @onUnitChange={{this.updateCustomEntityDimensionsUnit}} />
30
30
  </InputGroup>
31
31
  <InputGroup
32
- @name={{t "custom-entity.form.width"}}
33
- @helpText={{t "custom-entity.form.width-text"}}
32
+ @name={{t "custom-entity.fields.width"}}
33
+ @helpText={{t "custom-entity.fields.width-text"}}
34
34
  @wrapperClass="mb-2"
35
35
  >
36
36
  <UnitInput class="w-full" @value={{@resource.width}} @unit={{@resource.dimensions_unit}} @canSelectUnit={{true}} @onUnitChange={{this.updateCustomEntityDimensionsUnit}} />
37
37
  </InputGroup>
38
38
  <InputGroup
39
- @name={{t "custom-entity.form.height"}}
40
- @helpText={{t "custom-entity.form.height-text"}}
39
+ @name={{t "custom-entity.fields.height"}}
40
+ @helpText={{t "custom-entity.fields.height-text"}}
41
41
  @wrapperClass="mb-2"
42
42
  >
43
43
  <UnitInput class="w-full" @value={{@resource.height}} @unit={{@resource.dimensions_unit}} @canSelectUnit={{true}} @onUnitChange={{this.updateCustomEntityDimensionsUnit}} />
44
44
  </InputGroup>
45
45
  <InputGroup
46
- @name={{t "custom-entity.form.weight"}}
47
- @helpText={{t "custom-entity.form.weight-text"}}
46
+ @name={{t "custom-entity.fields.weight"}}
47
+ @helpText={{t "custom-entity.fields.weight-text"}}
48
48
  @wrapperClass="mb-2"
49
49
  >
50
50
  <UnitInput
@@ -0,0 +1 @@
1
+ {{yield}}
@@ -0,0 +1,3 @@
1
+ import Component from '@glimmer/component';
2
+
3
+ export default class DeviceCardComponent extends Component {}
@@ -1,14 +1,44 @@
1
1
  <div class="details-wrapper" ...attributes>
2
- <ContentPanel @title={{t "device.fields.title"}} @open={{true}} @wrapperClass="bordered-top">
2
+ <ContentPanel @title="Integration" @open={{true}} @wrapperClass="bordered-top">
3
+ <div class="grid grid-cols-1 lg:grid-cols-3 gap-2">
4
+ <div class="field-info-container lg:col-span-3">
5
+ <div class="field-name">{{t "device.fields.telematic"}}</div>
6
+ <div class="field-value">
7
+ {{#if @resource.telematic}}
8
+ <div class="flex flex-row space-x-2 text-sm">
9
+ <div class="pt-1.5">
10
+ <Image src={{@resource.telematic.provider_descriptor.icon}} class="w-5 h-5" />
11
+ </div>
12
+ <div>
13
+ <div class="font-semibold">{{@resource.telematic.provider_descriptor.label}}</div>
14
+ <div class="text-xs text-gray-400 dark:text-gray-500">
15
+ {{n-a @resource.telematic.provider_descriptor.description}}
16
+ </div>
17
+ </div>
18
+ </div>
19
+ {{else}}
20
+ {{n-a null}}
21
+ {{/if}}
22
+ </div>
23
+ </div>
24
+
25
+ <div class="field-info-container">
26
+ <div class="field-name">{{t "device.fields.data-frequency"}}</div>
27
+ <div class="field-value">{{n-a @resource.data_frequency}}</div>
28
+ </div>
29
+ </div>
30
+ </ContentPanel>
31
+
32
+ <ContentPanel @title="Identity" @open={{true}} @wrapperClass="bordered-top">
3
33
  <div class="grid grid-cols-1 lg:grid-cols-3 gap-2">
4
34
  <div class="field-info-container">
5
35
  <div class="field-name">{{t "device.fields.device-name"}}</div>
6
- <div class="field-value">{{n-a @resource.device_name}}</div>
36
+ <div class="field-value">{{n-a @resource.name}}</div>
7
37
  </div>
8
38
 
9
39
  <div class="field-info-container">
10
40
  <div class="field-name">{{t "device.fields.device-type"}}</div>
11
- <div class="field-value">{{smart-humanize @resource.device_type}}</div>
41
+ <div class="field-value">{{n-a (get-fleet-ops-option-label "deviceTypes" @resource.type)}}</div>
12
42
  </div>
13
43
 
14
44
  <div class="field-info-container">
@@ -17,13 +47,22 @@
17
47
  </div>
18
48
 
19
49
  <div class="field-info-container">
20
- <div class="field-name">{{t "device.fields.provider"}}</div>
21
- <div class="field-value">{{n-a @resource.device_provider}}</div>
50
+ <div class="field-name">Internal ID</div>
51
+ <div class="field-value">{{n-a @resource.internal_id}}</div>
52
+ </div>
53
+ </div>
54
+ </ContentPanel>
55
+
56
+ <ContentPanel @title="Hardware" @open={{true}} @wrapperClass="bordered-top">
57
+ <div class="grid grid-cols-1 lg:grid-cols-3 gap-2">
58
+ <div class="field-info-container">
59
+ <div class="field-name">{{t "device.fields.device-provider"}}</div>
60
+ <div class="field-value">{{n-a (titleize @resource.provider)}}</div>
22
61
  </div>
23
62
 
24
63
  <div class="field-info-container">
25
- <div class="field-name">{{t "device.fields.model"}}</div>
26
- <div class="field-value">{{n-a @resource.device_model}}</div>
64
+ <div class="field-name">{{t "device.fields.device-model"}}</div>
65
+ <div class="field-value">{{n-a @resource.model}}</div>
27
66
  </div>
28
67
 
29
68
  <div class="field-info-container">
@@ -31,70 +70,80 @@
31
70
  <div class="field-value">{{n-a @resource.manufacturer}}</div>
32
71
  </div>
33
72
 
34
- <div class="field-info-container">
73
+ <div class="field-info-container lg:col-span-3">
35
74
  <div class="field-name">{{t "device.fields.serial-number"}}</div>
36
75
  <div class="field-value">{{n-a @resource.serial_number}}</div>
37
76
  </div>
77
+ </div>
78
+ </ContentPanel>
38
79
 
39
- <div class="field-info-container">
40
- <div class="field-name">{{t "device.fields.location"}}</div>
41
- <div class="field-value">{{n-a @resource.device_location}}</div>
42
- </div>
43
-
44
- <div class="field-info-container">
45
- <div class="field-name">{{t "device.fields.status"}}</div>
46
- <div class="field-value">
47
- <Badge @status={{@resource.status}}>{{smart-humanize @resource.status}}</Badge>
48
- </div>
80
+ <ContentPanel @title="Installation & Maintenance" @open={{true}} @wrapperClass="bordered-top">
81
+ <div class="grid grid-cols-1 lg:grid-cols-3 gap-2">
82
+ <div class="field-info-container lg:col-span-2">
83
+ <div class="field-name">{{t "device.fields.device-location"}}</div>
84
+ <div class="field-value">{{n-a @resource.location}}</div>
49
85
  </div>
50
86
 
51
87
  <div class="field-info-container">
52
88
  <div class="field-name">{{t "device.fields.installation-date"}}</div>
53
- <div class="field-value">{{format-date @resource.installation_date}}</div>
89
+ <div class="field-value">{{or (format-date @resource.installation_date) "-"}}</div>
54
90
  </div>
55
91
 
56
92
  <div class="field-info-container">
57
- <div class="field-name">{{t "device.fields.last-maintenance"}}</div>
58
- <div class="field-value">{{format-date @resource.last_maintenance_date}}</div>
93
+ <div class="field-name">{{t "device.fields.last-maintenance-date"}}</div>
94
+ <div class="field-value">{{or (format-date @resource.last_maintenance_date) "-"}}</div>
59
95
  </div>
96
+ </div>
97
+ </ContentPanel>
60
98
 
99
+ <ContentPanel @title="Connectivity" @open={{true}} @wrapperClass="bordered-top">
100
+ <div class="grid grid-cols-1 lg:grid-cols-3 gap-2">
61
101
  <div class="field-info-container">
62
- <div class="field-name">{{t "device.fields.data-frequency"}}</div>
63
- <div class="field-value">{{n-a @resource.data_frequency}}</div>
102
+ <div class="field-name">{{t "common.online"}}</div>
103
+ <div class="field-value">
104
+ {{#if @resource.is_online}}
105
+ <Badge @status="online">{{t "common.online"}}</Badge>
106
+ {{else}}
107
+ <Badge @status="offline">{{t "common.offline"}}</Badge>
108
+ {{/if}}
109
+ </div>
64
110
  </div>
65
111
 
66
112
  <div class="field-info-container">
67
- <div class="field-name">{{t "device.fields.telematic"}}</div>
68
- <div class="field-value">{{n-a @resource.telematic_name}}</div>
113
+ <div class="field-name">{{t "common.last-seen-at"}}</div>
114
+ <div class="field-value">{{or (format-date @resource.last_online_at) "-"}}</div>
69
115
  </div>
70
116
 
71
- <div class="field-info-container">
72
- <div class="field-name">{{t "device.fields.warranty"}}</div>
73
- <div class="field-value">{{n-a @resource.warranty_name}}</div>
74
- </div>
117
+ {{#if @resource.signal_strength}}
118
+ <div class="field-info-container">
119
+ <div class="field-name">{{t "device.fields.signal-strength"}}</div>
120
+ <div class="field-value">{{n-a @resource.signal_strength}}</div>
121
+ </div>
122
+ {{/if}}
123
+ </div>
124
+ </ContentPanel>
75
125
 
126
+ <ContentPanel @title="Warranty & Status" @open={{true}} @wrapperClass="bordered-top">
127
+ <div class="grid grid-cols-1 lg:grid-cols-3 gap-2">
76
128
  <div class="field-info-container">
77
- <div class="field-name">{{t "device.fields.online-status"}}</div>
129
+ <div class="field-name">{{t "device.fields.status"}}</div>
78
130
  <div class="field-value">
79
- {{#if @resource.is_online}}
80
- <Badge @status="success">{{t "device.fields.online"}}</Badge>
81
- {{else}}
82
- <Badge @status="danger">{{t "device.fields.offline"}}</Badge>
83
- {{/if}}
131
+ <Badge @status={{@resource.status}}>
132
+ {{smart-humanize @resource.status}}
133
+ </Badge>
84
134
  </div>
85
135
  </div>
86
136
 
87
- <div class="field-info-container">
88
- <div class="field-name">{{t "device.fields.attached-to"}}</div>
89
- <div class="field-value">{{n-a @resource.attached_to_name}}</div>
137
+ <div class="field-info-container lg:col-span-2">
138
+ <div class="field-name">{{t "device.fields.warranty"}}</div>
139
+ <div class="field-value">{{n-a @resource.warranty.name}}</div>
90
140
  </div>
141
+ </div>
142
+ </ContentPanel>
91
143
 
144
+ <ContentPanel @title="Notes" @open={{true}} @wrapperClass="bordered-top">
145
+ <div class="grid grid-cols-1 lg:grid-cols-1 gap-2">
92
146
  <div class="field-info-container">
93
- <div class="field-name">{{t "device.fields.last-online"}}</div>
94
- <div class="field-value">{{format-date @resource.last_online_at}}</div>
95
- </div>
96
-
97
- <div class="field-info-container col-span-3">
98
147
  <div class="field-name">{{t "device.fields.notes"}}</div>
99
148
  <div class="field-value">{{n-a @resource.notes}}</div>
100
149
  </div>
@@ -1,45 +1,109 @@
1
1
  <div class="form-wrapper" ...attributes>
2
- <ContentPanel @title={{t "device.fields.title"}} @open={{true}} @wrapperClass="bordered-top">
2
+ <ContentPanel @title="Integration" @open={{true}} @wrapperClass="bordered-top">
3
3
  <div class="grid grid-cols-1 gap-2 lg:grid-cols-2 lg:gap-2 no-input-group-padding text-xs">
4
- <InputGroup @name={{t "device.fields.device-name"}} @wrapperClass="col-span-2">
5
- <Input
6
- @value={{@resource.device_name}}
7
- @type="text"
8
- class="w-full form-input"
9
- placeholder={{t "device.fields.device-name-placeholder"}}
10
- disabled={{cannot-write @resource}}
11
- />
4
+ <InputGroup @name={{t "device.fields.telematic"}}>
5
+ <ModelSelect
6
+ @modelName="telematic"
7
+ @selectedModel={{@resource.telematic}}
8
+ @placeholder={{t "device.fields.telematic-placeholder"}}
9
+ @triggerClass="form-select form-input"
10
+ @infiniteScroll={{false}}
11
+ @renderInPlace={{true}}
12
+ @onChange={{this.selectTelematic}}
13
+ @disabled={{cannot-write @resource}}
14
+ as |model|
15
+ >
16
+ <div class="space-x-2 text-sm">
17
+ <div class="inline-block align-top">
18
+ <div class="hide-from-trigger h-1.5 w-full" />
19
+ <Image src={{model.provider_descriptor.icon}} class="w-5 h-5" />
20
+ </div>
21
+ <div class="inline-block">
22
+ <div class="font-semibold normalize-in-trigger">{{or model.name model.public_id}}</div>
23
+ <div class="hide-from-trigger">{{n-a model.provider_descriptor.label (titleize model.provider)}}</div>
24
+ </div>
25
+ </div>
26
+ </ModelSelect>
12
27
  </InputGroup>
13
28
 
14
29
  <InputGroup @name={{t "device.fields.device-type"}}>
15
30
  <div class="fleetbase-model-select fleetbase-power-select ember-model-select">
16
31
  <PowerSelect
17
- @options={{this.deviceTypeOptions}}
18
- @selected={{@resource.device_type}}
19
- @onChange={{fn (mut @resource.device_type)}}
20
- @placeholder={{t "device.fields.device-type-placeholder"}}
32
+ @options={{get-fleet-ops-options "deviceTypes"}}
33
+ @selected={{find-by "value" @resource.type (get-fleet-ops-options "deviceTypes")}}
34
+ @onChange={{set-model-attr @resource "type"}}
35
+ @placeholder={{t "common.select-field" field=(t "common.type")}}
21
36
  @triggerClass="form-select form-input"
22
37
  @disabled={{cannot-write @resource}}
23
- as |type|
38
+ as |option|
24
39
  >
25
- {{smart-humanize type}}
40
+ <div class="text-sm">
41
+ <div class="font-semibold normalize-in-trigger">{{option.label}}</div>
42
+ <div class="hide-from-trigger">{{option.description}}</div>
43
+ </div>
26
44
  </PowerSelect>
27
45
  </div>
28
46
  </InputGroup>
29
47
 
30
- <InputGroup @name={{t "device.fields.device-id"}}>
48
+ <InputGroup @name={{t "device.fields.data-frequency"}}>
31
49
  <Input
32
- @value={{@resource.device_id}}
50
+ @value={{@resource.data_frequency}}
33
51
  @type="text"
34
52
  class="w-full form-input"
35
- placeholder={{t "device.fields.device-id-placeholder"}}
53
+ placeholder={{t "device.fields.data-frequency-placeholder"}}
36
54
  disabled={{cannot-write @resource}}
37
55
  />
38
56
  </InputGroup>
57
+ </div>
58
+ </ContentPanel>
39
59
 
60
+ <ContentPanel @title="Identity" @open={{true}} @wrapperClass="bordered-top">
61
+ <div class="flex flex-row space-x-3 mb-3">
62
+ <div class="flex items-center justify-start rounded-full">
63
+ <Image src={{@resource.photo_url}} alt={{@resource.name}} height="48" width="48" class="h-14 w-14 rounded-full shadow-sm" />
64
+ <Attach::Tooltip @class="clean" @animation="scale" @placement="top">
65
+ <InputInfo @text={{t "common.upload-new-photo"}} />
66
+ </Attach::Tooltip>
67
+ </div>
68
+ <div>
69
+ <UploadButton
70
+ @name={{t "common.photos"}}
71
+ @accept="image/*"
72
+ @onFileAdded={{perform this.handlePhotoUpload}}
73
+ @icon="upload"
74
+ @size="xs"
75
+ @buttonText={{t "common.upload-image"}}
76
+ @disabled={{cannot-write @resource}}
77
+ />
78
+ <div class="px-1">
79
+ <span class="text-gray-300 dark:text-gray-600 text-xs">{{t "common.upload-image-supported"}}</span>
80
+ </div>
81
+ </div>
82
+ </div>
83
+ <div class="grid grid-cols-1 gap-2 lg:grid-cols-2 lg:gap-2 no-input-group-padding text-xs">
84
+ <InputGroup @name={{t "device.fields.device-name"}}>
85
+ <Input
86
+ @value={{@resource.name}}
87
+ @type="text"
88
+ class="w-full form-input"
89
+ placeholder={{t "device.fields.device-name-placeholder"}}
90
+ disabled={{cannot-write @resource}}
91
+ />
92
+ </InputGroup>
93
+
94
+ <InputGroup @name={{t "device.fields.device-id"}}>
95
+ <Input @value={{@resource.device_id}} @type="text" class="w-full form-input" placeholder={{t "device.fields.device-id-placeholder"}} disabled={{cannot-write @resource}} />
96
+ </InputGroup>
97
+
98
+ <InputGroup @name="Internal ID" @value={{@resource.internal_id}} disabled={{cannot-write @resource}} />
99
+ </div>
100
+ </ContentPanel>
101
+
102
+ <ContentPanel @title="Hardware" @open={{true}} @wrapperClass="bordered-top">
103
+ <div class="grid grid-cols-1 gap-2 lg:grid-cols-3 lg:gap-2 no-input-group-padding text-xs">
40
104
  <InputGroup @name={{t "device.fields.device-provider"}}>
41
105
  <Input
42
- @value={{@resource.device_provider}}
106
+ @value={{@resource.provider}}
43
107
  @type="text"
44
108
  class="w-full form-input"
45
109
  placeholder={{t "device.fields.device-provider-placeholder"}}
@@ -49,7 +113,7 @@
49
113
 
50
114
  <InputGroup @name={{t "device.fields.device-model"}}>
51
115
  <Input
52
- @value={{@resource.device_model}}
116
+ @value={{@resource.model}}
53
117
  @type="text"
54
118
  class="w-full form-input"
55
119
  placeholder={{t "device.fields.device-model-placeholder"}}
@@ -67,7 +131,7 @@
67
131
  />
68
132
  </InputGroup>
69
133
 
70
- <InputGroup @name={{t "device.fields.serial-number"}}>
134
+ <InputGroup @name={{t "device.fields.serial-number"}} @wrapperClass="lg:col-span-3">
71
135
  <Input
72
136
  @value={{@resource.serial_number}}
73
137
  @type="text"
@@ -76,10 +140,14 @@
76
140
  disabled={{cannot-write @resource}}
77
141
  />
78
142
  </InputGroup>
143
+ </div>
144
+ </ContentPanel>
79
145
 
80
- <InputGroup @name={{t "device.fields.device-location"}}>
146
+ <ContentPanel @title="Installation & Maintenance" @open={{true}} @wrapperClass="bordered-top">
147
+ <div class="grid grid-cols-1 gap-2 lg:grid-cols-3 lg:gap-2 no-input-group-padding text-xs">
148
+ <InputGroup @name={{t "device.fields.device-location"}} @wrapperClass="lg:col-span-2">
81
149
  <Input
82
- @value={{@resource.device_location}}
150
+ @value={{@resource.location}}
83
151
  @type="text"
84
152
  class="w-full form-input"
85
153
  placeholder={{t "device.fields.device-location-placeholder"}}
@@ -106,50 +174,30 @@
106
174
  class="w-full form-input"
107
175
  />
108
176
  </InputGroup>
177
+ </div>
178
+ </ContentPanel>
109
179
 
110
- <InputGroup @name={{t "device.fields.data-frequency"}}>
111
- <Input
112
- @value={{@resource.data_frequency}}
113
- @type="text"
114
- class="w-full form-input"
115
- placeholder={{t "device.fields.data-frequency-placeholder"}}
116
- disabled={{cannot-write @resource}}
117
- />
118
- </InputGroup>
119
-
180
+ <ContentPanel @title="Warranty & Status" @open={{true}} @wrapperClass="bordered-top">
181
+ <div class="grid grid-cols-1 gap-2 lg:grid-cols-2 lg:gap-2 no-input-group-padding text-xs">
120
182
  <InputGroup @name={{t "device.fields.status"}}>
121
183
  <div class="fleetbase-model-select fleetbase-power-select ember-model-select">
122
184
  <PowerSelect
123
- @options={{this.statusOptions}}
124
- @selected={{@resource.status}}
125
- @onChange={{fn (mut @resource.status)}}
126
- @placeholder={{t "device.fields.status-placeholder"}}
185
+ @options={{get-fleet-ops-options "deviceStatuses"}}
186
+ @selected={{find-by "value" @resource.status (get-fleet-ops-options "deviceStatuses")}}
187
+ @onChange={{set-model-attr @resource "status"}}
188
+ @placeholder={{t "common.select-field" field=(t "common.status")}}
127
189
  @triggerClass="form-select form-input"
128
190
  @disabled={{cannot-write @resource}}
129
- as |status|
191
+ as |option|
130
192
  >
131
- {{smart-humanize status}}
193
+ <div class="text-sm">
194
+ <div class="font-semibold normalize-in-trigger">{{option.label}}</div>
195
+ <div class="hide-from-trigger">{{option.description}}</div>
196
+ </div>
132
197
  </PowerSelect>
133
198
  </div>
134
199
  </InputGroup>
135
200
 
136
- <InputGroup @name={{t "device.fields.telematic"}}>
137
- <ModelSelect
138
- @modelName="telematic"
139
- @selectedModel={{@resource.telematic}}
140
- @placeholder={{t "device.fields.telematic-placeholder"}}
141
- @triggerClass="form-select form-input"
142
- @infiniteScroll={{false}}
143
- @renderInPlace={{true}}
144
- @onChange={{fn (mut @resource.telematic)}}
145
- @onChangeId={{fn (mut @resource.telematic_uuid)}}
146
- @disabled={{cannot-write @resource}}
147
- as |model|
148
- >
149
- {{model.name}}
150
- </ModelSelect>
151
- </InputGroup>
152
-
153
201
  <InputGroup @name={{t "device.fields.warranty"}}>
154
202
  <ModelSelect
155
203
  @modelName="warranty"
@@ -168,7 +216,11 @@
168
216
  {{model.provider}}
169
217
  </ModelSelect>
170
218
  </InputGroup>
219
+ </div>
220
+ </ContentPanel>
171
221
 
222
+ <ContentPanel @title="Notes" @open={{true}} @wrapperClass="bordered-top">
223
+ <div class="grid grid-cols-1 gap-2 no-input-group-padding text-xs">
172
224
  <InputGroup @name={{t "device.fields.notes"}} @wrapperClass="col-span-2">
173
225
  <Textarea
174
226
  @value={{@resource.notes}}
@@ -179,10 +231,6 @@
179
231
  rows={{4}}
180
232
  />
181
233
  </InputGroup>
182
-
183
- <RegistryYield @registry="fleet-ops:component:device:form:details" as |RegistryComponent|>
184
- <RegistryComponent @resource={{@resource}} @controller={{@controller}} @permission={{get-write-permission @resource}} />
185
- </RegistryYield>
186
234
  </div>
187
235
  </ContentPanel>
188
236
 
@@ -1,13 +1,41 @@
1
1
  import Component from '@glimmer/component';
2
+ import { action } from '@ember/object';
3
+ import { inject as service } from '@ember/service';
4
+ import { task } from 'ember-concurrency';
2
5
 
3
6
  export default class DeviceFormComponent extends Component {
4
- /**
5
- * Device type options
6
- */
7
- deviceTypeOptions = ['dashcam', 'obd', 'blackbox', 'tablet', 'tracker', 'sensor'];
7
+ @service fetch;
8
+ @service currentUser;
9
+ @service notifications;
8
10
 
9
- /**
10
- * Status options for devices
11
- */
12
- statusOptions = ['active', 'inactive', 'maintenance', 'retired'];
11
+ @action selectTelematic(telematic) {
12
+ this.args.resource.setProperties({
13
+ telematic,
14
+ telematic_uuid: telematic.id,
15
+ provider: telematic.provider,
16
+ });
17
+ }
18
+
19
+ @task *handlePhotoUpload(file) {
20
+ try {
21
+ yield this.fetch.uploadFile.perform(
22
+ file,
23
+ {
24
+ path: `uploads/${this.currentUser.companyId}/devices/${this.args.resource.id}`,
25
+ subject_uuid: this.args.resource.id,
26
+ subject_type: 'fleet-ops:device',
27
+ type: 'device_photo',
28
+ },
29
+ (uploadedFile) => {
30
+ this.args.resource.setProperties({
31
+ photo_uuid: uploadedFile.id,
32
+ photo_url: uploadedFile.url,
33
+ photo: uploadedFile,
34
+ });
35
+ }
36
+ );
37
+ } catch (err) {
38
+ this.notifications.error('Unable to upload photo: ' + err.message);
39
+ }
40
+ }
13
41
  }
@@ -0,0 +1,29 @@
1
+ <div class="fleetops-device-manager" ...attributes>
2
+ <div class="flex flex-row items-center justify-between mb-4 px-3 pt-2">
3
+ <div></div>
4
+ <div>
5
+ <Button @icon="plus" @text="Attach device" @onClick={{this.addDevice}} @disabled={{not @resource}} />
6
+ </div>
7
+ </div>
8
+ <div>
9
+ {{#each this.devices as |device|}}
10
+ <div class="flex flex-row justify-between px-3 py-4 border-t border-gray-200 dark:border-gray-700">
11
+ <Device::Pill @device={{device}} />
12
+ <div>
13
+ <Button @type="danger" @icon="times" @text="Detach" @size="xs" @onClick={{fn this.removeDevice device}} />
14
+ </div>
15
+ </div>
16
+ {{else}}
17
+ <div class="empty-state bg-white dark:bg-gray-800 rounded-lg shadow-sm p-12 text-center">
18
+ <FaIcon @icon="hard-drive" @size="2x" class="text-gray-400 mb-2" />
19
+ <h3 class="text-base font-semibold text-gray-700 dark:text-gray-300">
20
+ No Devices Attached
21
+ </h3>
22
+ <p class="text-sm text-gray-500 dark:text-gray-400">
23
+ Click Attach device above to start receiving telematic data for this
24
+ {{get-model-name @resource}}
25
+ </p>
26
+ </div>
27
+ {{/each}}
28
+ </div>
29
+ </div>