Spaces:
Running
Running
| import React from "react"; | |
| import { Card, CardContent, Typography, Box, Stack } from "@mui/material"; | |
| import FavoriteBorderIcon from "@mui/icons-material/FavoriteBorder"; | |
| import { alpha } from "@mui/material/styles"; | |
| import { useLeaderboard } from "../context/LeaderboardContext"; | |
| const LeaderboardCard = ({ leaderboard }) => { | |
| const { | |
| card_data = {}, | |
| likes = 0, | |
| enriched, | |
| consolidated_notes, | |
| organization, | |
| } = leaderboard; | |
| const { getHighlightedText, searchQuery } = useLeaderboard(); | |
| const { | |
| text: highlightedText, | |
| shouldHighlight, | |
| highlightStart, | |
| highlightEnd, | |
| } = getHighlightedText(card_data.title || leaderboard.id, searchQuery); | |
| // Fonction pour capitaliser la première lettre | |
| const capitalizeFirst = (str) => { | |
| if (!str) return ""; | |
| return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase(); | |
| }; | |
| // Fonction pour formater les likes | |
| const formatLikes = (count) => { | |
| if (count >= 1000) { | |
| return (count / 1000).toFixed(2).replace(/\.?0+$/, "") + "k"; | |
| } | |
| return count; | |
| }; | |
| // Séparer les emojis s'il y en a plusieurs | |
| const emojis = card_data.emoji?.trim().split(/\s+/) || ["🎯"]; | |
| return ( | |
| <Box | |
| component="a" | |
| href={`https://huggingface.co/spaces/${leaderboard.id}`} | |
| target="_blank" | |
| rel="noopener noreferrer" | |
| sx={{ | |
| textDecoration: "none", | |
| color: "inherit", | |
| display: "block", | |
| "&:hover": { | |
| opacity: 0.95, | |
| }, | |
| }} | |
| > | |
| <Stack spacing={1}> | |
| <Card | |
| elevation={0} | |
| sx={{ | |
| position: "relative", | |
| minHeight: 180, | |
| background: `linear-gradient(135deg, ${ | |
| card_data.colorFrom || "#6366f1" | |
| } 0%, ${card_data.colorTo || "#a855f7"} 100%)`, | |
| color: "white", | |
| overflow: "visible", | |
| "&::after": { | |
| content: '""', | |
| position: "absolute", | |
| top: 0, | |
| left: 0, | |
| right: 0, | |
| bottom: 0, | |
| backgroundColor: "rgba(0, 0, 0, 0.15)", | |
| pointerEvents: "none", | |
| zIndex: 1, | |
| }, | |
| }} | |
| > | |
| {/* Background Emoji */} | |
| <Box | |
| sx={{ | |
| position: "absolute", | |
| top: "50%", | |
| left: "50%", | |
| transform: "translate(-50%, -50%)", | |
| fontSize: "3.75rem", | |
| opacity: 0.6, | |
| pointerEvents: "none", | |
| display: "flex", | |
| zIndex: 2, | |
| }} | |
| > | |
| {emojis.map((emoji, index) => ( | |
| <span key={index}>{emoji}</span> | |
| ))} | |
| </Box> | |
| {/* Header with likes and status */} | |
| <Box | |
| sx={{ | |
| position: "absolute", | |
| top: 0, | |
| left: 0, | |
| right: 0, | |
| p: 1.5, | |
| display: "flex", | |
| justifyContent: "space-between", | |
| alignItems: "center", | |
| zIndex: 2, | |
| }} | |
| > | |
| <Typography | |
| variant="body2" | |
| sx={{ | |
| backgroundColor: "rgba(0, 0, 0, 0.1)", | |
| px: 1, | |
| py: 0.25, | |
| borderRadius: 1, | |
| fontSize: "0.75rem", | |
| }} | |
| > | |
| {capitalizeFirst(leaderboard.runtime_stage)} | |
| </Typography> | |
| <Box | |
| sx={{ | |
| display: "flex", | |
| alignItems: "center", | |
| gap: 0.25, | |
| fontSize: "0.75rem", | |
| }} | |
| > | |
| <FavoriteBorderIcon sx={{ fontSize: 16 }} /> | |
| <Typography variant="body2" sx={{ fontSize: "inherit" }}> | |
| {formatLikes(likes)} | |
| </Typography> | |
| </Box> | |
| </Box> | |
| {/* Footer with author */} | |
| <Box | |
| sx={{ | |
| position: "absolute", | |
| bottom: 0, | |
| left: 0, | |
| right: 0, | |
| p: 1.5, | |
| zIndex: 2, | |
| }} | |
| > | |
| <Typography | |
| variant="body2" | |
| sx={{ | |
| fontSize: "0.75rem", | |
| opacity: 0.9, | |
| }} | |
| > | |
| by {organization} | |
| </Typography> | |
| </Box> | |
| <CardContent | |
| sx={{ | |
| height: "100%", | |
| display: "flex", | |
| alignItems: "center", | |
| justifyContent: "center", | |
| position: "absolute", | |
| top: "50%", | |
| left: "50%", | |
| transform: "translate(-50%, -50%)", | |
| width: "100%", | |
| zIndex: 2, | |
| p: 0, | |
| "&:last-child": { | |
| paddingBottom: 0, | |
| }, | |
| }} | |
| > | |
| <Typography | |
| variant="subtitle1" | |
| sx={{ | |
| fontWeight: 900, | |
| whiteSpace: "nowrap", | |
| overflow: "hidden", | |
| textOverflow: "ellipsis", | |
| }} | |
| > | |
| {shouldHighlight ? ( | |
| <> | |
| {highlightedText.slice(0, highlightStart)} | |
| <Box | |
| component="span" | |
| sx={{ | |
| bgcolor: "primary.main", | |
| color: "primary.contrastText", | |
| px: 0.5, | |
| borderRadius: 0.5, | |
| }} | |
| > | |
| {highlightedText.slice(highlightStart, highlightEnd)} | |
| </Box> | |
| {highlightedText.slice(highlightEnd)} | |
| </> | |
| ) : ( | |
| card_data.title || leaderboard.id | |
| )} | |
| </Typography> | |
| </CardContent> | |
| </Card> | |
| {(card_data.short_description || consolidated_notes) && ( | |
| <Typography | |
| variant="body2" | |
| sx={{ | |
| color: "text.secondary", | |
| fontSize: "0.875rem", | |
| }} | |
| > | |
| {card_data.short_description || consolidated_notes} | |
| </Typography> | |
| )} | |
| </Stack> | |
| </Box> | |
| ); | |
| }; | |
| export default LeaderboardCard; | |