Files
ai-agent/web/app/dashboard/_components/trend-panel.tsx
T

142 lines
3.9 KiB
TypeScript
Raw Normal View History

2026-04-09 10:01:23 +08:00
"use client"
import { Area, AreaChart, Bar, BarChart, CartesianGrid, XAxis, YAxis } from "recharts"
import type { DashboardStatusDistributionItem, DashboardTrendItem } from "@/lib/api/dashboard"
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card"
import {
ChartContainer,
ChartTooltip,
ChartTooltipContent,
type ChartConfig,
} from "@/components/ui/chart"
2026-05-25 12:06:15 +08:00
import { useI18n } from "@/i18n/provider"
2026-04-09 10:01:23 +08:00
type TrendPanelProps = {
title: string
description: string
trend: DashboardTrendItem[]
distribution: DashboardStatusDistributionItem[]
}
export function TrendPanel({
title,
description,
trend,
distribution,
}: TrendPanelProps) {
2026-05-25 12:06:15 +08:00
const t = useI18n()
const trendConfig = {
newCount: {
label: t("dashboardHome.chartNew"),
color: "hsl(24 95% 53%)",
},
closedCount: {
label: t("dashboardHome.chartClosed"),
color: "hsl(190 95% 39%)",
},
} satisfies ChartConfig
const distributionConfig = {
count: {
label: t("dashboardHome.chartCount"),
theme: {
light: "hsl(222 47% 11%)",
dark: "hsl(210 40% 98%)",
},
},
} satisfies ChartConfig
const localizedDistribution = distribution.map((item) => ({
...item,
label: getStatusLabel(item.status, item.label, t),
}))
2026-04-09 10:01:23 +08:00
return (
<div className="grid gap-4 xl:grid-cols-[1.5fr_0.9fr]">
<Card className="rounded-md shadow-none">
2026-04-09 10:01:23 +08:00
<CardHeader>
<CardTitle>{title}</CardTitle>
<CardDescription>{description}</CardDescription>
</CardHeader>
<CardContent>
<ChartContainer config={trendConfig} className="h-72 w-full">
<AreaChart data={trend}>
<CartesianGrid vertical={false} />
<XAxis dataKey="date" tickLine={false} axisLine={false} tickMargin={8} />
<ChartTooltip content={<ChartTooltipContent />} />
<Area
type="monotone"
dataKey="newCount"
stroke="var(--color-newCount)"
fill="var(--color-newCount)"
fillOpacity={0.18}
strokeWidth={2}
/>
<Area
type="monotone"
dataKey="closedCount"
stroke="var(--color-closedCount)"
fill="var(--color-closedCount)"
fillOpacity={0.08}
strokeWidth={2}
/>
</AreaChart>
</ChartContainer>
</CardContent>
</Card>
<Card className="rounded-md shadow-none">
2026-04-09 10:01:23 +08:00
<CardHeader>
2026-05-25 12:06:15 +08:00
<CardTitle>{t("dashboardHome.statusDistribution")}</CardTitle>
<CardDescription>{t("dashboardHome.statusDistributionDescription")}</CardDescription>
2026-04-09 10:01:23 +08:00
</CardHeader>
<CardContent className="space-y-4">
<ChartContainer config={distributionConfig} className="h-72 w-full">
2026-05-25 12:06:15 +08:00
<BarChart data={localizedDistribution} layout="vertical" margin={{ left: 20 }}>
2026-04-09 10:01:23 +08:00
<CartesianGrid horizontal={false} />
<YAxis
type="category"
dataKey="label"
tickLine={false}
axisLine={false}
width={64}
/>
<XAxis type="number" hide />
<ChartTooltip content={<ChartTooltipContent hideLabel />} />
<Bar
dataKey="count"
fill="var(--color-count)"
radius={[0, 8, 8, 0]}
/>
</BarChart>
</ChartContainer>
</CardContent>
</Card>
</div>
)
}
2026-05-25 12:06:15 +08:00
function getStatusLabel(
status: number,
fallback: string,
t: (key: string) => string
) {
switch (status) {
case 1:
return t("dashboardHome.statusAiServing")
case 2:
return t("dashboardHome.statusPending")
case 3:
return t("dashboardHome.statusActive")
case 4:
return t("dashboardHome.statusClosed")
default:
return fallback
}
}