This commit is contained in:
chamikaJ
2025-04-17 18:28:54 +05:30
parent f583291d8a
commit 8825b0410a
2837 changed files with 241385 additions and 127578 deletions

View 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);
};

View 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;

View 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;