import { useState } from 'react' import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query' import { useTranslation } from 'react-i18next' import { adminWheelApi, type WheelPrizeAdmin } from '../api/wheel' // Icons const CogIcon = () => ( ) const GiftIcon = () => ( ) const ChartIcon = () => ( ) const PlusIcon = () => ( ) const TrashIcon = () => ( ) const PRIZE_TYPE_KEYS = [ { value: 'subscription_days', key: 'subscription_days', emoji: '📅' }, { value: 'balance_bonus', key: 'balance_bonus', emoji: '💰' }, { value: 'traffic_gb', key: 'traffic_gb', emoji: '📊' }, { value: 'promocode', key: 'promocode', emoji: '🎟️' }, { value: 'nothing', key: 'nothing', emoji: '😔' }, ] type Tab = 'settings' | 'prizes' | 'statistics' export default function AdminWheel() { const { t } = useTranslation() const queryClient = useQueryClient() const [activeTab, setActiveTab] = useState('settings') const [editingPrize, setEditingPrize] = useState(null) const [isCreating, setIsCreating] = useState(false) // Fetch config const { data: config, isLoading } = useQuery({ queryKey: ['admin-wheel-config'], queryFn: adminWheelApi.getConfig, }) // Fetch statistics const { data: stats } = useQuery({ queryKey: ['admin-wheel-stats'], queryFn: () => adminWheelApi.getStatistics(), enabled: activeTab === 'statistics', }) // Update config mutation const updateConfigMutation = useMutation({ mutationFn: adminWheelApi.updateConfig, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['admin-wheel-config'] }) }, }) // Prize mutations const createPrizeMutation = useMutation({ mutationFn: adminWheelApi.createPrize, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['admin-wheel-config'] }) setIsCreating(false) }, }) const updatePrizeMutation = useMutation({ mutationFn: ({ id, data }: { id: number; data: Partial }) => adminWheelApi.updatePrize(id, data), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['admin-wheel-config'] }) setEditingPrize(null) }, }) const deletePrizeMutation = useMutation({ mutationFn: adminWheelApi.deletePrize, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['admin-wheel-config'] }) }, }) if (isLoading) { return (
) } if (!config) { return
{t('wheel.errors.loadFailed')}
} return (
{/* Header */}

{t('admin.wheel.title')}

{config.is_enabled ? t('admin.wheel.enabled') : t('admin.wheel.disabled')}
{/* Tabs */}
{/* Settings Tab */} {activeTab === 'settings' && (
{/* Enable toggle */}

{t('admin.wheel.settings.enableWheel')}

{t('admin.wheel.settings.allowSpins')}


{/* Spin costs */}
updateConfigMutation.mutate({ spin_cost_stars: parseInt(e.target.value) || 1 })} min={1} max={1000} className="input flex-1" />
updateConfigMutation.mutate({ spin_cost_days: parseInt(e.target.value) || 1 })} min={1} max={30} className="input flex-1" />

{/* RTP and limits */}
updateConfigMutation.mutate({ rtp_percent: parseInt(e.target.value) })} className="w-full" />
0% {config.rtp_percent}% 100%
updateConfigMutation.mutate({ daily_spin_limit: parseInt(e.target.value) || 0 })} min={0} max={100} className="input w-full" />
updateConfigMutation.mutate({ min_subscription_days_for_day_payment: parseInt(e.target.value) || 1 })} min={1} max={30} className="input w-full" />
updateConfigMutation.mutate({ promo_prefix: e.target.value })} maxLength={20} className="input w-full" />
)} {/* Prizes Tab */} {activeTab === 'prizes' && (
{/* Prize list */}
{config.prizes.map((prize) => (
{prize.emoji}
{prize.display_name}
{t(`admin.wheel.prizes.types.${prize.prize_type}`)} • {t('admin.wheel.prizes.fields.value')}: {prize.prize_value} • {t('admin.wheel.prizes.fields.worth')}: {(prize.prize_value_kopeks / 100).toFixed(2)}₽
))}
{config.prizes.length === 0 && (
{t('admin.wheel.prizes.noPrizes')}
)}
)} {/* Statistics Tab */} {activeTab === 'statistics' && stats && (
{/* Stats cards */}
{stats.total_spins}
{t('admin.wheel.statistics.totalSpins')}
{(stats.total_revenue_kopeks / 100).toFixed(0)}₽
{t('admin.wheel.statistics.revenue')}
{(stats.total_payout_kopeks / 100).toFixed(0)}₽
{t('admin.wheel.statistics.payouts')}
{stats.actual_rtp_percent.toFixed(1)}%
{t('admin.wheel.statistics.actualRtp')} ({t('admin.wheel.statistics.targetRtp')}: {stats.configured_rtp_percent}%)
{/* Prize distribution */} {stats.prizes_distribution.length > 0 && (

{t('admin.wheel.statistics.prizeDistribution')}

{stats.prizes_distribution.map((prize, i) => (
{prize.display_name} {prize.count} {t('admin.wheel.statistics.times')}
))}
)} {/* Top wins */} {stats.top_wins.length > 0 && (

{t('admin.wheel.statistics.topWins')}

{stats.top_wins.slice(0, 5).map((win, i) => (
{win.username || `User #${win.user_id}`} {win.prize_display_name} ({(win.prize_value_kopeks / 100).toFixed(0)}₽)
))}
)}
)} {/* Create/Edit Prize Modal */} {(isCreating || editingPrize) && ( { setIsCreating(false) setEditingPrize(null) }} onSave={(data) => { if (editingPrize) { updatePrizeMutation.mutate({ id: editingPrize.id, data }) } else { createPrizeMutation.mutate(data as any) } }} /> )}
) } // Prize Modal Component function PrizeModal({ prize, onClose, onSave, }: { prize: WheelPrizeAdmin | null onClose: () => void onSave: (data: Partial) => void }) { const { t } = useTranslation() const [formData, setFormData] = useState({ prize_type: prize?.prize_type || 'balance_bonus', prize_value: prize?.prize_value || 0, display_name: prize?.display_name || '', emoji: prize?.emoji || '🎁', color: prize?.color || '#3B82F6', prize_value_kopeks: prize?.prize_value_kopeks || 0, is_active: prize?.is_active ?? true, manual_probability: prize?.manual_probability || null, promo_balance_bonus_kopeks: prize?.promo_balance_bonus_kopeks || 0, promo_subscription_days: prize?.promo_subscription_days || 0, promo_traffic_gb: prize?.promo_traffic_gb || 0, }) const handleSubmit = (e: React.FormEvent) => { e.preventDefault() onSave(formData) } return (

{prize ? t('admin.wheel.prizes.editPrize') : t('admin.wheel.prizes.addPrize')}

{/* Prize type */}
{/* Display name */}
setFormData({ ...formData, display_name: e.target.value })} required maxLength={100} className="input w-full" placeholder="e.g. 7 Days Free" />
{/* Prize value */} {formData.prize_type !== 'nothing' && (
setFormData({ ...formData, prize_value: parseInt(e.target.value) || 0 })} min={0} className="input w-full" />
)} {/* Prize value in kopeks (for RTP calculation) */}
setFormData({ ...formData, prize_value_kopeks: parseInt(e.target.value) || 0 })} min={0} className="input w-full" />

= {(formData.prize_value_kopeks / 100).toFixed(2)} RUB

{/* Emoji and color */}
setFormData({ ...formData, emoji: e.target.value })} maxLength={10} className="input w-full text-center text-2xl" />
setFormData({ ...formData, color: e.target.value })} className="w-12 h-10 rounded cursor-pointer" /> setFormData({ ...formData, color: e.target.value })} className="input flex-1" pattern="^#[0-9A-Fa-f]{6}$" />
{/* Active toggle */}
setFormData({ ...formData, is_active: e.target.checked })} className="rounded border-dark-600" />
{/* Promocode settings */} {formData.prize_type === 'promocode' && (

{t('admin.wheel.prizes.promo.title')}

setFormData({ ...formData, promo_balance_bonus_kopeks: parseInt(e.target.value) || 0 })} min={0} className="input w-full" />
setFormData({ ...formData, promo_subscription_days: parseInt(e.target.value) || 0 })} min={0} className="input w-full" />
)} {/* Buttons */}
) }