import { useState } from 'react'; import { useNavigate } from 'react-router'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { useTranslation } from 'react-i18next'; import { adminPaymentsApi } from '../api/adminPayments'; import { useCurrency } from '../hooks/useCurrency'; import type { PendingPayment, PaginatedResponse } from '../types'; import { usePlatform } from '../platform/hooks/usePlatform'; // BackIcon const BackIcon = () => ( ); export default function AdminPayments() { const { t } = useTranslation(); const navigate = useNavigate(); const queryClient = useQueryClient(); const { formatAmount, currencySymbol } = useCurrency(); const { capabilities } = usePlatform(); const [page, setPage] = useState(1); const [methodFilter, setMethodFilter] = useState(''); const [checkingPaymentId, setCheckingPaymentId] = useState(null); // Fetch payments const { data: payments, isLoading, refetch, } = useQuery>({ queryKey: ['admin-payments', page, methodFilter], queryFn: () => adminPaymentsApi.getPendingPayments({ page, per_page: 20, method_filter: methodFilter || undefined, }), refetchInterval: 30000, // Auto-refresh every 30 seconds }); // Fetch stats const { data: stats } = useQuery({ queryKey: ['admin-payments-stats'], queryFn: adminPaymentsApi.getStats, refetchInterval: 30000, }); // Check payment mutation const checkPaymentMutation = useMutation({ mutationFn: ({ method, paymentId }: { method: string; paymentId: number }) => adminPaymentsApi.checkPaymentStatus(method, paymentId), onSuccess: async (result) => { if (result.status_changed) { await refetch(); queryClient.invalidateQueries({ queryKey: ['admin-payments-stats'] }); } else { await refetch(); } }, onSettled: () => { setCheckingPaymentId(null); }, }); const handleCheckPayment = (payment: PendingPayment) => { setCheckingPaymentId(`${payment.method}_${payment.id}`); checkPaymentMutation.mutate({ method: payment.method, paymentId: payment.id }); }; // Get unique methods from stats for filter const methodOptions = stats?.by_method ? Object.keys(stats.by_method) : []; return ( {/* Header */} {/* Show back button only on web, not in Telegram Mini App */} {!capabilities.hasBackButton && ( navigate('/admin')} className="flex h-10 w-10 items-center justify-center rounded-xl border border-dark-700 bg-dark-800 transition-colors hover:border-dark-600" > )} {t('admin.payments.title')} {t('admin.payments.description')} refetch()} className="btn-secondary flex items-center gap-2"> {t('common.refresh')} {/* Stats cards */} {stats && ( {stats.total_pending} {t('admin.payments.totalPending')} {Object.entries(stats.by_method).map(([method, count]) => ( setMethodFilter(methodFilter === method ? '' : method)} > {count} {method} ))} )} {/* Filter */} {methodOptions.length > 0 && ( {t('admin.payments.filterByMethod')}: setMethodFilter('')} className={`rounded-lg px-3 py-1.5 text-sm transition-all ${ methodFilter === '' ? 'bg-accent-500 text-white' : 'bg-dark-800 text-dark-300 hover:bg-dark-700' }`} > {t('common.all')} {methodOptions.map((method) => ( setMethodFilter(method)} className={`rounded-lg px-3 py-1.5 text-sm transition-all ${ methodFilter === method ? 'bg-accent-500 text-white' : 'bg-dark-800 text-dark-300 hover:bg-dark-700' }`} > {method} ))} )} {/* Payments list */} {isLoading ? ( ) : payments?.items && payments.items.length > 0 ? ( {payments.items.map((payment) => { const paymentKey = `${payment.method}_${payment.id}`; const isChecking = checkingPaymentId === paymentKey; return ( {payment.method_display} {payment.status_emoji} {payment.status_text} {payment.is_paid && ( {t('admin.payments.paid')} )} {formatAmount(payment.amount_rubles)} {currencySymbol} ID: {payment.identifier} {new Date(payment.created_at).toLocaleString()} {/* User info */} {(payment.user_username || payment.user_telegram_id) && ( {t('admin.payments.user')}:{' '} {payment.user_username ? ( @{payment.user_username} ) : ( ID: {payment.user_telegram_id} )} )} {payment.payment_url && ( {t('admin.payments.openLink')} )} {payment.is_checkable && ( handleCheckPayment(payment)} disabled={isChecking} className="btn-primary px-3 py-1.5 text-xs" > {isChecking ? ( {t('admin.payments.checking')} ) : ( t('admin.payments.checkStatus') )} )} {/* Show result after check */} {checkPaymentMutation.isSuccess && checkPaymentMutation.variables?.paymentId === payment.id && ( {checkPaymentMutation.data?.message} )} ); })} ) : ( {t('admin.payments.noPayments')} )} {/* Pagination */} {payments && payments.pages > 1 && ( setPage((prev) => Math.max(1, prev - 1))} disabled={payments.page <= 1} className={`btn-secondary min-w-[100px] flex-1 text-xs sm:flex-none sm:text-sm ${ payments.page <= 1 ? 'cursor-not-allowed opacity-50' : '' }`} > {t('admin.payments.prev')} {t('balance.page', '{current} / {total}', { current: payments.page, total: payments.pages, })} setPage((prev) => Math.min(payments.pages, prev + 1))} disabled={payments.page >= payments.pages} className={`btn-secondary min-w-[100px] flex-1 text-xs sm:flex-none sm:text-sm ${ payments.page >= payments.pages ? 'cursor-not-allowed opacity-50' : '' }`} > {t('admin.payments.next')} )} ); }
{t('admin.payments.description')}
{payment.identifier}