@fleetbase/ember-ui 0.3.10 → 0.3.12
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/SCHEDULING_COMPONENTS.md +228 -0
- package/addon/components/availability-editor.js +81 -0
- package/addon/components/chart.js +10 -10
- package/addon/components/click-to-reveal.hbs +1 -1
- package/addon/components/click-to-reveal.js +5 -23
- package/addon/components/dashboard/create.hbs +5 -3
- package/addon/components/dashboard/widget-panel.hbs +17 -7
- package/addon/components/dashboard/widget-panel.js +61 -13
- package/addon/components/dashboard.js +0 -1
- package/addon/components/filter/multi-input.hbs +16 -0
- package/addon/components/filter/multi-input.js +68 -0
- package/addon/components/filter/range.hbs +52 -0
- package/addon/components/filter/range.js +74 -0
- package/addon/components/filters-picker.js +13 -6
- package/addon/components/layout/resource/tabular.hbs +2 -0
- package/addon/components/layout/resource/tabular.js +28 -0
- package/addon/components/layout/sidebar/item.hbs +2 -2
- package/addon/components/lazy-engine-component.hbs +8 -0
- package/addon/components/lazy-engine-component.js +136 -0
- package/addon/components/load-engine.hbs +1 -0
- package/addon/components/load-engine.js +82 -0
- package/addon/components/modals/query-builder-computed-column-editor.hbs +80 -0
- package/addon/components/modals/query-builder-computed-column-editor.js +251 -0
- package/addon/components/pill.js +0 -1
- package/addon/components/query-builder/computed-columns.hbs +59 -0
- package/addon/components/query-builder/computed-columns.js +104 -0
- package/addon/components/query-builder.hbs +10 -0
- package/addon/components/query-builder.js +5 -0
- package/addon/components/registry-yield.hbs +7 -1
- package/addon/components/registry-yield.js +107 -15
- package/addon/components/report-builder.hbs +1 -0
- package/addon/components/report-builder.js +2 -1
- package/addon/components/schedule-calendar.hbs +37 -0
- package/addon/components/schedule-calendar.js +166 -0
- package/addon/components/schedule-item-card.hbs +39 -0
- package/addon/components/schedule-item-card.js +74 -0
- package/addon/components/tab-navigation.hbs +78 -38
- package/addon/components/tab-navigation.js +22 -9
- package/addon/components/table/cell/dropdown.hbs +2 -0
- package/addon/components/table/cell/dropdown.js +11 -0
- package/addon/components/table/cell/resizer.js +1 -1
- package/addon/components/table/cell.hbs +15 -2
- package/addon/components/table/cell.js +24 -0
- package/addon/components/table/foot.js +1 -1
- package/addon/components/table/td.hbs +2 -2
- package/addon/components/table/td.js +66 -1
- package/addon/components/table/th.hbs +15 -1
- package/addon/components/table/th.js +84 -0
- package/addon/components/table.hbs +22 -15
- package/addon/components/table.js +288 -0
- package/addon/helpers/avatar-url.js +9 -0
- package/addon/helpers/lazy-engine-component.js +116 -0
- package/addon/helpers/point-coordinates.js +10 -0
- package/addon/helpers/point-to-coordinates.js +32 -0
- package/addon/helpers/resolve-component.js +18 -0
- package/addon/helpers/set-object-kv.js +9 -0
- package/addon/helpers/unwrap-coordinates.js +249 -0
- package/addon/services/dashboard.js +39 -17
- package/addon/styles/components/badge.css +8 -0
- package/addon/styles/components/input.css +8 -0
- package/addon/styles/components/table.css +183 -0
- package/addon/styles/layout/next.css +245 -28
- package/app/components/filter/multi-input.js +1 -0
- package/app/components/filter/range.js +1 -0
- package/app/components/lazy-engine-component.js +1 -0
- package/app/components/load-engine.js +1 -0
- package/app/components/modals/query-builder-computed-column-editor.js +1 -0
- package/app/components/query-builder/computed-columns.js +1 -0
- package/app/components/schedule-calendar.js +1 -0
- package/app/helpers/avatar-url.js +1 -0
- package/app/helpers/lazy-engine-component.js +1 -0
- package/app/helpers/point-coordinates.js +1 -0
- package/app/helpers/point-to-coordinates.js +1 -0
- package/app/helpers/set-object-kv.js +1 -0
- package/app/helpers/unwrap-coordinates.js +1 -0
- package/package.json +1 -1
|
@@ -1,16 +1,34 @@
|
|
|
1
1
|
import Helper from '@ember/component/helper';
|
|
2
2
|
import { getOwner } from '@ember/application';
|
|
3
3
|
import { ensureSafeComponent } from '@embroider/util';
|
|
4
|
+
import { ExtensionComponent } from '@fleetbase/ember-core/contracts';
|
|
4
5
|
|
|
5
6
|
export default class ResolveComponentHelper extends Helper {
|
|
6
7
|
compute([value]) {
|
|
7
8
|
const owner = getOwner(this);
|
|
8
9
|
|
|
10
|
+
// Handle string form extension components
|
|
11
|
+
if (typeof value === 'string' && value.startsWith('#extension-component')) {
|
|
12
|
+
/* eslint-disable no-unused-vars */
|
|
13
|
+
const [_, engineName, componentPathOrName] = value.split(':');
|
|
14
|
+
return new ExtensionComponent(engineName, componentPathOrName);
|
|
15
|
+
}
|
|
16
|
+
|
|
9
17
|
// If it's a string name, pass through — resolver will handle it.
|
|
10
18
|
if (typeof value === 'string') {
|
|
11
19
|
return value;
|
|
12
20
|
}
|
|
13
21
|
|
|
22
|
+
// Handle ExtensionComponent definition (lazy loading)
|
|
23
|
+
if (value && typeof value === 'object' && value.engine && value.path) {
|
|
24
|
+
return value;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// If it has a component property, recurse
|
|
28
|
+
if (value && typeof value === 'object' && value.component) {
|
|
29
|
+
return this.compute([value.component]);
|
|
30
|
+
}
|
|
31
|
+
|
|
14
32
|
// If it's a component class or a pre-wrapped safe definition,
|
|
15
33
|
// ensure it's safe for this template's owner and return it.
|
|
16
34
|
if (value && (typeof value === 'function' || typeof value === 'object')) {
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
import { helper } from '@ember/component/helper';
|
|
2
|
+
import { isArray } from '@ember/array';
|
|
3
|
+
|
|
4
|
+
const L = window.leaflet || window.L;
|
|
5
|
+
/**
|
|
6
|
+
* Recursively wraps geographic coordinates so that all longitude values are normalized
|
|
7
|
+
* to the canonical range of [-180, 180] degrees.
|
|
8
|
+
*
|
|
9
|
+
* This function assumes coordinates are provided in GeoJSON order: [lng, lat, ...].
|
|
10
|
+
* It works in two primary scenarios:
|
|
11
|
+
*
|
|
12
|
+
* 1. **Single Coordinate Array:**
|
|
13
|
+
* If the input is a coordinate point (an array of numbers where the first element
|
|
14
|
+
* is the longitude and the second is the latitude), the function applies the wrap
|
|
15
|
+
* formula to the longitude component. Any additional dimensions (such as altitude)
|
|
16
|
+
* are preserved.
|
|
17
|
+
*
|
|
18
|
+
* For example:
|
|
19
|
+
* ```js
|
|
20
|
+
* // Input: [200, 45] (200° longitude is out-of-range)
|
|
21
|
+
* // Output: [-160, 45]
|
|
22
|
+
* ```
|
|
23
|
+
*
|
|
24
|
+
* 2. **Nested Coordinate Arrays:**
|
|
25
|
+
* If the input is an array of coordinate points (e.g. a line string, polygon ring, or
|
|
26
|
+
* multi-ring/multi-polygon structure), the function recursively processes each sub-array.
|
|
27
|
+
*
|
|
28
|
+
* For example:
|
|
29
|
+
* ```js
|
|
30
|
+
* // Input: [[200, 45], [210, 46], [220, 47]]
|
|
31
|
+
* // Output: [[-160, 45], [-150, 46], [-140, 47]]
|
|
32
|
+
* ```
|
|
33
|
+
*
|
|
34
|
+
* If the input is not an array, the function returns it unchanged.
|
|
35
|
+
*
|
|
36
|
+
* @param {*} coords - The coordinate or nested array of coordinates to be wrapped.
|
|
37
|
+
* Expected format for a coordinate is [lng, lat, ...].
|
|
38
|
+
* @returns {*} A new coordinate or nested array of coordinates with longitudes normalized
|
|
39
|
+
* to the range [-180, 180]. Non-array inputs are returned as-is.
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* // Wrapping a single coordinate:
|
|
43
|
+
* const coord = [200, 45];
|
|
44
|
+
* const wrappedCoord = leafletWrapCoordinates(coord);
|
|
45
|
+
* // wrappedCoord is [-160, 45]
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* // Wrapping a polygon ring:
|
|
49
|
+
* const ring = [
|
|
50
|
+
* [200, 45],
|
|
51
|
+
* [210, 46],
|
|
52
|
+
* [220, 47],
|
|
53
|
+
* [200, 45] // closing the ring
|
|
54
|
+
* ];
|
|
55
|
+
* const wrappedRing = leafletWrapCoordinates(ring);
|
|
56
|
+
* // wrappedRing is [[-160, 45], [-150, 46], [-140, 47], [-160, 45]]
|
|
57
|
+
*
|
|
58
|
+
* @export
|
|
59
|
+
*/
|
|
60
|
+
export function leafletWrapCoordinates(coords) {
|
|
61
|
+
// If this is a coordinate point (e.g. [lng, lat, ...])
|
|
62
|
+
if (isArray(coords) && typeof coords[0] === 'number' && coords.length >= 2) {
|
|
63
|
+
const lng = coords[0];
|
|
64
|
+
// Wrap the longitude into [-180, 180]
|
|
65
|
+
const wrappedLng = ((((lng + 180) % 360) + 360) % 360) - 180;
|
|
66
|
+
// Return a new coordinate array preserving any extra dimensions (such as altitude)
|
|
67
|
+
return [wrappedLng, ...coords.slice(1)];
|
|
68
|
+
}
|
|
69
|
+
// Otherwise, assume it's a nested array (e.g. a ring or array of rings)
|
|
70
|
+
else if (isArray(coords)) {
|
|
71
|
+
return coords.map(leafletWrapCoordinates);
|
|
72
|
+
}
|
|
73
|
+
// If it's not an array, just return it.
|
|
74
|
+
return coords;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Converts a latitude and longitude pair to a projected CRS point using Leaflet's projection.
|
|
79
|
+
*
|
|
80
|
+
* @param {number} lat - The latitude value.
|
|
81
|
+
* @param {number} lng - The longitude value.
|
|
82
|
+
* @param {Object} [crs=L.CRS.EPSG3857] - The coordinate reference system to use (defaults to EPSG:3857).
|
|
83
|
+
* @returns {L.Point} The projected CRS point as an instance of L.Point.
|
|
84
|
+
*/
|
|
85
|
+
export function latLngToCRS(lat, lng, crs = L.CRS.EPSG3857) {
|
|
86
|
+
const latLng = L.latLng(lat, lng);
|
|
87
|
+
const point = crs.project(latLng);
|
|
88
|
+
return crs.unproject(point);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Recursively converts an array of geographic coordinates in GeoJSON order ([lng, lat, ...])
|
|
93
|
+
* to Leaflet's projected CRS coordinates.
|
|
94
|
+
*
|
|
95
|
+
* This function expects that the input `coords` is an array containing coordinate arrays
|
|
96
|
+
* in GeoJSON order. For example:
|
|
97
|
+
*
|
|
98
|
+
* - A single coordinate: [lng, lat, ...]
|
|
99
|
+
* - A line string or ring: an array of coordinates, e.g., [[lng, lat], [lng, lat], ...]
|
|
100
|
+
* - A nested array for polygons or multipolygons.
|
|
101
|
+
*
|
|
102
|
+
* The conversion process involves two steps:
|
|
103
|
+
*
|
|
104
|
+
* 1. **Wrapping:**
|
|
105
|
+
* The coordinates are first passed through `leafletWrapCoordinates` to ensure that
|
|
106
|
+
* all longitude values are normalized to the canonical range of [-180, 180].
|
|
107
|
+
*
|
|
108
|
+
* 2. **Projection:**
|
|
109
|
+
* The geographic coordinates are then converted to a projected CRS using `latLngToCRS`.
|
|
110
|
+
*
|
|
111
|
+
* Depending on the structure of the input, the function returns:
|
|
112
|
+
*
|
|
113
|
+
* - A single projected point (L.Point) if a single coordinate array is provided.
|
|
114
|
+
* - An array of projected points if an array of coordinates (e.g., line string) is provided.
|
|
115
|
+
* - A nested array of projected points for polygons or multipolygons.
|
|
116
|
+
*
|
|
117
|
+
* @param {*} coords - An array of coordinate arrays in GeoJSON order ([lng, lat, ...]).
|
|
118
|
+
* This can be a single coordinate, an array of coordinates, or a nested array.
|
|
119
|
+
* @returns {*} The coordinate(s) converted to the projected CRS, matching the structure of the input.
|
|
120
|
+
*
|
|
121
|
+
* @example
|
|
122
|
+
* // Converting a single coordinate:
|
|
123
|
+
* const projectedPoint = unwrapCoordinates([ -80, 26 ]);
|
|
124
|
+
*
|
|
125
|
+
* @example
|
|
126
|
+
* // Converting a line string (ring):
|
|
127
|
+
* const projectedLine = unwrapCoordinates([
|
|
128
|
+
* [ -80, 26 ],
|
|
129
|
+
* [ -80.1, 26.1 ],
|
|
130
|
+
* [ -80.2, 26.2 ]
|
|
131
|
+
* ]);
|
|
132
|
+
*
|
|
133
|
+
* @example
|
|
134
|
+
* // Converting a polygon (array of rings):
|
|
135
|
+
* const projectedPolygon = unwrapCoordinates([
|
|
136
|
+
* [
|
|
137
|
+
* [ -80, 26 ],
|
|
138
|
+
* [ -80.1, 26.1 ],
|
|
139
|
+
* [ -80.2, 26.2 ],
|
|
140
|
+
* [ -80, 26 ]
|
|
141
|
+
* ]
|
|
142
|
+
* ]);
|
|
143
|
+
*/
|
|
144
|
+
export function leafletUnwrapCoordinates(coords) {
|
|
145
|
+
if (!isArray(coords)) {
|
|
146
|
+
// If it's not an array, return it as-is.
|
|
147
|
+
return coords;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Ensure coordinates are wrapped properly to the canonical [-180, 180] range.
|
|
151
|
+
coords = leafletWrapCoordinates(coords);
|
|
152
|
+
|
|
153
|
+
// If the first element is a number, assume this is a single coordinate [lng, lat, ...].
|
|
154
|
+
if (typeof coords[0] === 'number') {
|
|
155
|
+
// Call leafletWrapCoordinates again for safety (though it should already be wrapped).
|
|
156
|
+
coords = leafletWrapCoordinates(coords);
|
|
157
|
+
// Note: Since our input is in GeoJSON order ([lng, lat]), we convert by swapping the order.
|
|
158
|
+
return latLngToCRS(coords[1], coords[0]);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// If the first element is an array and its first element is a number,
|
|
162
|
+
// assume this is an array of coordinates (e.g., a line string or ring).
|
|
163
|
+
if (isArray(coords[0]) && typeof coords[0][0] === 'number') {
|
|
164
|
+
return coords.map((c) => latLngToCRS(c[1], c[0]));
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Otherwise, assume it's a nested array (e.g., for polygons or multipolygons)
|
|
168
|
+
return coords.map(leafletUnwrapCoordinates);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Ember helper to "unwrap" coordinate arrays for Leaflet usage.
|
|
173
|
+
*
|
|
174
|
+
* This helper converts coordinate data into a form that is compatible with Leaflet’s
|
|
175
|
+
* coordinate reference system. It supports two types of input:
|
|
176
|
+
*
|
|
177
|
+
* 1. **GeoJSON Geometry Object:**
|
|
178
|
+
* An object with a `coordinates` property. In this case the coordinates are assumed
|
|
179
|
+
* to already be in GeoJSON order ([lng, lat, ...]). The helper unwraps these
|
|
180
|
+
* coordinates using `leafletUnwrapCoordinates` and returns a new geometry object
|
|
181
|
+
* with the unwrapped coordinates.
|
|
182
|
+
*
|
|
183
|
+
* 2. **Array of Coordinates:**
|
|
184
|
+
* An array of coordinate arrays (e.g. a line string, ring, or polygon) provided in
|
|
185
|
+
* [lat, lng] order. The helper first converts these to GeoJSON order ([lng, lat])
|
|
186
|
+
* and then unwraps them using `leafletUnwrapCoordinates`.
|
|
187
|
+
*
|
|
188
|
+
* **Note:**
|
|
189
|
+
* - The unwrapping process adjusts coordinate values to ensure they form a continuous
|
|
190
|
+
* representation (for example, when dealing with dateline-crossing geometries).
|
|
191
|
+
* - If the input is neither an object with a `coordinates` property nor an array,
|
|
192
|
+
* it is returned unchanged.
|
|
193
|
+
*
|
|
194
|
+
* @param {Array|Object} input - Either:
|
|
195
|
+
* - A GeoJSON geometry object with a `coordinates` property, where coordinates are in [lng, lat] order.
|
|
196
|
+
* - An array of coordinate arrays in [lat, lng] order.
|
|
197
|
+
* @returns {Array|Object} A new geometry object or coordinate array with unwrapped coordinates,
|
|
198
|
+
* preserving the structure of the input.
|
|
199
|
+
*
|
|
200
|
+
* @example
|
|
201
|
+
* // Example 1: GeoJSON geometry object input:
|
|
202
|
+
* let geojson = {
|
|
203
|
+
* type: 'Polygon',
|
|
204
|
+
* coordinates: [
|
|
205
|
+
* [[-80, 26], [-80.1, 26.1], [-80.2, 26.2], [-80, 26]]
|
|
206
|
+
* ]
|
|
207
|
+
* };
|
|
208
|
+
* let result = unwrapCoordinates([geojson]);
|
|
209
|
+
*
|
|
210
|
+
* @example
|
|
211
|
+
* // Example 2: Array of coordinates in [lat, lng] order:
|
|
212
|
+
* let coords = [
|
|
213
|
+
* [26, -80],
|
|
214
|
+
* [26.1, -80.1],
|
|
215
|
+
* [26.2, -80.2]
|
|
216
|
+
* ];
|
|
217
|
+
* let result = unwrapCoordinates([coords]);
|
|
218
|
+
*/
|
|
219
|
+
export default helper(function unwrapCoordinates([input]) {
|
|
220
|
+
if (!input) {
|
|
221
|
+
return input;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// If input is an object with a "coordinates" property,
|
|
225
|
+
// assume it is a GeoJSON geometry where coordinates are in [lng, lat] order.
|
|
226
|
+
if (typeof input === 'object' && input.coordinates) {
|
|
227
|
+
const unwrappedCoordinates = leafletUnwrapCoordinates(input.coordinates);
|
|
228
|
+
return {
|
|
229
|
+
...input,
|
|
230
|
+
coordinates: unwrappedCoordinates,
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Otherwise, assume input is an array of coordinates.
|
|
235
|
+
// The helper expects these coordinates to be in [lat, lng] order.
|
|
236
|
+
// Convert them to GeoJSON order ([lng, lat]) before unwrapping.
|
|
237
|
+
if (isArray(input) && input.length > 0) {
|
|
238
|
+
if (typeof input[0][0] === 'number') {
|
|
239
|
+
// Input is an array of coordinates in [lat, lng] order.
|
|
240
|
+
input = input.map(([latitude, longitude]) => [longitude, latitude]);
|
|
241
|
+
} else {
|
|
242
|
+
// If the structure is nested (e.g., for polygons or multipolygons),
|
|
243
|
+
// reverse the outer array as a fallback. (This branch can be customized as needed.)
|
|
244
|
+
input = input.reverse();
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
const unwrappedCoordinates = leafletUnwrapCoordinates(input);
|
|
248
|
+
return unwrappedCoordinates;
|
|
249
|
+
});
|
|
@@ -17,6 +17,7 @@ export default class DashboardService extends Service {
|
|
|
17
17
|
@service fetch;
|
|
18
18
|
@service notifications;
|
|
19
19
|
@service universe;
|
|
20
|
+
@service('universe/widget-service') widgetService;
|
|
20
21
|
@service intl;
|
|
21
22
|
|
|
22
23
|
/**
|
|
@@ -52,8 +53,9 @@ export default class DashboardService extends Service {
|
|
|
52
53
|
|
|
53
54
|
/**
|
|
54
55
|
* Task for loading dashboards from the store. It sets the current dashboard and checks if adding widget is necessary.
|
|
56
|
+
* Uses drop modifier to prevent concurrent executions and race conditions.
|
|
55
57
|
*/
|
|
56
|
-
@task *loadDashboards({ defaultDashboardId = 'dashboard', defaultDashboardName = 'Default Dashboard', extension = 'core' }) {
|
|
58
|
+
@task({ drop: true }) *loadDashboards({ defaultDashboardId = 'dashboard', defaultDashboardName = 'Default Dashboard', extension = 'core' }) {
|
|
57
59
|
this.universe.registerDashboard(defaultDashboardId);
|
|
58
60
|
|
|
59
61
|
const dashboards = yield this.store.query('dashboard', { limit: -1, extension });
|
|
@@ -174,10 +176,10 @@ export default class DashboardService extends Service {
|
|
|
174
176
|
reset() {
|
|
175
177
|
this.currentDashboard = null;
|
|
176
178
|
this.dashboards = [];
|
|
177
|
-
// unload from store
|
|
179
|
+
// unload from store - must unload widgets first to avoid orphaned records
|
|
178
180
|
next(() => {
|
|
181
|
+
this.store.unloadAll('dashboard-widget');
|
|
179
182
|
this.store.unloadAll('dashboard');
|
|
180
|
-
// this.store.unloadAll('dashboard-widget');
|
|
181
183
|
});
|
|
182
184
|
}
|
|
183
185
|
|
|
@@ -193,20 +195,37 @@ export default class DashboardService extends Service {
|
|
|
193
195
|
const loadedDashboards = this.store.peekAll('dashboard');
|
|
194
196
|
|
|
195
197
|
// check for default dashboard loaded in store
|
|
196
|
-
defaultDashboard = loadedDashboards.find((dashboard) =>
|
|
197
|
-
|
|
198
|
-
|
|
198
|
+
defaultDashboard = loadedDashboards.find((dashboard) => {
|
|
199
|
+
return dashboard && dashboard.id === defaultDashboardId && !dashboard.isDeleted && !dashboard.isDestroying && !dashboard.isDestroyed;
|
|
200
|
+
});
|
|
201
|
+
if (defaultDashboard) return defaultDashboard;
|
|
202
|
+
|
|
203
|
+
// Peek existing record in identity map
|
|
204
|
+
const existingDashboard = this.store.peekRecord('dashboard', defaultDashboardId);
|
|
205
|
+
// Only return if it's in a valid state (not deleted, destroying, or destroyed)
|
|
206
|
+
if (existingDashboard && !existingDashboard.isDeleted && !existingDashboard.isDestroying && !existingDashboard.isDestroyed) {
|
|
207
|
+
return existingDashboard;
|
|
199
208
|
}
|
|
200
209
|
|
|
201
210
|
// create new default dashboard
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
211
|
+
try {
|
|
212
|
+
defaultDashboard = this.store.createRecord('dashboard', {
|
|
213
|
+
id: defaultDashboardId,
|
|
214
|
+
uuid: defaultDashboardId,
|
|
215
|
+
name: defaultDashboardName,
|
|
216
|
+
is_default: false,
|
|
217
|
+
user_uuid: 'system',
|
|
218
|
+
widgets: this._createDefaultDashboardWidgets(defaultDashboardId),
|
|
219
|
+
});
|
|
220
|
+
} catch (error) {
|
|
221
|
+
// Handle race condition where record was created between our peek and createRecord
|
|
222
|
+
if (error.message && error.message.includes('already been used')) {
|
|
223
|
+
defaultDashboard = this.store.peekRecord('dashboard', defaultDashboardId);
|
|
224
|
+
if (defaultDashboard) return defaultDashboard;
|
|
225
|
+
}
|
|
226
|
+
// Re-throw if it's a different error
|
|
227
|
+
throw error;
|
|
228
|
+
}
|
|
210
229
|
|
|
211
230
|
return defaultDashboard;
|
|
212
231
|
}
|
|
@@ -217,11 +236,14 @@ export default class DashboardService extends Service {
|
|
|
217
236
|
* @returns {Array} An array of default dashboard widgets.
|
|
218
237
|
*/
|
|
219
238
|
_createDefaultDashboardWidgets(defaultDashboardId = 'dashboard') {
|
|
220
|
-
const widgets = this.
|
|
239
|
+
const widgets = this.widgetService.getDefaultWidgets(defaultDashboardId);
|
|
240
|
+
|
|
241
|
+
return widgets.map((defaultWidget) => {
|
|
242
|
+
const existingWidget = this.store.peekRecord('dashboard-widget', defaultWidget.id);
|
|
243
|
+
if (existingWidget) return existingWidget;
|
|
244
|
+
|
|
221
245
|
return this.store.createRecord('dashboard-widget', defaultWidget);
|
|
222
246
|
});
|
|
223
|
-
|
|
224
|
-
return widgets;
|
|
225
247
|
}
|
|
226
248
|
|
|
227
249
|
/**
|
|
@@ -30,6 +30,8 @@
|
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
.status-badge[class*='2'] > span,
|
|
33
|
+
.status-badge.confirmed-status-badge > span,
|
|
34
|
+
.status-badge.confirm-status-badge > span,
|
|
33
35
|
.status-badge.green-status-badge > span,
|
|
34
36
|
.status-badge.resolved-status-badge > span,
|
|
35
37
|
.status-badge.published-status-badge > span,
|
|
@@ -49,6 +51,8 @@
|
|
|
49
51
|
}
|
|
50
52
|
|
|
51
53
|
.status-badge[class*='2'] > span svg,
|
|
54
|
+
.status-badge.confirmed-status-badge > span svg,
|
|
55
|
+
.status-badge.confirm-status-badge > span svg,
|
|
52
56
|
.status-badge.green-status-badge > span svg,
|
|
53
57
|
.status-badge.resolved-status-badge > span svg,
|
|
54
58
|
.status-badge.published-status-badge > span svg,
|
|
@@ -148,6 +152,7 @@
|
|
|
148
152
|
.status-badge.disputed-status-badge > span,
|
|
149
153
|
.status-badge.inactive-status-badge > span,
|
|
150
154
|
.status-badge.failed-status-badge > span,
|
|
155
|
+
.status-badge.cancelled-status-badge > span,
|
|
151
156
|
.status-badge.canceled-status-badge > span {
|
|
152
157
|
@apply bg-red-800 border-red-700 text-red-100;
|
|
153
158
|
}
|
|
@@ -166,11 +171,13 @@
|
|
|
166
171
|
.status-badge.disabled-status-badge > span svg,
|
|
167
172
|
.status-badge.disputed-status-badge > span svg,
|
|
168
173
|
.status-badge.failed-status-badge > span svg,
|
|
174
|
+
.status-badge.cancelled-status-badge > span svg,
|
|
169
175
|
.status-badge.canceled-status-badge > span svg {
|
|
170
176
|
@apply text-red-300;
|
|
171
177
|
}
|
|
172
178
|
|
|
173
179
|
.status-badge[class*='4'] > span,
|
|
180
|
+
.status-badge.in-transit-status-badge > span,
|
|
174
181
|
.status-badge.idle-status-badge > span,
|
|
175
182
|
.status-badge.draft-status-badge > span,
|
|
176
183
|
.status-badge.yellow-status-badge > span,
|
|
@@ -194,6 +201,7 @@
|
|
|
194
201
|
}
|
|
195
202
|
|
|
196
203
|
.status-badge[class*='4'] > span svg,
|
|
204
|
+
.status-badge.in-transit-status-badge > span svg,
|
|
197
205
|
.status-badge.idle-status-badge > span svg,
|
|
198
206
|
.status-badge.draft-status-badge > span svg,
|
|
199
207
|
.status-badge.yellow-status-badge > span svg,
|
|
@@ -457,6 +457,14 @@ body[data-theme='dark'] .money-input-currency-selector .ember-basic-dropdown-con
|
|
|
457
457
|
line-height: 1rem;
|
|
458
458
|
}
|
|
459
459
|
|
|
460
|
+
.emberTagInput.form-input-sm {
|
|
461
|
+
height: 34px;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
.emberTagInput.form-input-sm .emberTagInput-new > input.emberTagInput-input {
|
|
465
|
+
padding: 0.1rem;
|
|
466
|
+
}
|
|
467
|
+
|
|
460
468
|
body[data-theme='dark'] .emberTagInput.form-input:disabled,
|
|
461
469
|
body[data-theme='dark'] .emberTagInput.form-input.disabled,
|
|
462
470
|
body[data-theme='dark'] .emberTagInput:disabled,
|
|
@@ -326,3 +326,186 @@ body[data-theme='dark'] .next-table-wrapper.rounded-b > table > tbody > tr:last-
|
|
|
326
326
|
.next-table-wrapper.auto-height > table > tbody > tr:last-child > td:last-child {
|
|
327
327
|
border-bottom-right-radius: 0.25rem;
|
|
328
328
|
}
|
|
329
|
+
|
|
330
|
+
/* Sortable column styles */
|
|
331
|
+
.table-wrapper table thead tr th.is-sortable {
|
|
332
|
+
@apply cursor-pointer select-none;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
.table-wrapper table thead tr th .th-inner-wrapper {
|
|
336
|
+
@apply flex items-center justify-between;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
.table-wrapper table thead tr th .th-content {
|
|
340
|
+
@apply flex-grow;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
.table-wrapper table thead tr th .sort-icon-wrapper {
|
|
344
|
+
@apply ml-2 flex items-center gap-1 text-gray-400;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
.table-wrapper table thead tr th .sort-icon-wrapper > .sort-priority-badge {
|
|
348
|
+
@apply font-semibold text-blue-500 bg-blue-100 rounded-full flex items-center justify-center;
|
|
349
|
+
height: 0.75rem;
|
|
350
|
+
width: 0.75rem;
|
|
351
|
+
font-size: 0.5rem;
|
|
352
|
+
line-height: 0.5rem;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
.table-wrapper table thead tr th .sort-icon-wrapper > div:last-child {
|
|
356
|
+
@apply w-4 h-4 flex flex-col items-center justify-center;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
.table-wrapper table thead tr th .sort-icon-wrapper > .sort-icon:first-child {
|
|
360
|
+
margin-bottom: -7px;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
.table-wrapper table thead tr th .sort-icon {
|
|
364
|
+
@apply text-gray-400;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
.table-wrapper table thead tr th:hover .sort-icon {
|
|
368
|
+
@apply text-gray-300;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
.table-wrapper table thead tr th .sort-icon.is-ascending,
|
|
372
|
+
.table-wrapper table thead tr th .sort-icon.is-descending {
|
|
373
|
+
@apply text-blue-400;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
.next-table-wrapper table thead tr th.is-sortable {
|
|
377
|
+
@apply cursor-pointer select-none;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
.next-table-wrapper table thead tr th .th-inner-wrapper {
|
|
381
|
+
@apply flex items-center justify-between;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
.next-table-wrapper table thead tr th .th-content {
|
|
385
|
+
@apply flex-grow;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
.next-table-wrapper table thead tr th .sort-icon-wrapper {
|
|
389
|
+
@apply ml-2 flex items-center gap-1 text-gray-400;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
.next-table-wrapper table thead tr th .sort-icon-wrapper > .sort-priority-badge {
|
|
393
|
+
@apply font-semibold text-blue-500 bg-blue-100 rounded-full flex items-center justify-center;
|
|
394
|
+
height: 0.75rem;
|
|
395
|
+
width: 0.75rem;
|
|
396
|
+
font-size: 0.5rem;
|
|
397
|
+
line-height: 0.5rem;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
.next-table-wrapper table thead tr th .sort-icon-wrapper > div:last-child {
|
|
401
|
+
@apply w-4 h-4 flex flex-col items-center justify-center;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
.next-table-wrapper table thead tr th .sort-icon-wrapper .sort-icon:first-child {
|
|
405
|
+
margin-bottom: -7px;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
.next-table-wrapper table thead tr th .sort-icon {
|
|
409
|
+
@apply text-gray-400;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
.next-table-wrapper table thead tr th:hover .sort-icon {
|
|
413
|
+
@apply text-gray-300;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
.next-table-wrapper table thead tr th .sort-icon.is-ascending,
|
|
417
|
+
.next-table-wrapper table thead tr th .sort-icon.is-descending {
|
|
418
|
+
@apply text-blue-400;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
/* Dark theme sortable column styles */
|
|
422
|
+
[data-theme='dark'] .table-wrapper table thead tr th .sort-icon {
|
|
423
|
+
@apply text-gray-400;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
[data-theme='dark'] .table-wrapper table thead tr th:hover .sort-icon {
|
|
427
|
+
@apply text-gray-100;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
[data-theme='dark'] .table-wrapper table thead tr th .sort-icon.is-ascending,
|
|
431
|
+
[data-theme='dark'] .table-wrapper table thead tr th .sort-icon.is-descending {
|
|
432
|
+
@apply text-blue-400;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
[data-theme='dark'] .table-wrapper table thead tr th .sort-icon-wrapper > .sort-priority-badge {
|
|
436
|
+
@apply text-blue-100 bg-blue-900;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
[data-theme='dark'] .next-table-wrapper table thead tr th .sort-icon {
|
|
440
|
+
@apply text-gray-400;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
[data-theme='dark'] .next-table-wrapper table thead tr th:hover .sort-icon {
|
|
444
|
+
@apply text-gray-100;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
[data-theme='dark'] .next-table-wrapper table thead tr th .sort-icon.is-ascending,
|
|
448
|
+
[data-theme='dark'] .next-table-wrapper table thead tr th .sort-icon.is-descending {
|
|
449
|
+
@apply text-blue-400;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
[data-theme='dark'] .next-table-wrapper table thead tr th .sort-icon-wrapper > .sort-priority-badge {
|
|
453
|
+
@apply text-blue-100 bg-blue-900;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
/* Floating pagination styles */
|
|
457
|
+
.next-table-wrapper.has-floating-pagination {
|
|
458
|
+
@apply relative pb-16;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
.next-table-wrapper .floating-pagination {
|
|
462
|
+
@apply fixed bottom-4 right-4 z-50;
|
|
463
|
+
@apply bg-white dark:bg-gray-800;
|
|
464
|
+
@apply rounded-lg shadow-lg;
|
|
465
|
+
@apply border border-gray-200 dark:border-gray-700;
|
|
466
|
+
padding: 0.25rem 0.5rem;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
/* Remove default pagination padding when floating */
|
|
470
|
+
.next-table-wrapper .floating-pagination .fleetbase-pagination {
|
|
471
|
+
@apply p-0;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
/* Add spacing between meta info and buttons */
|
|
475
|
+
.next-table-wrapper .floating-pagination .fleetbase-pagination-meta-info {
|
|
476
|
+
margin-right: 0.75rem;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
/* Thin horizontal scrollbar to match vertical scrollbar */
|
|
480
|
+
.next-table-wrapper {
|
|
481
|
+
overflow-x: auto;
|
|
482
|
+
overflow-y: visible;
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
.next-table-wrapper::-webkit-scrollbar {
|
|
486
|
+
height: 8px; /* Match thin vertical scrollbar */
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
.next-table-wrapper::-webkit-scrollbar-track {
|
|
490
|
+
@apply bg-gray-100 dark:bg-gray-700;
|
|
491
|
+
@apply rounded;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
.next-table-wrapper::-webkit-scrollbar-thumb {
|
|
495
|
+
@apply bg-gray-400 dark:bg-gray-500;
|
|
496
|
+
@apply rounded;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
.next-table-wrapper::-webkit-scrollbar-thumb:hover {
|
|
500
|
+
@apply bg-gray-500 dark:bg-gray-400;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
/* Firefox thin scrollbar */
|
|
504
|
+
.next-table-wrapper {
|
|
505
|
+
scrollbar-width: thin;
|
|
506
|
+
scrollbar-color: rgb(156 163 175) rgb(243 244 246); /* gray-400 gray-100 */
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
[data-theme='dark'] .next-table-wrapper {
|
|
510
|
+
scrollbar-color: rgb(107 114 128) rgb(55 65 81); /* gray-500 gray-700 */
|
|
511
|
+
}
|