improvements
This commit is contained in:
@@ -172,6 +172,8 @@ const SlidePanel = ({
|
||||
onChange={setTempValue}
|
||||
{...(panelConfig.componentProps || {})}
|
||||
/>
|
||||
<Button mt="md" onClick={handleConfirm}>Confirm</Button>
|
||||
<Button variant="subtle" onClick={closePanel} mt="sm" color="red">Cancel</Button>
|
||||
</>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
@@ -8,9 +8,10 @@ import {
|
||||
Container,
|
||||
Divider,
|
||||
UnstyledButton,
|
||||
Badge,
|
||||
Select,
|
||||
Pagination,
|
||||
Code,
|
||||
Alert,
|
||||
} from "@mantine/core";
|
||||
import {
|
||||
MagnifyingGlassIcon,
|
||||
@@ -18,15 +19,19 @@ import {
|
||||
CaretDownIcon,
|
||||
CheckIcon,
|
||||
XIcon,
|
||||
ChecksIcon,
|
||||
} from "@phosphor-icons/react";
|
||||
import { Activity, ActivitySearchParams } from "../types";
|
||||
import { useActivities } from "../queries";
|
||||
import Sheet from "@/components/sheet/sheet";
|
||||
import { useSheet } from "@/hooks/use-sheet";
|
||||
|
||||
interface ActivityListItemProps {
|
||||
activity: Activity;
|
||||
onClick: () => void;
|
||||
}
|
||||
|
||||
const ActivityListItem = memo(({ activity }: ActivityListItemProps) => {
|
||||
const ActivityListItem = memo(({ activity, onClick }: ActivityListItemProps) => {
|
||||
const playerName = typeof activity.player === "object" && activity.player
|
||||
? `${activity.player.first_name} ${activity.player.last_name}`
|
||||
: "System";
|
||||
@@ -37,7 +42,22 @@ const ActivityListItem = memo(({ activity }: ActivityListItemProps) => {
|
||||
};
|
||||
|
||||
return (
|
||||
<Box p="md">
|
||||
<UnstyledButton
|
||||
w="100%"
|
||||
p="md"
|
||||
onClick={onClick}
|
||||
style={{
|
||||
borderRadius: 0,
|
||||
transition: "background-color 0.15s ease",
|
||||
}}
|
||||
styles={{
|
||||
root: {
|
||||
'&:hover': {
|
||||
backgroundColor: 'var(--mantine-color-gray-0)',
|
||||
},
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Group justify="space-between" align="flex-start" w="100%">
|
||||
<Stack gap={4} flex={1}>
|
||||
<Group gap="xs">
|
||||
@@ -68,59 +88,183 @@ const ActivityListItem = memo(({ activity }: ActivityListItemProps) => {
|
||||
)}
|
||||
</Stack>
|
||||
</Group>
|
||||
</Box>
|
||||
</UnstyledButton>
|
||||
);
|
||||
});
|
||||
|
||||
const ActivitiesResults = ({ searchParams, page, setPage }: any) => {
|
||||
ActivityListItem.displayName = "ActivityListItem";
|
||||
|
||||
interface ActivityDetailsSheetProps {
|
||||
activity: Activity | null;
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
const ActivityDetailsSheet = memo(({ activity, isOpen, onClose }: ActivityDetailsSheetProps) => {
|
||||
if (!activity) return null;
|
||||
|
||||
const playerName = typeof activity.player === "object" && activity.player
|
||||
? `${activity.player.first_name} ${activity.player.last_name}`
|
||||
: "System";
|
||||
|
||||
const formatDate = (dateStr: string) => {
|
||||
const date = new Date(dateStr);
|
||||
return date.toLocaleString();
|
||||
};
|
||||
|
||||
return (
|
||||
<Sheet title="Activity Details" opened={isOpen} onChange={onClose}>
|
||||
<Stack gap="md" p="md">
|
||||
<Stack gap="xs">
|
||||
<Text size="xs" fw={700} c="dimmed">
|
||||
Function Name
|
||||
</Text>
|
||||
<Text size="sm">{activity.name}</Text>
|
||||
</Stack>
|
||||
|
||||
<Stack gap="xs">
|
||||
<Text size="xs" fw={700} c="dimmed">
|
||||
Status
|
||||
</Text>
|
||||
<Group gap="xs">
|
||||
{activity.success ? (
|
||||
<>
|
||||
<CheckIcon size={16} color="var(--mantine-color-green-6)" />
|
||||
<Text size="sm" c="green">
|
||||
Success
|
||||
</Text>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<XIcon size={16} color="var(--mantine-color-red-6)" />
|
||||
<Text size="sm" c="red">
|
||||
Failed
|
||||
</Text>
|
||||
</>
|
||||
)}
|
||||
</Group>
|
||||
</Stack>
|
||||
|
||||
<Stack gap="xs">
|
||||
<Text size="xs" fw={700} c="dimmed">
|
||||
Player
|
||||
</Text>
|
||||
<Text size="sm">{playerName}</Text>
|
||||
</Stack>
|
||||
|
||||
<Stack gap="xs">
|
||||
<Text size="xs" fw={700} c="dimmed">
|
||||
Duration
|
||||
</Text>
|
||||
<Text size="sm">{activity.duration}ms</Text>
|
||||
</Stack>
|
||||
|
||||
<Stack gap="xs">
|
||||
<Text size="xs" fw={700} c="dimmed">
|
||||
Created
|
||||
</Text>
|
||||
<Text size="sm">{formatDate(activity.created)}</Text>
|
||||
</Stack>
|
||||
|
||||
{activity.user_agent && (
|
||||
<Stack gap="xs">
|
||||
<Text size="xs" fw={700} c="dimmed">
|
||||
User Agent
|
||||
</Text>
|
||||
<Text size="xs" style={{ wordBreak: "break-word" }}>
|
||||
{activity.user_agent}
|
||||
</Text>
|
||||
</Stack>
|
||||
)}
|
||||
|
||||
{activity.error && (
|
||||
<Stack gap="xs">
|
||||
<Text size="xs" fw={700} c="dimmed">
|
||||
Error Message
|
||||
</Text>
|
||||
<Alert color="red" variant="light">
|
||||
<Text size="sm" style={{ wordBreak: "break-word" }}>
|
||||
{activity.error}
|
||||
</Text>
|
||||
</Alert>
|
||||
</Stack>
|
||||
)}
|
||||
|
||||
{activity.arguments && (
|
||||
<Stack gap="xs">
|
||||
<Text size="xs" fw={700} c="dimmed">
|
||||
Arguments
|
||||
</Text>
|
||||
<Code block style={{ fontSize: "11px" }}>
|
||||
{JSON.stringify(activity.arguments, null, 2)}
|
||||
</Code>
|
||||
</Stack>
|
||||
)}
|
||||
</Stack>
|
||||
</Sheet>
|
||||
);
|
||||
});
|
||||
|
||||
ActivityDetailsSheet.displayName = "ActivityDetailsSheet";
|
||||
|
||||
const ActivitiesResults = ({ searchParams, page, setPage, onActivityClick }: any) => {
|
||||
const { data: result } = useActivities(searchParams);
|
||||
return (
|
||||
<>
|
||||
<Stack gap={0}>
|
||||
{result.items.map((activity: Activity, index: number) => (
|
||||
<Box key={activity.id}>
|
||||
<ActivityListItem activity={activity} />
|
||||
{index < result.items.length - 1 && <Divider />}
|
||||
</Box>
|
||||
))}
|
||||
</Stack>
|
||||
|
||||
{result.items.length === 0 && (
|
||||
<Text ta="center" c="dimmed" py="xl">
|
||||
No activities found
|
||||
</Text>
|
||||
)}
|
||||
|
||||
{result.totalPages > 1 && (
|
||||
<Group justify="center" py="md">
|
||||
<Pagination
|
||||
total={result.totalPages}
|
||||
value={page}
|
||||
onChange={setPage}
|
||||
size="sm"
|
||||
{result.items.map((activity: Activity, index: number) => (
|
||||
<Box key={activity.id}>
|
||||
<ActivityListItem
|
||||
activity={activity}
|
||||
onClick={() => onActivityClick(activity)}
|
||||
/>
|
||||
</Group>
|
||||
)}
|
||||
{index < result.items.length - 1 && <Divider />}
|
||||
</Box>
|
||||
))}
|
||||
</Stack>
|
||||
|
||||
{result.items.length === 0 && (
|
||||
<Text ta="center" c="dimmed" py="xl">
|
||||
No activities found
|
||||
</Text>
|
||||
)}
|
||||
|
||||
{result.totalPages > 1 && (
|
||||
<Group justify="center" py="md">
|
||||
<Pagination
|
||||
total={result.totalPages}
|
||||
value={page}
|
||||
onChange={setPage}
|
||||
size="sm"
|
||||
/>
|
||||
</Group>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
export const ActivitiesTable = () => {
|
||||
const [page, setPage] = useState(1);
|
||||
const [perPage, setPerPage] = useState(100);
|
||||
const [search, setSearch] = useState("");
|
||||
const [successFilter, setSuccessFilter] = useState<string | null>(null);
|
||||
const [sortBy, setSortBy] = useState("-created");
|
||||
const [selectedActivity, setSelectedActivity] = useState<Activity | null>(null);
|
||||
|
||||
const {
|
||||
isOpen: detailsOpened,
|
||||
open: openDetails,
|
||||
close: closeDetails,
|
||||
} = useSheet();
|
||||
|
||||
const searchParams: ActivitySearchParams = useMemo(
|
||||
() => ({
|
||||
page,
|
||||
perPage,
|
||||
perPage: 100,
|
||||
name: search || undefined,
|
||||
success: successFilter === "success" ? true : successFilter === "failure" ? false : undefined,
|
||||
sortBy,
|
||||
}),
|
||||
[page, perPage, search, successFilter, sortBy]
|
||||
[page, search, successFilter, sortBy]
|
||||
);
|
||||
|
||||
const { data: result } = useActivities(searchParams);
|
||||
@@ -139,6 +283,16 @@ export const ActivitiesTable = () => {
|
||||
return null;
|
||||
};
|
||||
|
||||
const handleActivityClick = (activity: Activity) => {
|
||||
setSelectedActivity(activity);
|
||||
openDetails();
|
||||
};
|
||||
|
||||
const handleCloseDetails = () => {
|
||||
setSelectedActivity(null);
|
||||
closeDetails();
|
||||
};
|
||||
|
||||
return (
|
||||
<Container size="100%" px={0}>
|
||||
<Stack gap="xs">
|
||||
@@ -214,9 +368,19 @@ export const ActivitiesTable = () => {
|
||||
</Group>
|
||||
</Group>
|
||||
|
||||
<ActivitiesResults searchParams={searchParams} page={page} setPage={setPage} />
|
||||
|
||||
<ActivitiesResults
|
||||
searchParams={searchParams}
|
||||
page={page}
|
||||
setPage={setPage}
|
||||
onActivityClick={handleActivityClick}
|
||||
/>
|
||||
</Stack>
|
||||
|
||||
<ActivityDetailsSheet
|
||||
activity={selectedActivity}
|
||||
isOpen={detailsOpened}
|
||||
onClose={handleCloseDetails}
|
||||
/>
|
||||
</Container>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -42,7 +42,7 @@ const Layout: React.FC<PropsWithChildren> = ({ children }) => {
|
||||
pos='relative'
|
||||
h='100%'
|
||||
mah='100%'
|
||||
pb={{ base: 65, md: 0 }}
|
||||
pb={{ base: 65, sm: 0 }}
|
||||
px={{ base: 0.01, sm: 100, md: 200, lg: 300 }}
|
||||
maw='100dvw'
|
||||
style={{ transition: 'none', overflow: 'hidden' }}
|
||||
|
||||
@@ -9,7 +9,7 @@ interface MatchListProps {
|
||||
const MatchList = ({ matches }: MatchListProps) => {
|
||||
const filteredMatches = matches?.filter(match =>
|
||||
match.home && match.away && !match.bye && match.status != "tbd"
|
||||
) || [];
|
||||
).sort((a, b) => a.start_time < b.start_time ? 1 : -1) || [];
|
||||
|
||||
if (!filteredMatches.length) {
|
||||
return undefined;
|
||||
|
||||
112
src/features/tournaments/components/podium.tsx
Normal file
112
src/features/tournaments/components/podium.tsx
Normal file
@@ -0,0 +1,112 @@
|
||||
import { Stack, Group, Text, ThemeIcon, Box, Center } from "@mantine/core";
|
||||
import { CrownIcon, MedalIcon } from "@phosphor-icons/react";
|
||||
import { Tournament } from "../types";
|
||||
|
||||
interface PodiumProps {
|
||||
tournament: Tournament;
|
||||
}
|
||||
|
||||
export const Podium = ({ tournament }: PodiumProps) => {
|
||||
if (!tournament.first_place) {
|
||||
return (
|
||||
<Box p="md">
|
||||
<Center>
|
||||
<Text c="dimmed" size="sm">
|
||||
Podium will appear here when the tournament is over
|
||||
</Text>
|
||||
</Center>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Stack gap="xs" px="md">
|
||||
{tournament.first_place && (
|
||||
<Group
|
||||
gap="md"
|
||||
p="md"
|
||||
style={{
|
||||
backgroundColor: 'var(--mantine-color-yellow-light)',
|
||||
borderRadius: 'var(--mantine-radius-md)',
|
||||
border: '3px solid var(--mantine-color-yellow-outline)',
|
||||
boxShadow: 'var(--mantine-shadow-md)',
|
||||
}}
|
||||
>
|
||||
<ThemeIcon size="xl" color="yellow" variant="light" radius="xl">
|
||||
<CrownIcon size={24} />
|
||||
</ThemeIcon>
|
||||
<Stack gap={4} style={{ flex: 1 }}>
|
||||
<Text size="md" fw={600}>
|
||||
{tournament.first_place.name}
|
||||
</Text>
|
||||
<Group gap="xs">
|
||||
{tournament.first_place.players?.map((player) => (
|
||||
<Text key={player.id} size="sm" c="dimmed">
|
||||
{player.first_name} {player.last_name}
|
||||
</Text>
|
||||
))}
|
||||
</Group>
|
||||
</Stack>
|
||||
</Group>
|
||||
)}
|
||||
|
||||
{tournament.second_place && (
|
||||
<Group
|
||||
gap="md"
|
||||
p="xs"
|
||||
style={{
|
||||
backgroundColor: 'var(--mantine-color-default)',
|
||||
borderRadius: 'var(--mantine-radius-md)',
|
||||
border: '2px solid var(--mantine-color-default-border)',
|
||||
boxShadow: 'var(--mantine-shadow-sm)',
|
||||
}}
|
||||
>
|
||||
<ThemeIcon size="lg" color="gray" variant="light" radius="xl">
|
||||
<MedalIcon size={20} />
|
||||
</ThemeIcon>
|
||||
<Stack gap={4} style={{ flex: 1 }}>
|
||||
<Text size="sm" fw={600}>
|
||||
{tournament.second_place.name}
|
||||
</Text>
|
||||
<Group gap="xs">
|
||||
{tournament.second_place.players?.map((player) => (
|
||||
<Text key={player.id} size="xs" c="dimmed">
|
||||
{player.first_name} {player.last_name}
|
||||
</Text>
|
||||
))}
|
||||
</Group>
|
||||
</Stack>
|
||||
</Group>
|
||||
)}
|
||||
|
||||
{tournament.third_place && (
|
||||
<Group
|
||||
gap="md"
|
||||
p="xs"
|
||||
style={{
|
||||
backgroundColor: 'var(--mantine-color-orange-light)',
|
||||
borderRadius: 'var(--mantine-radius-md)',
|
||||
border: '2px solid var(--mantine-color-orange-outline)',
|
||||
boxShadow: 'var(--mantine-shadow-sm)',
|
||||
}}
|
||||
>
|
||||
<ThemeIcon size="lg" color="orange" variant="light" radius="xl">
|
||||
<MedalIcon size={18} />
|
||||
</ThemeIcon>
|
||||
<Stack gap={4} style={{ flex: 1 }}>
|
||||
<Text size="sm" fw={600}>
|
||||
{tournament.third_place.name}
|
||||
</Text>
|
||||
<Group gap="xs">
|
||||
{tournament.third_place.players?.map((player) => (
|
||||
<Text key={player.id} size="xs" c="dimmed">
|
||||
{player.first_name} {player.last_name}
|
||||
</Text>
|
||||
))}
|
||||
</Group>
|
||||
</Stack>
|
||||
</Group>
|
||||
)}
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
@@ -4,11 +4,12 @@ import { useAuth } from "@/contexts/auth-context";
|
||||
import { Box, Divider, Stack, Text, Card, Center } from "@mantine/core";
|
||||
import { Carousel } from "@mantine/carousel";
|
||||
import ListLink from "@/components/list-link";
|
||||
import { TreeStructureIcon, UsersIcon, ClockIcon } from "@phosphor-icons/react";
|
||||
import { TreeStructureIcon, UsersIcon, ClockIcon, TrophyIcon } from "@phosphor-icons/react";
|
||||
import TeamListButton from "../upcoming-tournament/team-list-button";
|
||||
import RulesListButton from "../upcoming-tournament/rules-list-button";
|
||||
import MatchCard from "@/features/matches/components/match-card";
|
||||
import Header from "./header";
|
||||
import { Podium } from "../podium";
|
||||
|
||||
const StartedTournament: React.FC<{ tournament: Tournament }> = ({
|
||||
tournament,
|
||||
@@ -22,6 +23,20 @@ const StartedTournament: React.FC<{ tournament: Tournament }> = ({
|
||||
[tournament.matches]
|
||||
);
|
||||
|
||||
const isTournamentOver = useMemo(() => {
|
||||
const matches = tournament.matches || [];
|
||||
if (matches.length === 0) return false;
|
||||
|
||||
const nonByeMatches = matches.filter((match) => !(match.status === 'tbd' && match.bye === true));
|
||||
if (nonByeMatches.length === 0) return false;
|
||||
|
||||
const finalsMatch = nonByeMatches.reduce((highest, current) =>
|
||||
(!highest || current.lid > highest.lid) ? current : highest
|
||||
);
|
||||
|
||||
return finalsMatch?.status === 'ended';
|
||||
}, [tournament.matches]);
|
||||
|
||||
return (
|
||||
<Stack gap="lg">
|
||||
<Header tournament={tournament} />
|
||||
@@ -42,6 +57,10 @@ const StartedTournament: React.FC<{ tournament: Tournament }> = ({
|
||||
))}
|
||||
</Carousel>
|
||||
</Box>
|
||||
) : isTournamentOver ? (
|
||||
<Box px="lg" w="100%">
|
||||
<Podium tournament={tournament} />
|
||||
</Box>
|
||||
) : (
|
||||
<Card withBorder radius="lg" p="xl" mx="md">
|
||||
<Center>
|
||||
|
||||
@@ -146,12 +146,11 @@ export const TournamentStats = memo(({ tournament }: TournamentStatsProps) => {
|
||||
};
|
||||
|
||||
const teamStatsWithCalculations = useMemo(() => {
|
||||
return sortedTeamStats.map((stat, index) => ({
|
||||
return sortedTeamStats.map((stat) => ({
|
||||
...stat,
|
||||
index,
|
||||
winPercentage: stat.matches > 0 ? (stat.wins / stat.matches) * 100 : 0,
|
||||
avgCupsPerMatch: stat.matches > 0 ? stat.total_cups_made / stat.matches : 0,
|
||||
}));
|
||||
})).sort((a, b) => b.winPercentage - a.winPercentage);;
|
||||
}, [sortedTeamStats]);
|
||||
|
||||
const renderTeamStatsTable = () => {
|
||||
@@ -170,23 +169,14 @@ export const TournamentStats = memo(({ tournament }: TournamentStatsProps) => {
|
||||
return (
|
||||
<Stack gap={0}>
|
||||
<Text px="md" size="lg" fw={600}>Results</Text>
|
||||
{teamStatsWithCalculations.map((stat) => {
|
||||
<Text px="md" c="dimmed" size="xs" fw={500}>Sorted by win percentage</Text>
|
||||
{teamStatsWithCalculations.map((stat, index) => {
|
||||
return (
|
||||
<Box key={stat.id}>
|
||||
<UnstyledButton
|
||||
w="100%"
|
||||
p="md"
|
||||
style={{
|
||||
borderRadius: 0,
|
||||
transition: "background-color 0.15s ease",
|
||||
}}
|
||||
styles={{
|
||||
root: {
|
||||
'&:hover': {
|
||||
backgroundColor: 'var(--mantine-color-gray-0)',
|
||||
},
|
||||
},
|
||||
}}
|
||||
style={{ borderRadius: 0 }}
|
||||
>
|
||||
<Group justify="space-between" align="center" w="100%">
|
||||
<Group gap="sm" align="center">
|
||||
@@ -194,12 +184,12 @@ export const TournamentStats = memo(({ tournament }: TournamentStatsProps) => {
|
||||
<Stack gap={2}>
|
||||
<Group gap='xs'>
|
||||
<Text size="xs" c="dimmed">
|
||||
#{stat.index + 1}
|
||||
#{index + 1}
|
||||
</Text>
|
||||
<Text size="sm" fw={600}>
|
||||
{stat.team_name}
|
||||
</Text>
|
||||
{stat.index === 0 && isComplete && (
|
||||
{index === 0 && isComplete && (
|
||||
<ThemeIcon size="xs" color="yellow" variant="light" radius="xl">
|
||||
<CrownIcon size={12} />
|
||||
</ThemeIcon>
|
||||
@@ -259,7 +249,7 @@ export const TournamentStats = memo(({ tournament }: TournamentStatsProps) => {
|
||||
</Group>
|
||||
</Group>
|
||||
</UnstyledButton>
|
||||
{stat.index < teamStatsWithCalculations.length - 1 && <Divider />}
|
||||
{index < teamStatsWithCalculations.length - 1 && <Divider />}
|
||||
</Box>
|
||||
);
|
||||
})}
|
||||
|
||||
@@ -6,6 +6,7 @@ import { logger } from ".";
|
||||
import { z } from "zod";
|
||||
import { toServerResult } from "@/lib/tanstack-query/utils/to-server-result";
|
||||
import { serverFnLoggingMiddleware } from "@/utils/activities";
|
||||
import { fa } from "zod/v4/locales";
|
||||
|
||||
export const listTournaments = createServerFn()
|
||||
.middleware([superTokensFunctionMiddleware])
|
||||
@@ -64,6 +65,14 @@ export const enrollTeam = createServerFn()
|
||||
// throw new Error('You do not have permission to enroll this team');
|
||||
//}
|
||||
|
||||
const freeAgents = await pbAdmin.getFreeAgents(tournamentId);
|
||||
for (const player of team.players || []) {
|
||||
const isFreeAgent = freeAgents.some(fa => fa.player?.id === player.id);
|
||||
if (isFreeAgent) {
|
||||
await pbAdmin.unenrollFreeAgent(player.id, tournamentId);
|
||||
}
|
||||
}
|
||||
|
||||
logger.info('Enrolling team in tournament', { tournamentId, teamId, userId });
|
||||
const tournament = await pbAdmin.enrollTeam(tournamentId, teamId);
|
||||
return tournament;
|
||||
|
||||
@@ -57,7 +57,7 @@ const MantineProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
setIsHydrated(true);
|
||||
}, []);
|
||||
|
||||
const colorScheme = isHydrated ? metadata.colorScheme || "dark" : "dark";
|
||||
const colorScheme = isHydrated ? metadata.colorScheme || "auto" : "auto";
|
||||
const primaryColor = isHydrated ? metadata.accentColor || "blue" : "blue";
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,78 +0,0 @@
|
||||
import { Card, Container, createTheme, Paper, rem, Select } from "@mantine/core";
|
||||
import type { MantineThemeOverride } from "@mantine/core";
|
||||
|
||||
const CONTAINER_SIZES: Record<string, string> = {
|
||||
xxs: rem("200px"),
|
||||
xs: rem("300px"),
|
||||
sm: rem("400px"),
|
||||
md: rem("500px"),
|
||||
lg: rem("600px"),
|
||||
xl: rem("1400px"),
|
||||
xxl: rem("1600px"),
|
||||
};
|
||||
|
||||
export const defaultTheme: MantineThemeOverride = createTheme({
|
||||
scale: 1.1,
|
||||
autoContrast: true,
|
||||
fontSizes: {
|
||||
xs: rem("12px"),
|
||||
sm: rem("14px"),
|
||||
md: rem("16px"),
|
||||
lg: rem("18px"),
|
||||
xl: rem("20px"),
|
||||
"2xl": rem("24px"),
|
||||
"3xl": rem("30px"),
|
||||
"4xl": rem("36px"),
|
||||
"5xl": rem("48px"),
|
||||
},
|
||||
spacing: {
|
||||
"3xs": rem("4px"),
|
||||
"2xs": rem("8px"),
|
||||
xs: rem("10px"),
|
||||
sm: rem("12px"),
|
||||
md: rem("16px"),
|
||||
lg: rem("20px"),
|
||||
xl: rem("24px"),
|
||||
"2xl": rem("28px"),
|
||||
"3xl": rem("32px"),
|
||||
},
|
||||
primaryColor: "red",
|
||||
components: {
|
||||
Container: Container.extend({
|
||||
vars: (_, { size, fluid }) => ({
|
||||
root: {
|
||||
"--container-size": fluid
|
||||
? "100%"
|
||||
: size !== undefined && size in CONTAINER_SIZES
|
||||
? CONTAINER_SIZES[size]
|
||||
: rem(size),
|
||||
},
|
||||
}),
|
||||
}),
|
||||
Paper: Paper.extend({
|
||||
defaultProps: {
|
||||
p: "md",
|
||||
shadow: "xl",
|
||||
radius: "md",
|
||||
withBorder: true,
|
||||
},
|
||||
}),
|
||||
|
||||
Card: Card.extend({
|
||||
defaultProps: {
|
||||
p: "xl",
|
||||
shadow: "xl",
|
||||
radius: "var(--mantine-radius-default)",
|
||||
withBorder: true,
|
||||
},
|
||||
}),
|
||||
Select: Select.extend({
|
||||
defaultProps: {
|
||||
checkIconPosition: "right",
|
||||
},
|
||||
}),
|
||||
},
|
||||
other: {
|
||||
style: "mantine",
|
||||
},
|
||||
});
|
||||
@@ -63,7 +63,9 @@ export function createBadgesService(pb: PocketBase) {
|
||||
},
|
||||
|
||||
async clearAllBadgeProgress(): Promise<number> {
|
||||
const existingProgress = await pb.collection("badge_progress").getFullList();
|
||||
const existingProgress = await pb.collection("badge_progress").getFullList({
|
||||
filter: 'badge.type != "manual"',
|
||||
});
|
||||
for (const progress of existingProgress) {
|
||||
await pb.collection("badge_progress").delete(progress.id);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user