From e5e3ce6b9da7eb5d17b169adf0c962213c7231a6 Mon Sep 17 00:00:00 2001 From: Razmig Sarkissian Date: Wed, 12 Jun 2024 13:42:27 +0200 Subject: [PATCH] fix stuff --- PadelClub/Data/Club.swift | 2 +- PadelClub/Data/GroupStage.swift | 2 +- PadelClub/Data/Match.swift | 2 +- PadelClub/Data/MatchScheduler.swift | 41 +++++++++++-------- PadelClub/Data/PlayerRegistration.swift | 2 +- PadelClub/Data/Tournament.swift | 22 +++++++--- PadelClub/Utils/DisplayContext.swift | 1 + PadelClub/Utils/PadelRule.swift | 20 ++++----- .../Event/TournamentConfiguratorView.swift | 20 +++++---- .../Components/DateUpdateManagerView.swift | 4 ++ .../Views/Planning/PlanningSettingsView.swift | 26 +++++++++++- .../Team/Components/TeamWeightView.swift | 10 +++-- .../TournamentLevelPickerView.swift | 22 ++++++---- .../Screen/TournamentRankView.swift | 17 ++++++-- .../Shared/TournamentCellView.swift | 10 ++--- .../Views/Tournament/TournamentView.swift | 4 +- PadelClubTests/ServerDataTests.swift | 3 +- 17 files changed, 142 insertions(+), 66 deletions(-) diff --git a/PadelClub/Data/Club.swift b/PadelClub/Data/Club.swift index ee42ba9..196dedb 100644 --- a/PadelClub/Data/Club.swift +++ b/PadelClub/Data/Club.swift @@ -55,7 +55,7 @@ class Club : ModelObject, Storable, Hashable { func clubTitle(_ displayStyle: DisplayStyle = .wide) -> String { switch displayStyle { - case .wide: + case .wide, .title: return name case .short: return acronym diff --git a/PadelClub/Data/GroupStage.swift b/PadelClub/Data/GroupStage.swift index 008dc39..70a9b4c 100644 --- a/PadelClub/Data/GroupStage.swift +++ b/PadelClub/Data/GroupStage.swift @@ -60,7 +60,7 @@ class GroupStage: ModelObject, Storable { func groupStageTitle(_ displayStyle: DisplayStyle = .wide) -> String { if let name { return name } switch displayStyle { - case .wide: + case .wide, .title: return "Poule \(index + 1)" case .short: return "#\(index + 1)" diff --git a/PadelClub/Data/Match.swift b/PadelClub/Data/Match.swift index 3a743b7..6e60b37 100644 --- a/PadelClub/Data/Match.swift +++ b/PadelClub/Data/Match.swift @@ -92,7 +92,7 @@ class Match: ModelObject, Storable { } switch displayStyle { - case .wide: + case .wide, .title: return "Match \(indexInRound(in: matches) + 1)" case .short: return "#\(indexInRound(in: matches) + 1)" diff --git a/PadelClub/Data/MatchScheduler.swift b/PadelClub/Data/MatchScheduler.swift index a85429a..03a49d3 100644 --- a/PadelClub/Data/MatchScheduler.swift +++ b/PadelClub/Data/MatchScheduler.swift @@ -27,6 +27,7 @@ class MatchScheduler : ModelObject, Storable { var shouldHandleUpperRoundSlice: Bool var shouldEndRoundBeforeStartingNext: Bool var groupStageChunkCount: Int? + var overrideCourtsUnavailability: Bool = false init(tournament: String, timeDifferenceLimit: Int = 5, @@ -38,7 +39,7 @@ class MatchScheduler : ModelObject, Storable { rotationDifferenceIsImportant: Bool = false, shouldHandleUpperRoundSlice: Bool = true, shouldEndRoundBeforeStartingNext: Bool = true, - groupStageChunkCount: Int? = nil) { + groupStageChunkCount: Int? = nil, overrideCourtsUnavailability: Bool = false) { self.tournament = tournament self.timeDifferenceLimit = timeDifferenceLimit self.loserBracketRotationDifference = loserBracketRotationDifference @@ -50,6 +51,7 @@ class MatchScheduler : ModelObject, Storable { self.shouldHandleUpperRoundSlice = shouldHandleUpperRoundSlice self.shouldEndRoundBeforeStartingNext = shouldEndRoundBeforeStartingNext self.groupStageChunkCount = groupStageChunkCount + self.overrideCourtsUnavailability = overrideCourtsUnavailability } enum CodingKeys: String, CodingKey { @@ -65,11 +67,12 @@ class MatchScheduler : ModelObject, Storable { case _shouldHandleUpperRoundSlice = "shouldHandleUpperRoundSlice" case _shouldEndRoundBeforeStartingNext = "shouldEndRoundBeforeStartingNext" case _groupStageChunkCount = "groupStageChunkCount" + case _overrideCourtsUnavailability = "overrideCourtsUnavailability" } var courtsUnavailability: [DateInterval]? { guard let event = tournamentObject()?.eventObject() else { return nil } - return event.courtsUnavailability + event.tournamentsCourtsUsed(exluding: tournament) + return event.courtsUnavailability + (overrideCourtsUnavailability ? [] : event.tournamentsCourtsUsed(exluding: tournament)) } var additionalEstimationDuration : Int { @@ -403,7 +406,7 @@ class MatchScheduler : ModelObject, Storable { if rotationIndex > 0, let freeCourtPreviousRotation = freeCourtPerRotation[rotationIndex - 1], freeCourtPreviousRotation.count > 0 { print("scenario where we are waiting for a breaktime to be over without any match to play in between or a free court was available and we need to recheck breaktime left on it") let previousPreviousRotationSlots = slots.filter({ $0.rotationIndex == rotationIndex - 2 && freeCourtPreviousRotation.contains($0.courtIndex) }) - let previousEndDate = getNextStartDate(fromPreviousRotationSlots: previousPreviousRotationSlots, includeBreakTime: true) + let previousEndDate = getNextStartDate(fromPreviousRotationSlots: previousPreviousRotationSlots, includeBreakTime: accountUpperBracketBreakTime) let previousEndDateNoBreak = getNextStartDate(fromPreviousRotationSlots: previousPreviousRotationSlots, includeBreakTime: false) let noBreakAlreadyTested = previousRotationSlots.anySatisfy({ $0.startDate == previousEndDateNoBreak }) @@ -420,7 +423,7 @@ class MatchScheduler : ModelObject, Storable { difference = noBreakAlreadyTested ? differenceWithBreak : max(differenceWithBreak, differenceWithoutBreak) } - if difference > timeDifferenceLimitInSeconds { + if difference > timeDifferenceLimitInSeconds && rotationStartDate.addingTimeInterval(-difference) != previousEndDate { courts.removeAll(where: { index in freeCourtPreviousRotation.contains(index) }) freeCourtPerRotation[rotationIndex] = courts @@ -451,12 +454,12 @@ class MatchScheduler : ModelObject, Storable { } func dispatchCourts(availableCourts: Int, courts: [Int], availableMatchs: inout [Match], slots: inout [TimeMatch], rotationIndex: Int, rotationStartDate: Date, freeCourtPerRotation: inout [Int: [Int]], courtsUnavailability: [DateInterval]?) { - var matchPerRound = [Int: Int]() + var matchPerRound = [String: Int]() var minimumTargetedEndDate: Date = rotationStartDate print("dispatchCourts", courts.sorted(), rotationStartDate, rotationIndex) - courts.sorted().forEach { courtIndex in - print("trying to find a match for \(courtIndex) in \(rotationIndex)") + for (courtPosition, courtIndex) in courts.sorted().enumerated() { if let first = availableMatchs.first(where: { match in + print("trying to find a match for \(courtIndex) in \(rotationIndex)") let roundObject = match.roundObject! let courtsUnavailable = courtsUnavailable(startDate: rotationStartDate, duration: match.matchFormat.getEstimatedDuration(additionalEstimationDuration), courtsUnavailability: courtsUnavailability) print("courtsUnavailable \(courtsUnavailable)") @@ -466,10 +469,11 @@ class MatchScheduler : ModelObject, Storable { let canBePlayed = roundMatchCanBePlayed(match, roundObject: roundObject, slots: slots, rotationIndex: rotationIndex, targetedStartDate: rotationStartDate, minimumTargetedEndDate: &minimumTargetedEndDate) - let currentRotationSameRoundMatches = matchPerRound[roundObject.index] ?? 0 + let currentRotationSameRoundMatches = matchPerRound[roundObject.id] ?? 0 + let roundMatchesCount = roundObject.playedMatches().count + if shouldHandleUpperRoundSlice { - let roundMatchesCount = roundObject.playedMatches().count print("shouldHandleUpperRoundSlice \(roundMatchesCount)") if roundObject.parent == nil && roundMatchesCount > courts.count { print("roundMatchesCount \(roundMatchesCount) > \(courts.count)") @@ -480,24 +484,27 @@ class MatchScheduler : ModelObject, Storable { } } + //if all is ok, we do a final check to see if the first let indexInRound = match.indexInRound() print("Upper Round, index > 0, first Match of round \(indexInRound) and more than one court available; looking for next match (same round) \(indexInRound + 1)") - if roundObject.parent == nil && roundObject.index > 0, indexInRound == 0, courts.count > 1, let nextMatch = match.next() { + if roundObject.parent == nil && roundObject.index > 0, indexInRound == 0, let nextMatch = match.next() { + guard courtPosition < courts.count - 1, courts.count > 1 else { + print("next match and this match can not be played at the same time, returning false") + return false + } if canBePlayed && roundMatchCanBePlayed(nextMatch, roundObject: roundObject, slots: slots, rotationIndex: rotationIndex, targetedStartDate: rotationStartDate, minimumTargetedEndDate: &minimumTargetedEndDate) { print("next match and this match can be played, returning true") return true - } else { - print("next match and this match can not be played at the same time, returning false") - return false } } + //not adding a last match of a 4-match round (final not included obviously) print("\(currentRotationSameRoundMatches) modulo \(currentRotationSameRoundMatches%2) same round match is even, index of round is not 0 and upper bracket. If it's not the last court available \(courtIndex) == \(courts.count - 1)") - if currentRotationSameRoundMatches%2 == 0 && roundObject.index != 0 && roundObject.parent == nil && courtIndex == courts.count - 1 { + if roundMatchesCount <= 4 && currentRotationSameRoundMatches%2 == 0 && roundObject.index != 0 && roundObject.parent == nil && ((courts.count > 1 && courtPosition >= courts.count - 1) || courts.count == 1 && availableCourts > 1) { print("we return false") return false } @@ -508,10 +515,10 @@ class MatchScheduler : ModelObject, Storable { print(first.roundObject!.roundTitle(), first.matchTitle(), courtIndex, rotationStartDate) if first.roundObject!.parent == nil { - if let roundIndex = matchPerRound[first.roundObject!.index] { - matchPerRound[first.roundObject!.index] = roundIndex + 1 + if let roundIndex = matchPerRound[first.roundObject!.id] { + matchPerRound[first.roundObject!.id] = roundIndex + 1 } else { - matchPerRound[first.roundObject!.index] = 1 + matchPerRound[first.roundObject!.id] = 1 } } let timeMatch = TimeMatch(matchID: first.id, rotationIndex: rotationIndex, courtIndex: courtIndex, startDate: rotationStartDate, durationLeft: first.matchFormat.getEstimatedDuration(additionalEstimationDuration), minimumBreakTime: first.matchFormat.breakTime.breakTime) diff --git a/PadelClub/Data/PlayerRegistration.swift b/PadelClub/Data/PlayerRegistration.swift index 595b208..a4a6c1f 100644 --- a/PadelClub/Data/PlayerRegistration.swift +++ b/PadelClub/Data/PlayerRegistration.swift @@ -149,7 +149,7 @@ class PlayerRegistration: ModelObject, Storable { func playerLabel(_ displayStyle: DisplayStyle = .wide) -> String { switch displayStyle { - case .wide: + case .wide, .title: return lastName.trimmed.capitalized + " " + firstName.trimmed.capitalized case .short: let names = lastName.components(separatedBy: .whitespaces) diff --git a/PadelClub/Data/Tournament.swift b/PadelClub/Data/Tournament.swift index 9da10b3..149c66b 100644 --- a/PadelClub/Data/Tournament.swift +++ b/PadelClub/Data/Tournament.swift @@ -52,7 +52,8 @@ class Tournament : ModelObject, Storable { var shouldVerifyBracket: Bool = false var hideTeamsWeight: Bool = false var publishTournament: Bool = false - + var hidePointsEarned: Bool = false + @ObservationIgnored var navigationPath: [Screen] = [] @@ -98,9 +99,10 @@ class Tournament : ModelObject, Storable { case _shouldVerifyBracket = "shouldVerifyBracket" case _hideTeamsWeight = "hideTeamsWeight" case _publishTournament = "publishTournament" + case _hidePointsEarned = "hidePointsEarned" } - 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, shouldVerifyBracket: Bool = false, shouldVerifyGroupStage: Bool = false, hideTeamsWeight: Bool = false, publishTournament: 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, shouldVerifyBracket: Bool = false, shouldVerifyGroupStage: Bool = false, hideTeamsWeight: Bool = false, publishTournament: Bool = false, hidePointsEarned: Bool = false) { self.event = event self.name = name self.startDate = startDate @@ -136,6 +138,7 @@ class Tournament : ModelObject, Storable { self.shouldVerifyGroupStage = shouldVerifyGroupStage self.hideTeamsWeight = hideTeamsWeight self.publishTournament = publishTournament + self.hidePointsEarned = hidePointsEarned } required init(from decoder: Decoder) throws { @@ -178,6 +181,7 @@ class Tournament : ModelObject, Storable { shouldVerifyGroupStage = try container.decodeIfPresent(Bool.self, forKey: ._shouldVerifyGroupStage) ?? false hideTeamsWeight = try container.decodeIfPresent(Bool.self, forKey: ._hideTeamsWeight) ?? false publishTournament = try container.decodeIfPresent(Bool.self, forKey: ._publishTournament) ?? false + hidePointsEarned = try container.decodeIfPresent(Bool.self, forKey: ._hidePointsEarned) ?? false } fileprivate static let _numberFormatter: NumberFormatter = NumberFormatter() @@ -291,6 +295,7 @@ class Tournament : ModelObject, Storable { try container.encode(shouldVerifyGroupStage, forKey: ._shouldVerifyGroupStage) try container.encode(hideTeamsWeight, forKey: ._hideTeamsWeight) try container.encode(publishTournament, forKey: ._publishTournament) + try container.encode(hidePointsEarned, forKey: ._hidePointsEarned) } fileprivate func _encodePayment(container: inout KeyedEncodingContainer) throws { @@ -491,7 +496,7 @@ class Tournament : ModelObject, Storable { func locationLabel(_ displayStyle: DisplayStyle = .wide) -> String { if let club = club() { switch displayStyle { - case .wide: + case .wide, .title: return club.name case .short: return club.acronym @@ -1173,20 +1178,27 @@ class Tournament : ModelObject, Storable { } func tournamentTitle(_ displayStyle: DisplayStyle = .wide) -> String { - [tournamentLevel.localizedLabel(displayStyle) + " " + tournamentCategory.localizedLabel(displayStyle), displayStyle == .wide ? name : nil].compactMap({ $0 }).joined(separator: " - ") + if tournamentLevel == .unlisted, displayStyle == .title, let name { + return name + } + return [tournamentLevel.localizedLabel(displayStyle) + " " + tournamentCategory.localizedLabel(), displayStyle == .wide ? name : nil].compactMap({ $0 }).joined(separator: " - ") } func hideWeight() -> Bool { tournamentLevel.hideWeight() } + func isAnimation() -> Bool { + federalLevelCategory == .unlisted + } + func subtitle(_ displayStyle: DisplayStyle = .wide) -> String { name ?? "" } func formattedDate(_ displayStyle: DisplayStyle = .wide) -> String { switch displayStyle { - case .wide: + case .wide, .title: startDate.formatted(date: Date.FormatStyle.DateStyle.complete, time: Date.FormatStyle.TimeStyle.omitted) case .short: startDate.formatted(date: .numeric, time: .omitted) diff --git a/PadelClub/Utils/DisplayContext.swift b/PadelClub/Utils/DisplayContext.swift index 704454f..1e99890 100644 --- a/PadelClub/Utils/DisplayContext.swift +++ b/PadelClub/Utils/DisplayContext.swift @@ -15,6 +15,7 @@ enum DisplayContext { } enum DisplayStyle { + case title case wide case short } diff --git a/PadelClub/Utils/PadelRule.swift b/PadelClub/Utils/PadelRule.swift index 870b360..e14f1fd 100644 --- a/PadelClub/Utils/PadelRule.swift +++ b/PadelClub/Utils/PadelRule.swift @@ -210,7 +210,7 @@ enum FederalTournamentAge: Int, Hashable, Codable, CaseIterable, Identifiable { func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String { switch self { case .unlisted: - return displayStyle == .wide ? "Aucune" : "" + return displayStyle == .title ? "Aucune" : "" case .a11_12: return "11/12 ans" case .a13_14: @@ -402,7 +402,7 @@ enum TournamentLevel: Int, Hashable, Codable, CaseIterable, Identifiable { } func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String { - if self == .unlisted { return displayStyle == .wide ? "Animation" : "" } + if self == .unlisted { return displayStyle == .title ? "Animation" : "Anim." } return String(describing: self).capitalized } @@ -727,7 +727,7 @@ enum TournamentCategory: Int, Hashable, Codable, CaseIterable, Identifiable { var buildLabel: String { switch self { case .unlisted: - return "-" + return "" case .men: return "H" case .women: @@ -740,7 +740,7 @@ enum TournamentCategory: Int, Hashable, Codable, CaseIterable, Identifiable { var requestLabel: String { switch self { case .unlisted: - return "-" + return "" case .men: return "DM" case .women: @@ -753,7 +753,7 @@ enum TournamentCategory: Int, Hashable, Codable, CaseIterable, Identifiable { var importingRawValue: String { switch self { case .unlisted: - return "-" + return "" case .men: return "messieurs" case .women: @@ -766,13 +766,13 @@ enum TournamentCategory: Int, Hashable, Codable, CaseIterable, Identifiable { func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String { switch self { case .unlisted: - return displayStyle == .wide ? "Aucune" : "" + return displayStyle == .title ? "Aucune" : "" case .men: - return displayStyle == .wide ? "Hommes" : "H" + return displayStyle != .short ? "Hommes" : "H" case .women: - return displayStyle == .wide ? "Dames" : "D" + return displayStyle != .short ? "Dames" : "D" case .mix: - return displayStyle == .wide ? "Mixte" : "MX" + return displayStyle != .short ? "Mixte" : "MX" } } @@ -864,7 +864,7 @@ enum TeamPosition: Int, Identifiable, Hashable, Codable, CaseIterable { } switch displayStyle { - case .wide: + case .wide, .title: return "Équipe " + shortName case .short: return shortName diff --git a/PadelClub/Views/Cashier/Event/TournamentConfiguratorView.swift b/PadelClub/Views/Cashier/Event/TournamentConfiguratorView.swift index b2d79cc..1f36215 100644 --- a/PadelClub/Views/Cashier/Event/TournamentConfiguratorView.swift +++ b/PadelClub/Views/Cashier/Event/TournamentConfiguratorView.swift @@ -20,19 +20,25 @@ struct TournamentConfigurationView: View { @ViewBuilder var body: some View { - Picker(selection: $tournament.federalCategory, label: Text("Catégorie")) { - ForEach(TournamentCategory.allCases) { type in - Text(type.localizedLabel()).tag(type) - } - } Picker(selection: $tournament.federalLevelCategory, label: Text("Niveau")) { ForEach(TournamentLevel.allCases) { type in - Text(type.localizedLabel()).tag(type) + Text(type.localizedLabel(.title)).tag(type) + } + } + .onChange(of: tournament.federalLevelCategory) { + if tournament.federalLevelCategory == .unlisted { + tournament.federalCategory = .unlisted + tournament.federalAgeCategory = .unlisted + } + } + Picker(selection: $tournament.federalCategory, label: Text("Catégorie")) { + ForEach(TournamentCategory.allCases) { type in + Text(type.localizedLabel(.title)).tag(type) } } Picker(selection: $tournament.federalAgeCategory, label: Text("Limite d'âge")) { ForEach(FederalTournamentAge.allCases) { type in - Text(type.localizedLabel()).tag(type) + Text(type.localizedLabel(.title)).tag(type) } } LabeledContent { diff --git a/PadelClub/Views/Planning/Components/DateUpdateManagerView.swift b/PadelClub/Views/Planning/Components/DateUpdateManagerView.swift index 45253be..3151d65 100644 --- a/PadelClub/Views/Planning/Components/DateUpdateManagerView.swift +++ b/PadelClub/Views/Planning/Components/DateUpdateManagerView.swift @@ -76,6 +76,10 @@ struct DatePickingView: View { FooterButtonView("retirer l'horaire bloqué") { currentDate = nil } + } else { + FooterButtonView("bloquer l'horaire") { + currentDate = startDate + } } } .buttonStyle(.borderless) diff --git a/PadelClub/Views/Planning/PlanningSettingsView.swift b/PadelClub/Views/Planning/PlanningSettingsView.swift index 45223f6..f988534 100644 --- a/PadelClub/Views/Planning/PlanningSettingsView.swift +++ b/PadelClub/Views/Planning/PlanningSettingsView.swift @@ -21,10 +21,14 @@ struct PlanningSettingsView: View { init(tournament: Tournament) { self.tournament = tournament if let matchScheduler = tournament.matchScheduler() { + if matchScheduler.groupStageChunkCount == nil { + matchScheduler.groupStageChunkCount = tournament.getGroupStageChunkValue() + } + self.matchScheduler = matchScheduler self._groupStageChunkCount = State(wrappedValue: matchScheduler.groupStageChunkCount ?? tournament.getGroupStageChunkValue()) } else { - self.matchScheduler = MatchScheduler(tournament: tournament.id) + self.matchScheduler = MatchScheduler(tournament: tournament.id, groupStageChunkCount: tournament.getGroupStageChunkValue()) self._groupStageChunkCount = State(wrappedValue: tournament.getGroupStageChunkValue()) } } @@ -101,6 +105,22 @@ struct PlanningSettingsView: View { 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 { + allMatches.forEach({ + $0.startDate = nil + $0.confirmed = false + }) + try dataStore.matches.addOrUpdate(contentOfs: allMatches) + } 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 { @@ -183,6 +203,10 @@ struct PlanningSettingsView: View { } Section { + Toggle(isOn: $matchScheduler.overrideCourtsUnavailability) { + Text("Ne pas tenir compte des autres épreuves") + } + Toggle(isOn: $matchScheduler.randomizeCourts) { Text("Distribuer les terrains au hasard") } diff --git a/PadelClub/Views/Team/Components/TeamWeightView.swift b/PadelClub/Views/Team/Components/TeamWeightView.swift index 896ecfd..e74226c 100644 --- a/PadelClub/Views/Team/Components/TeamWeightView.swift +++ b/PadelClub/Views/Team/Components/TeamWeightView.swift @@ -8,20 +8,22 @@ import SwiftUI struct TeamWeightView: View { - @Environment(Tournament.self) var tournament: Tournament var team: TeamRegistration var teamPosition: TeamPosition? = nil var teamIndex: Int? + var displayWeight: Bool = true init(team: TeamRegistration, teamPosition: TeamPosition? = nil) { self.team = team self.teamPosition = teamPosition - self.teamIndex = team.tournamentObject()?.indexOf(team: team) + let tournament = team.tournamentObject() + self.teamIndex = tournament?.indexOf(team: team) + self.displayWeight = tournament?.hideWeight() == false } var body: some View { VStack(alignment: .trailing, spacing: 0) { - if (teamPosition == .one || teamPosition == nil) && tournament.hideWeight() == false { + if (teamPosition == .one || teamPosition == nil) && displayWeight { Text(team.weight.formatted()) .monospacedDigit() .font(.caption) @@ -30,7 +32,7 @@ struct TeamWeightView: View { Text("#" + (teamIndex + 1).formatted(.number.precision(.integerLength(2...3)))) .monospacedDigit() } - if teamPosition == .two && tournament.hideWeight() == false { + if teamPosition == .two && displayWeight { Text(team.weight.formatted()) .monospacedDigit() .font(.caption) diff --git a/PadelClub/Views/Tournament/Screen/Components/TournamentLevelPickerView.swift b/PadelClub/Views/Tournament/Screen/Components/TournamentLevelPickerView.swift index 5fa5c69..5550b66 100644 --- a/PadelClub/Views/Tournament/Screen/Components/TournamentLevelPickerView.swift +++ b/PadelClub/Views/Tournament/Screen/Components/TournamentLevelPickerView.swift @@ -13,19 +13,27 @@ struct TournamentLevelPickerView: View { var body: some View { @Bindable var tournament = tournament - Picker(selection: $tournament.tournamentCategory, label: Text("Catégorie")) { - ForEach(TournamentCategory.allCases) { type in - Text(type.localizedLabel()).tag(type) - } - } Picker(selection: $tournament.tournamentLevel, label: Text("Niveau")) { ForEach(TournamentLevel.allCases) { type in - Text(type.localizedLabel()).tag(type) + Text(type.localizedLabel(.title)).tag(type) + } + } + .onChange(of: tournament.federalLevelCategory) { + if tournament.federalLevelCategory == .unlisted { + tournament.federalCategory = .unlisted + tournament.federalAgeCategory = .unlisted } } + + Picker(selection: $tournament.tournamentCategory, label: Text("Catégorie")) { + ForEach(TournamentCategory.allCases) { type in + Text(type.localizedLabel(.title)).tag(type) + } + } + Picker(selection: $tournament.federalTournamentAge, label: Text("Limite d'âge")) { ForEach(FederalTournamentAge.allCases) { type in - Text(type.localizedLabel()).tag(type) + Text(type.localizedLabel(.title)).tag(type) } } Picker(selection: $tournament.groupStageOrderingMode, label: Text("Répartition en poule")) { diff --git a/PadelClub/Views/Tournament/Screen/TournamentRankView.swift b/PadelClub/Views/Tournament/Screen/TournamentRankView.swift index fd6b318..70ed34e 100644 --- a/PadelClub/Views/Tournament/Screen/TournamentRankView.swift +++ b/PadelClub/Views/Tournament/Screen/TournamentRankView.swift @@ -16,7 +16,7 @@ struct TournamentRankView: View { var body: some View { List { - + @Bindable var tournament = tournament Section { let matchs = tournament.runningMatches(tournament.allMatches()) let rankingPublished = tournament.selectedSortedTeams().allSatisfy({ $0.finalRanking != nil }) @@ -35,13 +35,24 @@ struct TournamentRankView: View { } label: { Text("Classement publié") } + + Toggle(isOn: $tournament.hidePointsEarned) { + Text("Masquer les points gagnés") + } + .onChange(of: tournament.hidePointsEarned) { + do { + try dataStore.tournaments.addOrUpdate(instance: tournament) + } catch { + Logger.error(error) + } + } RowButtonView(rankingPublished ? "Re-publier le classement" : "Publier le classement", role: .destructive) { rankings.keys.sorted().forEach { rank in if let rankedTeams = rankings[rank] { rankedTeams.forEach { team in team.finalRanking = rank - team.pointsEarned = tournament.tournamentLevel.points(for: rank - 1, count: tournament.teamCount) + team.pointsEarned = tournament.isAnimation() ? nil : tournament.tournamentLevel.points(for: rank - 1, count: tournament.teamCount) } } } @@ -119,7 +130,7 @@ struct TournamentRankView: View { } } - if tournament.hideWeight() == false { + if tournament.isAnimation() == false { Spacer() VStack(alignment: .trailing) { HStack(alignment: .lastTextBaseline, spacing: 0.0) { diff --git a/PadelClub/Views/Tournament/Shared/TournamentCellView.swift b/PadelClub/Views/Tournament/Shared/TournamentCellView.swift index 2d7f3d2..9b2b0f3 100644 --- a/PadelClub/Views/Tournament/Shared/TournamentCellView.swift +++ b/PadelClub/Views/Tournament/Shared/TournamentCellView.swift @@ -46,7 +46,7 @@ struct TournamentCellView: View { switch displayStyle { case .short: DateVerticalView(date: tournament.startDate) - case .wide: + case .wide, .title: VStack(alignment: .center, spacing: -2.0) { Text(tournament.startDate.formatted(.dateTime.weekday(.abbreviated)).uppercased()) .font(.system(size: 14.0)).fontWeight(.medium) @@ -79,8 +79,8 @@ struct TournamentCellView: View { .fontWeight(.semibold) if displayStyle == .wide { VStack(alignment: .leading, spacing: 0) { - Text(build.category.localizedLabel(.short)) - Text(build.age.localizedLabel(.short)) + Text(build.category.localizedLabel()) + Text(build.age.localizedLabel()) } .font(.caption) } @@ -117,8 +117,8 @@ struct TournamentCellView: View { } Text(tournament.subtitleLabel()).lineLimit(1) } else { - Text(build.category.localizedLabel(.short)) - Text(build.age.localizedLabel(.short)) + Text(build.category.localizedLabel()) + Text(build.age.localizedLabel()) } } } diff --git a/PadelClub/Views/Tournament/TournamentView.swift b/PadelClub/Views/Tournament/TournamentView.swift index 52d3868..c9806da 100644 --- a/PadelClub/Views/Tournament/TournamentView.swift +++ b/PadelClub/Views/Tournament/TournamentView.swift @@ -115,7 +115,7 @@ struct TournamentView: View { if let event = tournament.eventObject() { Picker(selection: selectedTournamentId) { ForEach(event.tournaments) { tournament in - Text(tournament.tournamentTitle()).tag(tournament.id as String) + Text(tournament.tournamentTitle(.title)).tag(tournament.id as String) } } label: { @@ -127,7 +127,7 @@ struct TournamentView: View { .toolbar { ToolbarItem(placement: .principal) { VStack(spacing: -4.0) { - Text(tournament.tournamentTitle(.short)).font(.headline) + Text(tournament.tournamentTitle(.title)).font(.headline) Text(tournament.formattedDate()) .font(.subheadline).foregroundStyle(.secondary) } diff --git a/PadelClubTests/ServerDataTests.swift b/PadelClubTests/ServerDataTests.swift index b1d80db..f042080 100644 --- a/PadelClubTests/ServerDataTests.swift +++ b/PadelClubTests/ServerDataTests.swift @@ -99,7 +99,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, 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, shouldVerifyBracket: true, shouldVerifyGroupStage: true, hideTeamsWeight: true, publishTournament: 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, shouldVerifyBracket: true, shouldVerifyGroupStage: true, hideTeamsWeight: true, publishTournament: true, hidePointsEarned: true) let t = try await Store.main.service().post(tournament) assert(t.event == tournament.event) @@ -137,6 +137,7 @@ final class ServerDataTests: XCTestCase { assert(t.shouldVerifyGroupStage == tournament.shouldVerifyGroupStage) assert(t.hideTeamsWeight == tournament.hideTeamsWeight) assert(t.publishTournament == tournament.publishTournament) + assert(t.hidePointsEarned == tournament.hidePointsEarned) } func testGroupStage() async throws {