From 2dd8c2197c787a42b35250631d5f17ea932a3708 Mon Sep 17 00:00:00 2001 From: SpecialX <47072643+wangxiner55@users.noreply.github.com> Date: Fri, 3 Jul 2026 10:30:41 +0800 Subject: [PATCH] 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) --- src/modules/error-book/actions.ts | 14 +- .../components/add-error-book-dialog.tsx | 84 +-- .../components/analytics-stats-cards.tsx | 32 +- .../components/chapter-weakness-chart.tsx | 76 +- .../components/class-error-bar-chart.tsx | 62 +- .../components/class-error-overview.tsx | 198 ----- .../error-book/components/class-filter.tsx | 12 +- .../components/error-book-detail-dialog.tsx | 175 ++--- .../components/error-book-filters.tsx | 32 +- .../components/error-book-item-card.tsx | 58 +- .../error-book/components/error-book-list.tsx | 30 +- .../components/error-book-stats-cards.tsx | 24 +- .../grouped-student-error-table.tsx | 34 +- .../knowledge-point-weakness-chart.tsx | 61 +- .../error-book/components/review-buttons.tsx | 72 +- .../components/subject-distribution-chart.tsx | 50 +- .../error-book/components/subject-tabs.tsx | 10 +- .../components/top-wrong-questions.tsx | 57 +- .../error-book/data-access-analytics.ts | 552 ++++++++++++++ src/modules/error-book/data-access.ts | 685 ++---------------- 20 files changed, 1090 insertions(+), 1228 deletions(-) delete mode 100644 src/modules/error-book/components/class-error-overview.tsx create mode 100644 src/modules/error-book/data-access-analytics.ts diff --git a/src/modules/error-book/actions.ts b/src/modules/error-book/actions.ts index 639daf6..d5a5802 100644 --- a/src/modules/error-book/actions.ts +++ b/src/modules/error-book/actions.ts @@ -58,16 +58,14 @@ export async function getErrorBookItemsAction( } } - const status = params.status && params.status !== "all" - ? (z.enum(["new", "learning", "mastered", "archived"]).safeParse(params.status).success - ? (params.status as "new" | "learning" | "mastered" | "archived") - : undefined) + const statusParse = z.enum(["new", "learning", "mastered", "archived"]).safeParse(params.status) + const status = params.status && params.status !== "all" && statusParse.success + ? statusParse.data : undefined - const sourceType = params.sourceType && params.sourceType !== "all" - ? (z.enum(["exam", "homework", "manual"]).safeParse(params.sourceType).success - ? (params.sourceType as "exam" | "homework" | "manual") - : undefined) + const sourceTypeParse = z.enum(["exam", "homework", "manual"]).safeParse(params.sourceType) + const sourceType = params.sourceType && params.sourceType !== "all" && sourceTypeParse.success + ? sourceTypeParse.data : undefined const data = await getErrorBookItems({ diff --git a/src/modules/error-book/components/add-error-book-dialog.tsx b/src/modules/error-book/components/add-error-book-dialog.tsx index 40326ed..5dcd71e 100644 --- a/src/modules/error-book/components/add-error-book-dialog.tsx +++ b/src/modules/error-book/components/add-error-book-dialog.tsx @@ -3,6 +3,7 @@ import { useState, useTransition, useEffect } from "react" import { Plus } from "lucide-react" import { toast } from "sonner" +import { useTranslations } from "next-intl" import { Dialog, @@ -23,53 +24,44 @@ import { SelectValue, } from "@/shared/components/ui/select" import { Textarea } from "@/shared/components/ui/textarea" -import { getQuestionsAction } from "@/modules/questions/actions" import { createErrorBookItemAction } from "../actions" import { COMMON_ERROR_TAGS } from "../types" -export function AddErrorBookDialog() { +interface QuestionOption { + id: string + preview: string +} + +interface AddErrorBookDialogProps { + /** 预加载的题目选项(若提供则直接使用,不再调用 onLoadQuestions) */ + questionOptions?: QuestionOption[] + /** 懒加载题目选项的回调(对话框打开时调用) */ + onLoadQuestions?: () => Promise +} + +export function AddErrorBookDialog({ + questionOptions: questionOptionsProp, + onLoadQuestions, +}: AddErrorBookDialogProps) { + const t = useTranslations("error-book") const [open, setOpen] = useState(false) const [isPending, startTransition] = useTransition() const [questionId, setQuestionId] = useState("") const [note, setNote] = useState("") const [errorTags, setErrorTags] = useState([]) - const [questionOptions, setQuestionOptions] = useState>([]) + const [loadedOptions, setLoadedOptions] = useState([]) - function extractPreview(content: unknown): string { - if (typeof content === "string") return content.slice(0, 60) - if (Array.isArray(content)) { - const texts: string[] = [] - for (const node of content) { - if (typeof node === "string") texts.push(node) - else if (typeof node === "object" && node !== null) { - const n = node as Record - if (typeof n.text === "string") texts.push(n.text) - } - } - return texts.join("").slice(0, 60) - } - return "题目" - } + const questionOptions = questionOptionsProp ?? loadedOptions useEffect(() => { - if (open && questionOptions.length === 0) { - getQuestionsAction({ pageSize: 100 }) - .then((res) => { - if (res.success && res.data) { - setQuestionOptions( - res.data.data.map((q) => ({ - id: q.id, - preview: extractPreview(q.content), - })) - ) - } + if (open && onLoadQuestions && loadedOptions.length === 0 && !questionOptionsProp) { + onLoadQuestions() + .then((options) => { + setLoadedOptions(options) }) .catch(() => {}) } - }, [open, questionOptions.length]) + }, [open, onLoadQuestions, loadedOptions.length, questionOptionsProp]) function toggleTag(tag: string) { setErrorTags((prev) => @@ -79,7 +71,7 @@ export function AddErrorBookDialog() { function handleSubmit() { if (!questionId) { - toast.error("请选择题目") + toast.error(t("messages.selectQuestion")) return } startTransition(async () => { @@ -90,13 +82,13 @@ export function AddErrorBookDialog() { ) const res = await createErrorBookItemAction(undefined, formData) if (res.success) { - toast.success(res.message ?? "已添加") + toast.success(res.message ?? t("messages.addedShort")) setOpen(false) setQuestionId("") setNote("") setErrorTags([]) } else { - toast.error(res.message ?? "添加失败") + toast.error(res.message ?? t("messages.addFailed")) } }) } @@ -106,23 +98,23 @@ export function AddErrorBookDialog() { - 添加错题 + {t("addDialog.title")} - 从题库中选择题目,添加到你的错题本。你也可以在完成作业/考试后自动采集。 + {t("addDialog.description")}
- +