455 lines
13 KiB
TypeScript
455 lines
13 KiB
TypeScript
/*
|
|
* File: StatsOverview.tsx
|
|
* Description: Statistics overview component for AI predictions dashboard
|
|
* Design & Developed by Tech4Biz Solutions
|
|
* Copyright (c) Spurrin Innovations. All rights reserved.
|
|
*/
|
|
|
|
import React from 'react';
|
|
import {
|
|
View,
|
|
Text,
|
|
StyleSheet,
|
|
TouchableOpacity,
|
|
Dimensions,
|
|
} from 'react-native';
|
|
import Icon from 'react-native-vector-icons/Feather';
|
|
import { theme } from '../../../theme';
|
|
import type { AIPredictionStats } from '../types';
|
|
|
|
// ============================================================================
|
|
// INTERFACES
|
|
// ============================================================================
|
|
|
|
interface StatsOverviewProps {
|
|
stats: AIPredictionStats;
|
|
onStatsPress?: (statType: string) => void;
|
|
isLoading?: boolean;
|
|
style?: any;
|
|
}
|
|
|
|
interface StatCardProps {
|
|
title: string;
|
|
value: string | number;
|
|
subtitle?: string;
|
|
iconName: string;
|
|
color: string;
|
|
onPress?: () => void;
|
|
trend?: number;
|
|
isPercentage?: boolean;
|
|
}
|
|
|
|
// ============================================================================
|
|
// CONSTANTS
|
|
// ============================================================================
|
|
|
|
const { width } = Dimensions.get('window');
|
|
const CARD_WIDTH = (width - 48) / 2; // Two cards per row with margins
|
|
|
|
// ============================================================================
|
|
// STAT CARD COMPONENT
|
|
// ============================================================================
|
|
|
|
/**
|
|
* StatCard Component
|
|
*
|
|
* Purpose: Individual statistics card
|
|
*/
|
|
const StatCard: React.FC<StatCardProps> = ({
|
|
title,
|
|
value,
|
|
subtitle,
|
|
iconName,
|
|
color,
|
|
onPress,
|
|
trend,
|
|
isPercentage = false,
|
|
}) => {
|
|
const displayValue = typeof value === 'number'
|
|
? isPercentage
|
|
? `${Math.round(value * 100)}%`
|
|
: value.toLocaleString()
|
|
: value;
|
|
|
|
return (
|
|
<TouchableOpacity
|
|
style={[styles.statCard, { borderLeftColor: color }]}
|
|
onPress={onPress}
|
|
disabled={!onPress}
|
|
accessibilityRole="button"
|
|
accessibilityLabel={`${title}: ${displayValue}${subtitle ? `, ${subtitle}` : ''}`}
|
|
>
|
|
{/* Card Header */}
|
|
<View style={styles.cardHeader}>
|
|
<View style={{flexDirection: 'row', alignItems: 'center', gap: theme.spacing.sm}}>
|
|
<View style={[styles.iconContainer, { backgroundColor: color + '20' }]}>
|
|
<Icon name={iconName} size={20} color={color} />
|
|
</View>
|
|
<Text style={styles.statValue}>{displayValue}</Text>
|
|
</View>
|
|
|
|
|
|
{trend !== undefined && (
|
|
<View style={styles.trendContainer}>
|
|
<Icon
|
|
name={trend >= 0 ? 'trending-up' : 'trending-down'}
|
|
size={14}
|
|
color={trend >= 0 ? theme.colors.success : theme.colors.error}
|
|
/>
|
|
<Text style={[
|
|
styles.trendText,
|
|
{ color: trend >= 0 ? theme.colors.success : theme.colors.error }
|
|
]}>
|
|
{Math.abs(trend).toFixed(1)}%
|
|
</Text>
|
|
</View>
|
|
)}
|
|
</View>
|
|
|
|
{/* Card Content */}
|
|
<View style={styles.cardContent}>
|
|
|
|
<Text style={styles.statTitle} numberOfLines={2}>{title}</Text>
|
|
{subtitle && (
|
|
<Text style={styles.statSubtitle} numberOfLines={1}>{subtitle}</Text>
|
|
)}
|
|
</View>
|
|
</TouchableOpacity>
|
|
);
|
|
};
|
|
|
|
// ============================================================================
|
|
// STATS OVERVIEW COMPONENT
|
|
// ============================================================================
|
|
|
|
/**
|
|
* StatsOverview Component
|
|
*
|
|
* Purpose: Display comprehensive AI predictions statistics
|
|
*
|
|
* Features:
|
|
* - Total cases overview
|
|
* - Critical and urgent case counts
|
|
* - Review progress tracking
|
|
* - Average confidence metrics
|
|
* - Trend indicators
|
|
* - Interactive stat cards
|
|
* - Responsive grid layout
|
|
* - Modern card design
|
|
* - Accessibility support
|
|
*/
|
|
const StatsOverview: React.FC<StatsOverviewProps> = ({
|
|
stats,
|
|
onStatsPress,
|
|
isLoading = false,
|
|
style,
|
|
}) => {
|
|
// ============================================================================
|
|
// EVENT HANDLERS
|
|
// ============================================================================
|
|
|
|
/**
|
|
* Handle Stat Press
|
|
*
|
|
* Purpose: Handle statistics card press
|
|
*/
|
|
const handleStatPress = (statType: string) => {
|
|
if (onStatsPress) {
|
|
onStatsPress(statType);
|
|
}
|
|
};
|
|
|
|
// ============================================================================
|
|
// RENDER
|
|
// ============================================================================
|
|
|
|
if (isLoading) {
|
|
return (
|
|
<View style={[styles.container, style]}>
|
|
<View style={styles.header}>
|
|
<Text style={styles.sectionTitle}>AI Predictions Overview</Text>
|
|
</View>
|
|
<View style={styles.loadingContainer}>
|
|
<Text style={styles.loadingText}>Loading statistics...</Text>
|
|
</View>
|
|
</View>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<View style={[styles.container, style]}>
|
|
{/* Section Header */}
|
|
<View style={styles.header}>
|
|
<Text style={styles.sectionTitle}>AI Predictions Overview</Text>
|
|
<TouchableOpacity
|
|
style={styles.viewAllButton}
|
|
onPress={() => handleStatPress('all')}
|
|
accessibilityRole="button"
|
|
accessibilityLabel="View all statistics"
|
|
>
|
|
<Text style={styles.viewAllText}>View All</Text>
|
|
<Icon name="arrow-right" size={16} color={theme.colors.primary} />
|
|
</TouchableOpacity>
|
|
</View>
|
|
|
|
{/* Statistics Grid */}
|
|
<View style={styles.statsGrid}>
|
|
{/* Total Cases */}
|
|
<StatCard
|
|
title="Total Cases"
|
|
value={stats.totalCases}
|
|
subtitle="All predictions"
|
|
iconName="database"
|
|
color={theme.colors.primary}
|
|
onPress={() => handleStatPress('total')}
|
|
/>
|
|
|
|
{/* Critical Cases */}
|
|
<StatCard
|
|
title="Critical Cases"
|
|
value={stats.criticalCases}
|
|
subtitle="Require attention"
|
|
iconName="alert-triangle"
|
|
color={theme.colors.error}
|
|
onPress={() => handleStatPress('critical')}
|
|
/>
|
|
|
|
{/* Urgent Cases */}
|
|
<StatCard
|
|
title="Urgent Cases"
|
|
value={stats.urgentCases}
|
|
subtitle="High priority"
|
|
iconName="clock"
|
|
color={theme.colors.warning}
|
|
onPress={() => handleStatPress('urgent')}
|
|
/>
|
|
|
|
{/* Reviewed Cases */}
|
|
<StatCard
|
|
title="Reviewed Cases"
|
|
value={stats.reviewedCases}
|
|
subtitle="Completed reviews"
|
|
iconName="check-circle"
|
|
color={theme.colors.success}
|
|
onPress={() => handleStatPress('reviewed')}
|
|
/>
|
|
|
|
{/* Pending Cases */}
|
|
<StatCard
|
|
title="Pending Reviews"
|
|
value={stats.pendingCases}
|
|
subtitle="Awaiting review"
|
|
iconName="eye"
|
|
color={theme.colors.info}
|
|
onPress={() => handleStatPress('pending')}
|
|
/>
|
|
|
|
{/* Average Confidence */}
|
|
<StatCard
|
|
title="Avg Confidence"
|
|
value={stats.averageConfidence}
|
|
subtitle="AI accuracy"
|
|
iconName="trending-up"
|
|
color={theme.colors.primary}
|
|
onPress={() => handleStatPress('confidence')}
|
|
isPercentage={true}
|
|
/>
|
|
|
|
{/* Today's Cases */}
|
|
<StatCard
|
|
title="Today's Cases"
|
|
value={stats.todaysCases}
|
|
subtitle="New predictions"
|
|
iconName="calendar"
|
|
color={theme.colors.info}
|
|
onPress={() => handleStatPress('today')}
|
|
/>
|
|
|
|
{/* Weekly Trend */}
|
|
<StatCard
|
|
title="Weekly Trend"
|
|
value={`${stats.weeklyTrend >= 0 ? '+' : ''}${stats.weeklyTrend.toFixed(1)}%`}
|
|
subtitle="vs last week"
|
|
iconName={stats.weeklyTrend >= 0 ? 'trending-up' : 'trending-down'}
|
|
color={stats.weeklyTrend >= 0 ? theme.colors.success : theme.colors.error}
|
|
onPress={() => handleStatPress('trend')}
|
|
trend={stats.weeklyTrend}
|
|
/>
|
|
</View>
|
|
|
|
{/* Summary Section */}
|
|
<View style={styles.summarySection}>
|
|
<View style={styles.summaryCard}>
|
|
<View style={styles.summaryHeader}>
|
|
<Icon name="activity" size={20} color={theme.colors.primary} />
|
|
<Text style={styles.summaryTitle}>Quick Insights</Text>
|
|
</View>
|
|
|
|
<View style={styles.summaryContent}>
|
|
<View style={styles.summaryItem}>
|
|
<Text style={styles.summaryLabel}>Review Progress:</Text>
|
|
<Text style={styles.summaryValue}>
|
|
{Math.round((stats.reviewedCases / stats.totalCases) * 100)}%
|
|
</Text>
|
|
</View>
|
|
|
|
<View style={styles.summaryItem}>
|
|
<Text style={styles.summaryLabel}>Critical Rate:</Text>
|
|
<Text style={styles.summaryValue}>
|
|
{Math.round((stats.criticalCases / stats.totalCases) * 100)}%
|
|
</Text>
|
|
</View>
|
|
|
|
<View style={styles.summaryItem}>
|
|
<Text style={styles.summaryLabel}>Daily Average:</Text>
|
|
<Text style={styles.summaryValue}>
|
|
{Math.round(stats.totalCases / 7)} cases
|
|
</Text>
|
|
</View>
|
|
</View>
|
|
</View>
|
|
</View>
|
|
</View>
|
|
);
|
|
};
|
|
|
|
// ============================================================================
|
|
// STYLES
|
|
// ============================================================================
|
|
|
|
const styles = StyleSheet.create({
|
|
container: {
|
|
backgroundColor: theme.colors.background,
|
|
paddingVertical: theme.spacing.lg,
|
|
},
|
|
header: {
|
|
flexDirection: 'row',
|
|
justifyContent: 'space-between',
|
|
alignItems: 'center',
|
|
paddingHorizontal: theme.spacing.md,
|
|
marginBottom: theme.spacing.lg,
|
|
},
|
|
sectionTitle: {
|
|
fontSize: theme.typography.fontSize.displaySmall,
|
|
fontWeight: theme.typography.fontWeight.bold,
|
|
color: theme.colors.textPrimary,
|
|
},
|
|
viewAllButton: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
gap: theme.spacing.xs,
|
|
},
|
|
viewAllText: {
|
|
fontSize: theme.typography.fontSize.bodyMedium,
|
|
color: theme.colors.primary,
|
|
fontWeight: theme.typography.fontWeight.medium,
|
|
},
|
|
loadingContainer: {
|
|
paddingVertical: theme.spacing.xxl,
|
|
alignItems: 'center',
|
|
},
|
|
loadingText: {
|
|
fontSize: theme.typography.fontSize.bodyMedium,
|
|
color: theme.colors.textMuted,
|
|
},
|
|
statsGrid: {
|
|
flexDirection: 'row',
|
|
flexWrap: 'wrap',
|
|
paddingHorizontal: theme.spacing.md,
|
|
gap: theme.spacing.md,
|
|
},
|
|
statCard: {
|
|
width: CARD_WIDTH,
|
|
backgroundColor: theme.colors.background,
|
|
borderRadius: theme.borderRadius.medium,
|
|
borderLeftWidth: 4,
|
|
padding: theme.spacing.md,
|
|
...theme.shadows.medium,
|
|
},
|
|
cardHeader: {
|
|
flexDirection: 'row',
|
|
justifyContent: 'space-between',
|
|
alignItems: 'center',
|
|
marginBottom: theme.spacing.sm,
|
|
},
|
|
iconContainer: {
|
|
width: 36,
|
|
height: 36,
|
|
borderRadius: 18,
|
|
justifyContent: 'center',
|
|
alignItems: 'center',
|
|
},
|
|
trendContainer: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
gap: theme.spacing.xs,
|
|
},
|
|
trendText: {
|
|
fontSize: theme.typography.fontSize.caption,
|
|
fontWeight: theme.typography.fontWeight.medium,
|
|
},
|
|
cardContent: {
|
|
gap: theme.spacing.xs,
|
|
},
|
|
statValue: {
|
|
fontSize: theme.typography.fontSize.displayMedium,
|
|
fontWeight: theme.typography.fontWeight.bold,
|
|
color: theme.colors.textPrimary,
|
|
},
|
|
statTitle: {
|
|
fontSize: theme.typography.fontSize.bodyMedium,
|
|
color: theme.colors.textSecondary,
|
|
fontWeight: theme.typography.fontWeight.medium,
|
|
},
|
|
statSubtitle: {
|
|
fontSize: theme.typography.fontSize.bodySmall,
|
|
color: theme.colors.textMuted,
|
|
},
|
|
summarySection: {
|
|
paddingHorizontal: theme.spacing.md,
|
|
marginTop: theme.spacing.lg,
|
|
},
|
|
summaryCard: {
|
|
backgroundColor: theme.colors.background,
|
|
borderRadius: theme.borderRadius.medium,
|
|
padding: theme.spacing.lg,
|
|
...theme.shadows.small,
|
|
},
|
|
summaryHeader: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
gap: theme.spacing.sm,
|
|
marginBottom: theme.spacing.md,
|
|
},
|
|
summaryTitle: {
|
|
fontSize: theme.typography.fontSize.bodyLarge,
|
|
fontWeight: theme.typography.fontWeight.bold,
|
|
color: theme.colors.textPrimary,
|
|
},
|
|
summaryContent: {
|
|
gap: theme.spacing.sm,
|
|
},
|
|
summaryItem: {
|
|
flexDirection: 'row',
|
|
justifyContent: 'space-between',
|
|
alignItems: 'center',
|
|
},
|
|
summaryLabel: {
|
|
fontSize: theme.typography.fontSize.bodyMedium,
|
|
color: theme.colors.textSecondary,
|
|
},
|
|
summaryValue: {
|
|
fontSize: theme.typography.fontSize.bodyMedium,
|
|
fontWeight: theme.typography.fontWeight.bold,
|
|
color: theme.colors.textPrimary,
|
|
},
|
|
});
|
|
|
|
export default StatsOverview;
|
|
|
|
/*
|
|
* End of File: StatsOverview.tsx
|
|
* Design & Developed by Tech4Biz Solutions
|
|
* Copyright (c) Spurrin Innovations. All rights reserved.
|
|
*/
|