- Backend: POST /suggestions (público) + GET /suggestions (JWT admin) - Admin: página pública /sugerencias con formulario de sugerencias - schema.prisma: modelo suggestions - AuthGuard + Sidebar: /sugerencias como ruta pública Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
107 lines
3.6 KiB
TypeScript
107 lines
3.6 KiB
TypeScript
'use client';
|
|
|
|
import Link from 'next/link';
|
|
import { usePathname } from 'next/navigation';
|
|
import { useAuth } from '@/lib/auth';
|
|
import {
|
|
LayoutDashboard,
|
|
Users,
|
|
Briefcase,
|
|
ClipboardList,
|
|
MapPin,
|
|
Wrench,
|
|
Settings,
|
|
MessageSquare,
|
|
Star,
|
|
LogOut,
|
|
ChevronLeft,
|
|
Menu,
|
|
} from 'lucide-react';
|
|
import { Button } from '@/components/ui/button';
|
|
import { cn } from '@/lib/utils';
|
|
import { useState } from 'react';
|
|
import { Avatar, AvatarFallback } from '@/components/ui/avatar';
|
|
|
|
const menu = [
|
|
{ href: '/', label: 'Dashboard', icon: LayoutDashboard },
|
|
{ href: '/users', label: 'Usuarios', icon: Users },
|
|
{ href: '/professionals', label: 'Profesionales', icon: Briefcase },
|
|
{ href: '/services', label: 'Servicios', icon: ClipboardList },
|
|
{ href: '/locations', label: 'Ubicaciones', icon: MapPin },
|
|
{ href: '/professions', label: 'Profesiones', icon: Wrench },
|
|
{ href: '/comments', label: 'Comentarios', icon: Star },
|
|
{ href: '/sms', label: 'SMS', icon: MessageSquare },
|
|
{ href: '/settings', label: 'Configuración', icon: Settings },
|
|
];
|
|
|
|
export default function Sidebar({ children }: { children: React.ReactNode }) {
|
|
const path = usePathname();
|
|
const { user, logout } = useAuth();
|
|
const [collapsed, setCollapsed] = useState(false);
|
|
|
|
if (path === '/login' || path === '/sugerencias') return <>{children}</>;
|
|
|
|
return (
|
|
<div className="flex h-screen">
|
|
<aside
|
|
className={cn(
|
|
'flex flex-col border-r bg-card transition-all duration-200',
|
|
collapsed ? 'w-16' : 'w-56',
|
|
)}
|
|
>
|
|
<div className="flex h-14 items-center justify-between border-b px-3">
|
|
{!collapsed && (
|
|
<img src="https://prosapp.co/img/logo_prosapp.png" alt="ProsApp" className="h-7 w-auto object-contain" />
|
|
)}
|
|
{collapsed && (
|
|
<div className="flex h-7 w-7 items-center justify-center rounded-lg bg-[#42A4EF] mx-auto">
|
|
<span className="text-xs font-black text-white">P</span>
|
|
</div>
|
|
)}
|
|
<Button variant="ghost" size="icon" onClick={() => setCollapsed(!collapsed)}>
|
|
{collapsed ? <Menu size={18} /> : <ChevronLeft size={18} />}
|
|
</Button>
|
|
</div>
|
|
|
|
<nav className="flex-1 space-y-1 p-2">
|
|
{menu.map((item) => (
|
|
<Link key={item.href} href={item.href}>
|
|
<Button
|
|
variant={path === item.href ? 'secondary' : 'ghost'}
|
|
className={cn('w-full justify-start', collapsed ? 'px-2' : 'px-3')}
|
|
>
|
|
<item.icon size={18} className={collapsed ? 'mx-auto' : 'mr-2'} />
|
|
{!collapsed && item.label}
|
|
</Button>
|
|
</Link>
|
|
))}
|
|
</nav>
|
|
|
|
<div className="border-t p-2">
|
|
{collapsed ? (
|
|
<Button variant="ghost" size="icon" className="w-full" onClick={logout}>
|
|
<LogOut size={18} />
|
|
</Button>
|
|
) : (
|
|
<div className="flex items-center gap-2">
|
|
<Avatar className="h-8 w-8">
|
|
<AvatarFallback>{user?.name?.charAt(0) || 'A'}</AvatarFallback>
|
|
</Avatar>
|
|
<div className="flex-1 min-w-0">
|
|
<p className="text-sm font-medium truncate">{user?.name}</p>
|
|
</div>
|
|
<Button variant="ghost" size="icon" onClick={logout}>
|
|
<LogOut size={16} />
|
|
</Button>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</aside>
|
|
|
|
<main className="flex-1 overflow-auto bg-muted/20">
|
|
<div className="mx-auto max-w-7xl p-6">{children}</div>
|
|
</main>
|
|
</div>
|
|
);
|
|
}
|