Last commit not found
raw
history blame
11.6 kB
import React, { useState, useEffect, useMemo } from "react";
import {
Box,
CircularProgress,
Typography,
Chip,
Tooltip,
} from "@mui/material";
import SearchOffIcon from "@mui/icons-material/SearchOff";
import WarningAmberIcon from "@mui/icons-material/WarningAmber";
import Logo from "../../components/Logo/Logo";
import PageTitle from "../../components/PageTitle/PageTitle";
import LeaderboardSection from "../../components/LeaderboardSection";
import LeaderboardFilters from "../../components/LeaderboardFilters/LeaderboardFilters";
import API_URLS from "../../config/api";
import {
LeaderboardProvider,
useLeaderboard,
} from "../../context/LeaderboardContext";
import EmptyState from "../../components/LeaderboardSection/components/EmptyState";
const LeaderboardPageContent = () => {
const [loading, setLoading] = useState(true);
const [showUncategorized, setShowUncategorized] = useState(false);
const {
setLeaderboards,
filterLeaderboards,
sections,
allSections,
sectionsSortedByCount,
searchQuery,
arenaOnly,
selectedCategories,
leaderboards,
} = useLeaderboard();
// Vérifier si on a uniquement une recherche textuelle active ou arena only
const isOnlyTextSearch =
(searchQuery || arenaOnly) && selectedCategories.size === 0;
// Obtenir tous les leaderboards uniques de toutes les sections
const allUniqueLeaderboards = useMemo(() => {
if (!allSections) return [];
return Array.from(
new Set(
allSections.reduce((acc, section) => {
return [...acc, ...(section.data || [])];
}, [])
)
);
}, [allSections]);
// Calculer le nombre de leaderboards non catégorisés
const uncategorizedLeaderboards = useMemo(() => {
if (!leaderboards || leaderboards.length === 0) return [];
return leaderboards.filter(
(board) =>
board.approval_status === "approved" &&
!board.tags?.some(
(tag) =>
tag.startsWith("modality:") ||
tag.startsWith("eval:") ||
tag.startsWith("domain:") ||
tag.startsWith("language:")
)
);
}, [leaderboards]);
// Filtrer tous les leaderboards pour la recherche textuelle ou arena only
const searchResults = useMemo(() => {
if (!isOnlyTextSearch) return [];
return filterLeaderboards(allUniqueLeaderboards);
}, [isOnlyTextSearch, filterLeaderboards, allUniqueLeaderboards]);
useEffect(() => {
fetch(API_URLS.leaderboards)
.then((res) => {
if (!res.ok) {
throw new Error(`Failed to fetch leaderboards: ${res.status}`);
}
return res.json();
})
.then((data) => {
// Les données sont directement dans le format attendu depuis le dataset HF
setLeaderboards(data);
setLoading(false);
})
.catch((error) => {
console.error("Error fetching leaderboards:", error);
setLoading(false);
});
}, [setLeaderboards]);
// Check if any leaderboards are found after filtering
const totalFilteredLeaderboards = sections.reduce(
(total, { data }) => total + filterLeaderboards(data).length,
0
);
const hasLeaderboards = totalFilteredLeaderboards > 0;
const isFiltering = searchQuery || arenaOnly;
return (
<Box
sx={{
width: "100%",
ph: 2,
display: "flex",
flexDirection: "column",
}}
>
<Box
sx={{ display: "flex", justifyContent: "center", pt: 6, mb: -4, pb: 0 }}
>
<Logo />
</Box>
<Box
sx={{
display: "flex",
flexDirection: "column",
alignItems: "center",
textAlign: "center",
mb: 0,
mt: 6,
gap: 2,
}}
>
<PageTitle />
<Typography variant="h6" color="text.secondary">
<span style={{ fontWeight: 600 }}>Discover</span> and{" "}
<span style={{ fontWeight: 600 }}>explore</span> all leaderboards from
the <span style={{ fontWeight: 600 }}>Hugging Face community</span>
</Typography>
{uncategorizedLeaderboards.length > 0 && (
<Tooltip
title={`${uncategorizedLeaderboards.length} leaderboards approuvés n'ont pas de catégorie et ne sont pas affichés dans les sections`}
>
<Chip
icon={<WarningAmberIcon />}
label={`${uncategorizedLeaderboards.length} leaderboards non catégorisés`}
color="warning"
variant="outlined"
sx={{ mt: 1 }}
onClick={() => {
console.log("=== LEADERBOARDS NON CATÉGORISÉS ===");
console.table(
uncategorizedLeaderboards.map((board) => ({
id: board.id,
uid: board.uid,
title: board.card_data?.title || "Sans titre",
tags: (board.tags || []).join(", "),
url: board.card_data?.url || "",
}))
);
setShowUncategorized(!showUncategorized);
}}
/>
</Tooltip>
)}
</Box>
{loading ? (
<Box sx={{ display: "flex", justifyContent: "center", mt: 4 }}>
<CircularProgress />
</Box>
) : (
<Box
sx={{
width: "100%",
maxWidth: "1200px",
mx: "auto",
mt: 4,
}}
>
<LeaderboardFilters allSections={allSections} />
{/* Section pour les leaderboards non catégorisés */}
{showUncategorized && uncategorizedLeaderboards.length > 0 && (
<Box
sx={{
mb: 4,
p: 2,
border: "1px dashed",
borderColor: "warning.main",
borderRadius: 2,
bgcolor: (theme) => theme.palette.warning.light + "20",
}}
>
<LeaderboardSection
id="uncategorized"
title={`Leaderboards non catégorisés (${uncategorizedLeaderboards.length})`}
leaderboards={uncategorizedLeaderboards}
filteredLeaderboards={uncategorizedLeaderboards}
showEmptyState={false}
/>
</Box>
)}
{isOnlyTextSearch ? (
// Vue spéciale pour la recherche textuelle
searchResults.length > 0 ? (
<Box key="search-results">
<LeaderboardSection
id="search-results"
title={
searchQuery
? `All leaderboards matching "${searchQuery}"${
arenaOnly ? " (Arena only)" : ""
}`
: "All Arena leaderboards"
}
leaderboards={allUniqueLeaderboards}
filteredLeaderboards={searchResults}
/>
</Box>
) : (
// Message d'erreur pour la recherche textuelle sans résultats
<Box key="search-results">
<LeaderboardSection
id="search-results"
title={
searchQuery
? `All leaderboards matching "${searchQuery}"${
arenaOnly ? " (Arena only)" : ""
}`
: "All Arena leaderboards"
}
leaderboards={allUniqueLeaderboards}
filteredLeaderboards={[]}
showEmptyState={true}
/>
</Box>
)
) : selectedCategories.size > 0 ? (
// Si des catégories sont sélectionnées
selectedCategories.size === 1 ? (
// Si une seule catégorie est sélectionnée, on affiche sa section
sections
.filter(({ id }) => selectedCategories.has(id))
.map(({ id, title, data }) => {
const filteredLeaderboards = filterLeaderboards(data);
// Ajouter le terme de recherche au titre si présent
const sectionTitle = searchQuery
? `${title} matching "${searchQuery}"`
: title;
// Toujours afficher la section, même si elle est vide
return (
<Box key={id} id={id}>
<LeaderboardSection
id={id}
title={sectionTitle}
leaderboards={data}
filteredLeaderboards={filteredLeaderboards}
showEmptyState={true} // Toujours afficher l'état vide sous le header
/>
</Box>
);
})
) : (
// Si plusieurs catégories sont sélectionnées, on les agrège
(() => {
// Agréger les données de toutes les sections sélectionnées
const selectedSections = sections.filter(({ id }) =>
selectedCategories.has(id)
);
// Créer un titre combiné avec le terme de recherche si présent
const combinedTitle = selectedSections
.map(({ title }) => title)
.join(" + ");
const finalTitle = searchQuery
? `${combinedTitle} matching "${searchQuery}"`
: combinedTitle;
// Agréger les leaderboards
const combinedData = selectedSections.reduce(
(acc, { data }) => [...acc, ...data],
[]
);
// Filtrer les doublons par ID
const uniqueData = Array.from(
new Map(combinedData.map((item) => [item.id, item])).values()
);
const filteredLeaderboards = filterLeaderboards(uniqueData);
return (
<Box key="combined">
<LeaderboardSection
id="combined"
title={finalTitle}
leaderboards={uniqueData}
filteredLeaderboards={filteredLeaderboards}
showEmptyState={true} // Toujours afficher l'état vide sous le header
/>
</Box>
);
})()
)
) : (
// Si aucune catégorie n'est sélectionnée, on affiche toutes les sections avec des résultats
// triées par nombre de leaderboards
(hasLeaderboards || !isFiltering) &&
sectionsSortedByCount.map(({ id, title, data }) => {
const filteredLeaderboards = filterLeaderboards(data);
if (filteredLeaderboards.length === 0) return null;
return (
<Box key={id} id={id}>
<LeaderboardSection
id={id}
title={title}
leaderboards={data}
filteredLeaderboards={filteredLeaderboards}
/>
</Box>
);
})
)}
</Box>
)}
</Box>
);
};
const LeaderboardPage = () => {
return (
<LeaderboardProvider>
<LeaderboardPageContent />
</LeaderboardProvider>
);
};
export default LeaderboardPage;