/** * CoinRush Social Panel - Friends, Guilds, and Private Messages * Comprehensive social system with React */ const { useState, useEffect, useRef, useCallback } = React; // ============================================ // ICONS // ============================================ const SocialIcons = { users: ( ), guild: ( ), message: ( ), add: ( ), check: ( ), x: ( ), send: ( ), crown: ( ), logout: ( ), }; // ============================================ // FRIEND LIST COMPONENT // ============================================ function FriendsList({ currentUser, onOpenDM }) { const [friends, setFriends] = useState([]); const [requests, setRequests] = useState({ received: [], sent: [] }); const [showAddFriend, setShowAddFriend] = useState(false); const [addUsername, setAddUsername] = useState(''); const [loading, setLoading] = useState(false); const loadFriends = useCallback(async () => { try { const res = await fetch('/api/friends/list'); if (res.ok) { const data = await res.json(); setFriends(data.friends || []); } } catch (e) { console.error('Failed to load friends:', e); } }, []); const loadRequests = useCallback(async () => { try { const res = await fetch('/api/friends/requests'); if (res.ok) { const data = await res.json(); setRequests(data); } } catch (e) { console.error('Failed to load requests:', e); } }, []); useEffect(() => { loadFriends(); loadRequests(); const interval = setInterval(() => { loadFriends(); loadRequests(); }, 30000); // Refresh every 30 seconds return () => clearInterval(interval); }, [loadFriends, loadRequests]); const sendRequest = async () => { if (!addUsername.trim()) return; setLoading(true); try { const res = await fetch('/api/friends/add', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username: addUsername.trim() }) }); const data = await res.json(); if (res.ok) { alert(data.message); setAddUsername(''); setShowAddFriend(false); loadFriends(); loadRequests(); } else { alert(data.detail || 'Failed to send request'); } } catch (e) { alert('Network error'); } finally { setLoading(false); } }; const acceptRequest = async (requestId) => { try { const res = await fetch(`/api/friends/accept/${requestId}`, { method: 'POST' }); if (res.ok) { loadFriends(); loadRequests(); } } catch (e) { console.error('Failed to accept:', e); } }; const rejectRequest = async (requestId) => { try { const res = await fetch(`/api/friends/reject/${requestId}`, { method: 'POST' }); if (res.ok) { loadRequests(); } } catch (e) { console.error('Failed to reject:', e); } }; const removeFriend = async (friendId) => { if (!confirm('Remove this friend?')) return; try { const res = await fetch(`/api/friends/remove/${friendId}`, { method: 'DELETE' }); if (res.ok) { loadFriends(); } } catch (e) { console.error('Failed to remove:', e); } }; return (

{SocialIcons.users} Friends ({friends.length})

{showAddFriend && (
setAddUsername(e.target.value)} onKeyPress={(e) => e.key === 'Enter' && sendRequest()} disabled={loading} />
)} {requests.received.length > 0 && (

Friend Requests ({requests.received.length})

{requests.received.map(req => (
{req.username} LVL {req.level}
))}
)}
{friends.length === 0 ? (

No friends yet. Add some to start chatting!

) : ( friends.map(friend => (
{friend.username.charAt(0).toUpperCase()}
{friend.username} LVL {friend.level}
)) )}
); } // ============================================ // PRIVATE MESSAGE MODAL // ============================================ function PrivateMessageModal({ friend, onClose, currentUser }) { const [messages, setMessages] = useState([]); const [inputValue, setInputValue] = useState(''); const [loading, setLoading] = useState(false); const messagesEndRef = useRef(null); const loadMessages = useCallback(async () => { if (!friend) return; try { const res = await fetch(`/api/messages/conversation/${friend.id}`); if (res.ok) { const data = await res.json(); setMessages(data.messages || []); } } catch (e) { console.error('Failed to load messages:', e); } }, [friend]); useEffect(() => { loadMessages(); const interval = setInterval(loadMessages, 3000); // Poll every 3 seconds return () => clearInterval(interval); }, [loadMessages]); useEffect(() => { messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }); }, [messages]); const sendMessage = async () => { if (!inputValue.trim() || loading) return; setLoading(true); try { const res = await fetch('/api/messages/send', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ receiver_username: friend.username, message: inputValue.trim() }) }); if (res.ok) { setInputValue(''); loadMessages(); } else { const data = await res.json(); alert(data.detail || 'Failed to send message'); } } catch (e) { alert('Network error'); } finally { setLoading(false); } }; if (!friend) return null; return (
e.stopPropagation()}>

{SocialIcons.message} {friend.username}

{messages.length === 0 ? (

No messages yet. Start the conversation!

) : ( messages.map((msg, idx) => (
{msg.message}
{new Date(msg.created_at).toLocaleTimeString()}
)) )}
setInputValue(e.target.value)} onKeyPress={(e) => e.key === 'Enter' && sendMessage()} disabled={loading} maxLength={500} />
); } // ============================================ // GUILD WARS COMPONENT // ============================================ function GuildWars({ guild, myRole }) { const [activeWar, setActiveWar] = useState(null); const [warHistory, setWarHistory] = useState([]); const [timeRemaining, setTimeRemaining] = useState('--:--:--'); useEffect(() => { // Mock data - in production, fetch from API const mockWar = { id: 1, enemy_guild: { name: 'Shadow Legion', tag: 'SHDW', score: 4250 }, our_score: 5120, status: 'active', ends_at: new Date(Date.now() + 3600000 * 2).toISOString() // 2 hours }; setActiveWar(mockWar); // Timer countdown const timer = setInterval(() => { if (mockWar) { const now = new Date(); const end = new Date(mockWar.ends_at); const diff = end - now; if (diff > 0) { const hours = Math.floor(diff / 3600000); const mins = Math.floor((diff % 3600000) / 60000); const secs = Math.floor((diff % 60000) / 1000); setTimeRemaining(`${hours.toString().padStart(2, '0')}:${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`); } else { setTimeRemaining('ENDED'); } } }, 1000); return () => clearInterval(timer); }, []); if (!activeWar) { return (

⚔️ Guild Wars

No Active War

No active guild war. Start one to compete against other guilds!

{myRole === 'leader' && ( )}
); } return (

⚔️ Guild Wars

🔴 LIVE
[{guild?.tag || 'YOU'}] {activeWar.our_score.toLocaleString()}
VS
[{activeWar.enemy_guild.tag}] {activeWar.enemy_guild.score.toLocaleString()}
Time Remaining
{timeRemaining}
); } // ============================================ // WEEKLY CHALLENGES COMPONENT // ============================================ function WeeklyChallenges({ guild }) { const [challenges, setChallenges] = useState([]); const [timeUntilReset, setTimeUntilReset] = useState(''); useEffect(() => { // Mock challenges data setChallenges([ { id: 1, title: 'Wager $50,000 collectively', icon: '💰', iconType: 'wager', current: 32500, target: 50000, reward: 5000, completed: false }, { id: 2, title: 'Win 100 games as a guild', icon: '🏆', iconType: 'wins', current: 87, target: 100, reward: 3000, completed: false }, { id: 3, title: 'Have 5 members hit a 10x multiplier', icon: '🔥', iconType: 'streak', current: 5, target: 5, reward: 2500, completed: true }, { id: 4, title: 'Recruit 3 new members', icon: '👥', iconType: 'social', current: 1, target: 3, reward: 1500, completed: false } ]); // Calculate time until Sunday midnight const now = new Date(); const sunday = new Date(now); sunday.setDate(sunday.getDate() + (7 - sunday.getDay())); sunday.setHours(23, 59, 59, 999); const updateTimer = () => { const diff = sunday - new Date(); if (diff > 0) { const days = Math.floor(diff / (1000 * 60 * 60 * 24)); const hours = Math.floor((diff % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)); setTimeUntilReset(`${days}d ${hours}h`); } }; updateTimer(); const timer = setInterval(updateTimer, 60000); return () => clearInterval(timer); }, []); return (

🎯 Weekly Challenges

⏱️ Resets in {timeUntilReset}
{challenges.map(challenge => (
{challenge.icon}
{challenge.title}
{challenge.current.toLocaleString()} / {challenge.target.toLocaleString()}
${(challenge.reward / 100).toFixed(0)} Reward
))}
); } // ============================================ // GUILD PERKS COMPONENT // ============================================ function GuildPerks({ guild, level }) { const allPerks = [ { id: 1, name: 'Lucky Bonus', description: '+1% on all wins', icon: '🍀', requiredLevel: 1, active: level >= 1 }, { id: 2, name: 'Group Chat', description: 'Private guild chat', icon: '💬', requiredLevel: 2, active: level >= 2 }, { id: 3, name: 'XP Boost', description: '+10% XP for all members', icon: '⚡', requiredLevel: 3, active: level >= 3 }, { id: 4, name: 'Daily Bonus', description: 'Extra daily reward', icon: '🎁', requiredLevel: 5, active: level >= 5 }, { id: 5, name: 'Fee Rebate', description: '-5% platform fees', icon: '💎', requiredLevel: 7, active: level >= 7 }, { id: 6, name: 'VIP Access', description: 'Exclusive games', icon: '👑', requiredLevel: 10, active: level >= 10 }, ]; return (

Guild Perks

{allPerks.map(perk => (
{perk.icon}
{perk.name}
{perk.description}
{!perk.active && (
Level {perk.requiredLevel}
)}
))}
); } // ============================================ // GUILD ACHIEVEMENTS COMPONENT // ============================================ function GuildAchievements({ guild }) { const achievements = [ { id: 1, name: 'First Steps', icon: '👶', unlocked: true }, { id: 2, name: 'Growing Strong', icon: '💪', unlocked: true }, { id: 3, name: 'War Victor', icon: '⚔️', unlocked: false }, { id: 4, name: 'Millionaire', icon: '💰', unlocked: true }, { id: 5, name: 'Social Butterfly', icon: '🦋', unlocked: false }, { id: 6, name: 'Challenge Master', icon: '🎯', unlocked: false }, { id: 7, name: 'Legendary', icon: '🌟', unlocked: false }, { id: 8, name: 'Undefeated', icon: '🏆', unlocked: false }, ]; return (

🏅 Achievements

{achievements.map(ach => (
{ach.icon}
{ach.name}
))}
); } // ============================================ // GUILD ACTIVITY FEED COMPONENT // ============================================ function GuildActivityFeed({ guildId }) { const [activities, setActivities] = useState([]); useEffect(() => { // Mock activity data setActivities([ { id: 1, user: 'CryptoKing', action: 'won big', amount: '+$2,450', time: '2m ago', positive: true }, { id: 2, user: 'LuckyAce', action: 'completed challenge', amount: '+$50', time: '5m ago', positive: true }, { id: 3, user: 'NightRider', action: 'donated to treasury', amount: '+$100', time: '12m ago', positive: true }, { id: 4, user: 'StarGazer', action: 'joined the guild', amount: null, time: '25m ago', positive: null }, { id: 5, user: 'WaveRunner', action: 'won war point', amount: '+10 pts', time: '32m ago', positive: true }, ]); }, [guildId]); return (

Live Activity

{activities.map(activity => (
{activity.user.charAt(0)}
{activity.user} {activity.action}
{activity.time}
{activity.amount && (
{activity.amount}
)}
))}
); } // ============================================ // GUILD PANEL // ============================================ // ============================================ // Donate Modal // ============================================ function DonateModal({ onClose, onDonate }) { const [amount, setAmount] = useState(''); return (
e.stopPropagation()}>

Donate to Treasury

Contribute to your guild's treasury to help unlock perks and events.

$ setAmount(e.target.value)} autoFocus />
); } function GuildPanel({ currentUser }) { const [myGuild, setMyGuild] = useState(null); const [activeTab, setActiveTab] = useState('overview'); // overview, members, challenges, leaderboard, browse, create const [publicGuilds, setPublicGuilds] = useState([]); const [leaderboard, setLeaderboard] = useState([]); const [createForm, setCreateForm] = useState({ name: '', tag: '', description: '', is_public: true }); const [loading, setLoading] = useState(false); const [showDonate, setShowDonate] = useState(false); const loadMyGuild = useCallback(async () => { try { const res = await fetch('/api/guilds/my-guild'); if (res.ok) { const data = await res.json(); if (data.in_guild) { setMyGuild(data); if (activeTab === 'browse' || activeTab === 'create') setActiveTab('overview'); } else { setMyGuild(null); if (activeTab === 'overview' || activeTab === 'members') setActiveTab('browse'); } } } catch (e) { console.error('Failed to load guild:', e); } }, [activeTab]); const loadPublicGuilds = useCallback(async () => { try { const res = await fetch('/api/guilds/list'); if (res.ok) { const data = await res.json(); setPublicGuilds(data.guilds || []); } } catch (e) { console.error('Failed to load guilds:', e); } }, []); const loadLeaderboard = useCallback(async () => { try { const res = await fetch('/api/guilds/leaderboard'); if (res.ok) { const data = await res.json(); setLeaderboard(data.leaderboard || []); } } catch (e) { console.error('Failed to load leaderboard:', e); } }, []); useEffect(() => { loadMyGuild(); if (activeTab === 'browse') loadPublicGuilds(); if (activeTab === 'leaderboard') loadLeaderboard(); }, [loadMyGuild, activeTab, loadPublicGuilds, loadLeaderboard]); const createGuild = async () => { if (!createForm.name.trim() || !createForm.tag.trim()) { alert('Name and tag are required'); return; } setLoading(true); try { const res = await fetch('/api/guilds/create', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(createForm) }); const data = await res.json(); if (res.ok) { alert(data.message); setCreateForm({ name: '', tag: '', description: '', is_public: true }); loadMyGuild(); } else { alert(data.detail || 'Failed to create guild'); } } catch (e) { alert('Network error'); } finally { setLoading(false); } }; const joinGuild = async (guildId) => { try { const res = await fetch(`/api/guilds/join/${guildId}`, { method: 'POST' }); const data = await res.json(); if (res.ok) { alert(data.message); loadMyGuild(); } else { alert(data.detail || 'Failed to join guild'); } } catch (e) { alert('Network error'); } }; const leaveGuild = async () => { if (!confirm('Are you sure you want to leave your guild?')) return; try { const res = await fetch('/api/guilds/leave', { method: 'POST' }); const data = await res.json(); if (res.ok) { alert(data.message); loadMyGuild(); } } catch (e) { alert('Network error'); } }; const handleDonate = async (amount) => { if (!amount || amount <= 0) return; try { const res = await fetch('/api/guilds/donate', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ amount }) }); const data = await res.json(); if (res.ok) { alert(data.message); setShowDonate(false); loadMyGuild(); } else { alert(data.detail || 'Failed to donate'); } } catch (e) { alert('Network error'); } }; const handleKick = async (userId) => { if (!confirm('Kick this member?')) return; try { const res = await fetch(`/api/guilds/kick/${userId}`, { method: 'POST' }); if (res.ok) { loadMyGuild(); } else { const data = await res.json(); alert(data.detail); } } catch (e) { alert('Network error'); } }; const handlePromote = async (userId) => { try { const res = await fetch(`/api/guilds/promote/${userId}`, { method: 'POST' }); if (res.ok) { loadMyGuild(); } else { const data = await res.json(); alert(data.detail); } } catch (e) { alert('Network error'); } }; // Render Content based on state const renderContent = () => { if (activeTab === 'leaderboard') { const top3 = leaderboard.slice(0, 3); const rest = leaderboard.slice(3); return (
{/* Podium for top 3 */} {top3.length >= 3 && (
🥈
[{top3[1].tag}]
{top3[1].name}
${(top3[1].total_wagered / 100).toLocaleString()}
👑
[{top3[0].tag}]
{top3[0].name}
${(top3[0].total_wagered / 100).toLocaleString()}
🥉
[{top3[2].tag}]
{top3[2].name}
${(top3[2].total_wagered / 100).toLocaleString()}
)} {/* Rest of leaderboard */}
Rank
Guild
Members
Total Wagered
{(top3.length < 3 ? leaderboard : rest).map((g, idx) => (
{top3.length < 3 && idx === 0 ? '🥇' : top3.length < 3 && idx === 1 ? '🥈' : top3.length < 3 && idx === 2 ? '🥉' : `#${g.rank || idx + 4}`}
[{g.tag}] {g.name}
{g.total_members}
${(g.total_wagered / 100).toLocaleString()}
))}
); } if (myGuild && myGuild.in_guild) { const { guild, members, my_role } = myGuild; if (activeTab === 'wars') { return (

📜 War History

[{guild.tag}] vs [SHDW]
5,120 - 4,250
Victory
[{guild.tag}] vs [APEX]
3,800 - 2,100
Victory
[{guild.tag}] vs [ELITE]
2,500 - 4,800
Defeat
); } if (activeTab === 'members') { return (

Members ({members.length})

{members.map(member => (
{member.username} {member.role === 'leader' && {SocialIcons.crown} Leader} {member.role === 'officer' && Officer}
LVL {member.level} ${(member.contribution_wagered / 100).toLocaleString()} contributed
{/* Management Actions */} {my_role === 'leader' && member.role !== 'leader' && (
)}
))}
); } // Overview const nextLevelWager = guild.level * 1000000; // Mock requirement const progress = Math.min(100, (guild.total_wagered / nextLevelWager) * 100); return (

[{guild.tag}] {guild.name}

{guild.description || 'No description set.'}

{/* Level Progress */}
Level {guild.level} Level {guild.level + 1}
${(guild.total_wagered / 100).toLocaleString()} / ${(nextLevelWager / 100).toLocaleString()} wagered
Level {guild.level}
Members {guild.total_members}/{guild.max_members}
Total Wagered ${(guild.total_wagered / 100).toLocaleString()}
Treasury
${(guild.treasury / 100).toLocaleString()}
{/* Guild Wars */} {/* Weekly Challenges */} {/* Guild Perks */} {/* Guild Achievements */} {/* Activity Feed */}
); } // Not in guild if (activeTab === 'create') { return (

Create a New Guild

setCreateForm({...createForm, name: e.target.value})} maxLength={32} /> setCreateForm({...createForm, tag: e.target.value.toUpperCase()})} maxLength={6} />