190 lines
5.2 KiB
TypeScript
190 lines
5.2 KiB
TypeScript
import { Box, Text, Tooltip, Card } from "@mantine/core";
|
|
import { usePlayerBadges, useAllBadges } from "../queries";
|
|
import { useAuth } from "@/contexts/auth-context";
|
|
import { Badge, BadgeProgress } from "../types";
|
|
import { useMemo } from "react";
|
|
|
|
interface BadgeShowcaseProps {
|
|
playerId: string;
|
|
}
|
|
|
|
interface BadgeDisplay {
|
|
badge: Badge;
|
|
progress?: BadgeProgress;
|
|
earned: boolean;
|
|
progressText: string;
|
|
}
|
|
|
|
const BadgeShowcase = ({ playerId }: BadgeShowcaseProps) => {
|
|
const { user } = useAuth();
|
|
const { data: badgeProgress } = usePlayerBadges(playerId);
|
|
const { data: allBadges } = useAllBadges();
|
|
|
|
const isCurrentUser = user?.id === playerId;
|
|
|
|
const badgesToDisplay = useMemo(() => {
|
|
const displays: BadgeDisplay[] = [];
|
|
|
|
if (isCurrentUser) {
|
|
for (const badge of allBadges) {
|
|
const progress = badgeProgress.find(bp => bp.badge.id === badge.id);
|
|
const earned = progress?.earned || false;
|
|
|
|
if (badge.type === 'manual' && !earned) {
|
|
continue;
|
|
}
|
|
|
|
let progressText = "";
|
|
if (progress) {
|
|
const target = getTargetProgress(badge);
|
|
progressText = `${progress.progress} / ${target}`;
|
|
} else {
|
|
const target = getTargetProgress(badge);
|
|
progressText = `0 / ${target}`;
|
|
}
|
|
|
|
displays.push({
|
|
badge,
|
|
progress,
|
|
earned,
|
|
progressText,
|
|
});
|
|
}
|
|
|
|
displays.sort((a, b) => {
|
|
if (a.earned && !b.earned) return -1;
|
|
if (!a.earned && b.earned) return 1;
|
|
return a.badge.order - b.badge.order;
|
|
});
|
|
} else {
|
|
const earnedProgress = badgeProgress.filter(bp => bp.earned);
|
|
for (const progress of earnedProgress) {
|
|
const badge: Badge = {
|
|
...progress.badge,
|
|
criteria: {},
|
|
created: progress.created,
|
|
updated: progress.updated,
|
|
};
|
|
|
|
const target = getTargetProgress(badge);
|
|
displays.push({
|
|
badge,
|
|
progress,
|
|
earned: true,
|
|
progressText: `${progress.progress} / ${target}`,
|
|
});
|
|
}
|
|
|
|
displays.sort((a, b) => a.badge.order - b.badge.order);
|
|
}
|
|
|
|
return displays;
|
|
}, [allBadges, badgeProgress, isCurrentUser]);
|
|
|
|
if (badgesToDisplay.length === 0) {
|
|
return null;
|
|
}
|
|
|
|
return (
|
|
<Box mb="lg">
|
|
<Card
|
|
withBorder
|
|
radius="md"
|
|
p={0}
|
|
>
|
|
<Box
|
|
p="md"
|
|
style={{
|
|
background: 'light-dark(var(--mantine-color-gray-1), var(--mantine-color-dark-6))',
|
|
borderBottom: '1px solid light-dark(var(--mantine-color-gray-3), var(--mantine-color-dark-4))'
|
|
}}
|
|
>
|
|
<Text size="sm" fw={600} tt="uppercase" c="dimmed" style={{ letterSpacing: '0.5px' }}>
|
|
Badges
|
|
</Text>
|
|
</Box>
|
|
|
|
<Box
|
|
p="md"
|
|
mah={120}
|
|
style={{
|
|
display: 'grid',
|
|
gridTemplateColumns: 'repeat(auto-fill, minmax(110px, 1fr))',
|
|
gap: 'var(--mantine-spacing-sm)',
|
|
overflow: 'scroll',
|
|
}}
|
|
>
|
|
{badgesToDisplay.map((display) => (
|
|
<Tooltip
|
|
key={display.badge.id}
|
|
label={
|
|
<Box>
|
|
<Text size="xs" fw={600} mb={4}>
|
|
{display.badge.name}
|
|
</Text>
|
|
<Text size="xs" mb={4}>
|
|
{display.badge.description}
|
|
</Text>
|
|
{isCurrentUser && (
|
|
<Text size="xs" c="dimmed">
|
|
Progress: {display.progressText}
|
|
</Text>
|
|
)}
|
|
</Box>
|
|
}
|
|
multiline
|
|
w={220}
|
|
>
|
|
<Card
|
|
withBorder
|
|
padding="sm"
|
|
radius="md"
|
|
shadow={display.earned ? "xs" : undefined}
|
|
style={(theme) => ({
|
|
opacity: display.earned ? 1 : 0.35,
|
|
cursor: "pointer",
|
|
transition: 'all 0.2s ease',
|
|
minHeight: 70,
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
borderStyle: display.earned ? 'solid' : 'dashed',
|
|
':hover': {
|
|
transform: display.earned ? 'translateY(-2px)' : 'none',
|
|
boxShadow: display.earned ? theme.shadows.sm : undefined,
|
|
},
|
|
})}
|
|
>
|
|
<Text
|
|
size="xs"
|
|
ta="center"
|
|
fw={display.earned ? 600 : 500}
|
|
c={display.earned ? undefined : "dimmed"}
|
|
style={{ lineHeight: 1.3 }}
|
|
>
|
|
{display.badge.name}
|
|
</Text>
|
|
</Card>
|
|
</Tooltip>
|
|
))}
|
|
</Box>
|
|
</Card>
|
|
</Box>
|
|
);
|
|
};
|
|
|
|
function getTargetProgress(badge: Badge): number {
|
|
const criteria = badge.criteria;
|
|
return (
|
|
criteria.matches_played ||
|
|
criteria.tournament_wins ||
|
|
criteria.tournaments_attended ||
|
|
criteria.overtime_matches ||
|
|
criteria.overtime_wins ||
|
|
criteria.consecutive_wins ||
|
|
1
|
|
);
|
|
}
|
|
|
|
export default BadgeShowcase;
|