/* * 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) => { state.activeTab = action.payload; }, /** * Set Search Query * * Purpose: Update search query for filtering */ setSearchQuery: (state, action: PayloadAction) => { 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) => { 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; return activeTab === '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. */