/** * CoinRush User Profile & Settings - React App * Complete premium profile/settings page with modern UI */ const { useState, useEffect, useRef } = React; // ============================================ // UTILITIES // ============================================ const fmt = (cents) => '$' + ((cents || 0) / 100).toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 }); const colorFrom = (name) => { if (!name) return '#5bffb2'; let hash = 0; for (let i = 0; i < name.length; i++) { hash = ((hash << 5) - hash) + name.charCodeAt(i); } const hue = Math.abs(hash % 360); return `hsl(${hue}, 65%, 50%)`; }; const formatDate = (dateStr) => { if (!dateStr) return '—'; return new Date(dateStr).toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' }); }; const formatTimeAgo = (dateStr) => { if (!dateStr) return 'Never'; const diff = Date.now() - new Date(dateStr); const mins = Math.floor(diff / 60000); if (mins < 1) return 'Just now'; if (mins < 60) return `${mins}m ago`; const hours = Math.floor(mins / 60); if (hours < 24) return `${hours}h ago`; const days = Math.floor(hours / 24); return `${days}d ago`; }; // Get the PNG border image path based on level const getBorderImage = (level) => { if (level >= 50) return '/static/img/borders/Challengerborder.png'; if (level >= 40) return '/static/img/borders/Rubyborder.png'; if (level >= 30) return '/static/img/borders/Diamondborder.png'; if (level >= 20) return '/static/img/borders/Goldborder.png'; if (level >= 10) return '/static/img/borders/Silverborder.png'; return '/static/img/borders/Bronzeborder.png'; }; // ============================================ // FLOATING ORBS COMPONENT // ============================================ function FloatingOrbs() { return ( <>
{[...Array(12)].map((_, i) => (
))} ); } // ============================================ // LOADING COMPONENT // ============================================ function LoadingState() { return (
Loading your profile...
); } // ============================================ // TOGGLE SWITCH COMPONENT // ============================================ function Toggle({ checked, onChange, disabled }) { return ( ); } // ============================================ // TAB NAVIGATION // ============================================ function TabNav({ activeTab, onTabChange }) { const tabs = [ { id: 'overview', label: 'Overview', icon: 'M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6' }, { id: 'stats', label: 'Statistics', icon: 'M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z' }, { id: 'referrals', label: 'Referrals', icon: 'M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z', special: true }, { id: 'security', label: 'Security', icon: 'M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z' }, { id: 'settings', label: 'Settings', icon: 'M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z M15 12a3 3 0 11-6 0 3 3 0 016 0z' }, ]; return (
{tabs.map(tab => ( ))}
); } // ============================================ // PROFILE HERO SECTION // ============================================ function ProfileHero({ user, stats, levelInfo, xpBoost }) { const avatarColor = colorFrom(user?.username); const avatarInitials = user?.username ? user.username.slice(0, 2).toUpperCase() : '??'; const level = user?.level || 1; // Level progress calculation const currentXP = levelInfo?.progress_in_level || 0; const xpToNext = levelInfo?.next_level_wagered ? (levelInfo.next_level_wagered - (levelInfo.current_level_wagered || 0)) : 10000; const progressPct = xpToNext > 0 ? Math.min(100, (currentXP / xpToNext) * 100) : 100; // Account tier based on level const getTier = (lvl) => { if (lvl >= 50) return { name: 'Challenger', color: '#ff6b6b', glow: 'rgba(255, 107, 107, 0.3)' }; if (lvl >= 40) return { name: 'Ruby', color: '#e11d48', glow: 'rgba(225, 29, 72, 0.3)' }; if (lvl >= 30) return { name: 'Diamond', color: '#06b6d4', glow: 'rgba(6, 182, 212, 0.3)' }; if (lvl >= 20) return { name: 'Gold', color: '#fbbf24', glow: 'rgba(251, 191, 36, 0.3)' }; if (lvl >= 10) return { name: 'Silver', color: '#94a3b8', glow: 'rgba(148, 163, 184, 0.3)' }; return { name: 'Bronze', color: '#cd7f32', glow: 'rgba(205, 127, 50, 0.3)' }; }; const tier = getTier(level); const borderImage = getBorderImage(level); return (
{/* Avatar Section */}
{avatarInitials}
Level Border
{user?.username || 'Player'}
{user?.email || ''}
{/* Tier Badge */}
{tier.name} Tier
{/* Level Progress */}
Level {level} {fmt(currentXP)} / {fmt(xpToNext)}
{xpToNext - currentXP > 0 ? `${fmt(xpToNext - currentXP)} to Level ${level + 1}` : 'Max Level!'}
{/* XP Boost Indicator */} {xpBoost > 0 && (
+{xpBoost}% XP Boost Active
)}
{/* Balance Display */}
Available Balance
{fmt(user?.balance || 0)}
); } // ============================================ // QUICK STATS CARDS // ============================================ function QuickStats({ user, stats }) { const overall = stats?.overall || {}; const cf = stats?.coinflip || {}; const bb = stats?.barbarian || {}; const totalBattles = (cf.played || 0) + (bb.played || 0); const totalWins = (cf.wins || 0) + (bb.wins || 0); const winRate = totalBattles > 0 ? Math.round((totalWins / totalBattles) * 100) : 0; const netPL = overall.net || 0; const statsData = [ { label: 'Total Wagered', value: fmt(user?.total_wagered || overall.wagered || 0), icon: 'M12 8c-1.657 0-3 .895-3 2s1.343 2 3 2 3 .895 3 2-1.343 2-3 2m0-8c1.11 0 2.08.402 2.599 1M12 8V7m0 1v8m0 0v1m0-1c-1.11 0-2.08-.402-2.599-1M21 12a9 9 0 11-18 0 9 9 0 0118 0z' }, { label: 'Games Played', value: totalBattles, icon: 'M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z M21 12a9 9 0 11-18 0 9 9 0 0118 0z' }, { label: 'Win Rate', value: `${winRate}%`, icon: 'M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z', highlight: winRate >= 50 }, { label: 'Net P/L', value: `${netPL >= 0 ? '+' : ''}${fmt(Math.abs(netPL))}`, icon: 'M13 7h8m0 0v8m0-8l-8 8-4-4-6 6', isPositive: netPL >= 0, isNegative: netPL < 0 }, ]; return (
{statsData.map((stat, i) => (
{stat.value}
{stat.label}
))}
); } // ============================================ // RECENT ACTIVITY // ============================================ function RecentActivity({ stats }) { const activities = stats?.recent_activity || []; if (activities.length === 0) { return (

Recent Activity

No recent activity yet

Start playing to see your game history here!
); } return (

Recent Activity

{activities.slice(0, 5).map((act, i) => (
{act.type === 'win' ? ( ) : ( )}
{act.description}
{formatTimeAgo(act.timestamp)}
{act.type === 'win' ? '+' : ''}{fmt(Math.abs(act.amount || 0))}
))}
); } // ============================================ // GAME STATISTICS TAB // ============================================ function StatisticsTab({ stats }) { const cf = stats?.coinflip || {}; const bb = stats?.barbarian || {}; const jp = stats?.jackpot || {}; const lottery = stats?.lottery || {}; const sliceDice = stats?.slice_dice || {}; const crash = stats?.crash || {}; const games = [ { name: 'Coinflip', data: cf, color: '#fbbf24', icon: 'M12 8c-1.657 0-3 .895-3 2s1.343 2 3 2 3 .895 3 2-1.343 2-3 2m0-8c1.11 0 2.08.402 2.599 1M12 8V7m0 1v8m0 0v1m0-1c-1.11 0-2.08-.402-2.599-1M21 12a9 9 0 11-18 0 9 9 0 0118 0z' }, { name: 'Knights Arena', data: bb, color: '#ef4444', icon: 'M9 12l2 2 4-4M7.835 4.697a3.42 3.42 0 001.946-.806 3.42 3.42 0 014.438 0 3.42 3.42 0 001.946.806 3.42 3.42 0 013.138 3.138 3.42 3.42 0 00.806 1.946 3.42 3.42 0 010 4.438 3.42 3.42 0 00-.806 1.946 3.42 3.42 0 01-3.138 3.138 3.42 3.42 0 00-1.946.806 3.42 3.42 0 01-4.438 0 3.42 3.42 0 00-1.946-.806 3.42 3.42 0 01-3.138-3.138 3.42 3.42 0 00-.806-1.946 3.42 3.42 0 010-4.438 3.42 3.42 0 00.806-1.946 3.42 3.42 0 013.138-3.138z' }, { name: 'Jackpot', data: jp, color: '#22c55e', icon: 'M12 8v13m0-13V6a2 2 0 112 2h-2zm0 0V5.5A2.5 2.5 0 109.5 8H12zm-7 4h14M5 12a2 2 0 110-4h14a2 2 0 110 4M5 12v7a2 2 0 002 2h10a2 2 0 002-2v-7' }, { name: 'Lottery', data: lottery, color: '#a855f7', icon: 'M15 5v2m0 4v2m0 4v2M5 5a2 2 0 00-2 2v3a2 2 0 110 4v3a2 2 0 002 2h14a2 2 0 002-2v-3a2 2 0 110-4V7a2 2 0 00-2-2H5z' }, { name: "Slice n' Dice", data: sliceDice, color: '#ec4899', icon: 'M4 5a1 1 0 011-1h14a1 1 0 011 1v2a1 1 0 01-1 1H5a1 1 0 01-1-1V5zM4 13a1 1 0 011-1h6a1 1 0 011 1v6a1 1 0 01-1 1H5a1 1 0 01-1-1v-6zM16 13a1 1 0 011-1h2a1 1 0 011 1v6a1 1 0 01-1 1h-2a1 1 0 01-1-1v-6z' }, { name: 'Crash', data: crash, color: '#06b6d4', icon: 'M13 7h8m0 0v8m0-8l-8 8-4-4-6 6' }, ]; return (
{/* Game Cards */}
{games.map((game, i) => (

{game.name}

Games Played {game.data.played || 0}
Wins {game.data.wins || 0}
Losses {game.data.losses || 0}
Win Rate {game.data.win_rate || 0}%
Net P/L = 0 ? 'positive' : 'negative'}> {(game.data.net || 0) >= 0 ? '+' : ''}{fmt(game.data.net || 0)}
{(game.data.biggest_win || 0) > 0 && (
Biggest Win {fmt(game.data.biggest_win)}
)}
))}
{/* Overall Stats */}

Overall Performance

{fmt(stats?.overall?.wagered || 0)}
Total Wagered
= 0 ? 'positive' : 'negative'}`}> {(stats?.overall?.net || 0) >= 0 ? '+' : ''}{fmt(Math.abs(stats?.overall?.net || 0))}
Net Profit/Loss
= 0 ? 'positive' : 'negative'}`}> {stats?.overall?.roi || 0}%
Return on Investment
); } // ============================================ // REFERRALS TAB (Affiliate Program) - Premium Glowing Design // ============================================ function ReferralsTab({ referralData, onClaimBonus, onClaimVault, vaultClaimLoading, hasReferrer }) { const [copyText, setCopyText] = useState('Copy'); const [claimCode, setClaimCode] = useState(''); const [claimMessage, setClaimMessage] = useState(null); const [isClaimLoading, setIsClaimLoading] = useState(false); const copyReferralCode = () => { navigator.clipboard.writeText(referralData?.referral_code || ''); setCopyText('Copied!'); setTimeout(() => setCopyText('Copy'), 2000); }; const handleClaimBonus = async () => { if (!claimCode.trim()) { setClaimMessage({ type: 'error', text: 'Please enter a referral code' }); return; } setIsClaimLoading(true); setClaimMessage(null); try { const res = await fetch('/api/referral/claim-bonus', { method: 'POST', headers: { 'Content-Type': 'application/json' }, credentials: 'include', body: JSON.stringify({ referral_code: claimCode.trim() }) }); const data = await res.json(); if (!res.ok) { throw new Error(data.detail || 'Failed to claim bonus'); } setClaimMessage({ type: 'success', text: data.message }); onClaimBonus(); } catch (error) { setClaimMessage({ type: 'error', text: error.message }); } finally { setIsClaimLoading(false); } }; const unclaimedAmount = referralData?.unclaimed_earnings || 0; return (
{/* Floating Reward Particles - SVG coins */}
{[...Array(8)].map((_, i) => (
))}
{/* Vault Section - Clean & Glowy */} {unclaimedAmount > 0 && (
REFERRAL VAULT
{fmt(unclaimedAmount)}
Ready to claim!
)} {/* Hero Banner - Attention Grabbing */}

Earn Up To $2.50 Per Friend!

Share your code • Friends sign up • You earn 1% of their first $250 in winnings

Popular
{/* How It Works - Visual Steps */}

How It Works

1
Share Your Code

Copy your unique referral code below

2
Friends Sign Up

They register using your code

3
Earn Rewards!

Get 1% of their first $250 winnings

{/* Your Referral Code - Premium Glow Box */}

Your Referral Code Click to copy!

{/* Referral Stats - Glowing Cards */}
{referralData?.total_referrals || 0}
Total Referrals
{fmt(referralData?.total_earnings || 0)}
Total Earned
{fmt(unclaimedAmount)}
Pending
{/* Claim a Code */} {!hasReferrer && (

Have a Friend's Code?

{claimMessage && (
{claimMessage.text}
)}
setClaimCode(e.target.value.toUpperCase())} />
)}
); } // ============================================ // SECURITY TAB // ============================================ function SecurityTab({ user }) { const [twoFactorEnabled, setTwoFactorEnabled] = useState(user?.two_factor_enabled || false); const [showPasswordModal, setShowPasswordModal] = useState(false); return (
{/* Account Security Status */}

Security Status

{user?.email_verified ? ( ) : ( )}
Email Verification
{user?.email_verified ? 'Verified' : 'Not verified'}
Two-Factor Authentication
{twoFactorEnabled ? 'Enabled' : 'Disabled'}
setTwoFactorEnabled(val)} />
{/* Password & Sessions */}

Password & Access

{/* Login History */}

Recent Logins

Current Session
Active now
This device
); } // ============================================ // SETTINGS TAB // ============================================ function SettingsTab({ user, preferences, onUpdatePreferences }) { const [soundEnabled, setSoundEnabled] = useState(preferences?.sound ?? true); const [notificationsEnabled, setNotificationsEnabled] = useState(preferences?.notifications ?? true); const [compactMode, setCompactMode] = useState(preferences?.compactMode ?? false); const [showConfirmations, setShowConfirmations] = useState(preferences?.confirmations ?? true); return (
{/* Display Preferences */}

Display

Compact Mode
Use a more condensed layout
{/* Sound & Notifications */}

Sound & Notifications

Sound Effects
Play sounds for wins, losses, and actions
Browser Notifications
Get notified about game results
{/* Gameplay */}

Gameplay

Bet Confirmations
Show confirmation dialog before placing bets
{/* Account Info */}

Account

Username {user?.username}
Email {user?.email}
Member Since {formatDate(user?.created_at)}
Account ID #{user?.id || '—'}
{/* Danger Zone */}

Danger Zone

These actions are irreversible. Please be certain.

); } // ============================================ // MAIN APP COMPONENT // ============================================ function UserProfileApp() { const [user, setUser] = useState(null); const [stats, setStats] = useState(null); const [referralData, setReferralData] = useState(null); const [levelInfo, setLevelInfo] = useState(null); const [xpBoost, setXpBoost] = useState(0); const [isLoading, setIsLoading] = useState(true); const [activeTab, setActiveTab] = useState('overview'); const [vaultClaimLoading, setVaultClaimLoading] = useState(false); const [preferences, setPreferences] = useState({}); useEffect(() => { loadData(); }, []); const loadData = async () => { try { // Check authentication const meRes = await fetch('/me', { credentials: 'include' }); if (!meRes.ok) { alert('You need to be logged in to view your profile.'); window.location.href = '/'; return; } const meData = await meRes.json(); setUser(meData); setLevelInfo(meData.level_info); // Fetch extended profile stats const profileRes = await fetch('/api/user/profile', { credentials: 'include' }); if (profileRes.ok) { const profileData = await profileRes.json(); setStats(profileData.statistics); setUser(prev => ({ ...prev, ...profileData.user })); } // Fetch daily reward status for XP boost try { const dailyRes = await fetch('/api/daily-reward/status', { credentials: 'include' }); if (dailyRes.ok) { const dailyData = await dailyRes.json(); if (dailyData.active_bonuses?.xp_boost > 0) { setXpBoost(dailyData.active_bonuses.xp_boost); } } } catch {} // Fetch referral data await loadReferralData(); } catch (error) { console.error('Failed to load profile:', error); } finally { setIsLoading(false); } }; const loadReferralData = async () => { try { const res = await fetch('/api/referral/stats', { credentials: 'include' }); if (res.ok) { const data = await res.json(); setReferralData(data); } } catch (error) { console.error('Error loading referral data:', error); } }; const claimVaultEarnings = async () => { setVaultClaimLoading(true); try { const res = await fetch('/api/referral/claim-earnings', { method: 'POST', credentials: 'include', headers: { 'Content-Type': 'application/json' } }); const data = await res.json(); if (res.ok && data.success) { setUser(prev => ({ ...prev, balance: data.new_balance * 100 })); await loadReferralData(); } } catch (error) { console.error('Error claiming vault:', error); } finally { setVaultClaimLoading(false); } }; if (isLoading) { return ( <>
); } return ( <>
{/* Profile Hero */} {/* Tab Navigation */} {/* Tab Content */}
{activeTab === 'overview' && ( <> )} {activeTab === 'stats' && ( )} {activeTab === 'referrals' && ( )} {activeTab === 'security' && ( )} {activeTab === 'settings' && ( )}
{/* Back Button */}
); } // ============================================ // MOUNT APP // ============================================ const root = ReactDOM.createRoot(document.getElementById('root')); root.render();