diff --git a/addon/components/chat-tray.js b/addon/components/chat-tray.js
index 8bafac4..07e74fd 100644
--- a/addon/components/chat-tray.js
+++ b/addon/components/chat-tray.js
@@ -4,6 +4,7 @@ import { action } from '@ember/object';
import { inject as service } from '@ember/service';
import { isNone } from '@ember/utils';
import { task } from 'ember-concurrency';
+import calculatePosition from 'ember-basic-dropdown/utils/calculate-position';
import noop from '../utils/noop';
export default class ChatTrayComponent extends Component {
@@ -13,6 +14,7 @@ export default class ChatTrayComponent extends Component {
@service store;
@service modalsManager;
@service currentUser;
+ @service media;
@tracked channels = [];
@tracked unreadCount = 0;
@tracked notificationSound = new Audio('/sounds/message-notification-sound.mp3');
@@ -29,6 +31,26 @@ export default class ChatTrayComponent extends Component {
});
}
+ /**
+ * Calculate dropdown content position.
+ *
+ * @param {HTMLElement} trigger
+ * @param {HTMLElement} content
+ * @return {Object}
+ * @memberof ChatTrayComponent
+ */
+ @action calculatePosition(trigger, content) {
+ if (this.media.isMobile) {
+ content.classList.add('is-mobile');
+ const triggerRect = trigger.getBoundingClientRect();
+ const top = triggerRect.height + triggerRect.top;
+
+ return { style: { left: '0px', right: '0px', top, width: '100%' } };
+ }
+
+ return calculatePosition(...arguments);
+ }
+
willDestroy() {
this.chat.off('chat.feed_updated', this.reloadChannelWithDelay.bind(this));
super.willDestroy(...arguments);
diff --git a/addon/components/dashboard.hbs b/addon/components/dashboard.hbs
new file mode 100644
index 0000000..a7a632e
--- /dev/null
+++ b/addon/components/dashboard.hbs
@@ -0,0 +1,100 @@
+
+
+
+
+
+
+
+ {{#if this.dashboard.isAddingWidget}}
+
+
+
+ {{/if}}
+
\ No newline at end of file
diff --git a/addon/components/dashboard.js b/addon/components/dashboard.js
new file mode 100644
index 0000000..c7984c0
--- /dev/null
+++ b/addon/components/dashboard.js
@@ -0,0 +1,113 @@
+import Component from '@glimmer/component';
+import { action } from '@ember/object';
+import { inject as service } from '@ember/service';
+
+/**
+ * DashboardComponent for managing dashboards in an Ember application.
+ * This component handles actions such as selecting, creating, deleting dashboards,
+ * and managing widget selectors and dashboard editing states.
+ *
+ * @extends Component
+ */
+export default class DashboardComponent extends Component {
+ @service store;
+ @service intl;
+ @service notifications;
+ @service modalsManager;
+ @service fetch;
+ @service dashboard;
+ @service universe;
+
+ /**
+ * Creates an instance of DashboardComponent.
+ * @memberof DashboardComponent
+ */
+ constructor(owner, { defaultDashboardId = 'dashboard', defaultDashboardName = 'Default Dashboard', showPanelWhenZeroWidgets = false }) {
+ super(...arguments);
+ this.dashboard.showPanelWhenZeroWidgets = showPanelWhenZeroWidgets;
+ this.dashboard.loadDashboards.perform(defaultDashboardId, defaultDashboardName);
+ }
+
+ /**
+ * Action to select a dashboard.
+ * @param {Object} dashboard - The dashboard to be selected.
+ */
+ @action selectDashboard(dashboard) {
+ this.dashboard.selectDashboard.perform(dashboard);
+ }
+
+ /**
+ * Sets the context for the widget selector panel.
+ * @param {Object} widgetSelectorContext - The context object for the widget selector.
+ */
+ @action setWidgetSelectorPanelContext(widgetSelectorContext) {
+ this.widgetSelectorContext = widgetSelectorContext;
+ }
+
+ /**
+ * Creates a new dashboard.
+ * @param {Object} dashboard - The dashboard to be created.
+ * @param {Object} [options={}] - Optional parameters for dashboard creation.
+ */
+ @action createDashboard(dashboard, options = {}) {
+ this.modalsManager.show('modals/create-dashboard', {
+ title: this.intl.t('component.dashboard.create-a-new-dashboard'),
+ acceptButtonText: this.intl.t('component.dashboard.confirm-create-dashboard'),
+ confirm: async (modal, done) => {
+ modal.startLoading();
+
+ // Get the name from the modal options
+ const { name } = modal.getOptions();
+
+ await this.dashboard.createDashboard.perform(name);
+ done();
+ },
+ ...options,
+ });
+ }
+
+ /**
+ * Deletes a dashboard.
+ * @param {Object} dashboard - The dashboard to be deleted.
+ * @param {Object} [options={}] - Optional parameters for dashboard deletion.
+ */
+ @action deleteDashboard(dashboard, options = {}) {
+ if (this.dashboard.dashboards?.length === 1) {
+ return this.notifications.error(this.intl.t('component.dashboard.you-cannot-delete-this-dashboard'));
+ }
+
+ this.modalsManager.confirm({
+ title: this.intl.t('component.dashboard.are-you-sure-you-want-delete-dashboard', { dashboardName: dashboard.name }),
+ confirm: async (modal, done) => {
+ modal.startLoading();
+ await this.dashboard.deleteDashboard.perform(dashboard);
+ done();
+ },
+ ...options,
+ });
+ }
+
+ /**
+ * Action to handle the addition of a widget.
+ * @param {boolean} [state=true] - The state to set for adding a widget.
+ */
+ @action onAddingWidget(state = true) {
+ this.dashboard.onAddingWidget(state);
+ }
+
+ /**
+ * Sets the current dashboard.
+ * @param {Object} dashboard - The dashboard to be set as current.
+ */
+ @action setCurrentDashboard(dashboard) {
+ this.dashboard.setCurrentDashboard.perform(dashboard);
+ }
+
+ /**
+ * Changes the editing state of the dashboard.
+ * @param {boolean} [state=true] - The state to set for editing the dashboard.
+ */
+ @action onChangeEdit(state = true) {
+ this.dashboard.onChangeEdit(state);
+ }
+}
diff --git a/addon/components/dashboard/create.hbs b/addon/components/dashboard/create.hbs
new file mode 100644
index 0000000..e197f83
--- /dev/null
+++ b/addon/components/dashboard/create.hbs
@@ -0,0 +1,16 @@
+
+
+ {{#each @dashboard.widgets as |widget|}}
+ {{#if (component-resolvable widget.component)}}
+
+ {{component widget.component options=widget.options}}
+ {{#if @isEdit}}
+
+
+
+ {{/if}}
+
+ {{/if}}
+ {{/each}}
+
+
\ No newline at end of file
diff --git a/addon/components/dashboard/create.js b/addon/components/dashboard/create.js
new file mode 100644
index 0000000..cbad60c
--- /dev/null
+++ b/addon/components/dashboard/create.js
@@ -0,0 +1,99 @@
+import Component from '@glimmer/component';
+import { tracked } from '@glimmer/tracking';
+import { action, computed } from '@ember/object';
+import { inject as service } from '@ember/service';
+
+/**
+ * Component responsible for creating and managing the dashboard layout.
+ * Provides functionalities such as toggling widget float, changing grid layout, and removing widgets.
+ *
+ * @extends Component
+ */
+export default class DashboardCreateComponent extends Component {
+ /**
+ * Notifications service for displaying alerts or errors.
+ * @type {Service}
+ */
+ @service notifications;
+
+ /**
+ * Tracked array to keep track of widgets that have been updated.
+ * @type {Array}
+ */
+ @tracked updatedWidgets = [];
+
+ /**
+ * Action to toggle the floating state of widgets on the grid.
+ */
+ @action toggleFloat() {
+ this.shouldFloat = !this.shouldFloat;
+ }
+
+ /**
+ * Handles changes to the grid layout, such as repositioning or resizing widgets.
+ * Iterates over each widget event detail and updates the corresponding widget's properties if necessary.
+ *
+ * @param {Event} event - Event containing details about the grid change.
+ * @action
+ */
+ @action onChangeGrid(event) {
+ const { dashboard } = this.args;
+
+ event.detail.forEach((currentWidgetEvent) => {
+ const alreadyUpdated = this.updatedWidgets.find((item) => item.id === currentWidgetEvent.id);
+ if (alreadyUpdated || !this.dashboard) {
+ return;
+ }
+
+ const changedWidget = dashboard.widgets.find((widget) => widget.id === currentWidgetEvent.id);
+ if (!changedWidget) {
+ return;
+ }
+
+ const { x, y, w, h } = currentWidgetEvent;
+ const response = changedWidget.updateProperties({
+ grid_options: { x, y, w, h },
+ });
+ if (response) {
+ this.updatedWidgets.push(changedWidget);
+ }
+ });
+ }
+
+ /**
+ * Removes a specified widget from the dashboard.
+ * Performs a removal operation on the dashboard and handles any errors that occur during the process.
+ *
+ * @param {Object} widget - The widget object to be removed.
+ * @action
+ */
+ @action removeWidget(widget) {
+ const { dashboard } = this.args;
+
+ if (dashboard) {
+ dashboard.removeWidget(widget.id).catch((error) => {
+ this.notifications.serverError(error);
+ });
+ }
+ }
+
+ /**
+ * Computed property that returns grid options based on the current edit state.
+ * Configures grid behavior such as floating, animation, and drag and resize capabilities.
+ *
+ * @computed
+ * @returns {Object} An object containing grid configuration options.
+ */
+ @computed('args.isEdit') get gridOptions() {
+ return {
+ float: true,
+ animate: true,
+ acceptWidgets: true,
+ alwaysShowResizeHandle: this.args.isEdit,
+ disableDrag: !this.args.isEdit,
+ disableResize: !this.args.isEdit,
+ resizable: { handles: 'all' },
+ cellHeight: 30,
+ };
+ }
+}
diff --git a/addon/components/dashboard/widget-panel.hbs b/addon/components/dashboard/widget-panel.hbs
new file mode 100644
index 0000000..e6e7969
--- /dev/null
+++ b/addon/components/dashboard/widget-panel.hbs
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+ {{#each this.availableWidgets as |widget|}}
+
+
+
+
+
+
+ {{t "component.dashboard-widget-panel.widget-name" widgetName=widget.name}}
+
+
+
+
{{widget.description}}
+
+
+ {{/each}}
+
+
+
+
\ No newline at end of file
diff --git a/addon/components/dashboard/widget-panel.js b/addon/components/dashboard/widget-panel.js
new file mode 100644
index 0000000..882e62b
--- /dev/null
+++ b/addon/components/dashboard/widget-panel.js
@@ -0,0 +1,60 @@
+import Component from '@glimmer/component';
+import { tracked } from '@glimmer/tracking';
+import { inject as service } from '@ember/service';
+import { action } from '@ember/object';
+
+export default class DashboardWidgetPanelComponent extends Component {
+ @service universe;
+ @tracked availableWidgets = [];
+ @tracked dashboard;
+ @tracked isOpen = true;
+ @service notifications;
+
+ /**
+ * Constructs the component and applies initial state.
+ */
+ constructor(owner, { dashboard, defaultDashboardId = 'dashboard' }) {
+ super(...arguments);
+
+ this.availableWidgets = this.universe.getWidgets(defaultDashboardId);
+ this.dashboard = dashboard;
+ }
+
+ /**
+ * Sets the overlay context.
+ *
+ * @action
+ * @param {OverlayContextObject} overlayContext
+ */
+ @action setOverlayContext(overlayContext) {
+ this.context = overlayContext;
+
+ if (typeof this.args.onLoad === 'function') {
+ this.args.onLoad(...arguments);
+ }
+ }
+
+ @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);
+ });
+ }
+
+ /**
+ * Handles cancel button press.
+ *
+ * @action
+ */
+ @action onPressClose() {
+ this.isOpen = false;
+
+ if (typeof this.args.onClose === 'function') {
+ this.args.onClose();
+ }
+ }
+}
diff --git a/addon/components/layout/header.hbs b/addon/components/layout/header.hbs
index 7c41ba5..bc22f8e 100644
--- a/addon/components/layout/header.hbs
+++ b/addon/components/layout/header.hbs
@@ -3,7 +3,7 @@
- {{#if @showSidebarToggle}}
+ {{#if (and @showSidebarToggle (not (media "isMobile")))}}
{{/if}}
{{#unless (media "isMobile")}}
diff --git a/addon/components/layout/header.js b/addon/components/layout/header.js
index 6d0156e..987e64c 100644
--- a/addon/components/layout/header.js
+++ b/addon/components/layout/header.js
@@ -40,7 +40,7 @@ export default class LayoutHeaderComponent extends Component {
const visibleMenuItems = [];
for (let i = 0; i < headerMenuItems.length; i++) {
const menuItem = headerMenuItems[i];
- if (this.abilities.can(`${menuItem.slug} see extension`)) {
+ if (this.abilities.can(`${menuItem.id} see extension`)) {
visibleMenuItems.pushObject(menuItem);
}
}
@@ -274,6 +274,7 @@ export default class LayoutHeaderComponent extends Component {
href: 'javascript:;',
text: 'Logout',
action: 'invalidateSession',
+ icon: 'person-running',
},
]);
diff --git a/addon/components/layout/header/dropdown.hbs b/addon/components/layout/header/dropdown.hbs
index 0c41576..a0d17db 100644
--- a/addon/components/layout/header/dropdown.hbs
+++ b/addon/components/layout/header/dropdown.hbs
@@ -1,18 +1,19 @@
-
+