claudqunwang's picture
feat: 添加国际化、文件上传和专属聊天页面
e8f612b
// 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;