Inicial
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
import { useLayoutEffect } from 'react'
|
||||
import { Outlet, NavLink, useNavigate, useLocation } from 'react-router'
|
||||
import { useLogout } from '@/hooks/useAuth'
|
||||
import { useAuthStore } from '@/store/authStore'
|
||||
import { queryClient } from '@/lib/queryClient'
|
||||
|
||||
const nav = [
|
||||
{ to: '/app', label: 'Dashboard', end: true },
|
||||
{ to: '/app/work-orders', label: 'Ordens de Trabalho' },
|
||||
{ to: '/app/clients', label: 'Clientes' },
|
||||
{ to: '/app/catalog', label: 'Catálogo' },
|
||||
{ to: '/app/staff', label: 'Técnicos' },
|
||||
{ to: '/app/expenses', label: 'Despesas' },
|
||||
{ to: '/app/invoices', label: 'Faturação' },
|
||||
{ to: '/app/settings', label: 'Definições' },
|
||||
]
|
||||
|
||||
export default function AppLayout() {
|
||||
const { mutate: logout } = useLogout()
|
||||
const { previousSession, restoreSession, user, impersonateTenant } = useAuthStore()
|
||||
const navigate = useNavigate()
|
||||
const location = useLocation()
|
||||
|
||||
useLayoutEffect(() => {
|
||||
const imp = location.state?.impersonation
|
||||
if (imp) {
|
||||
impersonateTenant(imp.token, imp.user)
|
||||
queryClient.clear()
|
||||
// Remove impersonation payload from history state so back/forward doesn't re-apply it
|
||||
navigate(location.pathname, { replace: true, state: {} })
|
||||
}
|
||||
}, []) // eslint-disable-line react-hooks/exhaustive-deps
|
||||
|
||||
function handleRestore() {
|
||||
restoreSession()
|
||||
navigate('/admin')
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex flex-col h-screen bg-slate-950">
|
||||
{previousSession && (
|
||||
<div className="flex items-center justify-between px-4 py-2 bg-amber-500 text-amber-950 text-sm font-medium shrink-0">
|
||||
<span>
|
||||
TechXCar Admin — a gerir: <strong>{user?.name}</strong>
|
||||
</span>
|
||||
<button
|
||||
onClick={handleRestore}
|
||||
className="px-3 py-1 rounded bg-amber-950 text-amber-100 hover:bg-amber-900 text-xs font-semibold transition-colors"
|
||||
>
|
||||
← Voltar ao painel
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
<div className="flex flex-1 overflow-hidden">
|
||||
<aside className="w-64 bg-slate-900 border-r border-slate-800 flex flex-col">
|
||||
<div className="p-4 border-b border-slate-800">
|
||||
<h1 className="text-lg font-bold text-white">TechXCar</h1>
|
||||
<p className="text-xs text-slate-400 mt-0.5">Gestão de Oficina</p>
|
||||
</div>
|
||||
<nav className="flex-1 p-3 space-y-1">
|
||||
{nav.map(({ to, label, end }) => (
|
||||
<NavLink
|
||||
key={to}
|
||||
to={to}
|
||||
end={end}
|
||||
className={({ isActive }) =>
|
||||
`flex items-center px-3 py-2 rounded-md text-sm transition-colors ${
|
||||
isActive
|
||||
? 'bg-slate-700 text-white'
|
||||
: 'text-slate-400 hover:bg-slate-800 hover:text-white'
|
||||
}`
|
||||
}
|
||||
>
|
||||
{label}
|
||||
</NavLink>
|
||||
))}
|
||||
</nav>
|
||||
<div className="p-3 border-t border-slate-800">
|
||||
<button
|
||||
onClick={() => logout()}
|
||||
className="w-full text-left px-3 py-2 text-sm text-slate-400 hover:text-white rounded-md hover:bg-slate-800 transition-colors"
|
||||
>
|
||||
Terminar sessão
|
||||
</button>
|
||||
</div>
|
||||
</aside>
|
||||
<main className="flex-1 overflow-auto p-6 text-white">
|
||||
<Outlet />
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user