WIP: Notification Settings interface on Admin

This commit is contained in:
TemuulenBM
2023-10-26 17:54:29 +08:00
parent a8b2042d85
commit 74a782f4ea
12 changed files with 214 additions and 5 deletions

View File

@@ -0,0 +1,91 @@
import Controller from '@ember/controller';
import { tracked } from '@glimmer/tracking';
import { action } from '@ember/object';
import { inject as service } from '@ember/service';
import createNotificationKey from '../../../utils/create-notification-key';
export default class ConsoleAdminNotificationsController extends Controller {
@service notifications;
@service fetch;
@tracked notificationSettings = {};
@tracked notificationTransportMethods = ['email', 'sms'];
@tracked isLoading = false;
constructor() {
super(...arguments);
this.getSettings();
}
@action onSelectNotifiable(notification, notifiables) {
const notificationKey = createNotificationKey(notification.definition);
const _notificationSettings = { ...this.notificationSettings };
if (!_notificationSettings[notificationKey]) {
_notificationSettings[notificationKey] = {};
}
_notificationSettings[notificationKey].recipients = notifiables;
_notificationSettings[notificationKey].definition = notification.definition;
_notificationSettings[notificationKey].via = notifiables.map((notifiable) => {
return {
identifier: notifiable.value,
methods: this.notificationTransportMethods,
};
});
this.mutateNotificationSettings(_notificationSettings);
}
mutateNotificationSettings(_notificationSettings = {}) {
this.notificationSettings = {
...this.notificationSettings,
..._notificationSettings,
};
}
/**
* Save notification settings to the server.
*
*
* @action
* @method saveSettings
* @returns {Promise}
* @memberof ConsoleAdminNotificationsController
*/
@action saveSettings() {
const { notificationSettings } = this;
this.isLoading = true;
return this.fetch
.post('notifications/save-settings', { notificationSettings })
.then(() => {
this.notifications.success('Notification settings successfully saved.');
})
.catch((error) => {
this.notifications.serverError(error);
})
.finally(() => {
this.isLoading = false;
});
}
/**
* Fetches and updates notification settings asynchronously.
*
*
* @returns {Promise<void>} A promise for successful retrieval and update, or an error on failure.
*/
getSettings() {
return this.fetch
.get('notifications/get-settings')
.then(({ notificationSettings }) => {
this.notificationSettings = notificationSettings;
})
.catch((error) => {
this.notifications.serverError(error);
});
}
}

View File

@@ -50,6 +50,7 @@ export default class NotificationsController extends Controller {
*
* @param {Object} notification - The notification to select or deselect.
*/
@action selectNotification(notification) {
if (this.selected.includes(notification)) {
this.selected.removeObject(notification);

View File

@@ -0,0 +1,6 @@
import { helper } from '@ember/component/helper';
import createNotificationKey from '../utils/create-notification-key';
export default helper(function getNotificationKey([definition]) {
return createNotificationKey(definition);
});

View File

@@ -43,6 +43,7 @@ Router.map(function () {
});
this.route('branding');
this.route('virtual', { path: '/:slug/:view' });
this.route('notifications');
});
this.mount('@fleetbase/dev-engine', {
@@ -50,6 +51,11 @@ Router.map(function () {
path: 'developers',
});
this.mount('@fleetbase/fleetops-engine', {
as: 'fleet-ops',
path: 'fleet-ops',
});
this.mount('@fleetbase/iam-engine', {
as: 'iam',
path: 'iam',
@@ -59,11 +65,6 @@ Router.map(function () {
as: 'storefront',
path: 'storefront',
});
this.mount('@fleetbase/fleetops-engine', {
as: 'fleet-ops',
path: 'fleet-ops',
});
});
this.route('install');
});

View File

@@ -0,0 +1,20 @@
import Route from '@ember/routing/route';
import { inject as service } from '@ember/service';
import { hash } from 'rsvp';
import groupBy from '@fleetbase/ember-core/utils/group-by';
export default class ConsoleAdminNotificationsRoute extends Route {
@service fetch;
model() {
return hash({
registry: this.fetch.get('notifications/registry'),
notifiables: this.fetch.get('notifications/notifiables'),
});
}
setupController(controller, { registry, notifiables }) {
controller.groupedNotifications = groupBy(registry, 'package');
controller.notifiables = notifiables;
}
}

View File

@@ -3,6 +3,7 @@
<EmberWormhole @to="sidebar-menu-items">
<Layout::Sidebar::Item @route="console.admin.index" @icon="rectangle-list">Overview</Layout::Sidebar::Item>
<Layout::Sidebar::Item @route="console.admin.branding" @icon="palette">Branding</Layout::Sidebar::Item>
<Layout::Sidebar::Item @route="console.admin.notifications" @icon="bell">Notifications</Layout::Sidebar::Item>
{{#each this.universe.adminMenuItems as |menuItem|}}
<Layout::Sidebar::Item @onClick={{fn this.universe.transitionMenuItem "console.admin.virtual" menuItem}} @item={{menuItem}} @icon={{menuItem.icon}}>{{menuItem.title}}</Layout::Sidebar::Item>
{{/each}}

View File

@@ -0,0 +1,33 @@
{{page-title "Notifications"}}
<Layout::Section::Header @title="Notifications" />
<Layout::Section::Body class="overflow-y-scroll h-full">
<div class="container mx-auto h-screen" {{increase-height-by 1200}}>
<div class="max-w-3xl my-10 mx-auto">
{{#each-in this.groupedNotifications as |groupName notifications|}}
<ContentPanel @title={{concat (humanize groupName) " Notification Settings"}} @open={{true}} @pad={{true}} @panelBodyClass="bg-white dark:bg-gray-800">
{{#each notifications as |notification|}}
<InputGroup @name={{notification.name}} @helpText={{notification.description}}>
<div class="fleetbase-model-select fleetbase-power-select ember-model-select">
<PowerSelectMultiple
@searchEnabled={{true}}
@options={{this.notifiables}}
@selected={{get this.notificationSettings (concat (get-notification-key notification.definition) ".recipients")}}
@onChange={{fn this.onSelectNotifiable notification}}
@placeholder="Select notifiables..."
@triggerClass="form-select form-input form-input-sm flex-1"
as |notifiable|
>
{{notifiable.label}}
</PowerSelectMultiple>
</div>
</InputGroup>
{{/each}}
</ContentPanel>
{{/each-in}}
<div class="mt-3 flex items-center justify-end">
<Button @type="primary" @size="lg" @icon="save" @text="Save Changes" @onClick={{this.saveSettings}} @disabled={{this.isLoading}} @isLoading={{this.isLoading}} />
</div>
</div>
</div>
</Layout::Section::Body>

View File

@@ -0,0 +1,6 @@
import { camelize } from '@ember/string';
export default function createNotificationKey(definition) {
const withoutSlashes = definition.replace(/[\W_]+/g, '');
return camelize(withoutSlashes);
}

View File

@@ -0,0 +1,17 @@
import { module, test } from 'qunit';
import { setupRenderingTest } from '@fleetbase/console/tests/helpers';
import { render } from '@ember/test-helpers';
import { hbs } from 'ember-cli-htmlbars';
module('Integration | Helper | get-notification-key', function (hooks) {
setupRenderingTest(hooks);
// TODO: Replace this with your real tests.
test('it renders', async function (assert) {
this.set('inputValue', '1234');
await render(hbs`{{get-notification-key this.inputValue}}`);
assert.dom(this.element).hasText('1234');
});
});

View File

@@ -0,0 +1,12 @@
import { module, test } from 'qunit';
import { setupTest } from '@fleetbase/console/tests/helpers';
module('Unit | Controller | console/admin/notifications', function (hooks) {
setupTest(hooks);
// TODO: Replace this with your real tests.
test('it exists', function (assert) {
let controller = this.owner.lookup('controller:console/admin/notifications');
assert.ok(controller);
});
});

View File

@@ -0,0 +1,11 @@
import { module, test } from 'qunit';
import { setupTest } from '@fleetbase/console/tests/helpers';
module('Unit | Route | console/admin/notifications', function (hooks) {
setupTest(hooks);
test('it exists', function (assert) {
let route = this.owner.lookup('route:console/admin/notifications');
assert.ok(route);
});
});

View File

@@ -0,0 +1,10 @@
import createNotificationKey from '@fleetbase/console/utils/create-notification-key';
import { module, test } from 'qunit';
module('Unit | Utility | create-notification-key', function () {
// TODO: Replace this with your real tests.
test('it works', function (assert) {
let result = createNotificationKey();
assert.ok(result);
});
});