NeoScan_Physician/app/modules/Dashboard/redux/predictionsSlice.ts
2025-08-22 14:57:50 +05:30

250 lines
8.6 KiB
TypeScript

/*
* File: predictionsSlice.ts
* Description: Redux slice for managing AI predictions state
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/
import { createSlice, createAsyncThunk, PayloadAction } from '@reduxjs/toolkit';
import { predictionsAPI } from '../services/predictionsAPI';
import type { PredictionsResponse, PredictionData, PredictionTabType } from '../types/predictions';
// ============================================================================
// ASYNC THUNKS
// ============================================================================
/**
* Fetch All Predictions Async Thunk
*
* Purpose: Fetch all predictions and handle filtering on frontend
*/
export const fetchAllPredictions = createAsyncThunk(
'predictions/fetchAll',
async (token: string, { rejectWithValue }) => {
try {
const response :any = await predictionsAPI.fetchAllPredictions(token);
console.log('dashboard predction data response', response);
if (response.ok && response.data && response.data.data) {
return response.data.data as PredictionData[];
} else {
return rejectWithValue(response.problem || 'Failed to fetch predictions');
}
} catch (error) {
return rejectWithValue('Network error occurred while fetching predictions');
}
}
);
// ============================================================================
// STATE INTERFACE
// ============================================================================
interface PredictionsState {
// Data
allPredictions: PredictionData[];
predictionsWithFeedback: PredictionData[];
predictionsWithoutFeedback: PredictionData[];
// Loading states
isLoading: boolean;
// Error states
error: string | null;
// UI state
activeTab: PredictionTabType;
searchQuery: string;
}
// ============================================================================
// INITIAL STATE
// ============================================================================
const initialState: PredictionsState = {
// Data
allPredictions: [],
predictionsWithFeedback: [],
predictionsWithoutFeedback: [],
// Loading states
isLoading: false,
// Error states
error: null,
// UI state
activeTab: 'with-feedback',
searchQuery: '',
};
// ============================================================================
// PREDICTIONS SLICE
// ============================================================================
const predictionsSlice = createSlice({
name: 'predictions',
initialState,
reducers: {
/**
* Set Active Tab
*
* Purpose: Switch between feedback tabs
*/
setActiveTab: (state, action: PayloadAction<PredictionTabType>) => {
state.activeTab = action.payload;
},
/**
* Set Search Query
*
* Purpose: Update search query for filtering
*/
setSearchQuery: (state, action: PayloadAction<string>) => {
state.searchQuery = action.payload;
},
/**
* Clear Errors
*
* Purpose: Clear error states
*/
clearErrors: (state) => {
state.error = null;
},
/**
* Clear Search
*
* Purpose: Clear search query
*/
clearSearch: (state) => {
state.searchQuery = '';
},
/**
* Filter Predictions
*
* Purpose: Filter predictions based on feedback status
*/
filterPredictions: (state) => {
// Filter predictions with feedback
state.predictionsWithFeedback = state.allPredictions.filter(
prediction => prediction.has_provided_feedback
);
// Filter predictions without feedback
state.predictionsWithoutFeedback = state.allPredictions.filter(
prediction => !prediction.has_provided_feedback
);
},
},
extraReducers: (builder) => {
// ============================================================================
// FETCH ALL PREDICTIONS
// ============================================================================
// Pending
builder.addCase(fetchAllPredictions.pending, (state) => {
state.isLoading = true;
state.error = null;
});
// Fulfilled
builder.addCase(fetchAllPredictions.fulfilled, (state, action: PayloadAction<PredictionData[]>) => {
state.isLoading = false;
state.allPredictions = action.payload;
state.error = null;
// Debug logging to see what's happening with feedback filtering
console.log('🔍 Predictions filtering debug:');
console.log('Total predictions:', action.payload.length);
console.log('Predictions with feedback field:', action.payload.filter(p => p.has_provided_feedback).length);
console.log('Predictions with feedbacks array:', action.payload.filter(p => p.feedbacks && p.feedbacks.length > 0).length);
console.log('Sample prediction feedback data:', action.payload.slice(0, 2).map(p => ({
id: p.id,
has_provided_feedback: p.has_provided_feedback,
feedbacks_count: p.feedbacks?.length || 0,
user_feedback_count: p.user_feedback_count
})));
// Automatically filter predictions after fetching
// Primary filter: use has_provided_feedback field
// Fallback filter: check if feedbacks array has items
state.predictionsWithFeedback = action.payload.filter(
prediction => prediction.has_provided_feedback || (prediction.feedbacks && prediction.feedbacks.length > 0)
);
state.predictionsWithoutFeedback = action.payload.filter(
prediction => !prediction.has_provided_feedback && (!prediction.feedbacks || prediction.feedbacks.length === 0)
);
console.log('Filtered results:');
console.log('With feedback tab:', state.predictionsWithFeedback.length);
console.log('Without feedback tab:', state.predictionsWithoutFeedback.length);
});
// Rejected
builder.addCase(fetchAllPredictions.rejected, (state, action) => {
state.isLoading = false;
state.error = action.error.message || 'Failed to fetch predictions';
});
},
});
// ============================================================================
// ACTIONS
// ============================================================================
export const {
setActiveTab,
setSearchQuery,
clearErrors,
clearSearch,
filterPredictions,
} = predictionsSlice.actions;
// ============================================================================
// SELECTORS
// ============================================================================
export const selectActiveTab = (state: { predictions: PredictionsState }) => state.predictions.activeTab;
export const selectSearchQuery = (state: { predictions: PredictionsState }) => state.predictions.searchQuery;
export const selectAllPredictions = (state: { predictions: PredictionsState }) => state.predictions.allPredictions;
export const selectPredictionsWithFeedback = (state: { predictions: PredictionsState }) => state.predictions.predictionsWithFeedback;
export const selectPredictionsWithoutFeedback = (state: { predictions: PredictionsState }) => state.predictions.predictionsWithoutFeedback;
export const selectIsLoading = (state: { predictions: PredictionsState }) => state.predictions.isLoading;
export const selectError = (state: { predictions: PredictionsState }) => state.predictions.error;
export const selectCurrentPredictions = (state: { predictions: PredictionsState }) => {
const { activeTab, predictionsWithFeedback, predictionsWithoutFeedback } = state.predictions;
// Performance optimization: Direct dataset selection instead of filtering
return activeTab === 'with-feedback' ? predictionsWithFeedback : predictionsWithoutFeedback;
};
// Performance optimization: Pre-computed selectors for instant tab switching
export const selectPredictionsForTab = (state: { predictions: PredictionsState }, tab: PredictionTabType) => {
const { predictionsWithFeedback, predictionsWithoutFeedback } = state.predictions;
return tab === 'with-feedback' ? predictionsWithFeedback : predictionsWithoutFeedback;
};
export const selectCurrentLoadingState = (state: { predictions: PredictionsState }) => {
return state.predictions.isLoading;
};
export const selectCurrentError = (state: { predictions: PredictionsState }) => {
return state.predictions.error;
};
// ============================================================================
// EXPORT
// ============================================================================
export default predictionsSlice.reducer;
/*
* End of File: predictionsSlice.ts
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/