From 5496bf2e964823df0411d1237b594a456a52c696 Mon Sep 17 00:00:00 2001 From: Raz Date: Thu, 24 Oct 2024 12:39:19 +0200 Subject: [PATCH] improve unavailibity view --- PadelClub/Data/Tournament.swift | 2 +- .../CourtAvailabilitySettingsView.swift | 210 ++++++++++++------ PadelClub/Views/Round/RoundSettingsView.swift | 9 +- 3 files changed, 143 insertions(+), 78 deletions(-) diff --git a/PadelClub/Data/Tournament.swift b/PadelClub/Data/Tournament.swift index e1d6a4d..3dc56c1 100644 --- a/PadelClub/Data/Tournament.swift +++ b/PadelClub/Data/Tournament.swift @@ -2264,7 +2264,7 @@ defer { } func seedsCount() -> Int { - teamCount - groupStageSpots() + selectedSortedTeams().count - groupStageSpots() } func lastDrawnDate() -> Date? { diff --git a/PadelClub/Views/Planning/CourtAvailabilitySettingsView.swift b/PadelClub/Views/Planning/CourtAvailabilitySettingsView.swift index 9ade115..93d52fa 100644 --- a/PadelClub/Views/Planning/CourtAvailabilitySettingsView.swift +++ b/PadelClub/Views/Planning/CourtAvailabilitySettingsView.swift @@ -15,9 +15,6 @@ struct CourtAvailabilitySettingsView: View { let event: Event @State private var showingPopover: Bool = false - @State private var courtIndex: Int = 0 - @State private var startDate: Date = Date() - @State private var endDate: Date = Date() @State private var editingSlot: DateInterval? var courtsUnavailability: [Int: [DateInterval]] { @@ -45,10 +42,6 @@ struct CourtAvailabilitySettingsView: View { } Button("éditer") { editingSlot = dateInterval - courtIndex = dateInterval.courtIndex - startDate = dateInterval.startDate - endDate = dateInterval.endDate - showingPopover = true } Button("effacer", role: .destructive) { do { @@ -110,8 +103,6 @@ struct CourtAvailabilitySettingsView: View { Text("Vous pouvez précisez l'indisponibilité d'une ou plusieurs terrains, que ce soit pour une journée entière ou un créneau précis.") } actions: { RowButtonView("Ajouter une indisponibilité", systemImage: "plus.circle.fill") { - startDate = tournament.startDate - endDate = tournament.startDate.addingTimeInterval(5400) showingPopover = true } } @@ -120,8 +111,6 @@ struct CourtAvailabilitySettingsView: View { .toolbar { ToolbarItem(placement: .topBarTrailing) { BarButtonView("Ajouter une indisponibilité", icon: "plus.circle.fill") { - startDate = tournament.startDate - endDate = tournament.startDate.addingTimeInterval(5400) showingPopover = true } } @@ -130,66 +119,10 @@ struct CourtAvailabilitySettingsView: View { .toolbarBackground(.visible, for: .navigationBar) .navigationTitle("Créneau indisponible") .sheet(isPresented: $showingPopover) { - NavigationStack { - Form { - Section { - CourtPicker(title: "Terrain", selection: $courtIndex, maxCourt: tournament.courtCount) - } - - Section { - DatePicker("Début", selection: $startDate) - .onChange(of: startDate) { - if endDate < startDate { - endDate = startDate.addingTimeInterval(90*60) - } - } - DatePicker("Fin", selection: $endDate) - .onChange(of: endDate) { - if startDate > endDate { - startDate = endDate.addingTimeInterval(-90*60) - } - - } - } footer: { - FooterButtonView("jour entier") { - startDate = startDate.startOfDay - endDate = startDate.endOfDay() - } - } - } - .toolbar { - ButtonValidateView { - if editingSlot == nil { - let dateInterval = DateInterval(event: event.id, courtIndex: courtIndex, startDate: startDate, endDate: endDate) - do { - try dataStore.dateIntervals.addOrUpdate(instance: dateInterval) - } catch { - Logger.error(error) - } - } else { - editingSlot?.courtIndex = courtIndex - editingSlot?.endDate = endDate - editingSlot?.startDate = startDate - do { - try dataStore.dateIntervals.addOrUpdate(instance: editingSlot!) - } catch { - Logger.error(error) - } - } - showingPopover = false - } - } - .navigationBarTitleDisplayMode(.inline) - .toolbarBackground(.visible, for: .navigationBar) - .navigationTitle("Nouveau créneau") - .tint(.master) - } - .onAppear { - UIDatePicker.appearance().minuteInterval = 5 - } - .onDisappear { - UIDatePicker.appearance().minuteInterval = 1 - } + CourtAvailabilityEditorView(event: event) + } + .sheet(item: $editingSlot) { editingSlot in + CourtAvailabilityEditorView(editingSlot: editingSlot, event: event) } } } @@ -210,6 +143,135 @@ struct CourtPicker: View { } } -//#Preview { -// CourtAvailabilitySettingsView(event: Event.mock()) -//} +struct CourtAvailabilityEditorView: View { + @Environment(Tournament.self) var tournament: Tournament + @EnvironmentObject var dataStore: DataStore + @Environment(\.dismiss) private var dismiss + var editingSlot: DateInterval? + let event: Event + + @State private var courtIndex: Int + @State private var startDate: Date + @State private var endDate: Date + + init(editingSlot: DateInterval, event: Event) { + self.editingSlot = editingSlot + self.event = event + _courtIndex = .init(wrappedValue: editingSlot.courtIndex) + _startDate = .init(wrappedValue: editingSlot.startDate) + _endDate = .init(wrappedValue: editingSlot.endDate) + } + + init(event: Event) { + self.event = event + _courtIndex = .init(wrappedValue: 0) + let startDate = event.eventStartDate() + _startDate = .init(wrappedValue: event.eventStartDate()) + _endDate = .init(wrappedValue: startDate.addingTimeInterval(5400)) + } + + var body: some View { + NavigationStack { + Form { + Section { + CourtPicker(title: "Terrain", selection: $courtIndex, maxCourt: tournament.courtCount) + } + + Section { + DatePicker("Début", selection: $startDate) + .onChange(of: startDate) { + if endDate < startDate { + endDate = startDate.addingTimeInterval(90*60) + } + } + DatePicker("Fin", selection: $endDate) + .onChange(of: endDate) { + if startDate > endDate { + startDate = endDate.addingTimeInterval(-90*60) + } + + } + } footer: { + FooterButtonView("jour entier") { + startDate = startDate.startOfDay + endDate = startDate.tomorrowAtNine.startOfDay + } + } + + + Section { + DateAdjusterView(date: $startDate) + } header: { + Text("Modifier rapidement l'horaire de début") + } + Section { + DateAdjusterView(date: $endDate) + } header: { + Text("Modifier rapidement l'horaire de fin") + } + } + .toolbar { + ButtonValidateView { + if editingSlot == nil { + let dateInterval = DateInterval(event: event.id, courtIndex: courtIndex, startDate: startDate, endDate: endDate) + do { + try dataStore.dateIntervals.addOrUpdate(instance: dateInterval) + } catch { + Logger.error(error) + } + } else { + editingSlot?.courtIndex = courtIndex + editingSlot?.endDate = endDate + editingSlot?.startDate = startDate + do { + try dataStore.dateIntervals.addOrUpdate(instance: editingSlot!) + } catch { + Logger.error(error) + } + } + + dismiss() + } + + ToolbarItem(placement: .topBarLeading) { + Button("Annuler", role: .cancel) { + dismiss() + } + } + } + .navigationBarTitleDisplayMode(.inline) + .toolbarBackground(.visible, for: .navigationBar) + .navigationTitle(_navigationTitle()) + .tint(.master) + } + } + + private func _navigationTitle() -> String { + editingSlot == nil ? "Nouveau créneau" : "Édition du créneau" + } +} + +struct DateAdjusterView: View { + @Binding var date: Date + + var body: some View { + HStack { + _createButton(label: "-1h", timeOffset: -1, component: .hour) + _createButton(label: "-30m", timeOffset: -30, component: .minute) + _createButton(label: "+30m", timeOffset: 30, component: .minute) + _createButton(label: "+1h", timeOffset: 1, component: .hour) + } + .font(.headline) + } + + private func _createButton(label: String, timeOffset: Int, component: Calendar.Component) -> some View { + Button(action: { + date = Calendar.current.date(byAdding: component, value: timeOffset, to: date) ?? date + }) { + Text(label) + .frame(maxWidth: .infinity) // Make buttons take equal space + } + .buttonStyle(.borderedProminent) + .tint(.master) + } +} diff --git a/PadelClub/Views/Round/RoundSettingsView.swift b/PadelClub/Views/Round/RoundSettingsView.swift index 35311cb..0024d88 100644 --- a/PadelClub/Views/Round/RoundSettingsView.swift +++ b/PadelClub/Views/Round/RoundSettingsView.swift @@ -73,7 +73,8 @@ struct RoundSettingsView: View { Text("Suite à un changement dans votre liste d'inscrits, veuillez vérifier l'intégrité de votre tableau et valider que tout est ok.") } } - + + let previewAvailable = tournament.rounds().flatMap({ $0.seeds() }).count < tournament.seedsCount() && tournament.lastDrawnDate() != nil && tournament.seedSpotsLeft() Section { NavigationLink { DrawLogsView() @@ -82,7 +83,7 @@ struct RoundSettingsView: View { Text("Gestionnaire des tirages au sort") } - if tournament.rounds().flatMap({ $0.seeds() }).count < tournament.seedsCount(), tournament.lastDrawnDate() != nil, tournament.seedSpotsLeft() { + if previewAvailable { NavigationLink { PreviewBracketPositionView(seeds: tournament.seeds(), drawLogs: tournament.drawLogs()) @@ -94,7 +95,9 @@ struct RoundSettingsView: View { } } } footer: { - Text("Vous avez une ou plusieurs places libres dans votre tableau. Pour respecter le tirage au sort effectué, vous pouvez décaler les têtes de séries.") + if previewAvailable { + Text("Vous avez une ou plusieurs places libres dans votre tableau. Pour respecter le tirage au sort effectué, vous pouvez décaler les têtes de séries.") + } } // Section {