feat(admin): menú de navegación compartido — dashboard, respuestas, usuarios
AdminNavBar reemplaza los links sueltos y duplicados entre páginas (cada una solo enlazaba a una de las otras dos). Ahora las tres pantallas detrás del login (/dashboard, /admin/responses, /admin/usuarios) comparten la misma barra con las 3 secciones + cerrar sesión, sin importar por dónde se entre primero. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -2,7 +2,7 @@ import { createServerClient } from '@supabase/ssr'
|
|||||||
import { cookies } from 'next/headers'
|
import { cookies } from 'next/headers'
|
||||||
import { ResponsesTable, type DisplayRow } from './responses-table'
|
import { ResponsesTable, type DisplayRow } from './responses-table'
|
||||||
import { SurveySelect } from './survey-select'
|
import { SurveySelect } from './survey-select'
|
||||||
import { SignOutButton } from '../sign-out-button'
|
import { AdminNavBar } from '@/components/admin-nav-bar'
|
||||||
import { formatDate, renderValue } from './admin-format'
|
import { formatDate, renderValue } from './admin-format'
|
||||||
|
|
||||||
export const dynamic = 'force-dynamic'
|
export const dynamic = 'force-dynamic'
|
||||||
@@ -218,7 +218,9 @@ export default async function AdminResponsesPage({
|
|||||||
})
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="admin-page" role="main">
|
<>
|
||||||
|
<AdminNavBar active="responses" />
|
||||||
|
<div className="admin-page" role="main">
|
||||||
<header className="admin-header">
|
<header className="admin-header">
|
||||||
<div>
|
<div>
|
||||||
<h1 className="admin-title">Control de respuestas</h1>
|
<h1 className="admin-title">Control de respuestas</h1>
|
||||||
@@ -241,8 +243,6 @@ export default async function AdminResponsesPage({
|
|||||||
</div>
|
</div>
|
||||||
<div className="admin-header-actions">
|
<div className="admin-header-actions">
|
||||||
<a href="/admin/responses" className="admin-refresh">Actualizar</a>
|
<a href="/admin/responses" className="admin-refresh">Actualizar</a>
|
||||||
<a href="/admin/usuarios" className="admin-refresh">Usuarios</a>
|
|
||||||
<SignOutButton />
|
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
@@ -268,6 +268,7 @@ export default async function AdminResponsesPage({
|
|||||||
)}
|
)}
|
||||||
</nav>
|
</nav>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { createServerClient } from '@supabase/ssr'
|
|||||||
import { cookies } from 'next/headers'
|
import { cookies } from 'next/headers'
|
||||||
import { getSupabaseAdmin } from '@/lib/supabase'
|
import { getSupabaseAdmin } from '@/lib/supabase'
|
||||||
import { formatDate } from '../responses/admin-format'
|
import { formatDate } from '../responses/admin-format'
|
||||||
import { SignOutButton } from '../sign-out-button'
|
import { AdminNavBar } from '@/components/admin-nav-bar'
|
||||||
import { InviteForm } from './invite-form'
|
import { InviteForm } from './invite-form'
|
||||||
|
|
||||||
export const dynamic = 'force-dynamic'
|
export const dynamic = 'force-dynamic'
|
||||||
@@ -146,16 +146,14 @@ export default async function AdminUsuariosPage() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="admin-page" role="main">
|
<>
|
||||||
|
<AdminNavBar active="usuarios" />
|
||||||
|
<div className="admin-page" role="main">
|
||||||
<header className="admin-header">
|
<header className="admin-header">
|
||||||
<div>
|
<div>
|
||||||
<h1 className="admin-title">Usuarios</h1>
|
<h1 className="admin-title">Usuarios</h1>
|
||||||
<p className="admin-subtitle">{userRows.length} usuarios registrados</p>
|
<p className="admin-subtitle">{userRows.length} usuarios registrados</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="admin-header-actions">
|
|
||||||
<a href="/admin/responses" className="admin-refresh">Respuestas</a>
|
|
||||||
<SignOutButton />
|
|
||||||
</div>
|
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<section className="admin-detail-section">
|
<section className="admin-detail-section">
|
||||||
@@ -191,6 +189,7 @@ export default async function AdminUsuariosPage() {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import { useSearchParams } from 'next/navigation'
|
import { useSearchParams } from 'next/navigation'
|
||||||
|
import { AdminNavBar } from '@/components/admin-nav-bar'
|
||||||
import { FilterSidebar } from '@/components/dashboard/filter-sidebar'
|
import { FilterSidebar } from '@/components/dashboard/filter-sidebar'
|
||||||
import { TalentoRiesgoCard } from '@/components/dashboard/talento-riesgo-card'
|
import { TalentoRiesgoCard } from '@/components/dashboard/talento-riesgo-card'
|
||||||
import { PrevalenciaCuidadoresCard } from '@/components/dashboard/prevalencia-cuidadores-card'
|
import { PrevalenciaCuidadoresCard } from '@/components/dashboard/prevalencia-cuidadores-card'
|
||||||
@@ -46,7 +47,9 @@ export default function DashboardPage() {
|
|||||||
const [lens, setLens] = useState<Lens>('empresa')
|
const [lens, setLens] = useState<Lens>('empresa')
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="db-page">
|
<>
|
||||||
|
<AdminNavBar active="dashboard" />
|
||||||
|
<div className="db-page">
|
||||||
<FilterSidebar
|
<FilterSidebar
|
||||||
filters={filters}
|
filters={filters}
|
||||||
onChange={setFilters}
|
onChange={setFilters}
|
||||||
@@ -144,6 +147,7 @@ export default function DashboardPage() {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -998,6 +998,21 @@ body {
|
|||||||
.db-placeholder-title { font-size: 1rem; font-weight: 600; color: #494F4F; }
|
.db-placeholder-title { font-size: 1rem; font-weight: 600; color: #494F4F; }
|
||||||
.db-placeholder-hint { font-size: 0.8125rem; max-width: 420px; line-height: 1.5; }
|
.db-placeholder-hint { font-size: 0.8125rem; max-width: 420px; line-height: 1.5; }
|
||||||
|
|
||||||
|
/* ── Nav compartido (dashboard / respuestas / usuarios) ── */
|
||||||
|
.admin-nav-bar {
|
||||||
|
display: flex; align-items: center; justify-content: space-between;
|
||||||
|
gap: 16px; padding: 10px 24px; background: var(--color-surface);
|
||||||
|
border-bottom: 1px solid var(--color-border); flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
.admin-nav { display: flex; gap: 4px; }
|
||||||
|
.admin-nav-link {
|
||||||
|
padding: 6px 14px; border-radius: var(--radius); font-size: 0.875rem;
|
||||||
|
font-weight: 600; color: var(--color-text-muted); text-decoration: none;
|
||||||
|
transition: background 0.15s, color 0.15s;
|
||||||
|
}
|
||||||
|
.admin-nav-link:hover { background: var(--color-primary-light); color: var(--color-text); }
|
||||||
|
.admin-nav-link-active { background: var(--color-primary-light); color: var(--color-primary-hover); }
|
||||||
|
|
||||||
/* ── Admin: control de respuestas (Etapa 3V) ── */
|
/* ── Admin: control de respuestas (Etapa 3V) ── */
|
||||||
.admin-page { max-width: 1100px; margin: 0 auto; padding: 24px; }
|
.admin-page { max-width: 1100px; margin: 0 auto; padding: 24px; }
|
||||||
.admin-header {
|
.admin-header {
|
||||||
|
|||||||
34
components/admin-nav-bar.tsx
Normal file
34
components/admin-nav-bar.tsx
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import { SignOutButton } from '@/app/admin/sign-out-button'
|
||||||
|
|
||||||
|
interface AdminNavBarProps {
|
||||||
|
active: 'dashboard' | 'responses' | 'usuarios'
|
||||||
|
}
|
||||||
|
|
||||||
|
const LINKS = [
|
||||||
|
{ id: 'dashboard', href: '/dashboard', label: 'Dashboard' },
|
||||||
|
{ id: 'responses', href: '/admin/responses', label: 'Respuestas' },
|
||||||
|
{ id: 'usuarios', href: '/admin/usuarios', label: 'Usuarios' },
|
||||||
|
] as const
|
||||||
|
|
||||||
|
// Nav compartido entre /dashboard, /admin/responses y /admin/usuarios — las
|
||||||
|
// tres pantallas detrás del login. Sin esto, cada una era una isla y el único
|
||||||
|
// modo de llegar a otra era escribir la URL a mano.
|
||||||
|
export function AdminNavBar({ active }: AdminNavBarProps) {
|
||||||
|
return (
|
||||||
|
<div className="admin-nav-bar">
|
||||||
|
<nav className="admin-nav" aria-label="Navegación admin">
|
||||||
|
{LINKS.map((link) => (
|
||||||
|
<a
|
||||||
|
key={link.id}
|
||||||
|
href={link.href}
|
||||||
|
className={`admin-nav-link${active === link.id ? ' admin-nav-link-active' : ''}`}
|
||||||
|
aria-current={active === link.id ? 'page' : undefined}
|
||||||
|
>
|
||||||
|
{link.label}
|
||||||
|
</a>
|
||||||
|
))}
|
||||||
|
</nav>
|
||||||
|
<SignOutButton />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user