diff --git a/backend/bracket/logic/ranking/elo.py b/backend/bracket/logic/ranking/elo.py
index 8973be40..155da26e 100644
--- a/backend/bracket/logic/ranking/elo.py
+++ b/backend/bracket/logic/ranking/elo.py
@@ -96,7 +96,7 @@ def determine_ranking_for_stage_item(
return team_x_stats
-async def determine_team_ranking_for_stage_item(
+def determine_team_ranking_for_stage_item(
stage_item: StageItemWithRounds,
ranking: Ranking,
) -> list[tuple[TeamId, TeamStatistics]]:
diff --git a/backend/bracket/logic/scheduling/handle_stage_activation.py b/backend/bracket/logic/scheduling/handle_stage_activation.py
index 60f5e76e..88cfd96c 100644
--- a/backend/bracket/logic/scheduling/handle_stage_activation.py
+++ b/backend/bracket/logic/scheduling/handle_stage_activation.py
@@ -3,9 +3,9 @@ from bracket.logic.ranking.elo import (
)
from bracket.logic.ranking.statistics import TeamStatistics
from bracket.models.db.match import MatchWithDetails
+from bracket.models.db.util import StageWithStageItems
from bracket.sql.matches import sql_get_match, sql_update_team_ids_for_match
from bracket.sql.rankings import get_ranking_for_stage_item
-from bracket.sql.stage_items import get_stage_item
from bracket.sql.stages import get_full_tournament_details
from bracket.utils.id_types import MatchId, StageId, StageItemId, TeamId, TournamentId
from bracket.utils.types import assert_some
@@ -65,16 +65,12 @@ async def set_team_ids_for_match(
await sql_update_team_ids_for_match(assert_some(match.id), team1_id, team2_id)
-async def get_team_rankings_lookup(tournament_id: TournamentId) -> StageItemXTeamRanking:
- stages = await get_full_tournament_details(tournament_id)
-
- stage_items = {
- stage_item.id: assert_some(await get_stage_item(tournament_id, stage_item.id))
- for stage in stages
- for stage_item in stage.stage_items
- }
+async def get_team_rankings_lookup_for_stage(
+ tournament_id: TournamentId, stage: StageWithStageItems
+) -> StageItemXTeamRanking:
+ stage_items = {stage_item.id: stage_item for stage_item in stage.stage_items}
return {
- stage_item_id: await determine_team_ranking_for_stage_item(
+ stage_item_id: determine_team_ranking_for_stage_item(
stage_item,
assert_some(await get_ranking_for_stage_item(tournament_id, stage_item.id)),
)
@@ -82,6 +78,16 @@ async def get_team_rankings_lookup(tournament_id: TournamentId) -> StageItemXTea
}
+async def get_team_rankings_lookup(tournament_id: TournamentId) -> StageItemXTeamRanking:
+ return {
+ stage_item_id: team_ranking
+ for stage in await get_full_tournament_details(tournament_id)
+ for stage_item_id, team_ranking in (
+ await get_team_rankings_lookup_for_stage(tournament_id, stage)
+ ).items()
+ }
+
+
async def update_matches_in_activated_stage(tournament_id: TournamentId, stage_id: StageId) -> None:
[stage] = await get_full_tournament_details(tournament_id, stage_id=stage_id)
stage_item_x_team_rankings = await get_team_rankings_lookup(tournament_id)
diff --git a/backend/bracket/routes/stages.py b/backend/bracket/routes/stages.py
index fa6b0eda..101a0eeb 100644
--- a/backend/bracket/routes/stages.py
+++ b/backend/bracket/routes/stages.py
@@ -2,11 +2,11 @@ from fastapi import APIRouter, Depends, HTTPException
from starlette import status
from bracket.database import database
-from bracket.logic.ranking.elo import (
- determine_team_ranking_for_stage_item,
-)
from bracket.logic.scheduling.builder import determine_available_inputs
-from bracket.logic.scheduling.handle_stage_activation import update_matches_in_activated_stage
+from bracket.logic.scheduling.handle_stage_activation import (
+ get_team_rankings_lookup_for_stage,
+ update_matches_in_activated_stage,
+)
from bracket.logic.subscriptions import check_requirement
from bracket.models.db.stage import Stage, StageActivateBody, StageUpdateBody
from bracket.models.db.user import UserPublic
@@ -22,8 +22,6 @@ from bracket.routes.models import (
SuccessResponse,
)
from bracket.routes.util import stage_dependency
-from bracket.sql.rankings import get_ranking_for_stage_item
-from bracket.sql.stage_items import get_stage_item
from bracket.sql.stages import (
get_full_tournament_details,
get_next_stage_in_tournament,
@@ -33,7 +31,6 @@ from bracket.sql.stages import (
)
from bracket.sql.teams import get_teams_with_members
from bracket.utils.id_types import StageId, TournamentId
-from bracket.utils.types import assert_some
router = APIRouter()
@@ -158,17 +155,4 @@ async def get_rankings(
Get the rankings for the stage items in this stage.
"""
[stage] = await get_full_tournament_details(tournament_id, stage_id=stage_id)
-
- stage_items = {
- stage_item.id: assert_some(await get_stage_item(tournament_id, stage_item.id))
- for stage_item in stage.stage_items
- }
- stage_item_x_ranking = {
- stage_item_id: await determine_team_ranking_for_stage_item(
- stage_item,
- assert_some(await get_ranking_for_stage_item(tournament_id, stage_item.id)),
- )
- for stage_item_id, stage_item in stage_items.items()
- }
-
- return StageRankingResponse(data=stage_item_x_ranking)
+ return StageRankingResponse(data=await get_team_rankings_lookup_for_stage(tournament_id, stage))
diff --git a/frontend/public/locales/en/common.json b/frontend/public/locales/en/common.json
index 35a084ff..52949012 100644
--- a/frontend/public/locales/en/common.json
+++ b/frontend/public/locales/en/common.json
@@ -7,6 +7,12 @@
"active_next_round_modal_choose_option_checked": "Adjust the start times of the next matches to start immediately(now). This will be done by modifying the margin times of the matches in the previous round.",
"active_next_round_modal_choose_option_unchecked": "Use default timing (the next matches will be planned tightly after the matches of the active round end, taking margin into account)",
"active_next_round_modal_description": "This will assign times and courts to matches of next round, which is the round after the current activated (green) round.",
+ "active_next_stage_modal_description": "This will start the next stage. Teams will be automatically assigned to the matches.",
+ "plan_next_stage_button": "Start the next stage",
+ "active_next_stage_modal_title": "Start the next stage",
+ "active_previous_stage_modal_description": "Are you sure you want to go back to the previous stage? Match results will be discarded.",
+ "plan_previous_stage_button": "Go back to the previous stage",
+ "active_previous_stage_modal_title": "Go back to the previous stage",
"active_next_round_modal_title": "Assign times and courts to matches of next round",
"active_player_checkbox_label": "This player is active",
"active_players_checkbox_label": "These players are active",
diff --git a/frontend/src/components/builder/builder.tsx b/frontend/src/components/builder/builder.tsx
index 763205eb..69b66374 100644
--- a/frontend/src/components/builder/builder.tsx
+++ b/frontend/src/components/builder/builder.tsx
@@ -255,7 +255,7 @@ export default function Builder({
const button = (
-
+
diff --git a/frontend/src/components/buttons/next_stage_button.tsx b/frontend/src/components/buttons/next_stage_button.tsx
deleted file mode 100644
index fc717b4b..00000000
--- a/frontend/src/components/buttons/next_stage_button.tsx
+++ /dev/null
@@ -1,43 +0,0 @@
-import { Button } from '@mantine/core';
-import { IconSquareArrowLeft, IconSquareArrowRight } from '@tabler/icons-react';
-import { useTranslation } from 'next-i18next';
-import React from 'react';
-
-import { activateNextStage } from '../../services/stage';
-
-export function NextStageButton({ tournamentData, swrStagesResponse }: any) {
- const { t } = useTranslation();
- return (
- }
- onClick={async () => {
- await activateNextStage(tournamentData.id, 'next');
- swrStagesResponse.mutate();
- }}
- >
- {t('next_stage_button')}
-
- );
-}
-
-export function PreviousStageButton({ tournamentData, swrStagesResponse }: any) {
- const { t } = useTranslation();
-
- return (
- }
- onClick={async () => {
- await activateNextStage(tournamentData.id, 'previous');
- swrStagesResponse.mutate();
- }}
- >
- {t('previous_stage_button')}
-
- );
-}
diff --git a/frontend/src/components/modals/activate_next_stage_modal.tsx b/frontend/src/components/modals/activate_next_stage_modal.tsx
new file mode 100644
index 00000000..9478813e
--- /dev/null
+++ b/frontend/src/components/modals/activate_next_stage_modal.tsx
@@ -0,0 +1,69 @@
+import { Alert, Button, Modal } from '@mantine/core';
+import { useForm } from '@mantine/form';
+import { IconAlertCircle, IconSquareArrowRight } from '@tabler/icons-react';
+import { useTranslation } from 'next-i18next';
+import React, { useState } from 'react';
+import { SWRResponse } from 'swr';
+
+import { activateNextStage } from '../../services/stage';
+
+export default function ActivateNextStageModal({
+ tournamentId,
+ swrStagesResponse,
+}: {
+ tournamentId: number;
+ swrStagesResponse: SWRResponse;
+}) {
+ const { t } = useTranslation();
+ const [opened, setOpened] = useState(false);
+
+ const form = useForm({
+ initialValues: {},
+ });
+
+ return (
+ <>
+ setOpened(false)}
+ title={t('active_next_stage_modal_title')}
+ size="40rem"
+ >
+
+
+
+ }
+ onClick={async () => {
+ setOpened(true);
+ }}
+ >
+ {t('next_stage_button')}
+
+ >
+ );
+}
diff --git a/frontend/src/components/modals/activate_previous_stage_modal.tsx b/frontend/src/components/modals/activate_previous_stage_modal.tsx
new file mode 100644
index 00000000..0cd4e96b
--- /dev/null
+++ b/frontend/src/components/modals/activate_previous_stage_modal.tsx
@@ -0,0 +1,69 @@
+import { Alert, Button, Modal } from '@mantine/core';
+import { useForm } from '@mantine/form';
+import { IconAlertCircle, IconSquareArrowLeft } from '@tabler/icons-react';
+import { useTranslation } from 'next-i18next';
+import React, { useState } from 'react';
+import { SWRResponse } from 'swr';
+
+import { activateNextStage } from '../../services/stage';
+
+export default function ActivatePreviousStageModal({
+ tournamentId,
+ swrStagesResponse,
+}: {
+ tournamentId: number;
+ swrStagesResponse: SWRResponse;
+}) {
+ const { t } = useTranslation();
+ const [opened, setOpened] = useState(false);
+
+ const form = useForm({
+ initialValues: {},
+ });
+
+ return (
+ <>
+ setOpened(false)}
+ title={t('active_previous_stage_modal_title')}
+ size="40rem"
+ >
+
+
+
+ }
+ onClick={async () => {
+ setOpened(true);
+ }}
+ >
+ {t('previous_stage_button')}
+
+ >
+ );
+}
diff --git a/frontend/src/pages/tournaments/[id]/stages.tsx b/frontend/src/pages/tournaments/[id]/stages.tsx
index 0f502129..e89f328b 100644
--- a/frontend/src/pages/tournaments/[id]/stages.tsx
+++ b/frontend/src/pages/tournaments/[id]/stages.tsx
@@ -5,10 +5,8 @@ import React from 'react';
import Builder from '../../../components/builder/builder';
import { CreateStageButtonLarge } from '../../../components/buttons/create_stage';
-import {
- NextStageButton,
- PreviousStageButton,
-} from '../../../components/buttons/next_stage_button';
+import ActivateNextStageModal from '../../../components/modals/activate_next_stage_modal';
+import ActivatePreviousStageModal from '../../../components/modals/activate_previous_stage_modal';
import { NoContent } from '../../../components/no_content/empty_table_info';
import { TableSkeletonTwoColumnsSmall } from '../../../components/utils/skeletons';
import { getTournamentIdFromRouter } from '../../../components/utils/util';
@@ -47,11 +45,14 @@ export default function StagesPage() {
content = (
<>
-
+
-