Twilioapp-admin/components/ErrorBoundary.tsx
mars 1ba859196a 修复退出登录重定向问题和相关功能优化
- 修复DashboardLayout中的退出登录函数,确保清除所有认证信息
- 恢复_app.tsx中的认证逻辑,确保仪表盘页面需要登录访问
- 完善退出登录流程:清除本地存储 -> 调用登出API -> 重定向到登录页面
- 添加错误边界组件提升用户体验
- 优化React水合错误处理
- 添加JWT令牌验证API
- 完善各个仪表盘页面的功能和样式
2025-07-03 20:56:17 +08:00

99 lines
3.6 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React, { Component, ErrorInfo, ReactNode } from 'react';
import { hydrationDebug } from '../utils/hydrationDebug';
interface Props {
children: ReactNode;
fallback?: ReactNode;
}
interface State {
hasError: boolean;
error?: Error;
}
class ErrorBoundary extends Component<Props, State> {
public state: State = {
hasError: false,
};
public static getDerivedStateFromError(error: Error): State {
// 更新 state 使下一次渲染能够显示降级后的 UI
return { hasError: true, error };
}
public componentDidCatch(error: Error, errorInfo: ErrorInfo) {
console.error('ErrorBoundary caught an error:', error, errorInfo);
// 使用调试工具记录水合错误
if (error.message.includes('hydration') || error.message.includes('Hydration')) {
hydrationDebug.logHydrationError(error, 'ErrorBoundary');
}
}
public render() {
if (this.state.hasError) {
// 自定义降级 UI
if (this.props.fallback) {
return this.props.fallback;
}
return (
<div className="min-h-screen flex items-center justify-center bg-gray-50">
<div className="max-w-md w-full bg-white shadow-lg rounded-lg p-6">
<div className="flex items-center mb-4">
<div className="flex-shrink-0">
<svg className="h-8 w-8 text-red-500" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.732-.833-2.5 0L4.268 18.5c-.77.833.192 2.5 1.732 2.5z" />
</svg>
</div>
<div className="ml-3">
<h3 className="text-lg font-medium text-gray-900">
</h3>
</div>
</div>
<div className="mb-4">
<p className="text-sm text-gray-600">
</p>
{this.state.error?.message.includes('hydration') && (
<p className="text-sm text-amber-600 mt-2">
</p>
)}
</div>
<div className="flex space-x-3">
<button
onClick={() => window.location.reload()}
className="flex-1 bg-blue-600 text-white px-4 py-2 rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500"
>
</button>
<button
onClick={() => window.history.back()}
className="flex-1 bg-gray-300 text-gray-700 px-4 py-2 rounded-md hover:bg-gray-400 focus:outline-none focus:ring-2 focus:ring-gray-500"
>
</button>
</div>
{process.env.NODE_ENV === 'development' && this.state.error && (
<details className="mt-4">
<summary className="text-sm text-gray-500 cursor-pointer">
</summary>
<pre className="mt-2 text-xs text-red-600 bg-red-50 p-2 rounded overflow-auto">
{this.state.error.message}
{this.state.error.stack}
</pre>
</details>
)}
</div>
</div>
);
}
return this.props.children;
}
}
export default ErrorBoundary;