profit-planet-frontend/src/app/admin/finance-management/hooks/getInvoices.ts
seaznCode bcc953edc1 iwd
2026-05-17 16:17:30 +02:00

154 lines
5.0 KiB
TypeScript

'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<AdminInvoice[]>([]);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string>('');
const inFlight = useRef<AbortController | null>(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<AdminInvoiceRevenueSummary | null>(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string>('');
const inFlight = useRef<AbortController | null>(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 };
}