portal/src/App.tsx

70 lines
3.1 KiB
TypeScript
Raw 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 { BrowserRouter, Routes, Route, Navigate, useParams } from 'react-router-dom'
import { AuthGate } from './components/AuthGate'
import { Login } from './pages/Login'
import { Register } from './pages/Register'
import { Dashboard } from './pages/Dashboard'
import { AppFrame } from './pages/AppFrame'
import { AdminUsers } from './pages/AdminUsers'
import { AdminRoles } from './pages/AdminRoles'
import { AdminDepartments } from './pages/AdminDepartments'
import { AdminMonitor } from './pages/AdminMonitor'
import { AdminSettings } from './pages/AdminSettings'
import type { User } from './types'
// Force full remount of AppFrame when slug changes so initialSrc recomputes
function AppFrameKeyed({ user }: { user: User }) {
const { slug } = useParams<{ slug: string }>()
return <AppFrame key={slug} user={user} />
}
// The portal is only ever the top-level frame. If it finds itself loaded inside
// an iframe, it means an app's path fell back to the portal (that app isn't
// deployed or routed in NPM yet) — so render a placeholder instead of recursing
// the whole shell endlessly inside itself.
function EmbeddedFallback() {
return (
<div style={{
height: '100dvh', display: 'flex', alignItems: 'center', justifyContent: 'center',
textAlign: 'center', padding: '2rem', background: 'var(--navy-dark)', color: 'var(--text-muted)',
}}>
<div>
<div style={{ fontSize: '2.5rem', marginBottom: '0.75rem' }}>🚧</div>
<p style={{ fontSize: '1rem', color: 'var(--text)', marginBottom: '0.35rem' }}>
This app isnt available yet
</p>
<p style={{ fontSize: '0.85rem' }}>
It hasnt been deployed or routed on this site.
</p>
</div>
</div>
)
}
export default function App() {
if (window.self !== window.top) return <EmbeddedFallback />
return (
<BrowserRouter>
<Routes>
<Route path="/login" element={<Login />} />
<Route path="/register" element={<Register />} />
<Route path="/*" element={
<AuthGate>
{user => (
<Routes>
<Route path="/" element={<Dashboard user={user} />} />
<Route path="/app/:slug" element={<AppFrameKeyed user={user} />} />
<Route path="/admin/users" element={user.is_admin ? <AdminUsers user={user} /> : <Navigate to="/" />} />
<Route path="/admin/roles" element={user.is_admin ? <AdminRoles user={user} /> : <Navigate to="/" />} />
<Route path="/admin/departments" element={user.is_admin ? <AdminDepartments user={user} /> : <Navigate to="/" />} />
<Route path="/admin/monitor" element={user.is_admin ? <AdminMonitor user={user} /> : <Navigate to="/" />} />
<Route path="/admin/settings" element={user.is_admin ? <AdminSettings user={user} /> : <Navigate to="/" />} />
<Route path="*" element={<Navigate to="/" />} />
</Routes>
)}
</AuthGate>
} />
</Routes>
</BrowserRouter>
)
}