@fleetbase/fleetops-engine 0.6.48 → 0.6.49
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/details.hbs +5 -1
- package/addon/components/driver/form.hbs +11 -1
- package/addon/components/fleet-listing-panel.hbs +2 -2
- package/addon/components/fleet-listing-panel.js +10 -0
- package/addon/components/map/google-live-map.js +12 -73
- package/addon/components/map/leaflet-live-map.hbs +149 -33
- package/addon/components/orchestrator/phase-builder.hbs +21 -0
- package/addon/components/orchestrator/phase-builder.js +13 -0
- package/addon/components/orchestrator-workbench.js +1 -0
- package/addon/components/order/form.hbs +2 -2
- package/addon/components/widget/fleet-ops-key-metrics.hbs +19 -17
- package/addon/components/widget/fleet-ops-key-metrics.js +26 -0
- package/addon/components/widget/fuel-efficiency.hbs +26 -0
- package/addon/components/widget/fuel-efficiency.js +43 -0
- package/addon/components/widget/geofence-violations.hbs +44 -0
- package/addon/components/widget/geofence-violations.js +55 -0
- package/addon/components/widget/issues-insights.hbs +43 -0
- package/addon/components/widget/issues-insights.js +62 -0
- package/addon/components/widget/kpi-active-orders.hbs +10 -0
- package/addon/components/widget/kpi-active-orders.js +5 -0
- package/addon/components/widget/kpi-aov.hbs +10 -0
- package/addon/components/widget/kpi-aov.js +5 -0
- package/addon/components/widget/kpi-distance.hbs +10 -0
- package/addon/components/widget/kpi-distance.js +5 -0
- package/addon/components/widget/kpi-drivers-online.hbs +10 -0
- package/addon/components/widget/kpi-drivers-online.js +5 -0
- package/addon/components/widget/kpi-earnings.hbs +10 -0
- package/addon/components/widget/kpi-earnings.js +5 -0
- package/addon/components/widget/kpi-open-issues.hbs +11 -0
- package/addon/components/widget/kpi-open-issues.js +5 -0
- package/addon/components/widget/kpi-tile.hbs +59 -0
- package/addon/components/widget/kpi-tile.js +168 -0
- package/addon/components/widget/live-fleet.hbs +122 -0
- package/addon/components/widget/live-fleet.js +365 -0
- package/addon/components/widget/maintenance-overview.hbs +45 -0
- package/addon/components/widget/maintenance-overview.js +40 -0
- package/addon/components/widget/on-time-delivery.hbs +27 -0
- package/addon/components/widget/on-time-delivery.js +73 -0
- package/addon/components/widget/operations-pulse.hbs +51 -0
- package/addon/components/widget/operations-pulse.js +173 -0
- package/addon/components/widget/orders-by-status.hbs +26 -0
- package/addon/components/widget/orders-by-status.js +54 -0
- package/addon/components/widget/revenue-trend.hbs +34 -0
- package/addon/components/widget/revenue-trend.js +67 -0
- package/addon/components/widget/top-drivers.hbs +44 -0
- package/addon/components/widget/top-drivers.js +54 -0
- package/addon/controllers/settings/routing.js +26 -0
- package/addon/extension.js +172 -3
- package/addon/services/map-adapter/google.js +13 -2
- package/addon/styles/fleetops-engine.css +114 -87
- package/addon/templates/maintenance/equipment/index.hbs +1 -1
- package/addon/templates/maintenance/parts/index.hbs +1 -1
- package/addon/templates/maintenance/schedules/index.hbs +1 -1
- package/addon/templates/management/vehicles/index.hbs +1 -1
- package/addon/templates/settings/avatars.hbs +1 -1
- package/addon/templates/settings/custom-fields.hbs +1 -1
- package/addon/templates/settings/map.hbs +1 -1
- package/addon/templates/settings/navigator-app.hbs +1 -1
- package/addon/templates/settings/notifications.hbs +1 -1
- package/addon/templates/settings/orchestrator.hbs +1 -1
- package/addon/templates/settings/payments/index.hbs +1 -1
- package/addon/templates/settings/payments/onboard.hbs +1 -1
- package/addon/templates/settings/routing.hbs +22 -2
- package/addon/templates/settings/scheduling.hbs +1 -1
- package/addon/utils/live-map-card-content.js +171 -0
- package/app/components/widget/fuel-efficiency.js +1 -0
- package/app/components/widget/geofence-violations.js +1 -0
- package/app/components/widget/issues-insights.js +1 -0
- package/app/components/widget/kpi-active-orders.js +1 -0
- package/app/components/widget/kpi-aov.js +1 -0
- package/app/components/widget/kpi-distance.js +1 -0
- package/app/components/widget/kpi-drivers-online.js +1 -0
- package/app/components/widget/kpi-earnings.js +1 -0
- package/app/components/widget/kpi-open-issues.js +1 -0
- package/app/components/widget/kpi-tile.js +1 -0
- package/app/components/widget/live-fleet.js +1 -0
- package/app/components/widget/maintenance-overview.js +1 -0
- package/app/components/widget/on-time-delivery.js +1 -0
- package/app/components/widget/operations-pulse.js +1 -0
- package/app/components/widget/orders-by-status.js +1 -0
- package/app/components/widget/revenue-trend.js +1 -0
- package/app/components/widget/top-drivers.js +1 -0
- package/app/utils/live-map-card-content.js +1 -0
- package/composer.json +1 -1
- package/extension.json +1 -1
- package/package.json +3 -3
- package/pnpm-workspace.yaml +1 -0
- package/server/config/telematics.php +111 -1
- package/server/migrations/2026_05_18_000001_add_license_expiry_to_drivers_table.php +25 -0
- package/server/seeders/Testing/Concerns/SeedsTestingData.php +183 -0
- package/server/seeders/Testing/ConnectivitySeeder.php +212 -0
- package/server/seeders/Testing/FleetSeeder.php +341 -0
- package/server/seeders/Testing/MaintenanceSeeder.php +238 -0
- package/server/seeders/Testing/NetworkSeeder.php +250 -0
- package/server/seeders/Testing/OrdersSeeder.php +214 -0
- package/server/seeders/Testing/TestingSeeder.php +40 -0
- package/server/seeders/{VroomOrchestrationSeeder.php → Testing/VroomOrchestrationSeeder.php} +8 -1
- package/server/src/Console/Commands/ProcessOperationalAlerts.php +279 -0
- package/server/src/Http/Controllers/Internal/v1/AnalyticsController.php +119 -0
- package/server/src/Http/Controllers/Internal/v1/MetricsController.php +82 -3
- package/server/src/Http/Controllers/Internal/v1/OrderController.php +12 -6
- package/server/src/Http/Controllers/Internal/v1/SettingController.php +46 -1
- package/server/src/Http/Resources/v1/Driver.php +2 -0
- package/server/src/Http/Resources/v1/Fleet.php +4 -0
- package/server/src/Http/Resources/v1/Index/Driver.php +48 -0
- package/server/src/Http/Resources/v1/Index/Vehicle.php +74 -1
- package/server/src/Models/Driver.php +2 -0
- package/server/src/Models/OrderConfig.php +14 -11
- package/server/src/Notifications/LateDeparture.php +48 -0
- package/server/src/Notifications/ProlongedStoppage.php +48 -0
- package/server/src/Notifications/RouteDeviation.php +48 -0
- package/server/src/Orchestration/Engines/VroomOrchestrationEngine.php +28 -1
- package/server/src/Providers/FleetOpsServiceProvider.php +5 -0
- package/server/src/Providers/NotificationServiceProvider.php +3 -0
- package/server/src/Support/Analytics/AbstractAnalytics.php +77 -0
- package/server/src/Support/Analytics/FuelEfficiency.php +83 -0
- package/server/src/Support/Analytics/GeofenceViolations.php +63 -0
- package/server/src/Support/Analytics/IssuesInsights.php +67 -0
- package/server/src/Support/Analytics/LiveFleet.php +127 -0
- package/server/src/Support/Analytics/MaintenanceOverview.php +70 -0
- package/server/src/Support/Analytics/OnTimeDelivery.php +75 -0
- package/server/src/Support/Analytics/OperationsPulse.php +86 -0
- package/server/src/Support/Analytics/OrdersByStatus.php +64 -0
- package/server/src/Support/Analytics/RevenueTrend.php +79 -0
- package/server/src/Support/Analytics/TopDrivers.php +92 -0
- package/server/src/Support/Metrics/AbstractMetric.php +170 -0
- package/server/src/Support/Metrics/ActiveLiveOrdersMetric.php +28 -0
- package/server/src/Support/Metrics/AvgOrderValueMetric.php +53 -0
- package/server/src/Support/Metrics/DriversOnlineMetric.php +30 -0
- package/server/src/Support/Metrics/EarningsMetric.php +30 -0
- package/server/src/Support/Metrics/FuelCostsMetric.php +30 -0
- package/server/src/Support/Metrics/MoneyMetric.php +30 -0
- package/server/src/Support/Metrics/OpenIssuesMetric.php +35 -0
- package/server/src/Support/Metrics/OrdersCanceledMetric.php +35 -0
- package/server/src/Support/Metrics/OrdersCompletedMetric.php +35 -0
- package/server/src/Support/Metrics/OrdersInProgressMetric.php +51 -0
- package/server/src/Support/Metrics/OrdersScheduledMetric.php +37 -0
- package/server/src/Support/Metrics/Registry.php +46 -0
- package/server/src/Support/Metrics/ResolvedIssuesMetric.php +35 -0
- package/server/src/Support/Metrics/TotalCustomersMetric.php +29 -0
- package/server/src/Support/Metrics/TotalDistanceTraveledMetric.php +35 -0
- package/server/src/Support/Metrics/TotalDriversMetric.php +28 -0
- package/server/src/Support/Metrics/TotalTimeTraveledMetric.php +35 -0
- package/server/src/Support/Metrics.php +46 -249
- package/server/src/Support/Telematics/Providers/AfaqyProvider.php +290 -0
- package/server/src/Support/Telematics/Providers/SafeeProvider.php +333 -0
- package/server/src/routes.php +16 -0
- package/server/tests/AnalyticsRoutesTest.php +41 -0
- package/server/tests/MetricsRegistryTest.php +88 -0
- package/server/tests/VroomOrchestrationEngineTest.php +91 -0
- package/translations/en-us.yaml +37 -0
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
@isLoading={{or this.saveSettings.isRunning this.loadSettings.isRunning}}
|
|
10
10
|
/>
|
|
11
11
|
</Layout::Section::Header>
|
|
12
|
-
<Layout::Section::Body class="overflow-y-scroll h-full">
|
|
12
|
+
<Layout::Section::Body class="overflow-y-scroll-i h-full">
|
|
13
13
|
<div class="container mx-auto h-screen">
|
|
14
14
|
<div class="max-w-3xl my-10 mx-auto space-y-6">
|
|
15
15
|
{{#if this.loadSettings.isRunning}}
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
{{/if}}
|
|
12
12
|
</Layout::Section::Header>
|
|
13
13
|
|
|
14
|
-
<Layout::Section::Body class="overflow-y-scroll h-full">
|
|
14
|
+
<Layout::Section::Body class="overflow-y-scroll-i h-full">
|
|
15
15
|
{{#if this.lookupStripeConnectAccount.isRunning}}
|
|
16
16
|
<div class="flex items-center justify-center flex-1 p-4">
|
|
17
17
|
<Spinner @loadingMessage={{t "settings.payments.loading-settings"}} @loadingMessageClass="ml-2 text-black dark:text-white" @wrapperClass="flex flex-row items-center" />
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<Layout::Section::Header @title={{t "payment.settings.onboard.header-title"}} />
|
|
2
2
|
|
|
3
|
-
<Layout::Section::Body class="overflow-y-scroll h-full">
|
|
3
|
+
<Layout::Section::Body class="overflow-y-scroll-i h-full">
|
|
4
4
|
<div class="container">
|
|
5
5
|
<div class="max-w-3xl mx-auto mt-4">
|
|
6
6
|
<div class="content">
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
/>
|
|
11
11
|
</Layout::Section::Header>
|
|
12
12
|
|
|
13
|
-
<Layout::Section::Body class="overflow-y-scroll h-full">
|
|
13
|
+
<Layout::Section::Body class="overflow-y-scroll-i h-full">
|
|
14
14
|
<div class="container mx-auto h-screen">
|
|
15
15
|
<div class="max-w-3xl my-10 mx-auto space-y-6">
|
|
16
16
|
<ContentPanel @title={{t "settings.routing.configure-routing"}} @open={{true}} @wrapperClass="bordered-classic">
|
|
@@ -106,10 +106,30 @@
|
|
|
106
106
|
</div>
|
|
107
107
|
{{/if}}
|
|
108
108
|
</ContentPanel>
|
|
109
|
+
<ContentPanel @title="Operational Alerts" @open={{true}} @wrapperClass="bordered-classic">
|
|
110
|
+
<InputGroup @name="Late Departures" @helpText="Notify when an order has not departed after the scheduled pickup window plus the configured grace period.">
|
|
111
|
+
<Toggle @isToggled={{this.lateDepartureAlertsEnabled}} @onToggle={{fn (mut this.lateDepartureAlertsEnabled)}} />
|
|
112
|
+
</InputGroup>
|
|
113
|
+
<InputGroup @name="Late Departure Grace Period" @helpText="Minutes after scheduled pickup before the order is considered late to depart.">
|
|
114
|
+
<Input @type="number" @value={{this.lateDepartureGracePeriodMinutes}} min="0" max="1440" class="form-input w-full" />
|
|
115
|
+
</InputGroup>
|
|
116
|
+
<InputGroup @name="Route Deviations" @helpText="Notify when tracked vehicle location drifts beyond the configured distance from the active route.">
|
|
117
|
+
<Toggle @isToggled={{this.routeDeviationAlertsEnabled}} @onToggle={{fn (mut this.routeDeviationAlertsEnabled)}} />
|
|
118
|
+
</InputGroup>
|
|
119
|
+
<InputGroup @name="Route Deviation Threshold" @helpText="Meters from the active route before a deviation alert is eligible.">
|
|
120
|
+
<Input @type="number" @value={{this.routeDeviationDistanceThresholdMeters}} min="0" max="100000" class="form-input w-full" />
|
|
121
|
+
</InputGroup>
|
|
122
|
+
<InputGroup @name="Prolonged Stoppages" @helpText="Notify when a vehicle remains stopped beyond the configured duration during an active trip.">
|
|
123
|
+
<Toggle @isToggled={{this.prolongedStoppageAlertsEnabled}} @onToggle={{fn (mut this.prolongedStoppageAlertsEnabled)}} />
|
|
124
|
+
</InputGroup>
|
|
125
|
+
<InputGroup @name="Prolonged Stoppage Duration" @helpText="Minutes stopped before a stoppage alert is eligible.">
|
|
126
|
+
<Input @type="number" @value={{this.prolongedStoppageDurationThresholdMinutes}} min="0" max="1440" class="form-input w-full" />
|
|
127
|
+
</InputGroup>
|
|
128
|
+
</ContentPanel>
|
|
109
129
|
<RegistryYield @registry="fleet-ops:template:settings:routing" as |RegistryComponent|>
|
|
110
130
|
<RegistryComponent @controller={{this}} />
|
|
111
131
|
</RegistryYield>
|
|
112
132
|
</div>
|
|
133
|
+
<Spacer @height="600px" />
|
|
113
134
|
</div>
|
|
114
|
-
<Spacer @height="600px" />
|
|
115
135
|
</Layout::Section::Body>
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
@isLoading={{or this.saveSettings.isRunning this.getSettings.isRunning}}
|
|
10
10
|
/>
|
|
11
11
|
</Layout::Section::Header>
|
|
12
|
-
<Layout::Section::Body class="overflow-y-scroll h-full">
|
|
12
|
+
<Layout::Section::Body class="overflow-y-scroll-i h-full">
|
|
13
13
|
<div class="container mx-auto h-screen">
|
|
14
14
|
<div class="max-w-3xl my-10 mx-auto space-y-6">
|
|
15
15
|
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { isArray } from '@ember/array';
|
|
2
|
+
import { dasherize } from '@ember/string';
|
|
3
|
+
import titleize from 'ember-cli-string-helpers/utils/titleize';
|
|
4
|
+
|
|
5
|
+
function resolveStatusClass(status) {
|
|
6
|
+
const statusKey = dasherize(status ?? 'default');
|
|
7
|
+
|
|
8
|
+
return `status-badge ${statusKey}-status-badge status-badge-${statusKey}`;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function titleizeStatus(status) {
|
|
12
|
+
if (!status) {
|
|
13
|
+
return '-';
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return titleize(`${status}`.replace(/[_-]+/g, ' '));
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function buildStatusBadge(status, label = status) {
|
|
20
|
+
return `
|
|
21
|
+
<div class="${resolveStatusClass(status)} shadow-none ml-auto">
|
|
22
|
+
<span class="rounded-full text-[9px] px-1.5 py-0.5">${titleizeStatus(label)}</span>
|
|
23
|
+
</div>`;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function buildMetaCell(label, value, valueClass = '') {
|
|
27
|
+
const shouldTruncate = valueClass.split(' ').includes('truncate');
|
|
28
|
+
const flowClass = shouldTruncate ? 'min-w-0 w-full' : 'whitespace-normal break-words';
|
|
29
|
+
|
|
30
|
+
return `
|
|
31
|
+
<div class="rounded bg-gray-900 shadow-md px-1.5 py-1 min-w-0">
|
|
32
|
+
<div class="text-[9px] font-semibold uppercase text-gray-400 leading-none">${label}</div>
|
|
33
|
+
<div class="text-[11px] text-white leading-tight ${flowClass} ${valueClass}">${value ?? '-'}</div>
|
|
34
|
+
</div>`;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function buildLiveMapCard(content, framed = false) {
|
|
38
|
+
const frameClasses = framed ? 'relative rounded-lg bg-gray-900 shadow-lg p-1.5 text-white' : '';
|
|
39
|
+
const closeButton = framed ? '<button type="button" class="fleetops-google-popover__close" data-fleetops-google-popover-close aria-label="Close">×</button>' : '';
|
|
40
|
+
|
|
41
|
+
return `<div class="w-[280px] max-w-[calc(100vw-2rem)] ${frameClasses}">${closeButton}${content}</div>`;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function resolveDriverStatus(driver) {
|
|
45
|
+
return driver.meta?.status_label ?? titleizeStatus(driver.status);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function resolveDriverLocation(driver) {
|
|
49
|
+
return driver.meta?.location_coordinates ?? resolveLocationCoordinates(driver.location);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function resolveDriverSpeed(driver) {
|
|
53
|
+
return driver.meta?.speed_label ?? `${driver.speed ?? '-'} km/h`;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function resolveDriverHeading(driver) {
|
|
57
|
+
return driver.meta?.heading_label ?? driver.heading ?? '-';
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function resolveDriverId(driver) {
|
|
61
|
+
return driver.public_id ?? driver.id ?? '-';
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function resolveVehicleNumber(vehicle) {
|
|
65
|
+
return vehicle.internal_id ?? vehicle.public_id ?? vehicle.plate_number ?? vehicle.serial_number ?? vehicle.vin ?? '-';
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function resolveVehicleStatus(vehicle) {
|
|
69
|
+
return vehicle.meta?.status_label ?? vehicle.current_status ?? vehicle.status ?? '-';
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function resolveVehicleLocation(vehicle) {
|
|
73
|
+
return vehicle.meta?.location_coordinates ?? resolveLocationCoordinates(vehicle.location);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function resolveVehicleSpeed(vehicle) {
|
|
77
|
+
return vehicle.meta?.speed_label ?? `${vehicle.speed ?? '-'} km/h`;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function resolveVehicleHeading(vehicle) {
|
|
81
|
+
return vehicle.meta?.heading_label ?? vehicle.heading ?? '-';
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function resolveLocationCoordinates(location) {
|
|
85
|
+
if (!location) {
|
|
86
|
+
return '-';
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (location?.coordinates && isArray(location.coordinates)) {
|
|
90
|
+
const [lng, lat] = location.coordinates;
|
|
91
|
+
return Number.isFinite(lat) && Number.isFinite(lng) ? `${formatCoordinate(lat)} ${formatCoordinate(lng)}` : '-';
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (isArray(location) && location.length >= 2) {
|
|
95
|
+
const [lat, lng] = location;
|
|
96
|
+
return Number.isFinite(lat) && Number.isFinite(lng) ? `${formatCoordinate(lat)} ${formatCoordinate(lng)}` : '-';
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return '-';
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function formatCoordinate(coordinate) {
|
|
103
|
+
return `${Math.round(coordinate * 10000) / 10000}`;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export function buildDriverLiveMapContent(driver, framed = false) {
|
|
107
|
+
const status = resolveDriverStatus(driver);
|
|
108
|
+
const onlineClass = driver.online ? 'bg-green-400' : 'bg-red-500';
|
|
109
|
+
|
|
110
|
+
return buildLiveMapCard(
|
|
111
|
+
`
|
|
112
|
+
<div class="fleetops-google-hover-tooltip__title mb-1.5 flex items-center gap-1.5">
|
|
113
|
+
<span class="inline-block w-2 h-2 rounded-full ${onlineClass}"></span>
|
|
114
|
+
<span>${driver.name ?? '-'}</span>
|
|
115
|
+
${buildStatusBadge(driver.status, status)}
|
|
116
|
+
</div>
|
|
117
|
+
<div class="grid grid-cols-2 gap-1">
|
|
118
|
+
${buildMetaCell('ID', resolveDriverId(driver))}
|
|
119
|
+
${buildMetaCell('Phone', driver.phone ?? '-')}
|
|
120
|
+
${buildMetaCell('Vehicle', driver.vehicle_name ?? '-')}
|
|
121
|
+
${buildMetaCell('Email', driver.email ?? '-', 'truncate')}
|
|
122
|
+
${buildMetaCell('Order', driver.meta?.current_order_reference ?? '-')}
|
|
123
|
+
${buildMetaCell('Speed', resolveDriverSpeed(driver))}
|
|
124
|
+
${buildMetaCell('Heading', resolveDriverHeading(driver))}
|
|
125
|
+
${buildMetaCell('Location', resolveDriverLocation(driver))}
|
|
126
|
+
</div>
|
|
127
|
+
`,
|
|
128
|
+
framed
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export function buildVehicleLiveMapContent(vehicle, framed = false) {
|
|
133
|
+
const status = resolveVehicleStatus(vehicle);
|
|
134
|
+
const onlineClass = vehicle.online ? 'bg-green-400' : 'bg-red-500';
|
|
135
|
+
|
|
136
|
+
return buildLiveMapCard(
|
|
137
|
+
`
|
|
138
|
+
<div class="fleetops-google-hover-tooltip__title mb-1.5 flex items-center gap-1.5">
|
|
139
|
+
<span class="inline-block w-2 h-2 rounded-full ${onlineClass}"></span>
|
|
140
|
+
<span>${vehicle.displayName ?? '-'}</span>
|
|
141
|
+
${buildStatusBadge(vehicle.status, status)}
|
|
142
|
+
</div>
|
|
143
|
+
<div class="grid grid-cols-2 gap-1">
|
|
144
|
+
${buildMetaCell('Vehicle #', resolveVehicleNumber(vehicle))}
|
|
145
|
+
${buildMetaCell('Driver', vehicle.driver_name ?? vehicle.driver?.name ?? '-')}
|
|
146
|
+
${buildMetaCell('Order', vehicle.meta?.current_order_reference ?? '-')}
|
|
147
|
+
${buildMetaCell('Speed', resolveVehicleSpeed(vehicle))}
|
|
148
|
+
${buildMetaCell('Heading', resolveVehicleHeading(vehicle))}
|
|
149
|
+
${buildMetaCell('Location', resolveVehicleLocation(vehicle))}
|
|
150
|
+
</div>
|
|
151
|
+
`,
|
|
152
|
+
framed
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export function buildPlaceInfoWindowContent(place) {
|
|
157
|
+
return `
|
|
158
|
+
<div class="fleetops-google-popover fleetops-google-popover--compact">
|
|
159
|
+
<div class="fleetops-google-popover__body">
|
|
160
|
+
<div class="fleetops-google-popover__title">${place.name ?? place.address ?? '-'}</div>
|
|
161
|
+
<div class="fleetops-google-popover__meta">${place.address ?? ''}</div>
|
|
162
|
+
</div>
|
|
163
|
+
</div>`;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export function buildPlaceTooltipContent(place) {
|
|
167
|
+
return `
|
|
168
|
+
<div class="fleetops-google-hover-tooltip__title">${place.name ?? place.address ?? '-'}</div>
|
|
169
|
+
<div class="fleetops-google-hover-tooltip__meta">${place.address ?? ''}</div>
|
|
170
|
+
`;
|
|
171
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '@fleetbase/fleetops-engine/components/widget/fuel-efficiency';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '@fleetbase/fleetops-engine/components/widget/geofence-violations';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '@fleetbase/fleetops-engine/components/widget/issues-insights';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '@fleetbase/fleetops-engine/components/widget/kpi-active-orders';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '@fleetbase/fleetops-engine/components/widget/kpi-aov';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '@fleetbase/fleetops-engine/components/widget/kpi-distance';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '@fleetbase/fleetops-engine/components/widget/kpi-drivers-online';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '@fleetbase/fleetops-engine/components/widget/kpi-earnings';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '@fleetbase/fleetops-engine/components/widget/kpi-open-issues';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '@fleetbase/fleetops-engine/components/widget/kpi-tile';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '@fleetbase/fleetops-engine/components/widget/live-fleet';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '@fleetbase/fleetops-engine/components/widget/maintenance-overview';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '@fleetbase/fleetops-engine/components/widget/on-time-delivery';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '@fleetbase/fleetops-engine/components/widget/operations-pulse';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '@fleetbase/fleetops-engine/components/widget/orders-by-status';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '@fleetbase/fleetops-engine/components/widget/revenue-trend';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '@fleetbase/fleetops-engine/components/widget/top-drivers';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from '@fleetbase/fleetops-engine/utils/live-map-card-content';
|
package/composer.json
CHANGED
package/extension.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fleetbase/fleetops-engine",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.49",
|
|
4
4
|
"description": "Fleet & Transport Management Extension for Fleetbase",
|
|
5
5
|
"fleetbase": {
|
|
6
6
|
"route": "fleet-ops"
|
|
@@ -43,8 +43,8 @@
|
|
|
43
43
|
"dependencies": {
|
|
44
44
|
"@babel/core": "^7.23.2",
|
|
45
45
|
"@fleetbase/ember-core": "^0.3.19",
|
|
46
|
-
"@fleetbase/ember-ui": "^0.3.
|
|
47
|
-
"@fleetbase/fleetops-data": "^0.1.
|
|
46
|
+
"@fleetbase/ember-ui": "^0.3.30",
|
|
47
|
+
"@fleetbase/fleetops-data": "^0.1.33",
|
|
48
48
|
"@fleetbase/leaflet-routing-machine": "^3.2.17",
|
|
49
49
|
"@fortawesome/ember-fontawesome": "^2.0.0",
|
|
50
50
|
"@fortawesome/fontawesome-svg-core": "6.4.0",
|
package/pnpm-workspace.yaml
CHANGED
|
@@ -106,6 +106,116 @@ return [
|
|
|
106
106
|
'pagination' => 'cursor',
|
|
107
107
|
],
|
|
108
108
|
],
|
|
109
|
+
|
|
110
|
+
[
|
|
111
|
+
'key' => 'afaqy',
|
|
112
|
+
'label' => 'AFAQY',
|
|
113
|
+
'type' => 'native',
|
|
114
|
+
'driver_class' => \Fleetbase\FleetOps\Support\Telematics\Providers\AfaqyProvider::class,
|
|
115
|
+
'icon' => 'https://api.afaqy.sa/docs/img/favicon.ico',
|
|
116
|
+
'description' => 'AFAQY AVL REST integration for unit discovery, live position, speed, heading, odometer, fuel, and signal data.',
|
|
117
|
+
'docs_url' => 'https://api.afaqy.sa/docs/',
|
|
118
|
+
'required_fields' => [
|
|
119
|
+
[
|
|
120
|
+
'name' => 'base_url',
|
|
121
|
+
'label' => 'Base URL',
|
|
122
|
+
'type' => 'text',
|
|
123
|
+
'placeholder' => 'https://api.afaqy.sa',
|
|
124
|
+
'required' => false,
|
|
125
|
+
'validation' => 'nullable|url',
|
|
126
|
+
],
|
|
127
|
+
[
|
|
128
|
+
'name' => 'username',
|
|
129
|
+
'label' => 'Username',
|
|
130
|
+
'type' => 'text',
|
|
131
|
+
'placeholder' => 'Enter your AFAQY username',
|
|
132
|
+
'required' => false,
|
|
133
|
+
'validation' => 'required_without:token|string',
|
|
134
|
+
],
|
|
135
|
+
[
|
|
136
|
+
'name' => 'password',
|
|
137
|
+
'label' => 'Password',
|
|
138
|
+
'type' => 'password',
|
|
139
|
+
'placeholder' => 'Enter your AFAQY password',
|
|
140
|
+
'required' => false,
|
|
141
|
+
'validation' => 'required_without:token|string',
|
|
142
|
+
],
|
|
143
|
+
[
|
|
144
|
+
'name' => 'token',
|
|
145
|
+
'label' => 'Token',
|
|
146
|
+
'type' => 'password',
|
|
147
|
+
'placeholder' => 'Use an existing AFAQY token',
|
|
148
|
+
'required' => false,
|
|
149
|
+
'validation' => 'required_without:username|string',
|
|
150
|
+
],
|
|
151
|
+
],
|
|
152
|
+
'supports_webhooks' => false,
|
|
153
|
+
'supports_discovery' => true,
|
|
154
|
+
'metadata' => [
|
|
155
|
+
'rate_limit' => 60,
|
|
156
|
+
'pagination' => 'offset',
|
|
157
|
+
'capabilities' => ['tracking', 'speed', 'heading', 'odometer', 'fuel_level', 'ignition'],
|
|
158
|
+
],
|
|
159
|
+
],
|
|
160
|
+
|
|
161
|
+
[
|
|
162
|
+
'key' => 'safee',
|
|
163
|
+
'label' => 'Safee Tracking',
|
|
164
|
+
'type' => 'native',
|
|
165
|
+
'driver_class' => \Fleetbase\FleetOps\Support\Telematics\Providers\SafeeProvider::class,
|
|
166
|
+
'description' => 'Safee Tracking REST integration for vehicle discovery, last state, live positions, odometer, fuel, and sensor data.',
|
|
167
|
+
'required_fields' => [
|
|
168
|
+
[
|
|
169
|
+
'name' => 'server_uri',
|
|
170
|
+
'label' => 'Server URI',
|
|
171
|
+
'type' => 'text',
|
|
172
|
+
'placeholder' => 'https://tracking.example.com',
|
|
173
|
+
'required' => true,
|
|
174
|
+
'validation' => 'required|url',
|
|
175
|
+
],
|
|
176
|
+
[
|
|
177
|
+
'name' => 'realm_id',
|
|
178
|
+
'label' => 'Realm ID',
|
|
179
|
+
'type' => 'text',
|
|
180
|
+
'placeholder' => 'Enter Safee realm ID',
|
|
181
|
+
'required' => true,
|
|
182
|
+
],
|
|
183
|
+
[
|
|
184
|
+
'name' => 'client_id',
|
|
185
|
+
'label' => 'Client ID',
|
|
186
|
+
'type' => 'text',
|
|
187
|
+
'placeholder' => 'Enter Safee client ID',
|
|
188
|
+
'required' => true,
|
|
189
|
+
],
|
|
190
|
+
[
|
|
191
|
+
'name' => 'client_secret',
|
|
192
|
+
'label' => 'Client Secret',
|
|
193
|
+
'type' => 'password',
|
|
194
|
+
'placeholder' => 'Enter Safee client secret',
|
|
195
|
+
'required' => true,
|
|
196
|
+
],
|
|
197
|
+
[
|
|
198
|
+
'name' => 'username',
|
|
199
|
+
'label' => 'Username',
|
|
200
|
+
'type' => 'text',
|
|
201
|
+
'placeholder' => 'Enter Safee username',
|
|
202
|
+
'required' => true,
|
|
203
|
+
],
|
|
204
|
+
[
|
|
205
|
+
'name' => 'password',
|
|
206
|
+
'label' => 'Password',
|
|
207
|
+
'type' => 'password',
|
|
208
|
+
'placeholder' => 'Enter Safee password',
|
|
209
|
+
'required' => true,
|
|
210
|
+
],
|
|
211
|
+
],
|
|
212
|
+
'supports_webhooks' => false,
|
|
213
|
+
'supports_discovery' => true,
|
|
214
|
+
'metadata' => [
|
|
215
|
+
'rate_limit' => 3000,
|
|
216
|
+
'pagination' => 'none',
|
|
217
|
+
'capabilities' => ['tracking', 'speed', 'heading', 'odometer', 'fuel_level', 'ignition'],
|
|
218
|
+
],
|
|
219
|
+
],
|
|
109
220
|
]
|
|
110
221
|
];
|
|
111
|
-
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
use Illuminate\Database\Migrations\Migration;
|
|
4
|
+
use Illuminate\Database\Schema\Blueprint;
|
|
5
|
+
use Illuminate\Support\Facades\Schema;
|
|
6
|
+
|
|
7
|
+
return new class extends Migration {
|
|
8
|
+
public function up()
|
|
9
|
+
{
|
|
10
|
+
Schema::table('drivers', function (Blueprint $table) {
|
|
11
|
+
if (!Schema::hasColumn('drivers', 'license_expiry')) {
|
|
12
|
+
$table->date('license_expiry')->nullable()->after('drivers_license_number')->index();
|
|
13
|
+
}
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
public function down()
|
|
18
|
+
{
|
|
19
|
+
Schema::table('drivers', function (Blueprint $table) {
|
|
20
|
+
if (Schema::hasColumn('drivers', 'license_expiry')) {
|
|
21
|
+
$table->dropColumn('license_expiry');
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
};
|