diff --git a/frontend/public/locales/en/common.json b/frontend/public/locales/en/common.json
index 6172466ad..fa8b729ad 100644
--- a/frontend/public/locales/en/common.json
+++ b/frontend/public/locales/en/common.json
@@ -29,6 +29,8 @@
"auto_create_matches_button": "Add new matches automatically",
"back_home_nav": "Take me back to home page",
"back_to_login_nav": "Back to login page",
+ "calculate_datetime_match_label": "Calculate date and time of the match",
+ "calculate_label": "Calculate",
"checkbox_status_checked": "Checked",
"checkbox_status_unchecked": "Unchecked",
"club_choose_title": "Please choose a club",
@@ -141,6 +143,7 @@
"negative_score_validation": "Score cannot be negative",
"next_matches_badge": "Next matches",
"next_stage_button": "Next Stage",
+ "next_match_time_label": "Next match time",
"no_matches_description": "First, add matches by creating stages and stage items. Then, schedule them using the button in the topright corner.",
"no_matches_title": "No matches scheduled yet",
"no_players_title": "No players yet",
diff --git a/frontend/src/components/brackets/match.tsx b/frontend/src/components/brackets/match.tsx
index d3dfa91a2..697e9d8f7 100644
--- a/frontend/src/components/brackets/match.tsx
+++ b/frontend/src/components/brackets/match.tsx
@@ -1,4 +1,4 @@
-import { Center, Grid, UnstyledButton, useMantineTheme } from '@mantine/core';
+import { Center, Grid, Text, UnstyledButton, useMantineTheme } from '@mantine/core';
import assert from 'assert';
import React, { useState } from 'react';
import { SWRResponse } from 'swr';
@@ -13,7 +13,7 @@ import {
import { TournamentMinimal } from '../../interfaces/tournament';
import { getMatchLookup, getStageItemLookup } from '../../services/lookups';
import MatchModal from '../modals/match_modal';
-import { Time } from '../utils/datetime';
+import { DateTime } from '../utils/datetime';
import classes from './match.module.css';
export function MatchBadge({ match, theme }: { match: MatchInterface; theme: any }) {
@@ -30,10 +30,14 @@ export function MatchBadge({ match, theme }: { match: MatchInterface; theme: any
}}
>
-
- {match.court?.name} |{' '}
- {match.start_time != null ? : null}
-
+
+
+ {match.court?.name}
+
+
+ {match.start_time != null ? : null}
+
+
@@ -128,6 +132,7 @@ export default function Match({
opened={opened}
setOpened={setOpened}
dynamicSchedule={dynamicSchedule}
+ priorMatch={null}
/>
>
);
diff --git a/frontend/src/components/builder/builder.tsx b/frontend/src/components/builder/builder.tsx
index 8ca895922..796b5b69c 100644
--- a/frontend/src/components/builder/builder.tsx
+++ b/frontend/src/components/builder/builder.tsx
@@ -28,12 +28,10 @@ function StageItemInputSectionLast({
lastInList,
}: {
input: StageItemInput;
- team: TeamInterface | null;
- teamStageItem: TeamInterface | null;
+ team: TeamInterface;
+ teamStageItem: StageItemWithRounds;
lastInList: boolean;
}) {
- assert(team != null || teamStageItem != null);
-
const content = team
? team.name
: // @ts-ignore
@@ -53,7 +51,7 @@ function StageItemRow({
stageItem,
swrStagesResponse,
}: {
- teamsMap: any;
+ teamsMap: NonNullable>;
tournament: Tournament;
stageItem: StageItemWithRounds;
swrStagesResponse: SWRResponse;
@@ -70,12 +68,14 @@ function StageItemRow({
? stageItemsLookup[input.winner_from_stage_item_id]
: null;
+ assert(team != null || teamStageItem != null);
+
return (
);
diff --git a/frontend/src/components/modals/match_modal.tsx b/frontend/src/components/modals/match_modal.tsx
index e221d8e84..381c8e252 100644
--- a/frontend/src/components/modals/match_modal.tsx
+++ b/frontend/src/components/modals/match_modal.tsx
@@ -1,7 +1,20 @@
-import { Button, Center, Checkbox, Divider, Grid, Modal, NumberInput, Text } from '@mantine/core';
+import {
+ Button,
+ Center,
+ Checkbox,
+ Divider,
+ Grid,
+ Input,
+ Modal,
+ NumberInput,
+ Text,
+} from '@mantine/core';
+import { DateTimePicker } from '@mantine/dates';
import { useForm } from '@mantine/form';
+import { showNotification } from '@mantine/notifications';
+import { format, fromUnixTime, getUnixTime, parseISO } from 'date-fns';
import { useTranslation } from 'next-i18next';
-import React, { useState } from 'react';
+import React, { useMemo, useState } from 'react';
import { SWRResponse } from 'swr';
import {
@@ -15,18 +28,33 @@ import { getMatchLookup, getStageItemLookup } from '../../services/lookups';
import { deleteMatch, updateMatch } from '../../services/match';
import DeleteButton from '../buttons/delete';
+interface MatchModalBaseProps {
+ tournamentData: TournamentMinimal;
+ swrUpcomingMatchesResponse: SWRResponse | null;
+ dynamicSchedule: boolean;
+}
+
+interface MatchModalProps extends MatchModalBaseProps {
+ match: MatchInterface | null;
+ swrStagesResponse: SWRResponse;
+ setOpened: (value: boolean) => void;
+ priorMatch: MatchInterface | null;
+}
+
+/**
+ * A typical implementation for opening a match modal. Useful for other components, especially in pages.
+ */
+export type OpenMatchModalFn = (match: MatchInterface, priorMatch: MatchInterface | null) => void;
+
function MatchDeleteButton({
tournamentData,
match,
swrRoundsResponse,
swrUpcomingMatchesResponse,
dynamicSchedule,
-}: {
- tournamentData: TournamentMinimal;
+}: MatchModalBaseProps & {
match: MatchInterface;
swrRoundsResponse: SWRResponse;
- swrUpcomingMatchesResponse: SWRResponse | null;
- dynamicSchedule: boolean;
}) {
const { t } = useTranslation();
if (!dynamicSchedule) return null;
@@ -52,14 +80,8 @@ function MatchModalForm({
swrUpcomingMatchesResponse,
setOpened,
dynamicSchedule,
-}: {
- tournamentData: TournamentMinimal;
- match: MatchInterface | null;
- swrStagesResponse: SWRResponse;
- swrUpcomingMatchesResponse: SWRResponse | null;
- setOpened: any;
- dynamicSchedule: boolean;
-}) {
+ priorMatch,
+}: MatchModalProps) {
if (match == null) {
return null;
}
@@ -90,6 +112,26 @@ function MatchModalForm({
match.custom_margin_minutes != null
);
+ const [date, setDate] = useState(null);
+
+ const matchDuration = useMemo(() => {
+ const value = customDurationEnabled
+ ? form.values.custom_duration_minutes
+ : match.duration_minutes;
+ return value ?? 0;
+ }, [customDurationEnabled, form.values.custom_duration_minutes, match.duration_minutes]);
+
+ const matchMargin = useMemo(() => {
+ const value = customMarginEnabled ? form.values.custom_margin_minutes : match.margin_minutes;
+ return value ?? 0;
+ }, [customMarginEnabled, form.values.custom_margin_minutes, match.margin_minutes]);
+
+ const endDatetime = useMemo(
+ () =>
+ fromUnixTime(getUnixTime(parseISO(match.start_time)) + matchDuration * 60 + matchMargin * 60),
+ [match.start_time, matchDuration, matchMargin]
+ );
+
const stageItemsLookup = getStageItemLookup(swrStagesResponse);
const matchesLookup = getMatchLookup(swrStagesResponse);
@@ -123,19 +165,18 @@ function MatchModalForm({
/>
-
-
- {t('custom_match_duration_label')}
-
+
+
{t('minutes')}}
placeholder={`${match.duration_minutes}`}
@@ -144,6 +185,7 @@ function MatchModalForm({
/>
+
-
- {t('custom_match_margin_label')}
-
{t('minutes')}}
@@ -170,6 +210,7 @@ function MatchModalForm({
/>
+
+
+
+ {format(endDatetime, 'd LLLL yyyy HH:mm')}
+
+
+
+ {priorMatch && (
+ <>
+
+
+
+
+
+
+
+
+
+
+
+ >
+ )}
+
@@ -205,14 +310,9 @@ export default function MatchModal({
opened,
setOpened,
dynamicSchedule,
-}: {
- tournamentData: TournamentMinimal;
- match: MatchInterface | null;
- swrStagesResponse: SWRResponse;
- swrUpcomingMatchesResponse: SWRResponse | null;
+ priorMatch,
+}: MatchModalProps & {
opened: boolean;
- setOpened: any;
- dynamicSchedule: boolean;
}) {
const { t } = useTranslation();
@@ -226,6 +326,7 @@ export default function MatchModal({
match={match}
setOpened={setOpened}
dynamicSchedule={dynamicSchedule}
+ priorMatch={priorMatch}
/>
>
diff --git a/frontend/src/components/utils/datetime.tsx b/frontend/src/components/utils/datetime.tsx
index a91d2300f..d29f2c0ae 100644
--- a/frontend/src/components/utils/datetime.tsx
+++ b/frontend/src/components/utils/datetime.tsx
@@ -1,13 +1,25 @@
import { format, parseISO } from 'date-fns';
+export function BareDateTime({
+ datetime,
+ formatStr,
+ datetimeAttr = datetime instanceof Date ? datetime.toISOString() : datetime.toString(),
+}: {
+ datetime: string | number | Date;
+ datetimeAttr?: string;
+ formatStr: string;
+}) {
+ return ;
+}
+
export function DateTime({ datetime }: { datetime: string }) {
const date = parseISO(datetime);
- return ;
+ return ;
}
export function Time({ datetime }: { datetime: string }) {
const date = parseISO(datetime);
- return ;
+ return ;
}
export function formatTime(datetime: string) {
diff --git a/frontend/src/components/utils/util.tsx b/frontend/src/components/utils/util.tsx
index b57f502e5..97a43c0aa 100644
--- a/frontend/src/components/utils/util.tsx
+++ b/frontend/src/components/utils/util.tsx
@@ -73,11 +73,17 @@ export function getBaseURL() {
return typeof window !== 'undefined' && window.location.origin ? window.location.origin : '';
}
-export const groupBy = (keys: any) => (array: any) =>
- array.reduce((objectsByKeyValue: any, obj: any) => {
- const value = keys.map((key: any) => obj[key]).join('-');
+export const groupBy = <
+ K extends PropertyKey = PropertyKey,
+ T extends Record = Record,
+>(
+ keys: K[],
+ array: T[]
+) =>
+ array.reduce((objectsByKeyValue: Record, obj) => {
+ const value = keys.map((key) => obj[key]).join('');
// eslint-disable-next-line no-param-reassign
- objectsByKeyValue[value] = (objectsByKeyValue[value] || []).concat(obj);
+ objectsByKeyValue[value] = (objectsByKeyValue[value] ?? []).concat(obj);
return objectsByKeyValue;
}, {});
diff --git a/frontend/src/interfaces/stage.tsx b/frontend/src/interfaces/stage.tsx
index 644251f33..40d0a8bda 100644
--- a/frontend/src/interfaces/stage.tsx
+++ b/frontend/src/interfaces/stage.tsx
@@ -12,5 +12,7 @@ export interface StageWithStageItems {
}
export function getStageById(swrStagesResponse: SWRResponse, stageId: number) {
- return swrStagesResponse.data.data.filter((stage: StageWithStageItems) => stage.id === stageId);
+ return (swrStagesResponse.data.data as StageWithStageItems[]).filter(
+ (stage: StageWithStageItems) => stage.id === stageId
+ );
}
diff --git a/frontend/src/interfaces/stage_item.tsx b/frontend/src/interfaces/stage_item.tsx
index 46993c30b..57281e69a 100644
--- a/frontend/src/interfaces/stage_item.tsx
+++ b/frontend/src/interfaces/stage_item.tsx
@@ -3,7 +3,7 @@ import { StageItemInput } from './stage_item_input';
export interface StageItemWithRounds {
id: number;
- tournament_id: number;
+ stage_id: number;
created: string;
type: string;
name: string;
diff --git a/frontend/src/pages/tournaments/[id]/dashboard/present/courts.tsx b/frontend/src/pages/tournaments/[id]/dashboard/present/courts.tsx
index 9a3470a6a..2c17bfe03 100644
--- a/frontend/src/pages/tournaments/[id]/dashboard/present/courts.tsx
+++ b/frontend/src/pages/tournaments/[id]/dashboard/present/courts.tsx
@@ -48,7 +48,7 @@ export default function CourtsPage() {
const courts = responseIsValid(swrCourtsResponse) ? swrCourtsResponse.data.data : [];
const matchesByCourtId = responseIsValid(swrStagesResponse)
? getMatchLookupByCourt(swrStagesResponse)
- : [];
+ : {};
const rows = courts.map((court: Court) => {
const matchesForCourt = matchesByCourtId[court.id] || [];
diff --git a/frontend/src/pages/tournaments/[id]/results.tsx b/frontend/src/pages/tournaments/[id]/results.tsx
index 312d06d57..832c7b794 100644
--- a/frontend/src/pages/tournaments/[id]/results.tsx
+++ b/frontend/src/pages/tournaments/[id]/results.tsx
@@ -14,9 +14,9 @@ import {
import { IconAlertCircle } from '@tabler/icons-react';
import { useTranslation } from 'next-i18next';
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
-import React, { useState } from 'react';
+import React, { useCallback, useState } from 'react';
-import MatchModal from '../../../components/modals/match_modal';
+import MatchModal, { OpenMatchModalFn } from '../../../components/modals/match_modal';
import { NoContentDashboard } from '../../../components/no_content/empty_table_info';
import { Time, formatTime } from '../../../components/utils/datetime';
import { Translator } from '../../../components/utils/types';
@@ -31,11 +31,13 @@ function ScheduleRow({
openMatchModal,
stageItemsLookup,
matchesLookup,
+ previousMatch,
}: {
- data: any;
- openMatchModal: any;
+ data: { match: MatchInterface; stageItem: any };
+ openMatchModal: OpenMatchModalFn;
stageItemsLookup: any;
matchesLookup: any;
+ previousMatch: MatchInterface | null;
}) {
const winColor = '#2a8f37';
const drawColor = '#656565';
@@ -62,14 +64,14 @@ function ScheduleRow({
mt="md"
pt="0rem"
onClick={() => {
- openMatchModal(data.match);
+ openMatchModal(data.match, previousMatch);
}}
>
- {data.match.court.name}
+ {data.match.court!.name}
@@ -147,10 +149,10 @@ function Schedule({
}: {
t: Translator;
stageItemsLookup: any;
- openMatchModal: CallableFunction;
- matchesLookup: any;
+ openMatchModal: OpenMatchModalFn;
+ matchesLookup: ReturnType;
}) {
- const matches: any[] = Object.values(matchesLookup);
+ const matches = Object.values(matchesLookup);
const sortedMatches = matches
.filter((m1: any) => m1.match.start_time != null)
.sort((m1: any, m2: any) => (m1.match.court?.name > m2.match.court?.name ? 1 : -1))
@@ -182,6 +184,7 @@ function Schedule({
openMatchModal={openMatchModal}
stageItemsLookup={stageItemsLookup}
matchesLookup={matchesLookup}
+ previousMatch={c > 0 ? sortedMatches[c - 1].match : null}
/>
);
}
@@ -193,7 +196,7 @@ function Schedule({
}
const noItemsAlert =
- matchesLookup.length < 1 ? (
+ Object.getOwnPropertyNames(matchesLookup).length < 1 ? (
}
title={t('no_matches_title')}
@@ -217,6 +220,7 @@ function Schedule({
export default function SchedulePage() {
const [modalOpened, modalSetOpened] = useState(false);
const [match, setMatch] = useState(null);
+ const [priorMatch, setPriorMatch] = useState(null);
const { t } = useTranslation();
const { tournamentData } = getTournamentIdFromRouter();
@@ -226,23 +230,20 @@ export default function SchedulePage() {
const stageItemsLookup = responseIsValid(swrStagesResponse)
? getStageItemLookup(swrStagesResponse)
: [];
- const matchesLookup = responseIsValid(swrStagesResponse) ? getMatchLookup(swrStagesResponse) : [];
+ const matchesLookup = responseIsValid(swrStagesResponse) ? getMatchLookup(swrStagesResponse) : {};
+
+ const openMatchModal: OpenMatchModalFn = useCallback(
+ (matchToOpen: MatchInterface, priorMatchToOpen: MatchInterface | null) => {
+ setMatch(matchToOpen);
+ setPriorMatch(priorMatchToOpen);
+ modalSetOpened(true);
+ },
+ [setMatch, setPriorMatch, modalSetOpened]
+ );
if (!responseIsValid(swrStagesResponse)) return null;
if (!responseIsValid(swrCourtsResponse)) return null;
- function openMatchModal(matchToOpen: MatchInterface) {
- setMatch(matchToOpen);
- modalSetOpened(true);
- }
-
- function modalSetOpenedAndUpdateMatch(opened: boolean) {
- if (!opened) {
- setMatch(null);
- }
- modalSetOpened(opened);
- }
-
return (
{
+ if (openend === false) setMatch(null);
+ modalSetOpened(openend);
+ }}
dynamicSchedule={false}
+ priorMatch={priorMatch}
/>
{t('results_title')}
diff --git a/frontend/src/pages/tournaments/[id]/schedule.tsx b/frontend/src/pages/tournaments/[id]/schedule.tsx
index 37de256c4..17d1c76b8 100644
--- a/frontend/src/pages/tournaments/[id]/schedule.tsx
+++ b/frontend/src/pages/tournaments/[id]/schedule.tsx
@@ -1,13 +1,25 @@
import { DragDropContext, Draggable, Droppable } from '@hello-pangea/dnd';
-import { Alert, Badge, Button, Card, Grid, Group, Stack, Text, Title } from '@mantine/core';
+import {
+ ActionIcon,
+ Alert,
+ Badge,
+ Button,
+ Card,
+ Grid,
+ Group,
+ Stack,
+ Text,
+ Title,
+} from '@mantine/core';
import { IconAlertCircle, IconCalendarPlus } from '@tabler/icons-react';
import { useTranslation } from 'next-i18next';
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
-import React, { useState } from 'react';
+import React, { useCallback, useState } from 'react';
+import { BiEditAlt } from 'react-icons/bi';
-import MatchModal from '../../../components/modals/match_modal';
+import MatchModal, { OpenMatchModalFn } from '../../../components/modals/match_modal';
import { NoContent } from '../../../components/no_content/empty_table_info';
-import { Time } from '../../../components/utils/datetime';
+import { DateTime } from '../../../components/utils/datetime';
import { Translator } from '../../../components/utils/types';
import { getTournamentIdFromRouter, responseIsValid } from '../../../components/utils/util';
import { Court } from '../../../interfaces/court';
@@ -29,12 +41,14 @@ function ScheduleRow({
openMatchModal,
stageItemsLookup,
matchesLookup,
+ previousMatch,
}: {
index: number;
match: MatchInterface;
- openMatchModal: any;
+ openMatchModal: OpenMatchModalFn;
stageItemsLookup: any;
matchesLookup: any;
+ previousMatch: MatchInterface | null;
}) {
return (
@@ -47,7 +61,7 @@ function ScheduleRow({
withBorder
mt="md"
onClick={() => {
- openMatchModal(match);
+ openMatchModal(match, previousMatch);
}}
{...provided.dragHandleProps}
>
@@ -57,10 +71,15 @@ function ScheduleRow({
{formatMatchTeam2(stageItemsLookup, matchesLookup, match)}
-
-
- {match.start_time != null ? : null}
-
+
+
+
+ {match.start_time != null ? : null}
+
+
+
+
+
0 ? matches[index - 1] : null}
/>
));
@@ -118,7 +138,7 @@ function ScheduleColumn({
{(provided) => (
-
+
{court.name}
{rows}
{noItemsAlert}
@@ -141,7 +161,7 @@ function Schedule({
stageItemsLookup: any;
matchesLookup: any;
schedule: { court: Court; matches: MatchInterface[] }[];
- openMatchModal: CallableFunction;
+ openMatchModal: OpenMatchModalFn;
}) {
const columns = schedule.map((item) => (
(null);
+ const [priorMatch, setPriorMatch] = useState(null);
const { t } = useTranslation();
const { tournamentData } = getTournamentIdFromRouter();
@@ -184,17 +205,24 @@ export default function SchedulePage() {
const data =
responseIsValid(swrCourtsResponse) && responseIsValid(swrStagesResponse)
- ? getScheduleData(swrCourtsResponse, matchesByCourtId)
+ ? getScheduleData(
+ swrCourtsResponse,
+ matchesByCourtId as ReturnType
+ )
: [];
+ const openMatchModal: OpenMatchModalFn = useCallback(
+ (matchToOpen: MatchInterface, priorMatchToOpen: MatchInterface | null) => {
+ setMatch(matchToOpen);
+ setPriorMatch(priorMatchToOpen);
+ modalSetOpened(true);
+ },
+ [setMatch, setPriorMatch, modalSetOpened]
+ );
+
if (!responseIsValid(swrStagesResponse)) return null;
if (!responseIsValid(swrCourtsResponse)) return null;
- function openMatchModal(matchToOpen: MatchInterface) {
- setMatch(matchToOpen);
- modalSetOpened(true);
- }
-
return (
{match != null ? (
@@ -206,6 +234,7 @@ export default function SchedulePage() {
opened={modalOpened}
setOpened={modalSetOpened}
dynamicSchedule={false}
+ priorMatch={priorMatch}
/>
) : null}
diff --git a/frontend/src/pages/tournaments/[id]/swiss/[stage_item_id].tsx b/frontend/src/pages/tournaments/[id]/swiss/[stage_item_id].tsx
index c819760cb..bec942525 100644
--- a/frontend/src/pages/tournaments/[id]/swiss/[stage_item_id].tsx
+++ b/frontend/src/pages/tournaments/[id]/swiss/[stage_item_id].tsx
@@ -18,8 +18,11 @@ import {
import { BracketDisplaySettings } from '../../../../interfaces/brackets';
import { SchedulerSettings } from '../../../../interfaces/match';
import { RoundInterface } from '../../../../interfaces/round';
-import { getStageById } from '../../../../interfaces/stage';
-import { stageItemIsHandledAutomatically } from '../../../../interfaces/stage_item';
+import { StageWithStageItems, getStageById } from '../../../../interfaces/stage';
+import {
+ StageItemWithRounds,
+ stageItemIsHandledAutomatically,
+} from '../../../../interfaces/stage_item';
import { getTournamentEndpoint } from '../../../../interfaces/tournament';
import {
checkForAuthError,
@@ -71,12 +74,12 @@ export default function TournamentPage() {
const tournamentDataFull =
swrTournamentResponse.data != null ? swrTournamentResponse.data.data : null;
- let activeStage = null;
- let draftRound = null;
- let stageItem = null;
+ let activeStage: StageWithStageItems | null = null;
+ let draftRound: RoundInterface | null = null;
+ let stageItem: StageItemWithRounds | null = null;
- if (responseIsValid(swrStagesResponse) && stageItemId != null) {
- stageItem = getStageItemLookup(swrStagesResponse)[stageItemId];
+ if (responseIsValid(swrStagesResponse)) {
+ stageItem = getStageItemLookup(swrStagesResponse)[stageItemId] ?? null;
[activeStage] = getStageById(swrStagesResponse, stageItem.stage_id);
if (activeStage != null && activeStage.stage_items != null) {
@@ -90,7 +93,11 @@ export default function TournamentPage() {
}
}
- const swrUpcomingMatchesResponse = getUpcomingMatches(id, draftRound?.id, schedulerSettings);
+ const swrUpcomingMatchesResponse = getUpcomingMatches(
+ id,
+ draftRound?.id ?? null,
+ schedulerSettings
+ );
const scheduler =
draftRound != null &&
stageItem != null &&
@@ -163,7 +170,7 @@ export default function TournamentPage() {
swrStagesResponse={swrStagesResponse}
swrUpcomingMatchesResponse={swrUpcomingMatchesResponse}
readOnly={false}
- stageItem={stageItem}
+ stageItem={stageItem!} // TODO: Actually check if stageItem exists before using it, remove this once proper checks are in place
displaySettings={displaySettings}
/>
{scheduler}
diff --git a/frontend/src/services/adapter.tsx b/frontend/src/services/adapter.tsx
index 4e6015c6a..9c6739505 100644
--- a/frontend/src/services/adapter.tsx
+++ b/frontend/src/services/adapter.tsx
@@ -187,7 +187,7 @@ export function getUser(): SWRResponse {
export function getUpcomingMatches(
tournament_id: number,
- round_id: number,
+ round_id: number | null,
schedulerSettings: SchedulerSettings
): SWRResponse {
return useSWR(
diff --git a/frontend/src/services/lookups.tsx b/frontend/src/services/lookups.tsx
index 039336ed5..246141555 100644
--- a/frontend/src/services/lookups.tsx
+++ b/frontend/src/services/lookups.tsx
@@ -5,6 +5,7 @@ import { groupBy, responseIsValid } from '../components/utils/util';
import { Court } from '../interfaces/court';
import { MatchInterface } from '../interfaces/match';
import { StageWithStageItems } from '../interfaces/stage';
+import { StageItemWithRounds } from '../interfaces/stage_item';
import { TeamInterface } from '../interfaces/team';
import { getTeams } from './adapter';
@@ -15,11 +16,13 @@ export function getTeamsLookup(tournamentId: number) {
if (!isResponseValid) {
return null;
}
- return Object.fromEntries(swrTeamsResponse.data.data.teams.map((x: TeamInterface) => [x.id, x]));
+ return Object.fromEntries(
+ (swrTeamsResponse.data.data.teams as TeamInterface[]).map((x: TeamInterface) => [x.id, x])
+ );
}
export function getStageItemLookup(swrStagesResponse: SWRResponse) {
- let result: any[] = [];
+ let result: [number, StageItemWithRounds][] = [];
swrStagesResponse.data.data.map((stage: StageWithStageItems) =>
stage.stage_items.forEach((stage_item) => {
@@ -30,7 +33,7 @@ export function getStageItemLookup(swrStagesResponse: SWRResponse) {
}
export function getStageItemList(swrStagesResponse: SWRResponse) {
- let result: any[] = [];
+ let result: [StageItemWithRounds][] = [];
swrStagesResponse.data.data.map((stage: StageWithStageItems) =>
stage.stage_items.forEach((stage_item) => {
@@ -41,7 +44,7 @@ export function getStageItemList(swrStagesResponse: SWRResponse) {
}
export function getStageItemTeamIdsLookup(swrStagesResponse: SWRResponse) {
- let result: any[] = [];
+ let result: [number, (number | null)[]][] = [];
swrStagesResponse.data.data.map((stage: StageWithStageItems) =>
stage.stage_items.forEach((stageItem) => {
@@ -79,7 +82,7 @@ export function getStageItemTeamsLookup(
}
export function getMatchLookup(swrStagesResponse: SWRResponse) {
- let result: any[] = [];
+ let result: [number, { match: MatchInterface; stageItem: StageItemWithRounds }][] = [];
swrStagesResponse.data.data.map((stage: StageWithStageItems) =>
stage.stage_items.forEach((stageItem) => {
@@ -117,12 +120,12 @@ export function stringToColour(input: string) {
export function getMatchLookupByCourt(swrStagesResponse: SWRResponse) {
const matches = Object.values(getMatchLookup(swrStagesResponse)).map((x) => x.match);
- return groupBy(['court_id'])(matches);
+ return groupBy(['court_id'], matches);
}
export function getScheduleData(
swrCourtsResponse: SWRResponse,
- matchesByCourtId: any
+ matchesByCourtId: ReturnType>
): { court: Court; matches: MatchInterface[] }[] {
return swrCourtsResponse.data.data.map((court: Court) => ({
matches: (matchesByCourtId[court.id] || [])