475 lines
13 KiB
TypeScript
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.
|
|
*/
|