From 355833d808846374f35795f14a5c038e0d563890 Mon Sep 17 00:00:00 2001 From: Razmig Sarkissian Date: Wed, 24 Apr 2024 18:26:33 +0200 Subject: [PATCH] add new tab --- PadelClub.xcodeproj/project.pbxproj | 12 +++ .../Coredata/ImportedPlayer+Extensions.swift | 4 +- PadelClub/Data/GroupStage.swift | 2 +- PadelClub/Data/Match.swift | 2 +- PadelClub/Data/Round.swift | 2 +- PadelClub/Data/Tournament.swift | 17 ++-- PadelClub/Manager/PadelRule.swift | 2 +- PadelClub/ViewModel/TabDestination.swift | 5 ++ PadelClub/Views/Navigation/MainView.swift | 6 +- .../Navigation/Ongoing/OngoingView.swift | 35 +++++++++ .../Views/Navigation/PadelClubView.swift | 78 +++++++++---------- .../Views/Navigation/Umpire/UmpireView.swift | 42 +++++++++- .../CourtAvailabilitySettingsView.swift | 35 ++++++--- .../TournamentMatchFormatsSettingsView.swift | 8 +- 14 files changed, 178 insertions(+), 72 deletions(-) create mode 100644 PadelClub/Views/Navigation/Ongoing/OngoingView.swift diff --git a/PadelClub.xcodeproj/project.pbxproj b/PadelClub.xcodeproj/project.pbxproj index c336c5a..9d768e8 100644 --- a/PadelClub.xcodeproj/project.pbxproj +++ b/PadelClub.xcodeproj/project.pbxproj @@ -144,6 +144,7 @@ FF5D0D8B2BB4D1E3005CB568 /* CalendarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5D0D8A2BB4D1E3005CB568 /* CalendarView.swift */; }; FF5D30512BD94E1000F2B93D /* ImportedPlayer+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5D30502BD94E1000F2B93D /* ImportedPlayer+Extensions.swift */; }; FF5D30532BD94E2E00F2B93D /* PlayerHolder.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5D30522BD94E2E00F2B93D /* PlayerHolder.swift */; }; + FF5D30562BD95B1100F2B93D /* OngoingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5D30552BD95B1100F2B93D /* OngoingView.swift */; }; FF5DA18F2BB9268800A33061 /* GroupStageSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5DA18E2BB9268800A33061 /* GroupStageSettingsView.swift */; }; FF5DA1932BB9279B00A33061 /* RoundSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5DA1922BB9279B00A33061 /* RoundSettingsView.swift */; }; FF5DA1952BB927E800A33061 /* GenericDestinationPickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5DA1942BB927E800A33061 /* GenericDestinationPickerView.swift */; }; @@ -442,6 +443,7 @@ FF5D0D8A2BB4D1E3005CB568 /* CalendarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarView.swift; sourceTree = ""; }; FF5D30502BD94E1000F2B93D /* ImportedPlayer+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ImportedPlayer+Extensions.swift"; sourceTree = ""; }; FF5D30522BD94E2E00F2B93D /* PlayerHolder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerHolder.swift; sourceTree = ""; }; + FF5D30552BD95B1100F2B93D /* OngoingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OngoingView.swift; sourceTree = ""; }; FF5DA18E2BB9268800A33061 /* GroupStageSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupStageSettingsView.swift; sourceTree = ""; }; FF5DA1922BB9279B00A33061 /* RoundSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoundSettingsView.swift; sourceTree = ""; }; FF5DA1942BB927E800A33061 /* GenericDestinationPickerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GenericDestinationPickerView.swift; sourceTree = ""; }; @@ -899,6 +901,7 @@ FF3F74FA2B91A04B004CFE0E /* Organizer */, FF3F74FB2B91A060004CFE0E /* Toolbox */, FF3F74FC2B91A06B004CFE0E /* Umpire */, + FF5D30542BD95AF600F2B93D /* Ongoing */, ); path = Navigation; sourceTree = ""; @@ -991,6 +994,14 @@ path = ViewModel; sourceTree = ""; }; + FF5D30542BD95AF600F2B93D /* Ongoing */ = { + isa = PBXGroup; + children = ( + FF5D30552BD95B1100F2B93D /* OngoingView.swift */, + ); + path = Ongoing; + sourceTree = ""; + }; FF6EC8FC2B9478C800EA7F5A /* Shared */ = { isa = PBXGroup; children = ( @@ -1458,6 +1469,7 @@ FFA1B1292BB71773006CE248 /* PadelClubButtonView.swift in Sources */, FF5DA19B2BB9662200A33061 /* TournamentSeedEditing.swift in Sources */, FF70916C2B91005400AB08DA /* TournamentView.swift in Sources */, + FF5D30562BD95B1100F2B93D /* OngoingView.swift in Sources */, FF1DC5552BAB36DD00FD8220 /* CreateClubView.swift in Sources */, FFC1E10A2BAC2A77008D6F59 /* NetworkFederalService.swift in Sources */, FF025AEF2BD1AE9400A86CF8 /* DurationSettingsView.swift in Sources */, diff --git a/PadelClub/Data/Coredata/ImportedPlayer+Extensions.swift b/PadelClub/Data/Coredata/ImportedPlayer+Extensions.swift index 5bdf6ec..9b8ce71 100644 --- a/PadelClub/Data/Coredata/ImportedPlayer+Extensions.swift +++ b/PadelClub/Data/Coredata/ImportedPlayer+Extensions.swift @@ -92,6 +92,6 @@ extension ImportedPlayer: PlayerHolder { fileprivate extension Int { var femaleInMaleAssimilation: Int { - self + TournamentCatgory.femaleInMaleAssimilationAddition(self) - } + self + TournamentCategory.femaleInMaleAssimilationAddition(self) + } } diff --git a/PadelClub/Data/GroupStage.swift b/PadelClub/Data/GroupStage.swift index 17f57e0..cdd2c3e 100644 --- a/PadelClub/Data/GroupStage.swift +++ b/PadelClub/Data/GroupStage.swift @@ -17,7 +17,7 @@ class GroupStage: ModelObject, Storable { var tournament: String var index: Int var size: Int - var format: MatchFormat? + private var format: MatchFormat? var startDate: Date? var name: String? diff --git a/PadelClub/Data/Match.swift b/PadelClub/Data/Match.swift index f5623b7..7431513 100644 --- a/PadelClub/Data/Match.swift +++ b/PadelClub/Data/Match.swift @@ -19,7 +19,7 @@ class Match: ModelObject, Storable { var startDate: Date? var endDate: Date? var index: Int - var format: MatchFormat? + private var format: MatchFormat? //var court: String? var servingTeamId: String? var winningTeamId: String? diff --git a/PadelClub/Data/Round.swift b/PadelClub/Data/Round.swift index a2ceec3..b7649ed 100644 --- a/PadelClub/Data/Round.swift +++ b/PadelClub/Data/Round.swift @@ -16,7 +16,7 @@ class Round: ModelObject, Storable { var tournament: String var index: Int var parent: String? - var format: MatchFormat? + private var format: MatchFormat? var startDate: Date? internal init(tournament: String, index: Int, parent: String? = nil, matchFormat: MatchFormat? = nil) { diff --git a/PadelClub/Data/Tournament.swift b/PadelClub/Data/Tournament.swift index 5ea6491..3ea6e3b 100644 --- a/PadelClub/Data/Tournament.swift +++ b/PadelClub/Data/Tournament.swift @@ -20,9 +20,9 @@ class Tournament : ModelObject, Storable { var endDate: Date? private(set) var creationDate: Date var isPrivate: Bool - var groupStageFormat: MatchFormat? - var roundFormat: MatchFormat? - var loserRoundFormat: MatchFormat? + private var groupStageFormat: MatchFormat? + private var roundFormat: MatchFormat? + private var loserRoundFormat: MatchFormat? var groupStageSortMode: GroupStageOrderingMode var groupStageCount: Int var rankSourceDate: Date? @@ -1073,14 +1073,13 @@ class Tournament : ModelObject, Storable { var femaleUnrankedValue: Int? { currentMonthData()?.femaleUnrankedValue } + + func courtNameIfAvailable(atIndex courtIndex: Int) -> String? { + club()?.courts.first(where: { $0.index == courtIndex })?.name + } func courtName(atIndex courtIndex: Int) -> String { - let courts = club()?.courts - if let courts, let court = courts.first(where: { $0.index == courtIndex }) { - return court.courtTitle() - } else { - return Court.courtIndexedTitle(atIndex: courtIndex) - } + courtNameIfAvailable(atIndex: courtIndex) ?? Court.courtIndexedTitle(atIndex: courtIndex) } } diff --git a/PadelClub/Manager/PadelRule.swift b/PadelClub/Manager/PadelRule.swift index b4d47bf..aeeae15 100644 --- a/PadelClub/Manager/PadelRule.swift +++ b/PadelClub/Manager/PadelRule.swift @@ -651,7 +651,7 @@ enum TournamentCategory: Int, Hashable, Codable, CaseIterable, Identifiable { } } - func femaleInMaleAssimilationAddition(_ rank: Int) -> Int { + static func femaleInMaleAssimilationAddition(_ rank: Int) -> Int { switch rank { case 1...10: return 400 case 11...30: return 1000 diff --git a/PadelClub/ViewModel/TabDestination.swift b/PadelClub/ViewModel/TabDestination.swift index 015978c..b773564 100644 --- a/PadelClub/ViewModel/TabDestination.swift +++ b/PadelClub/ViewModel/TabDestination.swift @@ -18,6 +18,7 @@ enum TabDestination: CaseIterable, Identifiable { case tournamentOrganizer case umpire case padelClub + case ongoing var title: String { switch self { @@ -25,6 +26,8 @@ enum TabDestination: CaseIterable, Identifiable { return "Activité" case .eventList: return "Journal" + case .ongoing: + return "En cours" case .toolbox: return "Outils" case .tournamentOrganizer: @@ -42,6 +45,8 @@ enum TabDestination: CaseIterable, Identifiable { return "calendar.day.timeline.left" case .eventList: return "book.closed" + case .ongoing: + return "figure.tennis" case .toolbox: return "wrench.and.screwdriver" case .tournamentOrganizer: diff --git a/PadelClub/Views/Navigation/MainView.swift b/PadelClub/Views/Navigation/MainView.swift index 55cec3a..2395e2c 100644 --- a/PadelClub/Views/Navigation/MainView.swift +++ b/PadelClub/Views/Navigation/MainView.swift @@ -34,12 +34,14 @@ struct MainView: View { .tabItem(for: .activity) TournamentOrganizerView() .tabItem(for: .tournamentOrganizer) + OngoingView() + .tabItem(for: .ongoing) ToolboxView() .tabItem(for: .toolbox) UmpireView() .tabItem(for: .umpire) - PadelClubView() - .tabItem(for: .padelClub) +// PadelClubView() +// .tabItem(for: .padelClub) } .environmentObject(dataStore) .task { diff --git a/PadelClub/Views/Navigation/Ongoing/OngoingView.swift b/PadelClub/Views/Navigation/Ongoing/OngoingView.swift new file mode 100644 index 0000000..4c89fd8 --- /dev/null +++ b/PadelClub/Views/Navigation/Ongoing/OngoingView.swift @@ -0,0 +1,35 @@ +// +// OngoingView.swift +// PadelClub +// +// Created by Razmig Sarkissian on 24/04/2024. +// + +import SwiftUI + +struct OngoingView: View { + @EnvironmentObject var dataStore: DataStore + + var matches: [Match] { + dataStore.matches.filter({ $0.isRunning() }).sorted(by: \.startDate!) + } + + var body: some View { + NavigationStack { + List { + ForEach(matches) { match in + MatchRowView(match: match, matchViewStyle: .feedStyle) + } + } + .overlay { + ContentUnavailableView("Aucun match en cours", systemImage: "figure.tennis", description: Text("Tous vos matchs en cours seront visibles ici, quelque soit le tournoi.")) + } + .navigationTitle("En cours") + .toolbar(matches.isEmpty ? .hidden : .visible, for: .navigationBar) + } + } +} + +#Preview { + OngoingView() +} diff --git a/PadelClub/Views/Navigation/PadelClubView.swift b/PadelClub/Views/Navigation/PadelClubView.swift index 08c66d5..7c74e05 100644 --- a/PadelClub/Views/Navigation/PadelClubView.swift +++ b/PadelClub/Views/Navigation/PadelClubView.swift @@ -37,42 +37,41 @@ struct PadelClubView: View { } var body: some View { - NavigationStack { - List { - if let _lastDataSourceDate { - Section { - LabeledContent { - Image(systemName: "checkmark") - } label: { - Text(_lastDataSourceDate.monthYearFormatted) - Text("Classement mensuel utilisé") - } + List { + if let _lastDataSourceDate { + Section { + LabeledContent { + Image(systemName: "checkmark") + } label: { + Text(_lastDataSourceDate.monthYearFormatted) + Text("Classement mensuel utilisé") } } - - let monthData = dataStore.monthData.sorted(by: \.creationDate).reversed() - ForEach(monthData) { monthData in - Section { - LabeledContent { - if let maleUnrankedValue = monthData.maleUnrankedValue { - Text(maleUnrankedValue.formatted()) - } - } label: { - Text("Messieurs") - Text("Rang d'un non classé") + } + + let monthData = dataStore.monthData.sorted(by: \.creationDate).reversed() + ForEach(monthData) { monthData in + Section { + LabeledContent { + if let maleUnrankedValue = monthData.maleUnrankedValue { + Text(maleUnrankedValue.formatted()) } - LabeledContent { - if let femaleUnrankedValue = monthData.femaleUnrankedValue { - Text(femaleUnrankedValue.formatted()) - } - } label: { - Text("Dames") - Text("Rang d'une non classée") + } label: { + Text("Messieurs") + Text("Rang d'un non classé") + } + LabeledContent { + if let femaleUnrankedValue = monthData.femaleUnrankedValue { + Text(femaleUnrankedValue.formatted()) } - } header: { - Text(monthData.monthKey) + } label: { + Text("Dames") + Text("Rang d'une non classée") } + } header: { + Text(monthData.monthKey) } + } // // if players.isEmpty { // ContentUnavailableView { @@ -85,18 +84,17 @@ struct PadelClubView: View { // } // } // } + } + .task { + await self._checkSourceFileAvailability() + } + .refreshable { + Task { + await self._checkSourceFileAvailability() } - .headerProminence(.increased) - .navigationTitle(TabDestination.padelClub.title) -// .task { -// await self._checkSourceFileAvailability() -// } -// .refreshable { -// Task { -// await self._checkSourceFileAvailability() -// } -// } } + .headerProminence(.increased) + .navigationTitle(TabDestination.padelClub.title) } @ViewBuilder diff --git a/PadelClub/Views/Navigation/Umpire/UmpireView.swift b/PadelClub/Views/Navigation/Umpire/UmpireView.swift index 77306ad..ad416cf 100644 --- a/PadelClub/Views/Navigation/Umpire/UmpireView.swift +++ b/PadelClub/Views/Navigation/Umpire/UmpireView.swift @@ -10,7 +10,19 @@ import CoreLocation struct UmpireView: View { @EnvironmentObject var dataStore: DataStore - + var lastDataSource: String? { + dataStore.appSettings.lastDataSource + } + + var _mostRecentDateAvailable: Date? { + SourceFileManager.shared.mostRecentDateAvailable + } + + var _lastDataSourceDate: Date? { + guard let lastDataSource else { return nil } + return URL.importDateFormatter.date(from: lastDataSource) + } + var body: some View { NavigationStack { List { @@ -102,6 +114,34 @@ struct UmpireView: View { } } + Section { + NavigationLink { + PadelClubView() + } label: { + if let _lastDataSourceDate { + LabeledContent { + Image(systemName: "checkmark.circle.fill") + .tint(.green) + } label: { + Text(_lastDataSourceDate.monthYearFormatted) + Text("Classement mensuel utilisé") + } + } else { + LabeledContent { + Image(systemName: "xmark.circle.fill") + .tint(.red) + } label: { + if let _mostRecentDateAvailable { + Text(_mostRecentDateAvailable.monthYearFormatted) + } else { + Text("Aucun") + } + Text("Classement mensuel disponible") + } + } + } + } + // Section { // Text("Tenup ID") // } diff --git a/PadelClub/Views/Planning/CourtAvailabilitySettingsView.swift b/PadelClub/Views/Planning/CourtAvailabilitySettingsView.swift index 0ec779c..f41b2a6 100644 --- a/PadelClub/Views/Planning/CourtAvailabilitySettingsView.swift +++ b/PadelClub/Views/Planning/CourtAvailabilitySettingsView.swift @@ -38,8 +38,11 @@ struct CourtAvailabilitySettingsView: View { Text(dateInterval.startDate.localizedDay()).font(.caption) } Spacer() - Image(systemName: "arrowshape.forward.fill") - .tint(.master) + VStack { + Image(systemName: "arrowshape.forward.fill") + .tint(.master) + Text("indisponible").foregroundStyle(.red).font(.caption) + } Spacer() VStack(alignment: .trailing, spacing: 0) { Text(dateInterval.endDate.localizedTime()).font(.largeTitle) @@ -70,27 +73,39 @@ struct CourtAvailabilitySettingsView: View { } } } header: { - Text(tournament.courtName(atIndex: key)) + HStack { + Text(Court.courtIndexedTitle(atIndex: key)) + Spacer() + if let courtName = tournament.courtNameIfAvailable(atIndex: key) { + Text(courtName) + } + } } .headerProminence(.increased) } } } + .overlay { + ContentUnavailableView { + Label("Tous les terrains sont disponibles", systemImage: "checkmark.circle.fill") + } description: { + 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") { + showingPopover = true + } + } + } .toolbar { ToolbarItem(placement: .topBarTrailing) { - Button { + BarButtonView("Ajouter une indisponibilité", icon: "plus.circle.fill") { showingPopover = true - } label: { - Image(systemName: "plus.circle.fill") - .resizable() - .scaledToFit() - .frame(minHeight: 28) } } } .navigationBarTitleDisplayMode(.inline) .toolbarBackground(.visible, for: .navigationBar) - .navigationTitle("Créneaux") + .navigationTitle("Créneau indisponible") .popover(isPresented: $showingPopover) { NavigationStack { Form { diff --git a/PadelClub/Views/Tournament/Screen/Components/TournamentMatchFormatsSettingsView.swift b/PadelClub/Views/Tournament/Screen/Components/TournamentMatchFormatsSettingsView.swift index 7bca1b6..0c159f7 100644 --- a/PadelClub/Views/Tournament/Screen/Components/TournamentMatchFormatsSettingsView.swift +++ b/PadelClub/Views/Tournament/Screen/Components/TournamentMatchFormatsSettingsView.swift @@ -41,12 +41,12 @@ struct TournamentMatchFormatsSettingsView: View { Label("Définir les durées moyennes", systemImage: "deskclock") } } footer: { - Text("Vous pouvez définir vos propores estimations de durées de match en fonction du format de jeu.") + Text("Vous pouvez définir vos propres estimations de durées de match en fonction du format de jeu.") } } - .onChange(of: [tournament.roundFormat, - tournament.groupStageFormat, - tournament.loserRoundFormat, + .onChange(of: [tournament.loserBracketMatchFormat, + tournament.groupStageMatchFormat, + tournament.matchFormat, ]) { _save() _confirmOrSave()