Fix match being locked giving no feedback to users before server error

This commit is contained in:
Kalle 2026-07-06 20:21:22 +03:00
parent d5701d6282
commit ac5a9e071d
21 changed files with 81 additions and 1 deletions

View File

@ -15,7 +15,7 @@ import {
} from "~/features/tournament/tournament-utils";
import type * as Progression from "~/features/tournament-bracket/core/Progression";
import type { TournamentManagerDataSet } from "~/modules/brackets-manager/types";
import type { Match, Stage } from "~/modules/brackets-model";
import { type Match, type Stage, Status } from "~/modules/brackets-model";
import type { ModeShort } from "~/modules/in-game-lists/types";
import { isAdmin } from "~/modules/permissions/utils";
import {
@ -798,6 +798,11 @@ export class Tournament {
// match didn't start yet
if (!match.opponent1 || !match.opponent2) return false;
// waiting for teams to finish their previous matches
if (match.status === Status.Locked || match.status === Status.Waiting) {
return false;
}
const matchIsOver =
match.opponent1.result === "win" || match.opponent2.result === "win";

View File

@ -77,6 +77,12 @@ export const action: ActionFunction = async ({ params, request }) => {
(p) => p.id === user?.id,
);
errorToastIfFalsy(
match.status !== TournamentMatchStatus.Locked &&
match.status !== TournamentMatchStatus.Waiting,
"Match is locked, waiting for teams to finish their previous matches",
);
errorToastIfFalsy(
canReportTournamentScore({
match,

View File

@ -55,6 +55,7 @@ export function TournamentMatchBanner({
currentMap,
teamsMissingActiveRoster,
matchIsLocked,
waitingForPreviousMatch,
joinPool,
joinPass,
teams,
@ -156,6 +157,16 @@ export function TournamentMatchBanner({
header={t("tournament:match.waitingForTeams.header")}
subtitle={t("tournament:match.waitingForTeams.subtitle")}
/>
) : waitingForPreviousMatch ? (
<IconBanner
icon={<Hourglass size={32} />}
header={t("tournament:match.waitingForPrevious.header")}
subtitle={t("tournament:match.waitingForPrevious.subtitle")}
screenLegal={screenLegal}
joinPool={joinPool}
joinPass={joinPass}
host={host}
/>
) : teamsMissingActiveRoster.length > 0 ? (
<IconBanner
icon={<Users size={32} />}

View File

@ -1,6 +1,7 @@
import * as React from "react";
import { TAB_KEYS } from "~/components/match-page/MatchTabs";
import { resolveRoomPass } from "~/components/match-page/utils";
import { TournamentMatchStatus } from "~/db/tables";
import { useUser } from "~/features/auth/core/user";
import { useTournament } from "~/features/tournament/routes/to.$id";
import { isLeagueRoundLocked } from "~/features/tournament/tournament-utils";
@ -32,6 +33,7 @@ type MatchPageContextValue = {
turnOfResult: ReturnType<typeof PickBan.turnOf>;
isPickBanStep: boolean;
matchIsLocked: boolean;
waitingForPreviousMatch: boolean;
joinPool: string | null;
joinPass: string | null;
};
@ -99,6 +101,10 @@ export function MatchPageProvider({
scores,
});
const waitingForPreviousMatch =
data.match.status === TournamentMatchStatus.Locked ||
data.match.status === TournamentMatchStatus.Waiting;
const joinInfo = resolveJoinInfo({ tournament, data, teams });
const tabs = resolveVisibleTabs({
@ -117,6 +123,7 @@ export function MatchPageProvider({
tournament.isOrganizerOrStreamer(user) && !tournament.ctx.isFinalized,
leagueRoundLocked: isLeagueRoundLocked(tournament, data.match.roundId),
lockedForCast,
waitingForPreviousMatch,
});
return (
@ -132,6 +139,7 @@ export function MatchPageProvider({
turnOfResult,
isPickBanStep,
matchIsLocked: lockedForCast,
waitingForPreviousMatch,
joinPool: joinInfo?.pool ?? null,
joinPass: joinInfo?.pass ?? null,
}}
@ -160,6 +168,7 @@ function resolveVisibleTabs({
isAdminEligible,
leagueRoundLocked,
lockedForCast,
waitingForPreviousMatch,
}: {
canReportScore: boolean;
canReportWeapons: boolean;
@ -171,11 +180,13 @@ function resolveVisibleTabs({
isAdminEligible: boolean;
leagueRoundLocked: boolean;
lockedForCast: boolean;
waitingForPreviousMatch: boolean;
}): MatchTabKey[] {
const tabs: MatchTabKey[] = [TAB_KEYS.ROSTERS];
if (
!leagueRoundLocked &&
!waitingForPreviousMatch &&
(isPickBanStep ||
(canReportScore &&
hasCurrentMap &&

View File

@ -246,6 +246,21 @@ describe("Tournament match page", () => {
});
});
describe("locked match", () => {
it("should return error when reporting score for a match waiting on previous matches", async () => {
await setActiveRosterAction();
await db
.updateTable("TournamentMatch")
.set({ status: 0 })
.where("id", "=", 1)
.execute();
const res = await reportScoreAction({ position: 0 });
assertResponseErrored(res, "Match is locked");
});
});
describe("BYE matches", () => {
it("should 404 when accessing a BYE match", async () => {
await db

View File

@ -171,6 +171,8 @@
"match.locked.subtitle": "",
"match.waitingForTeams.header": "",
"match.waitingForTeams.subtitle": "",
"match.waitingForPrevious.header": "",
"match.waitingForPrevious.subtitle": "",
"match.tbd": "",
"match.action.undoLastScore": "Annuller sidste score",
"match.action.reopenMatch": "Genåbn kamp",

View File

@ -171,6 +171,8 @@
"match.locked.subtitle": "",
"match.waitingForTeams.header": "",
"match.waitingForTeams.subtitle": "",
"match.waitingForPrevious.header": "",
"match.waitingForPrevious.subtitle": "",
"match.tbd": "",
"match.action.undoLastScore": "Letztes Ergebnis widerrufen",
"match.action.reopenMatch": "Match erneut öffnen",

View File

@ -171,6 +171,8 @@
"match.locked.subtitle": "Please wait for staff to unlock",
"match.waitingForTeams.header": "Waiting for teams",
"match.waitingForTeams.subtitle": "Teams will be resolved from earlier matches",
"match.waitingForPrevious.header": "Previous match ongoing",
"match.waitingForPrevious.subtitle": "Match will be reportable when both teams are ready to play",
"match.tbd": "TBD",
"match.action.undoLastScore": "Undo last score",
"match.action.reopenMatch": "Reopen match",

View File

@ -173,6 +173,8 @@
"match.locked.subtitle": "",
"match.waitingForTeams.header": "",
"match.waitingForTeams.subtitle": "",
"match.waitingForPrevious.header": "",
"match.waitingForPrevious.subtitle": "",
"match.tbd": "",
"match.action.undoLastScore": "Anular resultado previo",
"match.action.reopenMatch": "Reabrir partido",

View File

@ -173,6 +173,8 @@
"match.locked.subtitle": "",
"match.waitingForTeams.header": "",
"match.waitingForTeams.subtitle": "",
"match.waitingForPrevious.header": "",
"match.waitingForPrevious.subtitle": "",
"match.tbd": "",
"match.action.undoLastScore": "Anular resultado previo",
"match.action.reopenMatch": "Reabrir partido",

View File

@ -173,6 +173,8 @@
"match.locked.subtitle": "",
"match.waitingForTeams.header": "",
"match.waitingForTeams.subtitle": "",
"match.waitingForPrevious.header": "",
"match.waitingForPrevious.subtitle": "",
"match.tbd": "",
"match.action.undoLastScore": "Annuler le dernier score",
"match.action.reopenMatch": "Rouvrir le match",

View File

@ -173,6 +173,8 @@
"match.locked.subtitle": "",
"match.waitingForTeams.header": "",
"match.waitingForTeams.subtitle": "",
"match.waitingForPrevious.header": "",
"match.waitingForPrevious.subtitle": "",
"match.tbd": "",
"match.action.undoLastScore": "Annuler le dernier score",
"match.action.reopenMatch": "Rouvrir le match",

View File

@ -173,6 +173,8 @@
"match.locked.subtitle": "",
"match.waitingForTeams.header": "",
"match.waitingForTeams.subtitle": "",
"match.waitingForPrevious.header": "",
"match.waitingForPrevious.subtitle": "",
"match.tbd": "",
"match.action.undoLastScore": "בטלו את התוצאה האחרונה",
"match.action.reopenMatch": "פתיחה מחדש של הקרב",

View File

@ -173,6 +173,8 @@
"match.locked.subtitle": "",
"match.waitingForTeams.header": "",
"match.waitingForTeams.subtitle": "",
"match.waitingForPrevious.header": "",
"match.waitingForPrevious.subtitle": "",
"match.tbd": "",
"match.action.undoLastScore": "Annulla ultimo punteggio",
"match.action.reopenMatch": "Riapri match",

View File

@ -167,6 +167,8 @@
"match.locked.subtitle": "",
"match.waitingForTeams.header": "",
"match.waitingForTeams.subtitle": "",
"match.waitingForPrevious.header": "",
"match.waitingForPrevious.subtitle": "",
"match.tbd": "",
"match.action.undoLastScore": "最後のスコアをやりなおす",
"match.action.reopenMatch": "対戦を再度開く",

View File

@ -167,6 +167,8 @@
"match.locked.subtitle": "",
"match.waitingForTeams.header": "",
"match.waitingForTeams.subtitle": "",
"match.waitingForPrevious.header": "",
"match.waitingForPrevious.subtitle": "",
"match.tbd": "",
"match.action.undoLastScore": "",
"match.action.reopenMatch": "",

View File

@ -171,6 +171,8 @@
"match.locked.subtitle": "",
"match.waitingForTeams.header": "",
"match.waitingForTeams.subtitle": "",
"match.waitingForPrevious.header": "",
"match.waitingForPrevious.subtitle": "",
"match.tbd": "",
"match.action.undoLastScore": "",
"match.action.reopenMatch": "",

View File

@ -175,6 +175,8 @@
"match.locked.subtitle": "",
"match.waitingForTeams.header": "",
"match.waitingForTeams.subtitle": "",
"match.waitingForPrevious.header": "",
"match.waitingForPrevious.subtitle": "",
"match.tbd": "",
"match.action.undoLastScore": "",
"match.action.reopenMatch": "",

View File

@ -173,6 +173,8 @@
"match.locked.subtitle": "",
"match.waitingForTeams.header": "",
"match.waitingForTeams.subtitle": "",
"match.waitingForPrevious.header": "",
"match.waitingForPrevious.subtitle": "",
"match.tbd": "",
"match.action.undoLastScore": "Desfazer última pontuação",
"match.action.reopenMatch": "Reabrir partida",

View File

@ -175,6 +175,8 @@
"match.locked.subtitle": "",
"match.waitingForTeams.header": "",
"match.waitingForTeams.subtitle": "",
"match.waitingForPrevious.header": "",
"match.waitingForPrevious.subtitle": "",
"match.tbd": "",
"match.action.undoLastScore": "Отменить последний результат",
"match.action.reopenMatch": "Открыть матч заново",

View File

@ -169,6 +169,8 @@
"match.locked.subtitle": "请等待工作人员解锁",
"match.waitingForTeams.header": "正在等待队伍",
"match.waitingForTeams.subtitle": "队伍将由先前的对局结果决定",
"match.waitingForPrevious.header": "",
"match.waitingForPrevious.subtitle": "",
"match.tbd": "待定",
"match.action.undoLastScore": "撤销上一局比分",
"match.action.reopenMatch": "重新开启对局",