'use client'; import { useCallback, useEffect, useRef, useState } from 'react'; import useAuthStore from '../../../store/authStore'; export type AdminInvoice = { id: string | number; invoice_number?: string | null; user_id?: string | number | null; buyer_name?: string | null; buyer_email?: string | null; buyer_street?: string | null; buyer_postal_code?: string | null; buyer_city?: string | null; buyer_country?: string | null; currency?: string | null; total_net?: number | null; total_tax?: number | null; total_gross?: number | null; vat_rate?: number | null; status?: string; issued_at?: string | null; due_at?: string | null; pdf_storage_key?: string | null; context?: any | null; created_at?: string | null; updated_at?: string | null; }; export type AdminInvoiceRevenueSummary = { totalPaidAllTime: number; currency?: string | null; paidInvoiceCount?: number; }; export function useAdminInvoices(params?: { status?: string; limit?: number; offset?: number }) { const accessToken = useAuthStore(s => s.accessToken); const [invoices, setInvoices] = useState([]); const [loading, setLoading] = useState(false); const [error, setError] = useState(''); const inFlight = useRef(null); const fetchInvoices = useCallback(async () => { setError(''); // Abort previous inFlight.current?.abort(); const controller = new AbortController(); inFlight.current = controller; try { const base = process.env.NEXT_PUBLIC_API_BASE_URL || ''; const qp = new URLSearchParams(); if (params?.status) qp.set('status', params.status); qp.set('limit', String(params?.limit ?? 200)); qp.set('offset', String(params?.offset ?? 0)); const url = `${base}/api/admin/invoices${qp.toString() ? `?${qp.toString()}` : ''}`; setLoading(true); const res = await fetch(url, { method: 'GET', credentials: 'include', headers: { 'Accept': 'application/json', ...(accessToken ? { Authorization: `Bearer ${accessToken}` } : {}), }, signal: controller.signal, }); const body = await res.json().catch(() => ({})); if (!res.ok || body?.success === false) { setInvoices([]); setError(body?.message || `Failed to load invoices (${res.status})`); return; } const list: AdminInvoice[] = Array.isArray(body?.data) ? body.data : []; // sort fallback (issued_at DESC then created_at DESC) list.sort((a, b) => { const ad = new Date(a.issued_at ?? a.created_at ?? 0).getTime(); const bd = new Date(b.issued_at ?? b.created_at ?? 0).getTime(); return bd - ad; }); setInvoices(list); } catch (e: any) { if (e?.name === 'AbortError') return; setError(e?.message || 'Network error'); setInvoices([]); } finally { setLoading(false); if (inFlight.current === controller) inFlight.current = null; } }, [accessToken, params?.status, params?.limit, params?.offset]); useEffect(() => { if (accessToken) fetchInvoices(); return () => inFlight.current?.abort(); }, [accessToken, fetchInvoices]); return { invoices, loading, error, reload: fetchInvoices }; } export function useAdminInvoiceRevenueSummary() { const accessToken = useAuthStore(s => s.accessToken); const [summary, setSummary] = useState(null); const [loading, setLoading] = useState(false); const [error, setError] = useState(''); const inFlight = useRef(null); const fetchSummary = useCallback(async () => { setError(''); inFlight.current?.abort(); const controller = new AbortController(); inFlight.current = controller; try { const base = process.env.NEXT_PUBLIC_API_BASE_URL || ''; const url = `${base}/api/admin/invoices/revenue-summary`; setLoading(true); const res = await fetch(url, { method: 'GET', credentials: 'include', headers: { Accept: 'application/json', ...(accessToken ? { Authorization: `Bearer ${accessToken}` } : {}), }, signal: controller.signal, }); const body = await res.json().catch(() => ({})); if (!res.ok || body?.success === false) { setSummary(null); setError(body?.message || `Failed to load revenue summary (${res.status})`); return; } setSummary(body?.data || { totalPaidAllTime: 0, currency: 'EUR', paidInvoiceCount: 0 }); } catch (e: any) { if (e?.name === 'AbortError') return; setError(e?.message || 'Network error'); setSummary(null); } finally { setLoading(false); if (inFlight.current === controller) inFlight.current = null; } }, [accessToken]); useEffect(() => { if (accessToken) fetchSummary(); return () => inFlight.current?.abort(); }, [accessToken, fetchSummary]); return { summary, loading, error, reload: fetchSummary }; }