Skip to content

Commit 8d44d8a

Browse files
committed
feat: implement different toast notification center
1 parent d4824cd commit 8d44d8a

File tree

10 files changed

+63
-88
lines changed

10 files changed

+63
-88
lines changed

spring-boot-admin-server-ui/package-lock.json

Lines changed: 14 additions & 18 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

spring-boot-admin-server-ui/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
"@fortawesome/free-solid-svg-icons": "5.15.4",
1818
"@fortawesome/vue-fontawesome": "3.0.0-5",
1919
"@headlessui/vue": "1.7.0",
20+
"@stekoe/vue-toast-notificationcenter": "https://github.com/SteKoe/vue-toast-notificationcenter/archive/refs/tags/1.0.0-RC2.tar.gz",
2021
"@tailwindcss/forms": "0.5.0",
2122
"@tailwindcss/typography": "0.5.2",
2223
"ansi_up": "5.1.0",
@@ -46,7 +47,6 @@
4647
"vue": "3.2.33",
4748
"vue-i18n": "9.1.9",
4849
"vue-router": "4.0.16",
49-
"vue-toast-notification": "3.0.4",
5050
"vue3-click-away": "1.2.4"
5151
},
5252
"devDependencies": {

spring-boot-admin-server-ui/src/main/frontend/index.js

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,11 @@ import {createApplicationStore, useApplicationStore} from "./composables/useAppl
2929
import {createViewRegistry} from "./composables/ViewRegistry.js";
3030
import { worker } from './mocks/browser';
3131

32-
import VueToast from 'vue-toast-notification';
3332
import './toast-theme.css';
3433

3534
import SbaModalPlugin from "./plugins/modal";
3635
import {useI18n} from "vue-i18n";
36+
import NotificationcenterPlugin from "@stekoe/vue-toast-notificationcenter";
3737

3838
moment.locale(navigator.language.split('-')[0]);
3939

@@ -79,9 +79,8 @@ const app = createApp({
7979

8080
app.use(i18n);
8181
app.use(components);
82-
app.use(VueToast, {
83-
position: 'top-right',
84-
duration: 3_500
82+
app.use(NotificationcenterPlugin, {
83+
duration: 10_000
8584
});
8685
app.use(SbaModalPlugin, {i18n});
8786
app.use(router(viewRegistry.routes));

spring-boot-admin-server-ui/src/main/frontend/mocks/browser.js

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,7 @@ const handler = [
88
...mappingsEndpoint,
99
...liquibaseEndpoints,
1010
...flywayEndpoints,
11-
...auditEventsEndpoint,
12-
rest.post(
13-
'*/actuator/shutdown',
14-
(req, res, ctx) => {
15-
return res(ctx.status(403), ctx.json("Knöterich is pöterich"));
16-
}
17-
),
11+
...auditEventsEndpoint
1812
];
1913

2014
export const worker = setupWorker(...handler)

spring-boot-admin-server-ui/src/main/frontend/services/instance.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
* limitations under the License.
1515
*/
1616

17-
import axios, {redirectOn401} from '../utils/axios.js';
17+
import axios, {redirectOn401, registerErrorToastInterceptor} from '../utils/axios.js';
1818
import waitForPolyfill from '../utils/eventsource-polyfill';
1919
import logtail from '../utils/logtail';
2020
import {concat, from, ignoreElements, Observable} from '../utils/rxjs';
@@ -42,6 +42,7 @@ class Instance {
4242
response => response,
4343
redirectOn401(error => !isInstanceActuatorRequest(error.config.baseURL + error.config.url))
4444
);
45+
registerErrorToastInterceptor(this.axios);
4546
}
4647

4748
hasEndpoint(endpointId) {
Lines changed: 3 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,5 @@
1-
@import "vue-toast-notification/dist/theme-default.css";
1+
@import "@stekoe/vue-toast-notificationcenter/dist/style.css";
22

3-
.v-toast {
4-
@apply p-2 pt-14;
5-
}
6-
7-
.v-toast--fade-out {
8-
animation-name: fadeInDown;
9-
animation-direction: reverse;
10-
}
11-
12-
.v-toast__item {
13-
@apply flex items-center p-2 mb-2 w-full max-w-xs shadow-lg bg-white border border-l-4 border-gray-300 text-black;
14-
}
15-
16-
.v-toast__item--warning {
17-
@apply border-l-orange-500;
18-
}
19-
20-
.v-toast__item--info {
21-
@apply border-l-blue-500;
22-
}
23-
24-
.v-toast__item--error {
25-
@apply border-l-red-500;
26-
}
27-
28-
.v-toast__item--default {
29-
@apply border-l-gray-500;
30-
}
31-
32-
.v-toast__item--success {
33-
@apply border-l-green-500;
34-
}
35-
36-
@keyframes fadeInDown {
37-
from {
38-
opacity: 0;
39-
transform: translate3d(100%, 0, 0)
40-
}
41-
to {
42-
opacity: 1;
43-
transform: none
44-
}
3+
.v-toast-container {
4+
padding: 4em 1em;
455
}

spring-boot-admin-server-ui/src/main/frontend/utils/axios.js

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,13 @@
1515
*/
1616
import sbaConfig from '../sba-config.js'
1717
import axios from 'axios';
18+
import {useNotificationCenter} from "@stekoe/vue-toast-notificationcenter";
19+
import {useI18n} from "vue-i18n";
20+
21+
22+
const nc = useNotificationCenter({
23+
24+
});
1825

1926
axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
2027
axios.defaults.xsrfHeaderName = sbaConfig.csrf.headerName;
@@ -31,3 +38,21 @@ instance.interceptors.response.use(response => response, redirectOn401());
3138
instance.create = axios.create;
3239

3340
export default instance;
41+
42+
export const registerErrorToastInterceptor = (axios) => {
43+
axios.interceptors.response.use(
44+
response => response,
45+
(error) => {
46+
const data = error.request;
47+
let message = `
48+
Request failed: ${data.statusText}<br>
49+
<small>${data.responseURL}</small>
50+
`;
51+
nc.error(message, {
52+
context: data.status ?? 'axios',
53+
title: `Error ${data.status}`,
54+
duration: 10_000
55+
});
56+
}
57+
)
58+
}

spring-boot-admin-server-ui/src/main/frontend/views/applications/index.vue

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -224,30 +224,30 @@ export default {
224224
try {
225225
await item.unregister();
226226
const message = item instanceof Application ? 'applications.unregister_successful' : 'instances.unregister_successful';
227-
this.$toast.success(this.t(message, {name: item.id || item.name}));
227+
this.$notificationCenter.success(this.t(message, {name: item.id || item.name}));
228228
} catch (error) {
229229
const message = item instanceof Application ? 'applications.unregister_failed' : 'instances.unregister_failed';
230-
this.$toast.error(this.t(message, {name: item.id || item.name, error: error.response.status}));
230+
this.$notificationCenter.error(this.t(message, {name: item.id || item.name, error: error.response.status}));
231231
}
232232
},
233233
async shutdown(item) {
234234
try {
235235
await item.shutdown();
236236
const message = item instanceof Application ? 'applications.shutdown_successful' : 'instances.shutdown_successful';
237-
this.$toast.success(this.t(message, {name: item.id || item.name}));
237+
this.$notificationCenter.success(this.t(message, {name: item.id || item.name}));
238238
} catch (error) {
239239
const message = item instanceof Application ? 'applications.shutdown_failed' : 'instances.shutdown_failed';
240-
this.$toast.error(this.t(message, {name: item.id || item.name, error: error.response.status}));
240+
this.$notificationCenter.error(this.t(message, {name: item.id || item.name, error: error.response.status}));
241241
}
242242
},
243243
async restart(item) {
244244
try {
245245
await item.restart();
246246
const message = item instanceof Application ? 'applications.restarted' : 'instances.restarted';
247-
this.$toast.success(this.t(message, {name: item.id || item.name}));
247+
this.$notificationCenter.success(this.t(message, {name: item.id || item.name}));
248248
} catch (error) {
249249
const message = item instanceof Application ? 'applications.restart_failed' : 'instances.restart_failed';
250-
this.$toast.error(this.t(message, {name: item.id || item.name, error: error.response.status}));
250+
this.$notificationCenter.error(this.t(message, {name: item.id || item.name, error: error.response.status}));
251251
}
252252
},
253253
createSubscription() {
@@ -264,7 +264,7 @@ export default {
264264
},
265265
error: error => {
266266
console.warn('Fetching notification filters failed with error:', error);
267-
this.$toast.error(this.t('applications.fetching_notification_filters_failed'));
267+
this.$notificationCenter.error(this.t('applications.fetching_notification_filters_failed'));
268268
}
269269
});
270270
},
@@ -280,7 +280,7 @@ export default {
280280
const response = await NotificationFilter.addFilter(object, ttl);
281281
let notificationFilter = response.data;
282282
this.notificationFilterSubject.next(notificationFilter);
283-
this.$toast.success(
283+
this.$notificationCenter.success(
284284
`${this.t('applications.notifications_suppressed_for', {name: notificationFilter.applicationName || notificationFilter.instanceId})} <strong>${notificationFilter.expiry.fromNow(true)}</strong>.`);
285285
} catch (error) {
286286
console.warn('Adding notification filter failed:', error);
@@ -292,7 +292,7 @@ export default {
292292
try {
293293
await activeFilter.delete();
294294
this.notificationFilterSubject.next(activeFilter.id);
295-
this.$toast.success(this.t('applications.notification_filter.removed'));
295+
this.$notificationCenter.success(this.t('applications.notification_filter.removed'));
296296
} catch (error) {
297297
console.warn('Deleting notification filter failed:', error);
298298
} finally {

spring-boot-admin-server-ui/src/main/frontend/views/instances/details/index.vue

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,8 +192,9 @@ export default {
192192
} catch (error) {
193193
console.warn('Fetching metric index failed:', error);
194194
this.error = error;
195+
} finally {
196+
this.hasLoaded = true;
195197
}
196-
this.hasLoaded = true;
197198
}
198199
}
199200
},

spring-boot-admin-server-ui/src/main/frontend/views/instances/shell/sba-instance-section.vue

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<template>
2-
<section :class="{'loading': loading}">
2+
<section :class="{'loading': showLoadingSpinner}">
33
<slot name="before"/>
44

55
<div class="px-2 md:px-6 py-6">
@@ -46,11 +46,10 @@ export default {
4646
}
4747
},
4848
watch: {
49-
loading: function(newVal, oldVal) {
49+
loading: function (newVal) {
5050
window.clearTimeout(this.debouncedLoader);
51-
5251
this.debouncedLoader = window.setTimeout(() => {
53-
this.loading = newVal;
52+
this.showLoadingSpinner = newVal;
5453
}, 250);
5554
}
5655
}

0 commit comments

Comments
 (0)