feat(FIX-158): completed feature
This commit is contained in:
@@ -1,21 +1,41 @@
|
||||
'use client';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useRouter, usePathname } from 'next/navigation';
|
||||
import { useEffect } from 'react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import Link from 'next/link';
|
||||
import { AuthProvider, useAuth } from '@/features/auth/components/AuthProvider';
|
||||
import { visibleNavItems, type NavItem } from '@/lib/permissions';
|
||||
import { topNavItems, subNavItems, type NavItem } from '@/lib/permissions';
|
||||
import type { Role } from '@/types';
|
||||
|
||||
function Sidebar({
|
||||
navItems,
|
||||
user,
|
||||
onLogout,
|
||||
}: {
|
||||
navItems: NavItem[];
|
||||
user: { email: string; role: Role };
|
||||
onLogout: () => void;
|
||||
}) {
|
||||
const pathname = usePathname();
|
||||
function NavItemRow({ item }: { item: NavItem }) {
|
||||
const pathname = window?.location?.pathname ?? '';
|
||||
const active = item.href === '/'
|
||||
? pathname === '/'
|
||||
: pathname.startsWith(item.href);
|
||||
|
||||
return (
|
||||
<Link
|
||||
key={item.href}
|
||||
href={item.href}
|
||||
className={`
|
||||
flex items-center gap-3 px-3 py-2 rounded-lg text-sm font-medium transition-all
|
||||
${active
|
||||
? 'bg-[#2D6A4F]/10 text-[#2D6A4F] border-l-[3px] border-[#2D6A4F]'
|
||||
: 'text-gray-600 hover:bg-gray-50 hover:text-gray-900'}
|
||||
`}
|
||||
>
|
||||
<span className="text-base">{item.icon}</span>
|
||||
<span className="truncate">{item.label}</span>
|
||||
{item.badge != null && item.badge > 0 && (
|
||||
<span className="ml-auto bg-[#E76F51] text-white text-xs font-bold rounded-full px-1.5 py-0.5 min-w-[18px] text-center">
|
||||
{item.badge}
|
||||
</span>
|
||||
)}
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
function Sidebar({ role, email }: { role: Role; email: string }) {
|
||||
const topItems = topNavItems(role);
|
||||
|
||||
return (
|
||||
<div className="w-60 bg-white border-r border-gray-200 flex flex-col h-screen sticky top-0">
|
||||
@@ -30,32 +50,20 @@ function Sidebar({
|
||||
|
||||
{/* Nav */}
|
||||
<nav className="flex-1 px-3 py-4 space-y-0.5 overflow-y-auto">
|
||||
{navItems.map((item) => {
|
||||
const active =
|
||||
item.href === '/'
|
||||
? pathname === '/'
|
||||
: pathname.startsWith(item.href);
|
||||
{topItems.map((item) => {
|
||||
const subs = subNavItems(item.href, role);
|
||||
|
||||
return (
|
||||
<Link
|
||||
key={item.href}
|
||||
href={item.href}
|
||||
className={`
|
||||
flex items-center gap-3 px-3 py-2 rounded-lg text-sm font-medium transition-all
|
||||
${
|
||||
active
|
||||
? 'bg-[#2D6A4F]/10 text-[#2D6A4F] border-l-[3px] border-[#2D6A4F]'
|
||||
: 'text-gray-600 hover:bg-gray-50 hover:text-gray-900'
|
||||
}
|
||||
`}
|
||||
>
|
||||
<span className="text-base">{item.icon}</span>
|
||||
<span className="truncate">{item.label}</span>
|
||||
{item.badge != null && item.badge > 0 && (
|
||||
<span className="ml-auto bg-[#E76F51] text-white text-xs font-bold rounded-full px-1.5 py-0.5 min-w-[18px] text-center">
|
||||
{item.badge}
|
||||
</span>
|
||||
<div key={item.href}>
|
||||
<NavItemRow item={item} />
|
||||
{subs.length > 0 && (
|
||||
<div className="ml-4 mt-0.5 space-y-0.5">
|
||||
{subs.map((sub) => (
|
||||
<NavItemRow key={sub.href} item={sub} />
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</Link>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</nav>
|
||||
@@ -63,18 +71,9 @@ function Sidebar({
|
||||
{/* User footer */}
|
||||
<div className="px-3 py-4 border-t border-gray-100">
|
||||
<div className="px-3 py-2 mb-2">
|
||||
<p className="text-xs text-gray-400 truncate">{user.email}</p>
|
||||
<p className="text-xs text-gray-500 capitalize">{user.role}</p>
|
||||
<p className="text-xs text-gray-400 truncate">{email}</p>
|
||||
<p className="text-xs text-gray-500 capitalize">{role}</p>
|
||||
</div>
|
||||
<button
|
||||
onClick={onLogout}
|
||||
className="w-full flex items-center gap-2 px-3 py-2 text-sm text-gray-500 hover:text-gray-700 hover:bg-gray-50 rounded-lg transition-colors"
|
||||
>
|
||||
<svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2} aria-hidden="true">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M15.75 9V5.25A2.25 2.25 0 0013.5 3h-6a2.25 2.25 0 00-2.25 2.25v13.5A2.25 2.25 0 007.5 21h6a2.25 2.25 0 002.25-2.25V15M12 9l-3 3m0 0l3 3m-3-3h12.75" />
|
||||
</svg>
|
||||
Cerrar sesión
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@@ -100,11 +99,9 @@ function DashboardShell({ children }: { children: React.ReactNode }) {
|
||||
|
||||
if (!user) return null;
|
||||
|
||||
const navItems = visibleNavItems(user.role);
|
||||
|
||||
return (
|
||||
<div className="flex min-h-screen bg-gray-50">
|
||||
<Sidebar navItems={navItems} user={user} onLogout={logout} />
|
||||
<Sidebar role={user.role} email={user.email} />
|
||||
<main className="flex-1 min-w-0">
|
||||
<div className="w-full px-4 sm:px-6 lg:px-10 py-6 lg:py-8">
|
||||
{children}
|
||||
@@ -114,11 +111,7 @@ function DashboardShell({ children }: { children: React.ReactNode }) {
|
||||
);
|
||||
}
|
||||
|
||||
export default function DashboardLayout({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
export default function DashboardLayout({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<AuthProvider>
|
||||
<DashboardShell>{children}</DashboardShell>
|
||||
|
||||
Reference in New Issue
Block a user