NeoScan_Radiologist/app/modules/PatientCare/redux/patientCareSlice.ts
2025-08-05 18:01:36 +05:30

475 lines
13 KiB
TypeScript

/*
* File: patientCareSlice.ts
* Description: Patient care state management slice
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/
import { createSlice, createAsyncThunk, PayloadAction } from '@reduxjs/toolkit';
import { Patient, PatientCareState } from '../../../shared/types';
// ============================================================================
// ASYNC THUNKS
// ============================================================================
/**
* Fetch Patients Async Thunk
*
* Purpose: Fetch patients list from API
*
* @returns Promise with patients data or error
*/
export const fetchPatients = createAsyncThunk(
'patientCare/fetchPatients',
async (_, { rejectWithValue }) => {
try {
// TODO: Replace with actual API call
await new Promise(resolve => setTimeout(resolve, 1500));
// Mock patients data
const mockPatients: Patient[] = [
{
id: '1',
mrn: 'MRN001',
firstName: 'John',
lastName: 'Doe',
dateOfBirth: new Date('1985-03-15'),
gender: 'MALE',
age: 38,
bedNumber: 'A1',
roomNumber: '101',
admissionDate: new Date('2024-01-15'),
status: 'ACTIVE',
priority: 'CRITICAL',
department: 'Emergency',
attendingPhysician: 'Dr. Smith',
allergies: [
{
id: '1',
name: 'Penicillin',
severity: 'SEVERE',
reaction: 'Anaphylaxis',
},
],
medications: [
{
id: '1',
name: 'Morphine',
dosage: '2mg',
frequency: 'Every 4 hours',
route: 'IV',
startDate: new Date(),
status: 'ACTIVE',
prescribedBy: 'Dr. Smith',
},
],
vitalSigns: {
bloodPressure: { systolic: 140, diastolic: 90, timestamp: new Date() },
heartRate: { value: 95, timestamp: new Date() },
temperature: { value: 37.2, timestamp: new Date() },
respiratoryRate: { value: 18, timestamp: new Date() },
oxygenSaturation: { value: 98, timestamp: new Date() },
},
medicalHistory: [],
currentDiagnosis: 'Chest pain, rule out MI',
lastUpdated: new Date(),
},
{
id: '2',
mrn: 'MRN002',
firstName: 'Jane',
lastName: 'Smith',
dateOfBirth: new Date('1990-07-22'),
gender: 'FEMALE',
age: 33,
bedNumber: 'B2',
roomNumber: '102',
admissionDate: new Date('2024-01-15'),
status: 'ACTIVE',
priority: 'HIGH',
department: 'Trauma',
attendingPhysician: 'Dr. Johnson',
allergies: [],
medications: [],
vitalSigns: {
bloodPressure: { systolic: 120, diastolic: 80, timestamp: new Date() },
heartRate: { value: 88, timestamp: new Date() },
temperature: { value: 36.8, timestamp: new Date() },
respiratoryRate: { value: 16, timestamp: new Date() },
oxygenSaturation: { value: 99, timestamp: new Date() },
},
medicalHistory: [],
currentDiagnosis: 'Multiple trauma from MVA',
lastUpdated: new Date(),
},
];
return mockPatients;
} catch (error) {
return rejectWithValue('Failed to fetch patients.');
}
}
);
/**
* Fetch Patient Details Async Thunk
*
* Purpose: Fetch detailed patient information
*
* @param patientId - ID of the patient to fetch
* @returns Promise with patient details or error
*/
export const fetchPatientDetails = createAsyncThunk(
'patientCare/fetchPatientDetails',
async (patientId: string, { rejectWithValue }) => {
try {
// TODO: Replace with actual API call
await new Promise(resolve => setTimeout(resolve, 1000));
// Mock patient details (same as above but with more detailed info)
const mockPatient: Patient = {
id: patientId,
mrn: `MRN${patientId.padStart(3, '0')}`,
firstName: 'John',
lastName: 'Doe',
dateOfBirth: new Date('1985-03-15'),
gender: 'MALE',
age: 38,
bedNumber: 'A1',
roomNumber: '101',
admissionDate: new Date('2024-01-15'),
status: 'ACTIVE',
priority: 'CRITICAL',
department: 'Emergency',
attendingPhysician: 'Dr. Smith',
allergies: [
{
id: '1',
name: 'Penicillin',
severity: 'SEVERE',
reaction: 'Anaphylaxis',
},
],
medications: [
{
id: '1',
name: 'Morphine',
dosage: '2mg',
frequency: 'Every 4 hours',
route: 'IV',
startDate: new Date(),
status: 'ACTIVE',
prescribedBy: 'Dr. Smith',
},
],
vitalSigns: {
bloodPressure: { systolic: 140, diastolic: 90, timestamp: new Date() },
heartRate: { value: 95, timestamp: new Date() },
temperature: { value: 37.2, timestamp: new Date() },
respiratoryRate: { value: 18, timestamp: new Date() },
oxygenSaturation: { value: 98, timestamp: new Date() },
},
medicalHistory: [],
currentDiagnosis: 'Chest pain, rule out MI',
lastUpdated: new Date(),
};
return mockPatient;
} catch (error) {
return rejectWithValue('Failed to fetch patient details.');
}
}
);
/**
* Update Patient Async Thunk
*
* Purpose: Update patient information
*
* @param patientData - Updated patient data
* @returns Promise with updated patient or error
*/
export const updatePatient = createAsyncThunk(
'patientCare/updatePatient',
async (patientData: Partial<Patient> & { id: string }, { rejectWithValue }) => {
try {
// TODO: Replace with actual API call
await new Promise(resolve => setTimeout(resolve, 800));
return patientData;
} catch (error) {
return rejectWithValue('Failed to update patient.');
}
}
);
// ============================================================================
// INITIAL STATE
// ============================================================================
/**
* Initial Patient Care State
*
* Purpose: Define the initial state for patient care
*
* Features:
* - Patients list and management
* - Current patient details
* - Loading states for async operations
* - Error handling and messages
* - Search and filtering
*/
const initialState: PatientCareState = {
// Patients data
patients: [],
currentPatient: null,
// Loading states
isLoading: false,
isRefreshing: false,
isLoadingPatientDetails: false,
// Error handling
error: null,
// Search and filtering
searchQuery: '',
selectedFilter: 'all',
sortBy: 'priority',
sortOrder: 'desc',
// Pagination
currentPage: 1,
itemsPerPage: 20,
totalItems: 0,
// Cache
lastUpdated: null,
cacheExpiry: null,
};
// ============================================================================
// PATIENT CARE SLICE
// ============================================================================
/**
* Patient Care Slice
*
* Purpose: Redux slice for patient care state management
*
* Features:
* - Patient data management
* - Search and filtering
* - Pagination
* - Caching
* - Error handling
* - Loading states
*/
const patientCareSlice = createSlice({
name: 'patientCare',
initialState,
reducers: {
/**
* Clear Error Action
*
* Purpose: Clear patient care errors
*/
clearError: (state) => {
state.error = null;
},
/**
* Set Search Query Action
*
* Purpose: Set search query for patients
*/
setSearchQuery: (state, action: PayloadAction<string>) => {
state.searchQuery = action.payload;
state.currentPage = 1; // Reset to first page when searching
},
/**
* Set Filter Action
*
* Purpose: Set patient filter
*/
setFilter: (state, action: PayloadAction<'all' | 'active' | 'discharged' | 'critical'>) => {
state.selectedFilter = action.payload;
state.currentPage = 1; // Reset to first page when filtering
},
/**
* Set Sort Action
*
* Purpose: Set patient sort options
*/
setSort: (state, action: PayloadAction<{ by: string; order: 'asc' | 'desc' }>) => {
state.sortBy = action.payload.by;
state.sortOrder = action.payload.order;
},
/**
* Set Current Page Action
*
* Purpose: Set current page for pagination
*/
setCurrentPage: (state, action: PayloadAction<number>) => {
state.currentPage = action.payload;
},
/**
* Set Items Per Page Action
*
* Purpose: Set items per page for pagination
*/
setItemsPerPage: (state, action: PayloadAction<number>) => {
state.itemsPerPage = action.payload;
state.currentPage = 1; // Reset to first page when changing items per page
},
/**
* Set Current Patient Action
*
* Purpose: Set the currently selected patient
*/
setCurrentPatient: (state, action: PayloadAction<Patient | null>) => {
state.currentPatient = action.payload;
},
/**
* Update Patient in List Action
*
* Purpose: Update a patient in the patients list
*/
updatePatientInList: (state, action: PayloadAction<Patient>) => {
const index = state.patients.findIndex(patient => patient.id === action.payload.id);
if (index !== -1) {
state.patients[index] = action.payload;
}
// Update current patient if it's the same patient
if (state.currentPatient && state.currentPatient.id === action.payload.id) {
state.currentPatient = action.payload;
}
},
/**
* Add Patient Action
*
* Purpose: Add a new patient to the list
*/
addPatient: (state, action: PayloadAction<Patient>) => {
state.patients.unshift(action.payload);
state.totalItems += 1;
},
/**
* Remove Patient Action
*
* Purpose: Remove a patient from the list
*/
removePatient: (state, action: PayloadAction<string>) => {
const index = state.patients.findIndex(patient => patient.id === action.payload);
if (index !== -1) {
state.patients.splice(index, 1);
state.totalItems -= 1;
}
// Clear current patient if it's the same patient
if (state.currentPatient && state.currentPatient.id === action.payload) {
state.currentPatient = null;
}
},
/**
* Clear Cache Action
*
* Purpose: Clear patient data cache
*/
clearCache: (state) => {
state.patients = [];
state.currentPatient = null;
state.lastUpdated = null;
state.cacheExpiry = null;
},
},
extraReducers: (builder) => {
// Fetch Patients
builder
.addCase(fetchPatients.pending, (state) => {
state.isLoading = true;
state.error = null;
})
.addCase(fetchPatients.fulfilled, (state, action) => {
state.isLoading = false;
state.patients = action.payload;
state.totalItems = action.payload.length;
state.lastUpdated = new Date();
state.cacheExpiry = new Date(Date.now() + 5 * 60 * 1000); // 5 minutes
state.error = null;
})
.addCase(fetchPatients.rejected, (state, action) => {
state.isLoading = false;
state.error = action.payload as string;
});
// Fetch Patient Details
builder
.addCase(fetchPatientDetails.pending, (state) => {
state.isLoadingPatientDetails = true;
state.error = null;
})
.addCase(fetchPatientDetails.fulfilled, (state, action) => {
state.isLoadingPatientDetails = false;
state.currentPatient = action.payload;
state.error = null;
})
.addCase(fetchPatientDetails.rejected, (state, action) => {
state.isLoadingPatientDetails = false;
state.error = action.payload as string;
});
// Update Patient
builder
.addCase(updatePatient.fulfilled, (state, action) => {
// Update patient in list
const index = state.patients.findIndex(patient => patient.id === action.payload.id);
if (index !== -1) {
state.patients[index] = { ...state.patients[index], ...action.payload };
}
// Update current patient if it's the same patient
if (state.currentPatient && state.currentPatient.id === action.payload.id) {
state.currentPatient = { ...state.currentPatient, ...action.payload };
}
})
.addCase(updatePatient.rejected, (state, action) => {
state.error = action.payload as string;
});
},
});
// ============================================================================
// EXPORTS
// ============================================================================
export const {
clearError,
setSearchQuery,
setFilter,
setSort,
setCurrentPage,
setItemsPerPage,
setCurrentPatient,
updatePatientInList,
addPatient,
removePatient,
clearCache,
} = patientCareSlice.actions;
export default patientCareSlice.reducer;
/*
* End of File: patientCareSlice.ts
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/