add tournament Setting view

multistore
Razmig Sarkissian 2 years ago
parent 97a15edc9f
commit c89c212c11
  1. 52
      PadelClub.xcodeproj/project.pbxproj
  2. 4
      PadelClub/Data/Club.swift
  3. 15
      PadelClub/Data/DataStore.swift
  4. 57
      PadelClub/Data/Event.swift
  5. 2
      PadelClub/Data/Migration/ClubV1.swift
  6. 2
      PadelClub/Data/Migration/TournamentV2.swift
  7. 12
      PadelClub/Data/MockData.swift
  8. 254
      PadelClub/Data/Tournament.swift
  9. 5
      PadelClub/Manager/DisplayContext.swift
  10. 104
      PadelClub/Manager/PadelRule.swift
  11. 6
      PadelClub/ViewModel/SearchViewModel.swift
  12. 7
      PadelClub/Views/Club/ClubDetailView.swift
  13. 2
      PadelClub/Views/ClubView.swift
  14. 134
      PadelClub/Views/Event/EventCreationView.swift
  15. 51
      PadelClub/Views/Event/TournamentConfiguratorView.swift
  16. 40
      PadelClub/Views/Navigation/Agenda/ActivityView.swift
  17. 8
      PadelClub/Views/Navigation/Agenda/EmptyActivityView.swift
  18. 4
      PadelClub/Views/Navigation/Organizer/TournamentOrganizerView.swift
  19. 52
      PadelClub/Views/Shared/MatchFormatPickerView.swift
  20. 17
      PadelClub/Views/Shared/MatchTypeSelectionView.swift
  21. 6
      PadelClub/Views/Shared/SelectablePlayerListView.swift
  22. 21
      PadelClub/Views/Tournament/Screen/Components/TournamentDatePickerView.swift
  23. 29
      PadelClub/Views/Tournament/Screen/Components/TournamentDurationManagerView.swift
  24. 28
      PadelClub/Views/Tournament/Screen/Components/TournamentFieldsManagerView.swift
  25. 25
      PadelClub/Views/Tournament/Screen/Components/TournamentFormatSelectionView.swift
  26. 37
      PadelClub/Views/Tournament/Screen/Components/TournamentLevelPickerView.swift
  27. 2
      PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift
  28. 1
      PadelClub/Views/Tournament/Screen/Screen.swift
  29. 74
      PadelClub/Views/Tournament/Screen/TournamentSettingsView.swift
  30. 2
      PadelClub/Views/Tournament/Shared/TournamentCellView.swift
  31. 43
      PadelClub/Views/Tournament/TournamentInitView.swift
  32. 47
      PadelClub/Views/Tournament/TournamentView.swift

@ -69,6 +69,17 @@
FF82CFC92B9132AF00B0CAF2 /* ActivityView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF82CFC82B9132AF00B0CAF2 /* ActivityView.swift */; };
FF8F26382BAD523300650388 /* PadelRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8F26352BAD523300650388 /* PadelRule.swift */; };
FF8F263B2BAD528600650388 /* EventCreationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8F263A2BAD528600650388 /* EventCreationView.swift */; };
FF8F263D2BAD627A00650388 /* TournamentConfiguratorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8F263C2BAD627A00650388 /* TournamentConfiguratorView.swift */; };
FF8F263F2BAD7D5C00650388 /* Event.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8F263E2BAD7D5C00650388 /* Event.swift */; };
FF8F26412BADFC8700650388 /* TournamentInitView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8F26402BADFC8700650388 /* TournamentInitView.swift */; };
FF8F26432BADFE5B00650388 /* TournamentSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8F26422BADFE5B00650388 /* TournamentSettingsView.swift */; };
FF8F26452BAE0A3400650388 /* TournamentDurationManagerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8F26442BAE0A3400650388 /* TournamentDurationManagerView.swift */; };
FF8F26472BAE0ACB00650388 /* TournamentFieldsManagerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8F26462BAE0ACB00650388 /* TournamentFieldsManagerView.swift */; };
FF8F264B2BAE0B4100650388 /* TournamentLevelPickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8F26492BAE0B4100650388 /* TournamentLevelPickerView.swift */; };
FF8F264C2BAE0B4100650388 /* TournamentFormatSelectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8F26482BAE0B4100650388 /* TournamentFormatSelectionView.swift */; };
FF8F264D2BAE0B4100650388 /* TournamentDatePickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8F264A2BAE0B4100650388 /* TournamentDatePickerView.swift */; };
FF8F264F2BAE0B9600650388 /* MatchTypeSelectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8F264E2BAE0B9600650388 /* MatchTypeSelectionView.swift */; };
FF8F26512BAE0BAD00650388 /* MatchFormatPickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8F26502BAE0BAD00650388 /* MatchFormatPickerView.swift */; };
FFC1E1042BAC28C6008D6F59 /* ClubSearchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFC1E1032BAC28C6008D6F59 /* ClubSearchView.swift */; };
FFC1E1082BAC29FC008D6F59 /* LocationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFC1E1072BAC29FC008D6F59 /* LocationManager.swift */; };
FFC1E10A2BAC2A77008D6F59 /* NetworkFederalService.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFC1E1092BAC2A77008D6F59 /* NetworkFederalService.swift */; };
@ -196,6 +207,17 @@
FF82CFC82B9132AF00B0CAF2 /* ActivityView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActivityView.swift; sourceTree = "<group>"; };
FF8F26352BAD523300650388 /* PadelRule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PadelRule.swift; sourceTree = "<group>"; };
FF8F263A2BAD528600650388 /* EventCreationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventCreationView.swift; sourceTree = "<group>"; };
FF8F263C2BAD627A00650388 /* TournamentConfiguratorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TournamentConfiguratorView.swift; sourceTree = "<group>"; };
FF8F263E2BAD7D5C00650388 /* Event.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Event.swift; sourceTree = "<group>"; };
FF8F26402BADFC8700650388 /* TournamentInitView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TournamentInitView.swift; sourceTree = "<group>"; };
FF8F26422BADFE5B00650388 /* TournamentSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TournamentSettingsView.swift; sourceTree = "<group>"; };
FF8F26442BAE0A3400650388 /* TournamentDurationManagerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TournamentDurationManagerView.swift; sourceTree = "<group>"; };
FF8F26462BAE0ACB00650388 /* TournamentFieldsManagerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TournamentFieldsManagerView.swift; sourceTree = "<group>"; };
FF8F26482BAE0B4100650388 /* TournamentFormatSelectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TournamentFormatSelectionView.swift; sourceTree = "<group>"; };
FF8F26492BAE0B4100650388 /* TournamentLevelPickerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TournamentLevelPickerView.swift; sourceTree = "<group>"; };
FF8F264A2BAE0B4100650388 /* TournamentDatePickerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TournamentDatePickerView.swift; sourceTree = "<group>"; };
FF8F264E2BAE0B9600650388 /* MatchTypeSelectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MatchTypeSelectionView.swift; sourceTree = "<group>"; };
FF8F26502BAE0BAD00650388 /* MatchFormatPickerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MatchFormatPickerView.swift; sourceTree = "<group>"; };
FFC1E1032BAC28C6008D6F59 /* ClubSearchView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClubSearchView.swift; sourceTree = "<group>"; };
FFC1E1072BAC29FC008D6F59 /* LocationManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationManager.swift; sourceTree = "<group>"; };
FFC1E1092BAC2A77008D6F59 /* NetworkFederalService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkFederalService.swift; sourceTree = "<group>"; };
@ -326,6 +348,7 @@
C4A47D5D2B6D38EC00ADC637 /* DataStore.swift */,
C4A47D592B6D383C00ADC637 /* Tournament.swift */,
C4A47D622B6D3D6500ADC637 /* Club.swift */,
FF8F263E2BAD7D5C00650388 /* Event.swift */,
FF1DC5522BAB354A00FD8220 /* MockData.swift */,
FF6EC9012B94799200EA7F5A /* Coredata */,
FF6EC9022B9479B900EA7F5A /* Federal */,
@ -423,6 +446,7 @@
isa = PBXGroup;
children = (
FF70916B2B91005400AB08DA /* TournamentView.swift */,
FF8F26402BADFC8700650388 /* TournamentInitView.swift */,
FF3F74F92B91A018004CFE0E /* Screen */,
FF3F74F82B919FB2004CFE0E /* Shared */,
);
@ -444,6 +468,8 @@
FF6EC8FD2B94792300EA7F5A /* Screen.swift */,
FF6EC8FF2B94794700EA7F5A /* PresentationContext.swift */,
FF70916D2B9108C600AB08DA /* InscriptionManagerView.swift */,
FF8F26422BADFE5B00650388 /* TournamentSettingsView.swift */,
FF8F26522BAE0E4E00650388 /* Components */,
);
path = Screen;
sourceTree = "<group>";
@ -487,6 +513,8 @@
FF6EC8FC2B9478C800EA7F5A /* Shared */ = {
isa = PBXGroup;
children = (
FF8F264E2BAE0B9600650388 /* MatchTypeSelectionView.swift */,
FF8F26502BAE0BAD00650388 /* MatchFormatPickerView.swift */,
FF4AB6BC2B9256E10002987F /* SelectablePlayerListView.swift */,
FF4AB6BE2B92577A0002987F /* ImportedPlayerView.swift */,
);
@ -524,10 +552,23 @@
isa = PBXGroup;
children = (
FF8F263A2BAD528600650388 /* EventCreationView.swift */,
FF8F263C2BAD627A00650388 /* TournamentConfiguratorView.swift */,
);
path = Event;
sourceTree = "<group>";
};
FF8F26522BAE0E4E00650388 /* Components */ = {
isa = PBXGroup;
children = (
FF8F26442BAE0A3400650388 /* TournamentDurationManagerView.swift */,
FF8F26462BAE0ACB00650388 /* TournamentFieldsManagerView.swift */,
FF8F264A2BAE0B4100650388 /* TournamentDatePickerView.swift */,
FF8F26482BAE0B4100650388 /* TournamentFormatSelectionView.swift */,
FF8F26492BAE0B4100650388 /* TournamentLevelPickerView.swift */,
);
path = Components;
sourceTree = "<group>";
};
FFD783FB2B91B919000F62A6 /* Agenda */ = {
isa = PBXGroup;
children = (
@ -731,9 +772,12 @@
files = (
C4A47D872B7BA36D00ADC637 /* UserCreationView.swift in Sources */,
FF7091662B90F0B000AB08DA /* TabDestination.swift in Sources */,
FF8F263F2BAD7D5C00650388 /* Event.swift in Sources */,
C4A47D9F2B7D0BCE00ADC637 /* StepperView.swift in Sources */,
FF8F26412BADFC8700650388 /* TournamentInitView.swift in Sources */,
C4A47D8A2B7BBB6500ADC637 /* SubscriptionView.swift in Sources */,
FF1DC5572BAB3AED00FD8220 /* ClubsView.swift in Sources */,
FF8F264F2BAE0B9600650388 /* MatchTypeSelectionView.swift in Sources */,
FF4AB6B52B9248200002987F /* NetworkManager.swift in Sources */,
C4A47DB12B86375E00ADC637 /* MainUserView.swift in Sources */,
FF7091682B90F79F00AB08DA /* TournamentCellView.swift in Sources */,
@ -749,6 +793,7 @@
FF1DC5552BAB36DD00FD8220 /* CreateClubView.swift in Sources */,
FFC1E10A2BAC2A77008D6F59 /* NetworkFederalService.swift in Sources */,
FF7091622B90F04300AB08DA /* TournamentOrganizerView.swift in Sources */,
FF8F264D2BAE0B4100650388 /* TournamentDatePickerView.swift in Sources */,
C4A47D742B72881F00ADC637 /* ClubView.swift in Sources */,
C4A47D902B7BBBEC00ADC637 /* StoreManager.swift in Sources */,
FF4AB6BB2B9256D50002987F /* SearchViewModel.swift in Sources */,
@ -757,10 +802,12 @@
C4A47D5A2B6D383C00ADC637 /* Tournament.swift in Sources */,
C4A47D7B2B73C0F900ADC637 /* TournamentV2.swift in Sources */,
FF3795662B9399AA004EA093 /* Persistence.swift in Sources */,
FF8F264B2BAE0B4100650388 /* TournamentLevelPickerView.swift in Sources */,
C4A47D5E2B6D38EC00ADC637 /* DataStore.swift in Sources */,
FF82CFC52B911F5B00B0CAF2 /* OrganizedTournamentView.swift in Sources */,
FF59FFB32B90EFAC0061EFF9 /* EventListView.swift in Sources */,
C4A47D7D2B73CDC300ADC637 /* ClubV1.swift in Sources */,
FF8F263D2BAD627A00650388 /* TournamentConfiguratorView.swift in Sources */,
FFC1E10C2BAC7FB0008D6F59 /* ClubImportView.swift in Sources */,
FF6EC8FE2B94792300EA7F5A /* Screen.swift in Sources */,
FF3F74FF2B91A2D4004CFE0E /* AgendaDestination.swift in Sources */,
@ -776,16 +823,20 @@
FFC1E1042BAC28C6008D6F59 /* ClubSearchView.swift in Sources */,
FF70916E2B9108C600AB08DA /* InscriptionManagerView.swift in Sources */,
FF82CFC92B9132AF00B0CAF2 /* ActivityView.swift in Sources */,
FF8F26472BAE0ACB00650388 /* TournamentFieldsManagerView.swift in Sources */,
FF1DC55B2BAB80C400FD8220 /* DisplayContext.swift in Sources */,
C425D4032B6D249D002A7B48 /* ContentView.swift in Sources */,
FFD783FF2B91BA42000F62A6 /* PadelClubView.swift in Sources */,
FF8F264C2BAE0B4100650388 /* TournamentFormatSelectionView.swift in Sources */,
C425D4012B6D249D002A7B48 /* PadelClubApp.swift in Sources */,
FF8F26432BADFE5B00650388 /* TournamentSettingsView.swift in Sources */,
FFDDD40C2B93B2BB00C91A49 /* DeferredViewModifier.swift in Sources */,
FFD784042B91C280000F62A6 /* EmptyActivityView.swift in Sources */,
FF3F74F62B919E45004CFE0E /* UmpireView.swift in Sources */,
C4A47D772B73789100ADC637 /* TournamentV1.swift in Sources */,
C4A47DAD2B85FCCD00ADC637 /* User.swift in Sources */,
FFF8ACD22B9238C3008466FA /* FileImportManager.swift in Sources */,
FF8F26452BAE0A3400650388 /* TournamentDurationManagerView.swift in Sources */,
FF1DC5532BAB354A00FD8220 /* MockData.swift in Sources */,
FF8F26382BAD523300650388 /* PadelRule.swift in Sources */,
FFF8ACDB2B923F48008466FA /* Date+Extensions.swift in Sources */,
@ -795,6 +846,7 @@
FFF8ACD62B923960008466FA /* URL+Extensions.swift in Sources */,
C4A47D922B7BBBEC00ADC637 /* StoreItem.swift in Sources */,
FF4AB6BD2B9256E10002987F /* SelectablePlayerListView.swift in Sources */,
FF8F26512BAE0BAD00650388 /* MatchFormatPickerView.swift in Sources */,
C4A47DA62B83948E00ADC637 /* LoginView.swift in Sources */,
FF70916A2B90F95E00AB08DA /* DateBoxView.swift in Sources */,
FFF8ACD42B92392C008466FA /* SourceFileManager.swift in Sources */,

@ -46,7 +46,7 @@ class Club : ModelObject, Storable, Hashable {
}
var tournaments: [Tournament] {
return Store.main.filter { $0.club_id == self.id }
return []
}
override func deleteDependencies() throws {
@ -73,7 +73,7 @@ extension Club {
}
func automaticShortName() -> String {
name.canonicalVersion.replaceCharactersFromSet(characterSet: .whitespacesAndNewlines)
String(name.canonicalVersion.replaceCharactersFromSet(characterSet: .whitespacesAndNewlines).prefix(10))
}
enum AcronymMode: String, CaseIterable {

@ -17,21 +17,13 @@ class DataStore: ObservableObject {
fileprivate(set) var tournaments: StoredCollection<Tournament>
fileprivate(set) var clubs: StoredCollection<Club>
fileprivate(set) var events: StoredCollection<Event>
fileprivate var _userStorage: OptionalStorage<User> = OptionalStorage<User>(fileName: "user.json")
var user: User? {
return self._userStorage.item
}
static let fakeTournaments: [Tournament] = [
Tournament(name: "P100", club_id: "", category: 0, playerCount: 16),
Tournament(name: "P250", club_id: "", category: 0, playerCount: 24),
Tournament(name: "P25", club_id: "", category: 0, playerCount: 4),
Tournament(name: "P1000", club_id: "", category: 0, playerCount: 8),
Tournament(name: "P500", club_id: "", category: 0, playerCount: 48),
]
}
func setUser(_ user: User?) {
self._userStorage.item = user
@ -47,6 +39,7 @@ class DataStore: ObservableObject {
self.clubs = store.registerCollection(synchronized: false)
self.tournaments = store.registerCollection(synchronized: false)
self.events = store.registerCollection(synchronized: false)
NotificationCenter.default.addObserver(self, selector: #selector(collectionWasUpdated), name: NSNotification.Name.CollectionDidLoad, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(collectionWasUpdated), name: NSNotification.Name.CollectionDidChange, object: nil)

@ -0,0 +1,57 @@
//
// Event_v2.swift
// Padel Tournament
//
// Created by razmig on 10/03/2024.
//
import Foundation
import LeStorage
import SwiftUI
@Observable
class Event: ModelObject, Storable {
static func resourceName() -> String { return "events" }
var id: String = Store.randomId()
var club: String?
var creationDate: Date = Date()
var name: String?
var courtCount: Int?
var tenupId: String?
var groupStageFormat: Int?
var roundFormat: Int?
var loserRoundFormat: Int?
internal init(club: String? = nil, name: String? = nil, courtCount: Int? = nil, tenupId: String? = nil, groupStageFormat: Int? = nil, roundFormat: Int? = nil, loserRoundFormat: Int? = nil) {
self.club = club
self.name = name
self.courtCount = courtCount
self.tenupId = tenupId
self.groupStageFormat = groupStageFormat
self.roundFormat = roundFormat
self.loserRoundFormat = loserRoundFormat
}
var tournaments: [Tournament] {
Store.main.filter { $0.event == self.id }
}
override func deleteDependencies() throws {
try Store.main.deleteDependencies(items: self.tournaments)
}
}
extension Event {
enum CodingKeys: String, CodingKey {
case _id = "id"
case _club = "club"
case _creationDate = "creationDate"
case _name = "name"
case _courtCount = "courtCount"
case _tenupId = "tenupId"
case _groupStageFormat = "groupStageFormat"
case _roundFormat = "roundFormat"
case _loserRoundFormat = "loserRoundFormat"
}
}

@ -20,7 +20,7 @@ class ClubV1 : ModelObject, Storable, MigrationSource {
}
var tournaments: [Tournament] {
return Store.main.filter { $0.club_id == self.id }
return []
}
override func deleteDependencies() throws {

@ -30,7 +30,7 @@ class TournamentV2 : ModelObject, Storable, MigrationSource {
typealias Destination = Tournament
func migrate() -> Tournament {
return Tournament(name: self.name, club_id: self.club_id, category: 0, playerCount: 12)
return Tournament.mock()
}
}

@ -5,6 +5,8 @@
// Created by Razmig Sarkissian on 20/03/2024.
//
import Foundation
extension Club {
static func mock() -> Club {
Club(name: "AUC", acronym: "AUC")
@ -14,3 +16,13 @@ extension Club {
Club(name: "", acronym: "")
}
}
extension Tournament {
static func mock() -> Tournament {
Tournament(groupStageSortMode: .snake, teamSorting: .inscriptionDate, federalCategory: .men, federalLevelCategory: .p100, federalAgeCategory: .senior)
}
static func newEmptyInstance() -> Tournament {
Tournament(groupStageSortMode: .snake, teamSorting: .inscriptionDate, federalCategory: .men, federalLevelCategory: .p100, federalAgeCategory: .senior)
}
}

@ -15,27 +15,257 @@ class Tournament : ModelObject, Storable, Hashable {
}
func hash(into hasher: inout Hasher) {
return hasher.combine(id)
hasher.combine(id)
hasher.combine(event)
hasher.combine(creator)
hasher.combine(courtCount)
}
@ObservationIgnored
var undoManager: Int = 0
static func resourceName() -> String { "tournaments" }
var id: String = Store.randomId()
var name: String
var club_id: String
var category: Int
var playerCount: Int
var event: String?
var creator: String?
var name: String?
var startDate: Date
var endDate: Date?
private(set) var creationDate: Date
var isPrivate: Bool
var groupStageFormat: Int?
var roundFormat: Int?
var loserRoundFormat: Int?
var groupStageSortMode: Int
var groupStageCount: Int
var rankSourceDate: Date?
var dayDuration: Int
var teamCount: Int
var teamSorting: Int
var federalCategory: Int
var federalLevelCategory: Int
var federalAgeCategory: Int
var groupStageCourtCount: Int?
var seedCount: Int
var closedRegistrationDate: Date?
var groupStageAdditionalQualified: Int
var courtCount: Int = 2
var prioritizeClubMembers: Bool
var qualifiedPerGroupStage: Int
var teamsPerGroupStage: Int
var entryFee: Double?
internal init(event: String? = nil, creator: String? = nil, name: String? = nil, startDate: Date = Date(), endDate: Date? = nil, creationDate: Date = Date(), isPrivate: Bool = true, groupStageFormat: Int? = nil, roundFormat: Int? = nil, loserRoundFormat: Int? = nil, groupStageSortMode: GroupStageOrderingMode, groupStageCount: Int = 4, rankSourceDate: Date? = nil, dayDuration: Int = 1, teamCount: Int = 24, teamSorting: TeamSortingType, federalCategory: TournamentCategory, federalLevelCategory: TournamentLevel, federalAgeCategory: FederalTournamentAge, groupStageCourtCount: Int? = nil, seedCount: Int = 8, closedRegistrationDate: Date? = nil, groupStageAdditionalQualified: Int = 0, courtCount: Int = 2, prioritizeClubMembers: Bool = false, qualifiedPerGroupStage: Int = 1, teamsPerGroupStage: Int = 4, entryFee: Double? = nil) {
self.event = event
self.creator = creator
self.name = name
self.startDate = startDate
self.endDate = endDate
self.creationDate = creationDate
self.isPrivate = isPrivate
self.groupStageFormat = groupStageFormat
self.roundFormat = roundFormat
self.loserRoundFormat = loserRoundFormat
self.groupStageSortMode = groupStageSortMode.rawValue
self.groupStageCount = groupStageCount
self.rankSourceDate = rankSourceDate
self.dayDuration = dayDuration
self.teamCount = teamCount
self.teamSorting = teamSorting.rawValue
self.federalCategory = federalCategory.rawValue
self.federalLevelCategory = federalLevelCategory.rawValue
self.federalAgeCategory = federalAgeCategory.rawValue
self.groupStageCourtCount = groupStageCourtCount
self.seedCount = seedCount
self.closedRegistrationDate = closedRegistrationDate
self.groupStageAdditionalQualified = groupStageAdditionalQualified
self.courtCount = courtCount
self.prioritizeClubMembers = prioritizeClubMembers
self.qualifiedPerGroupStage = qualifiedPerGroupStage
self.teamsPerGroupStage = teamsPerGroupStage
self.entryFee = entryFee
}
@ObservationIgnored
var navigationPath: [Screen] = []
init(name: String, club_id: String, category: Int, playerCount: Int) {
self.name = name
self.club_id = club_id
self.category = category
self.playerCount = playerCount
var rounds: Int {
4
}
func title(_ displayStyle: DisplayStyle = .wide) -> String {
[tournamentLevel.localizedLabel(displayStyle), tournamentCategory.localizedLabel(displayStyle), name].compactMap({ $0 }).joined(separator: " ")
}
func formattedDate(_ displayStyle: DisplayStyle = .wide) -> String {
switch displayStyle {
case .wide:
startDate.formatted(date: Date.FormatStyle.DateStyle.complete, time: Date.FormatStyle.TimeStyle.omitted)
case .short:
startDate.formatted(date: .numeric, time: .omitted)
}
}
func settingsDescriptionLocalizedLabel() -> String {
[dayDuration.formatted() + " jour\(dayDuration.pluralSuffix)", courtCount.formatted() + " terrain\(courtCount.pluralSuffix)"].joined(separator: ", ")
}
}
extension Tournament {
func isFree() -> Bool {
entryFee == nil || entryFee == 0
}
}
extension Tournament {
var teamSortingType: TeamSortingType {
get {
TeamSortingType(rawValue: teamSorting) ?? tournamentLevel.defaultTeamSortingType
}
set {
teamSorting = newValue.rawValue
}
}
var matchFormat: MatchFormat {
get {
MatchFormat(rawValue: roundFormat ?? 0) ?? .defaultFormatForMatchType(.bracket)
}
set {
roundFormat = newValue.rawValue
}
}
var groupStageMatchFormat: MatchFormat {
get {
MatchFormat(rawValue: groupStageFormat ?? 0) ?? .defaultFormatForMatchType(.groupStage)
}
set {
groupStageFormat = newValue.rawValue
}
}
var loserBracketMatchFormat: MatchFormat {
get {
MatchFormat(rawValue: loserRoundFormat ?? 0) ?? .defaultFormatForMatchType(.loserBracket)
}
set {
loserRoundFormat = newValue.rawValue
}
}
var groupStageOrderingMode: GroupStageOrderingMode {
get {
GroupStageOrderingMode(rawValue: groupStageSortMode) ?? .random
}
set {
groupStageSortMode = newValue.rawValue
}
}
var tournamentCategory: TournamentCategory {
get {
TournamentCategory(rawValue: federalCategory) ?? .men
}
set {
federalCategory = newValue.rawValue
}
}
var club: Club? { return self.findById(self.club_id) }
var tournamentLevel: TournamentLevel {
get {
TournamentLevel(rawValue: federalLevelCategory) ?? .p100
}
set {
federalLevelCategory = newValue.rawValue
teamSortingType = newValue.defaultTeamSortingType
groupStageMatchFormat = groupStageSmartMatchFormat()
loserBracketMatchFormat = loserBracketSmartMatchFormat(1)
matchFormat = roundSmartMatchFormat(1)
}
}
var federalTournamentAge: FederalTournamentAge {
get {
FederalTournamentAge(rawValue: federalAgeCategory) ?? .senior
}
set {
federalAgeCategory = newValue.rawValue
}
}
func loserBracketSmartMatchFormat(_ roundIndex: Int) -> MatchFormat {
let idx = rounds - roundIndex
let format = tournamentLevel.federalFormatForLoserBracketRound(idx)
if loserBracketMatchFormat.rank > format.rank {
return format
} else {
return loserBracketMatchFormat
}
}
func groupStageSmartMatchFormat() -> MatchFormat {
let format = tournamentLevel.federalFormatForGroupStage()
if groupStageMatchFormat.rank > format.rank {
return format
} else {
return groupStageMatchFormat
}
}
func roundSmartMatchFormat(_ roundIndex: Int) -> MatchFormat {
let idx = rounds - roundIndex
let format = tournamentLevel.federalFormatForBracketRound(idx)
if matchFormat.rank > format.rank {
return format
} else {
return matchFormat
}
}
}
extension Tournament {
enum CodingKeys: String, CodingKey {
case _id = "id"
case _event = "event"
case _creator = "creator"
case _name = "name"
case _startDate = "startDate"
case _endDate = "endDate"
case _creationDate = "creationDate"
case _isPrivate = "isPrivate"
case _groupStageFormat = "groupStageFormat"
case _roundFormat = "roundFormat"
case _loserRoundFormat = "loserRoundFormat"
case _groupStageSortMode = "groupStageSortMode"
case _groupStageCount = "groupStageCount"
case _rankSourceDate = "rankSourceDate"
case _dayDuration = "dayDuration"
case _teamCount = "teamCount"
case _teamSorting = "teamSorting"
case _federalCategory = "federalCategory"
case _federalLevelCategory = "federalLevelCategory"
case _federalAgeCategory = "federalAgeCategory"
case _groupStageCourtCount = "groupStageCourtCount"
case _seedCount = "seedCount"
case _closedRegistrationDate = "closedRegistrationDate"
case _groupStageAdditionalQualified = "groupStageAdditionalQualified"
case _courtCount = "courtCount"
case _prioritizeClubMembers = "prioritizeClubMembers"
case _qualifiedPerGroupStage = "qualifiedPerGroupStage"
case _teamsPerGroupStage = "teamsPerGroupStage"
case _entryFee = "entryFee"
}
}
extension Tournament {
func state() -> Tournament.State {
.initial
}
enum State {
case initial
}
}

@ -11,3 +11,8 @@ enum DisplayContext {
case addition
case edition
}
enum DisplayStyle {
case wide
case short
}

@ -12,7 +12,7 @@ enum RankSource: Hashable {
case ligue
case club(assimilation: Bool)
var localizedLabel: String {
func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String {
switch self {
case .national:
return "Classement National"
@ -34,20 +34,20 @@ struct TournamentBuild: Hashable, Codable, Identifiable {
var japLastName: String? = nil
var identifier: String {
level.localizedLabel+":"+category.localizedLabel+":"+age.localizedLabel
level.localizedLabel()+":"+category.localizedLabel()+":"+age.localizedLabel()
}
var computedLabel: String {
if age == .senior { return localizedLabel }
return localizedLabel + " " + localizedAge
if age == .senior { return localizedLabel() }
return localizedLabel() + " " + localizedAge
}
var localizedLabel: String {
level.localizedLabel + category.localizeLabelShort
func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String {
level.localizedLabel() + category.localizedLabel(.short)
}
var localizedTitle: String {
level.localizedLabel + " " + category.localizedLabel
level.localizedLabel() + " " + category.localizedLabel()
}
var localizedAge: String {
@ -58,7 +58,7 @@ struct TournamentBuild: Hashable, Codable, Identifiable {
extension TournamentBuild {
init?(category: String, level: String, age: FederalTournamentAge = .senior) {
guard let levelFound = TournamentLevel.allCases.first(where: { $0.localizedLabel == level }) else { return nil }
guard let levelFound = TournamentLevel.allCases.first(where: { $0.localizedLabel() == level }) else { return nil }
var c = category
if c.hasPrefix("ME") {
@ -82,7 +82,7 @@ enum FederalTournamentType: String, Hashable, Codable, CaseIterable, Identifiabl
case championnatParPaire = "L"
var id: String { self.rawValue }
var localizedLabel: String {
func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String {
switch self {
case .tournoi:
return "Tournois"
@ -116,7 +116,7 @@ enum TournamentDifficulty {
}
}
var localizedLabel: String {
func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String {
switch self {
case .rankS:
return "S"
@ -189,7 +189,7 @@ enum FederalTournamentAge: Int, Hashable, Codable, CaseIterable, Identifiable {
}
}
var localizedLabel: String {
func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String {
switch self {
case .a11_12:
return "11/12 ans"
@ -213,9 +213,9 @@ enum FederalTournamentAge: Int, Hashable, Codable, CaseIterable, Identifiable {
case .senior:
return ""
case .a45, .a55:
return "+" + localizedLabel
return "+" + localizedLabel()
default:
return localizedLabel
return localizedLabel()
}
}
}
@ -347,7 +347,7 @@ enum TournamentLevel: Int, Hashable, Codable, CaseIterable, Identifiable {
}
var defaultEntriesSortingType: EntriesSortingType {
var defaultTeamSortingType: TeamSortingType {
switch self {
case .p25, .p100, .p250:
return .inscriptionDate
@ -375,7 +375,7 @@ enum TournamentLevel: Int, Hashable, Codable, CaseIterable, Identifiable {
}
}
var localizedLabel: String {
func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String {
return String(describing: self).capitalized
}
@ -686,17 +686,6 @@ enum TournamentCategory: Int, Hashable, Codable, CaseIterable, Identifiable {
}
}
var localizeLabelShort: String {
switch self {
case .men:
return "H"
case .women:
return "D"
case .mix:
return "MX"
}
}
var requestLabel: String {
switch self {
case .men:
@ -719,14 +708,14 @@ enum TournamentCategory: Int, Hashable, Codable, CaseIterable, Identifiable {
}
}
var localizedLabel: String {
func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String {
switch self {
case .men:
return "Hommes"
return displayStyle == .wide ? "Hommes" : "H"
case .women:
return "Dames"
return displayStyle == .wide ? "Dames" : "D"
case .mix:
return "Mixte"
return displayStyle == .wide ? "Mixte" : "MX"
}
}
@ -754,13 +743,13 @@ enum TournamentCategory: Int, Hashable, Codable, CaseIterable, Identifiable {
}
enum BracketOrderingMode: Int, Hashable, Codable, CaseIterable, Identifiable {
enum GroupStageOrderingMode: Int, Hashable, Codable, CaseIterable, Identifiable {
case random
case snake
case swiss
var id: Int { self.rawValue }
var localizedLabel: String {
func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String {
switch self {
case .random:
return "Au hasard"
@ -788,7 +777,7 @@ enum TournamentType: Int, Hashable, Codable, CaseIterable, Identifiable {
case doubleBrackets
var id: Int { self.rawValue }
var localizedLabel: String {
func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String {
switch self {
case .classic:
return "Classique"
@ -811,7 +800,7 @@ enum TeamData: Int, Hashable, Codable, CaseIterable {
}
}
var localizedLabel: String {
func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String {
switch self {
case .one:
return "#1"
@ -1218,7 +1207,7 @@ enum Format: Int, Hashable, Codable {
case tiebreakTen
case tiebreakFiveTeen
var localizedLabel: String {
func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String {
switch self {
case .normal:
return "normal"
@ -1261,7 +1250,7 @@ enum ActionType: Int, Identifiable {
self
}
var localizedLabel: String {
func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String {
switch self {
case .fault:
return "Faute"
@ -1294,28 +1283,34 @@ enum ActionType: Int, Identifiable {
enum EventType: Int, CaseIterable, Identifiable {
case approvedTournament
case friendlyTournament
case simulation
case animation
var id: Self {
self
}
var localizedLabel: String {
func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String {
switch self {
case .approvedTournament:
return "Tournoi homologué"
case .friendlyTournament:
return "Tournoi amical"
case .simulation:
return "Simulation"
case .animation:
return "Animation"
}
}
}
enum EntriesSortingType: Int, Identifiable, CaseIterable, Hashable {
enum TeamSortingType: Int, Identifiable, CaseIterable, Hashable {
case rank = 1
case inscriptionDate = 2
var id: Int { rawValue }
var localizedLabel: String {
func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String {
switch self {
case .rank:
return "Rang"
@ -1334,7 +1329,7 @@ enum PlayersCountRange: Int, CaseIterable {
case N28 = 28
case N32 = 32
var localizedLabel: String {
func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String {
switch self {
case .N8:
return "4 à 8"
@ -1372,3 +1367,32 @@ enum RoundLabel {
}
}
enum AnimationType: Int, CaseIterable, Hashable, Identifiable {
case playerAnimation
case upAndDown
case brawl
var id: Int { rawValue }
func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String {
switch self {
case .playerAnimation:
return "Par joueur"
case .upAndDown:
return "Montante / Descandante"
case .brawl:
return "Brawl"
}
}
var descriptionLabel: String {
switch self {
case .playerAnimation:
return "Chaque joueur joue avec quelqu'un de différent à chaque rotation (8 à 12 joueurs)"
case .upAndDown:
return "Les gagnants montent sur le terrain d'à côté, les perdants descendent"
case .brawl:
return "A chaque rotaiton, les gagnants de la rotation pécédente se jouent entre eux"
}
}
}

@ -273,7 +273,7 @@ enum SearchToken: String, CaseIterable, Identifiable {
}
}
var localizedLabel: String {
func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String {
switch self {
case .club:
return "Club"
@ -341,7 +341,7 @@ enum DataSet: Int, CaseIterable, Identifiable {
case favorite
var id: Int { rawValue }
var localizedLabel: String {
func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String {
switch self {
case .national:
return "National"
@ -375,7 +375,7 @@ enum SortOption: Int, CaseIterable, Identifiable {
case points
var id: Int { self.rawValue }
var localizedLabel: String {
func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String {
switch self {
case .name:
return "Nom"

@ -52,10 +52,15 @@ struct ClubDetailView: View {
.focused($focusedField, equals: ._acronym)
.submitLabel(.done)
.multilineTextAlignment(.trailing)
.onSubmit(of: .text) {
if club.acronym.count > 10 {
club.acronym = String(club.acronym.prefix(10))
}
}
}
} label: {
VStack(alignment: .leading, spacing: 0) {
Text("Nom court").foregroundStyle(.secondary).font(.caption)
Text("Nom court (10 caractères max.)").foregroundStyle(.secondary).font(.caption)
Menu {
Section {
ForEach(Club.AcronymMode.allCases, id: \.self) { option in

@ -13,7 +13,7 @@ struct ClubView: View {
var body: some View {
List(club.tournaments) { tournament in
Text(tournament.name)
Text(tournament.title())
}.navigationTitle(club.name)
}
}

@ -8,11 +8,143 @@
import SwiftUI
struct EventCreationView: View {
@Environment(\.dismiss) private var dismiss
@EnvironmentObject var dataStore: DataStore
@State private var eventType: EventType = .approvedTournament
@State private var animationType: AnimationType = .upAndDown
@State private var startingDate: Date = Date()
@State private var duration: Int = 3
@State private var eventName: String = ""
@State var tournaments: [Tournament] = []
var body: some View {
Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/)
NavigationStack {
Form {
// Section {
// Picker(selection: $eventType) {
// ForEach(EventType.allCases) { eventType in
// Text(eventType.localizedLabel())
// }
// } label: {
// Text("Type")
// }
// }
Section {
TextField("Nom de l'événement", text: $eventName)
}
Section {
DatePicker(selection: $startingDate) {
Text(startingDate.formatted(.dateTime.weekday(.wide)).capitalized)
}
if eventType == .approvedTournament {
Stepper(value: $duration, in: 1...3) {
HStack {
Text("Durée")
Spacer()
Text("\(duration) jour" + duration.pluralSuffix)
}
}
}
} header: {
Text("Démarrage")
}
switch eventType {
case .approvedTournament:
approvedTournamentEditorView
case .friendlyTournament:
approvedTournamentEditorView
case .simulation:
approvedTournamentEditorView
case .animation:
animationEditorView
}
}
.toolbar {
ToolbarItem(placement: .cancellationAction) {
Button("Annuler", role: .cancel) {
dismiss()
}
}
ToolbarItem(placement: .confirmationAction) {
Button("Valider") {
if tournaments.count > 1 || eventName.trimmed.isEmpty == false {
let event = Event(name: eventName)
tournaments.forEach { tournament in
tournament.event = event.id
}
try? dataStore.events.addOrUpdate(instance: event)
}
try? dataStore.tournaments.append(contentOfs: tournaments)
dismiss()
}
.clipShape(Capsule())
.buttonStyle(.bordered)
}
}
.navigationTitle("Nouvel événement")
}
}
@ViewBuilder
private var approvedTournamentEditorView: some View {
ForEach(tournaments) { tournament in
Section {
TournamentConfigurationView(tournament: tournament)
} header: {
if tournaments.count > 1 {
HStack {
Spacer()
Button {
tournaments.removeAll(where: { $0 == tournament })
// viewContext.delete(tournament)
} label: {
Text("effacer")
}
.textCase(nil)
}
}
}
}
RowButtonView(title: "Ajouter une \((tournaments.count + 1).ordinalFormatted()) épreuve") {
let tournament = Tournament.newEmptyInstance()
// let tournament = Tournament(context: viewContext)
// tournament.tournamentLevel = TournamentLevel.mostUsed(tournaments: tournaments)
// tournament.tournamentCategory = TournamentCategory.mostUsed(tournaments: tournaments).next
// tournament.federalTournamentAge = FederalTournamentAge.mostUsed(tournaments: tournaments)
//
self.tournaments.append(tournament)
}
}
@ViewBuilder
var animationEditorView: some View {
Section {
Picker(selection: $animationType) {
ForEach(AnimationType.allCases) { animationType in
Text(animationType.localizedLabel()).tag(animationType)
}
} label: {
Text("Type")
}.pickerStyle(.menu)
}
Section {
Text(animationType.descriptionLabel)
}
}
}
#Preview {
EventCreationView()
.environmentObject(DataStore.shared)
}

@ -0,0 +1,51 @@
//
// TournamentConfiguratorView.swift
// PadelClub
//
// Created by Razmig Sarkissian on 22/03/2024.
//
import SwiftUI
struct TournamentConfigurationView: View {
@Bindable var tournament: Tournament
var minimumTeamsCount: Int {
4
}
var maximumTeamsCount: Int {
128
}
@ViewBuilder
var body: some View {
Picker(selection: $tournament.federalCategory, label: Text("Catégorie")) {
ForEach(TournamentCategory.allCases) { type in
Text(type.localizedLabel()).tag(type.rawValue)
}
}
Picker(selection: $tournament.federalLevelCategory, label: Text("Niveau")) {
ForEach(TournamentLevel.allCases) { type in
Text(type.localizedLabel()).tag(type.rawValue)
}
}
Picker(selection: $tournament.federalAgeCategory, label: Text("Limite d'âge")) {
ForEach(FederalTournamentAge.allCases) { type in
Text(type.localizedLabel()).tag(type.rawValue)
}
}
Stepper(value: $tournament.teamCount, in: minimumTeamsCount...maximumTeamsCount) {
HStack {
Text("Équipes souhaitées")
Spacer()
Text(tournament.teamCount.formatted())
}
}
}
}
//
//#Preview {
// TournamentConfiguratorView()
//}

@ -8,18 +8,29 @@
import SwiftUI
struct ActivityView: View {
@EnvironmentObject var dataStore: DataStore
@State private var searchText: String = ""
@State private var agendaDestination: AgendaDestination = .activity
@State private var filterEnabled: Bool = false
@State private var presentToolbar: Bool = false
@State private var newTournament: Tournament?
var runningTournaments: [Tournament] {
dataStore.tournaments.filter({ $0.endDate == nil }).sorted(by: \.startDate)
}
var endedTournaments: [Tournament] {
dataStore.tournaments.filter({ $0.endDate != nil }).sorted(using: SortDescriptor(\.startDate, order: .reverse))
}
var tournaments: [Tournament] {
switch agendaDestination {
case .activity:
[]
runningTournaments
case .history:
DataStore.fakeTournaments
endedTournaments
}
}
@ -58,9 +69,7 @@ struct ActivityView: View {
Text("Aucun événement n'est prévu dans votre agenda.")
} actions: {
RowButtonView(title: "Créer un nouvel évenement") {
let tournament = Tournament(name: "P100", club_id: "", category: 0, playerCount: 24)
try? DataStore.shared.tournaments.append(contentOfs: [tournament])
newTournament = Tournament.newEmptyInstance()
}
RowButtonView(title: "Importer vos tournois Tenup") {
@ -74,6 +83,9 @@ struct ActivityView: View {
.searchable(text: $searchText)
.onAppear { presentToolbar = true }
.onDisappear { presentToolbar = false }
.sheet(item: $newTournament) { tournament in
EventCreationView(tournaments: [tournament])
}
.toolbar {
if presentToolbar {
ToolbarItem(placement: .status) {
@ -94,16 +106,23 @@ struct ActivityView: View {
Button {
filterEnabled.toggle()
} label: {
Label("Vues", systemImage: "line.3.horizontal.decrease.circle")
.symbolVariant(filterEnabled ? .fill : .none)
Image(systemName: "line.3.horizontal.decrease.circle")
.resizable()
.scaledToFit()
.frame(minHeight: 28)
}
.symbolVariant(filterEnabled ? .fill : .none)
}
ToolbarItem(placement: .topBarTrailing) {
Button {
newTournament = Tournament.newEmptyInstance()
} label: {
Label("Ajouter", systemImage: "plus.circle.fill")
Image(systemName: "plus.circle.fill")
.resizable()
.scaledToFit()
.frame(minHeight: 28)
}
}
}
@ -118,4 +137,5 @@ struct ActivityView: View {
#Preview {
ActivityView()
.environmentObject(DataStore.shared)
}

@ -8,7 +8,7 @@
import SwiftUI
struct EmptyActivityView: View {
@State private var presentTournamentCreation: Bool = false
@State private var newTournament: Tournament?
var body: some View {
NavigationStack {
@ -17,7 +17,7 @@ struct EmptyActivityView: View {
Section {
RowButtonView(title: "Créer votre premier événement", action: {
presentTournamentCreation = true
newTournament = Tournament.newEmptyInstance()
})
}
@ -27,8 +27,8 @@ struct EmptyActivityView: View {
})
}
}
.sheet(isPresented: $presentTournamentCreation) {
EventCreationView()
.sheet(item: $newTournament) { tournament in
EventCreationView(tournaments: [tournament])
}
}
}

@ -12,7 +12,7 @@ struct TournamentOrganizerView: View {
var body: some View {
VStack(spacing: 0) {
ForEach(DataStore.fakeTournaments) { tournament in
ForEach(DataStore.shared.tournaments) { tournament in
if tournament.id == selectedTournamentId {
OrganizedTournamentView(tournament: tournament)
}
@ -29,7 +29,7 @@ struct TournamentOrganizerView: View {
HStack {
ScrollView(.horizontal) {
HStack {
ForEach(DataStore.fakeTournaments) { tournament in
ForEach(DataStore.shared.tournaments) { tournament in
TournamentButtonView(tournament: tournament, selectedId: $selectedTournamentId)
}
}

@ -0,0 +1,52 @@
//
// MatchFormatPickerView.swift
// PadelClub
//
// Created by Razmig Sarkissian on 22/03/2024.
//
import SwiftUI
struct MatchFormatPickerView: View {
let headerLabel: String
@Binding var matchFormat: MatchFormat
@State private var isExpanded: Bool = false
var body: some View {
DisclosureGroup(isExpanded: $isExpanded) {
Picker(selection: $matchFormat) {
ForEach(MatchFormat.allCases, id: \.rawValue) { format in
Text(format.computedShortLabel).tag(format)
}
} label: {
}
.pickerStyle(.inline)
.onChange(of: matchFormat) {
isExpanded = false
}
} label: {
descriptionView
}
}
var descriptionView: some View {
VStack(alignment: .leading) {
HStack {
Text(headerLabel).font(.caption)
Spacer()
Text("Durée").font(.caption)
}
HStack {
Text(matchFormat.format)
Spacer()
VStack(alignment: .trailing) {
Text("~" + matchFormat.estimatedDuration.formatted() + " minutes")
Text(matchFormat.breakTime.breakTime.formatted() + " minutes de pause").foregroundStyle(.secondary)
if matchFormat.breakTime.matchCount > 1 {
Text("après \(matchFormat.breakTime.matchCount) match" + matchFormat.breakTime.matchCount.pluralSuffix).foregroundStyle(.secondary)
}
}
}
}
}
}

@ -0,0 +1,17 @@
//
// MatchTypeSelectionView.swift
// PadelClub
//
// Created by Razmig Sarkissian on 22/03/2024.
//
import SwiftUI
struct MatchTypeSelectionView: View {
@Binding var selectedFormat: MatchFormat
let format: String
var body: some View {
MatchFormatPickerView(headerLabel: format, matchFormat: $selectedFormat)
}
}

@ -73,7 +73,7 @@ struct SelectablePlayerListView: View {
// Button {
// searchViewModel.tokens.append(token)
// } label: {
// Label(token.localizedLabel, systemImage: token.icon())
// Label(token.localizedLabel(), systemImage: token.icon())
// }
// }
// })
@ -374,7 +374,7 @@ struct MySearchView: View {
}
searchViewModel.sortOption = option
})) {
Label(option.localizedLabel, systemImage: searchViewModel.sortOption == option ? (searchViewModel.ascending ? "chevron.up" : "chevron.down") : "")
Label(option.localizedLabel(), systemImage: searchViewModel.sortOption == option ? (searchViewModel.ascending ? "chevron.up" : "chevron.down") : "")
}
}
} header: {
@ -400,7 +400,7 @@ struct MySearchView: View {
Text("Assimilés")
}
} label: {
Label(searchViewModel.sortOption.localizedLabel, systemImage: searchViewModel.ascending ? "chevron.up" : "chevron.down")
Label(searchViewModel.sortOption.localizedLabel(), systemImage: searchViewModel.ascending ? "chevron.up" : "chevron.down")
}
}
}

@ -0,0 +1,21 @@
//
// TournamentDatePickerView.swift
// Padel Tournament
//
// Created by Razmig Sarkissian on 05/10/2023.
//
import SwiftUI
struct TournamentDatePickerView: View {
@Environment(Tournament.self) private var tournament: Tournament
var body: some View {
@Bindable var tournament = tournament
DatePicker(selection: $tournament.startDate) {
Text(tournament.startDate.formatted(.dateTime.weekday(.wide)).capitalized)
.font(.headline)
}
.datePickerStyle(.compact)
}
}

@ -0,0 +1,29 @@
//
// TournamentDurationManagerView.swift
// PadelClub
//
// Created by Razmig Sarkissian on 22/03/2024.
//
import SwiftUI
struct TournamentDurationManagerView: View {
@Environment(Tournament.self) private var tournament: Tournament
var body: some View {
@Bindable var tournament = tournament
Stepper(value: $tournament.dayDuration, in: 1...3) {
LabeledContent {
Text("\(tournament.dayDuration) jour" + tournament.dayDuration.pluralSuffix)
} label: {
Text("Durée")
}
}
}
}
#Preview {
TournamentDurationManagerView()
.environment(Tournament.mock())
}

@ -0,0 +1,28 @@
//
// TournamentFieldsManagerView.swift
// PadelClub
//
// Created by Razmig Sarkissian on 22/03/2024.
//
import SwiftUI
struct TournamentFieldsManagerView: View {
@Environment(Tournament.self) private var tournament: Tournament
var body: some View {
@Bindable var tournament = tournament
Stepper(value: $tournament.courtCount, in: 1...Int.max) {
LabeledContent {
Text(tournament.courtCount.formatted())
} label: {
Text("Nombre de terrains")
}
}
}}
#Preview {
TournamentFieldsManagerView()
.environment(Tournament.mock())
}

@ -0,0 +1,25 @@
//
// TournamentFormatSelectionView.swift
// Padel Tournament
//
// Created by Razmig Sarkissian on 05/10/2023.
//
import SwiftUI
struct TournamentFormatSelectionView: View {
@Environment(Tournament.self) private var tournament: Tournament
@ViewBuilder
var body: some View {
@Bindable var tournament = tournament
Section {
MatchTypeSelectionView(selectedFormat: $tournament.groupStageMatchFormat, format: "Poule")
MatchTypeSelectionView(selectedFormat: $tournament.matchFormat, format: "Tableau")
MatchTypeSelectionView(selectedFormat: $tournament.loserBracketMatchFormat, format: "Match de classement")
} footer: {
Text("À minima, les règles fédérales seront toujours prises en compte par défaut.")
}
}
}

@ -0,0 +1,37 @@
//
// TournamentLevelPickerView.swift
// Padel Tournament
//
// Created by Razmig Sarkissian on 05/10/2023.
//
import SwiftUI
struct TournamentLevelPickerView: View {
@Environment(Tournament.self) private var tournament: Tournament
var body: some View {
@Bindable var tournament = tournament
Picker(selection: $tournament.tournamentCategory, label: Text("Catégorie")) {
ForEach(TournamentCategory.allCases) { type in
Text(type.localizedLabel()).tag(type)
}
}
Picker(selection: $tournament.tournamentLevel, label: Text("Niveau")) {
ForEach(TournamentLevel.allCases) { type in
Text(type.localizedLabel()).tag(type)
}
}
Picker(selection: $tournament.federalTournamentAge, label: Text("Limite d'âge")) {
ForEach(FederalTournamentAge.allCases) { type in
Text(type.localizedLabel()).tag(type)
}
}
Picker(selection: $tournament.groupStageOrderingMode, label: Text("Répartition en poule")) {
ForEach(GroupStageOrderingMode.allCases) { type in
Text(type.localizedLabel()).tag(type)
}
}
}
}

@ -12,7 +12,7 @@ struct InscriptionManagerView: View {
var body: some View {
List {
Text(tournament.playerCount.formatted())
Text("24")
}
.navigationTitle("Inscriptions")
}

@ -10,4 +10,5 @@ import Foundation
enum Screen: String, Codable {
case inscription
case groupStage
case settings
}

@ -0,0 +1,74 @@
//
// TournamentSettingsView.swift
// PadelClub
//
// Created by Razmig Sarkissian on 22/03/2024.
//
import SwiftUI
struct TournamentSettingsView: View {
@Environment(Tournament.self) private var tournament: Tournament
@EnvironmentObject var dataStore: DataStore
@State private var tournamentName: String = ""
var body: some View {
@Bindable var tournament = tournament
Form {
LabeledContent {
TextField(tournament.isFree() ? "Gratuite" : "Inscription", value: $tournament.entryFee, format: .currency(code: Locale.current.currency?.identifier ?? "EUR"))
.keyboardType(.decimalPad)
.fixedSize()
.multilineTextAlignment(.trailing)
} label: {
Text("Inscription")
}
LabeledContent {
TextField("Nom", text: $tournamentName)
.multilineTextAlignment(.trailing)
.fixedSize()
.keyboardType(.alphabet)
.autocorrectionDisabled()
.onSubmit {
if tournamentName.trimmed.isEmpty {
tournament.name = nil
} else {
tournament.name = tournamentName
}
}
} label: {
Text("Nom du tournoi")
}
TournamentDurationManagerView()
TournamentFieldsManagerView()
TournamentDatePickerView()
TournamentFormatSelectionView()
TournamentLevelPickerView()
}
.navigationTitle("Réglages")
.toolbarBackground(.visible, for: .navigationBar)
.onAppear {
tournamentName = tournament.name ?? ""
tournament.undoManager = tournament.hashValue
}
.onDisappear {
if tournament.undoManager != tournament.hashValue {
try? dataStore.tournaments.addOrUpdate(instance: tournament)
}
}
}
}
#Preview {
Group {
TournamentSettingsView()
.environmentObject(DataStore.shared)
.environment(Tournament.mock())
}
}

@ -21,7 +21,7 @@ struct TournamentCellView: View {
VStack(alignment: .leading, spacing: -2) {
Text("Homme")
.font(.caption2)
Text(tournament.name)
Text(tournament.title())
.font(.title)
Text("Senior")
.font(.caption2)

@ -0,0 +1,43 @@
//
// TournamentInitView.swift
// PadelClub
//
// Created by Razmig Sarkissian on 22/03/2024.
//
import SwiftUI
struct TournamentInitView: View {
@Environment(Tournament.self) private var tournament: Tournament
@ViewBuilder
var body: some View {
Section {
NavigationLink(value: Screen.settings) {
LabeledContent {
Text(tournament.settingsDescriptionLocalizedLabel())
} label: {
Label("Réglages", systemImage: "slider.horizontal.3")
}
}
} footer: {
Text("La date, la catégorie, le niveau, le nombre de terrain, les formats, etc.")
}
//
// Section {
// NavigationLink {
// TableStructureView(tournament: tournament)
// } label: {
// Label("Structure", systemImage: "hammer")
// .badge(tournament.structureDescriptionLocalizedLabel)
// }
// } footer: {
// Text("Nombre d'équipes, de poules, de qualifiés sortant, etc.")
// }
}
}
#Preview {
TournamentInitView()
.environment(Tournament.mock())
}

@ -13,22 +13,41 @@ struct TournamentView: View {
var body: some View {
List {
InscriptionManagerRowView(tournament: tournament)
NavigationLink(value: Screen.groupStage) {
Text("Poules")
.badge(2)
switch tournament.state() {
case .initial:
TournamentInitView()
}
// InscriptionManagerRowView(tournament: tournament)
// NavigationLink(value: Screen.groupStage) {
// Text("Poules")
// .badge(2)
// }
}
.toolbarBackground(.visible, for: .navigationBar)
.navigationDestination(for: Screen.self, destination: { screen in
switch screen {
case .inscription:
InscriptionManagerView(tournament: tournament)
case .groupStage:
Text("Poules \(screen.rawValue)")
Group {
switch screen {
case .settings:
TournamentSettingsView()
case .inscription:
InscriptionManagerView(tournament: tournament)
case .groupStage:
Text("Poules \(screen.rawValue)")
}
}
.environment(tournament)
})
.navigationTitle(tournament.name)
.environment(tournament)
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .principal) {
VStack {
Text(tournament.title()).font(.headline)
Text(tournament.formattedDate())
.font(.subheadline).foregroundStyle(.secondary)
}
}
if presentationContext == .agenda {
ToolbarItem(placement: .topBarTrailing) {
Menu {
@ -51,8 +70,14 @@ struct TournamentView: View {
var body: some View {
NavigationLink(value: Screen.inscription) {
Text("Inscriptions")
.badge(tournament.playerCount)
.badge(24)
}
}
}
}
#Preview {
NavigationStack {
TournamentView(tournament: .mock(), presentationContext: .agenda)
}
}

Loading…
Cancel
Save