Bracket action panel initial

This commit is contained in:
Kalle (Sendou) 2021-12-27 16:37:30 +02:00
parent c06de36cb2
commit ff5377fc1f
10 changed files with 128 additions and 20 deletions

View File

@ -4,7 +4,7 @@ import { useTimeoutState } from "~/utils/hooks";
import { Button, ButtonProps } from "./Button";
export function SubmitButton(
_props: ButtonProps & { actionType: string; successText: string }
_props: ButtonProps & { actionType: string; successText?: string }
) {
const { actionType, successText, children, ...rest } = _props;
const actionData = useActionData<{ ok?: string }>();
@ -12,7 +12,7 @@ export function SubmitButton(
const [showSuccess, setShowSuccess] = useTimeoutState(false);
useEffect(() => {
if (actionData?.ok !== actionType) return;
if (!successText || actionData?.ok !== actionType) return;
setShowSuccess(true);
}, [actionData]);
@ -30,10 +30,10 @@ export function SubmitButton(
<Button
type="submit"
loading={isLoading()}
variant={showSuccess ? "success" : rest.variant}
variant={showSuccess && successText ? "success" : rest.variant}
{...rest}
>
{showSuccess ? successText : children}
{showSuccess && successText ? successText : children}
</Button>
);
}

View File

@ -4,6 +4,7 @@ import { useUser } from "~/utils/hooks";
import { ActionSectionBeforeStartContent } from "./ActionSectionBeforeStartContent";
// TODO: warning when not registered but check in is open
// TODO: rename, refactor
export function ActionSection() {
const tournament = useLoaderData<FindTournamentByNameForUrlI>();
const user = useUser();
@ -14,16 +15,10 @@ export function ActionSection() {
)
);
if (!ownTeam) {
const tournamentHasStarted = tournament.brackets.some((b) => b.rounds.length);
if (!ownTeam || tournamentHasStarted) {
return null;
}
// TODO:
const tournamentStatus = "not-started"; // "ongoing" | "concluded"
if (tournamentStatus === "not-started") {
return <ActionSectionBeforeStartContent ownTeam={ownTeam} />;
}
return null;
return <ActionSectionBeforeStartContent ownTeam={ownTeam} />;
}

View File

@ -6,13 +6,15 @@ export function ActionSectionWrapper({
...rest
}: {
children: React.ReactNode;
icon: "warning" | "info" | "success" | "error";
icon?: "warning" | "info" | "success" | "error";
"data-cy"?: string;
}) {
// todo: flex-dir: column on mobile
const style: MyCSSProperties = {
"--action-section-icon-color": `var(--theme-${icon})`,
};
const style: MyCSSProperties | undefined = icon
? {
"--action-section-icon-color": `var(--theme-${icon})`,
}
: undefined;
return (
<section
className="tournament__action-section"

View File

@ -0,0 +1,102 @@
import { useLoaderData, useMatches } from "remix";
import invariant from "tiny-invariant";
import type {
BracketModified,
FindTournamentByNameForUrlI,
} from "~/services/tournament";
import { useUser } from "~/utils/hooks";
import { ActionSectionWrapper } from "./ActionSectionWrapper";
// 2) set up UI
// - 1) Add *fc* 2) Host/join room with pass XXXX 3) Done
// 3) report score
// - Show map played
// - Select players who played with radio boxes if team.length > min_roster_length
// - Report
export function BracketActions() {
const data = useLoaderData<BracketModified>();
const user = useUser();
const [, parentRoute] = useMatches();
const { teams } = parentRoute.data as FindTournamentByNameForUrlI;
const ownTeam = teams.find((team) =>
team.members.some(({ member }) => member.id === user?.id)
);
const tournamentIsOver = false;
if (tournamentIsOver || !ownTeam) return null;
const allMatches = [
...data.winners.flatMap((round, roundI) =>
round.matches.map((match) => ({
...match,
bestOf: round.bestOf,
isFirstRound: roundI === 0,
}))
),
...data.losers.flatMap((round) =>
round.matches.map((match) => ({
...match,
bestOf: round.bestOf,
isFirstRound: false,
}))
),
];
const currentMatch = allMatches.find((match) => {
const hasBothParticipants = match.participants?.length === 2;
const isOwnMatch = match.participants?.some((p) => p === ownTeam.name);
const higherCount = match.score
? Math.max(match.score[0], match.score[1])
: 0;
const isNotOver = higherCount > match.bestOf / 2;
return hasBothParticipants && isOwnMatch && isNotOver;
});
if (currentMatch) {
return <ActionSectionWrapper>current match stuff</ActionSectionWrapper>;
}
const nextMatch = allMatches.find((match) => {
return (
match.participants?.reduce(
(acc, cur) => acc + (cur === null ? 1 : 0),
0
) === 1 &&
match.participants.some((p) => p === ownTeam.name) &&
!match.isFirstRound
);
});
invariant(nextMatch, "nextMatch is undefined");
const matchWeAreWaitingFor = allMatches.find(
(match) =>
[match.winnerDestinationMatchId, match.loserDestinationMatchId].includes(
nextMatch.id
) && !match.participants?.includes(ownTeam.name)
);
invariant(matchWeAreWaitingFor, "matchWeAreWaitingFor is undefined");
if (matchWeAreWaitingFor.participants?.length !== 2) {
return (
<ActionSectionWrapper>
Waiting on match number {matchWeAreWaitingFor.number} (missing teams)
</ActionSectionWrapper>
);
}
return (
<ActionSectionWrapper>
Waiting on <b>{matchWeAreWaitingFor.participants[0]}</b> vs.
<b>{matchWeAreWaitingFor.participants[1]}</b>
<i>
{(matchWeAreWaitingFor.score ?? [0, 0]).join("-")} - Best of{" "}
{matchWeAreWaitingFor.bestOf}
</i>
</ActionSectionWrapper>
);
}

View File

@ -374,7 +374,6 @@ function resolveSide(
const matchNumbers = getWinnerDestinationMatchIdToMatchNumbers(rounds).get(
destinationMatch.id
);
console.log(matchNumbers);
const otherNumber = matchNumbers?.find((num) => num !== currentMatch.number);
invariant(
otherNumber,

View File

@ -19,6 +19,8 @@ export function findById(bracketId: string) {
select: {
id: true,
position: true,
winnerDestinationMatchId: true,
loserDestinationMatchId: true,
participants: {
select: {
team: {

View File

@ -9,6 +9,7 @@ import {
} from "~/services/tournament";
import type { BracketModified } from "~/services/tournament";
import { useUser } from "~/utils/hooks";
import { BracketActions } from "~/components/tournament/BracketActions";
export const links: LinksFunction = () => {
return [{ rel: "stylesheet", href: styles }];
@ -27,8 +28,8 @@ export const loader: LoaderFunction = async ({ params }) => {
export default function BracketTabWrapper() {
const data = useLoaderData<BracketModified>();
const [, parentRoute] = useMatches();
const user = useUser();
const { teams } = parentRoute.data as FindTournamentByNameForUrlI;
const user = useUser();
const ownTeam = teams.find((team) =>
team.members.some(({ member }) => member.id === user?.id)
@ -36,6 +37,7 @@ export default function BracketTabWrapper() {
return (
<div className="tournament-bracket__container">
<BracketActions />
<EliminationBracket
bracketSide={data.winners}
ownTeamName={ownTeam?.name}

View File

@ -292,7 +292,6 @@ export default function ManageRosterPage() {
className="tournament__manage-roster__input__button"
actionType="ADD_PLAYER"
loadingText="Adding..."
successText="Added!"
data-cy="add-to-roster-button"
>
Add to roster

View File

@ -309,6 +309,7 @@ function CheckOutButton({ teamId }: { teamId: string }) {
);
}
// TODO: error Cannot destructure property 'default' of 'routeModules[id]' as it is undefined.
function CheckInButton({ teamId }: { teamId: string }) {
const transition = useTransition();
return (

View File

@ -138,6 +138,8 @@ type BracketModifiedSide = {
number: number;
score?: [upperTeamScore: number, lowerTeamScore: number];
participants?: [upperTeamName: string | null, lowerTeamName: string | null];
winnerDestinationMatchId: string | null;
loserDestinationMatchId: string | null;
}[];
}[];
@ -199,6 +201,8 @@ function modifyRounds(
return {
number: match.position,
id: match.id,
winnerDestinationMatchId: match.winnerDestinationMatchId,
loserDestinationMatchId: match.loserDestinationMatchId,
score,
participants,
};
@ -274,6 +278,8 @@ export async function createTournamentRounds({
id: match.id,
position: match.number,
roundId: round.id,
winnerDestinationMatchId: match.winnerDestinationMatchId,
loserDestinationMatchId: match.loserDestinationMatchId,
}));
}),
}),