import React, {useCallback, useEffect, useState} from 'react'; import {ActivityIndicator, Alert, Pressable, ScrollView, StyleSheet, Text, View} from 'react-native'; import Screen from '../layout/Screen'; import Card from '../UI/Card'; import {Button} from '../UI/Button'; import PendingApprovalView from '../UI/PendingApprovalView'; import useGameHook from '../../hooks/useGameHook'; import {getSession} from '../../hooks/SessionStore'; const LeaderboardScreen = ({navigation, route}) => { // Initialisation ------------ const session = getSession(); const isAdmin = session.isAcceptedAdmin; const isBusiness = Boolean(session.isBusiness); const selectedDepartment = route?.params?.department || null; const effectiveSGid = isBusiness ? ((isAdmin && selectedDepartment?.SGid) ? selectedDepartment.SGid : (isAdmin ? null : session.currentSGid)) : null; const {getLobby, getSubgroup, getTeams, getTeamMembers, getGroupMembers, getCaches, getUser, deleteTeam, resetPlayerProgress, resetTeamProgress, getSubgroups} = useGameHook(); // State ---------------------- const [loading, setLoading] = useState(true); const [teamsEnabled, setTeamsEnabled] = useState(false); const [activeTab, setActiveTab] = useState('game'); const [teamRankings, setTeamRankings] = useState([]); const [playerRankings, setPlayerRankings] = useState([]); const [myTeamRanking, setMyTeamRanking] = useState([]); const [expandedTeam, setExpandedTeam] = useState(null); const [totalCacheCount, setTotalCacheCount] = useState(0); const [departmentList, setDepartmentList] = useState([]); // Handlers ------------------- const loadData = useCallback(async () => { if (!session.currentGid) { setLoading(false); return; } const group = await getLobby(session.currentGid); let isTeams = Boolean(group?.TeamsEnabled); if (effectiveSGid !== null && effectiveSGid !== undefined) { const subgroup = await getSubgroup(effectiveSGid); if (subgroup) isTeams = Boolean(subgroup.TeamsEnabled); } setTeamsEnabled(isTeams); if (isBusiness && isAdmin && !selectedDepartment) { const sgs = await getSubgroups(session.currentGid); setDepartmentList((sgs || []).filter((sg) => !sg.IsAdminGroup)); setLoading(false); return; } const caches = await getCaches(session.currentGid, effectiveSGid ?? null); const total = (caches || []).length; setTotalCacheCount(total); if (isTeams) { // Build team rankings const teams = await getTeams(session.currentGid, effectiveSGid ?? null); const teamData = []; for (const team of (teams || [])) { const members = await getTeamMembers(team.Tid); const memberData = []; for (const m of (members || [])) { const user = await getUser(m.Uid); const count = (caches || []).filter((c) => (c.Claims || []).some((cl) => cl.Uid === m.Uid) ).length; memberData.push({ uid: m.Uid, name: user?.username || `Player ${m.Uid}`, caches: count, isLeader: Boolean(m.IsLeader), }); } memberData.sort((a, b) => b.caches - a.caches); const totalCaches = (caches || []).filter((c) => (c.Claims || []).some((cl) => cl.Tid === team.Tid) ).length; teamData.push({ tid: team.Tid, name: team.TeamName || 'Unnamed Team', code: team.JoinCode, totalCaches, members: memberData, }); } teamData.sort((a, b) => b.totalCaches - a.totalCaches); setTeamRankings(teamData); // Build current user's team ranking (for TEAM tab) if (session.currentTid) { const myTeam = teamData.find((t) => t.tid === session.currentTid); setMyTeamRanking(myTeam ? myTeam.members : []); } } else { // Build player rankings (no teams) const scopedMembers = await getGroupMembers(session.currentGid, effectiveSGid ?? null); const playerData = []; for (const m of (scopedMembers || []).filter((mem) => !mem.IsAcceptedAdmin)) { const user = await getUser(m.Uid); const count = (caches || []).filter((c) => (c.Claims || []).some((cl) => cl.Uid === m.Uid) ).length; playerData.push({ uid: m.Uid, name: user?.username || `Player ${m.Uid}`, caches: count, }); } playerData.sort((a, b) => b.caches - a.caches); setPlayerRankings(playerData); } setLoading(false); }, [session.currentGid, session.currentTid, session.currentSGid, isBusiness, isAdmin, selectedDepartment, selectedDepartment?.SGid, effectiveSGid]); useEffect(() => { loadData(); }, [loadData]); // Admin handler: delete a team and reload const handleDeleteTeam = (team) => { Alert.alert('Delete Team', `Delete "${team.name}" and kick all its members?`, [ {text: 'Cancel', style: 'cancel'}, { text: 'Delete', style: 'destructive', onPress: async () => { await deleteTeam(team.tid); await loadData(); }, }, ]); }; // Admin handler: reset a single player's progress const handleResetPlayer = (player) => { Alert.alert('Reset Progress', `Reset all cache claims for ${player.name}?`, [ {text: 'Cancel', style: 'cancel'}, { text: 'Reset', style: 'destructive', onPress: async () => { await resetPlayerProgress(session.currentGid, player.uid, effectiveSGid); Alert.alert('Done', `Progress for ${player.name} has been reset.`); await loadData(); }, }, ]); }; const handleResetTeam = (team) => { Alert.alert('Reset Team Progress', `Reset all cache claims for team "${team.name}"?`, [ {text: 'Cancel', style: 'cancel'}, { text: 'Reset', style: 'destructive', onPress: async () => { await resetTeamProgress(session.currentGid, team.tid, effectiveSGid); Alert.alert('Done', `Progress for team "${team.name}" has been reset.`); await loadData(); }, }, ]); }; // View ----------------------- if (loading) { return ( ); } if (!session.currentGid) { return ( Join a game to view the leaderboard. ); } if (session.isPendingAdmin && !session.isAcceptedAdmin) { return ; } if (isBusiness && isAdmin && !selectedDepartment) { return ( Departments {departmentList.map((department) => ( navigation.navigate('LeaderboardScreen', {department})} > {department.SubGroupName} Open ))} {departmentList.length === 0 && ( No departments yet. )} ); } // Helper: render an expandable team card (admin version with delete + reset) const renderAdminTeamCard = (team, index) => ( setExpandedTeam(expandedTeam === team.tid ? null : team.tid)}> #{index + 1} {team.name} ({team.code}) {team.totalCaches}/{totalCacheCount} {expandedTeam === team.tid && ( {team.members.map((m) => ( // Highlight the current user's row so they can identify themselves {m.name}{m.isLeader ? ' (Team Leader)' : ''} {m.caches}/{totalCacheCount} handleResetPlayer(m)} > Reset ))}