From 28e94cb348f64b45df74a88553271689558ccb70 Mon Sep 17 00:00:00 2001 From: Raz Date: Wed, 27 Nov 2024 16:14:06 +0100 Subject: [PATCH 01/57] online reg feature --- PadelClub.xcodeproj/project.pbxproj | 16 ++ PadelClub/Data/PlayerRegistration.swift | 17 ++ PadelClub/Data/TeamRegistration.swift | 24 +- PadelClub/Data/Tournament.swift | 13 +- PadelClub/Extensions/Date+Extensions.swift | 5 + PadelClub/Views/Player/PlayerDetailView.swift | 10 +- PadelClub/Views/Shared/DateMenuView.swift | 33 +++ PadelClub/Views/Team/EditingTeamView.swift | 28 ++ .../Screen/InscriptionManagerView.swift | 37 +++ .../Screen/RegisrationSetupView.swift | 244 ++++++++++++++++++ 10 files changed, 419 insertions(+), 8 deletions(-) create mode 100644 PadelClub/Views/Shared/DateMenuView.swift create mode 100644 PadelClub/Views/Tournament/Screen/RegisrationSetupView.swift diff --git a/PadelClub.xcodeproj/project.pbxproj b/PadelClub.xcodeproj/project.pbxproj index acd4aa9..094c04d 100644 --- a/PadelClub.xcodeproj/project.pbxproj +++ b/PadelClub.xcodeproj/project.pbxproj @@ -798,6 +798,12 @@ FFBF41822BF73EB3001B24CB /* EventView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFBF41812BF73EB3001B24CB /* EventView.swift */; }; FFBF41842BF75ED7001B24CB /* EventTournamentsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFBF41832BF75ED7001B24CB /* EventTournamentsView.swift */; }; FFBF41862BF75FDA001B24CB /* EventSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFBF41852BF75FDA001B24CB /* EventSettingsView.swift */; }; + FFBFC3912CEE3A0E000EBD8D /* RegisrationSetupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFBFC3902CEE3A0E000EBD8D /* RegisrationSetupView.swift */; }; + FFBFC3922CEE3A0E000EBD8D /* RegisrationSetupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFBFC3902CEE3A0E000EBD8D /* RegisrationSetupView.swift */; }; + FFBFC3932CEE3A0E000EBD8D /* RegisrationSetupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFBFC3902CEE3A0E000EBD8D /* RegisrationSetupView.swift */; }; + FFBFC3952CF05CBB000EBD8D /* DateMenuView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFBFC3942CF05CBB000EBD8D /* DateMenuView.swift */; }; + FFBFC3962CF05CBB000EBD8D /* DateMenuView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFBFC3942CF05CBB000EBD8D /* DateMenuView.swift */; }; + FFBFC3972CF05CBB000EBD8D /* DateMenuView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFBFC3942CF05CBB000EBD8D /* DateMenuView.swift */; }; FFC1E1042BAC28C6008D6F59 /* ClubSearchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFC1E1032BAC28C6008D6F59 /* ClubSearchView.swift */; }; FFC1E1082BAC29FC008D6F59 /* LocationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFC1E1072BAC29FC008D6F59 /* LocationManager.swift */; }; FFC1E10A2BAC2A77008D6F59 /* NetworkFederalService.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFC1E1092BAC2A77008D6F59 /* NetworkFederalService.swift */; }; @@ -1188,6 +1194,8 @@ FFBF41812BF73EB3001B24CB /* EventView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventView.swift; sourceTree = ""; }; FFBF41832BF75ED7001B24CB /* EventTournamentsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventTournamentsView.swift; sourceTree = ""; }; FFBF41852BF75FDA001B24CB /* EventSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventSettingsView.swift; sourceTree = ""; }; + FFBFC3902CEE3A0E000EBD8D /* RegisrationSetupView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RegisrationSetupView.swift; sourceTree = ""; }; + FFBFC3942CF05CBB000EBD8D /* DateMenuView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateMenuView.swift; sourceTree = ""; }; FFC1E1032BAC28C6008D6F59 /* ClubSearchView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClubSearchView.swift; sourceTree = ""; }; FFC1E1072BAC29FC008D6F59 /* LocationManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationManager.swift; sourceTree = ""; }; FFC1E1092BAC2A77008D6F59 /* NetworkFederalService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkFederalService.swift; sourceTree = ""; }; @@ -1672,6 +1680,7 @@ isa = PBXGroup; children = ( FF70916D2B9108C600AB08DA /* InscriptionManagerView.swift */, + FFBFC3902CEE3A0E000EBD8D /* RegisrationSetupView.swift */, FF90FC1C2C44FB3E009339B2 /* AddTeamView.swift */, FF8F26422BADFE5B00650388 /* TournamentSettingsView.swift */, FF8F26532BAE1E4400650388 /* TableStructureView.swift */, @@ -1772,6 +1781,7 @@ FF663FBD2BE019EC0031AE83 /* TournamentFilterView.swift */, FFE2D2E12C231BEE00D0C7BE /* SupportButtonView.swift */, FFE103112C366E5900684FC9 /* ImagePickerView.swift */, + FFBFC3942CF05CBB000EBD8D /* DateMenuView.swift */, ); path = Shared; sourceTree = ""; @@ -2426,6 +2436,7 @@ C45BAE442BCA753E002EEC8A /* Purchase.swift in Sources */, FF6EC8FE2B94792300EA7F5A /* Screen.swift in Sources */, FF967CEE2BAECBD700A9A3BD /* Round.swift in Sources */, + FFBFC3922CEE3A0E000EBD8D /* RegisrationSetupView.swift in Sources */, FF5BAF6E2BE0B3C8008B4B7E /* FederalDataViewModel.swift in Sources */, FF3F74FF2B91A2D4004CFE0E /* AgendaDestination.swift in Sources */, FF3795622B9396D0004EA093 /* PadelClubApp.xcdatamodeld in Sources */, @@ -2458,6 +2469,7 @@ FF6525C32C8C61B400B9498E /* LoserBracketFromGroupStageView.swift in Sources */, FF5D30512BD94E1000F2B93D /* ImportedPlayer+Extensions.swift in Sources */, FFC1E1042BAC28C6008D6F59 /* ClubSearchView.swift in Sources */, + FFBFC3962CF05CBB000EBD8D /* DateMenuView.swift in Sources */, FF089EBB2BB0120700F0AEC7 /* PlayerPopoverView.swift in Sources */, FF70916E2B9108C600AB08DA /* InscriptionManagerView.swift in Sources */, FF77CE542CCCD1B200CBCBB4 /* MatchFormatPickingView.swift in Sources */, @@ -2711,6 +2723,7 @@ FF4CBFB92C996C0600151637 /* Purchase.swift in Sources */, FF4CBFBA2C996C0600151637 /* Screen.swift in Sources */, FF4CBFBB2C996C0600151637 /* Round.swift in Sources */, + FFBFC3912CEE3A0E000EBD8D /* RegisrationSetupView.swift in Sources */, FF4CBFBC2C996C0600151637 /* FederalDataViewModel.swift in Sources */, FF4CBFBD2C996C0600151637 /* AgendaDestination.swift in Sources */, FF4CBFBE2C996C0600151637 /* PadelClubApp.xcdatamodeld in Sources */, @@ -2743,6 +2756,7 @@ FF4CBFD72C996C0600151637 /* LoserBracketFromGroupStageView.swift in Sources */, FF4CBFD82C996C0600151637 /* ImportedPlayer+Extensions.swift in Sources */, FF4CBFD92C996C0600151637 /* ClubSearchView.swift in Sources */, + FFBFC3972CF05CBB000EBD8D /* DateMenuView.swift in Sources */, FF4CBFDA2C996C0600151637 /* PlayerPopoverView.swift in Sources */, FF4CBFDB2C996C0600151637 /* InscriptionManagerView.swift in Sources */, FF77CE522CCCD1B200CBCBB4 /* MatchFormatPickingView.swift in Sources */, @@ -2975,6 +2989,7 @@ FF70FB382C90584900129CC2 /* Purchase.swift in Sources */, FF70FB392C90584900129CC2 /* Screen.swift in Sources */, FF70FB3A2C90584900129CC2 /* Round.swift in Sources */, + FFBFC3932CEE3A0E000EBD8D /* RegisrationSetupView.swift in Sources */, FF70FB3B2C90584900129CC2 /* FederalDataViewModel.swift in Sources */, FF70FB3C2C90584900129CC2 /* AgendaDestination.swift in Sources */, FF70FB3D2C90584900129CC2 /* PadelClubApp.xcdatamodeld in Sources */, @@ -3007,6 +3022,7 @@ FF70FB562C90584900129CC2 /* LoserBracketFromGroupStageView.swift in Sources */, FF70FB572C90584900129CC2 /* ImportedPlayer+Extensions.swift in Sources */, FF70FB582C90584900129CC2 /* ClubSearchView.swift in Sources */, + FFBFC3952CF05CBB000EBD8D /* DateMenuView.swift in Sources */, FF70FB592C90584900129CC2 /* PlayerPopoverView.swift in Sources */, FF70FB5A2C90584900129CC2 /* InscriptionManagerView.swift in Sources */, FF77CE532CCCD1B200CBCBB4 /* MatchFormatPickingView.swift in Sources */, diff --git a/PadelClub/Data/PlayerRegistration.swift b/PadelClub/Data/PlayerRegistration.swift index 4dbc043..fe6b270 100644 --- a/PadelClub/Data/PlayerRegistration.swift +++ b/PadelClub/Data/PlayerRegistration.swift @@ -38,6 +38,22 @@ final class PlayerRegistration: ModelObject, Storable { var source: PlayerDataSource? var hasArrived: Bool = false + var coach: Bool = false + var captain: Bool = false + + func localizedSourceLabel() -> String { + switch source { + case .frenchFederation: + return "Via la base fédérale" + case .beachPadel: + return "Via le fichier beach-padel" + case .onlineRegistration: + return "Via un inscription en ligne" + case nil: + return "Manuellement" + } + } + init(teamRegistration: String? = nil, firstName: String, lastName: String, licenceId: String? = nil, rank: Int? = nil, paymentType: PlayerPaymentType? = nil, sex: PlayerSexType? = nil, tournamentPlayed: Int? = nil, points: Double? = nil, clubName: String? = nil, ligueName: String? = nil, assimilation: String? = nil, phoneNumber: String? = nil, email: String? = nil, birthdate: String? = nil, computedRank: Int = 0, source: PlayerDataSource? = nil, hasArrived: Bool = false) { self.teamRegistration = teamRegistration @@ -391,6 +407,7 @@ final class PlayerRegistration: ModelObject, Storable { enum PlayerDataSource: Int, Codable { case frenchFederation = 0 case beachPadel = 1 + case onlineRegistration = 2 } enum PlayerSexType: Int, Hashable, CaseIterable, Identifiable, Codable { diff --git a/PadelClub/Data/TeamRegistration.swift b/PadelClub/Data/TeamRegistration.swift index 14909a2..94cd724 100644 --- a/PadelClub/Data/TeamRegistration.swift +++ b/PadelClub/Data/TeamRegistration.swift @@ -39,6 +39,21 @@ final class TeamRegistration: ModelObject, Storable { var finalRanking: Int? var pointsEarned: Int? + var unregistered: Bool = false + var unregistrationDate: Date? = nil + + func hasUnregistered() -> Bool { + unregistered + } + + func hasRegisteredOnline() -> Bool { + players().anySatisfy({ $0.source == .onlineRegistration }) + } + + func isOutOfTournament() -> Bool { + walkOut || unregistered + } + init(tournament: String, groupStage: String? = nil, registrationDate: Date? = nil, callDate: Date? = nil, bracketPosition: Int? = nil, groupStagePosition: Int? = nil, comment: String? = nil, source: String? = nil, sourceValue: String? = nil, logo: String? = nil, name: String? = nil, walkOut: Bool = false, wildCardBracket: Bool = false, wildCardGroupStage: Bool = false, weight: Int = 0, lockedWeight: Int? = nil, confirmationDate: Date? = nil, qualified: Bool = false) { self.tournament = tournament self.groupStage = groupStage @@ -67,7 +82,7 @@ final class TeamRegistration: ModelObject, Storable { // MARK: - Computed dependencies func unsortedPlayers() -> [PlayerRegistration] { - return self.tournamentStore.playerRegistrations.filter { $0.teamRegistration == self.id } + return self.tournamentStore.playerRegistrations.filter { $0.teamRegistration == self.id && $0.coach == false } } // MARK: - @@ -314,6 +329,7 @@ final class TeamRegistration: ModelObject, Storable { } func initialRoundColor() -> Color? { + if unregistered { return Color.black } if walkOut { return Color.logoRed } if groupStagePosition != nil { return Color.blue } if let initialRound = initialRound(), let colorHex = RoundRule.colors[safe: initialRound.index] { @@ -463,7 +479,7 @@ final class TeamRegistration: ModelObject, Storable { func players() -> [PlayerRegistration] { - self.tournamentStore.playerRegistrations.filter { $0.teamRegistration == self.id }.sorted { (lhs, rhs) in + self.unsortedPlayers().sorted { (lhs, rhs) in let predicates: [AreInIncreasingOrder] = [ { $0.sex?.rawValue ?? 0 < $1.sex?.rawValue ?? 0 }, { $0.rank ?? 0 < $1.rank ?? 0 }, @@ -483,6 +499,10 @@ final class TeamRegistration: ModelObject, Storable { } } + func coaches() -> [PlayerRegistration] { + tournamentStore.playerRegistrations.filter({ $0.coach }) + } + func setWeight(from players: [PlayerRegistration], inTournamentCategory tournamentCategory: TournamentCategory) { let significantPlayerCount = significantPlayerCount() weight = (players.prefix(significantPlayerCount).map { $0.computedRank } + missingPlayerType(inTournamentCategory: tournamentCategory).map { unrankValue(for: $0 == 1 ? true : false ) }).prefix(significantPlayerCount).reduce(0,+) diff --git a/PadelClub/Data/Tournament.swift b/PadelClub/Data/Tournament.swift index 132db89..025212a 100644 --- a/PadelClub/Data/Tournament.swift +++ b/PadelClub/Data/Tournament.swift @@ -60,7 +60,12 @@ final class Tournament : ModelObject, Storable { var loserBracketMode: LoserBracketMode = .automatic var initialSeedRound: Int = 0 var initialSeedCount: Int = 0 - + var enableOnlineRegistration: Bool = false + var registrationDateLimit: Date? = nil + var openingRegistrationDate: Date? = nil + var targetTeamCount: Int? = nil + var waitingListLimit: Int? = nil + @ObservationIgnored var navigationPath: [Screen] = [] @@ -851,7 +856,7 @@ defer { } #endif var _sortedTeams : [TeamRegistration] = [] - var _teams = unsortedTeams().filter({ $0.walkOut == false }) + var _teams = unsortedTeams().filter({ $0.isOutOfTournament() == false }) if let closedRegistrationDate { _teams = _teams.filter({ team in @@ -907,7 +912,7 @@ defer { func waitingListTeams(in teams: [TeamRegistration], includingWalkOuts: Bool) -> [TeamRegistration] { let waitingList = Set(unsortedTeams()).subtracting(teams) - let waitings = waitingList.filter { $0.walkOut == false }.sorted(using: _defaultSorting(), order: .ascending) + let waitings = waitingList.filter { $0.isOutOfTournament() == false }.sorted(using: _defaultSorting(), order: .ascending) let walkOuts = waitingList.filter { $0.walkOut == true }.sorted(using: _defaultSorting(), order: .ascending) if includingWalkOuts { return waitings + walkOuts @@ -950,7 +955,7 @@ defer { } func unsortedTeamsWithoutWO() -> [TeamRegistration] { - return self.tournamentStore.teamRegistrations.filter { $0.walkOut == false } + return self.tournamentStore.teamRegistrations.filter { $0.isOutOfTournament() == false } // return Store.main.filter { $0.tournament == self.id && $0.walkOut == false } } diff --git a/PadelClub/Extensions/Date+Extensions.swift b/PadelClub/Extensions/Date+Extensions.swift index de590b3..8214bb7 100644 --- a/PadelClub/Extensions/Date+Extensions.swift +++ b/PadelClub/Extensions/Date+Extensions.swift @@ -254,4 +254,9 @@ extension Date { formatter.unitsStyle = .abbreviated // You can choose .abbreviated or .short return formatter }() + + func truncateMinutesAndSeconds() -> Date { + let calendar = Calendar.current + return calendar.date(bySetting: .minute, value: 0, of: self)!.withoutSeconds() + } } diff --git a/PadelClub/Views/Player/PlayerDetailView.swift b/PadelClub/Views/Player/PlayerDetailView.swift index 69c153a..fe4a275 100644 --- a/PadelClub/Views/Player/PlayerDetailView.swift +++ b/PadelClub/Views/Player/PlayerDetailView.swift @@ -34,7 +34,11 @@ struct PlayerDetailView: View { Form { Section { Toggle("Joueur sur place", isOn: $player.hasArrived) - + Toggle("Capitaine", isOn: $player.captain) + Toggle("Coach", isOn: $player.coach) + } + + Section { LabeledContent { TextField("Nom", text: $player.lastName) .keyboardType(.alphabet) @@ -66,6 +70,8 @@ struct PlayerDetailView: View { if let birthdate = player.birthdate { Text(birthdate) } + } footer: { + Text(player.localizedSourceLabel()) } Section { @@ -223,7 +229,7 @@ struct PlayerDetailView: View { } } } - .onChange(of: player.hasArrived) { + .onChange(of: [player.hasArrived, player.captain, player.coach]) { _save() } .onChange(of: player.sex) { diff --git a/PadelClub/Views/Shared/DateMenuView.swift b/PadelClub/Views/Shared/DateMenuView.swift new file mode 100644 index 0000000..cfcce39 --- /dev/null +++ b/PadelClub/Views/Shared/DateMenuView.swift @@ -0,0 +1,33 @@ +// +// DateMenuView.swift +// PadelClub +// +// Created by razmig on 22/11/2024. +// + + +import SwiftUI + +struct DateMenuView: View { + @Binding var date: Date + + private func adjustDate(byDays days: Int) { + let calendar = Calendar.current + if let newDate = calendar.date(byAdding: .day, value: days, to: date) { + date = newDate.truncateMinutesAndSeconds() + } + } + + var body: some View { + Menu { + Button("24h avant") { adjustDate(byDays: -1) } + Button("48h avant") { adjustDate(byDays: -2) } + Divider() + Button("24h après") { adjustDate(byDays: 1) } + Button("48h après") { adjustDate(byDays: 2) } + } label: { + Text("Ajuster la date") + .underline() + } + } +} diff --git a/PadelClub/Views/Team/EditingTeamView.swift b/PadelClub/Views/Team/EditingTeamView.swift index f8cc5d5..9432a3e 100644 --- a/PadelClub/Views/Team/EditingTeamView.swift +++ b/PadelClub/Views/Team/EditingTeamView.swift @@ -94,6 +94,34 @@ struct EditingTeamView: View { } } + Toggle(isOn: .init(get: { + return team.unregistered + }, set: { value in + team.resetPositions() + team.wildCardGroupStage = false + team.walkOut = false + team.wildCardBracket = false + team.unregistered = value + if value { + team.unregistrationDate = Date() + } else { + team.unregistered = false + team.unregistrationDate = nil + team.registrationDate = Date() + } + do { + try tournamentStore.teamRegistrations.addOrUpdate(instance: team) + } catch { + Logger.error(error) + } + })) { + Text("Annulation") + if let unregisterDate = team.unregistrationDate { + Text(unregisterDate.localizedDate()) + } + } + + Toggle(isOn: .init(get: { return team.wildCardBracket }, set: { value in diff --git a/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift b/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift index 7dd6dc2..8a6e2f4 100644 --- a/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift +++ b/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift @@ -72,8 +72,10 @@ struct InscriptionManagerView: View { enum FilterMode: Int, Identifiable, CaseIterable { var id: Int { self.rawValue } case all + case registeredOnline case walkOut case waiting + case unregistered case bracket case groupStage case wildcardGroupStage @@ -88,6 +90,8 @@ struct InscriptionManagerView: View { return "Vous n'avez aucune wildcard en poule." case .all: return "Vous n'avez encore aucune équipe inscrite." + case .registeredOnline: + return "Aucune équipe inscrite en ligne." case .walkOut: return "Vous n'avez aucune équipe forfait." case .waiting: @@ -98,6 +102,8 @@ struct InscriptionManagerView: View { return "Vous n'avez placé aucune équipe en poule." case .notImported: return "Vous n'avez aucune équipe non importé. Elles proviennent toutes du fichier." + case .unregistered: + return "Vous n'avez aucune équipe ayant annulé une inscription." } } @@ -109,6 +115,8 @@ struct InscriptionManagerView: View { return "Aucune wildcard en poule" case .all: return "Aucune équipe inscrite" + case .registeredOnline: + return "Aucune équipe inscrite en ligne" case .walkOut: return "Aucune équipe forfait" case .waiting: @@ -119,6 +127,8 @@ struct InscriptionManagerView: View { return "Aucune équipe en poule" case .notImported: return "Aucune équipe non importée" + case .unregistered: + return "Aucune équipe ayant annulé" } } @@ -130,6 +140,8 @@ struct InscriptionManagerView: View { return displayStyle == .wide ? "Wildcard Poule" : "wc poule" case .all: return displayStyle == .wide ? "Équipes inscrites" : "inscris" + case .registeredOnline: + return displayStyle == .wide ? "Inscrites en ligne" : "en ligne" case .bracket: return displayStyle == .wide ? "En Tableau" : "tableau" case .groupStage: @@ -140,10 +152,13 @@ struct InscriptionManagerView: View { return displayStyle == .wide ? "Liste d'attente" : "attente" case .notImported: return "Non importées" + case .unregistered: + return displayStyle == .wide ? "Retirées" : "retirées" } } } + init(tournament: Tournament) { self.tournament = tournament _currentRankSourceDate = State(wrappedValue: tournament.rankSourceDate) @@ -229,6 +244,16 @@ struct InscriptionManagerView: View { } } } + .refreshable { + do { + self.tournament.tournamentStore.teamRegistrations.reset() + try await self.tournament.tournamentStore.teamRegistrations.loadDataFromServerIfAllowed() + self.tournament.tournamentStore.playerRegistrations.reset() + try await self.tournament.tournamentStore.playerRegistrations.loadDataFromServerIfAllowed() + } catch { + Logger.error(error) + } + } .onAppear { _setHash() } @@ -320,6 +345,12 @@ struct InscriptionManagerView: View { .symbolVariant(filterMode == .all ? .none : .fill) } Menu { + NavigationLink { + RegisrationSetupView(tournament: tournament) + } label: { + Text("Inscription en ligne") + } + if tournament.isAnimation() == false { if tournament.inscriptionClosed() == false { Menu { @@ -664,6 +695,12 @@ struct InscriptionManagerView: View { case .notImported: let notImported: Int = max(0, sortedTeams.filter({ $0.isImported() == false }).count) return notImported.formatted() + case .unregistered: + let unregistered: Int = max(0, sortedTeams.filter({ $0.hasUnregistered() }).count) + return unregistered.formatted() + case .registeredOnline: + let registeredOnline: Int = max(0, sortedTeams.filter({ $0.hasRegisteredOnline() }).count) + return registeredOnline.formatted() } } diff --git a/PadelClub/Views/Tournament/Screen/RegisrationSetupView.swift b/PadelClub/Views/Tournament/Screen/RegisrationSetupView.swift new file mode 100644 index 0000000..aa2e266 --- /dev/null +++ b/PadelClub/Views/Tournament/Screen/RegisrationSetupView.swift @@ -0,0 +1,244 @@ +// +// RegisrationSetupView.swift +// PadelClub +// +// Created by razmig on 20/11/2024. +// + +import SwiftUI +import LeStorage + +struct RegisrationSetupView: View { + @EnvironmentObject var dataStore: DataStore + @Bindable var tournament: Tournament + @State private var enableOnlineRegistration: Bool + @State private var registrationDateLimit: Date + @State private var openingRegistrationDate: Date + @State private var targetTeamCount: Int + @State private var waitingListLimit: Int + @State private var registrationDateLimitEnabled: Bool + @State private var targetTeamCountEnabled: Bool + @State private var waitingListLimitEnabled: Bool + @State private var openingRegistrationDateEnabled: Bool + @State private var hasChanges: Bool = false + + @Environment(\.dismiss) private var dismiss + + init(tournament: Tournament) { + self.tournament = tournament + _enableOnlineRegistration = .init(wrappedValue: tournament.enableOnlineRegistration) + + // Registration Date Limit + if let registrationDateLimit = tournament.registrationDateLimit { + _registrationDateLimit = .init(wrappedValue: registrationDateLimit) + _registrationDateLimitEnabled = .init(wrappedValue: true) + } else { + _registrationDateLimit = .init(wrappedValue: tournament.startDate.truncateMinutesAndSeconds()) + _registrationDateLimitEnabled = .init(wrappedValue: false) + } + + // Opening Registration Date + if let openingRegistrationDate = tournament.openingRegistrationDate { + _openingRegistrationDate = .init(wrappedValue: openingRegistrationDate) + _openingRegistrationDateEnabled = .init(wrappedValue: true) + } else { + _openingRegistrationDate = .init(wrappedValue: tournament.creationDate.truncateMinutesAndSeconds()) + _openingRegistrationDateEnabled = .init(wrappedValue: false) + } + + // Target Team Count + if let targetTeamCount = tournament.targetTeamCount { + _targetTeamCount = .init(wrappedValue: targetTeamCount) + _targetTeamCountEnabled = .init(wrappedValue: true) + } else { + _targetTeamCount = .init(wrappedValue: tournament.teamCount) // Default value + _targetTeamCountEnabled = .init(wrappedValue: false) + } + + // Waiting List Limit + if let waitingListLimit = tournament.waitingListLimit { + _waitingListLimit = .init(wrappedValue: waitingListLimit) + _waitingListLimitEnabled = .init(wrappedValue: true) + } else { + _waitingListLimit = .init(wrappedValue: 0) // Default value + _waitingListLimitEnabled = .init(wrappedValue: false) + } + } + + var body: some View { + List { + Section { + Toggle(isOn: $enableOnlineRegistration) { + Text("Inscription en ligne") + } + } + + if enableOnlineRegistration { + + Section { + Toggle(isOn: $openingRegistrationDateEnabled) { + Text("Date d'ouverture des inscriptions") + } + + if openingRegistrationDateEnabled { + DatePicker(selection: $openingRegistrationDate, in: tournament.creationDate.truncateMinutesAndSeconds()...tournament.startDate.truncateMinutesAndSeconds()) { + DateMenuView(date: $openingRegistrationDate) + } + } + } header: { + Text("Date d'ouverture des inscriptions") + } footer: { + Text("Activez et définissez une date d'ouverture pour les inscriptions au tournoi.") + } + + Section { + Toggle(isOn: $registrationDateLimitEnabled) { + Text("Activer une limite") + } + + if registrationDateLimitEnabled { + DatePicker(selection: $registrationDateLimit, in: tournament.creationDate.truncateMinutesAndSeconds()...tournament.startDate.truncateMinutesAndSeconds()) { + DateMenuView(date: $registrationDateLimit) + } + } + } header: { + Text("Date de fermeture des inscriptions") + } footer: { + Text("Activez et définissez une date limite pour l'inscription en ligne.") + } + + Section { + Toggle(isOn: $targetTeamCountEnabled) { + Text("Nombre d'équipes cible") + } + + if targetTeamCountEnabled { + StepperView(count: $targetTeamCount, minimum: 4) + } + } header: { + Text("Nombre d'équipes cible") + } footer: { + Text("Activez et définissez le nombre d'équipes cible pour le tournoi.") + } + + Section { + Toggle(isOn: $waitingListLimitEnabled) { + Text("Limite de la liste d'attente") + } + + if waitingListLimitEnabled { + StepperView(count: $waitingListLimit, minimum: 0) + } + } header: { + Text("Limite de la liste d'attente") + } footer: { + Text("Activez et définissez une limite pour la liste d'attente des équipes.") + } + } else { + ContentUnavailableView( + "Activez les inscriptions en ligne", + systemImage: "person.2.crop.square.stack.fill", + description: Text("Permettez aux joueurs de s'inscrire eux-mêmes à ce tournoi. Les équipes inscrites apparaîtront automatiquement dans la liste de l'arbitre. L'inscription en ligne requiert un email de contact et une licence FFT.") + ) + } + } + .toolbar(content: { + if hasChanges { + ToolbarItem(placement: .topBarLeading) { + Button("Annuler", role: .cancel) { + dismiss() + } + } + + ToolbarItem(placement: .topBarTrailing) { + ButtonValidateView(role: .destructive) { + _save() + dismiss() + } + } + } + }) + .toolbarRole(.editor) + .headerProminence(.increased) + .navigationTitle("Inscription en ligne") + .navigationBarTitleDisplayMode(.inline) + .toolbarBackground(.visible, for: .navigationBar) + .navigationBarBackButtonHidden(hasChanges) + .onChange(of: enableOnlineRegistration, { + _hasChanged() + }) + .onChange(of: openingRegistrationDateEnabled) { + _hasChanged() + } + + .onChange(of: openingRegistrationDate) { + _hasChanged() + } + + .onChange(of: registrationDateLimitEnabled) { + _hasChanged() + } + + .onChange(of: registrationDateLimit) { + _hasChanged() + } + + .onChange(of: targetTeamCountEnabled) { + _hasChanged() + } + + .onChange(of: targetTeamCount) { + _hasChanged() + } + + .onChange(of: waitingListLimitEnabled) { + _hasChanged() + } + + .onChange(of: waitingListLimit) { + _hasChanged() + } + } + + private func _hasChanged() { + hasChanges = true + } + + private func _save() { + hasChanges = false + + tournament.enableOnlineRegistration = enableOnlineRegistration + + if openingRegistrationDateEnabled == false { + tournament.openingRegistrationDate = nil + } else { + tournament.openingRegistrationDate = openingRegistrationDate + } + + if registrationDateLimitEnabled == false { + tournament.registrationDateLimit = nil + } else { + tournament.registrationDateLimit = registrationDateLimit + } + + if targetTeamCountEnabled == false { + tournament.targetTeamCount = nil + } else { + tournament.targetTeamCount = targetTeamCount + } + + if waitingListLimitEnabled == false { + tournament.waitingListLimit = nil + } else { + tournament.waitingListLimit = waitingListLimit + } + + do { + try self.dataStore.tournaments.addOrUpdate(instance: tournament) + } catch { + Logger.error(error) + } + + dismiss() + } +} From 23810314525453649889e5a74531591f50dbda81 Mon Sep 17 00:00:00 2001 From: Raz Date: Fri, 29 Nov 2024 11:00:37 +0100 Subject: [PATCH 02/57] tiebreak value wip --- PadelClub/Data/Match.swift | 4 ++-- PadelClub/ViewModel/MatchDescriptor.swift | 4 ++-- PadelClub/ViewModel/SetDescriptor.swift | 19 +++++++++++++++++++ 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/PadelClub/Data/Match.swift b/PadelClub/Data/Match.swift index 09775dd..389543a 100644 --- a/PadelClub/Data/Match.swift +++ b/PadelClub/Data/Match.swift @@ -832,8 +832,8 @@ defer { if teamPosition == team(.two)?.groupStagePositionAtStep(step) { reverseValue = -1 } - let endedSetsOne = teamScoreTeam.score?.components(separatedBy: ",").compactMap({ Int($0) }) ?? matchFormat.defaultWalkOutScore(teamScoreTeam.isWalkOut()) - let endedSetsTwo = teamScoreOtherTeam.score?.components(separatedBy: ",").compactMap({ Int($0) }) ?? matchFormat.defaultWalkOutScore(teamScoreOtherTeam.isWalkOut()) + let endedSetsOne = teamScoreTeam.score?.components(separatedBy: ",").compactMap({ $0.components(separatedBy: "-").first }).compactMap({ Int($0) }) ?? matchFormat.defaultWalkOutScore(teamScoreTeam.isWalkOut()) + let endedSetsTwo = teamScoreOtherTeam.score?.components(separatedBy: ",").compactMap({ $0.components(separatedBy: "-").first }).compactMap({ Int($0) }) ?? matchFormat.defaultWalkOutScore(teamScoreOtherTeam.isWalkOut()) var setDifference : Int = 0 let zip = zip(endedSetsOne, endedSetsTwo) if matchFormat.setsToWin == 1 { diff --git a/PadelClub/ViewModel/MatchDescriptor.swift b/PadelClub/ViewModel/MatchDescriptor.swift index dd88446..6bea89e 100644 --- a/PadelClub/ViewModel/MatchDescriptor.swift +++ b/PadelClub/ViewModel/MatchDescriptor.swift @@ -94,11 +94,11 @@ class MatchDescriptor: ObservableObject { } var teamOneScores: [String] { - setDescriptors.compactMap { $0.valueTeamOne }.map { "\($0)" } + setDescriptors.compactMap { $0.getValue(teamPosition: .one) } } var teamTwoScores: [String] { - setDescriptors.compactMap { $0.valueTeamTwo }.map { "\($0)" } + setDescriptors.compactMap { $0.getValue(teamPosition: .two) } } var scoreTeamOne: Int { setDescriptors.compactMap { $0.winner }.filter { $0 == .one }.count } diff --git a/PadelClub/ViewModel/SetDescriptor.swift b/PadelClub/ViewModel/SetDescriptor.swift index 6ef5bfe..eb04555 100644 --- a/PadelClub/ViewModel/SetDescriptor.swift +++ b/PadelClub/ViewModel/SetDescriptor.swift @@ -40,4 +40,23 @@ struct SetDescriptor: Identifiable, Equatable { var shouldTieBreak: Bool { setFormat.shouldTiebreak(scoreTeamOne: valueTeamOne ?? 0, scoreTeamTwo: valueTeamTwo ?? 0) } + + func getValue(teamPosition: TeamPosition) -> String? { + switch teamPosition { + case .one: + if let valueTeamOne { + if let tieBreakValueTeamOne { + return "\(valueTeamOne)-\(tieBreakValueTeamOne)" + } + } + case .two: + if let valueTeamTwo { + if let tieBreakValueTeamTwo { + return "\(valueTeamTwo)-\(tieBreakValueTeamTwo)" + } + } + } + + return nil + } } From 9a8f9f8949370c7a901a48642de338577ec4bb33 Mon Sep 17 00:00:00 2001 From: Raz Date: Fri, 29 Nov 2024 15:53:15 +0100 Subject: [PATCH 03/57] improve online registration --- PadelClub/Data/PlayerRegistration.swift | 37 ++++++++++++- PadelClub/Data/TeamRegistration.swift | 40 +++++++++++++- PadelClub/Data/Tournament.swift | 40 ++++++++++++-- PadelClub/ViewModel/SetDescriptor.swift | 4 ++ .../Match/Components/PlayerBlockView.swift | 28 +++++++--- .../TournamentGeneralSettingsView.swift | 28 +++++++++- .../Screen/InscriptionManagerView.swift | 22 +++++++- .../Screen/RegisrationSetupView.swift | 53 +++++++++++++++++++ .../Screen/TournamentSettingsView.swift | 16 +++++- PadelClubTests/ServerDataTests.swift | 21 ++++++-- 10 files changed, 269 insertions(+), 20 deletions(-) diff --git a/PadelClub/Data/PlayerRegistration.swift b/PadelClub/Data/PlayerRegistration.swift index fe6b270..0ffc213 100644 --- a/PadelClub/Data/PlayerRegistration.swift +++ b/PadelClub/Data/PlayerRegistration.swift @@ -55,7 +55,7 @@ final class PlayerRegistration: ModelObject, Storable { } - init(teamRegistration: String? = nil, firstName: String, lastName: String, licenceId: String? = nil, rank: Int? = nil, paymentType: PlayerPaymentType? = nil, sex: PlayerSexType? = nil, tournamentPlayed: Int? = nil, points: Double? = nil, clubName: String? = nil, ligueName: String? = nil, assimilation: String? = nil, phoneNumber: String? = nil, email: String? = nil, birthdate: String? = nil, computedRank: Int = 0, source: PlayerDataSource? = nil, hasArrived: Bool = false) { + init(teamRegistration: String? = nil, firstName: String, lastName: String, licenceId: String? = nil, rank: Int? = nil, paymentType: PlayerPaymentType? = nil, sex: PlayerSexType? = nil, tournamentPlayed: Int? = nil, points: Double? = nil, clubName: String? = nil, ligueName: String? = nil, assimilation: String? = nil, phoneNumber: String? = nil, email: String? = nil, birthdate: String? = nil, computedRank: Int = 0, source: PlayerDataSource? = nil, hasArrived: Bool = false, coach: Bool = false, captain: Bool = false) { self.teamRegistration = teamRegistration self.firstName = firstName self.lastName = lastName @@ -74,6 +74,8 @@ final class PlayerRegistration: ModelObject, Storable { self.computedRank = computedRank self.source = source self.hasArrived = hasArrived + self.captain = captain + self.coach = coach } internal init(importedPlayer: ImportedPlayer) { @@ -376,9 +378,40 @@ final class PlayerRegistration: ModelObject, Storable { case _computedRank = "computedRank" case _source = "source" case _hasArrived = "hasArrived" + case _coach = "coach" + case _captain = "captain" } + init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + + // Non-optional properties + id = try container.decodeIfPresent(String.self, forKey: ._id) ?? Store.randomId() + firstName = try container.decode(String.self, forKey: ._firstName) + lastName = try container.decode(String.self, forKey: ._lastName) + computedRank = try container.decodeIfPresent(Int.self, forKey: ._computedRank) ?? 0 + hasArrived = try container.decodeIfPresent(Bool.self, forKey: ._hasArrived) ?? false + coach = try container.decodeIfPresent(Bool.self, forKey: ._coach) ?? false + captain = try container.decodeIfPresent(Bool.self, forKey: ._captain) ?? false + + // Optional properties + teamRegistration = try container.decodeIfPresent(String.self, forKey: ._teamRegistration) + licenceId = try container.decodeIfPresent(String.self, forKey: ._licenceId) + rank = try container.decodeIfPresent(Int.self, forKey: ._rank) + paymentType = try container.decodeIfPresent(PlayerPaymentType.self, forKey: ._paymentType) + sex = try container.decodeIfPresent(PlayerSexType.self, forKey: ._sex) + tournamentPlayed = try container.decodeIfPresent(Int.self, forKey: ._tournamentPlayed) + points = try container.decodeIfPresent(Double.self, forKey: ._points) + clubName = try container.decodeIfPresent(String.self, forKey: ._clubName) + ligueName = try container.decodeIfPresent(String.self, forKey: ._ligueName) + assimilation = try container.decodeIfPresent(String.self, forKey: ._assimilation) + phoneNumber = try container.decodeIfPresent(String.self, forKey: ._phoneNumber) + email = try container.decodeIfPresent(String.self, forKey: ._email) + birthdate = try container.decodeIfPresent(String.self, forKey: ._birthdate) + source = try container.decodeIfPresent(PlayerDataSource.self, forKey: ._source) + } + func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) @@ -402,6 +435,8 @@ final class PlayerRegistration: ModelObject, Storable { try container.encode(computedRank, forKey: ._computedRank) try container.encode(source, forKey: ._source) try container.encode(hasArrived, forKey: ._hasArrived) + try container.encode(captain, forKey: ._captain) + try container.encode(coach, forKey: ._coach) } enum PlayerDataSource: Int, Codable { diff --git a/PadelClub/Data/TeamRegistration.swift b/PadelClub/Data/TeamRegistration.swift index 94cd724..aeb2701 100644 --- a/PadelClub/Data/TeamRegistration.swift +++ b/PadelClub/Data/TeamRegistration.swift @@ -54,7 +54,7 @@ final class TeamRegistration: ModelObject, Storable { walkOut || unregistered } - init(tournament: String, groupStage: String? = nil, registrationDate: Date? = nil, callDate: Date? = nil, bracketPosition: Int? = nil, groupStagePosition: Int? = nil, comment: String? = nil, source: String? = nil, sourceValue: String? = nil, logo: String? = nil, name: String? = nil, walkOut: Bool = false, wildCardBracket: Bool = false, wildCardGroupStage: Bool = false, weight: Int = 0, lockedWeight: Int? = nil, confirmationDate: Date? = nil, qualified: Bool = false) { + init(tournament: String, groupStage: String? = nil, registrationDate: Date? = nil, callDate: Date? = nil, bracketPosition: Int? = nil, groupStagePosition: Int? = nil, comment: String? = nil, source: String? = nil, sourceValue: String? = nil, logo: String? = nil, name: String? = nil, walkOut: Bool = false, wildCardBracket: Bool = false, wildCardGroupStage: Bool = false, weight: Int = 0, lockedWeight: Int? = nil, confirmationDate: Date? = nil, qualified: Bool = false, finalRanking: Int? = nil, pointsEarned: Int? = nil, unregistered: Bool = false, unregistrationDate: Date? = nil) { self.tournament = tournament self.groupStage = groupStage self.registrationDate = registrationDate ?? Date() @@ -73,6 +73,10 @@ final class TeamRegistration: ModelObject, Storable { self.lockedWeight = lockedWeight self.confirmationDate = confirmationDate self.qualified = qualified + self.finalRanking = finalRanking + self.pointsEarned = pointsEarned + self.unregistered = unregistered + self.unregistrationDate = unregistrationDate } var tournamentStore: TournamentStore { @@ -636,6 +640,38 @@ final class TeamRegistration: ModelObject, Storable { case _qualified = "qualified" case _finalRanking = "finalRanking" case _pointsEarned = "pointsEarned" + case _unregistered = "unregistered" + case _unregistrationDate = "unregistrationDate" + } + + init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + // Non-optional properties + id = try container.decodeIfPresent(String.self, forKey: ._id) ?? Store.randomId() + tournament = try container.decode(String.self, forKey: ._tournament) + walkOut = try container.decodeIfPresent(Bool.self, forKey: ._walkOut) ?? false + wildCardBracket = try container.decodeIfPresent(Bool.self, forKey: ._wildCardBracket) ?? false + wildCardGroupStage = try container.decodeIfPresent(Bool.self, forKey: ._wildCardGroupStage) ?? false + weight = try container.decodeIfPresent(Int.self, forKey: ._weight) ?? 0 + qualified = try container.decodeIfPresent(Bool.self, forKey: ._qualified) ?? false + unregistered = try container.decodeIfPresent(Bool.self, forKey: ._unregistered) ?? false + + // Optional properties + groupStage = try container.decodeIfPresent(String.self, forKey: ._groupStage) + registrationDate = try container.decodeIfPresent(Date.self, forKey: ._registrationDate) + callDate = try container.decodeIfPresent(Date.self, forKey: ._callDate) + bracketPosition = try container.decodeIfPresent(Int.self, forKey: ._bracketPosition) + groupStagePosition = try container.decodeIfPresent(Int.self, forKey: ._groupStagePosition) + comment = try container.decodeIfPresent(String.self, forKey: ._comment) + source = try container.decodeIfPresent(String.self, forKey: ._source) + sourceValue = try container.decodeIfPresent(String.self, forKey: ._sourceValue) + logo = try container.decodeIfPresent(String.self, forKey: ._logo) + name = try container.decodeIfPresent(String.self, forKey: ._name) + lockedWeight = try container.decodeIfPresent(Int.self, forKey: ._lockedWeight) + confirmationDate = try container.decodeIfPresent(Date.self, forKey: ._confirmationDate) + finalRanking = try container.decodeIfPresent(Int.self, forKey: ._finalRanking) + pointsEarned = try container.decodeIfPresent(Int.self, forKey: ._pointsEarned) + unregistrationDate = try container.decodeIfPresent(Date.self, forKey: ._unregistrationDate) } func encode(to encoder: Encoder) throws { @@ -662,6 +698,8 @@ final class TeamRegistration: ModelObject, Storable { try container.encode(qualified, forKey: ._qualified) try container.encode(finalRanking, forKey: ._finalRanking) try container.encode(pointsEarned, forKey: ._pointsEarned) + try container.encode(unregistered, forKey: ._unregistered) + try container.encode(unregistrationDate, forKey: ._unregistrationDate) } func insertOnServer() { diff --git a/PadelClub/Data/Tournament.swift b/PadelClub/Data/Tournament.swift index 025212a..40a8aeb 100644 --- a/PadelClub/Data/Tournament.swift +++ b/PadelClub/Data/Tournament.swift @@ -65,7 +65,13 @@ final class Tournament : ModelObject, Storable { var openingRegistrationDate: Date? = nil var targetTeamCount: Int? = nil var waitingListLimit: Int? = nil - + var accountIsRequired: Bool = true + var licenseIsRequired: Bool = true + var minimumPlayerPerTeam: Int = 2 + var maximumPlayerPerTeam: Int = 2 + var information: String? = nil + var displayEntryFeeInformation: Bool = false + @ObservationIgnored var navigationPath: [Screen] = [] @@ -116,10 +122,16 @@ final class Tournament : ModelObject, Storable { case _loserBracketMode = "loserBracketMode" case _initialSeedRound = "initialSeedRound" case _initialSeedCount = "initialSeedCount" + case _accountIsRequired = "account_is_required" + case _licenseIsRequired = "license_is_required" + case _minimumPlayerPerTeam = "minimum_player_per_team" + case _maximumPlayerPerTeam = "maximum_player_per_team" + case _information = "information" + case _displayEntryFeeInformation = "displayEntryFeeInformation" } - 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, publishRankings: Bool = false, loserBracketMode: LoserBracketMode = .automatic, initialSeedRound: Int = 0, initialSeedCount: Int = 0) { + 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, publishRankings: Bool = false, loserBracketMode: LoserBracketMode = .automatic, initialSeedRound: Int = 0, initialSeedCount: Int = 0, accountIsRequired: Bool = true, licenseIsRequired: Bool = true, minimumPlayerPerTeam: Int = 2, maximumPlayerPerTeam: Int = 2, information: String? = nil, displayEntryFeeInformation: Bool = false) { self.event = event self.name = name self.startDate = startDate @@ -173,6 +185,13 @@ final class Tournament : ModelObject, Storable { self.loserBracketMode = loserBracketMode self.initialSeedRound = initialSeedRound self.initialSeedCount = initialSeedCount + + self.accountIsRequired = accountIsRequired + self.licenseIsRequired = licenseIsRequired + self.minimumPlayerPerTeam = minimumPlayerPerTeam + self.maximumPlayerPerTeam = maximumPlayerPerTeam + self.information = information + self.displayEntryFeeInformation = displayEntryFeeInformation } @@ -221,6 +240,15 @@ final class Tournament : ModelObject, Storable { loserBracketMode = try container.decodeIfPresent(LoserBracketMode.self, forKey: ._loserBracketMode) ?? .automatic initialSeedRound = try container.decodeIfPresent(Int.self, forKey: ._initialSeedRound) ?? 0 initialSeedCount = try container.decodeIfPresent(Int.self, forKey: ._initialSeedCount) ?? 0 + + accountIsRequired = try container.decodeIfPresent(Bool.self, forKey: ._accountIsRequired) ?? true + licenseIsRequired = try container.decodeIfPresent(Bool.self, forKey: ._licenseIsRequired) ?? true + minimumPlayerPerTeam = try container.decodeIfPresent(Int.self, forKey: ._minimumPlayerPerTeam) ?? 2 + maximumPlayerPerTeam = try container.decodeIfPresent(Int.self, forKey: ._maximumPlayerPerTeam) ?? 2 + + information = try container.decodeIfPresent(String.self, forKey: ._information) + displayEntryFeeInformation = try container.decodeIfPresent(Bool.self, forKey: ._displayEntryFeeInformation) ?? false + } fileprivate static let _numberFormatter: NumberFormatter = NumberFormatter() @@ -309,6 +337,12 @@ final class Tournament : ModelObject, Storable { try container.encode(loserBracketMode, forKey: ._loserBracketMode) try container.encode(initialSeedRound, forKey: ._initialSeedRound) try container.encode(initialSeedCount, forKey: ._initialSeedCount) + try container.encode(accountIsRequired, forKey: ._accountIsRequired) + try container.encode(licenseIsRequired, forKey: ._licenseIsRequired) + try container.encode(minimumPlayerPerTeam, forKey: ._minimumPlayerPerTeam) + try container.encode(maximumPlayerPerTeam, forKey: ._maximumPlayerPerTeam) + try container.encode(information, forKey: ._information) + try container.encode(displayEntryFeeInformation, forKey: ._displayEntryFeeInformation) } fileprivate func _encodePayment(container: inout KeyedEncodingContainer) throws { @@ -1019,7 +1053,7 @@ defer { //todo func significantPlayerCount() -> Int { - return 2 + return minimumPlayerPerTeam } func inadequatePlayers(in players: [PlayerRegistration]) -> [PlayerRegistration] { diff --git a/PadelClub/ViewModel/SetDescriptor.swift b/PadelClub/ViewModel/SetDescriptor.swift index eb04555..2d3be78 100644 --- a/PadelClub/ViewModel/SetDescriptor.swift +++ b/PadelClub/ViewModel/SetDescriptor.swift @@ -47,12 +47,16 @@ struct SetDescriptor: Identifiable, Equatable { if let valueTeamOne { if let tieBreakValueTeamOne { return "\(valueTeamOne)-\(tieBreakValueTeamOne)" + } else { + return "\(valueTeamOne)" } } case .two: if let valueTeamTwo { if let tieBreakValueTeamTwo { return "\(valueTeamTwo)-\(tieBreakValueTeamTwo)" + } else { + return "\(valueTeamTwo)" } } } diff --git a/PadelClub/Views/Match/Components/PlayerBlockView.swift b/PadelClub/Views/Match/Components/PlayerBlockView.swift index c98a724..d4c8d73 100644 --- a/PadelClub/Views/Match/Components/PlayerBlockView.swift +++ b/PadelClub/Views/Match/Components/PlayerBlockView.swift @@ -127,12 +127,28 @@ struct PlayerBlockView: View { } else { Divider().frame(width: width).overlay(Color(white: 0.9)) } - Text(string) - .font(.title3) - .frame(maxWidth: 20) - .scaledToFill() - .minimumScaleFactor(0.5) - .lineLimit(1) + + let parts = string.components(separatedBy: "-") + if parts.count == 2, let mainScore = parts.first, let supScore = parts.last { + HStack(spacing: 0) { + Text(mainScore) + .font(.title3) + .frame(maxWidth: 20) + .scaledToFill() + .minimumScaleFactor(0.5) + .lineLimit(1) + Text(supScore) + .font(.caption2) + .baselineOffset(10) + } + } else { + Text(string) + .font(.title3) + .frame(maxWidth: 20) + .scaledToFill() + .minimumScaleFactor(0.5) + .lineLimit(1) + } } } } else if let team { diff --git a/PadelClub/Views/Tournament/Screen/Components/TournamentGeneralSettingsView.swift b/PadelClub/Views/Tournament/Screen/Components/TournamentGeneralSettingsView.swift index 7b2f6fe..48ce125 100644 --- a/PadelClub/Views/Tournament/Screen/Components/TournamentGeneralSettingsView.swift +++ b/PadelClub/Views/Tournament/Screen/Components/TournamentGeneralSettingsView.swift @@ -13,6 +13,7 @@ struct TournamentGeneralSettingsView: View { @Bindable var tournament: Tournament @State private var tournamentName: String = "" + @State private var tournamentInformation: String = "" @State private var entryFee: Double? = nil @State private var confirmationRequired: Bool = false @State private var presentConfirmation: Bool = false @@ -24,6 +25,7 @@ struct TournamentGeneralSettingsView: View { self.tournament = tournament _loserBracketMode = .init(wrappedValue: tournament.loserBracketMode) _tournamentName = State(wrappedValue: tournament.name ?? "") + _tournamentInformation = State(wrappedValue: tournament.information ?? "") _entryFee = State(wrappedValue: tournament.entryFee) } @@ -41,6 +43,14 @@ struct TournamentGeneralSettingsView: View { Text("Nom du tournoi") } + Section { + TextEditor(text: $tournamentInformation) + .keyboardType(.alphabet) + .focused($focusedField, equals: ._information) + } header: { + Text("Description du tournoi") + } + Section { TournamentDatePickerView() TournamentDurationManagerView() @@ -53,6 +63,12 @@ struct TournamentGeneralSettingsView: View { } label: { Text("Inscription") } + + if tournament.isPrivate == false { + Toggle(isOn: $tournament.displayEntryFeeInformation) { + Text("Afficher sur la page d'infos") + } + } } footer: { Text("Si vous souhaitez que Padel Club vous aide à suivre les encaissements, indiquer un prix d'inscription. Sinon Padel Club vous aidera à suivre simplement l'arrivée et la présence des joueurs.") } @@ -151,6 +167,13 @@ struct TournamentGeneralSettingsView: View { } else { tournament.name = tournamentName } + } else if focusedField == ._information { + let tournamentInformation = tournamentInformation.prefixTrimmed(4000) + if tournamentInformation.isEmpty { + tournament.information = nil + } else { + tournament.information = tournamentInformation + } } else if focusedField == ._entryFee { tournament.entryFee = entryFee } @@ -167,7 +190,10 @@ struct TournamentGeneralSettingsView: View { .onChange(of: tournament.entryFee) { _save() } - .onChange(of: tournament.name) { + .onChange(of: [tournament.name, tournament.information]) { + _save() + } + .onChange(of: tournament.displayEntryFeeInformation) { _save() } .onChange(of: tournament.dayDuration) { diff --git a/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift b/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift index 8a6e2f4..bce278f 100644 --- a/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift +++ b/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift @@ -72,6 +72,7 @@ struct InscriptionManagerView: View { enum FilterMode: Int, Identifiable, CaseIterable { var id: Int { self.rawValue } case all + case registeredLocally case registeredOnline case walkOut case waiting @@ -92,6 +93,8 @@ struct InscriptionManagerView: View { return "Vous n'avez encore aucune équipe inscrite." case .registeredOnline: return "Aucune équipe inscrite en ligne." + case .registeredLocally: + return "Aucune équipe inscrite par vous-même." case .walkOut: return "Vous n'avez aucune équipe forfait." case .waiting: @@ -115,6 +118,8 @@ struct InscriptionManagerView: View { return "Aucune wildcard en poule" case .all: return "Aucune équipe inscrite" + case .registeredLocally: + return "Aucune équipe inscrite par vous-même" case .registeredOnline: return "Aucune équipe inscrite en ligne" case .walkOut: @@ -140,6 +145,8 @@ struct InscriptionManagerView: View { return displayStyle == .wide ? "Wildcard Poule" : "wc poule" case .all: return displayStyle == .wide ? "Équipes inscrites" : "inscris" + case .registeredLocally: + return displayStyle == .wide ? "Inscrites par vous-même" : "par vous-même" case .registeredOnline: return displayStyle == .wide ? "Inscrites en ligne" : "en ligne" case .bracket: @@ -246,10 +253,12 @@ struct InscriptionManagerView: View { } .refreshable { do { - self.tournament.tournamentStore.teamRegistrations.reset() - try await self.tournament.tournamentStore.teamRegistrations.loadDataFromServerIfAllowed() self.tournament.tournamentStore.playerRegistrations.reset() try await self.tournament.tournamentStore.playerRegistrations.loadDataFromServerIfAllowed() + + self.tournament.tournamentStore.teamRegistrations.reset() + try await self.tournament.tournamentStore.teamRegistrations.loadDataFromServerIfAllowed() + _setHash() } catch { Logger.error(error) } @@ -510,6 +519,12 @@ struct InscriptionManagerView: View { teams = teams.filter({ $0.inGroupStage() }) case .notImported: teams = teams.filter({ $0.isImported() == false }) + case .unregistered: + teams = teams.filter({ $0.hasUnregistered() == true }) + case .registeredLocally: + teams = teams.filter({ $0.hasRegisteredOnline() == false }) + case .registeredOnline: + teams = teams.filter({ $0.hasRegisteredOnline() == true }) default: break } @@ -698,6 +713,9 @@ struct InscriptionManagerView: View { case .unregistered: let unregistered: Int = max(0, sortedTeams.filter({ $0.hasUnregistered() }).count) return unregistered.formatted() + case .registeredLocally: + let registeredLocally: Int = max(0, sortedTeams.filter({ $0.hasRegisteredOnline() == false }).count) + return registeredLocally.formatted() case .registeredOnline: let registeredOnline: Int = max(0, sortedTeams.filter({ $0.hasRegisteredOnline() }).count) return registeredOnline.formatted() diff --git a/PadelClub/Views/Tournament/Screen/RegisrationSetupView.swift b/PadelClub/Views/Tournament/Screen/RegisrationSetupView.swift index aa2e266..a7f0713 100644 --- a/PadelClub/Views/Tournament/Screen/RegisrationSetupView.swift +++ b/PadelClub/Views/Tournament/Screen/RegisrationSetupView.swift @@ -20,6 +20,11 @@ struct RegisrationSetupView: View { @State private var targetTeamCountEnabled: Bool @State private var waitingListLimitEnabled: Bool @State private var openingRegistrationDateEnabled: Bool + @State private var userAccountIsRequired: Bool + @State private var licenseIsRequired: Bool + @State private var minPlayerPerTeam: Int + @State private var maxPlayerPerTeam: Int + @State private var hasChanges: Bool = false @Environment(\.dismiss) private var dismiss @@ -63,6 +68,12 @@ struct RegisrationSetupView: View { _waitingListLimit = .init(wrappedValue: 0) // Default value _waitingListLimitEnabled = .init(wrappedValue: false) } + + _userAccountIsRequired = .init(wrappedValue: tournament.accountIsRequired) + _licenseIsRequired = .init(wrappedValue: tournament.licenseIsRequired) + _maxPlayerPerTeam = .init(wrappedValue: tournament.maximumPlayerPerTeam) + _minPlayerPerTeam = .init(wrappedValue: tournament.minimumPlayerPerTeam) + } var body: some View { @@ -134,6 +145,29 @@ struct RegisrationSetupView: View { } footer: { Text("Activez et définissez une limite pour la liste d'attente des équipes.") } + + if tournament.isAnimation() { + Section { + Toggle(isOn: $userAccountIsRequired) { + Text("Compte Padel Club requis pour s'inscrire") + } + + Toggle(isOn: $licenseIsRequired) { + Text("Licence FFT requise pour s'inscrire") + } + + LabeledContent { + StepperView(count: $minPlayerPerTeam, minimum: 1, maximum: maxPlayerPerTeam) + } label: { + Text("Nombre minimum de joueurs possible") + } + LabeledContent { + StepperView(count: $maxPlayerPerTeam, minimum: minPlayerPerTeam) + } label: { + Text("Nombre maximum de joueurs possible") + } + } + } } else { ContentUnavailableView( "Activez les inscriptions en ligne", @@ -198,6 +232,13 @@ struct RegisrationSetupView: View { .onChange(of: waitingListLimit) { _hasChanged() } + + .onChange(of: [minPlayerPerTeam, maxPlayerPerTeam]) { + _hasChanged() + } + .onChange(of: [userAccountIsRequired, licenseIsRequired]) { + _hasChanged() + } } private func _hasChanged() { @@ -209,6 +250,18 @@ struct RegisrationSetupView: View { tournament.enableOnlineRegistration = enableOnlineRegistration + if enableOnlineRegistration { + tournament.accountIsRequired = userAccountIsRequired + tournament.licenseIsRequired = licenseIsRequired + tournament.minimumPlayerPerTeam = minPlayerPerTeam + tournament.maximumPlayerPerTeam = maxPlayerPerTeam + } else { + tournament.accountIsRequired = true + tournament.licenseIsRequired = true + tournament.minimumPlayerPerTeam = 2 + tournament.maximumPlayerPerTeam = 2 + } + if openingRegistrationDateEnabled == false { tournament.openingRegistrationDate = nil } else { diff --git a/PadelClub/Views/Tournament/Screen/TournamentSettingsView.swift b/PadelClub/Views/Tournament/Screen/TournamentSettingsView.swift index 5dc380e..15ec869 100644 --- a/PadelClub/Views/Tournament/Screen/TournamentSettingsView.swift +++ b/PadelClub/Views/Tournament/Screen/TournamentSettingsView.swift @@ -16,6 +16,7 @@ enum TournamentSettings: Identifiable, Selectable, Equatable { case general case club(Tournament) case matchFormats + case onlineRegistration(Tournament) var id: String { String(describing: self) } @@ -29,6 +30,8 @@ enum TournamentSettings: Identifiable, Selectable, Equatable { return "Général" case .club: return "Terrains" + case .onlineRegistration: + return "Inscriptions en ligne" } } @@ -46,6 +49,15 @@ enum TournamentSettings: Identifiable, Selectable, Equatable { } func badgeImage() -> Badge? { + switch self { + case .onlineRegistration(let tournament): + if tournament.enableOnlineRegistration { + return .checkmark + } + default: + return nil + } + return nil } } @@ -55,7 +67,7 @@ struct TournamentSettingsView: View { @Environment(Tournament.self) var tournament: Tournament private func destinations() -> [TournamentSettings] { - [.general, .club(tournament), .matchFormats] + [.general, .club(tournament), .matchFormats, .onlineRegistration(tournament)] } var body: some View { @@ -68,6 +80,8 @@ struct TournamentSettingsView: View { TournamentMatchFormatsSettingsView() case .general: TournamentGeneralSettingsView(tournament: tournament) + case .onlineRegistration: + RegisrationSetupView(tournament: tournament) case .club: TournamentClubSettingsView() } diff --git a/PadelClubTests/ServerDataTests.swift b/PadelClubTests/ServerDataTests.swift index 15887ad..7dc721c 100644 --- a/PadelClubTests/ServerDataTests.swift +++ b/PadelClubTests/ServerDataTests.swift @@ -100,7 +100,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, hidePointsEarned: true, publishRankings: true, loserBracketMode: .manual, initialSeedRound: 8, initialSeedCount: 4) + 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, publishRankings: true, loserBracketMode: .manual, initialSeedRound: 8, initialSeedCount: 4, accountIsRequired: false, licenseIsRequired: false, minimumPlayerPerTeam: 3, maximumPlayerPerTeam: 5, information: "Super", displayEntryFeeInformation: true) let t = try await StoreCenter.main.service().post(tournament) assert(t.event == tournament.event) @@ -143,6 +143,12 @@ final class ServerDataTests: XCTestCase { assert(t.loserBracketMode == tournament.loserBracketMode) assert(t.initialSeedCount == tournament.initialSeedCount) assert(t.initialSeedRound == tournament.initialSeedRound) + assert(t.accountIsRequired == tournament.accountIsRequired) + assert(t.licenseIsRequired == tournament.licenseIsRequired) + assert(t.minimumPlayerPerTeam == tournament.minimumPlayerPerTeam) + assert(t.maximumPlayerPerTeam == tournament.maximumPlayerPerTeam) + assert(t.information == tournament.information) + assert(t.displayEntryFeeInformation == tournament.displayEntryFeeInformation) } func testGroupStage() async throws { @@ -203,7 +209,7 @@ final class ServerDataTests: XCTestCase { return } - let teamRegistration = TeamRegistration(tournament: tournamentId, groupStage: groupStageId, registrationDate: Date(), callDate: Date(), bracketPosition: 1, groupStagePosition: 2, comment: "comment", source: "source", sourceValue: "source V", logo: "logo", name: "Stax", walkOut: true, wildCardBracket: true, wildCardGroupStage: true, weight: 1, lockedWeight: 11, confirmationDate: Date(), qualified: true) + let teamRegistration = TeamRegistration(tournament: tournamentId, groupStage: groupStageId, registrationDate: Date(), callDate: Date(), bracketPosition: 1, groupStagePosition: 2, comment: "comment", source: "source", sourceValue: "source V", logo: "logo", name: "Stax", walkOut: true, wildCardBracket: true, wildCardGroupStage: true, weight: 1, lockedWeight: 11, confirmationDate: Date(), qualified: true, finalRanking: 4, pointsEarned: 200, unregistered: true, unregistrationDate: Date()) let tr: TeamRegistration = try await StoreCenter.main.service().post(teamRegistration) @@ -225,7 +231,10 @@ final class ServerDataTests: XCTestCase { assert(tr.lockedWeight == teamRegistration.lockedWeight) assert(tr.confirmationDate?.formatted() == teamRegistration.confirmationDate?.formatted()) assert(tr.qualified == teamRegistration.qualified) - + assert(tr.finalRanking == teamRegistration.finalRanking) + assert(tr.pointsEarned == teamRegistration.pointsEarned) + assert(tr.unregistered == teamRegistration.unregistered) + assert(tr.unregistrationDate?.formatted() == teamRegistration.unregistrationDate?.formatted()) } func testPlayerRegistration() async throws { @@ -236,7 +245,7 @@ final class ServerDataTests: XCTestCase { return } - let playerRegistration = PlayerRegistration(teamRegistration: teamRegistrationId, firstName: "juan", lastName: "lebron", licenceId: "123", rank: 11, paymentType: PlayerRegistration.PlayerPaymentType.cash, sex: PlayerRegistration.PlayerSexType.male, tournamentPlayed: 2, points: 33, clubName: "le club", ligueName: "la league", assimilation: "ass", phoneNumber: "123123", email: "email@email.com", birthdate: nil, computedRank: 222, source: PlayerRegistration.PlayerDataSource.frenchFederation, hasArrived: true) + let playerRegistration = PlayerRegistration(teamRegistration: teamRegistrationId, firstName: "juan", lastName: "lebron", licenceId: "123", rank: 11, paymentType: PlayerRegistration.PlayerPaymentType.cash, sex: PlayerRegistration.PlayerSexType.male, tournamentPlayed: 2, points: 33, clubName: "le club", ligueName: "la league", assimilation: "ass", phoneNumber: "123123", email: "email@email.com", birthdate: nil, computedRank: 222, source: PlayerRegistration.PlayerDataSource.frenchFederation, hasArrived: true, coach: true, captain: true) let pr: PlayerRegistration = try await StoreCenter.main.service().post(playerRegistration) assert(pr.teamRegistration == playerRegistration.teamRegistration) @@ -256,7 +265,9 @@ final class ServerDataTests: XCTestCase { assert(pr.computedRank == playerRegistration.computedRank) assert(pr.source == playerRegistration.source) assert(pr.hasArrived == playerRegistration.hasArrived) - + assert(pr.captain == playerRegistration.captain) + assert(pr.coach == playerRegistration.coach) + } func testMatch() async throws { From 5f0eaa156bf3c6810f4b2137456fb448231926b8 Mon Sep 17 00:00:00 2001 From: Raz Date: Mon, 2 Dec 2024 10:43:10 +0100 Subject: [PATCH 04/57] wip online reg --- PadelClub.xcodeproj/project.pbxproj | 8 ++-- PadelClub/Data/Tournament.swift | 43 ++++++++++++------- PadelClub/Extensions/String+Extensions.swift | 4 ++ PadelClub/Views/Club/ClubDetailView.swift | 4 +- .../TournamentGeneralSettingsView.swift | 35 ++++++++------- .../Screen/TournamentSettingsView.swift | 2 +- PadelClubTests/ServerDataTests.swift | 3 +- 7 files changed, 60 insertions(+), 39 deletions(-) diff --git a/PadelClub.xcodeproj/project.pbxproj b/PadelClub.xcodeproj/project.pbxproj index 1464dc8..a7e9392 100644 --- a/PadelClub.xcodeproj/project.pbxproj +++ b/PadelClub.xcodeproj/project.pbxproj @@ -3527,7 +3527,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\""; @@ -3549,7 +3549,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.24; + MARKETING_VERSION = 1.0.34; PRODUCT_BUNDLE_IDENTIFIER = app.padelclub.beta; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -3569,7 +3569,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; @@ -3590,7 +3590,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.24; + MARKETING_VERSION = 1.0.34; PRODUCT_BUNDLE_IDENTIFIER = app.padelclub.beta; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; diff --git a/PadelClub/Data/Tournament.swift b/PadelClub/Data/Tournament.swift index 40a8aeb..317e06c 100644 --- a/PadelClub/Data/Tournament.swift +++ b/PadelClub/Data/Tournament.swift @@ -70,8 +70,7 @@ final class Tournament : ModelObject, Storable { var minimumPlayerPerTeam: Int = 2 var maximumPlayerPerTeam: Int = 2 var information: String? = nil - var displayEntryFeeInformation: Bool = false - + @ObservationIgnored var navigationPath: [Screen] = [] @@ -122,16 +121,20 @@ final class Tournament : ModelObject, Storable { case _loserBracketMode = "loserBracketMode" case _initialSeedRound = "initialSeedRound" case _initialSeedCount = "initialSeedCount" - case _accountIsRequired = "account_is_required" - case _licenseIsRequired = "license_is_required" - case _minimumPlayerPerTeam = "minimum_player_per_team" - case _maximumPlayerPerTeam = "maximum_player_per_team" + case _enableOnlineRegistration = "enableOnlineRegistration" + case _registrationDateLimit = "registrationDateLimit" + case _openingRegistrationDate = "openingRegistrationDate" + case _targetTeamCount = "targetTeamCount" + case _waitingListLimit = "waitingListLimit" + case _accountIsRequired = "accountIsRequired" + case _licenseIsRequired = "licenseIsRequired" + case _minimumPlayerPerTeam = "minimumPlayerPerTeam" + case _maximumPlayerPerTeam = "maximumPlayerPerTeam" case _information = "information" - case _displayEntryFeeInformation = "displayEntryFeeInformation" } - 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, publishRankings: Bool = false, loserBracketMode: LoserBracketMode = .automatic, initialSeedRound: Int = 0, initialSeedCount: Int = 0, accountIsRequired: Bool = true, licenseIsRequired: Bool = true, minimumPlayerPerTeam: Int = 2, maximumPlayerPerTeam: Int = 2, information: String? = nil, displayEntryFeeInformation: 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, publishRankings: Bool = false, loserBracketMode: LoserBracketMode = .automatic, initialSeedRound: Int = 0, initialSeedCount: Int = 0, enableOnlineRegistration: Bool = false, registrationDateLimit: Date? = nil, openingRegistrationDate: Date? = nil, targetTeamCount: Int? = nil, waitingListLimit: Int? = nil, accountIsRequired: Bool = true, licenseIsRequired: Bool = true, minimumPlayerPerTeam: Int = 2, maximumPlayerPerTeam: Int = 2, information: String? = nil) { self.event = event self.name = name self.startDate = startDate @@ -185,14 +188,17 @@ final class Tournament : ModelObject, Storable { self.loserBracketMode = loserBracketMode self.initialSeedRound = initialSeedRound self.initialSeedCount = initialSeedCount - + self.enableOnlineRegistration = enableOnlineRegistration + self.registrationDateLimit = registrationDateLimit + self.openingRegistrationDate = openingRegistrationDate + self.targetTeamCount = targetTeamCount + self.waitingListLimit = waitingListLimit + self.accountIsRequired = accountIsRequired self.licenseIsRequired = licenseIsRequired self.minimumPlayerPerTeam = minimumPlayerPerTeam self.maximumPlayerPerTeam = maximumPlayerPerTeam self.information = information - self.displayEntryFeeInformation = displayEntryFeeInformation - } required init(from decoder: Decoder) throws { @@ -240,15 +246,17 @@ final class Tournament : ModelObject, Storable { loserBracketMode = try container.decodeIfPresent(LoserBracketMode.self, forKey: ._loserBracketMode) ?? .automatic initialSeedRound = try container.decodeIfPresent(Int.self, forKey: ._initialSeedRound) ?? 0 initialSeedCount = try container.decodeIfPresent(Int.self, forKey: ._initialSeedCount) ?? 0 - + enableOnlineRegistration = try container.decodeIfPresent(Bool.self, forKey: ._enableOnlineRegistration) ?? false + registrationDateLimit = try container.decodeIfPresent(Date.self, forKey: ._registrationDateLimit) + openingRegistrationDate = try container.decodeIfPresent(Date.self, forKey: ._openingRegistrationDate) + targetTeamCount = try container.decodeIfPresent(Int.self, forKey: ._targetTeamCount) + waitingListLimit = try container.decodeIfPresent(Int.self, forKey: ._waitingListLimit) accountIsRequired = try container.decodeIfPresent(Bool.self, forKey: ._accountIsRequired) ?? true licenseIsRequired = try container.decodeIfPresent(Bool.self, forKey: ._licenseIsRequired) ?? true minimumPlayerPerTeam = try container.decodeIfPresent(Int.self, forKey: ._minimumPlayerPerTeam) ?? 2 maximumPlayerPerTeam = try container.decodeIfPresent(Int.self, forKey: ._maximumPlayerPerTeam) ?? 2 information = try container.decodeIfPresent(String.self, forKey: ._information) - displayEntryFeeInformation = try container.decodeIfPresent(Bool.self, forKey: ._displayEntryFeeInformation) ?? false - } fileprivate static let _numberFormatter: NumberFormatter = NumberFormatter() @@ -337,12 +345,17 @@ final class Tournament : ModelObject, Storable { try container.encode(loserBracketMode, forKey: ._loserBracketMode) try container.encode(initialSeedRound, forKey: ._initialSeedRound) try container.encode(initialSeedCount, forKey: ._initialSeedCount) + try container.encode(enableOnlineRegistration, forKey: ._enableOnlineRegistration) + try container.encodeIfPresent(registrationDateLimit, forKey: ._registrationDateLimit) + try container.encodeIfPresent(openingRegistrationDate, forKey: ._openingRegistrationDate) + try container.encodeIfPresent(targetTeamCount, forKey: ._targetTeamCount) + try container.encodeIfPresent(waitingListLimit, forKey: ._waitingListLimit) + try container.encode(accountIsRequired, forKey: ._accountIsRequired) try container.encode(licenseIsRequired, forKey: ._licenseIsRequired) try container.encode(minimumPlayerPerTeam, forKey: ._minimumPlayerPerTeam) try container.encode(maximumPlayerPerTeam, forKey: ._maximumPlayerPerTeam) try container.encode(information, forKey: ._information) - try container.encode(displayEntryFeeInformation, forKey: ._displayEntryFeeInformation) } fileprivate func _encodePayment(container: inout KeyedEncodingContainer) throws { diff --git a/PadelClub/Extensions/String+Extensions.swift b/PadelClub/Extensions/String+Extensions.swift index 37094b9..491f0c6 100644 --- a/PadelClub/Extensions/String+Extensions.swift +++ b/PadelClub/Extensions/String+Extensions.swift @@ -18,6 +18,10 @@ extension String { String(trimmed.prefix(length)) } + func prefixMultilineTrimmed(_ length: Int) -> String { + String(trimmedMultiline.prefix(length)) + } + var trimmed: String { replaceCharactersFromSet(characterSet: .newlines, replacementString: " ").trimmingCharacters(in: .whitespacesAndNewlines) } diff --git a/PadelClub/Views/Club/ClubDetailView.swift b/PadelClub/Views/Club/ClubDetailView.swift index 285c564..a7748ea 100644 --- a/PadelClub/Views/Club/ClubDetailView.swift +++ b/PadelClub/Views/Club/ClubDetailView.swift @@ -135,9 +135,9 @@ struct ClubDetailView: View { .onChange(of: acronymMode) { focusedField = ._acronym if acronymMode == .custom { - club.acronym = "" + //club.acronym = "" } else { - club.acronym = club.automaticShortName().uppercased() + //club.acronym = club.automaticShortName().uppercased() } } } footer: { diff --git a/PadelClub/Views/Tournament/Screen/Components/TournamentGeneralSettingsView.swift b/PadelClub/Views/Tournament/Screen/Components/TournamentGeneralSettingsView.swift index 48ce125..30a4a6c 100644 --- a/PadelClub/Views/Tournament/Screen/Components/TournamentGeneralSettingsView.swift +++ b/PadelClub/Views/Tournament/Screen/Components/TournamentGeneralSettingsView.swift @@ -44,11 +44,25 @@ struct TournamentGeneralSettingsView: View { } Section { - TextEditor(text: $tournamentInformation) - .keyboardType(.alphabet) - .focused($focusedField, equals: ._information) + ZStack { + Text(tournamentInformation).opacity(0) + Text(ContactType.defaultCustomMessage).opacity(0) + TextEditor(text: $tournamentInformation) + .keyboardType(.alphabet) + .focused($focusedField, equals: ._information) + } + .frame(maxHeight: 200) + .overlay { + if tournamentInformation.isEmpty { + Text("Texte visible dans l'onglet informations sur Padel Club.").italic() + } + } } header: { Text("Description du tournoi") + } footer: { + FooterButtonView("Ajouter le prix de l'inscription") { + tournamentInformation.append("\n" + tournament.entryFeeMessage) + } } Section { @@ -63,12 +77,6 @@ struct TournamentGeneralSettingsView: View { } label: { Text("Inscription") } - - if tournament.isPrivate == false { - Toggle(isOn: $tournament.displayEntryFeeInformation) { - Text("Afficher sur la page d'infos") - } - } } footer: { Text("Si vous souhaitez que Padel Club vous aide à suivre les encaissements, indiquer un prix d'inscription. Sinon Padel Club vous aidera à suivre simplement l'arrivée et la présence des joueurs.") } @@ -141,7 +149,7 @@ struct TournamentGeneralSettingsView: View { if focusedField == ._entryFee { if tournament.isFree() { ForEach(priceTags, id: \.self) { priceTag in - Button(priceTag.formatted(.currency(code: Locale.defaultCurrency()))) { + Button(priceTag.formatted(.currency(code: Locale.defaultCurrency()).precision(.fractionLength(0)))) { entryFee = priceTag tournament.entryFee = priceTag focusedField = nil @@ -161,14 +169,14 @@ struct TournamentGeneralSettingsView: View { Spacer() Button("Valider") { if focusedField == ._name { - let tournamentName = tournamentName.prefixTrimmed(200) + let tournamentName = tournamentName.prefixMultilineTrimmed(200) if tournamentName.isEmpty { tournament.name = nil } else { tournament.name = tournamentName } } else if focusedField == ._information { - let tournamentInformation = tournamentInformation.prefixTrimmed(4000) + let tournamentInformation = tournamentInformation.prefixMultilineTrimmed(4000) if tournamentInformation.isEmpty { tournament.information = nil } else { @@ -193,9 +201,6 @@ struct TournamentGeneralSettingsView: View { .onChange(of: [tournament.name, tournament.information]) { _save() } - .onChange(of: tournament.displayEntryFeeInformation) { - _save() - } .onChange(of: tournament.dayDuration) { _save() } diff --git a/PadelClub/Views/Tournament/Screen/TournamentSettingsView.swift b/PadelClub/Views/Tournament/Screen/TournamentSettingsView.swift index 15ec869..36d1078 100644 --- a/PadelClub/Views/Tournament/Screen/TournamentSettingsView.swift +++ b/PadelClub/Views/Tournament/Screen/TournamentSettingsView.swift @@ -31,7 +31,7 @@ enum TournamentSettings: Identifiable, Selectable, Equatable { case .club: return "Terrains" case .onlineRegistration: - return "Inscriptions en ligne" + return "Inscriptions" } } diff --git a/PadelClubTests/ServerDataTests.swift b/PadelClubTests/ServerDataTests.swift index 7dc721c..6cca7a4 100644 --- a/PadelClubTests/ServerDataTests.swift +++ b/PadelClubTests/ServerDataTests.swift @@ -100,7 +100,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, hidePointsEarned: true, publishRankings: true, loserBracketMode: .manual, initialSeedRound: 8, initialSeedCount: 4, accountIsRequired: false, licenseIsRequired: false, minimumPlayerPerTeam: 3, maximumPlayerPerTeam: 5, information: "Super", displayEntryFeeInformation: 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, publishRankings: true, loserBracketMode: .manual, initialSeedRound: 8, initialSeedCount: 4, accountIsRequired: false, licenseIsRequired: false, minimumPlayerPerTeam: 3, maximumPlayerPerTeam: 5, information: "Super") let t = try await StoreCenter.main.service().post(tournament) assert(t.event == tournament.event) @@ -148,7 +148,6 @@ final class ServerDataTests: XCTestCase { assert(t.minimumPlayerPerTeam == tournament.minimumPlayerPerTeam) assert(t.maximumPlayerPerTeam == tournament.maximumPlayerPerTeam) assert(t.information == tournament.information) - assert(t.displayEntryFeeInformation == tournament.displayEntryFeeInformation) } func testGroupStage() async throws { From 0ff0d3fd81d9c40988e05aaa126f779d6a8c3afc Mon Sep 17 00:00:00 2001 From: Raz Date: Mon, 2 Dec 2024 13:13:57 +0100 Subject: [PATCH 05/57] fix animation settings --- .../Screen/InscriptionManagerView.swift | 36 ++++++++++--------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift b/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift index 7dd6dc2..d8a5d51 100644 --- a/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift +++ b/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift @@ -320,25 +320,27 @@ struct InscriptionManagerView: View { .symbolVariant(filterMode == .all ? .none : .fill) } Menu { + if tournament.inscriptionClosed() == false { + Menu { + _sortingTypePickerView() + } label: { + Text("Méthode de sélection") + Text(tournament.teamSorting.localizedLabel()) + } + Divider() + rankingDateSourcePickerView(showDateInLabel: true) + + Divider() + Button { + tournament.lockRegistration() + _save() + } label: { + Label("Clôturer", systemImage: "lock") + } + + } if tournament.isAnimation() == false { if tournament.inscriptionClosed() == false { - Menu { - _sortingTypePickerView() - } label: { - Text("Méthode de sélection") - Text(tournament.teamSorting.localizedLabel()) - } - Divider() - rankingDateSourcePickerView(showDateInLabel: true) - - Divider() - Button { - tournament.lockRegistration() - _save() - } label: { - Label("Clôturer", systemImage: "lock") - } - Divider() Section { From 07a0b632f942ff0431ca0d18a9744f2d0885c8bf Mon Sep 17 00:00:00 2001 From: Raz Date: Wed, 11 Dec 2024 11:17:42 +0100 Subject: [PATCH 06/57] improve sentences --- PadelClub/Data/Tournament.swift | 57 +++++++++++++ .../TournamentGeneralSettingsView.swift | 23 +++++ .../Screen/RegisrationSetupView.swift | 83 ++++++++++++++++--- 3 files changed, 152 insertions(+), 11 deletions(-) diff --git a/PadelClub/Data/Tournament.swift b/PadelClub/Data/Tournament.swift index 317e06c..2e11f4c 100644 --- a/PadelClub/Data/Tournament.swift +++ b/PadelClub/Data/Tournament.swift @@ -2459,6 +2459,63 @@ defer { }) } + func getOnlineRegistrationStatus() -> OnlineRegistrationStatus { + if supposedlyInProgress() { + return .inProgress + } + if closedRegistrationDate != nil { + return .ended + } + if endDate != nil { + return .endedWithResults + } + + let now = Date() + + if let openingRegistrationDate = openingRegistrationDate { + let timezonedDateTime = openingRegistrationDate // Assuming dates are already in local timezone + if now < timezonedDateTime { + return .notStarted + } + } + + if let registrationDateLimit = registrationDateLimit { + let timezonedDateTime = registrationDateLimit // Assuming dates are already in local timezone + if now > timezonedDateTime { + return .ended + } + } + + if let targetTeamCount = targetTeamCount { + // Get all team registrations excluding walk_outs + let currentTeamCount = unsortedTeamsWithoutWO().count + + if currentTeamCount >= targetTeamCount { + if let waitingListLimit = waitingListLimit { + let waitingListCount = currentTeamCount - targetTeamCount + if waitingListCount >= waitingListLimit { + return .waitingListFull + } + return .waitingListPossible + } + return .registrationFull + } + + let nonWalkoutTeamCount = currentTeamCount + if nonWalkoutTeamCount >= targetTeamCount { + if let waitingListLimit = waitingListLimit { + let waitingListCount = nonWalkoutTeamCount - targetTeamCount + if waitingListCount >= waitingListLimit { + return .waitingListFull + } + return .waitingListPossible + } + return .registrationFull + } + } + + return .open + } // MARK: - diff --git a/PadelClub/Views/Tournament/Screen/Components/TournamentGeneralSettingsView.swift b/PadelClub/Views/Tournament/Screen/Components/TournamentGeneralSettingsView.swift index 30a4a6c..c731f66 100644 --- a/PadelClub/Views/Tournament/Screen/Components/TournamentGeneralSettingsView.swift +++ b/PadelClub/Views/Tournament/Screen/Components/TournamentGeneralSettingsView.swift @@ -81,6 +81,29 @@ struct TournamentGeneralSettingsView: View { Text("Si vous souhaitez que Padel Club vous aide à suivre les encaissements, indiquer un prix d'inscription. Sinon Padel Club vous aidera à suivre simplement l'arrivée et la présence des joueurs.") } + Section { + NavigationLink { + RegisrationSetupView(tournament: tournament) + } label: { + LabeledContent { + if tournament.enableOnlineRegistration { + Text("activée").foregroundStyle(.green) + .font(.headline) + } else { + Text("désactivée").foregroundStyle(.logoRed) + .font(.headline) + } + } label: { + Text("Accéder aux paramètres") + Text(tournament.getOnlineRegistrationStatus().statusLocalized()) + } + } + } header: { + Text("Inscription en ligne") + } footer: { + Text("Paramétrez les possibilités d'inscription en ligne à votre tournoi via Padel Club") + } + Section { TournamentLevelPickerView() } diff --git a/PadelClub/Views/Tournament/Screen/RegisrationSetupView.swift b/PadelClub/Views/Tournament/Screen/RegisrationSetupView.swift index a7f0713..3c2fd13 100644 --- a/PadelClub/Views/Tournament/Screen/RegisrationSetupView.swift +++ b/PadelClub/Views/Tournament/Screen/RegisrationSetupView.swift @@ -82,13 +82,15 @@ struct RegisrationSetupView: View { Toggle(isOn: $enableOnlineRegistration) { Text("Inscription en ligne") } + } footer: { + Text("Les inscriptions en ligne permettent à des joueurs de s'inscrire à votre tournoi en passant par le site Padel Club. Vous verrez alors votre liste d'inscription s'agrandir dans la vue Gestion des Inscriptions de l'application.") } if enableOnlineRegistration { Section { Toggle(isOn: $openingRegistrationDateEnabled) { - Text("Date d'ouverture des inscriptions") + Text("Définir une date") } if openingRegistrationDateEnabled { @@ -99,12 +101,12 @@ struct RegisrationSetupView: View { } header: { Text("Date d'ouverture des inscriptions") } footer: { - Text("Activez et définissez une date d'ouverture pour les inscriptions au tournoi.") + Text("Activez et définissez une date d'ouverture pour les inscriptions au tournoi. Les inscriptions en ligne ne seront possible qu'à partir de cette date.") } Section { Toggle(isOn: $registrationDateLimitEnabled) { - Text("Activer une limite") + Text("Définir une date") } if registrationDateLimitEnabled { @@ -115,35 +117,35 @@ struct RegisrationSetupView: View { } header: { Text("Date de fermeture des inscriptions") } footer: { - Text("Activez et définissez une date limite pour l'inscription en ligne.") + Text("Si une date de fermeture des inscriptions en ligne est définie, alors plus aucune inscription ne sera possible après cette date. Sinon, la date du début du tournoi ou la date de clôture des inscriptions seront utilisées.") } Section { Toggle(isOn: $targetTeamCountEnabled) { - Text("Nombre d'équipes cible") + Text("Activer une limite") } if targetTeamCountEnabled { StepperView(count: $targetTeamCount, minimum: 4) } } header: { - Text("Nombre d'équipes cible") + Text("Paires admises") } footer: { - Text("Activez et définissez le nombre d'équipes cible pour le tournoi.") + Text("Si une limite de paire existe, les inscriptions seront indiqués en attente pour les joueurs au-délà de cette limite dans le cas où aucune limite de liste d'attente n'est active ou non atteinte. Dans le cas contraire, plus aucune inscription ne seront possibles.") } Section { Toggle(isOn: $waitingListLimitEnabled) { - Text("Limite de la liste d'attente") + Text("Activer une limite") } if waitingListLimitEnabled { - StepperView(count: $waitingListLimit, minimum: 0) + StepperView(count: $waitingListLimit, minimum: 1) } } header: { - Text("Limite de la liste d'attente") + Text("Liste d'attente") } footer: { - Text("Activez et définissez une limite pour la liste d'attente des équipes.") + Text("Si une limite à la liste d'attente existe, les inscriptions ne seront plus possibles une fois la liste d'attente pleine. Si aucune limite de liste d'attente n'est active, alors les inscriptions seront toujours possibles. Les joueurs auront une indication comme quoi ils sont en liste d'attente.") } if tournament.isAnimation() { @@ -295,3 +297,62 @@ struct RegisrationSetupView: View { dismiss() } } + + +enum OnlineRegistrationStatus: Int { + case open = 1 + case notEnabled = 2 + case notStarted = 3 + case ended = 4 + case registrationFull = 5 + case waitingListPossible = 6 + case waitingListFull = 7 + case inProgress = 8 + case endedWithResults = 9 + + var displayName: String { + switch self { + case .open: + return "Open" + case .notEnabled: + return "Not Enabled" + case .notStarted: + return "Not Started" + case .ended: + return "Ended" + case .registrationFull: + return "Registration Full" + case .waitingListPossible: + return "Waiting List Possible" + case .waitingListFull: + return "Waiting List Full" + case .inProgress: + return "In Progress" + case .endedWithResults: + return "Ended with Results" + } + } + + func statusLocalized() -> String { + switch self { + case .open: + return "Inscription ouverte" + case .notEnabled: + return "Inscription désactivée" + case .notStarted: + return "Inscription pas encore ouverte" + case .ended: + return "Inscription terminée" + case .registrationFull: + return "Inscription complète" + case .waitingListPossible: + return "Liste d'attente disponible" + case .waitingListFull: + return "Liste d'attente complète" + case .inProgress: + return "Tournoi en cours" + case .endedWithResults: + return "Tournoi terminé" + } + } +} From 78624bd422a759504ecf4530e81c6f522b56be9d Mon Sep 17 00:00:00 2001 From: Raz Date: Wed, 11 Dec 2024 11:18:38 +0100 Subject: [PATCH 07/57] remove hscroll online reg --- .../Screen/TournamentSettingsView.swift | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/PadelClub/Views/Tournament/Screen/TournamentSettingsView.swift b/PadelClub/Views/Tournament/Screen/TournamentSettingsView.swift index 36d1078..5dc380e 100644 --- a/PadelClub/Views/Tournament/Screen/TournamentSettingsView.swift +++ b/PadelClub/Views/Tournament/Screen/TournamentSettingsView.swift @@ -16,7 +16,6 @@ enum TournamentSettings: Identifiable, Selectable, Equatable { case general case club(Tournament) case matchFormats - case onlineRegistration(Tournament) var id: String { String(describing: self) } @@ -30,8 +29,6 @@ enum TournamentSettings: Identifiable, Selectable, Equatable { return "Général" case .club: return "Terrains" - case .onlineRegistration: - return "Inscriptions" } } @@ -49,15 +46,6 @@ enum TournamentSettings: Identifiable, Selectable, Equatable { } func badgeImage() -> Badge? { - switch self { - case .onlineRegistration(let tournament): - if tournament.enableOnlineRegistration { - return .checkmark - } - default: - return nil - } - return nil } } @@ -67,7 +55,7 @@ struct TournamentSettingsView: View { @Environment(Tournament.self) var tournament: Tournament private func destinations() -> [TournamentSettings] { - [.general, .club(tournament), .matchFormats, .onlineRegistration(tournament)] + [.general, .club(tournament), .matchFormats] } var body: some View { @@ -80,8 +68,6 @@ struct TournamentSettingsView: View { TournamentMatchFormatsSettingsView() case .general: TournamentGeneralSettingsView(tournament: tournament) - case .onlineRegistration: - RegisrationSetupView(tournament: tournament) case .club: TournamentClubSettingsView() } From a356d9768cbb61033d596a7b5b19c01633b6076b Mon Sep 17 00:00:00 2001 From: Raz Date: Thu, 12 Dec 2024 08:06:01 +0100 Subject: [PATCH 08/57] fix reg stuff --- .../logoRed.colorset/Contents.json | 6 +- .../logoYellow.colorset/Contents.json | 6 +- PadelClub/Data/Tournament.swift | 25 ++---- .../Screen/InscriptionManagerView.swift | 81 ++++++++++++++++--- .../Screen/RegisrationSetupView.swift | 20 ++--- 5 files changed, 90 insertions(+), 48 deletions(-) diff --git a/PadelClub/Assets.xcassets/logoRed.colorset/Contents.json b/PadelClub/Assets.xcassets/logoRed.colorset/Contents.json index ded2ed9..7c05d3f 100644 --- a/PadelClub/Assets.xcassets/logoRed.colorset/Contents.json +++ b/PadelClub/Assets.xcassets/logoRed.colorset/Contents.json @@ -5,9 +5,9 @@ "color-space" : "srgb", "components" : { "alpha" : "1.000", - "blue" : "0.220", - "green" : "0.251", - "red" : "0.910" + "blue" : "0x38", + "green" : "0x40", + "red" : "0xE8" } }, "idiom" : "universal" diff --git a/PadelClub/Assets.xcassets/logoYellow.colorset/Contents.json b/PadelClub/Assets.xcassets/logoYellow.colorset/Contents.json index afbfa83..93a4a81 100644 --- a/PadelClub/Assets.xcassets/logoYellow.colorset/Contents.json +++ b/PadelClub/Assets.xcassets/logoYellow.colorset/Contents.json @@ -5,9 +5,9 @@ "color-space" : "srgb", "components" : { "alpha" : "1.000", - "blue" : "0.000", - "green" : "0.827", - "red" : "1.000" + "blue" : "0x00", + "green" : "0xD2", + "red" : "0xFF" } }, "idiom" : "universal" diff --git a/PadelClub/Data/Tournament.swift b/PadelClub/Data/Tournament.swift index 2e11f4c..b2f3407 100644 --- a/PadelClub/Data/Tournament.swift +++ b/PadelClub/Data/Tournament.swift @@ -346,10 +346,10 @@ final class Tournament : ModelObject, Storable { try container.encode(initialSeedRound, forKey: ._initialSeedRound) try container.encode(initialSeedCount, forKey: ._initialSeedCount) try container.encode(enableOnlineRegistration, forKey: ._enableOnlineRegistration) - try container.encodeIfPresent(registrationDateLimit, forKey: ._registrationDateLimit) - try container.encodeIfPresent(openingRegistrationDate, forKey: ._openingRegistrationDate) - try container.encodeIfPresent(targetTeamCount, forKey: ._targetTeamCount) - try container.encodeIfPresent(waitingListLimit, forKey: ._waitingListLimit) + try container.encode(registrationDateLimit, forKey: ._registrationDateLimit) + try container.encode(openingRegistrationDate, forKey: ._openingRegistrationDate) + try container.encode(targetTeamCount, forKey: ._targetTeamCount) + try container.encode(waitingListLimit, forKey: ._waitingListLimit) try container.encode(accountIsRequired, forKey: ._accountIsRequired) try container.encode(licenseIsRequired, forKey: ._licenseIsRequired) @@ -2460,7 +2460,7 @@ defer { } func getOnlineRegistrationStatus() -> OnlineRegistrationStatus { - if supposedlyInProgress() { + if hasStarted() { return .inProgress } if closedRegistrationDate != nil { @@ -2496,21 +2496,8 @@ defer { if waitingListCount >= waitingListLimit { return .waitingListFull } - return .waitingListPossible } - return .registrationFull - } - - let nonWalkoutTeamCount = currentTeamCount - if nonWalkoutTeamCount >= targetTeamCount { - if let waitingListLimit = waitingListLimit { - let waitingListCount = nonWalkoutTeamCount - targetTeamCount - if waitingListCount >= waitingListLimit { - return .waitingListFull - } - return .waitingListPossible - } - return .registrationFull + return .waitingListPossible } } diff --git a/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift b/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift index 8da301b..4117005 100644 --- a/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift +++ b/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift @@ -49,6 +49,9 @@ struct InscriptionManagerView: View { @State private var compactMode: Bool = true @State private var pasteString: String? @State private var registrationIssues: Int? = nil + @State private var refreshResult: String? = nil + @State private var refreshInProgress: Bool = false + @State private var refreshStatus: Bool? var tournamentStore: TournamentStore { return self.tournament.tournamentStore @@ -247,21 +250,18 @@ struct InscriptionManagerView: View { RowButtonView("Importer un fichier") { presentImportView = true } + + if tournament.enableOnlineRegistration { + RowButtonView("Rafraîchir la liste") { + await _refreshList() + } + } } } } } .refreshable { - do { - self.tournament.tournamentStore.playerRegistrations.reset() - try await self.tournament.tournamentStore.playerRegistrations.loadDataFromServerIfAllowed() - - self.tournament.tournamentStore.teamRegistrations.reset() - try await self.tournament.tournamentStore.teamRegistrations.loadDataFromServerIfAllowed() - _setHash() - } catch { - Logger.error(error) - } + await _refreshList() } .onAppear { _setHash() @@ -537,6 +537,42 @@ struct InscriptionManagerView: View { } } + private func _refreshList() async { + if refreshInProgress { return } + + refreshResult = nil + refreshStatus = nil + refreshInProgress = true + do { + + let storeIdentifier = StoreIdentifier(value: tournament.id, parameterName: "tournament") + let serverPlayers: [PlayerRegistration] = try await StoreCenter.main.service().get(identifier: storeIdentifier) + let serverTeamScores: [TeamScore] = try await StoreCenter.main.service().get(identifier: storeIdentifier) + let serverTeams: [TeamRegistration] = try await StoreCenter.main.service().get(identifier: storeIdentifier) + + self.tournamentStore.playerRegistrations.reset() + self.tournamentStore.teamScores.reset() + self.tournamentStore.teamRegistrations.reset() + + try self.tournamentStore.playerRegistrations.addOrUpdate(contentOfs: serverPlayers) + try self.tournamentStore.teamScores.addOrUpdate(contentOfs: serverTeamScores) + try self.tournamentStore.teamRegistrations.addOrUpdate(contentOfs: serverTeams) + + _setHash() + + self.refreshResult = "la synchronization a réussi" + self.refreshStatus = true + refreshInProgress = false + + } catch { + Logger.error(error) + + self.refreshResult = "la synchronization a échoué" + self.refreshStatus = false + refreshInProgress = false + } + } + private func _teamRegisteredView() -> some View { List { let selectedSortedTeams = tournament.selectedSortedTeams() @@ -798,7 +834,30 @@ struct InscriptionManagerView: View { if let closedRegistrationDate = tournament.closedRegistrationDate { CloseDatePicker(closedRegistrationDate: closedRegistrationDate) } - + if tournament.enableOnlineRegistration { + Button { + Task { + await _refreshList() + } + } label: { + LabeledContent { + if refreshInProgress { + ProgressView() + } else if let refreshStatus { + if refreshStatus { + Image(systemName: "checkmark").foregroundStyle(.green).font(.headline) + } else { + Image(systemName: "xmark").foregroundStyle(.logoRed).font(.headline) + } + } + } label: { + Text("Récupérer les inscriptions en ligne") + if let refreshResult { + Text(refreshResult) + } + } + } + } } header: { HStack { Spacer() diff --git a/PadelClub/Views/Tournament/Screen/RegisrationSetupView.swift b/PadelClub/Views/Tournament/Screen/RegisrationSetupView.swift index 3c2fd13..f602265 100644 --- a/PadelClub/Views/Tournament/Screen/RegisrationSetupView.swift +++ b/PadelClub/Views/Tournament/Screen/RegisrationSetupView.swift @@ -150,9 +150,10 @@ struct RegisrationSetupView: View { if tournament.isAnimation() { Section { - Toggle(isOn: $userAccountIsRequired) { - Text("Compte Padel Club requis pour s'inscrire") - } +// Toggle(isOn: $userAccountIsRequired) { +// Text("Compte Padel Club requis pour s'inscrire") +// } +// .disabled(true) Toggle(isOn: $licenseIsRequired) { Text("Licence FFT requise pour s'inscrire") @@ -304,11 +305,10 @@ enum OnlineRegistrationStatus: Int { case notEnabled = 2 case notStarted = 3 case ended = 4 - case registrationFull = 5 - case waitingListPossible = 6 - case waitingListFull = 7 - case inProgress = 8 - case endedWithResults = 9 + case waitingListPossible = 5 + case waitingListFull = 6 + case inProgress = 7 + case endedWithResults = 8 var displayName: String { switch self { @@ -320,8 +320,6 @@ enum OnlineRegistrationStatus: Int { return "Not Started" case .ended: return "Ended" - case .registrationFull: - return "Registration Full" case .waitingListPossible: return "Waiting List Possible" case .waitingListFull: @@ -343,8 +341,6 @@ enum OnlineRegistrationStatus: Int { return "Inscription pas encore ouverte" case .ended: return "Inscription terminée" - case .registrationFull: - return "Inscription complète" case .waitingListPossible: return "Liste d'attente disponible" case .waitingListFull: From 3c0fa45153adea38a597b8a952569fbcfa873e06 Mon Sep 17 00:00:00 2001 From: Raz Date: Fri, 13 Dec 2024 14:01:18 +0100 Subject: [PATCH 09/57] fix ranking --- PadelClub/Data/TeamRegistration.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PadelClub/Data/TeamRegistration.swift b/PadelClub/Data/TeamRegistration.swift index aeb2701..46ee221 100644 --- a/PadelClub/Data/TeamRegistration.swift +++ b/PadelClub/Data/TeamRegistration.swift @@ -486,7 +486,7 @@ final class TeamRegistration: ModelObject, Storable { self.unsortedPlayers().sorted { (lhs, rhs) in let predicates: [AreInIncreasingOrder] = [ { $0.sex?.rawValue ?? 0 < $1.sex?.rawValue ?? 0 }, - { $0.rank ?? 0 < $1.rank ?? 0 }, + { $0.rank ?? Int.max < $1.rank ?? Int.max }, { $0.lastName < $1.lastName}, { $0.firstName < $1.firstName } ] From 15b5ba97f96f3734cf7bfacc367546051ca0b668 Mon Sep 17 00:00:00 2001 From: Raz Date: Fri, 13 Dec 2024 15:39:44 +0100 Subject: [PATCH 10/57] fix senior fix sharelink lag --- PadelClub.xcodeproj/project.pbxproj | 4 ++ .../Data/Federal/FederalTournament.swift | 2 +- PadelClub/Data/Tournament.swift | 4 +- PadelClub/Info.plist | 2 - PadelClub/Utils/PadelRule.swift | 10 +-- PadelClub/Utils/URLs.swift | 3 + .../ViewModel/FederalDataViewModel.swift | 2 +- .../Event/TournamentConfiguratorView.swift | 2 +- .../GroupStage/GroupStagesSettingsView.swift | 6 +- .../Agenda/TournamentLookUpView.swift | 4 +- .../Navigation/Toolbox/ToolboxView.swift | 35 +++++++--- PadelClub/Views/Round/RoundSettingsView.swift | 7 +- .../Shared/SelectablePlayerListView.swift | 4 +- .../Views/Shared/TournamentFilterView.swift | 2 +- .../TournamentLevelPickerView.swift | 2 +- .../Screen/InscriptionManagerView.swift | 68 ++++++++++++++++--- .../Screen/RegisrationSetupView.swift | 9 +++ .../Shared/TournamentCellView.swift | 4 +- 18 files changed, 129 insertions(+), 41 deletions(-) diff --git a/PadelClub.xcodeproj/project.pbxproj b/PadelClub.xcodeproj/project.pbxproj index 04ffabc..12697b2 100644 --- a/PadelClub.xcodeproj/project.pbxproj +++ b/PadelClub.xcodeproj/project.pbxproj @@ -3450,6 +3450,7 @@ INFOPLIST_KEY_CFBundleDisplayName = "Padel Club (ProdTest)"; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.sports"; INFOPLIST_KEY_LSSupportsOpeningDocumentsInPlace = YES; + INFOPLIST_KEY_NSCalendarsUsageDescription = "Padel Club a besoin d'avoir accès à votre calendrier pour pouvoir y inscrire ce tournoi"; INFOPLIST_KEY_NSCameraUsageDescription = "En autorisant l'application à utiliser la caméra, vous pourrez prendre des photos des rencontres"; INFOPLIST_KEY_NSLocationWhenInUseUsageDescription = "Padel Club a besoin de votre position pour rechercher les clubs autour de vous."; INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; @@ -3493,6 +3494,7 @@ INFOPLIST_KEY_CFBundleDisplayName = "Padel Club (ProdTest)"; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.sports"; INFOPLIST_KEY_LSSupportsOpeningDocumentsInPlace = YES; + INFOPLIST_KEY_NSCalendarsUsageDescription = "Padel Club a besoin d'avoir accès à votre calendrier pour pouvoir y inscrire ce tournoi"; INFOPLIST_KEY_NSCameraUsageDescription = "En autorisant l'application à utiliser la caméra, vous pourrez prendre des photos des rencontres"; INFOPLIST_KEY_NSLocationWhenInUseUsageDescription = "Padel Club a besoin de votre position pour rechercher les clubs autour de vous."; INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; @@ -3537,6 +3539,7 @@ INFOPLIST_KEY_CFBundleDisplayName = "Padel Club (Beta)"; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.sports"; INFOPLIST_KEY_LSSupportsOpeningDocumentsInPlace = YES; + INFOPLIST_KEY_NSCalendarsUsageDescription = "Padel Club a besoin d'avoir accès à votre calendrier pour pouvoir y inscrire ce tournoi"; INFOPLIST_KEY_NSCameraUsageDescription = "En autorisant l'application à utiliser la caméra, vous pourrez prendre des photos des rencontres"; INFOPLIST_KEY_NSLocationWhenInUseUsageDescription = "Padel Club a besoin de votre position pour rechercher les clubs autour de vous."; INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; @@ -3578,6 +3581,7 @@ INFOPLIST_KEY_CFBundleDisplayName = "Padel Club (Beta)"; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.sports"; INFOPLIST_KEY_LSSupportsOpeningDocumentsInPlace = YES; + INFOPLIST_KEY_NSCalendarsUsageDescription = "Padel Club a besoin d'avoir accès à votre calendrier pour pouvoir y inscrire ce tournoi"; INFOPLIST_KEY_NSCameraUsageDescription = "En autorisant l'application à utiliser la caméra, vous pourrez prendre des photos des rencontres"; INFOPLIST_KEY_NSLocationWhenInUseUsageDescription = "Padel Club a besoin de votre position pour rechercher les clubs autour de vous."; INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; diff --git a/PadelClub/Data/Federal/FederalTournament.swift b/PadelClub/Data/Federal/FederalTournament.swift index e61f36c..003218e 100644 --- a/PadelClub/Data/Federal/FederalTournament.swift +++ b/PadelClub/Data/Federal/FederalTournament.swift @@ -239,7 +239,7 @@ struct CategorieAge: Codable { return FederalTournamentAge(rawValue: id) } if let libelle { - return FederalTournamentAge.allCases.first(where: { $0.localizedLabel().localizedCaseInsensitiveContains(libelle) }) + return FederalTournamentAge.allCases.first(where: { $0.localizedFederalAgeLabel().localizedCaseInsensitiveContains(libelle) }) } return nil } diff --git a/PadelClub/Data/Tournament.swift b/PadelClub/Data/Tournament.swift index b2f3407..dc58c5e 100644 --- a/PadelClub/Data/Tournament.swift +++ b/PadelClub/Data/Tournament.swift @@ -1546,7 +1546,7 @@ defer { return tournamentLevel.localizedLevelLabel(.title) } } - let title: String = [tournamentLevel.localizedLevelLabel(displayStyle), tournamentCategory.localizedLabel(displayStyle), federalTournamentAge.localizedLabel(displayStyle)].filter({ $0.isEmpty == false }).joined(separator: " ") + let title: String = [tournamentLevel.localizedLevelLabel(displayStyle), tournamentCategory.localizedLabel(displayStyle), federalTournamentAge.localizedFederalAgeLabel(displayStyle)].filter({ $0.isEmpty == false }).joined(separator: " ") if displayStyle == .wide, let name { return [title, name].joined(separator: " - ") } else { @@ -2633,7 +2633,7 @@ extension Tournament: FederalTournamentHolder { func subtitleLabel(forBuild build: any TournamentBuildHolder) -> String { if isAnimation() { if displayAgeAndCategory(forBuild: build) == false { - return [build.category.localizedLabel(), build.age.localizedLabel()].filter({ $0.isEmpty == false }).joined(separator: " ") + return [build.category.localizedLabel(), build.age.localizedFederalAgeLabel()].filter({ $0.isEmpty == false }).joined(separator: " ") } else if name != nil { return build.level.localizedLevelLabel(.title) } else { diff --git a/PadelClub/Info.plist b/PadelClub/Info.plist index 40756ec..7b68d4a 100644 --- a/PadelClub/Info.plist +++ b/PadelClub/Info.plist @@ -33,7 +33,5 @@ ITSAppUsesNonExemptEncryption - UIFileSharingEnabled - diff --git a/PadelClub/Utils/PadelRule.swift b/PadelClub/Utils/PadelRule.swift index e69e5ee..07371b1 100644 --- a/PadelClub/Utils/PadelRule.swift +++ b/PadelClub/Utils/PadelRule.swift @@ -48,7 +48,7 @@ struct TournamentBuild: TournamentBuildHolder, Hashable, Codable, Identifiable { } var identifier: String { - level.localizedLevelLabel()+":"+category.localizedLabel()+":"+age.localizedLabel() + level.localizedLevelLabel()+":"+category.localizedLabel()+":"+age.localizedFederalAgeLabel() } func computedLabel(_ displayStyle: DisplayStyle = .wide) -> String { @@ -65,7 +65,7 @@ struct TournamentBuild: TournamentBuildHolder, Hashable, Codable, Identifiable { } func localizedAge(_ displayStyle: DisplayStyle = .wide) -> String { - age.localizedLabel(displayStyle) + age.localizedFederalAgeLabel(displayStyle) } } @@ -252,7 +252,7 @@ enum FederalTournamentAge: Int, Hashable, Codable, CaseIterable, Identifiable { } } - func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String { + func localizedFederalAgeLabel(_ displayStyle: DisplayStyle = .wide) -> String { switch self { case .unlisted: return displayStyle == .title ? "Aucune" : "" @@ -265,7 +265,7 @@ enum FederalTournamentAge: Int, Hashable, Codable, CaseIterable, Identifiable { case .a17_18: return "17/18 ans" case .senior: - return "Senior" + return displayStyle == .short ? "" : "Senior" case .a45: return "+45 ans" case .a55: @@ -274,7 +274,7 @@ enum FederalTournamentAge: Int, Hashable, Codable, CaseIterable, Identifiable { } var tournamentDescriptionLabel: String { - return localizedLabel() + return localizedFederalAgeLabel() } func isAgeValid(age: Int?) -> Bool { diff --git a/PadelClub/Utils/URLs.swift b/PadelClub/Utils/URLs.swift index 16e0fba..20e1013 100644 --- a/PadelClub/Utils/URLs.swift +++ b/PadelClub/Utils/URLs.swift @@ -50,6 +50,7 @@ enum URLs: String, Identifiable { } enum PageLink: String, Identifiable, CaseIterable { + case info = "Informations" case teams = "Équipes" case summons = "Convocations" case groupStages = "Poules" @@ -68,6 +69,8 @@ enum PageLink: String, Identifiable, CaseIterable { switch self { case .matches: return "" + case .info: + return "info" case .teams: return "teams" case .summons: diff --git a/PadelClub/ViewModel/FederalDataViewModel.swift b/PadelClub/ViewModel/FederalDataViewModel.swift index a5579b7..2ca3b2f 100644 --- a/PadelClub/ViewModel/FederalDataViewModel.swift +++ b/PadelClub/ViewModel/FederalDataViewModel.swift @@ -28,7 +28,7 @@ class FederalDataViewModel { var labels: [String] = [] labels.append(contentsOf: levels.map { $0.localizedLevelLabel() }.formatList()) labels.append(contentsOf: categories.map { $0.localizedLabel() }.formatList()) - labels.append(contentsOf: ageCategories.map { $0.localizedLabel() }.formatList()) + labels.append(contentsOf: ageCategories.map { $0.localizedFederalAgeLabel() }.formatList()) let clubNames = selectedClubs.compactMap { codeClub in let club: Club? = DataStore.shared.clubs.first(where: { $0.code == codeClub }) return club?.clubTitle(.short) diff --git a/PadelClub/Views/Cashier/Event/TournamentConfiguratorView.swift b/PadelClub/Views/Cashier/Event/TournamentConfiguratorView.swift index 9333ee9..30ec0f6 100644 --- a/PadelClub/Views/Cashier/Event/TournamentConfiguratorView.swift +++ b/PadelClub/Views/Cashier/Event/TournamentConfiguratorView.swift @@ -46,7 +46,7 @@ struct TournamentConfigurationView: View { } Picker(selection: $tournament.federalAgeCategory, label: Text("Limite d'âge")) { ForEach(FederalTournamentAge.allCases) { type in - Text(type.localizedLabel(.title)).tag(type) + Text(type.localizedFederalAgeLabel(.title)).tag(type) } } LabeledContent { diff --git a/PadelClub/Views/GroupStage/GroupStagesSettingsView.swift b/PadelClub/Views/GroupStage/GroupStagesSettingsView.swift index 3bf6939..6961bae 100644 --- a/PadelClub/Views/GroupStage/GroupStagesSettingsView.swift +++ b/PadelClub/Views/GroupStage/GroupStagesSettingsView.swift @@ -262,11 +262,15 @@ struct GroupStagesSettingsView: View { } .toolbar { ToolbarItem(placement: .topBarTrailing) { - ShareLink(item: tournament.groupStages().compactMap { $0.pasteData() }.joined(separator: "\n\n")) + ShareLink(item: groupStagesPaste(), preview: .init("Données des poules")) } } } + func groupStagesPaste() -> TournamentGroupStageShareContent { + TournamentGroupStageShareContent(tournament: tournament) + } + var menuBuildAllGroupStages: some View { RowButtonView("Refaire les poules", role: .destructive) { diff --git a/PadelClub/Views/Navigation/Agenda/TournamentLookUpView.swift b/PadelClub/Views/Navigation/Agenda/TournamentLookUpView.swift index 93e9aa6..783359c 100644 --- a/PadelClub/Views/Navigation/Agenda/TournamentLookUpView.swift +++ b/PadelClub/Views/Navigation/Agenda/TournamentLookUpView.swift @@ -369,7 +369,7 @@ struct TournamentLookUpView: View { NavigationLink { List([FederalTournamentAge.senior, FederalTournamentAge.a45, FederalTournamentAge.a55, FederalTournamentAge.a17_18, FederalTournamentAge.a15_16, FederalTournamentAge.a13_14, FederalTournamentAge.a11_12], selection: $appSettings.tournamentAges) { type in - Text(type.localizedLabel()) + Text(type.localizedFederalAgeLabel()) } .navigationTitle("Limites d'âge") .environment(\.editMode, Binding.constant(EditMode.active)) @@ -381,7 +381,7 @@ struct TournamentLookUpView: View { Text("Tous les âges") .foregroundStyle(.secondary) } else { - Text(ages.map({ $0.localizedLabel()}).joined(separator: ", ")) + Text(ages.map({ $0.localizedFederalAgeLabel()}).joined(separator: ", ")) .foregroundStyle(.secondary) } } diff --git a/PadelClub/Views/Navigation/Toolbox/ToolboxView.swift b/PadelClub/Views/Navigation/Toolbox/ToolboxView.swift index 114112d..487f80c 100644 --- a/PadelClub/Views/Navigation/Toolbox/ToolboxView.swift +++ b/PadelClub/Views/Navigation/Toolbox/ToolboxView.swift @@ -228,10 +228,9 @@ struct ToolboxView: View { ShareLink(item: URLs.appStore.url) { Label("Lien AppStore", systemImage: "link") } - if let zip = _getZip() { - ShareLink(item: zip) { - Label("Mes données", systemImage: "server.rack") - } + + ShareLink(item: ZipLog(), preview: .init("Mon archive")) { + Label("Mes données", systemImage: "server.rack") } } label: { Label("Partagez", systemImage: "square.and.arrow.up").labelStyle(.iconOnly) @@ -240,7 +239,14 @@ struct ToolboxView: View { } } } - +} + +//#Preview { +// ToolboxView() +//} + + +struct ZipLog: Transferable { private func _getZip() -> URL? { do { let filePath = try Club.storageDirectoryPath() @@ -250,8 +256,19 @@ struct ToolboxView: View { return nil } } -} -//#Preview { -// ToolboxView() -//} + func shareFile() -> URL? { + print("Generating URL...") + return _getZip() + } + + static var transferRepresentation: some TransferRepresentation { + FileRepresentation(exportedContentType: .zip) { transferable in + return SentTransferredFile(transferable.shareFile()!) + }.exportingCondition { $0.shareFile() != nil } + + ProxyRepresentation { transferable in + return transferable.shareFile()! + }.exportingCondition { $0.shareFile() != nil } + } +} diff --git a/PadelClub/Views/Round/RoundSettingsView.swift b/PadelClub/Views/Round/RoundSettingsView.swift index 42a45b4..2928a49 100644 --- a/PadelClub/Views/Round/RoundSettingsView.swift +++ b/PadelClub/Views/Round/RoundSettingsView.swift @@ -147,11 +147,16 @@ struct RoundSettingsView: View { } .toolbar { ToolbarItem(placement: .topBarTrailing) { - ShareLink(item: tournament.rounds().compactMap { $0.pasteData() }.joined(separator: "\n\n")) + ShareLink(item: roundsPaste(), preview: .init("Données du tableau")) } } } + func roundsPaste() -> TournamentRoundShareContent { + TournamentRoundShareContent(tournament: tournament) + } + + private func _removeAllSeeds() async { await tournament.removeAllSeeds() self.isEditingTournamentSeed.wrappedValue = true diff --git a/PadelClub/Views/Shared/SelectablePlayerListView.swift b/PadelClub/Views/Shared/SelectablePlayerListView.swift index 00a4836..352251d 100644 --- a/PadelClub/Views/Shared/SelectablePlayerListView.swift +++ b/PadelClub/Views/Shared/SelectablePlayerListView.swift @@ -109,7 +109,7 @@ struct SelectablePlayerListView: View { Section { Picker(selection: $searchViewModel.selectedAgeCategory) { ForEach(FederalTournamentAge.allCases) { ageCategory in - Text(ageCategory.localizedLabel(.title)).tag(ageCategory) + Text(ageCategory.localizedFederalAgeLabel(.title)).tag(ageCategory) } } label: { Text("Catégorie d'âge") @@ -142,7 +142,7 @@ struct SelectablePlayerListView: View { VStack(alignment: .trailing) { Label(searchViewModel.sortOption.localizedLabel(), systemImage: searchViewModel.ascending ? "chevron.up" : "chevron.down") if searchViewModel.selectedAgeCategory != .unlisted { - Text(searchViewModel.selectedAgeCategory.localizedLabel()).font(.caption) + Text(searchViewModel.selectedAgeCategory.localizedFederalAgeLabel()).font(.caption) } } } diff --git a/PadelClub/Views/Shared/TournamentFilterView.swift b/PadelClub/Views/Shared/TournamentFilterView.swift index 6af4d7c..3686aff 100644 --- a/PadelClub/Views/Shared/TournamentFilterView.swift +++ b/PadelClub/Views/Shared/TournamentFilterView.swift @@ -107,7 +107,7 @@ struct TournamentFilterView: View { } } } label: { - Text(category.localizedLabel(.title)) + Text(category.localizedFederalAgeLabel(.title)) } } } header: { diff --git a/PadelClub/Views/Tournament/Screen/Components/TournamentLevelPickerView.swift b/PadelClub/Views/Tournament/Screen/Components/TournamentLevelPickerView.swift index bc81b41..524a65a 100644 --- a/PadelClub/Views/Tournament/Screen/Components/TournamentLevelPickerView.swift +++ b/PadelClub/Views/Tournament/Screen/Components/TournamentLevelPickerView.swift @@ -42,7 +42,7 @@ struct TournamentLevelPickerView: View { Picker(selection: $tournament.federalTournamentAge, label: Text("Limite d'âge")) { ForEach(FederalTournamentAge.allCases) { type in - Text(type.localizedLabel(.title)).tag(type) + Text(type.localizedFederalAgeLabel(.title)).tag(type) } } Picker(selection: $tournament.groupStageOrderingMode, label: Text("Répartition en poule")) { diff --git a/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift b/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift index 4117005..b3dbaed 100644 --- a/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift +++ b/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift @@ -461,15 +461,11 @@ struct InscriptionManagerView: View { private func _sharingTeamsMenuView() -> some View { Menu { - if let teamPaste = teamPaste() { - ShareLink(item: teamPaste) { - Text("En texte") - } + ShareLink(item: teamPaste(), preview: .init("Inscriptions")) { + Text("En texte") } - if let teamPaste = teamPaste(.csv) { - ShareLink(item: teamPaste) { - Text("En csv") - } + ShareLink(item: teamPaste(.csv), preview: .init("Inscriptions")) { + Text("En csv") } } label: { Label("Exporter les paires", systemImage: "square.and.arrow.up") @@ -484,8 +480,8 @@ struct InscriptionManagerView: View { tournament.unsortedTeamsWithoutWO() } - func teamPaste(_ exportFormat: ExportFormat = .rawText) -> URL? { - tournament.pasteDataForImporting(exportFormat).createFile(self.tournament.tournamentTitle(.short), exportFormat) + func teamPaste(_ exportFormat: ExportFormat = .rawText) -> TournamentShareFile { + TournamentShareFile(tournament: tournament, exportFormat: exportFormat) } var unsortedPlayers: [PlayerRegistration] { @@ -1124,3 +1120,55 @@ struct InscriptionManagerView: View { // .environment(Tournament.mock()) // } //} + +struct TournamentRoundShareContent: Transferable { + let tournament: Tournament + + func shareContent() -> String { + print("Generating URL...") + let content = tournament.rounds().compactMap { $0.pasteData() }.joined(separator: "\n\n") + return content + } + + static var transferRepresentation: some TransferRepresentation { + ProxyRepresentation { transferable in + return transferable.shareContent() + } + } +} + +struct TournamentGroupStageShareContent: Transferable { + let tournament: Tournament + + func shareContent() -> String { + print("Generating URL...") + let content = tournament.groupStages().compactMap { $0.pasteData() }.joined(separator: "\n\n") + return content + } + + static var transferRepresentation: some TransferRepresentation { + ProxyRepresentation { transferable in + return transferable.shareContent() + } + } +} + +struct TournamentShareFile: Transferable { + let tournament: Tournament + let exportFormat: ExportFormat + + func shareFile() -> URL { + print("Generating URL...") + return tournament.pasteDataForImporting(exportFormat).createFile(self.tournament.tournamentTitle()+"-inscriptions", exportFormat) + } + + static var transferRepresentation: some TransferRepresentation { + FileRepresentation(exportedContentType: .utf8PlainText) { transferable in + return SentTransferredFile(transferable.shareFile()) + } + + ProxyRepresentation { transferable in + return transferable.shareFile() + } + } +} diff --git a/PadelClub/Views/Tournament/Screen/RegisrationSetupView.swift b/PadelClub/Views/Tournament/Screen/RegisrationSetupView.swift index f602265..f77bc70 100644 --- a/PadelClub/Views/Tournament/Screen/RegisrationSetupView.swift +++ b/PadelClub/Views/Tournament/Screen/RegisrationSetupView.swift @@ -87,6 +87,15 @@ struct RegisrationSetupView: View { } if enableOnlineRegistration { + if let shareURL = tournament.shareURL(.info) { + Section { + ShareLink(item: shareURL) + } header: { + Text("Page d'inscription") + } footer: { + CopyPasteButtonView(pasteValue: shareURL.absoluteString) + } + } Section { Toggle(isOn: $openingRegistrationDateEnabled) { diff --git a/PadelClub/Views/Tournament/Shared/TournamentCellView.swift b/PadelClub/Views/Tournament/Shared/TournamentCellView.swift index c4426d7..e3b862d 100644 --- a/PadelClub/Views/Tournament/Shared/TournamentCellView.swift +++ b/PadelClub/Views/Tournament/Shared/TournamentCellView.swift @@ -107,7 +107,7 @@ struct TournamentCellView: View { if displayStyle == .wide, tournament.displayAgeAndCategory(forBuild: build) { VStack(alignment: .leading, spacing: 0) { Text(build.category.localizedLabel()) - Text(build.age.localizedLabel()) + Text(build.age.localizedFederalAgeLabel()) } .font(.caption) } @@ -155,7 +155,7 @@ struct TournamentCellView: View { } } else { Text(build.category.localizedLabel()) - Text(build.age.localizedLabel()) + Text(build.age.localizedFederalAgeLabel()) } } } From 768902e8274b820092f0eda00c726fd09332c643 Mon Sep 17 00:00:00 2001 From: Raz Date: Mon, 16 Dec 2024 17:23:20 +0100 Subject: [PATCH 11/57] remove unregistered var --- PadelClub/Data/TeamRegistration.swift | 20 ++----------- PadelClub/Views/Team/EditingTeamView.swift | 28 ------------------- .../Screen/InscriptionManagerView.swift | 12 -------- PadelClubTests/ServerDataTests.swift | 4 +-- 4 files changed, 3 insertions(+), 61 deletions(-) diff --git a/PadelClub/Data/TeamRegistration.swift b/PadelClub/Data/TeamRegistration.swift index 46ee221..756beb2 100644 --- a/PadelClub/Data/TeamRegistration.swift +++ b/PadelClub/Data/TeamRegistration.swift @@ -39,22 +39,15 @@ final class TeamRegistration: ModelObject, Storable { var finalRanking: Int? var pointsEarned: Int? - var unregistered: Bool = false - var unregistrationDate: Date? = nil - - func hasUnregistered() -> Bool { - unregistered - } - func hasRegisteredOnline() -> Bool { players().anySatisfy({ $0.source == .onlineRegistration }) } func isOutOfTournament() -> Bool { - walkOut || unregistered + walkOut } - init(tournament: String, groupStage: String? = nil, registrationDate: Date? = nil, callDate: Date? = nil, bracketPosition: Int? = nil, groupStagePosition: Int? = nil, comment: String? = nil, source: String? = nil, sourceValue: String? = nil, logo: String? = nil, name: String? = nil, walkOut: Bool = false, wildCardBracket: Bool = false, wildCardGroupStage: Bool = false, weight: Int = 0, lockedWeight: Int? = nil, confirmationDate: Date? = nil, qualified: Bool = false, finalRanking: Int? = nil, pointsEarned: Int? = nil, unregistered: Bool = false, unregistrationDate: Date? = nil) { + init(tournament: String, groupStage: String? = nil, registrationDate: Date? = nil, callDate: Date? = nil, bracketPosition: Int? = nil, groupStagePosition: Int? = nil, comment: String? = nil, source: String? = nil, sourceValue: String? = nil, logo: String? = nil, name: String? = nil, walkOut: Bool = false, wildCardBracket: Bool = false, wildCardGroupStage: Bool = false, weight: Int = 0, lockedWeight: Int? = nil, confirmationDate: Date? = nil, qualified: Bool = false, finalRanking: Int? = nil, pointsEarned: Int? = nil) { self.tournament = tournament self.groupStage = groupStage self.registrationDate = registrationDate ?? Date() @@ -75,8 +68,6 @@ final class TeamRegistration: ModelObject, Storable { self.qualified = qualified self.finalRanking = finalRanking self.pointsEarned = pointsEarned - self.unregistered = unregistered - self.unregistrationDate = unregistrationDate } var tournamentStore: TournamentStore { @@ -333,7 +324,6 @@ final class TeamRegistration: ModelObject, Storable { } func initialRoundColor() -> Color? { - if unregistered { return Color.black } if walkOut { return Color.logoRed } if groupStagePosition != nil { return Color.blue } if let initialRound = initialRound(), let colorHex = RoundRule.colors[safe: initialRound.index] { @@ -640,8 +630,6 @@ final class TeamRegistration: ModelObject, Storable { case _qualified = "qualified" case _finalRanking = "finalRanking" case _pointsEarned = "pointsEarned" - case _unregistered = "unregistered" - case _unregistrationDate = "unregistrationDate" } init(from decoder: Decoder) throws { @@ -654,7 +642,6 @@ final class TeamRegistration: ModelObject, Storable { wildCardGroupStage = try container.decodeIfPresent(Bool.self, forKey: ._wildCardGroupStage) ?? false weight = try container.decodeIfPresent(Int.self, forKey: ._weight) ?? 0 qualified = try container.decodeIfPresent(Bool.self, forKey: ._qualified) ?? false - unregistered = try container.decodeIfPresent(Bool.self, forKey: ._unregistered) ?? false // Optional properties groupStage = try container.decodeIfPresent(String.self, forKey: ._groupStage) @@ -671,7 +658,6 @@ final class TeamRegistration: ModelObject, Storable { confirmationDate = try container.decodeIfPresent(Date.self, forKey: ._confirmationDate) finalRanking = try container.decodeIfPresent(Int.self, forKey: ._finalRanking) pointsEarned = try container.decodeIfPresent(Int.self, forKey: ._pointsEarned) - unregistrationDate = try container.decodeIfPresent(Date.self, forKey: ._unregistrationDate) } func encode(to encoder: Encoder) throws { @@ -698,8 +684,6 @@ final class TeamRegistration: ModelObject, Storable { try container.encode(qualified, forKey: ._qualified) try container.encode(finalRanking, forKey: ._finalRanking) try container.encode(pointsEarned, forKey: ._pointsEarned) - try container.encode(unregistered, forKey: ._unregistered) - try container.encode(unregistrationDate, forKey: ._unregistrationDate) } func insertOnServer() { diff --git a/PadelClub/Views/Team/EditingTeamView.swift b/PadelClub/Views/Team/EditingTeamView.swift index 9432a3e..f8cc5d5 100644 --- a/PadelClub/Views/Team/EditingTeamView.swift +++ b/PadelClub/Views/Team/EditingTeamView.swift @@ -94,34 +94,6 @@ struct EditingTeamView: View { } } - Toggle(isOn: .init(get: { - return team.unregistered - }, set: { value in - team.resetPositions() - team.wildCardGroupStage = false - team.walkOut = false - team.wildCardBracket = false - team.unregistered = value - if value { - team.unregistrationDate = Date() - } else { - team.unregistered = false - team.unregistrationDate = nil - team.registrationDate = Date() - } - do { - try tournamentStore.teamRegistrations.addOrUpdate(instance: team) - } catch { - Logger.error(error) - } - })) { - Text("Annulation") - if let unregisterDate = team.unregistrationDate { - Text(unregisterDate.localizedDate()) - } - } - - Toggle(isOn: .init(get: { return team.wildCardBracket }, set: { value in diff --git a/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift b/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift index b3dbaed..e2cea7d 100644 --- a/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift +++ b/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift @@ -79,7 +79,6 @@ struct InscriptionManagerView: View { case registeredOnline case walkOut case waiting - case unregistered case bracket case groupStage case wildcardGroupStage @@ -108,8 +107,6 @@ struct InscriptionManagerView: View { return "Vous n'avez placé aucune équipe en poule." case .notImported: return "Vous n'avez aucune équipe non importé. Elles proviennent toutes du fichier." - case .unregistered: - return "Vous n'avez aucune équipe ayant annulé une inscription." } } @@ -135,8 +132,6 @@ struct InscriptionManagerView: View { return "Aucune équipe en poule" case .notImported: return "Aucune équipe non importée" - case .unregistered: - return "Aucune équipe ayant annulé" } } @@ -162,8 +157,6 @@ struct InscriptionManagerView: View { return displayStyle == .wide ? "Liste d'attente" : "attente" case .notImported: return "Non importées" - case .unregistered: - return displayStyle == .wide ? "Retirées" : "retirées" } } } @@ -512,8 +505,6 @@ struct InscriptionManagerView: View { teams = teams.filter({ $0.inGroupStage() }) case .notImported: teams = teams.filter({ $0.isImported() == false }) - case .unregistered: - teams = teams.filter({ $0.hasUnregistered() == true }) case .registeredLocally: teams = teams.filter({ $0.hasRegisteredOnline() == false }) case .registeredOnline: @@ -739,9 +730,6 @@ struct InscriptionManagerView: View { case .notImported: let notImported: Int = max(0, sortedTeams.filter({ $0.isImported() == false }).count) return notImported.formatted() - case .unregistered: - let unregistered: Int = max(0, sortedTeams.filter({ $0.hasUnregistered() }).count) - return unregistered.formatted() case .registeredLocally: let registeredLocally: Int = max(0, sortedTeams.filter({ $0.hasRegisteredOnline() == false }).count) return registeredLocally.formatted() diff --git a/PadelClubTests/ServerDataTests.swift b/PadelClubTests/ServerDataTests.swift index 6cca7a4..a929f03 100644 --- a/PadelClubTests/ServerDataTests.swift +++ b/PadelClubTests/ServerDataTests.swift @@ -208,7 +208,7 @@ final class ServerDataTests: XCTestCase { return } - let teamRegistration = TeamRegistration(tournament: tournamentId, groupStage: groupStageId, registrationDate: Date(), callDate: Date(), bracketPosition: 1, groupStagePosition: 2, comment: "comment", source: "source", sourceValue: "source V", logo: "logo", name: "Stax", walkOut: true, wildCardBracket: true, wildCardGroupStage: true, weight: 1, lockedWeight: 11, confirmationDate: Date(), qualified: true, finalRanking: 4, pointsEarned: 200, unregistered: true, unregistrationDate: Date()) + let teamRegistration = TeamRegistration(tournament: tournamentId, groupStage: groupStageId, registrationDate: Date(), callDate: Date(), bracketPosition: 1, groupStagePosition: 2, comment: "comment", source: "source", sourceValue: "source V", logo: "logo", name: "Stax", walkOut: true, wildCardBracket: true, wildCardGroupStage: true, weight: 1, lockedWeight: 11, confirmationDate: Date(), qualified: true, finalRanking: 4, pointsEarned: 200) let tr: TeamRegistration = try await StoreCenter.main.service().post(teamRegistration) @@ -232,8 +232,6 @@ final class ServerDataTests: XCTestCase { assert(tr.qualified == teamRegistration.qualified) assert(tr.finalRanking == teamRegistration.finalRanking) assert(tr.pointsEarned == teamRegistration.pointsEarned) - assert(tr.unregistered == teamRegistration.unregistered) - assert(tr.unregistrationDate?.formatted() == teamRegistration.unregistrationDate?.formatted()) } func testPlayerRegistration() async throws { From 7b06c8cefc1fa7897b8596e3cfd438818600b504 Mon Sep 17 00:00:00 2001 From: Raz Date: Wed, 18 Dec 2024 15:35:06 +0100 Subject: [PATCH 12/57] fix registration online --- PadelClub.xcodeproj/project.pbxproj | 16 ++-- PadelClub/Data/Tournament.swift | 32 +++++++- PadelClub/PadelClubApp.swift | 2 +- PadelClub/Utils/Tips.swift | 23 ++++++ PadelClub/ViewModel/Screen.swift | 1 + .../Cashier/Event/EventCreationView.swift | 2 +- .../Cashier/Event/EventTournamentsView.swift | 2 +- .../Navigation/Agenda/CalendarView.swift | 2 +- .../Navigation/Agenda/EventListView.swift | 2 +- PadelClub/Views/Team/EditingTeamView.swift | 12 ++- .../TournamentGeneralSettingsView.swift | 67 ++++++++-------- .../Screen/InscriptionManagerView.swift | 9 ++- ...View.swift => RegistrationSetupView.swift} | 8 +- .../Screen/TournamentRankView.swift | 42 ++++++---- .../Shared/TournamentCellView.swift | 3 +- .../Views/Tournament/TournamentView.swift | 80 +++++++++++++++---- 16 files changed, 218 insertions(+), 85 deletions(-) rename PadelClub/Views/Tournament/Screen/{RegisrationSetupView.swift => RegistrationSetupView.swift} (98%) diff --git a/PadelClub.xcodeproj/project.pbxproj b/PadelClub.xcodeproj/project.pbxproj index 12697b2..74ca800 100644 --- a/PadelClub.xcodeproj/project.pbxproj +++ b/PadelClub.xcodeproj/project.pbxproj @@ -798,9 +798,9 @@ FFBF41822BF73EB3001B24CB /* EventView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFBF41812BF73EB3001B24CB /* EventView.swift */; }; FFBF41842BF75ED7001B24CB /* EventTournamentsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFBF41832BF75ED7001B24CB /* EventTournamentsView.swift */; }; FFBF41862BF75FDA001B24CB /* EventSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFBF41852BF75FDA001B24CB /* EventSettingsView.swift */; }; - FFBFC3912CEE3A0E000EBD8D /* RegisrationSetupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFBFC3902CEE3A0E000EBD8D /* RegisrationSetupView.swift */; }; - FFBFC3922CEE3A0E000EBD8D /* RegisrationSetupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFBFC3902CEE3A0E000EBD8D /* RegisrationSetupView.swift */; }; - FFBFC3932CEE3A0E000EBD8D /* RegisrationSetupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFBFC3902CEE3A0E000EBD8D /* RegisrationSetupView.swift */; }; + FFBFC3912CEE3A0E000EBD8D /* RegistrationSetupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFBFC3902CEE3A0E000EBD8D /* RegistrationSetupView.swift */; }; + FFBFC3922CEE3A0E000EBD8D /* RegistrationSetupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFBFC3902CEE3A0E000EBD8D /* RegistrationSetupView.swift */; }; + FFBFC3932CEE3A0E000EBD8D /* RegistrationSetupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFBFC3902CEE3A0E000EBD8D /* RegistrationSetupView.swift */; }; FFBFC3952CF05CBB000EBD8D /* DateMenuView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFBFC3942CF05CBB000EBD8D /* DateMenuView.swift */; }; FFBFC3962CF05CBB000EBD8D /* DateMenuView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFBFC3942CF05CBB000EBD8D /* DateMenuView.swift */; }; FFBFC3972CF05CBB000EBD8D /* DateMenuView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFBFC3942CF05CBB000EBD8D /* DateMenuView.swift */; }; @@ -1194,7 +1194,7 @@ FFBF41812BF73EB3001B24CB /* EventView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventView.swift; sourceTree = ""; }; FFBF41832BF75ED7001B24CB /* EventTournamentsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventTournamentsView.swift; sourceTree = ""; }; FFBF41852BF75FDA001B24CB /* EventSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventSettingsView.swift; sourceTree = ""; }; - FFBFC3902CEE3A0E000EBD8D /* RegisrationSetupView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RegisrationSetupView.swift; sourceTree = ""; }; + FFBFC3902CEE3A0E000EBD8D /* RegistrationSetupView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RegistrationSetupView.swift; sourceTree = ""; }; FFBFC3942CF05CBB000EBD8D /* DateMenuView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateMenuView.swift; sourceTree = ""; }; FFC1E1032BAC28C6008D6F59 /* ClubSearchView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClubSearchView.swift; sourceTree = ""; }; FFC1E1072BAC29FC008D6F59 /* LocationManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationManager.swift; sourceTree = ""; }; @@ -1680,7 +1680,7 @@ isa = PBXGroup; children = ( FF70916D2B9108C600AB08DA /* InscriptionManagerView.swift */, - FFBFC3902CEE3A0E000EBD8D /* RegisrationSetupView.swift */, + FFBFC3902CEE3A0E000EBD8D /* RegistrationSetupView.swift */, FF90FC1C2C44FB3E009339B2 /* AddTeamView.swift */, FF8F26422BADFE5B00650388 /* TournamentSettingsView.swift */, FF8F26532BAE1E4400650388 /* TableStructureView.swift */, @@ -2436,7 +2436,7 @@ C45BAE442BCA753E002EEC8A /* Purchase.swift in Sources */, FF6EC8FE2B94792300EA7F5A /* Screen.swift in Sources */, FF967CEE2BAECBD700A9A3BD /* Round.swift in Sources */, - FFBFC3922CEE3A0E000EBD8D /* RegisrationSetupView.swift in Sources */, + FFBFC3922CEE3A0E000EBD8D /* RegistrationSetupView.swift in Sources */, FF5BAF6E2BE0B3C8008B4B7E /* FederalDataViewModel.swift in Sources */, FF3F74FF2B91A2D4004CFE0E /* AgendaDestination.swift in Sources */, FF3795622B9396D0004EA093 /* PadelClubApp.xcdatamodeld in Sources */, @@ -2723,7 +2723,7 @@ FF4CBFB92C996C0600151637 /* Purchase.swift in Sources */, FF4CBFBA2C996C0600151637 /* Screen.swift in Sources */, FF4CBFBB2C996C0600151637 /* Round.swift in Sources */, - FFBFC3912CEE3A0E000EBD8D /* RegisrationSetupView.swift in Sources */, + FFBFC3912CEE3A0E000EBD8D /* RegistrationSetupView.swift in Sources */, FF4CBFBC2C996C0600151637 /* FederalDataViewModel.swift in Sources */, FF4CBFBD2C996C0600151637 /* AgendaDestination.swift in Sources */, FF4CBFBE2C996C0600151637 /* PadelClubApp.xcdatamodeld in Sources */, @@ -2989,7 +2989,7 @@ FF70FB382C90584900129CC2 /* Purchase.swift in Sources */, FF70FB392C90584900129CC2 /* Screen.swift in Sources */, FF70FB3A2C90584900129CC2 /* Round.swift in Sources */, - FFBFC3932CEE3A0E000EBD8D /* RegisrationSetupView.swift in Sources */, + FFBFC3932CEE3A0E000EBD8D /* RegistrationSetupView.swift in Sources */, FF70FB3B2C90584900129CC2 /* FederalDataViewModel.swift in Sources */, FF70FB3C2C90584900129CC2 /* AgendaDestination.swift in Sources */, FF70FB3D2C90584900129CC2 /* PadelClubApp.xcdatamodeld in Sources */, diff --git a/PadelClub/Data/Tournament.swift b/PadelClub/Data/Tournament.swift index dc58c5e..2e7d5bb 100644 --- a/PadelClub/Data/Tournament.swift +++ b/PadelClub/Data/Tournament.swift @@ -2131,12 +2131,16 @@ defer { } } - func setupFederalSettings() { + func setupFederalSettings(fromEvent event: Event?) { teamSorting = tournamentLevel.defaultTeamSortingType groupStageMatchFormat = groupStageSmartMatchFormat() loserBracketMatchFormat = loserBracketSmartMatchFormat(5) matchFormat = roundSmartMatchFormat(5) entryFee = tournamentLevel.entryFee + if event?.tenupId != nil { + enableOnlineRegistration = true + registrationDateLimit = deadline(for: .inscription) + } } func roundSmartMatchFormat(_ roundIndex: Int) -> MatchFormat { @@ -2503,6 +2507,32 @@ defer { return .open } + + // MARK: - Status + + func shouldTournamentBeOver() -> Bool { +#if _DEBUGING_TIME //DEBUGING TIME +let start = Date() +defer { + let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000) + print("func shouldTournamentBeOver()", id, duration.formatted(.units(allowed: [.seconds, .milliseconds]))) +} +#endif + if isDeleted == false && hasEnded() == false && hasStarted() { + let allMatches = allMatches() + let remainingMatches = allMatches.filter({ $0.hasEnded() == false && $0.startDate != nil }) + + let calendar = Calendar.current + let anyTomorrow = remainingMatches.anySatisfy({ calendar.isDateInTomorrow($0.startDate!) }) + + + if anyTomorrow == false, let endDate = allMatches.filter({ $0.hasEnded() }).sorted(by: \.endDate!, order: .ascending).last?.endDate, endDate.timeIntervalSinceNow <= -2 * 3600 { + return true + } + } + + return false + } // MARK: - diff --git a/PadelClub/PadelClubApp.swift b/PadelClub/PadelClubApp.swift index 4cdba91..3482b96 100644 --- a/PadelClub/PadelClubApp.swift +++ b/PadelClub/PadelClubApp.swift @@ -102,7 +102,7 @@ print("Running in Release mode") //try? Tips.resetDatastore() try? Tips.configure([ - .displayFrequency(.daily), + .displayFrequency(.immediate), .datastoreLocation(.applicationDefault) ]) } diff --git a/PadelClub/Utils/Tips.swift b/PadelClub/Utils/Tips.swift index bb136dd..6547aec 100644 --- a/PadelClub/Utils/Tips.swift +++ b/PadelClub/Utils/Tips.swift @@ -572,6 +572,29 @@ struct PlayerTournamentSearchTip: Tip { } +struct OnlineRegistrationTip: Tip { + var title: Text { + Text("Inscription en ligne") + } + + var message: Text? { + Text("Facilitez les inscriptions à votre tournoi en activant l'inscription en ligne. Les joueurs pourront s'inscrire directement depuis l'application ou le site Padel Club.") + } + + var image: Image? { + Image(systemName: "person.2.crop.square.stack") + } + + var actions: [Action] { + Action(id: ActionKey.enableOnlineRegistration.rawValue, title: "Activer dans les réglages du tournoi") + } + + enum ActionKey: String { + case enableOnlineRegistration = "enableOnlineRegistration" + } +} + + struct TipStyleModifier: ViewModifier { @Environment(\.colorScheme) var colorScheme var tint: Color? diff --git a/PadelClub/ViewModel/Screen.swift b/PadelClub/ViewModel/Screen.swift index 45534b1..fec745f 100644 --- a/PadelClub/ViewModel/Screen.swift +++ b/PadelClub/ViewModel/Screen.swift @@ -21,4 +21,5 @@ enum Screen: String, Codable { case event case print case restingTime + case stateSettings } diff --git a/PadelClub/Views/Cashier/Event/EventCreationView.swift b/PadelClub/Views/Cashier/Event/EventCreationView.swift index 5ba6f87..cad9bac 100644 --- a/PadelClub/Views/Cashier/Event/EventCreationView.swift +++ b/PadelClub/Views/Cashier/Event/EventCreationView.swift @@ -143,7 +143,7 @@ struct EventCreationView: View { tournament.courtCount = selectedClub?.courtCount ?? 2 tournament.startDate = startingDate tournament.dayDuration = duration - tournament.setupFederalSettings() + tournament.setupFederalSettings(fromEvent: event) } do { diff --git a/PadelClub/Views/Cashier/Event/EventTournamentsView.swift b/PadelClub/Views/Cashier/Event/EventTournamentsView.swift index 198a313..4fb75fe 100644 --- a/PadelClub/Views/Cashier/Event/EventTournamentsView.swift +++ b/PadelClub/Views/Cashier/Event/EventTournamentsView.swift @@ -63,7 +63,7 @@ struct EventTournamentsView: View { newTournament.courtCount = event.eventCourtCount() newTournament.startDate = event.eventStartDate() newTournament.dayDuration = event.eventDayDuration() - newTournament.setupFederalSettings() + newTournament.setupFederalSettings(fromEvent: event) do { try dataStore.tournaments.addOrUpdate(instance: newTournament) diff --git a/PadelClub/Views/Navigation/Agenda/CalendarView.swift b/PadelClub/Views/Navigation/Agenda/CalendarView.swift index 1dab0aa..f771644 100644 --- a/PadelClub/Views/Navigation/Agenda/CalendarView.swift +++ b/PadelClub/Views/Navigation/Agenda/CalendarView.swift @@ -173,7 +173,7 @@ struct CalendarView: View { newTournament.federalTournamentAge = build.age newTournament.dayDuration = federalTournament.dayDuration newTournament.startDate = federalTournament.startDate.atBeginningOfDay(hourInt: 9) - newTournament.setupFederalSettings() + newTournament.setupFederalSettings(fromEvent: event) do { try dataStore.tournaments.addOrUpdate(instance: newTournament) } catch { diff --git a/PadelClub/Views/Navigation/Agenda/EventListView.swift b/PadelClub/Views/Navigation/Agenda/EventListView.swift index 04d9741..95471c4 100644 --- a/PadelClub/Views/Navigation/Agenda/EventListView.swift +++ b/PadelClub/Views/Navigation/Agenda/EventListView.swift @@ -37,7 +37,6 @@ struct EventListView: View { Text("\(count.formatted()) tournoi" + count.pluralSuffix) } } - .id(sectionIndex) .headerProminence(.increased) } } @@ -119,6 +118,7 @@ struct EventListView: View { NavigationLink(value: tournament) { TournamentCellView(tournament: tournament) } + .listRowView(isActive: tournament.shouldTournamentBeOver(), color: .red, hideColorVariation: true, alignment: .leading) .contextMenu { if tournament.hasEnded() == false { Button { diff --git a/PadelClub/Views/Team/EditingTeamView.swift b/PadelClub/Views/Team/EditingTeamView.swift index f8cc5d5..a642e8b 100644 --- a/PadelClub/Views/Team/EditingTeamView.swift +++ b/PadelClub/Views/Team/EditingTeamView.swift @@ -51,8 +51,14 @@ struct EditingTeamView: View { RowButtonView("Modifier la composition de l'équipe") { editedTeam = team } - + TeamDetailView(team: team) + } header: { + if team.hasRegisteredOnline() { + Text("Inscription en ligne") + } else { + Text("Inscription par vous-même") + } } footer: { HStack { CopyPasteButtonView(pasteValue: team.playersPasteData()) @@ -203,6 +209,10 @@ struct EditingTeamView: View { } dismiss() } + } footer: { + if team.hasRegisteredOnline() { + Text("Attention, supprimer cette équipe notifiera par email que leur inscription a été annulée.") + } } } .navigationBarBackButtonHidden(focusedField != nil) diff --git a/PadelClub/Views/Tournament/Screen/Components/TournamentGeneralSettingsView.swift b/PadelClub/Views/Tournament/Screen/Components/TournamentGeneralSettingsView.swift index c731f66..6b94b72 100644 --- a/PadelClub/Views/Tournament/Screen/Components/TournamentGeneralSettingsView.swift +++ b/PadelClub/Views/Tournament/Screen/Components/TournamentGeneralSettingsView.swift @@ -32,39 +32,6 @@ struct TournamentGeneralSettingsView: View { var body: some View { @Bindable var tournament = tournament Form { - - Section { - TextField("Nom du tournoi", text: $tournamentName, axis: .vertical) - .lineLimit(2) - .frame(maxWidth: .infinity) - .keyboardType(.alphabet) - .focused($focusedField, equals: ._name) - } header: { - Text("Nom du tournoi") - } - - Section { - ZStack { - Text(tournamentInformation).opacity(0) - Text(ContactType.defaultCustomMessage).opacity(0) - TextEditor(text: $tournamentInformation) - .keyboardType(.alphabet) - .focused($focusedField, equals: ._information) - } - .frame(maxHeight: 200) - .overlay { - if tournamentInformation.isEmpty { - Text("Texte visible dans l'onglet informations sur Padel Club.").italic() - } - } - } header: { - Text("Description du tournoi") - } footer: { - FooterButtonView("Ajouter le prix de l'inscription") { - tournamentInformation.append("\n" + tournament.entryFeeMessage) - } - } - Section { TournamentDatePickerView() TournamentDurationManagerView() @@ -83,7 +50,7 @@ struct TournamentGeneralSettingsView: View { Section { NavigationLink { - RegisrationSetupView(tournament: tournament) + RegistrationSetupView(tournament: tournament) } label: { LabeledContent { if tournament.enableOnlineRegistration { @@ -104,6 +71,38 @@ struct TournamentGeneralSettingsView: View { Text("Paramétrez les possibilités d'inscription en ligne à votre tournoi via Padel Club") } + Section { + TextField("Nom du tournoi", text: $tournamentName, axis: .vertical) + .lineLimit(2) + .frame(maxWidth: .infinity) + .keyboardType(.alphabet) + .focused($focusedField, equals: ._name) + } header: { + Text("Nom du tournoi") + } + + Section { + ZStack { + Text(tournamentInformation).opacity(0) + Text(ContactType.defaultCustomMessage).opacity(0) + TextEditor(text: $tournamentInformation) + .keyboardType(.alphabet) + .focused($focusedField, equals: ._information) + } + .frame(maxHeight: 200) + .overlay { + if tournamentInformation.isEmpty { + Text("Texte visible dans l'onglet informations sur Padel Club.").italic() + } + } + } header: { + Text("Description du tournoi") + } footer: { + FooterButtonView("Ajouter le prix de l'inscription") { + tournamentInformation.append("\n" + tournament.entryFeeMessage) + } + } + Section { TournamentLevelPickerView() } diff --git a/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift b/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift index e2cea7d..2a96c5b 100644 --- a/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift +++ b/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift @@ -23,7 +23,8 @@ let teamsExportTip = TeamsExportTip() struct InscriptionManagerView: View { @EnvironmentObject var dataStore: DataStore - + @Environment(NavigationViewModel.self) var navigation: NavigationViewModel + @Environment(\.dismiss) var dismiss @Bindable var tournament: Tournament @@ -248,6 +249,10 @@ struct InscriptionManagerView: View { RowButtonView("Rafraîchir la liste") { await _refreshList() } + } else { + RowButtonView("Inscription en ligne") { + navigation.path.append(Screen.settings) + } } } } @@ -608,9 +613,11 @@ struct InscriptionManagerView: View { } label: { TeamRowView(team: team) } + #if DEBUG .swipeActions(edge: .trailing, allowsFullSwipe: true) { _teamDeleteButtonView(team) } + #endif .listRowView(isActive: true, color: team.initialRoundColor() ?? tournament.cutLabelColor(index: teamIndex, teamCount: filterMode == .waiting ? 0 : selectedSortedTeams.count), hideColorVariation: true) } } header: { diff --git a/PadelClub/Views/Tournament/Screen/RegisrationSetupView.swift b/PadelClub/Views/Tournament/Screen/RegistrationSetupView.swift similarity index 98% rename from PadelClub/Views/Tournament/Screen/RegisrationSetupView.swift rename to PadelClub/Views/Tournament/Screen/RegistrationSetupView.swift index f77bc70..75f8134 100644 --- a/PadelClub/Views/Tournament/Screen/RegisrationSetupView.swift +++ b/PadelClub/Views/Tournament/Screen/RegistrationSetupView.swift @@ -1,5 +1,5 @@ // -// RegisrationSetupView.swift +// RegistrationSetupView.swift // PadelClub // // Created by razmig on 20/11/2024. @@ -8,7 +8,7 @@ import SwiftUI import LeStorage -struct RegisrationSetupView: View { +struct RegistrationSetupView: View { @EnvironmentObject var dataStore: DataStore @Bindable var tournament: Tournament @State private var enableOnlineRegistration: Bool @@ -89,7 +89,9 @@ struct RegisrationSetupView: View { if enableOnlineRegistration { if let shareURL = tournament.shareURL(.info) { Section { - ShareLink(item: shareURL) + ShareLink(item: shareURL) { + Text(shareURL.absoluteString) + } } header: { Text("Page d'inscription") } footer: { diff --git a/PadelClub/Views/Tournament/Screen/TournamentRankView.swift b/PadelClub/Views/Tournament/Screen/TournamentRankView.swift index 62c5352..2a8e137 100644 --- a/PadelClub/Views/Tournament/Screen/TournamentRankView.swift +++ b/PadelClub/Views/Tournament/Screen/TournamentRankView.swift @@ -28,6 +28,14 @@ struct TournamentRankView: View { } } + var hideRankings: Binding { + Binding { + tournament.publishRankings == false + } set: { value in + tournament.publishRankings = !value + } + } + var body: some View { List { @Bindable var tournament = tournament @@ -53,18 +61,17 @@ struct TournamentRankView: View { } //affiche l'onglet sur le site, car sur le broadcast c'est dispo automatiquement de toute façon Toggle(isOn: $tournament.publishRankings) { - Text("Publier sur Padel Club") - if let url = tournament.shareURL(.rankings) { - Link(destination: url) { - Text("Accéder à la page") - } + if calculating { + ProgressView("Calcul en cours") + } else { + Text("Publier sur Padel Club") } } - .onChange(of: tournament.publishRankings) { - do { - try dataStore.tournaments.addOrUpdate(instance: tournament) - } catch { - Logger.error(error) + .disabled(calculating) + } footer: { + if let url = tournament.shareURL(.rankings) { + Link(destination: url) { + Text("Voir la page des classements sur Padel Club") } } } @@ -88,6 +95,8 @@ struct TournamentRankView: View { team.finalRanking = nil team.pointsEarned = nil } + + tournament.publishRankings = false _save() } } @@ -107,6 +116,13 @@ struct TournamentRankView: View { } } .id(calculating) + .onChange(of: tournament.publishRankings) { + do { + try dataStore.tournaments.addOrUpdate(instance: tournament) + } catch { + Logger.error(error) + } + } .alert("Position", isPresented: isEditingTeam) { if let selectedTeam { @Bindable var team = selectedTeam @@ -131,16 +147,12 @@ struct TournamentRankView: View { } } } - .overlay(content: { - if calculating { - ProgressView() - } - }) .onAppear { let rankingPublished = tournament.selectedSortedTeams().anySatisfy({ $0.finalRanking != nil }) if rankingPublished == false { Task { await _calculateRankings() + tournament.publishRankings = true } } } diff --git a/PadelClub/Views/Tournament/Shared/TournamentCellView.swift b/PadelClub/Views/Tournament/Shared/TournamentCellView.swift index e3b862d..f1499b8 100644 --- a/PadelClub/Views/Tournament/Shared/TournamentCellView.swift +++ b/PadelClub/Views/Tournament/Shared/TournamentCellView.swift @@ -160,6 +160,7 @@ struct TournamentCellView: View { } } .font(.caption) + .listRowView(isActive: existingTournament != nil, color: .green, hideColorVariation: true, alignment: .leading) } private func _createOrShow(federalTournament: FederalTournament, existingTournament: Tournament?, build: any TournamentBuildHolder) { @@ -178,7 +179,7 @@ struct TournamentCellView: View { newTournament.federalTournamentAge = build.age newTournament.dayDuration = federalTournament.dayDuration newTournament.startDate = federalTournament.startDate.atBeginningOfDay(hourInt: 9) - newTournament.setupFederalSettings() + newTournament.setupFederalSettings(fromEvent: event) do { try dataStore.tournaments.addOrUpdate(instance: newTournament) } catch { diff --git a/PadelClub/Views/Tournament/TournamentView.swift b/PadelClub/Views/Tournament/TournamentView.swift index 4ca64cd..b8d0371 100644 --- a/PadelClub/Views/Tournament/TournamentView.swift +++ b/PadelClub/Views/Tournament/TournamentView.swift @@ -11,14 +11,15 @@ import TipKit struct TournamentView: View { @EnvironmentObject var dataStore: DataStore - + @Environment(NavigationViewModel.self) var navigation: NavigationViewModel @State var tournament: Tournament var presentationContext: PresentationContext = .agenda - + let tournamentSelectionTip: TournamentSelectionTip = TournamentSelectionTip() let tournamentRunningTip: TournamentRunningTip = TournamentRunningTip() - + let onlineRegistrationTip: OnlineRegistrationTip = OnlineRegistrationTip() + var selectedTournamentId: Binding { Binding( get: { tournament.id }, set: { id in @@ -36,7 +37,7 @@ struct TournamentView: View { guard let lastDataSource else { return nil } return URL.importDateFormatter.date(from: lastDataSource) } - + init(tournament: Tournament, presentationContext: PresentationContext = .agenda) { self.tournament = tournament self.presentationContext = presentationContext @@ -59,17 +60,63 @@ struct TournamentView: View { } } case .initial: + if tournament.enableOnlineRegistration == false { + TipView(onlineRegistrationTip) { actions in + navigation.path.append(Screen.settings) + } + .tipStyle(tint: .master, asSection: true) + } + Section { TournamentInscriptionView(tournament: tournament) } TournamentInitView(tournament: tournament) case .build: + if tournament.enableOnlineRegistration == false { + TipView(onlineRegistrationTip) { actions in + navigation.path.append(Screen.settings) + } + .tipStyle(tint: .master, asSection: true) + } + Section { TournamentInscriptionView(tournament: tournament) } TournamentBuildView(tournament: tournament) TournamentInitView(tournament: tournament) case .running: + if tournament.shouldTournamentBeOver() { + Section { + Menu { + + Button("Scores manquants") { + if let activeRound = tournament.getActiveRound() { + navigation.path.append(Screen.round) + } else if let activeGroupStage = tournament.getActiveGroupStage() { + navigation.path.append(Screen.groupStage) + } + } + + Button { + navigation.path.append(Screen.stateSettings) + } label: { + Text("Annuler le tournoi") + } + + } label: { + Label("Actions rapides", systemImage: "bolt.circle") + .foregroundColor(.white) + .frame(height: 32) + .font(.headline) + .frame(maxWidth: .infinity) + } + .menuStyle(.button) + .buttonStyle(.borderedProminent) + .tint(.logoYellow) + .listRowBackground(Color.clear) + .listRowInsets(EdgeInsets(.zero)) + } + } TournamentBuildView(tournament: tournament) TournamentRunningView(tournament: tournament) case .finished: @@ -113,6 +160,9 @@ struct TournamentView: View { PrintSettingsView(tournament: tournament) case .restingTime: TeamRestingView() + case .stateSettings: + TournamentStatusView(tournament: tournament) + } } .environment(tournament) @@ -126,17 +176,17 @@ struct TournamentView: View { Text(tournament.tournamentTitle(.title)).tag(tournament.id as String) } } label: { - + } - + Divider() } - + NavigationLink(value: Screen.event) { Text("Réglages de l'événement") } } - + } .toolbarBackground(.visible, for: .navigationBar) .toolbar { @@ -151,11 +201,11 @@ struct TournamentView: View { TournamentSelectionTip.tournamentCount = tournament.eventObject()?.tournaments.count } } - + if tournament.isCanceled == false { ToolbarItem(placement: .topBarTrailing) { Menu { - + #if DEBUG Button { do { @@ -167,7 +217,7 @@ struct TournamentView: View { Label("Payer le tournoi", systemImage: "dollarsign.circle.fill") } #endif - + if presentationContext == .agenda { Button { navigation.openTournamentInOrganizer(tournament) @@ -176,7 +226,7 @@ struct TournamentView: View { } Divider() } - + NavigationLink(value: Screen.event) { Text("Réglages de l'événement") } @@ -190,16 +240,14 @@ struct TournamentView: View { NavigationLink(value: Screen.broadcast) { Label("Publication", systemImage: "airplayvideo") } - + NavigationLink(value: Screen.print) { Label("Imprimer", systemImage: "printer") } Divider() - NavigationLink { - TournamentStatusView(tournament: tournament) - } label: { + NavigationLink(value: Screen.stateSettings) { Text("Gestion du tournoi") Text("Annuler, supprimer ou terminer le tournoi") } From 906172d9e26350760f76953ea0ab4a9ff14ba418 Mon Sep 17 00:00:00 2001 From: Raz Date: Wed, 18 Dec 2024 18:02:47 +0100 Subject: [PATCH 13/57] quick enhance tip --- PadelClub/PadelClubApp.swift | 2 +- PadelClub/Utils/Tips.swift | 95 +++++++++++-------- .../Navigation/Agenda/EventListView.swift | 3 +- .../Shared/TournamentCellView.swift | 20 ++-- .../Views/Tournament/TournamentView.swift | 35 ++----- 5 files changed, 77 insertions(+), 78 deletions(-) diff --git a/PadelClub/PadelClubApp.swift b/PadelClub/PadelClubApp.swift index 3482b96..20c2389 100644 --- a/PadelClub/PadelClubApp.swift +++ b/PadelClub/PadelClubApp.swift @@ -99,7 +99,7 @@ print("Running in Release mode") } .task { - //try? Tips.resetDatastore() + try? Tips.resetDatastore() try? Tips.configure([ .displayFrequency(.immediate), diff --git a/PadelClub/Utils/Tips.swift b/PadelClub/Utils/Tips.swift index 6547aec..6a49ad2 100644 --- a/PadelClub/Utils/Tips.swift +++ b/PadelClub/Utils/Tips.swift @@ -21,7 +21,7 @@ struct PadelBeachExportTip: Tip { var image: Image? { Image(systemName: "square.and.arrow.up") } - + var actions: [Action] { Action(id: "more-info-export", title: "En savoir plus") Action(id: "beach-padel", title: "beach-padel.app.fft.fr") @@ -42,7 +42,7 @@ struct PadelBeachImportTip: Tip { var image: Image? { Image(systemName: "square.and.arrow.down") } - + var actions: [Action] { Action(id: "more-info-import", title: "Importer le fichier excel beach-padel") } @@ -61,8 +61,8 @@ struct GenerateLoserBracketTip: Tip { var image: Image? { nil } - - + + var actions: [Action] { Action(id: "generate-loser-bracket", title: "Générer les matchs de classements") } @@ -83,7 +83,7 @@ struct TeamChampionshipTip: Tip { var image: Image? { Image(systemName: "person.3") } - + var actions: [Action] { Action(id: "list-manager", title: "Ouvrir le gestionnaire d'équipe") } @@ -104,7 +104,7 @@ struct TeamChampionshipMainScreenTip: Tip { var image: Image? { Image(systemName: "arrow.uturn.backward") } - + var actions: [Action] { Action(id: "set-list-manager-main", title: "Afficher sur l'écran principal") } @@ -193,7 +193,7 @@ struct InscriptionManagerWomanRankTip: Tip { var image: Image? { Image(systemName: "figure.dress.line.vertical.figure") } - + var title: Text { Text("Rang d'une joueuse dans un tournoi messieurs") } @@ -213,7 +213,7 @@ struct InscriptionManagerRankUpdateTip: Tip { var message: Text? { Text("Padel Club vous permet de mettre à jour le classement des équipes inscrites. Si vous avez clôturé les inscriptions, la mise à jour du classement ne modifie pas la phase d'intégration de l'équipe, poule ou tableau final. Vous pouvez manuellement mettre à jour cette option.") } - + var image: Image? { Image(systemName: "list.number") } @@ -232,7 +232,7 @@ struct SharePictureTip: Tip { var message: Text? { Text("Lors d'un partage d'une photo, le texte est disponible dans le presse-papier du téléphone") } - + var image: Image? { Image(systemName: "photo.badge.checkmark.fill") } @@ -246,7 +246,7 @@ struct NewRankDataAvailableTip: Tip { var message: Text? { Text("Padel Club récupère toutes les données publique provenant de la FFT. L'importation de ce nouveau classement peut prendre plusieurs dizaines de secondes.") } - + var image: Image? { Image(systemName: "exclamationmark.icloud") } @@ -266,7 +266,7 @@ struct ClubSearchTip: Tip { var message: Text? { Text("Padel Club peut rechercher un club autour de vous, d'une ville ou d'un code postal, facilitant ainsi la saisie d'information.") } - + var image: Image? { Image(systemName: "house.and.flag.fill") } @@ -275,7 +275,7 @@ struct ClubSearchTip: Tip { Action(id: ActionKey.searchAroundMe.rawValue, title: "Chercher autour de moi") Action(id: ActionKey.searchCity.rawValue, title: "Chercher une ville") } - + enum ActionKey: String { case searchAroundMe = "search-around-me" case searchCity = "search-city" @@ -291,7 +291,7 @@ struct SlideToDeleteTip: Tip { var message: Text? { Text("Vous pouvez effacer un club en glissant votre doigt vers la gauche") } - + var image: Image? { Image(systemName: "trash") } @@ -306,7 +306,7 @@ struct MultiTournamentsEventTip: Tip { var message: Text? { Text("Padel Club permet de gérer plusieurs tournois ayant lieu en même temps. Un P100 homme et dame le même week-end par exemple.") } - + var image: Image? { Image(systemName: "trophy.circle") } @@ -320,7 +320,7 @@ struct NotFoundAreWalkOutTip: Tip { var message: Text? { Text("Si une équipe déjà présente dans votre liste d'attente n'est pas dans le fichier, elle sera mise WO") } - + var image: Image? { Image(systemName: "person.2.slash.fill") } @@ -338,7 +338,7 @@ struct TournamentPublishingTip: Tip { var message: Text? { Text("Padel Club vous permet de publier votre tournoi et rendre accessible à tous les résultats des matchs et l'évolution de l'événement. Les informations seront accessibles sur le site Padel Club.") } - + var image: Image? { Image("PadelClub_logo_fondclair_transparent") } @@ -352,7 +352,7 @@ struct TournamentTVBroadcastTip: Tip { var message: Text? { return Text("Padel Club vous propose un site spéficique à utiliser sur les écrans de votre club, présentant de manière intelligente l'évolution de votre tournoi.") } - + var image: Image? { Image(systemName: "sparkles.tv") } @@ -361,7 +361,7 @@ struct TournamentTVBroadcastTip: Tip { struct TournamentSelectionTip: Tip { @Parameter static var tournamentCount: Int? = nil - + var rules: [Rule] { [ // Define a rule based on the app state. @@ -379,7 +379,7 @@ struct TournamentSelectionTip: Tip { var message: Text? { return Text("Vous pouvez appuyer sur la barre de navigation pour accéder à un tournoi de votre événement.") } - + var image: Image? { Image(systemName: "filemenu.and.selection") } @@ -388,7 +388,7 @@ struct TournamentSelectionTip: Tip { struct TournamentRunningTip: Tip { @Parameter static var isRunning: Bool = false - + var rules: [Rule] { [ // Define a rule based on the app state. @@ -406,7 +406,7 @@ struct TournamentRunningTip: Tip { var message: Text? { return Text("Le tournoi a commencé, les options utiles surtout à sa préparation sont maintenant accessibles dans le menu en haut à droite.") } - + var image: Image? { Image(systemName: "ellipsis.circle") } @@ -421,18 +421,18 @@ struct CreateAccountTip: Tip { let message = "Un compte est nécessaire pour publier le tournoi sur [Padel Club](\(URLs.main.rawValue)) et profiter de toutes les pages du site, comme le mode TV pour transformer l'expérience de vos tournois !" return Text(.init(message)) } - + var image: Image? { Image(systemName: "person.crop.circle") } - + var actions: [Action] { Action(id: ActionKey.createAccount.rawValue, title: "Créer votre compte") //todo //Action(id: ActionKey.learnMore.rawValue, title: "En savoir plus") Action(id: ActionKey.accessPadelClubWebPage.rawValue, title: "Voir le site Padel Club") } - + enum ActionKey: String { case createAccount = "createAccount" case learnMore = "learnMore" @@ -443,7 +443,7 @@ struct CreateAccountTip: Tip { struct SlideToDeleteSeedTip: Tip { @Parameter static var seeds: Int = 0 - + var rules: [Rule] { [ // Define a rule based on the app state. @@ -461,7 +461,7 @@ struct SlideToDeleteSeedTip: Tip { var message: Text? { Text("Vous pouvez retirer une tête de série de sa position en glissant votre doigt vers la gauche") } - + var image: Image? { Image(systemName: "person.fill.xmark") } @@ -470,7 +470,7 @@ struct SlideToDeleteSeedTip: Tip { struct PrintTip: Tip { @Parameter static var seeds: Int = 0 - + var rules: [Rule] { [ // Define a rule based on the app state. @@ -480,7 +480,7 @@ struct PrintTip: Tip { } ] } - + var title: Text { Text("Coup d'oeil de votre tableau") } @@ -488,7 +488,7 @@ struct PrintTip: Tip { var message: Text? { Text("Vous pouvez avoir un aperçu de votre tableau ou l'imprimer.") } - + var image: Image? { Image(systemName: "printer") } @@ -505,9 +505,9 @@ struct PrintTip: Tip { struct BracketEditTip: Tip { @Parameter static var matchesHidden: Int = 0 - + var nextRoundName: String? - + var rules: [Rule] { [ // Define a rule based on the app state. @@ -528,14 +528,14 @@ struct BracketEditTip: Tip { let wording = nextRoundName != nil ? "en \(nextRoundName!)" : "dans la manche suivante" return Text("Padel Club a bien pris en compte \(article) tête\(Self.matchesHidden.pluralSuffix) de série positionnée\(Self.matchesHidden.pluralSuffix) \(wording). Le\(Self.matchesHidden.pluralSuffix) \(Self.matchesHidden) match\(Self.matchesHidden.pluralSuffix) inutile\(Self.matchesHidden.pluralSuffix) \(grammar) été désactivé automatiquement.") } - + var image: Image? { Image(systemName: "rectangle.slash") } } struct TeamsExportTip: Tip { - + var title: Text { Text("Exporter les paires") } @@ -543,7 +543,7 @@ struct TeamsExportTip: Tip { var message: Text? { Text("Partager les paires comme indiqué dans le guide de la compétition à J-6 avant midi.") } - + var image: Image? { Image(systemName: "square.and.arrow.up") } @@ -557,7 +557,7 @@ struct PlayerTournamentSearchTip: Tip { var message: Text? { Text("Padel Club facilite la recherche de tournois et l'inscription !") } - + var image: Image? { Image(systemName: "trophy.circle") } @@ -580,7 +580,7 @@ struct OnlineRegistrationTip: Tip { var message: Text? { Text("Facilitez les inscriptions à votre tournoi en activant l'inscription en ligne. Les joueurs pourront s'inscrire directement depuis l'application ou le site Padel Club.") } - + var image: Image? { Image(systemName: "person.2.crop.square.stack") } @@ -588,19 +588,36 @@ struct OnlineRegistrationTip: Tip { var actions: [Action] { Action(id: ActionKey.enableOnlineRegistration.rawValue, title: "Activer dans les réglages du tournoi") } - + enum ActionKey: String { case enableOnlineRegistration = "enableOnlineRegistration" } } +struct ShouldTournamentBeOverTip: Tip { + var title: Text { + Text("Clôturer le tournoi ?") + } + + var message: Text? { + Text("Le dernier match est terminé depuis plus de 2 heures. Si le tournoi a été annulé pour cause de météo vous pouvez l'indiquer comme 'Annulé' dans le menu 􀍡, si ce n'est pas le cas, saisissez les scores manquants pour clôturer automatiquement le tournoi et publier le classement final.") + } + + var image: Image? { + Image(systemName: "clock.badge.questionmark") + } + + var actions: [Action] { + Action(id: "tournament-status", title: "Gérer le statut du tournoi") + } +} struct TipStyleModifier: ViewModifier { @Environment(\.colorScheme) var colorScheme var tint: Color? var background: Color? var asSection: Bool - + func body(content: Content) -> some View { if asSection { Section { @@ -610,7 +627,7 @@ struct TipStyleModifier: ViewModifier { preparedContent(content: content) } } - + @ViewBuilder func preparedContent(content: Content) -> some View { if let background { diff --git a/PadelClub/Views/Navigation/Agenda/EventListView.swift b/PadelClub/Views/Navigation/Agenda/EventListView.swift index 95471c4..cd20e93 100644 --- a/PadelClub/Views/Navigation/Agenda/EventListView.swift +++ b/PadelClub/Views/Navigation/Agenda/EventListView.swift @@ -116,9 +116,8 @@ struct EventListView: View { private func _tournamentView(_ tournament: Tournament) -> some View { NavigationLink(value: tournament) { - TournamentCellView(tournament: tournament) + TournamentCellView(tournament: tournament, shouldTournamentBeOver: tournament.shouldTournamentBeOver()) } - .listRowView(isActive: tournament.shouldTournamentBeOver(), color: .red, hideColorVariation: true, alignment: .leading) .contextMenu { if tournament.hasEnded() == false { Button { diff --git a/PadelClub/Views/Tournament/Shared/TournamentCellView.swift b/PadelClub/Views/Tournament/Shared/TournamentCellView.swift index f1499b8..9be8280 100644 --- a/PadelClub/Views/Tournament/Shared/TournamentCellView.swift +++ b/PadelClub/Views/Tournament/Shared/TournamentCellView.swift @@ -15,6 +15,7 @@ struct TournamentCellView: View { let tournament: FederalTournamentHolder // let color: Color = .black var displayStyle: DisplayStyle = .wide + var shouldTournamentBeOver: Bool = false var event: Event? { guard let federalTournament = tournament as? FederalTournament else { return nil } @@ -115,8 +116,9 @@ struct TournamentCellView: View { if let tournament = tournament as? Tournament, displayStyle == .wide { if tournament.isCanceled { Text("Annulé".uppercased()) - .capsule(foreground: .white, background: .red) - + .capsule(foreground: .white, background: .logoRed) + } else if shouldTournamentBeOver { + Image(systemName: "clock.badge.questionmark").foregroundStyle(.logoRed) } else if let teamCount { Text(teamCount.formatted()) } @@ -147,10 +149,15 @@ struct TournamentCellView: View { Text(tournament.durationLabel()) } Spacer() - if let tournament = tournament as? Tournament, tournament.isCanceled == false, let teamCount { - let hasStarted = tournament.inscriptionClosed() || tournament.hasStarted() - let word = hasStarted ? "équipe" : "inscription" - Text(word + teamCount.pluralSuffix) + if let tournament = tournament as? Tournament, tournament.isCanceled == false { + if shouldTournamentBeOver { + Text("à clôturer ?") + .foregroundStyle(.logoRed) + } else if let teamCount { + let hasStarted = tournament.inscriptionClosed() || tournament.hasStarted() + let word = hasStarted ? "équipe" : "inscription" + Text(word + teamCount.pluralSuffix) + } } } } else { @@ -160,7 +167,6 @@ struct TournamentCellView: View { } } .font(.caption) - .listRowView(isActive: existingTournament != nil, color: .green, hideColorVariation: true, alignment: .leading) } private func _createOrShow(federalTournament: FederalTournament, existingTournament: Tournament?, build: any TournamentBuildHolder) { diff --git a/PadelClub/Views/Tournament/TournamentView.swift b/PadelClub/Views/Tournament/TournamentView.swift index b8d0371..8de233d 100644 --- a/PadelClub/Views/Tournament/TournamentView.swift +++ b/PadelClub/Views/Tournament/TournamentView.swift @@ -19,7 +19,8 @@ struct TournamentView: View { let tournamentSelectionTip: TournamentSelectionTip = TournamentSelectionTip() let tournamentRunningTip: TournamentRunningTip = TournamentRunningTip() let onlineRegistrationTip: OnlineRegistrationTip = OnlineRegistrationTip() - + let shouldTournamentBeOverTip: ShouldTournamentBeOverTip = ShouldTournamentBeOverTip() + var selectedTournamentId: Binding { Binding( get: { tournament.id }, set: { id in @@ -76,7 +77,7 @@ struct TournamentView: View { TipView(onlineRegistrationTip) { actions in navigation.path.append(Screen.settings) } - .tipStyle(tint: .master, asSection: true) + .tipStyle(tint: .green, asSection: true) } Section { @@ -87,34 +88,10 @@ struct TournamentView: View { case .running: if tournament.shouldTournamentBeOver() { Section { - Menu { - - Button("Scores manquants") { - if let activeRound = tournament.getActiveRound() { - navigation.path.append(Screen.round) - } else if let activeGroupStage = tournament.getActiveGroupStage() { - navigation.path.append(Screen.groupStage) - } - } - - Button { - navigation.path.append(Screen.stateSettings) - } label: { - Text("Annuler le tournoi") - } - - } label: { - Label("Actions rapides", systemImage: "bolt.circle") - .foregroundColor(.white) - .frame(height: 32) - .font(.headline) - .frame(maxWidth: .infinity) + TipView(shouldTournamentBeOverTip) { actions in + navigation.path.append(Screen.stateSettings) } - .menuStyle(.button) - .buttonStyle(.borderedProminent) - .tint(.logoYellow) - .listRowBackground(Color.clear) - .listRowInsets(EdgeInsets(.zero)) + .tipStyle(tint: .logoRed, asSection: true) } } TournamentBuildView(tournament: tournament) From 242acffe225444950820b4da47ca2630d0a3be81 Mon Sep 17 00:00:00 2001 From: Raz Date: Wed, 18 Dec 2024 18:47:47 +0100 Subject: [PATCH 14/57] add tournament category settings --- PadelClub.xcodeproj/project.pbxproj | 8 ++++ .../TournamentCategorySettingsView.swift | 45 +++++++++++++++++++ .../TournamentGeneralSettingsView.swift | 19 -------- .../Screen/TournamentSettingsView.swift | 7 ++- 4 files changed, 59 insertions(+), 20 deletions(-) create mode 100644 PadelClub/Views/Tournament/Screen/Components/TournamentCategorySettingsView.swift diff --git a/PadelClub.xcodeproj/project.pbxproj b/PadelClub.xcodeproj/project.pbxproj index 74ca800..0023abe 100644 --- a/PadelClub.xcodeproj/project.pbxproj +++ b/PadelClub.xcodeproj/project.pbxproj @@ -130,6 +130,9 @@ FF3F74F62B919E45004CFE0E /* UmpireView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF3F74F52B919E45004CFE0E /* UmpireView.swift */; }; FF3F74FF2B91A2D4004CFE0E /* AgendaDestination.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF3F74FE2B91A2D4004CFE0E /* AgendaDestination.swift */; }; FF44421C2BE39FA2008BBF0B /* Launch Screen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = FFD784002B91BF79000F62A6 /* Launch Screen.storyboard */; }; + FF4623CB2D1340D200CB57B5 /* TournamentCategorySettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF4623CA2D1340D200CB57B5 /* TournamentCategorySettingsView.swift */; }; + FF4623CC2D1340D200CB57B5 /* TournamentCategorySettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF4623CA2D1340D200CB57B5 /* TournamentCategorySettingsView.swift */; }; + FF4623CD2D1340D200CB57B5 /* TournamentCategorySettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF4623CA2D1340D200CB57B5 /* TournamentCategorySettingsView.swift */; }; FF4AB6B52B9248200002987F /* NetworkManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF4AB6B42B9248200002987F /* NetworkManager.swift */; }; FF4AB6BB2B9256D50002987F /* SearchViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF4AB6BA2B9256D50002987F /* SearchViewModel.swift */; }; FF4AB6BD2B9256E10002987F /* SelectablePlayerListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF4AB6BC2B9256E10002987F /* SelectablePlayerListView.swift */; }; @@ -1072,6 +1075,7 @@ FF3B60A22BC49BBC008C2E66 /* MatchScheduler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MatchScheduler.swift; sourceTree = ""; }; FF3F74F52B919E45004CFE0E /* UmpireView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UmpireView.swift; sourceTree = ""; }; FF3F74FE2B91A2D4004CFE0E /* AgendaDestination.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AgendaDestination.swift; sourceTree = ""; }; + FF4623CA2D1340D200CB57B5 /* TournamentCategorySettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TournamentCategorySettingsView.swift; sourceTree = ""; }; FF4AB6B42B9248200002987F /* NetworkManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkManager.swift; sourceTree = ""; }; FF4AB6BA2B9256D50002987F /* SearchViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchViewModel.swift; sourceTree = ""; }; FF4AB6BC2B9256E10002987F /* SelectablePlayerListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectablePlayerListView.swift; sourceTree = ""; }; @@ -1831,6 +1835,7 @@ FF025AE02BD0EB9000A86CF8 /* TournamentClubSettingsView.swift */, FF025AE22BD0EBA900A86CF8 /* TournamentMatchFormatsSettingsView.swift */, FF025AE42BD0EBB800A86CF8 /* TournamentGeneralSettingsView.swift */, + FF4623CA2D1340D200CB57B5 /* TournamentCategorySettingsView.swift */, FF6087E92BE25EF1004E1E47 /* TournamentStatusView.swift */, FFCF76062C3BE9BC006C8C3D /* CloseDatePicker.swift */, ); @@ -2564,6 +2569,7 @@ FF70916A2B90F95E00AB08DA /* DateBoxView.swift in Sources */, FF5D0D722BB3EFA5005CB568 /* LearnMoreSheetView.swift in Sources */, FFF8ACD42B92392C008466FA /* SourceFileManager.swift in Sources */, + FF4623CC2D1340D200CB57B5 /* TournamentCategorySettingsView.swift in Sources */, C4EC6F592BE92D88000CEAB4 /* PListReader.swift in Sources */, FF0EC5222BB173E70056B6D1 /* UpdateSourceRankDateView.swift in Sources */, FF025AE72BD1111000A86CF8 /* GlobalSettingsView.swift in Sources */, @@ -2851,6 +2857,7 @@ FF4CC02C2C996C0600151637 /* DateBoxView.swift in Sources */, FF4CC02D2C996C0600151637 /* LearnMoreSheetView.swift in Sources */, FF4CC02E2C996C0600151637 /* SourceFileManager.swift in Sources */, + FF4623CB2D1340D200CB57B5 /* TournamentCategorySettingsView.swift in Sources */, FF4CC02F2C996C0600151637 /* PListReader.swift in Sources */, FF4CC0302C996C0600151637 /* UpdateSourceRankDateView.swift in Sources */, FF4CC0312C996C0600151637 /* GlobalSettingsView.swift in Sources */, @@ -3117,6 +3124,7 @@ FF70FBAB2C90584900129CC2 /* DateBoxView.swift in Sources */, FF70FBAC2C90584900129CC2 /* LearnMoreSheetView.swift in Sources */, FF70FBAD2C90584900129CC2 /* SourceFileManager.swift in Sources */, + FF4623CD2D1340D200CB57B5 /* TournamentCategorySettingsView.swift in Sources */, FF70FBAE2C90584900129CC2 /* PListReader.swift in Sources */, FF70FBAF2C90584900129CC2 /* UpdateSourceRankDateView.swift in Sources */, FF70FBB02C90584900129CC2 /* GlobalSettingsView.swift in Sources */, diff --git a/PadelClub/Views/Tournament/Screen/Components/TournamentCategorySettingsView.swift b/PadelClub/Views/Tournament/Screen/Components/TournamentCategorySettingsView.swift new file mode 100644 index 0000000..27856e1 --- /dev/null +++ b/PadelClub/Views/Tournament/Screen/Components/TournamentCategorySettingsView.swift @@ -0,0 +1,45 @@ +// +// TournamentCategorySettingsView.swift +// PadelClub +// +// Created by razmig on 18/12/2024. +// + + +import SwiftUI +import LeStorage + +struct TournamentCategorySettingsView: View { + @Environment(Tournament.self) private var tournament: Tournament + @EnvironmentObject var dataStore: DataStore + + var body: some View { + List { + TournamentLevelPickerView() + } + .onChange(of: [ + tournament.federalCategory, + ]) { + _save() + } + .onChange(of: [ + tournament.federalLevelCategory, + ]) { + _save() + } + .onChange(of: [ + tournament.federalAgeCategory, + ]) { + _save() + } + } + + private func _save() { + do { + try dataStore.tournaments.addOrUpdate(instance: tournament) + } catch { + Logger.error(error) + } + } + +} diff --git a/PadelClub/Views/Tournament/Screen/Components/TournamentGeneralSettingsView.swift b/PadelClub/Views/Tournament/Screen/Components/TournamentGeneralSettingsView.swift index 6b94b72..d000292 100644 --- a/PadelClub/Views/Tournament/Screen/Components/TournamentGeneralSettingsView.swift +++ b/PadelClub/Views/Tournament/Screen/Components/TournamentGeneralSettingsView.swift @@ -102,10 +102,6 @@ struct TournamentGeneralSettingsView: View { tournamentInformation.append("\n" + tournament.entryFeeMessage) } } - - Section { - TournamentLevelPickerView() - } Section { Picker(selection: $loserBracketMode) { @@ -226,21 +222,6 @@ struct TournamentGeneralSettingsView: View { .onChange(of: tournament.dayDuration) { _save() } - .onChange(of: [ - tournament.federalCategory, - ]) { - _save() - } - .onChange(of: [ - tournament.federalLevelCategory, - ]) { - _save() - } - .onChange(of: [ - tournament.federalAgeCategory, - ]) { - _save() - } .onChange(of: [ tournament.groupStageSortMode, ]) { diff --git a/PadelClub/Views/Tournament/Screen/TournamentSettingsView.swift b/PadelClub/Views/Tournament/Screen/TournamentSettingsView.swift index 5dc380e..2b14cc7 100644 --- a/PadelClub/Views/Tournament/Screen/TournamentSettingsView.swift +++ b/PadelClub/Views/Tournament/Screen/TournamentSettingsView.swift @@ -16,6 +16,7 @@ enum TournamentSettings: Identifiable, Selectable, Equatable { case general case club(Tournament) case matchFormats + case tournamentType var id: String { String(describing: self) } @@ -29,6 +30,8 @@ enum TournamentSettings: Identifiable, Selectable, Equatable { return "Général" case .club: return "Terrains" + case .tournamentType: + return "Type" } } @@ -55,7 +58,7 @@ struct TournamentSettingsView: View { @Environment(Tournament.self) var tournament: Tournament private func destinations() -> [TournamentSettings] { - [.general, .club(tournament), .matchFormats] + [.general, .tournamentType, .club(tournament), .matchFormats] } var body: some View { @@ -66,6 +69,8 @@ struct TournamentSettingsView: View { TournamentStatusView(tournament: tournament) case .matchFormats: TournamentMatchFormatsSettingsView() + case .tournamentType: + TournamentCategorySettingsView() case .general: TournamentGeneralSettingsView(tournament: tournament) case .club: From dc3d193b55d1881c997bccd3914d1c5ef9d5c8ca Mon Sep 17 00:00:00 2001 From: Raz Date: Wed, 18 Dec 2024 18:56:48 +0100 Subject: [PATCH 15/57] addcorner radius --- PadelClub/Views/Components/RowButtonView.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/PadelClub/Views/Components/RowButtonView.swift b/PadelClub/Views/Components/RowButtonView.swift index 6f741ed..1a38101 100644 --- a/PadelClub/Views/Components/RowButtonView.swift +++ b/PadelClub/Views/Components/RowButtonView.swift @@ -79,6 +79,7 @@ struct RowButtonView: View { if isLoading { ZStack { Color.master + .cornerRadius(20) ProgressView() .tint(.white) } From 494f083a340a280fc9871d15eecae5de2d06ed1b Mon Sep 17 00:00:00 2001 From: Raz Date: Fri, 20 Dec 2024 17:15:32 +0100 Subject: [PATCH 16/57] remove the need to have a target team count --- PadelClub/Data/Tournament.swift | 26 +++++++------------ PadelClub/Utils/PadelRule.swift | 9 +++++++ .../Screen/RegistrationSetupView.swift | 15 ++++------- .../Screen/TableStructureView.swift | 3 ++- 4 files changed, 25 insertions(+), 28 deletions(-) diff --git a/PadelClub/Data/Tournament.swift b/PadelClub/Data/Tournament.swift index 2e7d5bb..d4ade32 100644 --- a/PadelClub/Data/Tournament.swift +++ b/PadelClub/Data/Tournament.swift @@ -63,7 +63,6 @@ final class Tournament : ModelObject, Storable { var enableOnlineRegistration: Bool = false var registrationDateLimit: Date? = nil var openingRegistrationDate: Date? = nil - var targetTeamCount: Int? = nil var waitingListLimit: Int? = nil var accountIsRequired: Bool = true var licenseIsRequired: Bool = true @@ -124,7 +123,6 @@ final class Tournament : ModelObject, Storable { case _enableOnlineRegistration = "enableOnlineRegistration" case _registrationDateLimit = "registrationDateLimit" case _openingRegistrationDate = "openingRegistrationDate" - case _targetTeamCount = "targetTeamCount" case _waitingListLimit = "waitingListLimit" case _accountIsRequired = "accountIsRequired" case _licenseIsRequired = "licenseIsRequired" @@ -134,7 +132,7 @@ final class Tournament : ModelObject, Storable { } - 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, publishRankings: Bool = false, loserBracketMode: LoserBracketMode = .automatic, initialSeedRound: Int = 0, initialSeedCount: Int = 0, enableOnlineRegistration: Bool = false, registrationDateLimit: Date? = nil, openingRegistrationDate: Date? = nil, targetTeamCount: Int? = nil, waitingListLimit: Int? = nil, accountIsRequired: Bool = true, licenseIsRequired: Bool = true, minimumPlayerPerTeam: Int = 2, maximumPlayerPerTeam: Int = 2, information: String? = nil) { + 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, publishRankings: Bool = false, loserBracketMode: LoserBracketMode = .automatic, initialSeedRound: Int = 0, initialSeedCount: Int = 0, enableOnlineRegistration: Bool = false, registrationDateLimit: Date? = nil, openingRegistrationDate: Date? = nil, waitingListLimit: Int? = nil, accountIsRequired: Bool = true, licenseIsRequired: Bool = true, minimumPlayerPerTeam: Int = 2, maximumPlayerPerTeam: Int = 2, information: String? = nil) { self.event = event self.name = name self.startDate = startDate @@ -191,7 +189,6 @@ final class Tournament : ModelObject, Storable { self.enableOnlineRegistration = enableOnlineRegistration self.registrationDateLimit = registrationDateLimit self.openingRegistrationDate = openingRegistrationDate - self.targetTeamCount = targetTeamCount self.waitingListLimit = waitingListLimit self.accountIsRequired = accountIsRequired @@ -249,7 +246,6 @@ final class Tournament : ModelObject, Storable { enableOnlineRegistration = try container.decodeIfPresent(Bool.self, forKey: ._enableOnlineRegistration) ?? false registrationDateLimit = try container.decodeIfPresent(Date.self, forKey: ._registrationDateLimit) openingRegistrationDate = try container.decodeIfPresent(Date.self, forKey: ._openingRegistrationDate) - targetTeamCount = try container.decodeIfPresent(Int.self, forKey: ._targetTeamCount) waitingListLimit = try container.decodeIfPresent(Int.self, forKey: ._waitingListLimit) accountIsRequired = try container.decodeIfPresent(Bool.self, forKey: ._accountIsRequired) ?? true licenseIsRequired = try container.decodeIfPresent(Bool.self, forKey: ._licenseIsRequired) ?? true @@ -348,7 +344,6 @@ final class Tournament : ModelObject, Storable { try container.encode(enableOnlineRegistration, forKey: ._enableOnlineRegistration) try container.encode(registrationDateLimit, forKey: ._registrationDateLimit) try container.encode(openingRegistrationDate, forKey: ._openingRegistrationDate) - try container.encode(targetTeamCount, forKey: ._targetTeamCount) try container.encode(waitingListLimit, forKey: ._waitingListLimit) try container.encode(accountIsRequired, forKey: ._accountIsRequired) @@ -2490,19 +2485,16 @@ defer { } } - if let targetTeamCount = targetTeamCount { - // Get all team registrations excluding walk_outs - let currentTeamCount = unsortedTeamsWithoutWO().count - - if currentTeamCount >= targetTeamCount { - if let waitingListLimit = waitingListLimit { - let waitingListCount = currentTeamCount - targetTeamCount - if waitingListCount >= waitingListLimit { - return .waitingListFull - } + let currentTeamCount = unsortedTeamsWithoutWO().count + + if currentTeamCount >= teamCount { + if let waitingListLimit = waitingListLimit { + let waitingListCount = currentTeamCount - teamCount + if waitingListCount >= waitingListLimit { + return .waitingListFull } - return .waitingListPossible } + return .waitingListPossible } return .open diff --git a/PadelClub/Utils/PadelRule.swift b/PadelClub/Utils/PadelRule.swift index 07371b1..fc85147 100644 --- a/PadelClub/Utils/PadelRule.swift +++ b/PadelClub/Utils/PadelRule.swift @@ -369,6 +369,15 @@ enum TournamentLevel: Int, Hashable, Codable, CaseIterable, Identifiable { var id: Int { self.rawValue } + func wildcardArePossible() -> Bool { + switch self { + case .p500, .p1000, .p1500, .p2000: + return true + default: + return false + } + } + func minimumPlayerRank(category: TournamentCategory, ageCategory: FederalTournamentAge) -> Int { switch self { case .p25: diff --git a/PadelClub/Views/Tournament/Screen/RegistrationSetupView.swift b/PadelClub/Views/Tournament/Screen/RegistrationSetupView.swift index 75f8134..7bd44eb 100644 --- a/PadelClub/Views/Tournament/Screen/RegistrationSetupView.swift +++ b/PadelClub/Views/Tournament/Screen/RegistrationSetupView.swift @@ -52,14 +52,9 @@ struct RegistrationSetupView: View { } // Target Team Count - if let targetTeamCount = tournament.targetTeamCount { - _targetTeamCount = .init(wrappedValue: targetTeamCount) - _targetTeamCountEnabled = .init(wrappedValue: true) - } else { - _targetTeamCount = .init(wrappedValue: tournament.teamCount) // Default value - _targetTeamCountEnabled = .init(wrappedValue: false) - } - + _targetTeamCount = .init(wrappedValue: tournament.teamCount) // Default value + _targetTeamCountEnabled = .init(wrappedValue: false) + // Waiting List Limit if let waitingListLimit = tournament.waitingListLimit { _waitingListLimit = .init(wrappedValue: waitingListLimit) @@ -289,9 +284,9 @@ struct RegistrationSetupView: View { } if targetTeamCountEnabled == false { - tournament.targetTeamCount = nil + tournament.teamCount = 24 } else { - tournament.targetTeamCount = targetTeamCount + tournament.teamCount = targetTeamCount } if waitingListLimitEnabled == false { diff --git a/PadelClub/Views/Tournament/Screen/TableStructureView.swift b/PadelClub/Views/Tournament/Screen/TableStructureView.swift index b89c2b5..93c95cc 100644 --- a/PadelClub/Views/Tournament/Screen/TableStructureView.swift +++ b/PadelClub/Views/Tournament/Screen/TableStructureView.swift @@ -77,6 +77,7 @@ struct TableStructureView: View { teamsPerGroupStage = structurePreset.teamsPerGroupStage() qualifiedPerGroupStage = structurePreset.qualifiedPerGroupStage() groupStageAdditionalQualified = 0 + buildWildcards = tournament.level.wildcardArePossible() } } @@ -223,7 +224,7 @@ struct TableStructureView: View { } } - if structurePreset.hasWildcards() { + if structurePreset.hasWildcards() && tournament.level.wildcardArePossible() { Section { Toggle("Avec wildcards", isOn: $buildWildcards) } footer: { From b4e267f695ce5c6185c3f4d36ca3c4bfb6151de7 Mon Sep 17 00:00:00 2001 From: Raz Date: Mon, 23 Dec 2024 15:06:34 +0100 Subject: [PATCH 17/57] fix online reg --- PadelClub/Utils/Tips.swift | 2 +- PadelClub/Views/Components/StepperView.swift | 31 ++++++++++++--- .../Views/Shared/ImportedPlayerView.swift | 3 ++ .../Views/Tournament/FileImportView.swift | 2 +- .../Screen/InscriptionManagerView.swift | 22 +++-------- .../Screen/RegistrationSetupView.swift | 8 +++- .../Screen/TableStructureView.swift | 39 +++++++++++++------ 7 files changed, 71 insertions(+), 36 deletions(-) diff --git a/PadelClub/Utils/Tips.swift b/PadelClub/Utils/Tips.swift index 6a49ad2..0faba01 100644 --- a/PadelClub/Utils/Tips.swift +++ b/PadelClub/Utils/Tips.swift @@ -600,7 +600,7 @@ struct ShouldTournamentBeOverTip: Tip { } var message: Text? { - Text("Le dernier match est terminé depuis plus de 2 heures. Si le tournoi a été annulé pour cause de météo vous pouvez l'indiquer comme 'Annulé' dans le menu 􀍡, si ce n'est pas le cas, saisissez les scores manquants pour clôturer automatiquement le tournoi et publier le classement final.") + Text("Le dernier match est terminé depuis plus de 2 heures. Si le tournoi a été annulé pour cause de météo vous pouvez l'indiquer comme 'Annulé' dans le menu en haut à droite, si ce n'est pas le cas, saisissez les scores manquants pour clôturer automatiquement le tournoi et publier le classement final.") } var image: Image? { diff --git a/PadelClub/Views/Components/StepperView.swift b/PadelClub/Views/Components/StepperView.swift index 23f3108..aadca52 100644 --- a/PadelClub/Views/Components/StepperView.swift +++ b/PadelClub/Views/Components/StepperView.swift @@ -16,6 +16,8 @@ struct StepperView: View { var maximum: Int? = nil var countChanged: (() -> ())? = nil + var submitFollowUpAction: (() -> ())? = nil + @FocusState private var amountIsFocused: Bool var body: some View { VStack { @@ -32,17 +34,14 @@ struct StepperView: View { .buttonStyle(.borderless) TextField("00", value: $count, format: .number) + .focused($amountIsFocused) .keyboardType(.numberPad) .fixedSize() // .font(.title2) .monospacedDigit() .multilineTextAlignment(.center) .onSubmit { - if let minimum, count < minimum { - count = minimum - } else if let maximum, count > maximum { - count = maximum - } + _validate() } Button(action: { self._add() @@ -60,6 +59,28 @@ struct StepperView: View { } } .multilineTextAlignment(.trailing) + .toolbar { + ToolbarItem(placement: .keyboard) { + if amountIsFocused { + HStack { + Spacer() + Button("Confirmer") { + amountIsFocused = false + _validate() + } + } + } + } + } + } + + fileprivate func _validate() { + if let minimum, count < minimum { + count = minimum + } else if let maximum, count > maximum { + count = maximum + } + submitFollowUpAction?() } fileprivate func _minusIsDisabled() -> Bool { diff --git a/PadelClub/Views/Shared/ImportedPlayerView.swift b/PadelClub/Views/Shared/ImportedPlayerView.swift index f81ae56..4843241 100644 --- a/PadelClub/Views/Shared/ImportedPlayerView.swift +++ b/PadelClub/Views/Shared/ImportedPlayerView.swift @@ -114,5 +114,8 @@ struct ImportedPlayerView: View { } } } + .contextMenu { + CopyPasteButtonView(pasteValue: player.formattedLicense()) + } } } diff --git a/PadelClub/Views/Tournament/FileImportView.swift b/PadelClub/Views/Tournament/FileImportView.swift index a25219b..500d7dc 100644 --- a/PadelClub/Views/Tournament/FileImportView.swift +++ b/PadelClub/Views/Tournament/FileImportView.swift @@ -140,7 +140,7 @@ struct FileImportView: View { } } - if tournament.unsortedTeams().count > 0 { + if tournament.unsortedTeams().count > 0, tournament.enableOnlineRegistration == false { RowButtonView("Effacer les équipes déjà inscrites", role: .destructive) { await _deleteTeams() } diff --git a/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift b/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift index 2a96c5b..76f3089 100644 --- a/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift +++ b/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift @@ -536,20 +536,10 @@ struct InscriptionManagerView: View { refreshStatus = nil refreshInProgress = true do { + try await self.tournamentStore.playerRegistrations.loadDataFromServerIfAllowed(clear: true) + try await self.tournamentStore.teamScores.loadDataFromServerIfAllowed(clear: true) + try await self.tournamentStore.teamRegistrations.loadDataFromServerIfAllowed(clear: true) - let storeIdentifier = StoreIdentifier(value: tournament.id, parameterName: "tournament") - let serverPlayers: [PlayerRegistration] = try await StoreCenter.main.service().get(identifier: storeIdentifier) - let serverTeamScores: [TeamScore] = try await StoreCenter.main.service().get(identifier: storeIdentifier) - let serverTeams: [TeamRegistration] = try await StoreCenter.main.service().get(identifier: storeIdentifier) - - self.tournamentStore.playerRegistrations.reset() - self.tournamentStore.teamScores.reset() - self.tournamentStore.teamRegistrations.reset() - - try self.tournamentStore.playerRegistrations.addOrUpdate(contentOfs: serverPlayers) - try self.tournamentStore.teamScores.addOrUpdate(contentOfs: serverTeamScores) - try self.tournamentStore.teamRegistrations.addOrUpdate(contentOfs: serverTeams) - _setHash() self.refreshResult = "la synchronization a réussi" @@ -613,11 +603,11 @@ struct InscriptionManagerView: View { } label: { TeamRowView(team: team) } - #if DEBUG .swipeActions(edge: .trailing, allowsFullSwipe: true) { - _teamDeleteButtonView(team) + if tournament.enableOnlineRegistration == false { + _teamDeleteButtonView(team) + } } - #endif .listRowView(isActive: true, color: team.initialRoundColor() ?? tournament.cutLabelColor(index: teamIndex, teamCount: filterMode == .waiting ? 0 : selectedSortedTeams.count), hideColorVariation: true) } } header: { diff --git a/PadelClub/Views/Tournament/Screen/RegistrationSetupView.swift b/PadelClub/Views/Tournament/Screen/RegistrationSetupView.swift index 7bd44eb..f279b67 100644 --- a/PadelClub/Views/Tournament/Screen/RegistrationSetupView.swift +++ b/PadelClub/Views/Tournament/Screen/RegistrationSetupView.swift @@ -84,8 +84,14 @@ struct RegistrationSetupView: View { if enableOnlineRegistration { if let shareURL = tournament.shareURL(.info) { Section { + Text(shareURL.absoluteString) + ShareLink(item: shareURL) { - Text(shareURL.absoluteString) + Text("Partager le lien") + } + + Link(destination: shareURL) { + Text("Ouvrir le lien") } } header: { Text("Page d'inscription") diff --git a/PadelClub/Views/Tournament/Screen/TableStructureView.swift b/PadelClub/Views/Tournament/Screen/TableStructureView.swift index 93c95cc..a72e616 100644 --- a/PadelClub/Views/Tournament/Screen/TableStructureView.swift +++ b/PadelClub/Views/Tournament/Screen/TableStructureView.swift @@ -83,12 +83,20 @@ struct TableStructureView: View { Section { LabeledContent { - StepperView(count: $teamCount, minimum: 4, maximum: 128) + StepperView(count: $teamCount, minimum: 4, maximum: 128) { + + } submitFollowUpAction: { + _verifyValueIntegrity() + } } label: { Text("Nombre d'équipes") } LabeledContent { - StepperView(count: $groupStageCount, minimum: 0, maximum: maxGroupStages) + StepperView(count: $groupStageCount, minimum: 0, maximum: maxGroupStages) { + + } submitFollowUpAction: { + _verifyValueIntegrity() + } } label: { Text("Nombre de poules") } @@ -100,21 +108,33 @@ struct TableStructureView: View { if (teamCount / groupStageCount) > 1 { Section { LabeledContent { - StepperView(count: $teamsPerGroupStage, minimum: 2, maximum: (teamCount / groupStageCount)) + StepperView(count: $teamsPerGroupStage, minimum: 2, maximum: (teamCount / groupStageCount)) { + + } submitFollowUpAction: { + _verifyValueIntegrity() + } } label: { Text("Équipes par poule") } if structurePreset != .doubleGroupStage { LabeledContent { - StepperView(count: $qualifiedPerGroupStage, minimum: 0, maximum: (teamsPerGroupStage-1)) + StepperView(count: $qualifiedPerGroupStage, minimum: 0, maximum: (teamsPerGroupStage-1)) { + + } submitFollowUpAction: { + _verifyValueIntegrity() + } } label: { Text("Qualifié\(qualifiedPerGroupStage.pluralSuffix) par poule") } if qualifiedPerGroupStage < teamsPerGroupStage - 1 { LabeledContent { - StepperView(count: $groupStageAdditionalQualified, minimum: 0, maximum: maxMoreQualified) + StepperView(count: $groupStageAdditionalQualified, minimum: 0, maximum: maxMoreQualified) { + + } submitFollowUpAction: { + _verifyValueIntegrity() + } } label: { Text("Qualifié\(groupStageAdditionalQualified.pluralSuffix) supplémentaires") Text(moreQualifiedLabel) @@ -311,14 +331,9 @@ struct TableStructureView: View { updatedElements.insert(.groupStageAdditionalQualified) } else { updatedElements.remove(.groupStageAdditionalQualified) - } } - .toolbar { - ToolbarItem(placement: .keyboard) { - Button("Confirmer") { - stepperFieldIsFocused = false - _verifyValueIntegrity() - } } + } + .toolbar { ToolbarItem(placement: .confirmationAction) { if tournament.state() == .initial { ButtonValidateView { From e5c4ea08377d39a1b3a1fdc42353e3da39276a43 Mon Sep 17 00:00:00 2001 From: Raz Date: Tue, 24 Dec 2024 08:54:38 +0100 Subject: [PATCH 18/57] fix stuff --- .../Views/Components/CopyPasteButtonView.swift | 4 ++++ .../Views/Shared/ImportedPlayerView.swift | 2 +- .../Tournament/Screen/BroadcastView.swift | 4 ++-- .../Screen/RegistrationSetupView.swift | 18 +++++++++--------- 4 files changed, 16 insertions(+), 12 deletions(-) diff --git a/PadelClub/Views/Components/CopyPasteButtonView.swift b/PadelClub/Views/Components/CopyPasteButtonView.swift index 0b5c976..b70b95b 100644 --- a/PadelClub/Views/Components/CopyPasteButtonView.swift +++ b/PadelClub/Views/Components/CopyPasteButtonView.swift @@ -8,6 +8,7 @@ import SwiftUI struct CopyPasteButtonView: View { + var title: String? let pasteValue: String? @State private var copied: Bool = false @@ -20,6 +21,9 @@ struct CopyPasteButtonView: View { copied = true } label: { Label(copied ? "Copié" : "Copier", systemImage: "doc.on.doc").symbolVariant(copied ? .fill : .none) + if let title { + Text(title) + } } } } diff --git a/PadelClub/Views/Shared/ImportedPlayerView.swift b/PadelClub/Views/Shared/ImportedPlayerView.swift index 4843241..67b659e 100644 --- a/PadelClub/Views/Shared/ImportedPlayerView.swift +++ b/PadelClub/Views/Shared/ImportedPlayerView.swift @@ -115,7 +115,7 @@ struct ImportedPlayerView: View { } } .contextMenu { - CopyPasteButtonView(pasteValue: player.formattedLicense()) + CopyPasteButtonView(title: "Licence", pasteValue: player.formattedLicense()) } } } diff --git a/PadelClub/Views/Tournament/Screen/BroadcastView.swift b/PadelClub/Views/Tournament/Screen/BroadcastView.swift index 4e12408..4dcc1ba 100644 --- a/PadelClub/Views/Tournament/Screen/BroadcastView.swift +++ b/PadelClub/Views/Tournament/Screen/BroadcastView.swift @@ -19,7 +19,7 @@ struct BroadcastView: View { let filter = CIFilter.qrCodeGenerator() @State private var urlToShow: String? @State private var tvMode: Bool = false - @State private var pageLink: PageLink = .matches + @State private var pageLink: PageLink = .info let createAccountTip = CreateAccountTip() let tournamentPublishingTip = TournamentPublishingTip() @@ -269,7 +269,7 @@ struct BroadcastView: View { ToolbarItem(placement: .topBarTrailing) { Menu { Section { - let links : [PageLink] = [.teams, .summons, .groupStages, .matches, .rankings, .broadcast, .clubBroadcast] + let links : [PageLink] = [.info, .teams, .summons, .groupStages, .matches, .rankings, .broadcast, .clubBroadcast] Picker(selection: $pageLink) { ForEach(links) { pageLink in Text(pageLink.localizedLabel()).tag(pageLink) diff --git a/PadelClub/Views/Tournament/Screen/RegistrationSetupView.swift b/PadelClub/Views/Tournament/Screen/RegistrationSetupView.swift index f279b67..efddaa1 100644 --- a/PadelClub/Views/Tournament/Screen/RegistrationSetupView.swift +++ b/PadelClub/Views/Tournament/Screen/RegistrationSetupView.swift @@ -75,7 +75,7 @@ struct RegistrationSetupView: View { List { Section { Toggle(isOn: $enableOnlineRegistration) { - Text("Inscription en ligne") + Text("Activer") } } footer: { Text("Les inscriptions en ligne permettent à des joueurs de s'inscrire à votre tournoi en passant par le site Padel Club. Vous verrez alors votre liste d'inscription s'agrandir dans la vue Gestion des Inscriptions de l'application.") @@ -84,19 +84,19 @@ struct RegistrationSetupView: View { if enableOnlineRegistration { if let shareURL = tournament.shareURL(.info) { Section { - Text(shareURL.absoluteString) - - ShareLink(item: shareURL) { - Text("Partager le lien") - } - Link(destination: shareURL) { - Text("Ouvrir le lien") + Text(shareURL.absoluteString) } } header: { Text("Page d'inscription") } footer: { - CopyPasteButtonView(pasteValue: shareURL.absoluteString) + HStack { + CopyPasteButtonView(pasteValue: shareURL.absoluteString) + Spacer() + ShareLink(item: shareURL) { + Label("Partager", systemImage: "square.and.arrow.up") + } + } } } From 1f21cb5b0524029d5d4b8f6c951def9426522bab Mon Sep 17 00:00:00 2001 From: Raz Date: Tue, 24 Dec 2024 08:55:04 +0100 Subject: [PATCH 19/57] remove the workaround on playlist --- .../Shared/SelectablePlayerListView.swift | 496 +----------------- 1 file changed, 10 insertions(+), 486 deletions(-) diff --git a/PadelClub/Views/Shared/SelectablePlayerListView.swift b/PadelClub/Views/Shared/SelectablePlayerListView.swift index 352251d..c5e7f4e 100644 --- a/PadelClub/Views/Shared/SelectablePlayerListView.swift +++ b/PadelClub/Views/Shared/SelectablePlayerListView.swift @@ -489,103 +489,7 @@ struct MySearchView: View { } else { Section { ForEach(players, id: \.self) { player in - let index : Int? = nil - - VStack(alignment: .leading) { - HStack { - if player.isAnonymous() { - Text("Joueur Anonyme") - } else { - Text(player.getLastName().capitalized) - Text(player.getFirstName().capitalized) - } - if index == nil { - Text(player.male ? "♂︎" : "♀︎") - } - Spacer() - if let index { - HStack(alignment: .top, spacing: 0) { - Text(index.formatted()) - .foregroundStyle(.secondary) - .font(.title3) - Text(index.ordinalFormattedSuffix()) - .foregroundStyle(.secondary) - .font(.caption) - } - } - } - .font(.title3) - .lineLimit(1) - HStack { - HStack(alignment: .top, spacing: 0) { - Text(player.formattedRank()).italic(player.isAssimilated) - .font(.title3) - .background { - if player.isNotFromCurrentDate() { - UnderlineView() - } - } - if let rank = player.getRank() { - Text(rank.ordinalFormattedSuffix()).italic(player.isAssimilated) - .font(.caption) - } - } - - if showProgression, player.getProgression() != 0 { - HStack(alignment: .top, spacing: 2) { - Text("(") - Text(player.getProgression().formatted(.number.sign(strategy: .always()))) - .foregroundStyle(player.getProgressionColor(progression: player.getProgression())) - Text(")") - }.font(.title3) - } - - if let pts = player.getPoints(), pts > 0 { - HStack(alignment: .lastTextBaseline, spacing: 0) { - Text(pts.formatted()).font(.title3) - Text(" pts").font(.caption) - } - } - - if let tournamentPlayed = player.tournamentPlayed, tournamentPlayed > 0 { - HStack(alignment: .lastTextBaseline, spacing: 0) { - Text(tournamentPlayed.formatted()).font(.title3) - Text(" tournoi" + tournamentPlayed.pluralSuffix).font(.caption) - } - } - } - .lineLimit(1) - .truncationMode(.tail) - - if showFemaleInMaleAssimilation, let assimilatedAsMaleRank = player.getAssimilatedAsMaleRank() { - HStack(alignment: .top, spacing: 2) { - Text("(") - Text(assimilatedAsMaleRank.formatted()) - VStack(alignment: .leading, spacing: 0) { - Text("équivalence") - Text("messieurs") - } - .font(.caption) - Text(")").font(.title3) - } - } - - HStack { - Text(player.formattedLicense()) - if let computedAge = player.computedAge { - Text(computedAge.formatted() + " ans") - } - } - .font(.caption) - if let clubName = player.clubName { - Text(clubName) - .font(.caption) - } - if let ligueName = player.ligueName { - Text(ligueName) - .font(.caption) - } - } + ImportedPlayerView(player: player, index: nil, showFemaleInMaleAssimilation: searchViewModel.showFemaleInMaleAssimilation, showProgression: true) } } header: { if players.isEmpty == false { @@ -601,106 +505,10 @@ struct MySearchView: View { if searchViewModel.allowSingleSelection { Section { ForEach(players) { player in - let index : Int? = nil - Button { searchViewModel.selectedPlayers.insert(player) } label: { - VStack(alignment: .leading) { - HStack { - if player.isAnonymous() { - Text("Joueur Anonyme") - } else { - Text(player.getLastName().capitalized) - Text(player.getFirstName().capitalized) - } - if index == nil { - Text(player.male ? "♂︎" : "♀︎") - } - Spacer() - if let index { - HStack(alignment: .top, spacing: 0) { - Text(index.formatted()) - .foregroundStyle(.secondary) - .font(.title3) - Text(index.ordinalFormattedSuffix()) - .foregroundStyle(.secondary) - .font(.caption) - } - } - } - .font(.title3) - .lineLimit(1) - HStack { - HStack(alignment: .top, spacing: 0) { - Text(player.formattedRank()).italic(player.isAssimilated) - .font(.title3) - .background { - if player.isNotFromCurrentDate() { - UnderlineView() - } - } - if let rank = player.getRank() { - Text(rank.ordinalFormattedSuffix()).italic(player.isAssimilated) - .font(.caption) - } - } - - if showProgression, player.getProgression() != 0 { - HStack(alignment: .top, spacing: 2) { - Text("(") - Text(player.getProgression().formatted(.number.sign(strategy: .always()))) - .foregroundStyle(player.getProgressionColor(progression: player.getProgression())) - Text(")") - }.font(.title3) - } - - if let pts = player.getPoints(), pts > 0 { - HStack(alignment: .lastTextBaseline, spacing: 0) { - Text(pts.formatted()).font(.title3) - Text(" pts").font(.caption) - } - } - - if let tournamentPlayed = player.tournamentPlayed, tournamentPlayed > 0 { - HStack(alignment: .lastTextBaseline, spacing: 0) { - Text(tournamentPlayed.formatted()).font(.title3) - Text(" tournoi" + tournamentPlayed.pluralSuffix).font(.caption) - } - } - } - .lineLimit(1) - .truncationMode(.tail) - - if showFemaleInMaleAssimilation, let assimilatedAsMaleRank = player.getAssimilatedAsMaleRank() { - HStack(alignment: .top, spacing: 2) { - Text("(") - Text(assimilatedAsMaleRank.formatted()) - VStack(alignment: .leading, spacing: 0) { - Text("équivalence") - Text("messieurs") - } - .font(.caption) - Text(")").font(.title3) - } - } - - HStack { - Text(player.formattedLicense()) - if let computedAge = player.computedAge { - Text(computedAge.formatted() + " ans") - } - } - .font(.caption) - if let clubName = player.clubName { - Text(clubName) - .font(.caption) - } - if let ligueName = player.ligueName { - Text(ligueName) - .font(.caption) - } - } + ImportedPlayerView(player: player, showFemaleInMaleAssimilation: searchViewModel.showFemaleInMaleAssimilation, showProgression: true) } .buttonStyle(.plain) } @@ -712,105 +520,9 @@ struct MySearchView: View { .id(UUID()) } else { Section { - ForEach(players.indices, id: \.self) { playerIndex in - let player = players[playerIndex] - let index: Int? = searchViewModel.showIndex() ? (playerIndex + 1) : nil - - VStack(alignment: .leading) { - HStack { - if player.isAnonymous() { - Text("Joueur Anonyme") - } else { - Text(player.getLastName().capitalized) - Text(player.getFirstName().capitalized) - } - if index == nil { - Text(player.male ? "♂︎" : "♀︎") - } - Spacer() - if let index { - HStack(alignment: .top, spacing: 0) { - Text(index.formatted()) - .foregroundStyle(.secondary) - .font(.title3) - Text(index.ordinalFormattedSuffix()) - .foregroundStyle(.secondary) - .font(.caption) - } - } - } - .font(.title3) - .lineLimit(1) - HStack { - HStack(alignment: .top, spacing: 0) { - Text(player.formattedRank()).italic(player.isAssimilated) - .font(.title3) - .background { - if player.isNotFromCurrentDate() { - UnderlineView() - } - } - if let rank = player.getRank() { - Text(rank.ordinalFormattedSuffix()).italic(player.isAssimilated) - .font(.caption) - } - } - - if showProgression, player.getProgression() != 0 { - HStack(alignment: .top, spacing: 2) { - Text("(") - Text(player.getProgression().formatted(.number.sign(strategy: .always()))) - .foregroundStyle(player.getProgressionColor(progression: player.getProgression())) - Text(")") - }.font(.title3) - } - - if let pts = player.getPoints(), pts > 0 { - HStack(alignment: .lastTextBaseline, spacing: 0) { - Text(pts.formatted()).font(.title3) - Text(" pts").font(.caption) - } - } - - if let tournamentPlayed = player.tournamentPlayed, tournamentPlayed > 0 { - HStack(alignment: .lastTextBaseline, spacing: 0) { - Text(tournamentPlayed.formatted()).font(.title3) - Text(" tournoi" + tournamentPlayed.pluralSuffix).font(.caption) - } - } - } - .lineLimit(1) - .truncationMode(.tail) - - if showFemaleInMaleAssimilation, let assimilatedAsMaleRank = player.getAssimilatedAsMaleRank() { - HStack(alignment: .top, spacing: 2) { - Text("(") - Text(assimilatedAsMaleRank.formatted()) - VStack(alignment: .leading, spacing: 0) { - Text("équivalence") - Text("messieurs") - } - .font(.caption) - Text(")").font(.title3) - } - } - - HStack { - Text(player.formattedLicense()) - if let computedAge = player.computedAge { - Text(computedAge.formatted() + " ans") - } - } - .font(.caption) - if let clubName = player.clubName { - Text(clubName) - .font(.caption) - } - if let ligueName = player.ligueName { - Text(ligueName) - .font(.caption) - } - } + ForEach(players.indices, id: \.self) { index in + let player = players[index] + ImportedPlayerView(player: player, index: searchViewModel.showIndex() ? (index + 1) : nil, showFemaleInMaleAssimilation: searchViewModel.showFemaleInMaleAssimilation, showProgression: true) } } header: { if players.isEmpty == false { @@ -821,207 +533,19 @@ struct MySearchView: View { } } else { Section { - ForEach(players.indices, id: \.self) { playerIndex in - let player = players[playerIndex] - let index: Int? = searchViewModel.showIndex() ? (playerIndex + 1) : nil + ForEach(players.indices, id: \.self) { index in + let player = players[index] if searchViewModel.allowSingleSelection { Button { searchViewModel.selectedPlayers.insert(player) } label: { - VStack(alignment: .leading) { - HStack { - if player.isAnonymous() { - Text("Joueur Anonyme") - } else { - Text(player.getLastName().capitalized) - Text(player.getFirstName().capitalized) - } - if index == nil { - Text(player.male ? "♂︎" : "♀︎") - } - Spacer() - if let index { - HStack(alignment: .top, spacing: 0) { - Text(index.formatted()) - .foregroundStyle(.secondary) - .font(.title3) - Text(index.ordinalFormattedSuffix()) - .foregroundStyle(.secondary) - .font(.caption) - } - } - } - .font(.title3) - .lineLimit(1) - HStack { - HStack(alignment: .top, spacing: 0) { - Text(player.formattedRank()).italic(player.isAssimilated) - .font(.title3) - .background { - if player.isNotFromCurrentDate() { - UnderlineView() - } - } - if let rank = player.getRank() { - Text(rank.ordinalFormattedSuffix()).italic(player.isAssimilated) - .font(.caption) - } - } - - if showProgression, player.getProgression() != 0 { - HStack(alignment: .top, spacing: 2) { - Text("(") - Text(player.getProgression().formatted(.number.sign(strategy: .always()))) - .foregroundStyle(player.getProgressionColor(progression: player.getProgression())) - Text(")") - }.font(.title3) - } - - if let pts = player.getPoints(), pts > 0 { - HStack(alignment: .lastTextBaseline, spacing: 0) { - Text(pts.formatted()).font(.title3) - Text(" pts").font(.caption) - } - } - - if let tournamentPlayed = player.tournamentPlayed, tournamentPlayed > 0 { - HStack(alignment: .lastTextBaseline, spacing: 0) { - Text(tournamentPlayed.formatted()).font(.title3) - Text(" tournoi" + tournamentPlayed.pluralSuffix).font(.caption) - } - } - } - .lineLimit(1) - .truncationMode(.tail) - - if showFemaleInMaleAssimilation, let assimilatedAsMaleRank = player.getAssimilatedAsMaleRank() { - HStack(alignment: .top, spacing: 2) { - Text("(") - Text(assimilatedAsMaleRank.formatted()) - VStack(alignment: .leading, spacing: 0) { - Text("équivalence") - Text("messieurs") - } - .font(.caption) - Text(")").font(.title3) - } - } - - HStack { - Text(player.formattedLicense()) - if let computedAge = player.computedAge { - Text(computedAge.formatted() + " ans") - } - } - .font(.caption) - if let clubName = player.clubName { - Text(clubName) - .font(.caption) - } - if let ligueName = player.ligueName { - Text(ligueName) - .font(.caption) - } - } + ImportedPlayerView(player: player, index: searchViewModel.showIndex() ? (index + 1) : nil, showFemaleInMaleAssimilation: searchViewModel.showFemaleInMaleAssimilation, showProgression: true) + .contentShape(Rectangle()) } .frame(maxWidth: .infinity) .buttonStyle(.plain) } else { - VStack(alignment: .leading) { - HStack { - if player.isAnonymous() { - Text("Joueur Anonyme") - } else { - Text(player.getLastName().capitalized) - Text(player.getFirstName().capitalized) - } - if index == nil { - Text(player.male ? "♂︎" : "♀︎") - } - Spacer() - if let index { - HStack(alignment: .top, spacing: 0) { - Text(index.formatted()) - .foregroundStyle(.secondary) - .font(.title3) - Text(index.ordinalFormattedSuffix()) - .foregroundStyle(.secondary) - .font(.caption) - } - } - } - .font(.title3) - .lineLimit(1) - HStack { - HStack(alignment: .top, spacing: 0) { - Text(player.formattedRank()).italic(player.isAssimilated) - .font(.title3) - .background { - if player.isNotFromCurrentDate() { - UnderlineView() - } - } - if let rank = player.getRank() { - Text(rank.ordinalFormattedSuffix()).italic(player.isAssimilated) - .font(.caption) - } - } - - if showProgression, player.getProgression() != 0 { - HStack(alignment: .top, spacing: 2) { - Text("(") - Text(player.getProgression().formatted(.number.sign(strategy: .always()))) - .foregroundStyle(player.getProgressionColor(progression: player.getProgression())) - Text(")") - }.font(.title3) - } - - if let pts = player.getPoints(), pts > 0 { - HStack(alignment: .lastTextBaseline, spacing: 0) { - Text(pts.formatted()).font(.title3) - Text(" pts").font(.caption) - } - } - - if let tournamentPlayed = player.tournamentPlayed, tournamentPlayed > 0 { - HStack(alignment: .lastTextBaseline, spacing: 0) { - Text(tournamentPlayed.formatted()).font(.title3) - Text(" tournoi" + tournamentPlayed.pluralSuffix).font(.caption) - } - } - } - .lineLimit(1) - .truncationMode(.tail) - - if showFemaleInMaleAssimilation, let assimilatedAsMaleRank = player.getAssimilatedAsMaleRank() { - HStack(alignment: .top, spacing: 2) { - Text("(") - Text(assimilatedAsMaleRank.formatted()) - VStack(alignment: .leading, spacing: 0) { - Text("équivalence") - Text("messieurs") - } - .font(.caption) - Text(")").font(.title3) - } - } - - HStack { - Text(player.formattedLicense()) - if let computedAge = player.computedAge { - Text(computedAge.formatted() + " ans") - } - } - .font(.caption) - if let clubName = player.clubName { - Text(clubName) - .font(.caption) - } - if let ligueName = player.ligueName { - Text(ligueName) - .font(.caption) - } - } + ImportedPlayerView(player: player, index: searchViewModel.showIndex() ? (index + 1) : nil, showFemaleInMaleAssimilation: searchViewModel.showFemaleInMaleAssimilation, showProgression: true) } } } header: { From 9e2674e210e683eedd15e405a9781569ff608024 Mon Sep 17 00:00:00 2001 From: Raz Date: Wed, 8 Jan 2025 09:53:13 +0100 Subject: [PATCH 20/57] fix issue with locationbutton --- PadelClub.xcodeproj/project.pbxproj | 24 +++++++++--- PadelClub/Data/PlayerRegistration.swift | 25 ++++++++++++- PadelClub/Data/Tournament.swift | 13 ++++++- .../Agenda/TournamentLookUpView.swift | 30 +++++++++++++-- .../Components/UpdateSourceRankDateView.swift | 37 +++++++++---------- 5 files changed, 98 insertions(+), 31 deletions(-) diff --git a/PadelClub.xcodeproj/project.pbxproj b/PadelClub.xcodeproj/project.pbxproj index b6a7ba5..40c031f 100644 --- a/PadelClub.xcodeproj/project.pbxproj +++ b/PadelClub.xcodeproj/project.pbxproj @@ -3262,7 +3262,7 @@ CODE_SIGN_ENTITLEMENTS = PadelClub/PadelClub.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\""; @@ -3275,7 +3275,10 @@ INFOPLIST_KEY_LSSupportsOpeningDocumentsInPlace = YES; INFOPLIST_KEY_NSCalendarsUsageDescription = "Padel Club a besoin d'avoir accès à votre calendrier pour pouvoir y inscrire ce tournoi"; INFOPLIST_KEY_NSCameraUsageDescription = "En autorisant l'application à utiliser la caméra, vous pourrez prendre des photos des rencontres"; - INFOPLIST_KEY_NSLocationWhenInUseUsageDescription = "En utilisant votre position, Padel Club peut trouver plus rapidement les clubs et les tournois autour de vous."; + INFOPLIST_KEY_NSLocationAlwaysAndWhenInUseUsageDescription = "Padel Club utilise votre position simplement pour faciliter la recherche de tournois et de clubs autour de vous."; + INFOPLIST_KEY_NSLocationAlwaysUsageDescription = "Padel Club utilise votre position simplement pour faciliter la recherche de tournois et de clubs autour de vous."; + INFOPLIST_KEY_NSLocationUsageDescription = "Padel Club utilise votre position simplement pour faciliter la recherche de tournois et de clubs autour de vous."; + INFOPLIST_KEY_NSLocationWhenInUseUsageDescription = "Padel Club utilise votre position simplement pour faciliter la recherche de tournois et de clubs autour de vous."; INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchStoryboardName = "Launch Screen"; @@ -3307,7 +3310,7 @@ CODE_SIGN_ENTITLEMENTS = PadelClub/PadelClub.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEFINES_MODULE = YES; DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\""; DEVELOPMENT_TEAM = BQ3Y44M3Q6; @@ -3319,7 +3322,10 @@ INFOPLIST_KEY_LSSupportsOpeningDocumentsInPlace = YES; INFOPLIST_KEY_NSCalendarsUsageDescription = "Padel Club a besoin d'avoir accès à votre calendrier pour pouvoir y inscrire ce tournoi"; INFOPLIST_KEY_NSCameraUsageDescription = "En autorisant l'application à utiliser la caméra, vous pourrez prendre des photos des rencontres"; - INFOPLIST_KEY_NSLocationWhenInUseUsageDescription = "En utilisant votre position, Padel Club peut trouver plus rapidement les clubs et les tournois autour de vous."; + INFOPLIST_KEY_NSLocationAlwaysAndWhenInUseUsageDescription = "Padel Club utilise votre position simplement pour faciliter la recherche de tournois et de clubs autour de vous."; + INFOPLIST_KEY_NSLocationAlwaysUsageDescription = "Padel Club utilise votre position simplement pour faciliter la recherche de tournois et de clubs autour de vous."; + INFOPLIST_KEY_NSLocationUsageDescription = "Padel Club utilise votre position simplement pour faciliter la recherche de tournois et de clubs autour de vous."; + INFOPLIST_KEY_NSLocationWhenInUseUsageDescription = "Padel Club utilise votre position simplement pour faciliter la recherche de tournois et de clubs autour de vous."; INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchStoryboardName = "Launch Screen"; @@ -3436,7 +3442,10 @@ INFOPLIST_KEY_LSSupportsOpeningDocumentsInPlace = YES; INFOPLIST_KEY_NSCalendarsUsageDescription = "Padel Club a besoin d'avoir accès à votre calendrier pour pouvoir y inscrire ce tournoi"; INFOPLIST_KEY_NSCameraUsageDescription = "En autorisant l'application à utiliser la caméra, vous pourrez prendre des photos des rencontres"; - INFOPLIST_KEY_NSLocationWhenInUseUsageDescription = "Padel Club a besoin de votre position pour rechercher les clubs autour de vous."; + INFOPLIST_KEY_NSLocationAlwaysAndWhenInUseUsageDescription = "Padel Club utilise votre position simplement pour faciliter la recherche de tournois et de clubs autour de vous."; + INFOPLIST_KEY_NSLocationAlwaysUsageDescription = "Padel Club utilise votre position simplement pour faciliter la recherche de tournois et de clubs autour de vous."; + INFOPLIST_KEY_NSLocationUsageDescription = "Padel Club utilise votre position simplement pour faciliter la recherche de tournois et de clubs autour de vous."; + INFOPLIST_KEY_NSLocationWhenInUseUsageDescription = "Padel Club utilise votre position simplement pour faciliter la recherche de tournois et de clubs autour de vous."; INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchStoryboardName = "Launch Screen"; @@ -3480,7 +3489,10 @@ INFOPLIST_KEY_LSSupportsOpeningDocumentsInPlace = YES; INFOPLIST_KEY_NSCalendarsUsageDescription = "Padel Club a besoin d'avoir accès à votre calendrier pour pouvoir y inscrire ce tournoi"; INFOPLIST_KEY_NSCameraUsageDescription = "En autorisant l'application à utiliser la caméra, vous pourrez prendre des photos des rencontres"; - INFOPLIST_KEY_NSLocationWhenInUseUsageDescription = "Padel Club a besoin de votre position pour rechercher les clubs autour de vous."; + INFOPLIST_KEY_NSLocationAlwaysAndWhenInUseUsageDescription = "Padel Club utilise votre position simplement pour faciliter la recherche de tournois et de clubs autour de vous."; + INFOPLIST_KEY_NSLocationAlwaysUsageDescription = "Padel Club utilise votre position simplement pour faciliter la recherche de tournois et de clubs autour de vous."; + INFOPLIST_KEY_NSLocationUsageDescription = "Padel Club utilise votre position simplement pour faciliter la recherche de tournois et de clubs autour de vous."; + INFOPLIST_KEY_NSLocationWhenInUseUsageDescription = "Padel Club utilise votre position simplement pour faciliter la recherche de tournois et de clubs autour de vous."; INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchStoryboardName = "Launch Screen"; diff --git a/PadelClub/Data/PlayerRegistration.swift b/PadelClub/Data/PlayerRegistration.swift index 4dbc043..e3dd433 100644 --- a/PadelClub/Data/PlayerRegistration.swift +++ b/PadelClub/Data/PlayerRegistration.swift @@ -239,8 +239,15 @@ final class PlayerRegistration: ModelObject, Storable { } } - @MainActor func updateRank(from sources: [CSVParser], lastRank: Int) async throws { +#if DEBUG_TIME //DEBUGING TIME +let start = Date() +defer { + let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000) + print("func updateRank()", id, duration.formatted(.units(allowed: [.seconds, .milliseconds]))) +} +#endif + if let dataFound = try await history(from: sources) { rank = dataFound.rankValue?.toInt() points = dataFound.points @@ -251,6 +258,14 @@ final class PlayerRegistration: ModelObject, Storable { } func history(from sources: [CSVParser]) async throws -> Line? { +#if DEBUG_TIME //DEBUGING TIME +let start = Date() +defer { + let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000) + print("func history()", id, duration.formatted(.units(allowed: [.seconds, .milliseconds]))) +} +#endif + guard let license = licenceId?.strippedLicense else { return try await historyFromName(from: sources) } @@ -276,6 +291,14 @@ final class PlayerRegistration: ModelObject, Storable { } func historyFromName(from sources: [CSVParser]) async throws -> Line? { +#if DEBUG_TIME //DEBUGING TIME +let start = Date() +defer { + let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000) + print("func historyFromName()", id, duration.formatted(.units(allowed: [.seconds, .milliseconds]))) +} +#endif + return await withTaskGroup(of: Line?.self) { group in for source in sources.filter({ $0.maleData == isMalePlayer() }) { group.addTask { [lastName, firstName] in diff --git a/PadelClub/Data/Tournament.swift b/PadelClub/Data/Tournament.swift index 189c987..5f6ee07 100644 --- a/PadelClub/Data/Tournament.swift +++ b/PadelClub/Data/Tournament.swift @@ -1449,6 +1449,15 @@ defer { } func updateRank(to newDate: Date?) async throws { + +#if DEBUG_TIME //DEBUGING TIME +let start = Date() +defer { + let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000) + print("func updateRank()", id, duration.formatted(.units(allowed: [.seconds, .milliseconds]))) +} +#endif + guard let newDate else { return } rankSourceDate = newDate @@ -1472,8 +1481,8 @@ defer { let lastRankWoman = currentMonthData()?.femaleUnrankedValue let dataURLs = SourceFileManager.shared.allFiles.filter { $0.dateFromPath == newDate } let sources = dataURLs.map { CSVParser(url: $0) } - - try await unsortedPlayers().concurrentForEach { player in + let players = unsortedPlayers() + try await players.concurrentForEach { player in try await player.updateRank(from: sources, lastRank: (player.sex == .female ? lastRankWoman : lastRankMan) ?? 0) } } diff --git a/PadelClub/Views/Navigation/Agenda/TournamentLookUpView.swift b/PadelClub/Views/Navigation/Agenda/TournamentLookUpView.swift index 783359c..f7228f6 100644 --- a/PadelClub/Views/Navigation/Agenda/TournamentLookUpView.swift +++ b/PadelClub/Views/Navigation/Agenda/TournamentLookUpView.swift @@ -18,7 +18,7 @@ struct TournamentLookUpView: View { @State private var searchField: String = "" @State var page: Int = 0 @State var total: Int = 0 - + @State private var showingSettingsAlert = false @State private var searching: Bool = false @State private var requestedToGetAllPages: Bool = false @State private var revealSearchParameters: Bool = true @@ -57,6 +57,16 @@ struct TournamentLookUpView: View { } message: { Text("Aucune ville n'a été indiqué, il est préférable de se localiser ou d'indiquer une ville pour réduire le nombre de résultat.") } + .alert(isPresented: $showingSettingsAlert) { + Alert( + title: Text("Réglages"), + message: Text("Pour trouver les clubs autour de vous, vous devez l'autorisation à Padel Club de récupérer votre position."), + primaryButton: .default(Text("Ouvrir les réglages"), action: { + _openSettings() + }), + secondaryButton: .cancel() + ) + } .alert("Attention", isPresented: $presentAlert, actions: { Button { presentAlert = false @@ -305,9 +315,15 @@ struct TournamentLookUpView: View { } if locationManager.requestStarted { ProgressView() - } else { + } else if locationManager.manager.authorizationStatus != .restricted { LocationButton { - locationManager.requestLocation() + if locationManager.manager.authorizationStatus == .notDetermined { + locationManager.manager.requestWhenInUseAuthorization() + } else if locationManager.manager.authorizationStatus == .denied { + showingSettingsAlert = true + } else { + locationManager.requestLocation() + } } .symbolVariant(.fill) .foregroundColor (Color.white) @@ -485,4 +501,12 @@ struct TournamentLookUpView: View { return "Distance" } } + + private func _openSettings() { + guard let settingsURL = URL(string: UIApplication.openSettingsURLString) else { + return + } + UIApplication.shared.open(settingsURL) + } + } diff --git a/PadelClub/Views/Tournament/Screen/Components/UpdateSourceRankDateView.swift b/PadelClub/Views/Tournament/Screen/Components/UpdateSourceRankDateView.swift index 07f95d5..ceed51f 100644 --- a/PadelClub/Views/Tournament/Screen/Components/UpdateSourceRankDateView.swift +++ b/PadelClub/Views/Tournament/Screen/Components/UpdateSourceRankDateView.swift @@ -43,30 +43,29 @@ struct UpdateSourceRankDateView: View { Task { do { try await tournament.updateRank(to: currentRankSourceDate) - try await MainActor.run { - tournament.unsortedPlayers().forEach { player in - player.setComputedRank(in: tournament) - } - - try tournamentStore.playerRegistrations.addOrUpdate(contentOfs: tournament.unsortedPlayers()) - - tournament.unsortedTeams().forEach { team in - team.setWeight(from: team.players(), inTournamentCategory: tournament.tournamentCategory) - if forceRefreshLockWeight { - team.lockedWeight = team.weight - } + let unsortedPlayers = tournament.unsortedPlayers() + tournament.unsortedPlayers().forEach { player in + player.setComputedRank(in: tournament) + } + + try tournamentStore.playerRegistrations.addOrUpdate(contentOfs: unsortedPlayers) + + let unsortedTeams = tournament.unsortedTeams() + unsortedTeams.forEach { team in + team.setWeight(from: team.players(), inTournamentCategory: tournament.tournamentCategory) + if forceRefreshLockWeight { + team.lockedWeight = team.weight } - - try tournamentStore.teamRegistrations.addOrUpdate(contentOfs: tournament.unsortedTeams()) - - try dataStore.tournaments.addOrUpdate(instance: tournament) - - updatingRank = false - confirmUpdateRank = false } + + try tournamentStore.teamRegistrations.addOrUpdate(contentOfs: unsortedTeams) + + try dataStore.tournaments.addOrUpdate(instance: tournament) } catch { Logger.error(error) } + updatingRank = false + confirmUpdateRank = false } }.disabled(updatingRank) From 96bbe1fc3231647f412e7550d8c2fb97219b4741 Mon Sep 17 00:00:00 2001 From: Raz Date: Thu, 9 Jan 2025 09:39:02 +0100 Subject: [PATCH 21/57] fix fft jan 2025 --- PadelClub/Data/Tournament.swift | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/PadelClub/Data/Tournament.swift b/PadelClub/Data/Tournament.swift index 5f6ee07..98db9d1 100644 --- a/PadelClub/Data/Tournament.swift +++ b/PadelClub/Data/Tournament.swift @@ -2638,7 +2638,11 @@ extension Tournament { func deadline(for type: TournamentDeadlineType) -> Date? { guard [.p500, .p1000, .p1500, .p2000].contains(tournamentLevel) else { return nil } - if let date = Calendar.current.date(byAdding: .day, value: type.daysOffset, to: startDate) { + var daysOffset = type.daysOffset + if tournamentLevel == .p500 { + daysOffset += 7 + } + if let date = Calendar.current.date(byAdding: .day, value: daysOffset, to: startDate) { let startOfDay = Calendar.current.startOfDay(for: date) return Calendar.current.date(byAdding: type.timeOffset, to: startOfDay) } From 56852e75e71b50bedc883e4f2abecc91d5335088 Mon Sep 17 00:00:00 2001 From: Raz Date: Thu, 9 Jan 2025 11:01:32 +0100 Subject: [PATCH 22/57] merge --- PadelClub/Views/Player/PlayerDetailView.swift | 8 -------- 1 file changed, 8 deletions(-) diff --git a/PadelClub/Views/Player/PlayerDetailView.swift b/PadelClub/Views/Player/PlayerDetailView.swift index 9f68654..8a1454e 100644 --- a/PadelClub/Views/Player/PlayerDetailView.swift +++ b/PadelClub/Views/Player/PlayerDetailView.swift @@ -214,15 +214,7 @@ struct PlayerDetailView: View { } } } - .toolbar { - ToolbarItem(placement: .topBarTrailing) { - ShareLink(item: player.pasteData()) { - Label("Partager", systemImage: "square.and.arrow.up") - } - } - } .onChange(of: [player.hasArrived, player.captain, player.coach]) { - .onChange(of: player.hasArrived) { _save() } .onChange(of: player.sex) { From e4825b88e6ce7d53f755751fd8b3ec80021a3b46 Mon Sep 17 00:00:00 2001 From: Raz Date: Thu, 9 Jan 2025 11:02:58 +0100 Subject: [PATCH 23/57] 1.0.43 --- PadelClub.xcodeproj/project.pbxproj | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/PadelClub.xcodeproj/project.pbxproj b/PadelClub.xcodeproj/project.pbxproj index 40c031f..e8683e2 100644 --- a/PadelClub.xcodeproj/project.pbxproj +++ b/PadelClub.xcodeproj/project.pbxproj @@ -3262,7 +3262,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\""; @@ -3289,7 +3289,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.42; + MARKETING_VERSION = 1.0.43; PRODUCT_BUNDLE_IDENTIFIER = app.padelclub; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -3310,7 +3310,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; @@ -3336,7 +3336,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.42; + MARKETING_VERSION = 1.0.43; PRODUCT_BUNDLE_IDENTIFIER = app.padelclub; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; From 9a674de3cf3bc1778d55720a4a5d55899e11e2cb Mon Sep 17 00:00:00 2001 From: Raz Date: Thu, 9 Jan 2025 13:57:13 +0100 Subject: [PATCH 24/57] fix new urls --- PadelClub.xcodeproj/project.pbxproj | 4 ++-- PadelClub/Utils/URLs.swift | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/PadelClub.xcodeproj/project.pbxproj b/PadelClub.xcodeproj/project.pbxproj index e8683e2..1b89d98 100644 --- a/PadelClub.xcodeproj/project.pbxproj +++ b/PadelClub.xcodeproj/project.pbxproj @@ -3262,7 +3262,7 @@ CODE_SIGN_ENTITLEMENTS = PadelClub/PadelClub.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\""; @@ -3310,7 +3310,7 @@ CODE_SIGN_ENTITLEMENTS = PadelClub/PadelClub.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEFINES_MODULE = YES; DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\""; DEVELOPMENT_TEAM = BQ3Y44M3Q6; diff --git a/PadelClub/Utils/URLs.swift b/PadelClub/Utils/URLs.swift index 20e1013..63f4489 100644 --- a/PadelClub/Utils/URLs.swift +++ b/PadelClub/Utils/URLs.swift @@ -31,8 +31,8 @@ enum URLs: String, Identifiable { case beachPadel = "https://beach-padel.app.fft.fr/beachja/index/" //case padelClub = "https://padelclub.app" case tenup = "https://tenup.fft.fr" - case padelCompetitionGeneralGuide = "https://fft-site.cdn.prismic.io/fft-site/Zqi2PB5LeNNTxlrS_1-REGLESGENERALESDELACOMPETITION-ANNEESPORTIVE2025.pdf" - case padelCompetitionSpecificGuide = "https://fft-site.cdn.prismic.io/fft-site/Zqi4ax5LeNNTxlsu_3-CAHIERDESCHARGESDESTOURNOIS-ANNEESPORTIVE2025.pdf" + case padelCompetitionGeneralGuide = "https://fft-site.cdn.prismic.io/fft-site/Z2mH0ZbqstJ98yso_CHAPITREIRèglesgénérales.pdf" + case padelCompetitionSpecificGuide = "https://fft-site.cdn.prismic.io/fft-site/Z2mHz5bqstJ98ysm_CHAPITREIIICahierdeschargesdestournois.pdf" case padelRules = "https://xlr.alwaysdata.net/static/rules/padel-rules-2024.pdf" case restingDischarge = "https://club.fft.fr/tennisfirmidecazeville/60120370_d/data_1/pdf/fo/formlairededechargederesponsabilitetournoidepadel.pdf" case appReview = "https://apps.apple.com/app/padel-club/id6484163558?mt=8&action=write-review" From 51fbc26c12904d368fcc27698b4058721fdc904c Mon Sep 17 00:00:00 2001 From: Raz Date: Mon, 13 Jan 2025 07:54:10 +0100 Subject: [PATCH 25/57] fix crash in inscription info issue view --- PadelClub.xcodeproj/project.pbxproj | 8 ++++---- .../Screen/Components/InscriptionInfoView.swift | 1 + 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/PadelClub.xcodeproj/project.pbxproj b/PadelClub.xcodeproj/project.pbxproj index 1b89d98..7e42983 100644 --- a/PadelClub.xcodeproj/project.pbxproj +++ b/PadelClub.xcodeproj/project.pbxproj @@ -3262,7 +3262,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\""; @@ -3289,7 +3289,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.43; + MARKETING_VERSION = 1.0.44; PRODUCT_BUNDLE_IDENTIFIER = app.padelclub; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -3310,7 +3310,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; @@ -3336,7 +3336,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.43; + MARKETING_VERSION = 1.0.44; PRODUCT_BUNDLE_IDENTIFIER = app.padelclub; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; diff --git a/PadelClub/Views/Tournament/Screen/Components/InscriptionInfoView.swift b/PadelClub/Views/Tournament/Screen/Components/InscriptionInfoView.swift index d39e5e0..97f605c 100644 --- a/PadelClub/Views/Tournament/Screen/Components/InscriptionInfoView.swift +++ b/PadelClub/Views/Tournament/Screen/Components/InscriptionInfoView.swift @@ -81,6 +81,7 @@ struct InscriptionInfoView: View { DisclosureGroup { ForEach(callDateIssue) { team in TeamCallView(team: team) + .environment(tournament) } } label: { LabeledContent { From ecf89f6b1daa61642501aecffabbfab773c6e4b7 Mon Sep 17 00:00:00 2001 From: Raz Date: Mon, 13 Jan 2025 11:33:33 +0100 Subject: [PATCH 26/57] fix group stage scheduling --- PadelClub/Data/MatchScheduler.swift | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/PadelClub/Data/MatchScheduler.swift b/PadelClub/Data/MatchScheduler.swift index f45f08f..11865c2 100644 --- a/PadelClub/Data/MatchScheduler.swift +++ b/PadelClub/Data/MatchScheduler.swift @@ -229,14 +229,10 @@ final class MatchScheduler : ModelObject, Storable { return teamsAvailable })) - if rotationIndex > 0 { + if rotationIndex > 0, simultaneousStart == false { rotationMatches = rotationMatches.sorted(by: { if counts[$0.groupStageObject!.index] ?? 0 == counts[$1.groupStageObject!.index] ?? 0 { - if simultaneousStart { - return $0.groupStageObject!.orderedIndexOfMatch($0) < $1.groupStageObject!.orderedIndexOfMatch($1) - } else { - return $0.groupStageObject!.index < $1.groupStageObject!.index - } + return $0.groupStageObject!.index < $1.groupStageObject!.index } else { return counts[$0.groupStageObject!.index] ?? 0 < counts[$1.groupStageObject!.index] ?? 0 } From 3a5af393a69fc2e74e42a3b27b37856654ee71f7 Mon Sep 17 00:00:00 2001 From: Raz Date: Mon, 13 Jan 2025 12:05:47 +0100 Subject: [PATCH 27/57] fix auto publish rankings --- PadelClub/Data/Tournament.swift | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/PadelClub/Data/Tournament.swift b/PadelClub/Data/Tournament.swift index 98db9d1..fa6a2bf 100644 --- a/PadelClub/Data/Tournament.swift +++ b/PadelClub/Data/Tournament.swift @@ -1394,6 +1394,14 @@ defer { Logger.error(error) } + if self.publishRankings == false { + self.publishRankings = true + do { + try DataStore.shared.tournaments.addOrUpdate(instance: self) + } catch { + Logger.error(error) + } + } return rankings } From 945f9ddf29c541d8ce19febcb178d2890228b878 Mon Sep 17 00:00:00 2001 From: Raz Date: Tue, 14 Jan 2025 10:20:40 +0100 Subject: [PATCH 28/57] wip --- PadelClub/Data/TeamRegistration.swift | 5 + PadelClub/Views/Team/EditingTeamView.swift | 70 ++++++----- .../Screen/InscriptionManagerView.swift | 110 +++++++++++++++++- 3 files changed, 154 insertions(+), 31 deletions(-) diff --git a/PadelClub/Data/TeamRegistration.swift b/PadelClub/Data/TeamRegistration.swift index 2d24670..9fcefab 100644 --- a/PadelClub/Data/TeamRegistration.swift +++ b/PadelClub/Data/TeamRegistration.swift @@ -43,6 +43,11 @@ final class TeamRegistration: ModelObject, Storable { players().anySatisfy({ $0.source == .onlineRegistration }) } + func unrankedOrUnknown() -> Bool { + players().anySatisfy({ $0.source == nil }) + } + + func isOutOfTournament() -> Bool { walkOut } diff --git a/PadelClub/Views/Team/EditingTeamView.swift b/PadelClub/Views/Team/EditingTeamView.swift index 1afd064..ab452e0 100644 --- a/PadelClub/Views/Team/EditingTeamView.swift +++ b/PadelClub/Views/Team/EditingTeamView.swift @@ -22,7 +22,9 @@ struct EditingTeamView: View { @State private var registrationDate : Date @State private var name: String @FocusState private var focusedField: TeamRegistration.CodingKeys? - + @State private var presentOnlineRegistrationWarning: Bool = false + @State private var currentWaitingList: TeamRegistration? + var messageSentFailed: Binding { Binding { sentError != nil @@ -43,6 +45,22 @@ struct EditingTeamView: View { _registrationDate = State(wrappedValue: team.registrationDate ?? Date()) } + private func _resetTeam() { + self.currentWaitingList = tournament.waitingListSortedTeams().filter({ $0.hasRegisteredOnline() }).first + team.resetPositions() + team.wildCardGroupStage = false + team.walkOut = false + team.wildCardBracket = false + } + + private func _checkOnlineRegistrationWarning() { + guard let currentWaitingList else { return } + let selectedSortedTeams = tournament.selectedSortedTeams().map({ $0.id }) + if selectedSortedTeams.contains(currentWaitingList.id) { + presentOnlineRegistrationWarning = true + } + } + var body: some View { List { Section { @@ -101,15 +119,9 @@ struct EditingTeamView: View { Toggle(isOn: .init(get: { return team.wildCardBracket }, set: { value in - team.resetPositions() - team.wildCardGroupStage = false - team.walkOut = false + _resetTeam() team.wildCardBracket = value - do { - try tournamentStore.teamRegistrations.addOrUpdate(instance: team) - } catch { - Logger.error(error) - } + _save() })) { Text("Wildcard Tableau") } @@ -117,15 +129,9 @@ struct EditingTeamView: View { Toggle(isOn: .init(get: { return team.wildCardGroupStage }, set: { value in - team.resetPositions() - team.wildCardBracket = false - team.walkOut = false + _resetTeam() team.wildCardGroupStage = value - do { - try tournamentStore.teamRegistrations.addOrUpdate(instance: team) - } catch { - Logger.error(error) - } + _save() })) { Text("Wildcard Poule") } @@ -133,15 +139,9 @@ struct EditingTeamView: View { Toggle(isOn: .init(get: { return team.walkOut }, set: { value in - team.resetPositions() - team.wildCardBracket = false - team.wildCardGroupStage = false + _resetTeam() team.walkOut = value - do { - try tournamentStore.teamRegistrations.addOrUpdate(instance: team) - } catch { - Logger.error(error) - } + _save() })) { Text("Forfait") } @@ -199,12 +199,9 @@ struct EditingTeamView: View { Section { RowButtonView("Effacer l'équipe", role: .destructive, systemImage: "trash") { + _resetTeam() team.deleteTeamScores() - do { - try tournamentStore.teamRegistrations.delete(instance: team) - } catch { - Logger.error(error) - } + _save() dismiss() } } footer: { @@ -213,6 +210,19 @@ struct EditingTeamView: View { } } } + .alert("Attention", isPresented: $presentOnlineRegistrationWarning, actions: { + if let currentWaitingList { + Button("Prévenir") { + + } + + Button("OK") { + self.currentWaitingList = nil + } + } + }, message: { + Text("Cette équipe, inscrite en ligne, rentre dans votre sélection suite à la modification que vous venez de faire, voulez-vous les prévenir ?") + }) .navigationBarBackButtonHidden(focusedField != nil) .toolbar(content: { if focusedField != nil { diff --git a/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift b/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift index 125e6bc..6138632 100644 --- a/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift +++ b/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift @@ -53,10 +53,86 @@ struct InscriptionManagerView: View { @State private var refreshResult: String? = nil @State private var refreshInProgress: Bool = false @State private var refreshStatus: Bool? + @State private var showLegendView: Bool = false var tournamentStore: TournamentStore { return self.tournament.tournamentStore } + + enum LegendPositionTip: Int, Identifiable, CaseIterable { + var id: Int { self.rawValue } + case groupStage + case bracket + + func legendDescriptionLabel() -> String { + return "" + } + + var isActive: Bool { + switch self { + case .groupStage: + return true + case .bracket: + return true + } + } + + var color: Color { + switch self { + case .groupStage: + return .red + case .bracket: + return .green + } + } + + var hideColorVariation: Bool { + switch self { + case .groupStage: + return true + case .bracket: + return true + } + } + } + + enum LegendInscriptionTip: Int, Identifiable, CaseIterable { + var id: Int { self.rawValue } + case groupStage + case bracket + + func legendDescriptionLabel() -> String { + return "" + } + + var isActive: Bool { + switch self { + case .groupStage: + return true + case .bracket: + return true + } + } + + var color: Color { + switch self { + case .groupStage: + return .red + case .bracket: + return .green + } + } + + var hideColorVariation: Bool { + switch self { + case .groupStage: + return true + case .bracket: + return true + } + } + + } enum SortingMode: Int, Identifiable, CaseIterable { var id: Int { self.rawValue } @@ -592,6 +668,8 @@ struct InscriptionManagerView: View { } } + let isImported = teams.anySatisfy({ $0.isImported() }) + if teams.isEmpty == false { if compactMode { Section { @@ -602,13 +680,19 @@ struct InscriptionManagerView: View { .environment(tournament) } label: { TeamRowView(team: team) + if isImported && team.isImported() == false { + Text("ne provient pas du fichier beach-padel").foregroundStyle(.red) + } } .swipeActions(edge: .trailing, allowsFullSwipe: true) { if tournament.enableOnlineRegistration == false { _teamDeleteButtonView(team) } } - .listRowView(isActive: true, color: team.initialRoundColor() ?? tournament.cutLabelColor(index: teamIndex, teamCount: filterMode == .waiting ? 0 : selectedSortedTeams.count), hideColorVariation: true) + .listRowView(isActive: team.hasRegisteredOnline(), color: .master, hideColorVariation: true) + .listRowView(isActive: team.unrankedOrUnknown(), color: .logoYellow, hideColorVariation: true) + .listRowView(isActive: isImported && team.isImported() == false, color: .red, hideColorVariation: false) + .listRowView(isActive: true, color: team.initialRoundColor() ?? tournament.cutLabelColor(index: teamIndex, teamCount: filterMode == .waiting ? 0 : selectedSortedTeams.count), hideColorVariation: true, alignment: .trailing) } } header: { if filterMode == .all && walkoutTeams.isEmpty == false { @@ -616,8 +700,32 @@ struct InscriptionManagerView: View { } else { Text("\(teams.count.formatted()) équipe\(teams.count.pluralSuffix)") } + } footer: { + FooterButtonView("Légende") { + showLegendView = true + } } .headerProminence(.increased) + .sheet(isPresented: $showLegendView) { + List { + Section { + ForEach(LegendInscriptionTip.allCases) { legend in + Text(legend.legendDescriptionLabel()) + .listRowView(isActive: legend.isActive, color: legend.color, hideColorVariation: legend.hideColorVariation) + } + } header: { + Text("Statut de l'inscription") + } + Section { + ForEach(LegendPositionTip.allCases) { legend in + Text(legend.legendDescriptionLabel()) + .listRowView(isActive: legend.isActive, color: legend.color, hideColorVariation: legend.hideColorVariation, alignment: .trailing) + } + } header: { + Text("Statut de la position dans le tournoi") + } + } + } } else { ForEach(teams) { team in let teamIndex = team.index(in: sortedTeams) From 457900f64ab612fa1f845b8c3793f7d5601dbfe0 Mon Sep 17 00:00:00 2001 From: Raz Date: Wed, 15 Jan 2025 14:17:16 +0100 Subject: [PATCH 29/57] wip --- PadelClub.xcodeproj/project.pbxproj | 16 +++ PadelClub/Data/PlayerRegistration.swift | 12 +- PadelClub/Data/TeamRegistration.swift | 23 ++-- PadelClub/Data/Tournament.swift | 6 +- PadelClub/InscriptionLegendView.swift | 74 ++++++++++++ PadelClub/RegistrationInfoSheetView.swift | 89 ++++++++++++++ PadelClub/Utils/PadelRule.swift | 2 +- PadelClub/Utils/Tips.swift | 6 +- PadelClub/Views/Team/EditingTeamView.swift | 29 ++++- PadelClub/Views/Team/TeamRowView.swift | 7 +- .../Screen/InscriptionManagerView.swift | 113 ++---------------- .../Screen/RegistrationSetupView.swift | 13 +- .../Views/Tournament/TournamentView.swift | 33 ++++- .../ViewModifiers/ListRowViewModifier.swift | 7 +- PadelClubTests/ServerDataTests.swift | 3 +- 15 files changed, 293 insertions(+), 140 deletions(-) create mode 100644 PadelClub/InscriptionLegendView.swift create mode 100644 PadelClub/RegistrationInfoSheetView.swift diff --git a/PadelClub.xcodeproj/project.pbxproj b/PadelClub.xcodeproj/project.pbxproj index 4816398..6fd837e 100644 --- a/PadelClub.xcodeproj/project.pbxproj +++ b/PadelClub.xcodeproj/project.pbxproj @@ -126,6 +126,12 @@ FF2EFBF02BDE295E0049CE3B /* SendToAllView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF2EFBEF2BDE295E0049CE3B /* SendToAllView.swift */; }; FF3795622B9396D0004EA093 /* PadelClubApp.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = FF3795602B9396D0004EA093 /* PadelClubApp.xcdatamodeld */; }; FF3795662B9399AA004EA093 /* Persistence.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF3795652B9399AA004EA093 /* Persistence.swift */; }; + FF3A73F32D37C34D007E3032 /* RegistrationInfoSheetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF3A73F22D37C34C007E3032 /* RegistrationInfoSheetView.swift */; }; + FF3A73F42D37C34D007E3032 /* RegistrationInfoSheetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF3A73F22D37C34C007E3032 /* RegistrationInfoSheetView.swift */; }; + FF3A73F52D37C34D007E3032 /* RegistrationInfoSheetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF3A73F22D37C34C007E3032 /* RegistrationInfoSheetView.swift */; }; + FF3A74322D37DCF2007E3032 /* InscriptionLegendView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF3A74312D37DCF2007E3032 /* InscriptionLegendView.swift */; }; + FF3A74332D37DCF2007E3032 /* InscriptionLegendView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF3A74312D37DCF2007E3032 /* InscriptionLegendView.swift */; }; + FF3A74342D37DCF2007E3032 /* InscriptionLegendView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF3A74312D37DCF2007E3032 /* InscriptionLegendView.swift */; }; FF3B60A32BC49BBC008C2E66 /* MatchScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF3B60A22BC49BBC008C2E66 /* MatchScheduler.swift */; }; FF3F74F62B919E45004CFE0E /* UmpireView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF3F74F52B919E45004CFE0E /* UmpireView.swift */; }; FF3F74FF2B91A2D4004CFE0E /* AgendaDestination.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF3F74FE2B91A2D4004CFE0E /* AgendaDestination.swift */; }; @@ -1072,6 +1078,8 @@ FF2EFBEF2BDE295E0049CE3B /* SendToAllView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SendToAllView.swift; sourceTree = ""; }; FF3795612B9396D0004EA093 /* Model.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = Model.xcdatamodel; sourceTree = ""; }; FF3795652B9399AA004EA093 /* Persistence.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Persistence.swift; sourceTree = ""; }; + FF3A73F22D37C34C007E3032 /* RegistrationInfoSheetView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RegistrationInfoSheetView.swift; sourceTree = ""; }; + FF3A74312D37DCF2007E3032 /* InscriptionLegendView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InscriptionLegendView.swift; sourceTree = ""; }; FF3B60A22BC49BBC008C2E66 /* MatchScheduler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MatchScheduler.swift; sourceTree = ""; }; FF3F74F52B919E45004CFE0E /* UmpireView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UmpireView.swift; sourceTree = ""; }; FF3F74FE2B91A2D4004CFE0E /* AgendaDestination.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AgendaDestination.swift; sourceTree = ""; }; @@ -1340,6 +1348,8 @@ FFD784002B91BF79000F62A6 /* Launch Screen.storyboard */, FF2B515F2C7E300500FFF126 /* SeedData */, C4A47D722B72881500ADC637 /* Views */, + FF3A73F22D37C34C007E3032 /* RegistrationInfoSheetView.swift */, + FF3A74312D37DCF2007E3032 /* InscriptionLegendView.swift */, FF3F74FD2B91A087004CFE0E /* ViewModel */, C4A47D5F2B6D3B2D00ADC637 /* Data */, FFF8ACD02B9238A2008466FA /* Utils */, @@ -2411,6 +2421,7 @@ FF967CF32BAECC0B00A9A3BD /* PlayerRegistration.swift in Sources */, FF4AB6BF2B92577A0002987F /* ImportedPlayerView.swift in Sources */, FF1162872BD004AD000C4809 /* EditingTeamView.swift in Sources */, + FF3A74332D37DCF2007E3032 /* InscriptionLegendView.swift in Sources */, FF6EC9062B947A1000EA7F5A /* NetworkManagerError.swift in Sources */, C4A47D5A2B6D383C00ADC637 /* Tournament.swift in Sources */, C4FC2E2B2C2C0E4D0021F3BF /* TournamentStore.swift in Sources */, @@ -2499,6 +2510,7 @@ FFC2DCB42BBE9ECD0046DB9F /* LoserRoundsView.swift in Sources */, FF967CFC2BAEE52E00A9A3BD /* GroupStagesView.swift in Sources */, FFD783FF2B91BA42000F62A6 /* PadelClubView.swift in Sources */, + FF3A73F32D37C34D007E3032 /* RegistrationInfoSheetView.swift in Sources */, C49EF01B2BD6A1E80077B5AA /* URLs.swift in Sources */, FFCFC0142BBC59FC00B82851 /* MatchDescriptor.swift in Sources */, FF8F264C2BAE0B4100650388 /* TournamentFormatSelectionView.swift in Sources */, @@ -2699,6 +2711,7 @@ FF4CBF9B2C996C0600151637 /* PlayerRegistration.swift in Sources */, FF4CBF9C2C996C0600151637 /* ImportedPlayerView.swift in Sources */, FF4CBF9D2C996C0600151637 /* EditingTeamView.swift in Sources */, + FF3A74322D37DCF2007E3032 /* InscriptionLegendView.swift in Sources */, FF4CBF9E2C996C0600151637 /* NetworkManagerError.swift in Sources */, FF4CBF9F2C996C0600151637 /* Tournament.swift in Sources */, FF4CBFA02C996C0600151637 /* TournamentStore.swift in Sources */, @@ -2787,6 +2800,7 @@ FF4CBFED2C996C0600151637 /* LoserRoundsView.swift in Sources */, FF4CBFEE2C996C0600151637 /* GroupStagesView.swift in Sources */, FF4CBFEF2C996C0600151637 /* PadelClubView.swift in Sources */, + FF3A73F52D37C34D007E3032 /* RegistrationInfoSheetView.swift in Sources */, FF4CBFF02C996C0600151637 /* URLs.swift in Sources */, FF4CBFF12C996C0600151637 /* MatchDescriptor.swift in Sources */, FF4CBFF22C996C0600151637 /* TournamentFormatSelectionView.swift in Sources */, @@ -2966,6 +2980,7 @@ FF70FB1A2C90584900129CC2 /* PlayerRegistration.swift in Sources */, FF70FB1B2C90584900129CC2 /* ImportedPlayerView.swift in Sources */, FF70FB1C2C90584900129CC2 /* EditingTeamView.swift in Sources */, + FF3A74342D37DCF2007E3032 /* InscriptionLegendView.swift in Sources */, FF70FB1D2C90584900129CC2 /* NetworkManagerError.swift in Sources */, FF70FB1E2C90584900129CC2 /* Tournament.swift in Sources */, FF70FB1F2C90584900129CC2 /* TournamentStore.swift in Sources */, @@ -3054,6 +3069,7 @@ FF70FB6C2C90584900129CC2 /* LoserRoundsView.swift in Sources */, FF70FB6D2C90584900129CC2 /* GroupStagesView.swift in Sources */, FF70FB6E2C90584900129CC2 /* PadelClubView.swift in Sources */, + FF3A73F42D37C34D007E3032 /* RegistrationInfoSheetView.swift in Sources */, FF70FB6F2C90584900129CC2 /* URLs.swift in Sources */, FF70FB702C90584900129CC2 /* MatchDescriptor.swift in Sources */, FF70FB712C90584900129CC2 /* TournamentFormatSelectionView.swift in Sources */, diff --git a/PadelClub/Data/PlayerRegistration.swift b/PadelClub/Data/PlayerRegistration.swift index f9205e5..9638cc2 100644 --- a/PadelClub/Data/PlayerRegistration.swift +++ b/PadelClub/Data/PlayerRegistration.swift @@ -40,22 +40,21 @@ final class PlayerRegistration: ModelObject, Storable { var hasArrived: Bool = false var coach: Bool = false var captain: Bool = false + var registeredOnline: Bool = false func localizedSourceLabel() -> String { switch source { - case .frenchFederation: + case .frenchFederation, .onlineRegistration: return "Via la base fédérale" case .beachPadel: return "Via le fichier beach-padel" - case .onlineRegistration: - return "Via un inscription en ligne" case nil: return "Manuellement" } } - init(teamRegistration: String? = nil, firstName: String, lastName: String, licenceId: String? = nil, rank: Int? = nil, paymentType: PlayerPaymentType? = nil, sex: PlayerSexType? = nil, tournamentPlayed: Int? = nil, points: Double? = nil, clubName: String? = nil, ligueName: String? = nil, assimilation: String? = nil, phoneNumber: String? = nil, email: String? = nil, birthdate: String? = nil, computedRank: Int = 0, source: PlayerDataSource? = nil, hasArrived: Bool = false, coach: Bool = false, captain: Bool = false) { + init(teamRegistration: String? = nil, firstName: String, lastName: String, licenceId: String? = nil, rank: Int? = nil, paymentType: PlayerPaymentType? = nil, sex: PlayerSexType? = nil, tournamentPlayed: Int? = nil, points: Double? = nil, clubName: String? = nil, ligueName: String? = nil, assimilation: String? = nil, phoneNumber: String? = nil, email: String? = nil, birthdate: String? = nil, computedRank: Int = 0, source: PlayerDataSource? = nil, hasArrived: Bool = false, coach: Bool = false, captain: Bool = false, registeredOnline: Bool = false) { self.teamRegistration = teamRegistration self.firstName = firstName self.lastName = lastName @@ -76,6 +75,7 @@ final class PlayerRegistration: ModelObject, Storable { self.hasArrived = hasArrived self.captain = captain self.coach = coach + self.registeredOnline = registeredOnline } internal init(importedPlayer: ImportedPlayer) { @@ -403,6 +403,7 @@ defer { case _hasArrived = "hasArrived" case _coach = "coach" case _captain = "captain" + case _registeredOnline = "registeredOnline" } @@ -433,6 +434,8 @@ defer { email = try container.decodeIfPresent(String.self, forKey: ._email) birthdate = try container.decodeIfPresent(String.self, forKey: ._birthdate) source = try container.decodeIfPresent(PlayerDataSource.self, forKey: ._source) + registeredOnline = try container.decodeIfPresent(Bool.self, forKey: ._registeredOnline) ?? false + } func encode(to encoder: Encoder) throws { @@ -460,6 +463,7 @@ defer { try container.encode(hasArrived, forKey: ._hasArrived) try container.encode(captain, forKey: ._captain) try container.encode(coach, forKey: ._coach) + try container.encode(registeredOnline, forKey: ._registeredOnline) } enum PlayerDataSource: Int, Codable { diff --git a/PadelClub/Data/TeamRegistration.swift b/PadelClub/Data/TeamRegistration.swift index 9fcefab..a24e355 100644 --- a/PadelClub/Data/TeamRegistration.swift +++ b/PadelClub/Data/TeamRegistration.swift @@ -40,7 +40,7 @@ final class TeamRegistration: ModelObject, Storable { var pointsEarned: Int? func hasRegisteredOnline() -> Bool { - players().anySatisfy({ $0.source == .onlineRegistration }) + players().anySatisfy({ $0.registeredOnline }) } func unrankedOrUnknown() -> Bool { @@ -248,9 +248,9 @@ final class TeamRegistration: ModelObject, Storable { self.setWeight(from: self.players(), inTournamentCategory: tournamentCategory) } - func teamLabel(_ displayStyle: DisplayStyle = .wide, twoLines: Bool = false) -> String { + func teamLabel(_ displayStyle: DisplayStyle = .wide, twoLines: Bool = false, separator: String = "&") -> String { if let name { return name } - return players().map { $0.playerLabel(displayStyle) }.joined(separator: twoLines ? "\n" : " & ") + return players().map { $0.playerLabel(displayStyle) }.joined(separator: twoLines ? "\n" : " \(separator) ") } func teamLabelRanked(displayRank: Bool, displayTeamName: Bool) -> String { @@ -384,19 +384,16 @@ final class TeamRegistration: ModelObject, Storable { } func formattedInscriptionDate(_ exportFormat: ExportFormat = .rawText) -> String? { + guard let registrationDate else { return nil } + + let formattedDate = registrationDate.formatted(.dateTime.weekday().day().month().hour().minute()) + let onlineSuffix = hasRegisteredOnline() ? " en ligne" : "" + switch exportFormat { case .rawText: - if let registrationDate { - return "Inscrit le " + registrationDate.formatted(.dateTime.weekday().day().month().hour().minute()) - } else { - return nil - } + return "Inscrit\(onlineSuffix) le \(formattedDate)" case .csv: - if let registrationDate { - return registrationDate.formatted(.dateTime.weekday().day().month().hour().minute()) - } else { - return nil - } + return formattedDate } } diff --git a/PadelClub/Data/Tournament.swift b/PadelClub/Data/Tournament.swift index 1df0adf..a347ea0 100644 --- a/PadelClub/Data/Tournament.swift +++ b/PadelClub/Data/Tournament.swift @@ -141,7 +141,11 @@ final class Tournament : ModelObject, Storable { #if DEBUG self.isPrivate = false #else - self.isPrivate = Guard.main.purchasedTransactions.isEmpty + if Guard.main.currentPlan == .monthlyUnlimited { + self.isPrivate = true + } else { + self.isPrivate = Guard.main.purchasedTransactions.isEmpty + } #endif self.groupStageFormat = groupStageFormat self.roundFormat = roundFormat diff --git a/PadelClub/InscriptionLegendView.swift b/PadelClub/InscriptionLegendView.swift new file mode 100644 index 0000000..ed1db83 --- /dev/null +++ b/PadelClub/InscriptionLegendView.swift @@ -0,0 +1,74 @@ +// +// InscriptionLegendView.swift +// PadelClub +// +// Created by razmig on 15/01/2025. +// + +import SwiftUI + +struct InscriptionLegendView: View { + @Environment(\.dismiss) private var dismiss + + var body: some View { + NavigationView { + List { + Section { + ForEach(RoundRule.colors.prefix(6).indices, id: \.self) { colorIndex in + Text("Équipe placée en \(RoundRule.roundName(fromRoundIndex: colorIndex))") + .listRowView(isActive: true, color: Color(uiColor: .init(fromHex: RoundRule.colors[colorIndex])), hideColorVariation: true, alignment: .trailing) + } + } + + Section { + Text("Équipe placée en poule") + .listRowView(isActive: true, color: .blue, hideColorVariation: true, alignment: .trailing) + + } + + Section { + Text("Équipe estimée en tableau") + .listRowView(isActive: true, color: .mint, hideColorVariation: true, alignment: .trailing) + Text("Équipe estimée en poule") + .listRowView(isActive: true, color: .cyan, hideColorVariation: true, alignment: .trailing) + + } + + Section { + Text("Équipe en liste d'attente") + .listRowView(isActive: true, color: .gray, hideColorVariation: true, alignment: .trailing) + Text("Équipe forfaite") + .listRowView(isActive: true, color: .logoRed, hideColorVariation: true, alignment: .trailing) + } + + Section { + Label("Inscrit en ligne", systemImage: "person.badge.shield.checkmark.fill") + } footer: { + Text("Icône indiquant que le joueur s'est inscrit en ligne.") + } + + Section { + Text("Équipe ayant un joueur à vérifier") + .listRowView(isActive: true, color: .logoRed, hideColorVariation: true, backgroundColor: .logoRed, alignment: .leading) + } footer: { + Text("Une fois que vous avez importé votre fichier, Padel Club vous affiche ainsi les équipes ayant des joueurs ne provenant pas du fichier ni de la base fédérale.") + } + + Section { + Text("Équipe ayant un joueur ne provenant pas du fichier beach-padel") + .listRowView(isActive: true, color: .beige, hideColorVariation: true, backgroundColor: .beige, alignment: .leading) + } footer: { + Text("Une fois que vous avez importé votre fichier, Padel Club vous affiche ainsi les équipes ayant des joueurs ne provenant pas du fichier.") + } + + } + .headerProminence(.increased) + .navigationTitle("Légende") + .navigationBarTitleDisplayMode(.inline) + .toolbarBackground(.visible, for: .navigationBar) + .navigationBarItems(trailing: Button("Fermer") { + dismiss() + }) + } + } +} diff --git a/PadelClub/RegistrationInfoSheetView.swift b/PadelClub/RegistrationInfoSheetView.swift new file mode 100644 index 0000000..efbf3ac --- /dev/null +++ b/PadelClub/RegistrationInfoSheetView.swift @@ -0,0 +1,89 @@ +// +// RegistrationInfoSheetView.swift +// PadelClub +// +// Created by razmig on 15/01/2025. +// + +import SwiftUI + +struct RegistrationInfoSheetView: View { + @Environment(\.dismiss) private var dismiss + let registrationInfoText: String = + """ + Comment fonctionnent les inscriptions en ligne ? + + Les inscriptions en ligne permettent aux joueurs de s'inscrire directement au tournoi via la plateforme. Voici les informations importantes à connaître : + + Conditions d'inscription : + - Un compte Padel Club est requis pour s'inscrire + - Une licence valide peut être nécessaire + - Les équipes des tournois homologués doivent être composées de 2 joueurs + - Les animations ont moins de restrictions + + Déroulement des inscriptions : + 1. Les inscriptions peuvent avoir une date et heure d'ouverture définies par l'organisateur + 2. Le tournoi peut avoir une capacité maximale d'équipes + 3. Si une capacité maximale est définie, les nouvelles inscriptions seront placées en liste d'attente une fois celle-ci atteinte + 4. La liste d'attente peut également avoir une limite maximale d'équipes + 5. Les inscriptions peuvent se terminer à une date limite fixée par l'organisateur + + Désinscription : + La désinscription est possible tant que : + - Le tournoi n'a pas commencé + - La date limite d'inscription n'est pas dépassée + - Les inscriptions n'ont pas été clôturées par l'organisateur + + Validation des inscriptions : + - L'inscription n'est définitive qu'après validation des critères d'éligibilité (catégorie, classement, âge...) + - En cas de désistement d'une équipe inscrite, la première équipe en liste d'attente est automatiquement intégrée au tableau + - Une équipe en liste d'attente peut se désinscrire à tout moment selon les mêmes conditions + + L'organisateur se réserve le droit de modifier ces conditions ou de clôturer les inscriptions de manière anticipée. + """ + var body: some View { + NavigationView { + ScrollView { + VStack(alignment: .leading, spacing: 20) { + // Title + Text("Inscriptions en ligne") + .font(.title) + .fontWeight(.bold) + .padding(.bottom, 5) + + // Content sections + ForEach(registrationInfoText.components(separatedBy: "\n\n"), id: \.self) { section in + if !section.isEmpty { + VStack(alignment: .leading, spacing: 10) { + if section.contains(":") { + Text(section.components(separatedBy: ":")[0]) + .font(.headline) + .foregroundColor(.primary) + + let bulletPoints = section.components(separatedBy: "\n-") + if bulletPoints.count > 1 { + ForEach(bulletPoints.dropFirst(), id: \.self) { point in + HStack(alignment: .top) { + Text("•") + .padding(.trailing, 5) + Text(point) + .fixedSize(horizontal: false, vertical: true) + } + } + } + } else { + Text(section) + } + } + .padding(.bottom, 10) + } + } + } + .padding() + } + .navigationBarItems(trailing: Button("Fermer") { + dismiss() + }) + } + } +} diff --git a/PadelClub/Utils/PadelRule.swift b/PadelClub/Utils/PadelRule.swift index b346a14..ff9b1d8 100644 --- a/PadelClub/Utils/PadelRule.swift +++ b/PadelClub/Utils/PadelRule.swift @@ -1657,7 +1657,7 @@ enum PlayersCountRange: Int, CaseIterable { } enum RoundRule { - static let colors = ["#99ff99", "#66ff66", "#33cc33", "#009900", "#006600", "#006600", "#006600", "#006600", "#006600", "#006600"] + static let colors = ["#99ff99", "#66ff66", "#33cc33", "#009900", "#006600", "#336633", "#DD6600", "#EE6633", "#EE6633", "#EE6633"] static func loserBrackets(index: Int) -> [String] { switch index { diff --git a/PadelClub/Utils/Tips.swift b/PadelClub/Utils/Tips.swift index c3e145c..c44c9b3 100644 --- a/PadelClub/Utils/Tips.swift +++ b/PadelClub/Utils/Tips.swift @@ -615,10 +615,14 @@ struct OnlineRegistrationTip: Tip { } var actions: [Action] { - Action(id: ActionKey.enableOnlineRegistration.rawValue, title: "Activer dans les réglages du tournoi") + [ + Action(id: ActionKey.more.rawValue, title: "En savoir plus"), + Action(id: ActionKey.enableOnlineRegistration.rawValue, title: "Activer dans les réglages du tournoi") + ] } enum ActionKey: String { + case more = "more" case enableOnlineRegistration = "enableOnlineRegistration" } } diff --git a/PadelClub/Views/Team/EditingTeamView.swift b/PadelClub/Views/Team/EditingTeamView.swift index ab452e0..9c89875 100644 --- a/PadelClub/Views/Team/EditingTeamView.swift +++ b/PadelClub/Views/Team/EditingTeamView.swift @@ -24,6 +24,7 @@ struct EditingTeamView: View { @FocusState private var focusedField: TeamRegistration.CodingKeys? @State private var presentOnlineRegistrationWarning: Bool = false @State private var currentWaitingList: TeamRegistration? + @State private var presentTeamToWarn: Bool = false var messageSentFailed: Binding { Binding { @@ -64,7 +65,7 @@ struct EditingTeamView: View { var body: some View { List { Section { - RowButtonView("Modifier la composition de l'équipe") { + RowButtonView("Modifier la composition de l'équipe", role: team.hasRegisteredOnline() ? .destructive : .none) { editedTeam = team } @@ -72,6 +73,7 @@ struct EditingTeamView: View { } header: { if team.hasRegisteredOnline() { Text("Inscription en ligne") + .foregroundStyle(.master) } else { Text("Inscription par vous-même") } @@ -88,6 +90,7 @@ struct EditingTeamView: View { } } } + .headerProminence(.increased) Section { DatePicker(selection: $registrationDate) { @@ -206,22 +209,34 @@ struct EditingTeamView: View { } } footer: { if team.hasRegisteredOnline() { - Text("Attention, supprimer cette équipe notifiera par email que leur inscription a été annulée.") + Text("Attention, supprimer cette équipe notifiera par email que leur inscription a été annulée.").foregroundStyle(.logoRed) } } } - .alert("Attention", isPresented: $presentOnlineRegistrationWarning, actions: { + .sheet(isPresented: $presentTeamToWarn) { if let currentWaitingList { - Button("Prévenir") { - + NavigationStack { + EditingTeamView(team: currentWaitingList) } + .tint(.master) + } + } + .alert("Attention", isPresented: $presentOnlineRegistrationWarning, actions: { + if currentWaitingList != nil { + Button("Voir l'équipe") { + self.presentTeamToWarn = true + } + Button("OK") { self.currentWaitingList = nil + self.presentOnlineRegistrationWarning = false } } }, message: { - Text("Cette équipe, inscrite en ligne, rentre dans votre sélection suite à la modification que vous venez de faire, voulez-vous les prévenir ?") + if let currentWaitingList { + Text("L'équipe \(currentWaitingList.teamLabel(separator: "/")), inscrite en ligne, rentre dans votre sélection suite à la modification que vous venez de faire, voulez-vous les prévenir ?") + } }) .navigationBarBackButtonHidden(focusedField != nil) .toolbar(content: { @@ -351,6 +366,8 @@ struct EditingTeamView: View { } catch { Logger.error(error) } + + _checkOnlineRegistrationWarning() } private var _networkErrorMessage: String { diff --git a/PadelClub/Views/Team/TeamRowView.swift b/PadelClub/Views/Team/TeamRowView.swift index f32365b..c26766d 100644 --- a/PadelClub/Views/Team/TeamRowView.swift +++ b/PadelClub/Views/Team/TeamRowView.swift @@ -118,7 +118,12 @@ struct TeamRowView: View { var body: some View { ForEach(team.players()) { player in - Text(player.playerLabel()).lineLimit(1).truncationMode(.tail) + HStack { + if player.registeredOnline { + Image(systemName: "person.badge.shield.checkmark.fill") + } + Text(player.playerLabel()).lineLimit(1).truncationMode(.tail) + } } } } diff --git a/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift b/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift index 6138632..6942db4 100644 --- a/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift +++ b/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift @@ -59,81 +59,6 @@ struct InscriptionManagerView: View { return self.tournament.tournamentStore } - enum LegendPositionTip: Int, Identifiable, CaseIterable { - var id: Int { self.rawValue } - case groupStage - case bracket - - func legendDescriptionLabel() -> String { - return "" - } - - var isActive: Bool { - switch self { - case .groupStage: - return true - case .bracket: - return true - } - } - - var color: Color { - switch self { - case .groupStage: - return .red - case .bracket: - return .green - } - } - - var hideColorVariation: Bool { - switch self { - case .groupStage: - return true - case .bracket: - return true - } - } - } - - enum LegendInscriptionTip: Int, Identifiable, CaseIterable { - var id: Int { self.rawValue } - case groupStage - case bracket - - func legendDescriptionLabel() -> String { - return "" - } - - var isActive: Bool { - switch self { - case .groupStage: - return true - case .bracket: - return true - } - } - - var color: Color { - switch self { - case .groupStage: - return .red - case .bracket: - return .green - } - } - - var hideColorVariation: Bool { - switch self { - case .groupStage: - return true - case .bracket: - return true - } - } - - } - enum SortingMode: Int, Identifiable, CaseIterable { var id: Int { self.rawValue } case registrationDate @@ -455,11 +380,13 @@ struct InscriptionManagerView: View { Section { Button("+1 en tableau") { tournament.addWildCard(1, .bracket) + _setHash() } if tournament.groupStageCount > 0 { Button("+1 en poules") { tournament.addWildCard(1, .groupStage) + _setHash() } } } header: { @@ -468,6 +395,7 @@ struct InscriptionManagerView: View { Button("Bloquer une place") { tournament.addEmptyTeamRegistration(1) + _setHash() } Divider() @@ -675,24 +603,20 @@ struct InscriptionManagerView: View { Section { ForEach(teams) { team in let teamIndex = team.index(in: sortedTeams) + let color: Color? = isImported ? (team.unrankedOrUnknown() ? .logoRed : (team.isImported() == false ? .beige : nil)) : nil + NavigationLink { EditingTeamView(team: team) .environment(tournament) } label: { TeamRowView(team: team) - if isImported && team.isImported() == false { - Text("ne provient pas du fichier beach-padel").foregroundStyle(.red) - } } .swipeActions(edge: .trailing, allowsFullSwipe: true) { if tournament.enableOnlineRegistration == false { _teamDeleteButtonView(team) } } - .listRowView(isActive: team.hasRegisteredOnline(), color: .master, hideColorVariation: true) - .listRowView(isActive: team.unrankedOrUnknown(), color: .logoYellow, hideColorVariation: true) - .listRowView(isActive: isImported && team.isImported() == false, color: .red, hideColorVariation: false) - .listRowView(isActive: true, color: team.initialRoundColor() ?? tournament.cutLabelColor(index: teamIndex, teamCount: filterMode == .waiting ? 0 : selectedSortedTeams.count), hideColorVariation: true, alignment: .trailing) + .listRowView(isActive: true, color: team.initialRoundColor() ?? tournament.cutLabelColor(index: teamIndex, teamCount: filterMode == .waiting ? 0 : selectedSortedTeams.count), hideColorVariation: true, backgroundColor: color, alignment: .trailing) } } header: { if filterMode == .all && walkoutTeams.isEmpty == false { @@ -701,31 +625,11 @@ struct InscriptionManagerView: View { Text("\(teams.count.formatted()) équipe\(teams.count.pluralSuffix)") } } footer: { - FooterButtonView("Légende") { + FooterButtonView("Légende des codes couleurs") { showLegendView = true } } .headerProminence(.increased) - .sheet(isPresented: $showLegendView) { - List { - Section { - ForEach(LegendInscriptionTip.allCases) { legend in - Text(legend.legendDescriptionLabel()) - .listRowView(isActive: legend.isActive, color: legend.color, hideColorVariation: legend.hideColorVariation) - } - } header: { - Text("Statut de l'inscription") - } - Section { - ForEach(LegendPositionTip.allCases) { legend in - Text(legend.legendDescriptionLabel()) - .listRowView(isActive: legend.isActive, color: legend.color, hideColorVariation: legend.hideColorVariation, alignment: .trailing) - } - } header: { - Text("Statut de la position dans le tournoi") - } - } - } } else { ForEach(teams) { team in let teamIndex = team.index(in: sortedTeams) @@ -754,6 +658,9 @@ struct InscriptionManagerView: View { .searchable(text: $searchField, isPresented: $presentSearch, prompt: Text("Chercher parmi les équipes inscrites")) .keyboardType(.alphabet) .autocorrectionDisabled() + .sheet(isPresented: $showLegendView) { + InscriptionLegendView() + } } @ViewBuilder diff --git a/PadelClub/Views/Tournament/Screen/RegistrationSetupView.swift b/PadelClub/Views/Tournament/Screen/RegistrationSetupView.swift index efddaa1..73bbbe7 100644 --- a/PadelClub/Views/Tournament/Screen/RegistrationSetupView.swift +++ b/PadelClub/Views/Tournament/Screen/RegistrationSetupView.swift @@ -24,6 +24,7 @@ struct RegistrationSetupView: View { @State private var licenseIsRequired: Bool @State private var minPlayerPerTeam: Int @State private var maxPlayerPerTeam: Int + @State private var showMoreInfos: Bool = false @State private var hasChanges: Bool = false @@ -78,7 +79,14 @@ struct RegistrationSetupView: View { Text("Activer") } } footer: { - Text("Les inscriptions en ligne permettent à des joueurs de s'inscrire à votre tournoi en passant par le site Padel Club. Vous verrez alors votre liste d'inscription s'agrandir dans la vue Gestion des Inscriptions de l'application.") + VStack(alignment: .leading) { + Text("Les inscriptions en ligne permettent à des joueurs de s'inscrire à votre tournoi en passant par le site Padel Club. Vous verrez alors votre liste d'inscription s'agrandir dans la vue Gestion des Inscriptions de l'application.") + + FooterButtonView("En savoir plus") { + self.showMoreInfos = true + + } + } } if enableOnlineRegistration { @@ -191,6 +199,9 @@ struct RegistrationSetupView: View { ) } } + .sheet(isPresented: $showMoreInfos) { + RegistrationInfoSheetView() + } .toolbar(content: { if hasChanges { ToolbarItem(placement: .topBarLeading) { diff --git a/PadelClub/Views/Tournament/TournamentView.swift b/PadelClub/Views/Tournament/TournamentView.swift index 8de233d..1e2a8ea 100644 --- a/PadelClub/Views/Tournament/TournamentView.swift +++ b/PadelClub/Views/Tournament/TournamentView.swift @@ -14,6 +14,8 @@ struct TournamentView: View { @Environment(NavigationViewModel.self) var navigation: NavigationViewModel @State var tournament: Tournament + @State private var showMoreInfos: Bool = false + var presentationContext: PresentationContext = .agenda let tournamentSelectionTip: TournamentSelectionTip = TournamentSelectionTip() @@ -62,8 +64,17 @@ struct TournamentView: View { } case .initial: if tournament.enableOnlineRegistration == false { - TipView(onlineRegistrationTip) { actions in - navigation.path.append(Screen.settings) + TipView(onlineRegistrationTip) { action in + if let actionKey = OnlineRegistrationTip.ActionKey(rawValue: action.id) { + switch actionKey { + case .enableOnlineRegistration: + navigation.path.append(Screen.settings) + case .more: + self.showMoreInfos = true + } + } else { + print("Unknown action: \(action.id)") + } } .tipStyle(tint: .master, asSection: true) } @@ -74,10 +85,19 @@ struct TournamentView: View { TournamentInitView(tournament: tournament) case .build: if tournament.enableOnlineRegistration == false { - TipView(onlineRegistrationTip) { actions in - navigation.path.append(Screen.settings) + TipView(onlineRegistrationTip) { action in + if let actionKey = OnlineRegistrationTip.ActionKey(rawValue: action.id) { + switch actionKey { + case .enableOnlineRegistration: + navigation.path.append(Screen.settings) + case .more: + self.showMoreInfos = true + } + } else { + print("Unknown action: \(action.id)") + } } - .tipStyle(tint: .green, asSection: true) + .tipStyle(tint: .master, asSection: true) } Section { @@ -101,6 +121,9 @@ struct TournamentView: View { TournamentRunningView(tournament: tournament) } } + .sheet(isPresented: $showMoreInfos) { + RegistrationInfoSheetView() + } } .environment(tournament) .id(tournament.id) diff --git a/PadelClub/Views/ViewModifiers/ListRowViewModifier.swift b/PadelClub/Views/ViewModifiers/ListRowViewModifier.swift index 6cfc20a..02dcbc2 100644 --- a/PadelClub/Views/ViewModifiers/ListRowViewModifier.swift +++ b/PadelClub/Views/ViewModifiers/ListRowViewModifier.swift @@ -11,10 +11,11 @@ struct ListRowViewModifier: ViewModifier { let isActive: Bool let color: Color var hideColorVariation: Bool = false + var backgroundColor: Color? = nil let alignment: Alignment func colorVariation() -> Color { - hideColorVariation ? Color(uiColor: .systemBackground) : color.variation() + hideColorVariation ? (backgroundColor ?? Color(uiColor: .systemBackground)) : color.variation() } func body(content: Content) -> some View { @@ -33,7 +34,7 @@ struct ListRowViewModifier: ViewModifier { } extension View { - func listRowView(isActive: Bool = false, color: Color, hideColorVariation: Bool = false, alignment: Alignment = .leading) -> some View { - modifier(ListRowViewModifier(isActive: isActive, color: color, hideColorVariation: hideColorVariation, alignment: alignment)) + func listRowView(isActive: Bool = false, color: Color, hideColorVariation: Bool = false, backgroundColor: Color? = nil, alignment: Alignment = .leading) -> some View { + modifier(ListRowViewModifier(isActive: isActive, color: color, hideColorVariation: hideColorVariation, backgroundColor: backgroundColor, alignment: alignment)) } } diff --git a/PadelClubTests/ServerDataTests.swift b/PadelClubTests/ServerDataTests.swift index a929f03..2643987 100644 --- a/PadelClubTests/ServerDataTests.swift +++ b/PadelClubTests/ServerDataTests.swift @@ -242,7 +242,7 @@ final class ServerDataTests: XCTestCase { return } - let playerRegistration = PlayerRegistration(teamRegistration: teamRegistrationId, firstName: "juan", lastName: "lebron", licenceId: "123", rank: 11, paymentType: PlayerRegistration.PlayerPaymentType.cash, sex: PlayerRegistration.PlayerSexType.male, tournamentPlayed: 2, points: 33, clubName: "le club", ligueName: "la league", assimilation: "ass", phoneNumber: "123123", email: "email@email.com", birthdate: nil, computedRank: 222, source: PlayerRegistration.PlayerDataSource.frenchFederation, hasArrived: true, coach: true, captain: true) + let playerRegistration = PlayerRegistration(teamRegistration: teamRegistrationId, firstName: "juan", lastName: "lebron", licenceId: "123", rank: 11, paymentType: PlayerRegistration.PlayerPaymentType.cash, sex: PlayerRegistration.PlayerSexType.male, tournamentPlayed: 2, points: 33, clubName: "le club", ligueName: "la league", assimilation: "ass", phoneNumber: "123123", email: "email@email.com", birthdate: nil, computedRank: 222, source: PlayerRegistration.PlayerDataSource.frenchFederation, hasArrived: true, coach: true, captain: true, registeredOnline: true) let pr: PlayerRegistration = try await StoreCenter.main.service().post(playerRegistration) assert(pr.teamRegistration == playerRegistration.teamRegistration) @@ -264,6 +264,7 @@ final class ServerDataTests: XCTestCase { assert(pr.hasArrived == playerRegistration.hasArrived) assert(pr.captain == playerRegistration.captain) assert(pr.coach == playerRegistration.coach) + assert(pr.registeredOnline == playerRegistration.registeredOnline) } From d11af504b55b261251bcd68eeef2f3047d24d9f2 Mon Sep 17 00:00:00 2001 From: Raz Date: Wed, 15 Jan 2025 15:19:31 +0100 Subject: [PATCH 30/57] fixes and add legend / inscription guide --- PadelClub/Data/TeamRegistration.swift | 11 +++++++++-- PadelClub/Views/Team/EditingTeamView.swift | 2 +- .../Screen/InscriptionManagerView.swift | 18 ++++++++++++++++++ 3 files changed, 28 insertions(+), 3 deletions(-) diff --git a/PadelClub/Data/TeamRegistration.swift b/PadelClub/Data/TeamRegistration.swift index a24e355..cb14169 100644 --- a/PadelClub/Data/TeamRegistration.swift +++ b/PadelClub/Data/TeamRegistration.swift @@ -122,6 +122,7 @@ final class TeamRegistration: ModelObject, Storable { func isHere() -> Bool { let unsortedPlayers = unsortedPlayers() + if unsortedPlayers.isEmpty { return false } return unsortedPlayers.allSatisfy({ $0.hasArrived }) } @@ -197,7 +198,10 @@ final class TeamRegistration: ModelObject, Storable { } func isImported() -> Bool { - return unsortedPlayers().allSatisfy({ $0.isImported() }) + let unsortedPlayers = unsortedPlayers() + if unsortedPlayers.isEmpty { return false } + + return unsortedPlayers.allSatisfy({ $0.isImported() }) } func isWildCard() -> Bool { @@ -304,7 +308,10 @@ final class TeamRegistration: ModelObject, Storable { } func canPlay() -> Bool { - return matches().isEmpty == false || players().allSatisfy({ $0.hasPaid() || $0.hasArrived }) + let unsortedPlayers = unsortedPlayers() + if unsortedPlayers.isEmpty { return false } + + return matches().isEmpty == false || unsortedPlayers.allSatisfy({ $0.hasPaid() || $0.hasArrived }) } func availableForSeedPick() -> Bool { diff --git a/PadelClub/Views/Team/EditingTeamView.swift b/PadelClub/Views/Team/EditingTeamView.swift index 9c89875..60279e4 100644 --- a/PadelClub/Views/Team/EditingTeamView.swift +++ b/PadelClub/Views/Team/EditingTeamView.swift @@ -347,7 +347,7 @@ struct EditingTeamView: View { private var hasArrived: Binding { Binding { - team.unsortedPlayers().allSatisfy({ $0.hasArrived }) + team.isHere() } set: { hasArrived in team.unsortedPlayers().forEach { $0.hasArrived = hasArrived diff --git a/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift b/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift index 6942db4..f4117ea 100644 --- a/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift +++ b/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift @@ -533,6 +533,19 @@ struct InscriptionManagerView: View { } } + private func _fixModel() { + let players = tournament.players() + + players.forEach { player in + if player.source == .onlineRegistration { + player.source = .frenchFederation + player.registeredOnline = true + } + } + + try? tournamentStore.playerRegistrations.addOrUpdate(contentOfs: players) + } + private func _refreshList() async { if refreshInProgress { return } @@ -823,6 +836,11 @@ struct InscriptionManagerView: View { if let closedRegistrationDate = tournament.closedRegistrationDate { CloseDatePicker(closedRegistrationDate: closedRegistrationDate) } + + Button("bug fix") { + _fixModel() + } + if tournament.enableOnlineRegistration { Button { Task { From f2ce6c983a6a63df72e3ba89e052cf3a793ffe03 Mon Sep 17 00:00:00 2001 From: Laurent Date: Thu, 16 Jan 2025 10:34:31 +0100 Subject: [PATCH 31/57] fix purchase update --- PadelClub/Views/Tournament/Subscription/Guard.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/PadelClub/Views/Tournament/Subscription/Guard.swift b/PadelClub/Views/Tournament/Subscription/Guard.swift index e2a03ec..afbc3b6 100644 --- a/PadelClub/Views/Tournament/Subscription/Guard.swift +++ b/PadelClub/Views/Tournament/Subscription/Guard.swift @@ -143,6 +143,7 @@ import LeStorage purchase.revocationDate = transaction.revocationDate purchase.expirationDate = transaction.expirationDate purchase.purchaseDate = transaction.purchaseDate + purchase.productId = transaction.productID try purchases.addOrUpdate(instance: purchase) } else { let purchase: Purchase = try transaction.purchase() From 68a99a495b555e07269ebc5d21f1fd0b90feb0e5 Mon Sep 17 00:00:00 2001 From: Laurent Date: Thu, 16 Jan 2025 11:08:45 +0100 Subject: [PATCH 32/57] change data test user --- PadelClubTests/UserDataTests.swift | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/PadelClubTests/UserDataTests.swift b/PadelClubTests/UserDataTests.swift index 4980845..c7f7fd6 100644 --- a/PadelClubTests/UserDataTests.swift +++ b/PadelClubTests/UserDataTests.swift @@ -11,7 +11,7 @@ import LeStorage final class UserDataTests: XCTestCase { - let username: String = "test" + let username: String = "UserDataTests" let password: String = "MyPass1234--" override func setUpWithError() throws { @@ -24,7 +24,7 @@ final class UserDataTests: XCTestCase { func testUserCreation() async throws { - let userCreationForm = UserCreationForm(user: User.placeHolder(), username: self.username, password: self.password, firstName: "jean", lastName: "coco", email: "test@lolomo.com", phone: "0123", country: "France") + let userCreationForm = UserCreationForm(user: User.placeHolder(), username: self.username, password: self.password, firstName: "jean", lastName: "coco", email: "UserDataTests@lolomo.net", phone: "0123", country: "France") let user: User = try await StoreCenter.main.service().createAccount(user: userCreationForm) assert(user.username == userCreationForm.username) @@ -41,6 +41,10 @@ final class UserDataTests: XCTestCase { return user } + func testLogin() async throws { + let _ = try await self.login() + } + func testUserUpdate() async throws { let user = try await self.login() From 06662092ebb600abf3806d5df3157cb6bbc5ffb4 Mon Sep 17 00:00:00 2001 From: Laurent Date: Thu, 16 Jan 2025 11:11:03 +0100 Subject: [PATCH 33/57] fix test --- PadelClubTests/ServerDataTests.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/PadelClubTests/ServerDataTests.swift b/PadelClubTests/ServerDataTests.swift index 15887ad..fd773b8 100644 --- a/PadelClubTests/ServerDataTests.swift +++ b/PadelClubTests/ServerDataTests.swift @@ -11,7 +11,7 @@ import LeStorage final class ServerDataTests: XCTestCase { - let username: String = "test" + let username: String = "UserDataTests" let password: String = "MyPass1234--" override func setUpWithError() throws { @@ -68,7 +68,7 @@ final class ServerDataTests: XCTestCase { func testLogin() async throws { let user: User = try await StoreCenter.main.service().login(username: self.username, password: self.password) - assert(user.username == "test") + assert(user.username == self.username) } func testEvent() async throws { From 15870383e048d5a49af3c34224cbb9bf9179fd89 Mon Sep 17 00:00:00 2001 From: Raz Date: Fri, 17 Jan 2025 19:02:06 +0100 Subject: [PATCH 34/57] fix stuff --- PadelClub/Data/PlayerRegistration.swift | 10 +- PadelClub/Data/Tournament.swift | 4 + PadelClub/InscriptionLegendView.swift | 24 ++--- PadelClub/RegistrationInfoSheetView.swift | 8 +- PadelClub/Views/Player/PlayerDetailView.swift | 18 +++- PadelClub/Views/Shared/DateMenuView.swift | 2 +- .../Shared/SelectablePlayerListView.swift | 97 +------------------ PadelClub/Views/Team/EditingTeamView.swift | 4 +- PadelClub/Views/Team/TeamDetailView.swift | 10 +- PadelClub/Views/Team/TeamRowView.swift | 5 +- .../TournamentCategorySettingsView.swift | 3 + .../TournamentGeneralSettingsView.swift | 38 ++++---- .../Screen/InscriptionManagerView.swift | 36 +++---- .../Screen/RegistrationSetupView.swift | 4 +- .../Views/Tournament/TournamentView.swift | 4 +- 15 files changed, 101 insertions(+), 166 deletions(-) diff --git a/PadelClub/Data/PlayerRegistration.swift b/PadelClub/Data/PlayerRegistration.swift index 9638cc2..bd93be7 100644 --- a/PadelClub/Data/PlayerRegistration.swift +++ b/PadelClub/Data/PlayerRegistration.swift @@ -45,11 +45,11 @@ final class PlayerRegistration: ModelObject, Storable { func localizedSourceLabel() -> String { switch source { case .frenchFederation, .onlineRegistration: - return "Via la base fédérale" + return "base fédérale" case .beachPadel: - return "Via le fichier beach-padel" + return "beach-padel" case nil: - return "Manuellement" + return "créé par vous-même" } } @@ -233,6 +233,10 @@ final class PlayerRegistration: ModelObject, Storable { source == .beachPadel } + func unrankedOrUnknown() -> Bool { + source == nil + } + func isValidLicenseNumber(year: Int) -> Bool { guard let licenceId else { return false } guard licenceId.isLicenseNumber else { return false } diff --git a/PadelClub/Data/Tournament.swift b/PadelClub/Data/Tournament.swift index a347ea0..b5daf84 100644 --- a/PadelClub/Data/Tournament.swift +++ b/PadelClub/Data/Tournament.swift @@ -2165,6 +2165,10 @@ defer { } } + func onlineRegistrationCanBeEnabled() -> Bool { + isAnimation() == false + } + func roundSmartMatchFormat(_ roundIndex: Int) -> MatchFormat { let format = tournamentLevel.federalFormatForBracketRound(roundIndex) if tournamentLevel == .p25 { return .superTie } diff --git a/PadelClub/InscriptionLegendView.swift b/PadelClub/InscriptionLegendView.swift index ed1db83..031e206 100644 --- a/PadelClub/InscriptionLegendView.swift +++ b/PadelClub/InscriptionLegendView.swift @@ -13,38 +13,38 @@ struct InscriptionLegendView: View { var body: some View { NavigationView { List { + Section { + Label("Inscrit en ligne", systemImage: "circle.fill").foregroundStyle(.green) + } footer: { + Text("Icône indiquant que le joueur s'est inscrit en ligne.") + } + Section { ForEach(RoundRule.colors.prefix(6).indices, id: \.self) { colorIndex in Text("Équipe placée en \(RoundRule.roundName(fromRoundIndex: colorIndex))") - .listRowView(isActive: true, color: Color(uiColor: .init(fromHex: RoundRule.colors[colorIndex])), hideColorVariation: true, alignment: .trailing) + .listRowView(isActive: true, color: Color(uiColor: .init(fromHex: RoundRule.colors[colorIndex])), hideColorVariation: true, alignment: .leading) } } Section { Text("Équipe placée en poule") - .listRowView(isActive: true, color: .blue, hideColorVariation: true, alignment: .trailing) + .listRowView(isActive: true, color: .blue, hideColorVariation: true, alignment: .leading) } Section { Text("Équipe estimée en tableau") - .listRowView(isActive: true, color: .mint, hideColorVariation: true, alignment: .trailing) + .listRowView(isActive: true, color: .mint, hideColorVariation: true, alignment: .leading) Text("Équipe estimée en poule") - .listRowView(isActive: true, color: .cyan, hideColorVariation: true, alignment: .trailing) + .listRowView(isActive: true, color: .cyan, hideColorVariation: true, alignment: .leading) } Section { Text("Équipe en liste d'attente") - .listRowView(isActive: true, color: .gray, hideColorVariation: true, alignment: .trailing) + .listRowView(isActive: true, color: .gray, hideColorVariation: true, alignment: .leading) Text("Équipe forfaite") - .listRowView(isActive: true, color: .logoRed, hideColorVariation: true, alignment: .trailing) - } - - Section { - Label("Inscrit en ligne", systemImage: "person.badge.shield.checkmark.fill") - } footer: { - Text("Icône indiquant que le joueur s'est inscrit en ligne.") + .listRowView(isActive: true, color: .logoRed, hideColorVariation: true, alignment: .leading) } Section { diff --git a/PadelClub/RegistrationInfoSheetView.swift b/PadelClub/RegistrationInfoSheetView.swift index efbf3ac..a6dd9a4 100644 --- a/PadelClub/RegistrationInfoSheetView.swift +++ b/PadelClub/RegistrationInfoSheetView.swift @@ -45,12 +45,6 @@ struct RegistrationInfoSheetView: View { NavigationView { ScrollView { VStack(alignment: .leading, spacing: 20) { - // Title - Text("Inscriptions en ligne") - .font(.title) - .fontWeight(.bold) - .padding(.bottom, 5) - // Content sections ForEach(registrationInfoText.components(separatedBy: "\n\n"), id: \.self) { section in if !section.isEmpty { @@ -84,6 +78,8 @@ struct RegistrationInfoSheetView: View { .navigationBarItems(trailing: Button("Fermer") { dismiss() }) + .toolbarBackground(.visible, for: .navigationBar) + .navigationTitle("Inscription en ligne") } } } diff --git a/PadelClub/Views/Player/PlayerDetailView.swift b/PadelClub/Views/Player/PlayerDetailView.swift index 8a1454e..52ebb05 100644 --- a/PadelClub/Views/Player/PlayerDetailView.swift +++ b/PadelClub/Views/Player/PlayerDetailView.swift @@ -32,6 +32,22 @@ struct PlayerDetailView: View { var body: some View { Form { + Section { + Text(player.localizedSourceLabel().firstCapitalized) + } header: { + Text("Source des informations") + } + + if tournament.enableOnlineRegistration { + Section { + LabeledContent { + Text(player.registeredOnline ? "Oui" : "Non") + } label: { + Text("Inscription en ligne") + } + } + } + Section { Toggle("Joueur sur place", isOn: $player.hasArrived) Toggle("Capitaine", isOn: $player.captain) @@ -70,8 +86,6 @@ struct PlayerDetailView: View { if let birthdate = player.birthdate { Text(birthdate) } - } footer: { - Text(player.localizedSourceLabel()) } Section { diff --git a/PadelClub/Views/Shared/DateMenuView.swift b/PadelClub/Views/Shared/DateMenuView.swift index cfcce39..5e3ed15 100644 --- a/PadelClub/Views/Shared/DateMenuView.swift +++ b/PadelClub/Views/Shared/DateMenuView.swift @@ -26,7 +26,7 @@ struct DateMenuView: View { Button("24h après") { adjustDate(byDays: 1) } Button("48h après") { adjustDate(byDays: 2) } } label: { - Text("Ajuster la date") + Text("Ajuster") .underline() } } diff --git a/PadelClub/Views/Shared/SelectablePlayerListView.swift b/PadelClub/Views/Shared/SelectablePlayerListView.swift index c5e7f4e..36feb3a 100644 --- a/PadelClub/Views/Shared/SelectablePlayerListView.swift +++ b/PadelClub/Views/Shared/SelectablePlayerListView.swift @@ -379,102 +379,7 @@ struct MySearchView: View { let array = Array(searchViewModel.selectedPlayers) Section { ForEach(array) { player in - let index : Int? = nil - VStack(alignment: .leading) { - HStack { - if player.isAnonymous() { - Text("Joueur Anonyme") - } else { - Text(player.getLastName().capitalized) - Text(player.getFirstName().capitalized) - } - if index == nil { - Text(player.male ? "♂︎" : "♀︎") - } - Spacer() - if let index { - HStack(alignment: .top, spacing: 0) { - Text(index.formatted()) - .foregroundStyle(.secondary) - .font(.title3) - Text(index.ordinalFormattedSuffix()) - .foregroundStyle(.secondary) - .font(.caption) - } - } - } - .font(.title3) - .lineLimit(1) - HStack { - HStack(alignment: .top, spacing: 0) { - Text(player.formattedRank()).italic(player.isAssimilated) - .font(.title3) - .background { - if player.isNotFromCurrentDate() { - UnderlineView() - } - } - if let rank = player.getRank() { - Text(rank.ordinalFormattedSuffix()).italic(player.isAssimilated) - .font(.caption) - } - } - - if showProgression, player.getProgression() != 0 { - HStack(alignment: .top, spacing: 2) { - Text("(") - Text(player.getProgression().formatted(.number.sign(strategy: .always()))) - .foregroundStyle(player.getProgressionColor(progression: player.getProgression())) - Text(")") - }.font(.title3) - } - - if let pts = player.getPoints(), pts > 0 { - HStack(alignment: .lastTextBaseline, spacing: 0) { - Text(pts.formatted()).font(.title3) - Text(" pts").font(.caption) - } - } - - if let tournamentPlayed = player.tournamentPlayed, tournamentPlayed > 0 { - HStack(alignment: .lastTextBaseline, spacing: 0) { - Text(tournamentPlayed.formatted()).font(.title3) - Text(" tournoi" + tournamentPlayed.pluralSuffix).font(.caption) - } - } - } - .lineLimit(1) - .truncationMode(.tail) - - if showFemaleInMaleAssimilation, let assimilatedAsMaleRank = player.getAssimilatedAsMaleRank() { - HStack(alignment: .top, spacing: 2) { - Text("(") - Text(assimilatedAsMaleRank.formatted()) - VStack(alignment: .leading, spacing: 0) { - Text("équivalence") - Text("messieurs") - } - .font(.caption) - Text(")").font(.title3) - } - } - - HStack { - Text(player.formattedLicense()) - if let computedAge = player.computedAge { - Text(computedAge.formatted() + " ans") - } - } - .font(.caption) - if let clubName = player.clubName { - Text(clubName) - .font(.caption) - } - if let ligueName = player.ligueName { - Text(ligueName) - .font(.caption) - } - } + ImportedPlayerView(player: player, index: nil, showFemaleInMaleAssimilation: searchViewModel.showFemaleInMaleAssimilation, showProgression: true) } .onDelete { indexSet in for index in indexSet { diff --git a/PadelClub/Views/Team/EditingTeamView.swift b/PadelClub/Views/Team/EditingTeamView.swift index 60279e4..e942567 100644 --- a/PadelClub/Views/Team/EditingTeamView.swift +++ b/PadelClub/Views/Team/EditingTeamView.swift @@ -65,15 +65,13 @@ struct EditingTeamView: View { var body: some View { List { Section { - RowButtonView("Modifier la composition de l'équipe", role: team.hasRegisteredOnline() ? .destructive : .none) { + RowButtonView("Modifier la composition de l'équipe", role: team.hasRegisteredOnline() ? .destructive : .none, confirmationMessage: "Vous êtes sur le point de modifier une équipe qui s'est inscrite en ligne.") { editedTeam = team } - TeamDetailView(team: team) } header: { if team.hasRegisteredOnline() { Text("Inscription en ligne") - .foregroundStyle(.master) } else { Text("Inscription par vous-même") } diff --git a/PadelClub/Views/Team/TeamDetailView.swift b/PadelClub/Views/Team/TeamDetailView.swift index 889341c..5c22e95 100644 --- a/PadelClub/Views/Team/TeamDetailView.swift +++ b/PadelClub/Views/Team/TeamDetailView.swift @@ -22,7 +22,15 @@ struct TeamDetailView: View { PlayerDetailView(player: player) .environment(tournament) } label: { - PlayerView(player: player) + VStack(alignment: .leading, spacing: 0) { + HStack { + Text("inscrit en ligne") + Spacer() + Text(player.localizedSourceLabel()) + } + .font(.caption).foregroundStyle(.secondary) + PlayerView(player: player) + } } } } diff --git a/PadelClub/Views/Team/TeamRowView.swift b/PadelClub/Views/Team/TeamRowView.swift index c26766d..966f903 100644 --- a/PadelClub/Views/Team/TeamRowView.swift +++ b/PadelClub/Views/Team/TeamRowView.swift @@ -118,9 +118,10 @@ struct TeamRowView: View { var body: some View { ForEach(team.players()) { player in - HStack { + HStack(spacing: 4) { if player.registeredOnline { - Image(systemName: "person.badge.shield.checkmark.fill") + Image(systemName: "circle.fill").foregroundStyle(.green) + .font(.system(size: 8)) } Text(player.playerLabel()).lineLimit(1).truncationMode(.tail) } diff --git a/PadelClub/Views/Tournament/Screen/Components/TournamentCategorySettingsView.swift b/PadelClub/Views/Tournament/Screen/Components/TournamentCategorySettingsView.swift index 27856e1..4ae0340 100644 --- a/PadelClub/Views/Tournament/Screen/Components/TournamentCategorySettingsView.swift +++ b/PadelClub/Views/Tournament/Screen/Components/TournamentCategorySettingsView.swift @@ -36,6 +36,9 @@ struct TournamentCategorySettingsView: View { private func _save() { do { + if tournament.onlineRegistrationCanBeEnabled() == false, tournament.enableOnlineRegistration { + tournament.enableOnlineRegistration = false + } try dataStore.tournaments.addOrUpdate(instance: tournament) } catch { Logger.error(error) diff --git a/PadelClub/Views/Tournament/Screen/Components/TournamentGeneralSettingsView.swift b/PadelClub/Views/Tournament/Screen/Components/TournamentGeneralSettingsView.swift index d000292..803dbc5 100644 --- a/PadelClub/Views/Tournament/Screen/Components/TournamentGeneralSettingsView.swift +++ b/PadelClub/Views/Tournament/Screen/Components/TournamentGeneralSettingsView.swift @@ -48,27 +48,29 @@ struct TournamentGeneralSettingsView: View { Text("Si vous souhaitez que Padel Club vous aide à suivre les encaissements, indiquer un prix d'inscription. Sinon Padel Club vous aidera à suivre simplement l'arrivée et la présence des joueurs.") } - Section { - NavigationLink { - RegistrationSetupView(tournament: tournament) - } label: { - LabeledContent { - if tournament.enableOnlineRegistration { - Text("activée").foregroundStyle(.green) - .font(.headline) - } else { - Text("désactivée").foregroundStyle(.logoRed) - .font(.headline) - } + if tournament.onlineRegistrationCanBeEnabled() { + Section { + NavigationLink { + RegistrationSetupView(tournament: tournament) } label: { - Text("Accéder aux paramètres") - Text(tournament.getOnlineRegistrationStatus().statusLocalized()) + LabeledContent { + if tournament.enableOnlineRegistration { + Text("activée").foregroundStyle(.green) + .font(.headline) + } else { + Text("désactivée").foregroundStyle(.logoRed) + .font(.headline) + } + } label: { + Text("Accéder aux paramètres") + Text(tournament.getOnlineRegistrationStatus().statusLocalized()) + } } + } header: { + Text("Inscription en ligne") + } footer: { + Text("Paramétrez les possibilités d'inscription en ligne à votre tournoi via Padel Club") } - } header: { - Text("Inscription en ligne") - } footer: { - Text("Paramétrez les possibilités d'inscription en ligne à votre tournoi via Padel Club") } Section { diff --git a/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift b/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift index f4117ea..1a0fa82 100644 --- a/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift +++ b/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift @@ -250,7 +250,7 @@ struct InscriptionManagerView: View { RowButtonView("Rafraîchir la liste") { await _refreshList() } - } else { + } else if tournament.onlineRegistrationCanBeEnabled() { RowButtonView("Inscription en ligne") { navigation.path.append(Screen.settings) } @@ -533,19 +533,19 @@ struct InscriptionManagerView: View { } } - private func _fixModel() { - let players = tournament.players() - - players.forEach { player in - if player.source == .onlineRegistration { - player.source = .frenchFederation - player.registeredOnline = true - } - } - - try? tournamentStore.playerRegistrations.addOrUpdate(contentOfs: players) - } - +// private func _fixModel() { +// let players = tournament.players() +// +// players.forEach { player in +// if player.source == .onlineRegistration { +// player.source = .frenchFederation +// player.registeredOnline = true +// } +// } +// +// try? tournamentStore.playerRegistrations.addOrUpdate(contentOfs: players) +// } +// private func _refreshList() async { if refreshInProgress { return } @@ -629,7 +629,7 @@ struct InscriptionManagerView: View { _teamDeleteButtonView(team) } } - .listRowView(isActive: true, color: team.initialRoundColor() ?? tournament.cutLabelColor(index: teamIndex, teamCount: filterMode == .waiting ? 0 : selectedSortedTeams.count), hideColorVariation: true, backgroundColor: color, alignment: .trailing) + .listRowView(isActive: true, color: team.initialRoundColor() ?? tournament.cutLabelColor(index: teamIndex, teamCount: filterMode == .waiting ? 0 : selectedSortedTeams.count), hideColorVariation: true, backgroundColor: color, alignment: .leading) } } header: { if filterMode == .all && walkoutTeams.isEmpty == false { @@ -837,9 +837,9 @@ struct InscriptionManagerView: View { CloseDatePicker(closedRegistrationDate: closedRegistrationDate) } - Button("bug fix") { - _fixModel() - } +// Button("bug fix") { +// _fixModel() +// } if tournament.enableOnlineRegistration { Button { diff --git a/PadelClub/Views/Tournament/Screen/RegistrationSetupView.swift b/PadelClub/Views/Tournament/Screen/RegistrationSetupView.swift index 73bbbe7..ea4d552 100644 --- a/PadelClub/Views/Tournament/Screen/RegistrationSetupView.swift +++ b/PadelClub/Views/Tournament/Screen/RegistrationSetupView.swift @@ -114,7 +114,7 @@ struct RegistrationSetupView: View { } if openingRegistrationDateEnabled { - DatePicker(selection: $openingRegistrationDate, in: tournament.creationDate.truncateMinutesAndSeconds()...tournament.startDate.truncateMinutesAndSeconds()) { + DatePicker(selection: $openingRegistrationDate) { DateMenuView(date: $openingRegistrationDate) } } @@ -130,7 +130,7 @@ struct RegistrationSetupView: View { } if registrationDateLimitEnabled { - DatePicker(selection: $registrationDateLimit, in: tournament.creationDate.truncateMinutesAndSeconds()...tournament.startDate.truncateMinutesAndSeconds()) { + DatePicker(selection: $registrationDateLimit) { DateMenuView(date: $registrationDateLimit) } } diff --git a/PadelClub/Views/Tournament/TournamentView.swift b/PadelClub/Views/Tournament/TournamentView.swift index 1e2a8ea..2ee9907 100644 --- a/PadelClub/Views/Tournament/TournamentView.swift +++ b/PadelClub/Views/Tournament/TournamentView.swift @@ -63,7 +63,7 @@ struct TournamentView: View { } } case .initial: - if tournament.enableOnlineRegistration == false { + if tournament.enableOnlineRegistration == false, tournament.onlineRegistrationCanBeEnabled() { TipView(onlineRegistrationTip) { action in if let actionKey = OnlineRegistrationTip.ActionKey(rawValue: action.id) { switch actionKey { @@ -84,7 +84,7 @@ struct TournamentView: View { } TournamentInitView(tournament: tournament) case .build: - if tournament.enableOnlineRegistration == false { + if tournament.enableOnlineRegistration == false, tournament.onlineRegistrationCanBeEnabled() { TipView(onlineRegistrationTip) { action in if let actionKey = OnlineRegistrationTip.ActionKey(rawValue: action.id) { switch actionKey { From 993f9193daa4f769fbfe6506912d3b0500c3c7ce Mon Sep 17 00:00:00 2001 From: Raz Date: Tue, 21 Jan 2025 14:13:19 +0100 Subject: [PATCH 35/57] v1.1.0 --- PadelClub.xcodeproj/project.pbxproj | 4 ++-- PadelClub/Data/Tournament.swift | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/PadelClub.xcodeproj/project.pbxproj b/PadelClub.xcodeproj/project.pbxproj index 6fd837e..36744a9 100644 --- a/PadelClub.xcodeproj/project.pbxproj +++ b/PadelClub.xcodeproj/project.pbxproj @@ -3329,7 +3329,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.44; + MARKETING_VERSION = 1.1.0; PRODUCT_BUNDLE_IDENTIFIER = app.padelclub; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -3376,7 +3376,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.44; + MARKETING_VERSION = 1.1.0; PRODUCT_BUNDLE_IDENTIFIER = app.padelclub; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; diff --git a/PadelClub/Data/Tournament.swift b/PadelClub/Data/Tournament.swift index b5daf84..cafe5b5 100644 --- a/PadelClub/Data/Tournament.swift +++ b/PadelClub/Data/Tournament.swift @@ -2160,7 +2160,7 @@ defer { matchFormat = roundSmartMatchFormat(5) entryFee = tournamentLevel.entryFee if event?.tenupId != nil { - enableOnlineRegistration = true + //enableOnlineRegistration = true registrationDateLimit = deadline(for: .inscription) } } From 2447930e60b1a0778cb0eee8bfb06d3b79cd5a5e Mon Sep 17 00:00:00 2001 From: Raz Date: Tue, 21 Jan 2025 19:30:09 +0100 Subject: [PATCH 36/57] fix player import stuff --- PadelClub/Data/TeamRegistration.swift | 24 ++++++++++++++++++++++- PadelClub/Views/Team/TeamDetailView.swift | 4 +++- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/PadelClub/Data/TeamRegistration.swift b/PadelClub/Data/TeamRegistration.swift index cb14169..5a17734 100644 --- a/PadelClub/Data/TeamRegistration.swift +++ b/PadelClub/Data/TeamRegistration.swift @@ -433,6 +433,22 @@ final class TeamRegistration: ModelObject, Storable { func updatePlayers(_ players: Set, inTournamentCategory tournamentCategory: TournamentCategory) { let previousPlayers = Set(unsortedPlayers()) + + players.forEach { player in + previousPlayers.forEach { oldPlayer in + if player.licenceId?.strippedLicense == oldPlayer.licenceId?.strippedLicense, player.licenceId?.strippedLicense != nil { + player.registeredOnline = oldPlayer.registeredOnline + player.coach = oldPlayer.coach + player.tournamentPlayed = oldPlayer.tournamentPlayed + player.points = oldPlayer.points + player.captain = oldPlayer.captain + player.assimilation = oldPlayer.assimilation + player.ligueName = oldPlayer.ligueName + } + } + } + + let playersToRemove = previousPlayers.subtracting(players) do { try self.tournamentStore.playerRegistrations.delete(contentOfs: playersToRemove) @@ -443,7 +459,13 @@ final class TeamRegistration: ModelObject, Storable { players.forEach { player in player.teamRegistration = id - } + } + +// do { +// try self.tournamentStore.playerRegistrations.addOrUpdate(contentOfs: players) +// } catch { +// Logger.error(error) +// } } typealias TeamRange = (left: TeamRegistration?, right: TeamRegistration?) diff --git a/PadelClub/Views/Team/TeamDetailView.swift b/PadelClub/Views/Team/TeamDetailView.swift index 5c22e95..969731d 100644 --- a/PadelClub/Views/Team/TeamDetailView.swift +++ b/PadelClub/Views/Team/TeamDetailView.swift @@ -24,7 +24,9 @@ struct TeamDetailView: View { } label: { VStack(alignment: .leading, spacing: 0) { HStack { - Text("inscrit en ligne") + if player.registeredOnline { + Text("inscrit en ligne") + } Spacer() Text(player.localizedSourceLabel()) } From 4893f8bf273426815de9b92f1dbb4481bdd1c98e Mon Sep 17 00:00:00 2001 From: Raz Date: Wed, 22 Jan 2025 09:05:05 +0100 Subject: [PATCH 37/57] v1.1.0 b2 --- PadelClub.xcodeproj/project.pbxproj | 749 +++++++++++++++++++++++++++- 1 file changed, 747 insertions(+), 2 deletions(-) diff --git a/PadelClub.xcodeproj/project.pbxproj b/PadelClub.xcodeproj/project.pbxproj index 36744a9..2aa8816 100644 --- a/PadelClub.xcodeproj/project.pbxproj +++ b/PadelClub.xcodeproj/project.pbxproj @@ -124,6 +124,290 @@ FF2B51612C7E302C00FFF126 /* local.sqlite in Resources */ = {isa = PBXBuildFile; fileRef = FF2B51602C7E302C00FFF126 /* local.sqlite */; }; FF2B6F5E2C036A1500835EE7 /* EventLinksView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF2B6F5D2C036A1400835EE7 /* EventLinksView.swift */; }; FF2EFBF02BDE295E0049CE3B /* SendToAllView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF2EFBEF2BDE295E0049CE3B /* SendToAllView.swift */; }; + FF3366782D403D1F0012D1B4 /* UserCreationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47D862B7BA36D00ADC637 /* UserCreationView.swift */; }; + FF3366792D403D1F0012D1B4 /* TabDestination.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF7091652B90F0B000AB08DA /* TabDestination.swift */; }; + FF33667A2D403D1F0012D1B4 /* CashierView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF9267F72BCE78C70080F940 /* CashierView.swift */; }; + FF33667B2D403D1F0012D1B4 /* Event.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8F263E2BAD7D5C00650388 /* Event.swift */; }; + FF33667C2D403D1F0012D1B4 /* DatePickingViewWithFormat.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF77CE552CCCD1EB00CBCBB4 /* DatePickingViewWithFormat.swift */; }; + FF33667D2D403D1F0012D1B4 /* PlayerHolder.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5D30522BD94E2E00F2B93D /* PlayerHolder.swift */; }; + FF33667E2D403D1F0012D1B4 /* LoserRoundStepScheduleEditorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF11628B2BD05267000C4809 /* LoserRoundStepScheduleEditorView.swift */; }; + FF33667F2D403D1F0012D1B4 /* ClubCourtSetupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF53FBB72BFB302B0051D4C3 /* ClubCourtSetupView.swift */; }; + FF3366802D403D1F0012D1B4 /* Patcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B3A1542C2581DA0078EAA8 /* Patcher.swift */; }; + FF3366812D403D1F0012D1B4 /* FileImportView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF089EBE2BB0B14600F0AEC7 /* FileImportView.swift */; }; + FF3366822D403D1F0012D1B4 /* StepperView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47D9E2B7D0BCE00ADC637 /* StepperView.swift */; }; + FF3366832D403D1F0012D1B4 /* RoundsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFC83D4E2BB807D100750834 /* RoundsView.swift */; }; + FF3366842D403D1F0012D1B4 /* FederalTournament.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1CBC182BB53D1F0036DAAB /* FederalTournament.swift */; }; + FF3366852D403D1F0012D1B4 /* TournamentInitView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8F26402BADFC8700650388 /* TournamentInitView.swift */; }; + FF3366862D403D1F0012D1B4 /* SubscriptionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47D892B7BBB6500ADC637 /* SubscriptionView.swift */; }; + FF3366872D403D1F0012D1B4 /* TournamentLookUpView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFD655D72C8DE27400E5B35E /* TournamentLookUpView.swift */; }; + FF3366882D403D1F0012D1B4 /* ClubsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1DC5562BAB3AED00FD8220 /* ClubsView.swift */; }; + FF3366892D403D1F0012D1B4 /* ImagePickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFE103112C366E5900684FC9 /* ImagePickerView.swift */; }; + FF33668A2D403D1F0012D1B4 /* MatchTypeSelectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8F264E2BAE0B9600650388 /* MatchTypeSelectionView.swift */; }; + FF33668B2D403D1F0012D1B4 /* MatchSetupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967D052BAF3C4200A9A3BD /* MatchSetupView.swift */; }; + FF33668C2D403D1F0012D1B4 /* NetworkManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF4AB6B42B9248200002987F /* NetworkManager.swift */; }; + FF33668D2D403D1F0012D1B4 /* EventLinksView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF2B6F5D2C036A1400835EE7 /* EventLinksView.swift */; }; + FF33668E2D403D1F0012D1B4 /* SeedInterval.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFB9C8742BBADDF700A0EF4F /* SeedInterval.swift */; }; + FF33668F2D403D1F0012D1B4 /* TournamentClubSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF025AE02BD0EB9000A86CF8 /* TournamentClubSettingsView.swift */; }; + FF3366902D403D1F0012D1B4 /* GroupStageTeamView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFBF065B2BBD2657009D6715 /* GroupStageTeamView.swift */; }; + FF3366912D403D1F0012D1B4 /* RoundSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5DA1922BB9279B00A33061 /* RoundSettingsView.swift */; }; + FF3366922D403D1F0012D1B4 /* MatchViewStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFBE62042CE9DA0900815D33 /* MatchViewStyle.swift */; }; + FF3366932D403D1F0012D1B4 /* SupportButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFE2D2E12C231BEE00D0C7BE /* SupportButtonView.swift */; }; + FF3366942D403D1F0012D1B4 /* TournamentBroadcastRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFB1C98A2C10255100B154A7 /* TournamentBroadcastRowView.swift */; }; + FF3366952D403D1F0012D1B4 /* TeamWeightView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF025ADE2BD0CE0A00A86CF8 /* TeamWeightView.swift */; }; + FF3366962D403D1F0012D1B4 /* SeedsCallingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF9268002BCE94920080F940 /* SeedsCallingView.swift */; }; + FF3366972D403D1F0012D1B4 /* CallView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF9268082BCEDC2C0080F940 /* CallView.swift */; }; + FF3366982D403D1F0012D1B4 /* Color+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5D0D732BB41DF8005CB568 /* Color+Extensions.swift */; }; + FF3366992D403D1F0012D1B4 /* EditSharingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFE1030F2C366DCD00684FC9 /* EditSharingView.swift */; }; + FF33669A2D403D1F0012D1B4 /* TournamentCellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF7091672B90F79F00AB08DA /* TournamentCellView.swift */; }; + FF33669B2D403D1F0012D1B4 /* Sequence+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF6EC9032B9479F500EA7F5A /* Sequence+Extensions.swift */; }; + FF33669C2D403D1F0012D1B4 /* CashierDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF9267F92BCE78EB0080F940 /* CashierDetailView.swift */; }; + FF33669D2D403D1F0012D1B4 /* EventClubSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFE103072C353B7600684FC9 /* EventClubSettingsView.swift */; }; + FF33669E2D403D1F0012D1B4 /* AccountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47DB22B86387500ADC637 /* AccountView.swift */; }; + FF33669F2D403D1F0012D1B4 /* PlayersWithoutContactView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFCEDA4B2C2C08EA00F8C0F2 /* PlayersWithoutContactView.swift */; }; + FF3366A02D403D1F0012D1B4 /* FollowUpMatchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF17CA4C2CB9243E003C7323 /* FollowUpMatchView.swift */; }; + FF3366A12D403D1F0012D1B4 /* TeamsCallingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFCD16B22C3E5E590092707B /* TeamsCallingView.swift */; }; + FF3366A22D403D1F0012D1B4 /* Calendar+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1CBC1C2BB53DC10036DAAB /* Calendar+Extensions.swift */; }; + FF3366A32D403D1F0012D1B4 /* TeamScore.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967CEF2BAECC0A00A9A3BD /* TeamScore.swift */; }; + FF3366A42D403D1F0012D1B4 /* EditablePlayerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1162822BCFBE4E000C4809 /* EditablePlayerView.swift */; }; + FF3366A52D403D1F0012D1B4 /* PlayerDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1162842BD00279000C4809 /* PlayerDetailView.swift */; }; + FF3366A62D403D1F0012D1B4 /* UmpireStatisticView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFA252AC2CDB734A0074E63F /* UmpireStatisticView.swift */; }; + FF3366A72D403D1F0012D1B4 /* ListRowViewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5D0D752BB428B2005CB568 /* ListRowViewModifier.swift */; }; + FF3366A82D403D1F0012D1B4 /* PresentationContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF6EC8FF2B94794700EA7F5A /* PresentationContext.swift */; }; + FF3366A92D403D1F0012D1B4 /* AppSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFDB1C6C2BB2A02000F1E467 /* AppSettings.swift */; }; + FF3366AA2D403D1F0012D1B4 /* SwiftParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF0EC51D2BB16F680056B6D1 /* SwiftParser.swift */; }; + FF3366AB2D403D1F0012D1B4 /* ChangePasswordView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47DA82B85F82100ADC637 /* ChangePasswordView.swift */; }; + FF3366AC2D403D1F0012D1B4 /* TournamentSubscriptionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8044AB2C8F676D00A49A52 /* TournamentSubscriptionView.swift */; }; + FF3366AD2D403D1F0012D1B4 /* RoundView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFC83D502BB8087E00750834 /* RoundView.swift */; }; + FF3366AE2D403D1F0012D1B4 /* RowButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF6EC8F62B94773100EA7F5A /* RowButtonView.swift */; }; + FF3366AF2D403D1F0012D1B4 /* SendToAllView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF2EFBEF2BDE295E0049CE3B /* SendToAllView.swift */; }; + FF3366B02D403D1F0012D1B4 /* EventCreationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8F263A2BAD528600650388 /* EventCreationView.swift */; }; + FF3366B12D403D1F0012D1B4 /* LocationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFC1E1072BAC29FC008D6F59 /* LocationManager.swift */; }; + FF3366B22D403D1F0012D1B4 /* CapsuleViewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C01D972C481C0C0059087C /* CapsuleViewModifier.swift */; }; + FF3366B32D403D1F0012D1B4 /* BroadcastView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF6087EB2BE26A2F004E1E47 /* BroadcastView.swift */; }; + FF3366B42D403D1F0012D1B4 /* SchedulerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF964542BC266CF00EEF017 /* SchedulerView.swift */; }; + FF3366B52D403D1F0012D1B4 /* PadelClubButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFA1B1282BB71773006CE248 /* PadelClubButtonView.swift */; }; + FF3366B62D403D1F0012D1B4 /* TournamentSeedEditing.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5DA19A2BB9662200A33061 /* TournamentSeedEditing.swift */; }; + FF3366B72D403D1F0012D1B4 /* TournamentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF70916B2B91005400AB08DA /* TournamentView.swift */; }; + FF3366B82D403D1F0012D1B4 /* OngoingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5D30552BD95B1100F2B93D /* OngoingView.swift */; }; + FF3366B92D403D1F0012D1B4 /* CreateClubView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1DC5542BAB36DD00FD8220 /* CreateClubView.swift */; }; + FF3366BA2D403D1F0012D1B4 /* APICallsListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4607A7C2C04DDE2004CB781 /* APICallsListView.swift */; }; + FF3366BB2D403D1F0012D1B4 /* TeamRestingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF7DCD382CC330260041110C /* TeamRestingView.swift */; }; + FF3366BC2D403D1F0012D1B4 /* NetworkFederalService.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFC1E1092BAC2A77008D6F59 /* NetworkFederalService.swift */; }; + FF3366BD2D403D1F0012D1B4 /* DurationSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF025AEE2BD1AE9400A86CF8 /* DurationSettingsView.swift */; }; + FF3366BE2D403D1F0012D1B4 /* AppScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF025AEC2BD1513700A86CF8 /* AppScreen.swift */; }; + FF3366BF2D403D1F0012D1B4 /* CourtView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFC91B022BD85E2400B29808 /* CourtView.swift */; }; + FF3366C02D403D1F0012D1B4 /* PointSelectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFCFC00D2BBC3D4600B82851 /* PointSelectionView.swift */; }; + FF3366C12D403D1F0012D1B4 /* TeamRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF089EB52BB00A3800F0AEC7 /* TeamRowView.swift */; }; + FF3366C22D403D1F0012D1B4 /* ContactManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF92680A2BCEE3E10080F940 /* ContactManager.swift */; }; + FF3366C32D403D1F0012D1B4 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40CD2F22C412681000DBD9A /* AppDelegate.swift */; }; + FF3366C42D403D1F0012D1B4 /* SubscriptionInfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49EF0252BD80AE80077B5AA /* SubscriptionInfoView.swift */; }; + FF3366C52D403D1F0012D1B4 /* EditScoreView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFCFC0012BBC39A600B82851 /* EditScoreView.swift */; }; + FF3366C62D403D1F0012D1B4 /* TournamentOrganizerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF7091612B90F04300AB08DA /* TournamentOrganizerView.swift */; }; + FF3366C72D403D1F0012D1B4 /* NetworkMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF92680C2BCEE5EA0080F940 /* NetworkMonitor.swift */; }; + FF3366C82D403D1F0012D1B4 /* TournamentRunningView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967CF52BAED51600A9A3BD /* TournamentRunningView.swift */; }; + FF3366C92D403D1F0012D1B4 /* TournamentDatePickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8F264A2BAE0B4100650388 /* TournamentDatePickerView.swift */; }; + FF3366CA2D403D1F0012D1B4 /* CourtAvailabilitySettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF116E22BD2AF4800A33B06 /* CourtAvailabilitySettingsView.swift */; }; + FF3366CB2D403D1F0012D1B4 /* ConfirmButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFE8C2BF2C7601E80046B243 /* ConfirmButtonView.swift */; }; + FF3366CC2D403D1F0012D1B4 /* PasswordField.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4FC2E262C2AABC90021F3BF /* PasswordField.swift */; }; + FF3366CD2D403D1F0012D1B4 /* MatchRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967CFF2BAEEF6400A9A3BD /* MatchRowView.swift */; }; + FF3366CE2D403D1F0012D1B4 /* Locale+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44B79102BBDA63A00906534 /* Locale+Extensions.swift */; }; + FF3366CF2D403D1F0012D1B4 /* HtmlService.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1F4B732BFA00FC000B4573 /* HtmlService.swift */; }; + FF3366D02D403D1F0012D1B4 /* GroupStage.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967CE72BAEC70100A9A3BD /* GroupStage.swift */; }; + FF3366D12D403D1F0012D1B4 /* DrawLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF6761522CC77D1900CC9BF2 /* DrawLog.swift */; }; + FF3366D22D403D1F0012D1B4 /* TournamentCashierView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1162802BCF945C000C4809 /* TournamentCashierView.swift */; }; + FF3366D32D403D1F0012D1B4 /* StoreManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47D8D2B7BBBEC00ADC637 /* StoreManager.swift */; }; + FF3366D42D403D1F0012D1B4 /* SearchViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF4AB6BA2B9256D50002987F /* SearchViewModel.swift */; }; + FF3366D52D403D1F0012D1B4 /* PlayerRegistration.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967CF12BAECC0B00A9A3BD /* PlayerRegistration.swift */; }; + FF3366D62D403D1F0012D1B4 /* ImportedPlayerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF4AB6BE2B92577A0002987F /* ImportedPlayerView.swift */; }; + FF3366D72D403D1F0012D1B4 /* EditingTeamView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1162862BD004AD000C4809 /* EditingTeamView.swift */; }; + FF3366D82D403D1F0012D1B4 /* InscriptionLegendView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF3A74312D37DCF2007E3032 /* InscriptionLegendView.swift */; }; + FF3366D92D403D1F0012D1B4 /* NetworkManagerError.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF6EC9052B947A1000EA7F5A /* NetworkManagerError.swift */; }; + FF3366DA2D403D1F0012D1B4 /* Tournament.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47D592B6D383C00ADC637 /* Tournament.swift */; }; + FF3366DB2D403D1F0012D1B4 /* TournamentStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4FC2E2A2C2C0E4D0021F3BF /* TournamentStore.swift */; }; + FF3366DC2D403D1F0012D1B4 /* LoserRoundSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5647122C0B6F380081F995 /* LoserRoundSettingsView.swift */; }; + FF3366DD2D403D1F0012D1B4 /* Persistence.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF3795652B9399AA004EA093 /* Persistence.swift */; }; + FF3366DE2D403D1F0012D1B4 /* CloseDatePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFCF76062C3BE9BC006C8C3D /* CloseDatePicker.swift */; }; + FF3366DF2D403D1F0012D1B4 /* BarButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1DF49A2BD8D23900822FA0 /* BarButtonView.swift */; }; + FF3366E02D403D1F0012D1B4 /* PlanningView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF9644F2BC25E3700EEF017 /* PlanningView.swift */; }; + FF3366E12D403D1F0012D1B4 /* Match.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967CEB2BAECB9900A9A3BD /* Match.swift */; }; + FF3366E22D403D1F0012D1B4 /* TournamentLevelPickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8F26492BAE0B4100650388 /* TournamentLevelPickerView.swift */; }; + FF3366E32D403D1F0012D1B4 /* FederalTournamentHolder.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1CBC212BB53E590036DAAB /* FederalTournamentHolder.swift */; }; + FF3366E42D403D1F0012D1B4 /* DataStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47D5D2B6D38EC00ADC637 /* DataStore.swift */; }; + FF3366E52D403D1F0012D1B4 /* SetDescriptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFCFC01B2BBC5AAA00B82851 /* SetDescriptor.swift */; }; + FF3366E62D403D1F0012D1B4 /* TeamHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF025AD72BD0C10F00A86CF8 /* TeamHeaderView.swift */; }; + FF3366E72D403D1F0012D1B4 /* OrganizedTournamentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF82CFC42B911F5B00B0CAF2 /* OrganizedTournamentView.swift */; }; + FF3366E82D403D1F0012D1B4 /* RoundScheduleEditorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF964562BC26B3400EEF017 /* RoundScheduleEditorView.swift */; }; + FF3366E92D403D1F0012D1B4 /* EventListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF59FFB22B90EFAC0061EFF9 /* EventListView.swift */; }; + FF3366EA2D403D1F0012D1B4 /* TournamentConfiguratorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8F263C2BAD627A00650388 /* TournamentConfiguratorView.swift */; }; + FF3366EB2D403D1F0012D1B4 /* ClubImportView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFC1E10B2BAC7FB0008D6F59 /* ClubImportView.swift */; }; + FF3366EC2D403D1F0012D1B4 /* UnderlineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF558C622C6CDD020071F9AE /* UnderlineView.swift */; }; + FF3366ED2D403D1F0012D1B4 /* MatchScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF3B60A22BC49BBC008C2E66 /* MatchScheduler.swift */; }; + FF3366EE2D403D1F0012D1B4 /* CallMessageCustomizationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1162792BCF8109000C4809 /* CallMessageCustomizationView.swift */; }; + FF3366EF2D403D1F0012D1B4 /* TournamentStatusView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF6087E92BE25EF1004E1E47 /* TournamentStatusView.swift */; }; + FF3366F02D403D1F0012D1B4 /* MatchTeamDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF025ADA2BD0C2D000A86CF8 /* MatchTeamDetailView.swift */; }; + FF3366F12D403D1F0012D1B4 /* GenericDestinationPickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5DA1942BB927E800A33061 /* GenericDestinationPickerView.swift */; }; + FF3366F22D403D1F0012D1B4 /* DateInterval.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF116E02BD2A9B600A33B06 /* DateInterval.swift */; }; + FF3366F32D403D1F0012D1B4 /* TableStructureView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8F26532BAE1E4400650388 /* TableStructureView.swift */; }; + FF3366F42D403D1F0012D1B4 /* Purchase.swift in Sources */ = {isa = PBXBuildFile; fileRef = C45BAE432BCA753E002EEC8A /* Purchase.swift */; }; + FF3366F52D403D1F0012D1B4 /* Screen.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF6EC8FD2B94792300EA7F5A /* Screen.swift */; }; + FF3366F62D403D1F0012D1B4 /* Round.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967CED2BAECBD700A9A3BD /* Round.swift */; }; + FF3366F72D403D1F0012D1B4 /* RegistrationSetupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFBFC3902CEE3A0E000EBD8D /* RegistrationSetupView.swift */; }; + FF3366F82D403D1F0012D1B4 /* FederalDataViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5BAF6D2BE0B3C8008B4B7E /* FederalDataViewModel.swift */; }; + FF3366F92D403D1F0012D1B4 /* AgendaDestination.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF3F74FE2B91A2D4004CFE0E /* AgendaDestination.swift */; }; + FF3366FA2D403D1F0012D1B4 /* PadelClubApp.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = FF3795602B9396D0004EA093 /* PadelClubApp.xcdatamodeld */; }; + FF3366FB2D403D1F0012D1B4 /* SetInputView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFCFC0152BBC5A4C00B82851 /* SetInputView.swift */; }; + FF3366FC2D403D1F0012D1B4 /* ButtonValidateView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF03C932BD91D0C00B516FC /* ButtonValidateView.swift */; }; + FF3366FD2D403D1F0012D1B4 /* ClubRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5D0D882BB4935C005CB568 /* ClubRowView.swift */; }; + FF3366FE2D403D1F0012D1B4 /* ClubDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1DC5502BAB351300FD8220 /* ClubDetailView.swift */; }; + FF3366FF2D403D1F0012D1B4 /* GroupStageCallingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF9268022BCE94A30080F940 /* GroupStageCallingView.swift */; }; + FF3367002D403D1F0012D1B4 /* CryptoKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49EF0432BE286780077B5AA /* CryptoKey.swift */; }; + FF3367012D403D1F0012D1B4 /* CashierSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF11627C2BCF941A000C4809 /* CashierSettingsView.swift */; }; + FF3367022D403D1F0012D1B4 /* LoserRoundScheduleEditorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFFCDE0D2BCC833600317DEF /* LoserRoundScheduleEditorView.swift */; }; + FF3367032D403D1F0012D1B4 /* Club.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47D622B6D3D6500ADC637 /* Club.swift */; }; + FF3367042D403D1F0012D1B4 /* Array+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF6EC90A2B947AC000EA7F5A /* Array+Extensions.swift */; }; + FF3367052D403D1F0012D1B4 /* ToolboxView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF59FFB82B90EFD70061EFF9 /* ToolboxView.swift */; }; + FF3367062D403D1F0012D1B4 /* Alphabet.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8E1CE52C006E0200184680 /* Alphabet.swift */; }; + FF3367072D403D1F0012D1B4 /* String+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF8ACD82B923F3C008466FA /* String+Extensions.swift */; }; + FF3367082D403D1F0012D1B4 /* GroupStageSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFCB74122C4625BB008384D0 /* GroupStageSettingsView.swift */; }; + FF3367092D403D1F0012D1B4 /* TournamentGeneralSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF025AE42BD0EBB800A86CF8 /* TournamentGeneralSettingsView.swift */; }; + FF33670A2D403D1F0012D1B4 /* LoserRoundView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFC2DCB12BBE75D40046DB9F /* LoserRoundView.swift */; }; + FF33670B2D403D1F0012D1B4 /* AddTeamView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF90FC1C2C44FB3E009339B2 /* AddTeamView.swift */; }; + FF33670C2D403D1F0012D1B4 /* PlayerPayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF9267FB2BCE84870080F940 /* PlayerPayView.swift */; }; + FF33670D2D403D1F0012D1B4 /* PlanningByCourtView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF2B51542C7A4DAF00FFF126 /* PlanningByCourtView.swift */; }; + FF33670E2D403D1F0012D1B4 /* FileImportManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFA6D7842BB0B795003A31F3 /* FileImportManager.swift */; }; + FF33670F2D403D1F0012D1B4 /* OngoingContainerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFA252B02CDD2C080074E63F /* OngoingContainerView.swift */; }; + FF3367102D403D1F0012D1B4 /* TournamentButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF6EC8FA2B94788600EA7F5A /* TournamentButtonView.swift */; }; + FF3367112D403D1F0012D1B4 /* DrawLogsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF6761562CC7803600CC9BF2 /* DrawLogsView.swift */; }; + FF3367122D403D1F0012D1B4 /* FederalPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF8ACCC2B92367B008466FA /* FederalPlayer.swift */; }; + FF3367132D403D1F0012D1B4 /* NavigationViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFBF065F2BBD9F6D009D6715 /* NavigationViewModel.swift */; }; + FF3367142D403D1F0012D1B4 /* FixedWidthInteger+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF6EC9082B947A5300EA7F5A /* FixedWidthInteger+Extensions.swift */; }; + FF3367152D403D1F0012D1B4 /* LoserBracketFromGroupStageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF6525C22C8C61B400B9498E /* LoserBracketFromGroupStageView.swift */; }; + FF3367162D403D1F0012D1B4 /* ImportedPlayer+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5D30502BD94E1000F2B93D /* ImportedPlayer+Extensions.swift */; }; + FF3367172D403D1F0012D1B4 /* ClubSearchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFC1E1032BAC28C6008D6F59 /* ClubSearchView.swift */; }; + FF3367182D403D1F0012D1B4 /* DateMenuView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFBFC3942CF05CBB000EBD8D /* DateMenuView.swift */; }; + FF3367192D403D1F0012D1B4 /* PlayerPopoverView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF089EBA2BB0120700F0AEC7 /* PlayerPopoverView.swift */; }; + FF33671A2D403D1F0012D1B4 /* InscriptionManagerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF70916D2B9108C600AB08DA /* InscriptionManagerView.swift */; }; + FF33671B2D403D1F0012D1B4 /* MatchFormatPickingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF77CE512CCCD1AF00CBCBB4 /* MatchFormatPickingView.swift */; }; + FF33671C2D403D1F0012D1B4 /* ActivityView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF82CFC82B9132AF00B0CAF2 /* ActivityView.swift */; }; + FF33671D2D403D1F0012D1B4 /* MySortDescriptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFDB1C722BB2CFE900F1E467 /* MySortDescriptor.swift */; }; + FF33671E2D403D1F0012D1B4 /* CalendarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5D0D8A2BB4D1E3005CB568 /* CalendarView.swift */; }; + FF33671F2D403D1F0012D1B4 /* FederalTournamentSearchScope.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1CBC1E2BB53E0C0036DAAB /* FederalTournamentSearchScope.swift */; }; + FF3367202D403D1F0012D1B4 /* TournamentFieldsManagerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8F26462BAE0ACB00650388 /* TournamentFieldsManagerView.swift */; }; + FF3367212D403D1F0012D1B4 /* PrintSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1F4B812BFA0124000B4573 /* PrintSettingsView.swift */; }; + FF3367222D403D1F0012D1B4 /* TournamentMatchFormatsSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF025AE22BD0EBA900A86CF8 /* TournamentMatchFormatsSettingsView.swift */; }; + FF3367232D403D1F0012D1B4 /* DatePickingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1162892BD05247000C4809 /* DatePickingView.swift */; }; + FF3367242D403D1F0012D1B4 /* MatchFormatRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFCFC0192BBC5A8500B82851 /* MatchFormatRowView.swift */; }; + FF3367252D403D1F0012D1B4 /* MonthData.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF025AE82BD1307E00A86CF8 /* MonthData.swift */; }; + FF3367262D403D1F0012D1B4 /* MenuWarningView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFEF7F4D2BDE69130033D0F0 /* MenuWarningView.swift */; }; + FF3367272D403D1F0012D1B4 /* TournamentBuildView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1F4B6C2BF9E60B000B4573 /* TournamentBuildView.swift */; }; + FF3367282D403D1F0012D1B4 /* TeamPickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967D0A2BAF3D4C00A9A3BD /* TeamPickerView.swift */; }; + FF3367292D403D1F0012D1B4 /* CloudConvert.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFA6D7862BB0B7A2003A31F3 /* CloudConvert.swift */; }; + FF33672A2D403D1F0012D1B4 /* EventTournamentsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFBF41832BF75ED7001B24CB /* EventTournamentsView.swift */; }; + FF33672B2D403D1F0012D1B4 /* DisplayContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1DC55A2BAB80C400FD8220 /* DisplayContext.swift */; }; + FF33672C2D403D1F0012D1B4 /* MultiCourtPickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF17CA482CB915A1003C7323 /* MultiCourtPickerView.swift */; }; + FF33672D2D403D1F0012D1B4 /* TournamentCallView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF9268062BCE94D90080F940 /* TournamentCallView.swift */; }; + FF33672E2D403D1F0012D1B4 /* LoserRoundsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFC2DCB32BBE9ECD0046DB9F /* LoserRoundsView.swift */; }; + FF33672F2D403D1F0012D1B4 /* GroupStagesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967CFB2BAEE13900A9A3BD /* GroupStagesView.swift */; }; + FF3367302D403D1F0012D1B4 /* PadelClubView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFD783FE2B91BA42000F62A6 /* PadelClubView.swift */; }; + FF3367312D403D1F0012D1B4 /* RegistrationInfoSheetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF3A73F22D37C34C007E3032 /* RegistrationInfoSheetView.swift */; }; + FF3367322D403D1F0012D1B4 /* URLs.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49EF01A2BD6A1E80077B5AA /* URLs.swift */; }; + FF3367332D403D1F0012D1B4 /* MatchDescriptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFCFC0132BBC59FC00B82851 /* MatchDescriptor.swift */; }; + FF3367342D403D1F0012D1B4 /* TournamentFormatSelectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8F26482BAE0B4100650388 /* TournamentFormatSelectionView.swift */; }; + FF3367352D403D1F0012D1B4 /* CoachListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF17CA562CC02FEA003C7323 /* CoachListView.swift */; }; + FF3367362D403D1F0012D1B4 /* MatchListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFBF065D2BBD8040009D6715 /* MatchListView.swift */; }; + FF3367372D403D1F0012D1B4 /* PadelClubApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = C425D4002B6D249D002A7B48 /* PadelClubApp.swift */; }; + FF3367382D403D1F0012D1B4 /* TournamentSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8F26422BADFE5B00650388 /* TournamentSettingsView.swift */; }; + FF3367392D403D1F0012D1B4 /* String+Crypto.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49EF03B2BE15AF80077B5AA /* String+Crypto.swift */; }; + FF33673A2D403D1F0012D1B4 /* GroupStageTeamReplacementView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF9AC3942BE3627B00C2E883 /* GroupStageTeamReplacementView.swift */; }; + FF33673B2D403D1F0012D1B4 /* TabItemModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF4C7F012BBBD7150031B6A3 /* TabItemModifier.swift */; }; + FF33673C2D403D1F0012D1B4 /* DeferredViewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFDDD40B2B93B2BB00C91A49 /* DeferredViewModifier.swift */; }; + FF33673D2D403D1F0012D1B4 /* TournamentScheduleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF0E0B6C2BC254C6005F00A9 /* TournamentScheduleView.swift */; }; + FF33673E2D403D1F0012D1B4 /* MatchFormatStorageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF025AF02BD1AEBD00A86CF8 /* MatchFormatStorageView.swift */; }; + FF33673F2D403D1F0012D1B4 /* UmpireView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF3F74F52B919E45004CFE0E /* UmpireView.swift */; }; + FF3367402D403D1F0012D1B4 /* User.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47DAC2B85FCCD00ADC637 /* User.swift */; }; + FF3367412D403D1F0012D1B4 /* CodingContainer+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C33F752C9B1EC5006316DE /* CodingContainer+Extensions.swift */; }; + FF3367422D403D1F0012D1B4 /* MatchSummaryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967D002BAEF0B400A9A3BD /* MatchSummaryView.swift */; }; + FF3367432D403D1F0012D1B4 /* OngoingDestination.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFA252B42CDD2C630074E63F /* OngoingDestination.swift */; }; + FF3367442D403D1F0012D1B4 /* TournamentDurationManagerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8F26442BAE0A3400650388 /* TournamentDurationManagerView.swift */; }; + FF3367452D403D1F0012D1B4 /* MockData.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1DC5522BAB354A00FD8220 /* MockData.swift */; }; + FF3367462D403D1F0012D1B4 /* TeamDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967D082BAF3D4000A9A3BD /* TeamDetailView.swift */; }; + FF3367472D403D1F0012D1B4 /* PlayerStatisticView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFA252A82CDB70520074E63F /* PlayerStatisticView.swift */; }; + FF3367482D403D1F0012D1B4 /* GroupStagesSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5DA18E2BB9268800A33061 /* GroupStagesSettingsView.swift */; }; + FF3367492D403D1F0012D1B4 /* TournamentFilterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF663FBD2BE019EC0031AE83 /* TournamentFilterView.swift */; }; + FF33674A2D403D1F0012D1B4 /* PreviewBracketPositionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF67615A2CC8ED6900CC9BF2 /* PreviewBracketPositionView.swift */; }; + FF33674B2D403D1F0012D1B4 /* HtmlGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1F4B722BFA00FB000B4573 /* HtmlGenerator.swift */; }; + FF33674C2D403D1F0012D1B4 /* BracketCallingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF17CA522CBE4788003C7323 /* BracketCallingView.swift */; }; + FF33674D2D403D1F0012D1B4 /* PadelRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8F26352BAD523300650388 /* PadelRule.swift */; }; + FF33674E2D403D1F0012D1B4 /* TeamRegistration.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967CF02BAECC0B00A9A3BD /* TeamRegistration.swift */; }; + FF33674F2D403D1F0012D1B4 /* Date+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF8ACDA2B923F48008466FA /* Date+Extensions.swift */; }; + FF3367502D403D1F0012D1B4 /* GroupStageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967CFA2BAEE13800A9A3BD /* GroupStageView.swift */; }; + FF3367512D403D1F0012D1B4 /* Tips.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1DC5582BAB767000FD8220 /* Tips.swift */; }; + FF3367522D403D1F0012D1B4 /* MainView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF59FFB62B90EFBF0061EFF9 /* MainView.swift */; }; + FF3367532D403D1F0012D1B4 /* MatchDateView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967D0C2BAF3EB200A9A3BD /* MatchDateView.swift */; }; + FF3367542D403D1F0012D1B4 /* PlanningSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF964522BC262B000EEF017 /* PlanningSettingsView.swift */; }; + FF3367552D403D1F0012D1B4 /* MatchScheduleEditorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF527D52BC6DDD000FF4EF2 /* MatchScheduleEditorView.swift */; }; + FF3367562D403D1F0012D1B4 /* FortuneWheelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFC91AF82BD6A09100B29808 /* FortuneWheelView.swift */; }; + FF3367572D403D1F0012D1B4 /* URL+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF8ACD52B923960008466FA /* URL+Extensions.swift */; }; + FF3367582D403D1F0012D1B4 /* LoadingViewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = C493B37D2C10AD3600862481 /* LoadingViewModifier.swift */; }; + FF3367592D403D1F0012D1B4 /* PlayerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF089EBC2BB0287D00F0AEC7 /* PlayerView.swift */; }; + FF33675A2D403D1F0012D1B4 /* MatchDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967D022BAEF0C000A9A3BD /* MatchDetailView.swift */; }; + FF33675B2D403D1F0012D1B4 /* ExportFormat.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF1D2CA2C4A22B200C8D33D /* ExportFormat.swift */; }; + FF33675C2D403D1F0012D1B4 /* PlayerBlockView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967D0E2BAF63B000A9A3BD /* PlayerBlockView.swift */; }; + FF33675D2D403D1F0012D1B4 /* StoreItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47D8F2B7BBBEC00ADC637 /* StoreItem.swift */; }; + FF33675E2D403D1F0012D1B4 /* Selectable.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFB9C8702BBADDE200A0EF4F /* Selectable.swift */; }; + FF33675F2D403D1F0012D1B4 /* PointView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFCFC0112BBC3E1A00B82851 /* PointView.swift */; }; + FF3367602D403D1F0012D1B4 /* ClubHolder.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1CBC202BB53E590036DAAB /* ClubHolder.swift */; }; + FF3367612D403D1F0012D1B4 /* EventSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFBF41852BF75FDA001B24CB /* EventSettingsView.swift */; }; + FF3367622D403D1F0012D1B4 /* InscriptionInfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5D0D772BB42C5B005CB568 /* InscriptionInfoView.swift */; }; + FF3367632D403D1F0012D1B4 /* SelectablePlayerListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF4AB6BC2B9256E10002987F /* SelectablePlayerListView.swift */; }; + FF3367642D403D1F0012D1B4 /* MatchFormatSelectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8F26502BAE0BAD00650388 /* MatchFormatSelectionView.swift */; }; + FF3367652D403D1F0012D1B4 /* TournamentRankView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5BAF712BE19274008B4B7E /* TournamentRankView.swift */; }; + FF3367662D403D1F0012D1B4 /* GroupStageDatePickingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF77CE592CCCD1FF00CBCBB4 /* GroupStageDatePickingView.swift */; }; + FF3367672D403D1F0012D1B4 /* NumberFormatter+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5D0D862BB48AFD005CB568 /* NumberFormatter+Extensions.swift */; }; + FF3367682D403D1F0012D1B4 /* SetLabelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFCFC0172BBC5A6800B82851 /* SetLabelView.swift */; }; + FF3367692D403D1F0012D1B4 /* DebugSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4489BE12C05BF5000043F3D /* DebugSettingsView.swift */; }; + FF33676A2D403D1F0012D1B4 /* GroupStageScheduleEditorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF9645A2BC2D53B00EEF017 /* GroupStageScheduleEditorView.swift */; }; + FF33676B2D403D1F0012D1B4 /* EventView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFBF41812BF73EB3001B24CB /* EventView.swift */; }; + FF33676C2D403D1F0012D1B4 /* LoginView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47DA52B83948E00ADC637 /* LoginView.swift */; }; + FF33676D2D403D1F0012D1B4 /* Court.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFC91B002BD85C2F00B29808 /* Court.swift */; }; + FF33676E2D403D1F0012D1B4 /* Labels.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967CF72BAEDF0000A9A3BD /* Labels.swift */; }; + FF33676F2D403D1F0012D1B4 /* CopyPasteButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFCB74162C480411008384D0 /* CopyPasteButtonView.swift */; }; + FF3367702D403D1F0012D1B4 /* PlayerSexPickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF089EB32BB0020000F0AEC7 /* PlayerSexPickerView.swift */; }; + FF3367712D403D1F0012D1B4 /* TournamentInscriptionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1F4B702BF9EFE9000B4573 /* TournamentInscriptionView.swift */; }; + FF3367722D403D1F0012D1B4 /* CallSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF9267FE2BCE94830080F940 /* CallSettingsView.swift */; }; + FF3367732D403D1F0012D1B4 /* FooterButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF025ADC2BD0C94300A86CF8 /* FooterButtonView.swift */; }; + FF3367742D403D1F0012D1B4 /* RankCalculatorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5D0D822BB48997005CB568 /* RankCalculatorView.swift */; }; + FF3367752D403D1F0012D1B4 /* DateBoxView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF7091692B90F95E00AB08DA /* DateBoxView.swift */; }; + FF3367762D403D1F0012D1B4 /* LearnMoreSheetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5D0D6F2BB3EFA5005CB568 /* LearnMoreSheetView.swift */; }; + FF3367772D403D1F0012D1B4 /* SourceFileManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF8ACD32B92392C008466FA /* SourceFileManager.swift */; }; + FF3367782D403D1F0012D1B4 /* TournamentCategorySettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF4623CA2D1340D200CB57B5 /* TournamentCategorySettingsView.swift */; }; + FF3367792D403D1F0012D1B4 /* PListReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EC6F582BE92D88000CEAB4 /* PListReader.swift */; }; + FF33677A2D403D1F0012D1B4 /* UpdateSourceRankDateView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF0EC5212BB173E70056B6D1 /* UpdateSourceRankDateView.swift */; }; + FF33677B2D403D1F0012D1B4 /* GlobalSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF025AE62BD1111000A86CF8 /* GlobalSettingsView.swift */; }; + FF33677C2D403D1F0012D1B4 /* Guard.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47D8E2B7BBBEC00ADC637 /* Guard.swift */; }; + FF33677D2D403D1F0012D1B4 /* PurchaseListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49EF0182BD694290077B5AA /* PurchaseListView.swift */; }; + FF33677F2D403D1F0012D1B4 /* Algorithms in Frameworks */ = {isa = PBXBuildFile; productRef = FF3366732D403D1F0012D1B4 /* Algorithms */; }; + FF3367802D403D1F0012D1B4 /* Zip in Frameworks */ = {isa = PBXBuildFile; productRef = FF3366752D403D1F0012D1B4 /* Zip */; }; + FF3367812D403D1F0012D1B4 /* LeStorage.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C49EF0372BDFF3000077B5AA /* LeStorage.framework */; }; + FF3367832D403D1F0012D1B4 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C425D4072B6D249E002A7B48 /* Preview Assets.xcassets */; }; + FF3367842D403D1F0012D1B4 /* Launch Screen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = FFD784002B91BF79000F62A6 /* Launch Screen.storyboard */; }; + FF3367852D403D1F0012D1B4 /* tournament-template.html in Resources */ = {isa = PBXBuildFile; fileRef = FF1F4B7F2BFA0105000B4573 /* tournament-template.html */; }; + FF3367862D403D1F0012D1B4 /* local.sqlite in Resources */ = {isa = PBXBuildFile; fileRef = FF2B51602C7E302C00FFF126 /* local.sqlite */; }; + FF3367872D403D1F0012D1B4 /* groupstagescore-template.html in Resources */ = {isa = PBXBuildFile; fileRef = FF1F4B7B2BFA0105000B4573 /* groupstagescore-template.html */; }; + FF3367882D403D1F0012D1B4 /* player-template.html in Resources */ = {isa = PBXBuildFile; fileRef = FF1F4B7E2BFA0105000B4573 /* player-template.html */; }; + FF3367892D403D1F0012D1B4 /* groupstagerow-template.html in Resources */ = {isa = PBXBuildFile; fileRef = FF1F4B7A2BFA0105000B4573 /* groupstagerow-template.html */; }; + FF33678A2D403D1F0012D1B4 /* hiddenplayer-template.html in Resources */ = {isa = PBXBuildFile; fileRef = FF1F4B7C2BFA0105000B4573 /* hiddenplayer-template.html */; }; + FF33678B2D403D1F0012D1B4 /* bracket-template.html in Resources */ = {isa = PBXBuildFile; fileRef = FF1F4B762BFA0105000B4573 /* bracket-template.html */; }; + FF33678C2D403D1F0012D1B4 /* groupstagecol-template.html in Resources */ = {isa = PBXBuildFile; fileRef = FF1F4B782BFA0105000B4573 /* groupstagecol-template.html */; }; + FF33678D2D403D1F0012D1B4 /* groupstage-template.html in Resources */ = {isa = PBXBuildFile; fileRef = FF1F4B772BFA0105000B4573 /* groupstage-template.html */; }; + FF33678E2D403D1F0012D1B4 /* groupstageentrant-template.html in Resources */ = {isa = PBXBuildFile; fileRef = FF1F4B792BFA0105000B4573 /* groupstageentrant-template.html */; }; + FF33678F2D403D1F0012D1B4 /* match-template.html in Resources */ = {isa = PBXBuildFile; fileRef = FF1F4B7D2BFA0105000B4573 /* match-template.html */; }; + FF3367902D403D1F0012D1B4 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = FFF0241D2BF48B15001F14B4 /* Localizable.strings */; }; + FF3367912D403D1F0012D1B4 /* SyncedProducts.storekit in Resources */ = {isa = PBXBuildFile; fileRef = C45BAE3A2BC6DF10002EEC8A /* SyncedProducts.storekit */; }; + FF3367922D403D1F0012D1B4 /* local.plist in Resources */ = {isa = PBXBuildFile; fileRef = C4EC6F562BE92CAC000CEAB4 /* local.plist */; }; + FF3367932D403D1F0012D1B4 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = FF0CA5742BDA4AE10080E843 /* PrivacyInfo.xcprivacy */; }; + FF3367942D403D1F0012D1B4 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C425D4042B6D249E002A7B48 /* Assets.xcassets */; }; + FF3367962D403D1F0012D1B4 /* LeStorage.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = C49EF0372BDFF3000077B5AA /* LeStorage.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; FF3795622B9396D0004EA093 /* PadelClubApp.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = FF3795602B9396D0004EA093 /* PadelClubApp.xcdatamodeld */; }; FF3795662B9399AA004EA093 /* Persistence.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF3795652B9399AA004EA093 /* Persistence.swift */; }; FF3A73F32D37C34D007E3032 /* RegistrationInfoSheetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF3A73F22D37C34C007E3032 /* RegistrationInfoSheetView.swift */; }; @@ -897,6 +1181,17 @@ name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; }; + FF3367952D403D1F0012D1B4 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + FF3367962D403D1F0012D1B4 /* LeStorage.framework in Embed Frameworks */, + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; FF4CC04B2C996C0600151637 /* Embed Frameworks */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; @@ -1076,6 +1371,7 @@ FF2B51622C7F073100FFF126 /* Model_1_1.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = Model_1_1.xcdatamodel; sourceTree = ""; }; FF2B6F5D2C036A1400835EE7 /* EventLinksView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventLinksView.swift; sourceTree = ""; }; FF2EFBEF2BDE295E0049CE3B /* SendToAllView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SendToAllView.swift; sourceTree = ""; }; + FF33679A2D403D1F0012D1B4 /* ProdDev.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ProdDev.app; sourceTree = BUILT_PRODUCTS_DIR; }; FF3795612B9396D0004EA093 /* Model.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = Model.xcdatamodel; sourceTree = ""; }; FF3795652B9399AA004EA093 /* Persistence.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Persistence.swift; sourceTree = ""; }; FF3A73F22D37C34C007E3032 /* RegistrationInfoSheetView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RegistrationInfoSheetView.swift; sourceTree = ""; }; @@ -1289,6 +1585,16 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + FF33677E2D403D1F0012D1B4 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + FF33677F2D403D1F0012D1B4 /* Algorithms in Frameworks */, + FF3367802D403D1F0012D1B4 /* Zip in Frameworks */, + FF3367812D403D1F0012D1B4 /* LeStorage.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; FF4CC0342C996C0600151637 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -1331,6 +1637,7 @@ C425D4172B6D249E002A7B48 /* PadelClubUITests.xctest */, FF70FBCF2C90584900129CC2 /* PadelClub TestFlight.app */, FF4CC0502C996C0600151637 /* ProdTest PadelClub.app */, + FF33679A2D403D1F0012D1B4 /* ProdDev.app */, ); name = Products; sourceTree = ""; @@ -2134,6 +2441,28 @@ productReference = C425D4172B6D249E002A7B48 /* PadelClubUITests.xctest */; productType = "com.apple.product-type.bundle.ui-testing"; }; + FF3366722D403D1F0012D1B4 /* ProdDev */ = { + isa = PBXNativeTarget; + buildConfigurationList = FF3367972D403D1F0012D1B4 /* Build configuration list for PBXNativeTarget "ProdDev" */; + buildPhases = ( + FF3366772D403D1F0012D1B4 /* Sources */, + FF33677E2D403D1F0012D1B4 /* Frameworks */, + FF3367822D403D1F0012D1B4 /* Resources */, + FF3367952D403D1F0012D1B4 /* Embed Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = ProdDev; + packageProductDependencies = ( + FF3366732D403D1F0012D1B4 /* Algorithms */, + FF3366752D403D1F0012D1B4 /* Zip */, + ); + productName = PadelClub; + productReference = FF33679A2D403D1F0012D1B4 /* ProdDev.app */; + productType = "com.apple.product-type.application"; + }; FF4CBF3E2C996C0600151637 /* ProdTest PadelClub */ = { isa = PBXNativeTarget; buildConfigurationList = FF4CC04D2C996C0600151637 /* Build configuration list for PBXNativeTarget "ProdTest PadelClub" */; @@ -2224,6 +2553,7 @@ C425D4162B6D249E002A7B48 /* PadelClubUITests */, FF70FABD2C90584900129CC2 /* PadelClub TestFlight */, FF4CBF3E2C996C0600151637 /* ProdTest PadelClub */, + FF3366722D403D1F0012D1B4 /* ProdDev */, ); }; /* End PBXProject section */ @@ -2268,6 +2598,31 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + FF3367822D403D1F0012D1B4 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FF3367832D403D1F0012D1B4 /* Preview Assets.xcassets in Resources */, + FF3367842D403D1F0012D1B4 /* Launch Screen.storyboard in Resources */, + FF3367852D403D1F0012D1B4 /* tournament-template.html in Resources */, + FF3367862D403D1F0012D1B4 /* local.sqlite in Resources */, + FF3367872D403D1F0012D1B4 /* groupstagescore-template.html in Resources */, + FF3367882D403D1F0012D1B4 /* player-template.html in Resources */, + FF3367892D403D1F0012D1B4 /* groupstagerow-template.html in Resources */, + FF33678A2D403D1F0012D1B4 /* hiddenplayer-template.html in Resources */, + FF33678B2D403D1F0012D1B4 /* bracket-template.html in Resources */, + FF33678C2D403D1F0012D1B4 /* groupstagecol-template.html in Resources */, + FF33678D2D403D1F0012D1B4 /* groupstage-template.html in Resources */, + FF33678E2D403D1F0012D1B4 /* groupstageentrant-template.html in Resources */, + FF33678F2D403D1F0012D1B4 /* match-template.html in Resources */, + FF3367902D403D1F0012D1B4 /* Localizable.strings in Resources */, + FF3367912D403D1F0012D1B4 /* SyncedProducts.storekit in Resources */, + FF3367922D403D1F0012D1B4 /* local.plist in Resources */, + FF3367932D403D1F0012D1B4 /* PrivacyInfo.xcprivacy in Resources */, + FF3367942D403D1F0012D1B4 /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; FF4CC0382C996C0600151637 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -2611,6 +2966,275 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + FF3366772D403D1F0012D1B4 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FF3366782D403D1F0012D1B4 /* UserCreationView.swift in Sources */, + FF3366792D403D1F0012D1B4 /* TabDestination.swift in Sources */, + FF33667A2D403D1F0012D1B4 /* CashierView.swift in Sources */, + FF33667B2D403D1F0012D1B4 /* Event.swift in Sources */, + FF33667C2D403D1F0012D1B4 /* DatePickingViewWithFormat.swift in Sources */, + FF33667D2D403D1F0012D1B4 /* PlayerHolder.swift in Sources */, + FF33667E2D403D1F0012D1B4 /* LoserRoundStepScheduleEditorView.swift in Sources */, + FF33667F2D403D1F0012D1B4 /* ClubCourtSetupView.swift in Sources */, + FF3366802D403D1F0012D1B4 /* Patcher.swift in Sources */, + FF3366812D403D1F0012D1B4 /* FileImportView.swift in Sources */, + FF3366822D403D1F0012D1B4 /* StepperView.swift in Sources */, + FF3366832D403D1F0012D1B4 /* RoundsView.swift in Sources */, + FF3366842D403D1F0012D1B4 /* FederalTournament.swift in Sources */, + FF3366852D403D1F0012D1B4 /* TournamentInitView.swift in Sources */, + FF3366862D403D1F0012D1B4 /* SubscriptionView.swift in Sources */, + FF3366872D403D1F0012D1B4 /* TournamentLookUpView.swift in Sources */, + FF3366882D403D1F0012D1B4 /* ClubsView.swift in Sources */, + FF3366892D403D1F0012D1B4 /* ImagePickerView.swift in Sources */, + FF33668A2D403D1F0012D1B4 /* MatchTypeSelectionView.swift in Sources */, + FF33668B2D403D1F0012D1B4 /* MatchSetupView.swift in Sources */, + FF33668C2D403D1F0012D1B4 /* NetworkManager.swift in Sources */, + FF33668D2D403D1F0012D1B4 /* EventLinksView.swift in Sources */, + FF33668E2D403D1F0012D1B4 /* SeedInterval.swift in Sources */, + FF33668F2D403D1F0012D1B4 /* TournamentClubSettingsView.swift in Sources */, + FF3366902D403D1F0012D1B4 /* GroupStageTeamView.swift in Sources */, + FF3366912D403D1F0012D1B4 /* RoundSettingsView.swift in Sources */, + FF3366922D403D1F0012D1B4 /* MatchViewStyle.swift in Sources */, + FF3366932D403D1F0012D1B4 /* SupportButtonView.swift in Sources */, + FF3366942D403D1F0012D1B4 /* TournamentBroadcastRowView.swift in Sources */, + FF3366952D403D1F0012D1B4 /* TeamWeightView.swift in Sources */, + FF3366962D403D1F0012D1B4 /* SeedsCallingView.swift in Sources */, + FF3366972D403D1F0012D1B4 /* CallView.swift in Sources */, + FF3366982D403D1F0012D1B4 /* Color+Extensions.swift in Sources */, + FF3366992D403D1F0012D1B4 /* EditSharingView.swift in Sources */, + FF33669A2D403D1F0012D1B4 /* TournamentCellView.swift in Sources */, + FF33669B2D403D1F0012D1B4 /* Sequence+Extensions.swift in Sources */, + FF33669C2D403D1F0012D1B4 /* CashierDetailView.swift in Sources */, + FF33669D2D403D1F0012D1B4 /* EventClubSettingsView.swift in Sources */, + FF33669E2D403D1F0012D1B4 /* AccountView.swift in Sources */, + FF33669F2D403D1F0012D1B4 /* PlayersWithoutContactView.swift in Sources */, + FF3366A02D403D1F0012D1B4 /* FollowUpMatchView.swift in Sources */, + FF3366A12D403D1F0012D1B4 /* TeamsCallingView.swift in Sources */, + FF3366A22D403D1F0012D1B4 /* Calendar+Extensions.swift in Sources */, + FF3366A32D403D1F0012D1B4 /* TeamScore.swift in Sources */, + FF3366A42D403D1F0012D1B4 /* EditablePlayerView.swift in Sources */, + FF3366A52D403D1F0012D1B4 /* PlayerDetailView.swift in Sources */, + FF3366A62D403D1F0012D1B4 /* UmpireStatisticView.swift in Sources */, + FF3366A72D403D1F0012D1B4 /* ListRowViewModifier.swift in Sources */, + FF3366A82D403D1F0012D1B4 /* PresentationContext.swift in Sources */, + FF3366A92D403D1F0012D1B4 /* AppSettings.swift in Sources */, + FF3366AA2D403D1F0012D1B4 /* SwiftParser.swift in Sources */, + FF3366AB2D403D1F0012D1B4 /* ChangePasswordView.swift in Sources */, + FF3366AC2D403D1F0012D1B4 /* TournamentSubscriptionView.swift in Sources */, + FF3366AD2D403D1F0012D1B4 /* RoundView.swift in Sources */, + FF3366AE2D403D1F0012D1B4 /* RowButtonView.swift in Sources */, + FF3366AF2D403D1F0012D1B4 /* SendToAllView.swift in Sources */, + FF3366B02D403D1F0012D1B4 /* EventCreationView.swift in Sources */, + FF3366B12D403D1F0012D1B4 /* LocationManager.swift in Sources */, + FF3366B22D403D1F0012D1B4 /* CapsuleViewModifier.swift in Sources */, + FF3366B32D403D1F0012D1B4 /* BroadcastView.swift in Sources */, + FF3366B42D403D1F0012D1B4 /* SchedulerView.swift in Sources */, + FF3366B52D403D1F0012D1B4 /* PadelClubButtonView.swift in Sources */, + FF3366B62D403D1F0012D1B4 /* TournamentSeedEditing.swift in Sources */, + FF3366B72D403D1F0012D1B4 /* TournamentView.swift in Sources */, + FF3366B82D403D1F0012D1B4 /* OngoingView.swift in Sources */, + FF3366B92D403D1F0012D1B4 /* CreateClubView.swift in Sources */, + FF3366BA2D403D1F0012D1B4 /* APICallsListView.swift in Sources */, + FF3366BB2D403D1F0012D1B4 /* TeamRestingView.swift in Sources */, + FF3366BC2D403D1F0012D1B4 /* NetworkFederalService.swift in Sources */, + FF3366BD2D403D1F0012D1B4 /* DurationSettingsView.swift in Sources */, + FF3366BE2D403D1F0012D1B4 /* AppScreen.swift in Sources */, + FF3366BF2D403D1F0012D1B4 /* CourtView.swift in Sources */, + FF3366C02D403D1F0012D1B4 /* PointSelectionView.swift in Sources */, + FF3366C12D403D1F0012D1B4 /* TeamRowView.swift in Sources */, + FF3366C22D403D1F0012D1B4 /* ContactManager.swift in Sources */, + FF3366C32D403D1F0012D1B4 /* AppDelegate.swift in Sources */, + FF3366C42D403D1F0012D1B4 /* SubscriptionInfoView.swift in Sources */, + FF3366C52D403D1F0012D1B4 /* EditScoreView.swift in Sources */, + FF3366C62D403D1F0012D1B4 /* TournamentOrganizerView.swift in Sources */, + FF3366C72D403D1F0012D1B4 /* NetworkMonitor.swift in Sources */, + FF3366C82D403D1F0012D1B4 /* TournamentRunningView.swift in Sources */, + FF3366C92D403D1F0012D1B4 /* TournamentDatePickerView.swift in Sources */, + FF3366CA2D403D1F0012D1B4 /* CourtAvailabilitySettingsView.swift in Sources */, + FF3366CB2D403D1F0012D1B4 /* ConfirmButtonView.swift in Sources */, + FF3366CC2D403D1F0012D1B4 /* PasswordField.swift in Sources */, + FF3366CD2D403D1F0012D1B4 /* MatchRowView.swift in Sources */, + FF3366CE2D403D1F0012D1B4 /* Locale+Extensions.swift in Sources */, + FF3366CF2D403D1F0012D1B4 /* HtmlService.swift in Sources */, + FF3366D02D403D1F0012D1B4 /* GroupStage.swift in Sources */, + FF3366D12D403D1F0012D1B4 /* DrawLog.swift in Sources */, + FF3366D22D403D1F0012D1B4 /* TournamentCashierView.swift in Sources */, + FF3366D32D403D1F0012D1B4 /* StoreManager.swift in Sources */, + FF3366D42D403D1F0012D1B4 /* SearchViewModel.swift in Sources */, + FF3366D52D403D1F0012D1B4 /* PlayerRegistration.swift in Sources */, + FF3366D62D403D1F0012D1B4 /* ImportedPlayerView.swift in Sources */, + FF3366D72D403D1F0012D1B4 /* EditingTeamView.swift in Sources */, + FF3366D82D403D1F0012D1B4 /* InscriptionLegendView.swift in Sources */, + FF3366D92D403D1F0012D1B4 /* NetworkManagerError.swift in Sources */, + FF3366DA2D403D1F0012D1B4 /* Tournament.swift in Sources */, + FF3366DB2D403D1F0012D1B4 /* TournamentStore.swift in Sources */, + FF3366DC2D403D1F0012D1B4 /* LoserRoundSettingsView.swift in Sources */, + FF3366DD2D403D1F0012D1B4 /* Persistence.swift in Sources */, + FF3366DE2D403D1F0012D1B4 /* CloseDatePicker.swift in Sources */, + FF3366DF2D403D1F0012D1B4 /* BarButtonView.swift in Sources */, + FF3366E02D403D1F0012D1B4 /* PlanningView.swift in Sources */, + FF3366E12D403D1F0012D1B4 /* Match.swift in Sources */, + FF3366E22D403D1F0012D1B4 /* TournamentLevelPickerView.swift in Sources */, + FF3366E32D403D1F0012D1B4 /* FederalTournamentHolder.swift in Sources */, + FF3366E42D403D1F0012D1B4 /* DataStore.swift in Sources */, + FF3366E52D403D1F0012D1B4 /* SetDescriptor.swift in Sources */, + FF3366E62D403D1F0012D1B4 /* TeamHeaderView.swift in Sources */, + FF3366E72D403D1F0012D1B4 /* OrganizedTournamentView.swift in Sources */, + FF3366E82D403D1F0012D1B4 /* RoundScheduleEditorView.swift in Sources */, + FF3366E92D403D1F0012D1B4 /* EventListView.swift in Sources */, + FF3366EA2D403D1F0012D1B4 /* TournamentConfiguratorView.swift in Sources */, + FF3366EB2D403D1F0012D1B4 /* ClubImportView.swift in Sources */, + FF3366EC2D403D1F0012D1B4 /* UnderlineView.swift in Sources */, + FF3366ED2D403D1F0012D1B4 /* MatchScheduler.swift in Sources */, + FF3366EE2D403D1F0012D1B4 /* CallMessageCustomizationView.swift in Sources */, + FF3366EF2D403D1F0012D1B4 /* TournamentStatusView.swift in Sources */, + FF3366F02D403D1F0012D1B4 /* MatchTeamDetailView.swift in Sources */, + FF3366F12D403D1F0012D1B4 /* GenericDestinationPickerView.swift in Sources */, + FF3366F22D403D1F0012D1B4 /* DateInterval.swift in Sources */, + FF3366F32D403D1F0012D1B4 /* TableStructureView.swift in Sources */, + FF3366F42D403D1F0012D1B4 /* Purchase.swift in Sources */, + FF3366F52D403D1F0012D1B4 /* Screen.swift in Sources */, + FF3366F62D403D1F0012D1B4 /* Round.swift in Sources */, + FF3366F72D403D1F0012D1B4 /* RegistrationSetupView.swift in Sources */, + FF3366F82D403D1F0012D1B4 /* FederalDataViewModel.swift in Sources */, + FF3366F92D403D1F0012D1B4 /* AgendaDestination.swift in Sources */, + FF3366FA2D403D1F0012D1B4 /* PadelClubApp.xcdatamodeld in Sources */, + FF3366FB2D403D1F0012D1B4 /* SetInputView.swift in Sources */, + FF3366FC2D403D1F0012D1B4 /* ButtonValidateView.swift in Sources */, + FF3366FD2D403D1F0012D1B4 /* ClubRowView.swift in Sources */, + FF3366FE2D403D1F0012D1B4 /* ClubDetailView.swift in Sources */, + FF3366FF2D403D1F0012D1B4 /* GroupStageCallingView.swift in Sources */, + FF3367002D403D1F0012D1B4 /* CryptoKey.swift in Sources */, + FF3367012D403D1F0012D1B4 /* CashierSettingsView.swift in Sources */, + FF3367022D403D1F0012D1B4 /* LoserRoundScheduleEditorView.swift in Sources */, + FF3367032D403D1F0012D1B4 /* Club.swift in Sources */, + FF3367042D403D1F0012D1B4 /* Array+Extensions.swift in Sources */, + FF3367052D403D1F0012D1B4 /* ToolboxView.swift in Sources */, + FF3367062D403D1F0012D1B4 /* Alphabet.swift in Sources */, + FF3367072D403D1F0012D1B4 /* String+Extensions.swift in Sources */, + FF3367082D403D1F0012D1B4 /* GroupStageSettingsView.swift in Sources */, + FF3367092D403D1F0012D1B4 /* TournamentGeneralSettingsView.swift in Sources */, + FF33670A2D403D1F0012D1B4 /* LoserRoundView.swift in Sources */, + FF33670B2D403D1F0012D1B4 /* AddTeamView.swift in Sources */, + FF33670C2D403D1F0012D1B4 /* PlayerPayView.swift in Sources */, + FF33670D2D403D1F0012D1B4 /* PlanningByCourtView.swift in Sources */, + FF33670E2D403D1F0012D1B4 /* FileImportManager.swift in Sources */, + FF33670F2D403D1F0012D1B4 /* OngoingContainerView.swift in Sources */, + FF3367102D403D1F0012D1B4 /* TournamentButtonView.swift in Sources */, + FF3367112D403D1F0012D1B4 /* DrawLogsView.swift in Sources */, + FF3367122D403D1F0012D1B4 /* FederalPlayer.swift in Sources */, + FF3367132D403D1F0012D1B4 /* NavigationViewModel.swift in Sources */, + FF3367142D403D1F0012D1B4 /* FixedWidthInteger+Extensions.swift in Sources */, + FF3367152D403D1F0012D1B4 /* LoserBracketFromGroupStageView.swift in Sources */, + FF3367162D403D1F0012D1B4 /* ImportedPlayer+Extensions.swift in Sources */, + FF3367172D403D1F0012D1B4 /* ClubSearchView.swift in Sources */, + FF3367182D403D1F0012D1B4 /* DateMenuView.swift in Sources */, + FF3367192D403D1F0012D1B4 /* PlayerPopoverView.swift in Sources */, + FF33671A2D403D1F0012D1B4 /* InscriptionManagerView.swift in Sources */, + FF33671B2D403D1F0012D1B4 /* MatchFormatPickingView.swift in Sources */, + FF33671C2D403D1F0012D1B4 /* ActivityView.swift in Sources */, + FF33671D2D403D1F0012D1B4 /* MySortDescriptor.swift in Sources */, + FF33671E2D403D1F0012D1B4 /* CalendarView.swift in Sources */, + FF33671F2D403D1F0012D1B4 /* FederalTournamentSearchScope.swift in Sources */, + FF3367202D403D1F0012D1B4 /* TournamentFieldsManagerView.swift in Sources */, + FF3367212D403D1F0012D1B4 /* PrintSettingsView.swift in Sources */, + FF3367222D403D1F0012D1B4 /* TournamentMatchFormatsSettingsView.swift in Sources */, + FF3367232D403D1F0012D1B4 /* DatePickingView.swift in Sources */, + FF3367242D403D1F0012D1B4 /* MatchFormatRowView.swift in Sources */, + FF3367252D403D1F0012D1B4 /* MonthData.swift in Sources */, + FF3367262D403D1F0012D1B4 /* MenuWarningView.swift in Sources */, + FF3367272D403D1F0012D1B4 /* TournamentBuildView.swift in Sources */, + FF3367282D403D1F0012D1B4 /* TeamPickerView.swift in Sources */, + FF3367292D403D1F0012D1B4 /* CloudConvert.swift in Sources */, + FF33672A2D403D1F0012D1B4 /* EventTournamentsView.swift in Sources */, + FF33672B2D403D1F0012D1B4 /* DisplayContext.swift in Sources */, + FF33672C2D403D1F0012D1B4 /* MultiCourtPickerView.swift in Sources */, + FF33672D2D403D1F0012D1B4 /* TournamentCallView.swift in Sources */, + FF33672E2D403D1F0012D1B4 /* LoserRoundsView.swift in Sources */, + FF33672F2D403D1F0012D1B4 /* GroupStagesView.swift in Sources */, + FF3367302D403D1F0012D1B4 /* PadelClubView.swift in Sources */, + FF3367312D403D1F0012D1B4 /* RegistrationInfoSheetView.swift in Sources */, + FF3367322D403D1F0012D1B4 /* URLs.swift in Sources */, + FF3367332D403D1F0012D1B4 /* MatchDescriptor.swift in Sources */, + FF3367342D403D1F0012D1B4 /* TournamentFormatSelectionView.swift in Sources */, + FF3367352D403D1F0012D1B4 /* CoachListView.swift in Sources */, + FF3367362D403D1F0012D1B4 /* MatchListView.swift in Sources */, + FF3367372D403D1F0012D1B4 /* PadelClubApp.swift in Sources */, + FF3367382D403D1F0012D1B4 /* TournamentSettingsView.swift in Sources */, + FF3367392D403D1F0012D1B4 /* String+Crypto.swift in Sources */, + FF33673A2D403D1F0012D1B4 /* GroupStageTeamReplacementView.swift in Sources */, + FF33673B2D403D1F0012D1B4 /* TabItemModifier.swift in Sources */, + FF33673C2D403D1F0012D1B4 /* DeferredViewModifier.swift in Sources */, + FF33673D2D403D1F0012D1B4 /* TournamentScheduleView.swift in Sources */, + FF33673E2D403D1F0012D1B4 /* MatchFormatStorageView.swift in Sources */, + FF33673F2D403D1F0012D1B4 /* UmpireView.swift in Sources */, + FF3367402D403D1F0012D1B4 /* User.swift in Sources */, + FF3367412D403D1F0012D1B4 /* CodingContainer+Extensions.swift in Sources */, + FF3367422D403D1F0012D1B4 /* MatchSummaryView.swift in Sources */, + FF3367432D403D1F0012D1B4 /* OngoingDestination.swift in Sources */, + FF3367442D403D1F0012D1B4 /* TournamentDurationManagerView.swift in Sources */, + FF3367452D403D1F0012D1B4 /* MockData.swift in Sources */, + FF3367462D403D1F0012D1B4 /* TeamDetailView.swift in Sources */, + FF3367472D403D1F0012D1B4 /* PlayerStatisticView.swift in Sources */, + FF3367482D403D1F0012D1B4 /* GroupStagesSettingsView.swift in Sources */, + FF3367492D403D1F0012D1B4 /* TournamentFilterView.swift in Sources */, + FF33674A2D403D1F0012D1B4 /* PreviewBracketPositionView.swift in Sources */, + FF33674B2D403D1F0012D1B4 /* HtmlGenerator.swift in Sources */, + FF33674C2D403D1F0012D1B4 /* BracketCallingView.swift in Sources */, + FF33674D2D403D1F0012D1B4 /* PadelRule.swift in Sources */, + FF33674E2D403D1F0012D1B4 /* TeamRegistration.swift in Sources */, + FF33674F2D403D1F0012D1B4 /* Date+Extensions.swift in Sources */, + FF3367502D403D1F0012D1B4 /* GroupStageView.swift in Sources */, + FF3367512D403D1F0012D1B4 /* Tips.swift in Sources */, + FF3367522D403D1F0012D1B4 /* MainView.swift in Sources */, + FF3367532D403D1F0012D1B4 /* MatchDateView.swift in Sources */, + FF3367542D403D1F0012D1B4 /* PlanningSettingsView.swift in Sources */, + FF3367552D403D1F0012D1B4 /* MatchScheduleEditorView.swift in Sources */, + FF3367562D403D1F0012D1B4 /* FortuneWheelView.swift in Sources */, + FF3367572D403D1F0012D1B4 /* URL+Extensions.swift in Sources */, + FF3367582D403D1F0012D1B4 /* LoadingViewModifier.swift in Sources */, + FF3367592D403D1F0012D1B4 /* PlayerView.swift in Sources */, + FF33675A2D403D1F0012D1B4 /* MatchDetailView.swift in Sources */, + FF33675B2D403D1F0012D1B4 /* ExportFormat.swift in Sources */, + FF33675C2D403D1F0012D1B4 /* PlayerBlockView.swift in Sources */, + FF33675D2D403D1F0012D1B4 /* StoreItem.swift in Sources */, + FF33675E2D403D1F0012D1B4 /* Selectable.swift in Sources */, + FF33675F2D403D1F0012D1B4 /* PointView.swift in Sources */, + FF3367602D403D1F0012D1B4 /* ClubHolder.swift in Sources */, + FF3367612D403D1F0012D1B4 /* EventSettingsView.swift in Sources */, + FF3367622D403D1F0012D1B4 /* InscriptionInfoView.swift in Sources */, + FF3367632D403D1F0012D1B4 /* SelectablePlayerListView.swift in Sources */, + FF3367642D403D1F0012D1B4 /* MatchFormatSelectionView.swift in Sources */, + FF3367652D403D1F0012D1B4 /* TournamentRankView.swift in Sources */, + FF3367662D403D1F0012D1B4 /* GroupStageDatePickingView.swift in Sources */, + FF3367672D403D1F0012D1B4 /* NumberFormatter+Extensions.swift in Sources */, + FF3367682D403D1F0012D1B4 /* SetLabelView.swift in Sources */, + FF3367692D403D1F0012D1B4 /* DebugSettingsView.swift in Sources */, + FF33676A2D403D1F0012D1B4 /* GroupStageScheduleEditorView.swift in Sources */, + FF33676B2D403D1F0012D1B4 /* EventView.swift in Sources */, + FF33676C2D403D1F0012D1B4 /* LoginView.swift in Sources */, + FF33676D2D403D1F0012D1B4 /* Court.swift in Sources */, + FF33676E2D403D1F0012D1B4 /* Labels.swift in Sources */, + FF33676F2D403D1F0012D1B4 /* CopyPasteButtonView.swift in Sources */, + FF3367702D403D1F0012D1B4 /* PlayerSexPickerView.swift in Sources */, + FF3367712D403D1F0012D1B4 /* TournamentInscriptionView.swift in Sources */, + FF3367722D403D1F0012D1B4 /* CallSettingsView.swift in Sources */, + FF3367732D403D1F0012D1B4 /* FooterButtonView.swift in Sources */, + FF3367742D403D1F0012D1B4 /* RankCalculatorView.swift in Sources */, + FF3367752D403D1F0012D1B4 /* DateBoxView.swift in Sources */, + FF3367762D403D1F0012D1B4 /* LearnMoreSheetView.swift in Sources */, + FF3367772D403D1F0012D1B4 /* SourceFileManager.swift in Sources */, + FF3367782D403D1F0012D1B4 /* TournamentCategorySettingsView.swift in Sources */, + FF3367792D403D1F0012D1B4 /* PListReader.swift in Sources */, + FF33677A2D403D1F0012D1B4 /* UpdateSourceRankDateView.swift in Sources */, + FF33677B2D403D1F0012D1B4 /* GlobalSettingsView.swift in Sources */, + FF33677C2D403D1F0012D1B4 /* Guard.swift in Sources */, + FF33677D2D403D1F0012D1B4 /* PurchaseListView.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; FF4CBF432C996C0600151637 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -3302,7 +3926,7 @@ CODE_SIGN_ENTITLEMENTS = PadelClub/PadelClub.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\""; @@ -3350,7 +3974,7 @@ CODE_SIGN_ENTITLEMENTS = PadelClub/PadelClub.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEFINES_MODULE = YES; DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\""; DEVELOPMENT_TEAM = BQ3Y44M3Q6; @@ -3461,6 +4085,92 @@ }; name = Release; }; + FF3367982D403D1F0012D1B4 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_ENTITLEMENTS = PadelClub/PadelClub.entitlements; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEFINES_MODULE = YES; + DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\""; + DEVELOPMENT_TEAM = BQ3Y44M3Q6; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = "PadelClub TestFlight copy-Info.plist"; + INFOPLIST_KEY_CFBundleDisplayName = "Padel Club (Beta)"; + INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.sports"; + INFOPLIST_KEY_LSSupportsOpeningDocumentsInPlace = YES; + INFOPLIST_KEY_NSCalendarsUsageDescription = "Padel Club a besoin d'avoir accès à votre calendrier pour pouvoir y inscrire ce tournoi"; + INFOPLIST_KEY_NSCameraUsageDescription = "En autorisant l'application à utiliser la caméra, vous pourrez prendre des photos des rencontres"; + INFOPLIST_KEY_NSLocationWhenInUseUsageDescription = "Padel Club a besoin de votre position pour rechercher les clubs autour de vous."; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchStoryboardName = "Launch Screen"; + INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; + IPHONEOS_DEPLOYMENT_TARGET = 17.1; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0.34; + PRODUCT_BUNDLE_IDENTIFIER = app.padelclub.beta; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + FF3367992D403D1F0012D1B4 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_ENTITLEMENTS = PadelClub/PadelClub.entitlements; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\""; + DEVELOPMENT_TEAM = BQ3Y44M3Q6; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = "PadelClub TestFlight copy-Info.plist"; + INFOPLIST_KEY_CFBundleDisplayName = "Padel Club (Beta)"; + INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.sports"; + INFOPLIST_KEY_LSSupportsOpeningDocumentsInPlace = YES; + INFOPLIST_KEY_NSCalendarsUsageDescription = "Padel Club a besoin d'avoir accès à votre calendrier pour pouvoir y inscrire ce tournoi"; + INFOPLIST_KEY_NSCameraUsageDescription = "En autorisant l'application à utiliser la caméra, vous pourrez prendre des photos des rencontres"; + INFOPLIST_KEY_NSLocationWhenInUseUsageDescription = "Padel Club a besoin de votre position pour rechercher les clubs autour de vous."; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchStoryboardName = "Launch Screen"; + INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; + IPHONEOS_DEPLOYMENT_TARGET = 17.1; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0.34; + PRODUCT_BUNDLE_IDENTIFIER = app.padelclub.beta; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = PRODTEST; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; FF4CC04E2C996C0600151637 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -3682,6 +4392,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + FF3367972D403D1F0012D1B4 /* Build configuration list for PBXNativeTarget "ProdDev" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FF3367982D403D1F0012D1B4 /* Debug */, + FF3367992D403D1F0012D1B4 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; FF4CC04D2C996C0600151637 /* Build configuration list for PBXNativeTarget "ProdTest PadelClub" */ = { isa = XCConfigurationList; buildConfigurations = ( @@ -3703,6 +4422,22 @@ /* End XCConfigurationList section */ /* Begin XCRemoteSwiftPackageReference section */ + FF3366742D403D1F0012D1B4 /* XCRemoteSwiftPackageReference "swift-algorithms" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/apple/swift-algorithms.git"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 1.2.0; + }; + }; + FF3366762D403D1F0012D1B4 /* XCRemoteSwiftPackageReference "Zip" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/marmelroy/Zip"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 2.1.2; + }; + }; FF4C7F052BBBE6B90031B6A3 /* XCRemoteSwiftPackageReference "swift-algorithms" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/apple/swift-algorithms.git"; @@ -3754,6 +4489,16 @@ /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ + FF3366732D403D1F0012D1B4 /* Algorithms */ = { + isa = XCSwiftPackageProductDependency; + package = FF3366742D403D1F0012D1B4 /* XCRemoteSwiftPackageReference "swift-algorithms" */; + productName = Algorithms; + }; + FF3366752D403D1F0012D1B4 /* Zip */ = { + isa = XCSwiftPackageProductDependency; + package = FF3366762D403D1F0012D1B4 /* XCRemoteSwiftPackageReference "Zip" */; + productName = Zip; + }; FF4CBF3F2C996C0600151637 /* Algorithms */ = { isa = XCSwiftPackageProductDependency; package = FF4CBF402C996C0600151637 /* XCRemoteSwiftPackageReference "swift-algorithms" */; From 2ebfb79713e747e552d81833aba25cf2d458a65d Mon Sep 17 00:00:00 2001 From: Raz Date: Wed, 22 Jan 2025 11:06:28 +0100 Subject: [PATCH 38/57] =?UTF-8?q?forcer=20le=20score=20du=202eme=20=C3=A0?= =?UTF-8?q?=200=20si=20on=20valide=20sans=20finir=20de=20cliquer=20sur=20l?= =?UTF-8?q?e=20num=20pad=20(edit=20score)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- PadelClub/Data/Match.swift | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/PadelClub/Data/Match.swift b/PadelClub/Data/Match.swift index 389543a..7b89728 100644 --- a/PadelClub/Data/Match.swift +++ b/PadelClub/Data/Match.swift @@ -603,6 +603,13 @@ defer { teamScoreOne.score = matchDescriptor.teamOneScores.joined(separator: ",") let teamScoreTwo = teamScore(.two) ?? TeamScore(match: id, team: team(.two)) teamScoreTwo.score = matchDescriptor.teamTwoScores.joined(separator: ",") + + if matchDescriptor.teamOneScores.last?.contains("-") == true && matchDescriptor.teamTwoScores.last?.contains("-") == false { + teamScoreTwo.score = (matchDescriptor.teamTwoScores.dropLast() + [matchDescriptor.teamTwoScores.last! + "-0"]).joined(separator: ",") + } else if matchDescriptor.teamTwoScores.last?.contains("-") == true && matchDescriptor.teamOneScores.last?.contains("-") == false { + teamScoreOne.score = (matchDescriptor.teamOneScores.dropLast() + [matchDescriptor.teamOneScores.last! + "-0"]).joined(separator: ",") + } + do { try self.tournamentStore.teamScores.addOrUpdate(contentOfs: [teamScoreOne, teamScoreTwo]) } catch { From ca6b56f2f9dfa1ec11399f1c5c3fcc8984bd314d Mon Sep 17 00:00:00 2001 From: Raz Date: Wed, 22 Jan 2025 11:32:09 +0100 Subject: [PATCH 39/57] enable realtime final ranking update --- PadelClub/Data/GroupStage.swift | 2 ++ PadelClub/Data/Match.swift | 3 --- PadelClub/Data/Round.swift | 5 +++- PadelClub/Data/Tournament.swift | 24 +++++++++---------- .../Tournament/TournamentBuildView.swift | 2 +- .../Views/Tournament/TournamentView.swift | 17 +++++++++++++ 6 files changed, 36 insertions(+), 17 deletions(-) diff --git a/PadelClub/Data/GroupStage.swift b/PadelClub/Data/GroupStage.swift index 75e6946..c84ab31 100644 --- a/PadelClub/Data/GroupStage.swift +++ b/PadelClub/Data/GroupStage.swift @@ -239,6 +239,8 @@ final class GroupStage: ModelObject, Storable { Logger.error(error) } } + + tournament.updateTournamentState() } } diff --git a/PadelClub/Data/Match.swift b/PadelClub/Data/Match.swift index 7b89728..a71580d 100644 --- a/PadelClub/Data/Match.swift +++ b/PadelClub/Data/Match.swift @@ -232,7 +232,6 @@ defer { servingTeamId = nil groupStageObject?.updateGroupStageState() roundObject?.updateTournamentState() - currentTournament()?.updateTournamentState() teams().forEach({ $0.resetRestingTime() }) } @@ -554,7 +553,6 @@ defer { losingTeamId = teamScoreWalkout.teamRegistration groupStageObject?.updateGroupStageState() roundObject?.updateTournamentState() - currentTournament()?.updateTournamentState() updateFollowingMatchTeamScore() } @@ -593,7 +591,6 @@ defer { } catch { Logger.error(error) } - tournament.updateTournamentState() } updateFollowingMatchTeamScore() } diff --git a/PadelClub/Data/Round.swift b/PadelClub/Data/Round.swift index c9a8669..38355c2 100644 --- a/PadelClub/Data/Round.swift +++ b/PadelClub/Data/Round.swift @@ -584,7 +584,8 @@ defer { } func updateTournamentState() { - if let tournamentObject = tournamentObject(), index == 0, isUpperBracket(), hasEnded() { + let tournamentObject = tournamentObject() + if let tournamentObject, index == 0, isUpperBracket(), hasEnded() { tournamentObject.endDate = Date() do { try DataStore.shared.tournaments.addOrUpdate(instance: tournamentObject) @@ -592,6 +593,8 @@ defer { Logger.error(error) } } + + tournamentObject?.updateTournamentState() } func roundStatus() -> String { diff --git a/PadelClub/Data/Tournament.swift b/PadelClub/Data/Tournament.swift index cafe5b5..f4e2347 100644 --- a/PadelClub/Data/Tournament.swift +++ b/PadelClub/Data/Tournament.swift @@ -1403,14 +1403,16 @@ defer { groupStages.forEach { groupStage in let groupStageTeams = groupStage.teams(true) for (index, team) in groupStageTeams.enumerated() { - if team.qualified == false && alreadyPlaceTeams.contains(team.id) == false { - let groupStageWidth = max(((index == qualifiedPerGroupStage) ? groupStageCount - groupStageAdditionalQualified : groupStageCount) * (index - qualifiedPerGroupStage), 0) - - let _index = baseRank + groupStageWidth + 1 - (index > qualifiedPerGroupStage ? groupStageAdditionalQualified : 0) - if let existingTeams = teams[_index] { - teams[_index] = existingTeams + [team.id] - } else { - teams[_index] = [team.id] + if groupStage.hasEnded() { + if team.qualified == false && alreadyPlaceTeams.contains(team.id) == false { + let groupStageWidth = max(((index == qualifiedPerGroupStage) ? groupStageCount - groupStageAdditionalQualified : groupStageCount) * (index - qualifiedPerGroupStage), 0) + + let _index = baseRank + groupStageWidth + 1 - (index > qualifiedPerGroupStage ? groupStageAdditionalQualified : 0) + if let existingTeams = teams[_index] { + teams[_index] = existingTeams + [team.id] + } else { + teams[_index] = [team.id] + } } } } @@ -2347,10 +2349,8 @@ defer { func updateTournamentState() { Task { - if hasEnded() { - let fr = await finalRanking() - _ = await setRankings(finalRanks: fr) - } + let fr = await finalRanking() + _ = await setRankings(finalRanks: fr) } } diff --git a/PadelClub/Views/Tournament/TournamentBuildView.swift b/PadelClub/Views/Tournament/TournamentBuildView.swift index b76002c..92e23b0 100644 --- a/PadelClub/Views/Tournament/TournamentBuildView.swift +++ b/PadelClub/Views/Tournament/TournamentBuildView.swift @@ -113,7 +113,7 @@ struct TournamentBuildView: View { Section { - if tournament.hasEnded() { + if true { NavigationLink(value: Screen.rankings) { LabeledContent { if tournament.publishRankings == false { diff --git a/PadelClub/Views/Tournament/TournamentView.swift b/PadelClub/Views/Tournament/TournamentView.swift index 2ee9907..f2a3ec4 100644 --- a/PadelClub/Views/Tournament/TournamentView.swift +++ b/PadelClub/Views/Tournament/TournamentView.swift @@ -236,6 +236,23 @@ struct TournamentView: View { NavigationLink(value: Screen.structure) { LabelStructure() } + + NavigationLink(value: Screen.rankings) { + LabeledContent { + if tournament.publishRankings == false { + Image(systemName: "exclamationmark.circle.fill") + .foregroundStyle(.logoYellow) + } else { + Image(systemName: "checkmark") + .foregroundStyle(.green) + } + } label: { + Text("Classement final des équipes") + if tournament.publishRankings == false { + Text("Vérifiez le classement avant de publier").foregroundStyle(.logoRed) + } + } + } NavigationLink(value: Screen.broadcast) { Label("Publication", systemImage: "airplayvideo") From a7e4072cc9b60eda4959cb2fee68f1a7fe36b7af Mon Sep 17 00:00:00 2001 From: Raz Date: Wed, 22 Jan 2025 11:45:07 +0100 Subject: [PATCH 40/57] version bump --- PadelClub.xcodeproj/project.pbxproj | 757 +--------------------------- 1 file changed, 6 insertions(+), 751 deletions(-) diff --git a/PadelClub.xcodeproj/project.pbxproj b/PadelClub.xcodeproj/project.pbxproj index 2aa8816..af84379 100644 --- a/PadelClub.xcodeproj/project.pbxproj +++ b/PadelClub.xcodeproj/project.pbxproj @@ -124,290 +124,6 @@ FF2B51612C7E302C00FFF126 /* local.sqlite in Resources */ = {isa = PBXBuildFile; fileRef = FF2B51602C7E302C00FFF126 /* local.sqlite */; }; FF2B6F5E2C036A1500835EE7 /* EventLinksView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF2B6F5D2C036A1400835EE7 /* EventLinksView.swift */; }; FF2EFBF02BDE295E0049CE3B /* SendToAllView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF2EFBEF2BDE295E0049CE3B /* SendToAllView.swift */; }; - FF3366782D403D1F0012D1B4 /* UserCreationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47D862B7BA36D00ADC637 /* UserCreationView.swift */; }; - FF3366792D403D1F0012D1B4 /* TabDestination.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF7091652B90F0B000AB08DA /* TabDestination.swift */; }; - FF33667A2D403D1F0012D1B4 /* CashierView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF9267F72BCE78C70080F940 /* CashierView.swift */; }; - FF33667B2D403D1F0012D1B4 /* Event.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8F263E2BAD7D5C00650388 /* Event.swift */; }; - FF33667C2D403D1F0012D1B4 /* DatePickingViewWithFormat.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF77CE552CCCD1EB00CBCBB4 /* DatePickingViewWithFormat.swift */; }; - FF33667D2D403D1F0012D1B4 /* PlayerHolder.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5D30522BD94E2E00F2B93D /* PlayerHolder.swift */; }; - FF33667E2D403D1F0012D1B4 /* LoserRoundStepScheduleEditorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF11628B2BD05267000C4809 /* LoserRoundStepScheduleEditorView.swift */; }; - FF33667F2D403D1F0012D1B4 /* ClubCourtSetupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF53FBB72BFB302B0051D4C3 /* ClubCourtSetupView.swift */; }; - FF3366802D403D1F0012D1B4 /* Patcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B3A1542C2581DA0078EAA8 /* Patcher.swift */; }; - FF3366812D403D1F0012D1B4 /* FileImportView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF089EBE2BB0B14600F0AEC7 /* FileImportView.swift */; }; - FF3366822D403D1F0012D1B4 /* StepperView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47D9E2B7D0BCE00ADC637 /* StepperView.swift */; }; - FF3366832D403D1F0012D1B4 /* RoundsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFC83D4E2BB807D100750834 /* RoundsView.swift */; }; - FF3366842D403D1F0012D1B4 /* FederalTournament.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1CBC182BB53D1F0036DAAB /* FederalTournament.swift */; }; - FF3366852D403D1F0012D1B4 /* TournamentInitView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8F26402BADFC8700650388 /* TournamentInitView.swift */; }; - FF3366862D403D1F0012D1B4 /* SubscriptionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47D892B7BBB6500ADC637 /* SubscriptionView.swift */; }; - FF3366872D403D1F0012D1B4 /* TournamentLookUpView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFD655D72C8DE27400E5B35E /* TournamentLookUpView.swift */; }; - FF3366882D403D1F0012D1B4 /* ClubsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1DC5562BAB3AED00FD8220 /* ClubsView.swift */; }; - FF3366892D403D1F0012D1B4 /* ImagePickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFE103112C366E5900684FC9 /* ImagePickerView.swift */; }; - FF33668A2D403D1F0012D1B4 /* MatchTypeSelectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8F264E2BAE0B9600650388 /* MatchTypeSelectionView.swift */; }; - FF33668B2D403D1F0012D1B4 /* MatchSetupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967D052BAF3C4200A9A3BD /* MatchSetupView.swift */; }; - FF33668C2D403D1F0012D1B4 /* NetworkManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF4AB6B42B9248200002987F /* NetworkManager.swift */; }; - FF33668D2D403D1F0012D1B4 /* EventLinksView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF2B6F5D2C036A1400835EE7 /* EventLinksView.swift */; }; - FF33668E2D403D1F0012D1B4 /* SeedInterval.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFB9C8742BBADDF700A0EF4F /* SeedInterval.swift */; }; - FF33668F2D403D1F0012D1B4 /* TournamentClubSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF025AE02BD0EB9000A86CF8 /* TournamentClubSettingsView.swift */; }; - FF3366902D403D1F0012D1B4 /* GroupStageTeamView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFBF065B2BBD2657009D6715 /* GroupStageTeamView.swift */; }; - FF3366912D403D1F0012D1B4 /* RoundSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5DA1922BB9279B00A33061 /* RoundSettingsView.swift */; }; - FF3366922D403D1F0012D1B4 /* MatchViewStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFBE62042CE9DA0900815D33 /* MatchViewStyle.swift */; }; - FF3366932D403D1F0012D1B4 /* SupportButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFE2D2E12C231BEE00D0C7BE /* SupportButtonView.swift */; }; - FF3366942D403D1F0012D1B4 /* TournamentBroadcastRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFB1C98A2C10255100B154A7 /* TournamentBroadcastRowView.swift */; }; - FF3366952D403D1F0012D1B4 /* TeamWeightView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF025ADE2BD0CE0A00A86CF8 /* TeamWeightView.swift */; }; - FF3366962D403D1F0012D1B4 /* SeedsCallingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF9268002BCE94920080F940 /* SeedsCallingView.swift */; }; - FF3366972D403D1F0012D1B4 /* CallView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF9268082BCEDC2C0080F940 /* CallView.swift */; }; - FF3366982D403D1F0012D1B4 /* Color+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5D0D732BB41DF8005CB568 /* Color+Extensions.swift */; }; - FF3366992D403D1F0012D1B4 /* EditSharingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFE1030F2C366DCD00684FC9 /* EditSharingView.swift */; }; - FF33669A2D403D1F0012D1B4 /* TournamentCellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF7091672B90F79F00AB08DA /* TournamentCellView.swift */; }; - FF33669B2D403D1F0012D1B4 /* Sequence+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF6EC9032B9479F500EA7F5A /* Sequence+Extensions.swift */; }; - FF33669C2D403D1F0012D1B4 /* CashierDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF9267F92BCE78EB0080F940 /* CashierDetailView.swift */; }; - FF33669D2D403D1F0012D1B4 /* EventClubSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFE103072C353B7600684FC9 /* EventClubSettingsView.swift */; }; - FF33669E2D403D1F0012D1B4 /* AccountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47DB22B86387500ADC637 /* AccountView.swift */; }; - FF33669F2D403D1F0012D1B4 /* PlayersWithoutContactView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFCEDA4B2C2C08EA00F8C0F2 /* PlayersWithoutContactView.swift */; }; - FF3366A02D403D1F0012D1B4 /* FollowUpMatchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF17CA4C2CB9243E003C7323 /* FollowUpMatchView.swift */; }; - FF3366A12D403D1F0012D1B4 /* TeamsCallingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFCD16B22C3E5E590092707B /* TeamsCallingView.swift */; }; - FF3366A22D403D1F0012D1B4 /* Calendar+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1CBC1C2BB53DC10036DAAB /* Calendar+Extensions.swift */; }; - FF3366A32D403D1F0012D1B4 /* TeamScore.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967CEF2BAECC0A00A9A3BD /* TeamScore.swift */; }; - FF3366A42D403D1F0012D1B4 /* EditablePlayerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1162822BCFBE4E000C4809 /* EditablePlayerView.swift */; }; - FF3366A52D403D1F0012D1B4 /* PlayerDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1162842BD00279000C4809 /* PlayerDetailView.swift */; }; - FF3366A62D403D1F0012D1B4 /* UmpireStatisticView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFA252AC2CDB734A0074E63F /* UmpireStatisticView.swift */; }; - FF3366A72D403D1F0012D1B4 /* ListRowViewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5D0D752BB428B2005CB568 /* ListRowViewModifier.swift */; }; - FF3366A82D403D1F0012D1B4 /* PresentationContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF6EC8FF2B94794700EA7F5A /* PresentationContext.swift */; }; - FF3366A92D403D1F0012D1B4 /* AppSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFDB1C6C2BB2A02000F1E467 /* AppSettings.swift */; }; - FF3366AA2D403D1F0012D1B4 /* SwiftParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF0EC51D2BB16F680056B6D1 /* SwiftParser.swift */; }; - FF3366AB2D403D1F0012D1B4 /* ChangePasswordView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47DA82B85F82100ADC637 /* ChangePasswordView.swift */; }; - FF3366AC2D403D1F0012D1B4 /* TournamentSubscriptionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8044AB2C8F676D00A49A52 /* TournamentSubscriptionView.swift */; }; - FF3366AD2D403D1F0012D1B4 /* RoundView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFC83D502BB8087E00750834 /* RoundView.swift */; }; - FF3366AE2D403D1F0012D1B4 /* RowButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF6EC8F62B94773100EA7F5A /* RowButtonView.swift */; }; - FF3366AF2D403D1F0012D1B4 /* SendToAllView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF2EFBEF2BDE295E0049CE3B /* SendToAllView.swift */; }; - FF3366B02D403D1F0012D1B4 /* EventCreationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8F263A2BAD528600650388 /* EventCreationView.swift */; }; - FF3366B12D403D1F0012D1B4 /* LocationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFC1E1072BAC29FC008D6F59 /* LocationManager.swift */; }; - FF3366B22D403D1F0012D1B4 /* CapsuleViewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C01D972C481C0C0059087C /* CapsuleViewModifier.swift */; }; - FF3366B32D403D1F0012D1B4 /* BroadcastView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF6087EB2BE26A2F004E1E47 /* BroadcastView.swift */; }; - FF3366B42D403D1F0012D1B4 /* SchedulerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF964542BC266CF00EEF017 /* SchedulerView.swift */; }; - FF3366B52D403D1F0012D1B4 /* PadelClubButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFA1B1282BB71773006CE248 /* PadelClubButtonView.swift */; }; - FF3366B62D403D1F0012D1B4 /* TournamentSeedEditing.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5DA19A2BB9662200A33061 /* TournamentSeedEditing.swift */; }; - FF3366B72D403D1F0012D1B4 /* TournamentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF70916B2B91005400AB08DA /* TournamentView.swift */; }; - FF3366B82D403D1F0012D1B4 /* OngoingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5D30552BD95B1100F2B93D /* OngoingView.swift */; }; - FF3366B92D403D1F0012D1B4 /* CreateClubView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1DC5542BAB36DD00FD8220 /* CreateClubView.swift */; }; - FF3366BA2D403D1F0012D1B4 /* APICallsListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4607A7C2C04DDE2004CB781 /* APICallsListView.swift */; }; - FF3366BB2D403D1F0012D1B4 /* TeamRestingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF7DCD382CC330260041110C /* TeamRestingView.swift */; }; - FF3366BC2D403D1F0012D1B4 /* NetworkFederalService.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFC1E1092BAC2A77008D6F59 /* NetworkFederalService.swift */; }; - FF3366BD2D403D1F0012D1B4 /* DurationSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF025AEE2BD1AE9400A86CF8 /* DurationSettingsView.swift */; }; - FF3366BE2D403D1F0012D1B4 /* AppScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF025AEC2BD1513700A86CF8 /* AppScreen.swift */; }; - FF3366BF2D403D1F0012D1B4 /* CourtView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFC91B022BD85E2400B29808 /* CourtView.swift */; }; - FF3366C02D403D1F0012D1B4 /* PointSelectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFCFC00D2BBC3D4600B82851 /* PointSelectionView.swift */; }; - FF3366C12D403D1F0012D1B4 /* TeamRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF089EB52BB00A3800F0AEC7 /* TeamRowView.swift */; }; - FF3366C22D403D1F0012D1B4 /* ContactManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF92680A2BCEE3E10080F940 /* ContactManager.swift */; }; - FF3366C32D403D1F0012D1B4 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40CD2F22C412681000DBD9A /* AppDelegate.swift */; }; - FF3366C42D403D1F0012D1B4 /* SubscriptionInfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49EF0252BD80AE80077B5AA /* SubscriptionInfoView.swift */; }; - FF3366C52D403D1F0012D1B4 /* EditScoreView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFCFC0012BBC39A600B82851 /* EditScoreView.swift */; }; - FF3366C62D403D1F0012D1B4 /* TournamentOrganizerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF7091612B90F04300AB08DA /* TournamentOrganizerView.swift */; }; - FF3366C72D403D1F0012D1B4 /* NetworkMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF92680C2BCEE5EA0080F940 /* NetworkMonitor.swift */; }; - FF3366C82D403D1F0012D1B4 /* TournamentRunningView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967CF52BAED51600A9A3BD /* TournamentRunningView.swift */; }; - FF3366C92D403D1F0012D1B4 /* TournamentDatePickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8F264A2BAE0B4100650388 /* TournamentDatePickerView.swift */; }; - FF3366CA2D403D1F0012D1B4 /* CourtAvailabilitySettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF116E22BD2AF4800A33B06 /* CourtAvailabilitySettingsView.swift */; }; - FF3366CB2D403D1F0012D1B4 /* ConfirmButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFE8C2BF2C7601E80046B243 /* ConfirmButtonView.swift */; }; - FF3366CC2D403D1F0012D1B4 /* PasswordField.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4FC2E262C2AABC90021F3BF /* PasswordField.swift */; }; - FF3366CD2D403D1F0012D1B4 /* MatchRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967CFF2BAEEF6400A9A3BD /* MatchRowView.swift */; }; - FF3366CE2D403D1F0012D1B4 /* Locale+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44B79102BBDA63A00906534 /* Locale+Extensions.swift */; }; - FF3366CF2D403D1F0012D1B4 /* HtmlService.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1F4B732BFA00FC000B4573 /* HtmlService.swift */; }; - FF3366D02D403D1F0012D1B4 /* GroupStage.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967CE72BAEC70100A9A3BD /* GroupStage.swift */; }; - FF3366D12D403D1F0012D1B4 /* DrawLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF6761522CC77D1900CC9BF2 /* DrawLog.swift */; }; - FF3366D22D403D1F0012D1B4 /* TournamentCashierView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1162802BCF945C000C4809 /* TournamentCashierView.swift */; }; - FF3366D32D403D1F0012D1B4 /* StoreManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47D8D2B7BBBEC00ADC637 /* StoreManager.swift */; }; - FF3366D42D403D1F0012D1B4 /* SearchViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF4AB6BA2B9256D50002987F /* SearchViewModel.swift */; }; - FF3366D52D403D1F0012D1B4 /* PlayerRegistration.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967CF12BAECC0B00A9A3BD /* PlayerRegistration.swift */; }; - FF3366D62D403D1F0012D1B4 /* ImportedPlayerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF4AB6BE2B92577A0002987F /* ImportedPlayerView.swift */; }; - FF3366D72D403D1F0012D1B4 /* EditingTeamView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1162862BD004AD000C4809 /* EditingTeamView.swift */; }; - FF3366D82D403D1F0012D1B4 /* InscriptionLegendView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF3A74312D37DCF2007E3032 /* InscriptionLegendView.swift */; }; - FF3366D92D403D1F0012D1B4 /* NetworkManagerError.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF6EC9052B947A1000EA7F5A /* NetworkManagerError.swift */; }; - FF3366DA2D403D1F0012D1B4 /* Tournament.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47D592B6D383C00ADC637 /* Tournament.swift */; }; - FF3366DB2D403D1F0012D1B4 /* TournamentStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4FC2E2A2C2C0E4D0021F3BF /* TournamentStore.swift */; }; - FF3366DC2D403D1F0012D1B4 /* LoserRoundSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5647122C0B6F380081F995 /* LoserRoundSettingsView.swift */; }; - FF3366DD2D403D1F0012D1B4 /* Persistence.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF3795652B9399AA004EA093 /* Persistence.swift */; }; - FF3366DE2D403D1F0012D1B4 /* CloseDatePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFCF76062C3BE9BC006C8C3D /* CloseDatePicker.swift */; }; - FF3366DF2D403D1F0012D1B4 /* BarButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1DF49A2BD8D23900822FA0 /* BarButtonView.swift */; }; - FF3366E02D403D1F0012D1B4 /* PlanningView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF9644F2BC25E3700EEF017 /* PlanningView.swift */; }; - FF3366E12D403D1F0012D1B4 /* Match.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967CEB2BAECB9900A9A3BD /* Match.swift */; }; - FF3366E22D403D1F0012D1B4 /* TournamentLevelPickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8F26492BAE0B4100650388 /* TournamentLevelPickerView.swift */; }; - FF3366E32D403D1F0012D1B4 /* FederalTournamentHolder.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1CBC212BB53E590036DAAB /* FederalTournamentHolder.swift */; }; - FF3366E42D403D1F0012D1B4 /* DataStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47D5D2B6D38EC00ADC637 /* DataStore.swift */; }; - FF3366E52D403D1F0012D1B4 /* SetDescriptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFCFC01B2BBC5AAA00B82851 /* SetDescriptor.swift */; }; - FF3366E62D403D1F0012D1B4 /* TeamHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF025AD72BD0C10F00A86CF8 /* TeamHeaderView.swift */; }; - FF3366E72D403D1F0012D1B4 /* OrganizedTournamentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF82CFC42B911F5B00B0CAF2 /* OrganizedTournamentView.swift */; }; - FF3366E82D403D1F0012D1B4 /* RoundScheduleEditorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF964562BC26B3400EEF017 /* RoundScheduleEditorView.swift */; }; - FF3366E92D403D1F0012D1B4 /* EventListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF59FFB22B90EFAC0061EFF9 /* EventListView.swift */; }; - FF3366EA2D403D1F0012D1B4 /* TournamentConfiguratorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8F263C2BAD627A00650388 /* TournamentConfiguratorView.swift */; }; - FF3366EB2D403D1F0012D1B4 /* ClubImportView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFC1E10B2BAC7FB0008D6F59 /* ClubImportView.swift */; }; - FF3366EC2D403D1F0012D1B4 /* UnderlineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF558C622C6CDD020071F9AE /* UnderlineView.swift */; }; - FF3366ED2D403D1F0012D1B4 /* MatchScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF3B60A22BC49BBC008C2E66 /* MatchScheduler.swift */; }; - FF3366EE2D403D1F0012D1B4 /* CallMessageCustomizationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1162792BCF8109000C4809 /* CallMessageCustomizationView.swift */; }; - FF3366EF2D403D1F0012D1B4 /* TournamentStatusView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF6087E92BE25EF1004E1E47 /* TournamentStatusView.swift */; }; - FF3366F02D403D1F0012D1B4 /* MatchTeamDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF025ADA2BD0C2D000A86CF8 /* MatchTeamDetailView.swift */; }; - FF3366F12D403D1F0012D1B4 /* GenericDestinationPickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5DA1942BB927E800A33061 /* GenericDestinationPickerView.swift */; }; - FF3366F22D403D1F0012D1B4 /* DateInterval.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF116E02BD2A9B600A33B06 /* DateInterval.swift */; }; - FF3366F32D403D1F0012D1B4 /* TableStructureView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8F26532BAE1E4400650388 /* TableStructureView.swift */; }; - FF3366F42D403D1F0012D1B4 /* Purchase.swift in Sources */ = {isa = PBXBuildFile; fileRef = C45BAE432BCA753E002EEC8A /* Purchase.swift */; }; - FF3366F52D403D1F0012D1B4 /* Screen.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF6EC8FD2B94792300EA7F5A /* Screen.swift */; }; - FF3366F62D403D1F0012D1B4 /* Round.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967CED2BAECBD700A9A3BD /* Round.swift */; }; - FF3366F72D403D1F0012D1B4 /* RegistrationSetupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFBFC3902CEE3A0E000EBD8D /* RegistrationSetupView.swift */; }; - FF3366F82D403D1F0012D1B4 /* FederalDataViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5BAF6D2BE0B3C8008B4B7E /* FederalDataViewModel.swift */; }; - FF3366F92D403D1F0012D1B4 /* AgendaDestination.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF3F74FE2B91A2D4004CFE0E /* AgendaDestination.swift */; }; - FF3366FA2D403D1F0012D1B4 /* PadelClubApp.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = FF3795602B9396D0004EA093 /* PadelClubApp.xcdatamodeld */; }; - FF3366FB2D403D1F0012D1B4 /* SetInputView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFCFC0152BBC5A4C00B82851 /* SetInputView.swift */; }; - FF3366FC2D403D1F0012D1B4 /* ButtonValidateView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF03C932BD91D0C00B516FC /* ButtonValidateView.swift */; }; - FF3366FD2D403D1F0012D1B4 /* ClubRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5D0D882BB4935C005CB568 /* ClubRowView.swift */; }; - FF3366FE2D403D1F0012D1B4 /* ClubDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1DC5502BAB351300FD8220 /* ClubDetailView.swift */; }; - FF3366FF2D403D1F0012D1B4 /* GroupStageCallingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF9268022BCE94A30080F940 /* GroupStageCallingView.swift */; }; - FF3367002D403D1F0012D1B4 /* CryptoKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49EF0432BE286780077B5AA /* CryptoKey.swift */; }; - FF3367012D403D1F0012D1B4 /* CashierSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF11627C2BCF941A000C4809 /* CashierSettingsView.swift */; }; - FF3367022D403D1F0012D1B4 /* LoserRoundScheduleEditorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFFCDE0D2BCC833600317DEF /* LoserRoundScheduleEditorView.swift */; }; - FF3367032D403D1F0012D1B4 /* Club.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47D622B6D3D6500ADC637 /* Club.swift */; }; - FF3367042D403D1F0012D1B4 /* Array+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF6EC90A2B947AC000EA7F5A /* Array+Extensions.swift */; }; - FF3367052D403D1F0012D1B4 /* ToolboxView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF59FFB82B90EFD70061EFF9 /* ToolboxView.swift */; }; - FF3367062D403D1F0012D1B4 /* Alphabet.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8E1CE52C006E0200184680 /* Alphabet.swift */; }; - FF3367072D403D1F0012D1B4 /* String+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF8ACD82B923F3C008466FA /* String+Extensions.swift */; }; - FF3367082D403D1F0012D1B4 /* GroupStageSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFCB74122C4625BB008384D0 /* GroupStageSettingsView.swift */; }; - FF3367092D403D1F0012D1B4 /* TournamentGeneralSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF025AE42BD0EBB800A86CF8 /* TournamentGeneralSettingsView.swift */; }; - FF33670A2D403D1F0012D1B4 /* LoserRoundView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFC2DCB12BBE75D40046DB9F /* LoserRoundView.swift */; }; - FF33670B2D403D1F0012D1B4 /* AddTeamView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF90FC1C2C44FB3E009339B2 /* AddTeamView.swift */; }; - FF33670C2D403D1F0012D1B4 /* PlayerPayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF9267FB2BCE84870080F940 /* PlayerPayView.swift */; }; - FF33670D2D403D1F0012D1B4 /* PlanningByCourtView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF2B51542C7A4DAF00FFF126 /* PlanningByCourtView.swift */; }; - FF33670E2D403D1F0012D1B4 /* FileImportManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFA6D7842BB0B795003A31F3 /* FileImportManager.swift */; }; - FF33670F2D403D1F0012D1B4 /* OngoingContainerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFA252B02CDD2C080074E63F /* OngoingContainerView.swift */; }; - FF3367102D403D1F0012D1B4 /* TournamentButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF6EC8FA2B94788600EA7F5A /* TournamentButtonView.swift */; }; - FF3367112D403D1F0012D1B4 /* DrawLogsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF6761562CC7803600CC9BF2 /* DrawLogsView.swift */; }; - FF3367122D403D1F0012D1B4 /* FederalPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF8ACCC2B92367B008466FA /* FederalPlayer.swift */; }; - FF3367132D403D1F0012D1B4 /* NavigationViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFBF065F2BBD9F6D009D6715 /* NavigationViewModel.swift */; }; - FF3367142D403D1F0012D1B4 /* FixedWidthInteger+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF6EC9082B947A5300EA7F5A /* FixedWidthInteger+Extensions.swift */; }; - FF3367152D403D1F0012D1B4 /* LoserBracketFromGroupStageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF6525C22C8C61B400B9498E /* LoserBracketFromGroupStageView.swift */; }; - FF3367162D403D1F0012D1B4 /* ImportedPlayer+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5D30502BD94E1000F2B93D /* ImportedPlayer+Extensions.swift */; }; - FF3367172D403D1F0012D1B4 /* ClubSearchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFC1E1032BAC28C6008D6F59 /* ClubSearchView.swift */; }; - FF3367182D403D1F0012D1B4 /* DateMenuView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFBFC3942CF05CBB000EBD8D /* DateMenuView.swift */; }; - FF3367192D403D1F0012D1B4 /* PlayerPopoverView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF089EBA2BB0120700F0AEC7 /* PlayerPopoverView.swift */; }; - FF33671A2D403D1F0012D1B4 /* InscriptionManagerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF70916D2B9108C600AB08DA /* InscriptionManagerView.swift */; }; - FF33671B2D403D1F0012D1B4 /* MatchFormatPickingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF77CE512CCCD1AF00CBCBB4 /* MatchFormatPickingView.swift */; }; - FF33671C2D403D1F0012D1B4 /* ActivityView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF82CFC82B9132AF00B0CAF2 /* ActivityView.swift */; }; - FF33671D2D403D1F0012D1B4 /* MySortDescriptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFDB1C722BB2CFE900F1E467 /* MySortDescriptor.swift */; }; - FF33671E2D403D1F0012D1B4 /* CalendarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5D0D8A2BB4D1E3005CB568 /* CalendarView.swift */; }; - FF33671F2D403D1F0012D1B4 /* FederalTournamentSearchScope.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1CBC1E2BB53E0C0036DAAB /* FederalTournamentSearchScope.swift */; }; - FF3367202D403D1F0012D1B4 /* TournamentFieldsManagerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8F26462BAE0ACB00650388 /* TournamentFieldsManagerView.swift */; }; - FF3367212D403D1F0012D1B4 /* PrintSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1F4B812BFA0124000B4573 /* PrintSettingsView.swift */; }; - FF3367222D403D1F0012D1B4 /* TournamentMatchFormatsSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF025AE22BD0EBA900A86CF8 /* TournamentMatchFormatsSettingsView.swift */; }; - FF3367232D403D1F0012D1B4 /* DatePickingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1162892BD05247000C4809 /* DatePickingView.swift */; }; - FF3367242D403D1F0012D1B4 /* MatchFormatRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFCFC0192BBC5A8500B82851 /* MatchFormatRowView.swift */; }; - FF3367252D403D1F0012D1B4 /* MonthData.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF025AE82BD1307E00A86CF8 /* MonthData.swift */; }; - FF3367262D403D1F0012D1B4 /* MenuWarningView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFEF7F4D2BDE69130033D0F0 /* MenuWarningView.swift */; }; - FF3367272D403D1F0012D1B4 /* TournamentBuildView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1F4B6C2BF9E60B000B4573 /* TournamentBuildView.swift */; }; - FF3367282D403D1F0012D1B4 /* TeamPickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967D0A2BAF3D4C00A9A3BD /* TeamPickerView.swift */; }; - FF3367292D403D1F0012D1B4 /* CloudConvert.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFA6D7862BB0B7A2003A31F3 /* CloudConvert.swift */; }; - FF33672A2D403D1F0012D1B4 /* EventTournamentsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFBF41832BF75ED7001B24CB /* EventTournamentsView.swift */; }; - FF33672B2D403D1F0012D1B4 /* DisplayContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1DC55A2BAB80C400FD8220 /* DisplayContext.swift */; }; - FF33672C2D403D1F0012D1B4 /* MultiCourtPickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF17CA482CB915A1003C7323 /* MultiCourtPickerView.swift */; }; - FF33672D2D403D1F0012D1B4 /* TournamentCallView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF9268062BCE94D90080F940 /* TournamentCallView.swift */; }; - FF33672E2D403D1F0012D1B4 /* LoserRoundsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFC2DCB32BBE9ECD0046DB9F /* LoserRoundsView.swift */; }; - FF33672F2D403D1F0012D1B4 /* GroupStagesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967CFB2BAEE13900A9A3BD /* GroupStagesView.swift */; }; - FF3367302D403D1F0012D1B4 /* PadelClubView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFD783FE2B91BA42000F62A6 /* PadelClubView.swift */; }; - FF3367312D403D1F0012D1B4 /* RegistrationInfoSheetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF3A73F22D37C34C007E3032 /* RegistrationInfoSheetView.swift */; }; - FF3367322D403D1F0012D1B4 /* URLs.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49EF01A2BD6A1E80077B5AA /* URLs.swift */; }; - FF3367332D403D1F0012D1B4 /* MatchDescriptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFCFC0132BBC59FC00B82851 /* MatchDescriptor.swift */; }; - FF3367342D403D1F0012D1B4 /* TournamentFormatSelectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8F26482BAE0B4100650388 /* TournamentFormatSelectionView.swift */; }; - FF3367352D403D1F0012D1B4 /* CoachListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF17CA562CC02FEA003C7323 /* CoachListView.swift */; }; - FF3367362D403D1F0012D1B4 /* MatchListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFBF065D2BBD8040009D6715 /* MatchListView.swift */; }; - FF3367372D403D1F0012D1B4 /* PadelClubApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = C425D4002B6D249D002A7B48 /* PadelClubApp.swift */; }; - FF3367382D403D1F0012D1B4 /* TournamentSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8F26422BADFE5B00650388 /* TournamentSettingsView.swift */; }; - FF3367392D403D1F0012D1B4 /* String+Crypto.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49EF03B2BE15AF80077B5AA /* String+Crypto.swift */; }; - FF33673A2D403D1F0012D1B4 /* GroupStageTeamReplacementView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF9AC3942BE3627B00C2E883 /* GroupStageTeamReplacementView.swift */; }; - FF33673B2D403D1F0012D1B4 /* TabItemModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF4C7F012BBBD7150031B6A3 /* TabItemModifier.swift */; }; - FF33673C2D403D1F0012D1B4 /* DeferredViewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFDDD40B2B93B2BB00C91A49 /* DeferredViewModifier.swift */; }; - FF33673D2D403D1F0012D1B4 /* TournamentScheduleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF0E0B6C2BC254C6005F00A9 /* TournamentScheduleView.swift */; }; - FF33673E2D403D1F0012D1B4 /* MatchFormatStorageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF025AF02BD1AEBD00A86CF8 /* MatchFormatStorageView.swift */; }; - FF33673F2D403D1F0012D1B4 /* UmpireView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF3F74F52B919E45004CFE0E /* UmpireView.swift */; }; - FF3367402D403D1F0012D1B4 /* User.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47DAC2B85FCCD00ADC637 /* User.swift */; }; - FF3367412D403D1F0012D1B4 /* CodingContainer+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C33F752C9B1EC5006316DE /* CodingContainer+Extensions.swift */; }; - FF3367422D403D1F0012D1B4 /* MatchSummaryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967D002BAEF0B400A9A3BD /* MatchSummaryView.swift */; }; - FF3367432D403D1F0012D1B4 /* OngoingDestination.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFA252B42CDD2C630074E63F /* OngoingDestination.swift */; }; - FF3367442D403D1F0012D1B4 /* TournamentDurationManagerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8F26442BAE0A3400650388 /* TournamentDurationManagerView.swift */; }; - FF3367452D403D1F0012D1B4 /* MockData.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1DC5522BAB354A00FD8220 /* MockData.swift */; }; - FF3367462D403D1F0012D1B4 /* TeamDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967D082BAF3D4000A9A3BD /* TeamDetailView.swift */; }; - FF3367472D403D1F0012D1B4 /* PlayerStatisticView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFA252A82CDB70520074E63F /* PlayerStatisticView.swift */; }; - FF3367482D403D1F0012D1B4 /* GroupStagesSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5DA18E2BB9268800A33061 /* GroupStagesSettingsView.swift */; }; - FF3367492D403D1F0012D1B4 /* TournamentFilterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF663FBD2BE019EC0031AE83 /* TournamentFilterView.swift */; }; - FF33674A2D403D1F0012D1B4 /* PreviewBracketPositionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF67615A2CC8ED6900CC9BF2 /* PreviewBracketPositionView.swift */; }; - FF33674B2D403D1F0012D1B4 /* HtmlGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1F4B722BFA00FB000B4573 /* HtmlGenerator.swift */; }; - FF33674C2D403D1F0012D1B4 /* BracketCallingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF17CA522CBE4788003C7323 /* BracketCallingView.swift */; }; - FF33674D2D403D1F0012D1B4 /* PadelRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8F26352BAD523300650388 /* PadelRule.swift */; }; - FF33674E2D403D1F0012D1B4 /* TeamRegistration.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967CF02BAECC0B00A9A3BD /* TeamRegistration.swift */; }; - FF33674F2D403D1F0012D1B4 /* Date+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF8ACDA2B923F48008466FA /* Date+Extensions.swift */; }; - FF3367502D403D1F0012D1B4 /* GroupStageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967CFA2BAEE13800A9A3BD /* GroupStageView.swift */; }; - FF3367512D403D1F0012D1B4 /* Tips.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1DC5582BAB767000FD8220 /* Tips.swift */; }; - FF3367522D403D1F0012D1B4 /* MainView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF59FFB62B90EFBF0061EFF9 /* MainView.swift */; }; - FF3367532D403D1F0012D1B4 /* MatchDateView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967D0C2BAF3EB200A9A3BD /* MatchDateView.swift */; }; - FF3367542D403D1F0012D1B4 /* PlanningSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF964522BC262B000EEF017 /* PlanningSettingsView.swift */; }; - FF3367552D403D1F0012D1B4 /* MatchScheduleEditorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF527D52BC6DDD000FF4EF2 /* MatchScheduleEditorView.swift */; }; - FF3367562D403D1F0012D1B4 /* FortuneWheelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFC91AF82BD6A09100B29808 /* FortuneWheelView.swift */; }; - FF3367572D403D1F0012D1B4 /* URL+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF8ACD52B923960008466FA /* URL+Extensions.swift */; }; - FF3367582D403D1F0012D1B4 /* LoadingViewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = C493B37D2C10AD3600862481 /* LoadingViewModifier.swift */; }; - FF3367592D403D1F0012D1B4 /* PlayerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF089EBC2BB0287D00F0AEC7 /* PlayerView.swift */; }; - FF33675A2D403D1F0012D1B4 /* MatchDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967D022BAEF0C000A9A3BD /* MatchDetailView.swift */; }; - FF33675B2D403D1F0012D1B4 /* ExportFormat.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF1D2CA2C4A22B200C8D33D /* ExportFormat.swift */; }; - FF33675C2D403D1F0012D1B4 /* PlayerBlockView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967D0E2BAF63B000A9A3BD /* PlayerBlockView.swift */; }; - FF33675D2D403D1F0012D1B4 /* StoreItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47D8F2B7BBBEC00ADC637 /* StoreItem.swift */; }; - FF33675E2D403D1F0012D1B4 /* Selectable.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFB9C8702BBADDE200A0EF4F /* Selectable.swift */; }; - FF33675F2D403D1F0012D1B4 /* PointView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFCFC0112BBC3E1A00B82851 /* PointView.swift */; }; - FF3367602D403D1F0012D1B4 /* ClubHolder.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1CBC202BB53E590036DAAB /* ClubHolder.swift */; }; - FF3367612D403D1F0012D1B4 /* EventSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFBF41852BF75FDA001B24CB /* EventSettingsView.swift */; }; - FF3367622D403D1F0012D1B4 /* InscriptionInfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5D0D772BB42C5B005CB568 /* InscriptionInfoView.swift */; }; - FF3367632D403D1F0012D1B4 /* SelectablePlayerListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF4AB6BC2B9256E10002987F /* SelectablePlayerListView.swift */; }; - FF3367642D403D1F0012D1B4 /* MatchFormatSelectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8F26502BAE0BAD00650388 /* MatchFormatSelectionView.swift */; }; - FF3367652D403D1F0012D1B4 /* TournamentRankView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5BAF712BE19274008B4B7E /* TournamentRankView.swift */; }; - FF3367662D403D1F0012D1B4 /* GroupStageDatePickingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF77CE592CCCD1FF00CBCBB4 /* GroupStageDatePickingView.swift */; }; - FF3367672D403D1F0012D1B4 /* NumberFormatter+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5D0D862BB48AFD005CB568 /* NumberFormatter+Extensions.swift */; }; - FF3367682D403D1F0012D1B4 /* SetLabelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFCFC0172BBC5A6800B82851 /* SetLabelView.swift */; }; - FF3367692D403D1F0012D1B4 /* DebugSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4489BE12C05BF5000043F3D /* DebugSettingsView.swift */; }; - FF33676A2D403D1F0012D1B4 /* GroupStageScheduleEditorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF9645A2BC2D53B00EEF017 /* GroupStageScheduleEditorView.swift */; }; - FF33676B2D403D1F0012D1B4 /* EventView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFBF41812BF73EB3001B24CB /* EventView.swift */; }; - FF33676C2D403D1F0012D1B4 /* LoginView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47DA52B83948E00ADC637 /* LoginView.swift */; }; - FF33676D2D403D1F0012D1B4 /* Court.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFC91B002BD85C2F00B29808 /* Court.swift */; }; - FF33676E2D403D1F0012D1B4 /* Labels.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967CF72BAEDF0000A9A3BD /* Labels.swift */; }; - FF33676F2D403D1F0012D1B4 /* CopyPasteButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFCB74162C480411008384D0 /* CopyPasteButtonView.swift */; }; - FF3367702D403D1F0012D1B4 /* PlayerSexPickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF089EB32BB0020000F0AEC7 /* PlayerSexPickerView.swift */; }; - FF3367712D403D1F0012D1B4 /* TournamentInscriptionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1F4B702BF9EFE9000B4573 /* TournamentInscriptionView.swift */; }; - FF3367722D403D1F0012D1B4 /* CallSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF9267FE2BCE94830080F940 /* CallSettingsView.swift */; }; - FF3367732D403D1F0012D1B4 /* FooterButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF025ADC2BD0C94300A86CF8 /* FooterButtonView.swift */; }; - FF3367742D403D1F0012D1B4 /* RankCalculatorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5D0D822BB48997005CB568 /* RankCalculatorView.swift */; }; - FF3367752D403D1F0012D1B4 /* DateBoxView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF7091692B90F95E00AB08DA /* DateBoxView.swift */; }; - FF3367762D403D1F0012D1B4 /* LearnMoreSheetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5D0D6F2BB3EFA5005CB568 /* LearnMoreSheetView.swift */; }; - FF3367772D403D1F0012D1B4 /* SourceFileManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF8ACD32B92392C008466FA /* SourceFileManager.swift */; }; - FF3367782D403D1F0012D1B4 /* TournamentCategorySettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF4623CA2D1340D200CB57B5 /* TournamentCategorySettingsView.swift */; }; - FF3367792D403D1F0012D1B4 /* PListReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EC6F582BE92D88000CEAB4 /* PListReader.swift */; }; - FF33677A2D403D1F0012D1B4 /* UpdateSourceRankDateView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF0EC5212BB173E70056B6D1 /* UpdateSourceRankDateView.swift */; }; - FF33677B2D403D1F0012D1B4 /* GlobalSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF025AE62BD1111000A86CF8 /* GlobalSettingsView.swift */; }; - FF33677C2D403D1F0012D1B4 /* Guard.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47D8E2B7BBBEC00ADC637 /* Guard.swift */; }; - FF33677D2D403D1F0012D1B4 /* PurchaseListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49EF0182BD694290077B5AA /* PurchaseListView.swift */; }; - FF33677F2D403D1F0012D1B4 /* Algorithms in Frameworks */ = {isa = PBXBuildFile; productRef = FF3366732D403D1F0012D1B4 /* Algorithms */; }; - FF3367802D403D1F0012D1B4 /* Zip in Frameworks */ = {isa = PBXBuildFile; productRef = FF3366752D403D1F0012D1B4 /* Zip */; }; - FF3367812D403D1F0012D1B4 /* LeStorage.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C49EF0372BDFF3000077B5AA /* LeStorage.framework */; }; - FF3367832D403D1F0012D1B4 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C425D4072B6D249E002A7B48 /* Preview Assets.xcassets */; }; - FF3367842D403D1F0012D1B4 /* Launch Screen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = FFD784002B91BF79000F62A6 /* Launch Screen.storyboard */; }; - FF3367852D403D1F0012D1B4 /* tournament-template.html in Resources */ = {isa = PBXBuildFile; fileRef = FF1F4B7F2BFA0105000B4573 /* tournament-template.html */; }; - FF3367862D403D1F0012D1B4 /* local.sqlite in Resources */ = {isa = PBXBuildFile; fileRef = FF2B51602C7E302C00FFF126 /* local.sqlite */; }; - FF3367872D403D1F0012D1B4 /* groupstagescore-template.html in Resources */ = {isa = PBXBuildFile; fileRef = FF1F4B7B2BFA0105000B4573 /* groupstagescore-template.html */; }; - FF3367882D403D1F0012D1B4 /* player-template.html in Resources */ = {isa = PBXBuildFile; fileRef = FF1F4B7E2BFA0105000B4573 /* player-template.html */; }; - FF3367892D403D1F0012D1B4 /* groupstagerow-template.html in Resources */ = {isa = PBXBuildFile; fileRef = FF1F4B7A2BFA0105000B4573 /* groupstagerow-template.html */; }; - FF33678A2D403D1F0012D1B4 /* hiddenplayer-template.html in Resources */ = {isa = PBXBuildFile; fileRef = FF1F4B7C2BFA0105000B4573 /* hiddenplayer-template.html */; }; - FF33678B2D403D1F0012D1B4 /* bracket-template.html in Resources */ = {isa = PBXBuildFile; fileRef = FF1F4B762BFA0105000B4573 /* bracket-template.html */; }; - FF33678C2D403D1F0012D1B4 /* groupstagecol-template.html in Resources */ = {isa = PBXBuildFile; fileRef = FF1F4B782BFA0105000B4573 /* groupstagecol-template.html */; }; - FF33678D2D403D1F0012D1B4 /* groupstage-template.html in Resources */ = {isa = PBXBuildFile; fileRef = FF1F4B772BFA0105000B4573 /* groupstage-template.html */; }; - FF33678E2D403D1F0012D1B4 /* groupstageentrant-template.html in Resources */ = {isa = PBXBuildFile; fileRef = FF1F4B792BFA0105000B4573 /* groupstageentrant-template.html */; }; - FF33678F2D403D1F0012D1B4 /* match-template.html in Resources */ = {isa = PBXBuildFile; fileRef = FF1F4B7D2BFA0105000B4573 /* match-template.html */; }; - FF3367902D403D1F0012D1B4 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = FFF0241D2BF48B15001F14B4 /* Localizable.strings */; }; - FF3367912D403D1F0012D1B4 /* SyncedProducts.storekit in Resources */ = {isa = PBXBuildFile; fileRef = C45BAE3A2BC6DF10002EEC8A /* SyncedProducts.storekit */; }; - FF3367922D403D1F0012D1B4 /* local.plist in Resources */ = {isa = PBXBuildFile; fileRef = C4EC6F562BE92CAC000CEAB4 /* local.plist */; }; - FF3367932D403D1F0012D1B4 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = FF0CA5742BDA4AE10080E843 /* PrivacyInfo.xcprivacy */; }; - FF3367942D403D1F0012D1B4 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C425D4042B6D249E002A7B48 /* Assets.xcassets */; }; - FF3367962D403D1F0012D1B4 /* LeStorage.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = C49EF0372BDFF3000077B5AA /* LeStorage.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; FF3795622B9396D0004EA093 /* PadelClubApp.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = FF3795602B9396D0004EA093 /* PadelClubApp.xcdatamodeld */; }; FF3795662B9399AA004EA093 /* Persistence.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF3795652B9399AA004EA093 /* Persistence.swift */; }; FF3A73F32D37C34D007E3032 /* RegistrationInfoSheetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF3A73F22D37C34C007E3032 /* RegistrationInfoSheetView.swift */; }; @@ -1181,17 +897,6 @@ name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; }; - FF3367952D403D1F0012D1B4 /* Embed Frameworks */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 10; - files = ( - FF3367962D403D1F0012D1B4 /* LeStorage.framework in Embed Frameworks */, - ); - name = "Embed Frameworks"; - runOnlyForDeploymentPostprocessing = 0; - }; FF4CC04B2C996C0600151637 /* Embed Frameworks */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; @@ -1371,7 +1076,6 @@ FF2B51622C7F073100FFF126 /* Model_1_1.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = Model_1_1.xcdatamodel; sourceTree = ""; }; FF2B6F5D2C036A1400835EE7 /* EventLinksView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventLinksView.swift; sourceTree = ""; }; FF2EFBEF2BDE295E0049CE3B /* SendToAllView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SendToAllView.swift; sourceTree = ""; }; - FF33679A2D403D1F0012D1B4 /* ProdDev.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ProdDev.app; sourceTree = BUILT_PRODUCTS_DIR; }; FF3795612B9396D0004EA093 /* Model.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = Model.xcdatamodel; sourceTree = ""; }; FF3795652B9399AA004EA093 /* Persistence.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Persistence.swift; sourceTree = ""; }; FF3A73F22D37C34C007E3032 /* RegistrationInfoSheetView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RegistrationInfoSheetView.swift; sourceTree = ""; }; @@ -1585,16 +1289,6 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - FF33677E2D403D1F0012D1B4 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - FF33677F2D403D1F0012D1B4 /* Algorithms in Frameworks */, - FF3367802D403D1F0012D1B4 /* Zip in Frameworks */, - FF3367812D403D1F0012D1B4 /* LeStorage.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; FF4CC0342C996C0600151637 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -1637,7 +1331,6 @@ C425D4172B6D249E002A7B48 /* PadelClubUITests.xctest */, FF70FBCF2C90584900129CC2 /* PadelClub TestFlight.app */, FF4CC0502C996C0600151637 /* ProdTest PadelClub.app */, - FF33679A2D403D1F0012D1B4 /* ProdDev.app */, ); name = Products; sourceTree = ""; @@ -2441,28 +2134,6 @@ productReference = C425D4172B6D249E002A7B48 /* PadelClubUITests.xctest */; productType = "com.apple.product-type.bundle.ui-testing"; }; - FF3366722D403D1F0012D1B4 /* ProdDev */ = { - isa = PBXNativeTarget; - buildConfigurationList = FF3367972D403D1F0012D1B4 /* Build configuration list for PBXNativeTarget "ProdDev" */; - buildPhases = ( - FF3366772D403D1F0012D1B4 /* Sources */, - FF33677E2D403D1F0012D1B4 /* Frameworks */, - FF3367822D403D1F0012D1B4 /* Resources */, - FF3367952D403D1F0012D1B4 /* Embed Frameworks */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = ProdDev; - packageProductDependencies = ( - FF3366732D403D1F0012D1B4 /* Algorithms */, - FF3366752D403D1F0012D1B4 /* Zip */, - ); - productName = PadelClub; - productReference = FF33679A2D403D1F0012D1B4 /* ProdDev.app */; - productType = "com.apple.product-type.application"; - }; FF4CBF3E2C996C0600151637 /* ProdTest PadelClub */ = { isa = PBXNativeTarget; buildConfigurationList = FF4CC04D2C996C0600151637 /* Build configuration list for PBXNativeTarget "ProdTest PadelClub" */; @@ -2553,7 +2224,6 @@ C425D4162B6D249E002A7B48 /* PadelClubUITests */, FF70FABD2C90584900129CC2 /* PadelClub TestFlight */, FF4CBF3E2C996C0600151637 /* ProdTest PadelClub */, - FF3366722D403D1F0012D1B4 /* ProdDev */, ); }; /* End PBXProject section */ @@ -2598,31 +2268,6 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - FF3367822D403D1F0012D1B4 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - FF3367832D403D1F0012D1B4 /* Preview Assets.xcassets in Resources */, - FF3367842D403D1F0012D1B4 /* Launch Screen.storyboard in Resources */, - FF3367852D403D1F0012D1B4 /* tournament-template.html in Resources */, - FF3367862D403D1F0012D1B4 /* local.sqlite in Resources */, - FF3367872D403D1F0012D1B4 /* groupstagescore-template.html in Resources */, - FF3367882D403D1F0012D1B4 /* player-template.html in Resources */, - FF3367892D403D1F0012D1B4 /* groupstagerow-template.html in Resources */, - FF33678A2D403D1F0012D1B4 /* hiddenplayer-template.html in Resources */, - FF33678B2D403D1F0012D1B4 /* bracket-template.html in Resources */, - FF33678C2D403D1F0012D1B4 /* groupstagecol-template.html in Resources */, - FF33678D2D403D1F0012D1B4 /* groupstage-template.html in Resources */, - FF33678E2D403D1F0012D1B4 /* groupstageentrant-template.html in Resources */, - FF33678F2D403D1F0012D1B4 /* match-template.html in Resources */, - FF3367902D403D1F0012D1B4 /* Localizable.strings in Resources */, - FF3367912D403D1F0012D1B4 /* SyncedProducts.storekit in Resources */, - FF3367922D403D1F0012D1B4 /* local.plist in Resources */, - FF3367932D403D1F0012D1B4 /* PrivacyInfo.xcprivacy in Resources */, - FF3367942D403D1F0012D1B4 /* Assets.xcassets in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; FF4CC0382C996C0600151637 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -2966,275 +2611,6 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - FF3366772D403D1F0012D1B4 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - FF3366782D403D1F0012D1B4 /* UserCreationView.swift in Sources */, - FF3366792D403D1F0012D1B4 /* TabDestination.swift in Sources */, - FF33667A2D403D1F0012D1B4 /* CashierView.swift in Sources */, - FF33667B2D403D1F0012D1B4 /* Event.swift in Sources */, - FF33667C2D403D1F0012D1B4 /* DatePickingViewWithFormat.swift in Sources */, - FF33667D2D403D1F0012D1B4 /* PlayerHolder.swift in Sources */, - FF33667E2D403D1F0012D1B4 /* LoserRoundStepScheduleEditorView.swift in Sources */, - FF33667F2D403D1F0012D1B4 /* ClubCourtSetupView.swift in Sources */, - FF3366802D403D1F0012D1B4 /* Patcher.swift in Sources */, - FF3366812D403D1F0012D1B4 /* FileImportView.swift in Sources */, - FF3366822D403D1F0012D1B4 /* StepperView.swift in Sources */, - FF3366832D403D1F0012D1B4 /* RoundsView.swift in Sources */, - FF3366842D403D1F0012D1B4 /* FederalTournament.swift in Sources */, - FF3366852D403D1F0012D1B4 /* TournamentInitView.swift in Sources */, - FF3366862D403D1F0012D1B4 /* SubscriptionView.swift in Sources */, - FF3366872D403D1F0012D1B4 /* TournamentLookUpView.swift in Sources */, - FF3366882D403D1F0012D1B4 /* ClubsView.swift in Sources */, - FF3366892D403D1F0012D1B4 /* ImagePickerView.swift in Sources */, - FF33668A2D403D1F0012D1B4 /* MatchTypeSelectionView.swift in Sources */, - FF33668B2D403D1F0012D1B4 /* MatchSetupView.swift in Sources */, - FF33668C2D403D1F0012D1B4 /* NetworkManager.swift in Sources */, - FF33668D2D403D1F0012D1B4 /* EventLinksView.swift in Sources */, - FF33668E2D403D1F0012D1B4 /* SeedInterval.swift in Sources */, - FF33668F2D403D1F0012D1B4 /* TournamentClubSettingsView.swift in Sources */, - FF3366902D403D1F0012D1B4 /* GroupStageTeamView.swift in Sources */, - FF3366912D403D1F0012D1B4 /* RoundSettingsView.swift in Sources */, - FF3366922D403D1F0012D1B4 /* MatchViewStyle.swift in Sources */, - FF3366932D403D1F0012D1B4 /* SupportButtonView.swift in Sources */, - FF3366942D403D1F0012D1B4 /* TournamentBroadcastRowView.swift in Sources */, - FF3366952D403D1F0012D1B4 /* TeamWeightView.swift in Sources */, - FF3366962D403D1F0012D1B4 /* SeedsCallingView.swift in Sources */, - FF3366972D403D1F0012D1B4 /* CallView.swift in Sources */, - FF3366982D403D1F0012D1B4 /* Color+Extensions.swift in Sources */, - FF3366992D403D1F0012D1B4 /* EditSharingView.swift in Sources */, - FF33669A2D403D1F0012D1B4 /* TournamentCellView.swift in Sources */, - FF33669B2D403D1F0012D1B4 /* Sequence+Extensions.swift in Sources */, - FF33669C2D403D1F0012D1B4 /* CashierDetailView.swift in Sources */, - FF33669D2D403D1F0012D1B4 /* EventClubSettingsView.swift in Sources */, - FF33669E2D403D1F0012D1B4 /* AccountView.swift in Sources */, - FF33669F2D403D1F0012D1B4 /* PlayersWithoutContactView.swift in Sources */, - FF3366A02D403D1F0012D1B4 /* FollowUpMatchView.swift in Sources */, - FF3366A12D403D1F0012D1B4 /* TeamsCallingView.swift in Sources */, - FF3366A22D403D1F0012D1B4 /* Calendar+Extensions.swift in Sources */, - FF3366A32D403D1F0012D1B4 /* TeamScore.swift in Sources */, - FF3366A42D403D1F0012D1B4 /* EditablePlayerView.swift in Sources */, - FF3366A52D403D1F0012D1B4 /* PlayerDetailView.swift in Sources */, - FF3366A62D403D1F0012D1B4 /* UmpireStatisticView.swift in Sources */, - FF3366A72D403D1F0012D1B4 /* ListRowViewModifier.swift in Sources */, - FF3366A82D403D1F0012D1B4 /* PresentationContext.swift in Sources */, - FF3366A92D403D1F0012D1B4 /* AppSettings.swift in Sources */, - FF3366AA2D403D1F0012D1B4 /* SwiftParser.swift in Sources */, - FF3366AB2D403D1F0012D1B4 /* ChangePasswordView.swift in Sources */, - FF3366AC2D403D1F0012D1B4 /* TournamentSubscriptionView.swift in Sources */, - FF3366AD2D403D1F0012D1B4 /* RoundView.swift in Sources */, - FF3366AE2D403D1F0012D1B4 /* RowButtonView.swift in Sources */, - FF3366AF2D403D1F0012D1B4 /* SendToAllView.swift in Sources */, - FF3366B02D403D1F0012D1B4 /* EventCreationView.swift in Sources */, - FF3366B12D403D1F0012D1B4 /* LocationManager.swift in Sources */, - FF3366B22D403D1F0012D1B4 /* CapsuleViewModifier.swift in Sources */, - FF3366B32D403D1F0012D1B4 /* BroadcastView.swift in Sources */, - FF3366B42D403D1F0012D1B4 /* SchedulerView.swift in Sources */, - FF3366B52D403D1F0012D1B4 /* PadelClubButtonView.swift in Sources */, - FF3366B62D403D1F0012D1B4 /* TournamentSeedEditing.swift in Sources */, - FF3366B72D403D1F0012D1B4 /* TournamentView.swift in Sources */, - FF3366B82D403D1F0012D1B4 /* OngoingView.swift in Sources */, - FF3366B92D403D1F0012D1B4 /* CreateClubView.swift in Sources */, - FF3366BA2D403D1F0012D1B4 /* APICallsListView.swift in Sources */, - FF3366BB2D403D1F0012D1B4 /* TeamRestingView.swift in Sources */, - FF3366BC2D403D1F0012D1B4 /* NetworkFederalService.swift in Sources */, - FF3366BD2D403D1F0012D1B4 /* DurationSettingsView.swift in Sources */, - FF3366BE2D403D1F0012D1B4 /* AppScreen.swift in Sources */, - FF3366BF2D403D1F0012D1B4 /* CourtView.swift in Sources */, - FF3366C02D403D1F0012D1B4 /* PointSelectionView.swift in Sources */, - FF3366C12D403D1F0012D1B4 /* TeamRowView.swift in Sources */, - FF3366C22D403D1F0012D1B4 /* ContactManager.swift in Sources */, - FF3366C32D403D1F0012D1B4 /* AppDelegate.swift in Sources */, - FF3366C42D403D1F0012D1B4 /* SubscriptionInfoView.swift in Sources */, - FF3366C52D403D1F0012D1B4 /* EditScoreView.swift in Sources */, - FF3366C62D403D1F0012D1B4 /* TournamentOrganizerView.swift in Sources */, - FF3366C72D403D1F0012D1B4 /* NetworkMonitor.swift in Sources */, - FF3366C82D403D1F0012D1B4 /* TournamentRunningView.swift in Sources */, - FF3366C92D403D1F0012D1B4 /* TournamentDatePickerView.swift in Sources */, - FF3366CA2D403D1F0012D1B4 /* CourtAvailabilitySettingsView.swift in Sources */, - FF3366CB2D403D1F0012D1B4 /* ConfirmButtonView.swift in Sources */, - FF3366CC2D403D1F0012D1B4 /* PasswordField.swift in Sources */, - FF3366CD2D403D1F0012D1B4 /* MatchRowView.swift in Sources */, - FF3366CE2D403D1F0012D1B4 /* Locale+Extensions.swift in Sources */, - FF3366CF2D403D1F0012D1B4 /* HtmlService.swift in Sources */, - FF3366D02D403D1F0012D1B4 /* GroupStage.swift in Sources */, - FF3366D12D403D1F0012D1B4 /* DrawLog.swift in Sources */, - FF3366D22D403D1F0012D1B4 /* TournamentCashierView.swift in Sources */, - FF3366D32D403D1F0012D1B4 /* StoreManager.swift in Sources */, - FF3366D42D403D1F0012D1B4 /* SearchViewModel.swift in Sources */, - FF3366D52D403D1F0012D1B4 /* PlayerRegistration.swift in Sources */, - FF3366D62D403D1F0012D1B4 /* ImportedPlayerView.swift in Sources */, - FF3366D72D403D1F0012D1B4 /* EditingTeamView.swift in Sources */, - FF3366D82D403D1F0012D1B4 /* InscriptionLegendView.swift in Sources */, - FF3366D92D403D1F0012D1B4 /* NetworkManagerError.swift in Sources */, - FF3366DA2D403D1F0012D1B4 /* Tournament.swift in Sources */, - FF3366DB2D403D1F0012D1B4 /* TournamentStore.swift in Sources */, - FF3366DC2D403D1F0012D1B4 /* LoserRoundSettingsView.swift in Sources */, - FF3366DD2D403D1F0012D1B4 /* Persistence.swift in Sources */, - FF3366DE2D403D1F0012D1B4 /* CloseDatePicker.swift in Sources */, - FF3366DF2D403D1F0012D1B4 /* BarButtonView.swift in Sources */, - FF3366E02D403D1F0012D1B4 /* PlanningView.swift in Sources */, - FF3366E12D403D1F0012D1B4 /* Match.swift in Sources */, - FF3366E22D403D1F0012D1B4 /* TournamentLevelPickerView.swift in Sources */, - FF3366E32D403D1F0012D1B4 /* FederalTournamentHolder.swift in Sources */, - FF3366E42D403D1F0012D1B4 /* DataStore.swift in Sources */, - FF3366E52D403D1F0012D1B4 /* SetDescriptor.swift in Sources */, - FF3366E62D403D1F0012D1B4 /* TeamHeaderView.swift in Sources */, - FF3366E72D403D1F0012D1B4 /* OrganizedTournamentView.swift in Sources */, - FF3366E82D403D1F0012D1B4 /* RoundScheduleEditorView.swift in Sources */, - FF3366E92D403D1F0012D1B4 /* EventListView.swift in Sources */, - FF3366EA2D403D1F0012D1B4 /* TournamentConfiguratorView.swift in Sources */, - FF3366EB2D403D1F0012D1B4 /* ClubImportView.swift in Sources */, - FF3366EC2D403D1F0012D1B4 /* UnderlineView.swift in Sources */, - FF3366ED2D403D1F0012D1B4 /* MatchScheduler.swift in Sources */, - FF3366EE2D403D1F0012D1B4 /* CallMessageCustomizationView.swift in Sources */, - FF3366EF2D403D1F0012D1B4 /* TournamentStatusView.swift in Sources */, - FF3366F02D403D1F0012D1B4 /* MatchTeamDetailView.swift in Sources */, - FF3366F12D403D1F0012D1B4 /* GenericDestinationPickerView.swift in Sources */, - FF3366F22D403D1F0012D1B4 /* DateInterval.swift in Sources */, - FF3366F32D403D1F0012D1B4 /* TableStructureView.swift in Sources */, - FF3366F42D403D1F0012D1B4 /* Purchase.swift in Sources */, - FF3366F52D403D1F0012D1B4 /* Screen.swift in Sources */, - FF3366F62D403D1F0012D1B4 /* Round.swift in Sources */, - FF3366F72D403D1F0012D1B4 /* RegistrationSetupView.swift in Sources */, - FF3366F82D403D1F0012D1B4 /* FederalDataViewModel.swift in Sources */, - FF3366F92D403D1F0012D1B4 /* AgendaDestination.swift in Sources */, - FF3366FA2D403D1F0012D1B4 /* PadelClubApp.xcdatamodeld in Sources */, - FF3366FB2D403D1F0012D1B4 /* SetInputView.swift in Sources */, - FF3366FC2D403D1F0012D1B4 /* ButtonValidateView.swift in Sources */, - FF3366FD2D403D1F0012D1B4 /* ClubRowView.swift in Sources */, - FF3366FE2D403D1F0012D1B4 /* ClubDetailView.swift in Sources */, - FF3366FF2D403D1F0012D1B4 /* GroupStageCallingView.swift in Sources */, - FF3367002D403D1F0012D1B4 /* CryptoKey.swift in Sources */, - FF3367012D403D1F0012D1B4 /* CashierSettingsView.swift in Sources */, - FF3367022D403D1F0012D1B4 /* LoserRoundScheduleEditorView.swift in Sources */, - FF3367032D403D1F0012D1B4 /* Club.swift in Sources */, - FF3367042D403D1F0012D1B4 /* Array+Extensions.swift in Sources */, - FF3367052D403D1F0012D1B4 /* ToolboxView.swift in Sources */, - FF3367062D403D1F0012D1B4 /* Alphabet.swift in Sources */, - FF3367072D403D1F0012D1B4 /* String+Extensions.swift in Sources */, - FF3367082D403D1F0012D1B4 /* GroupStageSettingsView.swift in Sources */, - FF3367092D403D1F0012D1B4 /* TournamentGeneralSettingsView.swift in Sources */, - FF33670A2D403D1F0012D1B4 /* LoserRoundView.swift in Sources */, - FF33670B2D403D1F0012D1B4 /* AddTeamView.swift in Sources */, - FF33670C2D403D1F0012D1B4 /* PlayerPayView.swift in Sources */, - FF33670D2D403D1F0012D1B4 /* PlanningByCourtView.swift in Sources */, - FF33670E2D403D1F0012D1B4 /* FileImportManager.swift in Sources */, - FF33670F2D403D1F0012D1B4 /* OngoingContainerView.swift in Sources */, - FF3367102D403D1F0012D1B4 /* TournamentButtonView.swift in Sources */, - FF3367112D403D1F0012D1B4 /* DrawLogsView.swift in Sources */, - FF3367122D403D1F0012D1B4 /* FederalPlayer.swift in Sources */, - FF3367132D403D1F0012D1B4 /* NavigationViewModel.swift in Sources */, - FF3367142D403D1F0012D1B4 /* FixedWidthInteger+Extensions.swift in Sources */, - FF3367152D403D1F0012D1B4 /* LoserBracketFromGroupStageView.swift in Sources */, - FF3367162D403D1F0012D1B4 /* ImportedPlayer+Extensions.swift in Sources */, - FF3367172D403D1F0012D1B4 /* ClubSearchView.swift in Sources */, - FF3367182D403D1F0012D1B4 /* DateMenuView.swift in Sources */, - FF3367192D403D1F0012D1B4 /* PlayerPopoverView.swift in Sources */, - FF33671A2D403D1F0012D1B4 /* InscriptionManagerView.swift in Sources */, - FF33671B2D403D1F0012D1B4 /* MatchFormatPickingView.swift in Sources */, - FF33671C2D403D1F0012D1B4 /* ActivityView.swift in Sources */, - FF33671D2D403D1F0012D1B4 /* MySortDescriptor.swift in Sources */, - FF33671E2D403D1F0012D1B4 /* CalendarView.swift in Sources */, - FF33671F2D403D1F0012D1B4 /* FederalTournamentSearchScope.swift in Sources */, - FF3367202D403D1F0012D1B4 /* TournamentFieldsManagerView.swift in Sources */, - FF3367212D403D1F0012D1B4 /* PrintSettingsView.swift in Sources */, - FF3367222D403D1F0012D1B4 /* TournamentMatchFormatsSettingsView.swift in Sources */, - FF3367232D403D1F0012D1B4 /* DatePickingView.swift in Sources */, - FF3367242D403D1F0012D1B4 /* MatchFormatRowView.swift in Sources */, - FF3367252D403D1F0012D1B4 /* MonthData.swift in Sources */, - FF3367262D403D1F0012D1B4 /* MenuWarningView.swift in Sources */, - FF3367272D403D1F0012D1B4 /* TournamentBuildView.swift in Sources */, - FF3367282D403D1F0012D1B4 /* TeamPickerView.swift in Sources */, - FF3367292D403D1F0012D1B4 /* CloudConvert.swift in Sources */, - FF33672A2D403D1F0012D1B4 /* EventTournamentsView.swift in Sources */, - FF33672B2D403D1F0012D1B4 /* DisplayContext.swift in Sources */, - FF33672C2D403D1F0012D1B4 /* MultiCourtPickerView.swift in Sources */, - FF33672D2D403D1F0012D1B4 /* TournamentCallView.swift in Sources */, - FF33672E2D403D1F0012D1B4 /* LoserRoundsView.swift in Sources */, - FF33672F2D403D1F0012D1B4 /* GroupStagesView.swift in Sources */, - FF3367302D403D1F0012D1B4 /* PadelClubView.swift in Sources */, - FF3367312D403D1F0012D1B4 /* RegistrationInfoSheetView.swift in Sources */, - FF3367322D403D1F0012D1B4 /* URLs.swift in Sources */, - FF3367332D403D1F0012D1B4 /* MatchDescriptor.swift in Sources */, - FF3367342D403D1F0012D1B4 /* TournamentFormatSelectionView.swift in Sources */, - FF3367352D403D1F0012D1B4 /* CoachListView.swift in Sources */, - FF3367362D403D1F0012D1B4 /* MatchListView.swift in Sources */, - FF3367372D403D1F0012D1B4 /* PadelClubApp.swift in Sources */, - FF3367382D403D1F0012D1B4 /* TournamentSettingsView.swift in Sources */, - FF3367392D403D1F0012D1B4 /* String+Crypto.swift in Sources */, - FF33673A2D403D1F0012D1B4 /* GroupStageTeamReplacementView.swift in Sources */, - FF33673B2D403D1F0012D1B4 /* TabItemModifier.swift in Sources */, - FF33673C2D403D1F0012D1B4 /* DeferredViewModifier.swift in Sources */, - FF33673D2D403D1F0012D1B4 /* TournamentScheduleView.swift in Sources */, - FF33673E2D403D1F0012D1B4 /* MatchFormatStorageView.swift in Sources */, - FF33673F2D403D1F0012D1B4 /* UmpireView.swift in Sources */, - FF3367402D403D1F0012D1B4 /* User.swift in Sources */, - FF3367412D403D1F0012D1B4 /* CodingContainer+Extensions.swift in Sources */, - FF3367422D403D1F0012D1B4 /* MatchSummaryView.swift in Sources */, - FF3367432D403D1F0012D1B4 /* OngoingDestination.swift in Sources */, - FF3367442D403D1F0012D1B4 /* TournamentDurationManagerView.swift in Sources */, - FF3367452D403D1F0012D1B4 /* MockData.swift in Sources */, - FF3367462D403D1F0012D1B4 /* TeamDetailView.swift in Sources */, - FF3367472D403D1F0012D1B4 /* PlayerStatisticView.swift in Sources */, - FF3367482D403D1F0012D1B4 /* GroupStagesSettingsView.swift in Sources */, - FF3367492D403D1F0012D1B4 /* TournamentFilterView.swift in Sources */, - FF33674A2D403D1F0012D1B4 /* PreviewBracketPositionView.swift in Sources */, - FF33674B2D403D1F0012D1B4 /* HtmlGenerator.swift in Sources */, - FF33674C2D403D1F0012D1B4 /* BracketCallingView.swift in Sources */, - FF33674D2D403D1F0012D1B4 /* PadelRule.swift in Sources */, - FF33674E2D403D1F0012D1B4 /* TeamRegistration.swift in Sources */, - FF33674F2D403D1F0012D1B4 /* Date+Extensions.swift in Sources */, - FF3367502D403D1F0012D1B4 /* GroupStageView.swift in Sources */, - FF3367512D403D1F0012D1B4 /* Tips.swift in Sources */, - FF3367522D403D1F0012D1B4 /* MainView.swift in Sources */, - FF3367532D403D1F0012D1B4 /* MatchDateView.swift in Sources */, - FF3367542D403D1F0012D1B4 /* PlanningSettingsView.swift in Sources */, - FF3367552D403D1F0012D1B4 /* MatchScheduleEditorView.swift in Sources */, - FF3367562D403D1F0012D1B4 /* FortuneWheelView.swift in Sources */, - FF3367572D403D1F0012D1B4 /* URL+Extensions.swift in Sources */, - FF3367582D403D1F0012D1B4 /* LoadingViewModifier.swift in Sources */, - FF3367592D403D1F0012D1B4 /* PlayerView.swift in Sources */, - FF33675A2D403D1F0012D1B4 /* MatchDetailView.swift in Sources */, - FF33675B2D403D1F0012D1B4 /* ExportFormat.swift in Sources */, - FF33675C2D403D1F0012D1B4 /* PlayerBlockView.swift in Sources */, - FF33675D2D403D1F0012D1B4 /* StoreItem.swift in Sources */, - FF33675E2D403D1F0012D1B4 /* Selectable.swift in Sources */, - FF33675F2D403D1F0012D1B4 /* PointView.swift in Sources */, - FF3367602D403D1F0012D1B4 /* ClubHolder.swift in Sources */, - FF3367612D403D1F0012D1B4 /* EventSettingsView.swift in Sources */, - FF3367622D403D1F0012D1B4 /* InscriptionInfoView.swift in Sources */, - FF3367632D403D1F0012D1B4 /* SelectablePlayerListView.swift in Sources */, - FF3367642D403D1F0012D1B4 /* MatchFormatSelectionView.swift in Sources */, - FF3367652D403D1F0012D1B4 /* TournamentRankView.swift in Sources */, - FF3367662D403D1F0012D1B4 /* GroupStageDatePickingView.swift in Sources */, - FF3367672D403D1F0012D1B4 /* NumberFormatter+Extensions.swift in Sources */, - FF3367682D403D1F0012D1B4 /* SetLabelView.swift in Sources */, - FF3367692D403D1F0012D1B4 /* DebugSettingsView.swift in Sources */, - FF33676A2D403D1F0012D1B4 /* GroupStageScheduleEditorView.swift in Sources */, - FF33676B2D403D1F0012D1B4 /* EventView.swift in Sources */, - FF33676C2D403D1F0012D1B4 /* LoginView.swift in Sources */, - FF33676D2D403D1F0012D1B4 /* Court.swift in Sources */, - FF33676E2D403D1F0012D1B4 /* Labels.swift in Sources */, - FF33676F2D403D1F0012D1B4 /* CopyPasteButtonView.swift in Sources */, - FF3367702D403D1F0012D1B4 /* PlayerSexPickerView.swift in Sources */, - FF3367712D403D1F0012D1B4 /* TournamentInscriptionView.swift in Sources */, - FF3367722D403D1F0012D1B4 /* CallSettingsView.swift in Sources */, - FF3367732D403D1F0012D1B4 /* FooterButtonView.swift in Sources */, - FF3367742D403D1F0012D1B4 /* RankCalculatorView.swift in Sources */, - FF3367752D403D1F0012D1B4 /* DateBoxView.swift in Sources */, - FF3367762D403D1F0012D1B4 /* LearnMoreSheetView.swift in Sources */, - FF3367772D403D1F0012D1B4 /* SourceFileManager.swift in Sources */, - FF3367782D403D1F0012D1B4 /* TournamentCategorySettingsView.swift in Sources */, - FF3367792D403D1F0012D1B4 /* PListReader.swift in Sources */, - FF33677A2D403D1F0012D1B4 /* UpdateSourceRankDateView.swift in Sources */, - FF33677B2D403D1F0012D1B4 /* GlobalSettingsView.swift in Sources */, - FF33677C2D403D1F0012D1B4 /* Guard.swift in Sources */, - FF33677D2D403D1F0012D1B4 /* PurchaseListView.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; FF4CBF432C996C0600151637 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -4085,92 +3461,6 @@ }; name = Release; }; - FF3367982D403D1F0012D1B4 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - CODE_SIGN_ENTITLEMENTS = PadelClub/PadelClub.entitlements; - CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - DEFINES_MODULE = YES; - DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\""; - DEVELOPMENT_TEAM = BQ3Y44M3Q6; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_FILE = "PadelClub TestFlight copy-Info.plist"; - INFOPLIST_KEY_CFBundleDisplayName = "Padel Club (Beta)"; - INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.sports"; - INFOPLIST_KEY_LSSupportsOpeningDocumentsInPlace = YES; - INFOPLIST_KEY_NSCalendarsUsageDescription = "Padel Club a besoin d'avoir accès à votre calendrier pour pouvoir y inscrire ce tournoi"; - INFOPLIST_KEY_NSCameraUsageDescription = "En autorisant l'application à utiliser la caméra, vous pourrez prendre des photos des rencontres"; - INFOPLIST_KEY_NSLocationWhenInUseUsageDescription = "Padel Club a besoin de votre position pour rechercher les clubs autour de vous."; - INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; - INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; - INFOPLIST_KEY_UILaunchStoryboardName = "Launch Screen"; - INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; - IPHONEOS_DEPLOYMENT_TARGET = 17.1; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - MARKETING_VERSION = 1.0.34; - PRODUCT_BUNDLE_IDENTIFIER = app.padelclub.beta; - PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE_SPECIFIER = ""; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; - SUPPORTS_MACCATALYST = NO; - SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Debug; - }; - FF3367992D403D1F0012D1B4 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - CODE_SIGN_ENTITLEMENTS = PadelClub/PadelClub.entitlements; - CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = YES; - DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\""; - DEVELOPMENT_TEAM = BQ3Y44M3Q6; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_FILE = "PadelClub TestFlight copy-Info.plist"; - INFOPLIST_KEY_CFBundleDisplayName = "Padel Club (Beta)"; - INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.sports"; - INFOPLIST_KEY_LSSupportsOpeningDocumentsInPlace = YES; - INFOPLIST_KEY_NSCalendarsUsageDescription = "Padel Club a besoin d'avoir accès à votre calendrier pour pouvoir y inscrire ce tournoi"; - INFOPLIST_KEY_NSCameraUsageDescription = "En autorisant l'application à utiliser la caméra, vous pourrez prendre des photos des rencontres"; - INFOPLIST_KEY_NSLocationWhenInUseUsageDescription = "Padel Club a besoin de votre position pour rechercher les clubs autour de vous."; - INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; - INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; - INFOPLIST_KEY_UILaunchStoryboardName = "Launch Screen"; - INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; - IPHONEOS_DEPLOYMENT_TARGET = 17.1; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - MARKETING_VERSION = 1.0.34; - PRODUCT_BUNDLE_IDENTIFIER = app.padelclub.beta; - PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE_SPECIFIER = ""; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; - SUPPORTS_MACCATALYST = NO; - SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = PRODTEST; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Release; - }; FF4CC04E2C996C0600151637 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -4179,7 +3469,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\""; @@ -4206,7 +3496,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.30; + MARKETING_VERSION = 1.1.1; PRODUCT_BUNDLE_IDENTIFIER = app.padelclub; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -4227,7 +3517,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; @@ -4253,7 +3543,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.30; + MARKETING_VERSION = 1.1.1; PRODUCT_BUNDLE_IDENTIFIER = app.padelclub; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -4298,7 +3588,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.34; + MARKETING_VERSION = 1.1.1; PRODUCT_BUNDLE_IDENTIFIER = app.padelclub.beta; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -4340,7 +3630,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.34; + MARKETING_VERSION = 1.1.1; PRODUCT_BUNDLE_IDENTIFIER = app.padelclub.beta; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -4392,15 +3682,6 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - FF3367972D403D1F0012D1B4 /* Build configuration list for PBXNativeTarget "ProdDev" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - FF3367982D403D1F0012D1B4 /* Debug */, - FF3367992D403D1F0012D1B4 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; FF4CC04D2C996C0600151637 /* Build configuration list for PBXNativeTarget "ProdTest PadelClub" */ = { isa = XCConfigurationList; buildConfigurations = ( @@ -4422,22 +3703,6 @@ /* End XCConfigurationList section */ /* Begin XCRemoteSwiftPackageReference section */ - FF3366742D403D1F0012D1B4 /* XCRemoteSwiftPackageReference "swift-algorithms" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/apple/swift-algorithms.git"; - requirement = { - kind = upToNextMajorVersion; - minimumVersion = 1.2.0; - }; - }; - FF3366762D403D1F0012D1B4 /* XCRemoteSwiftPackageReference "Zip" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/marmelroy/Zip"; - requirement = { - kind = upToNextMajorVersion; - minimumVersion = 2.1.2; - }; - }; FF4C7F052BBBE6B90031B6A3 /* XCRemoteSwiftPackageReference "swift-algorithms" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/apple/swift-algorithms.git"; @@ -4489,16 +3754,6 @@ /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ - FF3366732D403D1F0012D1B4 /* Algorithms */ = { - isa = XCSwiftPackageProductDependency; - package = FF3366742D403D1F0012D1B4 /* XCRemoteSwiftPackageReference "swift-algorithms" */; - productName = Algorithms; - }; - FF3366752D403D1F0012D1B4 /* Zip */ = { - isa = XCSwiftPackageProductDependency; - package = FF3366762D403D1F0012D1B4 /* XCRemoteSwiftPackageReference "Zip" */; - productName = Zip; - }; FF4CBF3F2C996C0600151637 /* Algorithms */ = { isa = XCSwiftPackageProductDependency; package = FF4CBF402C996C0600151637 /* XCRemoteSwiftPackageReference "swift-algorithms" */; From ee86d08159945f1338e3b3c199cf97ac7b1abad3 Mon Sep 17 00:00:00 2001 From: Raz Date: Thu, 23 Jan 2025 09:45:37 +0100 Subject: [PATCH 41/57] fix tournament deletion --- .../Tournament/Screen/Components/TournamentStatusView.swift | 3 ++- PadelClub/Views/Tournament/TournamentBuildView.swift | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/PadelClub/Views/Tournament/Screen/Components/TournamentStatusView.swift b/PadelClub/Views/Tournament/Screen/Components/TournamentStatusView.swift index 9add4fd..799d634 100644 --- a/PadelClub/Views/Tournament/Screen/Components/TournamentStatusView.swift +++ b/PadelClub/Views/Tournament/Screen/Components/TournamentStatusView.swift @@ -48,9 +48,10 @@ struct TournamentStatusView: View { do { let event = tournament.eventObject() let isLastTournament = event?.tournaments.count == 1 - try dataStore.tournaments.delete(instance: tournament) if let event, isLastTournament { try dataStore.events.delete(instance: event) + } else { + try dataStore.tournaments.delete(instance: tournament) } if eventDismiss == false || isLastTournament { navigation.path = NavigationPath() diff --git a/PadelClub/Views/Tournament/TournamentBuildView.swift b/PadelClub/Views/Tournament/TournamentBuildView.swift index 92e23b0..b76002c 100644 --- a/PadelClub/Views/Tournament/TournamentBuildView.swift +++ b/PadelClub/Views/Tournament/TournamentBuildView.swift @@ -113,7 +113,7 @@ struct TournamentBuildView: View { Section { - if true { + if tournament.hasEnded() { NavigationLink(value: Screen.rankings) { LabeledContent { if tournament.publishRankings == false { From b4f66d96154a35a0d4d6096794c11cd11c6e7eea Mon Sep 17 00:00:00 2001 From: Raz Date: Thu, 23 Jan 2025 10:26:56 +0100 Subject: [PATCH 42/57] v bump --- PadelClub.xcodeproj/project.pbxproj | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/PadelClub.xcodeproj/project.pbxproj b/PadelClub.xcodeproj/project.pbxproj index af84379..92ea6fb 100644 --- a/PadelClub.xcodeproj/project.pbxproj +++ b/PadelClub.xcodeproj/project.pbxproj @@ -3302,7 +3302,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\""; @@ -3329,7 +3329,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.1.0; + MARKETING_VERSION = 1.1.1; PRODUCT_BUNDLE_IDENTIFIER = app.padelclub; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -3350,7 +3350,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; @@ -3376,7 +3376,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.1.0; + MARKETING_VERSION = 1.1.1; PRODUCT_BUNDLE_IDENTIFIER = app.padelclub; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; From 2c4781de9dd62be469e7fe0d8c74ad63c5aac9e8 Mon Sep 17 00:00:00 2001 From: Raz Date: Mon, 27 Jan 2025 11:36:11 +0100 Subject: [PATCH 43/57] fix delete team --- PadelClub.xcodeproj/project.pbxproj | 8 ++++---- PadelClub/Views/Team/EditingTeamView.swift | 8 ++++++-- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/PadelClub.xcodeproj/project.pbxproj b/PadelClub.xcodeproj/project.pbxproj index 92ea6fb..0c49b79 100644 --- a/PadelClub.xcodeproj/project.pbxproj +++ b/PadelClub.xcodeproj/project.pbxproj @@ -3302,7 +3302,7 @@ CODE_SIGN_ENTITLEMENTS = PadelClub/PadelClub.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 3; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\""; @@ -3329,7 +3329,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.1.1; + MARKETING_VERSION = 1.1.2; PRODUCT_BUNDLE_IDENTIFIER = app.padelclub; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -3350,7 +3350,7 @@ CODE_SIGN_ENTITLEMENTS = PadelClub/PadelClub.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 3; DEFINES_MODULE = YES; DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\""; DEVELOPMENT_TEAM = BQ3Y44M3Q6; @@ -3376,7 +3376,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.1.1; + MARKETING_VERSION = 1.1.2; PRODUCT_BUNDLE_IDENTIFIER = app.padelclub; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; diff --git a/PadelClub/Views/Team/EditingTeamView.swift b/PadelClub/Views/Team/EditingTeamView.swift index e942567..76bbe32 100644 --- a/PadelClub/Views/Team/EditingTeamView.swift +++ b/PadelClub/Views/Team/EditingTeamView.swift @@ -202,7 +202,11 @@ struct EditingTeamView: View { RowButtonView("Effacer l'équipe", role: .destructive, systemImage: "trash") { _resetTeam() team.deleteTeamScores() - _save() + do { + try tournamentStore.teamRegistrations.delete(instance: team) + } catch { + Logger.error(error) + } dismiss() } } footer: { @@ -356,8 +360,8 @@ struct EditingTeamView: View { Logger.error(error) } } - } + private func _save() { do { try tournamentStore.teamRegistrations.addOrUpdate(instance: team) From d4e193d60b3546d58223edf77ec85666bdda8511 Mon Sep 17 00:00:00 2001 From: Raz Date: Mon, 27 Jan 2025 17:50:19 +0100 Subject: [PATCH 44/57] fix crash in inscription info view --- PadelClub.xcodeproj/project.pbxproj | 8 ++++---- .../Views/Tournament/Screen/InscriptionManagerView.swift | 1 + 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/PadelClub.xcodeproj/project.pbxproj b/PadelClub.xcodeproj/project.pbxproj index 0c49b79..26002e6 100644 --- a/PadelClub.xcodeproj/project.pbxproj +++ b/PadelClub.xcodeproj/project.pbxproj @@ -3302,7 +3302,7 @@ CODE_SIGN_ENTITLEMENTS = PadelClub/PadelClub.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 3; + CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\""; @@ -3329,7 +3329,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.1.2; + MARKETING_VERSION = 1.1.3; PRODUCT_BUNDLE_IDENTIFIER = app.padelclub; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -3350,7 +3350,7 @@ CODE_SIGN_ENTITLEMENTS = PadelClub/PadelClub.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 3; + CURRENT_PROJECT_VERSION = 1; DEFINES_MODULE = YES; DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\""; DEVELOPMENT_TEAM = BQ3Y44M3Q6; @@ -3376,7 +3376,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.1.2; + MARKETING_VERSION = 1.1.3; PRODUCT_BUNDLE_IDENTIFIER = app.padelclub; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; diff --git a/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift b/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift index 1a0fa82..b945b40 100644 --- a/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift +++ b/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift @@ -818,6 +818,7 @@ struct InscriptionManagerView: View { if tournament.isAnimation() == false { NavigationLink { InscriptionInfoView(tournament: tournament) + .environment(tournament) } label: { LabeledContent { if let registrationIssues { From 4e62899b30e8522799cdd2be97515447f2b402a0 Mon Sep 17 00:00:00 2001 From: Laurent Date: Tue, 28 Jan 2025 12:08:58 +0100 Subject: [PATCH 45/57] Fix crash when deleting --- PadelClub/Data/Tournament.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PadelClub/Data/Tournament.swift b/PadelClub/Data/Tournament.swift index f4e2347..7daa905 100644 --- a/PadelClub/Data/Tournament.swift +++ b/PadelClub/Data/Tournament.swift @@ -417,7 +417,7 @@ final class Tournament : ModelObject, Storable { } store.groupStages.deleteDependencies(groups) - let rounds = self.tournamentStore.rounds + let rounds = Array(self.tournamentStore.rounds) for round in rounds { try round.deleteDependencies() } From 2813fcf1de6aacbbb57d17f7ac40fe768203378a Mon Sep 17 00:00:00 2001 From: Raz Date: Tue, 28 Jan 2025 18:48:53 +0100 Subject: [PATCH 46/57] v1.1.4 --- PadelClub.xcodeproj/project.pbxproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/PadelClub.xcodeproj/project.pbxproj b/PadelClub.xcodeproj/project.pbxproj index 26002e6..386aede 100644 --- a/PadelClub.xcodeproj/project.pbxproj +++ b/PadelClub.xcodeproj/project.pbxproj @@ -3329,7 +3329,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.1.3; + MARKETING_VERSION = 1.1.4; PRODUCT_BUNDLE_IDENTIFIER = app.padelclub; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -3376,7 +3376,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.1.3; + MARKETING_VERSION = 1.1.4; PRODUCT_BUNDLE_IDENTIFIER = app.padelclub; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; From 70355f47de7b500c24d552c1f516b6d40dd9f5e3 Mon Sep 17 00:00:00 2001 From: Raz Date: Wed, 29 Jan 2025 16:11:50 +0100 Subject: [PATCH 47/57] fix delete tournament issue with online reg --- PadelClub.xcodeproj/project.pbxproj | 4 ++-- .../Tournament/Screen/Components/TournamentStatusView.swift | 5 +++++ .../Views/Tournament/Screen/InscriptionManagerView.swift | 1 + 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/PadelClub.xcodeproj/project.pbxproj b/PadelClub.xcodeproj/project.pbxproj index 386aede..c0b888e 100644 --- a/PadelClub.xcodeproj/project.pbxproj +++ b/PadelClub.xcodeproj/project.pbxproj @@ -3329,7 +3329,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.1.4; + MARKETING_VERSION = 1.1.5; PRODUCT_BUNDLE_IDENTIFIER = app.padelclub; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -3376,7 +3376,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.1.4; + MARKETING_VERSION = 1.1.5; PRODUCT_BUNDLE_IDENTIFIER = app.padelclub; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; diff --git a/PadelClub/Views/Tournament/Screen/Components/TournamentStatusView.swift b/PadelClub/Views/Tournament/Screen/Components/TournamentStatusView.swift index 799d634..23bdad9 100644 --- a/PadelClub/Views/Tournament/Screen/Components/TournamentStatusView.swift +++ b/PadelClub/Views/Tournament/Screen/Components/TournamentStatusView.swift @@ -48,6 +48,11 @@ struct TournamentStatusView: View { do { let event = tournament.eventObject() let isLastTournament = event?.tournaments.count == 1 + + tournament.isDeleted = true + + try dataStore.tournaments.addOrUpdate(instance: tournament) + if let event, isLastTournament { try dataStore.events.delete(instance: event) } else { diff --git a/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift b/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift index b945b40..78f3d59 100644 --- a/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift +++ b/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift @@ -668,6 +668,7 @@ struct InscriptionManagerView: View { } } } + .id(refreshStatus) .searchable(text: $searchField, isPresented: $presentSearch, prompt: Text("Chercher parmi les équipes inscrites")) .keyboardType(.alphabet) .autocorrectionDisabled() From c119e5a412a54149fcfcd1e6f5a5424e557e5224 Mon Sep 17 00:00:00 2001 From: Raz Date: Mon, 3 Feb 2025 17:38:59 +0100 Subject: [PATCH 48/57] fix crash when filtering with no code club --- PadelClub/ViewModel/FederalDataViewModel.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/PadelClub/ViewModel/FederalDataViewModel.swift b/PadelClub/ViewModel/FederalDataViewModel.swift index 2ca3b2f..c841fb7 100644 --- a/PadelClub/ViewModel/FederalDataViewModel.swift +++ b/PadelClub/ViewModel/FederalDataViewModel.swift @@ -90,7 +90,7 @@ class FederalDataViewModel { && (ageCategories.isEmpty || tournament.tournaments.anySatisfy({ ageCategories.contains($0.age) })) && - (selectedClubs.isEmpty || selectedClubs.contains(tournament.codeClub!)) + (selectedClubs.isEmpty || (tournament.codeClub != nil && selectedClubs.contains(tournament.codeClub!))) && (dayPeriod == .all || (dayPeriod != .all && dayPeriod == tournament.dayPeriod)) && @@ -100,7 +100,7 @@ class FederalDataViewModel { func countForTournamentBuilds(from tournaments: [any FederalTournamentHolder]) -> Int { tournaments.filter({ tournament in - (selectedClubs.isEmpty || selectedClubs.contains(tournament.codeClub!)) + (selectedClubs.isEmpty || (tournament.codeClub != nil && selectedClubs.contains(tournament.codeClub!))) && (dayPeriod == .all || (dayPeriod != .all && dayPeriod == tournament.dayPeriod)) && @@ -151,7 +151,7 @@ class FederalDataViewModel { && (ageCategories.isEmpty || ageCategories.contains(build.age)) && - (selectedClubs.isEmpty || selectedClubs.contains(tournament.codeClub!)) + (selectedClubs.isEmpty || (tournament.codeClub != nil && selectedClubs.contains(tournament.codeClub!))) && (dayPeriod == .all || (dayPeriod != .all && dayPeriod == tournament.dayPeriod)) && From 86570858d1452146a41a1aa34af223223c576a8c Mon Sep 17 00:00:00 2001 From: Raz Date: Mon, 3 Feb 2025 17:39:22 +0100 Subject: [PATCH 49/57] v1.1.6 --- PadelClub.xcodeproj/project.pbxproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/PadelClub.xcodeproj/project.pbxproj b/PadelClub.xcodeproj/project.pbxproj index c0b888e..20266ce 100644 --- a/PadelClub.xcodeproj/project.pbxproj +++ b/PadelClub.xcodeproj/project.pbxproj @@ -3329,7 +3329,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.1.5; + MARKETING_VERSION = 1.1.6; PRODUCT_BUNDLE_IDENTIFIER = app.padelclub; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -3376,7 +3376,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.1.5; + MARKETING_VERSION = 1.1.6; PRODUCT_BUNDLE_IDENTIFIER = app.padelclub; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; From 8efa0307f7fd6c40b9d09a91160dd1bbf7026459 Mon Sep 17 00:00:00 2001 From: Raz Date: Wed, 5 Feb 2025 08:22:02 +0100 Subject: [PATCH 50/57] save id homologation for new ranks calc --- PadelClub/Views/Navigation/Umpire/PadelClubView.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/PadelClub/Views/Navigation/Umpire/PadelClubView.swift b/PadelClub/Views/Navigation/Umpire/PadelClubView.swift index 56e46d8..464a52d 100644 --- a/PadelClub/Views/Navigation/Umpire/PadelClubView.swift +++ b/PadelClub/Views/Navigation/Umpire/PadelClubView.swift @@ -250,7 +250,7 @@ struct PadelClubView: View { // Function to fetch data for a single license ID func fetchPlayerData(for licenseID: String) async throws -> [Player]? { - guard let url = URL(string: "https://beach-padel.app.fft.fr/beachja/rechercheJoueur/licencies?idHomologation=82477107&numeroLicence=\(licenseID)") else { + guard let url = URL(string: "https://beach-padel.app.fft.fr/beachja/rechercheJoueur/licencies?idHomologation=82469282&numeroLicence=\(licenseID)") else { throw URLError(.badURL) } @@ -268,7 +268,7 @@ func fetchPlayerData(for licenseID: String) async throws -> [Player]? { request.setValue("XMLHttpRequest", forHTTPHeaderField: "X-Requested-With") // Add cookies if needed (example cookie header value shown, replace with valid cookies) - request.setValue("JSESSIONID=F4ED2A1BCF3CD2694FE0B111B8027999; AWSALB=JoZEC/+cnAzmCdbbm3Vuc4CtMGx8BvbveFx+RBRuj8dQCQD52C9iDDbL/OVm98uMb7vc8Jv6/bVPkaByXWmOZmSGwAsN2s8/jt6W5L8QGz7omzNbYF01kvqffRvo; AWSALBCORS=JoZEC/+cnAzmCdbbm3Vuc4CtMGx8BvbveFx+RBRuj8dQCQD52C9iDDbL/OVm98uMb7vc8Jv6/bVPkaByXWmOZmSGwAsN2s8/jt6W5L8QGz7omzNbYF01kvqffRvo; datadome=KlbIdnrCgaY1zLVIZ5CfLJm~KXv9_YnXGhaQdqMEn6Ja9R6imBH~vhzmyuiLxGi1D0z90v5x2EiGDvQ7zsw~fajWLbOupFEajulc86PSJ7RIHpOiduCQ~cNoITQYJOXa; tc_cj_v2=m_iZZZ%22**%22%27%20ZZZKQLNQOPLOSLJOZZZ%5D777%5Ecl_%5Dny%5B%5D%5D_mmZZZZZZKQLQJRKOQKSMOZZZ%5D777m_iZZZ%22**%22%27%20ZZZKQLQJRKOQMSLNZZZ%5D777%5Ecl_%5Dny%5B%5D%5D_mmZZZZZZKQLQJRKOQNSJMZZZ%5D777m_iZZZ%22**%22%27%20ZZZKQLQJRKOSJMLJZZZ%5D777%5Ecl_%5Dny%5B%5D%5D_mmZZZZZZKQLRPQMQQNRQRZZZ%5D777m_iZZZ%22**%22%27%20ZZZKQLRPQNKSLOMSZZZ%5D777%5Ecl_%5Dny%5B%5D%5D_mmZZZZZZKQLSNSOPMSOPJZZZ%5D777m_iZZZ%22**%22%27%20ZZZKQMJQSRLJSOOJZZZ%5D777%5Ecl_%5Dny%5B%5D%5D_mmZZZZZZKQMJRJPJMSSKRZZZ%5D; tc_cj_v2_cmp=; tc_cj_v2_med=; tCdebugLib=1; incap_ses_2222_2712217=ui9wOOAjNziUTlU3gCHWHtv/KWcAAAAAhSzbpyITRp7YwRT3vJB2vg==; incap_ses_2224_2712217=NepDAr2kUDShMiCJaDzdHqbjKWcAAAAA0kLlk3lgvGnwWSTMceZoEw==; xtan=-; xtant=1; incap_ses_1350_2712217=g+XhSJRwOS8JlWTYCSq8EtOBJGcAAAAAffg2IobkPUW2BtvgJGHbMw==; TCSESSION=124101910177775608913; nlbi_2712217=jnhtOC5KDiLvfpy/b9lUTgAAAAA7zduh8JyZOVrEfGsEdFlq; TCID=12481811494814553052; xtvrn=$548419$; TCPID=12471746148351334672; visid_incap_2712217=PSfJngzoSuiowsuXXhvOu5K+7mUAAAAAQUIPAAAAAAAleL9ldvN/FC1VykkU9ret; SessionStatId=10.91.140.42.1662124965429001", forHTTPHeaderField: "Cookie") + request.setValue("JSESSIONID=48C272263C9454774F0DA95F491C3765; AWSALB=nNF/fDwCSmsO9PQD5jkXNUuoMuAzziHTeIkno1uRkNDkKfaOT7VVbh0KOdvGZ5afMw3epLw0p9J+4Ih6cpwqW+XdcLUrr9kJhpQEgP1oeLPRsi/4Yn9uCLCRgPKI; AWSALBCORS=nNF/fDwCSmsO9PQD5jkXNUuoMuAzziHTeIkno1uRkNDkKfaOT7VVbh0KOdvGZ5afMw3epLw0p9J+4Ih6cpwqW+XdcLUrr9kJhpQEgP1oeLPRsi/4Yn9uCLCRgPKI; datadome=3T1lKPP7j_r9MhBRIq_5sBwcCuhI0lfYgQ414DuY7BdYm3jpvHECT05w6Ohl0xMvGVJi3XayxoRsnsKvPti_TIZ90B~boSu2LYs2lm_OssxFSoDGEHTFOf4HTjVkM6i8; TCID=125221542552726081269; TCSESSION=125221542558139099625; TCPID=125221535336434794787; incap_ses_2224_2712217=T+1ySNxxGx/yVRcIdDzdHrUlomcAAAAAAWv/NX2ushG21NP0K9l10g==; nlbi_2712217=Wd9XXxrrXQSWKZBPb9lUTgAAAADRjV4zuALYepgab2n0ra/7; xtan=-; xtant=1; xtvrn=$548419$; visid_incap_2712217=PSfJngzoSuiowsuXXhvOu5K+7mUAAAAAQUIPAAAAAAAleL9ldvN/FC1VykkU9ret; SessionStatId=10.91.140.42.1662124965429001", forHTTPHeaderField: "Cookie") let (data, _) = try await URLSession.shared.data(for: request) let decoder = JSONDecoder() From 8acf5fed7d0a213aaab0b0ccbc4f9c7b2354df5e Mon Sep 17 00:00:00 2001 From: Laurent Date: Wed, 5 Feb 2025 12:20:18 +0100 Subject: [PATCH 51/57] Set the number to free tournaments to 3 --- PadelClub/Views/Tournament/Subscription/Guard.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/PadelClub/Views/Tournament/Subscription/Guard.swift b/PadelClub/Views/Tournament/Subscription/Guard.swift index afbc3b6..61a83c8 100644 --- a/PadelClub/Views/Tournament/Subscription/Guard.swift +++ b/PadelClub/Views/Tournament/Subscription/Guard.swift @@ -20,6 +20,8 @@ import LeStorage var updateListenerTask: Task? = nil + fileprivate let _freeTournaments: Int = 3 + override init() { super.init() @@ -273,7 +275,7 @@ import LeStorage fileprivate func _paymentWithoutSubscription() -> Tournament.TournamentPayment? { let freelyPayed: Int = DataStore.shared.tournaments.filter { $0.payment == .free && $0.isCanceled == false }.count - if freelyPayed < 1 { + if freelyPayed < self._freeTournaments { return Tournament.TournamentPayment.free } let tournamentCreditCount: Int = self._purchasedTournamentCount() From e5c113ddaddcdeaf8936a3bee5b5333d2e9f108d Mon Sep 17 00:00:00 2001 From: Raz Date: Wed, 5 Feb 2025 12:53:12 +0100 Subject: [PATCH 52/57] remove unecessary july 2024 download clean up some debug methods --- PadelClub/Data/Federal/FederalPlayer.swift | 5 +- PadelClub/Data/PlayerRegistration.swift | 92 +++++----- PadelClub/Data/TeamRegistration.swift | 2 +- PadelClub/Data/Tournament.swift | 58 +++--- PadelClub/Utils/FileImportManager.swift | 49 ++--- PadelClub/Utils/SourceFileManager.swift | 8 + PadelClub/Views/Components/StepperView.swift | 2 +- PadelClub/Views/Navigation/MainView.swift | 44 ++--- .../Navigation/Umpire/PadelClubView.swift | 170 ++++++++++++++---- 9 files changed, 267 insertions(+), 163 deletions(-) diff --git a/PadelClub/Data/Federal/FederalPlayer.swift b/PadelClub/Data/Federal/FederalPlayer.swift index a8b2780..d5a6784 100644 --- a/PadelClub/Data/Federal/FederalPlayer.swift +++ b/PadelClub/Data/Federal/FederalPlayer.swift @@ -197,8 +197,6 @@ class FederalPlayer: Decodable { } lastPlayerFetch.predicate = predicate let count = try? context.count(for: lastPlayerFetch) - - print("count", count) do { if let lr = try context.fetch(lastPlayerFetch).first?.rank { let fetch = ImportedPlayer.fetchRequest() @@ -207,8 +205,9 @@ class FederalPlayer: Decodable { rankPredicate = NSCompoundPredicate(andPredicateWithSubpredicates: [rankPredicate, NSPredicate(format: "importDate == %@", mostRecentDateAvailable as CVarArg)]) } fetch.predicate = rankPredicate - + print(fetch.predicate) let lastPlayersCount = try context.count(for: fetch) + print(Int(lr), Int(lastPlayersCount) - 1, count) return (Int(lr) + Int(lastPlayersCount) - 1, count) } } catch { diff --git a/PadelClub/Data/PlayerRegistration.swift b/PadelClub/Data/PlayerRegistration.swift index bd93be7..3d17346 100644 --- a/PadelClub/Data/PlayerRegistration.swift +++ b/PadelClub/Data/PlayerRegistration.swift @@ -44,7 +44,7 @@ final class PlayerRegistration: ModelObject, Storable { func localizedSourceLabel() -> String { switch source { - case .frenchFederation, .onlineRegistration: + case .frenchFederation: return "base fédérale" case .beachPadel: return "beach-padel" @@ -262,82 +262,87 @@ final class PlayerRegistration: ModelObject, Storable { } func updateRank(from sources: [CSVParser], lastRank: Int) async throws { -#if DEBUG_TIME //DEBUGING TIME -let start = Date() -defer { - let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000) - print("func updateRank()", id, duration.formatted(.units(allowed: [.seconds, .milliseconds]))) -} -#endif + #if DEBUG_TIME + let start = Date() + defer { + let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000) + print("func updateRank()", id, duration.formatted(.units(allowed: [.seconds, .milliseconds]))) + } + #endif if let dataFound = try await history(from: sources) { rank = dataFound.rankValue?.toInt() points = dataFound.points tournamentPlayed = dataFound.tournamentCountValue?.toInt() + } else if let dataFound = try await historyFromName(from: sources) { + rank = dataFound.rankValue?.toInt() + points = dataFound.points + tournamentPlayed = dataFound.tournamentCountValue?.toInt() } else { rank = lastRank } } - + func history(from sources: [CSVParser]) async throws -> Line? { -#if DEBUG_TIME //DEBUGING TIME -let start = Date() -defer { - let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000) - print("func history()", id, duration.formatted(.units(allowed: [.seconds, .milliseconds]))) -} -#endif + #if DEBUG_TIME + let start = Date() + defer { + let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000) + print("func history()", id, duration.formatted(.units(allowed: [.seconds, .milliseconds]))) + } + #endif guard let license = licenceId?.strippedLicense else { - return try await historyFromName(from: sources) + return nil // Do NOT call historyFromName here, let updateRank handle it } + let filteredSources = sources.filter { $0.maleData == isMalePlayer() } + return await withTaskGroup(of: Line?.self) { group in - for source in sources.filter({ $0.maleData == isMalePlayer() }) { + for source in filteredSources { group.addTask { guard !Task.isCancelled else { print("Cancelled"); return nil } - - return try? await source.first(where: { line in - line.rawValue.contains(";\(license);") - }) + return try? await source.first { $0.rawValue.contains(";\(license);") } } } - + if let first = await group.first(where: { $0 != nil }) { group.cancelAll() return first - } else { - return nil } + return nil } } - + func historyFromName(from sources: [CSVParser]) async throws -> Line? { -#if DEBUG_TIME //DEBUGING TIME -let start = Date() -defer { - let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000) - print("func historyFromName()", id, duration.formatted(.units(allowed: [.seconds, .milliseconds]))) -} -#endif + #if DEBUG_TIME + let start = Date() + defer { + let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000) + print("func historyFromName()", id, duration.formatted(.units(allowed: [.seconds, .milliseconds]))) + } + #endif + + let filteredSources = sources.filter { $0.maleData == isMalePlayer() } + let normalizedLastName = lastName.canonicalVersionWithPunctuation + let normalizedFirstName = firstName.canonicalVersionWithPunctuation return await withTaskGroup(of: Line?.self) { group in - for source in sources.filter({ $0.maleData == isMalePlayer() }) { - group.addTask { [lastName, firstName] in + for source in filteredSources { + group.addTask { guard !Task.isCancelled else { print("Cancelled"); return nil } - - return try? await source.first(where: { line in - line.rawValue.canonicalVersionWithPunctuation.contains(";\(lastName.canonicalVersionWithPunctuation);\(firstName.canonicalVersionWithPunctuation);") - }) + return try? await source.first { + let lineValue = $0.rawValue.canonicalVersionWithPunctuation + return lineValue.contains(";\(normalizedLastName);\(normalizedFirstName);") + } } } - + if let first = await group.first(where: { $0 != nil }) { group.cancelAll() return first - } else { - return nil } + return nil } } @@ -347,7 +352,7 @@ defer { return } - let currentRank = rank ?? tournament.unrankValue(for: isMalePlayer()) ?? 70_000 + let currentRank = rank ?? tournament.unrankValue(for: isMalePlayer()) ?? 90_000 switch tournament.tournamentCategory { case .men: computedRank = isMalePlayer() ? currentRank : currentRank + PlayerRegistration.addon(for: currentRank, manMax: tournament.maleUnrankedValue ?? 0, womanMax: tournament.femaleUnrankedValue ?? 0) @@ -473,7 +478,6 @@ defer { enum PlayerDataSource: Int, Codable { case frenchFederation = 0 case beachPadel = 1 - case onlineRegistration = 2 } enum PlayerSexType: Int, Hashable, CaseIterable, Identifiable, Codable { diff --git a/PadelClub/Data/TeamRegistration.swift b/PadelClub/Data/TeamRegistration.swift index 5a17734..457b6e7 100644 --- a/PadelClub/Data/TeamRegistration.swift +++ b/PadelClub/Data/TeamRegistration.swift @@ -551,7 +551,7 @@ final class TeamRegistration: ModelObject, Storable { } func unrankValue(for malePlayer: Bool) -> Int { - return tournamentObject()?.unrankValue(for: malePlayer) ?? 70_000 + return tournamentObject()?.unrankValue(for: malePlayer) ?? 90_000 } func groupStageObject() -> GroupStage? { diff --git a/PadelClub/Data/Tournament.swift b/PadelClub/Data/Tournament.swift index 7daa905..18d9245 100644 --- a/PadelClub/Data/Tournament.swift +++ b/PadelClub/Data/Tournament.swift @@ -1511,43 +1511,53 @@ defer { func updateRank(to newDate: Date?) async throws { -#if DEBUG_TIME //DEBUGING TIME -let start = Date() -defer { - let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000) - print("func updateRank()", id, duration.formatted(.units(allowed: [.seconds, .milliseconds]))) -} -#endif + #if DEBUG_TIME + let start = Date() + defer { + let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000) + print("func updateRank()", id, duration.formatted(.units(allowed: [.seconds, .milliseconds]))) + } + #endif guard let newDate else { return } rankSourceDate = newDate + + // Fetch current month data only once + let monthData = currentMonthData() - if currentMonthData() == nil { - let lastRankWoman = SourceFileManager.shared.getUnrankValue(forMale: false, rankSourceDate: rankSourceDate) - let lastRankMan = SourceFileManager.shared.getUnrankValue(forMale: true, rankSourceDate: rankSourceDate) - await MainActor.run { - let formatted: String = URL.importDateFormatter.string(from: newDate) - let monthData: MonthData = MonthData(monthKey: formatted) - monthData.maleUnrankedValue = lastRankMan - monthData.femaleUnrankedValue = lastRankWoman - do { - try DataStore.shared.monthData.addOrUpdate(instance: monthData) - } catch { - Logger.error(error) - } + if monthData == nil { + async let lastRankWoman = SourceFileManager.shared.getUnrankValue(forMale: false, rankSourceDate: rankSourceDate) + async let lastRankMan = SourceFileManager.shared.getUnrankValue(forMale: true, rankSourceDate: rankSourceDate) + + let formatted = URL.importDateFormatter.string(from: newDate) + let newMonthData = MonthData(monthKey: formatted) + + newMonthData.maleUnrankedValue = await lastRankMan + newMonthData.femaleUnrankedValue = await lastRankWoman + + do { + try DataStore.shared.monthData.addOrUpdate(instance: newMonthData) + } catch { + Logger.error(error) } } - - let lastRankMan = currentMonthData()?.maleUnrankedValue - let lastRankWoman = currentMonthData()?.femaleUnrankedValue + + let lastRankMan = monthData?.maleUnrankedValue ?? 0 + let lastRankWoman = monthData?.femaleUnrankedValue ?? 0 + + // Fetch only the required files let dataURLs = SourceFileManager.shared.allFiles.filter { $0.dateFromPath == newDate } + guard !dataURLs.isEmpty else { return } // Early return if no files found + let sources = dataURLs.map { CSVParser(url: $0) } let players = unsortedPlayers() try await players.concurrentForEach { player in - try await player.updateRank(from: sources, lastRank: (player.sex == .female ? lastRankWoman : lastRankMan) ?? 0) + let lastRank = (player.sex == .female) ? lastRankWoman : lastRankMan + try await player.updateRank(from: sources, lastRank: lastRank) } } + func missingUnrankedValue() -> Bool { return maleUnrankedValue == nil || femaleUnrankedValue == nil } diff --git a/PadelClub/Utils/FileImportManager.swift b/PadelClub/Utils/FileImportManager.swift index f67228b..f1c83a5 100644 --- a/PadelClub/Utils/FileImportManager.swift +++ b/PadelClub/Utils/FileImportManager.swift @@ -28,9 +28,6 @@ class ImportObserver { func currentlyImportingLabel() -> String { guard let currentImportDate else { return "import en cours" } - if URL.importDateFormatter.string(from: currentImportDate) == "07-2024" { - return "consolidation des données" - } return "import " + currentImportDate.monthYearFormatted } @@ -44,32 +41,38 @@ class ImportObserver { class FileImportManager { static let shared = FileImportManager() - func updatePlayers(isMale: Bool, players: inout [FederalPlayer]) { let replacements: [(Character, Character)] = [("Á", "ç"), ("‡", "à"), ("Ù", "ô"), ("Ë", "è"), ("Ó", "î"), ("Î", "ë"), ("…", "É"), ("Ô", "ï"), ("È", "é"), ("«", "Ç"), ("»", "È")] - var playersLeft = players - SourceFileManager.shared.allFilesSortedByDate(isMale).forEach({ url in - if playersLeft.isEmpty == false { - let federalPlayers = readCSV(inputFile: url) - let replacementsCharacters = url.dateFromPath.monthYearFormatted != "04-2024" ? [] : replacements + var playersLeft = Dictionary(uniqueKeysWithValues: players.map { ($0.license, $0) }) + + SourceFileManager.shared.allFilesSortedByDate(isMale).forEach { url in + if playersLeft.isEmpty { return } + + let federalPlayers = readCSV(inputFile: url) + let replacementsCharacters = url.dateFromPath.monthYearFormatted != "04-2024" ? [] : replacements + + let federalPlayersDict = Dictionary(uniqueKeysWithValues: federalPlayers.map { ($0.license, $0) }) + + for (license, importedPlayer) in playersLeft { + guard let federalPlayer = federalPlayersDict[license] else { continue } - playersLeft.forEach { importedPlayer in - if let federalPlayer = federalPlayers.first(where: { $0.license == importedPlayer.license }) { - var lastName = federalPlayer.lastName - lastName.replace(characters: replacementsCharacters) - var firstName = federalPlayer.firstName - firstName.replace(characters: replacementsCharacters) - importedPlayer.lastName = lastName.trimmed.uppercased() - importedPlayer.firstName = firstName.trimmed.capitalized - } - } + var lastName = federalPlayer.lastName + var firstName = federalPlayer.firstName + + lastName.replace(characters: replacementsCharacters) + firstName.replace(characters: replacementsCharacters) + + importedPlayer.lastName = lastName.trimmed.uppercased() + importedPlayer.firstName = firstName.trimmed.capitalized + + playersLeft.removeValue(forKey: license) // Remove processed player } - }) + } - players = playersLeft + players = Array(playersLeft.values) } - + func foundInWomenData(license: String?) -> Bool { guard let license = license?.strippedLicense else { return false @@ -148,7 +151,7 @@ class FileImportManager { } let significantPlayerCount = 2 let pl = players.prefix(significantPlayerCount).map { $0.computedRank } - let missingPl = (missing.map { tournament.unrankValue(for: $0 == 1 ? true : false ) ?? ($0 == 1 ? 70_000 : 10_000) }).prefix(significantPlayerCount) + let missingPl = (missing.map { tournament.unrankValue(for: $0 == 1 ? true : false ) ?? ($0 == 1 ? 90_000 : 10_000) }).prefix(significantPlayerCount) self.weight = pl.reduce(0,+) + missingPl.reduce(0,+) } else { self.weight = players.map { $0.computedRank }.reduce(0,+) diff --git a/PadelClub/Utils/SourceFileManager.swift b/PadelClub/Utils/SourceFileManager.swift index 80c330c..41fef12 100644 --- a/PadelClub/Utils/SourceFileManager.swift +++ b/PadelClub/Utils/SourceFileManager.swift @@ -16,6 +16,7 @@ class SourceFileManager { } let rankingSourceDirectory : URL = try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true).appending(path: "rankings") + let anonymousSourceDirectory : URL = try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true).appending(path: "anonymous") func createDirectoryIfNeeded() { let fileManager = FileManager.default @@ -193,6 +194,13 @@ class SourceFileManager { } } + func anonymousFiles() -> [URL] { + let allJSONFiles = try! FileManager.default.contentsOfDirectory(at: anonymousSourceDirectory, includingPropertiesForKeys: nil).filter({ url in + url.pathExtension == "csv" + }) + return allJSONFiles + } + func jsonFiles() -> [URL] { let allJSONFiles = try! FileManager.default.contentsOfDirectory(at: rankingSourceDirectory, includingPropertiesForKeys: nil).filter({ url in url.pathExtension == "json" diff --git a/PadelClub/Views/Components/StepperView.swift b/PadelClub/Views/Components/StepperView.swift index aadca52..217fce5 100644 --- a/PadelClub/Views/Components/StepperView.swift +++ b/PadelClub/Views/Components/StepperView.swift @@ -88,7 +88,7 @@ struct StepperView: View { } fileprivate func _plusIsDisabled() -> Bool { - count >= (maximum ?? 70_000) + count >= (maximum ?? 90_000) } fileprivate func _add() { diff --git a/PadelClub/Views/Navigation/MainView.swift b/PadelClub/Views/Navigation/MainView.swift index 62f9ceb..afd626c 100644 --- a/PadelClub/Views/Navigation/MainView.swift +++ b/PadelClub/Views/Navigation/MainView.swift @@ -185,11 +185,6 @@ struct MainView: View { importObserver.checkingFilesAttempt += 1 importObserver.checkingFiles = false - if lastDataSource == nil || (dataStore.monthData.first(where: { $0.monthKey == "07-2024" }) == nil) { -// await _downloadPreviousDate() - await _importMandatoryData() - } - if let mostRecentDateAvailable = SourceFileManager.shared.mostRecentDateAvailable, mostRecentDateAvailable > SourceFileManager.shared.lastDataSourceDate() ?? .distantPast { print("importing \(mostRecentDateAvailable)") @@ -222,17 +217,6 @@ struct MainView: View { await SourceFileManager.shared.getAllFiles(initialDate: "05-2024") } - private func _importMandatoryData() async { - let mandatoryKey = "07-2024" - if dataStore.monthData.first(where: { $0.monthKey == mandatoryKey }) == nil, let importingDate = URL.importDateFormatter.date(from: mandatoryKey) { - print("importing mandatory july data") - dataStore.appSettings.lastDataSource = mandatoryKey - dataStore.appSettingsStorage.write() - await SourceFileManager.shared.getAllFiles(initialDate: "07-2024") - await _calculateMonthData(dataSource: mandatoryKey) - } - } - private func _checkingDataIntegrity() { guard importObserver.checkingFiles == false, importObserver.isImportingFile() == false else { return @@ -245,27 +229,21 @@ struct MainView: View { Task { await self._checkSourceFileAvailability() } - } else if let lastDataSource, let mostRecentDateImported = URL.importDateFormatter.date(from: lastDataSource), SourceFileManager.isDateAfterUrlImportDate(date:mostRecentDateImported, dateString: "07-2024") { + } else if let lastDataSource, let mostRecentDateImported = URL.importDateFormatter.date(from: lastDataSource) { let monthData = dataStore.monthData.sorted(by: \.creationDate) - if monthData.first(where: { $0.monthKey == "07-2024" }) == nil { + if let current = monthData.last { Task { - await _checkSourceFileAvailability() - } - } else { - if let current = monthData.last { - Task { - let updated = await SourceFileManager.shared.fetchData(fromDate: mostRecentDateImported) - let fileURL = SourceFileManager.shared.allFiles(true).first(where: { $0.dateFromPath == mostRecentDateImported && $0.index == 0 }) - print("file updated", updated) - if let updated, updated == 1 { - await _startImporting(importingDate: mostRecentDateImported) - } else if current.dataModelIdentifier != PersistenceController.getModelVersion() && current.fileModelIdentifier != fileURL?.fileModelIdentifier() { - await _startImporting(importingDate: mostRecentDateImported) - } else if updated == 0 { - await _calculateMonthData(dataSource: current.monthKey) - } + let updated = await SourceFileManager.shared.fetchData(fromDate: mostRecentDateImported) + let fileURL = SourceFileManager.shared.allFiles(true).first(where: { $0.dateFromPath == mostRecentDateImported && $0.index == 0 }) + print("file updated", updated) + if let updated, updated == 1 { + await _startImporting(importingDate: mostRecentDateImported) + } else if current.dataModelIdentifier != PersistenceController.getModelVersion() && current.fileModelIdentifier != fileURL?.fileModelIdentifier() { + await _startImporting(importingDate: mostRecentDateImported) + } else if updated == 0 { + await _calculateMonthData(dataSource: current.monthKey) } } } diff --git a/PadelClub/Views/Navigation/Umpire/PadelClubView.swift b/PadelClub/Views/Navigation/Umpire/PadelClubView.swift index 464a52d..5263260 100644 --- a/PadelClub/Views/Navigation/Umpire/PadelClubView.swift +++ b/PadelClub/Views/Navigation/Umpire/PadelClubView.swift @@ -40,10 +40,10 @@ struct PadelClubView: View { if let currentMonth = monthData.first, currentMonth.incompleteMode { Section { - Text("Attention, depuis Août 2024, les données fédérales publiques des joueurs (messieurs) récupérables sont incomplètes car limité au 40.000 premiers joueurs.") + Text("Attention, depuis Août 2024, les données fédérales publiques des joueurs (messieurs) récupérables sont incomplètes car limité au 80.000 premiers joueurs.") if currentMonth.maleUnrankedValue == nil { - Text("Le rang d'un joueur non-classé n'est donc pas calculable pour le moment, Padel Club utilisera une valeur par défaut de de 70.000.") + Text("Le rang d'un joueur non-classé n'est donc pas calculable pour le moment, Padel Club utilisera une valeur par défaut de de 90.000.") } Text("Un classement souligné comme ci-dessous indiquera que l'information provient d'un mois précédent.") @@ -61,32 +61,22 @@ struct PadelClubView: View { ["36435", "BRUL…", "Romain", "France", "2993139", "15,00", "Non", "2", "NOUVELLE AQUITAINE", "59 33 0447", "SAINT LOUBES TC"] */ + + Section { + RowButtonView("Retry Anonymous") { + await _retryAnonymous() + } + } + + Section { + RowButtonView("Write anonymous") { + _writeAnonymous() + } + } + Section { RowButtonView("Exporter en csv") { - for fileURL in SourceFileManager.shared.jsonFiles() { - let decoder = JSONDecoder() - decoder.userInfo[.maleData] = fileURL.manData - - do { - let data = try Data(contentsOf: fileURL) - let players = try decoder.decode([FederalPlayer].self, from: data) - var anonymousPlayers = players.filter { $0.firstName.isEmpty && $0.lastName.isEmpty } - let okPlayers = players.filter { $0.firstName.isEmpty == false && $0.lastName.isEmpty == false } - - print("before anonymousPlayers.count", anonymousPlayers.count) - FileImportManager.shared.updatePlayers(isMale: fileURL.manData, players: &anonymousPlayers) - print("after local anonymousPlayers.count", anonymousPlayers.filter { $0.firstName.isEmpty && $0.lastName.isEmpty }.count) - - await fetchPlayersDataSequentially(for: &anonymousPlayers) - - print("after beach anonymousPlayers.count", anonymousPlayers.filter { $0.firstName.isEmpty && $0.lastName.isEmpty } - .count) - SourceFileManager.shared.exportToCSV(players: okPlayers + anonymousPlayers, sourceFileType: fileURL.manData ? .messieurs : .dames, date: fileURL.dateFromPath) - SourceFileManager.shared.exportToCSV("anonymes", players: anonymousPlayers.filter { $0.firstName.isEmpty && $0.lastName.isEmpty }, sourceFileType: fileURL.manData ? .messieurs : .dames, date: fileURL.dateFromPath) - } catch { - Logger.error(error) - } - } + await _exportCsv() } } #endif @@ -169,7 +159,7 @@ struct PadelClubView: View { if let maleUnrankedValue = monthData.maleUnrankedValue { Text(maleUnrankedValue.formatted()) } else { - Text(70_000.formatted()) + Text(90_000.formatted()) } } label: { Text("Rang d'un non classé") @@ -187,6 +177,11 @@ struct PadelClubView: View { Text("Rang d'une non classée") Text("Dames") } + #if DEBUG + RowButtonView("recalc") { + await _calculateLastRank(dataSource: monthData.monthKey) + } + #endif } header: { HStack { Text(monthData.monthKey) @@ -242,15 +237,110 @@ struct PadelClubView: View { await SourceFileManager.shared.getAllFiles(initialDate: "08-2022") self.uuid = UUID() } + +#if DEBUG + private func _calculateMonthData(dataSource: String?) async { + if let dataSource, let mostRecentDate = URL.importDateFormatter.date(from: dataSource) { + await MonthData.calculateCurrentUnrankedValues(fromDate: mostRecentDate) + } + } + + private func _calculateLastRank(dataSource: String) async { + await _calculateMonthData(dataSource: dataSource) + } + + private func _writeAnonymous() { + for fileURL in SourceFileManager.shared.anonymousFiles() { + let lastDateString = URL.importDateFormatter.string(from: fileURL.dateFromPath) + let sourceType = fileURL.manData ? SourceFile.messieurs : SourceFile.dames + let dateString = ["CLASSEMENT-PADEL", sourceType.rawValue, lastDateString].filter({ $0.isEmpty == false }).joined(separator: "-") + "." + "csv" + + let documentsUrl:URL = (FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first as URL?)! + let destinationFileUrl = documentsUrl.appendingPathComponent("rankings").appendingPathComponent("\(dateString)") + + updateCSVFile(sourceCSVURL: destinationFileUrl, updatedCSVURL: fileURL) + } + } + + private func _retryAnonymous() async { + for fileURL in SourceFileManager.shared.anonymousFiles() { + let players = FileImportManager.shared.readCSV(inputFile: fileURL) + var anonymousPlayers = players + print("before anonymousPlayers.count", anonymousPlayers.count) + await fetchPlayersDataSequentially(for: &anonymousPlayers) + print("after beach anonymousPlayers.count", anonymousPlayers.filter { $0.firstName.isEmpty && $0.lastName.isEmpty } + .count) + SourceFileManager.shared.exportToCSV("anonymes", players: anonymousPlayers, sourceFileType: fileURL.manData ? .messieurs : .dames, date: fileURL.dateFromPath) + } + } + + private func _exportCsv() async { + for fileURL in SourceFileManager.shared.jsonFiles() { + let decoder = JSONDecoder() + decoder.userInfo[.maleData] = fileURL.manData + + do { + let data = try Data(contentsOf: fileURL) + let players = try decoder.decode([FederalPlayer].self, from: data) + var anonymousPlayers = players.filter { $0.firstName.isEmpty && $0.lastName.isEmpty } + let okPlayers = players.filter { $0.firstName.isEmpty == false && $0.lastName.isEmpty == false } + + print("before anonymousPlayers.count", anonymousPlayers.count) + FileImportManager.shared.updatePlayers(isMale: fileURL.manData, players: &anonymousPlayers) + print("after local anonymousPlayers.count", anonymousPlayers.filter { $0.firstName.isEmpty && $0.lastName.isEmpty }.count) + + await fetchPlayersDataSequentially(for: &anonymousPlayers) + + print("after beach anonymousPlayers.count", anonymousPlayers.filter { $0.firstName.isEmpty && $0.lastName.isEmpty } + .count) + SourceFileManager.shared.exportToCSV(players: okPlayers + anonymousPlayers, sourceFileType: fileURL.manData ? .messieurs : .dames, date: fileURL.dateFromPath) + SourceFileManager.shared.exportToCSV("anonymes", players: anonymousPlayers.filter { $0.firstName.isEmpty && $0.lastName.isEmpty }, sourceFileType: fileURL.manData ? .messieurs : .dames, date: fileURL.dateFromPath) + } catch { + Logger.error(error) + } + } + } +#endif } -//#Preview { -// PadelClubView() -//} +func updateCSVFile(sourceCSVURL: URL, updatedCSVURL: URL) { + do { + let sourceCSVContent = try String(contentsOf: sourceCSVURL, encoding: .utf8) + var sourceCSVLines = sourceCSVContent.components(separatedBy: "\n") + let delimiter = ";" + + let updatedCSVContent = try String(contentsOf: updatedCSVURL, encoding: .utf8) + let updatedCSVLines = updatedCSVContent.components(separatedBy: "\n") + + // Create a dictionary of updated player data by licenseId + var updatedPlayerDict: [String: String] = [:] + for line in updatedCSVLines { + let components = line.components(separatedBy: delimiter) + if let licenseId = components.dropFirst(5).first { + updatedPlayerDict[licenseId] = line + } + } + + // Update the source CSV lines if licenseId matches + for (index, line) in sourceCSVLines.enumerated() { + let components = line.components(separatedBy: delimiter) + if let licenseId = components.dropFirst(5).first, let updatedLine = updatedPlayerDict[licenseId] { + sourceCSVLines[index] = updatedLine + } + } + + // Write back to the file + let finalCSVContent = sourceCSVLines.joined(separator: "\n") + try finalCSVContent.write(to: sourceCSVURL, atomically: true, encoding: .utf8) + print("CSV file updated successfully.") + } catch { + print("Error updating CSV file: \(error)") + } +} // Function to fetch data for a single license ID -func fetchPlayerData(for licenseID: String) async throws -> [Player]? { - guard let url = URL(string: "https://beach-padel.app.fft.fr/beachja/rechercheJoueur/licencies?idHomologation=82469282&numeroLicence=\(licenseID)") else { +func fetchPlayerData(for licenseID: String, idHomologation: String, sessionId: String) async throws -> [Player]? { + guard let url = URL(string: "https://beach-padel.app.fft.fr/beachja/rechercheJoueur/licencies?idHomologation=\(idHomologation)&numeroLicence=\(licenseID)") else { throw URLError(.badURL) } @@ -268,7 +358,7 @@ func fetchPlayerData(for licenseID: String) async throws -> [Player]? { request.setValue("XMLHttpRequest", forHTTPHeaderField: "X-Requested-With") // Add cookies if needed (example cookie header value shown, replace with valid cookies) - request.setValue("JSESSIONID=48C272263C9454774F0DA95F491C3765; AWSALB=nNF/fDwCSmsO9PQD5jkXNUuoMuAzziHTeIkno1uRkNDkKfaOT7VVbh0KOdvGZ5afMw3epLw0p9J+4Ih6cpwqW+XdcLUrr9kJhpQEgP1oeLPRsi/4Yn9uCLCRgPKI; AWSALBCORS=nNF/fDwCSmsO9PQD5jkXNUuoMuAzziHTeIkno1uRkNDkKfaOT7VVbh0KOdvGZ5afMw3epLw0p9J+4Ih6cpwqW+XdcLUrr9kJhpQEgP1oeLPRsi/4Yn9uCLCRgPKI; datadome=3T1lKPP7j_r9MhBRIq_5sBwcCuhI0lfYgQ414DuY7BdYm3jpvHECT05w6Ohl0xMvGVJi3XayxoRsnsKvPti_TIZ90B~boSu2LYs2lm_OssxFSoDGEHTFOf4HTjVkM6i8; TCID=125221542552726081269; TCSESSION=125221542558139099625; TCPID=125221535336434794787; incap_ses_2224_2712217=T+1ySNxxGx/yVRcIdDzdHrUlomcAAAAAAWv/NX2ushG21NP0K9l10g==; nlbi_2712217=Wd9XXxrrXQSWKZBPb9lUTgAAAADRjV4zuALYepgab2n0ra/7; xtan=-; xtant=1; xtvrn=$548419$; visid_incap_2712217=PSfJngzoSuiowsuXXhvOu5K+7mUAAAAAQUIPAAAAAAAleL9ldvN/FC1VykkU9ret; SessionStatId=10.91.140.42.1662124965429001", forHTTPHeaderField: "Cookie") + request.setValue(sessionId, forHTTPHeaderField: "Cookie") let (data, _) = try await URLSession.shared.data(for: request) let decoder = JSONDecoder() @@ -288,9 +378,21 @@ func fetchPlayerData(for licenseID: String) async throws -> [Player]? { // Function to fetch data for multiple license IDs using TaskGroup func fetchPlayersDataSequentially(for licenseIDs: inout [FederalPlayer]) async { + var idHomologation: String = "82469282" + if let _idHomologation = PListReader.readString(plist: "local", key: "idHomologation") { + idHomologation = _idHomologation + } + + var sessionId: String = "" + + if let _sessionId = PListReader.readString(plist: "local", key: "JSESSIONID") { + sessionId = _sessionId + } + + for licenseID in licenseIDs.filter({ $0.firstName.isEmpty && $0.lastName.isEmpty }) { do { - if let playerData = try await fetchPlayerData(for: licenseID.license)?.first { + if let playerData = try await fetchPlayerData(for: licenseID.license, idHomologation: idHomologation, sessionId: sessionId)?.first { licenseID.lastName = playerData.nom licenseID.firstName = playerData.prenom } From 892c5e29dc89b5bce9a8a34c2aa3f78f96b240dd Mon Sep 17 00:00:00 2001 From: Raz Date: Wed, 5 Feb 2025 13:28:19 +0100 Subject: [PATCH 53/57] change free tournament message accordingly to the 3 tournaments gift --- PadelClub.xcodeproj/project.pbxproj | 4 ++-- .../Views/Tournament/Subscription/SubscriptionInfoView.swift | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/PadelClub.xcodeproj/project.pbxproj b/PadelClub.xcodeproj/project.pbxproj index 20266ce..e6f594f 100644 --- a/PadelClub.xcodeproj/project.pbxproj +++ b/PadelClub.xcodeproj/project.pbxproj @@ -3302,7 +3302,7 @@ CODE_SIGN_ENTITLEMENTS = PadelClub/PadelClub.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\""; @@ -3350,7 +3350,7 @@ CODE_SIGN_ENTITLEMENTS = PadelClub/PadelClub.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEFINES_MODULE = YES; DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\""; DEVELOPMENT_TEAM = BQ3Y44M3Q6; diff --git a/PadelClub/Views/Tournament/Subscription/SubscriptionInfoView.swift b/PadelClub/Views/Tournament/Subscription/SubscriptionInfoView.swift index 4566f43..e5b86ca 100644 --- a/PadelClub/Views/Tournament/Subscription/SubscriptionInfoView.swift +++ b/PadelClub/Views/Tournament/Subscription/SubscriptionInfoView.swift @@ -33,7 +33,7 @@ struct SubscriptionInfoView: View { struct FreeTournamentTip: Tip { var title: Text { - return Text("Nous vous offrons votre premier tournoi ! Convoquez les équipes, créez les poules, le tableau comme vous le souhaitez. \nEnregistrez les résultats de chaque équipes et diffusez les scores en temps réel sur les écrans de votre club !") + return Text("Nous vous offrons vos 3 premiers tournois ! Convoquez les équipes, créez les poules, le tableau comme vous le souhaitez. \nEnregistrez les résultats de chaque équipes et diffusez les scores en temps réel sur les écrans de votre club !") } var image: Image? { From 31b13125f988a467e4f140ca942e0110c95736a3 Mon Sep 17 00:00:00 2001 From: Raz Date: Fri, 7 Feb 2025 14:25:02 +0100 Subject: [PATCH 54/57] fix animation glitch rowbutton view fix search tournament city management fix add team mad memory issue --- PadelClub.xcodeproj/project.pbxproj | 8 +++--- PadelClub/Utils/LocationManager.swift | 2 ++ PadelClub/ViewModel/SearchViewModel.swift | 5 ++-- .../Views/Components/RowButtonView.swift | 9 ++++--- .../Agenda/TournamentLookUpView.swift | 26 +++++++++++++++---- .../Views/Tournament/Screen/AddTeamView.swift | 2 +- .../Screen/InscriptionManagerView.swift | 2 +- 7 files changed, 38 insertions(+), 16 deletions(-) diff --git a/PadelClub.xcodeproj/project.pbxproj b/PadelClub.xcodeproj/project.pbxproj index e6f594f..92eb9b4 100644 --- a/PadelClub.xcodeproj/project.pbxproj +++ b/PadelClub.xcodeproj/project.pbxproj @@ -3302,7 +3302,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\""; @@ -3329,7 +3329,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.1.6; + MARKETING_VERSION = 1.1.7; PRODUCT_BUNDLE_IDENTIFIER = app.padelclub; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -3350,7 +3350,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; @@ -3376,7 +3376,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.1.6; + MARKETING_VERSION = 1.1.7; PRODUCT_BUNDLE_IDENTIFIER = app.padelclub; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; diff --git a/PadelClub/Utils/LocationManager.swift b/PadelClub/Utils/LocationManager.swift index 0af0e36..afd5e3f 100644 --- a/PadelClub/Utils/LocationManager.swift +++ b/PadelClub/Utils/LocationManager.swift @@ -37,6 +37,8 @@ class LocationManager: NSObject, ObservableObject, CLLocationManagerDelegate { func requestLocation() { lastError = nil manager.requestLocation() + city = nil + location = nil requestStarted = true } diff --git a/PadelClub/ViewModel/SearchViewModel.swift b/PadelClub/ViewModel/SearchViewModel.swift index be8a85c..cc16ead 100644 --- a/PadelClub/ViewModel/SearchViewModel.swift +++ b/PadelClub/ViewModel/SearchViewModel.swift @@ -319,10 +319,10 @@ class SearchViewModel: ObservableObject, Identifiable { // Remove all characters that are not in the allowedCharacterSet var text = pasteField.canonicalVersion.components(separatedBy: allowedCharacterSet.inverted).joined().trimmedMultiline // Define the regex pattern to match digits - let digitPattern = /\d+/ + let digitPattern = /\b\w*\d\w*\b/ // Replace all occurrences of the pattern (digits) with an empty string - text = text.replacing(digitPattern, with: "") + text = text.replacing(digitPattern, with: "").trimmingCharacters(in: .whitespacesAndNewlines) let textStrings: [String] = text.components(separatedBy: .whitespacesAndNewlines) let nonEmptyStrings: [String] = textStrings.compactMap { $0.isEmpty ? nil : $0 } @@ -367,6 +367,7 @@ class SearchViewModel: ObservableObject, Identifiable { } + print(predicate) return predicate } diff --git a/PadelClub/Views/Components/RowButtonView.swift b/PadelClub/Views/Components/RowButtonView.swift index 1a38101..0dce2d5 100644 --- a/PadelClub/Views/Components/RowButtonView.swift +++ b/PadelClub/Views/Components/RowButtonView.swift @@ -15,28 +15,31 @@ struct RowButtonView: View { var systemImage: String? = nil var image: String? = nil let confirmationMessage: String + let cornerRadius: CGFloat var action: (() -> ())? = nil var asyncAction: (() async -> ())? = nil @State private var askConfirmation: Bool = false @State private var isLoading = false - init(_ title: String, role: ButtonRole? = nil, systemImage: String? = nil, image: String? = nil, confirmationMessage: String? = nil, action: @escaping (() -> ())) { + init(_ title: String, role: ButtonRole? = nil, systemImage: String? = nil, image: String? = nil, cornerRadius: CGFloat = 8, confirmationMessage: String? = nil, action: @escaping (() -> ())) { self.role = role self.title = title self.systemImage = systemImage self.image = image self.confirmationMessage = confirmationMessage ?? defaultConfirmationMessage self.action = action + self.cornerRadius = cornerRadius } - init(_ title: String, role: ButtonRole? = nil, systemImage: String? = nil, image: String? = nil, confirmationMessage: String? = nil, asyncAction: @escaping (() async -> ())) { + init(_ title: String, role: ButtonRole? = nil, systemImage: String? = nil, image: String? = nil, cornerRadius: CGFloat = 8, confirmationMessage: String? = nil, asyncAction: @escaping (() async -> ())) { self.role = role self.title = title self.systemImage = systemImage self.image = image self.confirmationMessage = confirmationMessage ?? defaultConfirmationMessage self.asyncAction = asyncAction + self.cornerRadius = cornerRadius } var body: some View { @@ -79,7 +82,7 @@ struct RowButtonView: View { if isLoading { ZStack { Color.master - .cornerRadius(20) + .cornerRadius(cornerRadius) ProgressView() .tint(.white) } diff --git a/PadelClub/Views/Navigation/Agenda/TournamentLookUpView.swift b/PadelClub/Views/Navigation/Agenda/TournamentLookUpView.swift index f7228f6..8ebd7ee 100644 --- a/PadelClub/Views/Navigation/Agenda/TournamentLookUpView.swift +++ b/PadelClub/Views/Navigation/Agenda/TournamentLookUpView.swift @@ -14,6 +14,7 @@ struct TournamentLookUpView: View { @Environment(FederalDataViewModel.self) var federalDataViewModel: FederalDataViewModel @StateObject var locationManager = LocationManager() @Environment(\.dismiss) private var dismiss + @FocusState private var isFocused: Bool @State private var searchField: String = "" @State var page: Int = 0 @@ -24,6 +25,7 @@ struct TournamentLookUpView: View { @State private var revealSearchParameters: Bool = true @State private var presentAlert: Bool = false @State private var confirmSearch: Bool = false + @State private var locationRequested = false var tournaments: [FederalTournament] { federalDataViewModel.searchedFederalTournaments @@ -96,8 +98,9 @@ struct TournamentLookUpView: View { .navigationTitle("Chercher un tournoi") .navigationBarTitleDisplayMode(.inline) .onChange(of: locationManager.city) { - if let newValue = locationManager.city, dataStore.appSettings.city.isEmpty { + if locationRequested, let newValue = locationManager.city { dataStore.appSettings.city = newValue + locationRequested = false } } .toolbarTitleDisplayMode(.large) @@ -309,19 +312,32 @@ struct TournamentLookUpView: View { HStack { TextField("Ville", text: $appSettings.city) - if let city = locationManager.city { - Divider() - Text(city).italic() - } + .onSubmit(of: .text) { + locationManager.city = nil + locationManager.location = nil + } + .focused($isFocused) + .onChange(of: isFocused) { + if isFocused { + appSettings.city = "" + } + } + +// if let city = locationManager.city { +// Divider() +// Text(city).italic() +// } if locationManager.requestStarted { ProgressView() } else if locationManager.manager.authorizationStatus != .restricted { LocationButton { if locationManager.manager.authorizationStatus == .notDetermined { + locationRequested = true locationManager.manager.requestWhenInUseAuthorization() } else if locationManager.manager.authorizationStatus == .denied { showingSettingsAlert = true } else { + locationRequested = true locationManager.requestLocation() } } diff --git a/PadelClub/Views/Tournament/Screen/AddTeamView.swift b/PadelClub/Views/Tournament/Screen/AddTeamView.swift index 4850645..7fd4bf8 100644 --- a/PadelClub/Views/Tournament/Screen/AddTeamView.swift +++ b/PadelClub/Views/Tournament/Screen/AddTeamView.swift @@ -16,7 +16,7 @@ struct AddTeamView: View { private var fetchRequest: FetchRequest private var fetchPlayers: FetchedResults { fetchRequest.wrappedValue } - var tournament: Tournament + let tournament: Tournament var cancelShouldDismiss: Bool = false enum FocusField: Hashable { case pasteField diff --git a/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift b/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift index 78f3d59..e95fade 100644 --- a/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift +++ b/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift @@ -247,7 +247,7 @@ struct InscriptionManagerView: View { } if tournament.enableOnlineRegistration { - RowButtonView("Rafraîchir la liste") { + RowButtonView("Rafraîchir la liste", cornerRadius: 20) { await _refreshList() } } else if tournament.onlineRegistrationCanBeEnabled() { From 86b3af8337d12812e6243b5b1ce2d4aa8f97a669 Mon Sep 17 00:00:00 2001 From: Raz Date: Sat, 8 Feb 2025 14:10:18 +0100 Subject: [PATCH 55/57] Build 2 --- PadelClub.xcodeproj/project.pbxproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/PadelClub.xcodeproj/project.pbxproj b/PadelClub.xcodeproj/project.pbxproj index 92eb9b4..2c49c05 100644 --- a/PadelClub.xcodeproj/project.pbxproj +++ b/PadelClub.xcodeproj/project.pbxproj @@ -3302,7 +3302,7 @@ CODE_SIGN_ENTITLEMENTS = PadelClub/PadelClub.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\""; @@ -3350,7 +3350,7 @@ CODE_SIGN_ENTITLEMENTS = PadelClub/PadelClub.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEFINES_MODULE = YES; DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\""; DEVELOPMENT_TEAM = BQ3Y44M3Q6; From 715c1b69e073310ea49904da1d8816005abf5794 Mon Sep 17 00:00:00 2001 From: Raz Date: Sun, 9 Feb 2025 17:47:02 +0100 Subject: [PATCH 56/57] fix issue with remove all seeds --- PadelClub/Data/Tournament.swift | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/PadelClub/Data/Tournament.swift b/PadelClub/Data/Tournament.swift index 18d9245..1869399 100644 --- a/PadelClub/Data/Tournament.swift +++ b/PadelClub/Data/Tournament.swift @@ -2415,27 +2415,42 @@ defer { } func removeAllSeeds() async { - unsortedTeams().forEach({ team in + let teams = unsortedTeams() + teams.forEach({ team in team.bracketPosition = nil + team._cachedRestingTime = nil }) - let ts = allRoundMatches().flatMap { match in + let allMatches = allRoundMatches() + let ts = allMatches.flatMap { match in match.teamScores } - + allMatches.forEach { match in + match.disabled = false + match.losingTeamId = nil + match.winningTeamId = nil + match.endDate = nil + match.removeCourt() + match.servingTeamId = nil + } + do { try tournamentStore.teamScores.delete(contentOfs: ts) } catch { Logger.error(error) } + do { - try tournamentStore.teamRegistrations.addOrUpdate(contentOfs: unsortedTeams()) + try tournamentStore.matches.addOrUpdate(contentOfs: allMatches) } catch { Logger.error(error) } - allRounds().forEach({ round in - round.enableRound() - }) + do { + try tournamentStore.teamRegistrations.addOrUpdate(contentOfs: teams) + } catch { + Logger.error(error) + } + updateTournamentState() } func addNewRound(_ roundIndex: Int) async { From 78141957564a6267ff4c8a30b43b6e0091914541 Mon Sep 17 00:00:00 2001 From: Raz Date: Sun, 9 Feb 2025 18:00:55 +0100 Subject: [PATCH 57/57] v1.1.8 --- PadelClub.xcodeproj/project.pbxproj | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/PadelClub.xcodeproj/project.pbxproj b/PadelClub.xcodeproj/project.pbxproj index 2c49c05..dd34dcc 100644 --- a/PadelClub.xcodeproj/project.pbxproj +++ b/PadelClub.xcodeproj/project.pbxproj @@ -3302,7 +3302,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\""; @@ -3329,7 +3329,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.1.7; + MARKETING_VERSION = 1.1.8; PRODUCT_BUNDLE_IDENTIFIER = app.padelclub; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -3350,7 +3350,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; @@ -3376,7 +3376,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.1.7; + MARKETING_VERSION = 1.1.8; PRODUCT_BUNDLE_IDENTIFIER = app.padelclub; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = "";