diff --git a/PadelClub.xcodeproj/project.pbxproj b/PadelClub.xcodeproj/project.pbxproj index a05d0c6..1278226 100644 --- a/PadelClub.xcodeproj/project.pbxproj +++ b/PadelClub.xcodeproj/project.pbxproj @@ -157,6 +157,7 @@ FF4AB6BF2B92577A0002987F /* ImportedPlayerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF4AB6BE2B92577A0002987F /* ImportedPlayerView.swift */; }; FF4C7F022BBBD7150031B6A3 /* TabItemModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF4C7F012BBBD7150031B6A3 /* TabItemModifier.swift */; }; FF53FBB82BFB302B0051D4C3 /* ClubCourtSetupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF53FBB72BFB302B0051D4C3 /* ClubCourtSetupView.swift */; }; + FF5647132C0B6F390081F995 /* LoserRoundSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5647122C0B6F380081F995 /* LoserRoundSettingsView.swift */; }; FF59FFB32B90EFAC0061EFF9 /* EventListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF59FFB22B90EFAC0061EFF9 /* EventListView.swift */; }; FF59FFB72B90EFBF0061EFF9 /* MainView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF59FFB62B90EFBF0061EFF9 /* MainView.swift */; }; FF59FFB92B90EFD70061EFF9 /* ToolboxView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF59FFB82B90EFD70061EFF9 /* ToolboxView.swift */; }; @@ -478,6 +479,7 @@ FF4AB6BE2B92577A0002987F /* ImportedPlayerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImportedPlayerView.swift; sourceTree = ""; }; FF4C7F012BBBD7150031B6A3 /* TabItemModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabItemModifier.swift; sourceTree = ""; }; FF53FBB72BFB302B0051D4C3 /* ClubCourtSetupView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClubCourtSetupView.swift; sourceTree = ""; }; + FF5647122C0B6F380081F995 /* LoserRoundSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoserRoundSettingsView.swift; sourceTree = ""; }; FF59FFB22B90EFAC0061EFF9 /* EventListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventListView.swift; sourceTree = ""; }; FF59FFB62B90EFBF0061EFF9 /* MainView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainView.swift; sourceTree = ""; }; FF59FFB82B90EFD70061EFF9 /* ToolboxView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToolboxView.swift; sourceTree = ""; }; @@ -1235,6 +1237,7 @@ FF5DA1922BB9279B00A33061 /* RoundSettingsView.swift */, FFC2DCB12BBE75D40046DB9F /* LoserRoundView.swift */, FFC2DCB32BBE9ECD0046DB9F /* LoserRoundsView.swift */, + FF5647122C0B6F380081F995 /* LoserRoundSettingsView.swift */, ); path = Round; sourceTree = ""; @@ -1621,6 +1624,7 @@ FF1162872BD004AD000C4809 /* EditingTeamView.swift in Sources */, FF6EC9062B947A1000EA7F5A /* NetworkManagerError.swift in Sources */, C4A47D5A2B6D383C00ADC637 /* Tournament.swift in Sources */, + FF5647132C0B6F390081F995 /* LoserRoundSettingsView.swift in Sources */, FF3795662B9399AA004EA093 /* Persistence.swift in Sources */, FF1DF49B2BD8D23900822FA0 /* BarButtonView.swift in Sources */, FFF964502BC25E3700EEF017 /* PlanningView.swift in Sources */, @@ -1935,7 +1939,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 32; + CURRENT_PROJECT_VERSION = 33; DEFINES_MODULE = YES; DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\""; DEVELOPMENT_TEAM = BQ3Y44M3Q6; @@ -1973,7 +1977,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 32; + CURRENT_PROJECT_VERSION = 33; DEFINES_MODULE = YES; DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\""; DEVELOPMENT_TEAM = BQ3Y44M3Q6; diff --git a/PadelClub/Data/Match.swift b/PadelClub/Data/Match.swift index 4453ce3..4b4d864 100644 --- a/PadelClub/Data/Match.swift +++ b/PadelClub/Data/Match.swift @@ -616,7 +616,7 @@ class Match: ModelObject, Storable { } func isReady() -> Bool { - teamScores.count == 2 + teamScores.count >= 2 // teams().count == 2 } @@ -637,8 +637,9 @@ class Match: ModelObject, Storable { round != nil } - func walkoutTeam() -> [TeamRegistration] { - scores().filter({ $0.walkOut != nil }).compactMap { $0.team } + func walkoutTeam() -> [TeamRegistration] { + //walkout 0 means real walkout, walkout 1 means lucky loser situation + scores().filter({ $0.walkOut == 0 }).compactMap { $0.team } } func hasWalkoutTeam() -> Bool { diff --git a/PadelClub/Data/Round.swift b/PadelClub/Data/Round.swift index 7014eab..6926240 100644 --- a/PadelClub/Data/Round.swift +++ b/PadelClub/Data/Round.swift @@ -243,6 +243,10 @@ class Round: ModelObject, Storable { func isDisabled() -> Bool { _matches().allSatisfy({ $0.disabled }) } + + func isRankDisabled() -> Bool { + _matches().allSatisfy({ $0.disabled && $0.teamScores.isEmpty }) + } func resetFromRoundAllMatchesStartDate() { _matches().forEach({ @@ -285,11 +289,12 @@ class Round: ModelObject, Storable { _matches.forEach { match in match.disabled = disable match.resetMatch() - do { - try DataStore.shared.teamScores.delete(contentOfs: match.teamScores) - } catch { - Logger.error(error) - } + //we need to keep teamscores to handle disable ranking match round stuff +// do { +// try DataStore.shared.teamScores.delete(contentOfs: match.teamScores) +// } catch { +// Logger.error(error) +// } } do { try DataStore.shared.matches.addOrUpdate(contentOfs: _matches) diff --git a/PadelClub/Data/TeamRegistration.swift b/PadelClub/Data/TeamRegistration.swift index 98466c3..2ec0663 100644 --- a/PadelClub/Data/TeamRegistration.swift +++ b/PadelClub/Data/TeamRegistration.swift @@ -220,12 +220,25 @@ class TeamRegistration: ModelObject, Storable { bracketPosition != nil } - func resetPositions() { + func resetGroupeStagePosition() { groupStageObject()?._matches().forEach({ $0.updateTeamScores() }) groupStage = nil groupStagePosition = nil - tournamentObject()?.resetTeamScores(in: bracketPosition) - bracketPosition = nil + } + + func resetBracketPosition() { + guard let bracketPosition else { return } + guard let tournamentObject = tournamentObject() else { return } + if let match = tournamentObject.match(for: bracketPosition) { + let teamScores = match.teamScores.filter({ $0.teamRegistration != self.id }) + tournamentObject.resetTeamScores(in: bracketPosition, outsideOf: teamScores) + } + self.bracketPosition = nil + } + + func resetPositions() { + resetGroupeStagePosition() + resetBracketPosition() } func pasteData() -> String { diff --git a/PadelClub/Data/Tournament.swift b/PadelClub/Data/Tournament.swift index ae3193f..14c1c9e 100644 --- a/PadelClub/Data/Tournament.swift +++ b/PadelClub/Data/Tournament.swift @@ -1014,27 +1014,37 @@ class Tournament : ModelObject, Storable { func finalRanking() -> [Int: [String]] { var teams: [Int: [String]] = [:] - + var ids: Set = Set() let rounds = rounds() let final = rounds.last?.playedMatches().last if let winner = final?.winningTeamId { teams[1] = [winner] + ids.insert(winner) } if let finalist = final?.losingTeamId { teams[2] = [finalist] + ids.insert(finalist) } let others : [Round] = rounds.flatMap { round in - round.loserRoundsAndChildren().filter { $0.isDisabled() == false && $0.hasNextRound() == false } + round.loserRoundsAndChildren().filter { $0.isRankDisabled() == false && $0.hasNextRound() == false } }.compactMap({ $0 }) others.forEach { round in if let interval = round.seedInterval() { - let playedMatches = round.playedMatches().filter { $0.disabled == false } - let winners = playedMatches.compactMap({ $0.winningTeamId }) - let losers = playedMatches.compactMap({ $0.losingTeamId }) - teams[interval.first + winners.count - 1] = winners - teams[interval.last] = losers + let playedMatches = round.playedMatches().filter { $0.disabled == false || $0.isReady() } + let winners = playedMatches.compactMap({ $0.winningTeamId }).filter({ ids.contains($0) == false }) + let losers = playedMatches.compactMap({ $0.losingTeamId }).filter({ ids.contains($0) == false }) + if winners.isEmpty { + let disabledIds = playedMatches.flatMap({ $0.teamScores.compactMap({ $0.teamRegistration }) }).filter({ ids.contains($0) == false }) + teams[interval.last] = disabledIds + disabledIds.forEach { ids.insert($0) } + } else { + teams[interval.first + winners.count - 1] = winners + winners.forEach { ids.insert($0) } + teams[interval.last] = losers + losers.forEach { ids.insert($0) } + } } } @@ -1381,9 +1391,9 @@ class Tournament : ModelObject, Storable { return nil } - func resetTeamScores(in matchOfBracketPosition: Int?) { + func resetTeamScores(in matchOfBracketPosition: Int?, outsideOf: [TeamScore] = []) { guard let match = match(for: matchOfBracketPosition) else { return } - match.resetTeamScores(outsideOf: []) + match.resetTeamScores(outsideOf: outsideOf) } func updateTeamScores(in matchOfBracketPosition: Int?) { diff --git a/PadelClub/Views/Round/LoserRoundSettingsView.swift b/PadelClub/Views/Round/LoserRoundSettingsView.swift new file mode 100644 index 0000000..e254e6a --- /dev/null +++ b/PadelClub/Views/Round/LoserRoundSettingsView.swift @@ -0,0 +1,31 @@ +// +// LoserRoundSettingsView.swift +// PadelClub +// +// Created by Razmig Sarkissian on 01/06/2024. +// + +import SwiftUI +import LeStorage + +struct LoserRoundSettingsView: View { + @EnvironmentObject var dataStore: DataStore + @Environment(\.isEditingTournamentSeed) private var isEditingTournamentSeed + @Environment(Tournament.self) var tournament: Tournament + + var body: some View { + List { + Section { + RowButtonView(isEditingTournamentSeed.wrappedValue == true ? "Terminer l'édition" : "Éditer les tours joués") { + isEditingTournamentSeed.wrappedValue.toggle() + } + } + + //todo proposer ici l'impression des matchs de classements peut-être? + } + } +} + +#Preview { + LoserRoundSettingsView() +} diff --git a/PadelClub/Views/Round/LoserRoundView.swift b/PadelClub/Views/Round/LoserRoundView.swift index 89e7ca0..ceb9efa 100644 --- a/PadelClub/Views/Round/LoserRoundView.swift +++ b/PadelClub/Views/Round/LoserRoundView.swift @@ -25,9 +25,9 @@ struct LoserRoundView: View { } ForEach(loserRounds) { loserRound in - if isEditingTournamentSeed || loserRound.isDisabled() == false { + if true { Section { - let matches = (isEditingTournamentSeed ? loserRound.playedMatches() : loserRound.playedMatches().filter({ $0.disabled == false })).sorted(by: \.index) + let matches = loserRound.playedMatches().sorted(by: \.index) ForEach(matches) { match in MatchRowView(match: match, matchViewStyle: .sectionedStandardStyle) .overlay { diff --git a/PadelClub/Views/Round/LoserRoundsView.swift b/PadelClub/Views/Round/LoserRoundsView.swift index 460ba32..b7f2a83 100644 --- a/PadelClub/Views/Round/LoserRoundsView.swift +++ b/PadelClub/Views/Round/LoserRoundsView.swift @@ -59,7 +59,6 @@ extension LoserRound: Equatable { struct LoserRoundsView: View { - @Environment(\.isEditingTournamentSeed) private var isEditingTournamentSeed var upperBracketRound: Round @State private var selectedRound: LoserRound? let loserRounds: [Round] @@ -69,8 +68,7 @@ struct LoserRoundsView: View { self.upperBracketRound = upperBracketRound let _loserRounds = upperBracketRound.loserRounds() self.loserRounds = _loserRounds - let enabledLoserRounds = LoserRound.enabledLoserRounds(inLoserRounds: _loserRounds, inUpperBracketRound: upperBracketRound) - let rounds = LoserRound.updateDestinations(fromLoserRounds: enabledLoserRounds, inUpperBracketRound: upperBracketRound) + let rounds = LoserRound.updateDestinations(fromLoserRounds: _loserRounds, inUpperBracketRound: upperBracketRound) _allDestinations = State(wrappedValue: rounds) _selectedRound = State(wrappedValue: rounds.first(where: { $0.rounds.anySatisfy({ $0.getActiveLoserRound() != nil }) }) ?? rounds.first) @@ -83,14 +81,5 @@ struct LoserRoundsView: View { } .navigationBarTitleDisplayMode(.inline) .toolbarBackground(.visible, for: .navigationBar) - .onChange(of: isEditingTournamentSeed.wrappedValue) { - _updateDestinations() - } - } - - private func _updateDestinations() { - let enabledLoserRounds = isEditingTournamentSeed.wrappedValue ? loserRounds : LoserRound.enabledLoserRounds(inLoserRounds: loserRounds, inUpperBracketRound: upperBracketRound) - - self.allDestinations = LoserRound.updateDestinations(fromLoserRounds: enabledLoserRounds, inUpperBracketRound: upperBracketRound) } } diff --git a/PadelClub/Views/Round/RoundView.swift b/PadelClub/Views/Round/RoundView.swift index 753a01c..ea8f354 100644 --- a/PadelClub/Views/Round/RoundView.swift +++ b/PadelClub/Views/Round/RoundView.swift @@ -168,7 +168,7 @@ struct RoundView: View { SpinDrawView(drawees: [team], segments: spaceLeft) { results in Task { results.forEach { drawResult in - team.setSeedPosition(inSpot: spaceLeft[drawResult.drawIndex], slot: nil, opposingSeeding: false) + team.setSeedPosition(inSpot: spaceLeft[drawResult.drawIndex], slot: nil, opposingSeeding: true) } await _save() if tournament.availableSeeds().isEmpty && tournament.availableQualifiedTeams().isEmpty { diff --git a/PadelClub/Views/Team/EditingTeamView.swift b/PadelClub/Views/Team/EditingTeamView.swift index c239fb5..a42b4af 100644 --- a/PadelClub/Views/Team/EditingTeamView.swift +++ b/PadelClub/Views/Team/EditingTeamView.swift @@ -25,6 +25,22 @@ struct EditingTeamView: View { } header: { Text("Date d'inscription") } + + Section { + RowButtonView("Retirer des poules", role: .destructive) { + team.resetGroupeStagePosition() + _save() + } + .disabled(team.inGroupStage() == false) + } + + Section { + RowButtonView("Retirer du tableau", role: .destructive) { + team.resetBracketPosition() + _save() + } + .disabled(team.inRound() == false) + } } .onChange(of: registrationDate) { team.registrationDate = registrationDate diff --git a/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift b/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift index 551dc4d..d5b27a8 100644 --- a/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift +++ b/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift @@ -184,7 +184,9 @@ struct InscriptionManagerView: View { } else if tournament.unsortedTeams().isEmpty { _inscriptionTipsView() } - _teamRegisteredView() + if _isEditingTeam() == false { + _teamRegisteredView() + } } .onAppear { _getTeams() @@ -961,6 +963,7 @@ struct InscriptionManagerView: View { } } } + .headerProminence(.increased) .onReceive(fetchPlayers.publisher.count()) { _ in // <-- here if let pasteString, count == 2, autoSelect == true { fetchPlayers.filter { $0.hitForSearch(pasteString) >= hitTarget }.sorted(by: { $0.hitForSearch(pasteString) > $1.hitForSearch(pasteString) }).forEach { player in