@fleetbase/ember-ui 0.3.7 → 0.3.8
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/custom-fields-manager.js +52 -9
- package/addon/components/dashboard.js +1 -1
- package/addon/components/pill.hbs +48 -0
- package/addon/components/pill.js +31 -0
- package/addon/helpers/is-object-empty.js +6 -0
- package/app/components/pill.js +1 -0
- package/app/helpers/is-object-empty.js +1 -0
- package/package.json +1 -1
|
@@ -33,7 +33,8 @@ export default class CustomFieldsManagerComponent extends Component {
|
|
|
33
33
|
super(...arguments);
|
|
34
34
|
this.subjects = subjects ?? [];
|
|
35
35
|
next(() => {
|
|
36
|
-
if (!this.subjects) return;
|
|
36
|
+
if (!this.subjects || this.subjects.length === 0) return;
|
|
37
|
+
// Load the first subject immediately
|
|
37
38
|
this.loadCustomFields.perform(this.subjects[0]);
|
|
38
39
|
});
|
|
39
40
|
}
|
|
@@ -51,7 +52,44 @@ export default class CustomFieldsManagerComponent extends Component {
|
|
|
51
52
|
}
|
|
52
53
|
}
|
|
53
54
|
|
|
55
|
+
/**
|
|
56
|
+
* Restore custom fields from cache for subjects that haven't been loaded yet
|
|
57
|
+
* This method checks the CustomFieldsRegistry service cache and restores
|
|
58
|
+
* previously loaded data to avoid losing it during navigation
|
|
59
|
+
*/
|
|
60
|
+
async restoreFromCache() {
|
|
61
|
+
if (!this.subjects || this.subjects.length <= 1) return;
|
|
62
|
+
|
|
63
|
+
// Skip the first subject as it's loaded in constructor
|
|
64
|
+
const subjectsToRestore = this.subjects.slice(1);
|
|
65
|
+
|
|
66
|
+
for (const subject of subjectsToRestore) {
|
|
67
|
+
try {
|
|
68
|
+
const company = await this.currentUser.loadCompany();
|
|
69
|
+
const loadOptions = {
|
|
70
|
+
groupedFor: `${underscore(subject.model)}_custom_field_group`,
|
|
71
|
+
fieldFor: subject.type,
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
// Check if we have a cached manager for this subject
|
|
75
|
+
const cachedManager = this.customFieldsRegistry.forSubject(company, { loadOptions });
|
|
76
|
+
|
|
77
|
+
// Only restore if we have cached groups data
|
|
78
|
+
if (cachedManager && cachedManager.groups && cachedManager.groups.length > 0) {
|
|
79
|
+
this.#updateSubject(subject, (s) => {
|
|
80
|
+
return { ...s, groups: cachedManager.groups };
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
} catch (err) {
|
|
84
|
+
// Silently continue if cache restore fails for a subject
|
|
85
|
+
console.warn(`Failed to restore cache for subject ${subject.model}:`, err);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
54
90
|
@task *loadCustomFields(subject) {
|
|
91
|
+
if (!subject) return;
|
|
92
|
+
|
|
55
93
|
try {
|
|
56
94
|
const company = yield this.currentUser.loadCompany();
|
|
57
95
|
const customFieldsManager = yield this.customFieldsRegistry.loadSubjectCustomFields.perform(company, {
|
|
@@ -60,12 +98,14 @@ export default class CustomFieldsManagerComponent extends Component {
|
|
|
60
98
|
fieldFor: subject.type,
|
|
61
99
|
},
|
|
62
100
|
});
|
|
101
|
+
|
|
63
102
|
this.#updateSubject(subject, (s) => {
|
|
64
103
|
return { ...s, groups: customFieldsManager.customFieldGroups };
|
|
65
104
|
});
|
|
66
105
|
|
|
67
106
|
return customFieldsManager;
|
|
68
107
|
} catch (err) {
|
|
108
|
+
console.error(`❌ Failed to load custom fields for ${subject.model}:`, err);
|
|
69
109
|
this.notifications.serverError(err);
|
|
70
110
|
}
|
|
71
111
|
}
|
|
@@ -127,7 +167,7 @@ export default class CustomFieldsManagerComponent extends Component {
|
|
|
127
167
|
|
|
128
168
|
try {
|
|
129
169
|
await group.destroyRecord();
|
|
130
|
-
await this.loadCustomFields.perform(subject);
|
|
170
|
+
await this.loadCustomFields.perform(subject, true); // Force reload after deletion
|
|
131
171
|
modal.done();
|
|
132
172
|
} catch (error) {
|
|
133
173
|
this.notifications.serverError(error);
|
|
@@ -151,7 +191,7 @@ export default class CustomFieldsManagerComponent extends Component {
|
|
|
151
191
|
|
|
152
192
|
try {
|
|
153
193
|
await customField.destroyRecord();
|
|
154
|
-
await this.loadCustomFields.perform(subject);
|
|
194
|
+
await this.loadCustomFields.perform(subject, true); // Force reload after deletion
|
|
155
195
|
modal.done();
|
|
156
196
|
} catch (error) {
|
|
157
197
|
this.notifications.serverError(error);
|
|
@@ -161,6 +201,13 @@ export default class CustomFieldsManagerComponent extends Component {
|
|
|
161
201
|
});
|
|
162
202
|
}
|
|
163
203
|
|
|
204
|
+
@action onTabChange(subject) {
|
|
205
|
+
// Ensure custom fields are loaded when switching tabs
|
|
206
|
+
if (subject && (!subject.groups || subject.groups.length === 0)) {
|
|
207
|
+
this.loadCustomFields.perform(subject);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
164
211
|
#addCustomFieldToGroup(subject, customField, group) {
|
|
165
212
|
this.#updateGroupOnSubject(subject, group.id, (g) => {
|
|
166
213
|
const current = isArray(g.customFields) ? g.customFields : [];
|
|
@@ -207,12 +254,8 @@ export default class CustomFieldsManagerComponent extends Component {
|
|
|
207
254
|
#updateSubject(subject, updater) {
|
|
208
255
|
if (!subject) return;
|
|
209
256
|
|
|
210
|
-
//
|
|
211
|
-
const
|
|
212
|
-
const idx = this.subjects.findIndex((s) => {
|
|
213
|
-
if (s === subject) return true;
|
|
214
|
-
return idKey ? s?.[idKey] === subject?.[idKey] : false;
|
|
215
|
-
});
|
|
257
|
+
// Find by model property since tab objects have extra properties
|
|
258
|
+
const idx = this.subjects.findIndex((s) => s?.model === subject?.model);
|
|
216
259
|
|
|
217
260
|
if (idx === -1) return;
|
|
218
261
|
|
|
@@ -23,7 +23,7 @@ export default class DashboardComponent extends Component {
|
|
|
23
23
|
* Creates an instance of DashboardComponent.
|
|
24
24
|
* @memberof DashboardComponent
|
|
25
25
|
*/
|
|
26
|
-
constructor(owner, { defaultDashboardId = 'dashboard', defaultDashboardName = 'Default Dashboard', showPanelWhenZeroWidgets = false, extension = 'core' }) {
|
|
26
|
+
constructor(owner, { defaultDashboardId = 'dashboard', defaultDashboardName = 'Default Dashboard', showPanelWhenZeroWidgets = false, extension = 'core' } = {}) {
|
|
27
27
|
super(...arguments);
|
|
28
28
|
this.dashboard.reset(); // ensure service is reset when re-rendering
|
|
29
29
|
next(() => {
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
<div class="fleetbase-pill" ...attributes>
|
|
2
|
+
<a href="javascript:;" class="flex flex-row space-x-2 {{@anchorClass}}" {{on "click" this.handleClick}}>
|
|
3
|
+
<div class="relative shrink-0 {{@imageWrapperClass}}">
|
|
4
|
+
{{#if (has-block "image")}}
|
|
5
|
+
{{yield @resource to="image"}}
|
|
6
|
+
{{else}}
|
|
7
|
+
<Image
|
|
8
|
+
src={{@imageSrc}}
|
|
9
|
+
@fallbackSrc={{or @imageFallback (config (concat "defaultValues." @fallbackImageType)) (config "defaultValues.placeholderImage")}}
|
|
10
|
+
width={{or @imageSize @imageWidth "28"}}
|
|
11
|
+
height={{or @imageSize @imageHeight "28"}}
|
|
12
|
+
class="w-7 h-7 rounded-full ring-2 ring-gray-800 dark:ring-gray-700 shadow transition-shadow hover:shadow-md focus:shadow-md {{@imageClass}}"
|
|
13
|
+
alt={{or @imageAlt this.resourceName}}
|
|
14
|
+
/>
|
|
15
|
+
{{#if @showOnlineIndicator}}
|
|
16
|
+
<FaIcon
|
|
17
|
+
@icon="circle"
|
|
18
|
+
@size="2xs"
|
|
19
|
+
class="absolute left-0 top-0 h-2 w-2 {{if (get @resource (or @onlinePath 'online')) 'text-green-500' 'text-yellow-200'}} {{@onlineIndicatorClass}}"
|
|
20
|
+
/>
|
|
21
|
+
{{/if}}
|
|
22
|
+
{{yield @resource to="image"}}
|
|
23
|
+
{{/if}}
|
|
24
|
+
</div>
|
|
25
|
+
<div class={{@contentWrapperClass}}>
|
|
26
|
+
{{#if (has-block)}}
|
|
27
|
+
{{yield @resource}}
|
|
28
|
+
{{else}}
|
|
29
|
+
<div class="text-sm {{@titleClass}}">{{n-a @title this.resourceName}}</div>
|
|
30
|
+
{{#if @subtitle}}
|
|
31
|
+
<div class="text-xs text-gray-400 dark:text-gray-500 {{@subtitleClass}}">{{n-a @subtitle}}</div>
|
|
32
|
+
{{/if}}
|
|
33
|
+
{{yield @resource}}
|
|
34
|
+
{{/if}}
|
|
35
|
+
</div>
|
|
36
|
+
{{#if (has-block "tooltip")}}
|
|
37
|
+
<Attach::Tooltip @class="clean" @animation="scale" @placement={{or @tooltipPosition "top"}}>
|
|
38
|
+
<InputInfo>
|
|
39
|
+
{{yield @resource to="tooltip"}}
|
|
40
|
+
</InputInfo>
|
|
41
|
+
</Attach::Tooltip>
|
|
42
|
+
{{else if @tooltipComponent}}
|
|
43
|
+
<Attach::Tooltip @class="clean" @animation="scale" @placement={{or @tooltipPosition "top"}}>
|
|
44
|
+
{{component @tooltipComponent}}
|
|
45
|
+
</Attach::Tooltip>
|
|
46
|
+
{{/if}}
|
|
47
|
+
</a>
|
|
48
|
+
</div>
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import Component from '@glimmer/component';
|
|
2
|
+
import { action, get } from '@ember/object';
|
|
3
|
+
import getModelName from '@fleetbase/ember-core/utils/get-model-name';
|
|
4
|
+
|
|
5
|
+
export default class PillComponent extends Component {
|
|
6
|
+
/* eslint-disable ember/no-get */
|
|
7
|
+
get resourceName() {
|
|
8
|
+
const record = this.args.resource;
|
|
9
|
+
if (!record) return 'resource';
|
|
10
|
+
|
|
11
|
+
return (
|
|
12
|
+
get(record, this.args.namePath ?? 'name') ??
|
|
13
|
+
get(record, 'display_name') ??
|
|
14
|
+
get(record, 'displayName') ??
|
|
15
|
+
get(record, 'tracking') ??
|
|
16
|
+
get(record, 'public_id') ??
|
|
17
|
+
getModelName(record)
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
@action handleClick() {
|
|
22
|
+
console.log('handleClick called!', ...arguments);
|
|
23
|
+
if (typeof this.args.onClick === 'function') {
|
|
24
|
+
if (this.args.resource) {
|
|
25
|
+
this.args.onClick(this.args.resource, ...arguments);
|
|
26
|
+
} else {
|
|
27
|
+
this.args.onClick(...arguments);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '@fleetbase/ember-ui/components/pill';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '@fleetbase/ember-ui/helpers/is-object-empty';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fleetbase/ember-ui",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.8",
|
|
4
4
|
"description": "Fleetbase UI provides all the interface components, helpers, services and utilities for building a Fleetbase extension into the Console.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"fleetbase-ui",
|