'use client'; import { useState } from 'react'; import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { FileText, Search, User, Wallet, CreditCard, XCircle, Download } from 'lucide-react'; import { notify } from '@/lib/notify'; import api from '@/lib/api'; import { useT, useLocale } from '@/i18n/I18nProvider'; import type { Invoice, InvoiceStatus, PaymentMethod } from '@/types'; import { Select } from '@/components/ui/select'; const statusClasses: Record = { draft: 'badge-gray', issued: 'badge-yellow', partially_paid: 'badge-blue', paid: 'badge-green', void: 'badge-gray', failed: 'badge-red', }; export default function AdminInvoicesPage() { const t = useT(); const inv = t.dashboard.invoices; const locale = useLocale(); const statusLabel = (s: InvoiceStatus) => (inv.status as Record)[s] ?? s; const queryClient = useQueryClient(); const [status, setStatus] = useState<'all' | InvoiceStatus>('all'); const [paymentMethod, setPaymentMethod] = useState<'all' | PaymentMethod>('all'); const [search, setSearch] = useState(''); const [selectedId, setSelectedId] = useState(null); const [statusReason, setStatusReason] = useState(''); const { data: invoices = [], isLoading } = useQuery({ queryKey: ['admin-invoices', status, paymentMethod, search], queryFn: () => { const params: Record = { limit: '200' }; if (status !== 'all') params.status = status; if (paymentMethod !== 'all') params.paymentMethod = paymentMethod; if (search.trim()) params.search = search.trim(); return api.get('/billing/admin/invoices', { params }).then((r) => r.data); }, }); const { data: selectedInvoice } = useQuery({ queryKey: ['admin-invoice', selectedId], queryFn: () => api.get(`/billing/admin/invoices/${selectedId}`).then((r) => r.data), enabled: !!selectedId, }); const updateStatusMutation = useMutation({ mutationFn: ({ invoiceId, nextStatus, reason }: { invoiceId: string; nextStatus: InvoiceStatus; reason: string }) => api.patch(`/billing/admin/invoices/${invoiceId}/status`, { status: nextStatus, reason }).then((r) => r.data), onSuccess: () => { notify.success(inv.statusUpdated); setStatusReason(''); queryClient.invalidateQueries({ queryKey: ['admin-invoices'] }); queryClient.invalidateQueries({ queryKey: ['admin-invoice', selectedId] }); }, onError: (err: any) => notify.error(err, inv.statusUpdateFailed), }); const downloadPdfMutation = useMutation({ mutationFn: async (invoice: Invoice) => { const { data } = await api.get(`/billing/admin/invoices/${invoice.id}/pdf`, { responseType: 'blob' }); const url = window.URL.createObjectURL(new Blob([data], { type: 'application/pdf' })); const link = document.createElement('a'); link.href = url; link.download = `${invoice.invoiceNumber}.pdf`; document.body.appendChild(link); link.click(); link.remove(); window.URL.revokeObjectURL(url); }, onError: () => notify.error(inv.downloadFailed), }); const formatPrice = (amount: number) => Number(amount || 0).toLocaleString('en-US'); const formatDate = (value?: string) => value ? new Date(value).toLocaleString(locale) : '-'; const handleStatusUpdate = (nextStatus: InvoiceStatus) => { if (!selectedInvoice) return; if (!statusReason.trim()) { notify.error(inv.reasonRequired); return; } updateStatusMutation.mutate({ invoiceId: selectedInvoice.id, nextStatus, reason: statusReason.trim(), }); }; return (

{inv.adminTitle}

{inv.adminSubtitle}

setSearch(e.target.value)} placeholder={inv.searchPlaceholder} className="input-field pl-9 rtl:pl-3 rtl:pr-9 w-full" />
setPaymentMethod(v as any)} options={[ { value: 'all', label: inv.allMethods }, { value: 'wallet', label: inv.methodWallet }, { value: 'gateway', label: inv.methodGateway }, { value: 'mixed', label: inv.methodMixed }, ]} />
{isLoading ? ( ) : invoices.length === 0 ? ( ) : invoices.map((invoice) => ( setSelectedId(invoice.id)} className={`cursor-pointer hover:bg-gray-50 ${selectedId === invoice.id ? 'bg-primary-50' : ''}`} > ))}
{inv.colInvoice} {inv.colUser} {inv.colStatus} {inv.colMethod} {inv.colDue}
{t.common.loading}
{inv.noInvoices}

{invoice.invoiceNumber}

{invoice.application?.name || invoice.reason} · {formatDate(invoice.createdAt)}

{invoice.user?.email || invoice.userId}

{statusLabel(invoice.status)} {invoice.paymentMethod || '-'}

{formatPrice(invoice.dueAmount)} {t.common.currencyShort}

{inv.total} {formatPrice(invoice.total)} {t.common.currencyShort}

{!selectedInvoice ? (
{inv.selectInvoiceShort}
) : (

{selectedInvoice.invoiceNumber}

{selectedInvoice.application?.name || selectedInvoice.reason}

{statusLabel(selectedInvoice.status)}

{inv.total}

{formatPrice(selectedInvoice.total)} {t.common.currencyShort}

{inv.due}

{formatPrice(selectedInvoice.dueAmount)} {t.common.currencyShort}

{selectedInvoice.user?.email || selectedInvoice.userId}
{inv.tracking}: {selectedInvoice.gatewayTrackingCode || '-'}
{inv.method}: {selectedInvoice.paymentMethod || '-'}

{inv.lineItems}

{(selectedInvoice.lines || []).map((line) => (

{line.label}

{line.description &&

{line.description}

}
{formatPrice(line.amount)} {t.common.currencyShort}
))}

{inv.transactions}

{(selectedInvoice.transactions || []).length === 0 ? (

{inv.noTransactions}

) : (
{(selectedInvoice.transactions || []).map((tx) => (
{tx.description || tx.type} {formatPrice(tx.amount)} {t.common.currencyShort}

{tx.type} · {formatDate(tx.createdAt)}

))}
)}

{inv.manualStatusChange}