Initial commit: MyMidas personal finance tracker

Full-stack self-hosted finance app with FastAPI backend and React frontend.

Features:
- Accounts, transactions, budgets, investments with GBP base currency
- CSV import with auto-detection for 10 UK bank formats
- ML predictions: spending forecast, net worth projection, Monte Carlo
- 7 selectable themes (Obsidian, Arctic, Midnight, Vault, Terminal, Synthwave, Ledger)
- Receipt/document attachments on transactions (JPEG, PNG, WebP, PDF)
- AES-256-GCM field encryption, RS256 JWT, TOTP 2FA, RLS, audit log
- Encrypted nightly backups + key rotation script
- Mobile-responsive layout with bottom nav

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
megaproxy 2026-04-21 11:56:10 +00:00
commit 61a7884ee5
127 changed files with 13323 additions and 0 deletions

View file

@ -0,0 +1,84 @@
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,
DollarSign,
} from "lucide-react";
const navItems = [
{ href: "/", icon: LayoutDashboard, label: "Dashboard" },
{ href: "/accounts", icon: CreditCard, label: "Accounts" },
{ href: "/transactions", icon: ArrowLeftRight, label: "Transactions" },
{ href: "/budgets", icon: PiggyBank, label: "Budgets" },
{ href: "/investments", icon: TrendingUp, label: "Investments" },
{ href: "/reports", icon: BarChart3, label: "Reports" },
{ 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">
<DollarSign className="w-7 h-7 text-primary shrink-0" />
{sidebarOpen && (
<span className="ml-2 font-semibold text-lg truncate">Finance</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>
);
}