sync-docs-and-fixes

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

@@ -15,10 +15,8 @@ type Stat = {
}
export function StudentStatsGrid({
enrolledClassCount,
dueSoonCount,
overdueCount,
gradedCount,
ranking,
}: {
enrolledClassCount: number

View File

@@ -7,8 +7,6 @@ import { EmptyState } from "@/shared/components/ui/empty-state"
import type { TeacherClass } from "@/modules/classes/types"
export function TeacherClassesCard({ classes }: { classes: TeacherClass[] }) {
const totalStudents = classes.reduce((sum, c) => sum + (c.studentCount ?? 0), 0)
return (
<Card>
<CardHeader className="flex flex-row items-center justify-between">

View File

@@ -14,7 +14,6 @@ const toWeekday = (d: Date): 1 | 2 | 3 | 4 | 5 | 6 | 7 => {
}
export function TeacherDashboardView({ data }: { data: TeacherDashboardData }) {
const totalStudents = data.classes.reduce((sum, c) => sum + (c.studentCount ?? 0), 0)
const todayWeekday = toWeekday(new Date())
const classNameById = new Map(data.classes.map((c) => [c.id, c.name] as const))

View File

@@ -1,7 +1,7 @@
import "server-only"
import { cache } from "react"
import { count, desc, eq, gt } from "drizzle-orm"
import { count, desc, eq, gt, inArray } from "drizzle-orm"
import { db } from "@/shared/db"
import {
@@ -11,9 +11,11 @@ import {
homeworkAssignments,
homeworkSubmissions,
questions,
roles,
sessions,
textbooks,
users,
usersToRoles,
} from "@/shared/db/schema"
import type { AdminDashboardData } from "./types"
@@ -23,7 +25,7 @@ export const getAdminDashboardData = cache(async (): Promise<AdminDashboardData>
const [
activeSessionsRow,
userCountRow,
userRoleRows,
userRoleCountRows,
classCountRow,
textbookCountRow,
chapterCountRow,
@@ -37,7 +39,11 @@ export const getAdminDashboardData = cache(async (): Promise<AdminDashboardData>
] = await Promise.all([
db.select({ value: count() }).from(sessions).where(gt(sessions.expires, now)),
db.select({ value: count() }).from(users),
db.select({ role: users.role, value: count() }).from(users).groupBy(users.role),
db
.select({ role: roles.name, value: count() })
.from(usersToRoles)
.innerJoin(roles, eq(usersToRoles.roleId, roles.id))
.groupBy(roles.name),
db.select({ value: count() }).from(classes),
db.select({ value: count() }).from(textbooks),
db.select({ value: count() }).from(chapters),
@@ -52,7 +58,6 @@ export const getAdminDashboardData = cache(async (): Promise<AdminDashboardData>
id: users.id,
name: users.name,
email: users.email,
role: users.role,
createdAt: users.createdAt,
})
.from(users)
@@ -72,17 +77,55 @@ export const getAdminDashboardData = cache(async (): Promise<AdminDashboardData>
const homeworkSubmissionCount = Number(homeworkSubmissionCountRow[0]?.value ?? 0)
const homeworkSubmissionToGradeCount = Number(homeworkSubmissionToGradeCountRow[0]?.value ?? 0)
const userRoleCounts = userRoleRows
const userRoleCounts = userRoleCountRows
.map((r) => ({ role: r.role ?? "unknown", count: Number(r.value ?? 0) }))
.sort((a, b) => b.count - a.count)
const recentUsers = recentUserRows.map((u) => ({
id: u.id,
name: u.name,
email: u.email,
role: u.role,
createdAt: u.createdAt.toISOString(),
}))
const normalizeRole = (value: string) => {
const role = value.trim().toLowerCase()
if (role === "grade_head" || role === "teaching_head") return "teacher"
if (role === "admin" || role === "student" || role === "teacher" || role === "parent") return role
return ""
}
const recentUserIds = recentUserRows.map((u) => u.id)
const recentRoleRows = recentUserIds.length
? await db
.select({
userId: usersToRoles.userId,
roleName: roles.name,
})
.from(usersToRoles)
.innerJoin(roles, eq(usersToRoles.roleId, roles.id))
.where(inArray(usersToRoles.userId, recentUserIds))
: []
const rolesByUserId = new Map<string, string[]>()
for (const row of recentRoleRows) {
const list = rolesByUserId.get(row.userId) ?? []
list.push(row.roleName)
rolesByUserId.set(row.userId, list)
}
const resolvePrimaryRole = (roleNames: string[]) => {
const mapped = roleNames.map(normalizeRole).filter(Boolean)
if (mapped.includes("admin")) return "admin"
if (mapped.includes("teacher")) return "teacher"
if (mapped.includes("parent")) return "parent"
if (mapped.includes("student")) return "student"
return "student"
}
const recentUsers = recentUserRows.map((u) => {
const roleNames = rolesByUserId.get(u.id) ?? []
return {
id: u.id,
name: u.name,
email: u.email,
role: resolvePrimaryRole(roleNames),
createdAt: u.createdAt.toISOString(),
}
})
return {
activeSessionsCount,