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 */} setActiveTab('settings')} className={`flex items-center gap-2 px-4 py-2 rounded-t-lg transition-colors ${ activeTab === 'settings' ? 'bg-dark-800 text-accent-400 border-b-2 border-accent-500' : 'text-dark-400 hover:text-dark-200' }`} > {t('admin.wheel.tabs.settings')} setActiveTab('prizes')} className={`flex items-center gap-2 px-4 py-2 rounded-t-lg transition-colors ${ activeTab === 'prizes' ? 'bg-dark-800 text-accent-400 border-b-2 border-accent-500' : 'text-dark-400 hover:text-dark-200' }`} > {t('admin.wheel.tabs.prizes')} ({config.prizes.length}) setActiveTab('statistics')} className={`flex items-center gap-2 px-4 py-2 rounded-t-lg transition-colors ${ activeTab === 'statistics' ? 'bg-dark-800 text-accent-400 border-b-2 border-accent-500' : 'text-dark-400 hover:text-dark-200' }`} > {t('admin.wheel.tabs.statistics')} {/* Settings Tab */} {activeTab === 'settings' && ( {/* Enable toggle */} {t('admin.wheel.settings.enableWheel')} {t('admin.wheel.settings.allowSpins')} updateConfigMutation.mutate({ is_enabled: e.target.checked })} className="sr-only peer" /> {/* Spin costs */} {t('admin.wheel.settings.costInStars')} updateConfigMutation.mutate({ spin_cost_stars: parseInt(e.target.value) || 1 })} min={1} max={1000} className="input flex-1" /> updateConfigMutation.mutate({ spin_cost_stars_enabled: e.target.checked })} className="rounded border-dark-600" /> {t('admin.wheel.enabled')} {t('admin.wheel.settings.costInDays')} updateConfigMutation.mutate({ spin_cost_days: parseInt(e.target.value) || 1 })} min={1} max={30} className="input flex-1" /> updateConfigMutation.mutate({ spin_cost_days_enabled: e.target.checked })} className="rounded border-dark-600" /> {t('admin.wheel.enabled')} {/* RTP and limits */} {t('admin.wheel.settings.rtpPercent')} updateConfigMutation.mutate({ rtp_percent: parseInt(e.target.value) })} className="w-full" /> 0% {config.rtp_percent}% 100% {t('admin.wheel.settings.dailyLimit')} updateConfigMutation.mutate({ daily_spin_limit: parseInt(e.target.value) || 0 })} min={0} max={100} className="input w-full" /> {t('admin.wheel.settings.minSubDays')} updateConfigMutation.mutate({ min_subscription_days_for_day_payment: parseInt(e.target.value) || 1 })} min={1} max={30} className="input w-full" /> {t('admin.wheel.settings.promoPrefix')} updateConfigMutation.mutate({ promo_prefix: e.target.value })} maxLength={20} className="input w-full" /> )} {/* Prizes Tab */} {activeTab === 'prizes' && ( setIsCreating(true)} className="btn-primary flex items-center gap-2" > {t('admin.wheel.prizes.addPrize')} {/* 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)}₽ setEditingPrize(prize)} className="btn-ghost text-sm" > {t('common.edit')} { if (confirm(t('admin.wheel.prizes.deletePrize'))) { deletePrizeMutation.mutate(prize.id) } }} className="btn-ghost text-red-400" > ))} {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 */} {t('admin.wheel.prizes.fields.type')} setFormData({ ...formData, prize_type: e.target.value })} className="input w-full" > {PRIZE_TYPE_KEYS.map((type) => ( {type.emoji} {t(`admin.wheel.prizes.types.${type.key}`)} ))} {/* Display name */} {t('admin.wheel.prizes.fields.displayName')} 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' && ( {t('admin.wheel.prizes.fields.value')} ({formData.prize_type === 'balance_bonus' ? 'kopeks' : formData.prize_type === 'subscription_days' ? 'days' : 'GB'}) setFormData({ ...formData, prize_value: parseInt(e.target.value) || 0 })} min={0} className="input w-full" /> )} {/* Prize value in kopeks (for RTP calculation) */} {t('admin.wheel.prizes.fields.valueKopeks')} 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 */} {t('admin.wheel.prizes.fields.emoji')} setFormData({ ...formData, emoji: e.target.value })} maxLength={10} className="input w-full text-center text-2xl" /> {t('admin.wheel.prizes.fields.color')} 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" /> {t('admin.wheel.prizes.fields.active')} {/* Promocode settings */} {formData.prize_type === 'promocode' && ( {t('admin.wheel.prizes.promo.title')} {t('admin.wheel.prizes.promo.balanceBonus')} setFormData({ ...formData, promo_balance_bonus_kopeks: parseInt(e.target.value) || 0 })} min={0} className="input w-full" /> {t('admin.wheel.prizes.promo.subscriptionDays')} setFormData({ ...formData, promo_subscription_days: parseInt(e.target.value) || 0 })} min={0} className="input w-full" /> )} {/* Buttons */} {t('common.cancel')} {prize ? t('common.save') : t('common.confirm')} ) }
{t('admin.wheel.settings.allowSpins')}
= {(formData.prize_value_kopeks / 100).toFixed(2)} RUB