init
This commit is contained in:
25
worklenz-frontend/src/services/alerts/alertMiddleware.ts
Normal file
25
worklenz-frontend/src/services/alerts/alertMiddleware.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
// src/services/notification/notificationMiddleware.ts
|
||||
|
||||
import { Middleware } from '@reduxjs/toolkit';
|
||||
import { showAlert, hideAlert } from './alertSlice';
|
||||
import alertService from './alertService';
|
||||
|
||||
export const notificationMiddleware: Middleware = store => next => action => {
|
||||
if (showAlert.match(action)) {
|
||||
const notification: any = action.payload;
|
||||
// Show notification using service
|
||||
alertService.error(notification.title, notification.message, notification.duration);
|
||||
|
||||
// Auto-remove notification after duration
|
||||
if (notification.duration !== 0) {
|
||||
setTimeout(
|
||||
() => {
|
||||
store.dispatch(hideAlert(notification.id));
|
||||
},
|
||||
(notification.duration || store.getState().notification.config.duration) * 1000
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return next(action);
|
||||
};
|
||||
67
worklenz-frontend/src/services/alerts/alertService.ts
Normal file
67
worklenz-frontend/src/services/alerts/alertService.ts
Normal file
@@ -0,0 +1,67 @@
|
||||
import { AlertType } from '@/types/alert.types';
|
||||
import DOMPurify from 'dompurify';
|
||||
import { notification } from 'antd';
|
||||
class AlertService {
|
||||
private static instance: AlertService;
|
||||
private activeAlerts: Set<string> = new Set();
|
||||
|
||||
private constructor() {}
|
||||
|
||||
public static getInstance(): AlertService {
|
||||
if (!AlertService.instance) {
|
||||
AlertService.instance = new AlertService();
|
||||
}
|
||||
return AlertService.instance;
|
||||
}
|
||||
|
||||
private sanitizeHtml(content: string): string {
|
||||
return DOMPurify.sanitize(content, {
|
||||
ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'a'],
|
||||
ALLOWED_ATTR: ['href', 'target'],
|
||||
});
|
||||
}
|
||||
|
||||
private show(type: AlertType, title: string, message: string, duration?: number): void {
|
||||
if (this.activeAlerts.has(message)) return;
|
||||
|
||||
const safeTitle = this.sanitizeHtml(title);
|
||||
const safeMessage = this.sanitizeHtml(message);
|
||||
|
||||
this.activeAlerts.add(message);
|
||||
|
||||
notification[type]({
|
||||
message: safeTitle,
|
||||
description: safeMessage,
|
||||
duration: duration || 5,
|
||||
placement: 'topRight',
|
||||
style: { borderRadius: '4px' },
|
||||
onClose: () => {
|
||||
this.activeAlerts.delete(message);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
public success(title: string, message: string, duration?: number): void {
|
||||
this.show('success', title, message, duration);
|
||||
}
|
||||
|
||||
public error(title: string, message: string, duration?: number): void {
|
||||
this.show('error', title, message, duration);
|
||||
}
|
||||
|
||||
public info(title: string, message: string, duration?: number): void {
|
||||
this.show('info', title, message, duration);
|
||||
}
|
||||
|
||||
public warning(title: string, message: string, duration?: number): void {
|
||||
this.show('warning', title, message, duration);
|
||||
}
|
||||
|
||||
public clearAll(): void {
|
||||
notification.destroy();
|
||||
this.activeAlerts.clear();
|
||||
}
|
||||
}
|
||||
|
||||
export const alertService = AlertService.getInstance();
|
||||
export default alertService;
|
||||
43
worklenz-frontend/src/services/alerts/alertSlice.ts
Normal file
43
worklenz-frontend/src/services/alerts/alertSlice.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
import { AlertConfig, AlertState, AlertType } from '@/types/alert.types';
|
||||
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
|
||||
import { notification } from 'antd';
|
||||
import DOMPurify from 'dompurify';
|
||||
|
||||
const initialState: AlertState = {
|
||||
activeAlerts: new Set(),
|
||||
config: {
|
||||
position: 'topRight',
|
||||
duration: 4.5,
|
||||
maxCount: 5,
|
||||
},
|
||||
};
|
||||
|
||||
const alertSlice = createSlice({
|
||||
name: 'alert',
|
||||
initialState,
|
||||
reducers: {
|
||||
showAlert: (
|
||||
state,
|
||||
action: PayloadAction<{
|
||||
type: AlertType;
|
||||
title: string;
|
||||
message: string;
|
||||
duration?: number;
|
||||
}>
|
||||
) => {
|
||||
if (!state.activeAlerts.has(action.payload.message)) {
|
||||
state.activeAlerts.add(action.payload.message);
|
||||
}
|
||||
},
|
||||
hideAlert: (state, action: PayloadAction<string>) => {
|
||||
state.activeAlerts.delete(action.payload);
|
||||
},
|
||||
updateAlertConfig: (state, action: PayloadAction<Partial<AlertConfig>>) => {
|
||||
state.config = { ...state.config, ...action.payload };
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const { showAlert, hideAlert, updateAlertConfig } = alertSlice.actions;
|
||||
|
||||
export default alertSlice.reducer;
|
||||
Reference in New Issue
Block a user