Complete personal finance management application with: - Dashboard with financial metrics and alerts - Credit card management and payments - Fixed and variable debt tracking - Monthly budget planning - Intelligent alert system - Responsive design with Tailwind CSS Tech stack: Next.js 14, TypeScript, Zustand, Recharts 🤖 Generated with [Claude Code](https://claude.com/claude-code)
123 lines
3.8 KiB
TypeScript
123 lines
3.8 KiB
TypeScript
'use client';
|
|
|
|
import {
|
|
LayoutDashboard,
|
|
Wallet,
|
|
CreditCard,
|
|
PiggyBank,
|
|
Bell,
|
|
X,
|
|
} from 'lucide-react';
|
|
import Link from 'next/link';
|
|
import { usePathname } from 'next/navigation';
|
|
import { Logo } from './Logo';
|
|
|
|
interface SidebarProps {
|
|
isOpen: boolean;
|
|
onClose: () => void;
|
|
unreadAlertsCount?: number;
|
|
}
|
|
|
|
const navigationItems = [
|
|
{ name: 'Dashboard', href: '/', icon: LayoutDashboard },
|
|
{ name: 'Deudas', href: '/debts', icon: Wallet },
|
|
{ name: 'Tarjetas', href: '/cards', icon: CreditCard },
|
|
{ name: 'Presupuesto', href: '/budget', icon: PiggyBank },
|
|
{ name: 'Alertas', href: '/alerts', icon: Bell, hasBadge: true },
|
|
];
|
|
|
|
export function Sidebar({
|
|
isOpen,
|
|
onClose,
|
|
unreadAlertsCount = 0,
|
|
}: SidebarProps) {
|
|
const pathname = usePathname();
|
|
|
|
const isActive = (href: string) => {
|
|
if (href === '/') {
|
|
return pathname === '/';
|
|
}
|
|
return pathname.startsWith(href);
|
|
};
|
|
|
|
return (
|
|
<>
|
|
{/* Mobile overlay */}
|
|
{isOpen && (
|
|
<div
|
|
className="fixed inset-0 bg-black/50 z-40 lg:hidden"
|
|
onClick={onClose}
|
|
aria-hidden="true"
|
|
/>
|
|
)}
|
|
|
|
{/* Sidebar */}
|
|
<aside
|
|
className={`
|
|
fixed top-0 left-0 z-50 h-full w-64 bg-slate-900 border-r border-slate-800
|
|
transform transition-transform duration-300 ease-in-out
|
|
lg:translate-x-0 lg:static lg:h-screen
|
|
${isOpen ? 'translate-x-0' : '-translate-x-full'}
|
|
`}
|
|
>
|
|
<div className="flex flex-col h-full">
|
|
{/* Header */}
|
|
<div className="flex items-center justify-between p-4 border-b border-slate-800">
|
|
<Logo size="md" showText />
|
|
<button
|
|
onClick={onClose}
|
|
className="lg:hidden p-2 text-slate-400 hover:text-slate-200 hover:bg-slate-800 rounded-lg transition-colors"
|
|
aria-label="Cerrar menú"
|
|
>
|
|
<X className="w-5 h-5" />
|
|
</button>
|
|
</div>
|
|
|
|
{/* Navigation */}
|
|
<nav className="flex-1 overflow-y-auto py-4 px-3">
|
|
<ul className="space-y-1">
|
|
{navigationItems.map((item) => {
|
|
const active = isActive(item.href);
|
|
const Icon = item.icon;
|
|
|
|
return (
|
|
<li key={item.name}>
|
|
<Link
|
|
href={item.href}
|
|
onClick={onClose}
|
|
className={`
|
|
flex items-center gap-3 px-3 py-2.5 rounded-lg text-sm font-medium
|
|
transition-colors relative
|
|
${
|
|
active
|
|
? 'bg-slate-800 text-emerald-400 border-l-2 border-emerald-500'
|
|
: 'text-slate-300 hover:bg-slate-800 hover:text-slate-100'
|
|
}
|
|
`}
|
|
>
|
|
<Icon className="w-5 h-5 flex-shrink-0" />
|
|
<span className="flex-1">{item.name}</span>
|
|
{item.hasBadge && unreadAlertsCount > 0 && (
|
|
<span className="inline-flex items-center justify-center min-w-[20px] h-5 px-1.5 text-xs font-semibold bg-red-500 text-white rounded-full">
|
|
{unreadAlertsCount > 99 ? '99+' : unreadAlertsCount}
|
|
</span>
|
|
)}
|
|
</Link>
|
|
</li>
|
|
);
|
|
})}
|
|
</ul>
|
|
</nav>
|
|
|
|
{/* Footer */}
|
|
<div className="p-4 border-t border-slate-800">
|
|
<p className="text-xs text-slate-500 text-center">
|
|
Finanzas v{process.env.NEXT_PUBLIC_APP_VERSION || '1.0.0'}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</aside>
|
|
</>
|
|
);
|
|
}
|