@fleetbase/fleetops-engine 0.4.12 → 0.4.13
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.
- package/addon/components/admin/avatar-management.hbs +78 -0
- package/addon/components/admin/avatar-management.js +191 -0
- package/addon/components/avatar-picker.hbs +15 -0
- package/addon/components/avatar-picker.js +57 -0
- package/addon/components/driver-form-panel.hbs +12 -13
- package/addon/components/driver-panel/details.hbs +7 -7
- package/addon/components/edit-order-route-panel.hbs +9 -69
- package/addon/components/edit-order-route-panel.js +78 -27
- package/addon/components/fleet-listing-panel.hbs +1 -1
- package/addon/components/layout/fleet-ops-sidebar/driver-listing.hbs +1 -1
- package/addon/components/layout/fleet-ops-sidebar.hbs +2 -1
- package/addon/components/live-map.js +0 -1
- package/addon/components/order/schedule-card.hbs +29 -17
- package/addon/components/order/schedule-card.js +1 -1
- package/addon/components/place-form-panel.hbs +35 -51
- package/addon/components/place-panel/details.hbs +10 -14
- package/addon/components/place-panel.hbs +2 -12
- package/addon/components/vehicle-form-panel.hbs +11 -80
- package/addon/components/vehicle-form-panel.js +33 -0
- package/addon/controllers/management/places/index.js +29 -21
- package/addon/controllers/operations/orders/index/view.js +10 -10
- package/addon/controllers/operations/scheduler/index.js +7 -4
- package/addon/engine.js +7 -1
- package/addon/styles/fleetops-engine.css +94 -0
- package/addon/templates/management/places/index.hbs +1 -1
- package/addon/templates/operations/scheduler/index.hbs +5 -4
- package/addon/utils/create-full-calendar-event-from-order.js +23 -1
- package/app/components/admin/avatar-management.js +1 -0
- package/app/components/avatar-picker.js +1 -0
- package/composer.json +2 -2
- package/config/environment.js +2 -0
- package/extension.json +1 -1
- package/package.json +3 -3
- package/server/migrations/2023_09_14_074512_update_vehicles_table.php +2 -2
- package/server/migrations/2023_09_14_074619_create_vehicle_devices_table.php +2 -2
- package/server/migrations/2023_09_26_071205_add_online_column_to_vehicles_table.php +2 -2
- package/server/migrations/2023_09_26_083957_create_fleet_vehicles_table.php +2 -2
- package/server/migrations/2023_09_27_023757_add_column_to_fleets_table.php +2 -2
- package/server/migrations/2023_10_05_074811_update_vehicle.php +2 -2
- package/server/migrations/2023_10_25_093014_create_vehicle_device_events_table.php +2 -2
- package/server/migrations/2023_11_03_072503_refactor_issues_table_columns.php +2 -2
- package/server/migrations/2023_11_06_040645_refactor_fuel_reports_table.php +2 -2
- package/server/migrations/2023_11_09_041807_add_status_column_to_fuel_reports_table.php +2 -2
- package/server/migrations/2023_11_15_062814_add_comments_column_and_resolved_at_column_to_vehicle_device_events_table.php +2 -2
- package/server/migrations/2023_11_17_053001_add_category_uuid_to_entities_table.php +2 -2
- package/server/migrations/2023_11_28_074244_add_supplier_uuid_column_to_entities_table.php +2 -2
- 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 } +5 -3
- package/server/migrations/2024_02_18_100209_add_avatar_url_columns_table.php +54 -0
- package/server/src/Expansions/UserExpansion.php +1 -0
- package/server/src/Http/Controllers/Api/v1/DriverController.php +7 -1
- package/server/src/Http/Controllers/Api/v1/PlaceController.php +11 -2
- package/server/src/Http/Controllers/Internal/v1/DriverController.php +15 -8
- package/server/src/Http/Controllers/Internal/v1/OrderController.php +46 -0
- package/server/src/Http/Controllers/Internal/v1/PlaceController.php +12 -0
- package/server/src/Http/Resources/v1/Driver.php +6 -4
- package/server/src/Http/Resources/v1/Order.php +2 -1
- package/server/src/Http/Resources/v1/Payload.php +8 -4
- package/server/src/Http/Resources/v1/Place.php +2 -0
- package/server/src/Http/Resources/v1/Vehicle.php +1 -0
- package/server/src/Models/Driver.php +76 -0
- package/server/src/Models/Payload.php +49 -3
- package/server/src/Models/Place.php +71 -0
- package/server/src/Models/Vehicle.php +44 -20
- package/server/src/Notifications/OrderAssigned.php +2 -0
- package/server/src/routes.php +3 -0
- 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
|
-
<
|
|
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
|
-
|
|
113
|
-
|
|
114
|
-
|
|
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
|
-
<
|
|
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
|
-
<
|
|
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
|
-
<
|
|
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
|
-
@
|
|
3
|
-
|
|
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
|
|
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
|
|