feat: refine OT flow, reports, theming and sidebar UX
This commit is contained in:
@@ -1,53 +1,115 @@
|
||||
import { useMemo, useState } from 'react'
|
||||
import { Outlet, NavLink } from 'react-router'
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import { useLogout } from '@/hooks/useAuth'
|
||||
import { useTheme } from '@/hooks/useTheme'
|
||||
import { apiFetch } from '@/lib/api'
|
||||
import type { PlatformSettings } from '@/lib/types'
|
||||
|
||||
export default function AdminLayout() {
|
||||
const { mutate: logout } = useLogout()
|
||||
const { theme, toggleTheme } = useTheme('ui_theme', 'dark')
|
||||
const [logoBroken, setLogoBroken] = useState(false)
|
||||
const { data: settings } = useQuery<PlatformSettings>({
|
||||
queryKey: ['admin', 'settings'],
|
||||
queryFn: () => apiFetch<PlatformSettings>('/admin/settings'),
|
||||
})
|
||||
const primary = settings?.admin_primary_color || '#2563eb'
|
||||
const accent = settings?.admin_accent_color || '#0ea5e9'
|
||||
const isLight = theme === 'light'
|
||||
const shellStyle = useMemo(
|
||||
() => ({
|
||||
backgroundColor: 'var(--ui-bg)',
|
||||
color: 'var(--ui-text)',
|
||||
['--brand-primary' as string]: primary,
|
||||
['--brand-accent' as string]: accent,
|
||||
}),
|
||||
[primary, accent]
|
||||
)
|
||||
const asideStyle = useMemo(
|
||||
() => ({
|
||||
backgroundColor: 'var(--ui-sidebar)',
|
||||
borderColor: 'var(--ui-border)',
|
||||
}),
|
||||
[]
|
||||
)
|
||||
const mainStyle = useMemo(
|
||||
() => ({
|
||||
background: isLight
|
||||
? 'linear-gradient(180deg, #f8fbff 0%, #eef4fb 100%)'
|
||||
: 'linear-gradient(180deg, #0b1530 0%, #050b18 100%)',
|
||||
}),
|
||||
[isLight]
|
||||
)
|
||||
|
||||
return (
|
||||
<div className="flex h-screen bg-slate-950">
|
||||
<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">Administração</p>
|
||||
<div className="flex h-screen" style={shellStyle}>
|
||||
<aside className="w-64 border-r flex flex-col" style={asideStyle}>
|
||||
<div className="p-4 border-b" style={{ borderColor: 'var(--ui-border)' }}>
|
||||
{settings?.platform_logo && !logoBroken ? (
|
||||
<div className="mb-2 flex h-14 w-14 items-center justify-center rounded-md border bg-white p-1" style={{ borderColor: 'var(--ui-border)' }}>
|
||||
<img
|
||||
src={settings.platform_logo}
|
||||
alt=""
|
||||
className="h-full w-full object-contain"
|
||||
onError={() => setLogoBroken(true)}
|
||||
referrerPolicy="no-referrer"
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<div className="mb-2 h-14 w-14 rounded-md flex items-center justify-center text-sm font-semibold" style={{ backgroundColor: `${accent}22`, color: primary }}>
|
||||
TX
|
||||
</div>
|
||||
)}
|
||||
<h1 className="text-lg font-bold text-[var(--ui-text)]">{settings?.platform_name || 'TechXCar'}</h1>
|
||||
<p className="text-xs mt-0.5 text-[var(--ui-muted)]">{settings?.platform_subtitle || 'Administração Global'}</p>
|
||||
</div>
|
||||
<nav className="flex-1 p-3 space-y-1">
|
||||
<NavLink
|
||||
to="/admin"
|
||||
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'
|
||||
}`
|
||||
`flex items-center px-3 py-2 rounded-md text-sm transition-colors ${isActive ? 'text-white' : 'text-[var(--ui-muted)] hover:text-[var(--ui-text)]'}`
|
||||
}
|
||||
style={({ isActive }) => (isActive ? { backgroundColor: primary } : { backgroundColor: 'transparent' })}
|
||||
>
|
||||
Dashboard
|
||||
</NavLink>
|
||||
<NavLink
|
||||
to="/admin/tenants"
|
||||
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'
|
||||
}`
|
||||
`flex items-center px-3 py-2 rounded-md text-sm transition-colors ${isActive ? 'text-white' : 'text-[var(--ui-muted)] hover:text-[var(--ui-text)]'}`
|
||||
}
|
||||
style={({ isActive }) => (isActive ? { backgroundColor: primary } : { backgroundColor: 'transparent' })}
|
||||
>
|
||||
Oficinas
|
||||
</NavLink>
|
||||
<NavLink
|
||||
to="/admin/settings"
|
||||
className={({ isActive }) =>
|
||||
`flex items-center px-3 py-2 rounded-md text-sm transition-colors ${isActive ? 'text-white' : 'text-[var(--ui-muted)] hover:text-[var(--ui-text)]'}`
|
||||
}
|
||||
style={({ isActive }) => (isActive ? { backgroundColor: primary } : { backgroundColor: 'transparent' })}
|
||||
>
|
||||
Definições Globais
|
||||
</NavLink>
|
||||
</nav>
|
||||
<div className="p-3 border-t border-slate-800">
|
||||
<div className="p-3 border-t" style={{ borderColor: 'var(--ui-border)' }}>
|
||||
<button
|
||||
onClick={toggleTheme}
|
||||
className="mb-2 w-full text-left px-3 py-2 text-sm rounded-md transition-colors text-[var(--ui-muted)] hover:text-[var(--ui-text)] hover:bg-[var(--ui-hover)]"
|
||||
>
|
||||
Tema: {theme === 'dark' ? 'Dark' : 'Light'}
|
||||
</button>
|
||||
<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"
|
||||
className="w-full text-left px-3 py-2 text-sm rounded-md transition-colors text-[var(--ui-muted)] hover:text-[var(--ui-text)] hover:bg-[var(--ui-hover)]"
|
||||
>
|
||||
Terminar sessão
|
||||
</button>
|
||||
</div>
|
||||
</aside>
|
||||
<main className="flex-1 overflow-auto p-6 text-white">
|
||||
<main className="flex-1 overflow-auto p-6 text-[var(--ui-text)]" style={mainStyle}>
|
||||
<Outlet />
|
||||
</main>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user