diff --git a/PadelClub.xcodeproj/project.pbxproj b/PadelClub.xcodeproj/project.pbxproj index 2339df2..7464d21 100644 --- a/PadelClub.xcodeproj/project.pbxproj +++ b/PadelClub.xcodeproj/project.pbxproj @@ -1935,7 +1935,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 29; + CURRENT_PROJECT_VERSION = 30; DEFINES_MODULE = YES; DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\""; DEVELOPMENT_TEAM = BQ3Y44M3Q6; @@ -1973,7 +1973,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 29; + CURRENT_PROJECT_VERSION = 30; DEFINES_MODULE = YES; DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\""; DEVELOPMENT_TEAM = BQ3Y44M3Q6; diff --git a/PadelClub/Data/GroupStage.swift b/PadelClub/Data/GroupStage.swift index c1efdbe..9e97677 100644 --- a/PadelClub/Data/GroupStage.swift +++ b/PadelClub/Data/GroupStage.swift @@ -376,7 +376,8 @@ extension GroupStage: Selectable { } func badgeValue() -> Int? { - runningMatches(playedMatches: _matches()).count + if teams().count < size { return nil } + return runningMatches(playedMatches: _matches()).count } func badgeValueColor() -> Color? { @@ -384,6 +385,10 @@ extension GroupStage: Selectable { } func badgeImage() -> Badge? { - hasEnded() ? .checkmark : nil + if teams().count < size { + return .xmark + } else { + return hasEnded() ? .checkmark : nil + } } } diff --git a/PadelClub/Data/Tournament.swift b/PadelClub/Data/Tournament.swift index 4646f73..b623322 100644 --- a/PadelClub/Data/Tournament.swift +++ b/PadelClub/Data/Tournament.swift @@ -1523,7 +1523,6 @@ class Tournament : ModelObject, Storable { groupStageMatchFormat = groupStageSmartMatchFormat() loserBracketMatchFormat = loserBracketSmartMatchFormat(1) matchFormat = roundSmartMatchFormat(1) - } } @@ -1538,7 +1537,8 @@ class Tournament : ModelObject, Storable { func loserBracketSmartMatchFormat(_ roundIndex: Int) -> MatchFormat { let format = tournamentLevel.federalFormatForLoserBracketRound(roundIndex) - if format.rank > loserBracketMatchFormat.rank { + if tournamentLevel == .p25 { return .superTie } + if format.rank < loserBracketMatchFormat.rank { return format } else { return loserBracketMatchFormat @@ -1547,7 +1547,8 @@ class Tournament : ModelObject, Storable { func groupStageSmartMatchFormat() -> MatchFormat { let format = tournamentLevel.federalFormatForGroupStage() - if format.rank > groupStageMatchFormat.rank { + if tournamentLevel == .p25 { return .superTie } + if format.rank < groupStageMatchFormat.rank { return format } else { return groupStageMatchFormat @@ -1563,7 +1564,8 @@ class Tournament : ModelObject, Storable { func roundSmartMatchFormat(_ roundIndex: Int) -> MatchFormat { let format = tournamentLevel.federalFormatForBracketRound(roundIndex) - if format.rank > matchFormat.rank { + if tournamentLevel == .p25 { return .superTie } + if format.rank < matchFormat.rank { return format } else { return matchFormat @@ -1622,6 +1624,14 @@ class Tournament : ModelObject, Storable { return final?.playedMatches().first?.winner() } + func getGroupStageChunkValue() -> Int { + if teamsPerGroupStage >= 2 { + return min(groupStageCount, courtCount / (teamsPerGroupStage / 2)) + } else { + return 1 + } + } + // MARK: - func insertOnServer() throws { diff --git a/PadelClub/Views/Planning/PlanningSettingsView.swift b/PadelClub/Views/Planning/PlanningSettingsView.swift index d01280b..e09ec60 100644 --- a/PadelClub/Views/Planning/PlanningSettingsView.swift +++ b/PadelClub/Views/Planning/PlanningSettingsView.swift @@ -22,10 +22,10 @@ struct PlanningSettingsView: View { self.tournament = tournament if let matchScheduler = tournament.matchScheduler() { self.matchScheduler = matchScheduler - self._groupStageChunkCount = State(wrappedValue: matchScheduler.groupStageChunkCount ?? 1) + self._groupStageChunkCount = State(wrappedValue: matchScheduler.groupStageChunkCount ?? tournament.getGroupStageChunkValue()) } else { self.matchScheduler = MatchScheduler(tournament: tournament.id) - self._groupStageChunkCount = State(wrappedValue: 1) + self._groupStageChunkCount = State(wrappedValue: tournament.getGroupStageChunkValue()) } } @@ -130,10 +130,14 @@ struct PlanningSettingsView: View { Text("Des dates de démarrages ont été indiqué pour les manches et seront prises en compte.") } RowButtonView("Horaire intelligent", role: .destructive) { - schedulingDone = false + await MainActor.run { + schedulingDone = false + } await _setupSchedule() - _save() - schedulingDone = true + await MainActor.run { + _save() + schedulingDone = true + } } } footer: { Text("Padel Club programmera tous les matchs de votre tournoi en fonction de différents paramètres, ") + Text("tout en tenant compte des horaires que vous avez fixé.").underline() diff --git a/PadelClub/Views/Round/RoundView.swift b/PadelClub/Views/Round/RoundView.swift index 9d9f344..742396b 100644 --- a/PadelClub/Views/Round/RoundView.swift +++ b/PadelClub/Views/Round/RoundView.swift @@ -28,6 +28,7 @@ struct RoundView: View { List { let loserRounds = round.loserRounds() + let availableSeeds = tournament.availableSeeds() let availableQualifiedTeams = tournament.availableQualifiedTeams() let displayableMatches = round.displayableMatches().sorted(by: \.index) let spaceLeft = displayableMatches.filter({ $0.hasSpaceLeft() }) @@ -76,46 +77,80 @@ struct RoundView: View { if availableQualifiedTeams.isEmpty == false && spaceLeft.isEmpty == false { Section { - ForEach(availableQualifiedTeams) { team in - NavigationLink { - SpinDrawView(drawees: [team], segments: spaceLeft) { results in - Task { - results.forEach { drawResult in - team.setSeedPosition(inSpot: spaceLeft[drawResult.drawIndex], slot: nil, opposingSeeding: true) - } - _save() - if tournament.availableSeeds().isEmpty && tournament.availableQualifiedTeams().isEmpty { - self.isEditingTournamentSeed.wrappedValue = false + DisclosureGroup { + ForEach(availableQualifiedTeams) { team in + NavigationLink { + SpinDrawView(drawees: [team], segments: spaceLeft) { results in + Task { + results.forEach { drawResult in + team.setSeedPosition(inSpot: spaceLeft[drawResult.drawIndex], slot: nil, opposingSeeding: true) + } + _save() + if tournament.availableSeeds().isEmpty && tournament.availableQualifiedTeams().isEmpty { + self.isEditingTournamentSeed.wrappedValue = false + } } } + } label: { + TeamRowView(team: team, displayCallDate: false) } - } label: { - TeamRowView(team: team, displayCallDate: false) } + } label: { + Text("Qualifié\(availableQualifiedTeams.count.pluralSuffix) à placer").badge(availableQualifiedTeams.count) } - } header: { + } header: { Text("Tirage au sort visuel d'un qualifié").font(.subheadline) } } - if tournament.availableSeeds().isEmpty == false && seedSpaceLeft.isEmpty == false { + if availableSeeds.isEmpty == false && seedSpaceLeft.isEmpty == false { Section { - ForEach(tournament.availableSeeds()) { team in - NavigationLink { - SpinDrawView(drawees: [team], segments: seedSpaceLeft) { results in - Task { - results.forEach { drawResult in - team.setSeedPosition(inSpot: seedSpaceLeft[drawResult.drawIndex], slot: nil, opposingSeeding: false) + DisclosureGroup { + ForEach(availableSeeds) { team in + NavigationLink { + SpinDrawView(drawees: [team], segments: seedSpaceLeft) { results in + Task { + results.forEach { drawResult in + team.setSeedPosition(inSpot: seedSpaceLeft[drawResult.drawIndex], slot: nil, opposingSeeding: false) + } + _save() + if availableSeeds.isEmpty && tournament.availableQualifiedTeams().isEmpty { + self.isEditingTournamentSeed.wrappedValue = false + } } - _save() - if tournament.availableSeeds().isEmpty && tournament.availableQualifiedTeams().isEmpty { - self.isEditingTournamentSeed.wrappedValue = false + } + } label: { + TeamRowView(team: team, displayCallDate: false) + } + } + } label: { + Text("Tête\(availableSeeds.count.pluralSuffix) de série à placer").badge(availableSeeds.count) + } + } header: { + Text("Tirage au sort visuel d'une tête de série").font(.subheadline) + } + } else if availableSeeds.isEmpty == false && spaceLeft.isEmpty == false { + Section { + DisclosureGroup { + ForEach(availableSeeds) { team in + NavigationLink { + SpinDrawView(drawees: [team], segments: spaceLeft) { results in + Task { + results.forEach { drawResult in + team.setSeedPosition(inSpot: spaceLeft[drawResult.drawIndex], slot: nil, opposingSeeding: false) + } + _save() + if availableSeeds.isEmpty && tournament.availableQualifiedTeams().isEmpty { + self.isEditingTournamentSeed.wrappedValue = false + } } } + } label: { + TeamRowView(team: team, displayCallDate: false) } - } label: { - TeamRowView(team: team, displayCallDate: false) } + } label: { + Text("Tête\(availableSeeds.count.pluralSuffix) de série à placer").badge(availableSeeds.count) } } header: { Text("Tirage au sort visuel d'une tête de série").font(.subheadline) diff --git a/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift b/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift index a0b6373..2f5f3fe 100644 --- a/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift +++ b/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift @@ -147,7 +147,16 @@ struct InscriptionManagerView: View { if self.tournament.shouldVerifyBracket == false || self.tournament.shouldVerifyGroupStage == false { self.tournament.shouldVerifyBracket = true self.tournament.shouldVerifyGroupStage = true + + let waitingList = self.tournament.waitingListTeams(in: self.tournament.selectedSortedTeams()) + waitingList.forEach { team in + if team.bracketPosition != nil || team.groupStagePosition != nil { + team.resetPositions() + } + } + do { + try dataStore.teamRegistrations.addOrUpdate(contentOfs: waitingList) try dataStore.tournaments.addOrUpdate(instance: tournament) } catch { Logger.error(error) diff --git a/PadelClub/Views/Tournament/Shared/TournamentCellView.swift b/PadelClub/Views/Tournament/Shared/TournamentCellView.swift index f71de51..51546f8 100644 --- a/PadelClub/Views/Tournament/Shared/TournamentCellView.swift +++ b/PadelClub/Views/Tournament/Shared/TournamentCellView.swift @@ -27,6 +27,16 @@ struct TournamentCellView: View { } } + var teamCount: Int? { + if let tournament = tournament as? Tournament { + let hasStarted = tournament.inscriptionClosed() || tournament.hasStarted() + let count = hasStarted ? tournament.selectedSortedTeams().count : tournament.unsortedTeamsWithoutWO().count + return count + } else { + return nil + } + } + fileprivate func _spacing() -> CGFloat { self.displayStyle == .short ? 8.0 : 16.0 } @@ -78,10 +88,8 @@ struct TournamentCellView: View { if let tournament = tournament as? Tournament, displayStyle == .wide { if tournament.isCanceled { Text("Annulé").foregroundStyle(.logoRed) - } else { - let hasStarted = tournament.inscriptionClosed() || tournament.hasStarted() - let count = hasStarted ? tournament.selectedSortedTeams().count : tournament.unsortedTeamsWithoutWO().count - Text(count.formatted()) + } else if let teamCount { + Text(teamCount.formatted()) } } else if let federalTournament = tournament as? FederalTournament { Button { @@ -101,10 +109,10 @@ struct TournamentCellView: View { HStack { Text(tournament.durationLabel()) Spacer() - if let tournament = tournament as? Tournament, tournament.isCanceled == false { + if let tournament = tournament as? Tournament, tournament.isCanceled == false, let teamCount { let hasStarted = tournament.inscriptionClosed() || tournament.hasStarted() - let word = hasStarted ? "équipes" : "inscriptions" - Text(word) + let word = hasStarted ? "équipe" : "inscription" + Text(word + teamCount.pluralSuffix) } } Text(tournament.subtitleLabel())