sync_v2
Raz 8 months ago
parent 4a378f8737
commit ae3052f5e8
  1. 18
      PadelClub/Data/Gen/BaseTournament.swift
  2. 11
      PadelClub/Data/Gen/Tournament.json
  3. 14
      PadelClub/Data/Match.swift
  4. 22
      PadelClub/Data/Tournament.swift
  5. 6
      PadelClub/Views/Calling/Components/MenuWarningView.swift
  6. 2
      PadelClub/Views/Match/MatchDetailView.swift
  7. 19
      PadelClub/Views/Score/EditScoreView.swift
  8. 71
      PadelClub/Views/Tournament/Screen/Components/TournamentGeneralSettingsView.swift
  9. 8
      PadelClub/Views/Tournament/Screen/TournamentRankView.swift

@ -63,6 +63,8 @@ class BaseTournament: SyncedModelObject, SyncedStorable {
var minimumPlayerPerTeam: Int = 2
var maximumPlayerPerTeam: Int = 2
var information: String? = nil
var umpireCustomMail: String? = nil
var disableRankingFederalRuling: Bool = false
init(
id: String = Store.randomId(),
@ -116,7 +118,9 @@ class BaseTournament: SyncedModelObject, SyncedStorable {
licenseIsRequired: Bool = true,
minimumPlayerPerTeam: Int = 2,
maximumPlayerPerTeam: Int = 2,
information: String? = nil
information: String? = nil,
umpireCustomMail: String? = nil,
disableRankingFederalRuling: Bool = false
) {
super.init()
self.id = id
@ -171,6 +175,8 @@ class BaseTournament: SyncedModelObject, SyncedStorable {
self.minimumPlayerPerTeam = minimumPlayerPerTeam
self.maximumPlayerPerTeam = maximumPlayerPerTeam
self.information = information
self.umpireCustomMail = umpireCustomMail
self.disableRankingFederalRuling = disableRankingFederalRuling
}
enum CodingKeys: String, CodingKey {
@ -226,6 +232,8 @@ class BaseTournament: SyncedModelObject, SyncedStorable {
case _minimumPlayerPerTeam = "minimumPlayerPerTeam"
case _maximumPlayerPerTeam = "maximumPlayerPerTeam"
case _information = "information"
case _umpireCustomMail = "umpireCustomMail"
case _disableRankingFederalRuling = "disableRankingFederalRuling"
}
private static func _decodePayment(container: KeyedDecodingContainer<CodingKeys>) throws -> TournamentPayment? {
@ -343,6 +351,8 @@ class BaseTournament: SyncedModelObject, SyncedStorable {
self.minimumPlayerPerTeam = try container.decodeIfPresent(Int.self, forKey: ._minimumPlayerPerTeam) ?? 2
self.maximumPlayerPerTeam = try container.decodeIfPresent(Int.self, forKey: ._maximumPlayerPerTeam) ?? 2
self.information = try container.decodeIfPresent(String.self, forKey: ._information) ?? nil
self.umpireCustomMail = try container.decodeIfPresent(String.self, forKey: ._umpireCustomMail) ?? nil
self.disableRankingFederalRuling = try container.decodeIfPresent(Bool.self, forKey: ._disableRankingFederalRuling) ?? false
try super.init(from: decoder)
}
@ -400,6 +410,8 @@ class BaseTournament: SyncedModelObject, SyncedStorable {
try container.encode(self.minimumPlayerPerTeam, forKey: ._minimumPlayerPerTeam)
try container.encode(self.maximumPlayerPerTeam, forKey: ._maximumPlayerPerTeam)
try container.encode(self.information, forKey: ._information)
try container.encode(self.umpireCustomMail, forKey: ._umpireCustomMail)
try container.encode(self.disableRankingFederalRuling, forKey: ._disableRankingFederalRuling)
try super.encode(to: encoder)
}
@ -462,6 +474,8 @@ class BaseTournament: SyncedModelObject, SyncedStorable {
self.minimumPlayerPerTeam = tournament.minimumPlayerPerTeam
self.maximumPlayerPerTeam = tournament.maximumPlayerPerTeam
self.information = tournament.information
self.umpireCustomMail = tournament.umpireCustomMail
self.disableRankingFederalRuling = tournament.disableRankingFederalRuling
}
static func relationships() -> [Relationship] {
@ -470,4 +484,4 @@ class BaseTournament: SyncedModelObject, SyncedStorable {
]
}
}
}

@ -264,6 +264,17 @@
"name": "information",
"type": "String",
"optional": true
},
{
"name": "umpireCustomMail",
"type": "String",
"optional": true
},
{
"name": "disableRankingFederalRuling",
"type": "Bool",
"defaultValue": false,
"optional": false
}
]
}

@ -484,6 +484,20 @@ defer {
}
}
func removeWalkOut() {
teamScores.forEach { teamScore in
teamScore.walkOut = nil
teamScore.score = nil
}
tournamentStore?.teamScores.addOrUpdate(contentOfs: teamScores)
endDate = nil
winningTeamId = nil
losingTeamId = nil
groupStageObject?.updateGroupStageState()
roundObject?.updateTournamentState()
updateFollowingMatchTeamScore()
}
func setWalkOut(_ teamPosition: TeamPosition) {
let teamScoreWalkout = teamScore(teamPosition) ?? TeamScore(match: id, team: team(teamPosition))
teamScoreWalkout.walkOut = 0

@ -30,7 +30,10 @@ final class Tournament: BaseTournament {
// }
internal init(event: String? = nil, name: String? = nil, startDate: Date = Date(), endDate: Date? = nil, creationDate: Date = Date(), isPrivate: Bool = true, 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, enableOnlineRegistration: Bool = false, registrationDateLimit: Date? = nil, openingRegistrationDate: Date? = nil, waitingListLimit: Int? = nil, accountIsRequired: Bool = true, licenseIsRequired: Bool = true, minimumPlayerPerTeam: Int = 2, maximumPlayerPerTeam: Int = 2, information: String? = nil) {
internal init(event: String? = nil, name: String? = nil, startDate: Date = Date(), endDate: Date? = nil, creationDate: Date = Date(), isPrivate: Bool = true, 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, enableOnlineRegistration: Bool = false, registrationDateLimit: Date? = nil, openingRegistrationDate: Date? = nil, waitingListLimit: Int? = nil, accountIsRequired: Bool = true, licenseIsRequired: Bool = true, minimumPlayerPerTeam: Int = 2, maximumPlayerPerTeam: Int = 2, information: String? = nil,
umpireCustomMail: String? = nil,
disableRankingFederalRuling: Bool = false
) {
super.init()
self.event = event
self.name = name
@ -95,6 +98,8 @@ final class Tournament: BaseTournament {
self.minimumPlayerPerTeam = minimumPlayerPerTeam
self.maximumPlayerPerTeam = maximumPlayerPerTeam
self.information = information
self.umpireCustomMail = umpireCustomMail
self.disableRankingFederalRuling = disableRankingFederalRuling
}
required init(from decoder: Decoder) throws {
@ -1133,13 +1138,7 @@ defer {
let groupStages = groupStages()
var baseRank = teamCount - groupStageSpots() + qualifiedPerGroupStage * groupStageCount + groupStageAdditionalQualified
//TODO: RAZ ajouté une option pour choisir entre la règle officiel et la règle 'maison'
/*
Request by Philippe Morin 24/03/2025
*/
let defaultOption = false
if defaultOption {
if disableRankingFederalRuling == false {
baseRank += qualifiedPerGroupStage * groupStageCount + groupStageAdditionalQualified - 1
}
let alreadyPlaceTeams = Array(teams.values.flatMap({ $0 }))
@ -1443,7 +1442,7 @@ defer {
}
func umpireMail() -> [String]? {
return [DataStore.shared.user.email]
return [umpireCustomMail ?? DataStore.shared.user.email]
}
func earnings() -> Double {
@ -2583,12 +2582,15 @@ extension Tournament {
} else if Guard.main.purchasedTransactions.isEmpty == false {
shouldBePrivate = false
}
let disableRankingFederalRuling = tournaments.first?.disableRankingFederalRuling ?? false
let umpireCustomMail = tournaments.first?.umpireCustomMail
let tournamentLevel = TournamentLevel.mostUsed(inTournaments: tournaments)
let tournamentCategory = TournamentCategory.mostUsed(inTournaments: tournaments)
let federalTournamentAge = FederalTournamentAge.mostUsed(inTournaments: tournaments)
//creator: DataStore.shared.user?.id
return Tournament(isPrivate: shouldBePrivate, groupStageSortMode: .snake, rankSourceDate: rankSourceDate, teamSorting: tournamentLevel.defaultTeamSortingType, federalCategory: tournamentCategory, federalLevelCategory: tournamentLevel, federalAgeCategory: federalTournamentAge, loserBracketMode: DataStore.shared.user.loserBracketMode)
return Tournament(isPrivate: shouldBePrivate, groupStageSortMode: .snake, rankSourceDate: rankSourceDate, teamSorting: tournamentLevel.defaultTeamSortingType, federalCategory: tournamentCategory, federalLevelCategory: tournamentLevel, federalAgeCategory: federalTournamentAge, loserBracketMode: DataStore.shared.user.loserBracketMode, umpireCustomMail: umpireCustomMail, disableRankingFederalRuling: disableRankingFederalRuling)
}
static func fake() -> Tournament {

@ -13,7 +13,6 @@ struct MenuWarningView: View {
let teams: [TeamRegistration]
var date: Date?
var message: String?
var umpireMail: String?
var subject: String?
@Binding var contactType: ContactType?
@ -24,10 +23,7 @@ struct MenuWarningView: View {
@State var savedContactType: ContactType? = nil
private func _getUmpireMail() -> [String]? {
if let umpireMail {
return [umpireMail]
}
return nil
return tournament.umpireMail()
}
var body: some View {

@ -98,7 +98,7 @@ struct MatchDetailView: View {
}
Spacer()
if let tournament = match.currentTournament() {
MenuWarningView(tournament: tournament, teams: match.teams(), message: match.matchWarningMessage(), umpireMail: dataStore.user.email, subject: match.matchWarningSubject(), contactType: $contactType)
MenuWarningView(tournament: tournament, teams: match.teams(), message: match.matchWarningMessage(), subject: match.matchWarningSubject(), contactType: $contactType)
.buttonStyle(.borderless)
}
}

@ -134,6 +134,15 @@ struct EditScoreView: View {
} label: {
Text(matchDescriptor.teamLabelTwo)
}
Divider()
Button {
self.matchDescriptor.match?.removeWalkOut()
save()
} label: {
Text("Annuler un forfait")
}
} label: {
Text("Forfait d'une équipe ?")
.underline()
@ -190,6 +199,16 @@ struct EditScoreView: View {
} footer: {
Text("Met à jour le score, ne termine pas la rencontre")
}
Section {
RowButtonView("Terminer la rencontre") {
matchDescriptor.match?.setScore(fromMatchDescriptor: matchDescriptor)
save()
dismiss()
}
} footer: {
Text("Le match n'a pas pu aboutir.")
}
}
}
.sheet(isPresented: $presentMatchFormatSelection) {

@ -18,15 +18,18 @@ struct TournamentGeneralSettingsView: View {
@State private var confirmationRequired: Bool = false
@State private var presentConfirmation: Bool = false
@State private var loserBracketMode: LoserBracketMode
@State private var umpireCustomMail: String
@State private var umpireCustomMailIsInvalid: Bool = false
@FocusState private var focusedField: Tournament.CodingKeys?
let priceTags: [Double] = [15.0, 20.0, 25.0]
init(tournament: Tournament) {
self.tournament = tournament
_loserBracketMode = .init(wrappedValue: tournament.loserBracketMode)
_tournamentName = State(wrappedValue: tournament.name ?? "")
_tournamentInformation = State(wrappedValue: tournament.information ?? "")
_entryFee = State(wrappedValue: tournament.entryFee)
_umpireCustomMail = State(wrappedValue: tournament.umpireCustomMail ?? "")
}
var body: some View {
@ -73,6 +76,8 @@ struct TournamentGeneralSettingsView: View {
}
}
_customUmpireView()
Section {
TextField("Nom du tournoi", text: $tournamentName, axis: .vertical)
.lineLimit(2)
@ -150,7 +155,7 @@ struct TournamentGeneralSettingsView: View {
Button("Annuler", role: .cancel) {
loserBracketMode = tournament.loserBracketMode
}
})
.navigationBarBackButtonHidden(focusedField != nil)
.toolbar(content: {
@ -186,6 +191,27 @@ struct TournamentGeneralSettingsView: View {
.buttonStyle(.bordered)
}
} else {
if focusedField == ._name, tournamentName.isEmpty == false {
Button("Effacer") {
tournament.name = nil
tournamentName = ""
_save()
}
.buttonStyle(.borderless)
} else if focusedField == ._information, tournamentInformation.isEmpty == false {
Button("Effacer") {
tournament.information = nil
tournamentInformation = ""
_save()
}
.buttonStyle(.borderless)
} else if focusedField == ._umpireCustomMail, umpireCustomMail.isEmpty == false {
Button("Effacer") {
_deleteUmpireMail()
}
.buttonStyle(.borderless)
}
}
Spacer()
Button("Valider") {
@ -205,6 +231,8 @@ struct TournamentGeneralSettingsView: View {
}
} else if focusedField == ._entryFee {
tournament.entryFee = entryFee
} else if focusedField == ._umpireCustomMail {
_confirmUmpireMail()
}
focusedField = nil
}
@ -232,6 +260,26 @@ struct TournamentGeneralSettingsView: View {
}
}
private func _confirmUmpireMail() {
umpireCustomMailIsInvalid = false
if umpireCustomMail.isEmpty {
tournament.umpireCustomMail = nil
_save()
} else if umpireCustomMail.isValidEmail() {
tournament.umpireCustomMail = umpireCustomMail
_save()
} else {
umpireCustomMailIsInvalid = true
}
}
private func _deleteUmpireMail() {
umpireCustomMailIsInvalid = false
umpireCustomMail = ""
tournament.umpireCustomMail = nil
_save()
}
private func _save() {
do {
try dataStore.tournaments.addOrUpdate(instance: tournament)
@ -275,6 +323,25 @@ struct TournamentGeneralSettingsView: View {
}
}
private func _customUmpireView() -> some View {
Section {
if umpireCustomMailIsInvalid {
Text("Vous n'avez pas indiqué un email valide.").foregroundStyle(.logoRed)
}
TextField(dataStore.user.email, text: $umpireCustomMail)
.frame(maxWidth: .infinity)
.keyboardType(.emailAddress)
.focused($focusedField, equals: ._umpireCustomMail)
.onSubmit {
_confirmUmpireMail()
}
} header: {
Text("Email du juge-arbitre")
} footer: {
Text("Cet email sera utilisé pour vous contacter. Vous pouvez le modifier si vous souhaitez utiliser un autre email que celui de votre compte Padel Club.")
}
}
private func _footerView() -> some View {
Text(tournament.loserBracketMode.localizedLoserBracketModeDescription())
+

@ -59,6 +59,7 @@ struct TournamentRankView: View {
Logger.error(error)
}
}
//affiche l'onglet sur le site, car sur le broadcast c'est dispo automatiquement de toute façon
Toggle(isOn: $tournament.publishRankings) {
if calculating {
@ -68,6 +69,13 @@ struct TournamentRankView: View {
}
}
.disabled(calculating)
Toggle(isOn: $tournament.disableRankingFederalRuling) {
Text("Désactiver la règle fédéral")
Text("Dernier de poule ≠ derner du tournoi")
}
.disabled(calculating)
} footer: {
if let url = tournament.shareURL(.rankings) {
Link(destination: url) {

Loading…
Cancel
Save