This commit is contained in:
yohlo
2025-08-20 22:35:40 -05:00
commit f51c278cd3
169 changed files with 8173 additions and 0 deletions

View File

@@ -0,0 +1,40 @@
import { List, ListItem, Skeleton, Text } from "@mantine/core";
import { useNavigate } from "@tanstack/react-router";
import Avatar from "@/components/avatar";
import { Player } from "@/features/players/types";
interface PlayerListProps {
players: Player[];
loading?: boolean;
}
const PlayerList = ({ players, loading = false }: PlayerListProps) => {
const navigate = useNavigate();
if (loading) return <List>
{Array.from({ length: 10 }).map((_, i) => (
<ListItem py='xs'
icon={<Skeleton circle height={40} width={40} />}
>
<Skeleton height={20} width={200} />
</ListItem>
))}
</List>
return <List>
{players?.map((player) => (
<ListItem key={player.id}
py='xs'
icon={<Avatar size={40} name={`${player.first_name} ${player.last_name}`} />}
style={{ cursor: 'pointer' }}
onClick={() => {
navigate({ to: `/profile/${player.id}` });
}}
>
<Text fw={500}>{`${player.first_name} ${player.last_name}`}</Text>
</ListItem>
))}
</List>
}
export default PlayerList;

View File

@@ -0,0 +1,42 @@
import Sheet from "@/components/sheet/sheet";
import { useAuth } from "@/contexts/auth-context";
import { Flex, Title, ActionIcon } from "@mantine/core";
import { PencilIcon } from "@phosphor-icons/react";
import { useMemo } from "react";
import NameUpdateForm from "./name-form";
import Avatar from "@/components/avatar";
import { useSheet } from "@/hooks/use-sheet";
import { Player } from "../../types";
interface HeaderProps {
player: Player;
}
const Header = ({ player }: HeaderProps) => {
const sheet = useSheet();
const { user: authUser } = useAuth();
const owner = useMemo(() => authUser?.id === player.id, [authUser?.id, player.id]);
const name = useMemo(() => `${player.first_name} ${player.last_name}`, [player.first_name, player.last_name]);
return (
<>
<Flex px='xl' w='100%' align='self-end' gap='md'>
<Avatar name={name} size={125} />
<Flex align='center' justify='center' gap={4} pb={20} w='100%'>
<Title ta='center' order={2}>{name}</Title>
<ActionIcon display={owner ? 'block' : 'none'} radius='xl' variant='subtle' onClick={sheet.open}>
<PencilIcon size={20} />
</ActionIcon>
</Flex>
</Flex>
<Sheet title='Update Name' {...sheet.props}>
<NameUpdateForm player={player} toggle={sheet.toggle} />
</Sheet>
</>
)
};
export default Header;

View File

@@ -0,0 +1,22 @@
import { Box, Button, Text } from "@mantine/core";
import Header from "./header";
import { testEvent } from "@/utils/test-event";
import { Player } from "@/features/players/types";
import TeamList from "@/features/teams/components/team-list";
interface ProfileProps {
player: Player;
}
const Profile = ({ player }: ProfileProps) => {
return <>
<Header player={player} />
<Box m='sm' mt='lg'>
<Text size='xl' fw={600}>Teams</Text>
<TeamList teams={player.teams ?? []} />
</Box>
</>;
};
export default Profile;

View File

@@ -0,0 +1,60 @@
import { updatePlayer } from "@/features/players/server";
import { useMutation } from "@tanstack/react-query";
import { Button, Stack, TextInput } from "@mantine/core"
import { useForm } from "@mantine/form";
import toast from "@/lib/sonner";
import { useRouter } from "@tanstack/react-router";
import { Player } from "../../types";
interface NameUpdateFormProps {
player: Player;
toggle: () => void;
}
const NameUpdateForm = ({ player, toggle }: NameUpdateFormProps) => {
const router = useRouter();
const form = useForm({
initialValues: {
first_name: player.first_name,
last_name: player.last_name
},
validate: {
first_name: (value: string) => {
if (value.length === 0) return 'First name is required'
if (!(/^[a-zA-Z\s]{3,20}$/).test(value)) return 'First name must be 3-20 characters long and contain only letters'
},
last_name: (value: string) => {
if (value.length === 0) return 'Last name is required'
if (!(/^[a-zA-Z\s]{3,20}$/).test(value)) return 'Last name must be 3-20 characters long and contain only letters'
}
}
})
const { mutate: updateName, isPending } = useMutation({
mutationFn: async (data: { first_name: string, last_name: string }) => await updatePlayer({ data }),
onSuccess: () => {
toggle();
toast.success('Name updated successfully!');
router.invalidate();
},
onError: () => {
toast.error('There was an issue updating your name. Please try again later.');
}
});
const handleSubmit = async (data: { first_name: string, last_name: string }) => await updateName(data)
return (
<form onSubmit={form.onSubmit(handleSubmit)}>
<Stack gap='xs'>
<TextInput label='First Name' {...form.getInputProps('first_name')} />
<TextInput label='Last Name' {...form.getInputProps('last_name')} />
<Button fullWidth loading={isPending} type='submit'>Save</Button>
<Button fullWidth variant='subtle' color='red' onClick={toggle}>Cancel</Button>
</Stack>
</form>
)
}
export default NameUpdateForm;