import { useState, useRef, useMemo } from 'react'; import { useNavigate } from 'react-router-dom'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { useTranslation } from 'react-i18next'; import { adminBroadcastsApi, BroadcastFilter, TariffFilter, BroadcastChannel, CombinedBroadcastCreateRequest, } from '../api/adminBroadcasts'; import { AdminBackButton } from '../components/admin'; import { useBackButton } from '../platform/hooks/useBackButton'; // Icons const BroadcastIcon = () => ( ); const XIcon = () => ( ); const RefreshIcon = () => ( ); const PhotoIcon = () => ( ); const VideoIcon = () => ( ); const DocumentIcon = () => ( ); const UsersIcon = () => ( ); const ChevronDownIcon = () => ( ); const TelegramIcon = () => ( ); const EmailIcon = () => ( ); // Filter labels const FILTER_GROUP_LABEL_KEYS: Record = { basic: 'admin.broadcasts.filterGroups.basic', subscription: 'admin.broadcasts.filterGroups.subscription', traffic: 'admin.broadcasts.filterGroups.traffic', registration: 'admin.broadcasts.filterGroups.registration', activity: 'admin.broadcasts.filterGroups.activity', source: 'admin.broadcasts.filterGroups.source', tariff: 'admin.broadcasts.filterGroups.tariff', email: 'admin.broadcasts.filterGroups.email', }; export default function AdminBroadcastCreate() { const { t } = useTranslation(); const navigate = useNavigate(); const queryClient = useQueryClient(); const fileInputRef = useRef(null); useBackButton(() => navigate('/admin/broadcasts')); // Channel selection const [channel, setChannel] = useState('telegram'); // Common state const [target, setTarget] = useState(''); const [showFilters, setShowFilters] = useState(false); // Telegram-specific state const [messageText, setMessageText] = useState(''); const [selectedButtons, setSelectedButtons] = useState(['home']); const [mediaFile, setMediaFile] = useState(null); const [mediaType, setMediaType] = useState<'photo' | 'video' | 'document'>('photo'); const [mediaPreview, setMediaPreview] = useState(null); const [uploadedFileId, setUploadedFileId] = useState(null); const [isUploading, setIsUploading] = useState(false); // Email-specific state const [emailSubject, setEmailSubject] = useState(''); const [emailContent, setEmailContent] = useState(''); // Fetch Telegram filters const { data: filtersData, isLoading: filtersLoading } = useQuery({ queryKey: ['admin', 'broadcasts', 'filters'], queryFn: adminBroadcastsApi.getFilters, enabled: channel === 'telegram', }); // Fetch Email filters const { data: emailFiltersData, isLoading: emailFiltersLoading } = useQuery({ queryKey: ['admin', 'broadcasts', 'email-filters'], queryFn: adminBroadcastsApi.getEmailFilters, enabled: channel === 'email', }); // Fetch buttons const { data: buttonsData } = useQuery({ queryKey: ['admin', 'broadcasts', 'buttons'], queryFn: adminBroadcastsApi.getButtons, enabled: channel === 'telegram', }); // Preview mutations const previewMutation = useMutation({ mutationFn: adminBroadcastsApi.preview, }); const emailPreviewMutation = useMutation({ mutationFn: adminBroadcastsApi.previewEmail, }); // Create mutation const createMutation = useMutation({ mutationFn: adminBroadcastsApi.createCombined, onSuccess: (data) => { queryClient.invalidateQueries({ queryKey: ['admin', 'broadcasts'] }); navigate(`/admin/broadcasts/${data.id}`); }, }); // Group Telegram filters const groupedFilters = useMemo(() => { if (!filtersData) return {}; const groups: Record = {}; filtersData.filters.forEach((f) => { const group = f.group || 'basic'; if (!groups[group]) groups[group] = []; groups[group].push(f); }); if (filtersData.tariff_filters.length > 0) { groups['tariff'] = filtersData.tariff_filters; } filtersData.custom_filters.forEach((f) => { const group = f.group || 'custom'; if (!groups[group]) groups[group] = []; groups[group].push(f); }); return groups; }, [filtersData]); // Group Email filters const groupedEmailFilters = useMemo(() => { if (!emailFiltersData) return {}; const groups: Record = {}; emailFiltersData.filters.forEach((f) => { const group = f.group || 'email'; if (!groups[group]) groups[group] = []; groups[group].push(f); }); return groups; }, [emailFiltersData]); // Current filters based on channel const currentFilters = channel === 'telegram' ? groupedFilters : groupedEmailFilters; const isFiltersLoading = channel === 'telegram' ? filtersLoading : emailFiltersLoading; // Selected filter info const selectedFilter = useMemo(() => { if (!target) return null; if (channel === 'telegram' && filtersData) { const all = [ ...filtersData.filters, ...filtersData.tariff_filters, ...filtersData.custom_filters, ]; return all.find((f) => f.key === target); } if (channel === 'email' && emailFiltersData) { return emailFiltersData.filters.find((f) => f.key === target); } return null; }, [target, channel, filtersData, emailFiltersData]); // Handle channel change const handleChannelChange = (newChannel: BroadcastChannel) => { setChannel(newChannel); setTarget(''); previewMutation.reset(); emailPreviewMutation.reset(); }; // Handle filter selection const handleFilterSelect = (filterKey: string) => { setTarget(filterKey); setShowFilters(false); if (channel === 'telegram') { previewMutation.mutate(filterKey); } else { emailPreviewMutation.mutate(filterKey); } }; // Handle file selection const handleFileSelect = async (e: React.ChangeEvent) => { const file = e.target.files?.[0]; if (!file) return; setMediaFile(file); if (file.type.startsWith('image/')) { setMediaType('photo'); setMediaPreview(URL.createObjectURL(file)); } else if (file.type.startsWith('video/')) { setMediaType('video'); setMediaPreview(null); } else { setMediaType('document'); setMediaPreview(null); } setIsUploading(true); try { const result = await adminBroadcastsApi.uploadMedia(file, mediaType); setUploadedFileId(result.file_id); } catch (err) { console.error('Upload failed:', err); setMediaFile(null); setMediaPreview(null); } finally { setIsUploading(false); } }; // Remove media const handleRemoveMedia = () => { setMediaFile(null); setMediaPreview(null); setUploadedFileId(null); if (fileInputRef.current) { fileInputRef.current.value = ''; } }; // Toggle button const toggleButton = (key: string) => { setSelectedButtons((prev) => prev.includes(key) ? prev.filter((b) => b !== key) : [...prev, key], ); }; // Validate form const isValid = useMemo(() => { if (!target) return false; if (channel === 'telegram') { return messageText.trim().length > 0; } if (channel === 'email') { return emailSubject.trim().length > 0 && emailContent.trim().length > 0; } return false; }, [target, channel, messageText, emailSubject, emailContent]); // Submit const handleSubmit = () => { if (!isValid) return; const data: CombinedBroadcastCreateRequest = { channel, target, }; if (channel === 'telegram') { data.message_text = messageText; data.selected_buttons = selectedButtons; if (uploadedFileId) { data.media = { type: mediaType, file_id: uploadedFileId, }; } } if (channel === 'email') { data.email_subject = emailSubject; data.email_html_content = emailContent; } createMutation.mutate(data); }; const recipientsCount = channel === 'telegram' ? (previewMutation.data?.count ?? selectedFilter?.count ?? null) : (emailPreviewMutation.data?.count ?? selectedFilter?.count ?? null); return (
{/* Header */}

{t('admin.broadcasts.create')}

{t('admin.broadcasts.subtitle')}

{/* Channel selection */}
{/* Content */}
{/* Section title */}

{channel === 'telegram' ? t('admin.broadcasts.telegramSection') : t('admin.broadcasts.emailSection')}

{/* Filter selection */}
{showFilters && (
{isFiltersLoading ? (
{t('common.loading')}
) : ( Object.entries(currentFilters).map(([group, filters]) => (
{FILTER_GROUP_LABEL_KEYS[group] ? t(FILTER_GROUP_LABEL_KEYS[group]) : group}
{filters.map((filter) => ( ))}
)) )}
)}
{/* Telegram-specific fields */} {channel === 'telegram' && ( <> {/* Message text */}