/* * File: aiPredictionSlice.ts * Description: Redux slice for AI Prediction state management * Design & Developed by Tech4Biz Solutions * Copyright (c) Spurrin Innovations. All rights reserved. */ import { createSlice, createAsyncThunk, PayloadAction } from '@reduxjs/toolkit'; import { AIPredictionCase, AIPredictionState, AIPredictionStats, AIPredictionAPIResponse } from '../types'; import { aiPredictionAPI } from '../services'; // ============================================================================ // ASYNC THUNKS // ============================================================================ /** * Fetch AI Predictions Async Thunk * * Purpose: Fetch AI prediction results from API * * @param token - Authentication token * @param params - Optional query parameters for filtering * @returns Promise with AI prediction data or error */ export const fetchAIPredictions = createAsyncThunk( 'aiPrediction/fetchAIPredictions', async (payload: { token: string; params?: { page?: number; limit?: number; urgency?: string; severity?: string; category?: string; search?: string; } }, { rejectWithValue }) => { try { const response: any = await aiPredictionAPI.getAllPredictions(payload.token, payload.params); console.log('AI predictions response:', response); if (response.ok && response.data && response.data.success) { // Add additional metadata to each case for UI purposes const enhancedCases = response.data.data.map((aiCase: AIPredictionCase) => ({ ...aiCase, created_at: new Date().toISOString(), updated_at: new Date().toISOString(), review_status: 'pending' as const, priority: getPriorityFromPrediction(aiCase.prediction) })); console.log('Enhanced AI prediction cases:', enhancedCases); return { cases: enhancedCases as AIPredictionCase[], total: response.data.total || enhancedCases.length, page: response.data.page || 1, limit: response.data.limit || 20 }; } else { // Fallback to mock data for development const mockData = generateMockAIPredictions(); return { cases: mockData, total: mockData.length, page: 1, limit: 20 }; } } catch (error: any) { console.error('Fetch AI predictions error:', error); return rejectWithValue(error.message || 'Failed to fetch AI predictions.'); } } ); /** * Fetch AI Prediction Case Details Async Thunk * * Purpose: Fetch detailed information for a specific AI prediction case * * @param caseId - AI prediction case ID * @param token - Authentication token * @returns Promise with case details or error */ export const fetchAIPredictionDetails = createAsyncThunk( 'aiPrediction/fetchAIPredictionDetails', async (payload: { caseId: string; token: string }, { rejectWithValue }) => { try { const response: any = await aiPredictionAPI.getCaseDetails(payload.caseId, payload.token); if (response.ok && response.data) { return response.data as AIPredictionCase; } else { // Fallback to mock data const mockCase = generateMockAIPredictions().find(c => c.patid === payload.caseId); if (mockCase) { return mockCase; } throw new Error('Case not found'); } } catch (error: any) { console.error('Fetch AI prediction details error:', error); return rejectWithValue(error.message || 'Failed to fetch case details.'); } } ); /** * Update Case Review Async Thunk * * Purpose: Update review status of an AI prediction case * * @param caseId - Case ID to update * @param reviewData - Review data * @param token - Authentication token * @returns Promise with updated case or error */ export const updateCaseReview = createAsyncThunk( 'aiPrediction/updateCaseReview', async (payload: { caseId: string; reviewData: { review_status: 'pending' | 'reviewed' | 'confirmed' | 'disputed'; reviewed_by?: string; review_notes?: string; priority?: 'critical' | 'high' | 'medium' | 'low'; }; token: string; }, { rejectWithValue }) => { try { const response: any = await aiPredictionAPI.updateCaseReview( payload.caseId, payload.reviewData, payload.token ); if (response.ok && response.data) { return { caseId: payload.caseId, ...payload.reviewData, updated_at: new Date().toISOString() }; } else { throw new Error('Failed to update case review'); } } catch (error: any) { console.error('Update case review error:', error); return rejectWithValue(error.message || 'Failed to update case review.'); } } ); /** * Fetch AI Prediction Statistics Async Thunk * * Purpose: Fetch statistics for AI predictions dashboard * * @param token - Authentication token * @param timeRange - Time range filter * @returns Promise with statistics data or error */ export const fetchAIPredictionStats = createAsyncThunk( 'aiPrediction/fetchAIPredictionStats', async (payload: { token: string; timeRange?: 'today' | 'week' | 'month' }, { rejectWithValue }) => { try { const response: any = await aiPredictionAPI.getPredictionStats(payload.token, payload.timeRange); if (response.ok && response.data) { return response.data as AIPredictionStats; } else { // Fallback to mock stats return generateMockStats(); } } catch (error: any) { console.error('Fetch AI prediction stats error:', error); return rejectWithValue(error.message || 'Failed to fetch statistics.'); } } ); // ============================================================================ // HELPER FUNCTIONS // ============================================================================ /** * Get Priority from AI Prediction * * Purpose: Determine case priority based on AI prediction results */ function getPriorityFromPrediction(prediction: any): 'critical' | 'high' | 'medium' | 'low' { if (prediction.clinical_urgency === 'emergency' || prediction.primary_severity === 'high') { return 'critical'; } if (prediction.clinical_urgency === 'urgent' || prediction.primary_severity === 'medium') { return 'high'; } if (prediction.clinical_urgency === 'moderate' || prediction.primary_severity === 'low') { return 'medium'; } return 'low'; } /** * Generate Mock AI Predictions * * Purpose: Generate mock data for development and testing */ function generateMockAIPredictions(): AIPredictionCase[] { return [ { patid: "demogw05-08-2017", hospital_id: "eec24855-d8ae-4fad-8e54-af0480343dc2", prediction: { label: "midline shift", finding_type: "pathology", clinical_urgency: "urgent", confidence_score: 0.996, finding_category: "abnormal", primary_severity: "high", anatomical_location: "brain" }, created_at: "2024-01-15T10:30:00Z", updated_at: "2024-01-15T10:30:00Z", review_status: "pending", priority: "critical" }, { patid: "demo-patient-002", hospital_id: "eec24855-d8ae-4fad-8e54-af0480343dc2", prediction: { label: "normal brain", finding_type: "no_pathology", clinical_urgency: "routine", confidence_score: 0.892, finding_category: "normal", primary_severity: "none", anatomical_location: "not_applicable" }, created_at: "2024-01-15T09:15:00Z", updated_at: "2024-01-15T09:15:00Z", review_status: "reviewed", priority: "low" }, { patid: "demo-patient-003", hospital_id: "eec24855-d8ae-4fad-8e54-af0480343dc2", prediction: { label: "hemorrhage", finding_type: "pathology", clinical_urgency: "emergency", confidence_score: 0.945, finding_category: "critical", primary_severity: "high", anatomical_location: "temporal lobe" }, created_at: "2024-01-15T11:45:00Z", updated_at: "2024-01-15T11:45:00Z", review_status: "confirmed", priority: "critical" } ]; } /** * Generate Mock Statistics * * Purpose: Generate mock statistics for development */ function generateMockStats(): AIPredictionStats { return { totalCases: 156, criticalCases: 23, urgentCases: 45, reviewedCases: 89, pendingCases: 67, averageConfidence: 0.887, todaysCases: 12, weeklyTrend: 15.4 }; } // ============================================================================ // INITIAL STATE // ============================================================================ /** * Initial AI Prediction State * * Purpose: Define the initial state for AI predictions * * Features: * - Prediction cases list and management * - Current case details * - Loading states for async operations * - Error handling and messages * - Search and filtering * - Pagination support * - Cache management */ const initialState: AIPredictionState = { // Prediction data predictionCases: [], currentCase: null, // Loading states isLoading: false, isRefreshing: false, isLoadingCaseDetails: false, // Error handling error: null, // Search and filtering searchQuery: '', selectedUrgencyFilter: 'all', selectedSeverityFilter: 'all', selectedCategoryFilter: 'all', sortBy: 'date', sortOrder: 'desc', // Pagination currentPage: 1, itemsPerPage: 20, totalItems: 0, // Cache management lastUpdated: null, cacheExpiry: null, // UI state showFilters: false, selectedCaseIds: [], }; // ============================================================================ // AI PREDICTION SLICE // ============================================================================ /** * AI Prediction Slice * * Purpose: Redux slice for AI prediction state management * * Features: * - AI prediction data management * - Search and filtering * - Case review management * - Pagination * - Caching * - Error handling * - Loading states */ const aiPredictionSlice = createSlice({ name: 'aiPrediction', initialState, reducers: { /** * Clear Error Action * * Purpose: Clear AI prediction errors */ clearError: (state) => { state.error = null; }, /** * Set Search Query Action * * Purpose: Set search query for AI predictions */ setSearchQuery: (state, action: PayloadAction) => { state.searchQuery = action.payload; state.currentPage = 1; // Reset to first page when searching }, /** * Set Urgency Filter Action * * Purpose: Set urgency filter for AI predictions */ setUrgencyFilter: (state, action: PayloadAction) => { state.selectedUrgencyFilter = action.payload; state.currentPage = 1; // Reset to first page when filtering }, /** * Set Severity Filter Action * * Purpose: Set severity filter for AI predictions */ setSeverityFilter: (state, action: PayloadAction) => { state.selectedSeverityFilter = action.payload; state.currentPage = 1; // Reset to first page when filtering }, /** * Set Category Filter Action * * Purpose: Set category filter for AI predictions */ setCategoryFilter: (state, action: PayloadAction) => { state.selectedCategoryFilter = action.payload; state.currentPage = 1; // Reset to first page when filtering }, /** * Set Sort Action * * Purpose: Set sort options for AI predictions */ setSort: (state, action: PayloadAction<{ by: 'date' | 'urgency' | 'confidence' | 'severity'; 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) => { state.currentPage = action.payload; }, /** * Set Items Per Page Action * * Purpose: Set items per page for pagination */ setItemsPerPage: (state, action: PayloadAction) => { state.itemsPerPage = action.payload; state.currentPage = 1; // Reset to first page when changing items per page }, /** * Set Current Case Action * * Purpose: Set the currently selected AI prediction case */ setCurrentCase: (state, action: PayloadAction) => { state.currentCase = action.payload; }, /** * Update Case in List Action * * Purpose: Update an AI prediction case in the list */ updateCaseInList: (state, action: PayloadAction) => { const index = state.predictionCases.findIndex(case_ => case_.patid === action.payload.patid); if (index !== -1) { state.predictionCases[index] = action.payload; } // Update current case if it's the same case if (state.currentCase && state.currentCase.patid === action.payload.patid) { state.currentCase = action.payload; } }, /** * Toggle Show Filters Action * * Purpose: Toggle the display of filter options */ toggleShowFilters: (state) => { state.showFilters = !state.showFilters; }, /** * Clear All Filters Action * * Purpose: Reset all filters to default values */ clearAllFilters: (state) => { state.searchQuery = ''; state.selectedUrgencyFilter = 'all'; state.selectedSeverityFilter = 'all'; state.selectedCategoryFilter = 'all'; state.currentPage = 1; }, /** * Select Case Action * * Purpose: Add/remove case from selected cases */ toggleCaseSelection: (state, action: PayloadAction) => { const caseId = action.payload; const index = state.selectedCaseIds.indexOf(caseId); if (index === -1) { state.selectedCaseIds.push(caseId); } else { state.selectedCaseIds.splice(index, 1); } }, /** * Clear Selected Cases Action * * Purpose: Clear all selected cases */ clearSelectedCases: (state) => { state.selectedCaseIds = []; }, /** * Clear Cache Action * * Purpose: Clear AI prediction data cache */ clearCache: (state) => { state.predictionCases = []; state.currentCase = null; state.lastUpdated = null; state.cacheExpiry = null; }, }, extraReducers: (builder) => { // Fetch AI Predictions builder .addCase(fetchAIPredictions.pending, (state) => { state.isLoading = true; state.error = null; }) .addCase(fetchAIPredictions.fulfilled, (state, action) => { state.isLoading = false; state.predictionCases = action.payload.cases; state.totalItems = action.payload.total; state.lastUpdated = new Date().toLocaleString(); state.cacheExpiry = new Date(Date.now() + 5 * 60 * 1000).toLocaleString(); // 5 minutes state.error = null; }) .addCase(fetchAIPredictions.rejected, (state, action) => { state.isLoading = false; state.error = action.payload as string; }); // Fetch AI Prediction Details builder .addCase(fetchAIPredictionDetails.pending, (state) => { state.isLoadingCaseDetails = true; state.error = null; }) .addCase(fetchAIPredictionDetails.fulfilled, (state, action) => { state.isLoadingCaseDetails = false; state.currentCase = action.payload; state.error = null; }) .addCase(fetchAIPredictionDetails.rejected, (state, action) => { state.isLoadingCaseDetails = false; state.error = action.payload as string; }); // Update Case Review builder .addCase(updateCaseReview.fulfilled, (state, action) => { // Update case in list const index = state.predictionCases.findIndex(case_ => case_.patid === action.payload.caseId); if (index !== -1) { state.predictionCases[index] = { ...state.predictionCases[index], review_status: action.payload.review_status, reviewed_by: action.payload.reviewed_by, priority: action.payload.priority, updated_at: action.payload.updated_at }; } // Update current case if it's the same case if (state.currentCase && state.currentCase.patid === action.payload.caseId) { state.currentCase = { ...state.currentCase, review_status: action.payload.review_status, reviewed_by: action.payload.reviewed_by, priority: action.payload.priority, updated_at: action.payload.updated_at }; } }) .addCase(updateCaseReview.rejected, (state, action) => { state.error = action.payload as string; }); }, }); // ============================================================================ // EXPORTS // ============================================================================ export const { clearError, setSearchQuery, setUrgencyFilter, setSeverityFilter, setCategoryFilter, setSort, setCurrentPage, setItemsPerPage, setCurrentCase, updateCaseInList, toggleShowFilters, clearAllFilters, toggleCaseSelection, clearSelectedCases, clearCache, } = aiPredictionSlice.actions; export default aiPredictionSlice.reducer; /* * End of File: aiPredictionSlice.ts * Design & Developed by Tech4Biz Solutions * Copyright (c) Spurrin Innovations. All rights reserved. */