Show modal when activating stages (#897)

This commit is contained in:
Erik Vroon
2024-09-08 19:20:05 +02:00
committed by GitHub
parent 8a32b5c841
commit 31aae10839
9 changed files with 175 additions and 83 deletions

View File

@@ -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]]:

View File

@@ -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)

View File

@@ -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))

View File

@@ -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",

View File

@@ -255,7 +255,7 @@ export default function Builder({
const button = (
<Stack miw="24rem" align="top" key={-1}>
<h4>
<h4 style={{ marginTop: '0rem' }}>
<CreateStageButton tournament={tournament} swrStagesResponse={swrStagesResponse} />
</h4>
</Stack>

View File

@@ -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 (
<Button
size="md"
mb="10"
color="indigo"
leftSection={<IconSquareArrowRight size={24} />}
onClick={async () => {
await activateNextStage(tournamentData.id, 'next');
swrStagesResponse.mutate();
}}
>
{t('next_stage_button')}
</Button>
);
}
export function PreviousStageButton({ tournamentData, swrStagesResponse }: any) {
const { t } = useTranslation();
return (
<Button
size="md"
mb="10"
color="indigo"
leftSection={<IconSquareArrowLeft size={24} />}
onClick={async () => {
await activateNextStage(tournamentData.id, 'previous');
swrStagesResponse.mutate();
}}
>
{t('previous_stage_button')}
</Button>
);
}

View File

@@ -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 (
<>
<Modal
opened={opened}
onClose={() => setOpened(false)}
title={t('active_next_stage_modal_title')}
size="40rem"
>
<form
onSubmit={form.onSubmit(async () => {
await activateNextStage(tournamentId, 'next');
swrStagesResponse.mutate();
setOpened(false);
})}
>
<Alert icon={<IconAlertCircle size={16} />} color="gray" radius="lg">
{t('active_next_stage_modal_description')}
</Alert>
<Button
fullWidth
color="indigo"
size="md"
mt="lg"
type="submit"
leftSection={<IconSquareArrowRight size={24} />}
>
{t('plan_next_stage_button')}
</Button>
</form>
</Modal>
<Button
size="md"
mb="10"
color="indigo"
leftSection={<IconSquareArrowRight size={24} />}
onClick={async () => {
setOpened(true);
}}
>
{t('next_stage_button')}
</Button>
</>
);
}

View File

@@ -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 (
<>
<Modal
opened={opened}
onClose={() => setOpened(false)}
title={t('active_previous_stage_modal_title')}
size="40rem"
>
<form
onSubmit={form.onSubmit(async () => {
await activateNextStage(tournamentId, 'previous');
swrStagesResponse.mutate();
setOpened(false);
})}
>
<Alert icon={<IconAlertCircle size={16} />} color="orange" radius="lg">
{t('active_previous_stage_modal_description')}
</Alert>
<Button
fullWidth
color="indigo"
size="md"
mt="lg"
type="submit"
leftSection={<IconSquareArrowLeft size={24} />}
>
{t('plan_previous_stage_button')}
</Button>
</form>
</Modal>
<Button
size="md"
mb="10"
color="indigo"
leftSection={<IconSquareArrowLeft size={24} />}
onClick={async () => {
setOpened(true);
}}
>
{t('previous_stage_button')}
</Button>
</>
);
}

View File

@@ -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 = (
<>
<Group grow mt="1rem" maw="30rem">
<PreviousStageButton
tournamentData={tournamentData}
<ActivatePreviousStageModal
tournamentId={tournamentData.id}
swrStagesResponse={swrStagesResponse}
/>
<ActivateNextStageModal
tournamentId={tournamentData.id}
swrStagesResponse={swrStagesResponse}
/>
<NextStageButton tournamentData={tournamentData} swrStagesResponse={swrStagesResponse} />
</Group>
<Group mt="1rem" align="top">
<Builder