mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-08-23 19:46:28 -05:00
Tournament register page read only for non-captain participants
This commit is contained in:
@@ -19,6 +19,7 @@ export function ModeMapPoolPicker({
|
||||
onChange,
|
||||
modeTabs,
|
||||
onModeChange,
|
||||
disabled,
|
||||
}: {
|
||||
mode: ModeShort;
|
||||
amountToPick: number;
|
||||
@@ -28,6 +29,8 @@ export function ModeMapPoolPicker({
|
||||
/** When provided, the divider becomes a tab switcher between these modes. */
|
||||
modeTabs?: ModeShort[];
|
||||
onModeChange?: (mode: ModeShort) => void;
|
||||
/** When true, stages can't be picked or removed (view-only). */
|
||||
disabled?: boolean;
|
||||
}) {
|
||||
const [wigglingStageId, setWigglingStageId] = React.useState<StageId | null>(
|
||||
null,
|
||||
@@ -114,6 +117,7 @@ export function ModeMapPoolPicker({
|
||||
const selected = stages.includes(stageId);
|
||||
|
||||
const onClick = () => {
|
||||
if (disabled) return;
|
||||
if (isTiebreaker) return;
|
||||
if (banned) return;
|
||||
if (selected) return handlePickedStageClick(stageId);
|
||||
@@ -130,6 +134,7 @@ export function ModeMapPoolPicker({
|
||||
banned={banned}
|
||||
tiebreaker={isTiebreaker}
|
||||
wiggle={wigglingStageId === stageId}
|
||||
disabled={disabled}
|
||||
testId={`map-pool-${mode}-${stageId}`}
|
||||
/>
|
||||
);
|
||||
@@ -158,6 +163,7 @@ function MapButton({
|
||||
banned,
|
||||
tiebreaker,
|
||||
wiggle,
|
||||
disabled,
|
||||
testId,
|
||||
}: {
|
||||
stageId: StageId;
|
||||
@@ -166,6 +172,7 @@ function MapButton({
|
||||
banned?: boolean;
|
||||
tiebreaker?: boolean;
|
||||
wiggle?: boolean;
|
||||
disabled?: boolean;
|
||||
testId: string;
|
||||
}) {
|
||||
const { t } = useTranslation(["game-misc"]);
|
||||
@@ -181,7 +188,7 @@ function MapButton({
|
||||
})}
|
||||
style={{ "--map-image-url": `url("${stageImageUrl(stageId)}.avif")` }}
|
||||
onClick={onClick}
|
||||
disabled={banned}
|
||||
disabled={disabled || banned}
|
||||
type="button"
|
||||
data-testid={testId}
|
||||
/>
|
||||
|
||||
@@ -25,7 +25,6 @@ import { useAutoRerender } from "~/hooks/useAutoRerender";
|
||||
import { useCopyToClipboard } from "~/hooks/useCopyToClipboard";
|
||||
import { useHydrated } from "~/hooks/useHydrated";
|
||||
import { rankedModesShort } from "~/modules/in-game-lists/modes";
|
||||
import invariant from "~/utils/invariant";
|
||||
import {
|
||||
LOG_IN_URL,
|
||||
SENDOU_INK_BASE_URL,
|
||||
@@ -69,9 +68,12 @@ export default function TournamentRegisterPage() {
|
||||
return (
|
||||
<div className={clsx("stack lg", containerClassName("normal"))}>
|
||||
{isRegularMemberOfATeam ? (
|
||||
<div className="stack md items-center">
|
||||
<Alert>{t("tournament:pre.inATeam")}</Alert>
|
||||
<LeaveTeamControl />
|
||||
<div className="stack md">
|
||||
<Alert>{t("tournament:pre.captainOnlyEdit")}</Alert>
|
||||
<div className="stack md items-center">
|
||||
<LeaveTeamControl />
|
||||
</div>
|
||||
<RegistrationForms readOnly />
|
||||
</div>
|
||||
) : registrationClosedForNonParticipant ? (
|
||||
<Alert>{t("tournament:pre.registrationClosed")}</Alert>
|
||||
@@ -148,11 +150,15 @@ function PleaseLogIn() {
|
||||
);
|
||||
}
|
||||
|
||||
function RegistrationForms() {
|
||||
function RegistrationForms({ readOnly = false }: { readOnly?: boolean }) {
|
||||
const data = useLoaderData<TournamentRegisterPageLoader>();
|
||||
const user = useUser();
|
||||
const tournament = useTournament();
|
||||
|
||||
if (readOnly) {
|
||||
return <ReadOnlyRegistrationForms />;
|
||||
}
|
||||
|
||||
const ownTeam = tournament.ownedTeamByUser(user);
|
||||
const ownTeamCheckedIn = Boolean(ownTeam && ownTeam.checkIns.length > 0);
|
||||
const hasFriendCodeSet = Boolean(user?.friendCode);
|
||||
@@ -212,6 +218,32 @@ function RegistrationForms() {
|
||||
);
|
||||
}
|
||||
|
||||
function ReadOnlyRegistrationForms() {
|
||||
const user = useUser();
|
||||
const tournament = useTournament();
|
||||
|
||||
const team = tournament.teamMemberOfByUser(user);
|
||||
if (!team) return null;
|
||||
|
||||
const checkedIn = team.checkIns.length > 0;
|
||||
|
||||
return (
|
||||
<div className="stack lg">
|
||||
<RegistrationProgress
|
||||
checkedIn={checkedIn}
|
||||
name={team.name}
|
||||
mapPool={team.mapPool ?? undefined}
|
||||
members={team.members}
|
||||
/>
|
||||
<TeamInfo ownTeam={team} canUnregister={false} readOnly />
|
||||
<FillRoster ownTeam={team} ownTeamCheckedIn={checkedIn} readOnly />
|
||||
{tournament.teamsPrePickMaps ? (
|
||||
<CounterPickMapPoolPicker readOnly mapPool={team.mapPool ?? []} />
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function RegistrationProgress({
|
||||
checkedIn,
|
||||
name,
|
||||
@@ -419,16 +451,22 @@ function CheckIn({
|
||||
function TeamInfo({
|
||||
ownTeam,
|
||||
canUnregister,
|
||||
readOnly = false,
|
||||
}: {
|
||||
ownTeam?: TournamentDataTeam | null;
|
||||
canUnregister: boolean;
|
||||
readOnly?: boolean;
|
||||
}) {
|
||||
const { t } = useTranslation(["tournament", "common"]);
|
||||
const tournament = useTournament();
|
||||
|
||||
const defaultValues: Partial<RegisterTeamFormValues> = {
|
||||
teamId: ownTeam?.team ? String(ownTeam.team.id) : null,
|
||||
pickUpName: ownTeam?.team ? null : (ownTeam?.name ?? ""),
|
||||
teamId: readOnly ? null : ownTeam?.team ? String(ownTeam.team.id) : null,
|
||||
pickUpName: readOnly
|
||||
? (ownTeam?.name ?? "")
|
||||
: ownTeam?.team
|
||||
? null
|
||||
: (ownTeam?.name ?? ""),
|
||||
logo:
|
||||
!ownTeam?.team &&
|
||||
ownTeam?.pickupAvatarUrl &&
|
||||
@@ -488,19 +526,34 @@ function TeamInfo({
|
||||
className="stack md items-center"
|
||||
submitButtonText={t("common:actions.save")}
|
||||
submitButtonTestId="save-team-button"
|
||||
readOnly={readOnly}
|
||||
>
|
||||
<RegisterTeamFields />
|
||||
<RegisterTeamFields readOnly={readOnly} />
|
||||
</SendouForm>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function RegisterTeamFields() {
|
||||
function RegisterTeamFields({ readOnly = false }: { readOnly?: boolean }) {
|
||||
const data = useLoaderData<TournamentRegisterPageLoader>();
|
||||
const tournament = useTournament();
|
||||
const { values } = useFormFieldContext();
|
||||
|
||||
if (readOnly) {
|
||||
return (
|
||||
<>
|
||||
<div className={styles.sectionInputContainer}>
|
||||
<FormField name="pickUpName" />
|
||||
</div>
|
||||
<div className={styles.sectionInputContainer}>
|
||||
<FormField name="logo" />
|
||||
</div>
|
||||
<FormField name="prefersNotToHost" />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
const isLinked = Boolean(values.teamId);
|
||||
|
||||
const teamOptions = (data?.teams ?? []).map((team) => ({
|
||||
@@ -583,12 +636,13 @@ function GoogleFormsLink() {
|
||||
function FillRoster({
|
||||
ownTeam,
|
||||
ownTeamCheckedIn,
|
||||
readOnly = false,
|
||||
}: {
|
||||
ownTeam: TournamentDataTeam;
|
||||
ownTeamCheckedIn: boolean;
|
||||
readOnly?: boolean;
|
||||
}) {
|
||||
const data = useLoaderData<TournamentRegisterPageLoader>();
|
||||
const user = useUser();
|
||||
const tournament = useTournament();
|
||||
const { copyToClipboard, copySuccess } = useCopyToClipboard();
|
||||
const { t } = useTranslation(["common", "tournament"]);
|
||||
@@ -598,8 +652,7 @@ function FillRoster({
|
||||
inviteCode: ownTeam.inviteCode!,
|
||||
})}`;
|
||||
|
||||
const { members: ownTeamMembers } = tournament.ownedTeamByUser(user) ?? {};
|
||||
invariant(ownTeamMembers, "own team members should exist");
|
||||
const ownTeamMembers = ownTeam.members;
|
||||
|
||||
const missingMembers = Math.max(
|
||||
tournament.minMembersPerTeam - ownTeamMembers.length,
|
||||
@@ -612,11 +665,14 @@ function FillRoster({
|
||||
);
|
||||
|
||||
const showDeleteMemberSection =
|
||||
(!ownTeamCheckedIn && ownTeamMembers.length > 1) ||
|
||||
(ownTeamCheckedIn && ownTeamMembers.length > tournament.minMembersPerTeam);
|
||||
!readOnly &&
|
||||
((!ownTeamCheckedIn && ownTeamMembers.length > 1) ||
|
||||
(ownTeamCheckedIn &&
|
||||
ownTeamMembers.length > tournament.minMembersPerTeam));
|
||||
|
||||
const playersAvailableToDirectlyAdd = (() => {
|
||||
return (data!.friendPlayers?.friends ?? []).filter((user) => {
|
||||
if (readOnly) return [];
|
||||
return (data?.friendPlayers?.friends ?? []).filter((user) => {
|
||||
const isNotInTeam = tournament.ctx.teams.every((team) =>
|
||||
team.members.every((member) => member.userId !== user.id),
|
||||
);
|
||||
@@ -629,7 +685,7 @@ function FillRoster({
|
||||
})();
|
||||
|
||||
const teamIsFull = ownTeamMembers.length >= tournament.maxMembersPerTeam;
|
||||
const canAddMembers = !teamIsFull && tournament.registrationOpen;
|
||||
const canAddMembers = !teamIsFull && tournament.registrationOpen && !readOnly;
|
||||
|
||||
return (
|
||||
<div>
|
||||
@@ -842,13 +898,19 @@ function DeleteMember({ members }: { members: TournamentDataTeam["members"] }) {
|
||||
);
|
||||
}
|
||||
|
||||
function CounterPickMapPoolPicker() {
|
||||
function CounterPickMapPoolPicker({
|
||||
readOnly = false,
|
||||
mapPool,
|
||||
}: {
|
||||
readOnly?: boolean;
|
||||
mapPool?: NonNullable<TournamentDataTeam["mapPool"]>;
|
||||
}) {
|
||||
const { t } = useTranslation(["common", "game-misc", "tournament"]);
|
||||
const tournament = useTournament();
|
||||
const fetcher = useFetcher();
|
||||
const data = useLoaderData<TournamentRegisterPageLoader>();
|
||||
const [counterPickMaps, setCounterPickMaps] = React.useState(
|
||||
data?.mapPool ?? [],
|
||||
mapPool ?? data?.mapPool ?? [],
|
||||
);
|
||||
|
||||
const counterPickMapPool = new MapPool(counterPickMaps);
|
||||
@@ -899,14 +961,15 @@ function CounterPickMapPoolPicker() {
|
||||
...stageIds.map((stageId) => ({ mode, stageId })),
|
||||
])
|
||||
}
|
||||
disabled={readOnly}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
{validateCounterPickMapPool(
|
||||
counterPickMapPool,
|
||||
isOneModeTournamentOf,
|
||||
tournament.ctx.tieBreakerMapPool,
|
||||
) === "VALID" ? (
|
||||
{readOnly ? null : validateCounterPickMapPool(
|
||||
counterPickMapPool,
|
||||
isOneModeTournamentOf,
|
||||
tournament.ctx.tieBreakerMapPool,
|
||||
) === "VALID" ? (
|
||||
<SubmitButton
|
||||
_action="UPDATE_MAP_POOL"
|
||||
state={fetcher.state}
|
||||
|
||||
@@ -77,6 +77,7 @@ export function FormField({
|
||||
canRemoveItem,
|
||||
}: FormFieldProps) {
|
||||
const context = useOptionalFormFieldContext();
|
||||
const isDisabled = disabled ?? context?.readOnly ?? false;
|
||||
|
||||
const fieldSchema = React.useMemo(() => {
|
||||
if (field) return field;
|
||||
@@ -183,7 +184,7 @@ export function FormField({
|
||||
<InputFormField
|
||||
{...commonProps}
|
||||
{...formField}
|
||||
disabled={disabled}
|
||||
disabled={isDisabled}
|
||||
value={value as string}
|
||||
onChange={handleChange as (v: string) => void}
|
||||
/>
|
||||
@@ -195,7 +196,7 @@ export function FormField({
|
||||
<InGameNameFormField
|
||||
{...commonProps}
|
||||
{...formField}
|
||||
disabled={disabled}
|
||||
disabled={isDisabled}
|
||||
value={value as string}
|
||||
onChange={handleChange as (v: string) => void}
|
||||
/>
|
||||
@@ -207,7 +208,7 @@ export function FormField({
|
||||
<SwitchFormField
|
||||
{...commonProps}
|
||||
{...formField}
|
||||
isDisabled={disabled}
|
||||
isDisabled={isDisabled}
|
||||
checked={value as boolean}
|
||||
onChange={handleChange as (v: boolean) => void}
|
||||
/>
|
||||
@@ -219,7 +220,7 @@ export function FormField({
|
||||
<TextareaFormField
|
||||
{...commonProps}
|
||||
{...formField}
|
||||
disabled={disabled}
|
||||
disabled={isDisabled}
|
||||
value={value as string}
|
||||
onChange={handleChange as (v: string) => void}
|
||||
/>
|
||||
@@ -346,7 +347,7 @@ export function FormField({
|
||||
<ImageFormField
|
||||
{...commonProps}
|
||||
{...formField}
|
||||
disabled={disabled}
|
||||
disabled={isDisabled}
|
||||
value={value as ImageFieldValue}
|
||||
onChange={handleChange as (v: ImageFieldValue) => void}
|
||||
/>
|
||||
|
||||
@@ -37,6 +37,7 @@ export interface FormContextValue<T extends z.ZodRawShape = z.ZodRawShape> {
|
||||
clearServerError: (name: string) => void;
|
||||
onFieldChange?: (name: string, newValue: unknown) => void;
|
||||
hideRequiredIndicator: boolean;
|
||||
readOnly: boolean;
|
||||
values: Record<string, unknown>;
|
||||
setValue: (name: string, value: unknown) => void;
|
||||
setValueFromPrev: (name: string, updater: (prev: unknown) => unknown) => void;
|
||||
@@ -104,6 +105,11 @@ type BaseFormProps<T extends z.ZodRawShape> = {
|
||||
* adds noise (e.g. the settings page).
|
||||
*/
|
||||
hideRequiredIndicator?: boolean;
|
||||
/**
|
||||
* When true, renders the form for viewing only: every field is disabled and
|
||||
* the submit button is hidden.
|
||||
*/
|
||||
readOnly?: boolean;
|
||||
onApply?: (values: z.infer<z.ZodObject<T>>) => void;
|
||||
secondarySubmit?: React.ReactNode;
|
||||
/**
|
||||
@@ -150,6 +156,7 @@ export function SendouForm<T extends z.ZodRawShape>({
|
||||
className,
|
||||
fullWidth,
|
||||
hideRequiredIndicator = false,
|
||||
readOnly = false,
|
||||
onApply,
|
||||
secondarySubmit,
|
||||
onSuccess,
|
||||
@@ -266,6 +273,7 @@ export function SendouForm<T extends z.ZodRawShape>({
|
||||
onFieldChange:
|
||||
autoSubmit || autoApply ? actions.onFieldChange : undefined,
|
||||
hideRequiredIndicator,
|
||||
readOnly,
|
||||
setValue: actions.setValue,
|
||||
setValueFromPrev: actions.setValueFromPrev,
|
||||
revalidateAll: actions.revalidateAll,
|
||||
@@ -281,6 +289,7 @@ export function SendouForm<T extends z.ZodRawShape>({
|
||||
autoSubmit,
|
||||
autoApply,
|
||||
hideRequiredIndicator,
|
||||
readOnly,
|
||||
fetcher.state,
|
||||
store,
|
||||
actions,
|
||||
@@ -303,7 +312,7 @@ export function SendouForm<T extends z.ZodRawShape>({
|
||||
<>
|
||||
{title ? <h2 className={styles.title}>{title}</h2> : null}
|
||||
<React.Fragment key={locationKey}>{resolvedChildren}</React.Fragment>
|
||||
{autoSubmit || autoApply ? null : (
|
||||
{autoSubmit || autoApply || readOnly ? null : (
|
||||
<div className="mt-4 stack horizontal md mx-auto justify-center items-center">
|
||||
<SubmitButton
|
||||
_action={_action}
|
||||
@@ -673,6 +682,7 @@ export function useFormFieldContext(): FormContextValue {
|
||||
clearServerError: context.clearServerError,
|
||||
onFieldChange: context.onFieldChange,
|
||||
hideRequiredIndicator: context.hideRequiredIndicator,
|
||||
readOnly: context.readOnly,
|
||||
values,
|
||||
setValue: context.setValue,
|
||||
setValueFromPrev: context.setValueFromPrev,
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
"pre.footer": "Tilmeldingen til turneringen kan frit ændres inden turneringen starter",
|
||||
"pre.logIn": "Log in for at tilmelde dig",
|
||||
"pre.inATeam": "Du er allerede en del af et hold til denne begivenhed",
|
||||
"pre.captainOnlyEdit": "",
|
||||
"pre.registrationClosed": "",
|
||||
"pre.checkIn.range": "Tjek-ind kan gøres imellem {{start}} og {{finish}}",
|
||||
"pre.checkIn.over": "Tjek-ind er forbi",
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
"pre.footer": "Registrierung kann vor Turnierstart frei verändert werden",
|
||||
"pre.logIn": "Logge dich zum Registrieren ein",
|
||||
"pre.inATeam": "Du bist in einem Team für dieses Event",
|
||||
"pre.captainOnlyEdit": "",
|
||||
"pre.registrationClosed": "",
|
||||
"pre.checkIn.range": "Check-in ist zwischen {{start}} und {{finish}} geöffnet",
|
||||
"pre.checkIn.over": "Check-in ist vorbei",
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
"pre.footer": "Registration can be freely changed before the tournament starts",
|
||||
"pre.logIn": "Log in to register",
|
||||
"pre.inATeam": "You are in a team for this event",
|
||||
"pre.captainOnlyEdit": "You are in a team for this event. Only the team captain can edit the registration.",
|
||||
"pre.registrationClosed": "Registration for this tournament has closed",
|
||||
"pre.checkIn.range": "Check-in is open between {{start}} and {{finish}}",
|
||||
"pre.checkIn.over": "Check-in is over",
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
"pre.footer": "Registro puede ser ajustado antes de que empieze el torneo",
|
||||
"pre.logIn": "Ingresa al sitio para registrar",
|
||||
"pre.inATeam": "Estás en un equipo para este evento",
|
||||
"pre.captainOnlyEdit": "",
|
||||
"pre.registrationClosed": "",
|
||||
"pre.checkIn.range": "El check-in está abierto entre {{start}} y {{finish}}",
|
||||
"pre.checkIn.over": "Se acabó check-in",
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
"pre.footer": "Registro puede ser ajustado antes de que empieze el torneo",
|
||||
"pre.logIn": "Ingresa al sitio para registrar",
|
||||
"pre.inATeam": "Estás en un equipo para este evento",
|
||||
"pre.captainOnlyEdit": "",
|
||||
"pre.registrationClosed": "",
|
||||
"pre.checkIn.range": "El check-in está abierto entre {{start}} y {{finish}}",
|
||||
"pre.checkIn.over": "Se acabó check-in",
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
"pre.footer": "L'inscription peut être librement modifiée avant le début du tournoi",
|
||||
"pre.logIn": "Connectez-vous pour vous inscrire",
|
||||
"pre.inATeam": "Vous faites partie d'une équipe pour cet événement",
|
||||
"pre.captainOnlyEdit": "",
|
||||
"pre.registrationClosed": "",
|
||||
"pre.checkIn.range": "L'enregistrement est ouvert entre {{start}} et {{finish}}",
|
||||
"pre.checkIn.over": "L'enregistrement est fini",
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
"pre.footer": "L'inscription peut être librement modifiée avant le début du tournoi",
|
||||
"pre.logIn": "Connectez-vous pour vous inscrire",
|
||||
"pre.inATeam": "Vous faites partie d'une équipe pour cet événement",
|
||||
"pre.captainOnlyEdit": "",
|
||||
"pre.registrationClosed": "",
|
||||
"pre.checkIn.range": "L'enregistrement est ouvert entre {{start}} et {{finish}}",
|
||||
"pre.checkIn.over": "L'enregistrement est fini",
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
"pre.footer": "הרשמה ניתנת לשינוי עד שהטורניר מתחיל",
|
||||
"pre.logIn": "התחבר כדי להירשם",
|
||||
"pre.inATeam": "הנך בצוות לאירוע",
|
||||
"pre.captainOnlyEdit": "",
|
||||
"pre.registrationClosed": "",
|
||||
"pre.checkIn.range": "צ'ק-אין פתוח בין {{start}} ל-{{finish}}",
|
||||
"pre.checkIn.over": "צ'ק-אין נגמר",
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
"pre.footer": "L'iscrizione può essere cambiata liberamente prima del torneo",
|
||||
"pre.logIn": "Accedi per iscriverti",
|
||||
"pre.inATeam": "Sei in un team per questo evento",
|
||||
"pre.captainOnlyEdit": "",
|
||||
"pre.registrationClosed": "",
|
||||
"pre.checkIn.range": "Il Check-in è aperto tra {{start}} e {{finish}}",
|
||||
"pre.checkIn.over": "Il Check-in è chiuso",
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
"pre.footer": "参加登録はトーナメント開始前であればいつでも変更できます",
|
||||
"pre.logIn": "ログインして登録する",
|
||||
"pre.inATeam": "あなたはこのイベントでチームに参加しています",
|
||||
"pre.captainOnlyEdit": "",
|
||||
"pre.registrationClosed": "",
|
||||
"pre.checkIn.range": "チェックインは {{start}} から {{finish}} までのあいだ受け付けています",
|
||||
"pre.checkIn.over": "チェックインは終了しました",
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
"pre.footer": "",
|
||||
"pre.logIn": "",
|
||||
"pre.inATeam": "",
|
||||
"pre.captainOnlyEdit": "",
|
||||
"pre.registrationClosed": "",
|
||||
"pre.checkIn.range": "",
|
||||
"pre.checkIn.over": "",
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
"pre.footer": "",
|
||||
"pre.logIn": "",
|
||||
"pre.inATeam": "",
|
||||
"pre.captainOnlyEdit": "",
|
||||
"pre.registrationClosed": "",
|
||||
"pre.checkIn.range": "",
|
||||
"pre.checkIn.over": "",
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
"pre.footer": "",
|
||||
"pre.logIn": "",
|
||||
"pre.inATeam": "",
|
||||
"pre.captainOnlyEdit": "",
|
||||
"pre.registrationClosed": "",
|
||||
"pre.checkIn.range": "",
|
||||
"pre.checkIn.over": "",
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
"pre.footer": "O registro pode ser livremente alterado antes do torneio começar",
|
||||
"pre.logIn": "Faça o login para registrar",
|
||||
"pre.inATeam": "Você está em um time para este evento",
|
||||
"pre.captainOnlyEdit": "",
|
||||
"pre.registrationClosed": "",
|
||||
"pre.checkIn.range": "O check-in está aberto entre {{start}} e {{finish}}",
|
||||
"pre.checkIn.over": "O check-in acabou",
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
"pre.footer": "До начала турнира вся информация о команде может быть изменена",
|
||||
"pre.logIn": "Войдите, чтобы зарегистрироваться",
|
||||
"pre.inATeam": "Вы уже состоите в команде, записанной на этот турнир",
|
||||
"pre.captainOnlyEdit": "",
|
||||
"pre.registrationClosed": "",
|
||||
"pre.checkIn.range": "Чек-ин открыт с {{start}} по {{finish}}",
|
||||
"pre.checkIn.over": "Чек-ин закрыт",
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
"pre.footer": "在比赛开始前,报名信息可随意改动",
|
||||
"pre.logIn": "登录以报名",
|
||||
"pre.inATeam": "您已经在本次活动的某支队伍里了",
|
||||
"pre.captainOnlyEdit": "",
|
||||
"pre.registrationClosed": "",
|
||||
"pre.checkIn.range": "签到开放时间为 {{start}} 至 {{finish}}",
|
||||
"pre.checkIn.over": "签到已结束",
|
||||
|
||||
Reference in New Issue
Block a user