feat: vue dividende universel — overlay membres actifs géolocalisés
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/push/woodpecker Pipeline was successful
Bouton DU (gauche carte) : affiche en overlay des cercles verts proportionnels au nombre de membres WoT actifs géolocalisés par ville. Chargement à la demande, mis en cache 1h. Pipeline : SubsquidAdapter.fetchActiveMemberKeys() → isMember:true (~7000) CesiumAdapter.resolveGeoByKeysBatched() → lots de 500 clés DataService.fetchMemberCities() → agrégation + cache 1h HeatMap → CircleMarkers Leaflet en overlay Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+39
-3
@@ -5,8 +5,8 @@ import { HeatMap } from './components/HeatMap';
|
||||
import { FlowMap } from './components/FlowMap';
|
||||
import { AnimationPlayer } from './components/AnimationPlayer';
|
||||
import { SearchBar } from './components/SearchBar';
|
||||
import { fetchData } from './services/DataService';
|
||||
import type { PeriodStats } from './services/DataService';
|
||||
import { fetchData, fetchMemberCities } from './services/DataService';
|
||||
import type { PeriodStats, MemberCity } from './services/DataService';
|
||||
import type { Transaction } from './data/mockData';
|
||||
import type { TransactionArc } from './data/arcData';
|
||||
import { computeStats } from './data/mockData';
|
||||
@@ -31,8 +31,26 @@ export default function App() {
|
||||
const [focusCity, setFocusCity] = useState<string | null>(initialUrlState.city);
|
||||
const [panelOpen, setPanelOpen] = useState(false);
|
||||
const [infoOpen, setInfoOpen] = useState(false);
|
||||
const [showMembers, setShowMembers] = useState(false);
|
||||
const [memberCities, setMemberCities] = useState<MemberCity[]>([]);
|
||||
const [membersLoading, setMembersLoading] = useState(false);
|
||||
const isMobile = useMediaQuery('(max-width: 639px)');
|
||||
|
||||
const toggleMembers = async () => {
|
||||
if (showMembers) { setShowMembers(false); return; }
|
||||
if (memberCities.length > 0) { setShowMembers(true); return; }
|
||||
setMembersLoading(true);
|
||||
try {
|
||||
const cities = await fetchMemberCities();
|
||||
setMemberCities(cities);
|
||||
setShowMembers(true);
|
||||
} catch (err) {
|
||||
console.warn('fetchMemberCities error:', err);
|
||||
} finally {
|
||||
setMembersLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const animation = useAnimation(transactions, arcs, periodDays, allTimestamps);
|
||||
|
||||
// Synchronise l'état dans l'URL (deep link / partage)
|
||||
@@ -142,7 +160,10 @@ export default function App() {
|
||||
{/* Map area */}
|
||||
<div className="relative flex-1 min-w-0">
|
||||
{viewMode === 'heatmap' ? (
|
||||
<HeatMap transactions={animation.visibleTransactions} />
|
||||
<HeatMap
|
||||
transactions={animation.visibleTransactions}
|
||||
memberCities={showMembers ? memberCities : []}
|
||||
/>
|
||||
) : (
|
||||
<FlowMap
|
||||
arcs={animation.active ? animation.visibleArcs : arcs}
|
||||
@@ -181,6 +202,21 @@ export default function App() {
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Toggle overlay membres DU */}
|
||||
<button
|
||||
onClick={toggleMembers}
|
||||
disabled={membersLoading}
|
||||
title={showMembers ? 'Masquer les membres' : 'Afficher les membres Ğ1 actifs géolocalisés'}
|
||||
className={`absolute ${isMobile ? 'top-40' : 'top-28'} left-4 z-[1001] w-10 h-10 backdrop-blur-sm border rounded-xl flex items-center justify-center text-sm transition-colors
|
||||
${showMembers
|
||||
? 'bg-[#00c853]/20 border-[#00c853]/60 text-[#00c853]'
|
||||
: 'bg-[#0a0b0f]/90 border-[#2e2f3a] text-[#6b7280] hover:text-[#00c853]'
|
||||
}`}
|
||||
aria-label="Membres DU"
|
||||
>
|
||||
{membersLoading ? <span className="animate-spin inline-block text-xs">↻</span> : 'DU'}
|
||||
</button>
|
||||
|
||||
{/* Period selector — floating over map */}
|
||||
<div className={`absolute top-4 z-[1000] ${isMobile ? 'left-16 right-4' : 'left-1/2 -translate-x-1/2'}`}>
|
||||
<PeriodSelector
|
||||
|
||||
Reference in New Issue
Block a user