243 lines
8.2 KiB
TypeScript
243 lines
8.2 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;
|
|
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.
|
|
*/
|