feat: initial commit - finanzas app

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)
This commit is contained in:
renato97
2026-01-29 00:00:32 +00:00
commit 712b06f118
65 changed files with 8556 additions and 0 deletions

View File

@@ -0,0 +1,196 @@
'use client'
import { useState } from 'react'
import { useFinanzasStore } from '@/lib/store'
import { X, CreditCard, Calendar, DollarSign, Palette } from 'lucide-react'
import { cn } from '@/lib/utils'
interface AddCardModalProps {
isOpen: boolean
onClose: () => void
}
const COLORS = [
{ name: 'Slate', value: '#64748b' },
{ name: 'Blue', value: '#3b82f6' },
{ name: 'Cyan', value: '#06b6d4' },
{ name: 'Emerald', value: '#10b981' },
{ name: 'Violet', value: '#8b5cf6' },
{ name: 'Rose', value: '#f43f5e' },
{ name: 'Amber', value: '#f59e0b' },
]
export function AddCardModal({ isOpen, onClose }: AddCardModalProps) {
const addCreditCard = useFinanzasStore((state) => state.addCreditCard)
const [name, setName] = useState('')
const [lastFour, setLastFour] = useState('')
const [limit, setLimit] = useState('')
const [closingDay, setClosingDay] = useState('')
const [dueDay, setDueDay] = useState('')
const [selectedColor, setSelectedColor] = useState(COLORS[1].value)
if (!isOpen) return null
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault()
if (!name || !limit || !closingDay || !dueDay) return
addCreditCard({
name,
lastFourDigits: lastFour || '****',
closingDay: parseInt(closingDay),
dueDay: parseInt(dueDay),
currentBalance: 0,
creditLimit: parseFloat(limit),
color: selectedColor
})
// Reset
setName('')
setLastFour('')
setLimit('')
setClosingDay('')
setDueDay('')
setSelectedColor(COLORS[1].value)
onClose()
}
return (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-sm p-4 animate-in fade-in duration-200">
<div className="w-full max-w-lg rounded-xl bg-slate-900 border border-slate-800 shadow-2xl overflow-hidden scale-100 animate-in zoom-in-95 duration-200">
{/* Header */}
<div className="flex items-center justify-between p-6 border-b border-slate-800">
<h2 className="text-xl font-semibold text-white flex items-center gap-2">
<CreditCard className="text-cyan-500" /> Nueva Tarjeta
</h2>
<button onClick={onClose} className="text-slate-400 hover:text-white transition-colors">
<X size={20} />
</button>
</div>
<form onSubmit={handleSubmit} className="p-6 space-y-5">
{/* Name & Last 4 */}
<div className="grid grid-cols-3 gap-4">
<div className="col-span-2 space-y-2">
<label className="text-xs font-medium text-slate-400 uppercase tracking-wider">Nombre Banco / Tarjeta</label>
<input
type="text"
placeholder="Ej: Visa Santander"
value={name}
onChange={(e) => setName(e.target.value)}
className="w-full px-4 py-2.5 bg-slate-950 border border-slate-800 rounded-lg focus:ring-2 focus:ring-cyan-500/50 focus:border-cyan-500 text-white outline-none"
required
autoFocus
/>
</div>
<div className="col-span-1 space-y-2">
<label className="text-xs font-medium text-slate-400 uppercase tracking-wider">Ult. 4 Dig.</label>
<input
type="text"
maxLength={4}
placeholder="1234"
value={lastFour}
onChange={(e) => setLastFour(e.target.value)}
className="w-full px-4 py-2.5 bg-slate-950 border border-slate-800 rounded-lg focus:ring-2 focus:ring-cyan-500/50 focus:border-cyan-500 text-white outline-none text-center tracking-widest"
/>
</div>
</div>
{/* Limit */}
<div className="space-y-2">
<label className="text-xs font-medium text-slate-400 uppercase tracking-wider">Límite de Crédito</label>
<div className="relative">
<span className="absolute left-4 top-1/2 -translate-y-1/2 text-slate-400 font-semibold">$</span>
<input
type="number"
step="0.01"
placeholder="0.00"
value={limit}
onChange={(e) => setLimit(e.target.value)}
className="w-full pl-8 pr-4 py-3 bg-slate-950 border border-slate-800 rounded-lg focus:ring-2 focus:ring-cyan-500/50 focus:border-cyan-500 text-white text-lg font-mono outline-none"
required
/>
</div>
</div>
<div className="grid grid-cols-2 gap-4">
{/* Closing Day */}
<div className="space-y-2">
<label className="text-xs font-medium text-slate-400 uppercase tracking-wider">Día Cierre</label>
<div className="relative">
<input
type="number"
min="1"
max="31"
placeholder="20"
value={closingDay}
onChange={(e) => setClosingDay(e.target.value)}
className="w-full px-4 py-2.5 bg-slate-950 border border-slate-800 rounded-lg focus:ring-2 focus:ring-cyan-500/50 focus:border-cyan-500 text-white outline-none"
required
/>
<span className="absolute right-4 top-1/2 -translate-y-1/2 text-slate-500 text-sm">del mes</span>
</div>
</div>
{/* Due Day */}
<div className="space-y-2">
<label className="text-xs font-medium text-slate-400 uppercase tracking-wider">Día Vencimiento</label>
<div className="relative">
<input
type="number"
min="1"
max="31"
placeholder="5"
value={dueDay}
onChange={(e) => setDueDay(e.target.value)}
className="w-full px-4 py-2.5 bg-slate-950 border border-slate-800 rounded-lg focus:ring-2 focus:ring-cyan-500/50 focus:border-cyan-500 text-white outline-none"
required
/>
<span className="absolute right-4 top-1/2 -translate-y-1/2 text-slate-500 text-sm">del mes</span>
</div>
</div>
</div>
{/* Color Picker */}
<div className="space-y-2">
<label className="text-xs font-medium text-slate-400 uppercase tracking-wider flex items-center gap-2">
<Palette size={12} /> Color de Tarjeta
</label>
<div className="flex gap-3 overflow-x-auto pb-2">
{COLORS.map((color) => (
<button
key={color.value}
type="button"
onClick={() => setSelectedColor(color.value)}
className={cn(
"w-8 h-8 rounded-full border-2 transition-all",
selectedColor === color.value
? "border-white scale-110 shadow-lg"
: "border-transparent opacity-70 hover:opacity-100 hover:scale-105"
)}
style={{ backgroundColor: color.value }}
title={color.name}
/>
))}
</div>
</div>
<div className="pt-2">
<button
type="submit"
className="w-full py-3 bg-cyan-500 hover:bg-cyan-400 text-white font-semibold rounded-lg shadow-lg shadow-cyan-500/20 transition-all active:scale-[0.98]"
>
Crear Tarjeta
</button>
</div>
</form>
</div>
</div>
)
}