improve online registration

paca_championship
Raz 12 months ago
parent 420b5e9bc2
commit 9a8f9f8949
  1. 37
      PadelClub/Data/PlayerRegistration.swift
  2. 40
      PadelClub/Data/TeamRegistration.swift
  3. 40
      PadelClub/Data/Tournament.swift
  4. 4
      PadelClub/ViewModel/SetDescriptor.swift
  5. 28
      PadelClub/Views/Match/Components/PlayerBlockView.swift
  6. 28
      PadelClub/Views/Tournament/Screen/Components/TournamentGeneralSettingsView.swift
  7. 22
      PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift
  8. 53
      PadelClub/Views/Tournament/Screen/RegisrationSetupView.swift
  9. 16
      PadelClub/Views/Tournament/Screen/TournamentSettingsView.swift
  10. 21
      PadelClubTests/ServerDataTests.swift

@ -55,7 +55,7 @@ final class PlayerRegistration: ModelObject, Storable {
}
init(teamRegistration: String? = nil, firstName: String, lastName: String, licenceId: String? = nil, rank: Int? = nil, paymentType: PlayerPaymentType? = nil, sex: PlayerSexType? = nil, tournamentPlayed: Int? = nil, points: Double? = nil, clubName: String? = nil, ligueName: String? = nil, assimilation: String? = nil, phoneNumber: String? = nil, email: String? = nil, birthdate: String? = nil, computedRank: Int = 0, source: PlayerDataSource? = nil, hasArrived: Bool = false) {
init(teamRegistration: String? = nil, firstName: String, lastName: String, licenceId: String? = nil, rank: Int? = nil, paymentType: PlayerPaymentType? = nil, sex: PlayerSexType? = nil, tournamentPlayed: Int? = nil, points: Double? = nil, clubName: String? = nil, ligueName: String? = nil, assimilation: String? = nil, phoneNumber: String? = nil, email: String? = nil, birthdate: String? = nil, computedRank: Int = 0, source: PlayerDataSource? = nil, hasArrived: Bool = false, coach: Bool = false, captain: Bool = false) {
self.teamRegistration = teamRegistration
self.firstName = firstName
self.lastName = lastName
@ -74,6 +74,8 @@ final class PlayerRegistration: ModelObject, Storable {
self.computedRank = computedRank
self.source = source
self.hasArrived = hasArrived
self.captain = captain
self.coach = coach
}
internal init(importedPlayer: ImportedPlayer) {
@ -376,9 +378,40 @@ final class PlayerRegistration: ModelObject, Storable {
case _computedRank = "computedRank"
case _source = "source"
case _hasArrived = "hasArrived"
case _coach = "coach"
case _captain = "captain"
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
// Non-optional properties
id = try container.decodeIfPresent(String.self, forKey: ._id) ?? Store.randomId()
firstName = try container.decode(String.self, forKey: ._firstName)
lastName = try container.decode(String.self, forKey: ._lastName)
computedRank = try container.decodeIfPresent(Int.self, forKey: ._computedRank) ?? 0
hasArrived = try container.decodeIfPresent(Bool.self, forKey: ._hasArrived) ?? false
coach = try container.decodeIfPresent(Bool.self, forKey: ._coach) ?? false
captain = try container.decodeIfPresent(Bool.self, forKey: ._captain) ?? false
// Optional properties
teamRegistration = try container.decodeIfPresent(String.self, forKey: ._teamRegistration)
licenceId = try container.decodeIfPresent(String.self, forKey: ._licenceId)
rank = try container.decodeIfPresent(Int.self, forKey: ._rank)
paymentType = try container.decodeIfPresent(PlayerPaymentType.self, forKey: ._paymentType)
sex = try container.decodeIfPresent(PlayerSexType.self, forKey: ._sex)
tournamentPlayed = try container.decodeIfPresent(Int.self, forKey: ._tournamentPlayed)
points = try container.decodeIfPresent(Double.self, forKey: ._points)
clubName = try container.decodeIfPresent(String.self, forKey: ._clubName)
ligueName = try container.decodeIfPresent(String.self, forKey: ._ligueName)
assimilation = try container.decodeIfPresent(String.self, forKey: ._assimilation)
phoneNumber = try container.decodeIfPresent(String.self, forKey: ._phoneNumber)
email = try container.decodeIfPresent(String.self, forKey: ._email)
birthdate = try container.decodeIfPresent(String.self, forKey: ._birthdate)
source = try container.decodeIfPresent(PlayerDataSource.self, forKey: ._source)
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
@ -402,6 +435,8 @@ final class PlayerRegistration: ModelObject, Storable {
try container.encode(computedRank, forKey: ._computedRank)
try container.encode(source, forKey: ._source)
try container.encode(hasArrived, forKey: ._hasArrived)
try container.encode(captain, forKey: ._captain)
try container.encode(coach, forKey: ._coach)
}
enum PlayerDataSource: Int, Codable {

@ -54,7 +54,7 @@ final class TeamRegistration: ModelObject, Storable {
walkOut || unregistered
}
init(tournament: String, groupStage: String? = nil, registrationDate: Date? = nil, callDate: Date? = nil, bracketPosition: Int? = nil, groupStagePosition: Int? = nil, comment: String? = nil, source: String? = nil, sourceValue: String? = nil, logo: String? = nil, name: String? = nil, walkOut: Bool = false, wildCardBracket: Bool = false, wildCardGroupStage: Bool = false, weight: Int = 0, lockedWeight: Int? = nil, confirmationDate: Date? = nil, qualified: Bool = false) {
init(tournament: String, groupStage: String? = nil, registrationDate: Date? = nil, callDate: Date? = nil, bracketPosition: Int? = nil, groupStagePosition: Int? = nil, comment: String? = nil, source: String? = nil, sourceValue: String? = nil, logo: String? = nil, name: String? = nil, walkOut: Bool = false, wildCardBracket: Bool = false, wildCardGroupStage: Bool = false, weight: Int = 0, lockedWeight: Int? = nil, confirmationDate: Date? = nil, qualified: Bool = false, finalRanking: Int? = nil, pointsEarned: Int? = nil, unregistered: Bool = false, unregistrationDate: Date? = nil) {
self.tournament = tournament
self.groupStage = groupStage
self.registrationDate = registrationDate ?? Date()
@ -73,6 +73,10 @@ final class TeamRegistration: ModelObject, Storable {
self.lockedWeight = lockedWeight
self.confirmationDate = confirmationDate
self.qualified = qualified
self.finalRanking = finalRanking
self.pointsEarned = pointsEarned
self.unregistered = unregistered
self.unregistrationDate = unregistrationDate
}
var tournamentStore: TournamentStore {
@ -636,6 +640,38 @@ final class TeamRegistration: ModelObject, Storable {
case _qualified = "qualified"
case _finalRanking = "finalRanking"
case _pointsEarned = "pointsEarned"
case _unregistered = "unregistered"
case _unregistrationDate = "unregistrationDate"
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
// Non-optional properties
id = try container.decodeIfPresent(String.self, forKey: ._id) ?? Store.randomId()
tournament = try container.decode(String.self, forKey: ._tournament)
walkOut = try container.decodeIfPresent(Bool.self, forKey: ._walkOut) ?? false
wildCardBracket = try container.decodeIfPresent(Bool.self, forKey: ._wildCardBracket) ?? false
wildCardGroupStage = try container.decodeIfPresent(Bool.self, forKey: ._wildCardGroupStage) ?? false
weight = try container.decodeIfPresent(Int.self, forKey: ._weight) ?? 0
qualified = try container.decodeIfPresent(Bool.self, forKey: ._qualified) ?? false
unregistered = try container.decodeIfPresent(Bool.self, forKey: ._unregistered) ?? false
// Optional properties
groupStage = try container.decodeIfPresent(String.self, forKey: ._groupStage)
registrationDate = try container.decodeIfPresent(Date.self, forKey: ._registrationDate)
callDate = try container.decodeIfPresent(Date.self, forKey: ._callDate)
bracketPosition = try container.decodeIfPresent(Int.self, forKey: ._bracketPosition)
groupStagePosition = try container.decodeIfPresent(Int.self, forKey: ._groupStagePosition)
comment = try container.decodeIfPresent(String.self, forKey: ._comment)
source = try container.decodeIfPresent(String.self, forKey: ._source)
sourceValue = try container.decodeIfPresent(String.self, forKey: ._sourceValue)
logo = try container.decodeIfPresent(String.self, forKey: ._logo)
name = try container.decodeIfPresent(String.self, forKey: ._name)
lockedWeight = try container.decodeIfPresent(Int.self, forKey: ._lockedWeight)
confirmationDate = try container.decodeIfPresent(Date.self, forKey: ._confirmationDate)
finalRanking = try container.decodeIfPresent(Int.self, forKey: ._finalRanking)
pointsEarned = try container.decodeIfPresent(Int.self, forKey: ._pointsEarned)
unregistrationDate = try container.decodeIfPresent(Date.self, forKey: ._unregistrationDate)
}
func encode(to encoder: Encoder) throws {
@ -662,6 +698,8 @@ final class TeamRegistration: ModelObject, Storable {
try container.encode(qualified, forKey: ._qualified)
try container.encode(finalRanking, forKey: ._finalRanking)
try container.encode(pointsEarned, forKey: ._pointsEarned)
try container.encode(unregistered, forKey: ._unregistered)
try container.encode(unregistrationDate, forKey: ._unregistrationDate)
}
func insertOnServer() {

@ -65,7 +65,13 @@ final class Tournament : ModelObject, Storable {
var openingRegistrationDate: Date? = nil
var targetTeamCount: Int? = nil
var waitingListLimit: Int? = nil
var accountIsRequired: Bool = true
var licenseIsRequired: Bool = true
var minimumPlayerPerTeam: Int = 2
var maximumPlayerPerTeam: Int = 2
var information: String? = nil
var displayEntryFeeInformation: Bool = false
@ObservationIgnored
var navigationPath: [Screen] = []
@ -116,10 +122,16 @@ final class Tournament : ModelObject, Storable {
case _loserBracketMode = "loserBracketMode"
case _initialSeedRound = "initialSeedRound"
case _initialSeedCount = "initialSeedCount"
case _accountIsRequired = "account_is_required"
case _licenseIsRequired = "license_is_required"
case _minimumPlayerPerTeam = "minimum_player_per_team"
case _maximumPlayerPerTeam = "maximum_player_per_team"
case _information = "information"
case _displayEntryFeeInformation = "displayEntryFeeInformation"
}
internal init(event: String? = nil, name: String? = nil, startDate: Date = Date(), endDate: Date? = nil, creationDate: Date = Date(), isPrivate: Bool = false, groupStageFormat: MatchFormat? = nil, roundFormat: MatchFormat? = nil, loserRoundFormat: MatchFormat? = nil, groupStageSortMode: GroupStageOrderingMode, groupStageCount: Int = 4, rankSourceDate: Date? = nil, dayDuration: Int = 1, teamCount: Int = 24, teamSorting: TeamSortingType? = nil, federalCategory: TournamentCategory, federalLevelCategory: TournamentLevel, federalAgeCategory: FederalTournamentAge, closedRegistrationDate: Date? = nil, groupStageAdditionalQualified: Int = 0, courtCount: Int = 2, prioritizeClubMembers: Bool = false, qualifiedPerGroupStage: Int = 1, teamsPerGroupStage: Int = 4, entryFee: Double? = nil, additionalEstimationDuration: Int = 0, isDeleted: Bool = false, publishTeams: Bool = false, publishSummons: Bool = false, publishGroupStages: Bool = false, publishBrackets: Bool = false, shouldVerifyBracket: Bool = false, shouldVerifyGroupStage: Bool = false, hideTeamsWeight: Bool = false, publishTournament: Bool = false, hidePointsEarned: Bool = false, publishRankings: Bool = false, loserBracketMode: LoserBracketMode = .automatic, initialSeedRound: Int = 0, initialSeedCount: Int = 0) {
internal init(event: String? = nil, name: String? = nil, startDate: Date = Date(), endDate: Date? = nil, creationDate: Date = Date(), isPrivate: Bool = false, groupStageFormat: MatchFormat? = nil, roundFormat: MatchFormat? = nil, loserRoundFormat: MatchFormat? = nil, groupStageSortMode: GroupStageOrderingMode, groupStageCount: Int = 4, rankSourceDate: Date? = nil, dayDuration: Int = 1, teamCount: Int = 24, teamSorting: TeamSortingType? = nil, federalCategory: TournamentCategory, federalLevelCategory: TournamentLevel, federalAgeCategory: FederalTournamentAge, closedRegistrationDate: Date? = nil, groupStageAdditionalQualified: Int = 0, courtCount: Int = 2, prioritizeClubMembers: Bool = false, qualifiedPerGroupStage: Int = 1, teamsPerGroupStage: Int = 4, entryFee: Double? = nil, additionalEstimationDuration: Int = 0, isDeleted: Bool = false, publishTeams: Bool = false, publishSummons: Bool = false, publishGroupStages: Bool = false, publishBrackets: Bool = false, shouldVerifyBracket: Bool = false, shouldVerifyGroupStage: Bool = false, hideTeamsWeight: Bool = false, publishTournament: Bool = false, hidePointsEarned: Bool = false, publishRankings: Bool = false, loserBracketMode: LoserBracketMode = .automatic, initialSeedRound: Int = 0, initialSeedCount: Int = 0, accountIsRequired: Bool = true, licenseIsRequired: Bool = true, minimumPlayerPerTeam: Int = 2, maximumPlayerPerTeam: Int = 2, information: String? = nil, displayEntryFeeInformation: Bool = false) {
self.event = event
self.name = name
self.startDate = startDate
@ -173,6 +185,13 @@ final class Tournament : ModelObject, Storable {
self.loserBracketMode = loserBracketMode
self.initialSeedRound = initialSeedRound
self.initialSeedCount = initialSeedCount
self.accountIsRequired = accountIsRequired
self.licenseIsRequired = licenseIsRequired
self.minimumPlayerPerTeam = minimumPlayerPerTeam
self.maximumPlayerPerTeam = maximumPlayerPerTeam
self.information = information
self.displayEntryFeeInformation = displayEntryFeeInformation
}
@ -221,6 +240,15 @@ final class Tournament : ModelObject, Storable {
loserBracketMode = try container.decodeIfPresent(LoserBracketMode.self, forKey: ._loserBracketMode) ?? .automatic
initialSeedRound = try container.decodeIfPresent(Int.self, forKey: ._initialSeedRound) ?? 0
initialSeedCount = try container.decodeIfPresent(Int.self, forKey: ._initialSeedCount) ?? 0
accountIsRequired = try container.decodeIfPresent(Bool.self, forKey: ._accountIsRequired) ?? true
licenseIsRequired = try container.decodeIfPresent(Bool.self, forKey: ._licenseIsRequired) ?? true
minimumPlayerPerTeam = try container.decodeIfPresent(Int.self, forKey: ._minimumPlayerPerTeam) ?? 2
maximumPlayerPerTeam = try container.decodeIfPresent(Int.self, forKey: ._maximumPlayerPerTeam) ?? 2
information = try container.decodeIfPresent(String.self, forKey: ._information)
displayEntryFeeInformation = try container.decodeIfPresent(Bool.self, forKey: ._displayEntryFeeInformation) ?? false
}
fileprivate static let _numberFormatter: NumberFormatter = NumberFormatter()
@ -309,6 +337,12 @@ final class Tournament : ModelObject, Storable {
try container.encode(loserBracketMode, forKey: ._loserBracketMode)
try container.encode(initialSeedRound, forKey: ._initialSeedRound)
try container.encode(initialSeedCount, forKey: ._initialSeedCount)
try container.encode(accountIsRequired, forKey: ._accountIsRequired)
try container.encode(licenseIsRequired, forKey: ._licenseIsRequired)
try container.encode(minimumPlayerPerTeam, forKey: ._minimumPlayerPerTeam)
try container.encode(maximumPlayerPerTeam, forKey: ._maximumPlayerPerTeam)
try container.encode(information, forKey: ._information)
try container.encode(displayEntryFeeInformation, forKey: ._displayEntryFeeInformation)
}
fileprivate func _encodePayment(container: inout KeyedEncodingContainer<CodingKeys>) throws {
@ -1019,7 +1053,7 @@ defer {
//todo
func significantPlayerCount() -> Int {
return 2
return minimumPlayerPerTeam
}
func inadequatePlayers(in players: [PlayerRegistration]) -> [PlayerRegistration] {

@ -47,12 +47,16 @@ struct SetDescriptor: Identifiable, Equatable {
if let valueTeamOne {
if let tieBreakValueTeamOne {
return "\(valueTeamOne)-\(tieBreakValueTeamOne)"
} else {
return "\(valueTeamOne)"
}
}
case .two:
if let valueTeamTwo {
if let tieBreakValueTeamTwo {
return "\(valueTeamTwo)-\(tieBreakValueTeamTwo)"
} else {
return "\(valueTeamTwo)"
}
}
}

@ -127,12 +127,28 @@ struct PlayerBlockView: View {
} else {
Divider().frame(width: width).overlay(Color(white: 0.9))
}
Text(string)
.font(.title3)
.frame(maxWidth: 20)
.scaledToFill()
.minimumScaleFactor(0.5)
.lineLimit(1)
let parts = string.components(separatedBy: "-")
if parts.count == 2, let mainScore = parts.first, let supScore = parts.last {
HStack(spacing: 0) {
Text(mainScore)
.font(.title3)
.frame(maxWidth: 20)
.scaledToFill()
.minimumScaleFactor(0.5)
.lineLimit(1)
Text(supScore)
.font(.caption2)
.baselineOffset(10)
}
} else {
Text(string)
.font(.title3)
.frame(maxWidth: 20)
.scaledToFill()
.minimumScaleFactor(0.5)
.lineLimit(1)
}
}
}
} else if let team {

@ -13,6 +13,7 @@ struct TournamentGeneralSettingsView: View {
@Bindable var tournament: Tournament
@State private var tournamentName: String = ""
@State private var tournamentInformation: String = ""
@State private var entryFee: Double? = nil
@State private var confirmationRequired: Bool = false
@State private var presentConfirmation: Bool = false
@ -24,6 +25,7 @@ struct TournamentGeneralSettingsView: View {
self.tournament = tournament
_loserBracketMode = .init(wrappedValue: tournament.loserBracketMode)
_tournamentName = State(wrappedValue: tournament.name ?? "")
_tournamentInformation = State(wrappedValue: tournament.information ?? "")
_entryFee = State(wrappedValue: tournament.entryFee)
}
@ -41,6 +43,14 @@ struct TournamentGeneralSettingsView: View {
Text("Nom du tournoi")
}
Section {
TextEditor(text: $tournamentInformation)
.keyboardType(.alphabet)
.focused($focusedField, equals: ._information)
} header: {
Text("Description du tournoi")
}
Section {
TournamentDatePickerView()
TournamentDurationManagerView()
@ -53,6 +63,12 @@ struct TournamentGeneralSettingsView: View {
} label: {
Text("Inscription")
}
if tournament.isPrivate == false {
Toggle(isOn: $tournament.displayEntryFeeInformation) {
Text("Afficher sur la page d'infos")
}
}
} footer: {
Text("Si vous souhaitez que Padel Club vous aide à suivre les encaissements, indiquer un prix d'inscription. Sinon Padel Club vous aidera à suivre simplement l'arrivée et la présence des joueurs.")
}
@ -151,6 +167,13 @@ struct TournamentGeneralSettingsView: View {
} else {
tournament.name = tournamentName
}
} else if focusedField == ._information {
let tournamentInformation = tournamentInformation.prefixTrimmed(4000)
if tournamentInformation.isEmpty {
tournament.information = nil
} else {
tournament.information = tournamentInformation
}
} else if focusedField == ._entryFee {
tournament.entryFee = entryFee
}
@ -167,7 +190,10 @@ struct TournamentGeneralSettingsView: View {
.onChange(of: tournament.entryFee) {
_save()
}
.onChange(of: tournament.name) {
.onChange(of: [tournament.name, tournament.information]) {
_save()
}
.onChange(of: tournament.displayEntryFeeInformation) {
_save()
}
.onChange(of: tournament.dayDuration) {

@ -72,6 +72,7 @@ struct InscriptionManagerView: View {
enum FilterMode: Int, Identifiable, CaseIterable {
var id: Int { self.rawValue }
case all
case registeredLocally
case registeredOnline
case walkOut
case waiting
@ -92,6 +93,8 @@ struct InscriptionManagerView: View {
return "Vous n'avez encore aucune équipe inscrite."
case .registeredOnline:
return "Aucune équipe inscrite en ligne."
case .registeredLocally:
return "Aucune équipe inscrite par vous-même."
case .walkOut:
return "Vous n'avez aucune équipe forfait."
case .waiting:
@ -115,6 +118,8 @@ struct InscriptionManagerView: View {
return "Aucune wildcard en poule"
case .all:
return "Aucune équipe inscrite"
case .registeredLocally:
return "Aucune équipe inscrite par vous-même"
case .registeredOnline:
return "Aucune équipe inscrite en ligne"
case .walkOut:
@ -140,6 +145,8 @@ struct InscriptionManagerView: View {
return displayStyle == .wide ? "Wildcard Poule" : "wc poule"
case .all:
return displayStyle == .wide ? "Équipes inscrites" : "inscris"
case .registeredLocally:
return displayStyle == .wide ? "Inscrites par vous-même" : "par vous-même"
case .registeredOnline:
return displayStyle == .wide ? "Inscrites en ligne" : "en ligne"
case .bracket:
@ -246,10 +253,12 @@ struct InscriptionManagerView: View {
}
.refreshable {
do {
self.tournament.tournamentStore.teamRegistrations.reset()
try await self.tournament.tournamentStore.teamRegistrations.loadDataFromServerIfAllowed()
self.tournament.tournamentStore.playerRegistrations.reset()
try await self.tournament.tournamentStore.playerRegistrations.loadDataFromServerIfAllowed()
self.tournament.tournamentStore.teamRegistrations.reset()
try await self.tournament.tournamentStore.teamRegistrations.loadDataFromServerIfAllowed()
_setHash()
} catch {
Logger.error(error)
}
@ -510,6 +519,12 @@ struct InscriptionManagerView: View {
teams = teams.filter({ $0.inGroupStage() })
case .notImported:
teams = teams.filter({ $0.isImported() == false })
case .unregistered:
teams = teams.filter({ $0.hasUnregistered() == true })
case .registeredLocally:
teams = teams.filter({ $0.hasRegisteredOnline() == false })
case .registeredOnline:
teams = teams.filter({ $0.hasRegisteredOnline() == true })
default:
break
}
@ -698,6 +713,9 @@ struct InscriptionManagerView: View {
case .unregistered:
let unregistered: Int = max(0, sortedTeams.filter({ $0.hasUnregistered() }).count)
return unregistered.formatted()
case .registeredLocally:
let registeredLocally: Int = max(0, sortedTeams.filter({ $0.hasRegisteredOnline() == false }).count)
return registeredLocally.formatted()
case .registeredOnline:
let registeredOnline: Int = max(0, sortedTeams.filter({ $0.hasRegisteredOnline() }).count)
return registeredOnline.formatted()

@ -20,6 +20,11 @@ struct RegisrationSetupView: View {
@State private var targetTeamCountEnabled: Bool
@State private var waitingListLimitEnabled: Bool
@State private var openingRegistrationDateEnabled: Bool
@State private var userAccountIsRequired: Bool
@State private var licenseIsRequired: Bool
@State private var minPlayerPerTeam: Int
@State private var maxPlayerPerTeam: Int
@State private var hasChanges: Bool = false
@Environment(\.dismiss) private var dismiss
@ -63,6 +68,12 @@ struct RegisrationSetupView: View {
_waitingListLimit = .init(wrappedValue: 0) // Default value
_waitingListLimitEnabled = .init(wrappedValue: false)
}
_userAccountIsRequired = .init(wrappedValue: tournament.accountIsRequired)
_licenseIsRequired = .init(wrappedValue: tournament.licenseIsRequired)
_maxPlayerPerTeam = .init(wrappedValue: tournament.maximumPlayerPerTeam)
_minPlayerPerTeam = .init(wrappedValue: tournament.minimumPlayerPerTeam)
}
var body: some View {
@ -134,6 +145,29 @@ struct RegisrationSetupView: View {
} footer: {
Text("Activez et définissez une limite pour la liste d'attente des équipes.")
}
if tournament.isAnimation() {
Section {
Toggle(isOn: $userAccountIsRequired) {
Text("Compte Padel Club requis pour s'inscrire")
}
Toggle(isOn: $licenseIsRequired) {
Text("Licence FFT requise pour s'inscrire")
}
LabeledContent {
StepperView(count: $minPlayerPerTeam, minimum: 1, maximum: maxPlayerPerTeam)
} label: {
Text("Nombre minimum de joueurs possible")
}
LabeledContent {
StepperView(count: $maxPlayerPerTeam, minimum: minPlayerPerTeam)
} label: {
Text("Nombre maximum de joueurs possible")
}
}
}
} else {
ContentUnavailableView(
"Activez les inscriptions en ligne",
@ -198,6 +232,13 @@ struct RegisrationSetupView: View {
.onChange(of: waitingListLimit) {
_hasChanged()
}
.onChange(of: [minPlayerPerTeam, maxPlayerPerTeam]) {
_hasChanged()
}
.onChange(of: [userAccountIsRequired, licenseIsRequired]) {
_hasChanged()
}
}
private func _hasChanged() {
@ -209,6 +250,18 @@ struct RegisrationSetupView: View {
tournament.enableOnlineRegistration = enableOnlineRegistration
if enableOnlineRegistration {
tournament.accountIsRequired = userAccountIsRequired
tournament.licenseIsRequired = licenseIsRequired
tournament.minimumPlayerPerTeam = minPlayerPerTeam
tournament.maximumPlayerPerTeam = maxPlayerPerTeam
} else {
tournament.accountIsRequired = true
tournament.licenseIsRequired = true
tournament.minimumPlayerPerTeam = 2
tournament.maximumPlayerPerTeam = 2
}
if openingRegistrationDateEnabled == false {
tournament.openingRegistrationDate = nil
} else {

@ -16,6 +16,7 @@ enum TournamentSettings: Identifiable, Selectable, Equatable {
case general
case club(Tournament)
case matchFormats
case onlineRegistration(Tournament)
var id: String { String(describing: self) }
@ -29,6 +30,8 @@ enum TournamentSettings: Identifiable, Selectable, Equatable {
return "Général"
case .club:
return "Terrains"
case .onlineRegistration:
return "Inscriptions en ligne"
}
}
@ -46,6 +49,15 @@ enum TournamentSettings: Identifiable, Selectable, Equatable {
}
func badgeImage() -> Badge? {
switch self {
case .onlineRegistration(let tournament):
if tournament.enableOnlineRegistration {
return .checkmark
}
default:
return nil
}
return nil
}
}
@ -55,7 +67,7 @@ struct TournamentSettingsView: View {
@Environment(Tournament.self) var tournament: Tournament
private func destinations() -> [TournamentSettings] {
[.general, .club(tournament), .matchFormats]
[.general, .club(tournament), .matchFormats, .onlineRegistration(tournament)]
}
var body: some View {
@ -68,6 +80,8 @@ struct TournamentSettingsView: View {
TournamentMatchFormatsSettingsView()
case .general:
TournamentGeneralSettingsView(tournament: tournament)
case .onlineRegistration:
RegisrationSetupView(tournament: tournament)
case .club:
TournamentClubSettingsView()
}

@ -100,7 +100,7 @@ final class ServerDataTests: XCTestCase {
return
}
let tournament = Tournament(event: eventId, name: "RG Homme", startDate: Date(), endDate: nil, creationDate: Date(), isPrivate: false, groupStageFormat: MatchFormat.megaTie, roundFormat: MatchFormat.nineGames, loserRoundFormat: MatchFormat.nineGamesDecisivePoint, groupStageSortMode: GroupStageOrderingMode.snake, groupStageCount: 2, rankSourceDate: Date(), dayDuration: 5, teamCount: 3, teamSorting: TeamSortingType.rank, federalCategory: TournamentCategory.mix, federalLevelCategory: TournamentLevel.p1000, federalAgeCategory: FederalTournamentAge.a45, closedRegistrationDate: Date(), groupStageAdditionalQualified: 4, courtCount: 9, prioritizeClubMembers: true, qualifiedPerGroupStage: 1, teamsPerGroupStage: 2, entryFee: 30.0, additionalEstimationDuration: 5, isDeleted: true, publishTeams: true, publishSummons: true, publishGroupStages: true, publishBrackets: true, shouldVerifyBracket: true, shouldVerifyGroupStage: true, hideTeamsWeight: true, publishTournament: true, hidePointsEarned: true, publishRankings: true, loserBracketMode: .manual, initialSeedRound: 8, initialSeedCount: 4)
let tournament = Tournament(event: eventId, name: "RG Homme", startDate: Date(), endDate: nil, creationDate: Date(), isPrivate: false, groupStageFormat: MatchFormat.megaTie, roundFormat: MatchFormat.nineGames, loserRoundFormat: MatchFormat.nineGamesDecisivePoint, groupStageSortMode: GroupStageOrderingMode.snake, groupStageCount: 2, rankSourceDate: Date(), dayDuration: 5, teamCount: 3, teamSorting: TeamSortingType.rank, federalCategory: TournamentCategory.mix, federalLevelCategory: TournamentLevel.p1000, federalAgeCategory: FederalTournamentAge.a45, closedRegistrationDate: Date(), groupStageAdditionalQualified: 4, courtCount: 9, prioritizeClubMembers: true, qualifiedPerGroupStage: 1, teamsPerGroupStage: 2, entryFee: 30.0, additionalEstimationDuration: 5, isDeleted: true, publishTeams: true, publishSummons: true, publishGroupStages: true, publishBrackets: true, shouldVerifyBracket: true, shouldVerifyGroupStage: true, hideTeamsWeight: true, publishTournament: true, hidePointsEarned: true, publishRankings: true, loserBracketMode: .manual, initialSeedRound: 8, initialSeedCount: 4, accountIsRequired: false, licenseIsRequired: false, minimumPlayerPerTeam: 3, maximumPlayerPerTeam: 5, information: "Super", displayEntryFeeInformation: true)
let t = try await StoreCenter.main.service().post(tournament)
assert(t.event == tournament.event)
@ -143,6 +143,12 @@ final class ServerDataTests: XCTestCase {
assert(t.loserBracketMode == tournament.loserBracketMode)
assert(t.initialSeedCount == tournament.initialSeedCount)
assert(t.initialSeedRound == tournament.initialSeedRound)
assert(t.accountIsRequired == tournament.accountIsRequired)
assert(t.licenseIsRequired == tournament.licenseIsRequired)
assert(t.minimumPlayerPerTeam == tournament.minimumPlayerPerTeam)
assert(t.maximumPlayerPerTeam == tournament.maximumPlayerPerTeam)
assert(t.information == tournament.information)
assert(t.displayEntryFeeInformation == tournament.displayEntryFeeInformation)
}
func testGroupStage() async throws {
@ -203,7 +209,7 @@ final class ServerDataTests: XCTestCase {
return
}
let teamRegistration = TeamRegistration(tournament: tournamentId, groupStage: groupStageId, registrationDate: Date(), callDate: Date(), bracketPosition: 1, groupStagePosition: 2, comment: "comment", source: "source", sourceValue: "source V", logo: "logo", name: "Stax", walkOut: true, wildCardBracket: true, wildCardGroupStage: true, weight: 1, lockedWeight: 11, confirmationDate: Date(), qualified: true)
let teamRegistration = TeamRegistration(tournament: tournamentId, groupStage: groupStageId, registrationDate: Date(), callDate: Date(), bracketPosition: 1, groupStagePosition: 2, comment: "comment", source: "source", sourceValue: "source V", logo: "logo", name: "Stax", walkOut: true, wildCardBracket: true, wildCardGroupStage: true, weight: 1, lockedWeight: 11, confirmationDate: Date(), qualified: true, finalRanking: 4, pointsEarned: 200, unregistered: true, unregistrationDate: Date())
let tr: TeamRegistration = try await StoreCenter.main.service().post(teamRegistration)
@ -225,7 +231,10 @@ final class ServerDataTests: XCTestCase {
assert(tr.lockedWeight == teamRegistration.lockedWeight)
assert(tr.confirmationDate?.formatted() == teamRegistration.confirmationDate?.formatted())
assert(tr.qualified == teamRegistration.qualified)
assert(tr.finalRanking == teamRegistration.finalRanking)
assert(tr.pointsEarned == teamRegistration.pointsEarned)
assert(tr.unregistered == teamRegistration.unregistered)
assert(tr.unregistrationDate?.formatted() == teamRegistration.unregistrationDate?.formatted())
}
func testPlayerRegistration() async throws {
@ -236,7 +245,7 @@ final class ServerDataTests: XCTestCase {
return
}
let playerRegistration = PlayerRegistration(teamRegistration: teamRegistrationId, firstName: "juan", lastName: "lebron", licenceId: "123", rank: 11, paymentType: PlayerRegistration.PlayerPaymentType.cash, sex: PlayerRegistration.PlayerSexType.male, tournamentPlayed: 2, points: 33, clubName: "le club", ligueName: "la league", assimilation: "ass", phoneNumber: "123123", email: "email@email.com", birthdate: nil, computedRank: 222, source: PlayerRegistration.PlayerDataSource.frenchFederation, hasArrived: true)
let playerRegistration = PlayerRegistration(teamRegistration: teamRegistrationId, firstName: "juan", lastName: "lebron", licenceId: "123", rank: 11, paymentType: PlayerRegistration.PlayerPaymentType.cash, sex: PlayerRegistration.PlayerSexType.male, tournamentPlayed: 2, points: 33, clubName: "le club", ligueName: "la league", assimilation: "ass", phoneNumber: "123123", email: "email@email.com", birthdate: nil, computedRank: 222, source: PlayerRegistration.PlayerDataSource.frenchFederation, hasArrived: true, coach: true, captain: true)
let pr: PlayerRegistration = try await StoreCenter.main.service().post(playerRegistration)
assert(pr.teamRegistration == playerRegistration.teamRegistration)
@ -256,7 +265,9 @@ final class ServerDataTests: XCTestCase {
assert(pr.computedRank == playerRegistration.computedRank)
assert(pr.source == playerRegistration.source)
assert(pr.hasArrived == playerRegistration.hasArrived)
assert(pr.captain == playerRegistration.captain)
assert(pr.coach == playerRegistration.coach)
}
func testMatch() async throws {

Loading…
Cancel
Save