feat(lesson-preparation): add AI evaluation, analytics, attachments, calendar, comments, review, substitutes, formative, and version diff

- Add actions-ai-evaluation, actions-analytics, actions-attachments, actions-calendar, actions-comments, actions-formative, actions-questions, actions-review, actions-substitutes

- Add corresponding data-access layers for each new action module

- Add calendar-view, curriculum-map-view, version-diff-viewer components

- Add editor-slice, selection-slice, version-slice hooks for state management

- Add document-diff and scope-check lib utilities

- Add default-question-service and external-questions-bridge services
This commit is contained in:
SpecialX
2026-07-03 10:25:21 +08:00
parent a16f09d3c3
commit 20023e13fd
75 changed files with 5131 additions and 1186 deletions

View File

@@ -1,12 +1,14 @@
"use client";
import { useState } from "react";
import Link from "next/link";
import { useRouter } from "next/navigation";
import { useTranslations } from "next-intl";
import { useLessonPlanEditor } from "../../hooks/use-lesson-plan-editor";
import { QuestionBankPicker } from "../question-bank-picker";
import { InlineQuestionEditor } from "../inline-question-editor";
import { PublishHomeworkDialog } from "../publish-homework-dialog";
import { LessonPlanErrorBoundary } from "../lesson-plan-error-boundary";
import { Button } from "@/shared/components/ui/button";
import { Plus, Trash2 } from "lucide-react";
import type {
@@ -120,12 +122,13 @@ export function ExerciseBlock({ blockId, data, classes, textbookId, chapterId }:
<span className="bg-tertiary-container/20 text-tertiary px-2 py-1 rounded">
{t("status.publishedAsHomework")}
</span>
<a
{/* V4 P1-5 修复:原生 <a> 替换为 next/link 的 <Link> */}
<Link
href="/teacher/homework"
className="text-primary underline"
>
{t("action.viewHomework")}
</a>
</Link>
</div>
) : (
data.purpose === "after_class_homework" &&
@@ -140,31 +143,37 @@ export function ExerciseBlock({ blockId, data, classes, textbookId, chapterId }:
)}
</div>
{showBank && (
<QuestionBankPicker
existingIds={data.items.map((i) => i.questionId)}
onPick={addItems}
onClose={() => setShowBank(false)}
/>
<LessonPlanErrorBoundary>
<QuestionBankPicker
existingIds={data.items.map((i) => i.questionId)}
onPick={addItems}
onClose={() => setShowBank(false)}
/>
</LessonPlanErrorBoundary>
)}
{showInline && (
<InlineQuestionEditor
textbookId={textbookId}
chapterId={chapterId}
onAdd={(item) => {
addItems([item]);
setShowInline(false);
}}
onClose={() => setShowInline(false)}
/>
<LessonPlanErrorBoundary>
<InlineQuestionEditor
textbookId={textbookId}
chapterId={chapterId}
onAdd={(item) => {
addItems([item]);
setShowInline(false);
}}
onClose={() => setShowInline(false)}
/>
</LessonPlanErrorBoundary>
)}
{showPublish && (
<PublishHomeworkDialog
planId={planId}
blockId={blockId}
classes={classes}
onClose={() => setShowPublish(false)}
onPublished={() => router.refresh()}
/>
<LessonPlanErrorBoundary>
<PublishHomeworkDialog
planId={planId}
blockId={blockId}
classes={classes}
onClose={() => setShowPublish(false)}
onPublished={() => router.refresh()}
/>
</LessonPlanErrorBoundary>
)}
</div>
);