369 lines
9.8 KiB
JavaScript
369 lines
9.8 KiB
JavaScript
|
|
/**
|
||
|
|
* StatsView Component
|
||
|
|
* Zoomer statistics and earnings dashboard
|
||
|
|
*/
|
||
|
|
|
||
|
|
import React from 'react';
|
||
|
|
import {
|
||
|
|
View,
|
||
|
|
Text,
|
||
|
|
StyleSheet,
|
||
|
|
ScrollView,
|
||
|
|
Animated,
|
||
|
|
} from 'react-native';
|
||
|
|
import Icon from 'react-native-vector-icons/MaterialIcons';
|
||
|
|
|
||
|
|
export default function StatsView({ stats }) {
|
||
|
|
const fadeAnim = new Animated.Value(0);
|
||
|
|
|
||
|
|
React.useEffect(() => {
|
||
|
|
Animated.timing(fadeAnim, {
|
||
|
|
toValue: 1,
|
||
|
|
duration: 600,
|
||
|
|
useNativeDriver: true,
|
||
|
|
}).start();
|
||
|
|
}, []);
|
||
|
|
|
||
|
|
const renderStatCard = (icon, title, value, subtitle, color) => (
|
||
|
|
<View style={[styles.statCard, { borderLeftColor: color }]}>
|
||
|
|
<View style={styles.statIconContainer}>
|
||
|
|
<Icon name={icon} size={24} color={color} />
|
||
|
|
</View>
|
||
|
|
<View style={styles.statContent}>
|
||
|
|
<Text style={styles.statValue}>{value}</Text>
|
||
|
|
<Text style={styles.statTitle}>{title}</Text>
|
||
|
|
{subtitle && <Text style={styles.statSubtitle}>{subtitle}</Text>}
|
||
|
|
</View>
|
||
|
|
</View>
|
||
|
|
);
|
||
|
|
|
||
|
|
const renderLevelProgress = () => {
|
||
|
|
const levels = ['Supplementary', 'Active', 'Professional'];
|
||
|
|
const currentLevelIndex = levels.indexOf(stats.level);
|
||
|
|
const nextLevel = levels[currentLevelIndex + 1];
|
||
|
|
const progress = stats.level_progress || 0;
|
||
|
|
|
||
|
|
return (
|
||
|
|
<View style={styles.levelCard}>
|
||
|
|
<View style={styles.levelHeader}>
|
||
|
|
<Text style={styles.levelTitle}>Zoomer Level</Text>
|
||
|
|
<View style={[styles.levelBadge, { backgroundColor: getLevelColor(stats.level) }]}>
|
||
|
|
<Text style={styles.levelBadgeText}>{stats.level}</Text>
|
||
|
|
</View>
|
||
|
|
</View>
|
||
|
|
|
||
|
|
<View style={styles.levelProgressContainer}>
|
||
|
|
<View style={styles.levelProgressBar}>
|
||
|
|
<View style={[styles.levelProgressFill, { width: `${progress}%` }]} />
|
||
|
|
</View>
|
||
|
|
<Text style={styles.levelProgressText}>{progress}% to {nextLevel || 'Max'}</Text>
|
||
|
|
</View>
|
||
|
|
|
||
|
|
<View style={styles.levelRequirements}>
|
||
|
|
<Text style={styles.levelRequirementsTitle}>Requirements for {nextLevel}:</Text>
|
||
|
|
{getLevelRequirements(nextLevel).map((req, index) => (
|
||
|
|
<View key={index} style={styles.requirementRow}>
|
||
|
|
<Icon
|
||
|
|
name={req.completed ? 'check-circle' : 'radio-button-unchecked'}
|
||
|
|
size={16}
|
||
|
|
color={req.completed ? '#34C759' : '#8E8E93'}
|
||
|
|
/>
|
||
|
|
<Text style={[styles.requirementText, req.completed && styles.requirementCompleted]}>
|
||
|
|
{req.text}
|
||
|
|
</Text>
|
||
|
|
</View>
|
||
|
|
))}
|
||
|
|
</View>
|
||
|
|
</View>
|
||
|
|
);
|
||
|
|
};
|
||
|
|
|
||
|
|
const getLevelColor = (level) => {
|
||
|
|
const colors = {
|
||
|
|
'Supplementary': '#8E8E93',
|
||
|
|
'Active': '#007AFF',
|
||
|
|
'Professional': '#34C759',
|
||
|
|
};
|
||
|
|
return colors[level] || '#8E8E93';
|
||
|
|
};
|
||
|
|
|
||
|
|
const getLevelRequirements = (level) => {
|
||
|
|
const requirements = {
|
||
|
|
'Active': [
|
||
|
|
{ text: 'Complete 10 missions', completed: (stats.completed_missions || 0) >= 10 },
|
||
|
|
{ text: 'Earn $500 total', completed: (stats.total_earnings || 0) >= 500 },
|
||
|
|
{ text: '95% approval rate', completed: (stats.approval_rate || 0) >= 95 },
|
||
|
|
],
|
||
|
|
'Professional': [
|
||
|
|
{ text: 'Complete 50 missions', completed: (stats.completed_missions || 0) >= 50 },
|
||
|
|
{ text: 'Earn $5,000 total', completed: (stats.total_earnings || 0) >= 5000 },
|
||
|
|
{ text: '98% approval rate', completed: (stats.approval_rate || 0) >= 98 },
|
||
|
|
{ text: '5+ countries', completed: (stats.countries || 0) >= 5 },
|
||
|
|
],
|
||
|
|
};
|
||
|
|
return requirements[level] || [];
|
||
|
|
};
|
||
|
|
|
||
|
|
return (
|
||
|
|
<Animated.View style={[styles.container, { opacity: fadeAnim }]}>
|
||
|
|
<ScrollView style={styles.scrollView}>
|
||
|
|
{/* Header */}
|
||
|
|
<View style={styles.header}>
|
||
|
|
<Text style={styles.headerTitle}>Your Stats</Text>
|
||
|
|
<Text style={styles.headerSubtitle}>Last 30 days</Text>
|
||
|
|
</View>
|
||
|
|
|
||
|
|
{/* Earnings Card */}
|
||
|
|
<View style={styles.earningsCard}>
|
||
|
|
<Text style={styles.earningsLabel}>Total Earnings</Text>
|
||
|
|
<Text style={styles.earningsValue}>${(stats.total_earnings || 0).toFixed(2)}</Text>
|
||
|
|
<View style={styles.earningsTrend}>
|
||
|
|
<Icon name="trending-up" size={16} color="#34C759" />
|
||
|
|
<Text style={styles.earningsTrendText}>+{(stats.earnings_growth || 0).toFixed(1)}% this month</Text>
|
||
|
|
</View>
|
||
|
|
</View>
|
||
|
|
|
||
|
|
{/* Stats Grid */}
|
||
|
|
<View style={styles.statsGrid}>
|
||
|
|
{renderStatCard('camera-alt', 'Observations', stats.total_observations || 0, `${stats.approved_observations || 0} approved`, '#007AFF')}
|
||
|
|
{renderStatCard('check-circle', 'Missions', stats.completed_missions || 0, `${stats.active_missions || 0} active`, '#34C759')}
|
||
|
|
{renderStatCard('star', 'Rating', `${stats.rating || 0}/5.0`, `${stats.reviews || 0} reviews`, '#FF9500')}
|
||
|
|
{renderStatCard('schedule', 'Response Time', `${stats.avg_response_time || 0}h`, 'Average', '#5856D6')}
|
||
|
|
</View>
|
||
|
|
|
||
|
|
{/* Level Progress */}
|
||
|
|
{renderLevelProgress()}
|
||
|
|
|
||
|
|
{/* Recent Activity */}
|
||
|
|
<View style={styles.activityCard}>
|
||
|
|
<Text style={styles.activityTitle}>Recent Activity</Text>
|
||
|
|
{(stats.recent_activity || []).map((activity, index) => (
|
||
|
|
<View key={index} style={styles.activityRow}>
|
||
|
|
<View style={[styles.activityIcon, { backgroundColor: activity.color || '#007AFF20' }]}>
|
||
|
|
<Icon name={activity.icon || 'circle'} size={16} color={activity.color || '#007AFF'} />
|
||
|
|
</View>
|
||
|
|
<View style={styles.activityContent}>
|
||
|
|
<Text style={styles.activityText}>{activity.text}</Text>
|
||
|
|
<Text style={styles.activityTime}>{activity.time}</Text>
|
||
|
|
</View>
|
||
|
|
{activity.amount && (
|
||
|
|
<Text style={[styles.activityAmount, { color: activity.amount > 0 ? '#34C759' : '#FF3B30' }]}>
|
||
|
|
{activity.amount > 0 ? '+' : ''}${Math.abs(activity.amount).toFixed(2)}
|
||
|
|
</Text>
|
||
|
|
)}
|
||
|
|
</View>
|
||
|
|
))}
|
||
|
|
</View>
|
||
|
|
</ScrollView>
|
||
|
|
</Animated.View>
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
const styles = StyleSheet.create({
|
||
|
|
container: {
|
||
|
|
flex: 1,
|
||
|
|
backgroundColor: '#000',
|
||
|
|
},
|
||
|
|
scrollView: {
|
||
|
|
flex: 1,
|
||
|
|
padding: 16,
|
||
|
|
},
|
||
|
|
header: {
|
||
|
|
marginBottom: 20,
|
||
|
|
},
|
||
|
|
headerTitle: {
|
||
|
|
fontSize: 28,
|
||
|
|
fontWeight: '700',
|
||
|
|
color: '#fff',
|
||
|
|
},
|
||
|
|
headerSubtitle: {
|
||
|
|
fontSize: 14,
|
||
|
|
color: '#8E8E93',
|
||
|
|
marginTop: 4,
|
||
|
|
},
|
||
|
|
earningsCard: {
|
||
|
|
backgroundColor: '#1C1C1E',
|
||
|
|
borderRadius: 16,
|
||
|
|
padding: 20,
|
||
|
|
marginBottom: 16,
|
||
|
|
alignItems: 'center',
|
||
|
|
},
|
||
|
|
earningsLabel: {
|
||
|
|
fontSize: 14,
|
||
|
|
color: '#8E8E93',
|
||
|
|
marginBottom: 8,
|
||
|
|
},
|
||
|
|
earningsValue: {
|
||
|
|
fontSize: 48,
|
||
|
|
fontWeight: '700',
|
||
|
|
color: '#34C759',
|
||
|
|
},
|
||
|
|
earningsTrend: {
|
||
|
|
flexDirection: 'row',
|
||
|
|
alignItems: 'center',
|
||
|
|
marginTop: 8,
|
||
|
|
},
|
||
|
|
earningsTrendText: {
|
||
|
|
fontSize: 14,
|
||
|
|
color: '#34C759',
|
||
|
|
marginLeft: 4,
|
||
|
|
},
|
||
|
|
statsGrid: {
|
||
|
|
flexDirection: 'row',
|
||
|
|
flexWrap: 'wrap',
|
||
|
|
justifyContent: 'space-between',
|
||
|
|
marginBottom: 16,
|
||
|
|
},
|
||
|
|
statCard: {
|
||
|
|
width: '48%',
|
||
|
|
backgroundColor: '#1C1C1E',
|
||
|
|
borderRadius: 12,
|
||
|
|
padding: 16,
|
||
|
|
marginBottom: 12,
|
||
|
|
borderLeftWidth: 3,
|
||
|
|
flexDirection: 'row',
|
||
|
|
alignItems: 'center',
|
||
|
|
},
|
||
|
|
statIconContainer: {
|
||
|
|
width: 40,
|
||
|
|
height: 40,
|
||
|
|
borderRadius: 10,
|
||
|
|
backgroundColor: 'rgba(255,255,255,0.05)',
|
||
|
|
justifyContent: 'center',
|
||
|
|
alignItems: 'center',
|
||
|
|
marginRight: 12,
|
||
|
|
},
|
||
|
|
statContent: {
|
||
|
|
flex: 1,
|
||
|
|
},
|
||
|
|
statValue: {
|
||
|
|
fontSize: 24,
|
||
|
|
fontWeight: '700',
|
||
|
|
color: '#fff',
|
||
|
|
},
|
||
|
|
statTitle: {
|
||
|
|
fontSize: 12,
|
||
|
|
color: '#8E8E93',
|
||
|
|
marginTop: 2,
|
||
|
|
},
|
||
|
|
statSubtitle: {
|
||
|
|
fontSize: 11,
|
||
|
|
color: '#34C759',
|
||
|
|
marginTop: 2,
|
||
|
|
},
|
||
|
|
levelCard: {
|
||
|
|
backgroundColor: '#1C1C1E',
|
||
|
|
borderRadius: 16,
|
||
|
|
padding: 20,
|
||
|
|
marginBottom: 16,
|
||
|
|
},
|
||
|
|
levelHeader: {
|
||
|
|
flexDirection: 'row',
|
||
|
|
justifyContent: 'space-between',
|
||
|
|
alignItems: 'center',
|
||
|
|
marginBottom: 12,
|
||
|
|
},
|
||
|
|
levelTitle: {
|
||
|
|
fontSize: 16,
|
||
|
|
fontWeight: '600',
|
||
|
|
color: '#fff',
|
||
|
|
},
|
||
|
|
levelBadge: {
|
||
|
|
paddingHorizontal: 10,
|
||
|
|
paddingVertical: 4,
|
||
|
|
borderRadius: 8,
|
||
|
|
},
|
||
|
|
levelBadgeText: {
|
||
|
|
color: '#fff',
|
||
|
|
fontSize: 12,
|
||
|
|
fontWeight: '700',
|
||
|
|
},
|
||
|
|
levelProgressContainer: {
|
||
|
|
marginBottom: 16,
|
||
|
|
},
|
||
|
|
levelProgressBar: {
|
||
|
|
height: 8,
|
||
|
|
backgroundColor: '#2C2C2E',
|
||
|
|
borderRadius: 4,
|
||
|
|
marginBottom: 8,
|
||
|
|
},
|
||
|
|
levelProgressFill: {
|
||
|
|
height: '100%',
|
||
|
|
backgroundColor: '#34C759',
|
||
|
|
borderRadius: 4,
|
||
|
|
},
|
||
|
|
levelProgressText: {
|
||
|
|
fontSize: 12,
|
||
|
|
color: '#8E8E93',
|
||
|
|
textAlign: 'right',
|
||
|
|
},
|
||
|
|
levelRequirements: {
|
||
|
|
borderTopWidth: 1,
|
||
|
|
borderTopColor: '#2C2C2E',
|
||
|
|
paddingTop: 12,
|
||
|
|
},
|
||
|
|
levelRequirementsTitle: {
|
||
|
|
fontSize: 14,
|
||
|
|
fontWeight: '600',
|
||
|
|
color: '#fff',
|
||
|
|
marginBottom: 8,
|
||
|
|
},
|
||
|
|
requirementRow: {
|
||
|
|
flexDirection: 'row',
|
||
|
|
alignItems: 'center',
|
||
|
|
marginBottom: 8,
|
||
|
|
},
|
||
|
|
requirementText: {
|
||
|
|
fontSize: 13,
|
||
|
|
color: '#8E8E93',
|
||
|
|
marginLeft: 8,
|
||
|
|
},
|
||
|
|
requirementCompleted: {
|
||
|
|
color: '#34C759',
|
||
|
|
textDecorationLine: 'line-through',
|
||
|
|
},
|
||
|
|
activityCard: {
|
||
|
|
backgroundColor: '#1C1C1E',
|
||
|
|
borderRadius: 16,
|
||
|
|
padding: 20,
|
||
|
|
marginBottom: 16,
|
||
|
|
},
|
||
|
|
activityTitle: {
|
||
|
|
fontSize: 16,
|
||
|
|
fontWeight: '600',
|
||
|
|
color: '#fff',
|
||
|
|
marginBottom: 12,
|
||
|
|
},
|
||
|
|
activityRow: {
|
||
|
|
flexDirection: 'row',
|
||
|
|
alignItems: 'center',
|
||
|
|
paddingVertical: 10,
|
||
|
|
borderBottomWidth: 1,
|
||
|
|
borderBottomColor: '#2C2C2E',
|
||
|
|
},
|
||
|
|
activityIcon: {
|
||
|
|
width: 32,
|
||
|
|
height: 32,
|
||
|
|
borderRadius: 8,
|
||
|
|
justifyContent: 'center',
|
||
|
|
alignItems: 'center',
|
||
|
|
marginRight: 12,
|
||
|
|
},
|
||
|
|
activityContent: {
|
||
|
|
flex: 1,
|
||
|
|
},
|
||
|
|
activityText: {
|
||
|
|
fontSize: 14,
|
||
|
|
color: '#fff',
|
||
|
|
},
|
||
|
|
activityTime: {
|
||
|
|
fontSize: 12,
|
||
|
|
color: '#8E8E93',
|
||
|
|
marginTop: 2,
|
||
|
|
},
|
||
|
|
activityAmount: {
|
||
|
|
fontSize: 14,
|
||
|
|
fontWeight: '600',
|
||
|
|
},
|
||
|
|
});
|