import { useState, useCallback } from 'react'; import { ChevronLeft, ChevronRight, Calculator } from 'lucide-react'; import { useQuery, useQueryClient } from '@tanstack/react-query'; import { toast } from 'sonner'; import api, { type Transaction } from '@/lib/api'; import { MONTH_NAMES_ES as MONTH_NAMES } from '@/lib/dates'; import { useBudget } from '@/hooks/useBudget'; import { Button } from '@/components/ui/button'; import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; import ErrorState from '@/components/ErrorState'; import MonthlyDetail from '@/components/budget/MonthlyDetail'; import RecurringItemsManager from '@/components/budget/RecurringItemsManager'; import TransactionList from '@/components/TransactionList'; const MIN_YEAR = 2026; const MAX_YEAR = 2030; export default function Budget() { const currentYear = Math.max(MIN_YEAR, new Date().getFullYear()); const { year, setYear, selectedMonth, setSelectedMonth, monthDetail, recurringItems, monthLoading, error: budgetError, addItem, updateItem, deleteItem, refresh, } = useBudget(currentYear); const queryClient = useQueryClient(); const [subTab, setSubTab] = useState<'detail' | 'transactions'>('detail'); const [txSearch, setTxSearch] = useState(''); const [txSource, setTxSource] = useState<'CREDIT_CARD' | 'CASH_AND_TRANSFER'>('CREDIT_CARD'); const txQuery = useQuery({ queryKey: ['transactions', 'budget', year, selectedMonth, txSource, txSearch], queryFn: async ({ signal }) => { const params: Record = { search: txSearch || undefined, limit: 200, }; if (txSource === 'CREDIT_CARD') { params.source = 'CREDIT_CARD'; // Credit card: billing cycle that ends around the 18th of selectedMonth const prevMonth = selectedMonth === 1 ? 12 : selectedMonth - 1; const prevYear = selectedMonth === 1 ? year - 1 : year; params.cycle_year = prevYear; params.cycle_month = prevMonth; } else { // Cash + Transfer merged: calendar month, exclude credit card params.exclude_source = 'CREDIT_CARD'; const startDate = `${year}-${String(selectedMonth).padStart(2, '0')}-01`; const endMonth = selectedMonth === 12 ? 1 : selectedMonth + 1; const endYear = selectedMonth === 12 ? year + 1 : year; const endDate = `${endYear}-${String(endMonth).padStart(2, '0')}-01`; params.start_date = startDate; params.end_date = endDate; } const { data } = await api.get('/transactions/', { params, signal }); const INCOME_TYPES = ['DEPOSITO', 'SALARY']; return data.filter((tx) => !INCOME_TYPES.includes(tx.transaction_type)); }, placeholderData: (prev) => prev, }); const transactions = txQuery.data ?? []; const invalidateTransactionsAndBudget = useCallback(() => { queryClient.invalidateQueries({ queryKey: ['transactions'] }); refresh(); }, [queryClient, refresh]); const handleToggleDeferred = useCallback(async (tx: Transaction) => { try { await api.patch(`/transactions/${tx.id}`, { deferred_to_next_cycle: !tx.deferred_to_next_cycle, }); toast.success( tx.deferred_to_next_cycle ? 'Transacción restaurada a su ciclo original' : 'Transacción diferida al siguiente ciclo', ); } catch { toast.error('No se pudo actualizar la transacción'); return; } invalidateTransactionsAndBudget(); }, [invalidateTransactionsAndBudget]); const handleNavigateToTransactions = useCallback(() => { setTxSource('CASH_AND_TRANSFER'); setSubTab('transactions'); }, []); return (
{/* Header */}

Presupuesto

{year}
Resumen Items Recurrentes setSubTab(v as typeof subTab)} >
Detalle Transacciones
{MONTH_NAMES[selectedMonth]} {year}
{budgetError ? ( ) : ( )} setTxSource(v as typeof txSource)} > Tarjeta Efectivo y Transferencias {txQuery.isError ? ( txQuery.refetch()} /> ) : ( )}
); }