From cf0259e50b86ecea60fe447e180985f35e65e2a4 Mon Sep 17 00:00:00 2001 From: Razmig Sarkissian Date: Thu, 16 May 2024 10:52:50 +0200 Subject: [PATCH] fix scheduler stuff add open organizer stuff --- PadelClub/Data/MatchScheduler.swift | 12 ++++++++---- PadelClub/Data/Tournament.swift | 9 ++------- PadelClub/ViewModel/NavigationViewModel.swift | 17 +++++++++++++++++ PadelClub/ViewModel/SearchViewModel.swift | 2 +- .../Navigation/Agenda/EventListView.swift | 2 +- .../Organizer/TournamentButtonView.swift | 18 ++++++------------ .../Organizer/TournamentOrganizerView.swift | 18 +++++++----------- .../Views/Planning/PlanningSettingsView.swift | 16 +++++++--------- PadelClubTests/ServerDataTests.swift | 3 +-- 9 files changed, 50 insertions(+), 47 deletions(-) diff --git a/PadelClub/Data/MatchScheduler.swift b/PadelClub/Data/MatchScheduler.swift index 023972e..28dacee 100644 --- a/PadelClub/Data/MatchScheduler.swift +++ b/PadelClub/Data/MatchScheduler.swift @@ -26,7 +26,8 @@ class MatchScheduler : ModelObject, Storable { var rotationDifferenceIsImportant: Bool var shouldHandleUpperRoundSlice: Bool var shouldEndRoundBeforeStartingNext: Bool - + var groupStageChunkCount: Int? + init(tournament: String, timeDifferenceLimit: Int = 5, loserBracketRotationDifference: Int = 0, @@ -36,7 +37,8 @@ class MatchScheduler : ModelObject, Storable { randomizeCourts: Bool = true, rotationDifferenceIsImportant: Bool = false, shouldHandleUpperRoundSlice: Bool = true, - shouldEndRoundBeforeStartingNext: Bool = true) { + shouldEndRoundBeforeStartingNext: Bool = true, + groupStageChunkCount: Int? = nil) { self.tournament = tournament self.timeDifferenceLimit = timeDifferenceLimit self.loserBracketRotationDifference = loserBracketRotationDifference @@ -47,6 +49,7 @@ class MatchScheduler : ModelObject, Storable { self.rotationDifferenceIsImportant = rotationDifferenceIsImportant self.shouldHandleUpperRoundSlice = shouldHandleUpperRoundSlice self.shouldEndRoundBeforeStartingNext = shouldEndRoundBeforeStartingNext + self.groupStageChunkCount = groupStageChunkCount } enum CodingKeys: String, CodingKey { @@ -61,6 +64,7 @@ class MatchScheduler : ModelObject, Storable { case _rotationDifferenceIsImportant = "rotationDifferenceIsImportant" case _shouldHandleUpperRoundSlice = "shouldHandleUpperRoundSlice" case _shouldEndRoundBeforeStartingNext = "shouldEndRoundBeforeStartingNext" + case _groupStageChunkCount = "groupStageChunkCount" } var courtsUnavailability: [DateInterval]? { @@ -77,7 +81,7 @@ class MatchScheduler : ModelObject, Storable { @discardableResult func updateGroupStageSchedule(tournament: Tournament) -> Date { - let groupStageCourtCount = tournament.groupStageCourtCount ?? 1 + let computedGroupStageChunkCount = groupStageChunkCount ?? 1 let groupStages = tournament.groupStages() let numberOfCourtsAvailablePerRotation: Int = tournament.courtCount @@ -116,7 +120,7 @@ class MatchScheduler : ModelObject, Storable { } }) - groupStages.filter({ $0.startDate == nil || times.contains($0.startDate!) == false }).chunked(into: groupStageCourtCount).forEach { groups in + groupStages.filter({ $0.startDate == nil || times.contains($0.startDate!) == false }).chunked(into: computedGroupStageChunkCount).forEach { groups in groups.forEach({ $0.startDate = lastDate }) try? DataStore.shared.groupStages.addOrUpdate(contentOfs: groups) diff --git a/PadelClub/Data/Tournament.swift b/PadelClub/Data/Tournament.swift index ee99eac..f39e06c 100644 --- a/PadelClub/Data/Tournament.swift +++ b/PadelClub/Data/Tournament.swift @@ -32,7 +32,6 @@ class Tournament : ModelObject, Storable { var federalCategory: TournamentCategory var federalLevelCategory: TournamentLevel var federalAgeCategory: FederalTournamentAge - var groupStageCourtCount: Int? var closedRegistrationDate: Date? var groupStageAdditionalQualified: Int var courtCount: Int = 2 @@ -74,7 +73,6 @@ class Tournament : ModelObject, Storable { case _federalCategory = "federalCategory" case _federalLevelCategory = "federalLevelCategory" case _federalAgeCategory = "federalAgeCategory" - case _groupStageCourtCount = "groupStageCourtCount" case _seedCount = "seedCount" case _closedRegistrationDate = "closedRegistrationDate" case _groupStageAdditionalQualified = "groupStageAdditionalQualified" @@ -94,7 +92,7 @@ class Tournament : ModelObject, Storable { case _publishBrackets = "publishBrackets" } - internal init(event: String? = nil, name: String? = nil, startDate: Date = Date(), endDate: Date? = nil, creationDate: Date = Date(), isPrivate: Bool = false, groupStageFormat: MatchFormat? = nil, roundFormat: MatchFormat? = nil, loserRoundFormat: MatchFormat? = nil, groupStageSortMode: GroupStageOrderingMode, groupStageCount: Int = 4, rankSourceDate: Date? = nil, dayDuration: Int = 1, teamCount: Int = 24, teamSorting: TeamSortingType? = nil, federalCategory: TournamentCategory, federalLevelCategory: TournamentLevel, federalAgeCategory: FederalTournamentAge, groupStageCourtCount: Int? = nil, closedRegistrationDate: Date? = nil, groupStageAdditionalQualified: Int = 0, courtCount: Int = 2, prioritizeClubMembers: Bool = false, qualifiedPerGroupStage: Int = 1, teamsPerGroupStage: Int = 4, entryFee: Double? = nil, additionalEstimationDuration: Int = 0, isDeleted: Bool = false, publishTeams: Bool = false, publishSummons: Bool = false, publishGroupStages: Bool = false, publishBrackets: Bool = false) { + internal init(event: String? = nil, name: String? = nil, startDate: Date = Date(), endDate: Date? = nil, creationDate: Date = Date(), isPrivate: Bool = false, groupStageFormat: MatchFormat? = nil, roundFormat: MatchFormat? = nil, loserRoundFormat: MatchFormat? = nil, groupStageSortMode: GroupStageOrderingMode, groupStageCount: Int = 4, rankSourceDate: Date? = nil, dayDuration: Int = 1, teamCount: Int = 24, teamSorting: TeamSortingType? = nil, federalCategory: TournamentCategory, federalLevelCategory: TournamentLevel, federalAgeCategory: FederalTournamentAge, closedRegistrationDate: Date? = nil, groupStageAdditionalQualified: Int = 0, courtCount: Int = 2, prioritizeClubMembers: Bool = false, qualifiedPerGroupStage: Int = 1, teamsPerGroupStage: Int = 4, entryFee: Double? = nil, additionalEstimationDuration: Int = 0, isDeleted: Bool = false, publishTeams: Bool = false, publishSummons: Bool = false, publishGroupStages: Bool = false, publishBrackets: Bool = false) { self.event = event self.name = name self.startDate = startDate @@ -113,7 +111,6 @@ class Tournament : ModelObject, Storable { self.federalCategory = federalCategory self.federalLevelCategory = federalLevelCategory self.federalAgeCategory = federalAgeCategory - self.groupStageCourtCount = groupStageCourtCount self.closedRegistrationDate = closedRegistrationDate self.groupStageAdditionalQualified = groupStageAdditionalQualified self.courtCount = courtCount @@ -150,7 +147,6 @@ class Tournament : ModelObject, Storable { federalCategory = try container.decode(TournamentCategory.self, forKey: ._federalCategory) federalLevelCategory = try container.decode(TournamentLevel.self, forKey: ._federalLevelCategory) federalAgeCategory = try container.decode(FederalTournamentAge.self, forKey: ._federalAgeCategory) - groupStageCourtCount = try container.decodeIfPresent(Int.self, forKey: ._groupStageCourtCount) closedRegistrationDate = try container.decodeIfPresent(Date.self, forKey: ._closedRegistrationDate) groupStageAdditionalQualified = try container.decode(Int.self, forKey: ._groupStageAdditionalQualified) courtCount = try container.decode(Int.self, forKey: ._courtCount) @@ -222,7 +218,6 @@ class Tournament : ModelObject, Storable { try container.encode(federalCategory, forKey: ._federalCategory) try container.encode(federalLevelCategory, forKey: ._federalLevelCategory) try container.encode(federalAgeCategory, forKey: ._federalAgeCategory) - try container.encodeIfPresent(groupStageCourtCount, forKey: ._groupStageCourtCount) try container.encodeIfPresent(closedRegistrationDate, forKey: ._closedRegistrationDate) try container.encode(groupStageAdditionalQualified, forKey: ._groupStageAdditionalQualified) try container.encode(courtCount, forKey: ._courtCount) @@ -1637,7 +1632,7 @@ extension Tournament { } static func fake() -> Tournament { - return Tournament(event: "Roland Garros", name: "Magic P100", startDate: Date(), endDate: Date(), creationDate: Date(), isPrivate: false, groupStageFormat: .nineGames, roundFormat: nil, loserRoundFormat: nil, groupStageSortMode: .snake, groupStageCount: 4, rankSourceDate: nil, dayDuration: 2, teamCount: 24, teamSorting: .rank, federalCategory: .men, federalLevelCategory: .p100, federalAgeCategory: .a45, groupStageCourtCount: nil, closedRegistrationDate: nil, groupStageAdditionalQualified: 0, courtCount: 4, prioritizeClubMembers: false, qualifiedPerGroupStage: 2, teamsPerGroupStage: 4, entryFee: nil) + return Tournament(event: "Roland Garros", name: "Magic P100", startDate: Date(), endDate: Date(), creationDate: Date(), isPrivate: false, groupStageFormat: .nineGames, roundFormat: nil, loserRoundFormat: nil, groupStageSortMode: .snake, groupStageCount: 4, rankSourceDate: nil, dayDuration: 2, teamCount: 24, teamSorting: .rank, federalCategory: .men, federalLevelCategory: .p100, federalAgeCategory: .a45, closedRegistrationDate: nil, groupStageAdditionalQualified: 0, courtCount: 4, prioritizeClubMembers: false, qualifiedPerGroupStage: 2, teamsPerGroupStage: 4, entryFee: nil) } } diff --git a/PadelClub/ViewModel/NavigationViewModel.swift b/PadelClub/ViewModel/NavigationViewModel.swift index dfe589c..722f686 100644 --- a/PadelClub/ViewModel/NavigationViewModel.swift +++ b/PadelClub/ViewModel/NavigationViewModel.swift @@ -16,4 +16,21 @@ class NavigationViewModel { var selectedTab: TabDestination? var agendaDestination: AgendaDestination? = .activity var tournament: Tournament? + var organizerTournament: Tournament? + + func isTournamentAlreadyOpenInOrganizer(_ tournament: Tournament) -> Bool { + organizerTournament?.id == tournament.id + } + + func closeTournamentFromOrganizer(_ tournament: Tournament) { + tournament.navigationPath.removeAll() + organizerTournament = nil + } + + func openTournamentInOrganizer(_ tournament: Tournament) { + organizerTournament = tournament + if selectedTab != .tournamentOrganizer { + selectedTab = .tournamentOrganizer + } + } } diff --git a/PadelClub/ViewModel/SearchViewModel.swift b/PadelClub/ViewModel/SearchViewModel.swift index dc5d794..cb9059d 100644 --- a/PadelClub/ViewModel/SearchViewModel.swift +++ b/PadelClub/ViewModel/SearchViewModel.swift @@ -69,7 +69,7 @@ class SearchViewModel: ObservableObject, Identifiable { } func codeClubs() -> [String] { - DataStore.shared.clubs.compactMap { $0.code } + DataStore.shared.user.clubsObjects().compactMap { $0.code } } func getCodeClub() -> String? { diff --git a/PadelClub/Views/Navigation/Agenda/EventListView.swift b/PadelClub/Views/Navigation/Agenda/EventListView.swift index 61942c7..3011b04 100644 --- a/PadelClub/Views/Navigation/Agenda/EventListView.swift +++ b/PadelClub/Views/Navigation/Agenda/EventListView.swift @@ -103,7 +103,7 @@ struct EventListView: View { } .contextMenu { Button { - + navigation.openTournamentInOrganizer(tournament) } label: { Label("Voir dans le gestionnaire", systemImage: "line.diagonal.arrow") } diff --git a/PadelClub/Views/Navigation/Organizer/TournamentButtonView.swift b/PadelClub/Views/Navigation/Organizer/TournamentButtonView.swift index ff48983..7d7a54b 100644 --- a/PadelClub/Views/Navigation/Organizer/TournamentButtonView.swift +++ b/PadelClub/Views/Navigation/Organizer/TournamentButtonView.swift @@ -8,21 +8,15 @@ import SwiftUI struct TournamentButtonView: View { + @Environment(NavigationViewModel.self) private var navigation let tournament: Tournament - @Binding var selectedId: String? - + var body: some View { Button { - if selectedId == tournament.id { - tournament.navigationPath.removeAll() - selectedId = nil -// if tournament.navigationPath.isEmpty { -// selectedId = nil -// } else { -// tournament.navigationPath.removeLast() -// } + if navigation.isTournamentAlreadyOpenInOrganizer(tournament) { + navigation.closeTournamentFromOrganizer(tournament) } else { - selectedId = tournament.id + navigation.openTournamentInOrganizer(tournament) } } label: { TournamentCellView(tournament: tournament, displayStyle: .short) @@ -34,7 +28,7 @@ struct TournamentButtonView: View { .fixedSize(horizontal: false, vertical: true) } .overlay(alignment: .top) { - if selectedId == tournament.id { + if navigation.isTournamentAlreadyOpenInOrganizer(tournament) { Image(systemName: "ellipsis") .offset(y: -10) } diff --git a/PadelClub/Views/Navigation/Organizer/TournamentOrganizerView.swift b/PadelClub/Views/Navigation/Organizer/TournamentOrganizerView.swift index 6c8f037..72f6afe 100644 --- a/PadelClub/Views/Navigation/Organizer/TournamentOrganizerView.swift +++ b/PadelClub/Views/Navigation/Organizer/TournamentOrganizerView.swift @@ -10,17 +10,13 @@ import LeStorage struct TournamentOrganizerView: View { @EnvironmentObject var dataStore: DataStore - @State private var selectedTournamentId: String? - + @Environment(NavigationViewModel.self) private var navigation + var body: some View { VStack(spacing: 0) { - ForEach(dataStore.tournaments) { tournament in - if tournament.id == selectedTournamentId { - OrganizedTournamentView(tournament: tournament) - } - } - - if selectedTournamentId == nil { + if let tournament = navigation.organizerTournament { + OrganizedTournamentView(tournament: tournament) + } else { NavigationStack { let userClubsEmpty = dataStore.user.clubs.isEmpty ContentUnavailableView( @@ -39,7 +35,7 @@ struct TournamentOrganizerView: View { ScrollView(.horizontal) { HStack { ForEach(dataStore.tournaments) { tournament in - TournamentButtonView(tournament: tournament, selectedId: $selectedTournamentId) + TournamentButtonView(tournament: tournament) } } .padding() @@ -48,7 +44,7 @@ struct TournamentOrganizerView: View { } } .onChange(of: Store.main.currentUserUUID) { - selectedTournamentId = nil + navigation.organizerTournament = nil } } } diff --git a/PadelClub/Views/Planning/PlanningSettingsView.swift b/PadelClub/Views/Planning/PlanningSettingsView.swift index d57f46a..fde3f8c 100644 --- a/PadelClub/Views/Planning/PlanningSettingsView.swift +++ b/PadelClub/Views/Planning/PlanningSettingsView.swift @@ -13,7 +13,7 @@ struct PlanningSettingsView: View { @Bindable var tournament: Tournament @Bindable var matchScheduler: MatchScheduler - @State private var groupStageCourtCount: Int + @State private var groupStageChunkCount: Int @State private var isScheduling: Bool = false @State private var schedulingDone: Bool = false @State private var showOptions: Bool = false @@ -22,10 +22,11 @@ struct PlanningSettingsView: View { self.tournament = tournament if let matchScheduler = tournament.matchScheduler() { self.matchScheduler = matchScheduler + self._groupStageChunkCount = State(wrappedValue: matchScheduler.groupStageChunkCount ?? 1) } else { self.matchScheduler = MatchScheduler(tournament: tournament.id) + self._groupStageChunkCount = State(wrappedValue: 1) } - self._groupStageCourtCount = State(wrappedValue: tournament.groupStageCourtCount ?? 1) } var body: some View { @@ -48,7 +49,7 @@ struct PlanningSettingsView: View { TournamentFieldsManagerView(localizedStringKey: "Terrains maximum", count: $tournament.courtCount) if tournament.groupStages().isEmpty == false { - TournamentFieldsManagerView(localizedStringKey: "Nombre de poule en même temps", count: $groupStageCourtCount, max: tournament.groupStageCount) + TournamentFieldsManagerView(localizedStringKey: "Nombre de poule en même temps", count: $groupStageChunkCount, max: tournament.groupStageCount) } if let event = tournament.eventObject() { @@ -73,6 +74,7 @@ struct PlanningSettingsView: View { RowButtonView("Horaire intelligent", role: .destructive) { schedulingDone = false await _setupSchedule() + _save() schedulingDone = true } } footer: { @@ -114,9 +116,8 @@ struct PlanningSettingsView: View { .deferredRendering(for: .seconds(2)) } } - .onChange(of: groupStageCourtCount) { - tournament.groupStageCourtCount = groupStageCourtCount - _save() + .onChange(of: groupStageChunkCount) { + matchScheduler.groupStageChunkCount = groupStageChunkCount } .onChange(of: tournament.startDate) { _save() @@ -124,9 +125,6 @@ struct PlanningSettingsView: View { .onChange(of: tournament.courtCount) { _save() } - .onChange(of: tournament.groupStageCourtCount) { - _save() - } .onChange(of: tournament.dayDuration) { _save() } diff --git a/PadelClubTests/ServerDataTests.swift b/PadelClubTests/ServerDataTests.swift index ed6b7f8..3fa68ca 100644 --- a/PadelClubTests/ServerDataTests.swift +++ b/PadelClubTests/ServerDataTests.swift @@ -96,7 +96,7 @@ final class ServerDataTests: XCTestCase { return } - let tournament = Tournament(event: eventId, name: "RG Homme", startDate: Date(), endDate: nil, creationDate: Date(), isPrivate: false, groupStageFormat: MatchFormat.megaTie, roundFormat: MatchFormat.nineGames, loserRoundFormat: MatchFormat.nineGamesDecisivePoint, groupStageSortMode: GroupStageOrderingMode.snake, groupStageCount: 2, rankSourceDate: Date(), dayDuration: 5, teamCount: 3, teamSorting: TeamSortingType.rank, federalCategory: TournamentCategory.mix, federalLevelCategory: TournamentLevel.p1000, federalAgeCategory: FederalTournamentAge.a45, groupStageCourtCount: 6, closedRegistrationDate: Date(), groupStageAdditionalQualified: 4, courtCount: 9, prioritizeClubMembers: true, qualifiedPerGroupStage: 1, teamsPerGroupStage: 2, entryFee: 30.0, additionalEstimationDuration: 5, isDeleted: true, publishTeams: true, publishSummons: true, publishGroupStages: true, publishBrackets: true) + let tournament = Tournament(event: eventId, name: "RG Homme", startDate: Date(), endDate: nil, creationDate: Date(), isPrivate: false, groupStageFormat: MatchFormat.megaTie, roundFormat: MatchFormat.nineGames, loserRoundFormat: MatchFormat.nineGamesDecisivePoint, groupStageSortMode: GroupStageOrderingMode.snake, groupStageCount: 2, rankSourceDate: Date(), dayDuration: 5, teamCount: 3, teamSorting: TeamSortingType.rank, federalCategory: TournamentCategory.mix, federalLevelCategory: TournamentLevel.p1000, federalAgeCategory: FederalTournamentAge.a45, closedRegistrationDate: Date(), groupStageAdditionalQualified: 4, courtCount: 9, prioritizeClubMembers: true, qualifiedPerGroupStage: 1, teamsPerGroupStage: 2, entryFee: 30.0, additionalEstimationDuration: 5, isDeleted: true, publishTeams: true, publishSummons: true, publishGroupStages: true, publishBrackets: true) let t = try await Store.main.service().post(tournament) assert(t.event == tournament.event) @@ -117,7 +117,6 @@ final class ServerDataTests: XCTestCase { assert(t.federalCategory == tournament.federalCategory) assert(t.federalLevelCategory == tournament.federalLevelCategory) assert(t.federalAgeCategory == tournament.federalAgeCategory) - assert(t.groupStageCourtCount == tournament.groupStageCourtCount) assert(t.closedRegistrationDate?.formatted() == tournament.closedRegistrationDate?.formatted()) assert(t.groupStageAdditionalQualified == tournament.groupStageAdditionalQualified) assert(t.courtCount == tournament.courtCount)