feat(app): add error/loading boundaries and update dashboard routes

- Add error.tsx and loading.tsx boundaries for admin, parent, student, teacher routes

- Add dashboard-error-fallback and dashboard-loading-skeleton components

- Add student/learning page, parent/leave routes, teacher textbook components

- Update existing app routes across auth, dashboard, and API endpoints

- Update proxy middleware and next-auth type declarations
This commit is contained in:
SpecialX
2026-06-23 17:38:28 +08:00
parent c4d3433cc9
commit 1a9377222c
90 changed files with 1690 additions and 741 deletions

View File

@@ -1,7 +1,7 @@
import { NextResponse } from "next/server"
import { and, desc, eq, like, or, sql } from "drizzle-orm"
import { requireAuth } from "@/shared/lib/auth-guard"
import { getAuthContext } from "@/shared/lib/auth-guard"
import { db } from "@/shared/db"
import {
announcements,
@@ -41,10 +41,12 @@ interface SearchResponse {
/**
* GET /api/search?q=keyword&type=all&page=1
* 全文检索questions / textbooks / exams / announcements
* 按角色过滤:学生只能搜索 textbook 和 announcement
*/
export async function GET(req: Request) {
try {
await requireAuth()
const ctx = await getAuthContext()
const isStudent = ctx.roles.includes("student") && !ctx.roles.includes("admin") && !ctx.roles.includes("teacher")
const { searchParams } = new URL(req.url)
const q = (searchParams.get("q") ?? "").trim()
@@ -72,16 +74,18 @@ export async function GET(req: Request) {
const offset = (page - 1) * pageSize
const results: SearchResultItem[] = []
// 并行查询各类型
// 并行查询各类型(按角色过滤)
const tasks: Promise<SearchResultItem[]>[] = []
if (type === "all" || type === "question") {
// 学生不能搜索题目和考试
if (!isStudent && (type === "all" || type === "question")) {
tasks.push(searchQuestions(kw, pageSize))
}
if (type === "all" || type === "textbook") {
tasks.push(searchTextbooks(kw, pageSize))
}
if (type === "all" || type === "exam") {
// 学生不能搜索考试
if (!isStudent && (type === "all" || type === "exam")) {
tasks.push(searchExams(kw, pageSize))
}
if (type === "all" || type === "announcement") {