Laurent 2 years ago
commit d5bb06a76d
  1. 8
      PadelClub.xcodeproj/project.pbxproj
  2. 12
      PadelClub/Data/Tournament.swift
  3. 8
      PadelClub/Manager/Network/NetworkFederalService.swift
  4. 5
      PadelClub/Manager/PadelRule.swift
  5. 19
      PadelClub/ViewModel/AgendaDestination.swift
  6. 104
      PadelClub/ViewModel/FederalDataViewModel.swift
  7. 2
      PadelClub/Views/Club/CourtView.swift
  8. 2
      PadelClub/Views/Components/GenericDestinationPickerView.swift
  9. 3
      PadelClub/Views/Event/EventCreationView.swift
  10. 2
      PadelClub/Views/GroupStage/GroupStageView.swift
  11. 12
      PadelClub/Views/Match/MatchDetailView.swift
  12. 75
      PadelClub/Views/Navigation/Agenda/ActivityView.swift
  13. 66
      PadelClub/Views/Navigation/Agenda/CalendarView.swift
  14. 32
      PadelClub/Views/Navigation/Agenda/EventListView.swift
  15. 147
      PadelClub/Views/Shared/TournamentFilterView.swift
  16. 2
      PadelClub/Views/Tournament/Screen/Components/InscriptionInfoView.swift
  17. 2
      PadelClub/Views/Tournament/Screen/TableStructureView.swift

@ -136,6 +136,7 @@
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 */; };
FF5BAF6E2BE0B3C8008B4B7E /* FederalDataViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5BAF6D2BE0B3C8008B4B7E /* FederalDataViewModel.swift */; };
FF5D0D722BB3EFA5005CB568 /* LearnMoreSheetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5D0D6F2BB3EFA5005CB568 /* LearnMoreSheetView.swift */; };
FF5D0D742BB41DF8005CB568 /* Color+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5D0D732BB41DF8005CB568 /* Color+Extensions.swift */; };
FF5D0D762BB428B2005CB568 /* ListRowViewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5D0D752BB428B2005CB568 /* ListRowViewModifier.swift */; };
@ -151,6 +152,7 @@
FF5DA1932BB9279B00A33061 /* RoundSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5DA1922BB9279B00A33061 /* RoundSettingsView.swift */; };
FF5DA1952BB927E800A33061 /* GenericDestinationPickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5DA1942BB927E800A33061 /* GenericDestinationPickerView.swift */; };
FF5DA19B2BB9662200A33061 /* TournamentSeedEditing.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5DA19A2BB9662200A33061 /* TournamentSeedEditing.swift */; };
FF663FBE2BE019EC0031AE83 /* TournamentFilterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF663FBD2BE019EC0031AE83 /* TournamentFilterView.swift */; };
FF6EC8F72B94773200EA7F5A /* RowButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF6EC8F62B94773100EA7F5A /* RowButtonView.swift */; };
FF6EC8FB2B94788600EA7F5A /* TournamentButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF6EC8FA2B94788600EA7F5A /* TournamentButtonView.swift */; };
FF6EC8FE2B94792300EA7F5A /* Screen.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF6EC8FD2B94792300EA7F5A /* Screen.swift */; };
@ -424,6 +426,7 @@
FF59FFB22B90EFAC0061EFF9 /* EventListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventListView.swift; sourceTree = "<group>"; };
FF59FFB62B90EFBF0061EFF9 /* MainView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainView.swift; sourceTree = "<group>"; };
FF59FFB82B90EFD70061EFF9 /* ToolboxView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToolboxView.swift; sourceTree = "<group>"; };
FF5BAF6D2BE0B3C8008B4B7E /* FederalDataViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FederalDataViewModel.swift; sourceTree = "<group>"; };
FF5D0D6F2BB3EFA5005CB568 /* LearnMoreSheetView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LearnMoreSheetView.swift; sourceTree = "<group>"; };
FF5D0D732BB41DF8005CB568 /* Color+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Color+Extensions.swift"; sourceTree = "<group>"; };
FF5D0D752BB428B2005CB568 /* ListRowViewModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListRowViewModifier.swift; sourceTree = "<group>"; };
@ -439,6 +442,7 @@
FF5DA1922BB9279B00A33061 /* RoundSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoundSettingsView.swift; sourceTree = "<group>"; };
FF5DA1942BB927E800A33061 /* GenericDestinationPickerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GenericDestinationPickerView.swift; sourceTree = "<group>"; };
FF5DA19A2BB9662200A33061 /* TournamentSeedEditing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TournamentSeedEditing.swift; sourceTree = "<group>"; };
FF663FBD2BE019EC0031AE83 /* TournamentFilterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TournamentFilterView.swift; sourceTree = "<group>"; };
FF6EC8F62B94773100EA7F5A /* RowButtonView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RowButtonView.swift; sourceTree = "<group>"; };
FF6EC8FA2B94788600EA7F5A /* TournamentButtonView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TournamentButtonView.swift; sourceTree = "<group>"; };
FF6EC8FD2B94792300EA7F5A /* Screen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Screen.swift; sourceTree = "<group>"; };
@ -974,6 +978,7 @@
FFCFC01B2BBC5AAA00B82851 /* SetDescriptor.swift */,
FFBF065F2BBD9F6D009D6715 /* NavigationViewModel.swift */,
FF3B60A22BC49BBC008C2E66 /* MatchScheduler.swift */,
FF5BAF6D2BE0B3C8008B4B7E /* FederalDataViewModel.swift */,
);
path = ViewModel;
sourceTree = "<group>";
@ -995,6 +1000,7 @@
FF4AB6BE2B92577A0002987F /* ImportedPlayerView.swift */,
FF5D0D6F2BB3EFA5005CB568 /* LearnMoreSheetView.swift */,
FFCFC0192BBC5A8500B82851 /* MatchTypeSmallSelectionView.swift */,
FF663FBD2BE019EC0031AE83 /* TournamentFilterView.swift */,
);
path = Shared;
sourceTree = "<group>";
@ -1495,6 +1501,7 @@
C45BAE442BCA753E002EEC8A /* Purchase.swift in Sources */,
FF6EC8FE2B94792300EA7F5A /* Screen.swift in Sources */,
FF967CEE2BAECBD700A9A3BD /* Round.swift in Sources */,
FF5BAF6E2BE0B3C8008B4B7E /* FederalDataViewModel.swift in Sources */,
FF3F74FF2B91A2D4004CFE0E /* AgendaDestination.swift in Sources */,
FF3795622B9396D0004EA093 /* PadelClubApp.xcdatamodeld in Sources */,
FFCFC0162BBC5A4C00B82851 /* SetInputView.swift in Sources */,
@ -1556,6 +1563,7 @@
FF1DC5532BAB354A00FD8220 /* MockData.swift in Sources */,
FF967D092BAF3D4000A9A3BD /* TeamDetailView.swift in Sources */,
FF5DA18F2BB9268800A33061 /* GroupStageSettingsView.swift in Sources */,
FF663FBE2BE019EC0031AE83 /* TournamentFilterView.swift in Sources */,
FF8F26382BAD523300650388 /* PadelRule.swift in Sources */,
FF967CF42BAECC0B00A9A3BD /* TeamRegistration.swift in Sources */,
FFF8ACDB2B923F48008466FA /* Date+Extensions.swift in Sources */,

@ -959,7 +959,7 @@ class Tournament : ModelObject, Storable {
var groupStageOrderingMode: GroupStageOrderingMode {
get {
groupStageSortMode ?? .random
groupStageSortMode
}
set {
groupStageSortMode = newValue
@ -968,7 +968,7 @@ class Tournament : ModelObject, Storable {
var tournamentCategory: TournamentCategory {
get {
federalCategory ?? .men
federalCategory
}
set {
if federalCategory != newValue {
@ -982,7 +982,7 @@ class Tournament : ModelObject, Storable {
var tournamentLevel: TournamentLevel {
get {
federalLevelCategory ?? .p100
federalLevelCategory
}
set {
federalLevelCategory = newValue
@ -996,7 +996,7 @@ class Tournament : ModelObject, Storable {
var federalTournamentAge: FederalTournamentAge {
get {
federalAgeCategory ?? .senior
federalAgeCategory
}
set {
federalAgeCategory = newValue
@ -1148,6 +1148,10 @@ extension Tournament: FederalTournamentHolder {
}
extension Tournament: TournamentBuildHolder {
func buildHolderTitle() -> String {
tournamentTitle()
}
var category: TournamentCategory {
tournamentCategory
}

@ -92,7 +92,7 @@ class NetworkFederalService {
}
func getClubFederalTournaments(page: Int, tournaments: [FederalTournament], club: String, codeClub: String, startDate: Date? = nil) async throws -> [FederalTournament] {
func getClubFederalTournaments(page: Int, tournaments: [FederalTournament], club: String, codeClub: String, startDate: Date? = nil, endDate: Date? = nil) async throws -> [FederalTournament] {
if formId.isEmpty {
do {
@ -103,8 +103,10 @@ class NetworkFederalService {
}
var dateComponent = ""
if let startDate {
dateComponent = "&date[start]=\(startDate.twoDigitsYearFormatted)"
if let startDate, let endDate {
dateComponent = "&date[start]=\(startDate.twoDigitsYearFormatted)&date[end]=\(endDate.endOfMonth.twoDigitsYearFormatted)"
} else if let startDate {
dateComponent = "&date[start]=\(startDate.twoDigitsYearFormatted)&date[end]=\(Calendar.current.date(byAdding: .month, value: 3, to: startDate)!.endOfMonth.twoDigitsYearFormatted)"
}
let parameters = """

@ -29,6 +29,7 @@ protocol TournamentBuildHolder: Identifiable {
var category: TournamentCategory { get }
var level: TournamentLevel { get }
var age: FederalTournamentAge { get }
func buildHolderTitle() -> String
}
struct TournamentBuild: TournamentBuildHolder, Hashable, Codable, Identifiable {
@ -40,6 +41,10 @@ struct TournamentBuild: TournamentBuildHolder, Hashable, Codable, Identifiable {
// var japFirstName: String? = nil
// var japLastName: String? = nil
func buildHolderTitle() -> String {
localizedLabel()
}
var identifier: String {
level.localizedLabel()+":"+category.localizedLabel()+":"+age.localizedLabel()
}

@ -48,15 +48,26 @@ enum AgendaDestination: CaseIterable, Identifiable, Selectable {
func badgeValue() -> Int? {
switch self {
case .activity:
DataStore.shared.tournaments.filter { $0.endDate == nil }.count
DataStore.shared.tournaments.filter { $0.endDate == nil && FederalDataViewModel.shared.isTournamentValidForFilters($0) }.count
case .history:
DataStore.shared.tournaments.filter { $0.endDate != nil }.count
DataStore.shared.tournaments.filter { $0.endDate != nil && FederalDataViewModel.shared.isTournamentValidForFilters($0) }.count
case .tenup:
nil
FederalDataViewModel.shared.filteredFederalTournaments.count
}
}
func badgeImage() -> Badge? {
nil
switch self {
case .activity:
return nil
case .history:
return nil
case .tenup:
if FederalDataViewModel.shared.federalTournaments.isEmpty {
return .custom(systemName: "exclamationmark.circle.fill", color: .yellow)
} else {
return nil
}
}
}
}

@ -0,0 +1,104 @@
//
// FederalDataViewModel.swift
// PadelClub
//
// Created by Razmig Sarkissian on 30/04/2024.
//
import SwiftUI
import LeStorage
@Observable
class FederalDataViewModel {
static let shared = FederalDataViewModel()
var federalTournaments: [FederalTournament] = []
var levels: Set<TournamentLevel> = Set()
var categories: Set<TournamentCategory> = Set()
var ageCategories: Set<FederalTournamentAge> = Set()
var selectedClubs: Set<String> = Set()
var id: UUID = UUID()
func filterStatus() -> String {
var labels: [String] = []
labels.append(contentsOf: levels.map { $0.localizedLabel() })
labels.append(contentsOf: categories.map { $0.localizedLabel() })
labels.append(contentsOf: ageCategories.map { $0.localizedLabel() })
let clubNames = selectedClubs.compactMap { codeClub in
let club: Club? = Store.main.filter(isIncluded: { $0.code == codeClub }).first
return club?.clubTitle(.short)
}
labels.append(contentsOf: clubNames)
return labels.joined(separator: ", ")
}
func selectedClub() -> Club? {
if selectedClubs.isEmpty == false {
return Store.main.filter(isIncluded: { $0.code == selectedClubs.first! }).first
} else {
return nil
}
}
func removeFilters() {
levels.removeAll()
categories.removeAll()
ageCategories.removeAll()
selectedClubs.removeAll()
id = UUID()
}
func areFiltersEnabled() -> Bool {
(levels.isEmpty && categories.isEmpty && ageCategories.isEmpty && selectedClubs.isEmpty) == false
}
var filteredFederalTournaments: [FederalTournament] {
federalTournaments.filter({ tournament in
(levels.isEmpty || tournament.tournaments.anySatisfy({ levels.contains($0.level) }))
&&
(categories.isEmpty || tournament.tournaments.anySatisfy({ categories.contains($0.category) }))
&&
(ageCategories.isEmpty || tournament.tournaments.anySatisfy({ ageCategories.contains($0.age) }))
&&
(selectedClubs.isEmpty || selectedClubs.contains(tournament.codeClub!))
})
}
func isTournamentValidForFilters(_ tournament: Tournament) -> Bool {
let firstPart = (levels.isEmpty || levels.contains(tournament.level))
&&
(categories.isEmpty || categories.contains(tournament.category))
&&
(ageCategories.isEmpty || ageCategories.contains(tournament.age))
if let codeClub = tournament.club()?.code {
return firstPart && (selectedClubs.isEmpty || selectedClubs.contains(codeClub))
} else {
return firstPart
}
}
func isFederalTournamentValidForFilters(_ tournament: FederalTournament, build: any TournamentBuildHolder) -> Bool {
(levels.isEmpty || levels.contains(build.level))
&&
(categories.isEmpty || categories.contains(build.category))
&&
(ageCategories.isEmpty || ageCategories.contains(build.age))
&&
(selectedClubs.isEmpty || selectedClubs.contains(tournament.codeClub!))
}
func gatherTournaments(clubs: [Club], startDate: Date, endDate: Date? = nil) async throws {
try await clubs.filter { $0.code != nil }.concurrentForEach { club in
let newTournaments = try await NetworkFederalService.shared.getClubFederalTournaments(page: 0, tournaments: [], club: club.name, codeClub: club.code!, startDate: startDate, endDate: endDate)
newTournaments.forEach { tournament in
if self.federalTournaments.contains(where: { $0.id == tournament.id }) == false {
self.federalTournaments.append(tournament)
}
}
}
}
}

@ -32,7 +32,7 @@ struct CourtView: View {
Section {
Toggle(isOn: $court.exitAllowed) {
Text("Sortie authorisée")
Text("Sortie autorisée")
}
Toggle(isOn: $court.indoor) {
Text("Terrain intérieur")

@ -22,6 +22,7 @@ struct GenericDestinationPickerView<T: Identifiable & Selectable>: View {
} label: {
Image(systemName: "wrench.and.screwdriver")
}
.foregroundStyle(selectedDestination == nil ? .white : .black)
.padding()
.background {
Circle()
@ -36,6 +37,7 @@ struct GenericDestinationPickerView<T: Identifiable & Selectable>: View {
selectedDestination = destination
} label: {
Text(destination.selectionLabel())
.foregroundStyle(selectedDestination?.id == destination.id ? .white : .black)
}
.padding()
.background {

@ -11,6 +11,7 @@ import TipKit
struct EventCreationView: View {
@Environment(\.dismiss) private var dismiss
@EnvironmentObject var dataStore: DataStore
@Environment(FederalDataViewModel.self) private var federalDataViewModel: FederalDataViewModel
@Environment(NavigationViewModel.self) private var navigation: NavigationViewModel
@State private var eventType: EventType = .approvedTournament
@State private var animationType: AnimationType = .upAndDown
@ -18,7 +19,7 @@ struct EventCreationView: View {
@State private var duration: Int = 3
@State private var eventName: String = ""
@State var tournaments: [Tournament] = []
@State private var selectedClub: Club?
@State var selectedClub: Club?
let multiTournamentsEventTip = MultiTournamentsEventTip()

@ -54,7 +54,7 @@ struct GroupStageView: View {
sortingMode = .score
}
} else if sortingMode == .weight {
sortingMode = .weight
sortingMode = .score
} else {
sortingMode = .weight
}

@ -134,6 +134,12 @@ struct MatchDetailView: View {
}
}
if match.isReady() {
Section {
inputScoreView
}
}
let players = match.teams().flatMap { $0.players() }
let unpaid = players.filter({ $0.hasPaid() == false })
@ -343,12 +349,6 @@ struct MatchDetailView: View {
@ViewBuilder
var menuView: some View {
if match.isReady() {
Section {
inputScoreView
}
}
broadcastView
if match.hasStarted() {

@ -10,28 +10,31 @@ import SwiftUI
struct ActivityView: View {
@EnvironmentObject var dataStore: DataStore
@Environment(NavigationViewModel.self) private var navigation
@State private var federalDataViewModel : FederalDataViewModel = .shared
@State private var searchText: String = ""
@State private var filterEnabled: Bool = false
@State private var presentFilterView: Bool = false
@State private var presentToolbar: Bool = false
@State private var newTournament: Tournament?
@State private var viewStyle: AgendaDestination.ViewStyle = .list
@State private var federalTournaments: [FederalTournament] = []
@State private var isGatheringFederalTournaments: Bool = false
@State private var error: Error?
@State private var uuid: UUID = UUID()
var runningTournaments: [FederalTournamentHolder] {
dataStore.tournaments.filter({ $0.endDate == nil })
.filter({ federalDataViewModel.isTournamentValidForFilters($0) })
}
var endedTournaments: [Tournament] {
dataStore.tournaments.filter({ $0.endDate != nil }).sorted(using: SortDescriptor(\.startDate, order: .reverse))
dataStore.tournaments.filter({ $0.endDate != nil })
.filter({ federalDataViewModel.isTournamentValidForFilters($0) })
.sorted(using: SortDescriptor(\.startDate, order: .reverse))
}
func _activityStatus() -> String? {
let tournaments = tournaments
if tournaments.isEmpty && filterEnabled == false {
if tournaments.isEmpty && federalDataViewModel.areFiltersEnabled() == false {
return nil
} else {
let count = tournaments.map { $0.tournaments.count }.reduce(0,+)
@ -46,7 +49,7 @@ struct ActivityView: View {
case .history:
endedTournaments
case .tenup:
federalTournaments
federalDataViewModel.filteredFederalTournaments
}
}
@ -63,9 +66,11 @@ struct ActivityView: View {
case .history:
EventListView(tournaments: endedTournaments, viewStyle: viewStyle)
case .tenup:
EventListView(tournaments: federalTournaments, viewStyle: viewStyle)
EventListView(tournaments: federalDataViewModel.federalTournaments, viewStyle: viewStyle)
.id(uuid)
}
}
.environment(federalDataViewModel)
.overlay {
if let error, navigation.agendaDestination == .tenup {
ContentUnavailableView {
@ -83,14 +88,14 @@ struct ActivityView: View {
if tournaments.isEmpty && viewStyle == .list {
if searchText.isEmpty == false {
ContentUnavailableView.search(text: searchText)
} else if filterEnabled {
} else if federalDataViewModel.areFiltersEnabled() {
ContentUnavailableView {
Text("Aucun résultat")
} description: {
Text("Description du filtre")
Text(federalDataViewModel.filterStatus())
} actions: {
RowButtonView("supprimer le filtre") {
filterEnabled.toggle()
federalDataViewModel.removeFilters()
}
}
} else {
@ -103,27 +108,28 @@ struct ActivityView: View {
.onAppear { presentToolbar = true }
.onDisappear { presentToolbar = false }
.sheet(item: $newTournament) { tournament in
EventCreationView(tournaments: [tournament])
EventCreationView(tournaments: [tournament], selectedClub: federalDataViewModel.selectedClub())
.environment(navigation)
.tint(.master)
}
.refreshable {
if navigation.agendaDestination == .tenup {
federalTournaments.removeAll()
federalDataViewModel.federalTournaments.removeAll()
NetworkFederalService.shared.formId = ""
_gatherFederalTournaments()
}
}
.task {
if navigation.agendaDestination == .tenup
&& dataStore.clubs.isEmpty == false
&& federalTournaments.isEmpty {
&& federalDataViewModel.federalTournaments.isEmpty {
_gatherFederalTournaments()
}
}
.onChange(of: navigation.agendaDestination) {
if navigation.agendaDestination == .tenup
&& dataStore.clubs.isEmpty == false
&& federalTournaments.isEmpty {
&& federalDataViewModel.federalTournaments.isEmpty {
_gatherFederalTournaments()
}
}
@ -131,8 +137,8 @@ struct ActivityView: View {
if presentToolbar {
ToolbarItem(placement: .status) {
VStack(spacing: -2) {
if filterEnabled {
Text("filtre actif")
if federalDataViewModel.areFiltersEnabled() {
Text(federalDataViewModel.filterStatus())
}
if let _activityStatus = _activityStatus() {
Text(_activityStatus)
@ -143,7 +149,7 @@ struct ActivityView: View {
}
ToolbarItem(placement: .topBarLeading) {
ToolbarItemGroup(placement: .topBarLeading) {
Button {
switch viewStyle {
case .list:
@ -158,17 +164,16 @@ struct ActivityView: View {
.frame(minHeight: 28)
}
.symbolVariant(viewStyle == .calendar ? .fill : .none)
//
// Button {
// filterEnabled.toggle()
// } label: {
// Image(systemName: "line.3.horizontal.decrease.circle")
// .resizable()
// .scaledToFit()
// .frame(minHeight: 28)
// }
// .symbolVariant(filterEnabled ? .fill : .none)
Button {
presentFilterView.toggle()
} label: {
Image(systemName: "line.3.horizontal.decrease.circle")
.resizable()
.scaledToFit()
.frame(minHeight: 28)
}
.symbolVariant(federalDataViewModel.areFiltersEnabled() ? .fill : .none)
}
ToolbarItem(placement: .topBarTrailing) {
@ -189,22 +194,25 @@ struct ActivityView: View {
TournamentView()
.environment(tournament)
}
.sheet(isPresented: $presentFilterView) {
TournamentFilterView(federalDataViewModel: federalDataViewModel)
.environment(navigation)
.tint(.master)
}
}
}
}
private func _gatherFederalTournaments() {
isGatheringFederalTournaments = true
NetworkFederalService.shared.formId = ""
Task {
do {
try await dataStore.clubs.filter { $0.code != nil }.concurrentForEach { club in
federalTournaments += try await NetworkFederalService.shared.getClubFederalTournaments(page: 0, tournaments: [], club: club.name, codeClub: club.code!, startDate: .now.startOfMonth)
}
try await federalDataViewModel.gatherTournaments(clubs: dataStore.clubs.filter { $0.code != nil }, startDate: .now.startOfMonth)
} catch {
self.error = error
}
isGatheringFederalTournaments = false
uuid = UUID()
}
}
@ -269,6 +277,7 @@ struct ActivityView: View {
Text("Aucun tournoi n'a pu être récupéré via tenup.")
} actions: {
RowButtonView("Rafraîchir") {
NetworkFederalService.shared.formId = ""
_gatherFederalTournaments()
}
}

@ -13,7 +13,9 @@
import SwiftUI
struct CalendarView: View {
@EnvironmentObject var dataStore: DataStore
@Environment(NavigationViewModel.self) var navigation: NavigationViewModel
@Environment(FederalDataViewModel.self) var federalDataViewModel: FederalDataViewModel
let date: Date
let tournaments: [FederalTournamentHolder]
let daysOfWeek = Date.capitalizedFirstLettersOfWeekdays
@ -42,7 +44,7 @@ struct CalendarView: View {
}
}
.sheet(item: $newTournament) { tournament in
EventCreationView(startingDate: tournament.startDate, tournaments: [tournament])
EventCreationView(startingDate: tournament.startDate, tournaments: [tournament], selectedClub: federalDataViewModel.selectedClub())
.environment(navigation)
.tint(.master)
}
@ -65,14 +67,42 @@ struct CalendarView: View {
Button("Créer un tournoi") {
let tournament = Tournament.newEmptyInstance()
tournament.startDate = day.atNine()
if federalDataViewModel.categories.isEmpty == false {
tournament.tournamentCategory = federalDataViewModel.categories.first!
}
if federalDataViewModel.levels.isEmpty == false {
tournament.tournamentLevel = federalDataViewModel.levels.first!
}
if federalDataViewModel.ageCategories.isEmpty == false {
tournament.federalTournamentAge = federalDataViewModel.ageCategories.first!
}
newTournament = tournament
}
Divider()
let tournamentsByDay = tournaments.filter { $0.startDate.dayInt == day.dayInt }
let tournamentsByDay = tournaments.filter { day.dayInt >= $0.startDate.dayInt && day.dayInt < $0.startDate.dayInt + $0.dayDuration }
ForEach(tournamentsByDay, id: \.holderId) { tournamentHolder in
if let tournament = tournamentHolder as? Tournament {
Button(tournament.tournamentTitle()) {
navigation.path.append(tournament)
Section {
Button(tournament.tournamentTitle()) {
navigation.path.append(tournament)
}
} header: {
Text("sur " + tournament.dayDuration.formatted() + " jour" + tournament.dayDuration.pluralSuffix)
}
} else if let tournament = tournamentHolder as? FederalTournament {
Menu {
ForEach(tournament.tournaments, id: \.id) { build in
if federalDataViewModel.isFederalTournamentValidForFilters(tournament, build: build) {
Button(build.buildHolderTitle()) {
_createOrShow(federalTournament: tournament, existingTournament: event(forTournament: tournament)?.existingBuild(build), build: build)
}
}
}
} label: {
Text(tournament.clubLabel())
Text("sur " + tournament.dayDuration.formatted() + " jour" + tournament.dayDuration.pluralSuffix)
}
}
}
@ -116,6 +146,34 @@ struct CalendarView: View {
}
counts = Dictionary(mappedItems, uniquingKeysWith: +)
}
func event(forTournament tournament: FederalTournamentHolder) -> Event? {
guard let federalTournament = tournament as? FederalTournament else { return nil }
return dataStore.events.first(where: { $0.tenupId == federalTournament.id.string })
}
private func _createOrShow(federalTournament: FederalTournament, existingTournament: Tournament?, build: any TournamentBuildHolder) {
if let existingTournament {
navigation.agendaDestination = .activity
navigation.path.append(existingTournament)
} else {
let event = federalTournament.getEvent()
let newTournament = Tournament.newEmptyInstance()
newTournament.event = event.id
//todo
//newTournament.umpireMail()
//newTournament.jsonData = jsonData
newTournament.tournamentLevel = build.level
newTournament.tournamentCategory = build.category
newTournament.federalTournamentAge = build.age
newTournament.dayDuration = federalTournament.dayDuration
newTournament.startDate = federalTournament.startDate.atBeginningOfDay(hourInt: 9)
newTournament.setupFederalSettings()
try? dataStore.tournaments.addOrUpdate(instance: newTournament)
navigation.path.append(newTournament)
}
}
}
#Preview {

@ -9,23 +9,20 @@ import SwiftUI
struct EventListView: View {
@EnvironmentObject var dataStore: DataStore
@Environment(NavigationViewModel.self) var navigation: NavigationViewModel
@Environment(FederalDataViewModel.self) var federalDataViewModel: FederalDataViewModel
let tournaments: [FederalTournamentHolder]
let viewStyle: AgendaDestination.ViewStyle
var body: some View {
let groupedTournamentsByDate = Dictionary(grouping: tournaments) { $0.startDate.startOfMonth }
let groupedTournamentsByDate = Dictionary(grouping: navigation.agendaDestination == .tenup ? federalDataViewModel.filteredFederalTournaments : tournaments) { $0.startDate.startOfMonth }
switch viewStyle {
case .list:
ForEach(groupedTournamentsByDate.keys.sorted(by: <), id: \.self) { section in
if let _tournaments = groupedTournamentsByDate[section]?.sorted(by: \.startDate) {
Section {
switch viewStyle {
case .list:
_listView(_tournaments)
case .calendar:
CalendarView(date: section, tournaments: _tournaments)
}
_listView(_tournaments)
} header: {
HStack {
Text(section.monthYearFormatted)
@ -41,7 +38,7 @@ struct EventListView: View {
ForEach(_nextMonths(), id: \.self) { section in
let _tournaments = groupedTournamentsByDate[section]?.sorted(by: \.startDate) ?? []
Section {
CalendarView(date: section, tournaments: _tournaments)
CalendarView(date: section, tournaments: _tournaments).id(federalDataViewModel.id)
} header: {
HStack {
Text(section.monthYearFormatted)
@ -51,7 +48,26 @@ struct EventListView: View {
}
}
.headerProminence(.increased)
.task {
if navigation.agendaDestination == .tenup
&& dataStore.clubs.isEmpty == false
&& _tournaments.isEmpty {
_gatherFederalTournaments(startDate: section)
}
}
}
}
}
private func _gatherFederalTournaments(startDate: Date) {
// isGatheringFederalTournaments = true
Task {
do {
try await federalDataViewModel.gatherTournaments(clubs: dataStore.clubs.filter { $0.code != nil }, startDate: startDate, endDate: startDate.endOfMonth)
} catch {
// self.error = error
}
// isGatheringFederalTournaments = false
}
}

@ -0,0 +1,147 @@
//
// TournamentFilterView.swift
// PadelClub
//
// Created by Razmig Sarkissian on 29/04/2024.
//
import SwiftUI
struct TournamentFilterView: View {
@EnvironmentObject var dataStore: DataStore
@Environment(\.dismiss) private var dismiss
@State private var levels: Set<TournamentLevel>
@State private var categories: Set<TournamentCategory>
@State private var ageCategories: Set<FederalTournamentAge>
@State private var selectedClubs: Set<String>
var federalDataViewModel: FederalDataViewModel
init(federalDataViewModel: FederalDataViewModel) {
self.federalDataViewModel = federalDataViewModel
_levels = State(wrappedValue: federalDataViewModel.levels)
_categories = State(wrappedValue: federalDataViewModel.categories)
_ageCategories = State(wrappedValue: federalDataViewModel.ageCategories)
_selectedClubs = State(wrappedValue: federalDataViewModel.selectedClubs)
}
var body: some View {
NavigationView {
Form {
Section {
ForEach(dataStore.clubs.filter({ $0.code != nil })) { club in
LabeledContent {
Button {
if selectedClubs.contains(club.code!) {
selectedClubs.remove(club.code!)
} else {
selectedClubs.insert(club.code!)
}
} label: {
if selectedClubs.contains(club.code!) {
Image(systemName: "checkmark.circle.fill")
}
}
} label: {
Text(club.clubTitle())
}
}
} header: {
Text("Clubs")
}
Section {
ForEach(TournamentLevel.allCases) { level in
LabeledContent {
Button {
if levels.contains(level) {
levels.remove(level)
} else {
levels.insert(level)
}
} label: {
if levels.contains(level) {
Image(systemName: "checkmark.circle.fill")
}
}
} label: {
Text(level.localizedLabel())
}
}
} header: {
Text("Niveaux")
}
Section {
ForEach(TournamentCategory.allCases) { category in
LabeledContent {
Button {
if categories.contains(category) {
categories.remove(category)
} else {
categories.insert(category)
}
} label: {
if categories.contains(category) {
Image(systemName: "checkmark.circle.fill")
}
}
} label: {
Text(category.localizedLabel())
}
}
} header: {
Text("Catégories")
}
Section {
ForEach(FederalTournamentAge.allCases) { category in
LabeledContent {
Button {
if ageCategories.contains(category) {
ageCategories.remove(category)
} else {
ageCategories.insert(category)
}
} label: {
if ageCategories.contains(category) {
Image(systemName: "checkmark.circle.fill")
}
}
} label: {
Text(category.localizedLabel())
}
}
} header: {
Text("Catégories d'âge")
}
}
.toolbar {
ToolbarItem(placement: .topBarLeading) {
if federalDataViewModel.areFiltersEnabled() {
Button("Tout retirer", role: .cancel) {
federalDataViewModel.removeFilters()
dismiss()
}
}
}
ToolbarItem(placement: .topBarTrailing) {
ButtonValidateView {
federalDataViewModel.levels = levels
federalDataViewModel.categories = categories
federalDataViewModel.ageCategories = ageCategories
federalDataViewModel.selectedClubs = selectedClubs
federalDataViewModel.id = UUID()
dismiss()
}
}
}
.headerProminence(.increased)
.navigationTitle("Filtres")
.navigationBarTitleDisplayMode(.inline)
.toolbarBackground(.visible, for: .navigationBar)
}
}
}
#Preview {
TournamentFilterView(federalDataViewModel: FederalDataViewModel())
}

@ -15,7 +15,7 @@ struct InscriptionInfoView: View {
var selectedTeams : [TeamRegistration] { tournament.selectedSortedTeams() }
var callDateIssue : [TeamRegistration] {
selectedTeams.filter { tournament.isStartDateIsDifferentThanCallDate($0) }
selectedTeams.filter { $0.callDate != nil && tournament.isStartDateIsDifferentThanCallDate($0) }
}
var waitingList : [TeamRegistration] { tournament.waitingListTeams(in: selectedTeams) }

@ -82,7 +82,7 @@ struct TableStructureView: View {
LabeledContent {
StepperView(count: $groupStageAdditionalQualified, minimum: 0, maximum: maxMoreQualified)
} label: {
Text("Qualifiés supplémentaires").foregroundStyle(.secondary).font(.caption)
Text("Qualifiés supplémentaires")
Text(moreQualifiedLabel)
}
.onChange(of: groupStageAdditionalQualified) {

Loading…
Cancel
Save