feat: responsive sidebar, client selector and layout with alert/device counts

This commit is contained in:
2026-02-12 15:26:01 -06:00
parent 5d698490e2
commit 20982aa077
5 changed files with 386 additions and 158 deletions

View File

@@ -1,37 +1,132 @@
'use client'
import { useState, useEffect } from 'react'
import { useEffect, useState } from 'react'
import { useRouter } from 'next/navigation'
import Sidebar from '@/components/layout/Sidebar'
import Header from '@/components/layout/Header'
import { SelectedClientProvider, useSelectedClient } from '@/components/providers/SelectedClientProvider'
import { trpc } from '@/lib/trpc-client'
export default function DashboardLayout({
children,
}: {
children: React.ReactNode
}) {
const [alertasActivas, setAlertasActivas] = useState(0)
const [user, setUser] = useState({
nombre: 'Admin',
email: 'admin@example.com',
rol: 'SUPER_ADMIN',
const router = useRouter()
const meQuery = trpc.auth.me.useQuery(undefined, {
retry: false,
staleTime: 60 * 1000,
})
const logoutMutation = trpc.auth.logout.useMutation({
onSuccess: () => {
window.location.href = '/login'
},
})
useEffect(() => {
// TODO: Cargar alertas activas desde API
// TODO: Cargar usuario desde sesion
}, [])
if (meQuery.isError) {
router.push('/login')
}
}, [meQuery.isError, router])
const handleLogout = async () => {
// TODO: Implementar logout
window.location.href = '/login'
const handleLogout = () => {
logoutMutation.mutate()
}
if (meQuery.isLoading || meQuery.isError) {
return (
<div className="flex h-screen bg-dark-500 items-center justify-center">
<div className="text-gray-400">Cargando...</div>
</div>
)
}
const user = meQuery.data
if (!user) return null
return (
<DashboardContent user={user} onLogout={handleLogout}>
{children}
</DashboardContent>
)
}
function DashboardContent({
user,
onLogout,
children,
}: {
user: { nombre: string; email: string; rol: string }
onLogout: () => void
children: React.ReactNode
}) {
return (
<SelectedClientProvider>
<DashboardContentInner user={user} onLogout={onLogout}>
{children}
</DashboardContentInner>
</SelectedClientProvider>
)
}
function DashboardContentInner({
user,
onLogout,
children,
}: {
user: { nombre: string; email: string; rol: string }
onLogout: () => void
children: React.ReactNode
}) {
const { selectedClientId } = useSelectedClient()
const clienteId = selectedClientId ?? undefined
const activeAlertsCountQuery = trpc.alertas.conteoActivas.useQuery(
{ clienteId },
{ refetchOnWindowFocus: true, staleTime: 30 * 1000 }
)
const activeAlertsCount = activeAlertsCountQuery.data?.total ?? 0
const devicesCountQuery = trpc.equipos.list.useQuery(
{ clienteId, page: 1, limit: 1 },
{ refetchOnWindowFocus: true, staleTime: 30 * 1000 }
)
const devicesCount = devicesCountQuery.data?.pagination?.total ?? 0
const clientsQuery = trpc.clientes.list.useQuery(
{ limit: 100 },
{ staleTime: 60 * 1000 }
)
const clients = (clientsQuery.data?.clientes ?? []).map((c) => ({
id: c.id,
nombre: c.nombre,
codigo: c.codigo,
}))
const [sidebarOpen, setSidebarOpen] = useState(false)
return (
<div className="flex h-screen bg-dark-500">
<Sidebar alertasActivas={alertasActivas} />
<div className="flex-1 flex flex-col overflow-hidden">
<Header user={user} onLogout={handleLogout} />
<main className="flex-1 overflow-y-auto p-6">
<Sidebar
activeAlertsCount={activeAlertsCount}
devicesCount={devicesCount}
open={sidebarOpen}
onClose={() => setSidebarOpen(false)}
/>
<div className="ml-0 md:ml-[260px] flex min-w-0 flex-1 flex-col overflow-hidden transition-[margin] duration-200">
<Header
user={{
nombre: user.nombre,
email: user.email,
rol: user.rol,
}}
onLogout={onLogout}
clients={clients}
showAllClientsOption={user.rol === 'SUPER_ADMIN'}
onOpenSidebar={() => setSidebarOpen(true)}
/>
<main className="flex-1 overflow-y-auto p-4 sm:p-6">
{children}
</main>
</div>