refactor(modules): update existing module implementations across attendance, audit, auth, classes, course-plans, exams, files, homework, layout, proctoring, questions, scheduling, textbooks, users

- Update attendance components and data-access for record management

- Update audit log views, filters, and data-access

- Update auth login and register forms

- Update classes actions, components, and data-access (admin, schedule, stats)

- Update course-plans actions, form, list, progress, and schema

- Update exams actions, AI pipeline, preview components, and hooks

- Update files components (icon, list, preview, upload) and data-access

- Update homework assignment form, review view, auto-save hook, and stats-service

- Update layout sidebar, header, and navigation config

- Update proctoring actions, anti-cheat monitor, and data-access

- Update questions actions, components (dialog, actions, columns, filters), and data-access

- Update scheduling actions, auto-scheduler, components, and schema

- Update textbooks constants and text-selection hook

- Update users class-registration, import-dialog, data-access, and user-service
This commit is contained in:
SpecialX
2026-06-23 17:38:56 +08:00
parent 1a9377222c
commit 4f0ef217a0
56 changed files with 1251 additions and 850 deletions

View File

@@ -11,13 +11,6 @@ import {
CollapsibleTrigger,
} from "@/shared/components/ui/collapsible"
import { ScrollArea } from "@/shared/components/ui/scroll-area"
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/shared/components/ui/select"
import {
Tooltip,
TooltipContent,
@@ -35,11 +28,13 @@ interface AppSidebarProps {
}
export function AppSidebar({ mode }: AppSidebarProps) {
const { expanded, toggleSidebar, isMobile, currentRole, setCurrentRole } = useSidebar()
const { expanded, toggleSidebar, isMobile } = useSidebar()
const pathname = usePathname()
const { permissions, roles, hasRole } = usePermission()
const { permissions, hasRole } = usePermission()
// 自动检测当前角色(优先级 admin > student > parent > teacher
// 注意grade_head / teaching_head 统一归入 teacher因为 teacher 导航已通过
// 权限点GRADE_MANAGE 等)动态显示班主任专属功能,无需切换角色。
function detectAutoRole(): Role {
if (hasRole("admin")) return "admin"
if (hasRole("student")) return "student"
@@ -47,14 +42,7 @@ export function AppSidebar({ mode }: AppSidebarProps) {
return "teacher"
}
// 用户在 NAV_CONFIG 中实际可用的角色(过滤掉未配置的角色)
const availableRoles = roles.filter((r) => NAV_CONFIG[r] !== undefined)
// 如果 context 中有 currentRole 且用户拥有该角色,使用 currentRole否则自动检测
const effectiveRole: Role =
currentRole !== null && availableRoles.includes(currentRole)
? currentRole
: detectAutoRole()
const effectiveRole: Role = detectAutoRole()
const allNavItems = NAV_CONFIG[effectiveRole] ?? NAV_CONFIG.teacher ?? []
@@ -71,7 +59,7 @@ export function AppSidebar({ mode }: AppSidebarProps) {
}))
// Ensure consistent state for hydration
if (!expanded && mode === 'mobile') return null
if (!expanded && mode === 'mobile') return null
return (
<div className="flex h-full flex-col gap-4">
@@ -179,26 +167,12 @@ export function AppSidebar({ mode }: AppSidebarProps) {
{/* Sidebar Footer */}
<div className="p-4">
{availableRoles.length > 1 && (expanded || isMobile) && (
<div className="px-2 pb-2">
<Select value={effectiveRole} onValueChange={(v) => setCurrentRole(v as Role)}>
<SelectTrigger className="w-full">
<SelectValue placeholder="切换角色" />
</SelectTrigger>
<SelectContent>
{availableRoles.map((r) => (
<SelectItem key={r} value={r}>{r}</SelectItem>
))}
</SelectContent>
</Select>
</div>
)}
{!isMobile && (
<button
onClick={toggleSidebar}
className="hover:bg-sidebar-accent text-sidebar-foreground flex w-full items-center justify-center rounded-md border p-2 text-sm transition-colors"
>
{expanded ? "收起" : <ChevronRight className="size-4" />}
{expanded ? "收起" : <ChevronRight className="size-4" />}
</button>
)}
</div>

View File

@@ -9,15 +9,12 @@ import {
SheetTitle,
} from "@/shared/components/ui/sheet"
import { cn } from "@/shared/lib/utils"
import type { Role } from "@/shared/types/permissions"
type SidebarContextType = {
expanded: boolean
setExpanded: (expanded: boolean) => void
isMobile: boolean
toggleSidebar: () => void
currentRole: Role | null
setCurrentRole: (role: Role | null) => void
}
const SidebarContext = React.createContext<SidebarContextType | undefined>(
@@ -41,8 +38,6 @@ export function SidebarProvider({ children, sidebar }: SidebarProviderProps) {
const [expanded, setExpanded] = React.useState(true)
const [isMobile, setIsMobile] = React.useState(false)
const [openMobile, setOpenMobile] = React.useState(false)
// null 表示自动检测(按现有优先级 admin > student > parent > teacher
const [currentRole, setCurrentRole] = React.useState<Role | null>(null)
React.useEffect(() => {
const checkMobile = () => {
@@ -67,7 +62,7 @@ export function SidebarProvider({ children, sidebar }: SidebarProviderProps) {
return (
<SidebarContext.Provider
value={{ expanded, setExpanded, isMobile, toggleSidebar, currentRole, setCurrentRole }}
value={{ expanded, setExpanded, isMobile, toggleSidebar }}
>
<div className="flex h-screen overflow-hidden w-full flex-col md:flex-row bg-background">
{/* Mobile Trigger & Sheet */}

View File

@@ -27,7 +27,7 @@ import {
} from "@/shared/components/ui/dropdown-menu"
import { GlobalSearch } from "@/shared/components/global-search"
import { NotificationDropdown } from "@/modules/messaging/components/notification-dropdown"
import { NotificationDropdown } from "@/modules/notifications/components/notification-dropdown"
import { useSidebar } from "./sidebar-provider"
import { NAV_CONFIG } from "../config/navigation"

View File

@@ -21,6 +21,7 @@ import {
BookMarked,
BookCopy,
Files,
BookX,
} from "lucide-react"
import type { LucideIcon } from "lucide-react"
import { Permissions } from "@/shared/types/permissions"
@@ -127,6 +128,12 @@ export const NAV_CONFIG: Partial<Record<Role, NavItem[]>> = {
href: "/admin/files",
permission: Permissions.FILE_READ,
},
{
title: "错题分析",
icon: BookX,
href: "/admin/error-book",
permission: Permissions.ERROR_BOOK_ANALYTICS_READ,
},
{
title: "Audit Logs",
icon: ScrollText,
@@ -242,6 +249,12 @@ export const NAV_CONFIG: Partial<Record<Role, NavItem[]>> = {
href: "/teacher/diagnostic",
permission: Permissions.DIAGNOSTIC_READ,
},
{
title: "错题分析",
icon: BookX,
href: "/teacher/error-book",
permission: Permissions.ERROR_BOOK_ANALYTICS_READ,
},
{
title: "选修课",
icon: BookMarked,
@@ -297,6 +310,12 @@ export const NAV_CONFIG: Partial<Record<Role, NavItem[]>> = {
{ title: "成绩分析", href: "/teacher/grades/analytics", permission: Permissions.GRADE_RECORD_READ },
]
},
{
title: "错题分析",
icon: BookX,
href: "/teacher/error-book",
permission: Permissions.ERROR_BOOK_ANALYTICS_READ,
},
...COMMON_NAV_ITEMS,
],
teaching_head: [
@@ -336,6 +355,12 @@ export const NAV_CONFIG: Partial<Record<Role, NavItem[]>> = {
{ title: "成绩分析", href: "/teacher/grades/analytics", permission: Permissions.GRADE_RECORD_READ },
]
},
{
title: "错题分析",
icon: BookX,
href: "/teacher/error-book",
permission: Permissions.ERROR_BOOK_ANALYTICS_READ,
},
...COMMON_NAV_ITEMS,
],
student: [
@@ -379,6 +404,12 @@ export const NAV_CONFIG: Partial<Record<Role, NavItem[]>> = {
href: "/student/diagnostic",
permission: Permissions.DIAGNOSTIC_READ,
},
{
title: "错题本",
icon: BookX,
href: "/student/error-book",
permission: Permissions.ERROR_BOOK_READ,
},
{
title: "Electives",
icon: BookMarked,
@@ -405,6 +436,12 @@ export const NAV_CONFIG: Partial<Record<Role, NavItem[]>> = {
href: "/parent/attendance",
permission: Permissions.ATTENDANCE_READ,
},
{
title: "错题本",
icon: BookX,
href: "/parent/error-book",
permission: Permissions.ERROR_BOOK_READ,
},
{
title: "Leave Request",
icon: CalendarRange,