mirror of
https://github.com/escalante29/WealthySmart.git
synced 2026-07-17 09:08:46 +02:00
AppSidebar (icon-collapsible, sync warning badge, brand header), NavUser footer (avatar + privacy/theme/logout dropdown), and a thin header with SidebarTrigger, breadcrumbs, and quick toggles. Mobile drawer and cmd+B come from the sidebar primitives. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
101 lines
3.3 KiB
TypeScript
101 lines
3.3 KiB
TypeScript
import { Wallet } from "lucide-react";
|
|
import { Link, useLocation } from "react-router-dom";
|
|
import { useQuery } from "@tanstack/react-query";
|
|
|
|
import { getSyncStatus } from "@/lib/api";
|
|
import { isNavActive, navSections } from "@/lib/navigation";
|
|
import { NavUser } from "@/components/nav-user";
|
|
import {
|
|
Sidebar,
|
|
SidebarContent,
|
|
SidebarFooter,
|
|
SidebarGroup,
|
|
SidebarGroupContent,
|
|
SidebarGroupLabel,
|
|
SidebarHeader,
|
|
SidebarMenu,
|
|
SidebarMenuBadge,
|
|
SidebarMenuButton,
|
|
SidebarMenuItem,
|
|
SidebarRail,
|
|
useSidebar,
|
|
} from "@/components/ui/sidebar";
|
|
|
|
export function AppSidebar(props: React.ComponentProps<typeof Sidebar>) {
|
|
const { pathname } = useLocation();
|
|
const { setOpenMobile } = useSidebar();
|
|
|
|
// Surface stalled ingestion sources as a dot on the Sincronización item.
|
|
const syncQ = useQuery({
|
|
queryKey: ["sync-status"],
|
|
queryFn: () => getSyncStatus().then((r) => r.data),
|
|
staleTime: 5 * 60_000,
|
|
});
|
|
const syncWarnings = syncQ.data?.warnings ?? 0;
|
|
|
|
return (
|
|
<Sidebar collapsible="icon" {...props}>
|
|
<SidebarHeader>
|
|
<SidebarMenu>
|
|
<SidebarMenuItem>
|
|
<SidebarMenuButton
|
|
size="lg"
|
|
render={<Link to="/" aria-label="Ir a Inicio" />}
|
|
>
|
|
<div className="bg-primary text-primary-foreground flex aspect-square size-8 items-center justify-center rounded-lg">
|
|
<Wallet className="size-4" strokeWidth={2.5} />
|
|
</div>
|
|
<span
|
|
className="text-base font-bold tracking-tight"
|
|
style={{ fontFamily: "var(--font-heading)" }}
|
|
>
|
|
Wealthy<span className="text-primary">Smart</span>
|
|
</span>
|
|
</SidebarMenuButton>
|
|
</SidebarMenuItem>
|
|
</SidebarMenu>
|
|
</SidebarHeader>
|
|
|
|
<SidebarContent>
|
|
{navSections.map((section) => (
|
|
<SidebarGroup key={section.label}>
|
|
<SidebarGroupLabel>{section.label}</SidebarGroupLabel>
|
|
<SidebarGroupContent>
|
|
<SidebarMenu>
|
|
{section.items.map(({ to, icon: Icon, label }) => (
|
|
<SidebarMenuItem key={to}>
|
|
<SidebarMenuButton
|
|
isActive={isNavActive(to, pathname)}
|
|
tooltip={label}
|
|
render={
|
|
<Link to={to} onClick={() => setOpenMobile(false)} />
|
|
}
|
|
>
|
|
<Icon />
|
|
<span>{label}</span>
|
|
</SidebarMenuButton>
|
|
{to === "/sync" && syncWarnings > 0 && (
|
|
<SidebarMenuBadge>
|
|
<span
|
|
className="size-2 rounded-full bg-amber-500"
|
|
title={`${syncWarnings} fuente(s) atrasada(s)`}
|
|
aria-label={`${syncWarnings} fuentes de datos atrasadas`}
|
|
/>
|
|
</SidebarMenuBadge>
|
|
)}
|
|
</SidebarMenuItem>
|
|
))}
|
|
</SidebarMenu>
|
|
</SidebarGroupContent>
|
|
</SidebarGroup>
|
|
))}
|
|
</SidebarContent>
|
|
|
|
<SidebarFooter>
|
|
<NavUser />
|
|
</SidebarFooter>
|
|
<SidebarRail />
|
|
</Sidebar>
|
|
);
|
|
}
|