File size: 9,654 Bytes
f145689
8d4d46d
 
 
 
 
 
 
1254641
8d4d46d
5470817
4a90775
5470817
953a39e
b4d058e
953a39e
 
 
 
2405b72
5470817
953a39e
5470817
8d4d46d
1254641
 
 
 
 
48cea17
1254641
 
5621ad9
8d4d46d
1254641
5470817
de3d81e
f145689
de3d81e
f145689
 
 
 
 
 
 
 
 
 
 
 
 
8d4d46d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
de3d81e
f145689
 
 
 
 
5470817
b4d058e
6d3b050
 
 
 
 
 
5470817
6d3b050
 
5470817
 
 
 
 
 
953a39e
5470817
1254641
 
 
 
 
 
 
 
 
5470817
 
 
 
 
 
 
 
 
 
 
 
 
 
953a39e
 
 
 
 
 
 
 
 
 
 
 
4a90775
953a39e
 
 
 
 
 
5470817
 
 
 
 
 
 
 
 
 
 
 
 
 
953a39e
5470817
f145689
 
6ba1570
 
 
 
de3d81e
 
 
 
 
 
 
6ba1570
 
 
 
 
 
2405b72
 
 
de3d81e
 
 
 
 
 
 
2405b72
 
 
6ba1570
 
 
f145689
 
 
 
 
 
 
 
6ba1570
f145689
 
 
 
6ba1570
 
81702ec
f145689
81702ec
f145689
 
 
81702ec
6ba1570
81702ec
 
 
f145689
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
81702ec
f145689
81702ec
f145689
 
 
81702ec
6ba1570
81702ec
 
 
f145689
 
 
 
48cea17
f145689
48cea17
f145689
 
 
 
 
 
 
 
 
 
 
 
 
 
5470817
 
 
 
 
 
953a39e
 
 
 
 
 
 
 
5470817
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
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]);

  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>
      </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} />

          {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;