"use client"; import { useEffect, useMemo, useState } from "react"; import * as d3 from "d3"; import {taxes} from '@/data/taxes'; import { CommuneData } from "@/type/CommuneData"; import { Departement } from "@/type/Departement"; /** * TODO : * - déplacer les années tous * - déplacer svg */ export default function ScatterDepartement() { const [year, setYear] = useState(2022); const [taxe, setTaxe] = useState("cves"); const [departements, setDepartements] = useState([]); const [selectedDepartement, setSelectedDepartement] = useState(""); const [hovered, setHovered] = useState(null); const [data, setData] = useState([]); const [isLoading, setIsLoading] = useState(false); const [tooltip, setTooltip] = useState<{ x: number; y: number; data: CommuneData; } | null>(null); const width = 700; const height = 420; const margin = { top: 40, right: 30, bottom: 50, left: 60 }; /** * Récupérer tous les départements */ useEffect(() => { const fetchData = async () => { setIsLoading(true); try { const res = await fetch(`https://localhost/departements`); if (!res.ok) throw new Error("Erreur API"); const apiData = await res.json(); const formatted: Departement[] = apiData.member.map((d: any) => ({ nom: d.nom })); setDepartements(formatted); if (formatted.length > 0) { setSelectedDepartement(formatted[0].nom); } } catch (err) { console.error("Erreur fetch", err); } finally { setIsLoading(false); } }; fetchData(); }, []); /** * Récupérer les taux et le volume collectés */ useEffect(() => { if (!selectedDepartement) return; const fetchData = async () => { setIsLoading(true); try { const res = await fetch( `https://localhost/${taxe}?page=1&departement.nom=${selectedDepartement}&annee=${year}&order[tauxNet]=desc` ); if (!res.ok) throw new Error("Erreur API data"); const apiData = await res.json(); console.log(apiData); const members = Array.isArray(apiData.member) ? apiData.member : []; if (members.length === 0) { setData([]); return; } const formatted: CommuneData[] = members.map((d: any) => ({ commune: d.nomCommune, departement: selectedDepartement, year: d.annee, taxType: taxe, taxRate: d.tauxNet, volume: d.montantReel, })); for (let x = 0; x < 5; x++) { console.log(formatted[x]); } setData(formatted); } catch (err) { console.error("Erreur fetch data:", err); } finally { setIsLoading(false); } }; fetchData(); }, [taxe, selectedDepartement, year]); /** * Couleurs */ const communes = useMemo(() => { return Array.from(new Set(data.map(d => d.commune))); }, [data]); const colorScale = useMemo(() => { return d3.scaleOrdinal() .domain(communes) .range(d3.schemeTableau10); }, [communes]); /** * Scales */ const xScale = useMemo(() => { const domain = data.length ? d3.extent(data, d => d.taxRate) as [number, number] : [0, 100]; // valeur par défaut return d3.scaleLinear() .domain(domain) .nice() .range([margin.left, width - margin.right]); }, [data]); const yScale = useMemo(() => { const domain = data.length ? d3.extent(data, d => d.volume) as [number, number] : [0, 1000000]; // valeur par défaut return d3.scaleLinear() .domain(domain) .nice() .range([height - margin.bottom, margin.top]); }, [data]); if (!xScale || !yScale) return

Chargement...

; const xTicks = xScale.ticks(5); const yTicks = yScale.ticks(5); return (

Relation taux d'imposition / Volume collecté

{/* CONTROLS */}
{isLoading &&

Chargement...

} {/* SVG */} {!data.length && !isLoading && ( Aucune donnée pour cette sélection )} {/* Label X */} Taux d'imposition (%) {/* Label Y */} Volume collecté (€) {yTicks.map(t => ( {d3.format(".2s")(t)} ))} {xTicks.map(t => ( {t.toFixed(1)} ))} {/* Axes */} {/* Points */} {data.map((d, i) => ( { setHovered(d.commune); const rect = (e.target as SVGCircleElement).getBoundingClientRect(); setTooltip({ x: rect.x + rect.width / 2, y: rect.y, data: d }); }} onMouseLeave={() => { setHovered(null); setTooltip(null); }} /> ))} {tooltip && (
{tooltip.data.commune}
Taux : {tooltip.data.taxRate.toFixed(2)} %
Volume : {d3.format(",")(tooltip.data.volume)} €
)} {/* légende */}
{communes.map(commune => (
setHovered(commune)} onMouseLeave={() => setHovered(null)} className="flex items-center gap-2 cursor-pointer transition-all" > {commune}
))}
); }