fix admin exit not redirecting, my posts crash, add error boundary

This commit is contained in:
2026-03-21 20:19:06 +02:00
parent acc71217a8
commit 5a2d26f62f
2 changed files with 32 additions and 10 deletions

View File

@@ -1,4 +1,4 @@
import { useState, useEffect, useRef } from 'react'
import { useState, useEffect, useRef, Component, type ReactNode, type ErrorInfo } from 'react'
import { BrowserRouter, Routes, Route, useLocation, useNavigate, Link } from 'react-router-dom'
import { IconShieldCheck, IconArrowRight, IconX, IconSearch, IconChevronUp, IconMessageCircle, IconBug, IconBulb, IconCommand, IconArrowNarrowRight } from '@tabler/icons-react'
import { AuthProvider, useAuthState } from './hooks/useAuth'
@@ -45,6 +45,26 @@ import ProfilePage from './pages/ProfilePage'
import RecoverPage from './pages/RecoverPage'
import EmbedBoard from './pages/EmbedBoard'
import { api } from './lib/api'
class ErrorBoundary extends Component<{ children: ReactNode }, { error: Error | null }> {
state = { error: null as Error | null }
static getDerivedStateFromError(error: Error) { return { error } }
componentDidCatch(error: Error, info: ErrorInfo) { console.error('React error:', error, info) }
render() {
if (this.state.error) {
return (
<div style={{ minHeight: '100vh', display: 'flex', alignItems: 'center', justifyContent: 'center', background: 'var(--bg)', padding: 24 }}>
<div style={{ textAlign: 'center', maxWidth: 400 }}>
<h1 style={{ fontFamily: 'var(--font-heading)', fontSize: '1.5rem', color: 'var(--text)', marginBottom: 8 }}>Something went wrong</h1>
<p style={{ color: 'var(--text-secondary)', fontSize: '0.875rem', marginBottom: 16 }}>{this.state.error.message}</p>
<button onClick={() => { this.setState({ error: null }); window.location.href = '/' }} className="btn btn-primary">Go home</button>
</div>
</div>
)
}
return this.props.children
}
}
import BoardIcon from './components/BoardIcon'
import StatusBadge from './components/StatusBadge'
import Avatar from './components/Avatar'
@@ -348,16 +368,16 @@ function SearchPage() {
}
function RequireAdmin({ children }: { children: React.ReactNode }) {
const [ok, setOk] = useState<boolean | null>(null)
const admin = useAdmin()
const nav = useNavigate()
useEffect(() => {
api.get('/admin/boards')
.then(() => setOk(true))
.catch(() => nav('/admin/login', { replace: true }))
}, [nav])
if (!admin.loading && !admin.isAdmin) {
nav('/', { replace: true })
}
}, [admin.loading, admin.isAdmin, nav])
if (!ok) return null
if (admin.loading || !admin.isAdmin) return null
return <>{children}</>
}
@@ -407,7 +427,7 @@ function AdminBanner() {
Admin panel <IconArrowRight size={11} stroke={2.5} />
</Link>
<button
onClick={admin.exitAdminMode}
onClick={() => { admin.exitAdminMode(); window.location.href = '/' }}
className="inline-flex items-center gap-1 px-2.5 py-1 rounded"
style={{
color: 'var(--text-tertiary)',
@@ -545,6 +565,7 @@ export default function App() {
}, [auth.user?.isPasskeyUser])
return (
<ErrorBoundary>
<BrandingProvider>
<ToastProvider>
<ConfirmProvider>
@@ -565,5 +586,6 @@ export default function App() {
</ConfirmProvider>
</ToastProvider>
</BrandingProvider>
</ErrorBoundary>
)
}

View File

@@ -41,8 +41,8 @@ export default function MySubmissions() {
const [loading, setLoading] = useState(true)
useEffect(() => {
api.get<Post[]>('/me/posts')
.then(setPosts)
api.get<{ posts: Post[] }>('/me/posts')
.then((r) => setPosts(r.posts ?? []))
.catch(() => {})
.finally(() => setLoading(false))
}, [])