162 lines
4.4 KiB
TypeScript
162 lines
4.4 KiB
TypeScript
import {
|
|
Box,
|
|
Text,
|
|
Group,
|
|
Stack,
|
|
ThemeIcon,
|
|
Skeleton,
|
|
Divider,
|
|
} from "@mantine/core";
|
|
import {
|
|
CrownIcon,
|
|
XIcon,
|
|
FireIcon,
|
|
ShieldIcon,
|
|
ChartLineUpIcon,
|
|
ShieldCheckIcon,
|
|
BoxingGloveIcon,
|
|
Icon,
|
|
ArrowUpIcon,
|
|
ArrowDownIcon,
|
|
} from "@phosphor-icons/react";
|
|
import { BaseStats } from "@/types/stats";
|
|
|
|
interface StatsOverviewProps {
|
|
statsData: BaseStats | null;
|
|
isLoading?: boolean;
|
|
}
|
|
|
|
const StatItem = ({
|
|
label,
|
|
value,
|
|
suffix = "",
|
|
Icon,
|
|
}: {
|
|
label: string;
|
|
value: number | null;
|
|
suffix?: string;
|
|
Icon?: Icon;
|
|
isLoading?: boolean;
|
|
}) => {
|
|
return (
|
|
<Group justify="space-between" align="center" py="md" px="sm">
|
|
<Group gap="sm" align="center">
|
|
{Icon && (
|
|
<ThemeIcon size="md" variant="light" radius="sm" color="gray">
|
|
<Icon size={16} />
|
|
</ThemeIcon>
|
|
)}
|
|
<Text size="sm" fw={500}>
|
|
{label}
|
|
</Text>
|
|
</Group>
|
|
{value !== null ? (
|
|
<Text size="sm" fw={700} c="dimmed">
|
|
{`${value}${suffix}`}
|
|
</Text>
|
|
) : (
|
|
<Skeleton width={20} height={20} />
|
|
)}
|
|
</Group>
|
|
);
|
|
};
|
|
|
|
const StatsOverview = ({ statsData, isLoading = false }: StatsOverviewProps) => {
|
|
if (!statsData && !isLoading) {
|
|
return (
|
|
<Box p="sm" h="auto" mih={200}>
|
|
<Text ta="center" size="sm" fw={600} c="dimmed">
|
|
No stats available yet
|
|
</Text>
|
|
</Box>
|
|
);
|
|
}
|
|
|
|
if (!statsData) return null;
|
|
|
|
const overallStats = {
|
|
matches: statsData.matches,
|
|
wins: statsData.wins,
|
|
losses: statsData.losses,
|
|
total_cups_made: statsData.total_cups_made,
|
|
total_cups_against: statsData.total_cups_against,
|
|
};
|
|
|
|
const avgCupsPerMatch =
|
|
overallStats.matches > 0
|
|
? parseFloat((overallStats.total_cups_made / overallStats.matches).toFixed(1))
|
|
: 0;
|
|
|
|
const avgCupsAgainstPerMatch =
|
|
overallStats.matches > 0
|
|
? parseFloat((overallStats.total_cups_against / overallStats.matches).toFixed(1))
|
|
: 0;
|
|
|
|
const avgMarginOfVictory = statsData.margin_of_victory ? parseFloat(statsData.margin_of_victory.toFixed(1)) : 0;
|
|
const avgMarginOfLoss = statsData.margin_of_loss ? parseFloat(statsData.margin_of_loss.toFixed(1)) : 0;
|
|
|
|
const allStats = [
|
|
{ label: "Matches Played", value: overallStats.matches, Icon: BoxingGloveIcon },
|
|
{ label: "Wins", value: overallStats.wins, Icon: CrownIcon },
|
|
{ label: "Losses", value: overallStats.losses, Icon: XIcon },
|
|
{ label: "Cups Made", value: overallStats.total_cups_made, Icon: FireIcon },
|
|
{ label: "Cups Against", value: overallStats.total_cups_against, Icon: ShieldIcon },
|
|
{ label: "Avg Cups Per Game", value: avgCupsPerMatch > 0 ? avgCupsPerMatch : null, Icon: ChartLineUpIcon },
|
|
{ label: "Avg Cups Against", value: avgCupsAgainstPerMatch > 0 ? avgCupsAgainstPerMatch : null, Icon: ShieldCheckIcon },
|
|
{ label: "Avg Win Margin", value: avgMarginOfVictory > 0 ? avgMarginOfVictory : null, Icon: ArrowUpIcon },
|
|
{ label: "Avg Loss Margin", value: avgMarginOfLoss > 0 ? avgMarginOfLoss : null, Icon: ArrowDownIcon },
|
|
];
|
|
|
|
return (
|
|
<Box>
|
|
<Stack gap={0}>
|
|
{allStats.map((stat, index) => (
|
|
<Box key={stat.label}>
|
|
<StatItem
|
|
label={stat.label}
|
|
value={stat.value}
|
|
Icon={stat.Icon}
|
|
isLoading={isLoading}
|
|
/>
|
|
{index < allStats.length - 1 && <Divider />}
|
|
</Box>
|
|
))}
|
|
</Stack>
|
|
</Box>
|
|
);
|
|
};
|
|
|
|
export const StatsSkeleton = () => {
|
|
const skeletonStats = [
|
|
{ label: "Matches Played", Icon: BoxingGloveIcon },
|
|
{ label: "Wins", Icon: CrownIcon },
|
|
{ label: "Losses", Icon: XIcon },
|
|
{ label: "Cups Made", Icon: FireIcon },
|
|
{ label: "Cups Against", Icon: ShieldIcon },
|
|
{ label: "Avg Cups Per Game", Icon: ChartLineUpIcon },
|
|
{ label: "Avg Cups Against", Icon: ShieldCheckIcon },
|
|
{ label: "Avg Win Margin", Icon: ArrowUpIcon },
|
|
{ label: "Avg Loss Margin", Icon: ArrowDownIcon },
|
|
];
|
|
|
|
return (
|
|
<Box>
|
|
<Stack gap={0}>
|
|
{skeletonStats.map((stat, index) => (
|
|
<Box key={stat.label}>
|
|
<StatItem
|
|
label={stat.label}
|
|
value={null}
|
|
Icon={stat.Icon}
|
|
isLoading={true}
|
|
/>
|
|
{index < skeletonStats.length - 1 && <Divider />}
|
|
</Box>
|
|
))}
|
|
</Stack>
|
|
</Box>
|
|
);
|
|
};
|
|
|
|
export default StatsOverview;
|