import { createContext, useContext, useState, useCallback, useEffect, useRef, type ReactNode } from "react"; import { usePeriodicExpenses, useCreatePeriodicExpense, useUpdatePeriodicExpense, useDeletePeriodicExpense, useGenerateMonthlyExpense, useExpenses, useCreateNonPeriodicExpense, usePayExpense, useMonthlyTotals, useTotalPending, useImportPeriodicExpenses, useImportExpenses, } from "@/lib/queries"; import type { PeriodicExpense, Expense, CreatePeriodicExpenseInput, CreateNonPeriodicExpenseInput, PayExpenseInput, PaginatedResponse, MonthlyTotals, MonthlyExpensesTotal, ImportResult, ImportExpensesResult, } from "@/lib/api"; interface ExpensesContextValue { periodicExpenses: PeriodicExpense[]; isLoadingPeriodic: boolean; expenses: PaginatedResponse; isLoadingExpenses: boolean; statusFilter: string; setStatusFilter: (status: string) => void; page: number; setPage: (page: number) => void; pageSize: number; periodicExpenseFilter: number | undefined; setPeriodicExpenseFilter: (id: number | undefined) => void; searchInput: string; setSearchInput: (value: string) => void; monthlyTotals: MonthlyTotals | undefined; isLoadingTotals: boolean; totalPending: MonthlyExpensesTotal | undefined; isLoadingTotalPending: boolean; createPeriodicExpense: (data: CreatePeriodicExpenseInput) => Promise; updatePeriodicExpense: (id: number, data: CreatePeriodicExpenseInput) => Promise; deletePeriodicExpense: (id: number) => Promise; generateMonthlyExpense: (id: number) => Promise; createNonPeriodicExpense: (data: CreateNonPeriodicExpenseInput) => Promise; payExpense: (id: number, data: PayExpenseInput) => Promise; importPeriodicExpenses: (file: File) => Promise; importExpenses: (file: File) => Promise; } const ExpensesContext = createContext(null); export function ExpensesProvider({ children }: { children: ReactNode }) { const [statusFilter, setStatusFilter] = useState("PENDING"); const [page, setPage] = useState(1); const pageSize = 10; const [periodicExpenseFilter, setPeriodicExpenseFilter] = useState(undefined); const [searchInput, setSearchInput] = useState(""); const [searchQuery, setSearchQuery] = useState(""); const searchTimerRef = useRef>(undefined); useEffect(() => { if (searchTimerRef.current) clearTimeout(searchTimerRef.current); searchTimerRef.current = setTimeout(() => { setSearchQuery(searchInput); }, 300); return () => { if (searchTimerRef.current) clearTimeout(searchTimerRef.current); }; }, [searchInput]); useEffect(() => { setPage(1); }, [periodicExpenseFilter, searchQuery]); const { data: periodicExpenses = [], isLoading: isLoadingPeriodic } = usePeriodicExpenses(); const { data: expenses = { data: [], total: 0, page: 1, pageSize: 10 }, isLoading: isLoadingExpenses } = useExpenses(statusFilter, page, pageSize, periodicExpenseFilter, searchQuery || undefined); const now = new Date(); const { data: monthlyTotals, isLoading: isLoadingTotals } = useMonthlyTotals(now.getFullYear(), now.getMonth() + 1); const { data: totalPending, isLoading: isLoadingTotalPending } = useTotalPending(); const createPeriodicMutation = useCreatePeriodicExpense(); const updatePeriodicMutation = useUpdatePeriodicExpense(); const deletePeriodicMutation = useDeletePeriodicExpense(); const generateMonthlyMutation = useGenerateMonthlyExpense(); const createExpenseMutation = useCreateNonPeriodicExpense(); const payExpenseMutation = usePayExpense(); const importPeriodicMutation = useImportPeriodicExpenses(); const importExpensesMutation = useImportExpenses(); const importPeriodicExpensesFn = useCallback( async (file: File): Promise => { return importPeriodicMutation.mutateAsync(file); }, [importPeriodicMutation], ); const importExpensesFn = useCallback( async (file: File): Promise => { return importExpensesMutation.mutateAsync(file); }, [importExpensesMutation], ); const createPeriodicExpense = useCallback( async (data: CreatePeriodicExpenseInput): Promise => { return createPeriodicMutation.mutateAsync(data); }, [createPeriodicMutation], ); const updatePeriodicExpenseFn = useCallback( async (id: number, data: CreatePeriodicExpenseInput) => { await updatePeriodicMutation.mutateAsync({ id, data }); }, [updatePeriodicMutation], ); const deletePeriodicExpenseFn = useCallback( async (id: number) => { await deletePeriodicMutation.mutateAsync(id); }, [deletePeriodicMutation], ); const generateMonthlyExpenseFn = useCallback( async (id: number): Promise => { return generateMonthlyMutation.mutateAsync(id); }, [generateMonthlyMutation], ); const createNonPeriodicExpenseFn = useCallback( async (data: CreateNonPeriodicExpenseInput) => { await createExpenseMutation.mutateAsync(data); }, [createExpenseMutation], ); const payExpenseFn = useCallback( async (id: number, data: PayExpenseInput) => { await payExpenseMutation.mutateAsync({ id, data }); }, [payExpenseMutation], ); return ( {children} ); } export function useExpensesContext() { const ctx = useContext(ExpensesContext); if (!ctx) throw new Error("useExpensesContext must be used within ExpensesProvider"); return ctx; }