Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
665118a
Update allowed SQL functions in computed column editor
roncodes Nov 20, 2025
8267bed
feat: Update for universe service refactor
roncodes Nov 26, 2025
bb425e6
Update LazyEngineComponent with proper engine component lookup
roncodes Nov 26, 2025
bbacf63
minor updates to chart component
roncodes Nov 26, 2025
cfe94ea
Fix: Update dashboard service to use new widget service API
roncodes Nov 26, 2025
c1a6e98
Refactor: Inject widget service directly in dashboard service
roncodes Nov 26, 2025
01a7277
few tweaks to handle refactor universe service
roncodes Dec 1, 2025
6f84c7a
feat: fix LazyEngineComponent and add LoadEngine component
roncodes Dec 1, 2025
8b7f59d
fix: update RegistryYield to listen to MenuService events
roncodes Dec 1, 2025
4df173f
fix: remove registryService event listener from RegistryYield
roncodes Dec 1, 2025
2606ff3
everything is almost working
roncodes Dec 2, 2025
1389c3a
minor ux tweaks to widget panel component
roncodes Dec 2, 2025
0113b60
feat: Support component classes in LazyEngineComponent and auto-wrap …
roncodes Dec 2, 2025
ce1ed9e
fix: Convert RegistryYield yieldables to getter for automatic reactivity
roncodes Dec 2, 2025
20372af
fix: convert availableWidgets to getter for reactivity in widget-panel
roncodes Dec 3, 2025
2413e81
fix: use args.defaultDashboardId in availableWidgets getter
roncodes Dec 3, 2025
2c2e859
minor ux tweaks to widget panel
roncodes Dec 3, 2025
6a02673
last ui tweak
roncodes Dec 3, 2025
c009fa3
feat: add widget search, floating table pagination, and thin scrollbar
roncodes Dec 3, 2025
1e0afd3
handle string represented widgets and components
roncodes Dec 3, 2025
9218ebe
fix: adjust floating pagination styling and horizontal scrollbar visi…
roncodes Dec 3, 2025
6b15b7b
Fix: Prevent duplicate dashboard ID errors in dashboard service
roncodes Dec 3, 2025
c2b8e02
Fix: Properly handle dashboard cleanup and recreation on route revisit
roncodes Dec 3, 2025
4a6e011
minor ux tweaks to widget-panel
roncodes Dec 3, 2025
8a075ed
Add lazy-engine-component helper for use with {{component}}
roncodes Dec 3, 2025
629c0bd
fixed linter
roncodes Dec 4, 2025
2405b8b
Merge pull request #102 from fleetbase/update-computed-column-allowed…
roncodes Dec 4, 2025
45075c8
fixed linter for query builder column editor
roncodes Dec 4, 2025
89b1802
pass tests
roncodes Dec 5, 2025
ce223cd
bump version to v0.3.12
roncodes Dec 5, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 10 additions & 10 deletions addon/components/chart.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
import { action } from '@ember/object';
import { debug } from '@ember/debug';
import Chart, { _adapters } from 'chart.js/auto';
import {
parse,
Expand Down Expand Up @@ -46,13 +47,7 @@ import {

export default class ChartComponent extends Component {
@tracked chart;
@tracked isLoading;

constructor() {
super(...arguments);

this.isLoading = this.args.isLoading === true;
}
@tracked isLoading = this.args.isLoading;

@action async renderChart(ctx) {
const options = {
Expand All @@ -65,9 +60,14 @@ export default class ChartComponent extends Component {
};

if (typeof options.data.datasets === 'function') {
this.isLoading = true;
options.data.datasets = await options.data.datasets();
this.isLoading = false;
try {
this.isLoading = true;
options.data.datasets = await options.data.datasets();
} catch (err) {
debug('Error loading Chart dataset: ' + err.message);
} finally {
this.isLoading = false;
}
}

this.useDateFns();
Expand Down
2 changes: 1 addition & 1 deletion addon/components/click-to-reveal.hbs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<div class="relative click-to-reveal {{if this.isVisible 'is-visible' 'is-blurred'}}" {{on "click" (fn this.copy @value)}} ...attributes>
<div class="relative click-to-reveal {{this.wrapperClass}} {{if this.isVisible 'is-visible' 'is-blurred'}}" {{on "click" (fn this.copy @value)}} ...attributes>
<span class="click-to-reveal--hidden-value">
{{#if (has-block)}}
{{yield}}
Expand Down
28 changes: 5 additions & 23 deletions addon/components/click-to-reveal.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,33 +4,11 @@ import { action, computed } from '@ember/object';
import { later } from '@ember/runloop';

export default class ClickToRevealComponent extends ClickToCopyComponent {
/**
* The visiblity state of the value
*
* @var {Boolean}
*/
@tracked isVisible = false;

/**
* The loading state of the reveal process
*
* @var {Boolean}
*/
@tracked isLoading = false;

/**
* The loading timing
*
* @var {Integer}
*/
@tracked timeout = 600;

/**
* If click to copy should be enabled.
*
* @var {Boolean}
*/
@tracked clickToCopy = false;
@tracked wrapperClass = this.args.wrapperClass ?? '';

/**
* Setup the component
Expand All @@ -47,6 +25,10 @@ export default class ClickToRevealComponent extends ClickToCopyComponent {
if (column && column.cellComponentArgs) {
this.clickToCopy = column.cellComponentArgs.clickToCopy ?? false;
}

if (column && column.cellComponentArgs) {
this.wrapperClass = column.cellComponentArgs.wrapperClass ?? '';
}
}

/**
Expand Down
1 change: 0 additions & 1 deletion addon/components/dashboard.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ export default class DashboardComponent extends Component {
@service modalsManager;
@service fetch;
@service dashboard;
@service universe;

/**
* Creates an instance of DashboardComponent.
Expand Down
8 changes: 5 additions & 3 deletions addon/components/dashboard/create.hbs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
<div class="fleetbase-dashboard-grid" ...attributes>
<GridStack @options={{this.gridOptions}} @onChange={{this.onChangeGrid}}>
{{#each @dashboard.widgets as |widget|}}
{{#let (resolve-component widget.component) as |ResolvedComponent|}}
{{#if ResolvedComponent}}
{{#let (resolve-component widget.component) as |componentDefinition|}}
{{#if componentDefinition}}
<GridStackItem id={{widget.id}} @options={{spread-widget-options (hash id=widget.id options=widget.grid_options)}} class="relative">
{{component ResolvedComponent widget=widget options=widget.options}}
<LazyEngineComponent @component={{componentDefinition}} as |resolvedComponent|>
{{component resolvedComponent widget=widget options=widget.options}}
</LazyEngineComponent>

{{#if @isEdit}}
<div class="absolute top-2 right-2">
Expand Down
24 changes: 17 additions & 7 deletions addon/components/dashboard/widget-panel.hbs
Original file line number Diff line number Diff line change
@@ -1,25 +1,35 @@
<Overlay @isOpen={{@isOpen}} @onLoad={{this.setOverlayContext}} @position="right" @noBackdrop={{true}} @fullHeight={{true}} @width={{or this.width @width "400px"}}>
<Overlay::Header @title={{t "component.dashboard-widget-panel.select-widgets"}} @hideStatusDot={{true}} @titleWrapperClass="leading-5">
<Overlay::Header @title={{t "dashboard-widget-panel.select-widgets"}} @hideStatusDot={{true}} @titleWrapperClass="leading-5 text-gray-800 dark:text-white">
<:actions>
<div class="flex flex-1 justify-end">
<Button @type="default" @icon="times" @helpText={{t "component.dashboard-widget-panel.close-and-save"}} @onClick={{this.onPressClose}} />
<Button @type="default" @icon="times" @helpText={{t "dashboard-widget-panel.close-and-save"}} @onClick={{this.onPressClose}} />
</div>
</:actions>
</Overlay::Header>

<Overlay::Body @wrapperClass="new-service-rate-overlay-body px-4 space-y-4 pt-4">
<Overlay::Body @wrapperClass="new-service-rate-overlay-body px-1 space-y-4 pt-2">
<div>
<Input
@type="text"
@value={{this.searchQuery}}
placeholder={{or (t "dashboard-widget-panel.filter-widgets") "Filter widgets by keyword"}}
class="w-full form-input form-input-sm"
{{on "input" this.updateSearchQuery}}
/>
</div>

<div class="grid grid-cols-1 gap-4 text-xs dark:text-gray-100">
{{#each this.availableWidgets as |widget|}}
<div
class="rounded-lg border border-gray-300 bg-white dark:border-gray-700 dark:bg-gray-800 hover:bg-gray-100 dark:hover:bg-gray-700 transition-all duration-300 ease-in-out shadow-md px-4 py-2 cursor-pointer"
{{on "click" (fn this.addWidgetToDashboard widget)}}
{{on "click" (perform this.addWidgetToDashboard widget)}}
>
<div class="flex flex-row items-center leading-6 mb-2.5">
<div class="w-8 flex items-center justify-start">
<div class="flex flex-row items-center leading-7 mb-2">
<div class="w-7 flex items-center justify-start">
<FaIcon @icon={{widget.icon}} class="text-lg text-gray-600 dark:text-gray-300" />
</div>
<p class="text-sm truncate font-semibold dark:text-gray-100 text-gray-800">
{{t "component.dashboard-widget-panel.widget-name" widgetName=widget.name}}
{{t "dashboard-widget-panel.widget-name" widgetName=widget.name}}
</p>
</div>
<div>
Expand Down
74 changes: 61 additions & 13 deletions addon/components/dashboard/widget-panel.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,72 @@ import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
import { inject as service } from '@ember/service';
import { action } from '@ember/object';
import { task } from 'ember-concurrency';
import { ExtensionComponent } from '@fleetbase/ember-core/contracts';

export default class DashboardWidgetPanelComponent extends Component {
@service universe;
@tracked availableWidgets = [];
@service('universe/widget-service') widgetService;
@service notifications;
@tracked defaultDashboardId = this.args.defaultDashboardId ?? 'dashboard';
@tracked dashboard;
@tracked isOpen = true;
@service notifications;
@tracked searchQuery = '';

/**
* Constructs the component and applies initial state.
*/
constructor(owner, { dashboard, defaultDashboardId = 'dashboard' }) {
super(...arguments);
this.availableWidgets = this.universe.getWidgets(defaultDashboardId);
this.defaultDashboardId = defaultDashboardId;
this.dashboard = dashboard;
}

/**
* Gets available widgets for the dashboard.
* Computed as a getter to ensure reactivity when widgets are registered.
* @returns {Array} Available widgets
*/
get availableWidgets() {
const dashboardId = this.args.defaultDashboardId || this.defaultDashboardId || 'dashboard';
const widgets = this.widgetService.getWidgets(dashboardId);

// Filter widgets by search query
if (this.searchQuery && this.searchQuery.trim()) {
const query = this.searchQuery.toLowerCase().trim();
return widgets.filter((widget) => {
const name = (widget.name || '').toLowerCase();
const description = (widget.description || '').toLowerCase();
return name.includes(query) || description.includes(query);
});
}

return widgets;
}

/**
* Adds a new available widget to the current dashboard.
*
* @param {Component|Widget|string} widget
* @memberof DashboardWidgetPanelComponent
*/
@task *addWidgetToDashboard(widget) {
// If widget is a component definition/class
if (typeof widget.component === 'function') {
widget.component = widget.component.name;
}

// If using extension component definition
if (widget.component instanceof ExtensionComponent) {
widget.component = widget.component.toString();
}

try {
yield this.dashboard.addWidget(widget);
} catch (err) {
this.notifications.serverError(err);
}
}

/**
* Sets the overlay context.
*
Expand All @@ -33,15 +82,14 @@ export default class DashboardWidgetPanelComponent extends Component {
}
}

@action addWidgetToDashboard(widget) {
// If widget is a component definition/class
if (typeof widget.component === 'function') {
widget.component = widget.component.name;
}

this.args.dashboard.addWidget(widget).catch((error) => {
this.notifications.serverError(error);
});
/**
* Updates the search query
*
* @action
* @param {Event} event
*/
@action updateSearchQuery(event) {
this.searchQuery = event.target.value;
}

/**
Expand Down
4 changes: 2 additions & 2 deletions addon/components/layout/sidebar/item.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@
>
<div class="next-nav-item-icon-container {{@iconWrapperClass}}">
{{#if @iconComponent}}
{{component @iconComponent options=@iconComponentOptions}}
{{component (lazy-engine-component @iconComponent) options=@iconComponentOptions}}
{{else if @icon}}
<FaIcon @prefix={{@iconPrefix}} @icon={{@icon}} @size={{or @iconSize "xs"}} class={{@iconClass}} />
{{/if}}
</div>
<div class="truncate w-10/12 {{@itemWrapperClass}}">{{yield}}</div>
<div class="next-nav-item-right-side {{@itemRightSideContainerClass}}">
{{#if @rightSideComponent}}
{{component @rightSideComponent context=@rightSideComponentContext}}
{{component (lazy-engine-component @rightSideComponent) context=@rightSideComponentContext}}
{{else}}
{{#if @rightSideStatus}}
<span class="ml-2 {{@rightSideStatusContainerClass}}">
Expand Down
8 changes: 8 additions & 0 deletions addon/components/lazy-engine-component.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{{#if this.resolvedComponent}}
{{#if (has-block)}}
{{yield this.resolvedComponent this.params}}
{{else}}
{{component this.resolvedComponent params=this.params}}
{{/if}}
{{/if}}
<div {{did-update this.handleChange @component @params}} />
Loading