From c74da09bde8d676de3408bad1b935f93cf35793b Mon Sep 17 00:00:00 2001 From: yohlo Date: Tue, 9 Sep 2025 23:20:19 -0500 Subject: [PATCH] upcoming tournament page, minor changes --- pb_migrations/1757386414_updated_players.js | 28 +++ pb_migrations/1757386423_updated_matches.js | 28 +++ pb_migrations/1757386431_updated_teams.js | 28 +++ .../1757386438_updated_tournaments.js | 28 +++ src/app/routes/_authed.tsx | 3 +- src/app/routes/_authed/index.tsx | 28 +-- src/components/countdown.tsx | 57 +++++ src/components/sheet/drawer.tsx | 14 +- src/components/sheet/modal.tsx | 14 +- .../sheet/slide-panel/slide-panel.tsx | 4 +- .../bracket/components/match-card.tsx | 2 +- src/features/teams/components/team-form.tsx | 229 ++++++++++++++++++ .../team-form/team-color-picker.tsx | 77 ++++++ .../teams/hooks/use-available-players.ts | 43 ++++ src/features/teams/hooks/use-create-team.ts | 22 ++ src/features/teams/hooks/use-update-team.ts | 17 ++ src/features/teams/server.ts | 87 ++++++- src/features/teams/types.ts | 35 ++- .../components/enroll-free-agent.tsx | 29 +++ .../components/enroll-team/index.tsx | 63 +++++ .../enroll-team/team-selection-view.tsx | 57 +++++ .../components/tournament-form.tsx | 25 +- .../components/upcoming-tournament.tsx | 146 +++++++++++ src/features/tournaments/queries.ts | 10 +- src/features/tournaments/server.ts | 6 + src/hooks/use-now.ts | 18 ++ src/lib/pocketbase/services/players.ts | 1 + src/lib/pocketbase/services/teams.ts | 61 ++++- src/lib/pocketbase/services/tournaments.ts | 11 + 29 files changed, 1125 insertions(+), 46 deletions(-) create mode 100644 pb_migrations/1757386414_updated_players.js create mode 100644 pb_migrations/1757386423_updated_matches.js create mode 100644 pb_migrations/1757386431_updated_teams.js create mode 100644 pb_migrations/1757386438_updated_tournaments.js create mode 100644 src/components/countdown.tsx create mode 100644 src/features/teams/components/team-form.tsx create mode 100644 src/features/teams/components/team-form/team-color-picker.tsx create mode 100644 src/features/teams/hooks/use-available-players.ts create mode 100644 src/features/teams/hooks/use-create-team.ts create mode 100644 src/features/teams/hooks/use-update-team.ts create mode 100644 src/features/tournaments/components/enroll-free-agent.tsx create mode 100644 src/features/tournaments/components/enroll-team/index.tsx create mode 100644 src/features/tournaments/components/enroll-team/team-selection-view.tsx create mode 100644 src/features/tournaments/components/upcoming-tournament.tsx create mode 100644 src/hooks/use-now.ts diff --git a/pb_migrations/1757386414_updated_players.js b/pb_migrations/1757386414_updated_players.js new file mode 100644 index 0000000..0225fb1 --- /dev/null +++ b/pb_migrations/1757386414_updated_players.js @@ -0,0 +1,28 @@ +/// +migrate((app) => { + const collection = app.findCollectionByNameOrId("pbc_3072146508") + + // update collection data + unmarshal({ + "createRule": "", + "deleteRule": "", + "listRule": "", + "updateRule": "", + "viewRule": "" + }, collection) + + return app.save(collection) +}, (app) => { + const collection = app.findCollectionByNameOrId("pbc_3072146508") + + // update collection data + unmarshal({ + "createRule": null, + "deleteRule": null, + "listRule": null, + "updateRule": null, + "viewRule": null + }, collection) + + return app.save(collection) +}) diff --git a/pb_migrations/1757386423_updated_matches.js b/pb_migrations/1757386423_updated_matches.js new file mode 100644 index 0000000..7cac184 --- /dev/null +++ b/pb_migrations/1757386423_updated_matches.js @@ -0,0 +1,28 @@ +/// +migrate((app) => { + const collection = app.findCollectionByNameOrId("pbc_2541054544") + + // update collection data + unmarshal({ + "createRule": "", + "deleteRule": "", + "listRule": "", + "updateRule": "", + "viewRule": "" + }, collection) + + return app.save(collection) +}, (app) => { + const collection = app.findCollectionByNameOrId("pbc_2541054544") + + // update collection data + unmarshal({ + "createRule": null, + "deleteRule": null, + "listRule": null, + "updateRule": null, + "viewRule": null + }, collection) + + return app.save(collection) +}) diff --git a/pb_migrations/1757386431_updated_teams.js b/pb_migrations/1757386431_updated_teams.js new file mode 100644 index 0000000..51e2332 --- /dev/null +++ b/pb_migrations/1757386431_updated_teams.js @@ -0,0 +1,28 @@ +/// +migrate((app) => { + const collection = app.findCollectionByNameOrId("pbc_1568971955") + + // update collection data + unmarshal({ + "createRule": "", + "deleteRule": "", + "listRule": "", + "updateRule": "", + "viewRule": "" + }, collection) + + return app.save(collection) +}, (app) => { + const collection = app.findCollectionByNameOrId("pbc_1568971955") + + // update collection data + unmarshal({ + "createRule": null, + "deleteRule": null, + "listRule": null, + "updateRule": null, + "viewRule": null + }, collection) + + return app.save(collection) +}) diff --git a/pb_migrations/1757386438_updated_tournaments.js b/pb_migrations/1757386438_updated_tournaments.js new file mode 100644 index 0000000..630969b --- /dev/null +++ b/pb_migrations/1757386438_updated_tournaments.js @@ -0,0 +1,28 @@ +/// +migrate((app) => { + const collection = app.findCollectionByNameOrId("pbc_340646327") + + // update collection data + unmarshal({ + "createRule": "", + "deleteRule": "", + "listRule": "", + "updateRule": "", + "viewRule": "" + }, collection) + + return app.save(collection) +}, (app) => { + const collection = app.findCollectionByNameOrId("pbc_340646327") + + // update collection data + unmarshal({ + "createRule": null, + "deleteRule": null, + "listRule": null, + "updateRule": null, + "viewRule": null + }, collection) + + return app.save(collection) +}) diff --git a/src/app/routes/_authed.tsx b/src/app/routes/_authed.tsx index 55c176f..bc951b2 100644 --- a/src/app/routes/_authed.tsx +++ b/src/app/routes/_authed.tsx @@ -2,6 +2,7 @@ import { redirect, createFileRoute, Outlet } from "@tanstack/react-router"; import Layout from "@/features/core/components/layout"; import { useServerEvents } from "@/hooks/use-server-events"; import { Loader } from "@mantine/core"; +import FullScreenLoader from "@/components/full-screen-loader"; export const Route = createFileRoute("/_authed")({ beforeLoad: ({ context }) => { @@ -26,7 +27,7 @@ export const Route = createFileRoute("/_authed")({ }, pendingComponent: () => ( - + ), }); diff --git a/src/app/routes/_authed/index.tsx b/src/app/routes/_authed/index.tsx index be521c3..49cb954 100644 --- a/src/app/routes/_authed/index.tsx +++ b/src/app/routes/_authed/index.tsx @@ -1,27 +1,21 @@ import { createFileRoute } from "@tanstack/react-router"; -import { TrophyIcon } from "@phosphor-icons/react"; -import ListLink from "@/components/list-link"; -import { Box, Divider, Text } from "@mantine/core"; +import { useCurrentTournament } from "@/features/tournaments/queries"; +import UpcomingTournament from "@/features/tournaments/components/upcoming-tournament"; export const Route = createFileRoute("/_authed/")({ component: Home, loader: () => ({ - withPadding: false - }) + withPadding: true, + }), }); function Home() { - return ( - <> - - Some Content Here - + const { data: tournament } = useCurrentTournament(); + const now = new Date(); - - Quick Links - - - - - ); + if (new Date(tournament.start_time) > now) { + return ; + } + + return

Started Tournament

} diff --git a/src/components/countdown.tsx b/src/components/countdown.tsx new file mode 100644 index 0000000..c75ddf5 --- /dev/null +++ b/src/components/countdown.tsx @@ -0,0 +1,57 @@ +import useNow from '@/hooks/use-now'; +import { Text, Group } from '@mantine/core'; +import { useMemo } from 'react'; + +interface CountdownProps { + date: Date; + label?: string; + color?: string; +} + +interface TimeLeft { + days: number; + hours: number; + minutes: number; + seconds: number; +} + +function calculateTimeLeft(targetDate: Date, currentTime = new Date()): TimeLeft { + const difference = targetDate.getTime() - currentTime.getTime(); + + if (difference <= 0) { + return { days: 0, hours: 0, minutes: 0, seconds: 0 }; + } + + return { + days: Math.floor(difference / (1000 * 60 * 60 * 24)), + hours: Math.floor((difference / (1000 * 60 * 60)) % 24), + minutes: Math.floor((difference / 1000 / 60) % 60), + seconds: Math.floor((difference / 1000) % 60) + }; +} + +export function Countdown({ date, label, color }: CountdownProps) { + const now = useNow(); + const timeLeft = useMemo(() => calculateTimeLeft(date, now), [date, now]); + + const formatTime = () => { + const pad = (num: number) => num.toString().padStart(2, '0'); + + if (timeLeft.days > 0) { + return `${timeLeft.days}d ${pad(timeLeft.hours)}:${pad(timeLeft.minutes)}:${pad(timeLeft.seconds)}`; + } else { + return `${pad(timeLeft.hours)}:${pad(timeLeft.minutes)}:${pad(timeLeft.seconds)}`; + } + }; + + return ( + + {label && {label}:} + + {formatTime()} + + + ); +} + +export default Countdown; \ No newline at end of file diff --git a/src/components/sheet/drawer.tsx b/src/components/sheet/drawer.tsx index 123c891..aa169c2 100644 --- a/src/components/sheet/drawer.tsx +++ b/src/components/sheet/drawer.tsx @@ -1,8 +1,8 @@ -import { Box, Container, useComputedColorScheme } from "@mantine/core"; -import { PropsWithChildren, useEffect } from "react"; +import { Box, Container, Flex, Loader, useComputedColorScheme } from "@mantine/core"; +import { PropsWithChildren, Suspense, useEffect } from "react"; import { Drawer as VaulDrawer } from "vaul"; -import { useMantineColorScheme } from "@mantine/core"; import styles from "./styles.module.css"; +import FullScreenLoader from "../full-screen-loader"; interface DrawerProps extends PropsWithChildren { title?: string; @@ -76,7 +76,13 @@ const Drawer: React.FC = ({ /> {title} - {children} + + + + }> + {children} + diff --git a/src/components/sheet/modal.tsx b/src/components/sheet/modal.tsx index 7633973..19c3ff5 100644 --- a/src/components/sheet/modal.tsx +++ b/src/components/sheet/modal.tsx @@ -1,5 +1,5 @@ -import { Modal as MantineModal, Title } from "@mantine/core"; -import { PropsWithChildren } from "react"; +import { Flex, Loader, Modal as MantineModal, Title } from "@mantine/core"; +import { PropsWithChildren, Suspense } from "react"; interface ModalProps extends PropsWithChildren { title?: string; @@ -13,7 +13,15 @@ const Modal: React.FC = ({ title, children, opened, onClose }) => ( onClose={onClose} title={{title}} > - {children} + + + + } + > + {children} + ); diff --git a/src/components/sheet/slide-panel/slide-panel.tsx b/src/components/sheet/slide-panel/slide-panel.tsx index 2507278..2f7749a 100644 --- a/src/components/sheet/slide-panel/slide-panel.tsx +++ b/src/components/sheet/slide-panel/slide-panel.tsx @@ -17,6 +17,7 @@ interface SlidePanelProps { onCancel?: () => void; submitText?: string; cancelText?: string; + cancelColor?: string; maxHeight?: string; formProps?: Record; loading?: boolean; @@ -28,6 +29,7 @@ const SlidePanel = ({ onCancel, submitText = "Submit", cancelText = "Cancel", + cancelColor = "red", maxHeight = "70vh", formProps = {}, loading = false, @@ -114,7 +116,7 @@ const SlidePanel = ({ {onCancel && (