import { useState, useEffect, useCallback } from 'react'; import { Card } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from '@/components/ui/table'; import { Select } from '@/components/ui/select'; import { Input } from '@/components/ui/input'; import { Skeleton } from '@/components/ui/skeleton'; import { ErrorDisplay } from '@/components/ui/ErrorDisplay'; import { RefreshCcw, ChevronLeft, ChevronRight } from 'lucide-react'; import { commerceService, type SellerTransfer } from '@/services/commerceService'; import { useToast } from '@/components/feedback/ToastProvider'; const LIMIT = 20; function statusBadgeClass(status: string): string { switch (status) { case 'completed': return 'bg-success/20 text-success'; case 'failed': return 'bg-destructive/20 text-destructive'; case 'permanently_failed': return 'bg-muted text-muted-foreground'; case 'pending': return 'bg-warning/20 text-warning'; case 'skipped': return 'bg-primary/20 text-primary'; default: return 'bg-muted text-muted-foreground'; } } function truncateId(id: string): string { if (!id || id.length < 12) return id; return `${id.slice(0, 8)}…`; } export function AdminTransfersView() { const { addToast } = useToast(); const [transfers, setTransfers] = useState([]); const [total, setTotal] = useState(0); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [statusFilter, setStatusFilter] = useState('all'); const [sellerIdFilter, setSellerIdFilter] = useState(''); const [offset, setOffset] = useState(0); const [retrying, setRetrying] = useState(null); useEffect(() => { setOffset(0); }, [statusFilter, sellerIdFilter]); const fetchData = useCallback(async () => { setLoading(true); setError(null); try { const params: Record = { limit: LIMIT, offset, }; if (statusFilter && statusFilter !== 'all') params.status = statusFilter; if (sellerIdFilter.trim()) params.seller_id = sellerIdFilter.trim(); const res = await commerceService.getAdminTransfers(params); setTransfers(res.transfers); setTotal(res.total); } catch (e) { setError(e instanceof Error ? e : new Error(String(e))); setTransfers([]); setTotal(0); } finally { setLoading(false); } }, [offset, statusFilter, sellerIdFilter]); useEffect(() => { fetchData(); }, [fetchData]); const handleRetry = async (transferId: string) => { setRetrying(transferId); try { await commerceService.retryAdminTransfer(transferId); addToast({ message: 'Transfer retried successfully', type: 'success' }); fetchData(); } catch (e) { addToast({ message: e instanceof Error ? e.message : 'Retry failed', type: 'error', }); } finally { setRetrying(null); } }; const hasNext = offset + LIMIT < total; const hasPrev = offset > 0; if (error) { return (
); } return (

Platform Transfers

setSellerIdFilter(e.target.value)} className="w-48" />
{loading ? (
) : transfers.length === 0 ? (
No transfers found
) : ( <> Seller Order Net (EUR) Fee (EUR) Status Retries Date Actions {transfers.map((t) => ( {truncateId(t.seller_id ?? t.id)} {truncateId(t.order_id)} €{(t.amount_cents / 100).toFixed(2)} €{(t.platform_fee_cents / 100).toFixed(2)} {t.status} {t.retry_count ?? 0} {new Date(t.created_at).toLocaleDateString()} {t.status === 'failed' && ( )} ))}
{offset + 1}–{Math.min(offset + LIMIT, total)} of {total}
)}
); }