Spaces:
Running
Running
File size: 3,018 Bytes
56c2a52 01916fc c8c6034 fbe1c8a 56c2a52 c8c6034 01916fc c8c6034 56c2a52 c8c6034 56c2a52 c8c6034 56c2a52 c8c6034 01916fc e8f612b 01916fc c8c6034 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 | // web/src/App.tsx - 教师专用版本(ClareCourseWare)
import React, { useState, useEffect, ErrorInfo, Component } from "react";
import { LoginScreen } from "./components/LoginScreen";
import { TeacherDashboard } from "./components/TeacherDashboard";
import { Toaster } from "./components/ui/sonner";
export interface User {
name: string;
email: string;
}
// ✅ Error Boundary for catching React errors
class ErrorBoundary extends Component<
{ children: React.ReactNode },
{ hasError: boolean; error: Error | null }
> {
constructor(props: { children: React.ReactNode }) {
super(props);
this.state = { hasError: false, error: null };
}
static getDerivedStateFromError(error: Error) {
return { hasError: true, error };
}
componentDidCatch(error: Error, errorInfo: ErrorInfo) {
console.error("React Error:", error, errorInfo);
}
render() {
if (this.state.hasError) {
return (
<div className="fixed inset-0 flex items-center justify-center bg-background p-4">
<div className="max-w-md text-center">
<h1 className="text-2xl font-bold text-destructive mb-4">应用错误</h1>
<p className="text-muted-foreground mb-4">
{this.state.error?.message || "未知错误"}
</p>
<button
onClick={() => {
this.setState({ hasError: false, error: null });
window.location.reload();
}}
className="px-4 py-2 bg-primary text-primary-foreground rounded-md"
>
重新加载
</button>
</div>
</div>
);
}
return this.props.children;
}
}
// ✅ localStorage helpers for user profile
function profileStorageKey(email: string) {
return `teacher_profile::${email}`;
}
function hydrateUserFromStorage(base: User): User {
try {
const raw = localStorage.getItem(profileStorageKey(base.email));
if (!raw) return base;
const saved = JSON.parse(raw) as Partial<User>;
return { ...base, ...saved };
} catch {
return base;
}
}
function App() {
const [user, setUser] = useState<User | null>(null);
// ✅ persist user profile whenever it changes (per-email)
useEffect(() => {
if (!user?.email) return;
try {
localStorage.setItem(profileStorageKey(user.email), JSON.stringify(user));
} catch {
// ignore
}
}, [user]);
// ✅ 教师专用:登录后直接进入 TeacherDashboard
const handleLogin = (newUser: User) => {
const hydrated = hydrateUserFromStorage(newUser);
setUser(hydrated);
};
if (!user) return <LoginScreen onLogin={handleLogin} />;
// ✅ 教师专用:只显示 TeacherDashboard,移除所有学生界面
return (
<ErrorBoundary>
<div className="fixed inset-0 w-full bg-background overflow-hidden">
<Toaster />
<TeacherDashboard user={user} onBack={() => setUser(null)} />
</div>
</ErrorBoundary>
);
}
export default App;
|