diff --git a/src/App.tsx b/src/App.tsx index 57c0635..2a9cd0f 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -38,6 +38,7 @@ const AdminServers = lazy(() => import('./pages/AdminServers')); const AdminDashboard = lazy(() => import('./pages/AdminDashboard')); const AdminBanSystem = lazy(() => import('./pages/AdminBanSystem')); const AdminBroadcasts = lazy(() => import('./pages/AdminBroadcasts')); +const AdminBroadcastCreate = lazy(() => import('./pages/AdminBroadcastCreate')); const AdminPromocodes = lazy(() => import('./pages/AdminPromocodes')); const AdminCampaigns = lazy(() => import('./pages/AdminCampaigns')); const AdminUsers = lazy(() => import('./pages/AdminUsers')); @@ -327,6 +328,16 @@ function App() { } /> + + + + + + } + /> ( + + + +); + +const XIcon = () => ( + + + +); + +const RefreshIcon = () => ( + + + +); + +const PhotoIcon = () => ( + + + +); + +const VideoIcon = () => ( + + + +); + +const DocumentIcon = () => ( + + + +); + +const UsersIcon = () => ( + + + +); + +const ChevronDownIcon = () => ( + + + +); + +// 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', +}; + +export default function AdminBroadcastCreate() { + const { t } = useTranslation(); + const navigate = useNavigate(); + const queryClient = useQueryClient(); + const fileInputRef = useRef(null); + + const [target, setTarget] = useState(''); + 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); + const [showFilters, setShowFilters] = useState(false); + + // Fetch filters + const { data: filtersData, isLoading: filtersLoading } = useQuery({ + queryKey: ['admin', 'broadcasts', 'filters'], + queryFn: adminBroadcastsApi.getFilters, + }); + + // Fetch buttons + const { data: buttonsData } = useQuery({ + queryKey: ['admin', 'broadcasts', 'buttons'], + queryFn: adminBroadcastsApi.getButtons, + }); + + // Preview mutation + const previewMutation = useMutation({ + mutationFn: adminBroadcastsApi.preview, + }); + + // Create mutation + const createMutation = useMutation({ + mutationFn: adminBroadcastsApi.create, + onSuccess: (data) => { + queryClient.invalidateQueries({ queryKey: ['admin', 'broadcasts'] }); + navigate(`/admin/broadcasts/${data.id}`); + }, + }); + + // Group filters + const groupedFilters = useMemo(() => { + if (!filtersData) return {}; + const groups: Record = {}; + + // Basic filters + filtersData.filters.forEach((f) => { + const group = f.group || 'basic'; + if (!groups[group]) groups[group] = []; + groups[group].push(f); + }); + + // Tariff filters + if (filtersData.tariff_filters.length > 0) { + groups['tariff'] = filtersData.tariff_filters; + } + + // Custom filters + filtersData.custom_filters.forEach((f) => { + const group = f.group || 'custom'; + if (!groups[group]) groups[group] = []; + groups[group].push(f); + }); + + return groups; + }, [filtersData]); + + // Selected filter info + const selectedFilter = useMemo(() => { + if (!target || !filtersData) return null; + const all = [ + ...filtersData.filters, + ...filtersData.tariff_filters, + ...filtersData.custom_filters, + ]; + return all.find((f) => f.key === target); + }, [target, filtersData]); + + // Handle file selection + const handleFileSelect = async (e: React.ChangeEvent) => { + const file = e.target.files?.[0]; + if (!file) return; + + setMediaFile(file); + + // Determine media type + 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); + } + + // Upload file + 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], + ); + }; + + // Submit + const handleSubmit = () => { + if (!target || !messageText.trim()) return; + + const data: BroadcastCreateRequest = { + target, + message_text: messageText, + selected_buttons: selectedButtons, + }; + + if (uploadedFileId) { + data.media = { + type: mediaType, + file_id: uploadedFileId, + }; + } + + createMutation.mutate(data); + }; + + const recipientsCount = previewMutation.data?.count ?? selectedFilter?.count ?? null; + + return ( +
+ {/* Header */} +
+ +
+
+ +
+
+

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

+

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

+
+
+
+ + {/* Content */} +
+ {/* Filter selection */} +
+ +
+ + + {showFilters && ( +
+ {filtersLoading ? ( +
{t('common.loading')}
+ ) : ( + Object.entries(groupedFilters).map(([group, filters]) => ( +
+
+ {FILTER_GROUP_LABEL_KEYS[group] ? t(FILTER_GROUP_LABEL_KEYS[group]) : group} +
+ {filters.map((filter) => ( + + ))} +
+ )) + )} +
+ )} +
+
+ + {/* Message text */} +
+ +