70 lines
3.1 KiB
TypeScript
70 lines
3.1 KiB
TypeScript
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 isn’t available yet
|
||
</p>
|
||
<p style={{ fontSize: '0.85rem' }}>
|
||
It hasn’t 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>
|
||
)
|
||
}
|