diff --git a/src/App.tsx b/src/App.tsx index f894e26..ac163dc 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -18,8 +18,9 @@ export default function App() { const [lastUpdate, setLastUpdate] = useState(null); const [source, setSource] = useState<'live' | 'mock'>('mock'); const [currentUD, setCurrentUD] = useState(11.78); + const [allTimestamps, setAllTimestamps] = useState([]); - const animation = useAnimation(transactions, periodDays); + const animation = useAnimation(transactions, periodDays, allTimestamps); const handlePeriodChange = (days: number) => { animation.deactivate(); @@ -33,12 +34,13 @@ export default function App() { if (showLoading) setLoading(true); else setRefreshing(true); fetchData(periodDays) - .then(({ transactions, stats, source, currentUD }) => { + .then(({ transactions, stats, source, currentUD, allTimestamps }) => { if (!cancelled) { setTransactions(transactions); setStats(stats); setSource(source); setCurrentUD(currentUD); + setAllTimestamps(allTimestamps); setLastUpdate(new Date()); } }) @@ -59,6 +61,8 @@ export default function App() { ? { ...computeStats(animation.visibleTransactions), geoCount: animation.visibleTransactions.length, + // frameTotalCount = total réel (géo + non-géo) dans cette frame + transactionCount: animation.frameTotalCount ?? animation.visibleTransactions.length, } : stats; @@ -72,7 +76,6 @@ export default function App() { source={source} currentUD={currentUD} animationLabel={animation.active ? (animation.currentFrame?.label ?? undefined) : undefined} - globalGeoStats={animation.active && stats ? { geoCount: stats.geoCount, transactionCount: stats.transactionCount } : undefined} /> {/* Map area */} diff --git a/src/components/StatsPanel.tsx b/src/components/StatsPanel.tsx index 05208a4..1434961 100644 --- a/src/components/StatsPanel.tsx +++ b/src/components/StatsPanel.tsx @@ -8,8 +8,6 @@ interface StatsPanelProps { source: 'live' | 'mock'; currentUD: number; animationLabel?: string; - // Stats de géoloc de la période complète (indépendantes de la frame courante) - globalGeoStats?: { geoCount: number; transactionCount: number }; } const MEDALS = ['🥇', '🥈', '🥉']; @@ -35,7 +33,7 @@ function formatDU(g1: number, ud: number): string { return `≈ ${Math.round(du).toLocaleString('fr-FR')} DU`; } -export function StatsPanel({ stats, loading, periodDays, source, currentUD, animationLabel, globalGeoStats }: StatsPanelProps) { +export function StatsPanel({ stats, loading, periodDays, source, currentUD, animationLabel }: StatsPanelProps) { const periodLabel = periodDays === 1 ? '24 dernières heures' : `${periodDays} derniers jours`; const prevStats = useRef(null); @@ -108,20 +106,14 @@ export function StatsPanel({ stats, loading, periodDays, source, currentUD, anim })()} delta={prevTxCount !== null ? (stats.transactionCount > prevTxCount ? 'up' : stats.transactionCount < prevTxCount ? 'down' : null) : null} /> - {/* Couverture géo — toujours basée sur la période complète (pas la frame) */} - {source === 'live' && (() => { - const geo = globalGeoStats ?? { geoCount: stats.geoCount, transactionCount: stats.transactionCount }; - const pct = geo.transactionCount > 0 - ? Math.round((geo.geoCount / geo.transactionCount) * 100) - : null; - if (pct === null) return null; + {/* Couverture géo — transactionCount inclut le total réel de la frame */} + {source === 'live' && stats.transactionCount > 0 && (() => { + const pct = Math.round((stats.geoCount / stats.transactionCount) * 100); return (
-

- Géolocalisées{animationLabel ? (période) : ''} -

-

{geo.geoCount} / {geo.transactionCount}

+

Géolocalisées

+

{stats.geoCount} / {stats.transactionCount}

t.timestamp >= frame.from && t.timestamp < frame.to); }, [active, transactions, frames, currentIndex]); + // Nombre total de transfers (géo + non-géo) dans la frame courante + const frameTotalCount = useMemo(() => { + if (!active || frames.length === 0 || allTimestamps.length === 0) return null; + const frame = frames[currentIndex]; + if (!frame) return null; + return allTimestamps.filter((ts) => ts >= frame.from && ts < frame.to).length; + }, [active, allTimestamps, frames, currentIndex]); + return { active, activate: () => { setActive(true); setSpeed(1); setPlaying(true); }, @@ -109,5 +117,6 @@ export function useAnimation(transactions: Transaction[], periodDays: number) { frames, currentFrame: frames[currentIndex] ?? null, visibleTransactions, + frameTotalCount, }; } diff --git a/src/services/DataService.ts b/src/services/DataService.ts index 3c14530..1ccc547 100644 --- a/src/services/DataService.ts +++ b/src/services/DataService.ts @@ -46,11 +46,12 @@ async function fetchLiveTransactions(periodDays: number): Promise<{ geolocated: Transaction[]; totalCount: number; totalVolume: number; + allTimestamps: number[]; }> { // ~400 tx/jour sur le réseau Ğ1v2 → marge ×1.5 arrondie, minimum 2000 const limit = Math.max(2000, Math.ceil(periodDays * 600)); const { transfers: rawTransfers, totalCount } = await fetchTransfers(periodDays, limit); - if (rawTransfers.length === 0) return { geolocated: [], totalCount: 0, totalVolume: 0 }; + if (rawTransfers.length === 0) return { geolocated: [], totalCount: 0, totalVolume: 0, allTimestamps: [] }; const totalVolume = rawTransfers.reduce((s, t) => s + t.amount, 0); @@ -99,7 +100,7 @@ async function fetchLiveTransactions(periodDays: number): Promise<{ }); } - return { geolocated, totalCount, totalVolume }; + return { geolocated, totalCount, totalVolume, allTimestamps: rawTransfers.map((t) => t.timestamp) }; } // --------------------------------------------------------------------------- @@ -113,10 +114,11 @@ export interface PeriodStats { } export interface DataResult { - transactions: Transaction[]; // uniquement géolocalisées → heatmap - stats: PeriodStats; - source: 'live' | 'mock'; - currentUD: number; // valeur du DU courant en Ğ1 + transactions: Transaction[]; // uniquement géolocalisées → heatmap + stats: PeriodStats; + source: 'live' | 'mock'; + currentUD: number; // valeur du DU courant en Ğ1 + allTimestamps: number[]; // timestamps de TOUS les transfers (géo + non-géo) } export async function fetchData(periodDays: number): Promise { @@ -129,10 +131,11 @@ export async function fetchData(periodDays: number): Promise { stats: { ...base, geoCount: transactions.length }, source: 'mock', currentUD: 11.78, + allTimestamps: transactions.map((t) => t.timestamp), }; } - const [{ geolocated, totalCount, totalVolume }, currentUD] = await Promise.all([ + const [{ geolocated, totalCount, totalVolume, allTimestamps }, currentUD] = await Promise.all([ fetchLiveTransactions(periodDays), getCurrentUD(), ]); @@ -148,5 +151,6 @@ export async function fetchData(periodDays: number): Promise { }, source: 'live', currentUD, + allTimestamps, }; }