Spaces:
Sleeping
Sleeping
| /** | |
| * Composant Analytics pour AfriDataHub | |
| * Created by BlackBenAI Team - AfriDataHub Platform | |
| */ | |
| import { useState, useEffect } from 'react' | |
| import { motion, AnimatePresence } from 'framer-motion' | |
| import { Button } from '@/components/ui/button' | |
| import AfricaMap from './AfricaMap' | |
| import { | |
| CountryBarChart, | |
| TrendLineChart, | |
| DomainPieChart, | |
| ComparisonBarChart, | |
| MetricsChart | |
| } from './Charts' | |
| import { | |
| BarChart3, | |
| TrendingUp, | |
| Globe, | |
| Filter, | |
| Download, | |
| RefreshCw | |
| } from 'lucide-react' | |
| import { API_URL } from '../config' | |
| const Analytics = () => { | |
| const [loading, setLoading] = useState(true) | |
| const [selectedDataset, setSelectedDataset] = useState('') | |
| const [selectedCountry, setSelectedCountry] = useState('') | |
| const [datasets, setDatasets] = useState([]) | |
| const [analyticsData, setAnalyticsData] = useState(null) | |
| useEffect(() => { | |
| fetchDatasets() | |
| fetchAnalyticsData() | |
| }, []) | |
| useEffect(() => { | |
| if (selectedDataset) { | |
| fetchAnalyticsData(selectedDataset) | |
| } | |
| }, [selectedDataset]) | |
| const fetchDatasets = async () => { | |
| try { | |
| const response = await fetch(`${API_URL}datasets/`) | |
| const data = await response.json() | |
| setDatasets(data.results || []) | |
| } catch (error) { | |
| console.error('Erreur lors du chargement des datasets:', error) | |
| } | |
| } | |
| const fetchAnalyticsData = async (datasetRef = null) => { | |
| try { | |
| setLoading(true) | |
| let url = `${API_URL}analytics/comprehensive/` | |
| if (datasetRef) { | |
| url += `?dataset_id=${datasetRef}` | |
| } | |
| const response = await fetch(url) | |
| const data = await response.json() | |
| setAnalyticsData(data) | |
| } catch (error) { | |
| console.error('Erreur lors du chargement des données d\'analyse:', error) | |
| } finally { | |
| setLoading(false) | |
| } | |
| } | |
| const handleCountryClick = (countryCode, countryInfo) => { | |
| setSelectedCountry(countryCode) | |
| } | |
| const handleExportData = () => { | |
| // Logique d'export des données | |
| console.log('Export des données d\'analyse') | |
| } | |
| if (loading) { | |
| return ( | |
| <div className="flex items-center justify-center h-64"> | |
| <div className="animate-spin rounded-full h-12 w-12 border-b-2 border-green-700"></div> | |
| </div> | |
| ) | |
| } | |
| return ( | |
| <div className="space-y-10 pb-20"> | |
| {/* En-tête */} | |
| <motion.div | |
| initial={{ opacity: 0, y: -20 }} | |
| animate={{ opacity: 1, y: 0 }} | |
| className="flex flex-col md:flex-row md:items-end md:justify-between gap-6" | |
| > | |
| <div> | |
| <h1 className="text-3xl md:text-4xl font-black text-foreground tracking-tight">Intelligence <span className="text-[#fcd34d]">Analytique</span></h1> | |
| <p className="text-muted-foreground font-medium mt-2 text-lg"> | |
| Visualisations avancées et corrélations stratégiques en temps réel. | |
| </p> | |
| </div> | |
| <div className="flex items-center space-x-3"> | |
| <Button | |
| variant="ghost" | |
| onClick={fetchAnalyticsData} | |
| disabled={loading} | |
| className="rounded-2xl hover:bg-primary/10 text-primary font-bold" | |
| > | |
| <RefreshCw className={`h-4 w-4 mr-2 ${loading ? 'animate-spin' : ''}`} /> | |
| Actualiser | |
| </Button> | |
| <Button | |
| onClick={handleExportData} | |
| className="rounded-2xl bg-[#166534] hover:bg-[#15803d] text-white font-bold shadow-lg shadow-primary/20 transition-all hover:scale-105" | |
| > | |
| <Download className="h-4 w-4 mr-2" /> | |
| Exporter le rapport | |
| </Button> | |
| </div> | |
| </motion.div> | |
| {/* Filtres Glassmorphism */} | |
| <motion.div | |
| initial={{ opacity: 0, y: 20 }} | |
| animate={{ opacity: 1, y: 0 }} | |
| className="glass rounded-[1.5rem] md:rounded-[2rem] p-4 md:p-6 shadow-xl" | |
| > | |
| <div className="flex flex-col md:flex-row gap-6"> | |
| <div className="flex-1"> | |
| <div className="relative group"> | |
| <Filter className="absolute left-4 top-1/2 transform -translate-y-1/2 h-5 w-5 text-muted-foreground group-focus-within:text-primary transition-colors" /> | |
| <select | |
| value={selectedDataset} | |
| onChange={(e) => setSelectedDataset(e.target.value)} | |
| className="w-full pl-12 pr-10 py-4 bg-background/50 dark:bg-black/20 border border-border rounded-2xl focus:ring-4 focus:ring-primary/10 focus:border-primary/30 transition-all outline-none font-bold text-foreground appearance-none cursor-pointer" | |
| > | |
| <option value="">Tous les datasets (Vue globale)</option> | |
| {datasets.map(dataset => ( | |
| <option key={dataset.slug} value={dataset.slug}> | |
| {dataset.title} | |
| </option> | |
| ))} | |
| </select> | |
| </div> | |
| </div> | |
| <div className="md:w-64"> | |
| <select | |
| className="w-full px-6 py-4 bg-background/50 dark:bg-black/20 border border-border rounded-2xl focus:ring-4 focus:ring-primary/10 focus:border-primary/30 transition-all outline-none font-bold text-foreground appearance-none cursor-pointer" | |
| > | |
| <option value="">Période: Historique Complet</option> | |
| <option value="2024">Année 2024</option> | |
| <option value="2023">Année 2023</option> | |
| <option value="last5">5 dernières années</option> | |
| </select> | |
| </div> | |
| </div> | |
| </motion.div> | |
| {/* Carte interactive et métriques */} | |
| <div className="grid grid-cols-1 lg:grid-cols-3 gap-10"> | |
| <motion.div | |
| initial={{ opacity: 0, scale: 0.95 }} | |
| animate={{ opacity: 1, scale: 1 }} | |
| className="lg:col-span-2 glass rounded-[2rem] md:rounded-[3rem] p-6 md:p-10 relative overflow-hidden" | |
| > | |
| <div className="absolute top-0 right-0 p-8 opacity-5 pointer-events-none"> | |
| <Globe className="w-48 h-48 text-primary" /> | |
| </div> | |
| <div className="relative z-10"> | |
| <div className="mb-8"> | |
| <h3 className="text-2xl font-black text-foreground mb-1">Distribution Géographique</h3> | |
| <p className="text-muted-foreground font-medium">Cliquez sur un pays pour isoler ses performances.</p> | |
| </div> | |
| <AfricaMap | |
| data={analyticsData?.mapData || {}} | |
| onCountryClick={handleCountryClick} | |
| selectedCountry={selectedCountry} | |
| /> | |
| </div> | |
| </motion.div> | |
| <motion.div | |
| initial={{ opacity: 0, x: 20 }} | |
| animate={{ opacity: 1, x: 0 }} | |
| className="glass rounded-[2rem] md:rounded-[3rem] p-6 md:p-10" | |
| > | |
| <MetricsChart | |
| data={analyticsData?.metricsData || []} | |
| title="Indicateurs de Performance" | |
| metric="value" | |
| /> | |
| </motion.div> | |
| </div> | |
| {/* Graphiques de données */} | |
| <div className="grid grid-cols-1 lg:grid-cols-2 gap-10"> | |
| <motion.div | |
| initial={{ opacity: 0, y: 40 }} | |
| animate={{ opacity: 1, y: 0 }} | |
| className="glass rounded-[2rem] md:rounded-[3rem] p-6 md:p-10" | |
| > | |
| <CountryBarChart | |
| data={analyticsData?.countryData || []} | |
| title="Top 10 des Leaders par Valeur" | |
| dataKey="value" | |
| /> | |
| </motion.div> | |
| <motion.div | |
| initial={{ opacity: 0, y: 40 }} | |
| animate={{ opacity: 1, y: 0 }} | |
| className="glass rounded-[2rem] md:rounded-[3rem] p-6 md:p-10" | |
| > | |
| <DomainPieChart | |
| data={analyticsData?.domainData || []} | |
| title="Structure du Portefeuille de Données" | |
| /> | |
| </motion.div> | |
| </div> | |
| {/* Tendances et comparaisons */} | |
| <div className="grid grid-cols-1 lg:grid-cols-2 gap-10"> | |
| <motion.div | |
| initial={{ opacity: 0, y: 40 }} | |
| animate={{ opacity: 1, y: 0 }} | |
| className="glass rounded-[2rem] md:rounded-[3rem] p-6 md:p-10" | |
| > | |
| <TrendLineChart | |
| data={analyticsData?.trendData || []} | |
| title="Trajectoire Temporelle (5 ans)" | |
| lines={[ | |
| { dataKey: 'agriculture', name: 'Agriculture' }, | |
| { dataKey: 'health', name: 'Santé' }, | |
| { dataKey: 'economy', name: 'Économie' } | |
| ]} | |
| /> | |
| </motion.div> | |
| <motion.div | |
| initial={{ opacity: 0, y: 40 }} | |
| animate={{ opacity: 1, y: 0 }} | |
| className="glass rounded-[2rem] md:rounded-[3rem] p-6 md:p-10" | |
| > | |
| <ComparisonBarChart | |
| data={analyticsData?.comparisonData || []} | |
| title="Analyse Comparative Régionale" | |
| bars={[ | |
| { dataKey: 'agriculture', name: 'Agriculture' }, | |
| { dataKey: 'health', name: 'Santé' }, | |
| { dataKey: 'economy', name: 'Économie' } | |
| ]} | |
| /> | |
| </motion.div> | |
| </div> | |
| {/* Insights automatiques */} | |
| <motion.div | |
| initial={{ opacity: 0, y: 40 }} | |
| animate={{ opacity: 1, y: 0 }} | |
| className="relative overflow-hidden rounded-[2rem] md:rounded-[3rem] p-6 md:p-12 text-white shadow-2xl" | |
| > | |
| <div className="absolute inset-0 bg-[#166534] opacity-95"></div> | |
| <div className="absolute inset-0 bg-mesh opacity-20"></div> | |
| <div className="relative z-10"> | |
| <div className="flex items-center justify-between mb-10"> | |
| <h3 className="text-xl sm:text-3xl font-black flex items-center"> | |
| <TrendingUp className="h-8 w-8 mr-4 text-yellow-300" /> | |
| Insights Stratégiques IA | |
| </h3> | |
| <div className="px-4 py-2 bg-white/20 backdrop-blur-md rounded-full text-xs font-bold uppercase tracking-widest border border-white/30"> | |
| Généré par Gemini 2.5 | |
| </div> | |
| </div> | |
| <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6"> | |
| {analyticsData?.global_trends && Object.entries(analyticsData.global_trends).length > 0 ? ( | |
| Object.entries(analyticsData.global_trends).map(([title, trend]) => ( | |
| <motion.div | |
| key={title} | |
| whileHover={{ y: -5 }} | |
| className="bg-white/10 backdrop-blur-lg rounded-3xl p-6 border border-white/20 group cursor-pointer" | |
| > | |
| <h4 className="font-black text-yellow-300 mb-3 text-lg group-hover:scale-105 transition-transform origin-left">{title}</h4> | |
| <p className="text-white/90 font-medium leading-relaxed"> | |
| {trend.trends_count} tendances analysées avec une variation moyenne de <span className="text-white font-black">{trend.avg_change.toFixed(1)}%</span> | |
| </p> | |
| </motion.div> | |
| )) | |
| ) : ( | |
| <div className="col-span-full py-12 text-center"> | |
| <div className="w-16 h-16 bg-white/10 rounded-full flex items-center justify-center mx-auto mb-4 animate-pulse"> | |
| <RefreshCw className="h-8 w-8 text-white animate-spin" /> | |
| </div> | |
| <p className="text-xl font-bold text-white/70">Analyse du flux de données en cours...</p> | |
| </div> | |
| )} | |
| </div> | |
| </div> | |
| </motion.div> | |
| </div> | |
| ) | |
| } | |
| export default Analytics | |