From 835bf8fd3fe7927443c542eebe45eb2fb9137c1b Mon Sep 17 00:00:00 2001 From: Raz Date: Fri, 1 Nov 2024 08:30:39 +0100 Subject: [PATCH] fix bugs with smart planning fix bug with planning view by court fix bug with seed deletion --- PadelClub.xcodeproj/project.pbxproj | 8 +- PadelClub/Data/Match.swift | 2 + PadelClub/Data/MatchScheduler.swift | 4 +- PadelClub/Views/Match/MatchDetailView.swift | 20 ++ PadelClub/Views/Match/MatchSetupView.swift | 2 +- .../Views/Planning/PlanningByCourtView.swift | 13 +- .../Views/Planning/PlanningSettingsView.swift | 222 ++++++++++++------ PadelClub/Views/Round/RoundView.swift | 6 +- 8 files changed, 190 insertions(+), 87 deletions(-) diff --git a/PadelClub.xcodeproj/project.pbxproj b/PadelClub.xcodeproj/project.pbxproj index c383318..8d9035b 100644 --- a/PadelClub.xcodeproj/project.pbxproj +++ b/PadelClub.xcodeproj/project.pbxproj @@ -3222,7 +3222,7 @@ CODE_SIGN_ENTITLEMENTS = PadelClub/PadelClub.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\""; @@ -3246,7 +3246,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.25; + MARKETING_VERSION = 1.0.26; PRODUCT_BUNDLE_IDENTIFIER = app.padelclub; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -3267,7 +3267,7 @@ CODE_SIGN_ENTITLEMENTS = PadelClub/PadelClub.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 1; DEFINES_MODULE = YES; DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\""; DEVELOPMENT_TEAM = BQ3Y44M3Q6; @@ -3290,7 +3290,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.25; + MARKETING_VERSION = 1.0.26; PRODUCT_BUNDLE_IDENTIFIER = app.padelclub; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; diff --git a/PadelClub/Data/Match.swift b/PadelClub/Data/Match.swift index 9d3b181..3e522eb 100644 --- a/PadelClub/Data/Match.swift +++ b/PadelClub/Data/Match.swift @@ -551,6 +551,8 @@ defer { } if startDate == nil { startDate = endDate?.addingTimeInterval(Double(-getDuration()*60)) + } else if let startDate, let endDate, startDate >= endDate { + self.startDate = endDate.addingTimeInterval(Double(-getDuration()*60)) } let teamOne = team(matchDescriptor.winner) diff --git a/PadelClub/Data/MatchScheduler.swift b/PadelClub/Data/MatchScheduler.swift index 3eb7450..f277d11 100644 --- a/PadelClub/Data/MatchScheduler.swift +++ b/PadelClub/Data/MatchScheduler.swift @@ -673,7 +673,7 @@ final class MatchScheduler : ModelObject, Storable { @discardableResult func updateBracketSchedule(tournament: Tournament, fromRoundId roundId: String?, fromMatchId matchId: String?, startDate: Date) -> Bool { let upperRounds: [Round] = tournament.rounds() - let allMatches: [Match] = tournament.allMatches().filter({ $0.hasEnded() == false }) + let allMatches: [Match] = tournament.allMatches().filter({ $0.hasEnded() == false && $0.hasStarted() == false }) var rounds = [Round]() @@ -694,7 +694,7 @@ final class MatchScheduler : ModelObject, Storable { } let flattenedMatches = rounds.flatMap { round in - round._matches().filter({ $0.disabled == false && $0.hasEnded() == false }).sorted(by: \.index) + round._matches().filter({ $0.disabled == false && $0.hasEnded() == false && $0.hasStarted() == false }).sorted(by: \.index) } flattenedMatches.forEach({ diff --git a/PadelClub/Views/Match/MatchDetailView.swift b/PadelClub/Views/Match/MatchDetailView.swift index 0548811..145ba90 100644 --- a/PadelClub/Views/Match/MatchDetailView.swift +++ b/PadelClub/Views/Match/MatchDetailView.swift @@ -354,6 +354,26 @@ struct MatchDetailView: View { Text("Remise-à-zéro") } + if match.teamScores.isEmpty == false { + Divider() + Menu { + ForEach(match.teamScores) { teamScore in + Button(role: .destructive) { + do { + try tournamentStore.teamScores.delete(instance: teamScore) + } catch { + Logger.error(error) + } + match.confirmed = false + _saveMatch() + } label: { + Text(teamScore.team?.teamLabel() ?? "Aucun nom") + } + } + } label: { + Text("Supprimer une équipe") + } + } } label: { LabelOptions() } diff --git a/PadelClub/Views/Match/MatchSetupView.swift b/PadelClub/Views/Match/MatchSetupView.swift index b804de0..1c7e833 100644 --- a/PadelClub/Views/Match/MatchSetupView.swift +++ b/PadelClub/Views/Match/MatchSetupView.swift @@ -205,7 +205,7 @@ struct MatchSetupView: View { } catch { Logger.error(error) } - match.previousMatches().forEach { previousMatch in + if let previousMatch = match.previousMatch(teamPosition) { if previousMatch.disabled { previousMatch.enableMatch() do { diff --git a/PadelClub/Views/Planning/PlanningByCourtView.swift b/PadelClub/Views/Planning/PlanningByCourtView.swift index 2ed79c0..f90edc6 100644 --- a/PadelClub/Views/Planning/PlanningByCourtView.swift +++ b/PadelClub/Views/Planning/PlanningByCourtView.swift @@ -16,6 +16,7 @@ struct PlanningByCourtView: View { @State private var viewByCourt: Bool = false @State private var selectedDay: Date @State private var selectedCourt: Int = 0 + @State private var uuid: UUID = UUID() var timeSlots: [Date:[Match]] { Dictionary(grouping: matches) { $0.startDate ?? .distantFuture } @@ -48,8 +49,14 @@ struct PlanningByCourtView: View { let noStartDate = matches.allSatisfy({ $0.startDate == nil }) List { - _byCourtView(noStartDate: noStartDate) - .id(selectedCourt) + _byCourtView(selectedCourt: selectedCourt, selectedDay: selectedDay, noStartDate: noStartDate) + .id(uuid) + } + .onChange(of: selectedCourt) { + uuid = UUID() + } + .onChange(of: selectedDay) { + uuid = UUID() } .overlay { if noStartDate { @@ -98,7 +105,7 @@ struct PlanningByCourtView: View { } @ViewBuilder - func _byCourtView(noStartDate: Bool) -> some View { + func _byCourtView(selectedCourt: Int, selectedDay: Date, noStartDate: Bool) -> some View { if let _matches = courtSlots[selectedCourt]?.filter({ $0.startDate?.dayInt == selectedDay.dayInt }) { let _sortedMatches = _matches.sorted(by: \.computedStartDateForSorting) if _sortedMatches.isEmpty == false { diff --git a/PadelClub/Views/Planning/PlanningSettingsView.swift b/PadelClub/Views/Planning/PlanningSettingsView.swift index 70957f6..d2ba9f9 100644 --- a/PadelClub/Views/Planning/PlanningSettingsView.swift +++ b/PadelClub/Views/Planning/PlanningSettingsView.swift @@ -143,82 +143,7 @@ struct PlanningSettingsView: View { } } - let allMatches = tournament.allMatches() - let allGroupStages = tournament.allGroupStages() - let allRounds = tournament.allRounds() - let matchesWithDate = allMatches.filter({ $0.startDate != nil }) - - let groupMatchesByDay = _groupMatchesByDay(matches: matchesWithDate) - - let countedSet = _matchCountPerDay(matchesByDay: groupMatchesByDay, tournament: tournament) - - _formatPerDayView(matchCountPerDay: countedSet) - - let groupStagesWithDate = allGroupStages.filter({ $0.startDate != nil }) - let roundsWithDate = allRounds.filter({ $0.startDate != nil }) - if matchesWithDate.isEmpty == false || groupStagesWithDate.isEmpty == false || roundsWithDate.isEmpty == false { - Section { - RowButtonView("Supprimer les horaires des matches", role: .destructive) { - do { - deletingDateMatchesDone = false - allMatches.forEach({ - $0.startDate = nil - $0.confirmed = false - }) - try self.tournamentStore.matches.addOrUpdate(contentOfs: allMatches) - deletingDateMatchesDone = true - } catch { - Logger.error(error) - } - } - } footer: { - Text("Garde les horaires définis pour les poules et les manches.") - } - - Section { - RowButtonView("Supprimer tous les horaires", role: .destructive) { - do { - deletingDone = false - allMatches.forEach({ - $0.startDate = nil - $0.confirmed = false - }) - try self.tournamentStore.matches.addOrUpdate(contentOfs: allMatches) - - allGroupStages.forEach({ $0.startDate = nil }) - try self.tournamentStore.groupStages.addOrUpdate(contentOfs: allGroupStages) - - allRounds.forEach({ $0.startDate = nil }) - try self.tournamentStore.rounds.addOrUpdate(contentOfs: allRounds) - deletingDone = true - } catch { - Logger.error(error) - } - } - } - } - - Section { - if groupStagesWithDate.isEmpty == false { - Text("Des dates de démarrages ont été indiqué pour les poules et seront prises en compte.") - } - if roundsWithDate.isEmpty == false { - Text("Des dates de démarrages ont été indiqué pour les manches et seront prises en compte.") - } - RowButtonView("Horaire intelligent", role: .destructive) { - await MainActor.run { - issueFound = false - schedulingDone = false - } - self.issueFound = await _setupSchedule() - 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() - } + _smartView() } .headerProminence(.increased) .onAppear { @@ -265,6 +190,151 @@ struct PlanningSettingsView: View { } } + @ViewBuilder + private func _smartView() -> some View { + let allMatches = tournament.allMatches().filter({ $0.hasEnded() == false && $0.hasStarted() == false }) + let allGroupStages = tournament.allGroupStages() + let allRounds = tournament.allRounds() + let matchesWithDate = allMatches.filter({ $0.startDate != nil }) + + let groupMatchesByDay = _groupMatchesByDay(matches: matchesWithDate) + + let countedSet = _matchCountPerDay(matchesByDay: groupMatchesByDay, tournament: tournament) + + _formatPerDayView(matchCountPerDay: countedSet) + + let groupStagesWithDate = allGroupStages.filter({ $0.startDate != nil }) + let roundsWithDate = allRounds.filter({ $0.startDate != nil }) + if matchesWithDate.isEmpty == false { + Section { + RowButtonView("Supprimer les horaires des matches", role: .destructive) { + do { + deletingDateMatchesDone = false + allMatches.forEach({ + $0.startDate = nil + $0.confirmed = false + }) + try self.tournamentStore.matches.addOrUpdate(contentOfs: allMatches) + deletingDateMatchesDone = true + } catch { + Logger.error(error) + } + } + } footer: { + Text("Supprime les horaires des matchs restants non démarrés. Garde les horaires définis pour les poules et les manches du tableau.") + } + } + + if groupStagesWithDate.isEmpty == false { + Section { + RowButtonView("Supprimer les horaires des poules", role: .destructive) { + do { + deletingDone = false + allGroupStages.forEach({ $0.startDate = nil }) + try self.tournamentStore.groupStages.addOrUpdate(contentOfs: allGroupStages) + + deletingDone = true + } catch { + Logger.error(error) + } + } + } + } + + if roundsWithDate.isEmpty == false { + Section { + RowButtonView("Supprimer les horaires du tableau", role: .destructive) { + do { + deletingDone = false + allRounds.forEach({ $0.startDate = nil }) + try self.tournamentStore.rounds.addOrUpdate(contentOfs: allRounds) + deletingDone = true + } catch { + Logger.error(error) + } + } + } footer: { + Text("Supprime les horaires définis pour les manches du tableau.") + } + } + + if matchesWithDate.isEmpty == false && groupStagesWithDate.isEmpty == false && roundsWithDate.isEmpty == false { + Section { + RowButtonView("Supprimer tous les horaires", role: .destructive) { + do { + deletingDone = false + allMatches.forEach({ + $0.startDate = nil + $0.confirmed = false + }) + try self.tournamentStore.matches.addOrUpdate(contentOfs: allMatches) + + allGroupStages.forEach({ $0.startDate = nil }) + try self.tournamentStore.groupStages.addOrUpdate(contentOfs: allGroupStages) + + allRounds.forEach({ $0.startDate = nil }) + try self.tournamentStore.rounds.addOrUpdate(contentOfs: allRounds) + deletingDone = true + } catch { + Logger.error(error) + } + } + } footer: { + Text("Supprime les horaires des matchs restants non démarrés, les horaires définis pour les poules et les manches du tableau.") + } + } + + #if DEBUG + Section { + RowButtonView("Supprimer tous les horaires", role: .destructive) { + do { + deletingDone = false + tournament.allMatches().forEach({ + $0.startDate = nil + $0.endDate = nil + $0.confirmed = false + }) + try self.tournamentStore.matches.addOrUpdate(contentOfs: tournament.allMatches()) + + allGroupStages.forEach({ $0.startDate = nil }) + try self.tournamentStore.groupStages.addOrUpdate(contentOfs: allGroupStages) + + allRounds.forEach({ $0.startDate = nil }) + try self.tournamentStore.rounds.addOrUpdate(contentOfs: allRounds) + deletingDone = true + } catch { + Logger.error(error) + } + } + } footer: { + Text("Supprime les horaires des matchs, les horaires définis pour les poules et les manches du tableau.") + } + #endif + + + Section { + if groupStagesWithDate.isEmpty == false { + Text("Des dates de démarrages ont été indiqué pour les poules et seront prises en compte.") + } + if roundsWithDate.isEmpty == false { + Text("Des dates de démarrages ont été indiqué pour les manches et seront prises en compte.") + } + RowButtonView("Horaire intelligent", role: .destructive) { + await MainActor.run { + issueFound = false + schedulingDone = false + } + self.issueFound = await _setupSchedule() + 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() + } + } + @ViewBuilder private func _optionsView() -> some View { List { diff --git a/PadelClub/Views/Round/RoundView.swift b/PadelClub/Views/Round/RoundView.swift index bfe0e13..68c0418 100644 --- a/PadelClub/Views/Round/RoundView.swift +++ b/PadelClub/Views/Round/RoundView.swift @@ -109,7 +109,11 @@ struct RoundView: View { LabeledContent { let status = upperRound.status() if status.0 == status.1 { - Image(systemName: "checkmark").foregroundStyle(.green) + if status.0 == 0 { + Text("aucun match") + } else { + Image(systemName: "checkmark").foregroundStyle(.green) + } } else { Text("\(status.0) terminé\(status.0.pluralSuffix) sur \(status.1)") }