mirror of
https://github.com/escalante29/WealthySmart.git
synced 2026-05-19 11:28:49 +02:00
Add budget module and push notifications for transactions
All checks were successful
Deploy to VPS / deploy (push) Successful in 34s
All checks were successful
Deploy to VPS / deploy (push) Successful in 34s
Budget: recurring items CRUD, yearly/monthly projections with no-double-count logic, and full UI (overview, monthly detail, recurring items manager). Push notifications: Web Push via VAPID keys, triggered on transaction creation from n8n. Includes service worker handlers, frontend subscription flow, and a test button on the Dashboard (temporary). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
97
frontend/src/components/budget/YearlyOverview.tsx
Normal file
97
frontend/src/components/budget/YearlyOverview.tsx
Normal file
@@ -0,0 +1,97 @@
|
||||
import { type MonthlyProjection } from '@/api';
|
||||
import { formatAmount } from '@/lib/format';
|
||||
import { cn } from '@/lib/utils';
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableHeader,
|
||||
TableRow,
|
||||
} from '@/components/ui/table';
|
||||
|
||||
const MONTH_NAMES = [
|
||||
'', 'Ene', 'Feb', 'Mar', 'Abr', 'May', 'Jun',
|
||||
'Jul', 'Ago', 'Sep', 'Oct', 'Nov', 'Dic',
|
||||
];
|
||||
|
||||
interface YearlyOverviewProps {
|
||||
months: MonthlyProjection[];
|
||||
selectedMonth: number;
|
||||
onSelectMonth: (month: number) => void;
|
||||
}
|
||||
|
||||
export default function YearlyOverview({
|
||||
months,
|
||||
selectedMonth,
|
||||
onSelectMonth,
|
||||
}: YearlyOverviewProps) {
|
||||
const currentMonth = new Date().getMonth() + 1;
|
||||
const currentYear = new Date().getFullYear();
|
||||
|
||||
return (
|
||||
<div className="overflow-x-auto">
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead>Mes</TableHead>
|
||||
<TableHead className="text-right">Ingresos</TableHead>
|
||||
<TableHead className="text-right">Egresos Fijos</TableHead>
|
||||
<TableHead className="text-right">Otros Gastos</TableHead>
|
||||
<TableHead className="text-right">Gran Total</TableHead>
|
||||
<TableHead className="text-right">Ahorro</TableHead>
|
||||
<TableHead className="text-right">Balance</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{months.map((m) => {
|
||||
const isSelected = m.month === selectedMonth;
|
||||
const isCurrent = m.month === currentMonth && m.year === currentYear;
|
||||
return (
|
||||
<TableRow
|
||||
key={m.month}
|
||||
className={cn(
|
||||
'cursor-pointer transition-colors',
|
||||
isSelected && 'bg-accent',
|
||||
isCurrent && !isSelected && 'bg-accent/40',
|
||||
)}
|
||||
onClick={() => onSelectMonth(m.month)}
|
||||
>
|
||||
<TableCell className="font-medium">
|
||||
{MONTH_NAMES[m.month]}
|
||||
{isCurrent && (
|
||||
<span className="ml-1.5 inline-block w-1.5 h-1.5 rounded-full bg-primary" />
|
||||
)}
|
||||
</TableCell>
|
||||
<TableCell className="text-right font-mono text-sm text-primary">
|
||||
{formatAmount(m.projected_income, 'CRC')}
|
||||
</TableCell>
|
||||
<TableCell className="text-right font-mono text-sm">
|
||||
{formatAmount(m.projected_fixed_expenses, 'CRC')}
|
||||
</TableCell>
|
||||
<TableCell className="text-right font-mono text-sm text-muted-foreground">
|
||||
{formatAmount(m.uncovered_actual, 'CRC')}
|
||||
</TableCell>
|
||||
<TableCell className="text-right font-mono text-sm font-medium">
|
||||
{formatAmount(m.gran_total_egresos, 'CRC')}
|
||||
</TableCell>
|
||||
<TableCell className="text-right font-mono text-sm text-muted-foreground">
|
||||
{formatAmount(m.projected_savings, 'CRC')}
|
||||
</TableCell>
|
||||
<TableCell
|
||||
className={cn(
|
||||
'text-right font-mono text-sm font-semibold',
|
||||
m.net_balance >= 0 ? 'text-primary' : 'text-destructive',
|
||||
)}
|
||||
>
|
||||
{m.net_balance >= 0 ? '+' : ''}
|
||||
{formatAmount(m.net_balance, 'CRC')}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
);
|
||||
})}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user