import { useState } from 'react'; import { useQuery, useQueryClient } from '@tanstack/react-query'; import { toast } from 'sonner'; import { type RecurringItemCreate, type RecurringItemUpdate, getYearlyProjection, getMonthlyDetail, getRecurringItems, createRecurringItem, updateRecurringItem as apiUpdateItem, deleteRecurringItem as apiDeleteItem, upsertBalanceOverride, deleteBalanceOverride, } from '@/lib/api'; /** All budget data hangs off the ['budget', ...] key family; every mutation * invalidates the family so projection, month detail and items stay in sync. */ export function useBudget(initialYear: number) { const queryClient = useQueryClient(); const [year, setYear] = useState(initialYear); const [selectedMonth, setSelectedMonth] = useState( new Date().getMonth() + 1, ); const projectionQ = useQuery({ queryKey: ['budget', 'projection', year], queryFn: () => getYearlyProjection(year).then((r) => r.data), }); const monthDetailQ = useQuery({ queryKey: ['budget', 'month', year, selectedMonth], queryFn: () => getMonthlyDetail(year, selectedMonth).then((r) => r.data), placeholderData: (prev) => prev, // keep last month visible while navigating }); const recurringQ = useQuery({ queryKey: ['budget', 'recurring'], queryFn: () => getRecurringItems().then((r) => r.data), }); const invalidate = () => queryClient.invalidateQueries({ queryKey: ['budget'] }); async function mutateWithToast( action: () => Promise, success: string, failure: string, ) { try { await action(); } catch (err) { toast.error(failure); throw err; } toast.success(success); await invalidate(); } return { year, setYear, selectedMonth, setSelectedMonth, projection: projectionQ.data ?? null, monthDetail: monthDetailQ.data ?? null, recurringItems: recurringQ.data ?? [], loading: projectionQ.isPending, monthLoading: monthDetailQ.isFetching, error: projectionQ.error ?? monthDetailQ.error ?? recurringQ.error ?? null, addItem: (data: RecurringItemCreate) => mutateWithToast( () => createRecurringItem(data), 'Item recurrente creado', 'No se pudo crear el item', ), updateItem: (id: number, data: RecurringItemUpdate) => mutateWithToast( () => apiUpdateItem(id, data), 'Item recurrente actualizado', 'No se pudo actualizar el item', ), deleteItem: (id: number) => mutateWithToast( () => apiDeleteItem(id), 'Item recurrente eliminado', 'No se pudo eliminar el item', ), saveBalanceOverride: (overrideYear: number, month: number, value: number) => mutateWithToast( () => upsertBalanceOverride(overrideYear, month, value), 'Balance ajustado', 'No se pudo ajustar el balance', ), clearBalanceOverride: (overrideYear: number, month: number) => mutateWithToast( () => deleteBalanceOverride(overrideYear, month), 'Ajuste de balance eliminado', 'No se pudo eliminar el ajuste', ), refresh: invalidate, }; }