feat(app): add lesson-plans, practice, and grade dashboard routes

- 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
This commit is contained in:
SpecialX
2026-06-24 12:03:47 +08:00
parent 8c2fe14c20
commit 37d2688a28
84 changed files with 2665 additions and 661 deletions

View File

@@ -1,14 +1,18 @@
import type { Metadata } from "next"
import type { JSX } from "react"
import { getTranslations } from "next-intl/server"
import { requirePermission } from "@/shared/lib/auth-guard"
import { Permissions } from "@/shared/types/permissions"
import { AiProviderSettingsCard } from "@/modules/settings/components/ai-provider-settings-card"
import { AiUsageDashboard } from "@/modules/ai/components/ai-usage-dashboard"
export const metadata: Metadata = {
title: "AI 配置 - Next_Edu",
description: "统一管理 AI 服务商、API 密钥与使用统计",
export async function generateMetadata(): Promise<Metadata> {
const t = await getTranslations("ai")
return {
title: `${t("admin.settings.title")} - Next_Edu`,
description: t("admin.settings.description"),
}
}
export const dynamic = "force-dynamic"
@@ -18,25 +22,31 @@ export const dynamic = "force-dynamic"
*
* 作为 AI 模块的独立配置入口,取代:
* - /settings?tab=ai已移除 AI 标签页)
* - 考试页面内嵌的 AI 配置弹窗(已改为跳转链接
* - 考试页面内嵌的 AI 配置弹窗(已移除
*
* 权限:AI_CONFIGURE当前仅 admin 角色拥有)
* 权限规则
* - AI_CHAT 用户均可访问(管理自己的 private provider
* - AI_CONFIGURE 用户(管理员)可额外管理 public provider 与他人 private provider
*/
export default async function AiSettingsPage(): Promise<JSX.Element> {
await requirePermission(Permissions.AI_CONFIGURE)
const t = await getTranslations("ai")
const ctx = await requirePermission(Permissions.AI_CHAT)
const isAdmin = ctx.permissions.includes(Permissions.AI_CONFIGURE)
return (
<div className="flex h-full flex-col gap-8 p-8">
<div className="flex flex-col justify-between gap-4 md:flex-row md:items-center">
<div className="space-y-1">
<h1 className="text-3xl font-bold tracking-tight">AI </h1>
<h1 className="text-3xl font-bold tracking-tight">{t("admin.settings.title")}</h1>
<div className="text-sm text-muted-foreground">
AI API 使
{isAdmin
? t("admin.settings.description")
: t("admin.settings.userDescription")}
</div>
</div>
</div>
<div className="space-y-6">
<AiProviderSettingsCard />
<AiUsageDashboard />
<AiProviderSettingsCard isAdmin={isAdmin} currentUserId={ctx.userId} />
{isAdmin ? <AiUsageDashboard /> : null}
</div>
</div>
)