mirror of
https://github.com/escalante29/WealthySmart.git
synced 2026-07-17 11:08:47 +02:00
Replace hand-rolled shell with shadcn sidebar + breadcrumb header
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>
This commit is contained in:
100
frontend/src/components/app-sidebar.tsx
Normal file
100
frontend/src/components/app-sidebar.tsx
Normal file
@@ -0,0 +1,100 @@
|
||||
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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user