mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-09-30 23:27:55 -05:00
Admin tabs
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import clsx from "clsx";
|
||||
import { Trash } from "lucide-react";
|
||||
import { Trash, Trophy, Tv, UserCog, Users } from "lucide-react";
|
||||
import * as React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useFetcher } from "react-router";
|
||||
@@ -7,6 +7,12 @@ import { Avatar } from "~/components/Avatar";
|
||||
import { Divider } from "~/components/Divider";
|
||||
import { LinkButton, SendouButton } from "~/components/elements/Button";
|
||||
import { SendouDialog } from "~/components/elements/Dialog";
|
||||
import {
|
||||
SendouTab,
|
||||
SendouTabList,
|
||||
SendouTabPanel,
|
||||
SendouTabs,
|
||||
} from "~/components/elements/Tabs";
|
||||
import { UserSearch } from "~/components/elements/UserSearch";
|
||||
import { FormMessage } from "~/components/FormMessage";
|
||||
import { FormWithConfirm } from "~/components/FormWithConfirm";
|
||||
@@ -20,6 +26,7 @@ import { useUser } from "~/features/auth/core/user";
|
||||
import * as Progression from "~/features/tournament-bracket/core/Progression";
|
||||
import type { TournamentData } from "~/features/tournament-bracket/core/Tournament.server";
|
||||
import { USER } from "~/features/user-page/user-page-constants";
|
||||
import { useSearchParamState } from "~/hooks/useSearchParamState";
|
||||
import { databaseTimestampToDate } from "~/utils/dates";
|
||||
import invariant from "~/utils/invariant";
|
||||
import { assertUnreachable } from "~/utils/types";
|
||||
@@ -35,8 +42,10 @@ import adminStyles from "./to.$id.admin.module.css";
|
||||
|
||||
export { action } from "../actions/to.$id.admin.server";
|
||||
|
||||
type AdminTab = "teams" | "staff" | "stream" | "brackets";
|
||||
|
||||
export default function TournamentAdminPage() {
|
||||
const { t } = useTranslation(["calendar"]);
|
||||
const { t } = useTranslation(["tournament", "calendar"]);
|
||||
const tournament = useTournament();
|
||||
const [editingProgression, setEditingProgression] = React.useState(false);
|
||||
|
||||
@@ -47,6 +56,39 @@ export default function TournamentAdminPage() {
|
||||
setEditingProgression(false);
|
||||
}, [tournament]);
|
||||
|
||||
const showReopen = Boolean(
|
||||
DANGEROUS_CAN_ACCESS_DEV_CONTROLS &&
|
||||
tournament.ctx.isFinalized &&
|
||||
tournament.isAdmin(user),
|
||||
);
|
||||
const showEditBrackets =
|
||||
tournament.isAdmin(user) &&
|
||||
tournament.hasStarted &&
|
||||
!tournament.ctx.isFinalized;
|
||||
const showStaffTab = tournament.isAdmin(user);
|
||||
const showBracketsTab =
|
||||
!tournament.isLeagueSignup || showEditBrackets || showReopen;
|
||||
|
||||
const isVisibleTab = (tab: string): tab is AdminTab => {
|
||||
switch (tab) {
|
||||
case "teams":
|
||||
case "stream":
|
||||
return true;
|
||||
case "staff":
|
||||
return showStaffTab;
|
||||
case "brackets":
|
||||
return showBracketsTab;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
const [tab, setTab] = useSearchParamState<AdminTab>({
|
||||
defaultValue: "teams",
|
||||
name: "tab",
|
||||
revive: (value) => (isVisibleTab(value) ? value : null),
|
||||
});
|
||||
|
||||
if (
|
||||
!tournament.isOrganizer(user) ||
|
||||
(tournament.ctx.isFinalized && !DANGEROUS_CAN_ACCESS_DEV_CONTROLS)
|
||||
@@ -86,51 +128,77 @@ export default function TournamentAdminPage() {
|
||||
) : null}
|
||||
</div>
|
||||
) : null}
|
||||
{tournament.isAdmin(user) &&
|
||||
tournament.hasStarted &&
|
||||
!tournament.ctx.isFinalized ? (
|
||||
<div className="stack horizontal justify-end">
|
||||
<SendouButton
|
||||
onPress={() => setEditingProgression(true)}
|
||||
size="small"
|
||||
variant="outlined"
|
||||
data-testid="edit-event-info-button"
|
||||
>
|
||||
Edit brackets
|
||||
</SendouButton>
|
||||
{editingProgression ? (
|
||||
<BracketProgressionEditDialog
|
||||
close={() => setEditingProgression(false)}
|
||||
/>
|
||||
<SendouTabs
|
||||
orientation="vertical"
|
||||
horizontalBelow={720}
|
||||
selectedKey={tab}
|
||||
onSelectionChange={(key) => setTab(key as AdminTab)}
|
||||
>
|
||||
<SendouTabList>
|
||||
<SendouTab id="teams" icon={<Users />}>
|
||||
{t("tournament:admin.tab.teams")}
|
||||
</SendouTab>
|
||||
{showStaffTab ? (
|
||||
<SendouTab id="staff" icon={<UserCog />}>
|
||||
{t("tournament:admin.tab.staff")}
|
||||
</SendouTab>
|
||||
) : null}
|
||||
</div>
|
||||
) : null}
|
||||
<Divider smallText>Team actions</Divider>
|
||||
<TeamActions />
|
||||
{tournament.isAdmin(user) ? (
|
||||
<>
|
||||
<Divider smallText>Staff</Divider>
|
||||
<Staff />
|
||||
</>
|
||||
) : null}
|
||||
<Divider smallText>Cast Twitch Accounts</Divider>
|
||||
<CastTwitchAccounts />
|
||||
<Divider smallText>Participant list download</Divider>
|
||||
<DownloadParticipants />
|
||||
{!tournament.isLeagueSignup ? (
|
||||
<>
|
||||
<Divider smallText>Bracket reset</Divider>
|
||||
<BracketReset />
|
||||
</>
|
||||
) : null}
|
||||
{DANGEROUS_CAN_ACCESS_DEV_CONTROLS &&
|
||||
tournament.ctx.isFinalized &&
|
||||
tournament.isAdmin(user) ? (
|
||||
<>
|
||||
<Divider smallText>Reopen tournament (dev only)</Divider>
|
||||
<ReopenTournament />
|
||||
</>
|
||||
) : null}
|
||||
<SendouTab id="stream" icon={<Tv />}>
|
||||
{t("tournament:admin.tab.stream")}
|
||||
</SendouTab>
|
||||
{showBracketsTab ? (
|
||||
<SendouTab id="brackets" icon={<Trophy />}>
|
||||
{t("tournament:admin.tab.brackets")}
|
||||
</SendouTab>
|
||||
) : null}
|
||||
</SendouTabList>
|
||||
<SendouTabPanel id="teams" className="stack lg">
|
||||
<TeamActions />
|
||||
<Divider smallText>Participant list download</Divider>
|
||||
<DownloadParticipants />
|
||||
</SendouTabPanel>
|
||||
{showStaffTab ? (
|
||||
<SendouTabPanel id="staff">
|
||||
<Staff />
|
||||
</SendouTabPanel>
|
||||
) : null}
|
||||
<SendouTabPanel id="stream">
|
||||
<CastTwitchAccounts />
|
||||
</SendouTabPanel>
|
||||
{showBracketsTab ? (
|
||||
<SendouTabPanel id="brackets" className="stack lg">
|
||||
{showEditBrackets ? (
|
||||
<div className="stack horizontal justify-end">
|
||||
<SendouButton
|
||||
onPress={() => setEditingProgression(true)}
|
||||
size="small"
|
||||
variant="outlined"
|
||||
data-testid="edit-event-info-button"
|
||||
>
|
||||
Edit brackets
|
||||
</SendouButton>
|
||||
{editingProgression ? (
|
||||
<BracketProgressionEditDialog
|
||||
close={() => setEditingProgression(false)}
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
) : null}
|
||||
{!tournament.isLeagueSignup ? (
|
||||
<>
|
||||
<Divider smallText>Bracket reset</Divider>
|
||||
<BracketReset />
|
||||
</>
|
||||
) : null}
|
||||
{showReopen ? (
|
||||
<>
|
||||
<Divider smallText>Reopen tournament (dev only)</Divider>
|
||||
<ReopenTournament />
|
||||
</>
|
||||
) : null}
|
||||
</SendouTabPanel>
|
||||
) : null}
|
||||
</SendouTabs>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -376,6 +376,7 @@ test.describe("Tournament bracket", () => {
|
||||
await submit(page);
|
||||
}
|
||||
|
||||
await page.getByRole("tab", { name: "Brackets" }).click();
|
||||
await page.getByTestId("edit-event-info-button").click();
|
||||
for (let i = 0; i < 3; i++) {
|
||||
await page.getByTestId("delete-bracket-button").last().click();
|
||||
@@ -436,6 +437,7 @@ test.describe("Tournament bracket", () => {
|
||||
url: tournamentAdminPage(tournamentId),
|
||||
});
|
||||
|
||||
await page.getByRole("tab", { name: "Brackets" }).click();
|
||||
await page.getByTestId("edit-event-info-button").click();
|
||||
await page.getByTestId("delete-bracket-button").last().click();
|
||||
await page.getByTestId("placements-input").last().fill("3,4");
|
||||
@@ -498,6 +500,7 @@ test.describe("Tournament bracket", () => {
|
||||
url: tournamentAdminPage(tournamentId),
|
||||
});
|
||||
|
||||
await page.getByRole("tab", { name: "Brackets" }).click();
|
||||
await page.getByTestId("edit-event-info-button").click();
|
||||
await page.getByTestId("delete-bracket-button").last().click();
|
||||
|
||||
@@ -596,6 +599,7 @@ test.describe("Tournament bracket", () => {
|
||||
url: tournamentAdminPage(tournamentId),
|
||||
});
|
||||
|
||||
await page.getByRole("tab", { name: "Brackets" }).click();
|
||||
await page.getByTestId("edit-event-info-button").click();
|
||||
|
||||
await page.getByRole("button", { name: "Clear" }).click();
|
||||
@@ -738,6 +742,7 @@ test.describe("Tournament bracket", () => {
|
||||
await submit(page);
|
||||
}
|
||||
|
||||
await page.getByRole("tab", { name: "Stream" }).click();
|
||||
await page.getByLabel("Twitch accounts").fill("test");
|
||||
await submit(page, "save-cast-twitch-accounts-button");
|
||||
|
||||
@@ -802,11 +807,13 @@ test.describe("Tournament bracket", () => {
|
||||
await reportResult(page, { mapsToReport: 2 });
|
||||
|
||||
await page.getByTestId("admin-tab").click();
|
||||
await page.getByRole("tab", { name: "Brackets" }).click();
|
||||
await page
|
||||
.getByLabel('Type bracket name ("Main bracket") to confirm')
|
||||
.fill("Main bracket");
|
||||
await submit(page, "reset-bracket-button");
|
||||
|
||||
await page.getByRole("tab", { name: "Teams" }).click();
|
||||
await page.getByLabel("Action").selectOption("CHECK_IN");
|
||||
await page.getByLabel("Team", { exact: true }).selectOption("1");
|
||||
await submit(page);
|
||||
|
||||
@@ -31,6 +31,7 @@ test.describe("Tournament staff", () => {
|
||||
url: tournamentAdminPage(TOURNAMENT_ID),
|
||||
});
|
||||
|
||||
await page.getByRole("tab", { name: "Staff" }).click();
|
||||
await selectUser({
|
||||
page,
|
||||
userName: "N-ZAP",
|
||||
@@ -65,6 +66,7 @@ test.describe("Tournament staff", () => {
|
||||
url: tournamentAdminPage(TOURNAMENT_ID),
|
||||
});
|
||||
|
||||
await page.getByRole("tab", { name: "Staff" }).click();
|
||||
await selectUser({
|
||||
page,
|
||||
userName: "N-ZAP",
|
||||
@@ -83,7 +85,7 @@ test.describe("Tournament staff", () => {
|
||||
await isNotVisible(page.getByTestId("add-staff-button"));
|
||||
|
||||
await page.getByLabel("Action").selectOption("CHECK_IN");
|
||||
await page.getByLabel("Team").selectOption("101");
|
||||
await page.getByLabel("Team", { exact: true }).selectOption("101");
|
||||
await submit(page);
|
||||
|
||||
await navigate({
|
||||
@@ -118,6 +120,7 @@ test.describe("Tournament staff", () => {
|
||||
url: tournamentAdminPage(TOURNAMENT_ID),
|
||||
});
|
||||
|
||||
await page.getByRole("tab", { name: "Staff" }).click();
|
||||
await selectUser({
|
||||
page,
|
||||
userName: "N-ZAP",
|
||||
|
||||
@@ -31,6 +31,7 @@ test.describe("Tournament streams", () => {
|
||||
url: tournamentAdminPage(tournamentId),
|
||||
});
|
||||
|
||||
await page.getByRole("tab", { name: "Stream" }).click();
|
||||
await page
|
||||
.getByLabel("Twitch accounts")
|
||||
.fill("test_cast_stream,another_cast");
|
||||
@@ -46,6 +47,7 @@ test.describe("Tournament streams", () => {
|
||||
url: tournamentAdminPage(tournamentId),
|
||||
});
|
||||
|
||||
await page.getByRole("tab", { name: "Stream" }).click();
|
||||
await expect(page.getByLabel("Twitch accounts")).toHaveValue(
|
||||
"test_cast_stream,another_cast",
|
||||
);
|
||||
@@ -114,6 +116,7 @@ test.describe("Tournament streams", () => {
|
||||
page,
|
||||
url: tournamentAdminPage(tournamentId),
|
||||
});
|
||||
await page.getByRole("tab", { name: "Stream" }).click();
|
||||
await page.getByLabel("Twitch accounts").fill("test_cast_stream");
|
||||
await submit(page, "save-cast-twitch-accounts-button");
|
||||
|
||||
|
||||
@@ -76,6 +76,10 @@
|
||||
"generator.error": "Ændringer, som du har lavet, blev ikke gemt, da turneringen nu er begyndt. ",
|
||||
"teams.mapsPickedStatus": "Status for banevalg",
|
||||
"admin.download": "Hent liste over deltagere",
|
||||
"admin.tab.teams": "",
|
||||
"admin.tab.staff": "",
|
||||
"admin.tab.stream": "",
|
||||
"admin.tab.brackets": "",
|
||||
"admin.actions.CHANGE_TEAM_OWNER": "Ændr holdkaptajn",
|
||||
"admin.actions.CHANGE_TEAM_NAME": "Ændr holdnavn",
|
||||
"admin.actions.CHECK_IN": "Tjek ind",
|
||||
|
||||
@@ -76,6 +76,10 @@
|
||||
"generator.error": "Die Änderungen wurden nicht gespeichert, weil das Turnier bereits begonnen hat",
|
||||
"teams.mapsPickedStatus": "Status ausgewählter Arenen",
|
||||
"admin.download": "Teilnehmerliste herunterladen",
|
||||
"admin.tab.teams": "",
|
||||
"admin.tab.staff": "",
|
||||
"admin.tab.stream": "",
|
||||
"admin.tab.brackets": "",
|
||||
"admin.actions.CHANGE_TEAM_OWNER": "Kapitän ändern",
|
||||
"admin.actions.CHANGE_TEAM_NAME": "Teamnamen ändern",
|
||||
"admin.actions.CHECK_IN": "Einchecken",
|
||||
|
||||
@@ -76,6 +76,10 @@
|
||||
"generator.error": "Changes you made weren't saved since the tournament started",
|
||||
"teams.mapsPickedStatus": "Maps picked status",
|
||||
"admin.download": "Download participants",
|
||||
"admin.tab.teams": "Teams",
|
||||
"admin.tab.staff": "Staff",
|
||||
"admin.tab.stream": "Stream",
|
||||
"admin.tab.brackets": "Brackets",
|
||||
"admin.actions.CHANGE_TEAM_OWNER": "Change captain",
|
||||
"admin.actions.CHANGE_TEAM_NAME": "Change team name",
|
||||
"admin.actions.CHECK_IN": "Check in",
|
||||
|
||||
@@ -77,6 +77,10 @@
|
||||
"generator.error": "Los cambios no fueron guardados ya que el torneo comenzó",
|
||||
"teams.mapsPickedStatus": "Estado de mapas elegidos",
|
||||
"admin.download": "Descarga participantes",
|
||||
"admin.tab.teams": "",
|
||||
"admin.tab.staff": "",
|
||||
"admin.tab.stream": "",
|
||||
"admin.tab.brackets": "",
|
||||
"admin.actions.CHANGE_TEAM_OWNER": "Cambiar capitán",
|
||||
"admin.actions.CHANGE_TEAM_NAME": "Cambiar nombre de equipo",
|
||||
"admin.actions.CHECK_IN": "Check in",
|
||||
|
||||
@@ -77,6 +77,10 @@
|
||||
"generator.error": "Cambios no fueron guardados ya que el torneo comenzó",
|
||||
"teams.mapsPickedStatus": "Estado de escenarios elegidos",
|
||||
"admin.download": "Descarga participantes",
|
||||
"admin.tab.teams": "",
|
||||
"admin.tab.staff": "",
|
||||
"admin.tab.stream": "",
|
||||
"admin.tab.brackets": "",
|
||||
"admin.actions.CHANGE_TEAM_OWNER": "Cambiar capitán",
|
||||
"admin.actions.CHANGE_TEAM_NAME": "Cambiar nombre de equipo",
|
||||
"admin.actions.CHECK_IN": "Check in",
|
||||
|
||||
@@ -77,6 +77,10 @@
|
||||
"generator.error": "Les modifications apportées n'ont pas été enregistrées depuis le début du tournoi",
|
||||
"teams.mapsPickedStatus": "Statut des stages sélectionnés",
|
||||
"admin.download": "Télécharger les participants",
|
||||
"admin.tab.teams": "",
|
||||
"admin.tab.staff": "",
|
||||
"admin.tab.stream": "",
|
||||
"admin.tab.brackets": "",
|
||||
"admin.actions.CHANGE_TEAM_OWNER": "Changer le capitaine",
|
||||
"admin.actions.CHANGE_TEAM_NAME": "",
|
||||
"admin.actions.CHECK_IN": "S'enregister",
|
||||
|
||||
@@ -77,6 +77,10 @@
|
||||
"generator.error": "Les modifications apportées n'ont pas été enregistrées depuis le début du tournoi",
|
||||
"teams.mapsPickedStatus": "Statut des stages sélectionnés",
|
||||
"admin.download": "Télécharger les participants",
|
||||
"admin.tab.teams": "",
|
||||
"admin.tab.staff": "",
|
||||
"admin.tab.stream": "",
|
||||
"admin.tab.brackets": "",
|
||||
"admin.actions.CHANGE_TEAM_OWNER": "Changer le capitaine",
|
||||
"admin.actions.CHANGE_TEAM_NAME": "Changer le nom de l'équipe ",
|
||||
"admin.actions.CHECK_IN": "S'enregister",
|
||||
|
||||
@@ -77,6 +77,10 @@
|
||||
"generator.error": "שינויים שביצעת לא נשמרו מאז תחילת הטורניר",
|
||||
"teams.mapsPickedStatus": "סטטוס בחירת המפות",
|
||||
"admin.download": "הורדת משתתפים",
|
||||
"admin.tab.teams": "",
|
||||
"admin.tab.staff": "",
|
||||
"admin.tab.stream": "",
|
||||
"admin.tab.brackets": "",
|
||||
"admin.actions.CHANGE_TEAM_OWNER": "שינוי קפטן",
|
||||
"admin.actions.CHANGE_TEAM_NAME": "",
|
||||
"admin.actions.CHECK_IN": "קבלה",
|
||||
|
||||
@@ -77,6 +77,10 @@
|
||||
"generator.error": "I cambiamenti che hai fatto non sono stati salvati visto che il torneo è già cominciato.",
|
||||
"teams.mapsPickedStatus": "Stato scenari selezionati",
|
||||
"admin.download": "Download partecipanti",
|
||||
"admin.tab.teams": "",
|
||||
"admin.tab.staff": "",
|
||||
"admin.tab.stream": "",
|
||||
"admin.tab.brackets": "",
|
||||
"admin.actions.CHANGE_TEAM_OWNER": "Cambia capitano",
|
||||
"admin.actions.CHANGE_TEAM_NAME": "Cambia nome team",
|
||||
"admin.actions.CHECK_IN": "Check-in",
|
||||
|
||||
@@ -74,6 +74,10 @@
|
||||
"generator.error": "トーナメントが開始済みなので変更は保存されませんでした。",
|
||||
"teams.mapsPickedStatus": "マップの選択状況",
|
||||
"admin.download": "参加者リストをダウンロード",
|
||||
"admin.tab.teams": "",
|
||||
"admin.tab.staff": "",
|
||||
"admin.tab.stream": "",
|
||||
"admin.tab.brackets": "",
|
||||
"admin.actions.CHANGE_TEAM_OWNER": "キャプテンを変更",
|
||||
"admin.actions.CHANGE_TEAM_NAME": "チーム名変更",
|
||||
"admin.actions.CHECK_IN": "チェックイン",
|
||||
|
||||
@@ -74,6 +74,10 @@
|
||||
"generator.error": "",
|
||||
"teams.mapsPickedStatus": "",
|
||||
"admin.download": "",
|
||||
"admin.tab.teams": "",
|
||||
"admin.tab.staff": "",
|
||||
"admin.tab.stream": "",
|
||||
"admin.tab.brackets": "",
|
||||
"admin.actions.CHANGE_TEAM_OWNER": "",
|
||||
"admin.actions.CHANGE_TEAM_NAME": "",
|
||||
"admin.actions.CHECK_IN": "",
|
||||
|
||||
@@ -76,6 +76,10 @@
|
||||
"generator.error": "",
|
||||
"teams.mapsPickedStatus": "",
|
||||
"admin.download": "",
|
||||
"admin.tab.teams": "",
|
||||
"admin.tab.staff": "",
|
||||
"admin.tab.stream": "",
|
||||
"admin.tab.brackets": "",
|
||||
"admin.actions.CHANGE_TEAM_OWNER": "",
|
||||
"admin.actions.CHANGE_TEAM_NAME": "",
|
||||
"admin.actions.CHECK_IN": "",
|
||||
|
||||
@@ -78,6 +78,10 @@
|
||||
"generator.error": "Zmiany nie zostały zapisane z powodu rozpoczęcia turnieju",
|
||||
"teams.mapsPickedStatus": "Status wybranych map",
|
||||
"admin.download": "Zapisz uczestników",
|
||||
"admin.tab.teams": "",
|
||||
"admin.tab.staff": "",
|
||||
"admin.tab.stream": "",
|
||||
"admin.tab.brackets": "",
|
||||
"admin.actions.CHANGE_TEAM_OWNER": "",
|
||||
"admin.actions.CHANGE_TEAM_NAME": "",
|
||||
"admin.actions.CHECK_IN": "",
|
||||
|
||||
@@ -77,6 +77,10 @@
|
||||
"generator.error": "Mudanças que você fez não foram salvas uma vez que o torneio já começou",
|
||||
"teams.mapsPickedStatus": "Status dos mapas escolhidos",
|
||||
"admin.download": "Download dos participantes",
|
||||
"admin.tab.teams": "",
|
||||
"admin.tab.staff": "",
|
||||
"admin.tab.stream": "",
|
||||
"admin.tab.brackets": "",
|
||||
"admin.actions.CHANGE_TEAM_OWNER": "Mudar capitão",
|
||||
"admin.actions.CHANGE_TEAM_NAME": "Mudar nome do time",
|
||||
"admin.actions.CHECK_IN": "Check-in",
|
||||
|
||||
@@ -78,6 +78,10 @@
|
||||
"generator.error": "Ваши изменения не были сохранены так как турнир начался",
|
||||
"teams.mapsPickedStatus": "Статус выбранных карт",
|
||||
"admin.download": "Скачать участников",
|
||||
"admin.tab.teams": "",
|
||||
"admin.tab.staff": "",
|
||||
"admin.tab.stream": "",
|
||||
"admin.tab.brackets": "",
|
||||
"admin.actions.CHANGE_TEAM_OWNER": "Изменить капитана",
|
||||
"admin.actions.CHANGE_TEAM_NAME": "Изменить имя команды",
|
||||
"admin.actions.CHECK_IN": "Зарегистрировать",
|
||||
|
||||
@@ -74,6 +74,10 @@
|
||||
"generator.error": "由于比赛已经开始,您所更改的内容无法保存",
|
||||
"teams.mapsPickedStatus": "地图的选择情况",
|
||||
"admin.download": "下载参赛者",
|
||||
"admin.tab.teams": "",
|
||||
"admin.tab.staff": "",
|
||||
"admin.tab.stream": "",
|
||||
"admin.tab.brackets": "",
|
||||
"admin.actions.CHANGE_TEAM_OWNER": "变更队长",
|
||||
"admin.actions.CHANGE_TEAM_NAME": "变更队名",
|
||||
"admin.actions.CHECK_IN": "登入",
|
||||
|
||||
Reference in New Issue
Block a user