name blurbs, skeleton and link
This commit is contained in:
@@ -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 (
|
||||
<Suspense fallback={<PlayerStatsTableSkeleton />}>
|
||||
<Suspense fallback={<BadgeStatsTableSkeleton />}>
|
||||
<div>
|
||||
<BadgeStatsTable />
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
import {
|
||||
Stack,
|
||||
Container,
|
||||
Box,
|
||||
Divider,
|
||||
Grid,
|
||||
Skeleton,
|
||||
} from '@mantine/core';
|
||||
|
||||
const BadgeStatsTableSkeleton = () => {
|
||||
return (
|
||||
<Container size='100%' px={0}>
|
||||
<Stack gap='xs'>
|
||||
<Stack gap={0}>
|
||||
{Array.from({ length: 15 }).map((_, index) => (
|
||||
<BadgeStatRowSkeleton
|
||||
key={`skeleton-${index}`}
|
||||
isLastRow={index >= 14}
|
||||
/>
|
||||
))}
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Container>
|
||||
);
|
||||
};
|
||||
|
||||
export default BadgeStatsTableSkeleton;
|
||||
|
||||
interface BadgeStatRowSkeletonProps {
|
||||
isLastRow: boolean;
|
||||
}
|
||||
|
||||
const BadgeStatRowSkeleton: React.FC<BadgeStatRowSkeletonProps> = ({
|
||||
isLastRow,
|
||||
}) => {
|
||||
return (
|
||||
<Box>
|
||||
<Grid p={'xs'} align='center'>
|
||||
<Grid.Col span={2} style={{ display: 'flex', alignItems: 'center' }}>
|
||||
<Skeleton circle height={48} width={48} />
|
||||
</Grid.Col>
|
||||
<Grid.Col span={'auto'}>
|
||||
<Stack gap={8}>
|
||||
<Skeleton height={16} width='60%' />
|
||||
<Skeleton height={14} width='80%' />
|
||||
<Skeleton height={12} width='50%' mt={4} />
|
||||
</Stack>
|
||||
</Grid.Col>
|
||||
<Grid.Col
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'flex-end',
|
||||
width: '73px',
|
||||
flexBasis: '73px',
|
||||
}}
|
||||
>
|
||||
<Stack gap={4} align='center'>
|
||||
<Skeleton height={20} width={40} />
|
||||
<Skeleton height={12} width={50} />
|
||||
</Stack>
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
{!isLastRow && <Divider />}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
@@ -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<BadgeStatRowProps> = ({
|
||||
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 (
|
||||
<Box key={badge.id}>
|
||||
<UnstyledButton onClick={badgeSheet.open} w='100%'>
|
||||
<Grid p={'xs'}>
|
||||
<Grid.Col span={2}>
|
||||
<Grid p={'xs'} align='center'>
|
||||
<Grid.Col span={2} style={{ display: 'flex', alignItems: 'center' }}>
|
||||
<BadgeIcon badge={badge} filled={true} />
|
||||
</Grid.Col>
|
||||
<Grid.Col span={'auto'}>
|
||||
<Stack gap={2}>
|
||||
<Text fw={700}>{badge.name}</Text>
|
||||
<Text fw={500}>{badge.description}</Text>
|
||||
<Text size="sm" fw={500}>{badge.description}</Text>
|
||||
<Text size='xs' c='dimmed' mt={4}>
|
||||
{playerNamesBlurb}
|
||||
</Text>
|
||||
</Stack>
|
||||
</Grid.Col>
|
||||
<Grid.Col
|
||||
style={{
|
||||
@@ -100,28 +156,20 @@ const BadgeStatRow: React.FC<BadgeStatRowProps> = ({
|
||||
}}
|
||||
>
|
||||
<Stack gap={0} align='center'>
|
||||
<Text size='lg' fw={700}>
|
||||
<Text c="dimmed" size='lg' fw={700}>
|
||||
{(
|
||||
((earnedBadges?.length ?? 0) / totalNumPlayers) *
|
||||
100
|
||||
).toFixed(0)}
|
||||
%
|
||||
</Text>
|
||||
<Text size='xs'>of players</Text>
|
||||
<Text c="dimmed" size='xs'>of players</Text>
|
||||
</Stack>
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
</UnstyledButton>
|
||||
<Sheet title={badge.name + ' Badge Holders'} {...badgeSheet.props}>
|
||||
<Box mx='xl'>
|
||||
{earnedBadges?.map((earnedBadge) => (
|
||||
<Text>
|
||||
{earnedBadge.player.first_name +
|
||||
' ' +
|
||||
earnedBadge.player.last_name}
|
||||
</Text>
|
||||
))}
|
||||
</Box>
|
||||
<PlayerList players={playersForList} />
|
||||
</Sheet>
|
||||
{!isLastRow && <Divider />}
|
||||
</Box>
|
||||
|
||||
@@ -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
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
@@ -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: <>
|
||||
<Stack px="md">
|
||||
<Group justify="space-between" align="center">
|
||||
<Text size="md" fw={700}>Badges</Text>
|
||||
<Anchor component={Link} to="/badges" size="sm" fw={500}>
|
||||
View all badges
|
||||
</Anchor>
|
||||
</Group>
|
||||
<Suspense fallback={<BadgeShowcaseSkeleton />}>
|
||||
<BadgeShowcase playerId={id} />
|
||||
</Suspense>
|
||||
|
||||
Reference in New Issue
Block a user