import { useState, useCallback, useEffect } from 'react';
import { useNavigate } from 'react-router';
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { useTranslation } from 'react-i18next';
import { usePlatform } from '../platform/hooks/usePlatform';
import { useNotify } from '../platform/hooks/useNotify';
import {
DndContext,
KeyboardSensor,
PointerSensor,
useSensor,
useSensors,
type DragEndEvent,
} from '@dnd-kit/core';
import {
arrayMove,
SortableContext,
sortableKeyboardCoordinates,
useSortable,
verticalListSortingStrategy,
} from '@dnd-kit/sortable';
import { CSS } from '@dnd-kit/utilities';
import { adminPaymentMethodsApi } from '../api/adminPaymentMethods';
import type { PaymentMethodConfig } from '../types';
import { BackIcon, GripIcon, ChevronRightIcon, SaveIcon } from '@/components/icons';
interface SortableCardProps {
config: PaymentMethodConfig;
onClick: () => void;
}
function SortablePaymentCard({ config, onClick }: SortableCardProps) {
const { t } = useTranslation();
const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({
id: config.method_id,
});
const style: React.CSSProperties = {
transform: CSS.Transform.toString(transform),
transition,
zIndex: isDragging ? 50 : undefined,
position: isDragging ? 'relative' : undefined,
};
const displayName = config.display_name || config.default_display_name;
// Build condition summary chips
const chips: string[] = [];
if (config.user_type_filter === 'telegram')
chips.push(t('admin.paymentMethods.userTypeTelegram'));
if (config.user_type_filter === 'email') chips.push(t('admin.paymentMethods.userTypeEmail'));
if (config.first_topup_filter === 'yes') chips.push(t('admin.paymentMethods.firstTopupYes'));
if (config.first_topup_filter === 'no') chips.push(t('admin.paymentMethods.firstTopupNo'));
if (config.promo_group_filter_mode === 'selected' && config.allowed_promo_group_ids.length > 0) {
chips.push(
`${config.allowed_promo_group_ids.length} ${t('admin.paymentMethods.promoGroupsShort')}`,
);
}
// Count enabled sub-options
let subOptionsInfo = '';
if (config.available_sub_options && config.sub_options) {
const enabledCount = config.available_sub_options.filter(
(o) => config.sub_options?.[o.id] !== false,
).length;
const totalCount = config.available_sub_options.length;
if (enabledCount < totalCount) {
subOptionsInfo = `${enabledCount}/${totalCount}`;
}
}
return (
{/* Drag handle */}
{/* Drag handle - larger touch target for mobile */}
{/* Content */}
{displayName}
{config.is_enabled ? (
{t('admin.paymentMethods.enabled')}
) : (
{t('admin.paymentMethods.disabled')}
)}
{!config.is_provider_configured && (
{t('admin.paymentMethods.notConfigured')}
)}
{subOptionsInfo && (
{subOptionsInfo}
)}
{/* Condition chips */}
{chips.length > 0 && (
{chips.map((chip, i) => (
{chip}
))}
)}
{/* Chevron */}
);
}
export default function AdminPaymentMethods() {
const { t } = useTranslation();
const navigate = useNavigate();
const queryClient = useQueryClient();
const { capabilities } = usePlatform();
const notify = useNotify();
const [methods, setMethods] = useState([]);
const [orderChanged, setOrderChanged] = useState(false);
// Fetch payment methods
const { data: fetchedMethods, isLoading } = useQuery({
queryKey: ['admin-payment-methods'],
queryFn: adminPaymentMethodsApi.getAll,
});
// Sync fetched data to local state
useEffect(() => {
if (fetchedMethods && !orderChanged) {
setMethods(fetchedMethods);
}
}, [fetchedMethods, orderChanged]);
// Save order mutation
const saveOrderMutation = useMutation({
mutationFn: (methodIds: string[]) => adminPaymentMethodsApi.updateOrder(methodIds),
onSuccess: () => {
setOrderChanged(false);
queryClient.invalidateQueries({ queryKey: ['admin-payment-methods'] });
notify.success(t('admin.paymentMethods.orderSaved'));
},
onError: () => {
notify.error(t('common.error'));
},
});
// DnD sensors - PointerSensor handles both mouse and touch
const sensors = useSensors(
useSensor(PointerSensor, { activationConstraint: { distance: 5 } }),
useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates }),
);
const handleDragEnd = useCallback((event: DragEndEvent) => {
const { active, over } = event;
if (over && active.id !== over.id) {
setMethods((prev) => {
const oldIndex = prev.findIndex((m) => m.method_id === active.id);
const newIndex = prev.findIndex((m) => m.method_id === over.id);
if (oldIndex === -1 || newIndex === -1) return prev;
return arrayMove(prev, oldIndex, newIndex);
});
setOrderChanged(true);
}
}, []);
const handleSaveOrder = () => {
saveOrderMutation.mutate(methods.map((m) => m.method_id));
};
return (
{/* Header */}
{/* Show back button only on web, not in Telegram Mini App */}
{!capabilities.hasBackButton && (
)}
{t('admin.paymentMethods.title')}
{t('admin.paymentMethods.description')}
{orderChanged && (
)}
{/* Drag hint */}
{t('admin.paymentMethods.dragHint')}
{/* Methods list */}
{isLoading ? (
) : methods.length > 0 ? (
m.method_id)}
strategy={verticalListSortingStrategy}
>
{methods.map((config) => (
navigate(`/admin/payment-methods/${config.method_id}/edit`)}
/>
))}
) : (
{t('admin.paymentMethods.noMethods')}
)}
);
}