import { useCallback, useEffect, useState } from "react"; import type { Entry } from "@/lib/shared/types"; import { entriesCreate, entriesDelete, entriesList, entriesUpdate } from "@/lib/client/entries"; import type { ApiResult } from "@/lib/client/fetch-json"; type CreateEntryInput = { entryType: "SPENDING" | "INCOME"; amountDollars: number; occurredAt: string; necessity: "NECESSARY" | "BOTH" | "UNNECESSARY"; purchaseType: string; notes?: string; tags?: string[]; bucketId?: number | null; isRecurring?: boolean; frequency?: "DAILY" | "WEEKLY" | "BIWEEKLY" | "MONTHLY" | "QUARTERLY" | "YEARLY" | null; intervalCount?: number; endCondition?: "NEVER" | "AFTER_COUNT" | "BY_DATE" | null; endCount?: number | null; endDate?: string | null; nextRunAt?: string | null; }; type UpdateEntryInput = CreateEntryInput & { id: number }; function isError(result: ApiResult): result is { error: { code: string; message: string } } { return "error" in result; } function compareEntriesDesc(a: Entry, b: Entry) { if (a.occurredAt === b.occurredAt) return Number(b.id) - Number(a.id); return a.occurredAt > b.occurredAt ? -1 : 1; } function upsertEntrySorted(entries: Entry[], next: Entry) { const without = entries.filter(entry => Number(entry.id) !== Number(next.id)); const merged = [next, ...without]; merged.sort(compareEntriesDesc); return merged; } export default function useEntries(activeGroupId?: number | null) { const [entries, setEntries] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(""); const load = useCallback(async () => { if (!activeGroupId) { setError(""); setEntries([]); setLoading(false); return; } setLoading(true); setError(""); const result = await entriesList(); if (isError(result)) { setError(result.error.message || ""); setEntries([]); } else { setEntries(result.data.entries || []); } setLoading(false); }, [activeGroupId]); const createEntry = useCallback(async (input: CreateEntryInput) => { setError(""); const result = await entriesCreate(input); if (isError(result)) { setError(result.error.message || ""); return null; } const created = result.data.entry; setEntries(prev => upsertEntrySorted(prev, created)); return created; }, []); const updateEntry = useCallback(async (input: UpdateEntryInput) => { setError(""); const result = await entriesUpdate(input); if (isError(result)) { setError(result.error.message || ""); return null; } const updated = result.data.entry; setEntries(prev => { return upsertEntrySorted(prev, updated); }); return updated; }, []); const deleteEntry = useCallback(async (id: number | string) => { setError(""); const numericId = Number(id); if (!Number.isFinite(numericId) || numericId <= 0) return null; let removed: Entry | null = null; const result = await entriesDelete({ id }); if (isError(result)) { setError(result.error.message || ""); return null; } setEntries(prev => { const index = prev.findIndex(entry => Number(entry.id) === numericId); if (index < 0) return prev; removed = prev[index]; return [...prev.slice(0, index), ...prev.slice(index + 1)]; }); return removed; }, []); useEffect(() => { load(); }, [load]); return { entries, loading, error, createEntry, updateEntry, deleteEntry, reload: load }; }