107 lines
2.6 KiB
TypeScript
107 lines
2.6 KiB
TypeScript
import {
|
|
PieChart,
|
|
Pie,
|
|
Cell,
|
|
ResponsiveContainer,
|
|
Legend,
|
|
Tooltip,
|
|
} from "recharts";
|
|
|
|
interface SentimentData {
|
|
name: string;
|
|
value: number;
|
|
color: string;
|
|
}
|
|
|
|
interface SentimentChartProps {
|
|
data: SentimentData[];
|
|
}
|
|
|
|
export function SentimentChart({ data }: SentimentChartProps) {
|
|
const total = data.reduce((sum, item) => sum + item.value, 0);
|
|
|
|
const CustomTooltip = ({ active, payload }: any) => {
|
|
if (active && payload && payload.length) {
|
|
const item = payload[0].payload;
|
|
const percentage = ((item.value / total) * 100).toFixed(1);
|
|
return (
|
|
<div className="rounded-lg border bg-card px-4 py-3 shadow-lg">
|
|
<p className="font-semibold" style={{ color: item.color }}>
|
|
{item.name}
|
|
</p>
|
|
<p className="text-sm text-muted-foreground">
|
|
{item.value.toLocaleString()} ulasan ({percentage}%)
|
|
</p>
|
|
</div>
|
|
);
|
|
}
|
|
return null;
|
|
};
|
|
|
|
const renderCustomLabel = ({
|
|
cx,
|
|
cy,
|
|
midAngle,
|
|
innerRadius,
|
|
outerRadius,
|
|
percent,
|
|
}: any) => {
|
|
if (percent < 0.05) return null;
|
|
const RADIAN = Math.PI / 180;
|
|
const radius = innerRadius + (outerRadius - innerRadius) * 0.5;
|
|
const x = cx + radius * Math.cos(-midAngle * RADIAN);
|
|
const y = cy + radius * Math.sin(-midAngle * RADIAN);
|
|
|
|
return (
|
|
<text
|
|
x={x}
|
|
y={y}
|
|
fill="white"
|
|
textAnchor="middle"
|
|
dominantBaseline="central"
|
|
className="text-sm font-semibold"
|
|
>
|
|
{`${(percent * 100).toFixed(0)}%`}
|
|
</text>
|
|
);
|
|
};
|
|
|
|
return (
|
|
<div className="h-[300px] w-full">
|
|
<ResponsiveContainer width="100%" height="100%">
|
|
<PieChart>
|
|
<Pie
|
|
data={data}
|
|
cx="50%"
|
|
cy="50%"
|
|
labelLine={false}
|
|
label={renderCustomLabel}
|
|
outerRadius={110}
|
|
innerRadius={60}
|
|
paddingAngle={3}
|
|
dataKey="value"
|
|
strokeWidth={2}
|
|
stroke="hsl(var(--card))"
|
|
>
|
|
{data.map((entry, index) => (
|
|
<Cell
|
|
key={`cell-${index}`}
|
|
fill={entry.color}
|
|
className="transition-all duration-300 hover:opacity-80"
|
|
/>
|
|
))}
|
|
</Pie>
|
|
<Tooltip content={<CustomTooltip />} />
|
|
<Legend
|
|
verticalAlign="bottom"
|
|
height={36}
|
|
formatter={(value: string) => (
|
|
<span className="text-sm text-foreground">{value}</span>
|
|
)}
|
|
/>
|
|
</PieChart>
|
|
</ResponsiveContainer>
|
|
</div>
|
|
);
|
|
}
|