575ca7a1fc
ci/woodpecker/push/woodpecker Pipeline was successful
- Raccourcis clavier : ←/→ (frames), Espace (play/pause), Échap (quitter animation/fermer info), H (basculer heatmap↔flux) - URL partageable : ?period=7&view=flow&city=Paris — état restauré au chargement et mis à jour sans rechargement (history.replaceState) - Sparkline : mini bar-chart SVG dans le StatsPanel montrant l'activité sur la période (données déjà en mémoire, aucune requête) - Recherche identité : champ flottant (⌕) acceptant un nom Ğ1 ou une clé g1…, résout via Subsquid + Cesium+, bascule en vue flux et met la ville en focus Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
67 lines
1.8 KiB
TypeScript
67 lines
1.8 KiB
TypeScript
import { useMemo } from 'react';
|
|
|
|
interface SparklineProps {
|
|
timestamps: number[];
|
|
periodDays: number;
|
|
}
|
|
|
|
/**
|
|
* Mini bar-chart SVG affichant l'activité journalière sur la période.
|
|
* Utilise les timestamps déjà en mémoire — aucune requête supplémentaire.
|
|
*/
|
|
export function Sparkline({ timestamps, periodDays }: SparklineProps) {
|
|
const buckets = useMemo(() => {
|
|
if (timestamps.length === 0) return [];
|
|
const n = periodDays === 1 ? 24 : Math.min(periodDays, 30);
|
|
const now = Date.now();
|
|
const start = now - periodDays * 864e5;
|
|
const step = (periodDays * 864e5) / n;
|
|
const counts = new Array(n).fill(0);
|
|
for (const ts of timestamps) {
|
|
const i = Math.floor((ts - start) / step);
|
|
if (i >= 0 && i < n) counts[i]++;
|
|
}
|
|
return counts;
|
|
}, [timestamps, periodDays]);
|
|
|
|
if (buckets.length === 0) return null;
|
|
|
|
const n = buckets.length;
|
|
const max = Math.max(...buckets, 1);
|
|
const W = 100;
|
|
const H = 32;
|
|
const barW = W / n;
|
|
const gap = barW * 0.18;
|
|
|
|
return (
|
|
<div className="space-y-1">
|
|
<svg
|
|
viewBox={`0 0 ${W} ${H}`}
|
|
preserveAspectRatio="none"
|
|
className="w-full h-8"
|
|
aria-hidden="true"
|
|
>
|
|
{buckets.map((count, i) => {
|
|
const h = Math.max(1, (count / max) * H);
|
|
return (
|
|
<rect
|
|
key={i}
|
|
x={i * barW + gap / 2}
|
|
y={H - h}
|
|
width={barW - gap}
|
|
height={h}
|
|
fill="#d4a843"
|
|
opacity={0.25 + 0.75 * (count / max)}
|
|
rx={0.5}
|
|
/>
|
|
);
|
|
})}
|
|
</svg>
|
|
<div className="flex justify-between text-[10px] text-[#4b5563]">
|
|
<span>{periodDays === 1 ? '0h' : 'J-' + periodDays}</span>
|
|
<span>{periodDays === 1 ? 'maintenant' : 'aujourd\'hui'}</span>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|