diff --git a/scenes/layout/components/Layout.tsx b/scenes/layout/components/Layout.tsx index 77d4d36f3..a52b1bfaa 100644 --- a/scenes/layout/components/Layout.tsx +++ b/scenes/layout/components/Layout.tsx @@ -14,6 +14,7 @@ export function Layout(p: { children: JSXElement }) { setMenuExpanded((expanded) => !expanded); } + // TODO: why logo wide? return ( <>
diff --git a/scenes/layout/components/UserItem.tsx b/scenes/layout/components/UserItem.tsx index 66270a48f..7848d726e 100644 --- a/scenes/layout/components/UserItem.tsx +++ b/scenes/layout/components/UserItem.tsx @@ -3,6 +3,7 @@ import { DiscordIcon } from "../../../components/icons/Discord"; import { useUser } from "../../../utils/UserContext"; import s from "../styles/UserItem.module.css"; +// TODO: redirect to same page on login export function UserItem() { const user = useUser(); diff --git a/scenes/tournament/TournamentPage.data.ts b/scenes/tournament/TournamentPage.data.ts index 98d17db7f..4db41c0a2 100644 --- a/scenes/tournament/TournamentPage.data.ts +++ b/scenes/tournament/TournamentPage.data.ts @@ -1,14 +1,12 @@ -import { useData } from "solid-app-router"; import { createResource } from "solid-js"; -import { InferQueryOutput, trpcClient } from "../../utils/trpc-client"; -import type { UseDataResult } from "../../utils/types"; +import { trpcClient } from "../../utils/trpc-client"; export default function TournamentData({ params, }: { params: { organization: string; tournament: string }; }) { - const [user] = createResource( + return createResource( () => [params.organization, params.tournament], ([organization, tournament]) => trpcClient.query("tournament.get", { @@ -16,10 +14,6 @@ export default function TournamentData({ tournament, }) ); - - return user; } -export function useTournamentData(delta?: number) { - return useData>>(delta); -} +export type ITournamentData = ReturnType; diff --git a/scenes/tournament/components/ActionSection.tsx b/scenes/tournament/components/ActionSection.tsx index cade29371..d1c07df86 100644 --- a/scenes/tournament/components/ActionSection.tsx +++ b/scenes/tournament/components/ActionSection.tsx @@ -1,9 +1,10 @@ +import { useData } from "solid-app-router"; import { createMemo, createSignal } from "solid-js"; import { ErrorMessage } from "../../../components/ErrorMessage"; import { trpcClient } from "../../../utils/trpc-client"; import { useForm } from "../../../utils/useForm"; import s from "../styles/ActionSection.module.css"; -import { useTournamentData } from "../TournamentPage.data"; +import type { ITournamentData } from "../TournamentPage.data"; const lengthIsCorrect = ({ value }: { value: string }) => { if (value.length >= 2 && value.length <= 40) { @@ -20,16 +21,31 @@ const uniqueName = return `There is already a team called "${value}" registered`; }; +// TODO: show login action when not logged in +// TODO: show add members action when captain and not started +// TODO: show leave member action when not captain and not started +// TODO: show checkin action when captain and check-in started +// TODO: show report score and other in-game actions when there is a match ongoing +// TODO: outlined when no crucial action, filled when there is? export function ActionSection() { const [expanded, setExpanded] = createSignal(false); - const tournament = useTournamentData(); + const [teamName, setTeamName] = createSignal(""); + const [tournament, { refetch }] = useData(); const { validate: _validate, formSubmit: _formSubmit, errors } = useForm(); - const fn = (form: HTMLFormElement) => { - return trpcClient.mutation("tournament.createTournamentTeam", { - name: "Team Olive", - tournamentId: 1, - }); + const fn = async () => { + try { + await trpcClient.mutation("tournament.createTournamentTeam", { + name: teamName(), + tournamentId: tournament()!.id, + }); + // TODO: mutate here? + refetch(); + setExpanded(false); + } catch (e) { + console.error(e); + // TODO: deal with error + } }; const registeredTeamNames = createMemo(() => @@ -60,6 +76,8 @@ export function ActionSection() { name="team-name" id="team-name" required + value={teamName()} + onInput={(e) => setTeamName(e.currentTarget.value)} // @ts-expect-error use:_validate={[ lengthIsCorrect, diff --git a/scenes/tournament/components/InfoBanner.tsx b/scenes/tournament/components/InfoBanner.tsx index 235b5eb46..dc46c3bf9 100644 --- a/scenes/tournament/components/InfoBanner.tsx +++ b/scenes/tournament/components/InfoBanner.tsx @@ -1,11 +1,12 @@ import { Show } from "solid-js"; import { DiscordIcon } from "../../../components/icons/Discord"; import { TwitterIcon } from "../../../components/icons/Twitter"; -import { useTournamentData } from "../TournamentPage.data"; +import type { ITournamentData } from "../TournamentPage.data"; import s from "../styles/InfoBanner.module.css"; +import { useData } from "solid-app-router"; export function InfoBanner() { - const tournament = useTournamentData(); + const [tournament] = useData(); // TODO: skeleton return ( diff --git a/scenes/tournament/components/MapPoolTab.tsx b/scenes/tournament/components/MapPoolTab.tsx index eb35fd8c3..f67646a4b 100644 --- a/scenes/tournament/components/MapPoolTab.tsx +++ b/scenes/tournament/components/MapPoolTab.tsx @@ -1,11 +1,12 @@ import { For, Show } from "solid-js"; import { modesShort, stages } from "../../../utils/constants"; -import { useTournamentData } from "../TournamentPage.data"; +import type { ITournamentData } from "../TournamentPage.data"; import s from "../styles/MapPoolTab.module.css"; import type { Mode } from ".prisma/client"; +import { useData } from "solid-app-router"; export function MapPoolTab() { - const tournament = useTournamentData(1); + const [tournament] = useData(1); return ( diff --git a/scenes/tournament/components/TeamsTab.tsx b/scenes/tournament/components/TeamsTab.tsx index b07a2e581..6ac76d062 100644 --- a/scenes/tournament/components/TeamsTab.tsx +++ b/scenes/tournament/components/TeamsTab.tsx @@ -1,26 +1,45 @@ import { createMemo, For, Show } from "solid-js"; -import { useTournamentData } from "../TournamentPage.data"; +import type { ITournamentData } from "../TournamentPage.data"; import s from "../styles/TeamsTab.module.css"; import { AvatarWithName } from "../../../components/AvatarWithName"; import type { InferQueryOutput } from "../../../utils/trpc-client"; +import { useUser } from "../../../utils/UserContext"; +import { useData } from "solid-app-router"; const sortCaptainFirst = (a: { captain: boolean }, b: { captain: boolean }) => { return Number(b.captain) - Number(a.captain); }; -const sortTeamsFullFirst = (a: { members: any[] }, b: { members: any[] }) => { - const aSortValue = a.members.length >= 4 ? 1 : 0; - const bSortValue = b.members.length >= 4 ? 1 : 0; +const sortOwnTeamsAndFullTeamsFirst = + (userId?: number) => + ( + a: { members: { member: { id: number } }[] }, + b: { members: { member: { id: number } }[] } + ) => { + if (userId) { + const aSortValue = Number( + a.members.some(({ member }) => userId === member.id) + ); + const bSortValue = Number( + b.members.some(({ member }) => userId === member.id) + ); - return bSortValue - aSortValue; -}; + if (aSortValue !== bSortValue) return bSortValue - aSortValue; + } + + const aSortValue = a.members.length >= 4 ? 1 : 0; + const bSortValue = b.members.length >= 4 ? 1 : 0; + + return bSortValue - aSortValue; + }; export function TeamsTab() { - const tournament = useTournamentData(1); + const [tournament] = useData(1); + const user = useUser(); const sortedTeams = createMemo(() => tournament() - ?.teams.sort(sortTeamsFullFirst) + ?.teams.sort(sortOwnTeamsAndFullTeamsFirst(user()?.id)) .map((team) => { return { ...team, @@ -29,6 +48,7 @@ export function TeamsTab() { }) ); + // TODO: looks kinda bad on mobile return (
{(teams) => } diff --git a/scenes/tournament/components/TournamentPage.tsx b/scenes/tournament/components/TournamentPage.tsx index c577b1ce4..359070a47 100644 --- a/scenes/tournament/components/TournamentPage.tsx +++ b/scenes/tournament/components/TournamentPage.tsx @@ -1,12 +1,12 @@ -import { NavLink, Outlet } from "solid-app-router"; +import { NavLink, Outlet, useData } from "solid-app-router"; import { InfoBanner } from "./InfoBanner"; import s from "../styles/TournamentPage.module.css"; -import { useTournamentData } from "../TournamentPage.data"; +import type { ITournamentData } from "../TournamentPage.data"; import { Show } from "solid-js"; import { ActionSection } from "./ActionSection"; export default function TournamentsPage() { - const tournament = useTournamentData(); + const [tournament] = useData(); return (
diff --git a/scenes/tournament/service.ts b/scenes/tournament/service.ts index 9839fafa4..24b9e7eec 100644 --- a/scenes/tournament/service.ts +++ b/scenes/tournament/service.ts @@ -12,6 +12,7 @@ export async function findTournamentByNameForUrl({ nameForUrl: tournamentNameForUrl.toLowerCase(), }, select: { + id: true, name: true, description: true, startTime: true, @@ -42,6 +43,7 @@ export async function findTournamentByNameForUrl({ captain: true, member: { select: { + id: true, discordAvatar: true, discordName: true, discordId: true, diff --git a/utils/trpc-client.ts b/utils/trpc-client.ts index 6ca6269bc..7e7c0dcda 100644 --- a/utils/trpc-client.ts +++ b/utils/trpc-client.ts @@ -2,6 +2,7 @@ import { createTRPCClient } from "@trpc/client"; import type { inferProcedureOutput } from "@trpc/server"; import type { AppRouter } from "../server"; +// TODO: transformer superjson export const trpcClient = createTRPCClient({ url: `${import.meta.env.VITE_BACKEND_URL}/trpc`, fetch: (input, init) => fetch(input, { ...init, credentials: "include" }),