import { DASHBOARD_PATH } from "@/lib/deployment"; import { SidebarProvider, SidebarTrigger } from "@/components/ui/sidebar"; import { AppSidebar } from "@/components/AppSidebar"; import { PageTransition } from "@/components/PageTransition"; import { useLocation, Link } from "react-router-dom"; import { Breadcrumb, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator, } from "@/components/ui/breadcrumb"; import { useFinanceData } from "@/context/FinanceDataContext"; import { normalizeMerchant } from "@/lib/merchant"; import React from "react"; import { Eye, EyeOff, Search } from "lucide-react"; import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"; import { CommandPalette } from "@/components/CommandPalette"; import { OPEN_PALETTE_EVENT } from "@/components/commandPaletteConfig"; import { isMac, MOD_LABEL } from "@/hooks/useHotkeys"; import { toggleAmountsHidden, usePrivacy } from "@/lib/privacy"; import { accountDisplayName } from "@/lib/accounts"; const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i; function DynamicBreadcrumbs() { const location = useLocation(); const { investmentAccounts, accounts, transactions } = useFinanceData(); const paths = location.pathname.split("/").filter(Boolean); // Resolve an opaque id segment (e.g. /investments/) to a human label so // the breadcrumb shows the account name instead of a raw UUID. const labelFor = (segment: string, index: number) => { const acct = investmentAccounts.find((a) => a.id === segment); if (acct) return acct.label; if (index > 0 && paths[index - 1] === "accounts") { const account = accounts.find((a) => a.id === segment); if (account) return accountDisplayName(account); } // A transaction id is the child of /transactions. Its ids look like // `plaid-t-0-3-2`, which the generic title-caser turned into the nonsense // "Plaid T 0 3 2", show the merchant, or a plain label when the row is not // loaded (the list is server-paged, so it often is not). if (index > 0 && paths[index - 1] === "transactions") { const txn = transactions.find((t) => t.id === segment); return txn ? normalizeMerchant(txn.description) : "Transaction"; } if (UUID_RE.test(segment) || /^[0-9a-f]{16,}$/i.test(segment)) return "Details"; return segment.split("-").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" "); }; if (location.pathname === DASHBOARD_PATH || paths.length === 0) { return ( Overview ); } return ( Overview {paths.map((path, index) => { const isLast = index === paths.length - 1; const to = `/${paths.slice(0, index + 1).join("/")}`; const title = labelFor(path, index); return ( {isLast ? ( {title} ) : ( {title} )} ); })} ); } /** Right side of the top bar: open the command palette, hide amounts. */ function TopBarActions() { const hidden = usePrivacy(); return (
{hidden ? "Show amounts" : "Hide amounts"} ⇧H
); } export function Layout({ children }: { children: React.ReactNode }) { return (
{children}
); }