diff --git a/src/app/routes/_authed/badges.tsx b/src/app/routes/_authed/badges.tsx index 8582299..c16290f 100644 --- a/src/app/routes/_authed/badges.tsx +++ b/src/app/routes/_authed/badges.tsx @@ -1,4 +1,5 @@ import BadgeStatsTable from '@/features/badges/components/badge-stats-table'; +import BadgeStatsTableSkeleton from '@/features/badges/components/badge-stats-table-skeleton'; import { badgeQueries, useAllBadges } from '@/features/badges/queries'; import PlayerStatsTableSkeleton from '@/features/players/components/player-stats-table-skeleton'; import { prefetchServerQuery } from '@/lib/tanstack-query/utils/prefetch'; @@ -16,15 +17,15 @@ export const Route = createFileRoute('/_authed/badges')({ fullWidth: true, header: { title: 'All Badges', + withBackButton: true, }, refresh: [badgeQueries.allBadges().queryKey], }), }); function Badges() { - //TODO: CHANGE FALLBACK return ( - }> + }>
diff --git a/src/features/badges/components/badge-stats-table-skeleton.tsx b/src/features/badges/components/badge-stats-table-skeleton.tsx new file mode 100644 index 0000000..f4044fa --- /dev/null +++ b/src/features/badges/components/badge-stats-table-skeleton.tsx @@ -0,0 +1,67 @@ +import { + Stack, + Container, + Box, + Divider, + Grid, + Skeleton, +} from '@mantine/core'; + +const BadgeStatsTableSkeleton = () => { + return ( + + + + {Array.from({ length: 15 }).map((_, index) => ( + = 14} + /> + ))} + + + + ); +}; + +export default BadgeStatsTableSkeleton; + +interface BadgeStatRowSkeletonProps { + isLastRow: boolean; +} + +const BadgeStatRowSkeleton: React.FC = ({ + isLastRow, +}) => { + return ( + + + + + + + + + + + + + + + + + + + + {!isLastRow && } + + ); +}; diff --git a/src/features/badges/components/badge-stats-table.tsx b/src/features/badges/components/badge-stats-table.tsx index 13cf1de..90aeb8b 100644 --- a/src/features/badges/components/badge-stats-table.tsx +++ b/src/features/badges/components/badge-stats-table.tsx @@ -15,6 +15,9 @@ import { Badge, EarnedBadge } from '../types'; import { useAllPlayers } from '@/features/teams/hooks/use-available-players'; import { useSheet } from '@/hooks/use-sheet'; import Sheet from '@/components/sheet/sheet'; +import PlayerList from '@/features/players/components/player-list'; +import { useAuth } from '@/contexts/auth-context'; +import { Player } from '@/features/players/types'; const BadgeStatsTable = () => { const { data: allBadges } = useAllBadges(); @@ -79,16 +82,69 @@ const BadgeStatRow: React.FC = ({ isLastRow, }) => { const badgeSheet = useSheet(); + const { user } = useAuth(); + + const playerNamesBlurb = useMemo(() => { + if (earnedBadges.length === 0) return 'No players yet'; + + const currentUserHasBadge = earnedBadges.some( + (eb) => eb.player.id === user?.id + ); + + const otherPlayers = earnedBadges.filter( + (eb) => eb.player.id !== user?.id + ); + + const displayPlayers = currentUserHasBadge + ? otherPlayers.slice(0, 2) + : earnedBadges.slice(0, 3); + + const names = displayPlayers.map((eb) => eb.player.first_name); + + if (currentUserHasBadge) { + const remaining = earnedBadges.length - 1 - names.length; + if (names.length === 0 && remaining === 0) { + return 'You'; + } else if (names.length === 0 && remaining > 0) { + return `You and ${remaining} other${remaining > 1 ? 's' : ''}`; + } else if (remaining > 0) { + return `You, ${names.join(', ')} and ${remaining} other${remaining > 1 ? 's' : ''}`; + } else { + return `You${names.length > 0 ? ` and ${names.join(', ')}` : ''}`; + } + } else { + const remaining = earnedBadges.length - names.length; + if (remaining > 0) { + return `${names.join(', ')} and ${remaining} other${remaining > 1 ? 's' : ''}`; + } else { + return names.join(', '); + } + } + }, [earnedBadges, user?.id]); + + const playersForList: Player[] = useMemo(() => { + return earnedBadges.map((eb) => ({ + id: eb.player.id, + first_name: eb.player.first_name, + last_name: eb.player.last_name, + } as Player)); + }, [earnedBadges]); + return ( - - + + - {badge.name} - {badge.description} + + {badge.name} + {badge.description} + + {playerNamesBlurb} + + = ({ }} > - + {( ((earnedBadges?.length ?? 0) / totalNumPlayers) * 100 ).toFixed(0)} % - of players + of players - - {earnedBadges?.map((earnedBadge) => ( - - {earnedBadge.player.first_name + - ' ' + - earnedBadge.player.last_name} - - ))} - + {!isLastRow && } diff --git a/src/features/core/hooks/use-links.ts b/src/features/core/hooks/use-links.ts index 7f23aca..4a2b357 100644 --- a/src/features/core/hooks/use-links.ts +++ b/src/features/core/hooks/use-links.ts @@ -1,4 +1,4 @@ -import { HouseIcon, RankingIcon, SealIcon, ShieldIcon, TrophyIcon, UserCircleIcon } from "@phosphor-icons/react"; +import { HouseIcon, RankingIcon, ShieldIcon, TrophyIcon, UserCircleIcon } from "@phosphor-icons/react"; import { useMemo } from "react"; export const useLinks = (userId: string | undefined, roles: string[]) => @@ -25,11 +25,6 @@ export const useLinks = (userId: string | undefined, roles: string[]) => href: `/profile/${userId}`, Icon: UserCircleIcon, include: ['/settings'] - }, - { - label: 'Badges', - href: '/badges', - Icon: SealIcon } ] diff --git a/src/features/players/components/profile/index.tsx b/src/features/players/components/profile/index.tsx index ff524bb..284059b 100644 --- a/src/features/players/components/profile/index.tsx +++ b/src/features/players/components/profile/index.tsx @@ -1,5 +1,6 @@ -import { Box, Stack, Text, Divider, Group, Button } from "@mantine/core"; +import { Box, Stack, Text, Divider, Group, Button, Anchor } from "@mantine/core"; import { Suspense, useState, useDeferredValue } from "react"; +import { Link } from "@tanstack/react-router"; import Header from "./header"; import SwipeableTabs from "@/components/swipeable-tabs"; import { usePlayer, usePlayerMatches, usePlayerStats } from "../../queries"; @@ -51,7 +52,12 @@ const Profile = ({ id }: ProfileProps) => { label: "Overview", content: <> - Badges + + Badges + + View all badges + + }>