@fleetbase/fleetops-engine 0.2.3 → 0.2.4
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/driver-form-panel.hbs +131 -0
- package/addon/components/driver-form-panel.js +39 -0
- package/addon/components/driver-panel/details.hbs +48 -0
- package/addon/components/driver-panel.hbs +60 -0
- package/addon/components/driver-panel.js +57 -0
- package/addon/components/fleet-form-panel.hbs +46 -0
- package/addon/components/fleet-form-panel.js +3 -0
- package/addon/components/fleet-panel.hbs +70 -0
- package/addon/components/fleet-panel.js +43 -0
- package/addon/components/fleet-vehicle-listing.hbs +73 -0
- package/addon/components/fleet-vehicle-listing.js +117 -0
- package/addon/components/live-map.js +13 -10
- package/addon/components/modals/fleet-details.hbs +1 -0
- package/addon/components/settings-window.hbs +1 -1
- package/addon/components/vehicle-form-panel.hbs +131 -0
- package/addon/components/vehicle-form-panel.js +39 -0
- package/addon/components/vehicle-panel/details.hbs +34 -0
- package/addon/components/vehicle-panel/tracking.hbs +1 -0
- package/addon/components/vehicle-panel.hbs +60 -0
- package/addon/components/vehicle-panel.js +57 -0
- package/addon/controllers/management/drivers/index/details.js +8 -0
- package/addon/controllers/management/drivers/index/edit.js +80 -0
- package/addon/controllers/management/drivers/index/new.js +128 -0
- package/addon/controllers/management/drivers/index.js +212 -202
- package/addon/controllers/management/fleets/index/details.js +8 -0
- package/addon/controllers/management/fleets/index/edit.js +77 -0
- package/addon/controllers/management/fleets/index/new.js +103 -0
- package/addon/controllers/management/fleets/index.js +49 -77
- package/addon/controllers/management/vehicles/index/details.js +12 -0
- package/addon/controllers/management/vehicles/index/edit.js +8 -0
- package/addon/controllers/management/vehicles/index/new.js +21 -0
- package/addon/controllers/management/vehicles/index.js +6 -129
- package/addon/controllers/operations/service-rates/index.js +0 -1
- package/addon/engine.js +6 -0
- package/addon/routes/management/drivers/index/details.js +9 -0
- package/addon/routes/management/drivers/index/edit.js +13 -0
- package/addon/routes/management/drivers/index/new.js +3 -0
- package/addon/routes/management/fleets/index/details.js +9 -0
- package/addon/routes/management/fleets/index/edit.js +9 -0
- package/addon/routes/management/fleets/index/new.js +3 -0
- package/addon/routes/management/fleets/index.js +1 -0
- package/addon/routes/management/vehicles/index/details.js +18 -0
- package/addon/routes/management/vehicles/index/edit.js +14 -0
- package/addon/routes/management/vehicles/index/new.js +3 -0
- package/addon/routes/operations/orders/index/view.js +5 -1
- package/addon/routes/operations/service-rates/index/edit.js +1 -1
- package/addon/routes.js +15 -3
- package/addon/templates/management/drivers/index/details.hbs +1 -0
- package/addon/templates/management/drivers/index/edit.hbs +1 -0
- package/addon/templates/management/drivers/index/new.hbs +1 -0
- package/addon/templates/management/drivers/index.hbs +3 -1
- package/addon/templates/management/fleets/index/details.hbs +1 -0
- package/addon/templates/management/fleets/index/edit.hbs +1 -0
- package/addon/templates/management/fleets/index/new.hbs +1 -0
- package/addon/templates/management/vehicles/index/details.hbs +1 -0
- package/addon/templates/management/vehicles/index/edit.hbs +1 -0
- package/addon/templates/management/vehicles/index/new.hbs +1 -0
- package/addon/templates/operations/orders/index/new.hbs +1 -1
- package/addon/templates/operations/service-rates/index/new.hbs +2 -2
- package/app/components/driver-form-panel.js +1 -0
- package/app/components/driver-panel/details.js +1 -0
- package/app/components/driver-panel.js +1 -0
- package/app/components/fleet-form-panel.js +1 -0
- package/app/components/fleet-panel.js +1 -0
- package/app/components/fleet-vehicle-listing.js +1 -0
- package/app/components/vehicle-form-panel.js +1 -0
- package/app/components/vehicle-panel/details.js +1 -0
- package/app/components/vehicle-panel/tracking.js +1 -0
- package/app/components/vehicle-panel.js +1 -0
- package/app/controllers/management/drivers/index/new.js +1 -0
- package/app/controllers/management/vehicles/index/details.js +1 -0
- package/app/controllers/management/vehicles/index/new.js +1 -0
- package/app/routes/management/drivers/index/details.js +1 -0
- package/app/routes/management/drivers/index/edit.js +1 -0
- package/app/routes/management/drivers/index/new.js +1 -0
- package/app/routes/management/vehicles/index/details.js +1 -0
- package/app/routes/management/vehicles/index/edit.js +1 -0
- package/app/routes/management/vehicles/index/new.js +1 -0
- package/app/templates/management/drivers/index/details.js +1 -0
- package/app/templates/management/drivers/index/edit.js +1 -0
- package/app/templates/management/drivers/index/new.js +1 -0
- package/app/templates/management/vehicles/index/details.js +1 -0
- package/app/templates/management/vehicles/index/edit.js +1 -0
- package/app/templates/management/vehicles/index/new.js +1 -0
- package/package.json +4 -4
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import Component from '@glimmer/component';
|
|
2
|
+
import { tracked } from '@glimmer/tracking';
|
|
3
|
+
import { inject as service } from '@ember/service';
|
|
4
|
+
import { action, computed } from '@ember/object';
|
|
5
|
+
import { isArray } from '@ember/array';
|
|
6
|
+
import { isBlank } from '@ember/utils';
|
|
7
|
+
import { timeout } from 'ember-concurrency';
|
|
8
|
+
import { task } from 'ember-concurrency-decorators';
|
|
9
|
+
|
|
10
|
+
export default class FleetVehicleListingComponent extends Component {
|
|
11
|
+
@service store;
|
|
12
|
+
@service appCache;
|
|
13
|
+
@tracked vehicles = [];
|
|
14
|
+
@tracked selected = [];
|
|
15
|
+
@tracked query;
|
|
16
|
+
@tracked isLoading = false;
|
|
17
|
+
@tracked isLoaded = false;
|
|
18
|
+
|
|
19
|
+
@computed('selected.[]') get ids() {
|
|
20
|
+
return this.selected.map((selection) => selection.id);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
@action async setupComponent() {
|
|
24
|
+
const { selected, fleet } = this.args;
|
|
25
|
+
|
|
26
|
+
this.selected = isArray(selected) && selected.length ? selected : [];
|
|
27
|
+
this.vehicles = await this.queryFleetVehicles(fleet);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
@action setSelected() {
|
|
31
|
+
const { selected } = this.args;
|
|
32
|
+
|
|
33
|
+
if (isArray(selected) && selected.length) {
|
|
34
|
+
this.selected = selected;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
@action select(option, selected) {
|
|
39
|
+
if (selected) {
|
|
40
|
+
this.selected.pushObject(option);
|
|
41
|
+
} else {
|
|
42
|
+
this.selected.removeObject(option);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
option.setProperties({ selected });
|
|
46
|
+
|
|
47
|
+
if (typeof this.args.onChange === 'function') {
|
|
48
|
+
this.args.onChange(this.selected);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
@action onAdd(vehicle) {
|
|
53
|
+
if (typeof this.args.onRemoveVehicle === 'function') {
|
|
54
|
+
this.args.onAddVehicle(vehicle);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const index = this.vehicles.findIndex((d) => d.id === vehicle.id);
|
|
58
|
+
|
|
59
|
+
if (index === -1) {
|
|
60
|
+
this.vehicles.pushObject(vehicle);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
@action onRemove(vehicle) {
|
|
65
|
+
if (typeof this.args.onRemoveVehicle === 'function') {
|
|
66
|
+
this.args.onRemoveVehicle(vehicle);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
this.vehicles.removeObject(vehicle);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
@action queryFleetVehicles(fleet, query = null) {
|
|
73
|
+
this.isLoading = true;
|
|
74
|
+
|
|
75
|
+
const cacheKey = `${fleet.id}:fleetVehicles`;
|
|
76
|
+
// const cachedResults = this.appCache.getEmberData(cacheKey, 'vehicle');
|
|
77
|
+
|
|
78
|
+
// if (cachedResults) {
|
|
79
|
+
// this.vehicles = this.toggleSelected(cachedResults);
|
|
80
|
+
// this.isLoaded = true;
|
|
81
|
+
// }
|
|
82
|
+
|
|
83
|
+
return new Promise((resolve) => {
|
|
84
|
+
this.store.query('vehicle', { query, fleet: fleet.id }).then((vehicles) => {
|
|
85
|
+
this.isLoading = false;
|
|
86
|
+
this.isLoaded = true;
|
|
87
|
+
|
|
88
|
+
// save to cache
|
|
89
|
+
if (!query) {
|
|
90
|
+
this.appCache.setEmberData(cacheKey, vehicles);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
resolve(this.toggleSelected(vehicles));
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
@action toggleSelected(options = []) {
|
|
99
|
+
return options.map((option) => {
|
|
100
|
+
option.set('selected', this.ids.includes(option.id));
|
|
101
|
+
|
|
102
|
+
return option;
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
@task({ restartable: true }) *search({ target: { value } }) {
|
|
107
|
+
const { fleet } = this.args;
|
|
108
|
+
|
|
109
|
+
if (isBlank(value)) {
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
yield timeout(300);
|
|
114
|
+
|
|
115
|
+
this.vehicles = yield this.queryFleetVehicles(fleet, value);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
@@ -804,7 +804,7 @@ export default class LiveMapComponent extends Component {
|
|
|
804
804
|
// get incoming data and console out
|
|
805
805
|
(async () => {
|
|
806
806
|
for await (let output of channel) {
|
|
807
|
-
const { event
|
|
807
|
+
const { event } = output;
|
|
808
808
|
|
|
809
809
|
if (event === 'driver.location_changed' || event === 'driver.simulated_location_changed') {
|
|
810
810
|
// Add the incoming event to the buffer
|
|
@@ -905,17 +905,20 @@ export default class LiveMapComponent extends Component {
|
|
|
905
905
|
this.isLoading = true;
|
|
906
906
|
|
|
907
907
|
return new Promise((resolve) => {
|
|
908
|
-
this.fetch
|
|
909
|
-
|
|
908
|
+
this.fetch
|
|
909
|
+
.get('fleet-ops/live/drivers', {}, { normalizeToEmberData: true, normalizeModelType: 'driver' })
|
|
910
|
+
.then((drivers) => {
|
|
911
|
+
this.isLoading = false;
|
|
910
912
|
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
913
|
+
if (typeof this.args.onDriversLoaded === 'function') {
|
|
914
|
+
this.args.onDriversLoaded(drivers);
|
|
915
|
+
}
|
|
914
916
|
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
917
|
+
resolve(drivers);
|
|
918
|
+
})
|
|
919
|
+
.catch(() => {
|
|
920
|
+
resolve([]);
|
|
921
|
+
});
|
|
919
922
|
});
|
|
920
923
|
}
|
|
921
924
|
|
|
@@ -27,5 +27,6 @@
|
|
|
27
27
|
</div>
|
|
28
28
|
</div>
|
|
29
29
|
<FleetDriverListing @fleet={{@options.fleet}} @onAddDriver={{@options.addDriver}} @onRemoveDriver={{@options.removeDriver}} />
|
|
30
|
+
<FleetVehicleListing @fleet={{@options.fleet}} @onAddVehicle={{@options.addVehicle}} @onRemoveVehicle={{@options.removeVehicle}} />
|
|
30
31
|
</div>
|
|
31
32
|
</Modal::Default>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<Overlay @onLoad={{this.onLoad}} @isOpen={{@isOpen}} @position="right" @noBackdrop={{true}} @fullHeight={{true}} @isResizable={{true}} @isMaximizable={{true}} @isMinimizable={{true}} as |overlay|>
|
|
2
2
|
<Overlay::Header @overlay={{overlay}} @title="FleetOps Settings" @onPressCancel={{@onPressCancel}}>
|
|
3
|
-
<Button @icon="save" @type="primary" @text={{if (media 'isMobile') "" "Save Changes"}} @onClick={{this.saveSettings}} @isLoading={{this.isSaving}} @disabled={{or this.isLoading this.isSaving}}
|
|
3
|
+
<Button @icon="save" @type="primary" @text={{if (media 'isMobile') "" "Save Changes"}} @onClick={{this.saveSettings}} @isLoading={{this.isSaving}} @disabled={{or this.isLoading this.isSaving}} />
|
|
4
4
|
</Overlay::Header>
|
|
5
5
|
|
|
6
6
|
<Overlay::Body>
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
<Overlay @position="right" @noBackdrop={{true}} @fullHeight={{true}} @width="700">
|
|
2
|
+
<Overlay::Header @hideLeftSection={{true}} @actionsWrapperClass="flex-1 flex-col py-3" class="h-auto-i min-h-[127px]">
|
|
3
|
+
<div class="flex flex-row items-center justify-between w-full mb-4">
|
|
4
|
+
<div class="flex flex-1 space-x-2">
|
|
5
|
+
{{#if @vehicle.id}}
|
|
6
|
+
<Button @type="default" @icon="car" @helpText="View vehicle details" @onClick={{this.viewDetails}} />
|
|
7
|
+
{{/if}}
|
|
8
|
+
</div>
|
|
9
|
+
<div class="flex flex-1 justify-end">
|
|
10
|
+
<div class="mr-2">
|
|
11
|
+
<Button @icon={{if @vehicle.id "save" "check"}} @type="primary" @text={{if @vehicle.id "Save Vehicle" "Create Vehicle"}} @onClick={{this.save}} />
|
|
12
|
+
</div>
|
|
13
|
+
<Button @type="default" @icon="times" @helpText={{if @vehicle.id "Cancel edit vehicle" "Cancel new vehicle"}} @onClick={{@onPressCancel}} />
|
|
14
|
+
</div>
|
|
15
|
+
</div>
|
|
16
|
+
<div class="flex flex-row justify-between w-full">
|
|
17
|
+
<div class="flex flex-col flex-1 w-3/4">
|
|
18
|
+
<div class="flex flex-row">
|
|
19
|
+
<div class="w-14 flex items-center justify-center">
|
|
20
|
+
<Image src={{@vehicle.photo_url}} @fallbackSrc={{config "defaultValues.vehicleImage"}} alt={{@vehicle.name}} height="48" width="48" class="h-12 w-12" />
|
|
21
|
+
<Attach::Tooltip @class="clean" @animation="scale" @placement={{"top"}}>
|
|
22
|
+
<InputInfo @text={{@vehicle.public_id}} />
|
|
23
|
+
</Attach::Tooltip>
|
|
24
|
+
</div>
|
|
25
|
+
<div class="flex flex-col">
|
|
26
|
+
<h1 class="text-gray-900 dark:text-white text-2xl">
|
|
27
|
+
{{#if @vehicle.id}}
|
|
28
|
+
{{@vehicle.displayName}}
|
|
29
|
+
{{else}}
|
|
30
|
+
{{#if @vehicle.displayName}}
|
|
31
|
+
{{@vehicle.displayName}}
|
|
32
|
+
{{else}}
|
|
33
|
+
<span>New Vehicle</span>
|
|
34
|
+
{{/if}}
|
|
35
|
+
{{/if}}
|
|
36
|
+
</h1>
|
|
37
|
+
<div class="-mt-1">
|
|
38
|
+
{{#if @vehicle.driver}}
|
|
39
|
+
<div class="flex flex-row items-center">
|
|
40
|
+
<span class="text-sm dark:text-gray-500 text-gray-700 mr-3">{{@vehicle.driver.name}}</span>
|
|
41
|
+
</div>
|
|
42
|
+
{{else}}
|
|
43
|
+
<div class="flex flex-row items-center">
|
|
44
|
+
<span class="text-sm dark:text-gray-500 text-gray-700 mr-3">No driver assigned</span>
|
|
45
|
+
</div>
|
|
46
|
+
{{/if}}
|
|
47
|
+
</div>
|
|
48
|
+
</div>
|
|
49
|
+
</div>
|
|
50
|
+
</div>
|
|
51
|
+
<div class="flex justify-end w-1/4">
|
|
52
|
+
<Badge @status={{@vehicle.status}} />
|
|
53
|
+
</div>
|
|
54
|
+
</div>
|
|
55
|
+
</Overlay::Header>
|
|
56
|
+
|
|
57
|
+
<Overlay::Body @wrapperClass="new-service-rate-overlay-body px-4 space-y-4 pt-4" @increaseInnerBodyHeightBy={{1000}}>
|
|
58
|
+
<div class="flex-1 space-y-4">
|
|
59
|
+
<ContentPanel @title="Details" @open={{true}} @pad={{true}} @panelBodyClass="bg-white dark:bg-gray-800">
|
|
60
|
+
<div class="grid grid-cols-1 gap-4 lg:grid-cols-2 lg:gap-2">
|
|
61
|
+
<div class="input-group mb-2">
|
|
62
|
+
<label>
|
|
63
|
+
Internal ID
|
|
64
|
+
</label>
|
|
65
|
+
<Input @value={{@vehicle.internal_id}} @type="text" class="w-full form-input" placeholder="Internal ID" />
|
|
66
|
+
</div>
|
|
67
|
+
|
|
68
|
+
<div class="input-group mb-2">
|
|
69
|
+
<label>
|
|
70
|
+
Plate Number
|
|
71
|
+
</label>
|
|
72
|
+
<Input @value={{@vehicle.plate_number}} @type="text" class="w-full form-input" placeholder="Plate Number" />
|
|
73
|
+
</div>
|
|
74
|
+
|
|
75
|
+
<div class="input-group mb-2">
|
|
76
|
+
<label>
|
|
77
|
+
VIN Number
|
|
78
|
+
</label>
|
|
79
|
+
<Input @value={{@vehicle.vin}} @type="text" class="w-full form-input" placeholder="VIN Number" />
|
|
80
|
+
</div>
|
|
81
|
+
|
|
82
|
+
<div class="input-group mb-2">
|
|
83
|
+
<label>
|
|
84
|
+
Make
|
|
85
|
+
</label>
|
|
86
|
+
<Input @value={{@vehicle.make}} @type="text" class="w-full form-input" placeholder="Vehicle Make" />
|
|
87
|
+
</div>
|
|
88
|
+
|
|
89
|
+
<div class="input-group mb-2">
|
|
90
|
+
<label>
|
|
91
|
+
Model
|
|
92
|
+
</label>
|
|
93
|
+
<Input @value={{@vehicle.model}} @type="text" class="w-full form-input" placeholder="Vehicle Model" />
|
|
94
|
+
</div>
|
|
95
|
+
|
|
96
|
+
<div class="input-group mb-2">
|
|
97
|
+
<label>
|
|
98
|
+
Year
|
|
99
|
+
</label>
|
|
100
|
+
<Input @value={{@vehicle.year}} @type="text" class="w-full form-input" placeholder="Vehicle Year" />
|
|
101
|
+
</div>
|
|
102
|
+
|
|
103
|
+
<div class="input-group mb-2">
|
|
104
|
+
<label>
|
|
105
|
+
Driver Assigned
|
|
106
|
+
</label>
|
|
107
|
+
<ModelSelect @modelName="driver" @selectedModel={{@vehicle.driver}} @placeholder="Select Driver" @triggerClass="form-select form-input" @infiniteScroll={{false}} @renderInPlace={{true}} @onChange={{fn (mut
|
|
108
|
+
@vehicle.driver)}} as |model|>
|
|
109
|
+
{{model.name}}
|
|
110
|
+
</ModelSelect>
|
|
111
|
+
</div>
|
|
112
|
+
</div>
|
|
113
|
+
</ContentPanel>
|
|
114
|
+
|
|
115
|
+
<ContentPanel @title="Avatar" @open={{false}} @pad={{true}} @panelBodyClass="bg-white dark:bg-gray-800">
|
|
116
|
+
<div class="grid grid-cols-1 gap-4 lg:grid-cols-2 lg:gap-2">
|
|
117
|
+
<div class="input-group mb-2">
|
|
118
|
+
<InputLabel @labelText="Select map avatar" @helpText="Select an avatar to be used for rendering on maps when using geolocation tracking features." />
|
|
119
|
+
<FetchSelect class="w-60" @placeholder="Select avatar" @path="vehicles/avatars" @value={{@vehicle.avatar_url}} @onSelect={{fn (mut
|
|
120
|
+
@vehicle.avatar_url)}} as |option key|>
|
|
121
|
+
{{titleize (humanize key)}}
|
|
122
|
+
</FetchSelect>
|
|
123
|
+
</div>
|
|
124
|
+
<div>
|
|
125
|
+
<img alt={{@vehicle.public_id}} src={{@vehicle.avatar_url}} class="w-24 h-24" />
|
|
126
|
+
</div>
|
|
127
|
+
</div>
|
|
128
|
+
</ContentPanel>
|
|
129
|
+
</div>
|
|
130
|
+
</Overlay::Body>
|
|
131
|
+
</Overlay>
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import Component from '@glimmer/component';
|
|
2
|
+
import { inject as service } from '@ember/service';
|
|
3
|
+
import { action } from '@ember/object';
|
|
4
|
+
|
|
5
|
+
export default class VehicleFormPanelComponent extends Component {
|
|
6
|
+
@service store;
|
|
7
|
+
@service notifications;
|
|
8
|
+
@service hostRouter;
|
|
9
|
+
@service loader;
|
|
10
|
+
|
|
11
|
+
@action save() {
|
|
12
|
+
const { vehicle, onAfterSave } = this.args;
|
|
13
|
+
|
|
14
|
+
this.loader.showLoader('.overlay-inner-content', 'Saving vehicle...');
|
|
15
|
+
|
|
16
|
+
try {
|
|
17
|
+
return vehicle
|
|
18
|
+
.save()
|
|
19
|
+
.then((vehicle) => {
|
|
20
|
+
this.notifications.success(`Vehicle (${vehicle.displayName}) saved successfully.`);
|
|
21
|
+
|
|
22
|
+
if (typeof onAfterSave === 'function') {
|
|
23
|
+
onAfterSave(vehicle);
|
|
24
|
+
}
|
|
25
|
+
})
|
|
26
|
+
.catch(this.notifications.serverError)
|
|
27
|
+
.finally(() => {
|
|
28
|
+
this.loader.removeLoader();
|
|
29
|
+
});
|
|
30
|
+
} catch (error) {
|
|
31
|
+
this.loader.removeLoader();
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
@action viewDetails() {
|
|
36
|
+
const { vehicle } = this.args;
|
|
37
|
+
return this.hostRouter.transitionTo('console.fleet-ops.management.vehicles.index.details', vehicle.public_id);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
<div class="p-4">
|
|
2
|
+
<ContentPanel @title="Details" @open={{true}} @pad={{true}} @panelBodyClass="bg-white dark:bg-gray-900">
|
|
3
|
+
<div class="grid grid-cols-2 gap-2 text-xs md:mb-6 dark:text-gray-100">
|
|
4
|
+
<div class="field-info-container">
|
|
5
|
+
<div class="field-name">Name</div>
|
|
6
|
+
<div class="field-value">{{n-a @vehicle.display_name}}</div>
|
|
7
|
+
</div>
|
|
8
|
+
<div class="field-info-container">
|
|
9
|
+
<div class="field-name">Internal ID</div>
|
|
10
|
+
<div class="field-value">{{n-a @vehicle.internal_id}}</div>
|
|
11
|
+
</div>
|
|
12
|
+
<div class="field-info-container">
|
|
13
|
+
<div class="field-name">Plate Number</div>
|
|
14
|
+
<div class="field-value">{{n-a @vehicle.plate_number}}</div>
|
|
15
|
+
</div>
|
|
16
|
+
<div class="field-info-container">
|
|
17
|
+
<div class="field-name">VIN Number</div>
|
|
18
|
+
<div class="field-value">{{n-a @vehicle.vin}}</div>
|
|
19
|
+
</div>
|
|
20
|
+
<div class="field-info-container">
|
|
21
|
+
<div class="field-name">Make</div>
|
|
22
|
+
<div class="field-value">{{n-a @vehicle.name}}</div>
|
|
23
|
+
</div>
|
|
24
|
+
<div class="field-info-container">
|
|
25
|
+
<div class="field-name">Model</div>
|
|
26
|
+
<div class="field-value">{{n-a @vehicle.model}}</div>
|
|
27
|
+
</div>
|
|
28
|
+
<div class="field-info-container">
|
|
29
|
+
<div class="field-name">Year</div>
|
|
30
|
+
<div class="field-value">{{n-a @vehicle.year}}</div>
|
|
31
|
+
</div>
|
|
32
|
+
</div>
|
|
33
|
+
</ContentPanel>
|
|
34
|
+
</div>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{{yield}}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
<Overlay @position="right" @noBackdrop={{true}} @fullHeight={{true}} @width="700">
|
|
2
|
+
<Overlay::Header @hideLeftSection={{true}} @actionsWrapperClass="flex-1 flex-col py-3" class="h-auto-i min-h-[127px]">
|
|
3
|
+
<div class="flex flex-row items-center justify-between w-full mb-4">
|
|
4
|
+
<div class="flex flex-1 space-x-2">
|
|
5
|
+
{{!-- <Button @type="default" @icon="location-arrow" @helpText="Locate vehicle" @onClick={{@onPressCancel}} /> --}}
|
|
6
|
+
<Button @type="default" @icon="pen" @helpText="Edit vehicle" @onClick={{this.editVehicle}} />
|
|
7
|
+
</div>
|
|
8
|
+
<div class="flex flex-1 justify-end">
|
|
9
|
+
<Button @type="default" @icon="times" @helpText="Close vehicle details" @onClick={{@onPressCancel}} />
|
|
10
|
+
</div>
|
|
11
|
+
</div>
|
|
12
|
+
<div class="flex flex-row justify-between w-full">
|
|
13
|
+
<div class="flex flex-col flex-1 w-3/4">
|
|
14
|
+
<div class="flex flex-row">
|
|
15
|
+
<div class="w-14 flex items-center justify-center">
|
|
16
|
+
<Image src={{@vehicle.photo_url}} @fallbackSrc={{@vehicle.avatar_url}} alt={{@vehicle.name}} height="48" width="48" class="h-12 w-12" />
|
|
17
|
+
<Attach::Tooltip @class="clean" @animation="scale" @placement={{"top"}}>
|
|
18
|
+
<InputInfo @text={{@vehicle.public_id}} />
|
|
19
|
+
</Attach::Tooltip>
|
|
20
|
+
</div>
|
|
21
|
+
<div class="flex flex-col">
|
|
22
|
+
<h1 class="text-gray-900 dark:text-white text-2xl">{{@vehicle.display_name}}</h1>
|
|
23
|
+
<div class="-mt-1">
|
|
24
|
+
{{#if @vehicle.driver}}
|
|
25
|
+
<div class="flex flex-row items-center">
|
|
26
|
+
<span class="text-sm dark:text-gray-500 text-gray-700 mr-3">{{@vehicle.driver.name}}</span>
|
|
27
|
+
</div>
|
|
28
|
+
{{else}}
|
|
29
|
+
<div class="flex flex-row items-center">
|
|
30
|
+
<span class="text-sm dark:text-gray-500 text-gray-700 mr-3">No driver assigned</span>
|
|
31
|
+
{{!-- <Button @icon="user-plus" @size="xs" @helpText="Assign a driver to vehicle" /> --}}
|
|
32
|
+
</div>
|
|
33
|
+
{{/if}}
|
|
34
|
+
</div>
|
|
35
|
+
</div>
|
|
36
|
+
</div>
|
|
37
|
+
</div>
|
|
38
|
+
<div class="flex justify-end w-1/4">
|
|
39
|
+
<Badge @status={{@vehicle.status}} />
|
|
40
|
+
</div>
|
|
41
|
+
</div>
|
|
42
|
+
</Overlay::Header>
|
|
43
|
+
<Overlay::Body class="no-padding" @increaseInnerBodyHeightBy={{1000}}>
|
|
44
|
+
<div class="section-header-actions w-full overflow-x-scroll lg:overflow-x-auto">
|
|
45
|
+
<div class="ui-tabs mt-4">
|
|
46
|
+
<nav>
|
|
47
|
+
{{#each this.tabs as |tab|}}
|
|
48
|
+
<a href="javascript:;" class="ui-tab {{if (eq this.currentTab tab.slug) 'active' }}" {{on "click" (fn this.changeTab tab.slug)}}>
|
|
49
|
+
<FaIcon @icon={{tab.icon}} class="mr-1" />
|
|
50
|
+
<span>{{tab.title}}</span>
|
|
51
|
+
</a>
|
|
52
|
+
{{/each}}
|
|
53
|
+
</nav>
|
|
54
|
+
</div>
|
|
55
|
+
</div>
|
|
56
|
+
<div class="tab-content tab-{{this.currentTab}}">
|
|
57
|
+
{{component this.tab.component vehicle=@vehicle tabOptions=this.tab params=this.tab.componentParams}}
|
|
58
|
+
</div>
|
|
59
|
+
</Overlay::Body>
|
|
60
|
+
</Overlay>
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import Component from '@glimmer/component';
|
|
2
|
+
import { tracked } from '@glimmer/tracking';
|
|
3
|
+
import { action, computed } from '@ember/object';
|
|
4
|
+
import { inject as service } from '@ember/service';
|
|
5
|
+
import { isArray } from '@ember/array';
|
|
6
|
+
import VehiclePanelDetailComponent from './vehicle-panel/details';
|
|
7
|
+
|
|
8
|
+
export default class VehiclePanelComponent extends Component {
|
|
9
|
+
@service fetch;
|
|
10
|
+
@service modalsManager;
|
|
11
|
+
@service universe;
|
|
12
|
+
@service store;
|
|
13
|
+
@service hostRouter;
|
|
14
|
+
@tracked currentTab;
|
|
15
|
+
@tracked devices = [];
|
|
16
|
+
@tracked deviceApi = {};
|
|
17
|
+
@tracked vehicle;
|
|
18
|
+
|
|
19
|
+
get tabs() {
|
|
20
|
+
const registeredTabs = this.universe.getMenuItemsFromRegistry('component:vehicle-panel');
|
|
21
|
+
// this.universe._createMenuItem('Tracking', null, { icon: 'satellite-dish', component: VehiclePanelTrackingComponent }),
|
|
22
|
+
const defaultTabs = [this.universe._createMenuItem('Details', null, { icon: 'circle-info', component: VehiclePanelDetailComponent })];
|
|
23
|
+
|
|
24
|
+
if (isArray(registeredTabs)) {
|
|
25
|
+
return [...defaultTabs, ...registeredTabs];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return defaultTabs;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
@computed('currentTab', 'tabs') get tab() {
|
|
32
|
+
if (this.currentTab) {
|
|
33
|
+
return this.tabs.find(({ slug }) => slug === this.currentTab);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
constructor() {
|
|
40
|
+
super(...arguments);
|
|
41
|
+
this.vehicle = this.args.vehicle;
|
|
42
|
+
this.changeTab(this.args.tab || 'details');
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
@action async changeTab(tab) {
|
|
46
|
+
this.currentTab = tab;
|
|
47
|
+
|
|
48
|
+
if (typeof this.args.onTabChanged === 'function') {
|
|
49
|
+
this.args.onTabChanged(tab);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
@action editVehicle() {
|
|
54
|
+
const { vehicle } = this.args;
|
|
55
|
+
return this.hostRouter.transitionTo('console.fleet-ops.management.vehicles.index.edit', vehicle.public_id);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import Controller from '@ember/controller';
|
|
2
|
+
import { action } from '@ember/object';
|
|
3
|
+
|
|
4
|
+
export default class ManagementDriverIndexDetailsController extends Controller {
|
|
5
|
+
@action transitionBack() {
|
|
6
|
+
return this.transitionToRoute('management.drivers.index');
|
|
7
|
+
}
|
|
8
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import Controller from '@ember/controller';
|
|
2
|
+
import { inject as service } from '@ember/service';
|
|
3
|
+
import { tracked } from '@glimmer/tracking';
|
|
4
|
+
import { action } from '@ember/object';
|
|
5
|
+
|
|
6
|
+
export default class ManagementDriversIndexEditController extends Controller {
|
|
7
|
+
/**
|
|
8
|
+
* True if updating service rate.
|
|
9
|
+
*
|
|
10
|
+
* @var {Boolean}
|
|
11
|
+
*/
|
|
12
|
+
@tracked isUpdatingDriver = false;
|
|
13
|
+
/**
|
|
14
|
+
* Inject the `loader` service
|
|
15
|
+
*
|
|
16
|
+
* @var {Service}
|
|
17
|
+
*/
|
|
18
|
+
@service loader;
|
|
19
|
+
/**
|
|
20
|
+
* Inject the `notifications` service
|
|
21
|
+
*
|
|
22
|
+
* @var {Service}
|
|
23
|
+
*/
|
|
24
|
+
@service notifications;
|
|
25
|
+
/**
|
|
26
|
+
* Inject the `hostRouter` service
|
|
27
|
+
*
|
|
28
|
+
* @var {Service}
|
|
29
|
+
*/
|
|
30
|
+
@service hostRouter;
|
|
31
|
+
/**
|
|
32
|
+
* Inject the `currentUser` service
|
|
33
|
+
*
|
|
34
|
+
* @var {Service}
|
|
35
|
+
*/
|
|
36
|
+
@service store;
|
|
37
|
+
/**
|
|
38
|
+
* Updates the drivers to server
|
|
39
|
+
*
|
|
40
|
+
* @void
|
|
41
|
+
*/
|
|
42
|
+
@action updateDriver() {
|
|
43
|
+
const { driver } = this;
|
|
44
|
+
|
|
45
|
+
this.isUpdatingDriver = true;
|
|
46
|
+
this.loader.showLoader('.overlay-inner-content', 'Updating driver...');
|
|
47
|
+
|
|
48
|
+
try {
|
|
49
|
+
return driver
|
|
50
|
+
.save()
|
|
51
|
+
.then((driver) => {
|
|
52
|
+
return this.transitionToRoute('management.drivers.index').then(() => {
|
|
53
|
+
this.notifications.success(`Driver '${driver.name}' updated`);
|
|
54
|
+
this.resetForm();
|
|
55
|
+
this.hostRouter.refresh();
|
|
56
|
+
});
|
|
57
|
+
})
|
|
58
|
+
.catch(this.notifications.serverError)
|
|
59
|
+
.finally(() => {
|
|
60
|
+
this.isUpdatingDriver = false;
|
|
61
|
+
this.loader.removeLoader();
|
|
62
|
+
});
|
|
63
|
+
} catch (error) {
|
|
64
|
+
this.isUpdatingDriver = false;
|
|
65
|
+
this.loader.removeLoader();
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
@action transitionBack() {
|
|
70
|
+
return this.transitionToRoute('management.drivers.index');
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Resets the driver form
|
|
74
|
+
*
|
|
75
|
+
* @void
|
|
76
|
+
*/
|
|
77
|
+
@action resetForm() {
|
|
78
|
+
this.driver = this.store.createRecord('driver');
|
|
79
|
+
}
|
|
80
|
+
}
|