Files
ffsaf-site/src/main/webapp/src/pages/admin/StatsLazy.jsx
Thibaut Valentin bc72b7912c
All checks were successful
Deploy Production Server / if_merged (pull_request) Successful in 6m46s
fix: cat graphe color and text alignement
2025-12-31 14:50:15 +01:00

169 lines
6.0 KiB
JavaScript

import {Area, AreaChart, CartesianGrid, Cell, Pie, PieChart, ResponsiveContainer, Sector, Tooltip, XAxis, YAxis} from "recharts";
import {useEffect, useState} from "react";
import {getSaison} from "../../utils/Tools.js";
export default function StatsLazy({data}) {
return <div className="container">
<div className="row" style={{marginTop: "2em"}}>
<h3>Nombre de licences</h3>
<div className="col-lg-8" style={{margin: "auto"}}>
<div style={{width: '100%', aspectRatio: 1.5}}>
<NbGraph raw_data={data}/>
</div>
</div>
</div>
<div className="row" style={{marginTop: "3em"}}>
<h3>Nombre de licences par catégorie pour {getSaison()}</h3>
<div className="col-lg-8" style={{margin: "auto"}}>
<div style={{width: '100%', aspectRatio: 1.5}}>
<CatGraph raw_data={data}/>
</div>
</div>
</div>
</div>
}
function NbGraph({raw_data}) {
const [showData, setShowData] = useState([])
useEffect(() => {
const data = []
for (const k in raw_data.licences) {
data.push({
name: k + "-" + (parseInt(k) + 1),
h: raw_data.licences[k].h,
f: raw_data.licences[k].f,
na: raw_data.licences[k].na,
not_valid: raw_data.licences[k].not_valid,
})
}
setShowData(data)
}, [raw_data])
const colors = ['#6e6e6e', '#1a07f8', '#f659d5', '#f80000']
return <ResponsiveContainer>
<AreaChart
width={500}
height={400}
data={showData}
margin={{
top: 10,
right: 30,
left: 0,
bottom: 0,
}}>
<CartesianGrid strokeDasharray="3 3"/>
<XAxis dataKey="name"/>
<YAxis/>
<Tooltip/>
<Area type="monotone" dataKey="na" name="Non définie" stackId="1" stroke={colors[0]} fill={colors[0]}/>
<Area type="monotone" dataKey="h" name="Homme" stackId="1" stroke={colors[1]} fill={colors[1]}/>
<Area type="monotone" dataKey="f" name="Femme" stackId="1" stroke={colors[2]} fill={colors[2]}/>
<Area type="monotone" dataKey="not_valid" name="Non validée" stackId="2" stroke={colors[3]} fill={colors[3]} strokeDasharray="5 5"
fillOpacity="30%"/>
</AreaChart>
</ResponsiveContainer>
}
function CatGraph({raw_data}) {
const [showData, setShowData] = useState([])
const [activeIndex, setActiveIndex] = useState(0)
useEffect(() => {
const data = []
for (const k of raw_data.categories) {
data.push({
name: k.name,
value: k.count + 1,
})
}
setShowData(data)
}, [raw_data])
const COLORS = ['#ffff99', '#a6cee3', '#1f78b4', '#b2df8a'
, '#33a02c', '#fb9a99', '#e31a1c', '#fdbf6f'
, '#ff7f00', '#cab2d6', '#6a3d9a'];
const RADIAN = Math.PI / 180;
const renderCustomizedLabel = ({cx, cy, midAngle, innerRadius, outerRadius, index}) => {
const radius = innerRadius + (outerRadius - innerRadius) * 0.6;
const x = cx + radius * Math.cos(-midAngle * RADIAN);
const y = cy + radius * Math.sin(-midAngle * RADIAN);
return <> {
showData[index]['value'] > 0 &&
<text x={x} y={y} fill={(index === 2 || index === 6 || index === 10) ? "white" : "black"} textAnchor="middle" dominantBaseline="central">
{`${showData[index]['name']}`}
</text>
}</>;
};
const onPieEnter = (_, index) => {
setActiveIndex(index);
};
const renderActiveShape = (props) => {
const RADIAN = Math.PI / 180;
const {cx, cy, midAngle, innerRadius, outerRadius, startAngle, endAngle, fill, payload, percent, value} = props;
const sin = Math.sin(-RADIAN * midAngle);
const cos = Math.cos(-RADIAN * midAngle);
const sx = cx + (outerRadius + 10) * cos;
const sy = cy + (outerRadius + 10) * sin;
const mx = cx + (outerRadius + 30) * cos;
const my = cy + (outerRadius + 30) * sin;
const ex = mx + (cos >= 0 ? 1 : -1) * 22;
const ey = my;
const textAnchor = cos >= 0 ? 'start' : 'end';
return <g>
<Sector
cx={cx}
cy={cy}
innerRadius={innerRadius}
outerRadius={outerRadius}
startAngle={startAngle}
endAngle={endAngle}
fill={fill}
/>
<Sector
cx={cx}
cy={cy}
startAngle={startAngle}
endAngle={endAngle}
innerRadius={outerRadius + 6}
outerRadius={outerRadius + 10}
fill={fill}
/>
<path d={`M${sx},${sy}L${mx},${my}L${ex},${ey}`} stroke={fill} fill="none"/>
<circle cx={ex} cy={ey} r={2} fill={fill} stroke="none"/>
<text x={ex + (cos >= 0 ? 1 : -1) * 12} y={ey} textAnchor={textAnchor} fill="#333">{`${payload.name}`}</text>
<text x={ex + (cos >= 0 ? 1 : -1) * 12} y={ey} dy={18} textAnchor={textAnchor} fill="#999">
{`${value} (${(percent * 100).toFixed(0)}%)`}
</text>
</g>;
};
return <ResponsiveContainer>
<PieChart>
<Pie
activeIndex={activeIndex}
activeShape={renderActiveShape}
data={showData}
labelLine={false}
label={renderCustomizedLabel}
outerRadius="80%"
fill="#8884d8"
dataKey="value"
onMouseEnter={onPieEnter}>
{showData.map((entry, index) => (
<Cell key={`cell-${index}`} fill={COLORS[(index) % COLORS.length]}/>
))}
</Pie>
</PieChart>
</ResponsiveContainer>
}