@fleetbase/fleetops-engine 0.6.44 → 0.6.46
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/map-settings.hbs +26 -0
- package/addon/components/admin/map-settings.js +46 -0
- package/addon/components/admin/routing-settings.hbs +11 -0
- package/addon/components/cell/order-route-type.hbs +46 -0
- package/addon/components/cell/order-route-type.js +90 -0
- package/addon/components/cell/place-address.hbs +1 -0
- package/addon/components/display-place.hbs +45 -56
- package/addon/components/display-place.js +21 -0
- package/addon/components/entity/form.hbs +24 -0
- package/addon/components/entity/form.js +30 -0
- package/addon/components/layout/fleet-ops-sidebar/driver-listing.js +5 -5
- package/addon/components/layout/fleet-ops-sidebar/fleet-listing.js +5 -5
- package/addon/components/layout/fleet-ops-sidebar.js +8 -0
- package/addon/components/leaflet-draw-control.js +1 -1
- package/addon/components/maintenance-schedule/form.js +2 -1
- package/addon/components/map/drawer/driver-listing.js +5 -5
- package/addon/components/map/drawer/geofence-event-listing.js +5 -2
- package/addon/components/map/drawer/place-listing.js +5 -5
- package/addon/components/map/drawer/position-listing.js +102 -21
- package/addon/components/map/drawer/vehicle-listing.js +5 -5
- package/addon/components/map/google-live-map.hbs +9 -0
- package/addon/components/map/google-live-map.js +445 -0
- package/addon/components/map/leaflet-live-map.hbs +186 -142
- package/addon/components/map/leaflet-live-map.js +121 -41
- package/addon/components/map/toolbar/visibility-control-panel.js +2 -2
- package/addon/components/map/toolbar/zones-panel.hbs +9 -9
- package/addon/components/map/toolbar.hbs +3 -3
- package/addon/components/map/toolbar.js +9 -1
- package/addon/components/modals/map-layer-form.hbs +132 -63
- package/addon/components/modals/orchestrator-import.hbs +4 -11
- package/addon/components/modals/orchestrator-import.js +17 -1
- package/addon/components/modals/update-order-activity.hbs +1 -1
- package/addon/components/orchestrator/order-pool.js +3 -2
- package/addon/components/orchestrator-workbench.js +29 -41
- package/addon/components/order/details/detail.js +2 -2
- package/addon/components/order/details/payload.hbs +5 -5
- package/addon/components/order/details/payload.js +18 -1
- package/addon/components/order/details/route.hbs +2 -2
- package/addon/components/order/details/route.js +76 -3
- package/addon/components/order/form/payload.hbs +33 -0
- package/addon/components/order/form/payload.js +1 -0
- package/addon/components/order/form/route.hbs +8 -8
- package/addon/components/order/form/route.js +78 -18
- package/addon/components/order/form/service-rate.hbs +1 -1
- package/addon/components/order/form.js +2 -2
- package/addon/components/order/route-editor.hbs +209 -147
- package/addon/components/order/route-editor.js +143 -102
- package/addon/components/place/address.hbs +1 -0
- package/addon/components/place/address.js +12 -0
- package/addon/components/route-list.hbs +43 -55
- package/addon/components/route-list.js +47 -0
- package/addon/components/route-optimization-wizard-panel.js +3 -4
- package/addon/components/service-rate/details.hbs +6 -6
- package/addon/components/service-rate/form.hbs +28 -15
- package/addon/components/vehicle/card.hbs +4 -4
- package/addon/controllers/maintenance/schedules/index.js +2 -1
- package/addon/controllers/operations/orders/index/details/virtual.js +5 -0
- package/addon/controllers/operations/orders/index/details.js +113 -15
- package/addon/controllers/operations/orders/index/new.js +1 -1
- package/addon/controllers/operations/orders/index.js +8 -1
- package/addon/controllers/operations/service-rates/index/edit.js +1 -0
- package/addon/controllers/operations/service-rates/index/new.js +1 -0
- package/addon/controllers/settings/map.js +97 -0
- package/addon/controllers/settings/routing.js +47 -12
- package/addon/extension.js +11 -0
- package/addon/helpers/f-to-int.js +6 -0
- package/addon/helpers/place-address-html.js +6 -0
- package/addon/instance-initializers/register-google-routes.js +17 -0
- package/addon/instance-initializers/register-osrm.js +5 -0
- package/addon/routes/application.js +9 -3
- package/addon/routes/operations/orchestrator.js +13 -0
- package/addon/routes/operations/orders/index/details/virtual.js +1 -0
- package/addon/routes/operations/orders/index/details.js +47 -24
- package/addon/routes/operations/orders/index/new.js +9 -0
- package/addon/routes/operations/orders/index.js +0 -1
- package/addon/routes/settings/map.js +3 -0
- package/addon/routes.js +1 -0
- package/addon/services/entity-actions.js +1 -1
- package/addon/services/equipment-actions.js +4 -6
- package/addon/services/geofence-event-bus.js +2 -1
- package/addon/services/geofence.js +204 -168
- package/addon/services/google-routes.js +300 -0
- package/addon/services/leaflet-draw-restriction.js +2 -1
- package/addon/services/leaflet-map-manager.js +21 -0
- package/addon/services/maintenance-actions.js +4 -6
- package/addon/services/map-adapter/google.js +2205 -0
- package/addon/services/map-adapter/leaflet.js +1042 -0
- package/addon/services/map-adapter-interface.js +551 -0
- package/addon/services/map-manager.js +768 -0
- package/addon/services/map-settings.js +75 -0
- package/addon/services/movement-tracker.js +83 -32
- package/addon/services/order-actions.js +11 -1
- package/addon/services/osrm.js +79 -6
- package/addon/services/part-actions.js +4 -6
- package/addon/services/position-playback.js +135 -62
- package/addon/services/route-engine.js +83 -0
- package/addon/services/service-areas.js +8 -1
- package/addon/services/service-rate-actions.js +8 -3
- package/addon/styles/fleetops-engine.css +555 -0
- package/addon/templates/management/vehicles/index.hbs +0 -1
- package/addon/templates/operations/orders/index/details/virtual.hbs +7 -1
- package/addon/templates/operations/orders/index/details.hbs +0 -1
- package/addon/templates/settings/map.hbs +53 -0
- package/addon/templates/settings/routing.hbs +15 -4
- package/addon/utils/fleet-ops-options.js +34 -0
- package/addon/utils/has-valid-coordinates.js +6 -0
- package/addon/utils/map-provider-options.js +6 -0
- package/addon/utils/order-route-editing.js +86 -0
- package/addon/utils/order-route-summary.js +20 -0
- package/addon/utils/place-address-html.js +41 -0
- package/addon/utils/route-colors.js +43 -12
- package/addon/utils/route-visualization.js +93 -0
- package/addon/utils/serialize-payload.js +1 -1
- package/addon/utils/vendor-integration.js +2 -1
- package/app/components/admin/map-settings.js +1 -0
- package/app/components/admin/routing-settings.js +1 -0
- package/app/components/cell/order-route-type.js +1 -0
- package/app/components/cell/place-address.js +1 -0
- package/app/components/map/google-live-map.js +1 -0
- package/app/components/place/address.js +1 -0
- package/app/controllers/operations/orders/index/details/virtual.js +1 -0
- package/app/controllers/settings/map.js +1 -0
- package/app/helpers/f-to-int.js +1 -0
- package/app/helpers/place-address-html.js +1 -0
- package/app/routes/settings/map.js +1 -0
- package/app/services/map-adapter/google.js +1 -0
- package/app/services/map-adapter/leaflet.js +1 -0
- package/app/services/map-adapter-interface.js +1 -0
- package/app/services/map-manager.js +1 -0
- package/app/services/map-settings.js +1 -0
- package/app/templates/settings/map.js +1 -0
- package/app/utils/has-valid-coordinates.js +1 -0
- package/app/utils/order-route-editing.js +2 -0
- package/app/utils/order-route-summary.js +1 -0
- package/app/utils/place-address-html.js +1 -0
- package/app/utils/route-colors.js +1 -1
- package/app/utils/route-visualization.js +1 -0
- package/assets/images/drawing-controls-spritesheet.svg +156 -0
- package/composer.json +1 -1
- package/extension.json +1 -1
- package/package.json +3 -3
- package/server/src/Auth/Schemas/FleetOps.php +5 -0
- package/server/src/Http/Controllers/Api/v1/OrderController.php +36 -8
- package/server/src/Http/Controllers/Api/v1/PayloadController.php +14 -1
- package/server/src/Http/Controllers/Api/v1/PurchaseRateController.php +11 -3
- package/server/src/Http/Controllers/Api/v1/ServiceAreaController.php +2 -0
- package/server/src/Http/Controllers/Api/v1/ServiceQuoteController.php +7 -2
- package/server/src/Http/Controllers/Api/v1/ServiceRateController.php +6 -1
- package/server/src/Http/Controllers/Api/v1/ZoneController.php +2 -0
- package/server/src/Http/Controllers/Internal/v1/LiveController.php +2 -1
- package/server/src/Http/Controllers/Internal/v1/OrchestrationController.php +7 -4
- package/server/src/Http/Controllers/Internal/v1/OrderController.php +61 -34
- package/server/src/Http/Controllers/Internal/v1/ServiceQuoteController.php +3 -2
- package/server/src/Http/Controllers/Internal/v1/SettingController.php +106 -3
- package/server/src/Http/Filter/OrderFilter.php +2 -1
- package/server/src/Http/Requests/CreateServiceRateRequest.php +2 -2
- package/server/src/Http/Resources/v1/Index/Payload.php +9 -7
- package/server/src/Http/Resources/v1/Order.php +20 -2
- package/server/src/Http/Resources/v1/ServiceQuote.php +2 -2
- package/server/src/Models/Entity.php +7 -0
- package/server/src/Models/Order.php +81 -8
- package/server/src/Models/OrderConfig.php +15 -0
- package/server/src/Models/Payload.php +131 -27
- package/server/src/Models/Place.php +182 -48
- package/server/src/Models/ServiceRate.php +125 -32
- package/server/src/Observers/PurchaseRateObserver.php +3 -5
- package/server/src/Orchestration/Engines/VroomOrchestrationEngine.php +59 -23
- package/server/src/Rules/ComputableAlgo.php +1 -3
- package/server/src/Support/Algo.php +87 -1
- package/server/src/Support/Utils.php +69 -0
- package/server/src/routes.php +4 -0
- package/translations/en-us.yaml +1937 -1916
|
@@ -123,6 +123,39 @@ export const contactTypes = [
|
|
|
123
123
|
{ label: 'Dispatcher', value: 'dispatcher', description: 'Scheduling and operations contact' },
|
|
124
124
|
];
|
|
125
125
|
|
|
126
|
+
export const entityTypes = [
|
|
127
|
+
{ label: 'Entity', value: 'entity', description: 'Standard generic entity representing an item.' },
|
|
128
|
+
{ label: 'Parcel', value: 'parcel', description: 'Standard parcel or package used by parcel-based pricing.' },
|
|
129
|
+
{ label: 'Package', value: 'package', description: 'Generic package or boxed item.' },
|
|
130
|
+
{ label: 'Product', value: 'product', description: 'Sellable product or catalog item.' },
|
|
131
|
+
{ label: 'Item', value: 'item', description: 'Generic inventory item when no stronger type applies.' },
|
|
132
|
+
{ label: 'SKU / Inventory Unit', value: 'sku', description: 'Stock keeping unit or tracked inventory piece.' },
|
|
133
|
+
{ label: 'Document', value: 'document', description: 'Envelopes, paperwork, and lightweight documents.' },
|
|
134
|
+
{ label: 'Envelope', value: 'envelope', description: 'Flat document mailer or envelope.' },
|
|
135
|
+
{ label: 'Box', value: 'box', description: 'Single boxed shipment unit.' },
|
|
136
|
+
{ label: 'Carton', value: 'carton', description: 'Single carton, box, or case.' },
|
|
137
|
+
{ label: 'Case', value: 'case', description: 'Case-packed goods or packaged case.' },
|
|
138
|
+
{ label: 'Bundle', value: 'bundle', description: 'Bundled or strapped items shipped together.' },
|
|
139
|
+
{ label: 'Bag', value: 'bag', description: 'Bagged goods, sacks, or pouches.' },
|
|
140
|
+
{ label: 'Tote', value: 'tote', description: 'Reusable tote, bin, or container.' },
|
|
141
|
+
{ label: 'Crate', value: 'crate', description: 'Rigid crate or caged shipment unit.' },
|
|
142
|
+
{ label: 'Pallet', value: 'pallet', description: 'Palletized freight or grouped cargo.' },
|
|
143
|
+
{ label: 'Container', value: 'container', description: 'Containerized shipment or storage container.' },
|
|
144
|
+
{ label: 'Freight', value: 'freight', description: 'Loose freight or larger shipment unit.' },
|
|
145
|
+
{ label: 'Cargo', value: 'cargo', description: 'General cargo not classified more specifically.' },
|
|
146
|
+
{ label: 'Equipment', value: 'equipment', description: 'Tools, machines, or operational equipment.' },
|
|
147
|
+
{ label: 'Machinery', value: 'machinery', description: 'Industrial machinery or large mechanical unit.' },
|
|
148
|
+
{ label: 'Asset', value: 'asset', description: 'Tracked business asset or returnable item.' },
|
|
149
|
+
{ label: 'Return', value: 'return', description: 'Returned goods or reverse-logistics item.' },
|
|
150
|
+
{ label: 'Sample', value: 'sample', description: 'Sample item for trial, demo, or inspection.' },
|
|
151
|
+
{ label: 'Spare Part', value: 'spare_part', description: 'Replacement component or service part.' },
|
|
152
|
+
{ label: 'Medical Supply', value: 'medical_supply', description: 'Healthcare, lab, or medical delivery item.' },
|
|
153
|
+
{ label: 'Food', value: 'food', description: 'Prepared food, meal, or grocery item.' },
|
|
154
|
+
{ label: 'Beverage', value: 'beverage', description: 'Drink, liquid refreshment, or bottled shipment.' },
|
|
155
|
+
{ label: 'Retail Goods', value: 'retail_goods', description: 'General consumer or retail merchandise.' },
|
|
156
|
+
{ label: 'Passenger', value: 'passenger', description: 'Person transported as part of the order payload.' },
|
|
157
|
+
];
|
|
158
|
+
|
|
126
159
|
export const contactStatuses = [
|
|
127
160
|
{ label: 'Active', value: 'active', description: 'Contact is valid and in use' },
|
|
128
161
|
{ label: 'Inactive', value: 'inactive', description: 'Contact is no longer valid' },
|
|
@@ -821,6 +854,7 @@ export default function fleetOpsOptions(key) {
|
|
|
821
854
|
fleetTypes,
|
|
822
855
|
fleetStatuses,
|
|
823
856
|
contactTypes,
|
|
857
|
+
entityTypes,
|
|
824
858
|
contactStatuses,
|
|
825
859
|
fuelReportTypes,
|
|
826
860
|
fuelReportStatuses,
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { isArray } from '@ember/array';
|
|
2
|
+
import { Point } from '@fleetbase/fleetops-data/utils/geojson';
|
|
3
|
+
import { buildRoutePointsFromPayload, waypointToPlace } from './route-visualization';
|
|
4
|
+
|
|
5
|
+
export function routePointsToCoordinates(routePoints = []) {
|
|
6
|
+
return routePoints
|
|
7
|
+
.map(({ place }) => place)
|
|
8
|
+
.filter((place) => place?.hasValidCoordinates)
|
|
9
|
+
.map((place) => [place.latitude, place.longitude]);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function getPayloadRouteCoordinates(payload) {
|
|
13
|
+
return routePointsToCoordinates(buildRoutePointsFromPayload(payload));
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function getPayloadIntermediateWaypoints(payload) {
|
|
17
|
+
const waypoints = payload?.waypoints;
|
|
18
|
+
|
|
19
|
+
if (typeof waypoints?.toArray === 'function') {
|
|
20
|
+
return waypoints.toArray();
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return isArray(waypoints) ? waypoints : Array.from(waypoints ?? []);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function hasRouteEndpoints(payload) {
|
|
27
|
+
return Boolean(payload?.pickup || payload?.dropoff);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function canOptimizeIntermediateWaypoints(payload) {
|
|
31
|
+
return getPayloadIntermediateWaypoints(payload).length >= 2;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function buildRouteOptimizationInput(order) {
|
|
35
|
+
const payload = order?.payload;
|
|
36
|
+
const routePoints = buildRoutePointsFromPayload(payload);
|
|
37
|
+
const coordinates = routePointsToCoordinates(routePoints).map(([lat, lng]) => [lng, lat]);
|
|
38
|
+
const waypoints = getPayloadIntermediateWaypoints(payload);
|
|
39
|
+
|
|
40
|
+
return {
|
|
41
|
+
order,
|
|
42
|
+
payload,
|
|
43
|
+
coordinates,
|
|
44
|
+
routePoints,
|
|
45
|
+
waypoints,
|
|
46
|
+
preserveEndpoints: hasRouteEndpoints(payload),
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function applyOptimizedIntermediateWaypoints(payload, sortedWaypoints = []) {
|
|
51
|
+
if (!payload) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (typeof payload.setWaypoints === 'function') {
|
|
56
|
+
payload.setWaypoints(sortedWaypoints);
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
payload.waypoints = sortedWaypoints;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function createWaypointRecord(store, properties = {}) {
|
|
64
|
+
const place = properties.place ? waypointToPlace(properties.place) : null;
|
|
65
|
+
const serializedPlace = typeof place?.serialize === 'function' ? place.serialize() : {};
|
|
66
|
+
|
|
67
|
+
return store.createRecord('waypoint', {
|
|
68
|
+
type: 'dropoff',
|
|
69
|
+
...serializedPlace,
|
|
70
|
+
...properties,
|
|
71
|
+
place: place ?? null,
|
|
72
|
+
place_uuid: properties.place_uuid ?? place?.id ?? null,
|
|
73
|
+
location: properties.location ?? place?.location ?? new Point(0, 0),
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export default {
|
|
78
|
+
routePointsToCoordinates,
|
|
79
|
+
getPayloadRouteCoordinates,
|
|
80
|
+
getPayloadIntermediateWaypoints,
|
|
81
|
+
hasRouteEndpoints,
|
|
82
|
+
canOptimizeIntermediateWaypoints,
|
|
83
|
+
buildRouteOptimizationInput,
|
|
84
|
+
applyOptimizedIntermediateWaypoints,
|
|
85
|
+
createWaypointRecord,
|
|
86
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export function buildRouteTypeSummary({ hasIntermediateWaypoints = false, intermediateStopCount = 0, hasPickup = false, hasDropoff = false } = {}) {
|
|
2
|
+
const count = Math.max(Number(intermediateStopCount) || 0, 0);
|
|
3
|
+
const hasIntermediateStops = count > 0 || Boolean(hasIntermediateWaypoints);
|
|
4
|
+
const hasEndpoints = Boolean(hasPickup || hasDropoff);
|
|
5
|
+
const kind = hasIntermediateStops ? (hasEndpoints ? 'pickup_dropoff_stops' : 'multi_stop') : 'pickup_dropoff';
|
|
6
|
+
|
|
7
|
+
return {
|
|
8
|
+
kind,
|
|
9
|
+
intermediateStopCount: count,
|
|
10
|
+
icon: hasIntermediateStops ? 'route' : 'exchange-alt',
|
|
11
|
+
badgeClass: hasIntermediateStops ? 'import-preview-badge import-preview-badge--blue' : 'import-preview-badge import-preview-badge--gray',
|
|
12
|
+
translationKey:
|
|
13
|
+
kind === 'pickup_dropoff_stops'
|
|
14
|
+
? 'orchestrator.col-preview-pickup-dropoff-stops'
|
|
15
|
+
: kind === 'multi_stop'
|
|
16
|
+
? 'orchestrator.col-preview-multi-stop'
|
|
17
|
+
: 'orchestrator.col-preview-pickup-dropoff',
|
|
18
|
+
translationOptions: hasIntermediateStops ? { count } : {},
|
|
19
|
+
};
|
|
20
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { htmlSafe } from '@ember/template';
|
|
2
|
+
import { isBlank } from '@ember/utils';
|
|
3
|
+
|
|
4
|
+
function escapeHtml(value) {
|
|
5
|
+
return String(value).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, ''');
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function line(content, className = '') {
|
|
9
|
+
const classAttribute = className ? ` class="${className}"` : '';
|
|
10
|
+
|
|
11
|
+
return `<div${classAttribute}>${escapeHtml(content)}</div>`;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export default function placeAddressHtml(place) {
|
|
15
|
+
if (!place) {
|
|
16
|
+
return htmlSafe('');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const name = place.name === place.street1 ? null : place.name;
|
|
20
|
+
const cityStatePostalCode = [place.city, place.province, place.postal_code].filter((value) => !isBlank(value)).join(', ');
|
|
21
|
+
const lines = [];
|
|
22
|
+
|
|
23
|
+
if (name) {
|
|
24
|
+
lines.push(line(name, 'font-semibold'));
|
|
25
|
+
lines.push(line(place.street1));
|
|
26
|
+
} else if (place.street1) {
|
|
27
|
+
lines.push(line(place.street1, 'font-semibold'));
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (place.street2) {
|
|
31
|
+
lines.push(line(place.street2));
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
lines.push(line(cityStatePostalCode));
|
|
35
|
+
|
|
36
|
+
if (place.country) {
|
|
37
|
+
lines.push(line(place.country_name));
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return htmlSafe(`<address class="uppercase truncate w-full">${lines.join('')}</address>`);
|
|
41
|
+
}
|
|
@@ -10,6 +10,15 @@
|
|
|
10
10
|
* Colors are chosen for high contrast against both light and dark CartoDB tile layers.
|
|
11
11
|
*/
|
|
12
12
|
export const ROUTE_COLOR_PALETTE = ['#0EA5E9', '#8B5CF6', '#F59E0B', '#10B981', '#F97316', '#EC4899', '#06B6D4', '#EF4444', '#84CC16', '#6366F1'];
|
|
13
|
+
export const ORDER_ROUTE_STATUS_COLORS = {
|
|
14
|
+
created: '#2563EB',
|
|
15
|
+
dispatched: '#4F46E5',
|
|
16
|
+
started: '#0891B2',
|
|
17
|
+
enroute: '#CA8A04',
|
|
18
|
+
driver_enroute: '#CA8A04',
|
|
19
|
+
completed: '#16A34A',
|
|
20
|
+
canceled: '#DC2626',
|
|
21
|
+
};
|
|
13
22
|
|
|
14
23
|
/**
|
|
15
24
|
* Darken a hex color by a given integer amount (0-255 per channel).
|
|
@@ -43,6 +52,23 @@ export function colorForId(id = '') {
|
|
|
43
52
|
return ROUTE_COLOR_PALETTE[Math.abs(hash) % ROUTE_COLOR_PALETTE.length];
|
|
44
53
|
}
|
|
45
54
|
|
|
55
|
+
export function normalizeOrderRouteStatus(status = 'created') {
|
|
56
|
+
switch (status) {
|
|
57
|
+
case 'pending':
|
|
58
|
+
return 'created';
|
|
59
|
+
case 'driver-enroute':
|
|
60
|
+
return 'driver_enroute';
|
|
61
|
+
case 'cancelled':
|
|
62
|
+
return 'canceled';
|
|
63
|
+
default:
|
|
64
|
+
return status && ORDER_ROUTE_STATUS_COLORS[status] ? status : 'created';
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function routeColorForStatus(status = 'created') {
|
|
69
|
+
return ORDER_ROUTE_STATUS_COLORS[normalizeOrderRouteStatus(status)] ?? ORDER_ROUTE_STATUS_COLORS.created;
|
|
70
|
+
}
|
|
71
|
+
|
|
46
72
|
/**
|
|
47
73
|
* Return a Leaflet Path options `styles` array for a route polyline
|
|
48
74
|
* based on the order status. Uses a two-layer "cased" approach:
|
|
@@ -52,28 +78,33 @@ export function colorForId(id = '') {
|
|
|
52
78
|
* @param {string} color - The base hex color for this route
|
|
53
79
|
* @returns {Array<Object>} Array of Leaflet Path options objects
|
|
54
80
|
*/
|
|
55
|
-
export function routeStyleForStatus(status,
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
case 'pending':
|
|
60
|
-
return [{ color: '#9CA3AF', weight: 5, opacity: 0.65, dashArray: '8 14', lineCap: 'round', lineJoin: 'round' }];
|
|
81
|
+
export function routeStyleForStatus(status, fallbackColor = null) {
|
|
82
|
+
const normalizedStatus = normalizeOrderRouteStatus(status);
|
|
83
|
+
const color = routeColorForStatus(normalizedStatus) ?? fallbackColor ?? ORDER_ROUTE_STATUS_COLORS.created;
|
|
84
|
+
const darker = darkenColor(color, 36);
|
|
61
85
|
|
|
86
|
+
switch (normalizedStatus) {
|
|
62
87
|
case 'canceled':
|
|
63
|
-
return [
|
|
88
|
+
return [
|
|
89
|
+
{ color: darkenColor(color, 18), weight: 5, opacity: 0.16, lineCap: 'round', lineJoin: 'round' },
|
|
90
|
+
{ color, weight: 3, opacity: 0.52, lineCap: 'round', lineJoin: 'round' },
|
|
91
|
+
];
|
|
64
92
|
|
|
65
93
|
case 'completed':
|
|
66
94
|
return [
|
|
67
|
-
{ color:
|
|
68
|
-
{ color
|
|
95
|
+
{ color: darker, weight: 6, opacity: 0.4, lineCap: 'round', lineJoin: 'round' },
|
|
96
|
+
{ color, weight: 3, opacity: 0.8, lineCap: 'round', lineJoin: 'round' },
|
|
69
97
|
];
|
|
70
98
|
|
|
71
|
-
case '
|
|
99
|
+
case 'created':
|
|
72
100
|
case 'dispatched':
|
|
101
|
+
case 'started':
|
|
102
|
+
case 'enroute':
|
|
103
|
+
case 'driver_enroute':
|
|
73
104
|
default:
|
|
74
105
|
return [
|
|
75
|
-
{ color: darker, weight:
|
|
76
|
-
{ color
|
|
106
|
+
{ color: darker, weight: 7, opacity: 0.82, lineCap: 'round', lineJoin: 'round' },
|
|
107
|
+
{ color, weight: 4, opacity: 1, lineCap: 'round', lineJoin: 'round' },
|
|
77
108
|
];
|
|
78
109
|
}
|
|
79
110
|
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import hasValidCoordinates from './has-valid-coordinates';
|
|
2
|
+
|
|
3
|
+
export function waypointToPlace(waypoint) {
|
|
4
|
+
if (!waypoint) {
|
|
5
|
+
return null;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
return waypoint.place ?? waypoint;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function buildPlaceAddressTooltip(title, place) {
|
|
12
|
+
if (!place) {
|
|
13
|
+
return title;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const name = place.name && place.name !== place.street1 ? place.name : null;
|
|
17
|
+
const cityStatePostalCode = [place.city, place.province, place.postal_code].filter(Boolean).join(', ');
|
|
18
|
+
const lines = [
|
|
19
|
+
name ? `<div class="fleetops-google-hover-tooltip__meta">${name}</div>` : '',
|
|
20
|
+
place.street1 ? `<div class="fleetops-google-hover-tooltip__meta">${place.street1}</div>` : '',
|
|
21
|
+
place.street2 ? `<div class="fleetops-google-hover-tooltip__meta">${place.street2}</div>` : '',
|
|
22
|
+
cityStatePostalCode ? `<div class="fleetops-google-hover-tooltip__meta">${cityStatePostalCode}</div>` : '',
|
|
23
|
+
place.country_name ? `<div class="fleetops-google-hover-tooltip__meta">${place.country_name}</div>` : '',
|
|
24
|
+
].filter(Boolean);
|
|
25
|
+
|
|
26
|
+
return `
|
|
27
|
+
<div class="fleetops-google-hover-tooltip__title">${title}</div>
|
|
28
|
+
${lines.join('')}
|
|
29
|
+
`;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function buildRoutePointsFromPayload(payload) {
|
|
33
|
+
if (!payload) {
|
|
34
|
+
return [];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const points = [];
|
|
38
|
+
const waypoints = payload.waypoints ?? [];
|
|
39
|
+
|
|
40
|
+
if (hasValidCoordinates(payload.pickup)) {
|
|
41
|
+
points.push({ role: 'pickup', place: payload.pickup });
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
waypoints.forEach((waypoint, index) => {
|
|
45
|
+
const place = waypointToPlace(waypoint);
|
|
46
|
+
|
|
47
|
+
if (hasValidCoordinates(place)) {
|
|
48
|
+
points.push({ role: 'waypoint', place, stopNumber: index + 1 });
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
if (hasValidCoordinates(payload.dropoff)) {
|
|
53
|
+
points.push({ role: 'dropoff', place: payload.dropoff });
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return points;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function describeRoutePoint(routePoint, routeColor) {
|
|
60
|
+
const role = routePoint?.role;
|
|
61
|
+
const stopNumber = routePoint?.stopNumber ?? 1;
|
|
62
|
+
const label = role === 'pickup' ? 'P' : role === 'dropoff' ? 'D' : String(stopNumber);
|
|
63
|
+
const markerColor = role === 'pickup' ? '#22C55E' : role === 'dropoff' ? '#EF4444' : routeColor;
|
|
64
|
+
const title = role === 'pickup' ? 'Pickup' : role === 'dropoff' ? 'Dropoff' : `Stop ${stopNumber}`;
|
|
65
|
+
|
|
66
|
+
return {
|
|
67
|
+
label,
|
|
68
|
+
markerColor,
|
|
69
|
+
title,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function buildRoutePointMarkerPresentation(routePoint, routeColor, options = {}) {
|
|
74
|
+
const role = routePoint?.role;
|
|
75
|
+
const place = routePoint?.place;
|
|
76
|
+
const { label, markerColor, title } = describeRoutePoint(routePoint, routeColor);
|
|
77
|
+
const zIndexOffset = role === 'pickup' ? 2200 : role === 'dropoff' ? 2100 : 1800;
|
|
78
|
+
|
|
79
|
+
return {
|
|
80
|
+
waypointLabel: label,
|
|
81
|
+
waypointColor: markerColor,
|
|
82
|
+
title,
|
|
83
|
+
zIndexOffset: options.zIndexOffset ?? zIndexOffset,
|
|
84
|
+
tooltip: buildPlaceAddressTooltip(title, place),
|
|
85
|
+
tooltipOptions: {
|
|
86
|
+
direction: 'top',
|
|
87
|
+
offset: [0, -20],
|
|
88
|
+
className: 'fleetops-waypoint-tooltip',
|
|
89
|
+
html: true,
|
|
90
|
+
...(options.tooltipOptions ?? {}),
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
}
|
|
@@ -5,7 +5,7 @@ export default function serializePayload(payload) {
|
|
|
5
5
|
const serialized = {
|
|
6
6
|
pickup: serializeModel(payload.pickup),
|
|
7
7
|
dropoff: serializeModel(payload.dropoff),
|
|
8
|
-
|
|
8
|
+
entities: serializeArray(payload.entities),
|
|
9
9
|
waypoints: serializeArray(payload.waypoints),
|
|
10
10
|
};
|
|
11
11
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import apiUrl from '@fleetbase/ember-core/utils/api-url';
|
|
2
|
+
import { isArray } from '@ember/array';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Normalize any array-like (EmberArray, Set, arguments) into a plain array.
|
|
@@ -8,7 +9,7 @@ import apiUrl from '@fleetbase/ember-core/utils/api-url';
|
|
|
8
9
|
*/
|
|
9
10
|
export function normalizeToArray(input) {
|
|
10
11
|
if (!input) return [];
|
|
11
|
-
if (
|
|
12
|
+
if (isArray(input)) return input;
|
|
12
13
|
if (typeof input.toArray === 'function') return input.toArray();
|
|
13
14
|
try {
|
|
14
15
|
return Array.from(input);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '@fleetbase/fleetops-engine/components/admin/map-settings';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '@fleetbase/fleetops-engine/components/admin/routing-settings';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '@fleetbase/fleetops-engine/components/cell/order-route-type';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '@fleetbase/fleetops-engine/components/cell/place-address';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '@fleetbase/fleetops-engine/components/map/google-live-map';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '@fleetbase/fleetops-engine/components/place/address';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '@fleetbase/fleetops-engine/controllers/operations/orders/index/details/virtual';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '@fleetbase/fleetops-engine/controllers/settings/map';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '@fleetbase/fleetops-engine/helpers/f-to-int';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '@fleetbase/fleetops-engine/helpers/place-address-html';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '@fleetbase/fleetops-engine/routes/settings/map';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '@fleetbase/fleetops-engine/services/map-adapter/google';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '@fleetbase/fleetops-engine/services/map-adapter/leaflet';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '@fleetbase/fleetops-engine/services/map-adapter-interface';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '@fleetbase/fleetops-engine/services/map-manager';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '@fleetbase/fleetops-engine/services/map-settings';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '@fleetbase/fleetops-engine/templates/settings/map';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '@fleetbase/fleetops-engine/utils/has-valid-coordinates';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from '@fleetbase/fleetops-engine/utils/order-route-summary';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '@fleetbase/fleetops-engine/utils/place-address-html';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export
|
|
1
|
+
export * from '@fleetbase/fleetops-engine/utils/route-colors';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from '@fleetbase/fleetops-engine/utils/route-visualization';
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
2
|
+
<svg
|
|
3
|
+
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
4
|
+
xmlns:cc="http://creativecommons.org/ns#"
|
|
5
|
+
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
6
|
+
xmlns:svg="http://www.w3.org/2000/svg"
|
|
7
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
8
|
+
xmlns:xlink="http://www.w3.org/1999/xlink"
|
|
9
|
+
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
10
|
+
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
11
|
+
viewBox="0 0 600 60"
|
|
12
|
+
height="60"
|
|
13
|
+
width="600"
|
|
14
|
+
id="svg4225"
|
|
15
|
+
version="1.1"
|
|
16
|
+
inkscape:version="0.91 r13725"
|
|
17
|
+
sodipodi:docname="spritesheet.svg"
|
|
18
|
+
inkscape:export-filename="/home/fpuga/development/upstream/icarto.Leaflet.draw/src/images/spritesheet-2x.png"
|
|
19
|
+
inkscape:export-xdpi="90"
|
|
20
|
+
inkscape:export-ydpi="90">
|
|
21
|
+
<metadata
|
|
22
|
+
id="metadata4258">
|
|
23
|
+
<rdf:RDF>
|
|
24
|
+
<cc:Work
|
|
25
|
+
rdf:about="">
|
|
26
|
+
<dc:format>image/svg+xml</dc:format>
|
|
27
|
+
<dc:type
|
|
28
|
+
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
|
29
|
+
<dc:title />
|
|
30
|
+
</cc:Work>
|
|
31
|
+
</rdf:RDF>
|
|
32
|
+
</metadata>
|
|
33
|
+
<defs
|
|
34
|
+
id="defs4256" />
|
|
35
|
+
<sodipodi:namedview
|
|
36
|
+
pagecolor="#ffffff"
|
|
37
|
+
bordercolor="#666666"
|
|
38
|
+
borderopacity="1"
|
|
39
|
+
objecttolerance="10"
|
|
40
|
+
gridtolerance="10"
|
|
41
|
+
guidetolerance="10"
|
|
42
|
+
inkscape:pageopacity="0"
|
|
43
|
+
inkscape:pageshadow="2"
|
|
44
|
+
inkscape:window-width="1920"
|
|
45
|
+
inkscape:window-height="1056"
|
|
46
|
+
id="namedview4254"
|
|
47
|
+
showgrid="false"
|
|
48
|
+
inkscape:zoom="1.3101852"
|
|
49
|
+
inkscape:cx="237.56928"
|
|
50
|
+
inkscape:cy="7.2419621"
|
|
51
|
+
inkscape:window-x="1920"
|
|
52
|
+
inkscape:window-y="24"
|
|
53
|
+
inkscape:window-maximized="1"
|
|
54
|
+
inkscape:current-layer="svg4225" />
|
|
55
|
+
<g
|
|
56
|
+
id="enabled"
|
|
57
|
+
style="fill:#464646;fill-opacity:1">
|
|
58
|
+
<g
|
|
59
|
+
id="polyline"
|
|
60
|
+
style="fill:#464646;fill-opacity:1">
|
|
61
|
+
<path
|
|
62
|
+
d="m 18,36 0,6 6,0 0,-6 -6,0 z m 4,4 -2,0 0,-2 2,0 0,2 z"
|
|
63
|
+
id="path4229"
|
|
64
|
+
inkscape:connector-curvature="0"
|
|
65
|
+
style="fill:#464646;fill-opacity:1" />
|
|
66
|
+
<path
|
|
67
|
+
d="m 36,18 0,6 6,0 0,-6 -6,0 z m 4,4 -2,0 0,-2 2,0 0,2 z"
|
|
68
|
+
id="path4231"
|
|
69
|
+
inkscape:connector-curvature="0"
|
|
70
|
+
style="fill:#464646;fill-opacity:1" />
|
|
71
|
+
<path
|
|
72
|
+
d="m 23.142,39.145 -2.285,-2.29 16,-15.998 2.285,2.285 z"
|
|
73
|
+
id="path4233"
|
|
74
|
+
inkscape:connector-curvature="0"
|
|
75
|
+
style="fill:#464646;fill-opacity:1" />
|
|
76
|
+
</g>
|
|
77
|
+
<path
|
|
78
|
+
id="polygon"
|
|
79
|
+
d="M 100,24.565 97.904,39.395 83.07,42 76,28.773 86.463,18 Z"
|
|
80
|
+
inkscape:connector-curvature="0"
|
|
81
|
+
style="fill:#464646;fill-opacity:1" />
|
|
82
|
+
<path
|
|
83
|
+
id="rectangle"
|
|
84
|
+
d="m 140,20 20,0 0,20 -20,0 z"
|
|
85
|
+
inkscape:connector-curvature="0"
|
|
86
|
+
style="fill:#464646;fill-opacity:1" />
|
|
87
|
+
<path
|
|
88
|
+
id="circle"
|
|
89
|
+
d="m 221,30 c 0,6.078 -4.926,11 -11,11 -6.074,0 -11,-4.922 -11,-11 0,-6.074 4.926,-11 11,-11 6.074,0 11,4.926 11,11 z"
|
|
90
|
+
inkscape:connector-curvature="0"
|
|
91
|
+
style="fill:#464646;fill-opacity:1" />
|
|
92
|
+
<path
|
|
93
|
+
id="marker"
|
|
94
|
+
d="m 270,19 c -4.971,0 -9,4.029 -9,9 0,4.971 5.001,12 9,14 4.001,-2 9,-9.029 9,-14 0,-4.971 -4.029,-9 -9,-9 z m 0,12.5 c -2.484,0 -4.5,-2.014 -4.5,-4.5 0,-2.484 2.016,-4.5 4.5,-4.5 2.485,0 4.5,2.016 4.5,4.5 0,2.486 -2.015,4.5 -4.5,4.5 z"
|
|
95
|
+
inkscape:connector-curvature="0"
|
|
96
|
+
style="fill:#464646;fill-opacity:1" />
|
|
97
|
+
<g
|
|
98
|
+
id="edit"
|
|
99
|
+
style="fill:#464646;fill-opacity:1">
|
|
100
|
+
<path
|
|
101
|
+
d="m 337,30.156 0,0.407 0,5.604 c 0,1.658 -1.344,3 -3,3 l -10,0 c -1.655,0 -3,-1.342 -3,-3 l 0,-10 c 0,-1.657 1.345,-3 3,-3 l 6.345,0 3.19,-3.17 -9.535,0 c -3.313,0 -6,2.687 -6,6 l 0,10 c 0,3.313 2.687,6 6,6 l 10,0 c 3.314,0 6,-2.687 6,-6 l 0,-8.809 -3,2.968"
|
|
102
|
+
id="path4240"
|
|
103
|
+
inkscape:connector-curvature="0"
|
|
104
|
+
style="fill:#464646;fill-opacity:1" />
|
|
105
|
+
<path
|
|
106
|
+
d="m 338.72,24.637 -8.892,8.892 -2.828,0 0,-2.829 8.89,-8.89 z"
|
|
107
|
+
id="path4242"
|
|
108
|
+
inkscape:connector-curvature="0"
|
|
109
|
+
style="fill:#464646;fill-opacity:1" />
|
|
110
|
+
<path
|
|
111
|
+
d="m 338.697,17.826 4,0 0,4 -4,0 z"
|
|
112
|
+
transform="matrix(-0.70698336,-0.70723018,0.70723018,-0.70698336,567.55917,274.78273)"
|
|
113
|
+
id="path4244"
|
|
114
|
+
inkscape:connector-curvature="0"
|
|
115
|
+
style="fill:#464646;fill-opacity:1" />
|
|
116
|
+
</g>
|
|
117
|
+
<g
|
|
118
|
+
id="remove"
|
|
119
|
+
style="fill:#464646;fill-opacity:1">
|
|
120
|
+
<path
|
|
121
|
+
d="m 381,42 18,0 0,-18 -18,0 0,18 z m 14,-16 2,0 0,14 -2,0 0,-14 z m -4,0 2,0 0,14 -2,0 0,-14 z m -4,0 2,0 0,14 -2,0 0,-14 z m -4,0 2,0 0,14 -2,0 0,-14 z"
|
|
122
|
+
id="path4247"
|
|
123
|
+
inkscape:connector-curvature="0"
|
|
124
|
+
style="fill:#464646;fill-opacity:1" />
|
|
125
|
+
<path
|
|
126
|
+
d="m 395,20 0,-4 -10,0 0,4 -6,0 0,2 22,0 0,-2 -6,0 z m -2,0 -6,0 0,-2 6,0 0,2 z"
|
|
127
|
+
id="path4249"
|
|
128
|
+
inkscape:connector-curvature="0"
|
|
129
|
+
style="fill:#464646;fill-opacity:1" />
|
|
130
|
+
</g>
|
|
131
|
+
</g>
|
|
132
|
+
<g
|
|
133
|
+
id="disabled"
|
|
134
|
+
transform="translate(120,0)"
|
|
135
|
+
style="fill:#bbbbbb">
|
|
136
|
+
<use
|
|
137
|
+
xlink:href="#edit"
|
|
138
|
+
id="edit-disabled"
|
|
139
|
+
x="0"
|
|
140
|
+
y="0"
|
|
141
|
+
width="100%"
|
|
142
|
+
height="100%" />
|
|
143
|
+
<use
|
|
144
|
+
xlink:href="#remove"
|
|
145
|
+
id="remove-disabled"
|
|
146
|
+
x="0"
|
|
147
|
+
y="0"
|
|
148
|
+
width="100%"
|
|
149
|
+
height="100%" />
|
|
150
|
+
</g>
|
|
151
|
+
<path
|
|
152
|
+
style="fill:none;stroke:#464646;stroke-width:2;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
|
153
|
+
id="circle-3"
|
|
154
|
+
d="m 581.65725,30 c 0,6.078 -4.926,11 -11,11 -6.074,0 -11,-4.922 -11,-11 0,-6.074 4.926,-11 11,-11 6.074,0 11,4.926 11,11 z"
|
|
155
|
+
inkscape:connector-curvature="0" />
|
|
156
|
+
</svg>
|