NeoScan_Physician/app/modules/Dashboard/screens/DashboardScreen.tsx
2025-08-22 14:57:50 +05:30

1294 lines
39 KiB
TypeScript

/*
* File: DashboardScreen.tsx
* Description: AI Analysis Dashboard - Main dashboard for AI predictions and analysis statistics
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/
import React, { useState } from 'react';
import {
View,
Text,
StyleSheet,
ScrollView,
TouchableOpacity,
RefreshControl,
FlatList,
Dimensions,
Alert,
} from 'react-native';
import Icon from 'react-native-vector-icons/Feather';
import { theme } from '../../../theme/theme';
import { DashboardHeader } from '../components/DashboardHeader';
import { BrainPredictionsOverview } from '../components/BrainPredictionsOverview';
import { FeedbackAnalysisPieChart } from '../components/FeedbackAnalysisPieChart';
import { PredictionsList } from '../components/PredictionsList';
import { useAIDashboard } from '../hooks/useAIDashboard';
import { selectUserDisplayName, selectUserFirstName } from '../../Auth/redux/authSelectors';
import { useAppSelector } from '../../../store/hooks';
import { CompositeNavigationProp } from '@react-navigation/native';
import { BottomTabNavigationProp } from '@react-navigation/bottom-tabs';
import { StackNavigationProp } from '@react-navigation/stack';
import { MainTabParamList } from '../../../navigation/navigationTypes';
import { PatientCareStackParamList } from '../../PatientCare/navigation/navigationTypes';
/**
* DashboardScreenProps Interface
*
* Purpose: Defines the props required by the DashboardScreen component
*
* Props:
* - navigation: Composite navigation object for tab and stack navigation
*/
type DashboardScreenNavigationProp = CompositeNavigationProp<
BottomTabNavigationProp<MainTabParamList, 'Dashboard'>,
StackNavigationProp<PatientCareStackParamList>
>;
interface DashboardScreenProps {
navigation: DashboardScreenNavigationProp;
}
/**
* Dashboard Stats Data Interface
*
* Purpose: Defines the structure of the dashboard statistics data
*/
interface DashboardStats {
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;
}
/**
* Dashboard Summary Interface
*
* Purpose: Defines the structure of the dashboard summary data
*/
interface DashboardSummary {
total_cases: number;
critical_cases: number;
routine_cases: number;
feedback_coverage: string;
critical_case_rate: string;
average_confidence: string;
}
/**
* Complete Dashboard Data Interface
*
* Purpose: Defines the complete structure of the dashboard API response
*/
interface DashboardData {
success: boolean;
data: DashboardStats;
summary: DashboardSummary;
message: string;
}
/**
* DashboardScreen Component
*
* Purpose: AI Analysis Dashboard for physicians showing prediction statistics
*
* Dashboard Features:
* 1. AI prediction statistics and breakdown
* 2. Feedback analysis and coverage metrics
* 3. Confidence score distribution
* 4. Time-based analysis trends
* 5. Urgency level distribution
* 6. Pull-to-refresh functionality for live updates
*/
export const DashboardScreen: React.FC<DashboardScreenProps> = ({
navigation,
}) => {
// ============================================================================
// CUSTOM HOOKS & SELECTORS
// ============================================================================
// Use custom hook for AI dashboard functionality
const {
dashboardData,
isLoading,
isRefreshing,
error,
dashboardMessage,
refreshDashboardStatistics
} = useAIDashboard();
// Get user display name from auth state
const userDisplayName = useAppSelector(selectUserFirstName);
// ============================================================================
// HELPER FUNCTIONS
// ============================================================================
/**
* getPersonalizedGreeting Function
*
* Purpose: Generate a personalized greeting based on time of day and user's display name
*
* @returns Personalized greeting string
*/
const getPersonalizedGreeting = (): string => {
const currentHour = new Date().getHours();
let timeGreeting = '';
// Determine time-based greeting
if (currentHour >= 5 && currentHour < 12) {
timeGreeting = 'Good Morning';
} else if (currentHour >= 12 && currentHour < 17) {
timeGreeting = 'Good Afternoon';
} else if (currentHour >= 17 && currentHour < 21) {
timeGreeting = 'Good Evening';
} else {
timeGreeting = 'Good Evening';
}
// Create personalized greeting with fallback
const displayName = userDisplayName || 'Doctor';
return `${timeGreeting}, Dr. ${displayName}`;
};
// ============================================================================
// EVENT HANDLERS
// ============================================================================
/**
* handleRefresh Function
*
* Purpose: Handle pull-to-refresh functionality to update dashboard data
*/
const handleRefresh = async () => {
// 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
*
* Purpose: Render individual statistics card component
*
* @param title - Card title
* @param value - Main value to display
* @param subtitle - Optional subtitle
* @param color - Optional color theme
* @param iconName - Icon name for the stats card
* @returns Statistics card component
*/
const renderStatsCard = (title: string, value: string | number, subtitle?: string, color?: string, iconName?: string) => (
<View style={[styles.statsCard, color && { borderLeftColor: color, borderLeftWidth: 4 }]}>
{/* Icon and Title Row */}
<View style={styles.statsCardHeader}>
{iconName && (
<View style={[styles.statsCardIcon, { backgroundColor: color ? color + '20' : theme.colors.backgroundAccent }]}>
<Icon name={iconName} size={20} color={color || theme.colors.primary} />
</View>
)}
<Text style={styles.statsCardTitle}>{title}</Text>
</View>
<Text style={[styles.statsCardValue, color && { color }]}>{value}</Text>
{subtitle && <Text style={styles.statsCardSubtitle}>{subtitle}</Text>}
</View>
);
/**
* renderConfidenceBreakdown Function
*
* Purpose: Render confidence score breakdown section
*/
/**
* renderUrgencyBreakdown Function
*
* Purpose: Render urgency level breakdown section with animated colored circles
*/
const renderUrgencyBreakdown = () => {
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}>
<Text style={styles.sectionTitle}>Case Urgency Distribution</Text>
<View style={styles.urgencyContainer}>
{/* Critical Cases Circle */}
<View style={styles.urgencyCircleItem}>
<View
style={[
styles.circleContainer,
{ borderColor: theme.colors.error }
]}
>
<Text style={styles.circleValue}>{critical}</Text>
</View>
<Text style={styles.urgencyLabel}>Critical</Text>
</View>
{/* Urgent Cases Circle */}
<View style={styles.urgencyCircleItem}>
<View
style={[
styles.circleContainer,
{ borderColor: theme.colors.warning }
]}
>
<Text style={styles.circleValue}>{urgent}</Text>
</View>
<Text style={styles.urgencyLabel}>Urgent</Text>
</View>
{/* Routine Cases Circle */}
<View style={styles.urgencyCircleItem}>
<View
style={[
styles.circleContainer,
{ borderColor: theme.colors.success }
]}
>
<Text style={styles.circleValue}>{routine}</Text>
</View>
<Text style={styles.urgencyLabel}>Routine</Text>
</View>
</View>
</View>
);
};
/**
* renderFeedbackAnalysis Function
*
* Purpose: Render feedback analysis section with pie chart
*/
const renderFeedbackAnalysis = () => {
if (!dashboardData?.data.feedback_analysis) return null;
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>
{/* 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
*
* Purpose: Render time-based analysis section
*/
const renderTimeAnalysis = () => {
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}>
<Text style={styles.sectionTitle}>Time-based Analysis</Text>
<View style={styles.timeContainer}>
<View style={styles.timeItem}>
<Text style={styles.timeLabel}>Today</Text>
<Text style={styles.timeValue}>{today}</Text>
</View>
<View style={styles.timeItem}>
<Text style={styles.timeLabel}>This Week</Text>
<Text style={styles.timeValue}>{this_week}</Text>
</View>
<View style={styles.timeItem}>
<Text style={styles.timeLabel}>This Month</Text>
<Text style={styles.timeValue}>{this_month}</Text>
</View>
<View style={styles.timeItem}>
<Text style={styles.timeLabel}>This Year</Text>
<Text style={styles.timeValue}>{this_year}</Text>
</View>
</View>
</View>
);
};
/**
* renderHeader Function
*
* Purpose: Render the dashboard header section with key metrics
*/
const renderHeader = () => (
<View style={styles.header}>
{/* Dashboard header with title and refresh button */}
<View style={styles.headerTop}>
<Text style={styles.dashboardTitle}>{getPersonalizedGreeting()}</Text>
<Text style={styles.dashboardSubtitle}>
{dashboardMessage}
</Text>
</View>
{/* Key statistics cards */}
<View style={styles.statsGrid}>
{renderStatsCard(
'Total Predictions',
dashboardData?.data.total_predictions || 0,
'AI analyses performed',
theme.colors.primary,
'activity'
)}
{renderStatsCard(
'Total Patients',
dashboardData?.data.total_patients || 0,
'Unique patients',
theme.colors.info,
'users'
)}
{renderStatsCard(
'Feedback Rate',
`${dashboardData?.data.feedback_rate_percentage || 0}%`,
'User feedback coverage',
theme.colors.success,
'message-circle'
)}
{renderStatsCard(
'Avg Confidence',
(dashboardData?.data.average_confidence_score || 0).toFixed(2),
'AI prediction confidence',
theme.colors.warning,
'trending-up'
)}
</View>
</View>
);
// ============================================================================
// MAIN RENDER
// ============================================================================
// 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}>
<Text style={styles.loadingText}>Loading AI Analysis Dashboard...</Text>
</View>
);
}
// Show no data state if dashboard loads but has no meaningful data
if (!dashboardData || !dashboardData.data) {
return renderNoDataState();
}
return (
<View style={styles.container}>
{/* Scrollable dashboard content */}
<ScrollView
style={styles.scrollView}
contentContainerStyle={styles.scrollContent}
refreshControl={
<RefreshControl
refreshing={isRefreshing}
onRefresh={handleRefresh}
colors={[theme.colors.primary]}
tintColor={theme.colors.primary}
/>
}
showsVerticalScrollIndicator={false}
>
{/* Dashboard header with key metrics */}
{renderHeader()}
{/* Urgency level breakdown */}
{renderUrgencyBreakdown()}
{/* Feedback analysis */}
{renderFeedbackAnalysis()}
{/* Time-based analysis */}
{renderTimeAnalysis()}
{/* AI Predictions List - Moved to main ScrollView */}
<View style={styles.section}>
<Text style={styles.sectionTitle}>AI Predictions</Text>
<Text style={styles.sectionSubtitle}>
Review AI predictions with and without user feedback
</Text>
</View>
{/* PredictionsList rendered directly */}
<View style={styles.predictionsContainer}>
<PredictionsList
onPredictionPress={(prediction) => {
try {
// Navigate to FeedbackDetailScreen with required parameters
navigation.navigate('FeedbackDetail',
{
patientId: prediction.patid,
patientName: prediction.patientdetails.Name || 'Unknown Patient',
seriesNumber: prediction.prediction.processing_info.filename || 'Unknown Series',
seriesData: {
series_num: prediction.prediction.processing_info.filename || 'Unknown Series',
series_description: prediction.prediction.finding_type || 'AI Analysis',
total_images: prediction.prediction.processing_info.frame_count || 0,
png_preview: prediction.preview || '',
modality: prediction.patientdetails.Modality || 'Unknown'
},
patientData: {
patid: prediction.patid,
hospital_id: prediction.hospital_id,
patient_info: {
name: prediction.patientdetails.Name || 'Unknown Patient',
age: prediction.patientdetails.PatAge || 'Unknown',
sex: prediction.patientdetails.PatSex || 'Unknown',
date: prediction.patientdetails.Date || 'Unknown',
institution: prediction.patientdetails.InstName || 'Unknown Institution',
modality: prediction.patientdetails.Modality || 'Unknown Modality',
status: prediction.patientdetails.Status || 'Unknown',
report_status: prediction.patientdetails.ReportStatus || 'Unknown',
file_name: prediction.prediction.processing_info.filename || 'Unknown',
file_type: prediction.prediction.processing_info.file_type || 'Unknown',
frame_count: prediction.prediction.processing_info.frame_count || 0
},
series_summary: [{
series_num: prediction.prediction.processing_info.filename || 'Unknown Series',
series_description: prediction.prediction.finding_type || 'AI Analysis',
total_images: prediction.prediction.processing_info.frame_count || 0,
png_preview: prediction.preview || '',
modality: prediction.patientdetails.Modality || 'Unknown'
}],
processing_metadata: prediction.processing_metadata,
total_predictions: 1,
first_processed_at: prediction.processed_at,
last_processed_at: prediction.processed_at
},
feedbackData: prediction.feedbacks || [],
onFeedbackSubmitted: () => {
// Refresh dashboard data when feedback is submitted
console.log('Feedback submitted, refreshing dashboard...');
}
}
);
console.log('Navigation successful to FeedbackDetailScreen');
} catch (error) {
console.error('Navigation error:', error);
// Fallback: show alert or handle error gracefully
Alert.alert(
'Navigation Error',
'Unable to open feedback details. Please try again.',
[{ text: 'OK' }]
);
}
}}
/>
</View>
{/* Bottom spacing for tab bar */}
<View style={styles.bottomSpacing} />
</ScrollView>
</View>
);
};
// ============================================================================
// STYLES SECTION
// ============================================================================
const styles = StyleSheet.create({
// Main container for the dashboard screen
container: {
flex: 1,
backgroundColor: theme.colors.background,
},
// Loading container for initial data loading
loadingContainer: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: theme.colors.background,
},
// Loading text styling
loadingText: {
fontSize: theme.typography.fontSize.bodyLarge,
color: theme.colors.textSecondary,
fontFamily: theme.typography.fontFamily.medium,
},
// Scroll view styling
scrollView: {
flex: 1,
},
// Scroll content styling
scrollContent: {
paddingBottom: theme.spacing.lg,
},
// Header section containing dashboard components
header: {
paddingHorizontal: theme.spacing.md,
paddingTop: theme.spacing.md,
},
// Header top section with title
headerTop: {
marginBottom: theme.spacing.lg,
},
// Dashboard title styling
dashboardTitle: {
fontSize: theme.typography.fontSize.displayMedium,
fontFamily: theme.typography.fontFamily.bold,
color: theme.colors.textPrimary,
marginBottom: theme.spacing.xs,
marginTop: theme.spacing.sm,
},
// Dashboard subtitle styling
dashboardSubtitle: {
fontSize: theme.typography.fontSize.bodyMedium,
fontFamily: theme.typography.fontFamily.regular,
color: theme.colors.textSecondary,
},
// Stats grid container
statsGrid: {
flexDirection: 'row',
flexWrap: 'wrap',
gap: theme.spacing.sm,
marginBottom: theme.spacing.lg,
},
// Individual stats card styling
statsCard: {
flex: 1,
minWidth: '45%',
backgroundColor: theme.colors.background,
borderRadius: theme.borderRadius.medium,
padding: theme.spacing.md,
borderLeftWidth: 0,
borderLeftColor: 'transparent',
...theme.shadows.primary,
},
// Stats card header styling (icon + title row)
statsCardHeader: {
flexDirection: 'row',
alignItems: 'center',
marginBottom: theme.spacing.xs,
gap: theme.spacing.sm,
},
// Stats card icon styling
statsCardIcon: {
width: 32,
height: 32,
borderRadius: theme.borderRadius.small,
justifyContent: 'center',
alignItems: 'center',
},
// Stats card title styling
statsCardTitle: {
fontSize: theme.typography.fontSize.bodySmall,
fontFamily: theme.typography.fontFamily.medium,
color: theme.colors.textSecondary,
flex: 1,
},
// Stats card value styling
statsCardValue: {
fontSize: theme.typography.fontSize.displayMedium,
fontFamily: theme.typography.fontFamily.bold,
color: theme.colors.textPrimary,
marginBottom: theme.spacing.xs,
},
// Stats card subtitle styling
statsCardSubtitle: {
fontSize: theme.typography.fontSize.bodySmall,
fontFamily: theme.typography.fontFamily.regular,
color: theme.colors.textMuted,
},
// Section container styling
section: {
backgroundColor: theme.colors.background,
borderRadius: theme.borderRadius.medium,
padding: theme.spacing.md,
marginHorizontal: theme.spacing.md,
marginBottom: theme.spacing.md,
...theme.shadows.primary,
},
// Section title styling
sectionTitle: {
fontSize: theme.typography.fontSize.displaySmall,
fontFamily: theme.typography.fontFamily.bold,
color: theme.colors.textPrimary,
marginBottom: theme.spacing.lg,
},
// Section subtitle styling
sectionSubtitle: {
fontSize: theme.typography.fontSize.bodyMedium,
fontFamily: theme.typography.fontFamily.regular,
color: theme.colors.textSecondary,
marginBottom: theme.spacing.md,
},
// Predictions container styling
predictionsContainer: {
height: 500, // Increased height for better FlatList performance
borderRadius: theme.borderRadius.medium,
overflow: 'hidden',
marginHorizontal: theme.spacing.md,
marginBottom: theme.spacing.md,
},
// Confidence breakdown container
confidenceContainer: {
gap: theme.spacing.md,
},
// Confidence item styling
confidenceItem: {
backgroundColor: theme.colors.backgroundAlt,
borderRadius: theme.borderRadius.medium,
padding: theme.spacing.md,
marginBottom: theme.spacing.sm,
...theme.shadows.small,
},
// Confidence bar styling
confidenceBar: {
height: 12,
borderRadius: theme.borderRadius.small,
backgroundColor: theme.colors.primary,
},
// Confidence label styling
confidenceLabel: {
fontSize: theme.typography.fontSize.bodyMedium,
fontFamily: theme.typography.fontFamily.medium,
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
urgencyContainer: {
flexDirection: 'row',
justifyContent: 'space-around',
alignItems: 'flex-start',
paddingHorizontal: theme.spacing.sm,
},
// Circle Container styling
circleContainer: {
width: 80,
height: 80,
borderRadius: 40,
justifyContent: 'center',
alignItems: 'center',
borderWidth: 8,
backgroundColor: theme.colors.background,
shadowColor: '#000',
shadowOffset: { width: 0, height: 4 },
shadowOpacity: 0.3,
shadowRadius: 8,
elevation: 8,
},
// Circle Value styling
circleValue: {
fontSize: theme.typography.fontSize.bodyLarge,
fontFamily: theme.typography.fontFamily.bold,
color: theme.colors.textPrimary,
},
// Urgency Label styling
urgencyLabel: {
fontSize: theme.typography.fontSize.bodySmall,
fontFamily: theme.typography.fontFamily.medium,
color: theme.colors.textSecondary,
marginTop: theme.spacing.sm,
textAlign: 'center',
},
// Urgency Circle Item styling
urgencyCircleItem: {
alignItems: 'center',
flex: 1,
},
// Feedback container styling
feedbackContainer: {
flexDirection: 'row',
justifyContent: 'space-around',
marginBottom: theme.spacing.md,
},
// Feedback item styling
feedbackItem: {
alignItems: 'center',
flex: 1,
},
// Feedback indicator styling
feedbackIndicator: {
width: 16,
height: 16,
borderRadius: 8,
marginBottom: theme.spacing.xs,
},
// Feedback label styling
feedbackLabel: {
fontSize: theme.typography.fontSize.bodySmall,
fontFamily: theme.typography.fontFamily.medium,
color: theme.colors.textSecondary,
marginBottom: theme.spacing.xs,
},
// Feedback value styling
feedbackValue: {
fontSize: theme.typography.fontSize.bodyMedium,
fontFamily: theme.typography.fontFamily.bold,
color: theme.colors.textPrimary,
marginBottom: theme.spacing.xs,
},
// Feedback percentage styling
feedbackPercentage: {
fontSize: theme.typography.fontSize.bodySmall,
fontFamily: theme.typography.fontFamily.regular,
color: theme.colors.textMuted,
},
// Feedback summary styling
feedbackSummary: {
borderTopWidth: 1,
borderTopColor: theme.colors.border,
paddingTop: theme.spacing.md,
},
// Feedback summary text styling
feedbackSummaryText: {
fontSize: theme.typography.fontSize.bodySmall,
fontFamily: theme.typography.fontFamily.regular,
color: theme.colors.textSecondary,
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: {
flexDirection: 'row',
justifyContent: 'space-around',
},
// Time item styling
timeItem: {
alignItems: 'center',
flex: 1,
},
// Time label styling
timeLabel: {
fontSize: theme.typography.fontSize.bodySmall,
fontFamily: theme.typography.fontFamily.medium,
color: theme.colors.textSecondary,
marginBottom: theme.spacing.xs,
},
// Time value styling
timeValue: {
fontSize: theme.typography.fontSize.bodyMedium,
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',
},
});
/*
* End of File: DashboardScreen.tsx
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/