mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-10-01 15:48:23 -05:00
Extract AddPlayers component
This commit is contained in:
98
app/components/AddPlayers.tsx
Normal file
98
app/components/AddPlayers.tsx
Normal file
@@ -0,0 +1,98 @@
|
||||
import { Form, useLocation } from "remix";
|
||||
import { useBaseURL, useTimeoutState } from "~/hooks/common";
|
||||
import { FindManyByTrustReceiverId } from "~/models/TrustRelationship.server";
|
||||
import { FormErrorMessage } from "./FormErrorMessage";
|
||||
import { Label } from "./Label";
|
||||
import { SubmitButton } from "./SubmitButton";
|
||||
|
||||
export function AddPlayers({
|
||||
inviteCode,
|
||||
addUserError,
|
||||
trustingUsers,
|
||||
hiddenInputs,
|
||||
}: {
|
||||
inviteCode: string;
|
||||
addUserError?: string;
|
||||
trustingUsers: FindManyByTrustReceiverId;
|
||||
hiddenInputs: { name: string; value: string }[];
|
||||
}) {
|
||||
const baseURL = useBaseURL();
|
||||
const location = useLocation();
|
||||
const urlWithInviteCode = `${baseURL}${location.pathname.replace(
|
||||
"manage-team",
|
||||
"join-team"
|
||||
)}?code=${inviteCode}`;
|
||||
|
||||
return (
|
||||
<fieldset className="add-players__actions">
|
||||
<legend>Add players to your team</legend>
|
||||
<div className="add-players__actions__section">
|
||||
<Label htmlFor="inviteCodeInput">Share this URL</Label>
|
||||
<input
|
||||
id="inviteCodeInput"
|
||||
className="add-players__input"
|
||||
disabled
|
||||
value={urlWithInviteCode}
|
||||
/>
|
||||
<CopyToClipboardButton urlWithInviteCode={urlWithInviteCode} />
|
||||
</div>
|
||||
{trustingUsers.length > 0 && (
|
||||
<div className="add-players__actions__section">
|
||||
<Form method="post">
|
||||
{hiddenInputs.map((input) => (
|
||||
<input
|
||||
key={input.value}
|
||||
name={input.name}
|
||||
value={input.value}
|
||||
type="hidden"
|
||||
/>
|
||||
))}
|
||||
<Label htmlFor="userId">
|
||||
Add players you previously played with
|
||||
</Label>
|
||||
<select className="add-players__select" name="userId" id="userId">
|
||||
{trustingUsers.map(({ trustGiver }) => (
|
||||
<option key={trustGiver.id} value={trustGiver.id}>
|
||||
{trustGiver.discordName}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
<FormErrorMessage errorMsg={addUserError} />
|
||||
<SubmitButton
|
||||
className="add-players__input__button"
|
||||
actionType="ADD_PLAYER"
|
||||
loadingText="Adding..."
|
||||
data-cy="add-to-roster-button"
|
||||
>
|
||||
Add to roster
|
||||
</SubmitButton>
|
||||
</Form>
|
||||
</div>
|
||||
)}
|
||||
</fieldset>
|
||||
);
|
||||
}
|
||||
|
||||
function CopyToClipboardButton({
|
||||
urlWithInviteCode,
|
||||
}: {
|
||||
urlWithInviteCode: string;
|
||||
}) {
|
||||
const [showCopied, setShowCopied] = useTimeoutState(false);
|
||||
|
||||
return (
|
||||
<button
|
||||
className="add-players__input__button"
|
||||
onClick={() => {
|
||||
navigator.clipboard
|
||||
.writeText(urlWithInviteCode)
|
||||
.then(() => setShowCopied(true))
|
||||
.catch((e) => console.error(e));
|
||||
}}
|
||||
type="button"
|
||||
data-cy="copy-to-clipboard-button"
|
||||
>
|
||||
{showCopied ? "Copied!" : "Copy to clipboard"}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
@@ -16,6 +16,7 @@ export const links: LinksFunction = () => {
|
||||
return [{ rel: "stylesheet", href: styles }];
|
||||
};
|
||||
|
||||
// TODO: Switch to layout component
|
||||
export const meta: MetaFunction = () => {
|
||||
return {
|
||||
title: makeTitle("Play!"),
|
||||
|
||||
@@ -8,12 +8,11 @@ import {
|
||||
redirect,
|
||||
useActionData,
|
||||
useLoaderData,
|
||||
useLocation,
|
||||
} from "remix";
|
||||
import { z } from "zod";
|
||||
import { AddPlayers } from "~/components/AddPlayers";
|
||||
import { Alert } from "~/components/Alert";
|
||||
import { Catcher } from "~/components/Catcher";
|
||||
import { FormErrorMessage } from "~/components/FormErrorMessage";
|
||||
import { FormInfoText } from "~/components/FormInfoText";
|
||||
import { Label } from "~/components/Label";
|
||||
import { SubmitButton } from "~/components/SubmitButton";
|
||||
@@ -26,19 +25,18 @@ import {
|
||||
roompassRegExpString,
|
||||
tournamentURL,
|
||||
} from "~/core/tournament/utils";
|
||||
import { useBaseURL, useTimeoutState } from "~/hooks/common";
|
||||
import * as Tournament from "~/models/Tournament.server";
|
||||
import * as TournamentTeam from "~/models/TournamentTeam.server";
|
||||
import * as TournamentTeamMember from "~/models/TournamentTeamMember.server";
|
||||
import * as User from "~/models/User.server";
|
||||
import type { FindManyByTrustReceiverId } from "~/models/TrustRelationship.server";
|
||||
import styles from "~/styles/tournament-manage-team.css";
|
||||
import { parseRequestFormData, requireUser, validate } from "~/utils";
|
||||
import {
|
||||
isCaptainOfTheTeam,
|
||||
teamHasNotCheckedIn,
|
||||
tournamentTeamIsNotFull,
|
||||
} from "~/core/tournament/validators";
|
||||
import * as Tournament from "~/models/Tournament.server";
|
||||
import * as TournamentTeam from "~/models/TournamentTeam.server";
|
||||
import * as TournamentTeamMember from "~/models/TournamentTeamMember.server";
|
||||
import type { FindManyByTrustReceiverId } from "~/models/TrustRelationship.server";
|
||||
import * as User from "~/models/User.server";
|
||||
import styles from "~/styles/tournament-manage-team.css";
|
||||
import { parseRequestFormData, requireUser, validate } from "~/utils";
|
||||
|
||||
export const links: LinksFunction = () => {
|
||||
return [{ rel: "stylesheet", href: styles }];
|
||||
@@ -205,13 +203,6 @@ export const loader: LoaderFunction = async ({ params, context }) => {
|
||||
export default function ManageTeamPage() {
|
||||
const actionData = useActionData<ActionData>();
|
||||
const { ownTeam, trustingUsers } = useLoaderData<Data>();
|
||||
const baseURL = useBaseURL();
|
||||
const location = useLocation();
|
||||
|
||||
const urlWithInviteCode = `${baseURL}${location.pathname.replace(
|
||||
"manage-team",
|
||||
"join-team"
|
||||
)}?code=${ownTeam.inviteCode}`;
|
||||
|
||||
return (
|
||||
<div className="tournament__manage-team">
|
||||
@@ -301,78 +292,19 @@ export default function ManageTeamPage() {
|
||||
</fieldset>
|
||||
</Form>
|
||||
{ownTeam.members.length < TOURNAMENT_TEAM_ROSTER_MAX_SIZE && (
|
||||
<fieldset className="tournament__manage-team__actions">
|
||||
<legend>Add players to your team</legend>
|
||||
<div className="tournament__manage-team__actions__section">
|
||||
<Label htmlFor="inviteCodeInput">Share this URL</Label>
|
||||
<input
|
||||
id="inviteCodeInput"
|
||||
className="tournament__manage-team__input"
|
||||
disabled
|
||||
value={urlWithInviteCode}
|
||||
/>
|
||||
<CopyToClipboardButton urlWithInviteCode={urlWithInviteCode} />
|
||||
</div>
|
||||
{trustingUsers.length > 0 && (
|
||||
<div className="tournament__manage-team__actions__section">
|
||||
<Form method="post">
|
||||
<input type="hidden" name="_action" value="ADD_PLAYER" />
|
||||
<input type="hidden" name="teamId" value={ownTeam.id} />
|
||||
<Label htmlFor="userId">
|
||||
Add players you previously played with
|
||||
</Label>
|
||||
<select
|
||||
className="tournament__manage-team__select"
|
||||
name="userId"
|
||||
id="userId"
|
||||
>
|
||||
{trustingUsers.map(({ trustGiver }) => (
|
||||
<option key={trustGiver.id} value={trustGiver.id}>
|
||||
{trustGiver.discordName}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
<FormErrorMessage errorMsg={actionData?.error?.userId} />
|
||||
<SubmitButton
|
||||
className="tournament__manage-team__input__button"
|
||||
actionType="ADD_PLAYER"
|
||||
loadingText="Adding..."
|
||||
data-cy="add-to-roster-button"
|
||||
>
|
||||
Add to roster
|
||||
</SubmitButton>
|
||||
</Form>
|
||||
</div>
|
||||
)}
|
||||
</fieldset>
|
||||
<AddPlayers
|
||||
inviteCode={ownTeam.inviteCode}
|
||||
trustingUsers={trustingUsers}
|
||||
hiddenInputs={[
|
||||
{ name: "_action", value: "ADD_PLAYER" },
|
||||
{ name: "teamId", value: ownTeam.id },
|
||||
]}
|
||||
addUserError={actionData?.error?.userId}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function CopyToClipboardButton({
|
||||
urlWithInviteCode,
|
||||
}: {
|
||||
urlWithInviteCode: string;
|
||||
}) {
|
||||
const [showCopied, setShowCopied] = useTimeoutState(false);
|
||||
|
||||
return (
|
||||
<button
|
||||
className="tournament__manage-team__input__button"
|
||||
onClick={() => {
|
||||
navigator.clipboard
|
||||
.writeText(urlWithInviteCode)
|
||||
.then(() => setShowCopied(true))
|
||||
.catch((e) => console.error(e));
|
||||
}}
|
||||
type="button"
|
||||
data-cy="copy-to-clipboard-button"
|
||||
>
|
||||
{showCopied ? "Copied!" : "Copy to clipboard"}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
// TODO: handle 404 (logged in but not registered)
|
||||
export const CatchBoundary = Catcher;
|
||||
|
||||
@@ -397,6 +397,34 @@ select::selection {
|
||||
padding-inline: var(--s-2);
|
||||
}
|
||||
|
||||
.add-players__actions {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
column-gap: var(--s-14);
|
||||
row-gap: var(--s-8);
|
||||
}
|
||||
|
||||
.add-players__actions__section {
|
||||
display: flex;
|
||||
width: 18rem;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.add-players__input {
|
||||
width: min(100%, 20rem);
|
||||
}
|
||||
|
||||
.add-players__input__button {
|
||||
margin-block-start: var(--s-4);
|
||||
}
|
||||
|
||||
.add-players__select {
|
||||
width: min(100%, 10rem);
|
||||
}
|
||||
|
||||
/* Utility classes */
|
||||
|
||||
.mt-2 {
|
||||
|
||||
@@ -9,30 +9,6 @@
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.tournament__manage-team__actions {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
column-gap: var(--s-14);
|
||||
row-gap: var(--s-8);
|
||||
}
|
||||
|
||||
.tournament__manage-team__actions__section {
|
||||
display: flex;
|
||||
width: 18rem;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.tournament__manage-team__input {
|
||||
width: min(100%, 20rem);
|
||||
}
|
||||
|
||||
.tournament__manage-team__select {
|
||||
width: min(100%, 10rem);
|
||||
}
|
||||
|
||||
.tournament__manage-team__radio-with-label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -43,7 +19,3 @@
|
||||
margin-block-end: 0;
|
||||
margin-inline-start: var(--s-2);
|
||||
}
|
||||
|
||||
.tournament__manage-team__input__button {
|
||||
margin-block-start: var(--s-4);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user