import { useState, useCallback } from 'react'; import { useNavigate, useParams } from 'react-router'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { useTranslation } from 'react-i18next'; import { promocodesApi, PromoGroup, PromoGroupCreateRequest, PromoGroupUpdateRequest, } from '../api/promocodes'; import { AdminBackButton } from '../components/admin'; // Icons const PlusIcon = () => ( ); const TrashIcon = () => ( ); const RefreshIcon = () => ( ); interface PeriodDiscount { days: number | ''; percent: number | ''; } export default function AdminPromoGroupCreate() { const { t } = useTranslation(); const navigate = useNavigate(); const { id } = useParams<{ id: string }>(); const queryClient = useQueryClient(); const isEdit = !!id; // Form state const [name, setName] = useState(''); const [serverDiscount, setServerDiscount] = useState(0); const [trafficDiscount, setTrafficDiscount] = useState(0); const [deviceDiscount, setDeviceDiscount] = useState(0); const [applyToAddons, setApplyToAddons] = useState(true); const [isDefault, setIsDefault] = useState(false); const [autoAssignSpent, setAutoAssignSpent] = useState(0); const [periodDiscounts, setPeriodDiscounts] = useState([]); // Fetch promo group for editing const { isLoading: isLoadingGroup } = useQuery({ queryKey: ['admin-promo-group', id], queryFn: () => promocodesApi.getPromoGroup(Number(id)), enabled: isEdit, staleTime: 0, gcTime: 0, refetchOnMount: true, refetchOnWindowFocus: false, select: useCallback((data: PromoGroup) => { setName(data.name); setServerDiscount(data.server_discount_percent || 0); setTrafficDiscount(data.traffic_discount_percent || 0); setDeviceDiscount(data.device_discount_percent || 0); setApplyToAddons(data.apply_discounts_to_addons ?? true); setIsDefault(data.is_default ?? false); setAutoAssignSpent( data.auto_assign_total_spent_kopeks ? data.auto_assign_total_spent_kopeks / 100 : 0, ); if (data.period_discounts && typeof data.period_discounts === 'object') { setPeriodDiscounts( Object.entries(data.period_discounts).map(([days, percent]) => ({ days: parseInt(days), percent: typeof percent === 'number' ? percent : 0, })), ); } return data; }, []), }); // Create mutation const createMutation = useMutation({ mutationFn: promocodesApi.createPromoGroup, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['admin-promo-groups'] }); navigate('/admin/promo-groups'); }, }); // Update mutation const updateMutation = useMutation({ mutationFn: ({ id, data }: { id: number; data: PromoGroupUpdateRequest }) => promocodesApi.updatePromoGroup(id, data), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['admin-promo-groups'] }); navigate('/admin/promo-groups'); }, }); const addPeriodDiscount = () => { setPeriodDiscounts([...periodDiscounts, { days: 30, percent: 0 }]); }; const removePeriodDiscount = (index: number) => { setPeriodDiscounts(periodDiscounts.filter((_, i) => i !== index)); }; const updatePeriodDiscount = (index: number, field: 'days' | 'percent', value: number | '') => { setPeriodDiscounts( periodDiscounts.map((item, i) => (i === index ? { ...item, [field]: value } : item)), ); }; const handleSubmit = () => { // Convert periodDiscounts array to Record const periodDiscountsRecord: Record = {}; periodDiscounts.forEach((pd) => { const days = pd.days === '' ? 0 : pd.days; const percent = pd.percent === '' ? 0 : pd.percent; if (days > 0 && percent >= 0) { periodDiscountsRecord[days] = percent; } }); const serverVal = serverDiscount === '' ? 0 : serverDiscount; const trafficVal = trafficDiscount === '' ? 0 : trafficDiscount; const deviceVal = deviceDiscount === '' ? 0 : deviceDiscount; const autoAssignVal = autoAssignSpent === '' ? 0 : autoAssignSpent; const data: PromoGroupCreateRequest | PromoGroupUpdateRequest = { name, server_discount_percent: serverVal, traffic_discount_percent: trafficVal, device_discount_percent: deviceVal, period_discounts: periodDiscountsRecord, apply_discounts_to_addons: applyToAddons, auto_assign_total_spent_kopeks: autoAssignVal > 0 ? Math.round(autoAssignVal * 100) : 0, is_default: isDefault, }; if (isEdit) { updateMutation.mutate({ id: Number(id), data }); } else { createMutation.mutate(data as PromoGroupCreateRequest); } }; const isLoading = createMutation.isPending || updateMutation.isPending; const isValid = name.trim().length > 0; // Loading state if (isEdit && isLoadingGroup) { return (
); } return (
{/* Header */}

{isEdit ? t('admin.promoGroups.editTitle') : t('admin.promoGroups.createTitle')}

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

{/* Form */}
{/* Name */}
setName(e.target.value)} className={`input ${name.length > 0 && name.trim().length === 0 ? 'border-error-500/50' : ''}`} placeholder={t('admin.promoGroups.form.namePlaceholder')} /> {name.length > 0 && name.trim().length === 0 && (

{t('admin.promoGroups.form.nameRequired')}

)}
{/* Category Discounts */}

{t('admin.promoGroups.form.categoryDiscounts')}

{t('admin.promoGroups.servers')}: { const val = e.target.value; if (val === '') { setServerDiscount(''); } else { setServerDiscount(Math.min(100, Math.max(0, parseInt(val) || 0))); } }} className="input w-20" min={0} max={100} placeholder="0" /> %
{t('admin.promoGroups.traffic')}: { const val = e.target.value; if (val === '') { setTrafficDiscount(''); } else { setTrafficDiscount(Math.min(100, Math.max(0, parseInt(val) || 0))); } }} className="input w-20" min={0} max={100} placeholder="0" /> %
{t('admin.promoGroups.devices')}: { const val = e.target.value; if (val === '') { setDeviceDiscount(''); } else { setDeviceDiscount(Math.min(100, Math.max(0, parseInt(val) || 0))); } }} className="input w-20" min={0} max={100} placeholder="0" /> %
{/* Period Discounts */}

{t('admin.promoGroups.form.periodDiscounts')}

{t('admin.promoGroups.form.periodHint')}

{periodDiscounts.length === 0 ? (

{t('admin.promoGroups.form.noPeriods')}

) : (
{periodDiscounts.map((pd, index) => (
{ const val = e.target.value; if (val === '') return updatePeriodDiscount(index, 'days', ''); const num = parseInt(val); if (!isNaN(num)) updatePeriodDiscount(index, 'days', num); }} className="input w-20" min={1} placeholder={t('admin.promoGroups.form.daysPlaceholder')} /> {t('admin.promoGroups.form.arrow')} { const val = e.target.value; if (val === '') return updatePeriodDiscount(index, 'percent', ''); const num = parseInt(val); if (!isNaN(num)) updatePeriodDiscount(index, 'percent', num); }} className="input w-20" min={0} max={100} placeholder="%" /> %
))}
)}
{/* Auto-assign */}
{ const val = e.target.value; if (val === '') { setAutoAssignSpent(''); } else { setAutoAssignSpent(Math.max(0, parseFloat(val) || 0)); } }} className="input w-32" min={0} placeholder="0" /> {t('admin.promoGroups.form.rub')}

{t('admin.promoGroups.form.autoAssignHint')}

{/* Apply to addons */} {/* Default group */}
{/* Footer */}
); }