diff --git a/dashboard/src/Index.vue b/dashboard/src/Index.vue
index ae1c80ef8..f7af936c5 100644
--- a/dashboard/src/Index.vue
+++ b/dashboard/src/Index.vue
@@ -34,6 +34,7 @@ import EmailSettingsView from './views/EmailSettingsView.vue';
import EmailEventlogView from './views/EmailEventlogView.vue';
import EventlogView from './views/EventlogView.vue';
import NetworkView from './views/NetworkView.vue';
+import NotificationsView from './views/NotificationsView.vue';
import ProfileView from './views/ProfileView.vue';
import ServicesView from './views/ServicesView.vue';
import SystemSettingsView from './views/SystemSettingsView.vue';
@@ -64,6 +65,7 @@ const VIEWS = Object.freeze({
EMAIL_EVENTLOG: '#/email-eventlog',
SERVER: '#/server',
NETWORK: '#/network',
+ NOTIFICATIONS: '#/notifications',
PROFILE: '#/profile',
SERVICES: '#/services',
SYSTEM_SETTINGS: '#/system-settings',
@@ -319,6 +321,8 @@ function onHashChange() {
view.value = VIEWS.EMAIL_EVENTLOG;
} else if (v === VIEWS.SERVER && profile.value.isAtLeastAdmin) {
view.value = VIEWS.SERVER;
+ } else if (v === VIEWS.NOTIFICATIONS && profile.value.isAtLeastAdmin) {
+ view.value = VIEWS.NOTIFICATIONS;
} else if (v === VIEWS.NETWORK && profile.value.isAtLeastAdmin) {
view.value = VIEWS.NETWORK;
} else if (v === VIEWS.PROFILE) {
@@ -481,6 +485,7 @@ onUnmounted(() => {
+
diff --git a/dashboard/src/components/Headerbar.vue b/dashboard/src/components/Headerbar.vue
index 4928954b9..6bbe37a38 100644
--- a/dashboard/src/components/Headerbar.vue
+++ b/dashboard/src/components/Headerbar.vue
@@ -6,9 +6,7 @@ const t = i18n.t;
import { onMounted, onUnmounted, ref, useTemplateRef, inject } from 'vue';
import { marked } from 'marked';
-import { eachLimit } from 'async';
-import { Menu, Button, Popover, Icon, InputDialog, Spinner } from '@cloudron/pankow';
-import { prettyDate, prettyLongDate } from '@cloudron/pankow/utils';
+import { Menu, Popover, Icon, InputDialog, Spinner } from '@cloudron/pankow';
import NotificationsModel from '../models/NotificationsModel.js';
import ServicesModel from '../models/ServicesModel.js';
import ProfileModel from '../models/ProfileModel.js';
@@ -28,47 +26,10 @@ const servicesModel = ServicesModel.create();
const profileModel = ProfileModel.create();
const notificationModel = NotificationsModel.create();
-const notificationButton = useTemplateRef('notificationButton');
-const notificationPopover = useTemplateRef('notificationPopover');
const notifications = ref([]);
-const notificationsAllBusy = ref(false);
-
-function onOpenNotifications(popover, event, elem) {
- popover.open(event, elem);
-
- // close after 2 seconds if there is nothing to show
- if (notifications.value.length === 0) setTimeout(popover.close, 2000);
-}
-
-async function onMarkNotificationRead(notification) {
- notification.busy = true;
- const [error] = await notificationModel.update(notification.id, true);
- if (error) return console.error(error);
-
- await refresh();
-
- // close after 2 seconds if there is nothing to show
- if (notifications.value.length === 0) setTimeout(notificationPopover.value.close, 2000);
-}
-
-async function onMarkAllNotificationRead() {
- notificationsAllBusy.value = true;
-
- await eachLimit(notifications.value, 5, async (notification) => {
- notification.busy = true;
- const [error] = await notificationModel.update(notification.id, true);
- if (error) return console.error(error);
- });
-
- await refresh();
-
- notificationsAllBusy.value = false;
-
- if (notifications.value.length === 0) setTimeout(notificationPopover.value.close, 2000);
-}
async function refresh() {
- const [error, result] = await notificationModel.list();
+ const [error, result] = await notificationModel.list(false);
if (error) return console.error(error);
result.forEach(n => {
@@ -150,30 +111,6 @@ onUnmounted(() => {
-
-
-
-
-
-
- {{ notification.title }}
- {{ prettyDate(notification.creationTime) }}
-
-
-
-
-
{{ JSON.stringify(notification.messageJson, null, 4) }}
-
-
-
-
-
-
- {{ $t('notifications.allCaughtUp') }}
-
-
-
-
{{ $t('support.help.title') }}
@@ -199,7 +136,7 @@ onUnmounted(() => {
-
+
@@ -249,40 +186,6 @@ onUnmounted(() => {
border-bottom: 1px solid var(--pankow-input-border-color);
}
-.notification-item {
- margin-bottom: 10px;
- padding-bottom: 10px;
- cursor: pointer;
- border-bottom: 1px solid var(--pankow-input-border-color);
- position: relative;
-}
-
-.notification-item-title {
- display: flex;
- justify-content: space-between;
- align-items: center;
- gap: 6px;
-}
-
-.notification-item-date {
- font-size: 10px;
-}
-
-.notification-read-button {
- visibility: hidden;
- margin-right: 10px;
-}
-
-.notification-item:hover .notification-read-button {
- visibility: visible;
-}
-
-@media (hover: none) {
- .notification-item .notification-read-button {
- visibility: visible;
- }
-}
-
.subscription-expired {
background-color: var(--pankow-color-danger);
color: white;
diff --git a/dashboard/src/models/NotificationsModel.js b/dashboard/src/models/NotificationsModel.js
index b9a01050e..e33480e31 100644
--- a/dashboard/src/models/NotificationsModel.js
+++ b/dashboard/src/models/NotificationsModel.js
@@ -6,10 +6,17 @@ function create() {
const accessToken = localStorage.token;
return {
- async list(acknowledged = false) {
+ async list(acknowledged = null) {
+ const query = {
+ access_token: accessToken,
+ per_page: 1000
+ };
+
+ if (acknowledged !== null) query.acknowledged = !!acknowledged;
+
let result;
try {
- result = await fetcher.get(`${API_ORIGIN}/api/v1/notifications`, { acknowledged, access_token: accessToken, per_page: 1000 });
+ result = await fetcher.get(`${API_ORIGIN}/api/v1/notifications`, query);
} catch (e) {
return [e];
}
diff --git a/dashboard/src/views/NotificationsView.vue b/dashboard/src/views/NotificationsView.vue
new file mode 100644
index 000000000..758808a2c
--- /dev/null
+++ b/dashboard/src/views/NotificationsView.vue
@@ -0,0 +1,160 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ notification.title }}
+
{{ prettyDate(notification.creationTime) }}
+
+
+
+
+
+
+
+
+
+
+
+