Add React workflows for experiments, campus registration, student approvals, account activation, document verification, classroom repair, venue displays, and teacher rosters. Wire role-aware navigation while preserving existing API authorization boundaries.
159 lines
8.3 KiB
TypeScript
159 lines
8.3 KiB
TypeScript
import { lazy, Suspense, useEffect } from 'react'
|
|
import { App as AntdApp, ConfigProvider, Skeleton } from 'antd'
|
|
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
|
|
import { BrowserRouter, Navigate, Route, Routes, useLocation, useNavigate } from 'react-router-dom'
|
|
import zhCN from 'antd/locale/zh_CN'
|
|
import { useAuthStore } from '../stores/auth'
|
|
|
|
const LoginPage = lazy(() => import('../pages/LoginPage'))
|
|
const DashboardPage = lazy(() => import('../pages/DashboardPage'))
|
|
const AccountPage = lazy(() => import('../pages/AccountPage'))
|
|
const UsersPage = lazy(() => import('../pages/UsersPage'))
|
|
const NotificationsPage = lazy(() => import('../pages/NotificationsPage'))
|
|
const MyTimetablePage = lazy(() => import('../pages/MyTimetablePage'))
|
|
const ExamsPage = lazy(() => import('../pages/ExamsPage'))
|
|
const StudentGradesPage = lazy(() => import('../pages/StudentGradesPage'))
|
|
const StudentWarningsPage = lazy(() => import('../pages/StudentWarningsPage'))
|
|
const StudentProfilePage = lazy(() => import('../pages/StudentProfilePage'))
|
|
const StudentAttendancePage = lazy(() => import('../pages/StudentAttendancePage'))
|
|
const CourseSelectionPage = lazy(() => import('../pages/CourseSelectionPage'))
|
|
const StudentCurriculumPage = lazy(() => import('../pages/StudentCurriculumPage'))
|
|
const AcademicOverviewPage = lazy(() => import('../pages/AcademicOverviewPage'))
|
|
const FreeClassroomsPage = lazy(() => import('../pages/FreeClassroomsPage'))
|
|
const StudentStatusChangesPage = lazy(() => import('../pages/StudentStatusChangesPage'))
|
|
const OfficialDocumentsPage = lazy(() => import('../pages/OfficialDocumentsPage'))
|
|
const GraduationAuditPage = lazy(() => import('../pages/GraduationAuditPage'))
|
|
const TuitionPage = lazy(() => import('../pages/TuitionPage'))
|
|
const AcademicCalendarPage = lazy(() => import('../pages/AcademicCalendarPage'))
|
|
const OtherExamsPage = lazy(() => import('../pages/OtherExamsPage'))
|
|
const MakeupExamsPage = lazy(() => import('../pages/MakeupExamsPage'))
|
|
const EvaluationsPage = lazy(() => import('../pages/EvaluationsPage'))
|
|
const AcademicPlanningPage = lazy(() => import('../pages/AcademicPlanningPage'))
|
|
const PhysicalFitnessPage = lazy(() => import('../pages/PhysicalFitnessPage'))
|
|
const DegreeAwardsPage = lazy(() => import('../pages/DegreeAwardsPage'))
|
|
const GraduationClearancePage = lazy(() => import('../pages/GraduationClearancePage'))
|
|
const StudentExperimentsPage = lazy(() => import('../pages/StudentExperimentsPage'))
|
|
const StudentExperimentGradesPage = lazy(() => import('../pages/StudentExperimentGradesPage'))
|
|
const CampusRegistrationsPage = lazy(() => import('../pages/CampusRegistrationsPage'))
|
|
const StudentApprovalsPage = lazy(() => import('../pages/StudentApprovalsPage'))
|
|
const TeacherRosterPage = lazy(() => import('../pages/TeacherRosterPage'))
|
|
const AccountActivationPage = lazy(() => import('../pages/AccountActivationPage'))
|
|
const OfficialDocumentVerifyPage = lazy(() => import('../pages/OfficialDocumentVerifyPage'))
|
|
const ClassroomRepairReportPage = lazy(() => import('../pages/ClassroomRepairReportPage'))
|
|
const VenueDisplayPage = lazy(() => import('../pages/VenueDisplayPage'))
|
|
const SsoCallbackPage = lazy(() => import('../pages/SsoCallbackPage'))
|
|
const SsoBindPage = lazy(() => import('../pages/SsoBindPage'))
|
|
const AppShell = lazy(() => import('../layout/AppShell'))
|
|
|
|
const queryClient = new QueryClient({
|
|
defaultOptions: {
|
|
queries: {
|
|
staleTime: 30_000,
|
|
retry: 1,
|
|
refetchOnWindowFocus: false,
|
|
},
|
|
},
|
|
})
|
|
|
|
function ProtectedRoute() {
|
|
const token = useAuthStore((state) => state.token)
|
|
const location = useLocation()
|
|
if (!token) return <Navigate to={`/login?redirect=${encodeURIComponent(location.pathname + location.search)}`} replace />
|
|
return <AppShell />
|
|
}
|
|
|
|
function SessionExpiryBridge() {
|
|
const navigate = useNavigate()
|
|
useEffect(() => {
|
|
const onExpired = () => navigate(`/login?redirect=${encodeURIComponent(location.pathname + location.search + location.hash)}`, { replace: true })
|
|
window.addEventListener('mingxu-session-expired', onExpired)
|
|
return () => window.removeEventListener('mingxu-session-expired', onExpired)
|
|
}, [navigate])
|
|
return null
|
|
}
|
|
|
|
function AppRoutes() {
|
|
return (
|
|
<Suspense fallback={<div style={{ maxWidth: 1180, margin: '64px auto', padding: 24 }}><Skeleton active paragraph={{ rows: 8 }} /></div>}>
|
|
<SessionExpiryBridge />
|
|
<Routes>
|
|
<Route path="/login" element={<LoginPage />} />
|
|
<Route path="/sso/callback" element={<SsoCallbackPage />} />
|
|
<Route path="/sso/bind" element={<SsoBindPage />} />
|
|
<Route path="/activate" element={<AccountActivationPage />} />
|
|
<Route path="/verify/:verificationCode?" element={<OfficialDocumentVerifyPage />} />
|
|
<Route path="/classroom-repairs/report/:classroomId" element={<ClassroomRepairReportPage />} />
|
|
<Route path="/venue-display/:type/:id" element={<VenueDisplayPage />} />
|
|
<Route element={<ProtectedRoute />}>
|
|
<Route path="/dashboard" element={<DashboardPage />} />
|
|
<Route path="/account" element={<AccountPage />} />
|
|
<Route path="/users" element={<UsersPage />} />
|
|
<Route path="/notifications" element={<NotificationsPage />} />
|
|
<Route path="/my-timetable" element={<MyTimetablePage />} />
|
|
<Route path="/exams" element={<ExamsPage />} />
|
|
<Route path="/grades" element={<StudentGradesPage />} />
|
|
<Route path="/warnings" element={<StudentWarningsPage />} />
|
|
<Route path="/my-profile" element={<StudentProfilePage />} />
|
|
<Route path="/my-attendance" element={<StudentAttendancePage />} />
|
|
<Route path="/course-selections" element={<CourseSelectionPage />} />
|
|
<Route path="/my-curriculum" element={<StudentCurriculumPage />} />
|
|
<Route path="/academic-overview" element={<AcademicOverviewPage />} />
|
|
<Route path="/free-classrooms" element={<FreeClassroomsPage />} />
|
|
<Route path="/student-status-changes" element={<StudentStatusChangesPage />} />
|
|
<Route path="/official-documents" element={<OfficialDocumentsPage />} />
|
|
<Route path="/graduation-audits" element={<GraduationAuditPage />} />
|
|
<Route path="/tuition" element={<TuitionPage />} />
|
|
<Route path="/academic-calendar" element={<AcademicCalendarPage />} />
|
|
<Route path="/other-exams" element={<OtherExamsPage />} />
|
|
<Route path="/makeup-exams" element={<MakeupExamsPage />} />
|
|
<Route path="/evaluations" element={<EvaluationsPage />} />
|
|
<Route path="/academic-planning" element={<AcademicPlanningPage />} />
|
|
<Route path="/physical-fitness" element={<PhysicalFitnessPage />} />
|
|
<Route path="/degree-awards" element={<DegreeAwardsPage />} />
|
|
<Route path="/graduation-clearance" element={<GraduationClearancePage />} />
|
|
<Route path="/experiments" element={<StudentExperimentsPage />} />
|
|
<Route path="/experiment-grades" element={<StudentExperimentGradesPage />} />
|
|
<Route path="/campus-registrations" element={<CampusRegistrationsPage />} />
|
|
<Route path="/approvals" element={<StudentApprovalsPage />} />
|
|
<Route path="/teacher-roster" element={<TeacherRosterPage />} />
|
|
<Route path="/" element={<Navigate to="/dashboard" replace />} />
|
|
</Route>
|
|
<Route path="*" element={<Navigate to="/dashboard" replace />} />
|
|
</Routes>
|
|
</Suspense>
|
|
)
|
|
}
|
|
|
|
export default function App() {
|
|
return (
|
|
<ConfigProvider
|
|
locale={zhCN}
|
|
theme={{
|
|
token: {
|
|
colorPrimary: '#284583',
|
|
colorSuccess: '#087f73',
|
|
colorWarning: '#b8781d',
|
|
colorError: '#b42318',
|
|
colorInfo: '#476282',
|
|
colorText: '#182033',
|
|
colorBgLayout: '#f2f4f7',
|
|
borderRadius: 7,
|
|
fontFamily: '"Microsoft YaHei", "PingFang SC", "Noto Sans CJK SC", sans-serif',
|
|
},
|
|
components: {
|
|
Button: { controlHeightLG: 44, fontWeight: 650 },
|
|
Input: { controlHeightLG: 44 },
|
|
Layout: { headerBg: '#ffffff', siderBg: '#162858' },
|
|
Menu: { darkItemBg: '#162858', darkItemSelectedBg: '#284583', darkItemSelectedColor: '#ffffff' },
|
|
},
|
|
}}
|
|
>
|
|
<AntdApp>
|
|
<QueryClientProvider client={queryClient}>
|
|
<BrowserRouter basename="/next"><AppRoutes /></BrowserRouter>
|
|
</QueryClientProvider>
|
|
</AntdApp>
|
|
</ConfigProvider>
|
|
)
|
|
}
|