import { useState, useEffect, useRef } from 'react'; import { useTranslation } from 'react-i18next'; import { useQuery } from '@tanstack/react-query'; import { useNavigate } from 'react-router'; import { useCurrency } from '../../../hooks/useCurrency'; import { useNotify } from '../../../platform/hooks/useNotify'; import { adminUsersApi, type UserDetailResponse, type UserListItem } from '../../../api/adminUsers'; import { XIcon } from '@/components/icons'; // ────────────────────────────────────────────────────────────────── // Referrals tab — top-of-graph referrer + stats + referrals list, // plus inline search/assign/remove flows. All state stays local; // the parent only owns the user query and tells us when it refreshes. // ────────────────────────────────────────────────────────────────── export interface ReferralsTabProps { user: UserDetailResponse; userId: number; onUserRefresh: () => Promise | void; } export function ReferralsTab({ user, userId, onUserRefresh }: ReferralsTabProps) { const { t } = useTranslation(); const { formatWithCurrency } = useCurrency(); const navigate = useNavigate(); const notify = useNotify(); // Referrals list — owned here, not in the parent. const referralsListQuery = useQuery({ queryKey: ['admin-user-referrals-list', userId] as const, queryFn: () => adminUsersApi.getReferrals(userId, 0, 100), enabled: !!userId, }); const referralsList = referralsListQuery.data?.users ?? []; const referralsTotal = referralsListQuery.data?.total ?? 0; const referralsListLoading = referralsListQuery.isFetching; // Action gating — local so other tabs' buttons aren't dimmed during a // referral mutation here. const [actionLoading, setActionLoading] = useState(false); // Inline referrer search dropdown const [showReferrerSearch, setShowReferrerSearch] = useState(false); const [referrerSearchQuery, setReferrerSearchQuery] = useState(''); const [referrerSearchResults, setReferrerSearchResults] = useState([]); const [referrerSearchLoading, setReferrerSearchLoading] = useState(false); const referrerSearchRef = useRef(null); // Inline add-referral search dropdown const [showAddReferral, setShowAddReferral] = useState(false); const [addReferralSearchQuery, setAddReferralSearchQuery] = useState(''); const [addReferralSearchResults, setAddReferralSearchResults] = useState([]); const [addReferralSearchLoading, setAddReferralSearchLoading] = useState(false); const addReferralSearchRef = useRef(null); // Debounced search for referrer useEffect(() => { if (referrerSearchQuery.length < 2 || !showReferrerSearch) { setReferrerSearchResults([]); setReferrerSearchLoading(false); return; } setReferrerSearchLoading(true); setReferrerSearchResults([]); let cancelled = false; const timer = setTimeout(async () => { try { const data = await adminUsersApi.getUsers({ search: referrerSearchQuery, limit: 10 }); if (!cancelled) { setReferrerSearchResults(data.users || []); setReferrerSearchLoading(false); } } catch { if (!cancelled) { setReferrerSearchResults([]); setReferrerSearchLoading(false); } } }, 300); return () => { cancelled = true; clearTimeout(timer); }; }, [referrerSearchQuery, showReferrerSearch]); // Close referrer dropdown on click outside useEffect(() => { const handleClickOutside = (e: MouseEvent) => { if (referrerSearchRef.current && !referrerSearchRef.current.contains(e.target as Node)) { setShowReferrerSearch(false); setReferrerSearchQuery(''); setReferrerSearchResults([]); } }; if (showReferrerSearch) { document.addEventListener('mousedown', handleClickOutside); return () => document.removeEventListener('mousedown', handleClickOutside); } }, [showReferrerSearch]); // Debounced search for adding referral useEffect(() => { if (addReferralSearchQuery.length < 2 || !showAddReferral) { setAddReferralSearchResults([]); setAddReferralSearchLoading(false); return; } setAddReferralSearchLoading(true); setAddReferralSearchResults([]); let cancelled = false; const timer = setTimeout(async () => { try { const data = await adminUsersApi.getUsers({ search: addReferralSearchQuery, limit: 10 }); if (!cancelled) { setAddReferralSearchResults(data.users || []); setAddReferralSearchLoading(false); } } catch { if (!cancelled) { setAddReferralSearchResults([]); setAddReferralSearchLoading(false); } } }, 300); return () => { cancelled = true; clearTimeout(timer); }; }, [addReferralSearchQuery, showAddReferral]); // Close add-referral dropdown on click outside useEffect(() => { const handleClickOutside = (e: MouseEvent) => { if ( addReferralSearchRef.current && !addReferralSearchRef.current.contains(e.target as Node) ) { setShowAddReferral(false); setAddReferralSearchQuery(''); setAddReferralSearchResults([]); } }; if (showAddReferral) { document.addEventListener('mousedown', handleClickOutside); return () => document.removeEventListener('mousedown', handleClickOutside); } }, [showAddReferral]); // ─── Mutation handlers ─────────────────────────────────────────── const handleAssignReferrer = async (referrerId: number) => { setActionLoading(true); try { await adminUsersApi.assignReferrer(userId, referrerId); await onUserRefresh(); setShowReferrerSearch(false); setReferrerSearchQuery(''); setReferrerSearchResults([]); notify.success(t('admin.users.detail.referrals.referrerAssigned')); } catch (error: unknown) { const axiosErr = error as { response?: { data?: { detail?: string } } }; notify.error(axiosErr?.response?.data?.detail || t('common.error')); } finally { setActionLoading(false); } }; const handleRemoveReferrer = async () => { setActionLoading(true); try { await adminUsersApi.removeReferrer(userId); await onUserRefresh(); notify.success(t('admin.users.detail.referrals.referrerRemoved')); } catch (error: unknown) { const axiosErr = error as { response?: { data?: { detail?: string } } }; notify.error(axiosErr?.response?.data?.detail || t('common.error')); } finally { setActionLoading(false); } }; const handleRemoveReferral = async (referralUserId: number) => { setActionLoading(true); try { await adminUsersApi.removeReferral(userId, referralUserId); await referralsListQuery.refetch(); await onUserRefresh(); notify.success(t('admin.users.detail.referrals.referralRemoved')); } catch (error: unknown) { const axiosErr = error as { response?: { data?: { detail?: string } } }; notify.error(axiosErr?.response?.data?.detail || t('common.error')); } finally { setActionLoading(false); } }; const handleAddReferral = async (targetUserId: number) => { setActionLoading(true); try { await adminUsersApi.assignReferrer(targetUserId, userId); await referralsListQuery.refetch(); await onUserRefresh(); setShowAddReferral(false); setAddReferralSearchQuery(''); setAddReferralSearchResults([]); notify.success(t('admin.users.detail.referrals.referralAdded')); } catch (error: unknown) { const axiosErr = error as { response?: { data?: { detail?: string } } }; notify.error(axiosErr?.response?.data?.detail || t('common.error')); } finally { setActionLoading(false); } }; // ─── Render ────────────────────────────────────────────────────── return (
{/* Section 1: Who referred this user */}

{t('admin.users.detail.referrals.referredBy')}

{user.referral.referred_by_id ? (
) : (
{showReferrerSearch ? (
setReferrerSearchQuery(e.target.value)} placeholder={t('admin.users.detail.referrals.searchPlaceholder')} className="flex-1 rounded-lg border border-dark-600 bg-dark-700 px-3 py-2.5 text-sm text-dark-100 placeholder-dark-500 focus:border-accent-500 focus:outline-none" autoFocus />
{referrerSearchQuery.length >= 2 && referrerSearchResults.length > 0 && (
{referrerSearchResults .filter((u) => u.id !== userId) .map((u) => ( ))} {referrerSearchResults.filter((u) => u.id !== userId).length === 0 && (
{t('admin.users.detail.referrals.noUsersFound')}
)}
)} {referrerSearchQuery.length >= 2 && !referrerSearchLoading && referrerSearchResults.length === 0 && (
{t('admin.users.detail.referrals.noUsersFound')}
)}
) : (
{t('admin.users.detail.referrals.noReferrer')}
)}
)}
{/* Section 2: Referral stats */}
{t('admin.users.detail.referrals.totalReferrals')}
{user.referral.referrals_count}
{t('admin.users.detail.referrals.totalEarnings')}
{formatWithCurrency(user.referral.total_earnings_kopeks / 100)}
{t('admin.users.detail.referrals.commission')}
{user.referral.commission_percent != null ? `${user.referral.commission_percent}%` : t('admin.users.detail.referrals.default')}
{t('admin.users.detail.referrals.referralCode')}
{user.referral.referral_code}
{/* Section 3: Referrals list */}

{t('admin.users.detail.referrals.referralsList')} ({referralsTotal})

{!showAddReferral && ( )}
{/* Add referral search */} {showAddReferral && (
setAddReferralSearchQuery(e.target.value)} placeholder={t('admin.users.detail.referrals.searchPlaceholder')} className="flex-1 rounded-lg border border-dark-600 bg-dark-700 px-3 py-2.5 text-sm text-dark-100 placeholder-dark-500 focus:border-accent-500 focus:outline-none" autoFocus />
{addReferralSearchQuery.length >= 2 && addReferralSearchResults.length > 0 && (
{addReferralSearchResults .filter((u) => u.id !== userId && !referralsList.some((r) => r.id === u.id)) .map((u) => ( ))} {addReferralSearchResults.filter( (u) => u.id !== userId && !referralsList.some((r) => r.id === u.id), ).length === 0 && (
{t('admin.users.detail.referrals.noUsersFound')}
)}
)} {addReferralSearchQuery.length >= 2 && !addReferralSearchLoading && addReferralSearchResults.length === 0 && (
{t('admin.users.detail.referrals.noUsersFound')}
)}
)} {referralsListLoading ? (
) : referralsList.length === 0 ? (
{t('admin.users.detail.referrals.noReferrals')}
) : (
{referralsList.map((ref) => (
))}
)}
); }