2025-12-12 10:38:18 +00:00
|
|
|
"use client";
|
2026-02-06 19:51:04 +00:00
|
|
|
|
2026-01-27 15:29:20 +00:00
|
|
|
import { useEffect, useState } from "react";
|
2026-02-06 19:51:04 +00:00
|
|
|
import { useAuth } from "@/hooks/use-auth";
|
2025-12-12 12:53:37 +00:00
|
|
|
import { getUser } from "@/lib/usersService";
|
2025-12-11 11:54:57 +00:00
|
|
|
|
|
|
|
|
import {
|
|
|
|
|
Sidebar,
|
|
|
|
|
SidebarContent,
|
2025-12-12 10:38:18 +00:00
|
|
|
SidebarFooter,
|
2025-12-11 11:54:57 +00:00
|
|
|
SidebarGroup,
|
|
|
|
|
SidebarGroupContent,
|
|
|
|
|
SidebarGroupLabel,
|
|
|
|
|
SidebarMenu,
|
|
|
|
|
SidebarMenuButton,
|
|
|
|
|
SidebarMenuItem,
|
2025-12-12 10:38:18 +00:00
|
|
|
} from "@/components/ui/sidebar";
|
|
|
|
|
import {
|
|
|
|
|
DropdownMenu,
|
|
|
|
|
DropdownMenuContent,
|
|
|
|
|
DropdownMenuItem,
|
|
|
|
|
DropdownMenuLabel,
|
|
|
|
|
DropdownMenuSeparator,
|
|
|
|
|
DropdownMenuTrigger,
|
|
|
|
|
} from "@/components/ui/dropdown-menu";
|
|
|
|
|
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
2026-02-06 18:24:39 +00:00
|
|
|
import { UserProfileDialog } from "@/components/user-profile";
|
2026-02-06 19:51:04 +00:00
|
|
|
import { SettingsDialog } from "@/components/settings";
|
2025-12-12 10:38:18 +00:00
|
|
|
import {
|
|
|
|
|
Home,
|
|
|
|
|
FolderKanban,
|
|
|
|
|
BarChart3,
|
|
|
|
|
Settings,
|
|
|
|
|
ChevronUp,
|
|
|
|
|
LogOut,
|
2026-01-30 21:44:22 +00:00
|
|
|
User,
|
2026-02-06 19:51:04 +00:00
|
|
|
GanttChart,
|
|
|
|
|
Loader2
|
2025-12-12 10:38:18 +00:00
|
|
|
} from "lucide-react";
|
2025-12-11 11:54:57 +00:00
|
|
|
|
2025-12-12 10:38:18 +00:00
|
|
|
// Datos del menú
|
2025-12-11 11:54:57 +00:00
|
|
|
const items = [
|
|
|
|
|
{
|
2025-12-12 10:38:18 +00:00
|
|
|
title: "Dashboard",
|
|
|
|
|
url: "/",
|
2025-12-11 11:54:57 +00:00
|
|
|
icon: Home,
|
|
|
|
|
},
|
|
|
|
|
{
|
2025-12-12 10:38:18 +00:00
|
|
|
title: "Proyectos",
|
|
|
|
|
url: "/proyectos",
|
|
|
|
|
icon: FolderKanban,
|
2025-12-11 11:54:57 +00:00
|
|
|
},
|
|
|
|
|
{
|
2025-12-12 10:38:18 +00:00
|
|
|
title: "Estadísticas",
|
|
|
|
|
url: "/estadisticas",
|
|
|
|
|
icon: BarChart3,
|
2025-12-11 11:54:57 +00:00
|
|
|
},
|
2026-01-30 21:44:22 +00:00
|
|
|
{
|
|
|
|
|
title: "Gantt",
|
|
|
|
|
url: "/gantt",
|
|
|
|
|
icon: GanttChart,
|
|
|
|
|
},
|
2025-12-12 10:38:18 +00:00
|
|
|
];
|
|
|
|
|
|
2026-01-27 15:29:20 +00:00
|
|
|
export function AppSidebar() {
|
2026-02-06 19:51:04 +00:00
|
|
|
const { logout, user: authUser } = useAuth();
|
2026-01-27 15:29:20 +00:00
|
|
|
const [user, setUser] = useState({
|
|
|
|
|
name: "Cargando...",
|
|
|
|
|
email: "",
|
|
|
|
|
avatar: "/avatar.jpg",
|
|
|
|
|
initials: "..."
|
|
|
|
|
});
|
2026-02-06 18:24:39 +00:00
|
|
|
const [isProfileOpen, setIsProfileOpen] = useState(false);
|
2026-02-06 19:51:04 +00:00
|
|
|
const [isSettingsOpen, setIsSettingsOpen] = useState(false);
|
|
|
|
|
const [isLoggingOut, setIsLoggingOut] = useState(false);
|
2025-12-12 12:53:37 +00:00
|
|
|
|
2026-01-27 15:29:20 +00:00
|
|
|
useEffect(() => {
|
|
|
|
|
async function loadUser() {
|
|
|
|
|
try {
|
2026-02-06 19:51:04 +00:00
|
|
|
// Si tenemos usuario del auth, usarlo primero
|
|
|
|
|
if (authUser) {
|
|
|
|
|
setUser({
|
|
|
|
|
name: `${authUser.firstname} ${authUser.lastname}`,
|
|
|
|
|
email: authUser.email,
|
|
|
|
|
avatar: "/avatar.jpg",
|
|
|
|
|
initials: `${authUser.firstname.charAt(0)}${authUser.lastname.charAt(0)}`
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Luego intentar cargar datos más completos
|
2026-01-27 15:29:20 +00:00
|
|
|
const realUser = await getUser();
|
|
|
|
|
setUser({
|
|
|
|
|
name: `${realUser.firstname} ${realUser.lastname}`,
|
|
|
|
|
email: realUser.email,
|
|
|
|
|
avatar: "/avatar.jpg",
|
|
|
|
|
initials: `${realUser.firstname.charAt(0)}${realUser.lastname.charAt(0)}`
|
|
|
|
|
});
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error("Failed to load user:", error);
|
2026-02-06 19:51:04 +00:00
|
|
|
// Si falla y tenemos authUser, mantener esos datos
|
|
|
|
|
if (!authUser) {
|
|
|
|
|
setUser(prev => ({ ...prev, name: "Usuario" }));
|
|
|
|
|
}
|
2026-01-27 15:29:20 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
loadUser();
|
2026-02-06 19:51:04 +00:00
|
|
|
}, [authUser]);
|
|
|
|
|
|
|
|
|
|
const handleLogout = async () => {
|
|
|
|
|
setIsLoggingOut(true);
|
|
|
|
|
try {
|
|
|
|
|
await logout();
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error("Logout error:", error);
|
|
|
|
|
} finally {
|
|
|
|
|
setIsLoggingOut(false);
|
|
|
|
|
}
|
|
|
|
|
};
|
2025-12-11 11:54:57 +00:00
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<Sidebar>
|
|
|
|
|
<SidebarContent>
|
2025-12-12 10:38:18 +00:00
|
|
|
{/* Header del Sidebar */}
|
2025-12-11 11:54:57 +00:00
|
|
|
<SidebarGroup>
|
2025-12-12 10:38:18 +00:00
|
|
|
<div className="px-4 py-6">
|
2026-02-06 19:51:04 +00:00
|
|
|
<h2 className="text-xl font-semibold text-foreground">Dolibarr</h2>
|
|
|
|
|
<p className="text-sm text-muted-foreground">Gestión de Proyectos</p>
|
2025-12-12 10:38:18 +00:00
|
|
|
</div>
|
|
|
|
|
</SidebarGroup>
|
|
|
|
|
|
|
|
|
|
{/* Menú de navegación */}
|
|
|
|
|
<SidebarGroup>
|
|
|
|
|
<SidebarGroupLabel className="text-sm">Menú Principal</SidebarGroupLabel>
|
2025-12-11 11:54:57 +00:00
|
|
|
<SidebarGroupContent>
|
|
|
|
|
<SidebarMenu>
|
|
|
|
|
{items.map((item) => (
|
|
|
|
|
<SidebarMenuItem key={item.title}>
|
2025-12-12 10:38:18 +00:00
|
|
|
<SidebarMenuButton asChild size="lg">
|
|
|
|
|
<a href={item.url} className="text-base">
|
|
|
|
|
<item.icon className="w-5 h-5" />
|
2025-12-11 11:54:57 +00:00
|
|
|
<span>{item.title}</span>
|
|
|
|
|
</a>
|
|
|
|
|
</SidebarMenuButton>
|
|
|
|
|
</SidebarMenuItem>
|
|
|
|
|
))}
|
|
|
|
|
</SidebarMenu>
|
|
|
|
|
</SidebarGroupContent>
|
|
|
|
|
</SidebarGroup>
|
|
|
|
|
</SidebarContent>
|
2025-12-12 10:38:18 +00:00
|
|
|
|
|
|
|
|
{/* Footer con Dropdown del Usuario */}
|
|
|
|
|
<SidebarFooter>
|
|
|
|
|
<SidebarMenu>
|
|
|
|
|
<SidebarMenuItem>
|
|
|
|
|
<DropdownMenu>
|
|
|
|
|
<DropdownMenuTrigger asChild>
|
|
|
|
|
<SidebarMenuButton
|
|
|
|
|
size="lg"
|
|
|
|
|
className="data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground"
|
|
|
|
|
>
|
|
|
|
|
<Avatar className="h-8 w-8 rounded-lg">
|
|
|
|
|
<AvatarImage src={user.avatar} alt={user.name} />
|
|
|
|
|
<AvatarFallback className="rounded-lg bg-gradient-to-br from-blue-500 to-purple-600 text-white">
|
|
|
|
|
{user.initials}
|
|
|
|
|
</AvatarFallback>
|
|
|
|
|
</Avatar>
|
|
|
|
|
<div className="grid flex-1 text-left text-sm leading-tight">
|
|
|
|
|
<span className="truncate font-semibold">{user.name}</span>
|
2026-02-06 19:51:04 +00:00
|
|
|
<span className="truncate text-xs text-muted-foreground">{user.email}</span>
|
2025-12-12 10:38:18 +00:00
|
|
|
</div>
|
|
|
|
|
<ChevronUp className="ml-auto size-4" />
|
|
|
|
|
</SidebarMenuButton>
|
|
|
|
|
</DropdownMenuTrigger>
|
|
|
|
|
<DropdownMenuContent
|
|
|
|
|
className="w-[--radix-dropdown-menu-trigger-width] min-w-56 rounded-lg"
|
|
|
|
|
side="bottom"
|
|
|
|
|
align="end"
|
|
|
|
|
sideOffset={4}
|
|
|
|
|
>
|
|
|
|
|
<DropdownMenuLabel className="p-0 font-normal">
|
|
|
|
|
<div className="flex items-center gap-2 px-1 py-1.5 text-left text-sm">
|
|
|
|
|
<Avatar className="h-8 w-8 rounded-lg">
|
|
|
|
|
<AvatarImage src={user.avatar} alt={user.name} />
|
|
|
|
|
<AvatarFallback className="rounded-lg bg-gradient-to-br from-blue-500 to-purple-600 text-white">
|
|
|
|
|
{user.initials}
|
|
|
|
|
</AvatarFallback>
|
|
|
|
|
</Avatar>
|
|
|
|
|
<div className="grid flex-1 text-left text-sm leading-tight">
|
|
|
|
|
<span className="truncate font-semibold">{user.name}</span>
|
2026-02-06 19:51:04 +00:00
|
|
|
<span className="truncate text-xs text-muted-foreground">{user.email}</span>
|
2025-12-12 10:38:18 +00:00
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</DropdownMenuLabel>
|
|
|
|
|
<DropdownMenuSeparator />
|
2026-02-06 18:24:39 +00:00
|
|
|
<DropdownMenuItem onClick={() => setIsProfileOpen(true)}>
|
2025-12-12 10:38:18 +00:00
|
|
|
<User className="mr-2 h-4 w-4" />
|
2026-02-06 18:24:39 +00:00
|
|
|
<span>Perfil</span>
|
2025-12-12 10:38:18 +00:00
|
|
|
</DropdownMenuItem>
|
2026-02-06 19:51:04 +00:00
|
|
|
<DropdownMenuItem onClick={() => setIsSettingsOpen(true)}>
|
2025-12-12 10:38:18 +00:00
|
|
|
<Settings className="mr-2 h-4 w-4" />
|
|
|
|
|
<span>Ajustes</span>
|
|
|
|
|
</DropdownMenuItem>
|
|
|
|
|
<DropdownMenuSeparator />
|
2026-02-06 19:51:04 +00:00
|
|
|
<DropdownMenuItem
|
|
|
|
|
className="text-destructive focus:text-destructive"
|
|
|
|
|
onClick={handleLogout}
|
|
|
|
|
disabled={isLoggingOut}
|
|
|
|
|
>
|
|
|
|
|
{isLoggingOut ? (
|
|
|
|
|
<>
|
|
|
|
|
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
|
|
|
|
|
<span>Cerrando sesión...</span>
|
|
|
|
|
</>
|
|
|
|
|
) : (
|
|
|
|
|
<>
|
|
|
|
|
<LogOut className="mr-2 h-4 w-4" />
|
|
|
|
|
<span>Cerrar sesión</span>
|
|
|
|
|
</>
|
|
|
|
|
)}
|
2025-12-12 10:38:18 +00:00
|
|
|
</DropdownMenuItem>
|
|
|
|
|
</DropdownMenuContent>
|
|
|
|
|
</DropdownMenu>
|
|
|
|
|
</SidebarMenuItem>
|
|
|
|
|
</SidebarMenu>
|
|
|
|
|
</SidebarFooter>
|
2026-02-06 18:24:39 +00:00
|
|
|
|
|
|
|
|
{/* Dialog de perfil de usuario */}
|
|
|
|
|
<UserProfileDialog
|
|
|
|
|
open={isProfileOpen}
|
|
|
|
|
onOpenChange={setIsProfileOpen}
|
|
|
|
|
/>
|
2026-02-06 19:51:04 +00:00
|
|
|
|
|
|
|
|
{/* Dialog de ajustes */}
|
|
|
|
|
<SettingsDialog
|
|
|
|
|
open={isSettingsOpen}
|
|
|
|
|
onOpenChange={setIsSettingsOpen}
|
|
|
|
|
/>
|
2025-12-11 11:54:57 +00:00
|
|
|
</Sidebar>
|
2025-12-12 10:38:18 +00:00
|
|
|
);
|
2026-02-06 19:51:04 +00:00
|
|
|
}
|