import { useState, useEffect } from 'react'; import { useParams, useNavigate } from 'react-router'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { useTranslation } from 'react-i18next'; import { adminPaymentMethodsApi } from '../api/adminPaymentMethods'; import { METHOD_LABELS } from '../constants/paymentMethods'; import type { PromoGroupSimple } from '../types'; import { usePlatform } from '../platform/hooks/usePlatform'; import { createNumberInputHandler, toNumber } from '../utils/inputHelpers'; import { BackIcon, CheckIcon, SaveIcon } from '@/components/icons'; export default function AdminPaymentMethodEdit() { const { t } = useTranslation(); const { methodId } = useParams<{ methodId: string }>(); const navigate = useNavigate(); const queryClient = useQueryClient(); const { capabilities } = usePlatform(); // Fetch payment methods const { data: methods, isLoading } = useQuery({ queryKey: ['admin-payment-methods'], queryFn: adminPaymentMethodsApi.getAll, }); // Fetch promo groups const { data: promoGroups = [] } = useQuery({ queryKey: ['admin-payment-methods-promo-groups'], queryFn: adminPaymentMethodsApi.getPromoGroups, }); const config = methods?.find((m) => m.method_id === methodId); // Local state for editing const [isEnabled, setIsEnabled] = useState(false); const [customName, setCustomName] = useState(''); const [subOptions, setSubOptions] = useState>({}); const [minAmount, setMinAmount] = useState(''); const [maxAmount, setMaxAmount] = useState(''); const [userTypeFilter, setUserTypeFilter] = useState<'all' | 'telegram' | 'email'>('all'); const [firstTopupFilter, setFirstTopupFilter] = useState<'any' | 'yes' | 'no'>('any'); const [promoGroupFilterMode, setPromoGroupFilterMode] = useState<'all' | 'selected'>('all'); const [selectedPromoGroupIds, setSelectedPromoGroupIds] = useState([]); const [openUrlDirect, setOpenUrlDirect] = useState(false); const [quickAmounts, setQuickAmounts] = useState([]); const [quickAmountInput, setQuickAmountInput] = useState(''); const [quickAmountsError, setQuickAmountsError] = useState(null); // Initialize state when config loads useEffect(() => { if (config) { setIsEnabled(config.is_enabled); setCustomName(config.display_name || ''); setSubOptions(config.sub_options || {}); setMinAmount(config.min_amount_kopeks ?? ''); setMaxAmount(config.max_amount_kopeks ?? ''); setUserTypeFilter(config.user_type_filter); setFirstTopupFilter(config.first_topup_filter); setPromoGroupFilterMode(config.promo_group_filter_mode); setSelectedPromoGroupIds(config.allowed_promo_group_ids); // ?? false — защита от stale-config (backend ещё не пришёл с миграцией) setOpenUrlDirect(config.open_url_direct ?? false); setQuickAmounts((config.quick_amounts ?? []).map((kopeks) => kopeks / 100)); } }, [config]); // Update method mutation const updateMethodMutation = useMutation({ mutationFn: (data: Record) => adminPaymentMethodsApi.update(methodId!, data), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['admin-payment-methods'] }); navigate('/admin/payment-methods'); }, }); const handleSave = () => { if (!config) return; const data: Record = { is_enabled: isEnabled, user_type_filter: userTypeFilter, first_topup_filter: firstTopupFilter, promo_group_filter_mode: promoGroupFilterMode, allowed_promo_group_ids: promoGroupFilterMode === 'selected' ? selectedPromoGroupIds : [], open_url_direct: openUrlDirect, }; // Display name if (customName.trim()) { data.display_name = customName.trim(); } else { data.reset_display_name = true; } // Sub-options if (config.available_sub_options) { data.sub_options = subOptions; } // Amounts if (minAmount !== '') { data.min_amount_kopeks = toNumber(minAmount) || null; } else { data.reset_min_amount = true; } if (maxAmount !== '') { data.max_amount_kopeks = toNumber(maxAmount) || null; } else { data.reset_max_amount = true; } if (quickAmounts.length > 0) { data.quick_amounts = [...quickAmounts] .sort((a, b) => a - b) .map((rubles) => Math.round(rubles * 100)); } else { data.reset_quick_amounts = true; } updateMethodMutation.mutate(data); }; const togglePromoGroup = (id: number) => { setSelectedPromoGroupIds((prev) => prev.includes(id) ? prev.filter((x) => x !== id) : [...prev, id], ); }; const addQuickAmount = () => { setQuickAmountsError(null); const value = parseFloat(quickAmountInput.replace(',', '.')); if (isNaN(value) || value <= 0) { setQuickAmountsError(t('admin.paymentMethods.quickAmountsInvalid')); return; } if (quickAmounts.includes(value)) { setQuickAmountInput(''); return; } if (quickAmounts.length >= 10) { setQuickAmountsError(t('admin.paymentMethods.quickAmountsLimit')); return; } setQuickAmounts((prev) => [...prev, value].sort((a, b) => a - b)); setQuickAmountInput(''); }; const removeQuickAmount = (value: number) => { setQuickAmounts((prev) => prev.filter((amount) => amount !== value)); }; if (isLoading) { return (
); } if (!config) { return (
{/* Show back button only on web, not in Telegram Mini App */} {!capabilities.hasBackButton && ( )}

{t('admin.paymentMethods.notFound', 'Payment method not found')}

); } const displayName = config.display_name || config.default_display_name; return (
{/* Header */}
{/* Show back button only on web, not in Telegram Mini App */} {!capabilities.hasBackButton && ( )}

{displayName}

{METHOD_LABELS[config.method_id] || config.method_id}

{/* Form */}
{/* Enable toggle */}
{t('admin.paymentMethods.methodEnabled')}
{!config.is_provider_configured && (
{t('admin.paymentMethods.providerNotConfigured')}
)}
{/* Open URL directly toggle */}
{t('admin.paymentMethods.openUrlDirect', 'Открывать страницу оплаты сразу')}
{t( 'admin.paymentMethods.openUrlDirectHint', 'Без панели со ссылкой — провайдер открывается внутри MiniApp/вкладки сразу после клика. После оплаты юзер возвращается на /balance/result.', )}
{/* Display name */}
setCustomName(e.target.value)} placeholder={config.default_display_name} className="input" />

{t('admin.paymentMethods.displayNameHint')}: {config.default_display_name}

{/* Sub-options */} {config.available_sub_options && config.available_sub_options.length > 0 && (
{config.available_sub_options.map((opt) => { const enabled = subOptions[opt.id] !== false; return ( ); })}
)} {/* Min/Max amounts */}
{quickAmounts.length > 0 && (
{quickAmounts.map((value) => ( ))}
)}
setQuickAmountInput(e.target.value)} onKeyDown={(e) => { if (e.key === 'Enter') { e.preventDefault(); addQuickAmount(); } }} placeholder={t('admin.paymentMethods.quickAmountsPlaceholder')} className="input flex-1" />
{quickAmountsError &&

{quickAmountsError}

}

{t('admin.paymentMethods.quickAmountsHint', { defaults: config.default_quick_amounts.map((kopeks) => kopeks / 100).join(', '), })}

{/* Display conditions */}

{t('admin.paymentMethods.conditions')}

{/* User type filter */}
{(['all', 'telegram', 'email'] as const).map((val) => ( ))}
{/* First topup filter */}
{(['any', 'yes', 'no'] as const).map((val) => ( ))}
{/* Promo groups filter */}
{(['all', 'selected'] as const).map((val) => ( ))}
{promoGroupFilterMode === 'selected' && (
{promoGroups.length === 0 ? (

{t('admin.paymentMethods.noPromoGroups')}

) : ( promoGroups.map((group) => { const selected = selectedPromoGroupIds.includes(group.id); return ( ); }) )}
)}
{/* Actions */}
); }