@fleetbase/ember-ui 0.3.7 → 0.3.9
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/modals/resend-verification-email.hbs +4 -19
- package/addon/components/model-coordinates-input.hbs +1 -1
- package/addon/components/pill.hbs +48 -0
- package/addon/components/pill.js +31 -0
- package/addon/components/tab-navigation.hbs +5 -1
- package/addon/helpers/is-object-empty.js +6 -0
- package/addon/styles/layout/next.css +2 -2
- 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(() => {
|
|
@@ -1,24 +1,9 @@
|
|
|
1
1
|
<Modal::Default @modalIsOpened={{@modalIsOpened}} @options={{@options}} @confirm={{@onConfirm}} @decline={{@onDecline}}>
|
|
2
2
|
<div class="modal-body-container">
|
|
3
|
-
<
|
|
4
|
-
<div class="
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
<path
|
|
8
|
-
fill-rule="evenodd"
|
|
9
|
-
d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z"
|
|
10
|
-
clip-rule="evenodd"
|
|
11
|
-
></path>
|
|
12
|
-
</svg>
|
|
13
|
-
</div>
|
|
14
|
-
<div class="ml-3 flex items-center">
|
|
15
|
-
<span class="font-extrabold text-blue-800">Verify your email address</span>
|
|
16
|
-
</div>
|
|
17
|
-
</div>
|
|
18
|
-
<div class="py-3">
|
|
19
|
-
<p class="text-blue-700">Re-verify your email address and click Send to continue.</p>
|
|
20
|
-
</div>
|
|
21
|
-
</div>
|
|
3
|
+
<InfoBlock @type="info" @icon="lightbulb" @iconSize="lg" class="mb-5">
|
|
4
|
+
<div class="font-extrabold">Verify your email address.</div>
|
|
5
|
+
<div>Re-verify your email address and click Send to continue.</div>
|
|
6
|
+
</InfoBlock>
|
|
22
7
|
|
|
23
8
|
<InputGroup
|
|
24
9
|
@type="email"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<CoordinatesInput
|
|
2
2
|
@value={{if @locationProperty (get @model @locationProperty) @model.location}}
|
|
3
|
-
@onChange={{this.
|
|
3
|
+
@onChange={{this.updateCoordinates}}
|
|
4
4
|
@onGeocode={{this.onAutocomplete}}
|
|
5
5
|
@onUpdatedFromMap={{perform this.reverseGeocode}}
|
|
6
6
|
@onInit={{this.setCoordinatesInput}}
|
|
@@ -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
|
+
}
|
|
@@ -2,11 +2,15 @@
|
|
|
2
2
|
<div class="tab-navigation {{@containerClass}}" data-style={{or @style "github"}} data-size={{or @size "md"}}>
|
|
3
3
|
<div class="tab-list justify-between {{@tablistClass}}" role="tablist">
|
|
4
4
|
<div class="flex flex-row items-center" role="tablist">
|
|
5
|
+
{{#if (has-block "title")}}
|
|
6
|
+
<div id="tab-navigation-title" class="tab-navigation-title {{@tabTitleWrapperClass}}">
|
|
7
|
+
{{yield to="title"}}
|
|
8
|
+
</div>
|
|
9
|
+
{{/if}}
|
|
5
10
|
{{#if (has-block "tabs")}}
|
|
6
11
|
{{yield this to="tabs"}}
|
|
7
12
|
{{else if @tabs}}
|
|
8
13
|
{{#each this.enhancedTabs as |tab|}}
|
|
9
|
-
|
|
10
14
|
{{#if tab.route}}
|
|
11
15
|
<LinkTo
|
|
12
16
|
@route={{tab.route}}
|
|
@@ -2053,7 +2053,7 @@ body[data-theme='light'] .next-table-wrapper table tfoot tr td {
|
|
|
2053
2053
|
}
|
|
2054
2054
|
|
|
2055
2055
|
.field-info-container > .field-name {
|
|
2056
|
-
@apply font-semibold text-gray-700;
|
|
2056
|
+
@apply text-[11px] tracking-wide uppercase font-semibold text-gray-700;
|
|
2057
2057
|
}
|
|
2058
2058
|
|
|
2059
2059
|
.field-info-container > .field-value {
|
|
@@ -2061,7 +2061,7 @@ body[data-theme='light'] .next-table-wrapper table tfoot tr td {
|
|
|
2061
2061
|
}
|
|
2062
2062
|
|
|
2063
2063
|
body[data-theme='dark'] .field-info-container > .field-name {
|
|
2064
|
-
@apply text-gray-
|
|
2064
|
+
@apply text-gray-500;
|
|
2065
2065
|
}
|
|
2066
2066
|
|
|
2067
2067
|
body[data-theme='dark'] .field-info-container > .field-value {
|
|
@@ -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.9",
|
|
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",
|