Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions convex/_generated/api.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ import type * as _model_tournaments_queries_getTournamentOpenRound from "../_mod
import type * as _model_tournaments_queries_getTournamentRankings from "../_model/tournaments/queries/getTournamentRankings.js";
import type * as _model_tournaments_queries_getTournaments from "../_model/tournaments/queries/getTournaments.js";
import type * as _model_tournaments_queries_getTournamentsByStatus from "../_model/tournaments/queries/getTournamentsByStatus.js";
import type * as _model_tournaments_queries_getTournamentsByUser from "../_model/tournaments/queries/getTournamentsByUser.js";
import type * as _model_users__helpers_checkUserAuth from "../_model/users/_helpers/checkUserAuth.js";
import type * as _model_users__helpers_checkUserTournamentForcedName from "../_model/users/_helpers/checkUserTournamentForcedName.js";
import type * as _model_users__helpers_checkUserTournamentRelationship from "../_model/users/_helpers/checkUserTournamentRelationship.js";
Expand Down Expand Up @@ -318,6 +319,7 @@ declare const fullApi: ApiFromModules<{
"_model/tournaments/queries/getTournamentRankings": typeof _model_tournaments_queries_getTournamentRankings;
"_model/tournaments/queries/getTournaments": typeof _model_tournaments_queries_getTournaments;
"_model/tournaments/queries/getTournamentsByStatus": typeof _model_tournaments_queries_getTournamentsByStatus;
"_model/tournaments/queries/getTournamentsByUser": typeof _model_tournaments_queries_getTournamentsByUser;
"_model/users/_helpers/checkUserAuth": typeof _model_users__helpers_checkUserAuth;
"_model/users/_helpers/checkUserTournamentForcedName": typeof _model_users__helpers_checkUserTournamentForcedName;
"_model/users/_helpers/checkUserTournamentRelationship": typeof _model_users__helpers_checkUserTournamentRelationship;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { deepenTournamentPairing, TournamentPairingDeep } from '../_helpers/deep

export const getActiveTournamentPairingsByUserArgs = v.object({
userId: v.id('users'),
round: v.optional(v.number()),
});

/**
Expand Down Expand Up @@ -37,8 +38,10 @@ export const getActiveTournamentPairingsByUser = async (
return (await Promise.all(tournamentPairings.map(async (tournamentPairing) => {
const tournament = activeTournaments.find((activeTournament) => activeTournament._id === tournamentPairing.tournamentId);

// If pairing belongs to an inactive tournament OR an active tournament but not the current round, exclude it:
if (!tournament || tournament.currentRound !== tournamentPairing.round) {
const round = args.round ?? tournament?.currentRound;

// If pairing belongs to an inactive tournament OR an active tournament but not the correct round, exclude it:
if (!tournament || round !== tournamentPairing.round) {
return null;
}

Expand Down
5 changes: 5 additions & 0 deletions convex/_model/tournaments/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,13 @@ export {
} from './queries/getTournamentRankings';
export {
getTournaments,
getTournamentsArgs,
} from './queries/getTournaments';
export {
getTournamentsByStatus,
getTournamentsByStatusArgs,
} from './queries/getTournamentsByStatus';
export {
getTournamentsByUser,
getTournamentsByUserArgs,
} from './queries/getTournamentsByUser';
20 changes: 19 additions & 1 deletion convex/_model/tournaments/queries/getTournaments.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,38 @@
import { Infer, v } from 'convex/values';

import { QueryCtx } from '../../../_generated/server';
import { notNullOrUndefined } from '../../common/_helpers/notNullOrUndefined';
import { checkTournamentVisibility } from '../_helpers/checkTournamentVisibility';
import { deepenTournament, TournamentDeep } from '../_helpers/deepenTournament';

export const getTournamentsArgs = v.object({
startsAfter: v.optional(v.union(v.string(), v.number())),
});

/**
* Gets an array of ALL deep Tournaments.
*
* @param ctx - Convex query context
* @param args - Convex query args
* @param args.startsAfter - Filter for tournaments starting after this date
* @returns An array of deep Tournaments
*/
export const getTournaments = async (
ctx: QueryCtx,
args: Infer<typeof getTournamentsArgs>,
): Promise<TournamentDeep[]> => {

const tournaments = await ctx.db.query('tournaments').collect();
const deepTournaments = await Promise.all(
tournaments.map(async (tournament) => {
tournaments.filter((tournament) => {
if (args.startsAfter) {
const startsAfter = typeof args.startsAfter === 'string' ? Date.parse(args.startsAfter) : args.startsAfter;
if (Date.parse(tournament.startsAt) < startsAfter) {
return false;
}
}
return true;
}).map(async (tournament) => {
if (await checkTournamentVisibility(ctx, tournament)) {
return await deepenTournament(ctx, tournament);
}
Expand Down
52 changes: 52 additions & 0 deletions convex/_model/tournaments/queries/getTournamentsByUser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { Infer, v } from 'convex/values';

import { QueryCtx } from '../../../_generated/server';
import { tournamentStatus } from '../../../common/tournamentStatus';
import { notNullOrUndefined } from '../../common/_helpers/notNullOrUndefined';
import { checkTournamentVisibility } from '../_helpers/checkTournamentVisibility';
import { deepenTournament, TournamentDeep } from '../_helpers/deepenTournament';

export const getTournamentsByUserArgs = v.object({
userId: v.id('users'),
status: v.optional(tournamentStatus),
});

/**
* Gets an array of deep tournaments which were attended by a given user.
*
* @param ctx - Convex query context
* @param args - Convex query args
* @param args.status - The user ID to filter by
* @param args.status - Tournament status to filter by
* @returns An array of deep tournaments
*/
export const getTournamentsByUser = async (
ctx: QueryCtx,
args: Infer<typeof getTournamentsByUserArgs>,
): Promise<TournamentDeep[]> => {
const tournaments = await ctx.db.query('tournaments').collect();
const deepTournaments = await Promise.all(
tournaments.map(async (tournament) => {
if (await checkTournamentVisibility(ctx, tournament)) {
return await deepenTournament(ctx, tournament);
}
return null;
}),
);
return deepTournaments.filter((tournament): tournament is TournamentDeep => {
if (!notNullOrUndefined(tournament)) {
return false;
}
const userIds = [
...tournament.organizerUserIds,
...tournament.activePlayerUserIds,
];
if (!userIds.includes(args.userId)) {
return false;
}
if (args.status && tournament.status !== args.status) {
return false;
}
return true;
});
};
17 changes: 11 additions & 6 deletions convex/_model/utils/createTestTournamentMatchResults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,12 +76,17 @@ export const createTestTournamentMatchResults = async (
playerData.player1Placeholder = 'Bye';
}

const matchResultId = await ctx.db.insert('matchResults', createMockFowV4MatchResultData({
...playerData,
tournamentPairingId: pairing._id,
gameSystemConfig: tournament.gameSystemConfig,
gameSystemId: tournament.gameSystemId,
}));
// TODO: Replace with actual call to the create mutation
const matchResultId = await ctx.db.insert('matchResults', {
...createMockFowV4MatchResultData({
...playerData,
tournamentPairingId: pairing._id,

gameSystemConfig: tournament.gameSystemConfig,
gameSystemId: tournament.gameSystemId,
}),
tournamentId: tournament._id,
});

if (matchResultId) {
matchResultIds.push(matchResultId);
Expand Down
12 changes: 11 additions & 1 deletion convex/tournaments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,20 @@ export const getTournament = query({
});

export const getTournaments = query({
args: {},
args: model.getTournamentsArgs,
handler: model.getTournaments,
});

export const getTournamentsByStatus = query({
args: model.getTournamentsByStatusArgs,
handler: model.getTournamentsByStatus,
});

export const getTournamentsByUser = query({
args: model.getTournamentsByUserArgs,
handler: model.getTournamentsByUser,
});

export const getTournamentOpenRound = query({
args: model.getTournamentOpenRoundArgs,
handler: model.getTournamentOpenRound,
Expand Down
4 changes: 4 additions & 0 deletions src/components/CheckInMatchDialog/CheckInMatchDialog.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { ReactNode, useState } from 'react';

import { TournamentPairingId } from '~/api';
import { FowV4MatchResultForm } from '~/components/FowV4MatchResultForm';
import { Dialog } from '~/components/generic/Dialog';
import { ScrollArea } from '~/components/generic/ScrollArea';
Expand All @@ -9,11 +10,13 @@ import styles from './CheckInMatchDialog.module.scss';
export interface CheckInMatchDialogProps {
children?: ReactNode;
trigger?: ReactNode;
tournamentPairingId?: TournamentPairingId;
}

export const CheckInMatchDialog = ({
children,
trigger,
tournamentPairingId,
}: CheckInMatchDialogProps): JSX.Element => {
const [open, setOpen] = useState<boolean>(false);
return (
Expand All @@ -31,6 +34,7 @@ export const CheckInMatchDialog = ({
<FowV4MatchResultForm
id="fow-v4-match-result-form"
className={styles.Form}
tournamentPairingId={tournamentPairingId}
onSuccess={() => setOpen(false)}
/>
</ScrollArea>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
@use "/src/style/flex";
@use "/src/style/text";

.TournamentTabEmptyState {
.EmptyState {
@include flex.stretchy;
@include flex.column($xAlign: center, $yAlign: center);
@include text.ui($muted: true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,31 +6,31 @@ import {
import clsx from 'clsx';
import { Satellite } from 'lucide-react';

import styles from './TournamentTabEmptyState.module.scss';
import styles from './EmptyState.module.scss';

export interface TournamentTabEmptyStateProps {
export interface EmptyStateProps {
className?: string;
children?: ReactNode;
icon?: ReactElement;
message?: string;
}

export const TournamentTabEmptyState = ({
export const EmptyState = ({
className,
message,
icon,
children,
}: TournamentTabEmptyStateProps): JSX.Element => {
}: EmptyStateProps): JSX.Element => {
const iconProps = {
className: styles.TournamentTabEmptyState_Icon,
className: styles.EmptyState_Icon,
size: 96,
absoluteStrokeWidth: true,
strokeWidth: 4,
};
return (
<div className={clsx(styles.TournamentTabEmptyState, className)}>
<div className={clsx(styles.EmptyState, className)}>
{icon ? cloneElement(icon, iconProps) : <Satellite {...iconProps} />}
<div className={styles.TournamentTabEmptyState_Message}>
<div className={styles.EmptyState_Message}>
{message ?? 'Nothing to show yet.'}
</div>
{children && (
Expand Down
1 change: 1 addition & 0 deletions src/components/EmptyState/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { EmptyState } from './EmptyState';
6 changes: 4 additions & 2 deletions src/components/FowV4MatchResultForm/FowV4MatchResultForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,15 @@ export interface FowV4MatchResultFormProps {
id: string;
className?: string;
matchResultId?: MatchResultId;
tournamentPairingId?: TournamentPairingId;
onSuccess?: () => void;
}

export const FowV4MatchResultForm = ({
id,
className,
matchResultId,
tournamentPairingId: forcedTournamentPairingId,
onSuccess,
}: FowV4MatchResultFormProps): JSX.Element => {
const user = useAuth();
Expand All @@ -60,7 +62,7 @@ export const FowV4MatchResultForm = ({
const [
tournamentPairingId,
setTournamentPairingId,
] = useAsyncState<TournamentPairingId | 'single'>('single', matchResult?.tournamentPairingId);
] = useAsyncState<TournamentPairingId | 'single'>('single', forcedTournamentPairingId ?? matchResult?.tournamentPairingId);

const {
open: openConfirmMatchResultDialog,
Expand Down Expand Up @@ -157,7 +159,7 @@ export const FowV4MatchResultForm = ({
options={resultForOptions}
value={tournamentPairingId}
onChange={handleChangeResultFor}
disabled={!!matchResult}
disabled={!!forcedTournamentPairingId || !!matchResult}
/>
</div>
<Separator />
Expand Down
4 changes: 2 additions & 2 deletions src/components/PageWrapper/PageWrapper.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,13 @@ $header-height: 2.5rem;

height: inherit;
margin: 0 auto;
padding: 1rem var(--modal-outer-gutter);
padding: 1rem var(--modal-outer-gutter) var(--modal-outer-gutter);
}

&_Header {
@include flex.row;

height: $header-height;
min-height: $header-height;
}

&_Body {
Expand Down
9 changes: 2 additions & 7 deletions src/components/TournamentTimer/TournamentTimer.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,8 @@
--digit-width: 1.5rem;

@include flex.column($gap: 0);
@include borders.normal;
@include shadows.elevated;
@include corners.normal;

min-width: 17.5rem; // Hardcoded for simplicity
height: min-content;
min-height: 8rem; // Hardcoded for simplicity
background-color: var(--card-bg);

&_TimeSection {
Expand All @@ -39,7 +34,7 @@
grid-template-rows: 1rem var(--digit-height) 1rem;
gap: 1rem;

padding: calc(1rem - var(--border-width));
padding: var(--container-padding-x);

color: var(--text-color-default);
}
Expand Down Expand Up @@ -130,7 +125,7 @@
@include flex.row($gap: 0.25rem, $xAlign: center);
@include borders.normal($side: top);

padding: 0.5rem;
padding: 0.5rem var(--container-padding-x);
}

&_LoadingIcon {
Expand Down
2 changes: 1 addition & 1 deletion src/components/generic/Table/Table.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@
overflow: hidden;
display: flex;
align-items: center;
min-width: 2.5rem;
min-width: 1.5rem;

&[data-align="left"] {
justify-content: start;
Expand Down
2 changes: 1 addition & 1 deletion src/components/generic/Table/Table.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export type ColumnDef<T extends RowData> = {
label?: string;
renderCell?: (row: T, index: number) => ReactNode;
renderHeader?: () => ReactNode;
width?: number;
width?: number | 'auto';
};

export type Row<T extends RowData> = [T, number];
Loading