mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-09-30 07:12:36 -05:00
ActionSection initial
This commit is contained in:
@@ -24,6 +24,7 @@ Prerequisites: [Node.js 16.13](https://nodejs.org/en/)
|
||||
|
||||
- [x] Captain can remove players from roster
|
||||
- [ ] Add info about architecture to README
|
||||
- [ ] Make description mandatory + overview tab
|
||||
- [ ] Captain can check in
|
||||
- [ ] Admin can check teams in (and out)
|
||||
- [ ] Admin can drop people out (show on bracket somehow that they dropped)
|
||||
@@ -49,6 +50,7 @@ Prerequisites: [Node.js 16.13](https://nodejs.org/en/)
|
||||
- [ ] Detailed match reports from Lanista
|
||||
- [ ] Stats
|
||||
- [ ] Routine to calculate Trueskill
|
||||
- [ ] Markdown support for tournament description
|
||||
- [ ] Can make a new tournament as org admin
|
||||
- [ ] Rules tab with a good default, highlight overrides
|
||||
- [ ] Friend code to add that is easily editable even before tournament starts
|
||||
|
||||
71
app/components/tournament/ActionSection.tsx
Normal file
71
app/components/tournament/ActionSection.tsx
Normal file
@@ -0,0 +1,71 @@
|
||||
import type { FindTournamentByNameForUrlI } from "~/services/tournament";
|
||||
import { useUser } from "~/utils/hooks";
|
||||
import { AlertIcon } from "../icons/Alert";
|
||||
|
||||
export function ActionSection({
|
||||
tournament,
|
||||
}: {
|
||||
tournament: FindTournamentByNameForUrlI;
|
||||
}) {
|
||||
const user = useUser();
|
||||
|
||||
const ownTeam = tournament.teams.find((team) =>
|
||||
team.members.some(
|
||||
({ member, captain }) => captain && member.id === user?.id
|
||||
)
|
||||
);
|
||||
|
||||
// TODO: or tournament is over
|
||||
if (!ownTeam) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const checkInHasStarted = new Date(tournament.checkInTime) < new Date();
|
||||
const teamHasEnoughMembers = true;
|
||||
// const teamHasEnoughMembers =
|
||||
// ownTeam.members.length >= TOURNAMENT_TEAM_ROSTER_MIN_SIZE;
|
||||
|
||||
if (!checkInHasStarted && !teamHasEnoughMembers) {
|
||||
return (
|
||||
<Wrapper icon="warning">
|
||||
<AlertIcon /> You need at least 4 players in your roster to play
|
||||
</Wrapper>
|
||||
);
|
||||
}
|
||||
|
||||
const differenceInMinutesBetweenCheckInAndStart = Math.floor(
|
||||
(new Date(tournament.startTime).getTime() -
|
||||
new Date(tournament.checkInTime).getTime()) /
|
||||
(1000 * 60)
|
||||
);
|
||||
|
||||
if (!checkInHasStarted && teamHasEnoughMembers) {
|
||||
return (
|
||||
<Wrapper icon="info">
|
||||
<AlertIcon /> Check-in starts{" "}
|
||||
{differenceInMinutesBetweenCheckInAndStart} minutes before the
|
||||
tournament starts
|
||||
</Wrapper>
|
||||
);
|
||||
}
|
||||
|
||||
console.error("Unexpected combination in ActionSection component");
|
||||
return null;
|
||||
}
|
||||
|
||||
function Wrapper({
|
||||
children,
|
||||
icon,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
icon: "warning" | "info";
|
||||
}) {
|
||||
return (
|
||||
<section
|
||||
className="tournament__action-section"
|
||||
style={{ "--action-section-icon-color": `var(--theme-${icon})` } as any}
|
||||
>
|
||||
<div className="tournament__action-section__content">{children}</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
@@ -15,18 +15,7 @@ export function InfoBanner() {
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className="info-banner"
|
||||
style={
|
||||
{
|
||||
"--background": data.bannerBackground,
|
||||
// TODO: do this on backend
|
||||
"--text": `hsl(${data.bannerTextHSLArgs})`,
|
||||
// TODO: and this
|
||||
"--text-transparent": `hsla(${data.bannerTextHSLArgs}, 0.3)`,
|
||||
} as Record<string, string>
|
||||
}
|
||||
>
|
||||
<div className="info-banner">
|
||||
<div className="info-banner__top-row">
|
||||
<div className="info-banner__top-row__date-name">
|
||||
<time
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
useLocation,
|
||||
} from "remix";
|
||||
import invariant from "tiny-invariant";
|
||||
import { ActionSection } from "~/components/tournament/ActionSection";
|
||||
import { InfoBanner } from "~/components/tournament/InfoBanner";
|
||||
import {
|
||||
findTournamentByNameForUrl,
|
||||
@@ -78,8 +79,21 @@ export default function TournamentPage() {
|
||||
})();
|
||||
|
||||
return (
|
||||
<div className="tournament__container">
|
||||
<div
|
||||
className="tournament__container"
|
||||
style={
|
||||
{
|
||||
"--tournaments-background": data.bannerBackground,
|
||||
// TODO: do this on backend
|
||||
"--tournaments-text": `hsl(${data.bannerTextHSLArgs})`,
|
||||
// TODO: and this
|
||||
"--tournaments-text-transparent": `hsla(${data.bannerTextHSLArgs}, 0.3)`,
|
||||
} as Record<string, string>
|
||||
}
|
||||
>
|
||||
<InfoBanner />
|
||||
<ActionSection tournament={data} />
|
||||
<div className="tournament__container__spacer" />
|
||||
{displayNavLinks && (
|
||||
<div
|
||||
style={{ "--tabs-count": navLinks.length } as any}
|
||||
|
||||
@@ -15,10 +15,7 @@ import { Button } from "~/components/Button";
|
||||
import ErrorMessage from "~/components/ErrorMessage";
|
||||
import { MyForm } from "~/components/MyForm";
|
||||
import { TeamRoster } from "~/components/tournament/TeamRoster";
|
||||
import {
|
||||
TOURNAMENT_TEAM_ROSTER_MAX_SIZE,
|
||||
TOURNAMENT_TEAM_ROSTER_MIN_SIZE,
|
||||
} from "~/constants";
|
||||
import { TOURNAMENT_TEAM_ROSTER_MAX_SIZE } from "~/constants";
|
||||
import {
|
||||
ownTeamWithInviteCode,
|
||||
OwnTeamWithInviteCodeI,
|
||||
@@ -145,13 +142,6 @@ export default function ManageRosterPage() {
|
||||
|
||||
return (
|
||||
<div className="tournament__manage-roster">
|
||||
{ownTeam.members.length < TOURNAMENT_TEAM_ROSTER_MIN_SIZE && (
|
||||
// TODO: show alert when team is full
|
||||
<Alert type="warning" data-cy="team-size-alert">
|
||||
You need at least {TOURNAMENT_TEAM_ROSTER_MIN_SIZE} players in your
|
||||
roster to play (max {TOURNAMENT_TEAM_ROSTER_MAX_SIZE})
|
||||
</Alert>
|
||||
)}
|
||||
{ownTeam.members.length >= TOURNAMENT_TEAM_ROSTER_MAX_SIZE && (
|
||||
// TODO: show alert when team is full
|
||||
<Alert type="info" data-cy="team-size-alert">
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
width: 100%;
|
||||
flex-direction: column;
|
||||
gap: var(--s-8);
|
||||
margin-block-start: var(--s-4);
|
||||
}
|
||||
|
||||
.tournament__manage-roster__roster-container {
|
||||
|
||||
@@ -6,12 +6,41 @@
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.tournament__container__spacer {
|
||||
margin-bottom: var(--s-5);
|
||||
}
|
||||
|
||||
.tournament__action-section {
|
||||
width: 100%;
|
||||
padding: var(--s-1-5);
|
||||
margin-top: var(--s-3);
|
||||
background: var(--tournaments-background);
|
||||
border-radius: var(--rounded);
|
||||
}
|
||||
|
||||
.tournament__action-section__content {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
padding: var(--s-2);
|
||||
background-color: var(--bg-lighter);
|
||||
border-radius: var(--rounded);
|
||||
font-size: var(--fonts-sm);
|
||||
font-weight: var(--semi-bold);
|
||||
gap: var(--s-1-5);
|
||||
}
|
||||
|
||||
.tournament__action-section__content > svg {
|
||||
width: 1.5rem;
|
||||
fill: var(--action-section-icon-color);
|
||||
}
|
||||
|
||||
.tournament__links-container {
|
||||
display: grid;
|
||||
justify-content: center;
|
||||
gap: var(--s-10);
|
||||
grid-template-columns: repeat(2, 100px);
|
||||
padding-block: var(--s-8);
|
||||
padding-block-end: var(--s-8);
|
||||
place-items: center;
|
||||
}
|
||||
|
||||
@@ -46,9 +75,9 @@
|
||||
.info-banner {
|
||||
width: 100%;
|
||||
padding: var(--s-6);
|
||||
background: var(--background);
|
||||
background: var(--tournaments-background);
|
||||
border-radius: var(--rounded);
|
||||
color: var(--text);
|
||||
color: var(--tournaments-text);
|
||||
}
|
||||
|
||||
.info-banner__top-row {
|
||||
@@ -82,9 +111,9 @@
|
||||
}
|
||||
|
||||
.info-banner__top-row__tournament-name {
|
||||
border-color: var(--text);
|
||||
border-color: var(--tournaments-text);
|
||||
border-inline-start: 1px solid;
|
||||
color: var(--text);
|
||||
color: var(--tournaments-text);
|
||||
font-size: var(--fonts-xl);
|
||||
font-weight: var(--extra-bold);
|
||||
padding-inline-start: var(--s-4);
|
||||
@@ -125,7 +154,7 @@
|
||||
justify-content: center;
|
||||
padding: var(--s-2);
|
||||
border: 1px solid;
|
||||
border-color: var(--text-transparent);
|
||||
border-color: var(--tournaments-text-transparent);
|
||||
block-size: 2.25rem;
|
||||
border-radius: 50%;
|
||||
color: inherit;
|
||||
@@ -142,7 +171,7 @@
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: 1px solid;
|
||||
border-color: var(--text-transparent);
|
||||
border-color: var(--tournaments-text-transparent);
|
||||
border-radius: var(--rounded);
|
||||
color: inherit;
|
||||
cursor: pointer;
|
||||
|
||||
Reference in New Issue
Block a user