feat: initialisation de ĞéoFlux — visualisation géographique Ğ1
- Carte Leaflet plein écran avec heatmap (OpenStreetMap, dark mode) - Sélecteur de période 24h / 7j / 30j - Panneau latéral : volume total, compteur de transactions, top 3 villes - mockData.ts : 2 400 transactions simulées sur 24 villes FR/EU - DataService.ts : abstraction prête pour branchement Subsquid/Ğ1v2 - Schémas Zod (g1.schema.ts) : validation runtime Duniter GVA + Cesium+ - Adaptateurs DuniterAdapter et CesiumAdapter (Ğ1v1, à migrer v2) - Suite de tests Vitest : 43 tests, conformité schéma Ğ1 vérifiée Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
63
src/App.tsx
Normal file
63
src/App.tsx
Normal file
@@ -0,0 +1,63 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { StatsPanel } from './components/StatsPanel';
|
||||
import { PeriodSelector } from './components/PeriodSelector';
|
||||
import { HeatMap } from './components/HeatMap';
|
||||
import { fetchData } from './services/DataService';
|
||||
import type { PeriodStats } from './services/DataService';
|
||||
import type { Transaction } from './data/mockData';
|
||||
|
||||
export default function App() {
|
||||
const [periodDays, setPeriodDays] = useState(7);
|
||||
const [transactions, setTransactions] = useState<Transaction[]>([]);
|
||||
const [stats, setStats] = useState<PeriodStats | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
let cancelled = false;
|
||||
setLoading(true);
|
||||
|
||||
fetchData(periodDays).then(({ transactions, stats }) => {
|
||||
if (!cancelled) {
|
||||
setTransactions(transactions);
|
||||
setStats(stats);
|
||||
setLoading(false);
|
||||
}
|
||||
});
|
||||
|
||||
return () => { cancelled = true; };
|
||||
}, [periodDays]);
|
||||
|
||||
return (
|
||||
<div className="flex h-svh w-full overflow-hidden bg-[#0a0b0f] text-white">
|
||||
{/* Side panel */}
|
||||
<StatsPanel stats={stats} loading={loading} periodDays={periodDays} />
|
||||
|
||||
{/* Map area */}
|
||||
<div className="relative flex-1 min-w-0">
|
||||
<HeatMap transactions={transactions} />
|
||||
|
||||
{/* Period selector — floating over map */}
|
||||
<div className="absolute top-4 left-1/2 -translate-x-1/2 z-[1000]">
|
||||
<PeriodSelector value={periodDays} onChange={setPeriodDays} />
|
||||
</div>
|
||||
|
||||
{/* Transaction count badge */}
|
||||
{!loading && (
|
||||
<div className="absolute bottom-8 left-1/2 -translate-x-1/2 z-[1000] bg-[#0a0b0f]/80 backdrop-blur-sm border border-[#2e2f3a] rounded-full px-4 py-1.5 text-xs text-[#6b7280]">
|
||||
<span className="text-[#d4a843] font-medium">{transactions.length}</span> transactions affichées
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Loading overlay */}
|
||||
{loading && (
|
||||
<div className="absolute inset-0 z-[999] flex items-center justify-center bg-[#0a0b0f]/60 backdrop-blur-sm">
|
||||
<div className="flex flex-col items-center gap-3">
|
||||
<div className="w-10 h-10 rounded-full border-2 border-[#d4a843] border-t-transparent animate-spin" />
|
||||
<p className="text-[#d4a843] text-sm">Chargement des flux…</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user