341 lines
9.1 KiB
TypeScript
341 lines
9.1 KiB
TypeScript
/*
|
|
* 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<AlertType>) => {
|
|
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<string>) => {
|
|
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<AlertType> }>) => {
|
|
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.
|
|
*/
|