Merge branch 'main' into new-match-page

This commit is contained in:
Kalle 2026-05-04 17:24:47 +03:00
commit f63ac7ec3c
8 changed files with 197 additions and 7 deletions

View File

@ -39,6 +39,8 @@ export const action = async ({ request }: ActionFunctionArgs) => {
errorToast(`Migration failed. Reason: ${errorMessage}`);
}
await refreshBannedCache();
message = "Account migrated";
break;
} catch (err) {

View File

@ -65,7 +65,7 @@ export default function NewArtPage() {
const submitButtonDisabled = () => {
if (fetcher.state !== "idle") return true;
return !img && !data.art;
return (!img || !smallImg) && !data.art;
};
if (!isArtist) {
@ -121,7 +121,6 @@ function ImageUpload({
<input
id={id}
type="file"
name="img"
accept="image/png, image/jpeg, image/jpg, image/webp"
onChange={(e) => {
const uploadedFile = e.target.files?.[0];

View File

@ -6,14 +6,19 @@ import {
} from "~/features/auth/core/authenticator.server";
import { authSessionStorage } from "~/features/auth/core/session.server";
import type { Nullish } from "~/utils/types";
import { userIsBanned } from "../core/banned.server";
import { refreshBannedCache, userIsBanned } from "../core/banned.server";
export const loader = async ({ request }: LoaderFunctionArgs) => {
const userId = await getUserIdEvenIfBanned(request);
if (!userId || !userIsBanned(userId)) return redirect("/");
const bannedStatus = (await AdminRepository.allBannedUsers()).get(userId)!;
const bannedStatus = (await AdminRepository.allBannedUsers()).get(userId);
if (!bannedStatus) {
await refreshBannedCache();
return redirect("/");
}
return {
banned: bannedStatus.banned,

View File

@ -257,9 +257,9 @@ export async function ownerIdById(buildId: number) {
.selectFrom("Build")
.select("ownerId")
.where("id", "=", buildId)
.executeTakeFirstOrThrow();
.executeTakeFirst();
return result.ownerId;
return result?.ownerId ?? null;
}
export async function abilityPointAverages(weaponSplId?: MainWeaponId | null) {

View File

@ -0,0 +1,178 @@
import { afterEach, beforeEach, describe, expect, test } from "vitest";
import { db } from "~/db/sql";
import { dbInsertUsers, dbReset } from "~/utils/Test";
import * as TournamentMatchRepository from "./TournamentMatchRepository.server";
const createTournament = () =>
db
.insertInto("Tournament")
.values({
mapPickingStyle: "TO",
settings: JSON.stringify({ bracketProgression: [] }),
})
.returning("id")
.executeTakeFirstOrThrow();
const createTeam = (tournamentId: number, name: string) =>
db
.insertInto("TournamentTeam")
.values({
tournamentId,
name,
inviteCode: `inv-${tournamentId}-${name}`,
})
.returning("id")
.executeTakeFirstOrThrow();
const createStage = (tournamentId: number, name: string, number: number) =>
db
.insertInto("TournamentStage")
.values({
tournamentId,
name,
number,
type: "double_elimination",
settings: "{}",
})
.returning("id")
.executeTakeFirstOrThrow();
const createGroup = (stageId: number, number: number) =>
db
.insertInto("TournamentGroup")
.values({ stageId, number })
.returning("id")
.executeTakeFirstOrThrow();
const createRound = (stageId: number, groupId: number, number: number) =>
db
.insertInto("TournamentRound")
.values({
stageId,
groupId,
number,
maps: JSON.stringify({ count: 3, type: "BEST_OF" }),
})
.returning("id")
.executeTakeFirstOrThrow();
const createMatch = async (args: {
stageId: number;
groupId: number;
roundId: number;
number: number;
teamOneId: number;
teamTwoId: number;
}) => {
const match = await db
.insertInto("TournamentMatch")
.values({
stageId: args.stageId,
groupId: args.groupId,
roundId: args.roundId,
number: args.number,
status: 4,
opponentOne: JSON.stringify({ id: args.teamOneId, score: 2 }),
opponentTwo: JSON.stringify({ id: args.teamTwoId, score: 0 }),
})
.returning("id")
.executeTakeFirstOrThrow();
await db
.insertInto("TournamentMatchGameResult")
.values({
matchId: match.id,
mode: "SZ",
number: 1,
reporterId: 1,
source: "TO",
stageId: 1,
winnerTeamId: args.teamOneId,
})
.returning("id")
.executeTakeFirstOrThrow()
.then((result) =>
db
.insertInto("TournamentMatchGameResultParticipant")
.values([
{
matchGameResultId: result.id,
userId: 1,
tournamentTeamId: args.teamOneId,
},
{
matchGameResultId: result.id,
userId: 2,
tournamentTeamId: args.teamTwoId,
},
])
.execute(),
);
return match;
};
describe("findByTournamentTeamId", () => {
beforeEach(async () => {
await dbInsertUsers(2);
});
afterEach(() => {
dbReset();
});
test("preserves stage order: matches from an earlier stage come first even when later stage has lower group numbers", async () => {
// Tournament with two stages. The first stage has a high group number
// (think: round-robin pool 8) and the second stage has group number 1
// (think: DE bracket winners). The team page should show stage 1's
// matches first, then stage 2's.
const tournament = await createTournament();
const teamA = await createTeam(tournament.id, "A");
const teamB = await createTeam(tournament.id, "B");
// Insert team members so we have someone to attribute results to
for (const userId of [1, 2]) {
await db
.insertInto("TournamentTeamMember")
.values({ tournamentTeamId: teamA.id, userId, role: "OWNER" })
.execute();
await db
.insertInto("TournamentTeamMember")
.values({ tournamentTeamId: teamB.id, userId, role: "OWNER" })
.execute();
}
const stage1 = await createStage(tournament.id, "Stage 1", 1);
const stage1Group = await createGroup(stage1.id, 8);
const stage1Round = await createRound(stage1.id, stage1Group.id, 1);
const stage1Match = await createMatch({
stageId: stage1.id,
groupId: stage1Group.id,
roundId: stage1Round.id,
number: 1,
teamOneId: teamA.id,
teamTwoId: teamB.id,
});
const stage2 = await createStage(tournament.id, "Stage 2", 2);
const stage2Group = await createGroup(stage2.id, 1);
const stage2Round = await createRound(stage2.id, stage2Group.id, 1);
const stage2Match = await createMatch({
stageId: stage2.id,
groupId: stage2Group.id,
roundId: stage2Round.id,
number: 1,
teamOneId: teamA.id,
teamTwoId: teamB.id,
});
const result = await TournamentMatchRepository.findByTournamentTeamId(
teamA.id,
);
expect(result.map((s) => s.tournamentMatchId)).toEqual([
stage1Match.id,
stage2Match.id,
]);
});
});

View File

@ -237,6 +237,7 @@ export function findByTournamentTeamId(tournamentTeamId: number) {
),
),
)
.orderBy("TournamentRound.stageId", "asc")
.orderBy("TournamentGroup.number", "asc")
.orderBy("TournamentRound.number", "asc")
.execute();

View File

@ -130,6 +130,11 @@ export default function TournamentRegisterPage() {
hour: "numeric",
day: "numeric",
month: "numeric",
year:
tournament.ctx.startTime.getFullYear() !==
new Date().getFullYear()
? "2-digit"
: undefined,
}}
/>
) : null}

View File

@ -29,7 +29,7 @@ import { useTournament } from "./to.$id";
export { loader };
export const meta: MetaFunction<typeof loader> = (args) => {
const tournamentData = (args.matches[1].data as any)
const tournamentData = JSON.parse(args.matches[1].data as any)
?.tournament as TournamentData;
if (!args.data || !tournamentData) return [];