diff --git a/PadelClub.xcodeproj/project.pbxproj b/PadelClub.xcodeproj/project.pbxproj index 9a2c640..c4b6851 100644 --- a/PadelClub.xcodeproj/project.pbxproj +++ b/PadelClub.xcodeproj/project.pbxproj @@ -153,6 +153,7 @@ FF4AB6BD2B9256E10002987F /* SelectablePlayerListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF4AB6BC2B9256E10002987F /* SelectablePlayerListView.swift */; }; FF4AB6BF2B92577A0002987F /* ImportedPlayerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF4AB6BE2B92577A0002987F /* ImportedPlayerView.swift */; }; FF4C7F022BBBD7150031B6A3 /* TabItemModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF4C7F012BBBD7150031B6A3 /* TabItemModifier.swift */; }; + FF53FBB82BFB302B0051D4C3 /* ClubCourtSetupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF53FBB72BFB302B0051D4C3 /* ClubCourtSetupView.swift */; }; FF59FFB32B90EFAC0061EFF9 /* EventListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF59FFB22B90EFAC0061EFF9 /* EventListView.swift */; }; FF59FFB72B90EFBF0061EFF9 /* MainView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF59FFB62B90EFBF0061EFF9 /* MainView.swift */; }; FF59FFB92B90EFD70061EFF9 /* ToolboxView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF59FFB82B90EFD70061EFF9 /* ToolboxView.swift */; }; @@ -469,6 +470,7 @@ FF4AB6BC2B9256E10002987F /* SelectablePlayerListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectablePlayerListView.swift; sourceTree = ""; }; FF4AB6BE2B92577A0002987F /* ImportedPlayerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImportedPlayerView.swift; sourceTree = ""; }; FF4C7F012BBBD7150031B6A3 /* TabItemModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabItemModifier.swift; sourceTree = ""; }; + FF53FBB72BFB302B0051D4C3 /* ClubCourtSetupView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClubCourtSetupView.swift; sourceTree = ""; }; FF59FFB22B90EFAC0061EFF9 /* EventListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventListView.swift; sourceTree = ""; }; FF59FFB62B90EFBF0061EFF9 /* MainView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainView.swift; sourceTree = ""; }; FF59FFB82B90EFD70061EFF9 /* ToolboxView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToolboxView.swift; sourceTree = ""; }; @@ -927,6 +929,7 @@ FFC1E10B2BAC7FB0008D6F59 /* ClubImportView.swift */, FF5D0D882BB4935C005CB568 /* ClubRowView.swift */, FFC91B022BD85E2400B29808 /* CourtView.swift */, + FF53FBB62BFB301A0051D4C3 /* Shared */, ); path = Club; sourceTree = ""; @@ -1055,6 +1058,14 @@ path = ViewModel; sourceTree = ""; }; + FF53FBB62BFB301A0051D4C3 /* Shared */ = { + isa = PBXGroup; + children = ( + FF53FBB72BFB302B0051D4C3 /* ClubCourtSetupView.swift */, + ); + path = Shared; + sourceTree = ""; + }; FF5D30542BD95AF600F2B93D /* Ongoing */ = { isa = PBXGroup; children = ( @@ -1533,6 +1544,7 @@ FF8F263F2BAD7D5C00650388 /* Event.swift in Sources */, FF5D30532BD94E2E00F2B93D /* PlayerHolder.swift in Sources */, FF11628C2BD05267000C4809 /* LoserRoundStepScheduleEditorView.swift in Sources */, + FF53FBB82BFB302B0051D4C3 /* ClubCourtSetupView.swift in Sources */, FF089EBF2BB0B14600F0AEC7 /* FileImportView.swift in Sources */, C4A47D9F2B7D0BCE00ADC637 /* StepperView.swift in Sources */, FFC83D4F2BB807D100750834 /* RoundsView.swift in Sources */, diff --git a/PadelClub/Views/Calling/CallMessageCustomizationView.swift b/PadelClub/Views/Calling/CallMessageCustomizationView.swift index 9e3ed71..5db94ba 100644 --- a/PadelClub/Views/Calling/CallMessageCustomizationView.swift +++ b/PadelClub/Views/Calling/CallMessageCustomizationView.swift @@ -192,7 +192,8 @@ struct CallMessageCustomizationView: View { if let eventClub = tournament.eventObject()?.clubObject() { let hasBeenCreated: Bool = eventClub.hasBeenCreated(by: dataStore.user.id) Section { - TextField("Nom du club", text: $customClubName) + TextField("Nom du club", text: $customClubName, axis: .vertical) + .lineLimit(2) .autocorrectionDisabled() .focused($focusedField, equals: .clubName) .onSubmit { @@ -204,8 +205,6 @@ struct CallMessageCustomizationView: View { } } .disabled(hasBeenCreated == false) - } header: { - Text("Nom du club") } footer: { if hasBeenCreated == false { Text("Édition impossible, vous n'êtes pas le créateur de ce club.").foregroundStyle(.logoRed) diff --git a/PadelClub/Views/Cashier/Event/EventSettingsView.swift b/PadelClub/Views/Cashier/Event/EventSettingsView.swift index 2b0492f..6b9d1a2 100644 --- a/PadelClub/Views/Cashier/Event/EventSettingsView.swift +++ b/PadelClub/Views/Cashier/Event/EventSettingsView.swift @@ -12,7 +12,6 @@ struct EventSettingsView: View { @EnvironmentObject var dataStore: DataStore @Bindable var event: Event @State private var eventName: String = "" - @FocusState private var textFieldIsFocus: Bool init(event: Event) { self.event = event @@ -22,21 +21,20 @@ struct EventSettingsView: View { var body: some View { Form { Section { - ZStack { - Text(eventName).hidden() - TextEditor(text: $eventName) - .focused($textFieldIsFocus) - .autocorrectionDisabled() - .keyboardType(.alphabet) - .onSubmit { - if eventName.trimmed.isEmpty == false { - event.name = eventName.trimmed - } else { - event.name = nil - } - _save() + TextField("Nom de l'événement", text: $eventName, axis: .vertical) + .lineLimit(2) + .autocorrectionDisabled() + .keyboardType(.alphabet) + .multilineTextAlignment(.leading) + .frame(maxWidth: .infinity) + .onSubmit { + if eventName.trimmed.isEmpty == false { + event.name = eventName.trimmed + } else { + event.name = nil } - } + _save() + } } header: { Text("Nom de l'événement") } footer: { @@ -49,19 +47,6 @@ struct EventSettingsView: View { } } } - .toolbar { - ToolbarItemGroup(placement: .keyboard) { - if textFieldIsFocus { - Spacer() - Button { - textFieldIsFocus = false - } label: { - Text("Valider") - } - .buttonStyle(.bordered) - } - } - } } private func _save() { diff --git a/PadelClub/Views/Club/ClubDetailView.swift b/PadelClub/Views/Club/ClubDetailView.swift index a848205..85ddfbc 100644 --- a/PadelClub/Views/Club/ClubDetailView.swift +++ b/PadelClub/Views/Club/ClubDetailView.swift @@ -15,6 +15,7 @@ struct ClubDetailView: View { @State private var acronymMode: Club.AcronymMode = .automatic @State private var city: String @State private var zipCode: String + @State private var selectedCourt: Court? @Bindable var club: Club var displayContext: DisplayContext var selection: ((Club) -> ())? = nil @@ -31,24 +32,23 @@ struct ClubDetailView: View { var body: some View { Form { Section { - ZStack { - Text(club.name).hidden() - TextEditor(text: $club.name) - .autocorrectionDisabled() - .keyboardType(.alphabet) - .frame(maxWidth: .infinity) - .focused($focusedField, equals: ._name) - .submitLabel( displayContext == .addition ? .next : .done) - .onSubmit { - if club.acronym.isEmpty { - club.acronym = club.name.canonicalVersion.replaceCharactersFromSet(characterSet: .whitespacesAndNewlines) - focusedField = ._city - } - if displayContext == .addition { - focusedField = ._acronym - } + TextField("Nom du club", text: $club.name, axis: .vertical) + .lineLimit(2) + .autocorrectionDisabled() + .keyboardType(.alphabet) + .frame(maxWidth: .infinity) + .focused($focusedField, equals: ._name) + .submitLabel( displayContext == .addition ? .next : .done) + .onSubmit { + if club.acronym.isEmpty { + club.acronym = club.name.canonicalVersion.replaceCharactersFromSet(characterSet: .whitespacesAndNewlines) + focusedField = ._city } - } + if displayContext == .addition { + focusedField = ._acronym + } + } + LabeledContent { if acronymMode == .automatic || displayContext == .lockedForEditing { Text(club.acronym) @@ -94,8 +94,6 @@ struct ClubDetailView: View { club.acronym = "" } } - } header: { - Text("Nom du club") } footer: { if displayContext == .lockedForEditing { Text("Édition impossible, vous n'êtes pas le créateur de ce club.").foregroundStyle(.logoRed) @@ -152,24 +150,7 @@ struct ClubDetailView: View { .disabled(displayContext == .lockedForEditing) } - - Section { - TournamentFieldsManagerView(localizedStringKey: "Terrains", count: $club.courtCount) - .disabled(displayContext == .lockedForEditing) - .onChange(of: club.courtCount) { - if displayContext != .addition { - do { - try dataStore.clubs.addOrUpdate(instance: club) - } catch { - Logger.error(error) - } - } - } - } footer: { - if displayContext == .lockedForEditing { - Text("Édition impossible, vous n'êtes pas le créateur de ce club.").foregroundStyle(.logoRed) - } - } + ClubCourtSetupView(club: club, displayContext: displayContext, selectedCourt: $selectedCourt) if let federalLink = club.federalLink() { Section { @@ -247,18 +228,8 @@ struct ClubDetailView: View { .toolbar(.visible, for: .navigationBar) .headerProminence(.increased) .toolbarBackground(.visible, for: .navigationBar) - .toolbar { - ToolbarItemGroup(placement: .keyboard) { - if focusedField == ._name { - Spacer() - Button { - focusedField = nil - } label: { - Text("Valider") - } - .buttonStyle(.bordered) - } - } + .navigationDestination(item: $selectedCourt) { court in + CourtView(court: court) } .onDisappear { if displayContext == .edition { diff --git a/PadelClub/Views/Club/Shared/ClubCourtSetupView.swift b/PadelClub/Views/Club/Shared/ClubCourtSetupView.swift new file mode 100644 index 0000000..76ba387 --- /dev/null +++ b/PadelClub/Views/Club/Shared/ClubCourtSetupView.swift @@ -0,0 +1,83 @@ +// +// ClubCourtSetupView.swift +// PadelClub +// +// Created by Razmig Sarkissian on 20/05/2024. +// + +import SwiftUI +import LeStorage + +struct ClubCourtSetupView: View { + @EnvironmentObject var dataStore: DataStore + @Bindable var club: Club + let displayContext: DisplayContext + @Binding var selectedCourt: Court? + + @ViewBuilder + var body: some View { + Section { + TournamentFieldsManagerView(localizedStringKey: "Terrains", count: $club.courtCount) + .disabled(displayContext == .lockedForEditing) + .onChange(of: club.courtCount) { + if displayContext != .addition { + do { + try dataStore.clubs.addOrUpdate(instance: club) + } catch { + Logger.error(error) + } + } + } + } footer: { + if displayContext == .lockedForEditing { + Text("Édition impossible, vous n'êtes pas le créateur de ce club.").foregroundStyle(.logoRed) + } + } + + Section { + ForEach((0.. some View { + let court = tournamentClub.customizedCourts.first(where: { $0.index == index }) + LabeledContent { + if displayContext == .edition { + FooterButtonView("personnaliser") { + if let court { + selectedCourt = court + } else { + let newCourt = Court(index: index, club: tournamentClub.id) + do { + try dataStore.courts.addOrUpdate(instance: newCourt) + } catch { + Logger.error(error) + } + selectedCourt = newCourt + } + } + } + } label: { + if let court { + Text(court.courtTitle()) + HStack { + if court.indoor { + Text("Couvert") + } + if court.exitAllowed { + Text("Sortie autorisée") + } + } + } else { + Text(_courtName(atIndex: index)) + } + } + } + + private func _courtName(atIndex index: Int) -> String { + Court.courtIndexedTitle(atIndex: index) + } +} diff --git a/PadelClub/Views/Event/EventCreationView.swift b/PadelClub/Views/Event/EventCreationView.swift index ec454e8..eca218f 100644 --- a/PadelClub/Views/Event/EventCreationView.swift +++ b/PadelClub/Views/Event/EventCreationView.swift @@ -20,7 +20,6 @@ struct EventCreationView: View { @State private var eventName: String = "" @State var tournaments: [Tournament] = [] @State var selectedClub: Club? - @FocusState private var textFieldIsFocus: Bool let multiTournamentsEventTip = MultiTournamentsEventTip() @@ -64,16 +63,12 @@ struct EventCreationView: View { } } - VStack(alignment: .leading) { - Text("Nom de l'événement").font(.footnote) - ZStack { - Text(eventName).hidden() - TextEditor(text: $eventName) - .focused($textFieldIsFocus) - .autocorrectionDisabled() - .keyboardType(.alphabet) - } - } + TextField("Nom de l'événement", text: $eventName, axis: .vertical) + .lineLimit(2) + .autocorrectionDisabled() + .keyboardType(.alphabet) + .multilineTextAlignment(.leading) + .frame(maxWidth: .infinity) LabeledContent { Text(tournaments.count.formatted()) @@ -143,19 +138,6 @@ struct EventCreationView: View { } .popoverTip(multiTournamentsEventTip) } - - ToolbarItemGroup(placement: .keyboard) { - if textFieldIsFocus { - Spacer() - Button { - textFieldIsFocus = false - } label: { - Text("Valider") - } - .buttonStyle(.bordered) - } - } - } .navigationTitle("Nouvel événement") .navigationBarTitleDisplayMode(.inline) diff --git a/PadelClub/Views/Planning/PlanningSettingsView.swift b/PadelClub/Views/Planning/PlanningSettingsView.swift index fde3f8c..db938d6 100644 --- a/PadelClub/Views/Planning/PlanningSettingsView.swift +++ b/PadelClub/Views/Planning/PlanningSettingsView.swift @@ -34,24 +34,18 @@ struct PlanningSettingsView: View { SubscriptionInfoView() Section { - DatePicker(tournament.startDate.formatted(.dateTime.weekday()), selection: $tournament.startDate) + DatePicker(selection: $tournament.startDate) { + Text(tournament.startDate.formatted(.dateTime.weekday(.wide)).capitalized) + } LabeledContent { StepperView(count: $tournament.dayDuration, minimum: 1) } label: { Text("Durée") Text("\(tournament.dayDuration) jour" + tournament.dayDuration.pluralSuffix) } - } header: { - Text("Démarrage et durée du tournoi") - } - - Section { + TournamentFieldsManagerView(localizedStringKey: "Terrains maximum", count: $tournament.courtCount) - if tournament.groupStages().isEmpty == false { - TournamentFieldsManagerView(localizedStringKey: "Nombre de poule en même temps", count: $groupStageChunkCount, max: tournament.groupStageCount) - } - if let event = tournament.eventObject() { NavigationLink { CourtAvailabilitySettingsView(event: event) @@ -61,13 +55,43 @@ struct PlanningSettingsView: View { } } } footer: { - FooterButtonView((showOptions ? "masquer" : "voir") + " les réglages avancées") { - showOptions.toggle() + if let club = tournament.club() { + if tournament.courtCount < club.courtCount { + let plural = tournament.courtCount.pluralSuffix + let verb = tournament.courtCount > 1 ? "seront" : "sera" + Text("En réduisant les terrains maximum, seul\(plural) le\(plural) \(tournament.courtCount) premier\(plural) terrain\(plural) \(verb) utilisé\(plural)") + Text(", par contre, si vous augmentez le nombre de terrains, vous pourrez plutôt préciser quel terrain n'est pas disponible.") + } else if tournament.courtCount > club.courtCount { + let isCreatedByUser = club.hasBeenCreated(by: dataStore.user.id) + Button { + do { + club.courtCount = tournament.courtCount + try dataStore.clubs.addOrUpdate(instance: club) + } catch { + Logger.error(error) + } + } label: { + if isCreatedByUser { + Text("Vous avez indiqué plus de terrains dans ce tournoi que dans le club.") + + Text("Mettre à jour le club ?").underline().foregroundStyle(.master) + } else { + Label("Vous avez indiqué plus de terrains dans ce tournoi que dans le club.", systemImage: "exclamationmark.triangle.fill").foregroundStyle(.logoRed) + } + } + .buttonStyle(.plain) + .disabled(isCreatedByUser == false) + } } } - if showOptions { - _optionsView() + NavigationLink { + List { + _optionsView() + } + .navigationTitle("Options avancées") + .navigationBarTitleDisplayMode(.inline) + .toolbarBackground(.visible, for: .navigationBar) + } label: { + Text("Options avancées") } Section { @@ -132,6 +156,13 @@ struct PlanningSettingsView: View { @ViewBuilder private func _optionsView() -> some View { + if tournament.groupStages().isEmpty == false { + Section { + TournamentFieldsManagerView(localizedStringKey: "Poule en parallèle", count: $groupStageChunkCount, max: tournament.groupStageCount) + } footer: { + Text("Vous pouvez indiquer le nombre de poule démarrant en même temps.") + } + } Section { Toggle(isOn: $matchScheduler.randomizeCourts) { diff --git a/PadelClub/Views/Tournament/Screen/Components/TournamentClubSettingsView.swift b/PadelClub/Views/Tournament/Screen/Components/TournamentClubSettingsView.swift index 3820956..29e7072 100644 --- a/PadelClub/Views/Tournament/Screen/Components/TournamentClubSettingsView.swift +++ b/PadelClub/Views/Tournament/Screen/Components/TournamentClubSettingsView.swift @@ -11,109 +11,63 @@ import LeStorage struct TournamentClubSettingsView: View { @Environment(Tournament.self) private var tournament: Tournament @EnvironmentObject var dataStore: DataStore - @State var selectedCourt: Court? - + @State private var selectedCourt: Court? + @State private var showClubDetail: Club? + var body: some View { @Bindable var tournament = tournament List { let event = tournament.eventObject() let selectedClub = event?.clubObject() Section { - if let selectedClub { - NavigationLink { - ClubDetailView(club: selectedClub, displayContext: selectedClub.hasBeenCreated(by: dataStore.user.id) ? .edition : .lockedForEditing) - } label: { - ClubRowView(club: selectedClub) - } - } else { - NavigationLink { - ClubsView() { club in - if let event { - event.club = club.id - do { - try dataStore.events.addOrUpdate(instance: event) - } catch { - Logger.error(error) - } + NavigationLink { + ClubsView() { club in + if let event { + event.club = club.id + do { + try dataStore.events.addOrUpdate(instance: event) + } catch { + Logger.error(error) } } - } label: { + } + } label: { + if let selectedClub { + ClubRowView(club: selectedClub) + } else { Text("Choisir un club") } } } header: { Text("Lieu du tournoi") } footer: { - if let event, selectedClub != nil { - HStack { - Spacer() - Button("modifier", role: .destructive) { - event.club = nil - try? dataStore.events.addOrUpdate(instance: event) - } + HStack { + Spacer() + FooterButtonView("détails du club") { + showClubDetail = selectedClub } } } - - Section { - TournamentFieldsManagerView(localizedStringKey: "Terrains maximum", count: $tournament.courtCount) - } if let selectedClub { - Section { - ForEach((0.. some View { - if let court = tournamentClub.customizedCourts.first(where: { $0.index == index }) { - LabeledContent { - FooterButtonView("personnaliser") { - selectedCourt = court - } - } label: { - Text(court.courtTitle()) - HStack { - if court.indoor { - Text("Couvert") - } - if court.exitAllowed { - Text("Sortie autorisée") - } - } - } - } else { - LabeledContent { - FooterButtonView("personnaliser") { - let court = Court(index: index, club: tournamentClub.id) - try? dataStore.courts.addOrUpdate(instance: court) - selectedCourt = court - } - } label: { - Text(_courtName(atIndex: index)) - } - } - } - - private func _courtName(atIndex index: Int) -> String { - Court.courtIndexedTitle(atIndex: index) - } - } #Preview {