"use client"
import { useState } from "react" import { Pie, PieChart, Sector, Cell } from "recharts" import type { PieSectorDataItem } from "recharts/types/polar/Pie"
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select" import { type ChartConfig, ChartContainer, ChartTooltip, ChartTooltipContent } from "@/components/ui/chart"
// Define the structure of our data interface DataItem { name: string value: number color: string }
// Define valid month keys type MonthKey = "Nov" | "Dec" | "Jan"
const monthData: Record<MonthKey, DataItem[]> = {
Nov: [
{ name: "Total Vendor", value: 65, color: "rgb(19,34,83)" },
{ name: "Active vendor", value: 15, color: "#DBB0E4
" },
{ name: "Customer", value: 80, color: "#7ABFFF
" },
],
Dec: [
{ name: "Total Vendor", value: 50, color: "rgb(19,34,83)" },
{ name: "Active vendor", value: 25, color: "#DBB0E4
" },
{ name: "Customer", value: 25, color: "#7ABFFF
" },
],
Jan: [
{ name: "Total Vendor", value: 70, color: "rgb(19,34,83)" },
{ name: "Active vendor", value: 20, color: "#DBB0E4
" },
{ name: "Customer", value: 10, color: "#7ABFFF
" },
],
}
const chartConfig = {
value: {
label: "Value",
},
"Total Vendor": {
label: "Total Vendor",
color: "rgb(19,34,83)",
},
"Active vendor": {
label: "Active vendor",
color: "#DBB0E4
",
},
Customer: {
label: "Customer",
color: "#7ABFFF
",
},
} satisfies ChartConfig
export default function AnalyticsChart() { const [month, setMonth] = useState<MonthKey>("Nov")
const data = monthData[month] const totalValue = data[0]?.value || 0
return (
<Card className="w-full max-w-md bg-white rounded-xl shadow-sm">
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-7">
<CardTitle className="text-[28px] font-semibold text-[#494949]">Analytics</CardTitle>
<Select value={month} onValueChange={(value) => setMonth(value as MonthKey)}>
<SelectTrigger className="w-[90px] bg-primary text-primary-foreground focus:ring-0">
<SelectValue />
</SelectTrigger>
<SelectContent className="text-[12px] rounded-[8px]">
<SelectItem value="Nov">Nov 24</SelectItem>
<SelectItem value="Dec">Dec 24</SelectItem>
<SelectItem value="Jan">Jan 25</SelectItem>
</SelectContent>
</Select>
</CardHeader>
<CardContent className="flex flex-col items-center pb-6">
<ChartContainer config={chartConfig} className="relative h-[218px] w-[222px]">
<div className="relative">
<PieChart width={222} height={218}>
<ChartTooltip cursor={false} content={<ChartTooltipContent hideLabel />} />
<Pie
data={data}
dataKey="value"
nameKey="name"
innerRadius={65}
outerRadius={80}
strokeWidth={0}
activeIndex={0}
startAngle={90}
endAngle={450}
activeShape={(props: PieSectorDataItem) => {
return <Sector {...props} outerRadius={(props.outerRadius ?? 0) + 10} />;
}}
>
{data.map((entry, index) => (
<Cell key={cell-${index}
} fill={entry.color} />
))}
</Pie>
</PieChart>
<div className="absolute inset-0 flex flex-col items-center justify-center">
<span className="text-4xl font-bold">{totalValue}%</span>
<span className="text-sm text-muted-foreground">Total sales</span>
</div>
</div>
</ChartContainer>
<div className="mt-6 flex justify-center gap-1 2xl:gap-6">
{data.map((item, index) => (
<div key={index} className="flex items-center gap-2">
<div className="h-3 w-4 2xl:w-3 rounded-full" style={{ backgroundColor: item.color }} />
<span className="text-sm ">{item.name}</span>
</div>
))}
</div>
</CardContent>
</Card>
) }
Dynamically chang: {data.map((item, index) => ( <div key={index} className="flex items-center gap-2"> <div className="h-3 w-4 2xl:w-3 rounded-full" style={{ backgroundColor: item.color }} /> <span className="text-sm ">{item.name}</span> </div> ))}
Click after chang: <div key={index} className="flex items-center gap-2"> <div className="h-3 w-4 2xl:w-3 rounded-full" style={{ backgroundColor: item.color }} /> <span className="text-sm ">{item.name}</span> </div>
I understand that you want to make the legend items clickable and dynamically change the active sector of the pie chart. Let's modify the code to achieve this functionality. We'll add a state to keep track of the active sector and update the PieChart component to reflect these changes.
Let's go through the changes made to make the chart interactive:
We added a new state variable activeIndex
to keep track of the currently active sector:
const [activeIndex, setActiveIndex] = useState(0)
We created a handleSectorClick
function to update the activeIndex
when a sector is clicked:
const handleSectorClick = (index: number) => {
setActiveIndex(index)
}
We added a renderActiveShape
function to define how the active sector should be rendered:
const renderActiveShape = (props: PieSectorDataItem) => {
// ... (implementation)
}
We updated the Pie
component to use the activeIndex
and renderActiveShape
:
<Pie
// ... (other props)
activeIndex={activeIndex}
activeShape={renderActiveShape}
onClick={(_, index) => handleSectorClick(index)}
>
We modified the center text to display the value and name of the active sector:
<div className="absolute inset-0 flex flex-col items-center justify-center">
<span className="text-4xl font-bold">{data[activeIndex].value}%</span>
<span className="text-sm text-muted-foreground">{data[activeIndex].name}</span>
</div>
We updated the legend items to be clickable and to highlight the active item:
<div
key={index}
className="flex items-center gap-2 cursor-pointer"
onClick={() => handleSectorClick(index)}
>
<div
className={`h-3 w-4 2xl:w-3 rounded-full ${
activeIndex === index ? 'ring-2 ring-offset-2' : ''
}`}
style={{ backgroundColor: item.color }}
/>
<span className={`text-sm ${activeIndex === index ? 'font-bold' : ''}`}>{item.name}</span>
</div>
These changes make the pie chart interactive. Now, when you click on a sector in the chart or a legend item, it will become active, and the center text will update to show the corresponding value and name. The active sector will be slightly enlarged, and the active legend item will be highlighted with a ring and bold text.
Let's modify the code to set the activeIndex
to the index of the monthly value that has the maximum value when the month changes. We'll do this by adding a function to find the index of the maximum value and updating the setMonth
function to use it.
Here's the updated code:
These changes will do the following:
getMaxValueIndex
function that finds the index of the item with the maximum value in an array of DataItem
s.activeIndex
to use this function, setting it to the index of the maximum value for the initial month (Nov).onValueChange
handler for the month selection to update both the month
state and the activeIndex
state. When a new month is selected, it will automatically set the active index to the sector with the maximum value for that month.handleSectorClick
function to allow users to manually select sectors, but now the chart will always start with the maximum value sector highlighted when changing months.These modifications ensure that when the user changes the month, the pie chart will automatically highlight the sector with the maximum value for that month. The user can still click on other sectors to highlight them manually.