@fleetbase/fleetops-engine 0.4.12 → 0.4.14

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 (66) hide show
  1. package/addon/components/admin/avatar-management.hbs +78 -0
  2. package/addon/components/admin/avatar-management.js +191 -0
  3. package/addon/components/avatar-picker.hbs +15 -0
  4. package/addon/components/avatar-picker.js +57 -0
  5. package/addon/components/driver-form-panel.hbs +12 -13
  6. package/addon/components/driver-panel/details.hbs +7 -7
  7. package/addon/components/edit-order-route-panel.hbs +9 -69
  8. package/addon/components/edit-order-route-panel.js +78 -27
  9. package/addon/components/fleet-listing-panel.hbs +1 -1
  10. package/addon/components/layout/fleet-ops-sidebar/driver-listing.hbs +1 -1
  11. package/addon/components/layout/fleet-ops-sidebar.hbs +2 -1
  12. package/addon/components/live-map.js +0 -1
  13. package/addon/components/order/schedule-card.hbs +29 -17
  14. package/addon/components/order/schedule-card.js +1 -1
  15. package/addon/components/place-form-panel.hbs +35 -51
  16. package/addon/components/place-panel/details.hbs +10 -14
  17. package/addon/components/place-panel.hbs +2 -12
  18. package/addon/components/vehicle-form-panel.hbs +11 -80
  19. package/addon/components/vehicle-form-panel.js +33 -0
  20. package/addon/controllers/management/places/index.js +29 -21
  21. package/addon/controllers/operations/orders/index/view.js +10 -10
  22. package/addon/controllers/operations/scheduler/index.js +7 -4
  23. package/addon/engine.js +7 -1
  24. package/addon/styles/fleetops-engine.css +94 -0
  25. package/addon/templates/management/places/index.hbs +1 -1
  26. package/addon/templates/operations/scheduler/index.hbs +5 -4
  27. package/addon/utils/create-full-calendar-event-from-order.js +23 -1
  28. package/app/components/admin/avatar-management.js +1 -0
  29. package/app/components/avatar-picker.js +1 -0
  30. package/composer.json +2 -2
  31. package/config/environment.js +2 -0
  32. package/extension.json +1 -1
  33. package/package.json +3 -3
  34. package/server/migrations/2023_09_14_074512_update_vehicles_table.php +2 -2
  35. package/server/migrations/2023_09_14_074619_create_vehicle_devices_table.php +2 -2
  36. package/server/migrations/2023_09_26_071205_add_online_column_to_vehicles_table.php +2 -2
  37. package/server/migrations/2023_09_26_083957_create_fleet_vehicles_table.php +2 -2
  38. package/server/migrations/2023_09_27_023757_add_column_to_fleets_table.php +2 -2
  39. package/server/migrations/2023_10_05_074811_update_vehicle.php +2 -2
  40. package/server/migrations/2023_10_25_093014_create_vehicle_device_events_table.php +2 -2
  41. package/server/migrations/2023_11_03_072503_refactor_issues_table_columns.php +2 -2
  42. package/server/migrations/2023_11_06_040645_refactor_fuel_reports_table.php +2 -2
  43. package/server/migrations/2023_11_09_041807_add_status_column_to_fuel_reports_table.php +2 -2
  44. package/server/migrations/2023_11_15_062814_add_comments_column_and_resolved_at_column_to_vehicle_device_events_table.php +2 -2
  45. package/server/migrations/2023_11_17_053001_add_category_uuid_to_entities_table.php +2 -2
  46. package/server/migrations/2023_11_28_074244_add_supplier_uuid_column_to_entities_table.php +2 -2
  47. package/server/migrations/{2023_12_14_100209_add_meta_column_on_driver_table.php → 2023_12_14_100209_add_meta_column_on_driver_table copy.php } +10 -4
  48. package/server/migrations/2024_02_18_100209_add_avatar_url_columns_table.php +54 -0
  49. package/server/src/Expansions/UserExpansion.php +1 -0
  50. package/server/src/Http/Controllers/Api/v1/DriverController.php +7 -1
  51. package/server/src/Http/Controllers/Api/v1/PlaceController.php +11 -2
  52. package/server/src/Http/Controllers/Internal/v1/DriverController.php +15 -8
  53. package/server/src/Http/Controllers/Internal/v1/OrderController.php +46 -0
  54. package/server/src/Http/Controllers/Internal/v1/PlaceController.php +12 -0
  55. package/server/src/Http/Resources/v1/Driver.php +6 -4
  56. package/server/src/Http/Resources/v1/Order.php +2 -1
  57. package/server/src/Http/Resources/v1/Payload.php +8 -4
  58. package/server/src/Http/Resources/v1/Place.php +2 -0
  59. package/server/src/Http/Resources/v1/Vehicle.php +1 -0
  60. package/server/src/Models/Driver.php +76 -0
  61. package/server/src/Models/Payload.php +49 -3
  62. package/server/src/Models/Place.php +71 -0
  63. package/server/src/Models/Vehicle.php +44 -20
  64. package/server/src/Notifications/OrderAssigned.php +2 -0
  65. package/server/src/routes.php +3 -0
  66. package/translations/en-us.yaml +16 -4
@@ -0,0 +1,78 @@
1
+ <div class="mb-4">
2
+ <div class="flex flex-row items-stretch rounded-lg border border-gray-200 dark:border-gray-700">
3
+ <div class="w-48 h-full bg-white dark:bg-gray-700 rounded-l-lg">
4
+ {{#each this.categories as |category index|}}
5
+ <a href="#" class="flex flex-row px-4 py-2 border-b border-gray-200 dark:border-gray-900 hover:bg-blue-400 {{if (eq this.currentCategory.type category.type) 'bg-blue-500'}} {{if (eq index 1) "rounded-tl-lg"}}" {{on "click" (fn this.switchCategory category)}}>
6
+ <div class="w-6">
7
+ <FaIcon @icon={{category.icon}} @size="sm" />
8
+ </div>
9
+ <div>{{category.name}}</div>
10
+ </a>
11
+ {{/each}}
12
+ </div>
13
+ <div class="flex-1 border-l border-gray-200 dark:border-gray-700">
14
+ <div class="p-4">
15
+ {{#if this.isUploading}}
16
+ <div class="dropzone w-full rounded-lg px-4 py-8 bg-gray-50 dark:bg-gray-900 bg-opacity-25 text-gray-900 dark:text-white text-center flex flex-col items-center justify-center border-2 border-dashed border-gray-200 dark:border-indigo-500">
17
+ <div class="flex items-center justify-center py-5">
18
+ <Spinner class="text-sm dar:text-gray-100" @loadingMessage={{t "fleet-ops.common.uploading"}} />
19
+ </div>
20
+ </div>
21
+ {{else}}
22
+ {{#let (file-queue name="files" onFileAdded=this.queueFile accept=(join "," this.acceptedFileTypes)) as |queue|}}
23
+ <FileDropzone @queue={{queue}} class="dropzone file-dropzone" as |dropzone|>
24
+ {{#if dropzone.active}}
25
+ {{#if dropzone.valid}}
26
+ {{t "component.dropzone.drop-to-upload"}}
27
+ {{else}}
28
+ {{t "component.dropzone.invalid"}}
29
+ {{/if}}
30
+ {{else if queue.files.length}}
31
+ <div class="my-2">
32
+ <FaIcon @icon="folder-open" class="text-indigo-500 mr-2" />
33
+ {{t "component.dropzone.files-ready-for-upload" numOfFiles=(pluralize queue.files.length (t "component.dropzone.file"))}}
34
+ </div>
35
+ <div class="my-2">({{queue.progress}}%)</div>
36
+ {{else}}
37
+ <h4 class="font-semibold">
38
+ <FaIcon @icon="folder-open" @size="lg" class="text-indigo-500 mr-2" />
39
+ {{t "component.dropzone.upload-avatar-files"}}
40
+ </h4>
41
+ <div>
42
+ {{#if dropzone.supported}}
43
+ <p class="text-sm my-5">{{t "component.dropzone.dropzone-supported-avatars"}}</p>
44
+ {{/if}}
45
+ <FileUpload @name="files" @for="files" @accept={{join "," this.acceptedFileTypes}} @multiple={{true}} @onFileAdded={{this.queueFile}}>
46
+ <a tabindex={{0}} class="btn btn-magic cursor-pointer ml-1">{{t "component.dropzone.or-select-button-text"}}</a>
47
+ </FileUpload>
48
+ </div>
49
+ {{/if}}
50
+ </FileDropzone>
51
+ {{/let}}
52
+ {{/if}}
53
+ <div class="space-y-4 mt-4">
54
+ {{#if this.uploadQueue}}
55
+ <div class="space-y-2">
56
+ {{#each this.uploadQueue as |file|}}
57
+ <div class="flex items-center justify-between bg-blue-100 border border-blue-800 dark:border-blue-800 py-1.5 shadow-sm rounded-lg px-4">
58
+ <div class="text-xs text-blue-900 truncate">{{truncate-filename file.name 50}}</div>
59
+ <div class="flex items-center text-sm">
60
+ <Spinner class="text-blue-900 mr-2" />
61
+ <span class="font-bold text-blue-900">{{round file.progress}}%</span>
62
+ </div>
63
+ </div>
64
+ {{/each}}
65
+ </div>
66
+ {{/if}}
67
+ <div>
68
+ <div class="grid grid-cols-2 md:grid-cols-4 gap-2 md:gap-4">
69
+ {{#each this.currentCategory.avatars as |file|}}
70
+ <File @file={{file}} @onDelete={{this.removeFile}} />
71
+ {{/each}}
72
+ </div>
73
+ </div>
74
+ </div>
75
+ </div>
76
+ </div>
77
+ </div>
78
+ </div>
@@ -0,0 +1,191 @@
1
+ import Component from '@glimmer/component';
2
+ import { inject as service } from '@ember/service';
3
+ import { action } from '@ember/object';
4
+ import { isArray } from '@ember/array';
5
+ import { tracked } from '@glimmer/tracking';
6
+ import { task } from 'ember-concurrency';
7
+ import { pluralize } from 'ember-inflector';
8
+
9
+ export default class AdminAvatarManagementComponent extends Component {
10
+ /**
11
+ * Inject the `fileQueue` service
12
+ *
13
+ * @var {Service}
14
+ */
15
+ @service fileQueue;
16
+
17
+ /**
18
+ * Injexts the `store` service
19
+ *
20
+ * @memberof AdminAvatarManagementComponent
21
+ */
22
+ @service store;
23
+
24
+ /**
25
+ * Injexts the `fetch` service
26
+ *
27
+ * @memberof AdminAvatarManagementComponent
28
+ */
29
+ @service fetch;
30
+
31
+ /**
32
+ * Inject the `notifications` service for handling notifications.
33
+ *
34
+ * @var {Service}
35
+ */
36
+ @service notifications;
37
+
38
+ /**
39
+ * Inject the `currentUser` service.
40
+ *
41
+ * @var {Service}
42
+ */
43
+ @service currentUser;
44
+
45
+ /**
46
+ * Tracks the files in the upload queue.
47
+ *
48
+ * @var {Array}
49
+ */
50
+ @tracked uploadQueue = [];
51
+
52
+ /**
53
+ * The only acceptable file types for avatars, png or svg.
54
+ *
55
+ * @memberof AdminAvatarManagementComponent
56
+ */
57
+ acceptedFileTypes = ['image/svg+xml', 'image/png'];
58
+
59
+ /**
60
+ * The current loaded avatars.
61
+ *
62
+ * @var {Array}
63
+ */
64
+ @tracked avatars = [];
65
+
66
+ /**
67
+ * Selectable categories for avatar management.
68
+ *
69
+ * @memberof AdminAvatarManagementComponent
70
+ */
71
+ @tracked categories = [
72
+ {
73
+ name: 'Vehicles',
74
+ icon: 'car',
75
+ type: 'vehicle',
76
+ avatars: [],
77
+ },
78
+ {
79
+ name: 'Places',
80
+ icon: 'building',
81
+ type: 'place',
82
+ avatars: [],
83
+ },
84
+ {
85
+ name: 'Drivers',
86
+ icon: 'id-card',
87
+ type: 'driver',
88
+ avatars: [],
89
+ },
90
+ ];
91
+
92
+ /**
93
+ * Tracks the selected category for avatar management.
94
+ *
95
+ * @var {string|null}
96
+ */
97
+ @tracked currentCategory;
98
+
99
+ /**
100
+ * Creates an instance of AdminAvatarManagementComponent.
101
+ * @memberof AdminAvatarManagementComponent
102
+ */
103
+ constructor() {
104
+ super(...arguments);
105
+ this.loadAvatars.perform();
106
+ }
107
+
108
+ /**
109
+ * Action triggered when a category is selected.
110
+ *
111
+ * @param {string} category - The selected category.
112
+ */
113
+ @action switchCategory(category) {
114
+ this.currentCategory = category;
115
+ }
116
+
117
+ /**
118
+ * Action triggered when a file is queued for upload.
119
+ *
120
+ * @param {File} file - The file to be queued.
121
+ */
122
+ @action queueFile(file) {
123
+ // since we have dropzone and upload button within dropzone validate the file state first
124
+ // as this method can be called twice from both functions
125
+ if (['queued', 'failed', 'timed_out', 'aborted'].indexOf(file.state) === -1) {
126
+ return;
127
+ }
128
+
129
+ // Get the current category
130
+ const category = this.currentCategory;
131
+
132
+ // Queue and upload immediatley
133
+ this.uploadQueue.pushObject(file);
134
+ this.fetch.uploadFile.perform(
135
+ file,
136
+ {
137
+ path: `custom-avatars/${pluralize(category.type)}/${this.currentUser.companyId}`,
138
+ type: `${category.type}-avatar`,
139
+ },
140
+ (uploadedFile) => {
141
+ this.currentCategory.avatars.pushObject(uploadedFile);
142
+ this.uploadQueue.removeObject(file);
143
+ },
144
+ () => {
145
+ this.uploadQueue.removeObject(file);
146
+ // remove file from queue
147
+ if (file.queue && typeof file.queue.remove === 'function') {
148
+ file.queue.remove(file);
149
+ }
150
+ }
151
+ );
152
+ }
153
+
154
+ /**
155
+ * Action triggered when a file is removed.
156
+ *
157
+ * @param {File} file - The file to be removed.
158
+ * @returns {Promise} - A promise representing the file destruction operation.
159
+ */
160
+ @action removeFile(file) {
161
+ this.currentCategory.avatars.removeObject(file);
162
+ return file.destroyRecord();
163
+ }
164
+
165
+ /**
166
+ * Task that loads files based on the selected category.
167
+ *
168
+ * @task
169
+ * @generator
170
+ * @yields {Array} - The filtered files based on the selected category.
171
+ */
172
+ @task *loadAvatars() {
173
+ this.avatars = yield this.store.query('file', { type_ends_with: 'avatar' });
174
+
175
+ // Assign avatars to their categories
176
+ if (isArray(this.avatars)) {
177
+ const categoriesWithAvatars = [];
178
+
179
+ for (let i = 0; i < this.categories.length; i++) {
180
+ const category = this.categories[i];
181
+ categoriesWithAvatars.pushObject({
182
+ ...category,
183
+ avatars: this.avatars.filter((file) => file.type === `${category.type}-avatar`),
184
+ });
185
+ }
186
+
187
+ this.categories = categoriesWithAvatars;
188
+ this.currentCategory = this.currentCategory ? this.categories.find((category) => category.type === this.currentCategory.type) : this.categories[0];
189
+ }
190
+ }
191
+ }
@@ -0,0 +1,15 @@
1
+ <div class="grid grid-cols-1 gap-4 lg:grid-cols-2 lg:gap-2">
2
+ <div class="input-group mb-2">
3
+ <InputLabel @labelText={{t "fleet-ops.component.avatar-picker.select-map-avatar"}} @helpText={{t "fleet-ops.component.avatar-picker.select-avatar-rendering"}} />
4
+ <FetchSelect @wrapperClass="w-60" @placeholder={{t "fleet-ops.component.avatar-picker.select-avatar"}} @endpoint={{this.endpoint}} @selected={{this.model.avatar_value}} @optionValue="value" @onChange={{this.selectAvatar}} as |option|>
5
+ {{titleize (humanize option.key)}}
6
+ </FetchSelect>
7
+ </div>
8
+ <div class="flex flex-row justify-end">
9
+ {{#if this.model.avatar_custom_url}}
10
+ <Image alt={{this.model.displayName}} src={{this.model.avatar_custom_url}} @fallbackSrc={{@defaultAvatar}} class="w-24 h-24" />
11
+ {{else}}
12
+ <Image alt={{this.model.displayName}} src={{this.model.avatar_url}} @fallbackSrc={{@defaultAvatar}} class="w-24 h-24" />
13
+ {{/if}}
14
+ </div>
15
+ </div>
@@ -0,0 +1,57 @@
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 { pluralize } from 'ember-inflector';
6
+ import getModelName from '@fleetbase/ember-core/utils/get-model-name';
7
+
8
+ function isUuid(str) {
9
+ if (typeof str !== 'string') {
10
+ return false;
11
+ }
12
+
13
+ const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
14
+ return uuidRegex.test(str);
15
+ }
16
+
17
+ export default class AvatarPickerComponent extends Component {
18
+ @service store;
19
+ @tracked model;
20
+ @tracked type;
21
+ @tracked endpoint;
22
+
23
+ constructor(owner, { model, endpoint }) {
24
+ super(...arguments);
25
+
26
+ this.model = model;
27
+ this.type = getModelName(model);
28
+ this.endpoint = endpoint ?? `${pluralize(this.type)}/avatars`;
29
+ }
30
+
31
+ /**
32
+ * Set the selected avatar
33
+ *
34
+ * @param {String} url
35
+ */
36
+ @action selectAvatar(url) {
37
+ // custom avatar file selected
38
+ if (isUuid(url)) {
39
+ return this.store.findRecord('file', url).then((file) => {
40
+ this.model.set('avatar_custom_url', file.url);
41
+ this.model.set('avatar_url', file.id);
42
+
43
+ if (typeof this.args.onSelect === 'function') {
44
+ this.args.onSelect(this.model, file.url);
45
+ }
46
+ });
47
+ }
48
+
49
+ // default url
50
+ this.model.set('avatar_url', url);
51
+ this.model.set('avatar_custom_url', null);
52
+
53
+ if (typeof this.args.onSelect === 'function') {
54
+ this.args.onSelect(this.model, url);
55
+ }
56
+ }
57
+ }
@@ -18,7 +18,7 @@
18
18
  <div class="flex flex-row">
19
19
  <div class="w-14">
20
20
  <div class="upload-avatar-overlay w-12 h-12 flex items-center justify-start rounded-lg">
21
- <img src={{this.driver.photo_url}} alt={{this.driver.name}} height="48" width="48" class="h-12 w-12 rounded-lg shadow-sm" />
21
+ <Image src={{this.driver.photo_url}} @fallbackSrc={{config "defaultValues.driverImage"}} alt={{this.driver.name}} height="48" width="48" class="h-12 w-12 rounded-lg shadow-sm" />
22
22
  <Attach::Tooltip @class="clean" @animation="scale" @placement="top">
23
23
  <InputInfo @text={{t "fleet-ops.component.driver-form-panel.upload-new-photo"}} />
24
24
  </Attach::Tooltip>
@@ -60,12 +60,12 @@
60
60
  </Overlay::Header>
61
61
 
62
62
  <Overlay::Body @wrapperClass="new-service-rate-overlay-body px-4 space-y-4 pt-4" @increaseInnerBodyHeightBy={{1000}}>
63
- <div class="flex-1">
63
+ <div class="flex-1 space-y-4">
64
64
  <div class="grid grid-cols-1 lg:grid-cols-2 gap-2 text-xs dark:text-gray-100">
65
65
  <InputGroup @name={{t "fleet-ops.common.name"}} @wrapperClass="col-span-2">
66
66
  <Input @value={{this.driver.name}} @type="text" class="w-full form-input" placeholder={{t "fleet-ops.common.name"}} />
67
67
  </InputGroup>
68
-
68
+
69
69
  <InputGroup @name={{t "fleet-ops.common.internal-id"}}>
70
70
  <Input @value={{this.driver.internal_id}} @type="text" class="w-full form-input" placeholder={{t "fleet-ops.common.internal-id"}} />
71
71
  </InputGroup>
@@ -109,17 +109,16 @@
109
109
  </PowerSelect>
110
110
  </div>
111
111
  </InputGroup>
112
- <InputGroup @name={{t "fleet-ops.common.coordinates"}} @wrapperClass="col-span-2">
113
- <CoordinatesInput
114
- @value={{this.driver.location}}
115
- @onChange={{this.onCoordinatesChanged}}
116
- @onGeocode={{this.onAutocomplete}}
117
- @onUpdatedFromMap={{this.onCoordinatesChanged}}
118
- @onInit={{this.setCoordinatesInput}}
119
- @renderInPlace={{false}}
120
- />
121
- </InputGroup>
112
+ <InputGroup @name={{t "fleet-ops.common.coordinates"}} @wrapperClass="col-span-2">
113
+ <CoordinatesInput @value={{this.driver.location}} @onChange={{this.onCoordinatesChanged}} @onGeocode={{this.onAutocomplete}} @onUpdatedFromMap={{this.onCoordinatesChanged}} @onInit={{this.setCoordinatesInput}} @renderInPlace={{false}} />
114
+ </InputGroup>
122
115
  </div>
116
+
117
+ <ContentPanel @title={{t "fleet-ops.component.avatar-picker.avatar"}} @open={{true}} @pad={{true}} @panelBodyClass="bg-white dark:bg-gray-800">
118
+ <AvatarPicker @model={{this.driver}} @defaultAvatar={{config "defaultValues.driverAvatar"}} />
119
+ </ContentPanel>
123
120
  </div>
121
+
122
+ <Spacer @height="300px" />
124
123
  </Overlay::Body>
125
124
  </Overlay>
@@ -1,29 +1,29 @@
1
1
  <div class="p-4">
2
2
  <ContentPanel @title={{t "fleet-ops.common.details"}} @open={{true}} @pad={{true}} @panelBodyClass="bg-white dark:bg-gray-900">
3
3
  <div class="grid grid-cols-2 gap-2 text-xs md:mb-6 dark:text-gray-100">
4
- <div class="field-info-container ">
4
+ <div class="field-info-container md:col-span-3">
5
5
  <div class="field-name">{{t "fleet-ops.common.name"}}</div>
6
6
  <div class="field-value">{{n-a @driver.name}}</div>
7
7
  </div>
8
8
  <div class="field-info-container">
9
9
  <div class="field-name">{{t "fleet-ops.common.id"}}</div>
10
- <div class="field-value">{{n-a @driver.public_id}}</div>
10
+ <ClickToCopy @value={{@driver.public_id}} class="field-value">{{n-a @driver.public_id}}</ClickToCopy>
11
11
  </div>
12
12
  <div class="field-info-container">
13
13
  <div class="field-name">{{t "fleet-ops.common.internal-id"}}</div>
14
14
  <div class="field-value">{{n-a @driver.internal_id}}</div>
15
15
  </div>
16
- <div class="field-info-container">
16
+ <div class="field-info-container md:col-span-3">
17
17
  <div class="field-name">{{t "fleet-ops.common.driver-license"}}</div>
18
18
  <div class="field-value">{{n-a @driver.drivers_license_number}}</div>
19
19
  </div>
20
- <div class="field-info-container">
20
+ <div class="field-info-container md:col-span-3">
21
21
  <div class="field-name">{{t "fleet-ops.common.email"}}</div>
22
- <div class="field-value">{{n-a @driver.email}}</div>
22
+ <ClickToCopy @value={{@driver.email}} class="field-value">{{n-a @driver.email}}</ClickToCopy>
23
23
  </div>
24
- <div class="field-info-container">
24
+ <div class="field-info-container md:col-span-3">
25
25
  <div class="field-name">{{t "fleet-ops.common.phone"}}</div>
26
- <div class="field-value">{{n-a @driver.phone}}</div>
26
+ <ClickToCopy @value={{@driver.phone}} class="field-value">{{n-a @driver.phone}}</ClickToCopy>
27
27
  </div>
28
28
  <div class="field-info-container">
29
29
  <div class="field-name">{{t "fleet-ops.common.city"}}</div>
@@ -1,16 +1,6 @@
1
- <Overlay
2
- @onLoad={{this.setOverlayContext}}
3
- @onOpen={{this.onOpen}}
4
- @onClose={{this.onClose}}
5
- @onToggle={{this.onToggle}}
6
- @position="right"
7
- @noBackdrop={{true}}
8
- @fullHeight={{true}}
9
- @isResizeble={{or this.isResizable @isResizable}}
10
- @width={{or this.width @width "570px"}}
11
- >
12
- <Overlay::Header @title={{concat "Edit order " this.order.public_id " route"}}>
13
- <Button @type="primary" @icon="check" @text="Save" @helpText="Save order route changes" @onClick={{this.onSave}} @wrapperClass="mr-2" />
1
+ <Overlay @onLoad={{this.setOverlayContext}} @onOpen={{this.onOpen}} @onClose={{this.onClose}} @onToggle={{this.onToggle}} @position="right" @noBackdrop={{true}} @fullHeight={{true}} @isResizeble={{or this.isResizable @isResizable}} @width={{or this.width @width "570px"}}>
2
+ <Overlay::Header @title={{concat "Edit Order: " this.order.public_id " Route"}}>
3
+ <Button @type="primary" @icon="check" @text="Save" @helpText="Save order route changes" @onClick={{perform this.save}} @isLoading={{this.save.isRunning}} @wrapperClass="mr-2" />
14
4
  <Button @type="default" @icon="times" @helpText={{t "fleet-ops.common.cancel"}} @onClick={{this.onPressCancel}} />
15
5
  </Overlay::Header>
16
6
 
@@ -23,13 +13,7 @@
23
13
  <div class="flex flex-1 justify-end space-x-2">
24
14
  {{#if this.order.payload.isMultiDrop}}
25
15
  {{!-- <Button @type="magic" @icon="magic" @text={{t "fleet-ops.component.modals.order-route-form.optimize"}} @size="sm" @onClick={{fn this.optimizeRoute}} @helpText={{t "fleet-ops.component.modals.order-route-form.optimize-text"}} @disabled={{lt this.order.payload.waypoints.length 3}} @isLoading={{this.isOptimizingRoute}} /> --}}
26
- <Button
27
- @icon="map-marked-alt"
28
- @text={{t "fleet-ops.component.modals.order-route-form.add"}}
29
- @size="sm"
30
- @onClick={{this.addWaypoint}}
31
- @helpText={{t "fleet-ops.component.modals.order-route-form.add-text"}}
32
- />
16
+ <Button @icon="map-marked-alt" @text={{t "fleet-ops.component.modals.order-route-form.add"}} @size="sm" @onClick={{this.addWaypoint}} @helpText={{t "fleet-ops.component.modals.order-route-form.add-text"}} />
33
17
  {{/if}}
34
18
  </div>
35
19
  </div>
@@ -46,18 +30,7 @@
46
30
  </div>
47
31
 
48
32
  <div class="flex-1 px-4">
49
- <ModelSelect
50
- @modelName="place"
51
- @selectedModel={{waypoint}}
52
- @placeholder={{concat (t "fleet-ops.operations.orders.index.new.select-waypoint-placeholder") " " (add index 1)}}
53
- @triggerClass="form-select form-input truncate max-w-300px"
54
- @infiniteScroll={{false}}
55
- @customSearchEndpoint="places/search"
56
- @query={{hash geo=true latitude=this.latitude longitude=this.longitude}}
57
- @renderInPlace={{true}}
58
- @onChange={{fn this.setWaypointPlace index}}
59
- as |model|
60
- >
33
+ <ModelSelect @modelName="place" @selectedModel={{waypoint}} @placeholder={{concat (t "fleet-ops.operations.orders.index.new.select-waypoint-placeholder") " " (add index 1)}} @triggerClass="form-select form-input truncate max-w-300px" @infiniteScroll={{false}} @customSearchEndpoint="places/search" @query={{hash geo=true latitude=this.latitude longitude=this.longitude}} @renderInPlace={{true}} @onChange={{fn this.setWaypointPlace index}} as |model|>
61
34
  {{model.address}}
62
35
  </ModelSelect>
63
36
 
@@ -79,7 +52,7 @@
79
52
  </div>
80
53
  </DragSortList>
81
54
  {{else}}
82
- <div class="grid grid-cols-1 md:grid-cols-2 gap-3 text-xs dark:text-gray-100">
55
+ <div class="grid grid-cols-1 gap-3 text-xs dark:text-gray-100">
83
56
  <div class="input-group">
84
57
  <div class="flex items-center justify-between">
85
58
  <label>{{t "fleet-ops.component.modals.order-route-form.pickup"}}</label>
@@ -89,18 +62,7 @@
89
62
  {{/if}}
90
63
  </div>
91
64
 
92
- <ModelSelect
93
- @modelName="place"
94
- @selectedModel={{this.order.payload.pickup}}
95
- @placeholder={{t "fleet-ops.component.modals.order-route-form.select-pickup"}}
96
- @triggerClass="form-select form-input"
97
- @infiniteScroll={{false}}
98
- @customSearchEndpoint="places/search"
99
- @query={{hash geo=true latitude=this.latitude longitude=this.longitude}}
100
- @renderInPlace={{true}}
101
- @onChange={{fn this.setPayloadPlace "pickup"}}
102
- as |model|
103
- >
65
+ <ModelSelect @modelName="place" @selectedModel={{this.order.payload.pickup}} @placeholder={{t "fleet-ops.component.modals.order-route-form.select-pickup"}} @triggerClass="form-select form-input" @infiniteScroll={{false}} @customSearchEndpoint="places/search" @query={{hash geo=true latitude=this.latitude longitude=this.longitude}} @renderInPlace={{true}} @onChange={{fn this.setPayloadPlace "pickup"}} as |model|>
104
66
  {{model.address}}
105
67
  </ModelSelect>
106
68
 
@@ -121,18 +83,7 @@
121
83
  {{/if}}
122
84
  </div>
123
85
 
124
- <ModelSelect
125
- @modelName="place"
126
- @selectedModel={{this.order.payload.dropoff}}
127
- @placeholder={{t "fleet-ops.component.modals.order-route-form.select-dropoff"}}
128
- @triggerClass="form-select form-input"
129
- @infiniteScroll={{false}}
130
- @customSearchEndpoint="places/search"
131
- @query={{hash geo=true latitude=this.latitude longitude=this.longitude}}
132
- @renderInPlace={{true}}
133
- @onChange={{fn this.setPayloadPlace "dropoff"}}
134
- as |model|
135
- >
86
+ <ModelSelect @modelName="place" @selectedModel={{this.order.payload.dropoff}} @placeholder={{t "fleet-ops.component.modals.order-route-form.select-dropoff"}} @triggerClass="form-select form-input" @infiniteScroll={{false}} @customSearchEndpoint="places/search" @query={{hash geo=true latitude=this.latitude longitude=this.longitude}} @renderInPlace={{true}} @onChange={{fn this.setPayloadPlace "dropoff"}} as |model|>
136
87
  {{model.address}}
137
88
  </ModelSelect>
138
89
 
@@ -153,18 +104,7 @@
153
104
  {{/if}}
154
105
  </div>
155
106
 
156
- <ModelSelect
157
- @modelName="place"
158
- @selectedModel={{this.order.payload.return}}
159
- @placeholder={{t "fleet-ops.component.modals.order-route-form.select-return"}}
160
- @triggerClass="form-select form-input"
161
- @infiniteScroll={{false}}
162
- @customSearchEndpoint="places/search"
163
- @query={{hash geo=true latitude=this.latitude longitude=this.longitude}}
164
- @renderInPlace={{true}}
165
- @onChange={{fn this.setPayloadPlace "return"}}
166
- as |model|
167
- >
107
+ <ModelSelect @modelName="place" @selectedModel={{this.order.payload.return}} @placeholder={{t "fleet-ops.component.modals.order-route-form.select-return"}} @triggerClass="form-select form-input" @infiniteScroll={{false}} @customSearchEndpoint="places/search" @query={{hash geo=true latitude=this.latitude longitude=this.longitude}} @renderInPlace={{true}} @onChange={{fn this.setPayloadPlace "return"}} as |model|>
168
108
  {{model.address}}
169
109
  </ModelSelect>
170
110