@fleetbase/fleetops-engine 0.6.50 → 0.6.51
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/README.md +185 -67
- package/addon/components/contact/panel-header.hbs +11 -48
- package/addon/components/customer/details.hbs +1 -2
- package/addon/components/customer/form.hbs +63 -51
- package/addon/components/customer/form.js +27 -1
- package/addon/components/customer/orders.hbs +48 -0
- package/addon/components/customer/orders.js +67 -5
- package/addon/components/driver/form.hbs +1 -1
- package/addon/components/home/getting-started-guidance.hbs +113 -0
- package/addon/components/home/getting-started-guidance.js +107 -0
- package/addon/components/issue/details/comments.hbs +3 -0
- package/addon/components/issue/details/comments.js +3 -0
- package/addon/components/issue/details/correspondence.hbs +9 -0
- package/addon/components/issue/details/correspondence.js +83 -0
- package/addon/components/issue/details/documents.hbs +69 -0
- package/addon/components/issue/details/documents.js +85 -0
- package/addon/components/issue/details.hbs +220 -59
- package/addon/components/issue/details.js +245 -0
- package/addon/components/issue/form.hbs +19 -1
- package/addon/components/issue/panel-header.hbs +43 -0
- package/addon/components/issue/panel-header.js +22 -0
- package/addon/components/issue/timeline.hbs +40 -0
- package/addon/components/issue/timeline.js +45 -0
- package/addon/components/leaflet-tracking-marker.js +3 -0
- package/addon/components/map/drawer/position-listing.js +3 -0
- package/addon/components/map/leaflet-live-map.hbs +2 -2
- package/addon/components/map/leaflet-live-map.js +17 -0
- package/addon/components/map/order-list-overlay.hbs +4 -4
- package/addon/components/modals/convert-customer-to-vendor.hbs +30 -0
- package/addon/components/modals/convert-customer-to-vendor.js +55 -0
- package/addon/components/modals/issue-assign.hbs +20 -0
- package/addon/components/modals/issue-assign.js +49 -0
- package/addon/components/modals/issue-close.hbs +30 -0
- package/addon/components/modals/issue-close.js +73 -0
- package/addon/components/modals/issue-status.hbs +20 -0
- package/addon/components/modals/issue-status.js +43 -0
- package/addon/components/modals/reset-customer-credentials.js +12 -6
- package/addon/components/modals/update-order-activity.hbs +51 -17
- package/addon/components/modals/update-order-activity.js +59 -5
- package/addon/components/order/details/activity.js +2 -2
- package/addon/components/order/details/proof.hbs +40 -38
- package/addon/components/order/details/proof.js +31 -4
- package/addon/components/order/details.hbs +5 -5
- package/addon/components/order/details.js +19 -1
- package/addon/components/order/header.hbs +1 -1
- package/addon/components/route-list.hbs +0 -12
- package/addon/components/service-rate/details.hbs +1 -1
- package/addon/components/service-rate/form.hbs +1 -1
- package/addon/components/service-rate/form.js +4 -0
- package/addon/components/vendor/personnel-manager.hbs +166 -0
- package/addon/components/vendor/personnel-manager.js +143 -0
- package/addon/components/widget/live-fleet.hbs +1 -0
- package/addon/components/widget/live-fleet.js +2 -7
- package/addon/components/widget/revenue-trend.hbs +1 -1
- package/addon/components/widget/revenue-trend.js +75 -2
- package/addon/controllers/management/contacts/customers/details.js +31 -13
- package/addon/controllers/management/contacts/customers.js +16 -5
- package/addon/controllers/management/contacts/index/details.js +31 -13
- package/addon/controllers/management/contacts/index.js +7 -0
- package/addon/controllers/management/issues/index/details.js +52 -6
- package/addon/controllers/management/vendors/index/details.js +4 -0
- package/addon/controllers/operations/orders/index/details.js +11 -3
- package/addon/controllers/operations/orders/index.js +1 -1
- package/addon/extension.js +19 -12
- package/addon/helpers/format-duration.js +1 -38
- package/addon/initializers/load-leaflet-assets.js +5 -6
- package/addon/initializers/patch-ember-leaflet-tooltip-layer.js +51 -0
- package/addon/routes/management/issues/index/details.js +1 -1
- package/addon/routes.js +1 -0
- package/addon/services/contact-actions.js +231 -1
- package/addon/services/getting-started.js +44 -0
- package/addon/services/issue-actions.js +71 -0
- package/addon/services/leaflet-map-manager.js +2 -0
- package/addon/services/map-adapter/leaflet.js +19 -0
- package/addon/services/service-areas.js +3 -0
- package/addon/styles/fleetops-engine.css +1036 -0
- package/addon/templates/management/contacts/customers/details.hbs +1 -1
- package/addon/templates/management/contacts/index/details.hbs +1 -1
- package/addon/templates/management/contacts.hbs +2 -2
- package/addon/templates/management/issues/index/details/index.hbs +3 -1
- package/addon/templates/management/issues/index/details.hbs +3 -2
- package/addon/templates/management/vendors/index/details/personnel.hbs +1 -2
- package/addon/templates/operations/orders/index/details/index.hbs +2 -2
- package/addon/templates/operations/orders/index.hbs +1 -1
- package/addon/templates/operations/service-rates/index/edit.hbs +1 -0
- package/addon/templates/operations/service-rates/index/new.hbs +1 -0
- package/addon/templates/settings/payments/onboard.hbs +8 -8
- package/addon/utils/leaflet-draw-namespace-guard.js +22 -0
- package/addon/utils/leaflet-plugin-loader.js +219 -0
- package/addon/utils/setup-customer-portal.js +4 -64
- package/app/components/home/getting-started-guidance.js +1 -0
- package/app/components/issue/details/comments.js +1 -0
- package/app/components/issue/details/correspondence.js +1 -0
- package/app/components/issue/details/documents.js +1 -0
- package/app/components/issue/panel-header.js +1 -0
- package/app/components/modals/convert-customer-to-vendor.js +1 -0
- package/app/components/modals/issue-assign.js +1 -0
- package/app/components/modals/issue-close.js +1 -0
- package/app/components/modals/issue-status.js +1 -0
- package/app/components/vendor/personnel-manager.js +1 -0
- package/app/helpers/format-duration.js +1 -1
- package/app/services/getting-started.js +1 -0
- package/app/utils/leaflet-draw-namespace-guard.js +1 -0
- package/app/utils/leaflet-plugin-loader.js +1 -0
- package/composer.json +1 -1
- package/extension.json +1 -1
- package/package.json +5 -5
- package/server/migrations/2026_05_31_000001_add_portal_fields_to_vendor_personnels_table.php +30 -0
- package/server/migrations/2026_06_03_000000_ensure_tags_column_on_issues_table.php +31 -0
- package/server/migrations/2026_06_03_000001_add_order_uuid_to_issues_table.php +25 -0
- package/server/migrations/2026_06_04_000001_backfill_order_transaction_subjects.php +39 -0
- package/server/migrations/2026_06_04_000002_backfill_order_transaction_directions.php +24 -0
- package/server/resources/views/mail/customer-credentials.blade.php +16 -13
- package/server/src/Expansions/UserFilterExpansion.php +28 -0
- package/server/src/Http/Controllers/Api/v1/IssueController.php +2 -0
- package/server/src/Http/Controllers/Api/v1/OrderController.php +26 -25
- package/server/src/Http/Controllers/Api/v1/PlaceController.php +10 -58
- package/server/src/Http/Controllers/Internal/v1/ContactController.php +135 -0
- package/server/src/Http/Controllers/Internal/v1/CustomerController.php +116 -6
- package/server/src/Http/Controllers/Internal/v1/GettingStartedController.php +17 -0
- package/server/src/Http/Controllers/Internal/v1/IssueController.php +251 -0
- package/server/src/Http/Controllers/Internal/v1/OrderController.php +172 -97
- package/server/src/Http/Controllers/Internal/v1/PlaceController.php +10 -68
- package/server/src/Http/Controllers/Internal/v1/VendorController.php +89 -0
- package/server/src/Http/Requests/CreateIssueRequest.php +4 -0
- package/server/src/Http/Requests/UpdateIssueRequest.php +4 -0
- package/server/src/Http/Resources/v1/Index/Order.php +12 -9
- package/server/src/Http/Resources/v1/Issue.php +25 -0
- package/server/src/Http/Resources/v1/Order.php +20 -18
- package/server/src/Mail/CustomerCredentialsMail.php +5 -6
- package/server/src/Models/Contact.php +30 -51
- package/server/src/Models/Issue.php +47 -6
- package/server/src/Models/Order.php +33 -6
- package/server/src/Models/ServiceArea.php +1 -1
- package/server/src/Models/VendorPersonnel.php +10 -1
- package/server/src/Models/Zone.php +1 -1
- package/server/src/Observers/PurchaseRateObserver.php +20 -0
- package/server/src/Observers/ZoneObserver.php +56 -0
- package/server/src/Providers/FleetOpsServiceProvider.php +1 -0
- package/server/src/Support/Analytics/TopDrivers.php +9 -1
- package/server/src/Support/GettingStarted.php +144 -0
- package/server/src/Support/PlaceSearch.php +282 -0
- package/server/src/Support/ResolvesOrderServiceStops.php +22 -12
- package/server/src/routes.php +33 -1
- package/server/tests/AnalyticsRoutesTest.php +8 -0
- package/server/tests/CustomerContactIdentitySafetyTest.php +41 -0
- package/server/tests/DriverVehicleStatusDefaultTest.php +2 -2
- package/server/tests/GettingStartedStatusTest.php +21 -0
- package/server/tests/OrderActivityFlowRegressionTest.php +116 -0
- package/server/tests/ServiceAreaZoneApiShapeTest.php +44 -0
- package/translations/ar-ae.yml +9 -0
- package/translations/bg-bg.yaml +9 -0
- package/translations/en-us.yaml +11 -0
- package/translations/es-pa.yaml +9 -0
- package/translations/fr-fr.yaml +9 -0
- package/translations/mn-mn.yaml +9 -0
- package/translations/pt-br.yaml +9 -0
- package/translations/ru-ru.yaml +9 -0
- package/translations/vi-vn.yaml +9 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<Layout::Resource::Panel
|
|
2
2
|
@resource={{@model}}
|
|
3
3
|
@controller={{this}}
|
|
4
|
-
@headerComponent={{component "contact/panel-header" resource=@model}}
|
|
4
|
+
@headerComponent={{component "contact/panel-header" resource=@model actionButtons=this.actionButtons}}
|
|
5
5
|
@headerTitle={{or @model.name}}
|
|
6
6
|
@actionButtons={{this.actionButtons}}
|
|
7
7
|
@onPressCancel={{transition-to "management.contacts.customers"}}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<Layout::Resource::Panel
|
|
2
2
|
@resource={{@model}}
|
|
3
3
|
@controller={{this}}
|
|
4
|
-
@headerComponent={{component "contact/panel-header" resource=@model}}
|
|
4
|
+
@headerComponent={{component "contact/panel-header" resource=@model actionButtons=this.actionButtons}}
|
|
5
5
|
@headerTitle={{or @model.name}}
|
|
6
6
|
@actionButtons={{this.actionButtons}}
|
|
7
7
|
@onPressCancel={{transition-to "management.contacts.index"}}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<TabNavigation @tabs={{this.tabs}} @size="lg" @tablistClass="next-view-section-subheader-fixed-height pl-2">
|
|
1
|
+
<TabNavigation @tabs={{this.tabs}} @size="lg" @containerClass="tab-navigation-fill" @tablistClass="next-view-section-subheader-fixed-height pl-2">
|
|
2
2
|
<:actions>
|
|
3
3
|
<div class="flex flex-row items-center space-x-1 pr-4">
|
|
4
4
|
<Layout::Resource::TabularActions
|
|
@@ -16,4 +16,4 @@
|
|
|
16
16
|
<:default>
|
|
17
17
|
{{outlet}}
|
|
18
18
|
</:default>
|
|
19
|
-
</TabNavigation>
|
|
19
|
+
</TabNavigation>
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
<Layout::Resource::Panel
|
|
2
2
|
@resource={{@model}}
|
|
3
3
|
@controller={{this}}
|
|
4
|
-
@
|
|
4
|
+
@headerComponent={{component "issue/panel-header" resource=@model}}
|
|
5
5
|
@actionButtons={{this.actionButtons}}
|
|
6
6
|
@onPressCancel={{transition-to "management.issues.index"}}
|
|
7
7
|
@onOverlayReady={{fn (mut this.overlay)}}
|
|
8
8
|
@headerClass="no-bottom-border"
|
|
9
9
|
@bodyClass="no-scroll"
|
|
10
|
+
@width="800px"
|
|
10
11
|
>
|
|
11
12
|
<TabNavigation @tabs={{this.tabs}} @contentClass="scrollable" @tablistClass="pl-2">
|
|
12
13
|
{{outlet}}
|
|
13
14
|
</TabNavigation>
|
|
14
|
-
</Layout::Resource::Panel>
|
|
15
|
+
</Layout::Resource::Panel>
|
|
@@ -1,2 +1 @@
|
|
|
1
|
-
{{
|
|
2
|
-
{{outlet}}
|
|
1
|
+
<Vendor::PersonnelManager @vendor={{@model}} />
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
<Order::Details @resource={{@model}} @onActivityChanged={{perform this.parent.refresh}} @isLoadingActivity={{this.parent.refresh.isRunning}} />
|
|
2
|
-
<Spacer @height="300px" />
|
|
1
|
+
<Order::Details @resource={{@model}} @onActivityChanged={{perform this.parent.refresh}} @proofReloadToken={{this.parent.proofReloadToken}} @isLoadingActivity={{this.parent.refresh.isRunning}} />
|
|
2
|
+
<Spacer @height="300px" />
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
@bulkSearchValue={{this.bulk_query}}
|
|
37
37
|
@onClearBulkSearch={{fn (mut this.bulk_query)}}
|
|
38
38
|
@onSubmitBulkSearch={{fn (mut this.bulk_query)}}
|
|
39
|
-
@bulkSearchPlaceholder={{t "
|
|
39
|
+
@bulkSearchPlaceholder={{t "order.prompts.order-bulk-search"}}
|
|
40
40
|
@data={{@model}}
|
|
41
41
|
@columns={{this.columns}}
|
|
42
42
|
@page={{this.page}}
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
@saveTask={{this.save}}
|
|
7
7
|
@onPressCancel={{transition-to "operations.service-rates.index"}}
|
|
8
8
|
@onOverlayReady={{fn (mut this.overlay)}}
|
|
9
|
+
@maxResizeWidth={{1000}}
|
|
9
10
|
>
|
|
10
11
|
<ServiceRate::Form @resource={{this.serviceRate}} @controller={{this}} />
|
|
11
12
|
<Spacer @height="200px" />
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<Layout::Section::Header @title={{t "
|
|
1
|
+
<Layout::Section::Header @title={{t "settings.payments.onboard.header-title"}} />
|
|
2
2
|
|
|
3
3
|
<Layout::Section::Body class="overflow-y-scroll-i h-full">
|
|
4
4
|
<div class="container">
|
|
@@ -13,16 +13,16 @@
|
|
|
13
13
|
>
|
|
14
14
|
<h2 id="title" class="text-base">
|
|
15
15
|
{{#if this.onboardCompleted}}
|
|
16
|
-
{{t "
|
|
16
|
+
{{t "settings.payments.onboard.title-completed"}}
|
|
17
17
|
{{else}}
|
|
18
|
-
{{t "
|
|
18
|
+
{{t "settings.payments.onboard.title-incomplete"}}
|
|
19
19
|
{{/if}}
|
|
20
20
|
</h2>
|
|
21
21
|
<span id="subtitle">
|
|
22
22
|
{{#if this.onboardCompleted}}
|
|
23
|
-
{{t "
|
|
23
|
+
{{t "settings.payments.onboard.subtitle-completed"}}
|
|
24
24
|
{{else}}
|
|
25
|
-
{{t "
|
|
25
|
+
{{t "settings.payments.onboard.subtitle-incomplete"}}
|
|
26
26
|
{{/if}}
|
|
27
27
|
</span>
|
|
28
28
|
<div class="mt-4 mb-1">
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
@icon="check"
|
|
32
32
|
@type="primary"
|
|
33
33
|
@size="lg"
|
|
34
|
-
@text={{t "
|
|
34
|
+
@text={{t "settings.payments.onboard.button-continue"}}
|
|
35
35
|
@onClick={{transition-to "settings.payments.index"}}
|
|
36
36
|
@disabled={{this.onboardInProgress}}
|
|
37
37
|
/>
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
@icon="play"
|
|
41
41
|
@type="primary"
|
|
42
42
|
@size="lg"
|
|
43
|
-
@text={{if this.onboardInProgress (t "
|
|
43
|
+
@text={{if this.onboardInProgress (t "settings.payments.onboard.button-in-progress") (t "settings.payments.onboard.button-start")}}
|
|
44
44
|
@onClick={{perform this.startOnboard}}
|
|
45
45
|
@isLoading={{this.startOnboard.isRunning}}
|
|
46
46
|
@disabled={{this.onboardInProgress}}
|
|
@@ -59,4 +59,4 @@
|
|
|
59
59
|
</div>
|
|
60
60
|
</div>
|
|
61
61
|
</div>
|
|
62
|
-
</Layout::Section::Body>
|
|
62
|
+
</Layout::Section::Body>
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export function ensureLeafletDrawEditNamespace(leaflet = null) {
|
|
2
|
+
const leaflets = [];
|
|
3
|
+
|
|
4
|
+
if (leaflet) {
|
|
5
|
+
leaflets.push(leaflet);
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
if (typeof window !== 'undefined') {
|
|
9
|
+
leaflets.push(window.leaflet, window.L);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const guarded = [];
|
|
13
|
+
for (const L of leaflets) {
|
|
14
|
+
if (!L || guarded.includes(L)) continue;
|
|
15
|
+
L.Edit = L.Edit || {};
|
|
16
|
+
guarded.push(L);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return guarded;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export default ensureLeafletDrawEditNamespace;
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import { debug } from '@ember/debug';
|
|
2
|
+
import ensureLeafletDrawEditNamespace from './leaflet-draw-namespace-guard';
|
|
3
|
+
|
|
4
|
+
const DEFAULT_BASE_PATH = 'engines-dist/leaflet';
|
|
5
|
+
const DEFAULT_SCRIPTS = ['leaflet.contextmenu.js', 'leaflet.draw-src.js'];
|
|
6
|
+
const DEFAULT_STYLESHEETS = ['leaflet.contextmenu.css', 'leaflet.draw.css'];
|
|
7
|
+
const SCRIPT_FLAG = 'fleetopsLeafletPluginLoaded';
|
|
8
|
+
const STYLESHEET_FLAG = 'fleetopsLeafletPluginStylesheet';
|
|
9
|
+
|
|
10
|
+
let pluginReadyPromise = null;
|
|
11
|
+
|
|
12
|
+
function normalizePath(path = '') {
|
|
13
|
+
return `/${path.replace(/^\/+/, '')}`;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function assetPath(basePath, assetName) {
|
|
17
|
+
return normalizePath(`${basePath ? `${basePath}/` : ''}${assetName}`);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function findAssetElement(tagName, srcAttribute, src) {
|
|
21
|
+
return Array.from(document.getElementsByTagName(tagName)).find((element) => {
|
|
22
|
+
const currentSrc = element.getAttribute(srcAttribute);
|
|
23
|
+
if (currentSrc === src) {
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
try {
|
|
28
|
+
return new URL(element[srcAttribute], window.location.origin).pathname === src;
|
|
29
|
+
} catch {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function normalizeLeafletGlobal() {
|
|
36
|
+
if (typeof window === 'undefined') {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const leaflet = window.L || window.leaflet;
|
|
41
|
+
if (!leaflet) {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
window.L = leaflet;
|
|
46
|
+
window.leaflet = leaflet;
|
|
47
|
+
ensureLeafletDrawEditNamespace(leaflet);
|
|
48
|
+
return leaflet;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function hasLeafletDrawPlugins(leaflet = normalizeLeafletGlobal()) {
|
|
52
|
+
return Boolean(leaflet?.Edit?.Marker && leaflet?.Edit?.Poly && leaflet?.Control?.Draw);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function hasLeafletContextmenuPlugin(leaflet = normalizeLeafletGlobal()) {
|
|
56
|
+
return Boolean(leaflet?.Map?.ContextMenu);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function hasLeafletPluginsReady() {
|
|
60
|
+
const leaflet = normalizeLeafletGlobal();
|
|
61
|
+
return Boolean(leaflet && hasLeafletDrawPlugins(leaflet) && hasLeafletContextmenuPlugin(leaflet));
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function waitForLeafletGlobal({ timeoutMs = 8000 } = {}) {
|
|
65
|
+
const leaflet = normalizeLeafletGlobal();
|
|
66
|
+
if (leaflet) {
|
|
67
|
+
return Promise.resolve(leaflet);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return new Promise((resolve, reject) => {
|
|
71
|
+
const startedAt = Date.now();
|
|
72
|
+
const interval = setInterval(() => {
|
|
73
|
+
const leaflet = normalizeLeafletGlobal();
|
|
74
|
+
if (leaflet) {
|
|
75
|
+
clearInterval(interval);
|
|
76
|
+
resolve(leaflet);
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (timeoutMs != null && Date.now() - startedAt >= timeoutMs) {
|
|
81
|
+
clearInterval(interval);
|
|
82
|
+
reject(new Error('[FleetOps Leaflet] Leaflet global is not available'));
|
|
83
|
+
}
|
|
84
|
+
}, 50);
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function appendStylesheet(href) {
|
|
89
|
+
if (typeof document === 'undefined') {
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const existing = findAssetElement('link', 'href', href);
|
|
94
|
+
if (existing) {
|
|
95
|
+
existing.dataset[STYLESHEET_FLAG] = 'true';
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const link = document.createElement('link');
|
|
100
|
+
link.rel = 'stylesheet';
|
|
101
|
+
link.href = href;
|
|
102
|
+
link.dataset[STYLESHEET_FLAG] = 'true';
|
|
103
|
+
document.head.appendChild(link);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function loadScript(src, { timeoutMs = 8000, isReady = null } = {}) {
|
|
107
|
+
if (typeof document === 'undefined') {
|
|
108
|
+
return Promise.reject(new Error('[FleetOps Leaflet] document is not available'));
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const existing = findAssetElement('script', 'src', src);
|
|
112
|
+
if (existing?.dataset[SCRIPT_FLAG] === 'true' || isReady?.()) {
|
|
113
|
+
if (existing) {
|
|
114
|
+
existing.dataset[SCRIPT_FLAG] = 'true';
|
|
115
|
+
}
|
|
116
|
+
return Promise.resolve(existing);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return new Promise((resolve, reject) => {
|
|
120
|
+
const script = existing ?? document.createElement('script');
|
|
121
|
+
let timeoutId;
|
|
122
|
+
|
|
123
|
+
const cleanup = () => {
|
|
124
|
+
script.removeEventListener('load', onLoad);
|
|
125
|
+
script.removeEventListener('error', onError);
|
|
126
|
+
if (timeoutId) {
|
|
127
|
+
clearTimeout(timeoutId);
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
const onLoad = () => {
|
|
132
|
+
cleanup();
|
|
133
|
+
script.dataset[SCRIPT_FLAG] = 'true';
|
|
134
|
+
normalizeLeafletGlobal();
|
|
135
|
+
resolve(script);
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
const onError = () => {
|
|
139
|
+
cleanup();
|
|
140
|
+
reject(new Error(`[FleetOps Leaflet] Failed to load ${src}`));
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
script.addEventListener('load', onLoad);
|
|
144
|
+
script.addEventListener('error', onError);
|
|
145
|
+
|
|
146
|
+
if (timeoutMs != null) {
|
|
147
|
+
timeoutId = setTimeout(() => {
|
|
148
|
+
cleanup();
|
|
149
|
+
reject(new Error(`[FleetOps Leaflet] Timed out loading ${src}`));
|
|
150
|
+
}, timeoutMs);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (!existing) {
|
|
154
|
+
script.src = src;
|
|
155
|
+
script.async = false;
|
|
156
|
+
script.dataset.fleetopsLeafletPlugin = 'true';
|
|
157
|
+
document.body.appendChild(script);
|
|
158
|
+
}
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export function resetLeafletPluginLoaderForTesting() {
|
|
163
|
+
pluginReadyPromise = null;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function pluginReadyCheck(script) {
|
|
167
|
+
if (script.includes('contextmenu')) {
|
|
168
|
+
return () => hasLeafletContextmenuPlugin();
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (script.includes('draw')) {
|
|
172
|
+
return () => hasLeafletDrawPlugins();
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return null;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export default function ensureLeafletPluginsReady(options = {}) {
|
|
179
|
+
const { basePath = DEFAULT_BASE_PATH, scripts = DEFAULT_SCRIPTS, stylesheets = DEFAULT_STYLESHEETS, timeoutMs = 8000, force = false } = options;
|
|
180
|
+
|
|
181
|
+
if (!force && hasLeafletPluginsReady()) {
|
|
182
|
+
window.fleetopsLeafletPluginsLoaded = true;
|
|
183
|
+
return Promise.resolve(normalizeLeafletGlobal());
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
if (!force && pluginReadyPromise) {
|
|
187
|
+
return pluginReadyPromise;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
window.fleetopsLeafletPluginsLoaded = false;
|
|
191
|
+
stylesheets.forEach((stylesheet) => appendStylesheet(assetPath(basePath, stylesheet)));
|
|
192
|
+
|
|
193
|
+
pluginReadyPromise = waitForLeafletGlobal({ timeoutMs })
|
|
194
|
+
.then(() =>
|
|
195
|
+
scripts.reduce((promise, script) => {
|
|
196
|
+
return promise.then(() => {
|
|
197
|
+
normalizeLeafletGlobal();
|
|
198
|
+
return loadScript(assetPath(basePath, script), { timeoutMs, isReady: pluginReadyCheck(script) });
|
|
199
|
+
});
|
|
200
|
+
}, Promise.resolve())
|
|
201
|
+
)
|
|
202
|
+
.then(() => {
|
|
203
|
+
const readyLeaflet = normalizeLeafletGlobal();
|
|
204
|
+
if (!hasLeafletPluginsReady()) {
|
|
205
|
+
throw new Error('[FleetOps Leaflet] Leaflet plugins loaded but required Draw/contextmenu APIs are missing');
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
window.fleetopsLeafletPluginsLoaded = true;
|
|
209
|
+
return readyLeaflet;
|
|
210
|
+
})
|
|
211
|
+
.catch((error) => {
|
|
212
|
+
window.fleetopsLeafletPluginsLoaded = false;
|
|
213
|
+
pluginReadyPromise = null;
|
|
214
|
+
debug(error.message);
|
|
215
|
+
throw error;
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
return pluginReadyPromise;
|
|
219
|
+
}
|
|
@@ -1,10 +1,6 @@
|
|
|
1
|
-
import CustomerOrdersComponent from '../components/customer/orders';
|
|
2
1
|
import CustomerAdminSettingsComponent from '../components/customer/admin-settings';
|
|
3
|
-
import { MenuItem } from '@fleetbase/ember-core/contracts';
|
|
4
|
-
import { setOwner } from '@ember/application';
|
|
5
|
-
import { debug } from '@ember/debug';
|
|
6
2
|
|
|
7
|
-
export default function setupCustomerPortal(
|
|
3
|
+
export default function setupCustomerPortal(_fleetopsEngine, universe) {
|
|
8
4
|
const extensionManager = universe.getService('universe/extension-manager');
|
|
9
5
|
const isCustomerPortalInstalled = extensionManager.isInstalled('@fleetbase/customer-portal-engine');
|
|
10
6
|
if (!isCustomerPortalInstalled) return;
|
|
@@ -13,81 +9,25 @@ export default function setupCustomerPortal(fleetopsEngine, universe) {
|
|
|
13
9
|
if (extensionManager.isEngineLoaded('@fleetbase/customer-portal-engine')) {
|
|
14
10
|
const customerPortalEngine = extensionManager.getEngineInstance('@fleetbase/customer-portal-engine');
|
|
15
11
|
if (customerPortalEngine) {
|
|
16
|
-
return setup(customerPortalEngine,
|
|
12
|
+
return setup(customerPortalEngine, universe);
|
|
17
13
|
}
|
|
18
14
|
}
|
|
19
15
|
|
|
20
16
|
// Otherwise, wait for customer portal engine to load to run setup
|
|
21
17
|
universe.onEngineLoaded('@fleetbase/customer-portal-engine', (customerPortalEngine) => {
|
|
22
|
-
setup(customerPortalEngine,
|
|
18
|
+
setup(customerPortalEngine, universe);
|
|
23
19
|
});
|
|
24
20
|
}
|
|
25
21
|
|
|
26
|
-
function setup(customerPortalEngine,
|
|
22
|
+
function setup(customerPortalEngine, universe) {
|
|
27
23
|
// If setup already completed don't run again
|
|
28
24
|
if (customerPortalEngine?._fleetopsSetupCompleted === true) return;
|
|
29
25
|
|
|
30
|
-
// Alias FleetOps services into Customer Portal before rendering
|
|
31
|
-
createServiceAlias(fleetopsEngine, customerPortalEngine, [
|
|
32
|
-
'leaflet-map-manager',
|
|
33
|
-
'location',
|
|
34
|
-
'movement-tracker',
|
|
35
|
-
'leaflet-routing-control',
|
|
36
|
-
'order-config-actions',
|
|
37
|
-
'order-creation',
|
|
38
|
-
'order-validation',
|
|
39
|
-
]);
|
|
40
|
-
|
|
41
|
-
const menuService = universe.getService('universe/menu-service');
|
|
42
26
|
const registryService = universe.getService('universe/registry-service');
|
|
43
27
|
|
|
44
|
-
// Register customer orders from Fleet-Ops
|
|
45
|
-
menuService.registerMenuItem(
|
|
46
|
-
'customer-portal:sidebar',
|
|
47
|
-
new MenuItem({
|
|
48
|
-
title: 'Orders',
|
|
49
|
-
route: 'customer-portal.portal.virtual',
|
|
50
|
-
icon: 'boxes-packing',
|
|
51
|
-
component: createEngineBoundComponent(customerPortalEngine, CustomerOrdersComponent),
|
|
52
|
-
})
|
|
53
|
-
);
|
|
54
|
-
|
|
55
28
|
// Register registries
|
|
56
29
|
registryService.register('customer-portal:admin-settings', '@fleetbase/customer-portal-engine', CustomerAdminSettingsComponent);
|
|
57
30
|
|
|
58
31
|
// Flag fleetops setup complete
|
|
59
32
|
customerPortalEngine._fleetopsSetupCompleted = true;
|
|
60
33
|
}
|
|
61
|
-
|
|
62
|
-
function createServiceAlias(sourceOwner, destOwner, serviceNames) {
|
|
63
|
-
serviceNames.forEach((name) => {
|
|
64
|
-
const key = `service:${name}`;
|
|
65
|
-
if (destOwner.hasRegistration?.(key)) return;
|
|
66
|
-
|
|
67
|
-
// alias the fully-wired instance
|
|
68
|
-
const instance = sourceOwner.lookup?.(key);
|
|
69
|
-
if (instance) {
|
|
70
|
-
destOwner.register(key, instance, { instantiate: false });
|
|
71
|
-
return;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// Faillback: only use factories for simple/stateless services
|
|
75
|
-
const factory = sourceOwner.resolveRegistration?.(key);
|
|
76
|
-
if (factory) {
|
|
77
|
-
destOwner.register(key, factory);
|
|
78
|
-
return;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
debug(`[alias] missing ${key} on source; not aliased`);
|
|
82
|
-
});
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
function createEngineBoundComponent(engineInstance, ComponentClass) {
|
|
86
|
-
return class EngineBoundComponent extends ComponentClass {
|
|
87
|
-
constructor(...args) {
|
|
88
|
-
super(...args);
|
|
89
|
-
// Ensure DI resolution happens within the engine
|
|
90
|
-
setOwner(this, engineInstance);
|
|
91
|
-
}
|
|
92
|
-
};
|
|
93
|
-
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '@fleetbase/fleetops-engine/components/home/getting-started-guidance';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '@fleetbase/fleetops-engine/components/issue/details/comments';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '@fleetbase/fleetops-engine/components/issue/details/correspondence';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '@fleetbase/fleetops-engine/components/issue/details/documents';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '@fleetbase/fleetops-engine/components/issue/panel-header';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '@fleetbase/fleetops-engine/components/modals/convert-customer-to-vendor';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '@fleetbase/fleetops-engine/components/modals/issue-assign';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '@fleetbase/fleetops-engine/components/modals/issue-close';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '@fleetbase/fleetops-engine/components/modals/issue-status';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '@fleetbase/fleetops-engine/components/vendor/personnel-manager';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { default, formatDurationValue } from '@fleetbase/
|
|
1
|
+
export { default, formatDurationValue } from '@fleetbase/ember-ui/helpers/format-duration';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '@fleetbase/fleetops-engine/services/getting-started';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default, ensureLeafletDrawEditNamespace } from '@fleetbase/fleetops-engine/utils/leaflet-draw-namespace-guard';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default, hasLeafletPluginsReady, resetLeafletPluginLoaderForTesting } from '@fleetbase/fleetops-engine/utils/leaflet-plugin-loader';
|
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.51",
|
|
4
4
|
"description": "Fleet & Transport Management Extension for Fleetbase",
|
|
5
5
|
"fleetbase": {
|
|
6
6
|
"route": "fleet-ops"
|
|
@@ -42,9 +42,9 @@
|
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
44
|
"@babel/core": "^7.23.2",
|
|
45
|
-
"@fleetbase/ember-core": "^0.3.
|
|
46
|
-
"@fleetbase/ember-ui": "^0.3.
|
|
47
|
-
"@fleetbase/fleetops-data": "^0.1.
|
|
45
|
+
"@fleetbase/ember-core": "^0.3.21",
|
|
46
|
+
"@fleetbase/ember-ui": "^0.3.33",
|
|
47
|
+
"@fleetbase/fleetops-data": "^0.1.36",
|
|
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",
|
|
@@ -91,7 +91,7 @@
|
|
|
91
91
|
"ember-cli-sri": "^2.1.1",
|
|
92
92
|
"ember-cli-terser": "^4.0.2",
|
|
93
93
|
"ember-composable-helpers": "^5.0.0",
|
|
94
|
-
"ember-concurrency": "^4.0.
|
|
94
|
+
"ember-concurrency": "^4.0.6",
|
|
95
95
|
"ember-data": "^4.12.5",
|
|
96
96
|
"ember-engines": "^0.9.0",
|
|
97
97
|
"ember-load-initializers": "^2.1.2",
|
|
@@ -0,0 +1,30 @@
|
|
|
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
|
+
/**
|
|
9
|
+
* Run the migrations.
|
|
10
|
+
*/
|
|
11
|
+
public function up(): void
|
|
12
|
+
{
|
|
13
|
+
Schema::table('vendor_personnels', function (Blueprint $table) {
|
|
14
|
+
$table->string('role')->default('member')->after('contact_uuid');
|
|
15
|
+
$table->string('status')->default('active')->after('role');
|
|
16
|
+
$table->foreignUuid('invited_by_uuid')->nullable()->after('status')->references('uuid')->on('users')->nullOnDelete();
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Reverse the migrations.
|
|
22
|
+
*/
|
|
23
|
+
public function down(): void
|
|
24
|
+
{
|
|
25
|
+
Schema::table('vendor_personnels', function (Blueprint $table) {
|
|
26
|
+
$table->dropForeign(['invited_by_uuid']);
|
|
27
|
+
$table->dropColumn(['role', 'status', 'invited_by_uuid']);
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
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
|
+
/**
|
|
9
|
+
* Run the migrations.
|
|
10
|
+
*
|
|
11
|
+
* @return void
|
|
12
|
+
*/
|
|
13
|
+
public function up()
|
|
14
|
+
{
|
|
15
|
+
if (!Schema::hasColumn('issues', 'tags')) {
|
|
16
|
+
Schema::table('issues', function (Blueprint $table) {
|
|
17
|
+
$table->json('tags')->nullable()->after('priority');
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Reverse the migrations.
|
|
24
|
+
*
|
|
25
|
+
* @return void
|
|
26
|
+
*/
|
|
27
|
+
public function down()
|
|
28
|
+
{
|
|
29
|
+
// The tags column is owned by an earlier FleetOps issue refactor migration on current installs.
|
|
30
|
+
}
|
|
31
|
+
};
|