import { useState, useEffect } from 'react'; import { useParams, useNavigate } from 'react-router'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { useTranslation } from 'react-i18next'; import { campaignsApi, CampaignUpdateRequest, CampaignBonusType, ServerSquadInfo, TariffListItem, AvailablePartner, } from '../api/campaigns'; import { AdminBackButton } from '../components/admin'; import { CheckIcon, CampaignIcon } from '../components/icons'; import { createNumberInputHandler, toNumber } from '../utils/inputHelpers'; import Twemoji from 'react-twemoji'; // Bonus type config const bonusTypeConfig: Record< CampaignBonusType, { labelKey: string; color: string; bgColor: string; borderColor: string } > = { balance: { labelKey: 'admin.campaigns.bonusType.balance', color: 'text-success-400', bgColor: 'bg-success-500/10', borderColor: 'border-success-500/30', }, subscription: { labelKey: 'admin.campaigns.bonusType.subscription', color: 'text-accent-400', bgColor: 'bg-accent-500/10', borderColor: 'border-accent-500/30', }, tariff: { labelKey: 'admin.campaigns.bonusType.tariff', color: 'text-accent-400', bgColor: 'bg-accent-500/10', borderColor: 'border-accent-500/30', }, none: { labelKey: 'admin.campaigns.bonusType.none', color: 'text-dark-400', bgColor: 'bg-dark-500/10', borderColor: 'border-dark-500/30', }, }; // Server selector component function ServerSelector({ servers, selected, onToggle, }: { servers: ServerSquadInfo[]; selected: string[]; onToggle: (uuid: string) => void; }) { const { t } = useTranslation(); if (servers.length === 0) return null; return (
{servers.map((server) => ( ))}
); } // Tariff selector component function TariffSelector({ tariffs, value, onChange, }: { tariffs: TariffListItem[]; value: number | null; onChange: (id: number | null) => void; }) { const { t } = useTranslation(); return (
); } // Partner selector component function PartnerSelector({ partners, value, onChange, }: { partners: AvailablePartner[]; value: number | null; onChange: (id: number | null) => void; }) { const { t } = useTranslation(); return (
); } export default function AdminCampaignEdit() { const { t } = useTranslation(); const { id } = useParams<{ id: string }>(); const navigate = useNavigate(); const queryClient = useQueryClient(); const campaignId = parseInt(id || '0'); // Fetch campaign const { data: campaign, isLoading, error, } = useQuery({ queryKey: ['admin-campaign', campaignId], queryFn: () => campaignsApi.getCampaign(campaignId), enabled: campaignId > 0, }); // Fetch servers const { data: servers = [] } = useQuery({ queryKey: ['admin-campaigns-servers'], queryFn: () => campaignsApi.getAvailableServers(), }); // Fetch tariffs const { data: tariffs = [] } = useQuery({ queryKey: ['admin-campaigns-tariffs'], queryFn: () => campaignsApi.getAvailableTariffs(), }); // Fetch partners const { data: partners = [] } = useQuery({ queryKey: ['admin-campaigns-partners'], queryFn: () => campaignsApi.getAvailablePartners(), }); // Form state const [name, setName] = useState(''); const [startParameter, setStartParameter] = useState(''); const [bonusType, setBonusType] = useState('balance'); const [isActive, setIsActive] = useState(true); // Balance bonus const [balanceBonusRubles, setBalanceBonusRubles] = useState(0); // Subscription bonus const [subscriptionDays, setSubscriptionDays] = useState(7); const [subscriptionTraffic, setSubscriptionTraffic] = useState(10); const [subscriptionDevices, setSubscriptionDevices] = useState(1); const [selectedSquads, setSelectedSquads] = useState([]); // Tariff bonus const [tariffId, setTariffId] = useState(null); const [tariffDays, setTariffDays] = useState(30); // Partner const [partnerUserId, setPartnerUserId] = useState(null); const [initialPartnerUserId, setInitialPartnerUserId] = useState(null); // Initialize form when campaign loads useEffect(() => { if (campaign) { setName(campaign.name || ''); setStartParameter(campaign.start_parameter || ''); setBonusType(campaign.bonus_type || 'balance'); setIsActive(campaign.is_active ?? true); setBalanceBonusRubles((campaign.balance_bonus_kopeks || 0) / 100); setSubscriptionDays(campaign.subscription_duration_days || 7); setSubscriptionTraffic(campaign.subscription_traffic_gb || 10); setSubscriptionDevices(campaign.subscription_device_limit || 1); setSelectedSquads(campaign.subscription_squads || []); setTariffId(campaign.tariff_id || null); setTariffDays(campaign.tariff_duration_days || 30); setPartnerUserId(campaign.partner_user_id ?? null); setInitialPartnerUserId(campaign.partner_user_id ?? null); } }, [campaign]); // Update mutation const updateMutation = useMutation({ mutationFn: (data: CampaignUpdateRequest) => campaignsApi.updateCampaign(campaignId, data), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['admin-campaigns'] }); queryClient.invalidateQueries({ queryKey: ['admin-campaign', campaignId] }); navigate('/admin/campaigns'); }, }); const toggleServer = (uuid: string) => { setSelectedSquads((prev) => prev.includes(uuid) ? prev.filter((s) => s !== uuid) : [...prev, uuid], ); }; const handleSubmit = () => { const data: CampaignUpdateRequest = { name, start_parameter: startParameter, bonus_type: bonusType, is_active: isActive, }; // Only send partner_user_id when it was actually changed if (partnerUserId !== initialPartnerUserId) { data.partner_user_id = partnerUserId; } if (bonusType === 'balance') { data.balance_bonus_kopeks = Math.round(toNumber(balanceBonusRubles) * 100); } else if (bonusType === 'subscription') { data.subscription_duration_days = toNumber(subscriptionDays, 7); data.subscription_traffic_gb = toNumber(subscriptionTraffic, 10); data.subscription_device_limit = toNumber(subscriptionDevices, 1); data.subscription_squads = selectedSquads; } else if (bonusType === 'tariff') { data.tariff_id = tariffId || undefined; data.tariff_duration_days = toNumber(tariffDays, 30); } updateMutation.mutate(data); }; const isNameValid = name.trim().length > 0; const isStartParamValid = startParameter.trim().length > 0 && /^[a-zA-Z0-9_-]+$/.test(startParameter); const isValid = isNameValid && isStartParamValid; if (isLoading) { return (
); } if (error || !campaign) { return (

{t('admin.campaigns.modal.editTitle')}

{t('admin.campaigns.loadError')}

); } return (
{/* Header */}

{t('admin.campaigns.modal.editTitle')}

{campaign.name}

{/* Basic Info */}
{/* Name */}
setName(e.target.value)} className={`input ${name.length > 0 && !isNameValid ? 'border-error-500/50' : ''}`} placeholder={t('admin.campaigns.form.namePlaceholder')} maxLength={255} /> {name.length > 0 && !isNameValid && (

{t('admin.campaigns.validation.nameRequired')}

)}
{/* Start Parameter */}
setStartParameter(e.target.value.replace(/[^a-zA-Z0-9_-]/g, ''))} className={`input font-mono ${startParameter.length > 0 && !isStartParamValid ? 'border-error-500/50' : ''}`} placeholder="instagram_jan2024" maxLength={100} />

{t('admin.campaigns.form.startParameterHint')}

{/* Active toggle */}
{t('admin.campaigns.form.active')}
{/* Partner */} {partners.length > 0 && ( )}
{/* Bonus Type */}

{t('admin.campaigns.form.bonusType')}

{(Object.keys(bonusTypeConfig) as CampaignBonusType[]).map((type) => ( ))}
{/* Bonus Settings */} {bonusType === 'balance' && (

{t('admin.campaigns.form.balanceBonus')}

)} {bonusType === 'subscription' && (

{t('admin.campaigns.form.trialSubscription')}

)} {bonusType === 'tariff' && (

{t('admin.campaigns.form.tariff')}

)} {bonusType === 'none' && (

{t('admin.campaigns.form.noBonusDescription')}

)} {/* Footer */}
{updateMutation.isError && (
{t('admin.campaigns.updateError')}
)}
); }