diff --git a/src/pages/AdminPromoOfferSend.tsx b/src/pages/AdminPromoOfferSend.tsx index b058460..25ab35f 100644 --- a/src/pages/AdminPromoOfferSend.tsx +++ b/src/pages/AdminPromoOfferSend.tsx @@ -1,4 +1,4 @@ -import { useState } from 'react'; +import { useState, useEffect, useRef } from 'react'; import { useNavigate } from 'react-router-dom'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { useTranslation } from 'react-i18next'; @@ -10,6 +10,7 @@ import { OFFER_TYPE_CONFIG, OfferType, } from '../api/promoOffers'; +import { adminUsersApi, UserListItem } from '../api/adminUsers'; import { AdminBackButton } from '../components/admin'; // Icons @@ -49,6 +50,22 @@ const UserIcon = () => ( ); +const SearchIcon = () => ( + + + +); + +const CloseIcon = () => ( + + + +); + const getOfferTypeIcon = (offerType: string): string => { return OFFER_TYPE_CONFIG[offerType as OfferType]?.icon || '🎁'; }; @@ -62,6 +79,10 @@ export default function AdminPromoOfferSend() { const [sendMode, setSendMode] = useState<'segment' | 'user'>('segment'); const [selectedTarget, setSelectedTarget] = useState('active'); const [userId, setUserId] = useState(''); + const [searchQuery, setSearchQuery] = useState(''); + const [selectedUser, setSelectedUser] = useState(null); + const [showDropdown, setShowDropdown] = useState(false); + const searchRef = useRef(null); const [result, setResult] = useState<{ title: string; message: string; @@ -83,6 +104,43 @@ export default function AdminPromoOfferSend() { setSelectedTemplateId(activeTemplates[0].id); } + // User search query with debounce + const { data: searchResults, isFetching: isSearching } = useQuery({ + queryKey: ['admin-users-search', searchQuery], + queryFn: () => adminUsersApi.getUsers({ search: searchQuery, limit: 10 }), + enabled: searchQuery.length >= 2 && sendMode === 'user', + staleTime: 30000, + }); + + // Filter users with telegram_id only + const filteredUsers = (searchResults?.users || []).filter((u) => u.telegram_id); + + // Close dropdown on click outside + useEffect(() => { + const handleClickOutside = (e: MouseEvent) => { + if (searchRef.current && !searchRef.current.contains(e.target as Node)) { + setShowDropdown(false); + } + }; + document.addEventListener('mousedown', handleClickOutside); + return () => document.removeEventListener('mousedown', handleClickOutside); + }, []); + + // Handle user selection + const handleSelectUser = (user: UserListItem) => { + setSelectedUser(user); + setUserId(user.telegram_id.toString()); + setSearchQuery(''); + setShowDropdown(false); + }; + + // Clear selected user + const handleClearUser = () => { + setSelectedUser(null); + setUserId(''); + setSearchQuery(''); + }; + // Broadcast mutation const broadcastMutation = useMutation({ mutationFn: promoOffersApi.broadcastOffer, @@ -327,13 +385,100 @@ export default function AdminPromoOfferSend() { ))} ) : ( - setUserId(e.target.value)} - placeholder={t('admin.promoOffers.send.userIdPlaceholder')} - className="w-full rounded-lg border border-dark-600 bg-dark-700 px-3 py-2.5 text-dark-100 focus:border-accent-500 focus:outline-none" - /> +
+ {selectedUser ? ( + // Selected user display +
+
+
+ +
+
+
+ {selectedUser.full_name || + selectedUser.username || + `ID: ${selectedUser.telegram_id}`} +
+
+ {selectedUser.username && `@${selectedUser.username} · `} + Telegram: {selectedUser.telegram_id} +
+
+
+ +
+ ) : ( + // Search input + <> +
+
+ +
+ { + setSearchQuery(e.target.value); + setShowDropdown(true); + }} + onFocus={() => setShowDropdown(true)} + placeholder={t( + 'admin.promoOffers.send.searchUserPlaceholder', + 'Search by name, username or Telegram ID...', + )} + className="w-full rounded-lg border border-dark-600 bg-dark-700 py-2.5 pl-10 pr-3 text-dark-100 focus:border-accent-500 focus:outline-none" + /> + {isSearching && ( +
+
+
+ )} +
+ + {/* Dropdown results */} + {showDropdown && searchQuery.length >= 2 && ( +
+ {filteredUsers.length > 0 ? ( + filteredUsers.map((user) => ( + + )) + ) : !isSearching ? ( +
+ {t('admin.promoOffers.send.noUsersFound', 'No users found')} +
+ ) : null} +
+ )} + + )} +
)}