diff --git a/PadelClub.xcodeproj/project.pbxproj b/PadelClub.xcodeproj/project.pbxproj index 28b1759..57ea32a 100644 --- a/PadelClub.xcodeproj/project.pbxproj +++ b/PadelClub.xcodeproj/project.pbxproj @@ -711,6 +711,12 @@ FFA252B62CDD2C6C0074E63F /* OngoingDestination.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFA252B42CDD2C630074E63F /* OngoingDestination.swift */; }; FFA252B72CDD2C6C0074E63F /* OngoingDestination.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFA252B42CDD2C630074E63F /* OngoingDestination.swift */; }; FFA6D7852BB0B795003A31F3 /* FileImportManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFA6D7842BB0B795003A31F3 /* FileImportManager.swift */; }; + FFB0FF672E81B671009EDEAC /* OnboardingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFB0FF662E81B671009EDEAC /* OnboardingView.swift */; }; + FFB0FF682E81B671009EDEAC /* OnboardingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFB0FF662E81B671009EDEAC /* OnboardingView.swift */; }; + FFB0FF692E81B671009EDEAC /* OnboardingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFB0FF662E81B671009EDEAC /* OnboardingView.swift */; }; + FFB0FF732E841042009EDEAC /* WeekdaySelectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFB0FF722E841042009EDEAC /* WeekdaySelectionView.swift */; }; + FFB0FF742E841042009EDEAC /* WeekdaySelectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFB0FF722E841042009EDEAC /* WeekdaySelectionView.swift */; }; + FFB0FF752E841042009EDEAC /* WeekdaySelectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFB0FF722E841042009EDEAC /* WeekdaySelectionView.swift */; }; FFB1C98B2C10255100B154A7 /* TournamentBroadcastRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFB1C98A2C10255100B154A7 /* TournamentBroadcastRowView.swift */; }; FFB378342D672ED200EE82E9 /* MatchFormatGuideView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFB378332D672ED100EE82E9 /* MatchFormatGuideView.swift */; }; FFB378352D672ED200EE82E9 /* MatchFormatGuideView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFB378332D672ED100EE82E9 /* MatchFormatGuideView.swift */; }; @@ -1112,6 +1118,8 @@ FFA252B42CDD2C630074E63F /* OngoingDestination.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OngoingDestination.swift; sourceTree = ""; }; FFA6D7842BB0B795003A31F3 /* FileImportManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FileImportManager.swift; sourceTree = ""; }; FFA6D78A2BB0BEB3003A31F3 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; }; + FFB0FF662E81B671009EDEAC /* OnboardingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingView.swift; sourceTree = ""; }; + FFB0FF722E841042009EDEAC /* WeekdaySelectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeekdaySelectionView.swift; sourceTree = ""; }; FFB1C98A2C10255100B154A7 /* TournamentBroadcastRowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TournamentBroadcastRowView.swift; sourceTree = ""; }; FFB378332D672ED100EE82E9 /* MatchFormatGuideView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MatchFormatGuideView.swift; sourceTree = ""; }; FFBE62042CE9DA0900815D33 /* MatchViewStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MatchViewStyle.swift; sourceTree = ""; }; @@ -1569,6 +1577,7 @@ isa = PBXGroup; children = ( FF59FFB62B90EFBF0061EFF9 /* MainView.swift */, + FFB0FF662E81B671009EDEAC /* OnboardingView.swift */, FFD783FB2B91B919000F62A6 /* Agenda */, FF3F74FA2B91A04B004CFE0E /* Organizer */, FF3F74FB2B91A060004CFE0E /* Toolbox */, @@ -1897,6 +1906,7 @@ FF59FFB22B90EFAC0061EFF9 /* EventListView.swift */, FF5D0D8A2BB4D1E3005CB568 /* CalendarView.swift */, FFD655D72C8DE27400E5B35E /* TournamentLookUpView.swift */, + FFB0FF722E841042009EDEAC /* WeekdaySelectionView.swift */, FF8044AB2C8F676D00A49A52 /* TournamentSubscriptionView.swift */, ); path = Agenda; @@ -2386,7 +2396,9 @@ FF9268072BCE94D90080F940 /* TournamentCallView.swift in Sources */, FFC2DCB42BBE9ECD0046DB9F /* LoserRoundsView.swift in Sources */, FF967CFC2BAEE52E00A9A3BD /* GroupStagesView.swift in Sources */, + FFB0FF682E81B671009EDEAC /* OnboardingView.swift in Sources */, FFD783FF2B91BA42000F62A6 /* PadelClubView.swift in Sources */, + FFB0FF732E841042009EDEAC /* WeekdaySelectionView.swift in Sources */, C497723A2DC28A92005CD239 /* ComposeViews.swift in Sources */, FF3A73F32D37C34D007E3032 /* RegistrationInfoSheetView.swift in Sources */, FF8F264C2BAE0B4100650388 /* TournamentFormatSelectionView.swift in Sources */, @@ -2651,7 +2663,9 @@ FF4CBFEF2C996C0600151637 /* PadelClubView.swift in Sources */, FFE8B5CC2DAA42A000BDE966 /* XlsToCsvService.swift in Sources */, FF3A73F52D37C34D007E3032 /* RegistrationInfoSheetView.swift in Sources */, + FFB0FF672E81B671009EDEAC /* OnboardingView.swift in Sources */, C4D05D4A2DC10CBE009B053C /* PaymentStatusView.swift in Sources */, + FFB0FF742E841042009EDEAC /* WeekdaySelectionView.swift in Sources */, C49772392DC28A92005CD239 /* ComposeViews.swift in Sources */, FF4CBFF22C996C0600151637 /* TournamentFormatSelectionView.swift in Sources */, FF17CA592CC02FEB003C7323 /* CoachListView.swift in Sources */, @@ -2894,7 +2908,9 @@ FF70FB6E2C90584900129CC2 /* PadelClubView.swift in Sources */, FFE8B5CB2DAA42A000BDE966 /* XlsToCsvService.swift in Sources */, FF3A73F42D37C34D007E3032 /* RegistrationInfoSheetView.swift in Sources */, + FFB0FF692E81B671009EDEAC /* OnboardingView.swift in Sources */, C4D05D4B2DC10CBE009B053C /* PaymentStatusView.swift in Sources */, + FFB0FF752E841042009EDEAC /* WeekdaySelectionView.swift in Sources */, C497723B2DC28A92005CD239 /* ComposeViews.swift in Sources */, FF70FB712C90584900129CC2 /* TournamentFormatSelectionView.swift in Sources */, FF17CA582CC02FEB003C7323 /* CoachListView.swift in Sources */, @@ -3123,7 +3139,6 @@ DEFINES_MODULE = YES; DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\""; DEVELOPMENT_TEAM = BQ3Y44M3Q6; - ENABLE_DEBUG_DYLIB = NO; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = PadelClub/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = "Padel Club"; diff --git a/PadelClub/ViewModel/FederalDataViewModel.swift b/PadelClub/ViewModel/FederalDataViewModel.swift index 593c6c5..4297c96 100644 --- a/PadelClub/ViewModel/FederalDataViewModel.swift +++ b/PadelClub/ViewModel/FederalDataViewModel.swift @@ -23,6 +23,7 @@ class FederalDataViewModel { var searchAttemptCount: Int = 0 var dayDuration: Int? var dayPeriod: DayPeriod = .all + var weekdays: Set = Set() var lastError: NetworkManagerError? func filterStatus() -> String { @@ -36,6 +37,7 @@ class FederalDataViewModel { } labels.append(contentsOf: clubNames.formatList()) + labels.append(contentsOf: weekdays.map { Date.weekdays[$0 - 1] }.formatList()) if dayPeriod != .all { labels.append(dayPeriod.localizedDayPeriodLabel()) } @@ -72,7 +74,7 @@ class FederalDataViewModel { } func areFiltersEnabled() -> Bool { - (levels.isEmpty && categories.isEmpty && ageCategories.isEmpty && selectedClubs.isEmpty && dayPeriod == .all && dayDuration == nil) == false + (weekdays.isEmpty && levels.isEmpty && categories.isEmpty && ageCategories.isEmpty && selectedClubs.isEmpty && dayPeriod == .all && dayDuration == nil) == false } var filteredFederalTournaments: [FederalTournamentHolder] { @@ -96,6 +98,8 @@ class FederalDataViewModel { (dayPeriod == .all || (dayPeriod != .all && dayPeriod == tournament.dayPeriod)) && (dayDuration == nil || (dayDuration != nil && dayDuration == tournament.dayDuration)) + && + (weekdays.isEmpty || weekdays.contains(tournament.startDate.weekDay)) }) } @@ -106,6 +110,8 @@ class FederalDataViewModel { (dayPeriod == .all || (dayPeriod != .all && dayPeriod == tournament.dayPeriod)) && (dayDuration == nil || (dayDuration != nil && dayDuration == tournament.dayDuration)) + && + (weekdays.isEmpty || weekdays.contains(tournament.startDate.weekDay)) }) .flatMap { $0.tournaments } .filter { @@ -137,6 +143,8 @@ class FederalDataViewModel { (dayPeriod == .all || (dayPeriod != .all && dayPeriod == tournament.dayPeriod)) && (dayDuration == nil || (dayDuration != nil && dayDuration == tournament.dayDuration)) + && + (weekdays.isEmpty || weekdays.contains(tournament.startDate.weekDay)) if let codeClub = tournament.club()?.code { return firstPart && (selectedClubs.isEmpty || selectedClubs.contains(codeClub)) @@ -157,6 +165,8 @@ class FederalDataViewModel { (dayPeriod == .all || (dayPeriod != .all && dayPeriod == tournament.dayPeriod)) && (dayDuration == nil || (dayDuration != nil && dayDuration == tournament.dayDuration)) + && + (weekdays.isEmpty || weekdays.contains(tournament.startDate.weekDay)) } func gatherTournaments(clubs: [Club], startDate: Date, endDate: Date? = nil) async throws { diff --git a/PadelClub/ViewModel/SearchViewModel.swift b/PadelClub/ViewModel/SearchViewModel.swift index e6ffc3b..cab9f3d 100644 --- a/PadelClub/ViewModel/SearchViewModel.swift +++ b/PadelClub/ViewModel/SearchViewModel.swift @@ -74,6 +74,16 @@ class SearchViewModel: ObservableObject, Identifiable { } return message.joined(separator: "\n") } + + func sortTitle() -> String { + var base = [sortOption.localizedLabel()] + base.append((ascending ? "croissant" : "décroissant")) + + if selectedAgeCategory != .unlisted { + base.append(selectedAgeCategory.localizedFederalAgeLabel()) + } + return base.joined(separator: " ") + } func codeClubs() -> [String] { let clubs: [Club] = DataStore.shared.user.clubsObjects() diff --git a/PadelClub/Views/Calling/Components/MenuWarningView.swift b/PadelClub/Views/Calling/Components/MenuWarningView.swift index fb3d4bf..86301a1 100644 --- a/PadelClub/Views/Calling/Components/MenuWarningView.swift +++ b/PadelClub/Views/Calling/Components/MenuWarningView.swift @@ -44,7 +44,6 @@ struct MenuWarningView: View { } } label: { Text("Prévenir") - .underline() } .sheet(isPresented: self.$showSubscriptionView, content: { NavigationStack { diff --git a/PadelClub/Views/Components/ButtonValidateView.swift b/PadelClub/Views/Components/ButtonValidateView.swift index 0543e4d..fb50b34 100644 --- a/PadelClub/Views/Components/ButtonValidateView.swift +++ b/PadelClub/Views/Components/ButtonValidateView.swift @@ -13,10 +13,16 @@ struct ButtonValidateView: View { let action: () -> () var body: some View { - Button(title, role: role) { - action() + if #available(iOS 26.0, *) { + + Button(title, systemImage: "checkmark", role: role) { + action() + } + .buttonStyle(.borderedProminent) + } else { + Button(title, role: role) { + action() + } } - .clipShape(Capsule()) - .buttonStyle(.bordered) } } diff --git a/PadelClub/Views/Components/Labels.swift b/PadelClub/Views/Components/Labels.swift index a87e34a..45f8947 100644 --- a/PadelClub/Views/Components/Labels.swift +++ b/PadelClub/Views/Components/Labels.swift @@ -9,7 +9,11 @@ import SwiftUI struct LabelOptions: View { var body: some View { - Label("Options", systemImage: "ellipsis.circle") + if #available(iOS 26.0, *) { + Label("Options", systemImage: "ellipsis") + } else { + Label("Options", systemImage: "ellipsis.circle") + } } } @@ -39,6 +43,10 @@ struct ShareLabel: View { struct LabelFilter: View { var body: some View { - Label("Filtrer", systemImage: "line.3.horizontal.decrease.circle") + if #available(iOS 26.0, *) { + Label("Filtrer", systemImage: "line.3.horizontal.decrease") + } else { + Label("Filtrer", systemImage: "line.3.horizontal.decrease.circle") + } } } diff --git a/PadelClub/Views/GroupStage/Components/GroupStageSettingsView.swift b/PadelClub/Views/GroupStage/Components/GroupStageSettingsView.swift index 865e42c..ade9616 100644 --- a/PadelClub/Views/GroupStage/Components/GroupStageSettingsView.swift +++ b/PadelClub/Views/GroupStage/Components/GroupStageSettingsView.swift @@ -126,17 +126,7 @@ struct GroupStageSettingsView: View { Section { RowButtonView("Retirer tout le monde", role: .destructive) { - let teams = groupStage.teams() - teams.forEach { team in - team.groupStagePosition = nil - team.groupStage = nil - groupStage._matches().forEach({ $0.updateTeamScores() }) - } - do { - try tournamentStore?.teamRegistrations.addOrUpdate(contentOfs: teams) - } catch { - Logger.error(error) - } + groupStage.removeAllTeams() } } footer: { Text("Toutes les équipes seront retirées et les scores des matchs seront perdus.") @@ -188,6 +178,14 @@ struct GroupStageSettingsView: View { } footer: { Text("Mets à jour les équipes de la poule si jamais une erreur est persistante.") } + + if tournament.lastStep() == 0 { + RowButtonView("Effacer la poule", role: .destructive) { + tournament.deleteGroupStage(groupStage) + dismiss() + dataStore.tournaments.addOrUpdate(instance: self.tournament) + } + } } .onChange(of: size) { if size != groupStage.size { diff --git a/PadelClub/Views/GroupStage/GroupStageView.swift b/PadelClub/Views/GroupStage/GroupStageView.swift index bc8acb9..7007e66 100644 --- a/PadelClub/Views/GroupStage/GroupStageView.swift +++ b/PadelClub/Views/GroupStage/GroupStageView.swift @@ -70,7 +70,7 @@ struct GroupStageView: View { } Section { - MatchListView(section: "à lancer", matches: groupStage.readyMatches(playedMatches: playedMatches), hideWhenEmpty: true) + MatchListView(section: "à lancer", matches: groupStage.readyMatches(playedMatches: playedMatches, runningMatches: runningMatches), hideWhenEmpty: true) } Section { diff --git a/PadelClub/Views/GroupStage/GroupStagesSettingsView.swift b/PadelClub/Views/GroupStage/GroupStagesSettingsView.swift index f87f6d3..d085b4d 100644 --- a/PadelClub/Views/GroupStage/GroupStagesSettingsView.swift +++ b/PadelClub/Views/GroupStage/GroupStagesSettingsView.swift @@ -99,6 +99,14 @@ struct GroupStagesSettingsView: View { } if tournament.lastStep() == 0, step == 0 { + + Section { + RowButtonView("Ajouter une poule", role: .destructive) { + self.tournament.addGroupStage() + dataStore.tournaments.addOrUpdate(instance: self.tournament) + } + } + Section { RowButtonView("Ajouter une phase de poule", role: .destructive) { tournament.addNewGroupStageStep() diff --git a/PadelClub/Views/GroupStage/GroupStagesView.swift b/PadelClub/Views/GroupStage/GroupStagesView.swift index ddf5d81..a14d4bb 100644 --- a/PadelClub/Views/GroupStage/GroupStagesView.swift +++ b/PadelClub/Views/GroupStage/GroupStagesView.swift @@ -234,7 +234,7 @@ struct GroupStagesView: View { Section { - MatchListView(section: "à lancer", matches: Tournament.readyMatches(allMatches), isExpanded: false) + MatchListView(section: "à lancer", matches: Tournament.readyMatches(allMatches, runningMatches: runningMatches), isExpanded: false) } Section { diff --git a/PadelClub/Views/Match/MatchDetailView.swift b/PadelClub/Views/Match/MatchDetailView.swift index 5e59ccb..2b130a8 100644 --- a/PadelClub/Views/Match/MatchDetailView.swift +++ b/PadelClub/Views/Match/MatchDetailView.swift @@ -317,7 +317,18 @@ struct MatchDetailView: View { })) { Text(match.confirmed ? "Confirmé" : "Non confirmé") } + + if match.hasWalkoutTeam() == true { + Divider() + Button(role: .destructive) { + match.removeWalkOut() + save() + } label: { + Text("Annuler le forfait") + } + } + Divider() if match.courtIndex != nil { diff --git a/PadelClub/Views/Navigation/Agenda/ActivityView.swift b/PadelClub/Views/Navigation/Agenda/ActivityView.swift index 6a29320..25573f3 100644 --- a/PadelClub/Views/Navigation/Agenda/ActivityView.swift +++ b/PadelClub/Views/Navigation/Agenda/ActivityView.swift @@ -25,7 +25,8 @@ struct ActivityView: View { @State private var quickAccessScreen: QuickAccessScreen? = nil @State private var displaySearchView: Bool = false @State private var pasteString: String? = nil - + @State private var presentOnboarding: Bool = false + enum QuickAccessScreen : Identifiable, Hashable { case inscription @@ -77,15 +78,21 @@ struct ActivityView: View { @ViewBuilder private func _pasteView() -> some View { - Button { - quickAccessScreen = .inscription - } label: { - Image(systemName: "person.crop.circle.badge.plus") - .resizable() - .scaledToFit() - .frame(minHeight: 32) + if #available(iOS 26.0, *) { + Button("Ajouter une équipe", systemImage: "person.badge.plus") { + quickAccessScreen = .inscription + } + } else { + Button { + quickAccessScreen = .inscription + } label: { + Image(systemName: "person.crop.circle.badge.plus") + .resizable() + .scaledToFit() + .frame(minHeight: 32) + } + .accessibilityLabel("Ajouter une équipe") } - .accessibilityLabel("Ajouter une équipe") // if pasteButtonIsDisplayed == nil || pasteButtonIsDisplayed == true { // PasteButton(payloadType: String.self) { strings in @@ -222,44 +229,82 @@ struct ActivityView: View { // print("disappearing", "pasteButtonIsDisplayed", pasteButtonIsDisplayed) // }) .toolbar { - ToolbarItemGroup(placement: .topBarLeading) { - Button { - switch viewStyle { - case .list: - viewStyle = .calendar - case .calendar: - viewStyle = .list + ToolbarItem(placement: .topBarLeading) { + if #available(iOS 26.0, *) { + Button("Vue calendrier", systemImage: "calendar") { + switch viewStyle { + case .list: + viewStyle = .calendar + case .calendar: + viewStyle = .list + } } - } label: { - Image(systemName: "calendar.circle") - .resizable() - .scaledToFit() - .frame(minHeight: 32) + .symbolVariant(viewStyle == .calendar ? .fill : .none) + } else { + Button { + switch viewStyle { + case .list: + viewStyle = .calendar + case .calendar: + viewStyle = .list + } + } label: { + Image(systemName: "calendar.circle") + .resizable() + .scaledToFit() + .frame(minHeight: 32) + } + .symbolVariant(viewStyle == .calendar ? .fill : .none) } - .symbolVariant(viewStyle == .calendar ? .fill : .none) + } + + if #available(iOS 26.0, *) { + ToolbarSpacer(placement: .topBarLeading) + } + + ToolbarItem(placement: .topBarLeading) { - Button { - presentFilterView.toggle() - } label: { - Image(systemName: "line.3.horizontal.decrease.circle") - .resizable() - .scaledToFit() - .frame(minHeight: 32) + if #available(iOS 26.0, *) { + + Button("Filtre", systemImage: "line.3.horizontal.decrease") { + presentFilterView.toggle() + } + .symbolVariant(federalDataViewModel.areFiltersEnabled() ? .fill : .none) + } else { + Button { + presentFilterView.toggle() + } label: { + Image(systemName: "line.3.horizontal.decrease.circle") + .resizable() + .scaledToFit() + .frame(minHeight: 32) + } + .symbolVariant(federalDataViewModel.areFiltersEnabled() ? .fill : .none) } - .symbolVariant(federalDataViewModel.areFiltersEnabled() ? .fill : .none) - + } + + if #available(iOS 26.0, *) { + ToolbarSpacer(placement: .topBarLeading) + } + + ToolbarItem(placement: .topBarLeading) { _pasteView() } ToolbarItem(placement: .topBarTrailing) { - Button { - newTournament = Tournament.newEmptyInstance() - - } label: { - Image(systemName: "plus.circle.fill") - .resizable() - .scaledToFit() - .frame(minHeight: 32) + if #available(iOS 26.0, *) { + Button("Ajouter", systemImage: "plus") { + newTournament = Tournament.newEmptyInstance() + } + } else { + Button { + newTournament = Tournament.newEmptyInstance() + } label: { + Image(systemName: "plus.circle.fill") + .resizable() + .scaledToFit() + .frame(minHeight: 32) + } } } @@ -296,6 +341,10 @@ struct ActivityView: View { } } } + .sheet(isPresented: $presentOnboarding, content: { + OnboardingView() + .environmentObject(dataStore) + }) .sheet(isPresented: $presentFilterView) { TournamentFilterView(federalDataViewModel: federalDataViewModel) .environment(navigation) @@ -474,6 +523,11 @@ struct ActivityView: View { navigation.agendaDestination = .tenup } SupportButtonView(contentIsUnavailable: true) + + FooterButtonView("Vous n'êtes pas un juge-arbitre ou un organisateur de tournoi ? En savoir plus") { + presentOnboarding = true + } + .tint(.logoBackground) } } @@ -485,6 +539,7 @@ struct ActivityView: View { } } + @ViewBuilder private func _tenupEmptyView() -> some View { if dataStore.user.hasTenupClubs() == false { ContentUnavailableView { @@ -496,6 +551,10 @@ struct ActivityView: View { presentClubSearchView = true } .padding() + FooterButtonView("Cette app est dédié aux juge-arbitres et organisateurs de tournoi. Vous êtes un joueur à la recherche d'un tournoi homologué ? Utilisez notre outil de recherche") { + navigation.agendaDestination = .around + } + .tint(.logoBackground) } } else { ContentUnavailableView { @@ -518,13 +577,16 @@ struct ActivityView: View { ContentUnavailableView { Label("Recherche de tournoi", systemImage: "magnifyingglass") } description: { - Text("Chercher les tournois autour de vous pour mieux décider les tournois à proposer dans votre club. Padel Club vous facilite même l'inscription !") + Text("Chercher les tournois homologués autour de vous. Padel Club vous facilite même l'inscription !") } actions: { - RowButtonView("Lancer la recherche") { + RowButtonView("Chercher un tournoi") { displaySearchView = true } .padding() } + .onAppear { + displaySearchView = true + } } else { if federalDataViewModel.lastError == nil { ContentUnavailableView { diff --git a/PadelClub/Views/Navigation/Agenda/TournamentLookUpView.swift b/PadelClub/Views/Navigation/Agenda/TournamentLookUpView.swift index d6417e7..102f900 100644 --- a/PadelClub/Views/Navigation/Agenda/TournamentLookUpView.swift +++ b/PadelClub/Views/Navigation/Agenda/TournamentLookUpView.swift @@ -30,7 +30,19 @@ struct TournamentLookUpView: View { @State private var confirmSearch: Bool = false @State private var locationRequested = false @State private var apiError: StoreError? + @State private var quickOption: QuickDateOption? = nil + enum QuickDateOption: String, Identifiable, Hashable { + case thisMonth + case thisWeek + case nextWeek + case nextMonth + case twoWeeks + case nextThreeMonth + + var id: String { self.rawValue } + } + var tournaments: [FederalTournament] { federalDataViewModel.searchedFederalTournaments } @@ -140,15 +152,21 @@ struct TournamentLookUpView: View { } .toolbarTitleDisplayMode(.large) .toolbar { + ToolbarItem(placement: .topBarLeading) { + Button("Annuler", systemImage: "xmark", role: .cancel) { + dismiss() + } + } ToolbarItem(placement: .bottomBar) { if revealSearchParameters { - FooterButtonView("Lancer la recherche") { + Button("Lancer la recherche") { if dataStore.appSettings.city.isEmpty { confirmSearch = true } else { runSearch() } } + .buttonStyle(.borderedProminent) .disabled(searching) } else if searching { HStack(spacing: 20) { @@ -197,7 +215,7 @@ struct TournamentLookUpView: View { Text("Ré-initialiser la recherche") } } label: { - Label("Options", systemImage: "ellipsis.circle") + LabelOptions() } } } @@ -253,6 +271,7 @@ struct TournamentLookUpView: View { federalDataViewModel.searchedFederalTournaments = [] searching = true requestedToGetAllPages = false + federalDataViewModel.weekdays = dataStore.appSettings.weekdays federalDataViewModel.searchAttemptCount += 1 federalDataViewModel.dayPeriod = dataStore.appSettings.dayPeriod federalDataViewModel.dayDuration = dataStore.appSettings.dayDuration @@ -340,6 +359,42 @@ struct TournamentLookUpView: View { var searchParametersView: some View { @Bindable var appSettings = dataStore.appSettings Section { + Picker(selection: $quickOption) { + Text("Libre").tag(nil as QuickDateOption?) + Text("Cette semaine").tag(QuickDateOption.thisWeek as QuickDateOption?) + Text("2 prochaines semaines").tag(QuickDateOption.twoWeeks as QuickDateOption?) + Text("La semaine prochaine").tag(QuickDateOption.nextWeek as QuickDateOption?) + Text("Ce mois-ci").tag(QuickDateOption.thisMonth as QuickDateOption?) + Text("2 prochains mois").tag(QuickDateOption.nextMonth as QuickDateOption?) + Text("3 prochains mois").tag(QuickDateOption.nextThreeMonth as QuickDateOption?) + } label: { + Text("Choix de dates") + } + .pickerStyle(.menu) + .onChange(of: quickOption) { oldValue, newValue in + switch newValue { + case nil: + break + case .twoWeeks: + appSettings.startDate = Date().startOfDay + appSettings.endDate = Date().endOfWeek.addingTimeInterval(14 * 24 * 60 * 60) + case .nextWeek: + appSettings.startDate = Date().endOfWeek.nextDay.startOfDay + appSettings.endDate = Date().endOfWeek.addingTimeInterval(7 * 24 * 60 * 60) + case .thisMonth: + appSettings.startDate = Date().startOfDay + appSettings.endDate = Date().endOfMonth.endOfDay() + case .thisWeek: + appSettings.startDate = Date().startOfDay + appSettings.endDate = Date().endOfWeek + case .nextMonth: + appSettings.startDate = Date().startOfDay + appSettings.endDate = Date().endOfMonth.nextDay.endOfMonth + case .nextThreeMonth: + appSettings.startDate = Date().startOfDay + appSettings.endDate = Date().endOfMonth.nextDay.endOfMonth.nextDay.endOfMonth + } + } DatePicker("Début", selection: $appSettings.startDate, displayedComponents: .date) DatePicker("Fin", selection: $appSettings.endDate, displayedComponents: .date) Picker(selection: $appSettings.dayDuration) { @@ -350,7 +405,9 @@ struct TournamentLookUpView: View { } label: { Text("Durée souhaitée (en jours)") } - + + WeekdayselectionView(weekdays: $appSettings.weekdays) + Picker(selection: $appSettings.dayPeriod) { ForEach(DayPeriod.allCases) { Text($0.localizedDayPeriodLabel().capitalized).tag($0) @@ -398,6 +455,7 @@ struct TournamentLookUpView: View { } Picker(selection: $appSettings.distance) { + Text(distanceLimit(distance:15).formatted()).tag(15.0) Text(distanceLimit(distance:30).formatted()).tag(30.0) Text(distanceLimit(distance:50).formatted()).tag(50.0) Text(distanceLimit(distance:60).formatted()).tag(60.0) diff --git a/PadelClub/Views/Navigation/Agenda/WeekdaySelectionView.swift b/PadelClub/Views/Navigation/Agenda/WeekdaySelectionView.swift new file mode 100644 index 0000000..6b4fddc --- /dev/null +++ b/PadelClub/Views/Navigation/Agenda/WeekdaySelectionView.swift @@ -0,0 +1,36 @@ +// +// WeekdayselectionView.swift +// PadelClub +// +// Created by Razmig Sarkissian on 24/09/2025. +// + +import SwiftUI +import PadelClubData +import LeStorage + +struct WeekdayselectionView: View { + @Binding var weekdays: Set + + var body: some View { + NavigationLink { + List((1...7), selection: $weekdays) { type in + Text(Date.weekdays[type - 1]).tag(type as Int) + } + .navigationTitle("Jour de la semaine") + .environment(\.editMode, Binding.constant(EditMode.active)) + } label: { + HStack { + Text("Jour de la semaine") + Spacer() + if weekdays.isEmpty || weekdays.count == 7 { + Text("N'importe") + .foregroundStyle(.secondary) + } else { + Text(weekdays.sorted().map({ Date.weekdays[$0 - 1] }).joined(separator: ", ")) + .foregroundStyle(.secondary) + } + } + } + } +} diff --git a/PadelClub/Views/Navigation/MainView.swift b/PadelClub/Views/Navigation/MainView.swift index 8d282fe..209a81f 100644 --- a/PadelClub/Views/Navigation/MainView.swift +++ b/PadelClub/Views/Navigation/MainView.swift @@ -19,7 +19,11 @@ struct MainView: View { @Environment(ImportObserver.self) private var importObserver: ImportObserver @State private var mainViewId: UUID = UUID() + @State private var presentOnboarding: Bool = false + @State private var canPresentOnboarding: Bool = false + @AppStorage("didSeeOnboarding") private var didSeeOnboarding: Bool = false + var lastDataSource: String? { dataStore.appSettings.lastDataSource } @@ -90,6 +94,17 @@ struct MainView: View { // PadelClubView() // .tabItem(for: .padelClub) } + .onAppear { + if canPresentOnboarding || StoreCenter.main.userId != nil { + if didSeeOnboarding == false { + presentOnboarding = true + } + } + } + .sheet(isPresented: $presentOnboarding, content: { + OnboardingView() + .environmentObject(dataStore) + }) .id(mainViewId) .onChange(of: dataStore.user.id) { print("dataStore.user.id = ", dataStore.user.id) @@ -98,6 +113,8 @@ struct MainView: View { navigation.path.removeLast(navigation.path.count) mainViewId = UUID() } + + canPresentOnboarding = true } .environmentObject(dataStore) .task { diff --git a/PadelClub/Views/Navigation/OnboardingView.swift b/PadelClub/Views/Navigation/OnboardingView.swift new file mode 100644 index 0000000..b167b9c --- /dev/null +++ b/PadelClub/Views/Navigation/OnboardingView.swift @@ -0,0 +1,235 @@ +import SwiftUI + +struct OnboardingView: View { + @Environment(NavigationViewModel.self) private var navigation: NavigationViewModel + @State private var selection = 0 + @Environment(\.openURL) var openURL + @Environment(\.dismiss) private var dismiss + @AppStorage("didSeeOnboarding") private var didSeeOnboarding: Bool = false + + var steps: [OnboardingStep] { + [ + // Écran 1 – Bienvenue + .single( + title: "Bienvenue sur Padel Club", + description: "L’outil idéal des juges-arbitres et organisateurs pour gérer leurs tournois de A à Z.", + image: .padelClubLogoFondclairTransparent, + imageSystem: nil, + buttonTitle: "Suivant", + action: { selection += 1 } + ), + + // Écran 2 – Juges arbitres + .single( + title: "Pour les Juges-Arbitres", + description: "Planification, convocations, tirages, résultats… Tout ce qu’il faut pour organiser un tournoi de padel.", + image: nil, + imageSystem: "calendar.badge.clock", + buttonTitle: "Suivant", + action: { selection += 1 } + ), + + // Écran 3 – Joueurs (Multi boutons) + .multi( + title: "Vous êtes joueur ?", + description: "Cette app a été pensée faite pour les organisateurs.\nPour suivre vos tournois et convocations, rendez-vous sur https://padelclub.app", + image: nil, + imageSystem: "person.fill.questionmark", + tools: [ + ("Aller sur le site joueur", { + if let url = URL(string: "https://padelclub.app") { + openURL(url) + } + }) + ], + finalButtonTitle: "Continuer", + finalAction: { + selection += 1 + } + ), + + // Écran 4 – Outils utiles aux joueurs + .multi( + title: "Quelques outils utiles", + description: "Même si pensée pour les organisateurs, vous trouverez aussi quelques fonctions pratiques en tant que joueur.", + image: nil, + imageSystem: "wrench.and.screwdriver", + tools: [ + ("Chercher un tournoi Ten'Up", { + dismiss() + navigation.agendaDestination = .around + }), + ("Calculateur de points", { + dismiss() + navigation.selectedTab = .toolbox + }), + ("Consulter les règles du jeu", { + dismiss() + navigation.selectedTab = .toolbox + }), + ("Créer vos animations amicales", { + dismiss() + navigation.agendaDestination = .activity + }) + ], + finalButtonTitle: "J'ai compris", + finalAction: { + UserDefaults.standard.set(true, forKey: "didSeeOnboarding") + dismiss() + } + ) + ] + } + + var body: some View { + NavigationStack { + TabView(selection: $selection) { + ForEach(Array(steps.enumerated()), id: \.offset) { index, step in + switch step { + case let .single(title, description, image, imageSystem, buttonTitle, action): + OnboardingPage( + title: title, + description: description, + image: image, + imageSystem: imageSystem, + buttonTitle: buttonTitle, + action: action + ) + .tag(index) + + case let .multi(title, description, image, imageSystem, tools, finalButtonTitle, finalAction): + OnboardingMultiButtonPage( + title: title, + description: description, + image: image, + imageSystem: imageSystem, + tools: tools, + finalButtonTitle: finalButtonTitle, + finalAction: finalAction + ) + .tag(index) + } + } + } + .tabViewStyle(PageTabViewStyle(indexDisplayMode: .always)) + .indexViewStyle(.page(backgroundDisplayMode: .always)) // <- ensures background + .tint(.black) // <- sets the indicator color + .toolbar { + ToolbarItem(placement: .topBarTrailing) { + Button { + didSeeOnboarding = true + dismiss() + } label: { + Text("Plus tard") + } + } + } + } + .tint(.master) + } +} + +// MARK: - Enum de configuration +enum OnboardingStep { + case single(title: String, description: String, image: ImageResource?, imageSystem: String?, buttonTitle: String, action: () -> Void) + case multi(title: String, description: String, image: ImageResource?, imageSystem: String?, tools: [(String, () -> Void)], finalButtonTitle: String?, finalAction: () -> Void) +} + +// MARK: - Vue de base commune +struct OnboardingBasePage: View { + var title: String + var description: String + var image: ImageResource? + var imageSystem: String? + @ViewBuilder var content: () -> Content + + var body: some View { + VStack(spacing: 20) { + Spacer() + + if let imageSystem { + Image(systemName: imageSystem) + .resizable() + .scaledToFit() + .frame(width: 100, height: 100) + } else if let image { + Image(image) + .resizable() + .scaledToFit() + .frame(width: 100, height: 100) + } + + Text(title) + .font(.title) + .fontWeight(.bold) + .multilineTextAlignment(.center) + + Text(description) + .font(.body) + .multilineTextAlignment(.center) + .padding(.horizontal, 30) + .lineLimit(nil) + .fixedSize(horizontal: false, vertical: true) + + Spacer() + + content() + + Spacer(minLength: 40) + } + } +} + +// MARK: - Page avec un bouton +struct OnboardingPage: View { + var title: String + var description: String + var image: ImageResource? + var imageSystem: String? + var buttonTitle: String + var action: () -> Void + + var body: some View { + OnboardingBasePage(title: title, description: description, image: image, imageSystem: imageSystem) { + RowButtonView(buttonTitle) { + action() + } + .padding() + } + } +} + +// MARK: - Page avec plusieurs boutons +struct OnboardingMultiButtonPage: View { + var title: String + var description: String + var image: ImageResource? + var imageSystem: String? + var tools: [(String, () -> Void)] + var finalButtonTitle: String? + var finalAction: () -> Void + + var body: some View { + OnboardingBasePage(title: title, description: description, image: image, imageSystem: imageSystem) { + VStack(spacing: 12) { + ForEach(Array(tools.enumerated()), id: \.offset) { _, tool in + FooterButtonView(tool.0) { + tool.1() + } + .tint(.master) + } + } + + if let finalButtonTitle = finalButtonTitle { + RowButtonView(finalButtonTitle) { + finalAction() + } + .padding() + } + } + } +} + +#Preview { + OnboardingView() +} diff --git a/PadelClub/Views/Score/EditScoreView.swift b/PadelClub/Views/Score/EditScoreView.swift index 9a355aa..49d6be6 100644 --- a/PadelClub/Views/Score/EditScoreView.swift +++ b/PadelClub/Views/Score/EditScoreView.swift @@ -139,13 +139,15 @@ struct EditScoreView: View { Text(matchDescriptor.teamLabelTwo) } - Divider() - - Button { - self.matchDescriptor.match?.removeWalkOut() - save() - } label: { - Text("Annuler un forfait") + if self.matchDescriptor.match?.hasWalkoutTeam() == true { + Divider() + + Button { + self.matchDescriptor.match?.removeWalkOut() + save() + } label: { + Text("Annuler un forfait") + } } } label: { Text("Forfait d'une équipe ?") @@ -174,6 +176,13 @@ struct EditScoreView: View { } if matchDescriptor.hasEnded { + if self.matchDescriptor.match?.hasWalkoutTeam() == true { + RowButtonView("Annuler le forfait", role: .destructive) { + self.matchDescriptor.match?.removeWalkOut() + save() + } + } + Section { HStack { Spacer() diff --git a/PadelClub/Views/Score/FollowUpMatchView.swift b/PadelClub/Views/Score/FollowUpMatchView.swift index 2b65381..93bb494 100644 --- a/PadelClub/Views/Score/FollowUpMatchView.swift +++ b/PadelClub/Views/Score/FollowUpMatchView.swift @@ -88,7 +88,7 @@ struct FollowUpMatchView: View { let allMatches = currentTournament?.allMatches() ?? [] self.matchesLeft = Tournament.matchesLeft(allMatches) let runningMatches = Tournament.runningMatches(allMatches) - let readyMatches = Tournament.readyMatches(allMatches) + let readyMatches = Tournament.readyMatches(allMatches, runningMatches: runningMatches) self.readyMatches = Tournament.availableToStart(readyMatches, in: runningMatches, checkCanPlay: false) self.isFree = currentTournament?.isFree() ?? true } @@ -100,7 +100,7 @@ struct FollowUpMatchView: View { self.autoDismiss = autoDismiss self.matchesLeft = Tournament.matchesLeft(allMatches) let runningMatches = Tournament.runningMatches(allMatches) - let readyMatches = Tournament.readyMatches(allMatches) + let readyMatches = Tournament.readyMatches(allMatches, runningMatches: runningMatches) self.readyMatches = Tournament.availableToStart(readyMatches, in: runningMatches, checkCanPlay: false) self.isFree = false } @@ -156,7 +156,7 @@ struct FollowUpMatchView: View { case .index: return matches case .restingTime: - return matches.sorted(by: \.restingTimeForSorting) + return readyMatches.sorted(by: \.restingTimeForSorting) case .court: return matchesLeft.filter({ $0.courtIndex == selectedCourt }) case .winner: diff --git a/PadelClub/Views/Shared/SelectablePlayerListView.swift b/PadelClub/Views/Shared/SelectablePlayerListView.swift index 61228b8..97a9b0f 100644 --- a/PadelClub/Views/Shared/SelectablePlayerListView.swift +++ b/PadelClub/Views/Shared/SelectablePlayerListView.swift @@ -96,16 +96,27 @@ struct SelectablePlayerListView: View { var body: some View { VStack(spacing: 0) { if importObserver.isImportingFile() == false { - if searchViewModel.filterSelectionEnabled == false { - VStack { - HStack { - Picker(selection: $searchViewModel.filterOption) { - ForEach(PlayerFilterOption.allCases, id: \.self) { scope in - Text(scope.icon().capitalized) - } - } label: { + VStack { + HStack { + Picker(selection: $searchViewModel.filterOption) { + ForEach(PlayerFilterOption.allCases, id: \.self) { scope in + Text(scope.icon().capitalized) + } + } label: { + } + .pickerStyle(.segmented) + + Picker(selection: $searchViewModel.dataSet) { + ForEach(DataSet.allCases) { dataSet in + Text(searchViewModel.label(forDataSet: dataSet)).tag(dataSet) } - .pickerStyle(.segmented) + } label: { + + } + } + + if searchViewModel.isPresented == false { + HStack { Menu { if let lastDataSource = dataStore.appSettings.localizedLastDataSource() { Section { @@ -132,7 +143,7 @@ struct SelectablePlayerListView: View { } Divider() - Section { + Menu { Picker(selection: $searchViewModel.selectedAgeCategory) { ForEach(FederalTournamentAge.allCases) { ageCategory in Text(ageCategory.localizedFederalAgeLabel(.title)).tag(ageCategory) @@ -141,11 +152,11 @@ struct SelectablePlayerListView: View { Text("Catégorie d'âge") } - } header: { + } label: { Text("Catégorie d'âge") } Divider() - + Section { Toggle(isOn: .init(get: { return searchViewModel.hideAssimilation == false @@ -165,23 +176,36 @@ struct SelectablePlayerListView: View { Text("Assimilés") } } label: { - VStack(alignment: .trailing) { - Label(searchViewModel.sortOption.localizedLabel(), systemImage: searchViewModel.ascending ? "chevron.up" : "chevron.down") - if searchViewModel.selectedAgeCategory != .unlisted { - Text(searchViewModel.selectedAgeCategory.localizedFederalAgeLabel()).font(.caption) - } + Text("tri par " + searchViewModel.sortTitle().lowercased()) + .underline() + .font(.caption) + // Label("Filtre", systemImage: "line.3.horizontal.decrease") + // .labelsHidden() + } + + if searchViewModel.selectedPlayers.count > 0 { + Divider() + + Button { + searchViewModel.filterSelectionEnabled.toggle() + } label: { + Text("\(searchViewModel.filterSelectionEnabled ? "masquer" : "voir") la sélection") + .underline() + .font(.caption) } } } + .fixedSize() } - .padding(.bottom) - .padding(.horizontal) - .background(Material.thick) - Divider() } + .padding(.bottom) + .padding(.horizontal) + .background(Material.thick) + Divider() + MySearchView(searchViewModel: searchViewModel, contentUnavailableAction: contentUnavailableAction) .environment(\.editMode, searchViewModel.allowMultipleSelection ? .constant(.active) : .constant(.inactive)) - .searchable(text: $searchViewModel.debouncableText, tokens: $searchViewModel.tokens, suggestedTokens: $searchViewModel.suggestedTokens, isPresented: $searchViewModel.isPresented, placement: .navigationBarDrawer(displayMode: .always), prompt: searchViewModel.prompt(forDataSet: searchViewModel.dataSet), token: { token in + .searchable(text: $searchViewModel.debouncableText, tokens: $searchViewModel.tokens, suggestedTokens: $searchViewModel.suggestedTokens, isPresented: $searchViewModel.isPresented, placement: .toolbar, prompt: searchViewModel.prompt(forDataSet: searchViewModel.dataSet), token: { token in Text(token.shortLocalizedLabel) }) .keyboardType(.alphabet) @@ -212,11 +236,10 @@ struct SelectablePlayerListView: View { } .scrollDismissesKeyboard(.immediately) .navigationBarBackButtonHidden(searchViewModel.allowMultipleSelection) - .toolbarBackground(searchViewModel.allowMultipleSelection ? .visible : .hidden, for: .bottomBar) + .toolbarBackground(.hidden, for: .bottomBar) .toolbarBackground(.visible, for: .navigationBar) // .toolbarRole(searchViewModel.allowMultipleSelection ? .navigationStack : .editor) .interactiveDismissDisabled(searchViewModel.selectedPlayers.isEmpty == false) - .navigationTitle(searchViewModel.label(forDataSet: searchViewModel.dataSet)) .navigationBarTitleDisplayMode(.inline) } else { List { @@ -284,7 +307,7 @@ struct SelectablePlayerListView: View { searchViewModel.selectedPlayers.removeAll() dismiss() } label: { - Text("Annuler") + Label("Annuler", systemImage: "xmark") } } @@ -297,28 +320,16 @@ struct SelectablePlayerListView: View { } .disabled(searchViewModel.selectedPlayers.isEmpty) } - ToolbarItem(placement: .status) { - let count = searchViewModel.selectedPlayers.count - VStack(spacing: 0) { - Text(count.formatted() + " joueur" + count.pluralSuffix + " séléctionné" + count.pluralSuffix).font(.footnote).foregroundStyle(.secondary) - FooterButtonView("\(searchViewModel.filterSelectionEnabled ? "masquer" : "voir") la liste") { - searchViewModel.filterSelectionEnabled.toggle() - } - } - } + } + + if #available(iOS 26.0, *) { + DefaultToolbarItem(kind: .search, placement: .bottomBar) } } + .navigationTitle("Recherche") + .navigationBarTitleDisplayMode(.large) // .modifierWithCondition(searchViewModel.user != nil) { thisView in // thisView - .toolbarTitleMenu { - Picker(selection: $searchViewModel.dataSet) { - ForEach(DataSet.allCases) { dataSet in - Text(searchViewModel.label(forDataSet: dataSet)).tag(dataSet) - } - } label: { - - } - } // } // .bottomBarAlternative(hide: searchViewModel.selectedPlayers.isEmpty) { // ZStack { diff --git a/PadelClub/Views/Shared/TournamentFilterView.swift b/PadelClub/Views/Shared/TournamentFilterView.swift index 3367e0f..cfdb29e 100644 --- a/PadelClub/Views/Shared/TournamentFilterView.swift +++ b/PadelClub/Views/Shared/TournamentFilterView.swift @@ -47,6 +47,8 @@ struct TournamentFilterView: View { } label: { Text("En semaine ou week-end") } + + WeekdayselectionView(weekdays: $federalDataViewModel.weekdays) } Section { diff --git a/PadelClub/Views/Team/TeamRestingView.swift b/PadelClub/Views/Team/TeamRestingView.swift index 765052b..9471e2a 100644 --- a/PadelClub/Views/Team/TeamRestingView.swift +++ b/PadelClub/Views/Team/TeamRestingView.swift @@ -90,7 +90,7 @@ struct TeamRestingView: View { let allMatches = tournament.allMatches() let matchesLeft = Tournament.matchesLeft(allMatches) let runningMatches = Tournament.runningMatches(allMatches) - let readyMatches = Tournament.readyMatches(allMatches) + let readyMatches = Tournament.readyMatches(allMatches, runningMatches: runningMatches) self.readyMatches = Tournament.availableToStart(readyMatches, in: runningMatches, checkCanPlay: false) self.matchesLeft = matchesLeft self.teams = tournament.selectedSortedTeams().filter({ $0.restingTime() != nil }).sorted(by: \.restingTimeForSorting) diff --git a/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift b/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift index 89b091d..04a5981 100644 --- a/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift +++ b/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift @@ -336,7 +336,7 @@ struct InscriptionManagerView: View { .tint(.master) } .toolbar { - ToolbarItemGroup(placement: .navigationBarTrailing) { + ToolbarItem(placement: .navigationBarTrailing) { Menu { Toggle(isOn: $compactMode) { Text("Vue compact") @@ -364,6 +364,14 @@ struct InscriptionManagerView: View { LabelFilter() .symbolVariant(filterMode == .all ? .none : .fill) } + } + + + if #available(iOS 26.0, *) { + ToolbarSpacer(placement: .navigationBarTrailing) + } + + ToolbarItem(placement: .navigationBarTrailing) { Menu { if tournament.inscriptionClosed() == false { Menu { diff --git a/PadelClub/Views/Tournament/Screen/PrintSettingsView.swift b/PadelClub/Views/Tournament/Screen/PrintSettingsView.swift index 2cc6c92..b5fddb2 100644 --- a/PadelClub/Views/Tournament/Screen/PrintSettingsView.swift +++ b/PadelClub/Views/Tournament/Screen/PrintSettingsView.swift @@ -201,7 +201,7 @@ struct PrintSettingsView: View { Text("Partager le code source HTML") } } label: { - Label("Options", systemImage: "ellipsis.circle") + LabelOptions() } } } diff --git a/PadelClub/Views/Tournament/Screen/TournamentRankView.swift b/PadelClub/Views/Tournament/Screen/TournamentRankView.swift index cfd8944..bb72722 100644 --- a/PadelClub/Views/Tournament/Screen/TournamentRankView.swift +++ b/PadelClub/Views/Tournament/Screen/TournamentRankView.swift @@ -42,7 +42,7 @@ struct TournamentRankView: View { Section { let all = tournament.allMatches() let runningMatches = Tournament.runningMatches(all) - let matchesLeft = Tournament.readyMatches(all) + let matchesLeft = Tournament.readyMatches(all, runningMatches: runningMatches) MatchListView(section: "Matchs restant", matches: matchesLeft, hideWhenEmpty: false, isExpanded: false) MatchListView(section: "Matchs en cours", matches: runningMatches, hideWhenEmpty: false, isExpanded: false) diff --git a/PadelClub/Views/Tournament/TournamentRunningView.swift b/PadelClub/Views/Tournament/TournamentRunningView.swift index fd6e575..b991db7 100644 --- a/PadelClub/Views/Tournament/TournamentRunningView.swift +++ b/PadelClub/Views/Tournament/TournamentRunningView.swift @@ -22,7 +22,7 @@ struct TournamentRunningView: View { let runningMatches = Tournament.runningMatches(allMatches) let matchesLeft = Tournament.matchesLeft(allMatches) - let readyMatches = Tournament.readyMatches(allMatches) + let readyMatches = Tournament.readyMatches(allMatches, runningMatches: runningMatches) let availableToStart = Tournament.availableToStart(allMatches, in: runningMatches, checkCanPlay: true) Section { diff --git a/PadelClub/Views/Tournament/TournamentView.swift b/PadelClub/Views/Tournament/TournamentView.swift index 457f9d2..49ab26f 100644 --- a/PadelClub/Views/Tournament/TournamentView.swift +++ b/PadelClub/Views/Tournament/TournamentView.swift @@ -238,10 +238,10 @@ struct TournamentView: View { } NavigationLink(value: Screen.event) { - Text("Réglages de l'événement") + Label("Événement", systemImage: "wrench.and.screwdriver") } NavigationLink(value: Screen.settings) { - LabelSettings() + Label("Tournoi", systemImage: "wrench.and.screwdriver") } NavigationLink(value: Screen.call) { @@ -290,10 +290,10 @@ struct TournamentView: View { } } - NavigationLink(value: Screen.broadcast) { - Label("Publication", systemImage: "airplayvideo") - } - +// NavigationLink(value: Screen.broadcast) { +// Label("Publication", systemImage: "airplayvideo") +// } +// NavigationLink(value: Screen.print) { Label("Imprimer", systemImage: "printer") } @@ -305,8 +305,7 @@ struct TournamentView: View { Divider() NavigationLink(value: Screen.stateSettings) { - Text("Gestion du tournoi") - Text("Annuler, supprimer ou terminer le tournoi") + Label("Tournoi", systemImage: "trash") } } label: { LabelOptions()