feat(error-book): update components, actions, data-access, and add analytics
- Add data-access-analytics for error book analytics queries - Update actions, data-access for improved error book operations - Update components: add-error-book-dialog, analytics-stats-cards, chapter-weakness-chart, class-error-bar-chart, class-filter, error-book-detail-dialog, error-book-filters, error-book-item-card, error-book-list, error-book-stats-cards, grouped-student-error-table, knowledge-point-weakness-chart, review-buttons, subject-distribution-chart, subject-tabs, top-wrong-questions - Remove class-error-overview (replaced by new components)
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
"use client"
|
||||
|
||||
import { Bar, BarChart, CartesianGrid, XAxis, YAxis, Cell } from "recharts"
|
||||
import { useTranslations } from "next-intl"
|
||||
import {
|
||||
ChartContainer,
|
||||
ChartTooltip,
|
||||
@@ -17,6 +18,25 @@ interface SubjectDistributionChartProps {
|
||||
className?: string
|
||||
}
|
||||
|
||||
interface SubjectDistChartPayload {
|
||||
name: string
|
||||
errorCount: number
|
||||
masteredCount: number
|
||||
masteryRate: number
|
||||
}
|
||||
|
||||
function isSubjectDistChartPayload(v: unknown): v is SubjectDistChartPayload {
|
||||
if (typeof v !== "object" || v === null) return false
|
||||
// 从 unknown 转换:类型守卫内需要属性访问来校验字段类型
|
||||
const obj = v as Record<string, unknown>
|
||||
return (
|
||||
typeof obj.name === "string" &&
|
||||
typeof obj.errorCount === "number" &&
|
||||
typeof obj.masteredCount === "number" &&
|
||||
typeof obj.masteryRate === "number"
|
||||
)
|
||||
}
|
||||
|
||||
const SUBJECT_COLORS = [
|
||||
"var(--color-chart-1)",
|
||||
"var(--color-chart-2)",
|
||||
@@ -34,6 +54,8 @@ export function SubjectDistributionChart({
|
||||
data,
|
||||
className,
|
||||
}: SubjectDistributionChartProps) {
|
||||
const t = useTranslations("error-book")
|
||||
|
||||
if (data.length === 0) return null
|
||||
|
||||
const chartData = data.map((d) => ({
|
||||
@@ -45,7 +67,7 @@ export function SubjectDistributionChart({
|
||||
|
||||
const chartConfig: ChartConfig = {
|
||||
errorCount: {
|
||||
label: "错题数",
|
||||
label: t("subjectDistChart.errorCount"),
|
||||
color: "var(--color-chart-1)",
|
||||
},
|
||||
}
|
||||
@@ -53,10 +75,14 @@ export function SubjectDistributionChart({
|
||||
return (
|
||||
<Card className={cn("overflow-hidden", className)}>
|
||||
<CardHeader>
|
||||
<CardTitle className="text-base">各学科错题分布</CardTitle>
|
||||
<CardTitle className="text-base">{t("subjectDistChart.title")}</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<ChartContainer config={chartConfig} className="h-[280px] w-full">
|
||||
<ChartContainer
|
||||
config={chartConfig}
|
||||
className="h-[280px] w-full"
|
||||
aria-label={t("subjectDistChart.title")}
|
||||
>
|
||||
<BarChart data={chartData} margin={{ left: 8, right: 8, top: 8, bottom: 8 }}>
|
||||
<CartesianGrid vertical={false} strokeDasharray="4 4" strokeOpacity={0.4} />
|
||||
<XAxis
|
||||
@@ -74,23 +100,21 @@ export function SubjectDistributionChart({
|
||||
<ChartTooltipContent
|
||||
className="w-[200px]"
|
||||
formatter={(payload: unknown) => {
|
||||
const p = payload as unknown as {
|
||||
name: string
|
||||
errorCount: number
|
||||
masteredCount: number
|
||||
masteryRate: number
|
||||
}
|
||||
if (!isSubjectDistChartPayload(payload)) return null
|
||||
return (
|
||||
<div className="space-y-1.5">
|
||||
<div className="font-medium">{p.name}</div>
|
||||
<div className="font-medium">{payload.name}</div>
|
||||
<div className="text-muted-foreground">
|
||||
错题数:<span className="font-medium text-foreground">{p.errorCount}</span>
|
||||
{t("subjectDistChart.errorCount")}:
|
||||
<span className="font-medium text-foreground">{payload.errorCount}</span>
|
||||
</div>
|
||||
<div className="text-muted-foreground">
|
||||
已掌握:<span className="font-medium text-emerald-600">{p.masteredCount}</span>
|
||||
{t("subjectDistChart.masteredLabel")}:
|
||||
<span className="font-medium text-emerald-600">{payload.masteredCount}</span>
|
||||
</div>
|
||||
<div className="text-muted-foreground">
|
||||
掌握率:<span className="font-medium text-foreground">{p.masteryRate}%</span>
|
||||
{t("subjectDistChart.masteryRateLabel")}:
|
||||
<span className="font-medium text-foreground">{payload.masteryRate}%</span>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user