197 lines
7.4 KiB
React
197 lines
7.4 KiB
React
|
|
import React, { useState, useEffect } from "react";
|
||
|
|
import "./PozoPublicPiechart.scss"
|
||
|
|
|
||
|
|
// Helper function to calculate the arcs for the donut chart
|
||
|
|
const calculateArcs = (data) => {
|
||
|
|
const total = data.reduce((acc, item) => acc + item.value, 0);
|
||
|
|
let cumulativePercentage = 0;
|
||
|
|
|
||
|
|
return data.map((item) => {
|
||
|
|
const percentage = (item.value / total) * 100;
|
||
|
|
const startAngle = cumulativePercentage;
|
||
|
|
const endAngle = cumulativePercentage + percentage;
|
||
|
|
cumulativePercentage = endAngle;
|
||
|
|
|
||
|
|
const largeArcFlag = percentage > 50 ? 1 : 0;
|
||
|
|
|
||
|
|
const outerRadius = 1; // Outer radius for the arc
|
||
|
|
const innerRadius = 0.7; // Inner radius for the hollow part
|
||
|
|
|
||
|
|
// Calculate the outer arc points
|
||
|
|
const startXOuter = outerRadius * Math.cos((2 * Math.PI * startAngle) / 100);
|
||
|
|
const startYOuter = outerRadius * Math.sin((2 * Math.PI * startAngle) / 100);
|
||
|
|
const endXOuter = outerRadius * Math.cos((2 * Math.PI * endAngle) / 100);
|
||
|
|
const endYOuter = outerRadius * Math.sin((2 * Math.PI * endAngle) / 100);
|
||
|
|
|
||
|
|
// Calculate the inner arc points
|
||
|
|
const startXInner = innerRadius * Math.cos((2 * Math.PI * startAngle) / 100);
|
||
|
|
const startYInner = innerRadius * Math.sin((2 * Math.PI * startAngle) / 100);
|
||
|
|
const endXInner = innerRadius * Math.cos((2 * Math.PI * endAngle) / 100);
|
||
|
|
const endYInner = innerRadius * Math.sin((2 * Math.PI * endAngle) / 100);
|
||
|
|
|
||
|
|
// Create the path for the arc (outer and inner)
|
||
|
|
const pathData = [
|
||
|
|
`M ${startXOuter} ${startYOuter}`, // Move to start of outer arc
|
||
|
|
`A ${outerRadius} ${outerRadius} 0 ${largeArcFlag} 1 ${endXOuter} ${endYOuter}`, // Outer arc
|
||
|
|
`L ${endXInner} ${endYInner}`, // Line to inner arc
|
||
|
|
`A ${innerRadius} ${innerRadius} 0 ${largeArcFlag} 0 ${startXInner} ${startYInner}`, // Inner arc
|
||
|
|
`Z`, // Close the path
|
||
|
|
].join(" ");
|
||
|
|
|
||
|
|
return {
|
||
|
|
pathData,
|
||
|
|
color: item.color,
|
||
|
|
value: item.value,
|
||
|
|
percentage: percentage.toFixed(2),
|
||
|
|
name: item.name,
|
||
|
|
date: item.date, // Add date to return object
|
||
|
|
};
|
||
|
|
|
||
|
|
|
||
|
|
});
|
||
|
|
};
|
||
|
|
|
||
|
|
const PieChart = ({ data }) => {
|
||
|
|
const [tooltip, setTooltip] = useState({ x: 0, y: 0, content: '', visible: false });
|
||
|
|
const [filter, setFilter] = useState(""); // State to hold the filter criteria
|
||
|
|
const [dateFilter, setDateFilter] = useState(""); // State to hold the date filter
|
||
|
|
const [animatedValues, setAnimatedValues] = useState(data.map(item => ({ ...item, animatedValue: 0 }))); // State for animated values
|
||
|
|
|
||
|
|
// Function to filter data
|
||
|
|
const filteredData = animatedValues.filter(item =>
|
||
|
|
item.name.toLowerCase().includes(filter.toLowerCase()) &&
|
||
|
|
(!dateFilter || item.date.includes(dateFilter)) // Filter by date
|
||
|
|
);
|
||
|
|
|
||
|
|
const handleMouseOver = (event, arc) => {
|
||
|
|
setTooltip({
|
||
|
|
x: event.clientX + 10,
|
||
|
|
y: event.clientY - 20,
|
||
|
|
content: `${arc.name}: ₹${arc.value} (${arc.percentage}%) on ${arc.date}`,
|
||
|
|
visible: true,
|
||
|
|
});
|
||
|
|
};
|
||
|
|
|
||
|
|
const handleMouseOut = () => {
|
||
|
|
setTooltip((prev) => ({ ...prev, visible: false }));
|
||
|
|
};
|
||
|
|
|
||
|
|
const arcs = calculateArcs(filteredData);
|
||
|
|
console.log(arcs, "yusgd");
|
||
|
|
// Animate values from 0 to actual value
|
||
|
|
useEffect(() => {
|
||
|
|
const animationDuration = 2000; // Animation duration in ms
|
||
|
|
const interval = 50; // Interval for the animation
|
||
|
|
const step = Math.floor(animationDuration / interval);
|
||
|
|
|
||
|
|
console.log("Animated Values:", animatedValues);
|
||
|
|
|
||
|
|
const newValues = animatedValues.map(item => ({
|
||
|
|
...item,
|
||
|
|
animatedValue: 0, // Start from 0
|
||
|
|
}));
|
||
|
|
|
||
|
|
let animationFrame = 0;
|
||
|
|
const intervalId = setInterval(() => {
|
||
|
|
if (animationFrame < step) {
|
||
|
|
setAnimatedValues(newValues.map(item => ({
|
||
|
|
...item,
|
||
|
|
animatedValue: Math.round((item.value / step) * (animationFrame + 0)), // Increment the value
|
||
|
|
})));
|
||
|
|
animationFrame++;
|
||
|
|
} else {
|
||
|
|
clearInterval(intervalId); // Stop the animation when complete
|
||
|
|
setAnimatedValues(newValues.map(item => ({ ...item, animatedValue: item.value }))); // Set to final value
|
||
|
|
}
|
||
|
|
}, interval);
|
||
|
|
|
||
|
|
return () => clearInterval(intervalId); // Cleanup interval on unmount
|
||
|
|
}, []);
|
||
|
|
|
||
|
|
return (
|
||
|
|
<div style={{ position: "relative",display: 'flex', alignItems: 'center', justifyContent: "space-around" , gap:"1rem"}}>
|
||
|
|
<div className="piechartsize">
|
||
|
|
{/* <input
|
||
|
|
type="text"
|
||
|
|
placeholder="Filter by name..."
|
||
|
|
value={filter}
|
||
|
|
onChange={(e) => setFilter(e.target.value)}
|
||
|
|
style={{ marginBottom: '1rem', padding: '8px', borderRadius: '4px', border: '1px solid #ccc' }}
|
||
|
|
/>
|
||
|
|
<input
|
||
|
|
type="text"
|
||
|
|
placeholder="Filter by date (YYYY-MM-DD)..."
|
||
|
|
value={dateFilter}
|
||
|
|
onChange={(e) => setDateFilter(e.target.value)}
|
||
|
|
style={{ marginBottom: '1rem', padding: '8px', borderRadius: '4px', border: '1px solid #ccc' }}
|
||
|
|
/> */}
|
||
|
|
|
||
|
|
<svg
|
||
|
|
viewBox="-1 -1 2 2"
|
||
|
|
className="loading-circle" // Add loading animation class here
|
||
|
|
style={{ transform: "rotate(-90deg)", width: "100%", height: "100%" }}
|
||
|
|
>
|
||
|
|
{arcs.map((arc, index) => (
|
||
|
|
<path
|
||
|
|
key={index}
|
||
|
|
d={arc.pathData}
|
||
|
|
fill={arc.color}
|
||
|
|
onMouseOver={(e) => handleMouseOver(e, arc)}
|
||
|
|
onMouseOut={handleMouseOut}
|
||
|
|
/>
|
||
|
|
))}
|
||
|
|
</svg>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div style={{ display: 'flex', flexDirection: 'column', gap: '0.4rem' }}>
|
||
|
|
{arcs.map((arc, index) => (
|
||
|
|
<p key={index} style={{
|
||
|
|
display: 'flex',
|
||
|
|
alignItems: "center",
|
||
|
|
gap: '0.5rem',
|
||
|
|
textTransform: 'uppercase',
|
||
|
|
fontWeight: '600',
|
||
|
|
fontFamily: "Gilroy",
|
||
|
|
fontSize: '10.5px',
|
||
|
|
textWrap:"nowrap"
|
||
|
|
}}>
|
||
|
|
<div style={{ width: "6px", height: '6px', borderRadius: '60px', backgroundColor: arc.color, }}> </div>
|
||
|
|
{arc.name}
|
||
|
|
<p style={{width:'25px', color: '#007E1C',fontSize: '10.5px', }}> ₹{animatedValues[index]?.animatedValue}</p>
|
||
|
|
<p style={{ color: 'green',fontSize: '10.5px', }}> ({arc.percentage}%) </p>
|
||
|
|
</p>
|
||
|
|
))}
|
||
|
|
</div>
|
||
|
|
|
||
|
|
{tooltip.visible && (
|
||
|
|
<div
|
||
|
|
style={{
|
||
|
|
position: "absolute",
|
||
|
|
backgroundColor: "#23282D",
|
||
|
|
color: "#ecf0f1",
|
||
|
|
padding: "10px 15px",
|
||
|
|
borderRadius: "8px",
|
||
|
|
pointerEvents: "none",
|
||
|
|
zIndex: 10,
|
||
|
|
transform: "translate(-50%, -100%)",
|
||
|
|
fontSize: "14px",
|
||
|
|
fontFamily: "Arial, sans-serif",
|
||
|
|
}}
|
||
|
|
>
|
||
|
|
<div style={{
|
||
|
|
position: "absolute",
|
||
|
|
top: "100%",
|
||
|
|
left: "50%",
|
||
|
|
marginLeft: "-5px",
|
||
|
|
borderWidth: "5px",
|
||
|
|
borderStyle: "solid",
|
||
|
|
}} />
|
||
|
|
{tooltip.content}
|
||
|
|
</div>
|
||
|
|
)}
|
||
|
|
</div>
|
||
|
|
);
|
||
|
|
};
|
||
|
|
|
||
|
|
export default PieChart;
|