Inclui console handoff Desk↔Console (Spec 019), melhorias DNS Viewer (037), OpenPanel/Nextcloud/VM116 deploy notes, contracts stack e sidebar actualizado. Co-authored-by: Cursor <cursoragent@cursor.com>
111 lines
2.8 KiB
JavaScript
111 lines
2.8 KiB
JavaScript
import { useEffect, useState } from 'react'
|
|
import { NavLink, Outlet } from 'react-router-dom'
|
|
import AdminLoginGate from '../components/AdminLoginGate'
|
|
import {
|
|
bootstrapAuthFromUrl,
|
|
clearSession,
|
|
getUser,
|
|
isLoggedIn,
|
|
} from '../lib/auth'
|
|
|
|
const adminNav = [
|
|
{ to: '/admin', label: 'Início', end: true },
|
|
{ to: '/admin/dominio', label: 'Domínio & DNS' },
|
|
]
|
|
|
|
const STAFF_ROLES = new Set([
|
|
'super_admin',
|
|
'ops_lead',
|
|
'technician',
|
|
'noc',
|
|
'seo',
|
|
'devops',
|
|
'developer',
|
|
'sales_admin',
|
|
'sales_support',
|
|
])
|
|
|
|
export default function AdminShell() {
|
|
const [authReady, setAuthReady] = useState(false)
|
|
const [authed, setAuthed] = useState(isLoggedIn())
|
|
const user = getUser()
|
|
const subtitle = user && STAFF_ROLES.has(user.role) ? 'Staff · suporte Ligbox' : 'Gerente de domínio'
|
|
|
|
useEffect(() => {
|
|
let cancelled = false
|
|
bootstrapAuthFromUrl().then((ok) => {
|
|
if (!cancelled) {
|
|
setAuthed(ok)
|
|
setAuthReady(true)
|
|
}
|
|
})
|
|
return () => {
|
|
cancelled = true
|
|
}
|
|
}, [])
|
|
|
|
function handleLoginSuccess() {
|
|
setAuthed(true)
|
|
}
|
|
|
|
function handleLogout() {
|
|
clearSession()
|
|
setAuthed(false)
|
|
}
|
|
|
|
return (
|
|
<div className="shell">
|
|
<aside className="sidebar sidebar-admin">
|
|
<div className="sidebar-brand">
|
|
<div className="sidebar-logo" aria-hidden="true">LB</div>
|
|
<div>
|
|
<h1>Ligbox Console</h1>
|
|
<p className="sidebar-sub">{subtitle}</p>
|
|
</div>
|
|
</div>
|
|
<p className="nav-section-label">Administração</p>
|
|
<nav>
|
|
{adminNav.map((item) => (
|
|
<NavLink
|
|
key={item.to}
|
|
to={item.to}
|
|
end={item.end}
|
|
className={({ isActive }) => `nav-link${isActive ? ' active' : ''}`}
|
|
>
|
|
{item.label}
|
|
</NavLink>
|
|
))}
|
|
</nav>
|
|
<p className="nav-section-label">Staff</p>
|
|
<nav>
|
|
<NavLink to="/" className="nav-link">← Console Ops</NavLink>
|
|
</nav>
|
|
{authed && user ? (
|
|
<p className="status-pill" style={{ marginTop: '1.5rem' }}>
|
|
{user.display_name || user.username}
|
|
{user.role ? ` · ${user.role}` : ''}
|
|
</p>
|
|
) : null}
|
|
{authed ? (
|
|
<button
|
|
type="button"
|
|
className="btn btn-ghost btn-sm"
|
|
style={{ marginTop: '0.5rem' }}
|
|
onClick={handleLogout}
|
|
>
|
|
Sair
|
|
</button>
|
|
) : null}
|
|
</aside>
|
|
<main className="main">
|
|
{!authReady ? (
|
|
<p className="loading">A validar sessão…</p>
|
|
) : !authed ? (
|
|
<AdminLoginGate onSuccess={handleLoginSuccess} />
|
|
) : (
|
|
<Outlet />
|
|
)}
|
|
</main>
|
|
</div>
|
|
)
|
|
}
|