- Add admin/lesson-plans, parent/lesson-plans, student/lesson-plans routes - Add student/practice and teacher/practice routes for adaptive practice - Add management/grade/dashboard and management/grade/practice routes - Add teacher/lesson-plans error and loading boundaries - Update existing admin, parent, student, teacher pages with new features - Update globals.css and proxy middleware
112 lines
3.7 KiB
TypeScript
112 lines
3.7 KiB
TypeScript
import type { JSX } from "react"
|
||
import { Suspense } from "react"
|
||
import { notFound } from "next/navigation"
|
||
import { getLessonPlanById } from "@/modules/lesson-preparation/data-access"
|
||
import { LessonPlanEditor } from "@/modules/lesson-preparation/components/lesson-plan-editor"
|
||
import { LessonPlanProviderSetup } from "@/modules/lesson-preparation/providers/lesson-plan-provider-setup"
|
||
import { getTeacherClasses } from "@/modules/classes/data-access"
|
||
import { getTextbookById, getChaptersByTextbookId } from "@/modules/textbooks/data-access"
|
||
import { getAuthContext } from "@/shared/lib/auth-guard"
|
||
import { Skeleton } from "@/shared/components/ui/skeleton"
|
||
import {
|
||
AiClientProvider,
|
||
type AiClientService,
|
||
} from "@/modules/ai/context/ai-client-provider"
|
||
import {
|
||
aiChatAction,
|
||
suggestSimilarQuestionsAction,
|
||
suggestGradingAction,
|
||
generateLessonContentAction,
|
||
generateQuestionVariantAction,
|
||
analyzeWeaknessAction,
|
||
} from "@/modules/ai/actions"
|
||
|
||
export const dynamic = "force-dynamic"
|
||
|
||
/**
|
||
* 构建 AI 客户端服务(Server Action 引用集合)
|
||
*
|
||
* 通过 React Context 注入,客户端组件不直接 import actions,
|
||
* 遵循依赖注入模式,便于测试时替换为 mock。
|
||
*/
|
||
function createAiClientService(): AiClientService {
|
||
return {
|
||
chat: aiChatAction,
|
||
suggestSimilarQuestions: suggestSimilarQuestionsAction,
|
||
suggestGrading: suggestGradingAction,
|
||
generateLessonContent: generateLessonContentAction,
|
||
generateQuestionVariant: generateQuestionVariantAction,
|
||
analyzeWeakness: analyzeWeaknessAction,
|
||
}
|
||
}
|
||
|
||
export default async function EditLessonPlanPage({
|
||
params,
|
||
}: {
|
||
params: Promise<{ planId: string }>
|
||
}): Promise<JSX.Element> {
|
||
const { planId } = await params
|
||
const ctx = await getAuthContext()
|
||
|
||
const [plan, teacherClasses] = await Promise.all([
|
||
getLessonPlanById(planId, ctx.userId),
|
||
getTeacherClasses({ teacherId: ctx.userId }),
|
||
])
|
||
if (!plan) notFound()
|
||
|
||
const classes = teacherClasses.map((c) => ({ id: c.id, name: c.name }))
|
||
|
||
// 拉取教材/章节标题用于工具栏显示
|
||
let textbookTitle: string | undefined
|
||
let chapterTitle: string | undefined
|
||
if (plan.textbookId) {
|
||
const textbook = await getTextbookById(plan.textbookId)
|
||
textbookTitle = textbook?.title
|
||
if (plan.chapterId) {
|
||
const chapters = await getChaptersByTextbookId(plan.textbookId)
|
||
const findChapter = (list: typeof chapters): typeof chapters[number] | undefined => {
|
||
for (const ch of list) {
|
||
if (ch.id === plan.chapterId) return ch
|
||
if (ch.children && ch.children.length > 0) {
|
||
const found = findChapter(ch.children as typeof chapters)
|
||
if (found) return found
|
||
}
|
||
}
|
||
return undefined
|
||
}
|
||
const chapter = findChapter(chapters)
|
||
chapterTitle = chapter?.title
|
||
}
|
||
}
|
||
|
||
const aiClientService = createAiClientService()
|
||
|
||
return (
|
||
<AiClientProvider service={aiClientService}>
|
||
<LessonPlanProviderSetup>
|
||
<div className="h-[calc(100vh-4rem)]">
|
||
<Suspense
|
||
fallback={
|
||
<div className="flex h-full items-center justify-center">
|
||
<Skeleton className="h-[80%] w-[80%]" />
|
||
</div>
|
||
}
|
||
>
|
||
<LessonPlanEditor
|
||
planId={plan.id}
|
||
initialTitle={plan.title}
|
||
initialDoc={plan.content}
|
||
initialStatus={plan.status}
|
||
textbookId={plan.textbookId ?? undefined}
|
||
chapterId={plan.chapterId ?? undefined}
|
||
textbookTitle={textbookTitle}
|
||
chapterTitle={chapterTitle}
|
||
classes={classes}
|
||
/>
|
||
</Suspense>
|
||
</div>
|
||
</LessonPlanProviderSetup>
|
||
</AiClientProvider>
|
||
)
|
||
}
|