diff --git a/backend/bracket/logic/scheduling/builder.py b/backend/bracket/logic/scheduling/builder.py index 5bf7a632..19f5ab4a 100644 --- a/backend/bracket/logic/scheduling/builder.py +++ b/backend/bracket/logic/scheduling/builder.py @@ -18,7 +18,7 @@ from bracket.models.db.team import FullTeamWithPlayers from bracket.models.db.util import StageWithStageItems from bracket.sql.rounds import get_next_round_name, sql_create_round from bracket.sql.stage_items import get_stage_item -from bracket.utils.id_types import StageId, TournamentId +from bracket.utils.id_types import StageId, StageItemId, TournamentId from tests.integration_tests.mocks import MOCK_NOW @@ -71,10 +71,9 @@ async def build_matches_for_stage_item(stage_item: StageItem, tournament_id: Tou def determine_available_inputs( - stage_id: StageId, teams: list[FullTeamWithPlayers], stages: list[StageWithStageItems], -) -> list[StageItemInputOptionTentative | StageItemInputOptionFinal]: +) -> dict[StageId, list[StageItemInputOptionTentative | StageItemInputOptionFinal]]: """ Returns available inputs for the given stage. @@ -83,35 +82,41 @@ def determine_available_inputs( - Previous ROUND_ROBIN stage items """ results_team_ids = {team.id: False for team in teams} - results_tentative = [] + results_tentative: dict[tuple[StageItemId, int], StageItemInputOptionTentative] = {} + results = {} for stage in stages: + # First, set options that are used in this round to have `already_taken=True` for stage_item in stage.stage_items: - item_team_id_inputs = [ - input.team_id for input in stage_item.inputs if input.team_id is not None - ] - for input_ in item_team_id_inputs: - if input_ in results_team_ids: - if stage_id != stage.id: - results_team_ids.pop(input_) - else: - results_team_ids[input_] = True + for input_ in stage_item.inputs: + if input_.team_id is not None and input_.team_id in results_team_ids: + results_team_ids[input_.team_id] = True - if stage_id == stage.id: - break + if ( + input_.winner_from_stage_item_id is not None + and input_.winner_position is not None + and (key := (input_.winner_from_stage_item_id, input_.winner_position)) + in results_tentative + ): + results_tentative[key].already_taken = True + # Store results for this stage + results_final = [ + StageItemInputOptionFinal(team_id=team_id, already_taken=taken) + for team_id, taken in results_team_ids.items() + ] + results[stage.id] = results_final + list(results_tentative.values()) + + # Then, add inputs from non-elimination stage items that can be used in the next stage. for stage_item in stage.stage_items: - for winner_position in range(1, 5): - results_tentative.append( - StageItemInputOptionTentative( - winner_from_stage_item_id=stage_item.id, - winner_position=winner_position, - already_taken=False, + if stage_item.type in {StageType.ROUND_ROBIN, StageType.SWISS}: + for winner_position in range(1, 5): + results_tentative[(stage_item.id, winner_position)] = ( + StageItemInputOptionTentative( + winner_from_stage_item_id=stage_item.id, + winner_position=winner_position, + already_taken=False, + ) ) - ) - results_final = [ - StageItemInputOptionFinal(team_id=team_id, already_taken=taken) - for team_id, taken in results_team_ids.items() - ] - return results_final + results_tentative + return results diff --git a/backend/bracket/routes/stages.py b/backend/bracket/routes/stages.py index 2ea3b0ac..a51549cb 100644 --- a/backend/bracket/routes/stages.py +++ b/backend/bracket/routes/stages.py @@ -145,10 +145,7 @@ async def get_available_inputs( ) -> StageItemInputOptionsResponse: stages = await get_full_tournament_details(tournament_id) teams = await get_teams_with_members(tournament_id) - available_inputs = { - stage.id: determine_available_inputs(stage.id, teams, stages) for stage in stages - } - return StageItemInputOptionsResponse(data=available_inputs) + return StageItemInputOptionsResponse(data=determine_available_inputs(teams, stages)) @router.get("/tournaments/{tournament_id}/next_stage_rankings") diff --git a/frontend/src/components/brackets/brackets.tsx b/frontend/src/components/brackets/brackets.tsx index a8e7bf33..04526549 100644 --- a/frontend/src/components/brackets/brackets.tsx +++ b/frontend/src/components/brackets/brackets.tsx @@ -84,8 +84,12 @@ export function RoundsGridCols({ return ; } - const items = stageItem.rounds + let result: React.JSX.Element[] | React.JSX.Element = stageItem.rounds .sort((r1: any, r2: any) => (r1.name > r2.name ? 1 : -1)) + .filter( + (round: RoundInterface) => + round.matches.length > 0 || displaySettings.matchVisibility === 'all' + ) .map((round: RoundInterface) => ( )); - let result: React.JSX.Element[] | React.JSX.Element = items; - if (result.length < 1) { - result = ( - - - - - - - ); + if (stageItem.rounds.length < 1) { + result = ( + + + + {stageItem.rounds.length < 1 && ( + + )} + + + ); + } else { + result = ( + + + + + + ); + } } const hideAddRoundButton = tournamentData == null || readOnly; diff --git a/frontend/src/components/brackets/round.tsx b/frontend/src/components/brackets/round.tsx index 12d81119..46f1d23d 100644 --- a/frontend/src/components/brackets/round.tsx +++ b/frontend/src/components/brackets/round.tsx @@ -70,10 +70,6 @@ export default function Round({ /> ); - if (matches.length < 1 && displaySettings.matchVisibility !== 'all') { - return null; - } - const item = (
-
{icon || }
+
{icon || }
{title} {description} diff --git a/frontend/src/interfaces/stage_item.tsx b/frontend/src/interfaces/stage_item.tsx index 69343aac..9043b3f9 100644 --- a/frontend/src/interfaces/stage_item.tsx +++ b/frontend/src/interfaces/stage_item.tsx @@ -8,7 +8,6 @@ export interface StageItemWithRounds { name: string; type_name: string; team_count: number; - is_active: boolean; rounds: RoundInterface[]; inputs: StageItemInput[]; stage_id: number;