From 66468d2a564fb6c3a7da33324facdd13aaf9a6ea Mon Sep 17 00:00:00 2001 From: Razmig Sarkissian Date: Wed, 22 May 2024 15:44:12 +0200 Subject: [PATCH 1/9] fix summon stuff --- PadelClub/Data/Match.swift | 13 ++ .../CallMessageCustomizationView.swift | 42 ++++-- .../Views/Calling/CallSettingsView.swift | 27 +++- PadelClub/Views/Calling/SendToAllView.swift | 10 +- PadelClub/Views/Match/MatchDetailView.swift | 136 ++++-------------- 5 files changed, 109 insertions(+), 119 deletions(-) diff --git a/PadelClub/Data/Match.swift b/PadelClub/Data/Match.swift index 4c9bff4..62a2d40 100644 --- a/PadelClub/Data/Match.swift +++ b/PadelClub/Data/Match.swift @@ -168,6 +168,19 @@ class Match: ModelObject, Storable { servingTeamId = nil } + func resetScores() { + if hasEnded() == false { + teamScores.forEach({ $0.score = nil }) + do { + try DataStore.shared.teamScores.addOrUpdate(contentOfs: teamScores) + } catch { + Logger.error(error) + } + } else { + updateTeamScores() + } + } + func teamWillBeWalkOut(_ team: TeamRegistration) { resetMatch() let previousScores = teamScores.filter({ $0.luckyLoser != nil }) diff --git a/PadelClub/Views/Calling/CallMessageCustomizationView.swift b/PadelClub/Views/Calling/CallMessageCustomizationView.swift index d9bb26e..88386db 100644 --- a/PadelClub/Views/Calling/CallMessageCustomizationView.swift +++ b/PadelClub/Views/Calling/CallMessageCustomizationView.swift @@ -24,6 +24,8 @@ struct CallMessageCustomizationView: View { @State private var customCallMessageSignature: String = "" @State private var summonsAvailablePaymentMethods: String = "" + var columns: [GridItem] = Array(repeating: .init(.flexible()), count: 3) + init(tournament: Tournament) { self.tournament = tournament _customCallMessageBody = State(wrappedValue: DataStore.shared.user.summonsMessageBody ?? (DataStore.shared.user.summonsUseFullCustomMessage ? "" : ContactType.defaultCustomMessage)) @@ -73,6 +75,16 @@ struct CallMessageCustomizationView: View { } footer: { HStack { Spacer() + FooterButtonView("effacer") { + customCallMessageSignature = "" + _save() + } + Divider() + FooterButtonView("défaut") { + customCallMessageSignature = DataStore.shared.user.defaultSignature() + _save() + } + Divider() FooterButtonView("éditer") { focusedField = .signature } @@ -142,16 +154,22 @@ struct CallMessageCustomizationView: View { if user.summonsUseFullCustomMessage == false { HStack { Spacer() + FooterButtonView("effacer") { + customCallMessageBody = "" + _save() + } + Divider() + FooterButtonView("défaut") { + customCallMessageBody = ContactType.defaultCustomMessage + _save() + } + Divider() FooterButtonView("éditer") { focusedField = .body } } - } - } - - if user.summonsUseFullCustomMessage { - Section { - LazyHStack { + } else { + LazyVGrid(columns: columns, spacing: 0) { FooterButtonView("#titre") { customCallMessageBody.append("#titre") focusedField = .body @@ -181,8 +199,6 @@ struct CallMessageCustomizationView: View { focusedField = .body } } - } header: { - Text("Utilisez ces balises") } } } @@ -275,6 +291,16 @@ struct CallMessageCustomizationView: View { } footer: { HStack { Spacer() + FooterButtonView("effacer") { + summonsAvailablePaymentMethods = "" + _save() + } + Divider() + FooterButtonView("défaut") { + summonsAvailablePaymentMethods = ContactType.defaultAvailablePaymentMethods + _save() + } + Divider() FooterButtonView("éditer") { focusedField = .paymentMethods } diff --git a/PadelClub/Views/Calling/CallSettingsView.swift b/PadelClub/Views/Calling/CallSettingsView.swift index 64ed7f7..425bcb7 100644 --- a/PadelClub/Views/Calling/CallSettingsView.swift +++ b/PadelClub/Views/Calling/CallSettingsView.swift @@ -6,11 +6,13 @@ // import SwiftUI +import LeStorage struct CallSettingsView: View { @EnvironmentObject var dataStore: DataStore @Environment(Tournament.self) var tournament: Tournament @State private var showSendToAllView: Bool = false + @State private var addLink: Bool = false var body: some View { List { @@ -29,6 +31,28 @@ struct CallSettingsView: View { } } + Section { + RowButtonView("Envoyer le lien du tournoi") { + addLink = true + showSendToAllView = true + } + .disabled(tournament.isPrivate) + } footer: { + if tournament.isPrivate { + Button { + tournament.isPrivate = false + do { + try dataStore.tournaments.addOrUpdate(instance: tournament) + } catch { + Logger.error(error) + } + } label: { + Text(.init("Le tournoi n'est pas visible sur [Padel Club](\(URLs.main.rawValue)), ")).foregroundStyle(.logoRed) + Text("le rendre visible ?").underline().foregroundStyle(.master) + } + } + } + + #if DEBUG Section { RowButtonView("Annuler toutes les convocations", role: .destructive) { let teams = tournament.unsortedTeams() @@ -48,9 +72,10 @@ struct CallSettingsView: View { try? dataStore.teamRegistrations.addOrUpdate(contentOfs: teams) } } + #endif } .sheet(isPresented: $showSendToAllView) { - SendToAllView() + SendToAllView(addLink: addLink) .tint(.master) } } diff --git a/PadelClub/Views/Calling/SendToAllView.swift b/PadelClub/Views/Calling/SendToAllView.swift index 5ebdc71..454701a 100644 --- a/PadelClub/Views/Calling/SendToAllView.swift +++ b/PadelClub/Views/Calling/SendToAllView.swift @@ -16,7 +16,7 @@ struct SendToAllView: View { @State private var contactMethod: Int = 1 @State private var contactRecipients: Set = Set() @State private var sentError: ContactManagerError? = nil - + let addLink: Bool @State var cannotPayForTournament: Bool = false var messageSentFailed: Binding { @@ -72,16 +72,16 @@ struct SendToAllView: View { Section { RowButtonView("Contacter \(_totalString())") { if contactMethod == 0 { - contactType = .message(date: nil, recipients: _teams().flatMap { $0.unsortedPlayers() }.compactMap { $0.phoneNumber }, body: tournament.tournamentTitle(), tournamentBuild: nil) + contactType = .message(date: nil, recipients: _teams().flatMap { $0.unsortedPlayers() }.compactMap { $0.phoneNumber }, body: tournament.shareURL()?.absoluteString , tournamentBuild: nil) } else { - contactType = .mail(date: nil, recipients: tournament.umpireMail(), bccRecipients: _teams().flatMap { $0.unsortedPlayers() }.compactMap { $0.email }, body: nil, subject: tournament.tournamentTitle(), tournamentBuild: nil) + contactType = .mail(date: nil, recipients: tournament.umpireMail(), bccRecipients: _teams().flatMap { $0.unsortedPlayers() }.compactMap { $0.email }, body: tournament.shareURL()?.absoluteString, subject: tournament.tournamentTitle(), tournamentBuild: nil) } } } } .environment(\.editMode, Binding.constant(EditMode.active)) .headerProminence(.increased) - .navigationTitle("Réglages") + .navigationTitle("Préparation") .navigationBarTitleDisplayMode(.inline) .toolbarBackground(.visible, for: .navigationBar) .alert("Un problème est survenu", isPresented: messageSentFailed) { @@ -169,5 +169,5 @@ struct SendToAllView: View { } #Preview { - SendToAllView() + SendToAllView(addLink: true) } diff --git a/PadelClub/Views/Match/MatchDetailView.swift b/PadelClub/Views/Match/MatchDetailView.swift index 4e74f57..3ed5934 100644 --- a/PadelClub/Views/Match/MatchDetailView.swift +++ b/PadelClub/Views/Match/MatchDetailView.swift @@ -281,70 +281,37 @@ struct MatchDetailView: View { } .tint(.master) } + .toolbar { + ToolbarItem(placement: .topBarTrailing) { + Menu { + if match.courtIndex != nil { + Button(role: .destructive) { + match.removeCourt() + save() + } label: { + Text("Supprimer le terrain") + } + } + Button(role: .destructive) { + match.startDate = nil + match.endDate = nil + save() + } label: { + Text("Supprimer l'horaire") + } -// .refreshable { -// if match.isBroadcasted() { -// match.refreshBroadcast() -// } -// } -// .toolbar { -// ToolbarItem(placement: .topBarTrailing) { -// Menu { -// Button { -// scoreType = .live -// } label: { -// Label("Saisie Live", systemImage: "airplayaudio.circle") -// } -// -// Button { -// scoreType = .prepare -// } label: { -// Label("Préparer", systemImage: "calendar") -// } -// -// Divider() -// Menu { -// if match.fieldIndex > 0 { -// Button(role: .destructive) { -// match.currentTournament?.removeField(match.fieldIndex) -// match.fieldIndex = 0 -// match.refreshBroadcast() -// save() -// } label: { -// Label("Supprimer le terrain", systemImage: "figure.run") -// } -// } -// Button(role: .destructive) { -// match.restartMatch() -// save() -// } label: { -// Label("Supprimer l'horaire", systemImage: "xmark.circle.fill") -// } -// -// Button(role: .destructive) { -// match.resetScore() -// save() -// } label: { -// Label("Supprimer les scores", systemImage: "xmark.circle.fill") -// } -// -// if match.isFederalTournament == false && match.isFriendlyMatch == false { -// Button(role: .destructive) { -// match.resetMatch() -// save() -// } label: { -// Label("Supprimer les équipes et les scores", systemImage: "xmark.circle.fill") -// } -// } -// } label: { -// Text("Éditer") -// } -// -// } label: { -// Label("Options", systemImage: "ellipsis.circle") -// } -// } -// } + Button(role: .destructive) { + match.resetScores() + save() + } label: { + Text("Supprimer les scores") + } + + } label: { + LabelOptions() + } + } + } .navigationTitle(match.matchTitle()) .navigationBarTitleDisplayMode(.inline) .toolbarBackground(.visible, for: .navigationBar) @@ -365,8 +332,6 @@ struct MatchDetailView: View { @ViewBuilder var menuView: some View { - broadcastView - if match.hasStarted() { Section { editionView @@ -454,24 +419,11 @@ struct MatchDetailView: View { Text("Tirage au sort visuel") } } - -// if match.canBroadcast() == true { -// Picker(selection: $broadcasted) { -// Text("Oui").tag(true) -// Text("Non").tag(false) -// } label: { -// Text("Diffuser automatiquement") -// } -// } RowButtonView("Valider") { match.validateMatch(fromStartDate: startDateSetup == .now ? Date() : startDate, toEndDate: endDate, fieldSetup: fieldSetup) - if broadcasted { - broadcastAndSave() - } else { - save() - } + save() isEditing.toggle() @@ -481,22 +433,6 @@ struct MatchDetailView: View { } } - @ViewBuilder - var broadcastView: some View { - Section { -// if match.isBroadcasted() { -// RowButtonView("Arrêter de diffuser") { -// match.stopBroadcast() -// save() -// } -// } else if match.canBroadcast() == true { -// RowButtonView("Diffuser", systemImage: "airplayvideo") { -// broadcastAndSave() -// } -// } - } - } - var shareView: some View { NavigationLink { //EditSharingView(match: match) @@ -509,16 +445,6 @@ struct MatchDetailView: View { private func save() { try? dataStore.matches.addOrUpdate(instance: match) } - - private func broadcastAndSave() { - Task { - //try? await match.broadcast() - - await MainActor.run { - } - } - } - } #Preview { From 5efbff8ba16942b35cb958da2f56908b155128a1 Mon Sep 17 00:00:00 2001 From: Razmig Sarkissian Date: Wed, 22 May 2024 15:46:10 +0200 Subject: [PATCH 2/9] fix displayStyle --- PadelClub/Data/Round.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PadelClub/Data/Round.swift b/PadelClub/Data/Round.swift index 9eeadda..d95e138 100644 --- a/PadelClub/Data/Round.swift +++ b/PadelClub/Data/Round.swift @@ -371,7 +371,7 @@ class Round: ModelObject, Storable { if parent != nil { return seedInterval()?.localizedLabel(displayStyle) ?? "Pas trouvé" } - return RoundRule.roundName(fromRoundIndex: index) + return RoundRule.roundName(fromRoundIndex: index, displayStyle: displayStyle) } func updateTournamentState() { From a128634ac57e718dc5d76f394f38a2b839f4c3fb Mon Sep 17 00:00:00 2001 From: Razmig Sarkissian Date: Thu, 23 May 2024 00:12:15 +0200 Subject: [PATCH 3/9] fix more stuff again --- PadelClub/Data/GroupStage.swift | 6 +- PadelClub/Data/Round.swift | 8 +- PadelClub/Data/Tournament.swift | 12 ++ PadelClub/Views/Match/MatchSummaryView.swift | 4 +- .../Navigation/Ongoing/OngoingView.swift | 39 +++++- .../Organizer/TournamentOrganizerView.swift | 2 +- PadelClub/Views/Planning/SchedulerView.swift | 124 +++++++++++++----- .../Screen/InscriptionManagerView.swift | 23 +++- .../Shared/TournamentCellView.swift | 2 +- .../TournamentInscriptionView.swift | 2 +- 10 files changed, 175 insertions(+), 47 deletions(-) diff --git a/PadelClub/Data/GroupStage.swift b/PadelClub/Data/GroupStage.swift index 941900b..690552f 100644 --- a/PadelClub/Data/GroupStage.swift +++ b/PadelClub/Data/GroupStage.swift @@ -304,7 +304,11 @@ class GroupStage: ModelObject, Storable { playedMatches.forEach { match in match.matchFormat = matchFormat } - try? DataStore.shared.matches.addOrUpdate(contentOfs: playedMatches) + do { + try DataStore.shared.matches.addOrUpdate(contentOfs: playedMatches) + } catch { + Logger.error(error) + } } override func deleteDependencies() throws { diff --git a/PadelClub/Data/Round.swift b/PadelClub/Data/Round.swift index d95e138..1abc4aa 100644 --- a/PadelClub/Data/Round.swift +++ b/PadelClub/Data/Round.swift @@ -369,7 +369,7 @@ class Round: ModelObject, Storable { func roundTitle(_ displayStyle: DisplayStyle = .wide) -> String { if parent != nil { - return seedInterval()?.localizedLabel(displayStyle) ?? "Pas trouvé" + return seedInterval()?.localizedLabel(displayStyle) ?? "Round pas trouvé" } return RoundRule.roundName(fromRoundIndex: index, displayStyle: displayStyle) } @@ -467,7 +467,11 @@ class Round: ModelObject, Storable { playedMatches.forEach { match in match.matchFormat = updatedMatchFormat } - try? DataStore.shared.matches.addOrUpdate(contentOfs: playedMatches) + do { + try DataStore.shared.matches.addOrUpdate(contentOfs: playedMatches) + } catch { + Logger.error(error) + } } override func deleteDependencies() throws { diff --git a/PadelClub/Data/Tournament.swift b/PadelClub/Data/Tournament.swift index 956012a..2f43caf 100644 --- a/PadelClub/Data/Tournament.swift +++ b/PadelClub/Data/Tournament.swift @@ -338,6 +338,14 @@ class Tournament : ModelObject, Storable { case finished } + func eventLabel() -> String { + if let event = eventObject(), let name = event.name { + return name + } else { + return "" + } + } + func publishedTeamsDate() -> Date { startDate } @@ -749,6 +757,10 @@ class Tournament : ModelObject, Storable { func unsortedTeams() -> [TeamRegistration] { Store.main.filter { $0.tournament == self.id } } + + func unsortedTeamsWithoutWO() -> [TeamRegistration] { + Store.main.filter { $0.tournament == self.id && $0.walkOut == false } + } func duplicates(in players: [PlayerRegistration]) -> [PlayerRegistration] { var duplicates = [PlayerRegistration]() diff --git a/PadelClub/Views/Match/MatchSummaryView.swift b/PadelClub/Views/Match/MatchSummaryView.swift index d796d37..0bbf819 100644 --- a/PadelClub/Views/Match/MatchSummaryView.swift +++ b/PadelClub/Views/Match/MatchSummaryView.swift @@ -29,12 +29,12 @@ struct MatchSummaryView: View { if let groupStage = match.groupStageObject { self.roundTitle = groupStage.groupStageTitle() } else if let round = match.roundObject { - self.roundTitle = round.roundTitle() + self.roundTitle = round.roundTitle(.short) } else { self.roundTitle = nil } - self.matchTitle = match.matchTitle() + self.matchTitle = match.matchTitle(.short) if let court = match.courtName(), match.hasEnded() == false { self.courtName = court diff --git a/PadelClub/Views/Navigation/Ongoing/OngoingView.swift b/PadelClub/Views/Navigation/Ongoing/OngoingView.swift index 9d7cb90..6753f1c 100644 --- a/PadelClub/Views/Navigation/Ongoing/OngoingView.swift +++ b/PadelClub/Views/Navigation/Ongoing/OngoingView.swift @@ -6,6 +6,7 @@ // import SwiftUI +import LeStorage struct OngoingView: View { @Environment(NavigationViewModel.self) private var navigation: NavigationViewModel @@ -16,20 +17,42 @@ struct OngoingView: View { var matches: [Match] { let sorting = sortByField ? fieldSorting : defaultSorting - return dataStore.matches.filter({ $0.startDate != nil && $0.endDate == nil && $0.courtIndex != nil }).sorted(using: sorting, order: .ascending) + let now = Date() + return dataStore.matches.filter({ $0.startDate != nil && $0.startDate! < now && $0.endDate == nil && $0.courtIndex != nil }).sorted(using: sorting, order: .ascending) } var body: some View { @Bindable var navigation = navigation NavigationStack(path: $navigation.ongoingPath) { - let matches = matches + let matches = matches.filter { $0.currentTournament()?.isDeleted == false } List { ForEach(matches) { match in Section { MatchRowView(match: match, matchViewStyle: .standardStyle) } header: { if let tournament = match.currentTournament() { - Text(tournament.tournamentTitle()) + HStack { + Text(tournament.tournamentTitle()) + Spacer() + if let club = tournament.club() { + Text("@" + club.clubTitle(.short)) + } + } + } else { + Text("Pas de tournoi") + } + } footer: { + HStack { + if let tournament = match.currentTournament() { + Text(tournament.eventLabel()) + } +#if DEBUG + Spacer() + FooterButtonView("copier l'id") { + let pasteboard = UIPasteboard.general + pasteboard.string = match.id + } +#endif } } } @@ -51,6 +74,16 @@ struct OngoingView: View { } label: { } + #if DEBUG + Button("effacer les mauvais matchs") { + let bad = matches.filter({ $0.currentTournament() == nil }) + do { + try dataStore.matches.delete(contentOfs: bad) + } catch { + Logger.error(error) + } + } + #endif //todo //presentFilterView.toggle() } label: { diff --git a/PadelClub/Views/Navigation/Organizer/TournamentOrganizerView.swift b/PadelClub/Views/Navigation/Organizer/TournamentOrganizerView.swift index 72f6afe..3a6ee6f 100644 --- a/PadelClub/Views/Navigation/Organizer/TournamentOrganizerView.swift +++ b/PadelClub/Views/Navigation/Organizer/TournamentOrganizerView.swift @@ -34,7 +34,7 @@ struct TournamentOrganizerView: View { HStack { ScrollView(.horizontal) { HStack { - ForEach(dataStore.tournaments) { tournament in + ForEach(dataStore.tournaments.filter({ $0.isDeleted == false })) { tournament in TournamentButtonView(tournament: tournament) } } diff --git a/PadelClub/Views/Planning/SchedulerView.swift b/PadelClub/Views/Planning/SchedulerView.swift index 9b22844..da758fc 100644 --- a/PadelClub/Views/Planning/SchedulerView.swift +++ b/PadelClub/Views/Planning/SchedulerView.swift @@ -30,23 +30,34 @@ struct SchedulerView: View { List { switch destination { case .scheduleGroupStage: - MatchFormatPickingView(matchFormat: $tournament.groupStageMatchFormat) { - Task { - tournament.matchScheduler()?.updateSchedule(tournament: tournament) + Section { + MatchFormatPickingView(matchFormat: $tournament.groupStageMatchFormat) { + Task { + tournament.matchScheduler()?.updateSchedule(tournament: tournament) + } } - } - .onChange(of: tournament.groupStageMatchFormat) { - let groupStages = tournament.groupStages() - groupStages.forEach { groupStage in - groupStage.updateMatchFormat(tournament.groupStageMatchFormat) + .onChange(of: tournament.groupStageMatchFormat) { + let groupStages = tournament.groupStages() + groupStages.forEach { groupStage in + groupStage.updateMatchFormat(tournament.groupStageMatchFormat) + } + do { + try dataStore.tournaments.addOrUpdate(instance: tournament) + try dataStore.groupStages.addOrUpdate(contentOfs: groupStages) + } catch { + Logger.error(error) + } + } - do { - try dataStore.tournaments.addOrUpdate(instance: tournament) - try dataStore.groupStages.addOrUpdate(contentOfs: groupStages) - } catch { - Logger.error(error) + } footer: { + if tournament.groupStageMatchFormat.weight > tournament.groupStageSmartMatchFormat().weight { + Button { + tournament.groupStageMatchFormat = tournament.groupStageSmartMatchFormat() + } label: { + Text("devrait être joué au moins en " + tournament.groupStageSmartMatchFormat().format + ", ") + Text("modifier ?").underline().foregroundStyle(.master) + } + .buttonStyle(.plain) } - } ForEach(tournament.groupStages()) { @@ -63,6 +74,19 @@ struct SchedulerView: View { } .headerProminence(.increased) .monospacedDigit() + .onChange(of: tournament.groupStageMatchFormat) { + let groupStages = tournament.groupStages() + tournament.groupStages().forEach { groupStage in + groupStage.updateMatchFormat(tournament.groupStageMatchFormat) + } + + do { + try dataStore.tournaments.addOrUpdate(instance: tournament) + try dataStore.groupStages.addOrUpdate(contentOfs: groupStages) + } catch { + Logger.error(error) + } + } } @ViewBuilder @@ -92,33 +116,67 @@ struct SchedulerView: View { } } header: { Text(round.titleLabel()) + } footer: { + let federalFormat = tournament.roundSmartMatchFormat(round.index) + if round.matchFormat.weight > federalFormat.weight { + Button { + round.updateMatchFormatAndAllMatches(federalFormat) + do { + try dataStore.rounds.addOrUpdate(instance: round) + } catch { + Logger.error(error) + } + } label: { + Text("devrait être joué au moins en " + federalFormat.format + ", ") + Text("modifier ?").underline().foregroundStyle(.master) + } + .buttonStyle(.plain) + } } - Section { - NavigationLink { - LoserRoundScheduleEditorView(upperRound: round, tournament: tournament) - .environment(tournament) - } label: { - LabeledContent { - let count = round.loserRounds().filter({ $0.isDisabled() == false }).count - Text(count.formatted() + " tour" + count.pluralSuffix) + if round.index != 0 { + Section { + NavigationLink { + LoserRoundScheduleEditorView(upperRound: round, tournament: tournament) + .environment(tournament) } label: { - if let startDate = round.getLoserRoundStartDate() { - HStack { - Text(startDate.formattedAsHourMinute()).font(.title3) - if let estimatedEndDate = round.estimatedLoserRoundEndDate(tournament.additionalEstimationDuration) { - Image(systemName: "arrowshape.forward.fill") - Text(estimatedEndDate.formattedAsHourMinute()).font(.title3) + LabeledContent { + let count = round.loserRounds().filter({ $0.isDisabled() == false }).count + Text(count.formatted() + " tour" + count.pluralSuffix) + } label: { + if let startDate = round.getLoserRoundStartDate() { + HStack { + Text(startDate.formattedAsHourMinute()).font(.title3) + if let estimatedEndDate = round.estimatedLoserRoundEndDate(tournament.additionalEstimationDuration) { + Image(systemName: "arrowshape.forward.fill") + Text(estimatedEndDate.formattedAsHourMinute()).font(.title3) + } } + Text(startDate.formattedAsDate()) + } else { + Text("Aucun horaire") } - Text(startDate.formattedAsDate()) - } else { - Text("Aucun horaire") + } + } + } header: { + Text("Match de classement \(round.roundTitle(.short))") + } footer: { + if round.index == 1, let semi = round.loserRounds().first { + let federalFormat = tournament.loserBracketSmartMatchFormat(1) + if semi.matchFormat.weight > federalFormat.weight { + Button { + round.updateMatchFormatAndAllMatches(federalFormat) + do { + try dataStore.rounds.addOrUpdate(instance: round) + } catch { + Logger.error(error) + } + } label: { + Text("devrait être joué au moins en " + federalFormat.format + ", ") + Text("modifier ?").underline().foregroundStyle(.master) + } + .buttonStyle(.plain) } } } - } header: { - Text("Match de classement \(round.roundTitle(.short))") } } } diff --git a/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift b/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift index 0c9a994..c0ec79a 100644 --- a/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift +++ b/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift @@ -531,12 +531,25 @@ struct InscriptionManagerView: View { private func _informationView(count: Int) -> some View { Section { + let unsortedTeamsWithoutWO = tournament.unsortedTeamsWithoutWO() + LabeledContent { + Text(unsortedTeamsWithoutWO.count.formatted() + "/" + tournament.teamCount.formatted()) + } label: { + Text("Paires inscrites") + } + + LabeledContent { + Text(max(0, unsortedTeamsWithoutWO.count - tournament.teamCount).formatted()) + } label: { + Text("Liste d'attente") + } + NavigationLink { InscriptionInfoView() .environment(tournament) } label: { LabeledContent { - Text(tournament.registrationIssues().formatted()).font(.largeTitle) + Text(tournament.registrationIssues().formatted()) } label: { Text("Problèmes détéctés") if let closedRegistrationDate = tournament.closedRegistrationDate { @@ -544,8 +557,6 @@ struct InscriptionManagerView: View { } } } - } header: { - Text(count.formatted() + "/" + tournament.teamCount.formatted() + " paires inscrites") } } @@ -849,6 +860,11 @@ struct InscriptionManagerView: View { private func _teamMenuOptionView(_ team: TeamRegistration) -> some View { Menu { Section { + Button("Copier") { + let pasteboard = UIPasteboard.general + pasteboard.string = team.playersPasteData() + } + Divider() Button("Changer les joueurs") { editedTeam = team team.unsortedPlayers().forEach { player in @@ -906,6 +922,7 @@ struct InscriptionManagerView: View { } label: { LabelDelete() } + // } header: { // Text(team.teamLabel(.short)) } diff --git a/PadelClub/Views/Tournament/Shared/TournamentCellView.swift b/PadelClub/Views/Tournament/Shared/TournamentCellView.swift index 3986974..9564134 100644 --- a/PadelClub/Views/Tournament/Shared/TournamentCellView.swift +++ b/PadelClub/Views/Tournament/Shared/TournamentCellView.swift @@ -71,7 +71,7 @@ struct TournamentCellView: View { Spacer() if let tournament = tournament as? Tournament, displayStyle == .wide { let hasStarted = tournament.inscriptionClosed() || tournament.hasStarted() - let count = hasStarted ? tournament.selectedSortedTeams().count : tournament.unsortedTeams().count + let count = hasStarted ? tournament.selectedSortedTeams().count : tournament.unsortedTeamsWithoutWO().count Text(count.formatted()) } else if let federalTournament = tournament as? FederalTournament { Button { diff --git a/PadelClub/Views/Tournament/TournamentInscriptionView.swift b/PadelClub/Views/Tournament/TournamentInscriptionView.swift index 868d261..ba99109 100644 --- a/PadelClub/Views/Tournament/TournamentInscriptionView.swift +++ b/PadelClub/Views/Tournament/TournamentInscriptionView.swift @@ -17,7 +17,7 @@ struct TournamentInscriptionView: View { Section { NavigationLink(value: Screen.inscription) { LabeledContent { - Text(tournament.unsortedTeams().count.formatted() + "/" + tournament.teamCount.formatted()) + Text(tournament.unsortedTeamsWithoutWO().count.formatted() + "/" + tournament.teamCount.formatted()) } label: { Text("Gestion des inscriptions") if let closedRegistrationDate = tournament.closedRegistrationDate { From 92f623c2b4fa4f22d17181f8d797e7294a422cb9 Mon Sep 17 00:00:00 2001 From: Razmig Sarkissian Date: Thu, 23 May 2024 00:21:55 +0200 Subject: [PATCH 4/9] b16 --- 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 c7ee99f..50b2b06 100644 --- a/PadelClub.xcodeproj/project.pbxproj +++ b/PadelClub.xcodeproj/project.pbxproj @@ -1919,7 +1919,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 15; + CURRENT_PROJECT_VERSION = 16; DEFINES_MODULE = YES; DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\""; DEVELOPMENT_TEAM = BQ3Y44M3Q6; @@ -1957,7 +1957,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 15; + CURRENT_PROJECT_VERSION = 16; DEFINES_MODULE = YES; DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\""; DEVELOPMENT_TEAM = BQ3Y44M3Q6; From f97531a767d0da3913b38bb947c452bf60d5c6cf Mon Sep 17 00:00:00 2001 From: Razmig Sarkissian Date: Thu, 23 May 2024 09:38:13 +0200 Subject: [PATCH 5/9] b17 --- PadelClub.xcodeproj/project.pbxproj | 4 +- PadelClub/Data/Tournament.swift | 8 ++- PadelClub/Extensions/String+Extensions.swift | 12 ++++ PadelClub/PadelClubApp.swift | 1 + PadelClub/Utils/ContactManager.swift | 8 +-- .../CallMessageCustomizationView.swift | 8 +-- .../Views/Calling/CallSettingsView.swift | 7 ++- PadelClub/Views/Calling/SendToAllView.swift | 4 +- PadelClub/Views/Cashier/CashierView.swift | 2 +- .../Navigation/Agenda/CalendarView.swift | 2 +- .../Navigation/Ongoing/OngoingView.swift | 2 +- .../Views/Shared/LearnMoreSheetView.swift | 2 +- .../TournamentGeneralSettingsView.swift | 56 ++++++++++--------- .../Screen/InscriptionManagerView.swift | 18 +++--- .../Screen/TournamentSettingsView.swift | 2 +- .../Views/Tournament/TournamentView.swift | 2 +- 16 files changed, 82 insertions(+), 56 deletions(-) diff --git a/PadelClub.xcodeproj/project.pbxproj b/PadelClub.xcodeproj/project.pbxproj index 50b2b06..999a7c0 100644 --- a/PadelClub.xcodeproj/project.pbxproj +++ b/PadelClub.xcodeproj/project.pbxproj @@ -1919,7 +1919,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 16; + CURRENT_PROJECT_VERSION = 17; DEFINES_MODULE = YES; DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\""; DEVELOPMENT_TEAM = BQ3Y44M3Q6; @@ -1957,7 +1957,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 16; + CURRENT_PROJECT_VERSION = 17; DEFINES_MODULE = YES; DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\""; DEVELOPMENT_TEAM = BQ3Y44M3Q6; diff --git a/PadelClub/Data/Tournament.swift b/PadelClub/Data/Tournament.swift index 2f43caf..aaad8a5 100644 --- a/PadelClub/Data/Tournament.swift +++ b/PadelClub/Data/Tournament.swift @@ -762,6 +762,10 @@ class Tournament : ModelObject, Storable { Store.main.filter { $0.tournament == self.id && $0.walkOut == false } } + func walkoutTeams() -> [TeamRegistration] { + Store.main.filter { $0.tournament == self.id && $0.walkOut == true } + } + func duplicates(in players: [PlayerRegistration]) -> [PlayerRegistration] { var duplicates = [PlayerRegistration]() Set(players.compactMap({ $0.licenceId })).forEach { licenceId in @@ -1029,7 +1033,7 @@ class Tournament : ModelObject, Storable { } func tournamentTitle(_ displayStyle: DisplayStyle = .wide) -> String { - [tournamentLevel.localizedLabel(displayStyle), tournamentCategory.localizedLabel(displayStyle), name].compactMap({ $0 }).joined(separator: " ") + [tournamentLevel.localizedLabel(.wide) + " " + tournamentCategory.localizedLabel(.wide), displayStyle == .wide ? name : nil].compactMap({ $0 }).joined(separator: " - ") } func subtitle(_ displayStyle: DisplayStyle = .wide) -> String { @@ -1723,7 +1727,7 @@ extension Tournament: FederalTournamentHolder { extension Tournament: TournamentBuildHolder { func buildHolderTitle() -> String { - tournamentTitle() + tournamentTitle(.short) } var category: TournamentCategory { diff --git a/PadelClub/Extensions/String+Extensions.swift b/PadelClub/Extensions/String+Extensions.swift index 2a54554..4e7c075 100644 --- a/PadelClub/Extensions/String+Extensions.swift +++ b/PadelClub/Extensions/String+Extensions.swift @@ -174,3 +174,15 @@ extension StringProtocol { extension LosslessStringConvertible { var string: String { .init(self) } } + +extension String { + func createTxtFile(_ withName: String = "temp") -> URL { + let url = FileManager.default.temporaryDirectory + .appendingPathComponent(withName) + .appendingPathExtension("txt") + let string = self + try? FileManager.default.removeItem(at: url) + try? string.write(to: url, atomically: true, encoding: .utf8) + return url + } +} diff --git a/PadelClub/PadelClubApp.swift b/PadelClub/PadelClubApp.swift index c899697..8dd82ed 100644 --- a/PadelClub/PadelClubApp.swift +++ b/PadelClub/PadelClubApp.swift @@ -22,6 +22,7 @@ struct PadelClubApp: App { .environment(navigationViewModel) .accentColor(.master) .onAppear { + networkMonitor.checkConnection() self._onAppear() } .task { diff --git a/PadelClub/Utils/ContactManager.swift b/PadelClub/Utils/ContactManager.swift index 077b0b2..53b7e99 100644 --- a/PadelClub/Utils/ContactManager.swift +++ b/PadelClub/Utils/ContactManager.swift @@ -31,7 +31,7 @@ enum ContactType: Identifiable { extension ContactType { static let defaultCustomMessage = "Il est conseillé de vous présenter 10 minutes avant de jouer.\nMerci de me confirmer votre présence avec votre nom et de prévenir votre partenaire." - static let defaultAvailablePaymentMethods = "Règlement possible par chèque ou espèce." + static let defaultAvailablePaymentMethods = "Règlement possible par chèque ou espèces." static func callingCustomMessage(source: String? = nil, tournament: Tournament?, startDate: Date?, roundLabel: String) -> String { let tournamentCustomMessage = source ?? DataStore.shared.user.summonsMessageBody ?? defaultCustomMessage @@ -41,7 +41,7 @@ extension ContactType { let date = startDate ?? tournament?.startDate ?? Date() if let tournament { - text = text.replacingOccurrences(of: "#titre", with: tournament.tournamentTitle()) + text = text.replacingOccurrences(of: "#titre", with: tournament.tournamentTitle(.short)) text = text.replacingOccurrences(of: "#prix", with: tournament.entryFeeMessage) } @@ -77,11 +77,11 @@ extension ContactType { } var computedMessage: String { - [entryFeeMessage, message].compacted().map { $0.trimmed }.joined(separator: "\n") + [entryFeeMessage, message].compacted().map { $0.trimmed }.joined(separator: "\n\n") } if let tournament { - return "Bonjour,\n\nVous êtes \(localizedCalled) pour jouer en \(roundLabel.lowercased()) du \(tournament.tournamentTitle()) au \(clubName) le \(date.formatted(Date.FormatStyle().weekday(.wide).day().month(.wide))) à \(date.formatted(Date.FormatStyle().hour().minute())).\n\n" + computedMessage + "\n\n\(signature)" + return "Bonjour,\n\nVous êtes \(localizedCalled) pour jouer en \(roundLabel.lowercased()) du \(tournament.tournamentTitle(.short)) au \(clubName) le \(date.formatted(Date.FormatStyle().weekday(.wide).day().month(.wide))) à \(date.formatted(Date.FormatStyle().hour().minute())).\n\n" + computedMessage + "\n\n\(signature)" } else { return "Bonjour,\n\nVous êtes \(localizedCalled) \(roundLabel) au \(clubName) le \(date.formatted(Date.FormatStyle().weekday(.wide).day().month(.wide))) à \(date.formatted(Date.FormatStyle().hour().minute())).\n\nMerci de confirmer en répondant à ce message et de prévenir votre partenaire !\n\n\(signature)" } diff --git a/PadelClub/Views/Calling/CallMessageCustomizationView.swift b/PadelClub/Views/Calling/CallMessageCustomizationView.swift index 88386db..08c3154 100644 --- a/PadelClub/Views/Calling/CallMessageCustomizationView.swift +++ b/PadelClub/Views/Calling/CallMessageCustomizationView.swift @@ -48,7 +48,7 @@ struct CallMessageCustomizationView: View { var finalMessage: String? { let localizedCalled = "convoqué" + (tournament.tournamentCategory == .women ? "e" : "") + "s" - return "Bonjour,\n\nVous êtes \(localizedCalled) pour jouer en \(RoundRule.roundName(fromRoundIndex: 2).lowercased()) du \(tournament.tournamentTitle()) au \(clubName) le \(tournament.startDate.formatted(Date.FormatStyle().weekday(.wide).day().month(.wide))) à \(tournament.startDate.formatted(Date.FormatStyle().hour().minute())).\n\n" + computedMessage + "\n\n\(customCallMessageSignature)" + return "Bonjour,\n\nVous êtes \(localizedCalled) pour jouer en \(RoundRule.roundName(fromRoundIndex: 2).lowercased()) du \(tournament.tournamentTitle(.short)) au \(clubName) le \(tournament.startDate.formatted(Date.FormatStyle().weekday(.wide).day().month(.wide))) à \(tournament.startDate.formatted(Date.FormatStyle().hour().minute())).\n\n" + computedMessage + "\n\n\(customCallMessageSignature)" } var body: some View { @@ -103,9 +103,6 @@ struct CallMessageCustomizationView: View { Spacer() Button { focusedField = nil - user.summonsMessageBody = customCallMessageBody - user.summonsMessageSignature = customCallMessageSignature - user.summonsAvailablePaymentMethods = summonsAvailablePaymentMethods _save() } label: { Text("Valider") @@ -129,6 +126,9 @@ struct CallMessageCustomizationView: View { } private func _save() { + self.dataStore.user.summonsMessageBody = customCallMessageBody + self.dataStore.user.summonsMessageSignature = customCallMessageSignature + self.dataStore.user.summonsAvailablePaymentMethods = summonsAvailablePaymentMethods self.dataStore.saveUser() } diff --git a/PadelClub/Views/Calling/CallSettingsView.swift b/PadelClub/Views/Calling/CallSettingsView.swift index 425bcb7..0043eb3 100644 --- a/PadelClub/Views/Calling/CallSettingsView.swift +++ b/PadelClub/Views/Calling/CallSettingsView.swift @@ -34,7 +34,6 @@ struct CallSettingsView: View { Section { RowButtonView("Envoyer le lien du tournoi") { addLink = true - showSendToAllView = true } .disabled(tournament.isPrivate) } footer: { @@ -75,7 +74,11 @@ struct CallSettingsView: View { #endif } .sheet(isPresented: $showSendToAllView) { - SendToAllView(addLink: addLink) + SendToAllView(addLink: false) + .tint(.master) + } + .sheet(isPresented: $addLink) { + SendToAllView(addLink: true) .tint(.master) } } diff --git a/PadelClub/Views/Calling/SendToAllView.swift b/PadelClub/Views/Calling/SendToAllView.swift index 454701a..cf7d8a8 100644 --- a/PadelClub/Views/Calling/SendToAllView.swift +++ b/PadelClub/Views/Calling/SendToAllView.swift @@ -72,9 +72,9 @@ struct SendToAllView: View { Section { RowButtonView("Contacter \(_totalString())") { if contactMethod == 0 { - contactType = .message(date: nil, recipients: _teams().flatMap { $0.unsortedPlayers() }.compactMap { $0.phoneNumber }, body: tournament.shareURL()?.absoluteString , tournamentBuild: nil) + contactType = .message(date: nil, recipients: _teams().flatMap { $0.unsortedPlayers() }.compactMap { $0.phoneNumber }, body: addLink ? tournament.shareURL()?.absoluteString : nil, tournamentBuild: nil) } else { - contactType = .mail(date: nil, recipients: tournament.umpireMail(), bccRecipients: _teams().flatMap { $0.unsortedPlayers() }.compactMap { $0.email }, body: tournament.shareURL()?.absoluteString, subject: tournament.tournamentTitle(), tournamentBuild: nil) + contactType = .mail(date: nil, recipients: tournament.umpireMail(), bccRecipients: _teams().flatMap { $0.unsortedPlayers() }.compactMap { $0.email }, body: addLink ? tournament.shareURL()?.absoluteString : nil, subject: tournament.tournamentTitle(), tournamentBuild: nil) } } } diff --git a/PadelClub/Views/Cashier/CashierView.swift b/PadelClub/Views/Cashier/CashierView.swift index 8fab647..5ca8df8 100644 --- a/PadelClub/Views/Cashier/CashierView.swift +++ b/PadelClub/Views/Cashier/CashierView.swift @@ -152,7 +152,7 @@ struct CashierView: View { .searchable(text: $searchText, isPresented: $isSearching, prompt: Text("Chercher un joueur")) .toolbar { ToolbarItem(placement: .topBarTrailing) { - ShareLink(item: _sharedData()) + ShareLink(item: _sharedData().createTxtFile("bilan")) } } } diff --git a/PadelClub/Views/Navigation/Agenda/CalendarView.swift b/PadelClub/Views/Navigation/Agenda/CalendarView.swift index e700bf9..771f439 100644 --- a/PadelClub/Views/Navigation/Agenda/CalendarView.swift +++ b/PadelClub/Views/Navigation/Agenda/CalendarView.swift @@ -85,7 +85,7 @@ struct CalendarView: View { ForEach(tournamentsByDay, id: \.holderId) { tournamentHolder in if let tournament = tournamentHolder as? Tournament { Section { - Button(tournament.tournamentTitle()) { + Button(tournament.tournamentTitle(.short)) { navigation.path.append(tournament) } } header: { diff --git a/PadelClub/Views/Navigation/Ongoing/OngoingView.swift b/PadelClub/Views/Navigation/Ongoing/OngoingView.swift index 6753f1c..0b4c24c 100644 --- a/PadelClub/Views/Navigation/Ongoing/OngoingView.swift +++ b/PadelClub/Views/Navigation/Ongoing/OngoingView.swift @@ -32,7 +32,7 @@ struct OngoingView: View { } header: { if let tournament = match.currentTournament() { HStack { - Text(tournament.tournamentTitle()) + Text(tournament.tournamentTitle(.short)) Spacer() if let club = tournament.club() { Text("@" + club.clubTitle(.short)) diff --git a/PadelClub/Views/Shared/LearnMoreSheetView.swift b/PadelClub/Views/Shared/LearnMoreSheetView.swift index fa64b4e..d055183 100644 --- a/PadelClub/Views/Shared/LearnMoreSheetView.swift +++ b/PadelClub/Views/Shared/LearnMoreSheetView.swift @@ -28,7 +28,7 @@ struct LearnMoreSheetView: View { .foregroundStyle(.secondary) - ShareLink(item: tournament.pasteDataForImporting()) { + ShareLink(item: tournament.pasteDataForImporting().createTxtFile(tournament.tournamentTitle(.short))) { HStack { Spacer() Text("Exporter les inscriptions") diff --git a/PadelClub/Views/Tournament/Screen/Components/TournamentGeneralSettingsView.swift b/PadelClub/Views/Tournament/Screen/Components/TournamentGeneralSettingsView.swift index 2a3528d..3c99479 100644 --- a/PadelClub/Views/Tournament/Screen/Components/TournamentGeneralSettingsView.swift +++ b/PadelClub/Views/Tournament/Screen/Components/TournamentGeneralSettingsView.swift @@ -9,12 +9,19 @@ import SwiftUI import LeStorage struct TournamentGeneralSettingsView: View { - @Environment(Tournament.self) private var tournament: Tournament @EnvironmentObject var dataStore: DataStore + var tournament: Tournament @State private var tournamentName: String = "" - @FocusState private var textFieldIsFocus: Bool - + @State private var entryFee: Double? = nil + @FocusState private var focusedField: Tournament.CodingKeys? + + init(tournament: Tournament) { + self.tournament = tournament + _tournamentName = State(wrappedValue: tournament.name ?? "") + _entryFee = State(wrappedValue: tournament.entryFee) + } + var body: some View { @Bindable var tournament = tournament Form { @@ -29,44 +36,43 @@ struct TournamentGeneralSettingsView: View { Section { LabeledContent { - TextField(tournament.isFree() ? "Gratuite" : "Inscription", value: $tournament.entryFee, format: .currency(code: Locale.current.currency?.identifier ?? "EUR")) + TextField(tournament.isFree() ? "Gratuite" : "Inscription", value: $entryFee, format: .currency(code: Locale.current.currency?.identifier ?? "EUR")) .keyboardType(.decimalPad) .multilineTextAlignment(.trailing) .frame(maxWidth: .infinity) - .focused($textFieldIsFocus) + .focused($focusedField, equals: ._entryFee) } label: { Text("Inscription") } } Section { - LabeledContent { - TextField("Nom", text: $tournamentName) - .multilineTextAlignment(.trailing) - .frame(maxWidth: .infinity) - .keyboardType(.alphabet) - .autocorrectionDisabled() - .onSubmit { - if tournamentName.trimmed.isEmpty { - tournament.name = nil - } else { - tournament.name = tournamentName - } - } - } label: { - Text("Nom du tournoi") - } + TextField("Nom du tournoi", text: $tournamentName, axis: .vertical) + .lineLimit(2) + .frame(maxWidth: .infinity) + .keyboardType(.alphabet) + .autocorrectionDisabled() + .focused($focusedField, equals: ._name) } } .scrollDismissesKeyboard(.immediately) .toolbarBackground(.visible, for: .navigationBar) .toolbar { - if textFieldIsFocus { + if focusedField != nil { ToolbarItem(placement: .keyboard) { HStack { Spacer() Button("Valider") { - textFieldIsFocus = false + if focusedField == ._name { + if tournamentName.trimmed.isEmpty { + tournament.name = nil + } else { + tournament.name = tournamentName + } + } else if focusedField == ._entryFee { + tournament.entryFee = entryFee + } + focusedField = nil } .buttonStyle(.bordered) } @@ -115,7 +121,3 @@ struct TournamentGeneralSettingsView: View { } } } - -#Preview { - TournamentGeneralSettingsView() -} diff --git a/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift b/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift index c0ec79a..e2b4f37 100644 --- a/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift +++ b/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift @@ -249,7 +249,7 @@ struct InscriptionManagerView: View { Label("Clôturer", systemImage: "lock") } Divider() - ShareLink(item: tournament.pasteDataForImporting()) { + ShareLink(item: tournament.pasteDataForImporting().createTxtFile(self.tournament.tournamentTitle(.short))) { Label("Exporter les paires", systemImage: "square.and.arrow.up") } Button { @@ -531,15 +531,19 @@ struct InscriptionManagerView: View { private func _informationView(count: Int) -> some View { Section { - let unsortedTeamsWithoutWO = tournament.unsortedTeamsWithoutWO() + let unsortedTeams = tournament.unsortedTeams() + let walkoutTeams = tournament.walkoutTeams() + LabeledContent { - Text(unsortedTeamsWithoutWO.count.formatted() + "/" + tournament.teamCount.formatted()) + Text(unsortedTeams.count.formatted() + "/" + tournament.teamCount.formatted()).font(.largeTitle) } label: { - Text("Paires inscrites") + Text("Paire\(unsortedTeams.count.pluralSuffix) inscrite\(unsortedTeams.count.pluralSuffix)") + Text("dont \(walkoutTeams.count.pluralSuffix) forfaite\(walkoutTeams.count.pluralSuffix)") } - + + let unsortedTeamsWithoutWO = tournament.unsortedTeamsWithoutWO() LabeledContent { - Text(max(0, unsortedTeamsWithoutWO.count - tournament.teamCount).formatted()) + Text(max(0, unsortedTeamsWithoutWO.count - tournament.teamCount).formatted()).font(.largeTitle) } label: { Text("Liste d'attente") } @@ -549,7 +553,7 @@ struct InscriptionManagerView: View { .environment(tournament) } label: { LabeledContent { - Text(tournament.registrationIssues().formatted()) + Text(tournament.registrationIssues().formatted()).font(.largeTitle) } label: { Text("Problèmes détéctés") if let closedRegistrationDate = tournament.closedRegistrationDate { diff --git a/PadelClub/Views/Tournament/Screen/TournamentSettingsView.swift b/PadelClub/Views/Tournament/Screen/TournamentSettingsView.swift index f81916d..d9f0121 100644 --- a/PadelClub/Views/Tournament/Screen/TournamentSettingsView.swift +++ b/PadelClub/Views/Tournament/Screen/TournamentSettingsView.swift @@ -67,7 +67,7 @@ struct TournamentSettingsView: View { case .matchFormats: TournamentMatchFormatsSettingsView() case .general: - TournamentGeneralSettingsView() + TournamentGeneralSettingsView(tournament: tournament) case .club: TournamentClubSettingsView() } diff --git a/PadelClub/Views/Tournament/TournamentView.swift b/PadelClub/Views/Tournament/TournamentView.swift index 9f22401..0a4ed9b 100644 --- a/PadelClub/Views/Tournament/TournamentView.swift +++ b/PadelClub/Views/Tournament/TournamentView.swift @@ -121,7 +121,7 @@ struct TournamentView: View { .toolbar { ToolbarItem(placement: .principal) { VStack(spacing: -4.0) { - Text(tournament.tournamentTitle()).font(.headline) + Text(tournament.tournamentTitle(.short)).font(.headline) Text(tournament.formattedDate()) .font(.subheadline).foregroundStyle(.secondary) } From f43abf9331c10ce7cfeb5509c771b6a8cd8bbe03 Mon Sep 17 00:00:00 2001 From: Razmig Sarkissian Date: Thu, 23 May 2024 11:01:11 +0200 Subject: [PATCH 6/9] b18 --- PadelClub.xcodeproj/project.pbxproj | 4 +- PadelClub/Data/Coredata/Persistence.swift | 7 +-- PadelClub/Data/PlayerRegistration.swift | 4 +- PadelClub/Utils/FileImportManager.swift | 4 +- .../Navigation/Toolbox/ToolboxView.swift | 43 +++++++++++++++++++ 5 files changed, 53 insertions(+), 9 deletions(-) diff --git a/PadelClub.xcodeproj/project.pbxproj b/PadelClub.xcodeproj/project.pbxproj index 999a7c0..346dc01 100644 --- a/PadelClub.xcodeproj/project.pbxproj +++ b/PadelClub.xcodeproj/project.pbxproj @@ -1919,7 +1919,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 17; + CURRENT_PROJECT_VERSION = 18; DEFINES_MODULE = YES; DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\""; DEVELOPMENT_TEAM = BQ3Y44M3Q6; @@ -1957,7 +1957,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 17; + CURRENT_PROJECT_VERSION = 18; DEFINES_MODULE = YES; DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\""; DEVELOPMENT_TEAM = BQ3Y44M3Q6; diff --git a/PadelClub/Data/Coredata/Persistence.swift b/PadelClub/Data/Coredata/Persistence.swift index 56ac96e..26a14d2 100644 --- a/PadelClub/Data/Coredata/Persistence.swift +++ b/PadelClub/Data/Coredata/Persistence.swift @@ -155,15 +155,16 @@ class PersistenceController: NSObject { importedPlayer.country?.replace(characters: replacementsCharacters) } importedPlayer.tournamentCount = Int64(data.tournamentCount ?? 0) - importedPlayer.lastName = data.lastName + importedPlayer.lastName = data.lastName.trimmed.uppercased() if fixApril2024 { importedPlayer.lastName?.replace(characters: replacementsCharacters) } - importedPlayer.firstName = data.firstName + importedPlayer.firstName = data.firstName.trimmed.capitalized if fixApril2024 { importedPlayer.firstName?.replace(characters: replacementsCharacters) } - importedPlayer.fullName = data.firstName + " " + data.lastName + + importedPlayer.fullName = data.firstName.trimmed.capitalized + " " + data.lastName.trimmed.uppercased() if fixApril2024 { importedPlayer.fullName?.replace(characters: replacementsCharacters) } diff --git a/PadelClub/Data/PlayerRegistration.swift b/PadelClub/Data/PlayerRegistration.swift index 19f2722..7dc0681 100644 --- a/PadelClub/Data/PlayerRegistration.swift +++ b/PadelClub/Data/PlayerRegistration.swift @@ -60,8 +60,8 @@ class PlayerRegistration: ModelObject, Storable { internal init(importedPlayer: ImportedPlayer) { self.teamRegistration = "" - self.firstName = importedPlayer.firstName ?? "" - self.lastName = importedPlayer.lastName ?? "" + self.firstName = (importedPlayer.firstName ?? "").trimmed.capitalized + self.lastName = (importedPlayer.lastName ?? "").trimmed.uppercased() self.licenceId = importedPlayer.license ?? nil self.rank = Int(importedPlayer.rank) self.sex = importedPlayer.male ? .male : .female diff --git a/PadelClub/Utils/FileImportManager.swift b/PadelClub/Utils/FileImportManager.swift index bde0d70..9e5c057 100644 --- a/PadelClub/Utils/FileImportManager.swift +++ b/PadelClub/Utils/FileImportManager.swift @@ -27,8 +27,8 @@ class FileImportManager { lastName.replace(characters: replacementsCharacters) var firstName = federalPlayer.firstName firstName.replace(characters: replacementsCharacters) - importedPlayer.lastName = lastName - importedPlayer.firstName = firstName + importedPlayer.lastName = lastName.trimmed.uppercased() + importedPlayer.firstName = firstName.trimmed.capitalized } } playersLeft.removeAll(where: { $0.lastName.isEmpty == false }) diff --git a/PadelClub/Views/Navigation/Toolbox/ToolboxView.swift b/PadelClub/Views/Navigation/Toolbox/ToolboxView.swift index 6e960e3..01d973e 100644 --- a/PadelClub/Views/Navigation/Toolbox/ToolboxView.swift +++ b/PadelClub/Views/Navigation/Toolbox/ToolboxView.swift @@ -6,14 +6,57 @@ // import SwiftUI +import LeStorage struct ToolboxView: View { + @EnvironmentObject var dataStore: DataStore @Environment(NavigationViewModel.self) private var navigation: NavigationViewModel var body: some View { @Bindable var navigation = navigation NavigationStack(path: $navigation.toolboxPath) { List { + #if DEBUG + Section { + RowButtonView("Fix Names") { + let playerRegistrations = dataStore.playerRegistrations + playerRegistrations.forEach { player in + player.firstName = player.firstName.trimmed.capitalized + player.lastName = player.lastName.trimmed.uppercased() + } + + do { + try dataStore.playerRegistrations.addOrUpdate(contentOfs: playerRegistrations) + } catch { + Logger.error(error) + } + } + } + + Section { + RowButtonView("Delete teams") { + let teamRegistrations = dataStore.teamRegistrations.filter({ $0.tournamentObject() == nil }) + do { + try dataStore.teamRegistrations.delete(contentOfs: teamRegistrations) + } catch { + Logger.error(error) + } + } + } + + Section { + RowButtonView("Delete players") { + let playersRegistrations = dataStore.playerRegistrations.filter({ $0.team() == nil }) + do { + try dataStore.playerRegistrations.delete(contentOfs: playersRegistrations) + } catch { + Logger.error(error) + } + } + + } + #endif + Section { NavigationLink { SelectablePlayerListView() From 5736755ea6021343525a748e7c8803c44bbfb0e3 Mon Sep 17 00:00:00 2001 From: Razmig Sarkissian Date: Thu, 23 May 2024 11:52:36 +0200 Subject: [PATCH 7/9] fix general settings textfield --- PadelClub.xcodeproj/project.pbxproj | 4 ++-- .../Screen/Components/TournamentGeneralSettingsView.swift | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/PadelClub.xcodeproj/project.pbxproj b/PadelClub.xcodeproj/project.pbxproj index 346dc01..61ec996 100644 --- a/PadelClub.xcodeproj/project.pbxproj +++ b/PadelClub.xcodeproj/project.pbxproj @@ -1919,7 +1919,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 18; + CURRENT_PROJECT_VERSION = 19; DEFINES_MODULE = YES; DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\""; DEVELOPMENT_TEAM = BQ3Y44M3Q6; @@ -1957,7 +1957,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 18; + CURRENT_PROJECT_VERSION = 19; DEFINES_MODULE = YES; DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\""; DEVELOPMENT_TEAM = BQ3Y44M3Q6; diff --git a/PadelClub/Views/Tournament/Screen/Components/TournamentGeneralSettingsView.swift b/PadelClub/Views/Tournament/Screen/Components/TournamentGeneralSettingsView.swift index 3c99479..ed37d53 100644 --- a/PadelClub/Views/Tournament/Screen/Components/TournamentGeneralSettingsView.swift +++ b/PadelClub/Views/Tournament/Screen/Components/TournamentGeneralSettingsView.swift @@ -51,11 +51,9 @@ struct TournamentGeneralSettingsView: View { .lineLimit(2) .frame(maxWidth: .infinity) .keyboardType(.alphabet) - .autocorrectionDisabled() .focused($focusedField, equals: ._name) } } - .scrollDismissesKeyboard(.immediately) .toolbarBackground(.visible, for: .navigationBar) .toolbar { if focusedField != nil { From 2aad3da572c63defb545d9382c7760df757dda56 Mon Sep 17 00:00:00 2001 From: Razmig Sarkissian Date: Thu, 23 May 2024 13:10:15 +0200 Subject: [PATCH 8/9] fix summon and re-importing stuff --- PadelClub.xcodeproj/project.pbxproj | 4 ++-- PadelClub/Data/Tournament.swift | 10 +++++++++- PadelClub/Utils/ContactManager.swift | 8 +++++--- PadelClub/Views/Calling/CallView.swift | 8 ++++++-- .../Tournament/Screen/InscriptionManagerView.swift | 2 +- 5 files changed, 23 insertions(+), 9 deletions(-) diff --git a/PadelClub.xcodeproj/project.pbxproj b/PadelClub.xcodeproj/project.pbxproj index 61ec996..d387608 100644 --- a/PadelClub.xcodeproj/project.pbxproj +++ b/PadelClub.xcodeproj/project.pbxproj @@ -1919,7 +1919,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 19; + CURRENT_PROJECT_VERSION = 20; DEFINES_MODULE = YES; DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\""; DEVELOPMENT_TEAM = BQ3Y44M3Q6; @@ -1957,7 +1957,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 19; + CURRENT_PROJECT_VERSION = 20; DEFINES_MODULE = YES; DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\""; DEVELOPMENT_TEAM = BQ3Y44M3Q6; diff --git a/PadelClub/Data/Tournament.swift b/PadelClub/Data/Tournament.swift index aaad8a5..9ea7d91 100644 --- a/PadelClub/Data/Tournament.swift +++ b/PadelClub/Data/Tournament.swift @@ -863,12 +863,20 @@ class Tournament : ModelObject, Storable { func importTeams(_ teams: [FileImportManager.TeamHolder]) { var teamsToImport = [TeamRegistration]() + let players = players().filter { $0.licenceId != nil } teams.forEach { team in if let previousTeam = team.previousTeam { previousTeam.updatePlayers(team.players, inTournamentCategory: team.tournamentCategory) teamsToImport.append(previousTeam) } else { - let newTeam = addTeam(team.players, registrationDate: team.registrationDate) + var registrationDate = team.registrationDate + if let previousPlayer = players.first(where: { player in + let ids = team.players.compactMap({ $0.licenceId }) + return ids.contains(player.licenceId!) + }), let previousTeamRegistrationDate = previousPlayer.team()?.registrationDate { + registrationDate = previousTeamRegistrationDate + } + let newTeam = addTeam(team.players, registrationDate: registrationDate) teamsToImport.append(newTeam) } } diff --git a/PadelClub/Utils/ContactManager.swift b/PadelClub/Utils/ContactManager.swift index 53b7e99..7557a7a 100644 --- a/PadelClub/Utils/ContactManager.swift +++ b/PadelClub/Utils/ContactManager.swift @@ -56,7 +56,7 @@ extension ContactType { return text } - static func callingMessage(tournament: Tournament?, startDate: Date?, roundLabel: String, matchFormat: MatchFormat?) -> String { + static func callingMessage(tournament: Tournament?, startDate: Date?, roundLabel: String, matchFormat: MatchFormat?, reSummon: Bool = false) -> String { let useFullCustomMessage = DataStore.shared.user.summonsUseFullCustomMessage @@ -79,11 +79,13 @@ extension ContactType { var computedMessage: String { [entryFeeMessage, message].compacted().map { $0.trimmed }.joined(separator: "\n\n") } + + var intro = reSummon ? "Suite à des forfaits, vous êtes finalement" : "Vous êtes" if let tournament { - return "Bonjour,\n\nVous êtes \(localizedCalled) pour jouer en \(roundLabel.lowercased()) du \(tournament.tournamentTitle(.short)) au \(clubName) le \(date.formatted(Date.FormatStyle().weekday(.wide).day().month(.wide))) à \(date.formatted(Date.FormatStyle().hour().minute())).\n\n" + computedMessage + "\n\n\(signature)" + return "Bonjour,\n\n\(intro) \(localizedCalled) pour jouer en \(roundLabel.lowercased()) du \(tournament.tournamentTitle(.short)) au \(clubName) le \(date.formatted(Date.FormatStyle().weekday(.wide).day().month(.wide))) à \(date.formatted(Date.FormatStyle().hour().minute())).\n\n" + computedMessage + "\n\n\(signature)" } else { - return "Bonjour,\n\nVous êtes \(localizedCalled) \(roundLabel) au \(clubName) le \(date.formatted(Date.FormatStyle().weekday(.wide).day().month(.wide))) à \(date.formatted(Date.FormatStyle().hour().minute())).\n\nMerci de confirmer en répondant à ce message et de prévenir votre partenaire !\n\n\(signature)" + return "Bonjour,\n\n\(intro) \(localizedCalled) \(roundLabel) au \(clubName) le \(date.formatted(Date.FormatStyle().weekday(.wide).day().month(.wide))) à \(date.formatted(Date.FormatStyle().hour().minute())).\n\nMerci de confirmer en répondant à ce message et de prévenir votre partenaire !\n\n\(signature)" } } } diff --git a/PadelClub/Views/Calling/CallView.swift b/PadelClub/Views/Calling/CallView.swift index e15ba4a..c6ccc2d 100644 --- a/PadelClub/Views/Calling/CallView.swift +++ b/PadelClub/Views/Calling/CallView.swift @@ -81,11 +81,15 @@ struct CallView: View { } var finalMessage: String { - ContactType.callingMessage(tournament: tournament, startDate: callDate, roundLabel: roundLabel, matchFormat: matchFormat) + ContactType.callingMessage(tournament: tournament, startDate: callDate, roundLabel: roundLabel, matchFormat: matchFormat, reSummon: reSummon) } + var reSummon: Bool { + teams.allSatisfy({ $0.called() }) + } + var body: some View { - let callWord = teams.allSatisfy({ $0.called() }) ? "Reconvoquer" : "Convoquer" + let callWord = reSummon ? "Reconvoquer" : "Convoquer" HStack { if teams.count == 1 { if let previousCallDate = teams.first?.callDate, Calendar.current.compare(previousCallDate, to: callDate, toGranularity: .minute) != .orderedSame { diff --git a/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift b/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift index e2b4f37..6f19425 100644 --- a/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift +++ b/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift @@ -538,7 +538,7 @@ struct InscriptionManagerView: View { Text(unsortedTeams.count.formatted() + "/" + tournament.teamCount.formatted()).font(.largeTitle) } label: { Text("Paire\(unsortedTeams.count.pluralSuffix) inscrite\(unsortedTeams.count.pluralSuffix)") - Text("dont \(walkoutTeams.count.pluralSuffix) forfaite\(walkoutTeams.count.pluralSuffix)") + Text("dont \(walkoutTeams.count) forfait\(walkoutTeams.count.pluralSuffix)") } let unsortedTeamsWithoutWO = tournament.unsortedTeamsWithoutWO() From 93515db50a0b9b2d96794a4fec9aa2dfb087f7c3 Mon Sep 17 00:00:00 2001 From: Razmig Sarkissian Date: Thu, 23 May 2024 14:55:18 +0200 Subject: [PATCH 9/9] fix stuff on manual player creation --- PadelClub.xcodeproj/project.pbxproj | 4 +-- PadelClub/Data/Tournament.swift | 22 ++++++++++--- PadelClub/Utils/ContactManager.swift | 2 +- .../GroupStage/GroupStageSettingsView.swift | 5 +++ .../Player/Components/PlayerPopoverView.swift | 33 +++++++++++-------- PadelClub/Views/Player/PlayerDetailView.swift | 3 ++ PadelClub/Views/Round/RoundSettingsView.swift | 6 ++++ .../Screen/InscriptionManagerView.swift | 19 +++++++---- PadelClubTests/ServerDataTests.swift | 5 +-- 9 files changed, 69 insertions(+), 30 deletions(-) diff --git a/PadelClub.xcodeproj/project.pbxproj b/PadelClub.xcodeproj/project.pbxproj index d387608..cde54b3 100644 --- a/PadelClub.xcodeproj/project.pbxproj +++ b/PadelClub.xcodeproj/project.pbxproj @@ -1919,7 +1919,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 20; + CURRENT_PROJECT_VERSION = 21; DEFINES_MODULE = YES; DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\""; DEVELOPMENT_TEAM = BQ3Y44M3Q6; @@ -1957,7 +1957,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 20; + CURRENT_PROJECT_VERSION = 21; DEFINES_MODULE = YES; DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\""; DEVELOPMENT_TEAM = BQ3Y44M3Q6; diff --git a/PadelClub/Data/Tournament.swift b/PadelClub/Data/Tournament.swift index 9ea7d91..ec95afd 100644 --- a/PadelClub/Data/Tournament.swift +++ b/PadelClub/Data/Tournament.swift @@ -48,8 +48,6 @@ class Tournament : ModelObject, Storable { var publishSummons: Bool = false var publishGroupStages: Bool = false var publishBrackets: Bool = false - - //local var shouldVerifyGroupStage: Bool = false var shouldVerifyBracket: Bool = false @@ -94,9 +92,11 @@ class Tournament : ModelObject, Storable { case _publishSummons = "publishSummons" case _publishGroupStages = "publishGroupStages" case _publishBrackets = "publishBrackets" + case _shouldVerifyGroupStage = "shouldVerifyGroupStage" + case _shouldVerifyBracket = "shouldVerifyBracket" } - 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) { + 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) { self.event = event self.name = name self.startDate = startDate @@ -128,6 +128,8 @@ class Tournament : ModelObject, Storable { self.publishSummons = publishSummons self.publishBrackets = publishBrackets self.publishGroupStages = publishGroupStages + self.shouldVerifyBracket = shouldVerifyBracket + self.shouldVerifyGroupStage = shouldVerifyGroupStage } required init(from decoder: Decoder) throws { @@ -166,7 +168,8 @@ class Tournament : ModelObject, Storable { publishSummons = try container.decodeIfPresent(Bool.self, forKey: ._publishSummons) ?? false publishGroupStages = try container.decodeIfPresent(Bool.self, forKey: ._publishGroupStages) ?? false publishBrackets = try container.decodeIfPresent(Bool.self, forKey: ._publishBrackets) ?? false - + shouldVerifyBracket = try container.decodeIfPresent(Bool.self, forKey: ._shouldVerifyBracket) ?? false + shouldVerifyGroupStage = try container.decodeIfPresent(Bool.self, forKey: ._shouldVerifyGroupStage) ?? false } fileprivate static let _numberFormatter: NumberFormatter = NumberFormatter() @@ -276,6 +279,8 @@ class Tournament : ModelObject, Storable { try container.encode(publishSummons, forKey: ._publishSummons) try container.encode(publishBrackets, forKey: ._publishBrackets) try container.encode(publishGroupStages, forKey: ._publishGroupStages) + try container.encode(shouldVerifyBracket, forKey: ._shouldVerifyBracket) + try container.encode(shouldVerifyGroupStage, forKey: ._shouldVerifyGroupStage) } fileprivate func _encodePayment(container: inout KeyedEncodingContainer) throws { @@ -1152,6 +1157,15 @@ class Tournament : ModelObject, Storable { return TournamentStatus(label: label, completion: completionLabel) } + func confirmedSummonStatus() -> TournamentStatus { + let selectedSortedTeams = selectedSortedTeams() + let called = selectedSortedTeams.filter { $0.confirmationDate != nil } + let label = called.count.formatted() + " / " + selectedSortedTeams.count.formatted() + " confirmées" + let completion = (Double(called.count) / Double(selectedSortedTeams.count)) + let completionLabel = completion.isNaN ? "" : completion.formatted(.percent.precision(.fractionLength(0))) + return TournamentStatus(label: label, completion: completionLabel) + } + func bracketStatus() -> String { let availableSeeds = availableSeeds() if availableSeeds.isEmpty == false { diff --git a/PadelClub/Utils/ContactManager.swift b/PadelClub/Utils/ContactManager.swift index 7557a7a..229e8c7 100644 --- a/PadelClub/Utils/ContactManager.swift +++ b/PadelClub/Utils/ContactManager.swift @@ -80,7 +80,7 @@ extension ContactType { [entryFeeMessage, message].compacted().map { $0.trimmed }.joined(separator: "\n\n") } - var intro = reSummon ? "Suite à des forfaits, vous êtes finalement" : "Vous êtes" + let intro = reSummon ? "Suite à des forfaits, vous êtes finalement" : "Vous êtes" if let tournament { return "Bonjour,\n\n\(intro) \(localizedCalled) pour jouer en \(roundLabel.lowercased()) du \(tournament.tournamentTitle(.short)) au \(clubName) le \(date.formatted(Date.FormatStyle().weekday(.wide).day().month(.wide))) à \(date.formatted(Date.FormatStyle().hour().minute())).\n\n" + computedMessage + "\n\n\(signature)" diff --git a/PadelClub/Views/GroupStage/GroupStageSettingsView.swift b/PadelClub/Views/GroupStage/GroupStageSettingsView.swift index 1c55364..fc17843 100644 --- a/PadelClub/Views/GroupStage/GroupStageSettingsView.swift +++ b/PadelClub/Views/GroupStage/GroupStageSettingsView.swift @@ -32,6 +32,11 @@ struct GroupStageSettingsView: View { Section { RowButtonView("Valider les poules", role: .destructive) { tournament.shouldVerifyGroupStage = false + do { + try dataStore.tournaments.addOrUpdate(instance: tournament) + } catch { + Logger.error(error) + } } } footer: { Text("Suite à changement dans votre liste d'inscrits, veuillez vérifier l'intégrité de vos poules et valider que tout est ok.") diff --git a/PadelClub/Views/Player/Components/PlayerPopoverView.swift b/PadelClub/Views/Player/Components/PlayerPopoverView.swift index 18974c1..3ceb4a4 100644 --- a/PadelClub/Views/Player/Components/PlayerPopoverView.swift +++ b/PadelClub/Views/Player/Components/PlayerPopoverView.swift @@ -32,7 +32,6 @@ struct PlayerPopoverView: View { @State private var source: String? init(source: String?, sex: Int, requiredField: [PlayerCreationField] = [.firstName, .lastName], creationCompletionHandler: @escaping (PlayerRegistration) -> Void) { - let source = source if let source { let words = source.components(separatedBy: .whitespaces) if words.isEmpty == false { @@ -44,6 +43,8 @@ struct PlayerPopoverView: View { } else { _firstName = State(wrappedValue: source) } + + _source = State(wrappedValue: source) } _sex = State(wrappedValue: sex) @@ -51,7 +52,6 @@ struct PlayerPopoverView: View { self.requiredField = requiredField self.creationCompletionHandler = creationCompletionHandler - _source = State(wrappedValue: source) } var body: some View { @@ -94,6 +94,7 @@ struct PlayerPopoverView: View { Spacer() TextField("Prénom", text: $firstName) .submitLabel(.next) + .autocorrectionDisabled() .keyboardType(.alphabet) .textInputAutocapitalization(.words) .focused($firstNameIsFocused) @@ -108,6 +109,7 @@ struct PlayerPopoverView: View { Spacer() TextField("Nom", text: $lastName) .submitLabel(.next) + .autocorrectionDisabled() .textInputAutocapitalization(.words) .keyboardType(.alphabet) .focused($lastNameIsFocused) @@ -180,7 +182,6 @@ struct PlayerPopoverView: View { .onAppear { firstNameIsFocused = true } - .autocorrectionDisabled() .navigationTitle(sex == 1 ? "Nouveau joueur" : "Nouvelle joueuse") .navigationBarTitleDisplayMode(.inline) .toolbarBackground(.visible, for: .navigationBar) @@ -199,23 +200,27 @@ struct PlayerPopoverView: View { } } - if amountIsFocused || licenseIsFocused { + if licenseIsFocused || amountIsFocused { ToolbarItem(placement: .keyboard) { - Button("Confirmer") { - if licenseIsFocused { - license = license.trimmed - if requiredField.contains(.license) { - if license.isLicenseNumber { - amountIsFocused = true + HStack { + Spacer() + Button("Confirmer") { + if licenseIsFocused { + license = license.trimmed + if requiredField.contains(.license) { + if license.isLicenseNumber { + amountIsFocused = true + } else { + displayWrongLicenceError = true + } } else { - displayWrongLicenceError = true + amountIsFocused = true } } else { - amountIsFocused = true + amountIsFocused = false } - } else { - amountIsFocused = false } + .buttonStyle(.bordered) } } } diff --git a/PadelClub/Views/Player/PlayerDetailView.swift b/PadelClub/Views/Player/PlayerDetailView.swift index 4d96fad..63907b1 100644 --- a/PadelClub/Views/Player/PlayerDetailView.swift +++ b/PadelClub/Views/Player/PlayerDetailView.swift @@ -46,6 +46,9 @@ struct PlayerDetailView: View { .focused($textFieldIsFocus) } label: { Text("Rang") + if player.rank == nil { + Text("Classement calculé : " + player.computedRank.formatted()) + } } } header: { Text("Classement actuel") diff --git a/PadelClub/Views/Round/RoundSettingsView.swift b/PadelClub/Views/Round/RoundSettingsView.swift index 59f8eb6..5dc9bc5 100644 --- a/PadelClub/Views/Round/RoundSettingsView.swift +++ b/PadelClub/Views/Round/RoundSettingsView.swift @@ -6,6 +6,7 @@ // import SwiftUI +import LeStorage struct RoundSettingsView: View { @EnvironmentObject var dataStore: DataStore @@ -18,6 +19,11 @@ struct RoundSettingsView: View { Section { RowButtonView("Valider le tableau", role: .destructive) { tournament.shouldVerifyBracket = false + do { + try dataStore.tournaments.addOrUpdate(instance: tournament) + } catch { + Logger.error(error) + } } } footer: { Text("Suite à changement dans votre liste d'inscrits, veuillez vérifier l'intégrité de votre tableau et valider que tout est ok.") diff --git a/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift b/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift index 6f19425..a253c5d 100644 --- a/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift +++ b/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift @@ -120,18 +120,22 @@ struct InscriptionManagerView: View { } } .onAppear { - self.presentationCount += 1 if self.teamsHash == nil { self.teamsHash = _simpleHash(ids: tournament.selectedSortedTeams().map { $0.id }) } } .onDisappear { - self.presentationCount -= 1 - if self.presentationCount == 0 { - let newHash = _simpleHash(ids: tournament.selectedSortedTeams().map { $0.id }) - if let teamsHash { - self.tournament.shouldVerifyBracket = newHash != teamsHash - self.tournament.shouldVerifyGroupStage = newHash != teamsHash + let newHash = _simpleHash(ids: tournament.selectedSortedTeams().map { $0.id }) + if let teamsHash, newHash != teamsHash { + self.teamsHash = newHash + if self.tournament.shouldVerifyBracket == false || self.tournament.shouldVerifyGroupStage == false { + self.tournament.shouldVerifyBracket = true + self.tournament.shouldVerifyGroupStage = true + do { + try dataStore.tournaments.addOrUpdate(instance: tournament) + } catch { + Logger.error(error) + } } } } @@ -161,6 +165,7 @@ struct InscriptionManagerView: View { } .sheet(isPresented: $presentPlayerCreation) { PlayerPopoverView(source: _searchSource(), sex: _addPlayerSex()) { p in + p.setComputedRank(in: tournament) createdPlayers.insert(p) createdPlayerIds.insert(p.id) } diff --git a/PadelClubTests/ServerDataTests.swift b/PadelClubTests/ServerDataTests.swift index 31235a2..4da5c93 100644 --- a/PadelClubTests/ServerDataTests.swift +++ b/PadelClubTests/ServerDataTests.swift @@ -96,7 +96,7 @@ final class ServerDataTests: XCTestCase { return } - let tournament = Tournament(event: eventId, name: "RG Homme", startDate: Date(), endDate: nil, creationDate: Date(), isPrivate: false, groupStageFormat: MatchFormat.megaTie, roundFormat: MatchFormat.nineGames, loserRoundFormat: MatchFormat.nineGamesDecisivePoint, groupStageSortMode: GroupStageOrderingMode.snake, groupStageCount: 2, rankSourceDate: Date(), dayDuration: 5, teamCount: 3, teamSorting: TeamSortingType.rank, federalCategory: TournamentCategory.mix, federalLevelCategory: TournamentLevel.p1000, federalAgeCategory: FederalTournamentAge.a45, closedRegistrationDate: Date(), groupStageAdditionalQualified: 4, courtCount: 9, prioritizeClubMembers: true, qualifiedPerGroupStage: 1, teamsPerGroupStage: 2, entryFee: 30.0, additionalEstimationDuration: 5, isDeleted: true, publishTeams: true, publishSummons: true, publishGroupStages: true, publishBrackets: true) + let tournament = Tournament(event: eventId, name: "RG Homme", startDate: Date(), endDate: nil, creationDate: Date(), isPrivate: false, groupStageFormat: MatchFormat.megaTie, roundFormat: MatchFormat.nineGames, loserRoundFormat: MatchFormat.nineGamesDecisivePoint, groupStageSortMode: GroupStageOrderingMode.snake, groupStageCount: 2, rankSourceDate: Date(), dayDuration: 5, teamCount: 3, teamSorting: TeamSortingType.rank, federalCategory: TournamentCategory.mix, federalLevelCategory: TournamentLevel.p1000, federalAgeCategory: FederalTournamentAge.a45, closedRegistrationDate: Date(), groupStageAdditionalQualified: 4, courtCount: 9, prioritizeClubMembers: true, qualifiedPerGroupStage: 1, teamsPerGroupStage: 2, entryFee: 30.0, additionalEstimationDuration: 5, isDeleted: true, publishTeams: true, publishSummons: true, publishGroupStages: true, publishBrackets: true, shouldVerifyBracket: true, shouldVerifyGroupStage: true) let t = try await Store.main.service().post(tournament) assert(t.event == tournament.event) @@ -130,7 +130,8 @@ final class ServerDataTests: XCTestCase { assert(t.publishSummons == tournament.publishSummons) assert(t.publishGroupStages == tournament.publishGroupStages) assert(t.publishBrackets == tournament.publishBrackets) - + assert(t.shouldVerifyBracket == tournament.shouldVerifyBracket) + assert(t.shouldVerifyGroupStage == tournament.shouldVerifyGroupStage) } func testGroupStage() async throws {