MyMidas/frontend/src/components/layout/Sidebar.tsx
megaproxy 1a2c8efd01 Add pensions module and integrate with tax report
Adds a full pensions feature: SIPP/workplace DC/LISA account metadata,
contribution recording with relief-at-source/net-pay/salary-sacrifice
gross calculations, state pension tracker, annual allowance monitor,
and LISA summary. Pension contributions feed into the tax report
(RAS gross totals, allowance used). Includes two Alembic migrations,
backend service/schema/API, and full frontend pensions page with
cards for allowance, state pension, LISA, and retirement projection.

Also fixes CSRF cookie secure flag (must be false for HTTP deployments)
and extends tax schemas/service to expose pension data in the report.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 09:59:01 +00:00

90 lines
2.9 KiB
TypeScript

import { Link, useLocation } from "react-router-dom";
import { cn } from "@/utils/cn";
import { useUiStore } from "@/store/uiStore";
import {
LayoutDashboard,
CreditCard,
ArrowLeftRight,
PiggyBank,
TrendingUp,
BarChart3,
Sparkles,
Settings,
ChevronLeft,
ChevronRight,
Coins,
Repeat,
Receipt,
ShieldCheck,
} from "lucide-react";
const navItems = [
{ href: "/", icon: LayoutDashboard, label: "Dashboard" },
{ href: "/accounts", icon: CreditCard, label: "Accounts" },
{ href: "/transactions", icon: ArrowLeftRight, label: "Transactions" },
{ href: "/subscriptions", icon: Repeat, label: "Subscriptions" },
{ href: "/budgets", icon: PiggyBank, label: "Budgets" },
{ href: "/investments", icon: TrendingUp, label: "Investments" },
{ href: "/reports", icon: BarChart3, label: "Reports" },
{ href: "/tax", icon: Receipt, label: "Tax" },
{ href: "/pensions", icon: ShieldCheck, label: "Pensions" },
{ href: "/predictions", icon: Sparkles, label: "Predictions" },
{ href: "/settings", icon: Settings, label: "Settings" },
];
export default function Sidebar() {
const location = useLocation();
const { sidebarOpen, setSidebarOpen } = useUiStore();
return (
<aside
className={cn(
"fixed left-0 top-0 h-full z-30 bg-card border-r border-border transition-all duration-200 flex flex-col",
sidebarOpen ? "w-64" : "w-16"
)}
>
{/* Logo */}
<div className="flex items-center h-16 px-4 border-b border-border shrink-0">
<Coins className="w-7 h-7 text-primary shrink-0" />
{sidebarOpen && (
<span className="ml-2 font-semibold text-lg truncate">MyMidas</span>
)}
</div>
{/* Nav */}
<nav className="flex-1 overflow-y-auto py-4 space-y-1 px-2">
{navItems.map(({ href, icon: Icon, label }) => {
const active = location.pathname === href || (href !== "/" && location.pathname.startsWith(href));
return (
<Link
key={href}
to={href}
className={cn(
"flex items-center gap-3 px-2 py-2 rounded-md text-sm font-medium transition-colors",
active
? "bg-primary/15 text-primary"
: "text-muted-foreground hover:bg-secondary hover:text-foreground"
)}
title={!sidebarOpen ? label : undefined}
>
<Icon className="w-5 h-5 shrink-0" />
{sidebarOpen && <span className="truncate">{label}</span>}
</Link>
);
})}
</nav>
{/* Toggle */}
<button
onClick={() => setSidebarOpen(!sidebarOpen)}
className="flex items-center justify-center h-10 w-full border-t border-border text-muted-foreground hover:text-foreground transition-colors"
>
{sidebarOpen ? (
<ChevronLeft className="w-4 h-4" />
) : (
<ChevronRight className="w-4 h-4" />
)}
</button>
</aside>
);
}