Spaces:
Running
Running
| import React, { useMemo } from "react"; | |
| import { Box } from "@mui/material"; | |
| import { useLeaderboard } from "../../context/LeaderboardContext"; | |
| import SectionHeader from "./components/SectionHeader"; | |
| import LanguageList from "./components/LanguageList"; | |
| import LeaderboardGrid from "./components/LeaderboardGrid"; | |
| import EmptyState from "./components/EmptyState"; | |
| import { useLanguageStats } from "./hooks/useLanguageStats"; | |
| const ITEMS_PER_PAGE = 4; | |
| const LeaderboardSection = ({ | |
| title, | |
| leaderboards, | |
| filteredLeaderboards, | |
| id, | |
| showEmptyState = false, | |
| }) => { | |
| const { | |
| expandedSections, | |
| setExpandedSections, | |
| selectedLanguage, | |
| setSelectedLanguage, | |
| searchQuery, | |
| selectedCategories, | |
| } = useLeaderboard(); | |
| const isExpanded = expandedSections.has(id); | |
| const { languages, languageStats, LANGUAGE_FAMILIES, findLanguageFamily } = | |
| useLanguageStats(leaderboards, filteredLeaderboards); | |
| // Créer un titre enrichi qui inclut les filtres de langue | |
| const enrichedTitle = useMemo(() => { | |
| let newTitle = title; | |
| // Ajouter les langues sélectionnées au titre si elles ne sont pas déjà incluses | |
| if (selectedLanguage && selectedLanguage.size > 0) { | |
| const languageNames = Array.from(selectedLanguage) | |
| .map((lang) => lang.charAt(0).toUpperCase() + lang.slice(1)) | |
| .join(", "); | |
| // Vérifier si le titre contient déjà "language" | |
| if (!newTitle.toLowerCase().includes("language")) { | |
| newTitle = `${newTitle} + Language: ${languageNames}`; | |
| } else if ( | |
| !newTitle.toLowerCase().includes(languageNames.toLowerCase()) | |
| ) { | |
| // Si le titre contient "language" mais pas les noms spécifiques | |
| newTitle = newTitle.replace( | |
| /language(\s+specific)?/i, | |
| `Language: ${languageNames}` | |
| ); | |
| } | |
| } | |
| // Ajouter le terme de recherche s'il existe | |
| if ( | |
| searchQuery && | |
| !newTitle | |
| .toLowerCase() | |
| .includes(`matching "${searchQuery.toLowerCase()}"`) | |
| ) { | |
| newTitle = `${newTitle} matching "${searchQuery}"`; | |
| } | |
| return newTitle; | |
| }, [title, selectedLanguage, searchQuery]); | |
| // Filtrer pour n'avoir que les leaderboards approuvés | |
| const approvedLeaderboards = useMemo(() => { | |
| // Filtrer d'abord pour n'avoir que les leaderboards approuvés | |
| const approved = filteredLeaderboards.filter( | |
| (leaderboard) => leaderboard.approval_status === "approved" | |
| ); | |
| // Trier par trending_score (ordre décroissant) | |
| return [...approved].sort((a, b) => { | |
| const scoreA = a.trending_score || 0; | |
| const scoreB = b.trending_score || 0; | |
| return scoreB - scoreA; // Tri décroissant | |
| }); | |
| }, [filteredLeaderboards]); | |
| // On ne retourne null que si on n'a pas de leaderboards bruts | |
| if (!leaderboards) return null; | |
| // Déterminer si on doit paginer ou montrer tous les leaderboards | |
| const shouldShowAll = | |
| (selectedCategories.size === 1 && selectedCategories.has(id)) || // Une seule catégorie sélectionnée ET c'est celle-ci | |
| (selectedCategories.size > 1 && id === "combined") || // Plusieurs catégories ET c'est la section combinée | |
| isExpanded || // Section dépliée (quelle que soit la sélection) | |
| id === "search-results"; // Toujours afficher tous les résultats pour la recherche textuelle | |
| // Si on doit tout montrer, on ne divise pas la liste | |
| const displayedLeaderboards = shouldShowAll | |
| ? approvedLeaderboards | |
| : approvedLeaderboards.slice(0, ITEMS_PER_PAGE); | |
| const remainingLeaderboards = shouldShowAll | |
| ? [] | |
| : approvedLeaderboards.slice(ITEMS_PER_PAGE); | |
| // Calculate how many skeletons we need (seulement si on ne montre pas tout) | |
| const skeletonsNeeded = shouldShowAll | |
| ? 0 | |
| : Math.max(0, 4 - approvedLeaderboards.length); | |
| // On affiche le bouton expand seulement quand on n'a pas de sélection et que ce n'est pas une recherche textuelle | |
| const showExpandButton = | |
| selectedCategories.size === 0 && id !== "search-results"; | |
| // Le bouton est actif seulement s'il y a plus de 4 leaderboards | |
| const isExpandButtonEnabled = approvedLeaderboards.length > ITEMS_PER_PAGE; | |
| const toggleExpanded = () => { | |
| setExpandedSections((prev) => { | |
| const newSet = new Set(prev); | |
| if (isExpanded) { | |
| newSet.delete(id); | |
| } else { | |
| newSet.add(id); | |
| } | |
| return newSet; | |
| }); | |
| }; | |
| // Déterminer si on doit afficher la liste des langues | |
| const showLanguageList = | |
| languages && | |
| selectedCategories.size > 0 && | |
| (id === "language" || | |
| (selectedCategories.size > 1 && selectedCategories.has("language"))); | |
| return ( | |
| <Box sx={{ mb: 6 }}> | |
| <SectionHeader | |
| title={enrichedTitle} | |
| count={approvedLeaderboards.length} | |
| isExpanded={isExpanded} | |
| onToggleExpand={toggleExpanded} | |
| showExpandButton={showExpandButton} | |
| isExpandButtonEnabled={isExpandButtonEnabled} | |
| /> | |
| {showLanguageList && ( | |
| <LanguageList | |
| languages={languages} | |
| languageStats={languageStats} | |
| selectedLanguage={selectedLanguage} | |
| onLanguageSelect={setSelectedLanguage} | |
| LANGUAGE_FAMILIES={LANGUAGE_FAMILIES} | |
| findLanguageFamily={findLanguageFamily} | |
| /> | |
| )} | |
| {approvedLeaderboards.length === 0 ? ( | |
| // Toujours afficher EmptyState, que showEmptyState soit true ou non | |
| <EmptyState title={enrichedTitle} searchQuery={searchQuery} /> | |
| ) : ( | |
| <LeaderboardGrid | |
| displayedLeaderboards={displayedLeaderboards} | |
| remainingLeaderboards={remainingLeaderboards} | |
| isExpanded={isExpanded} | |
| skeletonsNeeded={skeletonsNeeded} | |
| id={id} | |
| /> | |
| )} | |
| </Box> | |
| ); | |
| }; | |
| export default LeaderboardSection; | |