settings link, uneroll team server fn

This commit is contained in:
yohlo
2025-08-24 00:35:38 -05:00
parent 53276cc18e
commit 324e1742f1
9 changed files with 112 additions and 14 deletions

View File

@@ -12,10 +12,11 @@ export const Route = createFileRoute("/_authed/profile/$playerId")({
player player
} }
}, },
loader: ({ params }) => ({ loader: ({ params, context }) => ({
header: { header: {
collapsed: true, collapsed: true,
withBackButton: true withBackButton: true,
settingsLink: context?.auth.user.id === params.playerId ? 'settings' : undefined
}, },
refresh: { refresh: {
toRefresh: [playerQueries.details(params.playerId).queryKey], toRefresh: [playerQueries.details(params.playerId).queryKey],

View File

@@ -13,10 +13,11 @@ export const Route = createFileRoute('/_authed/tournaments/$tournamentId')({
const { queryClient } = context; const { queryClient } = context;
await queryClient.ensureQueryData(tournamentQueries.details(params.tournamentId)) await queryClient.ensureQueryData(tournamentQueries.details(params.tournamentId))
}, },
loader: ({ params }) => ({ loader: ({ params, context }) => ({
header: { header: {
collapsed: true, collapsed: true,
withBackButton: true withBackButton: true,
settingsLink: context.auth.roles.includes("Admin") ? `/admin/tournaments/${params.tournamentId}` : undefined
}, },
refresh: { refresh: {
toRefresh: tournamentQueries.details(params.tournamentId).queryKey, toRefresh: tournamentQueries.details(params.tournamentId).queryKey,

View File

@@ -2,17 +2,19 @@ import { Title, AppShell, Flex } from "@mantine/core";
import { HeaderConfig } from "../types/header-config"; import { HeaderConfig } from "../types/header-config";
import BackButton from "./back-button"; import BackButton from "./back-button";
import { useMemo } from "react"; import { useMemo } from "react";
import SettingsButton from "./settings-button";
interface HeaderProps extends HeaderConfig { interface HeaderProps extends HeaderConfig {
scrollPosition: { x: number, y: number }; scrollPosition: { x: number, y: number };
} }
const Header = ({ withBackButton, collapsed, title, scrollPosition }: HeaderProps) => { const Header = ({ withBackButton, settingsLink, collapsed, title, scrollPosition }: HeaderProps) => {
const offsetY = useMemo(() => { const offsetY = useMemo(() => {
return collapsed ? scrollPosition.y : 0; return collapsed ? scrollPosition.y : 0;
}, [collapsed, scrollPosition.y]); }, [collapsed, scrollPosition.y]);
return ( return (
<> <>
{settingsLink && <SettingsButton to={settingsLink} offsetY={offsetY} />}
{withBackButton && <BackButton offsetY={offsetY} />} {withBackButton && <BackButton offsetY={offsetY} />}
<AppShell.Header id='app-header' display={collapsed ? 'none' : 'block'}> <AppShell.Header id='app-header' display={collapsed ? 'none' : 'block'}>
<Flex justify='center' align='center' h='100%' px='md'> <Flex justify='center' align='center' h='100%' px='md'>

View File

@@ -0,0 +1,26 @@
import { Box } from "@mantine/core"
import { GearIcon } from "@phosphor-icons/react"
import { useNavigate } from "@tanstack/react-router"
interface SettingButtonProps {
offsetY: number;
to: string;
}
const SettingsButton = ({ offsetY, to }: SettingButtonProps) => {
const navigate = useNavigate();
return (
<Box
style={{ cursor: 'pointer', zIndex: 1000, transform: `translateY(-${offsetY}px)` }}
onClick={() => navigate({ to })}
pos='absolute'
right={{ base: 0, sm: 100, md: 200, lg: 300 }}
m={20}
>
<GearIcon weight='bold' size={20} />
</Box>
);
}
export default SettingsButton;

View File

@@ -2,6 +2,7 @@ interface HeaderConfig {
title?: string; title?: string;
withBackButton?: boolean; withBackButton?: boolean;
collapsed?: boolean; collapsed?: boolean;
settingsLink?: string;
} }
export type { HeaderConfig }; export type { HeaderConfig };

View File

@@ -0,0 +1,30 @@
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { unenrollTeam } from "@/features/tournaments/server";
import toast from '@/lib/sonner';
const useUnenrollTeam = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: (data: { tournamentId: string, teamId: string }) => {
return unenrollTeam({ data });
},
onSuccess: (data, { tournamentId }) => {
if (!data) {
toast.error('There was an issue unenrolling. Please try again later.');
} else {
queryClient.invalidateQueries({ queryKey: ['tournaments', 'detail', tournamentId] });
toast.success('Team unenrolled successfully.');
}
},
onError: (error: any) => {
if (error.message) {
toast.error(error.message);
} else {
toast.error('An unexpected error occurred when trying to unenroll the team. Please try again later.');
}
},
});
};
export default useUnenrollTeam;

View File

@@ -70,3 +70,20 @@ export const enrollTeam = createServerFn()
throw error; throw error;
} }
}); });
export const unenrollTeam = createServerFn()
.validator(z.object({
tournamentId: z.string(),
teamId: z.string()
}))
.middleware([superTokensAdminFunctionMiddleware])
.handler(async ({ data: { tournamentId, teamId }, context }) => {
try {
logger.info('Enrolling team in tournament', { tournamentId, teamId, context });
const tournament = await pbAdmin.unenrollTeam(tournamentId, teamId);
return tournament;
} catch (error) {
logger.error('Error enrolling team', error);
throw error;
}
});

View File

@@ -57,5 +57,16 @@ export function createTournamentsService(pb: PocketBase) {
); );
return transformTournament(result); return transformTournament(result);
}, },
async unenrollTeam(
tournamentId: string,
teamId: string
): Promise<Tournament> {
const result = await pb.collection("tournaments").update<Tournament>(
tournamentId,
{ "teams-": teamId },
{ expand: "teams, teams.players" }
);
return transformTournament(result);
},
}; };
} }

View File

@@ -7,8 +7,11 @@ import { Tournament } from "@/features/tournaments/types";
export function transformPlayer(record: any): Player { export function transformPlayer(record: any): Player {
const sadf: string[] = []; const sadf: string[] = [];
const teams = record.expand?.teams const teams =
?.sort((a: Team, b: Team) => new Date(a.created) < new Date(b.created) ? -1 : 0) record.expand?.teams
?.sort((a: Team, b: Team) =>
new Date(a.created) < new Date(b.created) ? -1 : 0
)
?.map(transformTeam) ?? []; ?.map(transformTeam) ?? [];
return { return {
@@ -23,8 +26,11 @@ export function transformPlayer(record: any): Player {
} }
export function transformTeam(record: any): Team { export function transformTeam(record: any): Team {
const players = record.expand?.players const players =
?.sort((a: Player, b: Player) => new Date(a.created!) < new Date(b.created!) ? -1 : 0) record.expand?.players
?.sort((a: Player, b: Player) =>
new Date(a.created!) < new Date(b.created!) ? -1 : 0
)
?.map(transformPlayer) ?? []; ?.map(transformPlayer) ?? [];
return { return {
@@ -48,8 +54,11 @@ export function transformTeam(record: any): Team {
} }
export function transformTournament(record: any): Tournament { export function transformTournament(record: any): Tournament {
const teams = record.expand?.teams const teams =
?.sort((a: Team, b: Team) => new Date(a.created) < new Date(b.created) ? -1 : 0) record.expand?.teams
?.sort((a: Team, b: Team) =>
new Date(a.created) < new Date(b.created) ? -1 : 0
)
?.map(transformTeam) ?? []; ?.map(transformTeam) ?? [];
return { return {