rinogeek's picture
Update for deployment
62fe6d4
/**
* 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