/* * File: alertsSlice.ts * Description: Alerts state management slice * Design & Developed by Tech4Biz Solutions * Copyright (c) Spurrin Innovations. All rights reserved. */ import { createSlice, createAsyncThunk, PayloadAction } from '@reduxjs/toolkit'; import { AlertType, AlertsState } from '../../../shared/types'; // ============================================================================ // ASYNC THUNKS // ============================================================================ /** * Fetch Alerts Async Thunk * * Purpose: Fetch alerts from API * * @returns Promise with alerts data or error */ export const fetchAlerts = createAsyncThunk( 'alerts/fetchAlerts', async (_, { rejectWithValue }) => { try { // TODO: Replace with actual API call await new Promise(resolve => setTimeout(resolve, 1000)); // Mock alerts data const mockAlerts: AlertType[] = [ { id: '1', type: 'CRITICAL_FINDING', priority: 'CRITICAL', title: 'Critical Finding Detected', message: 'AI has detected a potential brain bleed in CT scan. Immediate review required.', patientId: '1', patientName: 'John Doe', bedNumber: 'A1', timestamp: new Date(), isRead: false, isAcknowledged: false, actionRequired: true, }, { id: '2', type: 'VITAL_SIGNS_ALERT', priority: 'HIGH', title: 'Vital Signs Alert', message: 'Patient vitals showing concerning trends. Blood pressure elevated.', patientId: '2', patientName: 'Jane Smith', bedNumber: 'B2', timestamp: new Date(Date.now() - 300000), // 5 minutes ago isRead: true, isAcknowledged: true, actionRequired: false, }, ]; return mockAlerts; } catch (error) { return rejectWithValue('Failed to fetch alerts.'); } } ); /** * Acknowledge Alert Async Thunk * * Purpose: Acknowledge an alert * * @param alertId - ID of the alert to acknowledge * @returns Promise with success or error */ export const acknowledgeAlert = createAsyncThunk( 'alerts/acknowledgeAlert', async (alertId: string, { rejectWithValue }) => { try { // TODO: Replace with actual API call await new Promise(resolve => setTimeout(resolve, 500)); return alertId; } catch (error) { return rejectWithValue('Failed to acknowledge alert.'); } } ); /** * Mark Alert as Read Async Thunk * * Purpose: Mark an alert as read * * @param alertId - ID of the alert to mark as read * @returns Promise with success or error */ export const markAlertAsRead = createAsyncThunk( 'alerts/markAlertAsRead', async (alertId: string, { rejectWithValue }) => { try { // TODO: Replace with actual API call await new Promise(resolve => setTimeout(resolve, 300)); return alertId; } catch (error) { return rejectWithValue('Failed to mark alert as read.'); } } ); // ============================================================================ // INITIAL STATE // ============================================================================ /** * Initial Alerts State * * Purpose: Define the initial state for alerts * * Features: * - Alerts list and management * - Loading states for async operations * - Error handling and messages * - Real-time updates tracking */ const initialState: AlertsState = { // Alerts data alerts: [], // Loading states isLoading: false, isRefreshing: false, // Error handling error: null, // Real-time updates lastUpdated: null, unreadCount: 0, criticalCount: 0, // Filters and preferences selectedFilter: 'all', sortBy: 'timestamp', sortOrder: 'desc', }; // ============================================================================ // ALERTS SLICE // ============================================================================ /** * Alerts Slice * * Purpose: Redux slice for alerts state management * * Features: * - Alerts data management * - Real-time updates * - Filtering and sorting * - Error handling * - Loading states */ const alertsSlice = createSlice({ name: 'alerts', initialState, reducers: { /** * Clear Error Action * * Purpose: Clear alerts errors */ clearError: (state) => { state.error = null; }, /** * Set Filter Action * * Purpose: Set alerts filter */ setFilter: (state, action: PayloadAction<'all' | 'critical' | 'unread' | 'acknowledged'>) => { state.selectedFilter = action.payload; }, /** * Set Sort Action * * Purpose: Set alerts sort options */ setSort: (state, action: PayloadAction<{ by: string; order: 'asc' | 'desc' }>) => { state.sortBy = action.payload.by; state.sortOrder = action.payload.order; }, /** * Add Alert Action * * Purpose: Add a new alert */ addAlert: (state, action: PayloadAction) => { state.alerts.unshift(action.payload); state.unreadCount += 1; if (action.payload.priority === 'CRITICAL') { state.criticalCount += 1; } state.lastUpdated = new Date(); }, /** * Remove Alert Action * * Purpose: Remove an alert */ removeAlert: (state, action: PayloadAction) => { const alertIndex = state.alerts.findIndex(alert => alert.id === action.payload); if (alertIndex !== -1) { const alert = state.alerts[alertIndex]; if (!alert.isRead) { state.unreadCount -= 1; } if (alert.priority === 'CRITICAL') { state.criticalCount -= 1; } state.alerts.splice(alertIndex, 1); } }, /** * Update Alert Action * * Purpose: Update an existing alert */ updateAlert: (state, action: PayloadAction<{ id: string; updates: Partial }>) => { const alertIndex = state.alerts.findIndex(alert => alert.id === action.payload.id); if (alertIndex !== -1) { const oldAlert = state.alerts[alertIndex]; state.alerts[alertIndex] = { ...oldAlert, ...action.payload.updates }; // Update counters if (action.payload.updates.isRead !== undefined) { if (action.payload.updates.isRead && !oldAlert.isRead) { state.unreadCount -= 1; } else if (!action.payload.updates.isRead && oldAlert.isRead) { state.unreadCount += 1; } } } }, /** * Clear All Alerts Action * * Purpose: Clear all alerts */ clearAllAlerts: (state) => { state.alerts = []; state.unreadCount = 0; state.criticalCount = 0; }, /** * Mark All as Read Action * * Purpose: Mark all alerts as read */ markAllAsRead: (state) => { state.alerts.forEach(alert => { alert.isRead = true; }); state.unreadCount = 0; }, }, extraReducers: (builder) => { // Fetch Alerts builder .addCase(fetchAlerts.pending, (state) => { state.isLoading = true; state.error = null; }) .addCase(fetchAlerts.fulfilled, (state, action) => { state.isLoading = false; state.alerts = action.payload; state.unreadCount = action.payload.filter(alert => !alert.isRead).length; state.criticalCount = action.payload.filter(alert => alert.priority === 'CRITICAL').length; state.lastUpdated = new Date(); state.error = null; }) .addCase(fetchAlerts.rejected, (state, action) => { state.isLoading = false; state.error = action.payload as string; }); // Acknowledge Alert builder .addCase(acknowledgeAlert.fulfilled, (state, action) => { const alertIndex = state.alerts.findIndex(alert => alert.id === action.payload); if (alertIndex !== -1) { state.alerts[alertIndex].isAcknowledged = true; } }) .addCase(acknowledgeAlert.rejected, (state, action) => { state.error = action.payload as string; }); // Mark Alert as Read builder .addCase(markAlertAsRead.fulfilled, (state, action) => { const alertIndex = state.alerts.findIndex(alert => alert.id === action.payload); if (alertIndex !== -1 && !state.alerts[alertIndex].isRead) { state.alerts[alertIndex].isRead = true; state.unreadCount -= 1; } }) .addCase(markAlertAsRead.rejected, (state, action) => { state.error = action.payload as string; }); }, }); // ============================================================================ // EXPORTS // ============================================================================ export const { clearError, setFilter, setSort, addAlert, removeAlert, updateAlert, clearAllAlerts, markAllAsRead, } = alertsSlice.actions; export default alertsSlice.reducer; /* * End of File: alertsSlice.ts * Design & Developed by Tech4Biz Solutions * Copyright (c) Spurrin Innovations. All rights reserved. */