sync-docs-and-fixes
All checks were successful
CI / build-deploy (push) Successful in 4m39s

This commit is contained in:
SpecialX
2026-03-03 17:32:26 +08:00
parent 538805bad0
commit eb08c0ab68
73 changed files with 2218 additions and 422 deletions

View File

@@ -5,26 +5,21 @@ import { ClassAssignmentsWidget } from "@/modules/classes/components/class-detai
import { ClassTrendsWidget } from "@/modules/classes/components/class-detail/class-trends-widget"
import { ClassHeader } from "@/modules/classes/components/class-detail/class-header"
import { ClassOverviewStats } from "@/modules/classes/components/class-detail/class-overview-stats"
import { ClassQuickActions } from "@/modules/classes/components/class-detail/class-quick-actions"
import { ClassScheduleWidget } from "@/modules/classes/components/class-detail/class-schedule-widget"
import { ClassStudentsWidget } from "@/modules/classes/components/class-detail/class-students-widget"
export const dynamic = "force-dynamic"
type SearchParams = { [key: string]: string | string[] | undefined }
export default async function ClassDetailPage({
params,
searchParams,
}: {
params: Promise<{ id: string }>
searchParams: Promise<SearchParams>
}) {
const { id } = await params
// Parallel data fetching
const [insights, students, schedule] = await Promise.all([
getClassHomeworkInsights({ classId: id, limit: 20 }), // Limit increased to 20 for better list view
getClassHomeworkInsights({ classId: id, limit: 20 }),
getClassStudents({ classId: id }),
getClassSchedule({ classId: id }),
])
@@ -32,7 +27,7 @@ export default async function ClassDetailPage({
if (!insights) return notFound()
// Fetch subject scores
const studentScores = await getClassStudentSubjectScoresV2(id)
const studentScores = await getClassStudentSubjectScoresV2({ classId: id })
// Data mapping for widgets
const assignmentSummaries = insights.assignments.map(a => ({
@@ -91,10 +86,7 @@ export default async function ClassDetailPage({
<div className="grid grid-cols-1 gap-6 lg:grid-cols-3">
{/* Main Content Area (Left 2/3) */}
<div className="space-y-6 lg:col-span-2">
<ClassTrendsWidget
classId={insights.class.id}
assignments={assignmentSummaries}
/>
<ClassTrendsWidget assignments={assignmentSummaries} />
<ClassStudentsWidget
classId={insights.class.id}
students={studentSummaries}

View File

@@ -1,9 +1,5 @@
import { eq } from "drizzle-orm"
import { getTeacherClasses } from "@/modules/classes/data-access"
import { getClassSubjects, getTeacherClasses } from "@/modules/classes/data-access"
import { MyClassesGrid } from "@/modules/classes/components/my-classes-grid"
import { auth } from "@/auth"
import { db } from "@/shared/db"
import { grades } from "@/shared/db/schema"
export const dynamic = "force-dynamic"
@@ -12,11 +8,11 @@ export default function MyClassesPage() {
}
async function MyClassesPageImpl() {
const classes = await getTeacherClasses()
const [classes, subjectOptions] = await Promise.all([getTeacherClasses(), getClassSubjects()])
return (
<div className="flex h-full flex-col space-y-4 p-8">
<MyClassesGrid classes={classes} canCreateClass={false} />
<MyClassesGrid classes={classes} subjectOptions={subjectOptions} />
</div>
)
}

View File

@@ -82,7 +82,6 @@ function StudentsResultsFallback() {
export default async function StudentsPage({ searchParams }: { searchParams: Promise<SearchParams> }) {
const classes = await getTeacherClasses()
const params = await searchParams
// Logic to determine default class (first one available)
const defaultClassId = classes.length > 0 ? classes[0].id : undefined

View File

@@ -1,37 +1,9 @@
import { ExamForm } from "@/modules/exams/components/exam-form"
import {
Breadcrumb,
BreadcrumbItem,
BreadcrumbLink,
BreadcrumbList,
BreadcrumbPage,
BreadcrumbSeparator,
} from "@/shared/components/ui/breadcrumb"
export default function CreateExamPage() {
return (
<div className="flex flex-col space-y-8 p-8 max-w-[1200px] mx-auto">
<div className="space-y-4">
<Breadcrumb>
<BreadcrumbList>
<BreadcrumbItem>
<BreadcrumbLink href="/teacher/exams/all">Exams</BreadcrumbLink>
</BreadcrumbItem>
<BreadcrumbSeparator />
<BreadcrumbItem>
<BreadcrumbPage>Create</BreadcrumbPage>
</BreadcrumbItem>
</BreadcrumbList>
</Breadcrumb>
<div>
<h1 className="text-3xl font-bold tracking-tight">Create Exam</h1>
<p className="text-muted-foreground mt-2">
Set up a new exam draft and choose your assembly method.
</p>
</div>
</div>
<div className="flex justify-center items-center min-h-[calc(100vh-160px)] p-8 max-w-[1200px] mx-auto">
<ExamForm />
</div>
)

View File

@@ -5,7 +5,6 @@ import { HomeworkAssignmentExamContentCard } from "@/modules/homework/components
import { HomeworkAssignmentQuestionErrorOverviewCard } from "@/modules/homework/components/homework-assignment-question-error-overview-card"
import { Badge } from "@/shared/components/ui/badge"
import { Button } from "@/shared/components/ui/button"
import { Card, CardContent, CardHeader, CardTitle } from "@/shared/components/ui/card"
import { formatDate } from "@/shared/lib/utils"
import { ChevronLeft, Users, Calendar, BarChart3, CheckCircle2 } from "lucide-react"

View File

@@ -14,6 +14,7 @@ import { formatDate } from "@/shared/lib/utils"
import { getHomeworkAssignments } from "@/modules/homework/data-access"
import { getTeacherClasses } from "@/modules/classes/data-access"
import { PenTool, PlusCircle } from "lucide-react"
import { getTeacherIdForMutations } from "@/modules/classes/data-access"
export const dynamic = "force-dynamic"
@@ -27,9 +28,10 @@ const getParam = (params: SearchParams, key: string) => {
export default async function AssignmentsPage({ searchParams }: { searchParams: Promise<SearchParams> }) {
const sp = await searchParams
const classId = getParam(sp, "classId") || undefined
const creatorId = await getTeacherIdForMutations()
const [assignments, classes] = await Promise.all([
getHomeworkAssignments({ classId: classId && classId !== "all" ? classId : undefined }),
getHomeworkAssignments({ creatorId, classId: classId && classId !== "all" ? classId : undefined }),
classId && classId !== "all" ? getTeacherClasses() : Promise.resolve([]),
])
const hasAssignments = assignments.length > 0

View File

@@ -23,6 +23,7 @@ async function QuestionBankResults({ searchParams }: { searchParams: Promise<Sea
const q = getParam(params, "q")
const type = getParam(params, "type")
const difficulty = getParam(params, "difficulty")
const knowledgePointId = getParam(params, "kp")
const questionType: QuestionType | undefined =
type === "single_choice" ||
@@ -37,10 +38,16 @@ async function QuestionBankResults({ searchParams }: { searchParams: Promise<Sea
q: q || undefined,
type: questionType,
difficulty: difficulty && difficulty !== "all" ? Number(difficulty) : undefined,
knowledgePointId: knowledgePointId && knowledgePointId !== "all" ? knowledgePointId : undefined,
pageSize: 200,
})
const hasFilters = Boolean(q || (type && type !== "all") || (difficulty && difficulty !== "all"))
const hasFilters = Boolean(
q ||
(type && type !== "all") ||
(difficulty && difficulty !== "all") ||
(knowledgePointId && knowledgePointId !== "all")
)
if (questions.length === 0) {
return (