336 lines
9.1 KiB
TypeScript
336 lines
9.1 KiB
TypeScript
|
|
import React, { useEffect } from 'react';
|
||
|
|
import {
|
||
|
|
View,
|
||
|
|
Text,
|
||
|
|
ScrollView,
|
||
|
|
StyleSheet,
|
||
|
|
RefreshControl,
|
||
|
|
TouchableOpacity,
|
||
|
|
} from 'react-native';
|
||
|
|
import { useSelector, useDispatch } from 'react-redux';
|
||
|
|
import { Ionicons } from '@expo/vector-icons';
|
||
|
|
import { RootState } from '../store/store';
|
||
|
|
import { fetchFeatures } from '../store/slices/featuresSlice';
|
||
|
|
import { fetchAnalytics } from '../store/slices/analyticsSlice';
|
||
|
|
|
||
|
|
const DashboardScreen: React.FC = () => {
|
||
|
|
const dispatch = useDispatch();
|
||
|
|
const { features, loading: featuresLoading } = useSelector((state: RootState) => state.features);
|
||
|
|
const { realtimeData, loading: analyticsLoading } = useSelector((state: RootState) => state.analytics);
|
||
|
|
const [refreshing, setRefreshing] = React.useState(false);
|
||
|
|
|
||
|
|
useEffect(() => {
|
||
|
|
loadData();
|
||
|
|
}, []);
|
||
|
|
|
||
|
|
const loadData = () => {
|
||
|
|
dispatch(fetchFeatures());
|
||
|
|
dispatch(fetchAnalytics());
|
||
|
|
};
|
||
|
|
|
||
|
|
const onRefresh = async () => {
|
||
|
|
setRefreshing(true);
|
||
|
|
await Promise.all([
|
||
|
|
dispatch(fetchFeatures()),
|
||
|
|
dispatch(fetchAnalytics()),
|
||
|
|
]);
|
||
|
|
setRefreshing(false);
|
||
|
|
};
|
||
|
|
|
||
|
|
const getStatusColor = (status: string) => {
|
||
|
|
switch (status) {
|
||
|
|
case 'running':
|
||
|
|
return '#10B981';
|
||
|
|
case 'error':
|
||
|
|
return '#EF4444';
|
||
|
|
case 'stopped':
|
||
|
|
return '#6B7280';
|
||
|
|
default:
|
||
|
|
return '#F59E0B';
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
const getStatusIcon = (status: string) => {
|
||
|
|
switch (status) {
|
||
|
|
case 'running':
|
||
|
|
return 'checkmark-circle';
|
||
|
|
case 'error':
|
||
|
|
return 'close-circle';
|
||
|
|
case 'stopped':
|
||
|
|
return 'stop-circle';
|
||
|
|
default:
|
||
|
|
return 'help-circle';
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
return (
|
||
|
|
<ScrollView
|
||
|
|
style={styles.container}
|
||
|
|
refreshControl={
|
||
|
|
<RefreshControl refreshing={refreshing} onRefresh={onRefresh} />
|
||
|
|
}
|
||
|
|
>
|
||
|
|
{/* Header */}
|
||
|
|
<View style={styles.header}>
|
||
|
|
<Text style={styles.title}>Tableau de bord</Text>
|
||
|
|
<Text style={styles.subtitle}>Vue d'ensemble de la plateforme Veza</Text>
|
||
|
|
</View>
|
||
|
|
|
||
|
|
{/* Quick Stats */}
|
||
|
|
<View style={styles.statsContainer}>
|
||
|
|
<View style={styles.statCard}>
|
||
|
|
<Ionicons name="apps" size={24} color="#3B82F6" />
|
||
|
|
<Text style={styles.statNumber}>{features.length}</Text>
|
||
|
|
<Text style={styles.statLabel}>Features Actives</Text>
|
||
|
|
</View>
|
||
|
|
|
||
|
|
<View style={styles.statCard}>
|
||
|
|
<Ionicons name="analytics" size={24} color="#10B981" />
|
||
|
|
<Text style={styles.statNumber}>{realtimeData.length}</Text>
|
||
|
|
<Text style={styles.statLabel}>Métriques Temps Réel</Text>
|
||
|
|
</View>
|
||
|
|
|
||
|
|
<View style={styles.statCard}>
|
||
|
|
<Ionicons name="musical-notes" size={24} color="#F59E0B" />
|
||
|
|
<Text style={styles.statNumber}>12</Text>
|
||
|
|
<Text style={styles.statLabel}>Médias</Text>
|
||
|
|
</View>
|
||
|
|
|
||
|
|
<View style={styles.statCard}>
|
||
|
|
<Ionicons name="chatbubbles" size={24} color="#8B5CF6" />
|
||
|
|
<Text style={styles.statNumber}>5</Text>
|
||
|
|
<Text style={styles.statLabel}>Chats Actifs</Text>
|
||
|
|
</View>
|
||
|
|
</View>
|
||
|
|
|
||
|
|
{/* Feature Status */}
|
||
|
|
<View style={styles.section}>
|
||
|
|
<Text style={styles.sectionTitle}>Statut des Features</Text>
|
||
|
|
{features.slice(0, 5).map((feature) => (
|
||
|
|
<TouchableOpacity key={feature.id} style={styles.featureCard}>
|
||
|
|
<View style={styles.featureHeader}>
|
||
|
|
<View style={styles.featureInfo}>
|
||
|
|
<Text style={styles.featureName}>{feature.name}</Text>
|
||
|
|
<Text style={styles.featureDomain}>{feature.domain}</Text>
|
||
|
|
</View>
|
||
|
|
<View style={styles.statusContainer}>
|
||
|
|
<Ionicons
|
||
|
|
name={getStatusIcon(feature.status) as any}
|
||
|
|
size={20}
|
||
|
|
color={getStatusColor(feature.status)}
|
||
|
|
/>
|
||
|
|
<Text style={[styles.statusText, { color: getStatusColor(feature.status) }]}>
|
||
|
|
{feature.status}
|
||
|
|
</Text>
|
||
|
|
</View>
|
||
|
|
</View>
|
||
|
|
|
||
|
|
<View style={styles.metricsRow}>
|
||
|
|
<View style={styles.metric}>
|
||
|
|
<Text style={styles.metricLabel}>CPU</Text>
|
||
|
|
<Text style={styles.metricValue}>{feature.metrics.cpu}%</Text>
|
||
|
|
</View>
|
||
|
|
<View style={styles.metric}>
|
||
|
|
<Text style={styles.metricLabel}>RAM</Text>
|
||
|
|
<Text style={styles.metricValue}>{(feature.metrics.memory / 1024 / 1024).toFixed(0)}MB</Text>
|
||
|
|
</View>
|
||
|
|
<View style={styles.metric}>
|
||
|
|
<Text style={styles.metricLabel}>DISK</Text>
|
||
|
|
<Text style={styles.metricValue}>{(feature.metrics.disk / 1024 / 1024).toFixed(0)}MB</Text>
|
||
|
|
</View>
|
||
|
|
</View>
|
||
|
|
</TouchableOpacity>
|
||
|
|
))}
|
||
|
|
</View>
|
||
|
|
|
||
|
|
{/* Recent Activity */}
|
||
|
|
<View style={styles.section}>
|
||
|
|
<Text style={styles.sectionTitle}>Activité Récente</Text>
|
||
|
|
<View style={styles.activityCard}>
|
||
|
|
<View style={styles.activityItem}>
|
||
|
|
<Ionicons name="checkmark-circle" size={16} color="#10B981" />
|
||
|
|
<Text style={styles.activityText}>Feature "Smart Recommendations" démarrée</Text>
|
||
|
|
<Text style={styles.activityTime}>Il y a 2 min</Text>
|
||
|
|
</View>
|
||
|
|
|
||
|
|
<View style={styles.activityItem}>
|
||
|
|
<Ionicons name="musical-notes" size={16} color="#3B82F6" />
|
||
|
|
<Text style={styles.activityText}>Nouveau média uploadé</Text>
|
||
|
|
<Text style={styles.activityTime}>Il y a 5 min</Text>
|
||
|
|
</View>
|
||
|
|
|
||
|
|
<View style={styles.activityItem}>
|
||
|
|
<Ionicons name="analytics" size={16} color="#F59E0B" />
|
||
|
|
<Text style={styles.activityText}>Rapport analytics généré</Text>
|
||
|
|
<Text style={styles.activityTime}>Il y a 10 min</Text>
|
||
|
|
</View>
|
||
|
|
</View>
|
||
|
|
</View>
|
||
|
|
|
||
|
|
{/* Quick Actions */}
|
||
|
|
<View style={styles.section}>
|
||
|
|
<Text style={styles.sectionTitle}>Actions Rapides</Text>
|
||
|
|
<View style={styles.actionsContainer}>
|
||
|
|
<TouchableOpacity style={styles.actionButton}>
|
||
|
|
<Ionicons name="add" size={24} color="#FFFFFF" />
|
||
|
|
<Text style={styles.actionText}>Nouveau Feature</Text>
|
||
|
|
</TouchableOpacity>
|
||
|
|
|
||
|
|
<TouchableOpacity style={styles.actionButton}>
|
||
|
|
<Ionicons name="refresh" size={24} color="#FFFFFF" />
|
||
|
|
<Text style={styles.actionText}>Actualiser</Text>
|
||
|
|
</TouchableOpacity>
|
||
|
|
</View>
|
||
|
|
</View>
|
||
|
|
</ScrollView>
|
||
|
|
);
|
||
|
|
};
|
||
|
|
|
||
|
|
const styles = StyleSheet.create({
|
||
|
|
container: {
|
||
|
|
flex: 1,
|
||
|
|
backgroundColor: '#111827',
|
||
|
|
},
|
||
|
|
header: {
|
||
|
|
padding: 20,
|
||
|
|
paddingTop: 40,
|
||
|
|
},
|
||
|
|
title: {
|
||
|
|
fontSize: 28,
|
||
|
|
fontWeight: 'bold',
|
||
|
|
color: '#FFFFFF',
|
||
|
|
marginBottom: 8,
|
||
|
|
},
|
||
|
|
subtitle: {
|
||
|
|
fontSize: 16,
|
||
|
|
color: '#9CA3AF',
|
||
|
|
},
|
||
|
|
statsContainer: {
|
||
|
|
flexDirection: 'row',
|
||
|
|
flexWrap: 'wrap',
|
||
|
|
paddingHorizontal: 20,
|
||
|
|
marginBottom: 20,
|
||
|
|
},
|
||
|
|
statCard: {
|
||
|
|
backgroundColor: '#1F2937',
|
||
|
|
borderRadius: 12,
|
||
|
|
padding: 16,
|
||
|
|
marginBottom: 12,
|
||
|
|
width: '48%',
|
||
|
|
marginRight: '2%',
|
||
|
|
alignItems: 'center',
|
||
|
|
},
|
||
|
|
statNumber: {
|
||
|
|
fontSize: 24,
|
||
|
|
fontWeight: 'bold',
|
||
|
|
color: '#FFFFFF',
|
||
|
|
marginTop: 8,
|
||
|
|
},
|
||
|
|
statLabel: {
|
||
|
|
fontSize: 12,
|
||
|
|
color: '#9CA3AF',
|
||
|
|
marginTop: 4,
|
||
|
|
textAlign: 'center',
|
||
|
|
},
|
||
|
|
section: {
|
||
|
|
paddingHorizontal: 20,
|
||
|
|
marginBottom: 20,
|
||
|
|
},
|
||
|
|
sectionTitle: {
|
||
|
|
fontSize: 18,
|
||
|
|
fontWeight: 'bold',
|
||
|
|
color: '#FFFFFF',
|
||
|
|
marginBottom: 12,
|
||
|
|
},
|
||
|
|
featureCard: {
|
||
|
|
backgroundColor: '#1F2937',
|
||
|
|
borderRadius: 12,
|
||
|
|
padding: 16,
|
||
|
|
marginBottom: 12,
|
||
|
|
},
|
||
|
|
featureHeader: {
|
||
|
|
flexDirection: 'row',
|
||
|
|
justifyContent: 'space-between',
|
||
|
|
alignItems: 'center',
|
||
|
|
marginBottom: 12,
|
||
|
|
},
|
||
|
|
featureInfo: {
|
||
|
|
flex: 1,
|
||
|
|
},
|
||
|
|
featureName: {
|
||
|
|
fontSize: 16,
|
||
|
|
fontWeight: '600',
|
||
|
|
color: '#FFFFFF',
|
||
|
|
},
|
||
|
|
featureDomain: {
|
||
|
|
fontSize: 12,
|
||
|
|
color: '#9CA3AF',
|
||
|
|
marginTop: 2,
|
||
|
|
},
|
||
|
|
statusContainer: {
|
||
|
|
flexDirection: 'row',
|
||
|
|
alignItems: 'center',
|
||
|
|
},
|
||
|
|
statusText: {
|
||
|
|
fontSize: 12,
|
||
|
|
fontWeight: '500',
|
||
|
|
marginLeft: 4,
|
||
|
|
},
|
||
|
|
metricsRow: {
|
||
|
|
flexDirection: 'row',
|
||
|
|
justifyContent: 'space-between',
|
||
|
|
},
|
||
|
|
metric: {
|
||
|
|
alignItems: 'center',
|
||
|
|
},
|
||
|
|
metricLabel: {
|
||
|
|
fontSize: 10,
|
||
|
|
color: '#9CA3AF',
|
||
|
|
},
|
||
|
|
metricValue: {
|
||
|
|
fontSize: 12,
|
||
|
|
fontWeight: '600',
|
||
|
|
color: '#FFFFFF',
|
||
|
|
marginTop: 2,
|
||
|
|
},
|
||
|
|
activityCard: {
|
||
|
|
backgroundColor: '#1F2937',
|
||
|
|
borderRadius: 12,
|
||
|
|
padding: 16,
|
||
|
|
},
|
||
|
|
activityItem: {
|
||
|
|
flexDirection: 'row',
|
||
|
|
alignItems: 'center',
|
||
|
|
marginBottom: 12,
|
||
|
|
},
|
||
|
|
activityText: {
|
||
|
|
flex: 1,
|
||
|
|
fontSize: 14,
|
||
|
|
color: '#FFFFFF',
|
||
|
|
marginLeft: 8,
|
||
|
|
},
|
||
|
|
activityTime: {
|
||
|
|
fontSize: 12,
|
||
|
|
color: '#9CA3AF',
|
||
|
|
},
|
||
|
|
actionsContainer: {
|
||
|
|
flexDirection: 'row',
|
||
|
|
justifyContent: 'space-between',
|
||
|
|
},
|
||
|
|
actionButton: {
|
||
|
|
backgroundColor: '#3B82F6',
|
||
|
|
borderRadius: 12,
|
||
|
|
padding: 16,
|
||
|
|
flexDirection: 'row',
|
||
|
|
alignItems: 'center',
|
||
|
|
flex: 1,
|
||
|
|
marginRight: 8,
|
||
|
|
},
|
||
|
|
actionText: {
|
||
|
|
color: '#FFFFFF',
|
||
|
|
fontWeight: '600',
|
||
|
|
marginLeft: 8,
|
||
|
|
},
|
||
|
|
});
|
||
|
|
|
||
|
|
export default DashboardScreen;
|