feat(lesson-preparation): 备课模块审计重构 — 跨模块解耦 + i18n + 纯函数抽取 + 错误边界
P0-1 跨模块直查修复:publish-service 不再直查 examQuestions 表,新增 exams/data-access.addExamQuestions 接口,复用 classes/data-access.getStudentIdsByClassIds P0-2 i18n 接入:新增 zh-CN/en 翻译文件,注册 lessonPreparation 命名空间,17 个组件改造为 useTranslations/getTranslations P1 纯函数抽取:lib/document-migration.ts(类型守卫替代 as 断言)、lib/node-summary.ts(翻译函数注入)、lib/rf-mappers.ts P1 错误边界+骨架屏:新增 LessonPlanErrorBoundary 和 4 个 Skeleton 组件 P1 Block 注册表:新增 config/block-registry.tsx(BlockRenderer 组件),node-edit-panel 重构为配置驱动渲染 P1 其他修复:exercise-block 改用 router.refresh(),node-editor/lesson-node 复用 lib/ 纯函数 架构图同步:更新 004 和 005 文档 Refs: docs/architecture/audit/lesson-preparation-audit-report.md
This commit is contained in:
@@ -1,12 +1,17 @@
|
||||
"use client";
|
||||
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { Button } from "@/shared/components/ui/button";
|
||||
import { LESSON_PLAN_STATUS_LABELS } from "../constants";
|
||||
import { formatDateTime } from "@/shared/lib/utils";
|
||||
import { duplicateLessonPlanAction, deleteLessonPlanAction } from "../actions";
|
||||
import type { LessonPlanListItem } from "../types";
|
||||
|
||||
export function LessonPlanCard({ plan }: { plan: LessonPlanListItem }) {
|
||||
const t = useTranslations("lessonPreparation");
|
||||
const router = useRouter();
|
||||
|
||||
return (
|
||||
<div className="border border-outline-variant rounded-lg p-4 bg-surface-container-lowest hover:shadow-md transition-shadow">
|
||||
<Link
|
||||
@@ -18,17 +23,17 @@ export function LessonPlanCard({ plan }: { plan: LessonPlanListItem }) {
|
||||
</h3>
|
||||
</Link>
|
||||
<div className="text-sm text-on-surface-variant mt-1">
|
||||
{plan.textbookTitle ?? "无教材"} · {plan.chapterTitle ?? "无章节"}
|
||||
{plan.textbookTitle ?? t("list.noTextbook")} · {plan.chapterTitle ?? t("list.noChapter")}
|
||||
</div>
|
||||
<div className="text-xs text-on-surface-variant mt-1">
|
||||
{plan.templateName ?? "无模板"} ·{" "}
|
||||
{LESSON_PLAN_STATUS_LABELS[plan.status]}
|
||||
{plan.templateName ?? t("list.noTemplate")} ·{" "}
|
||||
{t(`status.${plan.status}`)}
|
||||
</div>
|
||||
<div className="text-xs text-on-surface-variant mt-2">
|
||||
最后保存:
|
||||
{t("list.lastSaved")}
|
||||
{plan.lastSavedAt
|
||||
? new Date(plan.lastSavedAt).toLocaleString()
|
||||
: "未保存"}
|
||||
? formatDateTime(plan.lastSavedAt)
|
||||
: t("list.neverSaved")}
|
||||
</div>
|
||||
<div className="flex gap-2 mt-3">
|
||||
<Button
|
||||
@@ -36,21 +41,21 @@ export function LessonPlanCard({ plan }: { plan: LessonPlanListItem }) {
|
||||
size="sm"
|
||||
onClick={async () => {
|
||||
const res = await duplicateLessonPlanAction(plan.id);
|
||||
if (res.success) window.location.reload();
|
||||
if (res.success) router.refresh();
|
||||
}}
|
||||
>
|
||||
复制
|
||||
{t("action.duplicate")}
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={async () => {
|
||||
if (!confirm("确认归档此课案?")) return;
|
||||
if (!confirm(t("confirm.archive"))) return;
|
||||
const res = await deleteLessonPlanAction(plan.id);
|
||||
if (res.success) window.location.reload();
|
||||
if (res.success) router.refresh();
|
||||
}}
|
||||
>
|
||||
归档
|
||||
{t("action.archive")}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user