dashboard modifiedd

This commit is contained in:
yashwin-foxy 2025-08-18 20:30:00 +05:30
parent 467dc0b8cf
commit 266de3b512
13 changed files with 2114 additions and 179 deletions

View File

@ -0,0 +1,337 @@
/*
* File: FeedbackAnalysisPieChart.tsx
* Description: Pie chart component for feedback analysis using react-native-chart-kit
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/
import React from 'react';
import { View, Text, StyleSheet, Dimensions } from 'react-native';
import { PieChart } from 'react-native-chart-kit';
import { theme } from '../../../theme/theme';
// ============================================================================
// TYPES
// ============================================================================
/**
* Feedback Analysis Data Interface
*
* Purpose: Defines the structure of feedback analysis data for pie chart
*/
interface FeedbackAnalysisData {
positive: number;
negative: number;
total: number;
}
/**
* FeedbackAnalysisPieChart Props Interface
*
* Purpose: Defines the props required by the FeedbackAnalysisPieChart component
*
* Props:
* - data: Feedback analysis data containing positive, negative, and total counts
* - title: Optional title for the chart
* - width: Chart width (defaults to screen width - 32)
* - height: Chart height (defaults to 220)
*/
interface FeedbackAnalysisPieChartProps {
data: FeedbackAnalysisData;
title?: string;
width?: number;
height?: number;
}
// ============================================================================
// COMPONENT
// ============================================================================
/**
* FeedbackAnalysisPieChart Component
*
* Purpose: Renders a pie chart showing feedback analysis distribution
*
* Features:
* - Pie chart visualization of positive vs negative feedback
* - Custom colors for different feedback types
* - Responsive sizing
* - Legend with percentages
* - Empty state handling
*/
export const FeedbackAnalysisPieChart: React.FC<FeedbackAnalysisPieChartProps> = ({
data,
title = 'Feedback Analysis Overview',
width = Dimensions.get('window').width - 32,
height = 220,
}) => {
// ============================================================================
// DATA PROCESSING
// ============================================================================
/**
* Process data for pie chart
*
* Purpose: Convert feedback data into chart-kit format
*/
const chartData = React.useMemo(() => {
const { positive, negative } = data;
// Only show data if there are actual feedbacks
if (positive === 0 && negative === 0) {
return [];
}
const chartDataArray = [];
// Add positive feedback data
if (positive > 0) {
chartDataArray.push({
name: 'Positive',
population: positive,
color: theme.colors.success,
legendFontColor: theme.colors.textPrimary,
legendFontSize: 12,
});
}
// Add negative feedback data
if (negative > 0) {
chartDataArray.push({
name: 'Negative',
population: negative,
color: theme.colors.error,
legendFontColor: theme.colors.textPrimary,
legendFontSize: 12,
});
}
return chartDataArray;
}, [data]);
// ============================================================================
// CHART CONFIGURATION
// ============================================================================
/**
* Chart configuration object
*
* Purpose: Configure pie chart appearance and behavior
*/
const chartConfig = {
backgroundColor: theme.colors.background,
backgroundGradientFrom: theme.colors.background,
backgroundGradientTo: theme.colors.background,
decimalPlaces: 0,
color: (opacity = 1) => theme.colors.primary,
labelColor: (opacity = 1) => theme.colors.textPrimary,
style: {
borderRadius: theme.borderRadius.medium,
},
propsForDots: {
r: '6',
strokeWidth: '2',
stroke: theme.colors.primary,
},
};
// ============================================================================
// RENDER FUNCTIONS
// ============================================================================
/**
* Render empty state
*
* Purpose: Show message when no feedback data is available
*/
const renderEmptyState = () => (
<View style={styles.emptyState}>
<Text style={styles.emptyStateText}>No feedback data available</Text>
<Text style={styles.emptyStateSubtext}>
Feedback will appear here once received
</Text>
</View>
);
/**
* Render chart legend
*
* Purpose: Display custom legend with percentages
*/
const renderLegend = () => {
const { positive, negative, total } = data;
if (total === 0) return null;
const positivePercentage = ((positive / total) * 100).toFixed(1);
const negativePercentage = ((negative / total) * 100).toFixed(1);
return (
<View style={styles.legendContainer}>
<View style={styles.legendItem}>
<View style={[styles.legendColor, { backgroundColor: theme.colors.success }]} />
<Text style={styles.legendText}>
Positive: {positive} ({positivePercentage}%)
</Text>
</View>
<View style={styles.legendItem}>
<View style={[styles.legendColor, { backgroundColor: theme.colors.error }]} />
<Text style={styles.legendText}>
Negative: {negative} ({negativePercentage}%)
</Text>
</View>
<View style={styles.totalContainer}>
<Text style={styles.totalText}>
Total Feedback: {total}
</Text>
</View>
</View>
);
};
// ============================================================================
// MAIN RENDER
// ============================================================================
return (
<View style={styles.container}>
{/* Chart Title */}
{/* {title && (
<Text style={styles.title}>{title}</Text>
)} */}
{/* Chart Container */}
<View style={styles.chartContainer}>
{chartData.length > 0 ? (
<>
{/* Pie Chart */}
<PieChart
data={chartData}
width={width}
height={height}
chartConfig={chartConfig}
accessor="population"
backgroundColor="transparent"
paddingLeft="0"
center={[width/4, 0]}
absolute
hasLegend={false}
/>
{/* Custom Legend */}
{renderLegend()}
</>
) : (
renderEmptyState()
)}
</View>
</View>
);
};
// ============================================================================
// STYLES
// ============================================================================
const styles = StyleSheet.create({
// Main container
container: {
backgroundColor: theme.colors.background,
borderRadius: theme.borderRadius.medium,
paddingHorizontal: theme.spacing.md,
alignItems: 'center',
justifyContent: 'center',
minHeight: 250,
},
// Chart title styling
title: {
fontSize: theme.typography.fontSize.displaySmall,
fontFamily: theme.typography.fontFamily.bold,
color: theme.colors.textPrimary,
marginBottom: theme.spacing.md,
textAlign: 'center',
},
// Chart container
chartContainer: {
alignItems: 'center',
justifyContent: 'center',
width: '100%',
flex: 1,
},
// Empty state styling
emptyState: {
alignItems: 'center',
justifyContent: 'center',
paddingVertical: theme.spacing.xl,
minHeight: 150,
},
// Empty state text styling
emptyStateText: {
fontSize: theme.typography.fontSize.bodyMedium,
fontFamily: theme.typography.fontFamily.medium,
color: theme.colors.textSecondary,
marginBottom: theme.spacing.xs,
},
// Empty state subtext styling
emptyStateSubtext: {
fontSize: theme.typography.fontSize.bodySmall,
fontFamily: theme.typography.fontFamily.regular,
color: theme.colors.textMuted,
textAlign: 'center',
},
// Legend container styling
legendContainer: {
marginTop: theme.spacing.md,
alignItems: 'center',
},
// Legend item styling
legendItem: {
flexDirection: 'row',
alignItems: 'center',
marginBottom: theme.spacing.sm,
},
// Legend color indicator styling
legendColor: {
width: 16,
height: 16,
borderRadius: 8,
marginRight: theme.spacing.sm,
},
// Legend text styling
legendText: {
fontSize: theme.typography.fontSize.bodyMedium,
fontFamily: theme.typography.fontFamily.medium,
color: theme.colors.textPrimary,
},
// Total container styling
totalContainer: {
marginTop: theme.spacing.sm,
paddingTop: theme.spacing.sm,
borderTopWidth: 1,
borderTopColor: theme.colors.border,
alignItems: 'center',
},
// Total text styling
totalText: {
fontSize: theme.typography.fontSize.bodyMedium,
fontFamily: theme.typography.fontFamily.bold,
color: theme.colors.textPrimary,
},
});
/*
* End of File: FeedbackAnalysisPieChart.tsx
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/

View File

@ -3,4 +3,5 @@ export { CriticalAlerts } from './CriticalAlerts';
export { DashboardHeader } from './DashboardHeader';
export { QuickActions } from './QuickActions';
export { DepartmentStats } from './DepartmentStats';
export { BrainPredictionsOverview } from './BrainPredictionsOverview';
export { BrainPredictionsOverview } from './BrainPredictionsOverview';
export { FeedbackAnalysisPieChart } from './FeedbackAnalysisPieChart';

View File

@ -0,0 +1,14 @@
/*
* File: index.ts
* Description: Dashboard hooks exports
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/
export * from './useAIDashboard';
/*
* End of File: index.ts
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/

View File

@ -0,0 +1,101 @@
/*
* File: useAIDashboard.ts
* Description: Custom hook for AI dashboard functionality
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/
import { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { AppDispatch } from '../../../store';
import { selectAIDashboardData, selectAIDashboardError, selectAIDashboardLoading, selectAIDashboardRefreshing, selectDashboardMessage } from '../redux/aiDashboardSelectors';
import { fetchAIDashboardStatistics, refreshAIDashboardStatistics } from '../redux/aiDashboardSlice';
import { selectUser } from '../../Auth/redux';
// import {
// fetchAIDashboardStatistics,
// refreshAIDashboardStatistics,
// selectAIDashboardData,
// selectAIDashboardLoading,
// selectAIDashboardRefreshing,
// selectAIDashboardError,
// selectDashboardMessage
// } from '../redux';
/**
* useAIDashboard Custom Hook
*
* Purpose: Custom hook for AI dashboard functionality
*
* Features:
* - Fetch dashboard statistics from API
* - Refresh dashboard data
* - Access dashboard state from Redux
* - Handle authentication token
*
* @returns Object containing dashboard state and actions
*/
export const useAIDashboard = () => {
const dispatch = useDispatch<AppDispatch>();
// Select dashboard data from Redux store
const dashboardData = useSelector(selectAIDashboardData);
const isLoading = useSelector(selectAIDashboardLoading);
const isRefreshing = useSelector(selectAIDashboardRefreshing);
const error = useSelector(selectAIDashboardError);
const dashboardMessage = useSelector(selectDashboardMessage);
// TODO: Get actual authentication token from auth store
// For now, using a placeholder token
const authToken = useSelector(selectUser)?.access_token;
/**
* Fetch Dashboard Statistics
*
* Purpose: Fetch dashboard statistics from API
*/
const fetchDashboardStatistics = () => {
dispatch(fetchAIDashboardStatistics(authToken));
};
/**
* Refresh Dashboard Statistics
*
* Purpose: Refresh dashboard statistics from API
*/
const refreshDashboardStatistics = () => {
dispatch(refreshAIDashboardStatistics(authToken));
};
/**
* useEffect for initial data loading
*
* Purpose: Load initial dashboard data from API when hook is used
*/
useEffect(() => {
// Fetch dashboard statistics from API
fetchDashboardStatistics();
}, []);
return {
// State
dashboardData,
isLoading,
isRefreshing,
error,
dashboardMessage,
// Actions
fetchDashboardStatistics,
refreshDashboardStatistics,
// Constants
authToken
};
};
/*
* End of File: useAIDashboard.ts
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/

View File

@ -39,6 +39,9 @@ export { default as DashboardHeader } from './components/DashboardHeader';
export { default as QuickActions } from './components/QuickActions';
export { default as DepartmentStats } from './components/DepartmentStats';
// Export hooks
export * from './hooks';
// Export Redux
export {
fetchDashboardData,
@ -51,6 +54,36 @@ export {
updateDashboardData,
} from './redux/dashboardSlice';
// Export AI Dashboard Redux
export {
fetchAIDashboardStatistics,
refreshAIDashboardStatistics,
clearError as clearAIDashboardError,
setTimeRange,
setHospital,
setDepartment,
updateDashboardData as updateAIDashboardData,
} from './redux/aiDashboardSlice';
// Export AI Dashboard Selectors
export {
selectAIDashboardData,
selectAIDashboardLoading,
selectAIDashboardRefreshing,
selectAIDashboardError,
selectDashboardMessage,
selectTotalPredictions,
selectTotalPatients,
selectTotalFeedbacks,
selectFeedbackRatePercentage,
selectAverageConfidenceScore,
selectCriticalCasePercentage,
selectConfidenceScores,
selectUrgencyLevels,
selectFeedbackAnalysis,
selectTimeAnalysis,
} from './redux/aiDashboardSelectors';
export {
fetchAlerts,
acknowledgeAlert,

View File

@ -0,0 +1,426 @@
/*
* File: aiDashboardSelectors.ts
* Description: Selectors for AI dashboard state management
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/
import { createSelector } from '@reduxjs/toolkit';
import { RootState } from '../../../store';
// ============================================================================
// BASE SELECTORS
// ============================================================================
/**
* Select AI Dashboard State
*
* Purpose: Get the entire AI dashboard state from root state
*/
const selectAIDashboardState = (state: RootState) => state.aiDashboard;
/**
* Select AI Dashboard Data
*
* Purpose: Get the AI dashboard data from state
*/
export const selectAIDashboardData = createSelector(
[selectAIDashboardState],
(aiDashboard) => aiDashboard.dashboardData
);
/**
* Select AI Dashboard Statistics
*
* Purpose: Get the AI dashboard statistics data
*/
export const selectAIDashboardStats = createSelector(
[selectAIDashboardData],
(dashboardData) => dashboardData?.data
);
/**
* Select AI Dashboard Summary
*
* Purpose: Get the AI dashboard summary data
*/
export const selectAIDashboardSummary = createSelector(
[selectAIDashboardData],
(dashboardData) => dashboardData?.summary
);
// ============================================================================
// LOADING STATE SELECTORS
// ============================================================================
/**
* Select AI Dashboard Loading State
*
* Purpose: Get the loading state for AI dashboard
*/
export const selectAIDashboardLoading = createSelector(
[selectAIDashboardState],
(aiDashboard) => aiDashboard.isLoading
);
/**
* Select AI Dashboard Refreshing State
*
* Purpose: Get the refreshing state for AI dashboard
*/
export const selectAIDashboardRefreshing = createSelector(
[selectAIDashboardState],
(aiDashboard) => aiDashboard.isRefreshing
);
// ============================================================================
// ERROR STATE SELECTORS
// ============================================================================
/**
* Select AI Dashboard Error
*
* Purpose: Get the error state for AI dashboard
*/
export const selectAIDashboardError = createSelector(
[selectAIDashboardState],
(aiDashboard) => aiDashboard.error
);
// ============================================================================
// FILTER STATE SELECTORS
// ============================================================================
/**
* Select Selected Time Range
*
* Purpose: Get the currently selected time range filter
*/
export const selectSelectedTimeRange = createSelector(
[selectAIDashboardState],
(aiDashboard) => aiDashboard.selectedTimeRange
);
/**
* Select Selected Hospital
*
* Purpose: Get the currently selected hospital filter
*/
export const selectSelectedHospital = createSelector(
[selectAIDashboardState],
(aiDashboard) => aiDashboard.selectedHospital
);
/**
* Select Selected Department
*
* Purpose: Get the currently selected department filter
*/
export const selectSelectedDepartment = createSelector(
[selectAIDashboardState],
(aiDashboard) => aiDashboard.selectedDepartment
);
// ============================================================================
// DERIVED DATA SELECTORS
// ============================================================================
/**
* Select Total Predictions Count
*
* Purpose: Get the total number of AI predictions
*/
export const selectTotalPredictions = createSelector(
[selectAIDashboardStats],
(stats) => stats?.total_predictions || 0
);
/**
* Select Total Patients Count
*
* Purpose: Get the total number of unique patients
*/
export const selectTotalPatients = createSelector(
[selectAIDashboardStats],
(stats) => stats?.total_patients || 0
);
/**
* Select Total Feedbacks Count
*
* Purpose: Get the total number of feedbacks received
*/
export const selectTotalFeedbacks = createSelector(
[selectAIDashboardStats],
(stats) => stats?.total_feedbacks || 0
);
/**
* Select Feedback Rate Percentage
*
* Purpose: Get the feedback rate as a percentage
*/
export const selectFeedbackRatePercentage = createSelector(
[selectAIDashboardStats],
(stats) => stats?.feedback_rate_percentage || 0
);
/**
* Select Average Confidence Score
*
* Purpose: Get the average confidence score for AI predictions
*/
export const selectAverageConfidenceScore = createSelector(
[selectAIDashboardStats],
(stats) => stats?.average_confidence_score || 0
);
/**
* Select Critical Case Percentage
*
* Purpose: Get the percentage of critical cases
*/
export const selectCriticalCasePercentage = createSelector(
[selectAIDashboardStats],
(stats) => stats?.critical_case_percentage || 0
);
// ============================================================================
// CONFIDENCE SCORE SELECTORS
// ============================================================================
/**
* Select Confidence Score Distribution
*
* Purpose: Get the distribution of confidence scores
*/
export const selectConfidenceScores = createSelector(
[selectAIDashboardStats],
(stats) => stats?.confidence_scores || { high: 0, medium: 0, low: 0 }
);
/**
* Select High Confidence Count
*
* Purpose: Get the count of high confidence predictions
*/
export const selectHighConfidenceCount = createSelector(
[selectConfidenceScores],
(confidenceScores) => confidenceScores.high
);
/**
* Select Medium Confidence Count
*
* Purpose: Get the count of medium confidence predictions
*/
export const selectMediumConfidenceCount = createSelector(
[selectConfidenceScores],
(confidenceScores) => confidenceScores.medium
);
/**
* Select Low Confidence Count
*
* Purpose: Get the count of low confidence predictions
*/
export const selectLowConfidenceCount = createSelector(
[selectConfidenceScores],
(confidenceScores) => confidenceScores.low
);
// ============================================================================
// URGENCY LEVEL SELECTORS
// ============================================================================
/**
* Select Urgency Level Distribution
*
* Purpose: Get the distribution of urgency levels
*/
export const selectUrgencyLevels = createSelector(
[selectAIDashboardStats],
(stats) => stats?.urgency_levels || { critical: 0, urgent: 0, routine: 0 }
);
/**
* Select Critical Urgency Count
*
* Purpose: Get the count of critical urgency cases
*/
export const selectCriticalUrgencyCount = createSelector(
[selectUrgencyLevels],
(urgencyLevels) => urgencyLevels.critical
);
/**
* Select Urgent Urgency Count
*
* Purpose: Get the count of urgent cases
*/
export const selectUrgentUrgencyCount = createSelector(
[selectUrgencyLevels],
(urgencyLevels) => urgencyLevels.urgent
);
/**
* Select Routine Urgency Count
*
* Purpose: Get the count of routine cases
*/
export const selectRoutineUrgencyCount = createSelector(
[selectUrgencyLevels],
(urgencyLevels) => urgencyLevels.routine
);
// ============================================================================
// FEEDBACK ANALYSIS SELECTORS
// ============================================================================
/**
* Select Feedback Analysis Data
*
* Purpose: Get the feedback analysis data
*/
export const selectFeedbackAnalysis = createSelector(
[selectAIDashboardStats],
(stats) => stats?.feedback_analysis || { positive: 0, negative: 0, total: 0 }
);
/**
* Select Positive Feedback Count
*
* Purpose: Get the count of positive feedbacks
*/
export const selectPositiveFeedbackCount = createSelector(
[selectFeedbackAnalysis],
(feedbackAnalysis) => feedbackAnalysis.positive
);
/**
* Select Negative Feedback Count
*
* Purpose: Get the count of negative feedbacks
*/
export const selectNegativeFeedbackCount = createSelector(
[selectFeedbackAnalysis],
(feedbackAnalysis) => feedbackAnalysis.negative
);
/**
* Select Total Feedback Count
*
* Purpose: Get the total count of feedbacks
*/
export const selectTotalFeedbackCount = createSelector(
[selectFeedbackAnalysis],
(feedbackAnalysis) => feedbackAnalysis.total
);
// ============================================================================
// TIME ANALYSIS SELECTORS
// ============================================================================
/**
* Select Time Analysis Data
*
* Purpose: Get the time-based analysis data
*/
export const selectTimeAnalysis = createSelector(
[selectAIDashboardStats],
(stats) => stats?.time_analysis || { today: 0, this_week: 0, this_month: 0, this_year: 0 }
);
/**
* Select Today's Count
*
* Purpose: Get the count for today
*/
export const selectTodayCount = createSelector(
[selectTimeAnalysis],
(timeAnalysis) => timeAnalysis.today
);
/**
* Select This Week's Count
*
* Purpose: Get the count for this week
*/
export const selectThisWeekCount = createSelector(
[selectTimeAnalysis],
(timeAnalysis) => timeAnalysis.this_week
);
/**
* Select This Month's Count
*
* Purpose: Get the count for this month
*/
export const selectThisMonthCount = createSelector(
[selectTimeAnalysis],
(timeAnalysis) => timeAnalysis.this_month
);
/**
* Select This Year's Count
*
* Purpose: Get the count for this year
*/
export const selectThisYearCount = createSelector(
[selectTimeAnalysis],
(timeAnalysis) => timeAnalysis.this_year
);
// ============================================================================
// LAST UPDATED SELECTORS
// ============================================================================
/**
* Select Last Updated Timestamp
*
* Purpose: Get the last updated timestamp
*/
export const selectLastUpdated = createSelector(
[selectAIDashboardState],
(aiDashboard) => aiDashboard.lastUpdated
);
// ============================================================================
// COMPUTED SELECTORS
// ============================================================================
/**
* Select Dashboard Message
*
* Purpose: Get the dashboard message or default message
*/
export const selectDashboardMessage = createSelector(
[selectAIDashboardData],
(dashboardData) => dashboardData?.message || 'Loading statistics...'
);
/**
* Select Is Dashboard Empty
*
* Purpose: Check if dashboard has no data
*/
export const selectIsDashboardEmpty = createSelector(
[selectAIDashboardData],
(dashboardData) => !dashboardData || !dashboardData.data
);
/**
* Select Has Dashboard Data
*
* Purpose: Check if dashboard has data
*/
export const selectHasDashboardData = createSelector(
[selectIsDashboardEmpty],
(isEmpty) => !isEmpty
);
/*
* End of File: aiDashboardSelectors.ts
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/

View File

@ -0,0 +1,368 @@
/*
* File: aiDashboardSlice.ts
* Description: AI Analysis Dashboard state management slice
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/
import { createSlice, createAsyncThunk, PayloadAction } from '@reduxjs/toolkit';
import { dashboardAPI } from '../services/dashboardAPI';
// ============================================================================
// TYPES
// ============================================================================
/**
* AI Dashboard Statistics Interface
*
* Purpose: Defines the structure of AI dashboard statistics data
*/
export interface AIDashboardStats {
total_predictions: number;
total_patients: number;
total_feedbacks: number;
prediction_breakdown: Record<string, number>;
critical_findings: Record<string, number>;
midline_shift_stats: Record<string, number>;
hemorrhage_stats: Record<string, number>;
mass_lesion_stats: Record<string, number>;
edema_stats: Record<string, number>;
fracture_stats: Record<string, number>;
feedback_analysis: {
positive: number;
negative: number;
total: number;
};
hospital_distribution: Record<string, number>;
time_analysis: {
today: number;
this_week: number;
this_month: number;
this_year: number;
};
urgency_levels: {
critical: number;
urgent: number;
routine: number;
};
confidence_scores: {
high: number;
medium: number;
low: number;
};
feedback_rate_percentage: number;
predictions_with_feedback: number;
predictions_without_feedback: number;
average_feedback_per_prediction: string;
critical_case_percentage: number;
average_confidence_score: number;
}
/**
* AI Dashboard Summary Interface
*
* Purpose: Defines the structure of the AI dashboard summary data
*/
export interface AIDashboardSummary {
total_cases: number;
critical_cases: number;
routine_cases: number;
feedback_coverage: string;
critical_case_rate: string;
average_confidence: string;
}
/**
* Complete AI Dashboard Data Interface
*
* Purpose: Defines the complete structure of the AI dashboard API response
*/
export interface AIDashboardData {
success: boolean;
data: AIDashboardStats;
summary: AIDashboardSummary;
message: string;
}
/**
* AI Dashboard State Interface
*
* Purpose: Defines the state structure for AI dashboard
*/
export interface AIDashboardState {
// Dashboard data
dashboardData: AIDashboardData | null;
// Loading states
isLoading: boolean;
isRefreshing: boolean;
// Error handling
error: string | null;
// Filters and preferences
selectedTimeRange: 'today' | 'week' | 'month' | 'year';
selectedHospital: string | null;
selectedDepartment: string | null;
// Last updated timestamp
lastUpdated: string | null;
}
// ============================================================================
// ASYNC THUNKS
// ============================================================================
/**
* Fetch AI Dashboard Statistics Async Thunk
*
* Purpose: Fetch AI analysis dashboard statistics from API
*
* @param token - Authentication token
* @returns Promise with AI dashboard statistics data or error
*/
export const fetchAIDashboardStatistics = createAsyncThunk(
'aiDashboard/fetchStatistics',
async (token: string, { rejectWithValue }) => {
try {
const response :any = await dashboardAPI.getDashboardStatistics(token);
console.log('statistics response',response);
if (response.ok && response.data ) {
return response.data as AIDashboardData;
} else {
return rejectWithValue(response.problem || 'Failed to fetch dashboard statistics');
}
} catch (error) {
return rejectWithValue('Network error occurred while fetching dashboard statistics');
}
}
);
/**
* Refresh AI Dashboard Statistics Async Thunk
*
* Purpose: Refresh AI dashboard statistics data
*
* @param token - Authentication token
* @returns Promise with refreshed AI dashboard statistics or error
*/
export const refreshAIDashboardStatistics = createAsyncThunk(
'aiDashboard/refreshStatistics',
async (token: string, { rejectWithValue }) => {
try {
const response = await dashboardAPI.getDashboardStatistics(token);
if (response.ok && response.data) {
return response.data as AIDashboardData;
} else {
return rejectWithValue(response.problem || 'Failed to refresh dashboard statistics');
}
} catch (error) {
return rejectWithValue('Network error occurred while refreshing dashboard statistics');
}
}
);
/**
* Fetch Time-based Analysis Async Thunk
*
* Purpose: Fetch time-based analysis data for specific time range
*
* @param params - Parameters including token and time range
* @returns Promise with time-based analysis data or error
*/
export const fetchTimeBasedAnalysis = createAsyncThunk(
'aiDashboard/fetchTimeAnalysis',
async (params: { token: string; timeRange: 'today' | 'week' | 'month' | 'year' }, { rejectWithValue }) => {
try {
const response = await dashboardAPI.getTimeBasedAnalysis(params.token, params.timeRange);
if (response.ok && response.data) {
return response.data as AIDashboardData;
} else {
return rejectWithValue(response.problem || 'Failed to fetch time-based analysis');
}
} catch (error) {
return rejectWithValue('Network error occurred while fetching time-based analysis');
}
}
);
// ============================================================================
// INITIAL STATE
// ============================================================================
/**
* Initial AI Dashboard State
*
* Purpose: Define the initial state for AI dashboard
*
* Features:
* - AI dashboard statistics data
* - Loading states for async operations
* - Error handling and messages
* - Filter preferences
* - Last updated tracking
*/
const initialState: AIDashboardState = {
// Dashboard data
dashboardData: null,
// Loading states
isLoading: false,
isRefreshing: false,
// Error handling
error: null,
// Filters and preferences
selectedTimeRange: 'today',
selectedHospital: null,
selectedDepartment: null,
// Last updated timestamp
lastUpdated: null,
};
// ============================================================================
// AI DASHBOARD SLICE
// ============================================================================
/**
* AI Dashboard Slice
*
* Purpose: Redux slice for AI dashboard state management
*
* Features:
* - AI dashboard statistics management
* - Time-based filtering
* - Hospital and department filtering
* - Error handling
* - Loading states
*/
const aiDashboardSlice = createSlice({
name: 'aiDashboard',
initialState,
reducers: {
/**
* Clear Error Action
*
* Purpose: Clear AI dashboard errors
*/
clearError: (state) => {
state.error = null;
},
/**
* Set Time Range Filter Action
*
* Purpose: Set time range filter for statistics
*/
setTimeRange: (state, action: PayloadAction<'today' | 'week' | 'month' | 'year'>) => {
state.selectedTimeRange = action.payload;
},
/**
* Set Hospital Filter Action
*
* Purpose: Set hospital filter for statistics
*/
setHospital: (state, action: PayloadAction<string | null>) => {
state.selectedHospital = action.payload;
},
/**
* Set Department Filter Action
*
* Purpose: Set department filter for statistics
*/
setDepartment: (state, action: PayloadAction<string | null>) => {
state.selectedDepartment = action.payload;
},
/**
* Update Dashboard Data Action
*
* Purpose: Update dashboard data manually
*/
updateDashboardData: (state, action: PayloadAction<Partial<AIDashboardData>>) => {
if (state.dashboardData) {
state.dashboardData = { ...state.dashboardData, ...action.payload };
state.lastUpdated = new Date().toLocaleDateString();
}
},
},
extraReducers: (builder) => {
// Fetch AI Dashboard Statistics
builder
.addCase(fetchAIDashboardStatistics.pending, (state) => {
state.isLoading = true;
state.error = null;
})
.addCase(fetchAIDashboardStatistics.fulfilled, (state, action) => {
state.isLoading = false;
state.dashboardData = action.payload;
state.lastUpdated = new Date().toLocaleDateString();
state.error = null;
})
.addCase(fetchAIDashboardStatistics.rejected, (state, action) => {
state.isLoading = false;
state.error = action.payload as string;
});
// Refresh AI Dashboard Statistics
builder
.addCase(refreshAIDashboardStatistics.pending, (state) => {
state.isRefreshing = true;
state.error = null;
})
.addCase(refreshAIDashboardStatistics.fulfilled, (state, action) => {
state.isRefreshing = false;
state.dashboardData = action.payload;
state.lastUpdated = new Date().toLocaleDateString();
state.error = null;
})
.addCase(refreshAIDashboardStatistics.rejected, (state, action) => {
state.isRefreshing = false;
state.error = action.payload as string;
});
// Fetch Time-based Analysis
builder
.addCase(fetchTimeBasedAnalysis.pending, (state) => {
state.isLoading = true;
state.error = null;
})
.addCase(fetchTimeBasedAnalysis.fulfilled, (state, action) => {
state.isLoading = false;
state.dashboardData = action.payload;
state.lastUpdated = new Date().toLocaleDateString();
state.error = null;
})
.addCase(fetchTimeBasedAnalysis.rejected, (state, action) => {
state.isLoading = false;
state.error = action.payload as string;
});
},
});
// ============================================================================
// EXPORTS
// ============================================================================
export const {
clearError,
setTimeRange,
setHospital,
setDepartment,
updateDashboardData,
} = aiDashboardSlice.actions;
export default aiDashboardSlice.reducer;
/*
* End of File: aiDashboardSlice.ts
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/

View File

@ -0,0 +1,31 @@
/*
* File: index.ts
* Description: Dashboard Redux exports
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/
// Dashboard Slice
export { default as dashboardReducer } from './dashboardSlice';
export * from './dashboardSlice';
// AI Dashboard Slice
// export { default as aiDashboardReducer } from './aiDashboardSlice';
// export * from './aiDashboardSlice';
// // UI Slice
// export { default as uiReducer } from './uiSlice';
// export * from './uiSlice';
// // Alerts Slice
// export { default as alertsReducer } from './alertsSlice';
// export * from './alertsSlice';
// // AI Dashboard Selectors
// export * from './aiDashboardSelectors';
/*
* End of File: index.ts
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/

View File

@ -14,10 +14,13 @@ import {
TouchableOpacity,
RefreshControl,
FlatList,
Dimensions,
} from 'react-native';
import { theme } from '../../../theme/theme';
import { DashboardHeader } from '../components/DashboardHeader';
import { BrainPredictionsOverview } from '../components/BrainPredictionsOverview';
import { FeedbackAnalysisPieChart } from '../components/FeedbackAnalysisPieChart';
import { useAIDashboard } from '../hooks/useAIDashboard';
/**
* DashboardScreenProps Interface
@ -120,108 +123,18 @@ export const DashboardScreen: React.FC<DashboardScreenProps> = ({
navigation,
}) => {
// ============================================================================
// STATE MANAGEMENT
// CUSTOM HOOKS
// ============================================================================
// Refresh state for pull-to-refresh functionality
const [refreshing, setRefreshing] = useState(false);
// Dashboard data state
const [dashboardData, setDashboardData] = useState<DashboardData | null>(null);
const [isLoading, setIsLoading] = useState(true);
// ============================================================================
// MOCK DATA GENERATION
// ============================================================================
/**
* generateMockDashboardData Function
*
* Purpose: Generate mock dashboard data based on the provided JSON structure
*
* Returns: DashboardData object with AI analysis statistics
*/
const generateMockDashboardData = (): DashboardData => ({
success: true,
data: {
total_predictions: 24,
total_patients: 9,
total_feedbacks: 6,
prediction_breakdown: {
"Other": 24
},
critical_findings: {},
midline_shift_stats: {},
hemorrhage_stats: {},
mass_lesion_stats: {},
edema_stats: {},
fracture_stats: {},
feedback_analysis: {
positive: 6,
negative: 0,
total: 6
},
hospital_distribution: {
"b491dfc2-521b-4eb1-8d88-02b0940ea1ff": 24
},
time_analysis: {
today: 24,
this_week: 24,
this_month: 24,
this_year: 24
},
urgency_levels: {
critical: 0,
urgent: 0,
routine: 24
},
confidence_scores: {
high: 23,
medium: 1,
low: 0
},
feedback_rate_percentage: 25,
predictions_with_feedback: 2,
predictions_without_feedback: 22,
average_feedback_per_prediction: "0.25",
critical_case_percentage: 0,
average_confidence_score: 0.89
},
summary: {
total_cases: 24,
critical_cases: 0,
routine_cases: 24,
feedback_coverage: "25.00%",
critical_case_rate: "0.00%",
average_confidence: "0.89"
},
message: "Statistics generated for 24 predictions"
});
// ============================================================================
// EFFECTS
// ============================================================================
/**
* useEffect for initial data loading
*
* Purpose: Load initial mock data when component mounts
*/
useEffect(() => {
const loadInitialData = async () => {
setIsLoading(true);
// Simulate API call delay
setTimeout(() => {}, 1000);
// Generate and set mock data
setDashboardData(generateMockDashboardData());
setIsLoading(false);
};
loadInitialData();
}, []);
// Use custom hook for AI dashboard functionality
const {
dashboardData,
isLoading,
isRefreshing,
error,
dashboardMessage,
refreshDashboardStatistics
} = useAIDashboard();
// ============================================================================
// EVENT HANDLERS
@ -233,21 +146,86 @@ export const DashboardScreen: React.FC<DashboardScreenProps> = ({
* Purpose: Handle pull-to-refresh functionality to update dashboard data
*/
const handleRefresh = async () => {
setRefreshing(true);
// Simulate API call with 1-second delay
setTimeout(() => {}, 1000);
// Update data with fresh mock data
setDashboardData(generateMockDashboardData());
setRefreshing(false);
// Refresh dashboard statistics from API
refreshDashboardStatistics();
};
// ============================================================================
// RENDER FUNCTIONS
// ============================================================================
/**
* renderErrorState Function
*
* Purpose: Render error state when there's a critical error
*/
const renderErrorState = () => {
if (!error) return null;
return (
<View style={styles.errorContainer}>
<Text style={styles.errorTitle}>Connection Error</Text>
<Text style={styles.errorMessage}>
Unable to connect to the dashboard service
</Text>
<View style={styles.errorInfo}>
<Text style={styles.errorInfoText}> Check your internet connection</Text>
<Text style={styles.errorInfoText}> Verify server status</Text>
<Text style={styles.errorInfoText}> Try again in a few moments</Text>
</View>
<TouchableOpacity style={styles.retryButton} onPress={handleRefresh}>
<Text style={styles.retryButtonText}>Retry Connection</Text>
</TouchableOpacity>
</View>
);
};
/**
* renderNetworkStatus Function
*
* Purpose: Render network status indicator
*/
const renderNetworkStatus = () => {
if (!error && dashboardData) return null;
return (
<View style={styles.networkStatusContainer}>
<Text style={styles.networkStatusText}>
{error ? '⚠️ Connection Issue' : '🔄 Checking Connection...'}
</Text>
<Text style={styles.networkStatusSubtext}>
{error ? 'Please check your internet connection' : 'Verifying dashboard service'}
</Text>
</View>
);
};
/**
* renderNoDataState Function
*
* Purpose: Render no data state when dashboard loads but has no meaningful data
*/
const renderNoDataState = () => {
return (
<View style={styles.container}>
<View style={styles.noDataContainer}>
<Text style={styles.noDataTitle}>Dashboard is Ready</Text>
<Text style={styles.noDataMessage}>
Your AI Analysis Dashboard is ready, but there's no data to display yet.
</Text>
<View style={styles.noDataInfo}>
<Text style={styles.noDataInfoText}> AI predictions will appear here once scans are processed</Text>
<Text style={styles.noDataInfoText}> Check back after medical scans are uploaded</Text>
<Text style={styles.noDataInfoText}> The system will automatically populate data</Text>
</View>
<TouchableOpacity style={styles.retryButton} onPress={handleRefresh}>
<Text style={styles.retryButtonText}>Check for Updates</Text>
</TouchableOpacity>
</View>
</View>
);
};
/**
* renderStatsCard Function
*
@ -273,31 +251,171 @@ export const DashboardScreen: React.FC<DashboardScreenProps> = ({
* Purpose: Render confidence score breakdown section
*/
const renderConfidenceBreakdown = () => {
if (!dashboardData?.data.confidence_scores) return null;
// Check if dashboard data exists
if (!dashboardData) {
return (
<View style={styles.section}>
<Text style={styles.sectionTitle}>Confidence Score Distribution</Text>
<View style={styles.emptyStateContainer}>
<Text style={styles.emptyStateText}>Dashboard data not available</Text>
<TouchableOpacity style={styles.retryButton} onPress={handleRefresh}>
<Text style={styles.retryButtonText}>Retry</Text>
</TouchableOpacity>
</View>
</View>
);
}
// Check if confidence scores data exists
if (!dashboardData.data?.confidence_scores) {
return (
<View style={styles.section}>
<Text style={styles.sectionTitle}>Confidence Score Distribution</Text>
<View style={styles.emptyStateContainer}>
<Text style={styles.emptyStateText}>Confidence data not available</Text>
<Text style={styles.emptyStateSubtext}>AI confidence scores are not currently accessible</Text>
<View style={styles.emptyStateInfo}>
<Text style={styles.emptyStateInfoText}> AI system may be initializing</Text>
<Text style={styles.emptyStateInfoText}> Check system status</Text>
<Text style={styles.emptyStateInfoText}> Refresh in a few minutes</Text>
</View>
</View>
</View>
);
}
const { high, medium, low } = dashboardData.data.confidence_scores;
// Check if the object is empty or if all values are undefined/null/zero
if (!high && !medium && !low) {
return (
<View style={styles.section}>
<Text style={styles.sectionTitle}>Confidence Score Distribution</Text>
<View style={styles.emptyStateContainer}>
<Text style={styles.emptyStateText}>No data found</Text>
</View>
</View>
);
}
// Check if all required fields exist and are numbers
if (typeof high !== 'number' || typeof medium !== 'number' || typeof low !== 'number') {
return (
<View style={styles.section}>
<Text style={styles.sectionTitle}>Confidence Score Distribution</Text>
<View style={styles.emptyStateContainer}>
<Text style={styles.emptyStateText}>No confidence data available</Text>
</View>
</View>
);
}
const total = high + medium + low;
// If no predictions, show empty state
if (total === 0) {
return (
<View style={styles.section}>
<Text style={styles.sectionTitle}>Confidence Score Distribution</Text>
<View style={styles.emptyStateContainer}>
<Text style={styles.emptyStateText}>No predictions available yet</Text>
<Text style={styles.emptyStateSubtext}>AI predictions will appear here once the system processes medical scans</Text>
</View>
</View>
);
}
// Calculate percentages for better visualization
const highPercent = Math.round((high / total) * 100);
const mediumPercent = Math.round((medium / total) * 100);
const lowPercent = Math.round((low / total) * 100);
// Helper function to get bar opacity
const getBarOpacity = (count: number) => {
if (count === 0) return 0.3; // Dimmed for zero values
return 0.9; // Full opacity for non-zero values
};
return (
<View style={styles.section}>
<Text style={styles.sectionTitle}>Confidence Score Distribution</Text>
<View style={styles.confidenceContainer}>
{/* High Confidence */}
<View style={styles.confidenceItem}>
<View style={[styles.confidenceBar, { backgroundColor: theme.colors.success, height: (high / total) * 100 }]} />
<Text style={styles.confidenceLabel}>High</Text>
<Text style={styles.confidenceValue}>{high}</Text>
<View style={styles.confidenceHeader}>
<View style={[styles.confidenceIndicator, { backgroundColor: theme.colors.success }]} />
<Text style={styles.confidenceLabel}>High Confidence</Text>
<Text style={styles.confidencePercentage}>{highPercent}%</Text>
</View>
<View style={styles.confidenceBarContainer}>
<View
style={[
styles.confidenceBar,
{
backgroundColor: theme.colors.success,
width: high === 0 ? 4 : `${Math.max(highPercent, 5)}%`,
opacity: getBarOpacity(high)
}
]}
/>
</View>
<Text style={styles.confidenceValue}>{high} predictions</Text>
</View>
{/* Medium Confidence */}
<View style={styles.confidenceItem}>
<View style={[styles.confidenceBar, { backgroundColor: theme.colors.warning, height: (medium / total) * 100 }]} />
<Text style={styles.confidenceLabel}>Medium</Text>
<Text style={styles.confidenceValue}>{medium}</Text>
<View style={styles.confidenceHeader}>
<View style={[styles.confidenceIndicator, { backgroundColor: theme.colors.warning }]} />
<Text style={styles.confidenceLabel}>Medium Confidence</Text>
<Text style={styles.confidencePercentage}>{mediumPercent}%</Text>
</View>
<View style={styles.confidenceBarContainer}>
<View
style={[
styles.confidenceBar,
{
backgroundColor: theme.colors.warning,
width: medium === 0 ? 4 : `${Math.max(mediumPercent, 5)}%`,
opacity: getBarOpacity(medium)
}
]}
/>
</View>
<Text style={styles.confidenceValue}>{medium} predictions</Text>
</View>
{/* Low Confidence */}
<View style={styles.confidenceItem}>
<View style={[styles.confidenceBar, { backgroundColor: theme.colors.error, height: (low / total) * 100 }]} />
<Text style={styles.confidenceLabel}>Low</Text>
<Text style={styles.confidenceValue}>{low}</Text>
<View style={styles.confidenceHeader}>
<View style={[styles.confidenceIndicator, { backgroundColor: theme.colors.error }]} />
<Text style={styles.confidenceLabel}>Low Confidence</Text>
<Text style={styles.confidencePercentage}>{lowPercent}%</Text>
</View>
<View style={styles.confidenceBarContainer}>
<View
style={[
styles.confidenceBar,
{
backgroundColor: theme.colors.error,
width: low === 0 ? 4 : `${Math.max(lowPercent, 5)}%`,
opacity: getBarOpacity(low)
}
]}
/>
</View>
<Text style={styles.confidenceValue}>{low} predictions</Text>
</View>
</View>
{/* Summary Stats */}
<View style={styles.confidenceSummary}>
<Text style={styles.summaryText}>
Total Predictions: <Text style={styles.summaryValue}>{total}</Text>
</Text>
<Text style={styles.summaryText}>
High Confidence Rate: <Text style={[styles.summaryValue, { color: theme.colors.success }]}>{highPercent}%</Text>
</Text>
</View>
</View>
);
};
@ -311,6 +429,30 @@ export const DashboardScreen: React.FC<DashboardScreenProps> = ({
if (!dashboardData?.data.urgency_levels) return null;
const { critical, urgent, routine } = dashboardData.data.urgency_levels;
// Check if the object is empty or if all values are undefined/null/zero
if (!critical && !urgent && !routine) {
return (
<View style={styles.section}>
<Text style={styles.sectionTitle}>Case Urgency Distribution</Text>
<View style={styles.emptyStateContainer}>
<Text style={styles.emptyStateText}>No data found</Text>
</View>
</View>
);
}
// Check if all values are zero (no cases)
if (critical === 0 && urgent === 0 && routine === 0) {
return (
<View style={styles.section}>
<Text style={styles.sectionTitle}>Case Urgency Distribution</Text>
<View style={styles.emptyStateContainer}>
<Text style={styles.emptyStateText}>No cases recorded yet</Text>
</View>
</View>
);
}
return (
<View style={styles.section}>
@ -336,46 +478,69 @@ export const DashboardScreen: React.FC<DashboardScreenProps> = ({
);
};
/**
/**
* renderFeedbackAnalysis Function
*
* Purpose: Render feedback analysis section
* Purpose: Render feedback analysis section with pie chart
*/
const renderFeedbackAnalysis = () => {
if (!dashboardData?.data.feedback_analysis) return null;
const renderFeedbackAnalysis = () => {
if (!dashboardData?.data.feedback_analysis) return null;
const { positive, negative, total } = dashboardData.data.feedback_analysis;
const positivePercentage = total > 0 ? ((positive / total) * 100).toFixed(1) : '0';
const negativePercentage = total > 0 ? ((negative / total) * 100).toFixed(1) : '0';
const { positive, negative, total } = dashboardData.data.feedback_analysis;
// Check if the object is empty or if all values are undefined/null/zero
if (!positive && !negative && !total) {
return (
<View style={styles.section}>
<Text style={styles.sectionTitle}>Feedback Analysis</Text>
<View style={styles.emptyStateContainer}>
<Text style={styles.emptyStateText}>No data found</Text>
</View>
</View>
);
}
// Check if all values are zero (no feedback)
if (positive === 0 && negative === 0 && total === 0) {
return (
<View style={styles.section}>
<Text style={styles.sectionTitle}>Feedback Analysis</Text>
<View style={styles.emptyStateContainer}>
<Text style={styles.emptyStateText}>No feedback recorded yet</Text>
<Text style={styles.emptyStateSubtext}>Feedback analysis will appear once users provide feedback</Text>
<View style={styles.emptyStateInfo}>
<Text style={styles.emptyStateInfoText}> No user feedback has been submitted yet</Text>
</View>
</View>
</View>
);
}
return (
<View style={styles.section}>
<Text style={styles.sectionTitle}>Feedback Analysis</Text>
<View style={styles.feedbackContainer}>
<View style={styles.feedbackItem}>
<View style={[styles.feedbackIndicator, { backgroundColor: theme.colors.success }]} />
<Text style={styles.feedbackLabel}>Positive</Text>
<Text style={styles.feedbackValue}>{positive}</Text>
<Text style={styles.feedbackPercentage}>({positivePercentage}%)</Text>
</View>
<View style={styles.feedbackItem}>
<View style={[styles.feedbackIndicator, { backgroundColor: theme.colors.error }]} />
<Text style={styles.feedbackLabel}>Negative</Text>
<Text style={styles.feedbackValue}>{negative}</Text>
<Text style={styles.feedbackPercentage}>({negativePercentage}%)</Text>
</View>
</View>
<View style={styles.feedbackSummary}>
<Text style={styles.feedbackSummaryText}>
Feedback Coverage: {dashboardData.data.feedback_rate_percentage}%
</Text>
<Text style={styles.feedbackSummaryText}>
Average Feedback per Prediction: {dashboardData.data.average_feedback_per_prediction}
</Text>
</View>
</View>
);
};
return (
<View style={styles.section}>
<Text style={styles.sectionTitle}>Feedback Analysis</Text>
{/* Pie Chart */}
<FeedbackAnalysisPieChart
data={{ positive, negative, total }}
title="Feedback Distribution"
width={Dimensions.get('window').width - (theme.spacing.md * 2)}
height={220}
/>
{/* Additional Feedback Metrics */}
<View style={styles.feedbackMetrics}>
<Text style={styles.feedbackMetricsText}>
Feedback Coverage: {dashboardData.data.feedback_rate_percentage}%
</Text>
<Text style={styles.feedbackMetricsText}>
Average Feedback per Prediction: {dashboardData.data.average_feedback_per_prediction}
</Text>
</View>
</View>
);
};
/**
* renderTimeAnalysis Function
@ -386,6 +551,31 @@ export const DashboardScreen: React.FC<DashboardScreenProps> = ({
if (!dashboardData?.data.time_analysis) return null;
const { today, this_week, this_month, this_year } = dashboardData.data.time_analysis;
// Check if the object is empty or if all values are undefined/null/zero
if (!today && !this_week && !this_month && !this_year) {
return (
<View style={styles.section}>
<Text style={styles.sectionTitle}>Time-based Analysis</Text>
<View style={styles.emptyStateContainer}>
<Text style={styles.emptyStateText}>No data found</Text>
</View>
</View>
);
}
// Check if all values are zero (no activity)
if (today === 0 && this_week === 0 && this_month === 0 && this_year === 0) {
return (
<View style={styles.section}>
<Text style={styles.sectionTitle}>Time-based Analysis</Text>
<View style={styles.emptyStateContainer}>
<Text style={styles.emptyStateText}>No activity recorded yet</Text>
<Text style={styles.emptyStateSubtext}>Time-based statistics will appear once AI predictions are made</Text>
</View>
</View>
);
}
return (
<View style={styles.section}>
@ -423,7 +613,7 @@ export const DashboardScreen: React.FC<DashboardScreenProps> = ({
<View style={styles.headerTop}>
<Text style={styles.dashboardTitle}>AI Analysis Dashboard</Text>
<Text style={styles.dashboardSubtitle}>
{dashboardData?.message || 'Loading statistics...'}
{dashboardMessage}
</Text>
</View>
@ -458,14 +648,19 @@ export const DashboardScreen: React.FC<DashboardScreenProps> = ({
);
// ============================================================================
// LOADING STATE
// MAIN RENDER
// ============================================================================
/**
* Loading state render
*
* Purpose: Show loading indicator while data is being generated
*/
// Show error state if there's a critical error
if (error) {
return (
<View style={styles.container}>
{renderErrorState()}
</View>
);
}
// Show loading state while data is being fetched
if (isLoading) {
return (
<View style={styles.loadingContainer}>
@ -474,9 +669,10 @@ export const DashboardScreen: React.FC<DashboardScreenProps> = ({
);
}
// ============================================================================
// MAIN RENDER
// ============================================================================
// Show no data state if dashboard loads but has no meaningful data
if (!dashboardData || !dashboardData.data) {
return renderNoDataState();
}
return (
<View style={styles.container}>
@ -486,7 +682,7 @@ export const DashboardScreen: React.FC<DashboardScreenProps> = ({
contentContainerStyle={styles.scrollContent}
refreshControl={
<RefreshControl
refreshing={refreshing}
refreshing={isRefreshing}
onRefresh={handleRefresh}
colors={[theme.colors.primary]}
tintColor={theme.colors.primary}
@ -641,39 +837,73 @@ const styles = StyleSheet.create({
// Confidence breakdown container
confidenceContainer: {
flexDirection: 'row',
justifyContent: 'space-around',
alignItems: 'flex-end',
height: 120,
gap: theme.spacing.md,
},
// Confidence item styling
confidenceItem: {
alignItems: 'center',
flex: 1,
backgroundColor: theme.colors.backgroundAlt,
borderRadius: theme.borderRadius.medium,
padding: theme.spacing.md,
marginBottom: theme.spacing.sm,
...theme.shadows.small,
},
// Confidence bar styling
confidenceBar: {
width: 40,
height: 12,
borderRadius: theme.borderRadius.small,
marginBottom: theme.spacing.sm,
minHeight: 4,
backgroundColor: theme.colors.primary,
},
// Confidence label styling
confidenceLabel: {
fontSize: theme.typography.fontSize.bodySmall,
fontSize: theme.typography.fontSize.bodyMedium,
fontFamily: theme.typography.fontFamily.medium,
color: theme.colors.textSecondary,
marginBottom: theme.spacing.xs,
color: theme.colors.textPrimary,
flex: 1,
},
// Confidence value styling
confidenceValue: {
fontSize: theme.typography.fontSize.bodySmall,
fontFamily: theme.typography.fontFamily.regular,
color: theme.colors.textMuted,
marginTop: theme.spacing.xs,
textAlign: 'center',
},
// Confidence header styling
confidenceHeader: {
flexDirection: 'row',
alignItems: 'center',
marginBottom: theme.spacing.sm,
},
// Confidence indicator styling
confidenceIndicator: {
width: 16,
height: 16,
borderRadius: 8,
marginRight: theme.spacing.xs,
},
// Confidence percentage styling
confidencePercentage: {
fontSize: theme.typography.fontSize.bodyMedium,
fontFamily: theme.typography.fontFamily.bold,
color: theme.colors.textPrimary,
marginLeft: 'auto',
},
// Confidence bar container styling
confidenceBarContainer: {
width: '100%',
height: 12,
backgroundColor: theme.colors.border,
borderRadius: theme.borderRadius.small,
marginBottom: theme.spacing.sm,
overflow: 'hidden',
},
// Urgency container styling
@ -770,6 +1000,24 @@ const styles = StyleSheet.create({
textAlign: 'center',
marginBottom: theme.spacing.xs,
},
// Feedback metrics container styling
feedbackMetrics: {
marginTop: theme.spacing.md,
paddingTop: theme.spacing.md,
borderTopWidth: 1,
borderTopColor: theme.colors.border,
alignItems: 'center',
},
// Feedback metrics text styling
feedbackMetricsText: {
fontSize: theme.typography.fontSize.bodySmall,
fontFamily: theme.typography.fontFamily.regular,
color: theme.colors.textSecondary,
textAlign: 'center',
marginBottom: theme.spacing.xs,
},
// Time container styling
timeContainer: {
@ -797,11 +1045,205 @@ const styles = StyleSheet.create({
fontFamily: theme.typography.fontFamily.bold,
color: theme.colors.textPrimary,
},
// Confidence summary styling
confidenceSummary: {
marginTop: theme.spacing.lg,
paddingTop: theme.spacing.md,
borderTopWidth: 1,
borderTopColor: theme.colors.border,
alignItems: 'center',
backgroundColor: theme.colors.backgroundAlt,
borderRadius: theme.borderRadius.medium,
padding: theme.spacing.md,
marginHorizontal: -theme.spacing.md,
},
// Summary text styling
summaryText: {
fontSize: theme.typography.fontSize.bodyMedium,
fontFamily: theme.typography.fontFamily.medium,
color: theme.colors.textSecondary,
marginBottom: theme.spacing.sm,
},
// Summary value styling
summaryValue: {
fontSize: theme.typography.fontSize.bodyLarge,
fontFamily: theme.typography.fontFamily.bold,
color: theme.colors.textPrimary,
},
// Bottom spacing for tab bar
bottomSpacing: {
height: theme.spacing.xl,
},
// Empty state container styling
emptyStateContainer: {
alignItems: 'center',
paddingVertical: theme.spacing.lg,
},
// Empty state text styling
emptyStateText: {
fontSize: theme.typography.fontSize.bodyLarge,
fontFamily: theme.typography.fontFamily.bold,
color: theme.colors.textSecondary,
marginBottom: theme.spacing.xs,
},
// Empty state subtext styling
emptyStateSubtext: {
fontSize: theme.typography.fontSize.bodySmall,
fontFamily: theme.typography.fontFamily.regular,
color: theme.colors.textMuted,
textAlign: 'center',
marginBottom: theme.spacing.sm,
},
// Empty state info styling
emptyStateInfo: {
marginTop: theme.spacing.sm,
paddingHorizontal: theme.spacing.md,
},
// Empty state info text styling
emptyStateInfoText: {
fontSize: theme.typography.fontSize.bodySmall,
fontFamily: theme.typography.fontFamily.regular,
color: theme.colors.textMuted,
marginBottom: theme.spacing.xs,
},
// Retry button styling
retryButton: {
marginTop: theme.spacing.md,
paddingVertical: theme.spacing.sm,
paddingHorizontal: theme.spacing.lg,
backgroundColor: theme.colors.primary,
borderRadius: theme.borderRadius.small,
borderWidth: 1,
borderColor: theme.colors.primary,
},
// Retry button text styling
retryButtonText: {
fontSize: theme.typography.fontSize.bodySmall,
fontFamily: theme.typography.fontFamily.medium,
color: theme.colors.background,
},
// Error container styling
errorContainer: {
alignItems: 'center',
paddingVertical: theme.spacing.lg,
backgroundColor: theme.colors.backgroundAlt,
borderRadius: theme.borderRadius.medium,
marginHorizontal: theme.spacing.md,
marginBottom: theme.spacing.md,
...theme.shadows.primary,
},
// Error title styling
errorTitle: {
fontSize: theme.typography.fontSize.bodyLarge,
fontFamily: theme.typography.fontFamily.bold,
color: theme.colors.textSecondary,
marginBottom: theme.spacing.xs,
},
// Error message styling
errorMessage: {
fontSize: theme.typography.fontSize.bodyMedium,
fontFamily: theme.typography.fontFamily.regular,
color: theme.colors.textMuted,
textAlign: 'center',
marginBottom: theme.spacing.sm,
},
// Error info styling
errorInfo: {
marginTop: theme.spacing.sm,
paddingHorizontal: theme.spacing.md,
},
// Error info text styling
errorInfoText: {
fontSize: theme.typography.fontSize.bodySmall,
fontFamily: theme.typography.fontFamily.regular,
color: theme.colors.textMuted,
marginBottom: theme.spacing.xs,
},
// No data container styling
noDataContainer: {
alignItems: 'center',
paddingVertical: theme.spacing.lg,
backgroundColor: theme.colors.backgroundAlt,
borderRadius: theme.borderRadius.medium,
marginHorizontal: theme.spacing.md,
marginBottom: theme.spacing.md,
...theme.shadows.primary,
},
// No data title styling
noDataTitle: {
fontSize: theme.typography.fontSize.bodyLarge,
fontFamily: theme.typography.fontFamily.bold,
color: theme.colors.textSecondary,
marginBottom: theme.spacing.xs,
},
// No data message styling
noDataMessage: {
fontSize: theme.typography.fontSize.bodyMedium,
fontFamily: theme.typography.fontFamily.regular,
color: theme.colors.textMuted,
textAlign: 'center',
marginBottom: theme.spacing.sm,
},
// No data info styling
noDataInfo: {
marginTop: theme.spacing.sm,
paddingHorizontal: theme.spacing.md,
},
// No data info text styling
noDataInfoText: {
fontSize: theme.typography.fontSize.bodySmall,
fontFamily: theme.typography.fontFamily.regular,
color: theme.colors.textMuted,
marginBottom: theme.spacing.xs,
},
// Network status container styling
networkStatusContainer: {
alignItems: 'center',
paddingVertical: theme.spacing.md,
backgroundColor: theme.colors.backgroundAlt,
borderRadius: theme.borderRadius.small,
marginHorizontal: theme.spacing.md,
marginBottom: theme.spacing.md,
...theme.shadows.small,
},
// Network status text styling
networkStatusText: {
fontSize: theme.typography.fontSize.bodySmall,
fontFamily: theme.typography.fontFamily.medium,
color: theme.colors.warning,
marginBottom: theme.spacing.xs,
},
// Network status subtext styling
networkStatusSubtext: {
fontSize: theme.typography.fontSize.caption,
fontFamily: theme.typography.fontFamily.regular,
color: theme.colors.warning,
textAlign: 'center',
},
});
/*

View File

@ -0,0 +1,165 @@
/*
* File: dashboardAPI.ts
* Description: API service for dashboard operations using apisauce
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/
import { create } from 'apisauce';
import { API_CONFIG, buildHeaders } from '../../../shared/utils';
const api = create({
baseURL: API_CONFIG.BASE_URL
});
/**
* Dashboard API Service
*
* Purpose: Handle all dashboard-related API operations
*
* Features:
* - Get AI analysis dashboard statistics
* - Get feedback statistics for AI cases
* - Get real-time dashboard metrics
* - Get time-based analysis data
*/
export const dashboardAPI = {
/**
* Get AI Analysis Dashboard Statistics
*
* Purpose: Fetch comprehensive dashboard statistics for AI analysis
*
* @param token - Authentication token
* @returns Promise with dashboard statistics data
*/
getDashboardStatistics: (token: string) => {
return api.get('/api/ai-cases/feedbacks/statistics', {}, buildHeaders({ token }));
},
/**
* Get Real-time Dashboard Metrics
*
* Purpose: Fetch real-time dashboard metrics for live updates
*
* @param token - Authentication token
* @returns Promise with real-time dashboard metrics
*/
getRealTimeMetrics: (token: string) => {
return api.get('/api/ai-cases/feedbacks/statistics/realtime', {}, buildHeaders({ token }));
},
/**
* Get Time-based Analysis Data
*
* Purpose: Fetch time-based analysis data for trend visualization
*
* @param token - Authentication token
* @param timeRange - Time range for analysis (today, week, month, year)
* @returns Promise with time-based analysis data
*/
getTimeBasedAnalysis: (token: string, timeRange: 'today' | 'week' | 'month' | 'year') => {
return api.get(`/api/ai-cases/feedbacks/statistics/time-analysis/${timeRange}`, {}, buildHeaders({ token }));
},
/**
* Get Hospital-specific Statistics
*
* Purpose: Fetch statistics for a specific hospital
*
* @param token - Authentication token
* @param hospitalId - Hospital identifier
* @returns Promise with hospital-specific statistics
*/
getHospitalStatistics: (token: string, hospitalId: string) => {
return api.get(`/api/ai-cases/feedbacks/statistics/hospital/${hospitalId}`, {}, buildHeaders({ token }));
},
/**
* Get Department Performance Metrics
*
* Purpose: Fetch performance metrics for specific departments
*
* @param token - Authentication token
* @param department - Department name
* @returns Promise with department performance data
*/
getDepartmentMetrics: (token: string, department: string) => {
return api.get(`/api/ai-cases/feedbacks/statistics/department/${department}`, {}, buildHeaders({ token }));
},
/**
* Get Confidence Score Distribution
*
* Purpose: Fetch confidence score distribution for AI predictions
*
* @param token - Authentication token
* @param timeRange - Optional time range filter
* @returns Promise with confidence score distribution data
*/
getConfidenceDistribution: (token: string, timeRange?: 'today' | 'week' | 'month' | 'year') => {
const params = timeRange ? { timeRange } : {};
return api.get('/api/ai-cases/feedbacks/statistics/confidence-distribution', params, buildHeaders({ token }));
},
/**
* Get Urgency Level Distribution
*
* Purpose: Fetch urgency level distribution for AI cases
*
* @param token - Authentication token
* @param timeRange - Optional time range filter
* @returns Promise with urgency level distribution data
*/
getUrgencyDistribution: (token: string, timeRange?: 'today' | 'week' | 'month' | 'year') => {
const params = timeRange ? { timeRange } : {};
return api.get('/api/ai-cases/feedbacks/statistics/urgency-distribution', params, buildHeaders({ token }));
},
/**
* Get Feedback Analysis Data
*
* Purpose: Fetch feedback analysis and coverage metrics
*
* @param token - Authentication token
* @param timeRange - Optional time range filter
* @returns Promise with feedback analysis data
*/
getFeedbackAnalysis: (token: string, timeRange?: 'today' | 'week' | 'month' | 'year') => {
const params = timeRange ? { timeRange } : {};
return api.get('/api/ai-cases/feedbacks/statistics/feedback-analysis', params, buildHeaders({ token }));
},
/**
* Get Critical Findings Statistics
*
* Purpose: Fetch statistics for critical findings and cases
*
* @param token - Authentication token
* @param timeRange - Optional time range filter
* @returns Promise with critical findings statistics
*/
getCriticalFindingsStats: (token: string, timeRange?: 'today' | 'week' | 'month' | 'year') => {
const params = timeRange ? { timeRange } : {};
return api.get('/api/ai-cases/feedbacks/statistics/critical-findings', params, buildHeaders({ token }));
},
/**
* Get Prediction Breakdown Statistics
*
* Purpose: Fetch breakdown of AI predictions by category
*
* @param token - Authentication token
* @param timeRange - Optional time range filter
* @returns Promise with prediction breakdown data
*/
getPredictionBreakdown: (token: string, timeRange?: 'today' | 'week' | 'month' | 'year') => {
const params = timeRange ? { timeRange } : {};
return api.get('/api/ai-cases/feedbacks/statistics/prediction-breakdown', params, buildHeaders({ token }));
}
};
/*
* End of File: dashboardAPI.ts
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/

View File

@ -0,0 +1,14 @@
/*
* File: index.ts
* Description: Dashboard services exports
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/
export * from './dashboardAPI';
/*
* End of File: index.ts
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/

View File

@ -798,7 +798,6 @@ const PatientDetailsScreen: React.FC<PatientDetailsScreenProps> = ({ navigation,
{patientData.series_summary.map((series, seriesIndex) => {
// Get predictions for this series
{console.log('series.png_preview', series)}
const seriesPredictions = patientData.predictions_by_series[series.series_num] || [];
const hasPredictions = seriesPredictions.length > 0;

View File

@ -12,6 +12,7 @@ import AsyncStorage from '@react-native-async-storage/async-storage';
// Import all slice reducers from their respective modules
import authReducer from '../modules/Auth/redux/authSlice';
import dashboardReducer from '../modules/Dashboard/redux/dashboardSlice';
import aiDashboardReducer from '../modules/Dashboard/redux/aiDashboardSlice';
import patientCareReducer from '../modules/PatientCare/redux/patientCareSlice';
import alertsReducer from '../modules/Dashboard/redux/alertsSlice';
import settingsReducer from '../modules/Settings/redux/settingsSlice';
@ -57,6 +58,7 @@ const persistConfig = {
'ui', // UI state (loading, modals, etc.)
'alerts', // Temporary alerts and notifications
'dashboard', // Real-time dashboard data
'aiDashboard', // AI dashboard statistics (fetched fresh each time)
'hospital', // Hospital data (fetched fresh each time)
],
@ -85,6 +87,7 @@ const persistConfig = {
* Structure:
* - auth: Authentication and user management
* - dashboard: ER dashboard data and statistics
* - aiDashboard: AI analysis dashboard statistics
* - patientCare: Patient information and medical records
* - aiPrediction: AI prediction cases and analysis
* - alerts: Critical alerts and notifications
@ -94,6 +97,7 @@ const persistConfig = {
const rootReducer = combineReducers({
auth: authReducer,
dashboard: dashboardReducer,
aiDashboard: aiDashboardReducer,
patientCare: patientCareReducer,
aiPrediction: aiPredictionReducer,
alerts: alertsReducer,