/* * File: patientCareSelectors.ts * Description: Redux selectors for patient care state * Design & Developed by Tech4Biz Solutions * Copyright (c) Spurrin Innovations. All rights reserved. */ import { createSelector } from '@reduxjs/toolkit'; import { RootState } from '../../../store/store'; import { MedicalCase } from '../../../shared/types'; // ============================================================================ // BASE SELECTORS // ============================================================================ /** * Select Patient Care State * * Purpose: Get the entire patient care state */ export const selectPatientCareState = (state: RootState) => state.patientCare; /** * Select Patients * * Purpose: Get the patients array */ export const selectPatients = (state: RootState) => state.patientCare.patients; /** * Select Current Patient * * Purpose: Get the currently selected patient */ export const selectCurrentPatient = (state: RootState) => state.patientCare.currentPatient; /** * Select Patients Loading State * * Purpose: Get the loading state for patients */ export const selectPatientsLoading = (state: RootState) => state.patientCare.isLoading; /** * Select Is Refreshing State * * Purpose: Get the refreshing state for pull-to-refresh */ export const selectIsRefreshing = (state: RootState) => state.patientCare.isRefreshing; /** * Select Patient Details Loading State * * Purpose: Get the loading state for patient details */ export const selectPatientDetailsLoading = (state: RootState) => state.patientCare.isLoadingPatientDetails; /** * Select Patients Error * * Purpose: Get the error state for patients */ export const selectPatientsError = (state: RootState) => state.patientCare.error; /** * Select Search Query * * Purpose: Get the current search query */ export const selectSearchQuery = (state: RootState) => state.patientCare.searchQuery; /** * Select Selected Filter * * Purpose: Get the currently selected filter */ export const selectSelectedFilter = (state: RootState) => state.patientCare.selectedFilter; /** * Select Sort By * * Purpose: Get the current sort option */ export const selectSortBy = (state: RootState) => state.patientCare.sortBy; /** * Select Sort Order * * Purpose: Get the current sort order */ export const selectSortOrder = (state: RootState) => state.patientCare.sortOrder; /** * Select Pagination Info * * Purpose: Get pagination-related state */ export const selectPaginationInfo = (state: RootState) => ({ currentPage: state.patientCare.currentPage, itemsPerPage: state.patientCare.itemsPerPage, totalItems: state.patientCare.totalItems, }); /** * Select Last Updated * * Purpose: Get the last updated timestamp */ export const selectLastUpdated = (state: RootState) => state.patientCare.lastUpdated; // ============================================================================ // COMPUTED SELECTORS // ============================================================================ /** * Select Filtered Patients * * Purpose: Get patients filtered by search query and selected filter */ export const selectFilteredPatients = createSelector( [selectPatients, selectSearchQuery, selectSelectedFilter, selectSortBy, selectSortOrder], (patients, searchQuery, selectedFilter, sortBy, sortOrder) => { let filteredPatients = [...patients]; // Helper function to parse JSON strings safely const parseJsonSafely = (jsonString: string | object) => { if (typeof jsonString === 'object') { return jsonString; } if (typeof jsonString === 'string') { try { return JSON.parse(jsonString); } catch (error) { console.warn('Failed to parse JSON:', error); return {}; } } return {}; }; // Apply filter if (selectedFilter !== 'all') { filteredPatients = filteredPatients.filter( patient => patient.type === selectedFilter ); } // Apply search if (searchQuery.trim()) { const query = searchQuery.toLowerCase().trim(); filteredPatients = filteredPatients.filter(patient => { const patientDetails = parseJsonSafely(patient.patientdetails); const patientData = patientDetails.patientdetails || patientDetails; const name = (patientData.Name || '').toLowerCase(); const patId = (patientData.PatID || '').toLowerCase(); const instName = (patientData.InstName || '').toLowerCase(); const modality = (patientData.Modality || '').toLowerCase(); return ( name.includes(query) || patId.includes(query) || instName.includes(query) || modality.includes(query) ); }); } // Apply sorting filteredPatients.sort((a, b) => { const patientDetailsA = parseJsonSafely(a.patientdetails); const patientDataA = patientDetailsA.patientdetails || patientDetailsA; const patientDetailsB = parseJsonSafely(b.patientdetails); const patientDataB = patientDetailsB.patientdetails || patientDetailsB; let aValue: any; let bValue: any; switch (sortBy) { case 'name': aValue = (patientDataA.Name || '').toLowerCase(); bValue = (patientDataB.Name || '').toLowerCase(); break; case 'age': aValue = parseInt(patientDataA.PatAge || '0'); bValue = parseInt(patientDataB.PatAge || '0'); break; case 'date': default: aValue = new Date(a.created_at).getTime(); bValue = new Date(b.created_at).getTime(); break; } if (aValue < bValue) { return sortOrder === 'asc' ? -1 : 1; } if (aValue > bValue) { return sortOrder === 'asc' ? 1 : -1; } return 0; }); return filteredPatients; } ); /** * Select Critical Patients * * Purpose: Get patients with critical priority */ export const selectCriticalPatients = createSelector( [selectPatients], (patients) => patients.filter(patient => patient.type === 'Critical') ); /** * Select Active Patients * * Purpose: Get patients with active status */ export const selectActivePatients = createSelector( [selectPatients], (patients: MedicalCase[]) => patients.filter((patient: MedicalCase) => { // Parse patient details to check status const parseJsonSafely = (jsonString: string | object) => { if (typeof jsonString === 'object') return jsonString; if (typeof jsonString === 'string') { try { return JSON.parse(jsonString); } catch { return {}; } } return {}; }; const patientDetails = parseJsonSafely(patient.patientdetails); const patientData = patientDetails.patientdetails || patientDetails; return patientData.Status === 'Active'; }) ); /** * Select Patients by Department * * Purpose: Get patients grouped by department */ export const selectPatientsByDepartment = createSelector( [selectPatients], (patients: MedicalCase[]) => { const grouped: { [key: string]: MedicalCase[] } = {}; patients.forEach((patient: MedicalCase) => { const dept = patient.type; // Use case type instead of department if (!grouped[dept]) { grouped[dept] = []; } grouped[dept].push(patient); }); return grouped; } ); /** * Select Patient Statistics * * Purpose: Get statistics about patients */ export const selectPatientStats = createSelector( [selectPatients], (patients: MedicalCase[]) => { const total = patients.length; const critical = patients.filter((p: MedicalCase) => p.type === 'Critical').length; const emergency = patients.filter((p: MedicalCase) => p.type === 'Emergency').length; const routine = patients.filter((p: MedicalCase) => p.type === 'Routine').length; // Parse patient details for age calculation const parseJsonSafely = (jsonString: string | object) => { if (typeof jsonString === 'object') return jsonString; if (typeof jsonString === 'string') { try { return JSON.parse(jsonString); } catch { return {}; } } return {}; }; const totalAge = patients.reduce((sum: number, patient: MedicalCase) => { const patientDetails = parseJsonSafely(patient.patientdetails); const patientData = patientDetails.patientdetails || patientDetails; return sum + parseInt(patientData.PatAge || '0'); }, 0); const averageAge = total > 0 ? Math.round(totalAge / total) : 0; // Case type distribution const caseTypes: { [key: string]: number } = {}; patients.forEach((patient: MedicalCase) => { caseTypes[patient.type] = (caseTypes[patient.type] || 0) + 1; }); return { total, critical, emergency, routine, averageAge, caseTypes, criticalPercentage: total > 0 ? Math.round((critical / total) * 100) : 0, emergencyPercentage: total > 0 ? Math.round((emergency / total) * 100) : 0, }; } ); /** * Select Patient by ID * * Purpose: Get a specific patient by ID * * @param patientId - The ID of the patient to find */ export const selectPatientById = (patientId: string) => createSelector( [selectPatients], (patients) => patients.find(patient => patient.id === patientId) ); /** * Select Patients Need Attention * * Purpose: Get patients that need immediate attention */ export const selectPatientsNeedAttention = createSelector( [selectPatients], (patients) => { return patients.filter(patient => { // Critical patients always need attention if (patient.priority === 'CRITICAL') return true; // Check vital signs for abnormal values const vitals = patient.vitalSigns; // Check blood pressure (hypertensive crisis) if (vitals.bloodPressure.systolic > 180 || vitals.bloodPressure.diastolic > 120) { return true; } // Check heart rate (too high or too low) if (vitals.heartRate.value > 120 || vitals.heartRate.value < 50) { return true; } // Check temperature (fever or hypothermia) if (vitals.temperature.value > 38.5 || vitals.temperature.value < 35) { return true; } // Check oxygen saturation (low) if (vitals.oxygenSaturation.value < 90) { return true; } return false; }); } ); /** * Select Has Data * * Purpose: Check if we have patient data */ export const selectHasPatientData = createSelector( [selectPatients], (patients) => patients.length > 0 ); /** * Select Is Empty State * * Purpose: Check if we should show empty state */ export const selectIsEmptyState = createSelector( [selectPatients, selectPatientsLoading, selectFilteredPatients], (patients, isLoading, filteredPatients) => !isLoading && patients.length > 0 && filteredPatients.length === 0 ); /* * End of File: patientCareSelectors.ts * Design & Developed by Tech4Biz Solutions * Copyright (c) Spurrin Innovations. All rights reserved. */