multistore
Razmig Sarkissian 1 year ago
parent 08c466e45f
commit e5e3ce6b9d
  1. 2
      PadelClub/Data/Club.swift
  2. 2
      PadelClub/Data/GroupStage.swift
  3. 2
      PadelClub/Data/Match.swift
  4. 41
      PadelClub/Data/MatchScheduler.swift
  5. 2
      PadelClub/Data/PlayerRegistration.swift
  6. 22
      PadelClub/Data/Tournament.swift
  7. 1
      PadelClub/Utils/DisplayContext.swift
  8. 20
      PadelClub/Utils/PadelRule.swift
  9. 20
      PadelClub/Views/Cashier/Event/TournamentConfiguratorView.swift
  10. 4
      PadelClub/Views/Planning/Components/DateUpdateManagerView.swift
  11. 26
      PadelClub/Views/Planning/PlanningSettingsView.swift
  12. 10
      PadelClub/Views/Team/Components/TeamWeightView.swift
  13. 22
      PadelClub/Views/Tournament/Screen/Components/TournamentLevelPickerView.swift
  14. 17
      PadelClub/Views/Tournament/Screen/TournamentRankView.swift
  15. 10
      PadelClub/Views/Tournament/Shared/TournamentCellView.swift
  16. 4
      PadelClub/Views/Tournament/TournamentView.swift
  17. 3
      PadelClubTests/ServerDataTests.swift

@ -55,7 +55,7 @@ class Club : ModelObject, Storable, Hashable {
func clubTitle(_ displayStyle: DisplayStyle = .wide) -> String {
switch displayStyle {
case .wide:
case .wide, .title:
return name
case .short:
return acronym

@ -60,7 +60,7 @@ class GroupStage: ModelObject, Storable {
func groupStageTitle(_ displayStyle: DisplayStyle = .wide) -> String {
if let name { return name }
switch displayStyle {
case .wide:
case .wide, .title:
return "Poule \(index + 1)"
case .short:
return "#\(index + 1)"

@ -92,7 +92,7 @@ class Match: ModelObject, Storable {
}
switch displayStyle {
case .wide:
case .wide, .title:
return "Match \(indexInRound(in: matches) + 1)"
case .short:
return "#\(indexInRound(in: matches) + 1)"

@ -27,6 +27,7 @@ class MatchScheduler : ModelObject, Storable {
var shouldHandleUpperRoundSlice: Bool
var shouldEndRoundBeforeStartingNext: Bool
var groupStageChunkCount: Int?
var overrideCourtsUnavailability: Bool = false
init(tournament: String,
timeDifferenceLimit: Int = 5,
@ -38,7 +39,7 @@ class MatchScheduler : ModelObject, Storable {
rotationDifferenceIsImportant: Bool = false,
shouldHandleUpperRoundSlice: Bool = true,
shouldEndRoundBeforeStartingNext: Bool = true,
groupStageChunkCount: Int? = nil) {
groupStageChunkCount: Int? = nil, overrideCourtsUnavailability: Bool = false) {
self.tournament = tournament
self.timeDifferenceLimit = timeDifferenceLimit
self.loserBracketRotationDifference = loserBracketRotationDifference
@ -50,6 +51,7 @@ class MatchScheduler : ModelObject, Storable {
self.shouldHandleUpperRoundSlice = shouldHandleUpperRoundSlice
self.shouldEndRoundBeforeStartingNext = shouldEndRoundBeforeStartingNext
self.groupStageChunkCount = groupStageChunkCount
self.overrideCourtsUnavailability = overrideCourtsUnavailability
}
enum CodingKeys: String, CodingKey {
@ -65,11 +67,12 @@ class MatchScheduler : ModelObject, Storable {
case _shouldHandleUpperRoundSlice = "shouldHandleUpperRoundSlice"
case _shouldEndRoundBeforeStartingNext = "shouldEndRoundBeforeStartingNext"
case _groupStageChunkCount = "groupStageChunkCount"
case _overrideCourtsUnavailability = "overrideCourtsUnavailability"
}
var courtsUnavailability: [DateInterval]? {
guard let event = tournamentObject()?.eventObject() else { return nil }
return event.courtsUnavailability + event.tournamentsCourtsUsed(exluding: tournament)
return event.courtsUnavailability + (overrideCourtsUnavailability ? [] : event.tournamentsCourtsUsed(exluding: tournament))
}
var additionalEstimationDuration : Int {
@ -403,7 +406,7 @@ class MatchScheduler : ModelObject, Storable {
if rotationIndex > 0, let freeCourtPreviousRotation = freeCourtPerRotation[rotationIndex - 1], freeCourtPreviousRotation.count > 0 {
print("scenario where we are waiting for a breaktime to be over without any match to play in between or a free court was available and we need to recheck breaktime left on it")
let previousPreviousRotationSlots = slots.filter({ $0.rotationIndex == rotationIndex - 2 && freeCourtPreviousRotation.contains($0.courtIndex) })
let previousEndDate = getNextStartDate(fromPreviousRotationSlots: previousPreviousRotationSlots, includeBreakTime: true)
let previousEndDate = getNextStartDate(fromPreviousRotationSlots: previousPreviousRotationSlots, includeBreakTime: accountUpperBracketBreakTime)
let previousEndDateNoBreak = getNextStartDate(fromPreviousRotationSlots: previousPreviousRotationSlots, includeBreakTime: false)
let noBreakAlreadyTested = previousRotationSlots.anySatisfy({ $0.startDate == previousEndDateNoBreak })
@ -420,7 +423,7 @@ class MatchScheduler : ModelObject, Storable {
difference = noBreakAlreadyTested ? differenceWithBreak : max(differenceWithBreak, differenceWithoutBreak)
}
if difference > timeDifferenceLimitInSeconds {
if difference > timeDifferenceLimitInSeconds && rotationStartDate.addingTimeInterval(-difference) != previousEndDate {
courts.removeAll(where: { index in freeCourtPreviousRotation.contains(index)
})
freeCourtPerRotation[rotationIndex] = courts
@ -451,12 +454,12 @@ class MatchScheduler : ModelObject, Storable {
}
func dispatchCourts(availableCourts: Int, courts: [Int], availableMatchs: inout [Match], slots: inout [TimeMatch], rotationIndex: Int, rotationStartDate: Date, freeCourtPerRotation: inout [Int: [Int]], courtsUnavailability: [DateInterval]?) {
var matchPerRound = [Int: Int]()
var matchPerRound = [String: Int]()
var minimumTargetedEndDate: Date = rotationStartDate
print("dispatchCourts", courts.sorted(), rotationStartDate, rotationIndex)
courts.sorted().forEach { courtIndex in
print("trying to find a match for \(courtIndex) in \(rotationIndex)")
for (courtPosition, courtIndex) in courts.sorted().enumerated() {
if let first = availableMatchs.first(where: { match in
print("trying to find a match for \(courtIndex) in \(rotationIndex)")
let roundObject = match.roundObject!
let courtsUnavailable = courtsUnavailable(startDate: rotationStartDate, duration: match.matchFormat.getEstimatedDuration(additionalEstimationDuration), courtsUnavailability: courtsUnavailability)
print("courtsUnavailable \(courtsUnavailable)")
@ -466,10 +469,11 @@ class MatchScheduler : ModelObject, Storable {
let canBePlayed = roundMatchCanBePlayed(match, roundObject: roundObject, slots: slots, rotationIndex: rotationIndex, targetedStartDate: rotationStartDate, minimumTargetedEndDate: &minimumTargetedEndDate)
let currentRotationSameRoundMatches = matchPerRound[roundObject.index] ?? 0
let currentRotationSameRoundMatches = matchPerRound[roundObject.id] ?? 0
let roundMatchesCount = roundObject.playedMatches().count
if shouldHandleUpperRoundSlice {
let roundMatchesCount = roundObject.playedMatches().count
print("shouldHandleUpperRoundSlice \(roundMatchesCount)")
if roundObject.parent == nil && roundMatchesCount > courts.count {
print("roundMatchesCount \(roundMatchesCount) > \(courts.count)")
@ -480,24 +484,27 @@ class MatchScheduler : ModelObject, Storable {
}
}
//if all is ok, we do a final check to see if the first
let indexInRound = match.indexInRound()
print("Upper Round, index > 0, first Match of round \(indexInRound) and more than one court available; looking for next match (same round) \(indexInRound + 1)")
if roundObject.parent == nil && roundObject.index > 0, indexInRound == 0, courts.count > 1, let nextMatch = match.next() {
if roundObject.parent == nil && roundObject.index > 0, indexInRound == 0, let nextMatch = match.next() {
guard courtPosition < courts.count - 1, courts.count > 1 else {
print("next match and this match can not be played at the same time, returning false")
return false
}
if canBePlayed && roundMatchCanBePlayed(nextMatch, roundObject: roundObject, slots: slots, rotationIndex: rotationIndex, targetedStartDate: rotationStartDate, minimumTargetedEndDate: &minimumTargetedEndDate) {
print("next match and this match can be played, returning true")
return true
} else {
print("next match and this match can not be played at the same time, returning false")
return false
}
}
//not adding a last match of a 4-match round (final not included obviously)
print("\(currentRotationSameRoundMatches) modulo \(currentRotationSameRoundMatches%2) same round match is even, index of round is not 0 and upper bracket. If it's not the last court available \(courtIndex) == \(courts.count - 1)")
if currentRotationSameRoundMatches%2 == 0 && roundObject.index != 0 && roundObject.parent == nil && courtIndex == courts.count - 1 {
if roundMatchesCount <= 4 && currentRotationSameRoundMatches%2 == 0 && roundObject.index != 0 && roundObject.parent == nil && ((courts.count > 1 && courtPosition >= courts.count - 1) || courts.count == 1 && availableCourts > 1) {
print("we return false")
return false
}
@ -508,10 +515,10 @@ class MatchScheduler : ModelObject, Storable {
print(first.roundObject!.roundTitle(), first.matchTitle(), courtIndex, rotationStartDate)
if first.roundObject!.parent == nil {
if let roundIndex = matchPerRound[first.roundObject!.index] {
matchPerRound[first.roundObject!.index] = roundIndex + 1
if let roundIndex = matchPerRound[first.roundObject!.id] {
matchPerRound[first.roundObject!.id] = roundIndex + 1
} else {
matchPerRound[first.roundObject!.index] = 1
matchPerRound[first.roundObject!.id] = 1
}
}
let timeMatch = TimeMatch(matchID: first.id, rotationIndex: rotationIndex, courtIndex: courtIndex, startDate: rotationStartDate, durationLeft: first.matchFormat.getEstimatedDuration(additionalEstimationDuration), minimumBreakTime: first.matchFormat.breakTime.breakTime)

@ -149,7 +149,7 @@ class PlayerRegistration: ModelObject, Storable {
func playerLabel(_ displayStyle: DisplayStyle = .wide) -> String {
switch displayStyle {
case .wide:
case .wide, .title:
return lastName.trimmed.capitalized + " " + firstName.trimmed.capitalized
case .short:
let names = lastName.components(separatedBy: .whitespaces)

@ -52,7 +52,8 @@ class Tournament : ModelObject, Storable {
var shouldVerifyBracket: Bool = false
var hideTeamsWeight: Bool = false
var publishTournament: Bool = false
var hidePointsEarned: Bool = false
@ObservationIgnored
var navigationPath: [Screen] = []
@ -98,9 +99,10 @@ class Tournament : ModelObject, Storable {
case _shouldVerifyBracket = "shouldVerifyBracket"
case _hideTeamsWeight = "hideTeamsWeight"
case _publishTournament = "publishTournament"
case _hidePointsEarned = "hidePointsEarned"
}
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) {
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) {
self.event = event
self.name = name
self.startDate = startDate
@ -136,6 +138,7 @@ class Tournament : ModelObject, Storable {
self.shouldVerifyGroupStage = shouldVerifyGroupStage
self.hideTeamsWeight = hideTeamsWeight
self.publishTournament = publishTournament
self.hidePointsEarned = hidePointsEarned
}
required init(from decoder: Decoder) throws {
@ -178,6 +181,7 @@ class Tournament : ModelObject, Storable {
shouldVerifyGroupStage = try container.decodeIfPresent(Bool.self, forKey: ._shouldVerifyGroupStage) ?? false
hideTeamsWeight = try container.decodeIfPresent(Bool.self, forKey: ._hideTeamsWeight) ?? false
publishTournament = try container.decodeIfPresent(Bool.self, forKey: ._publishTournament) ?? false
hidePointsEarned = try container.decodeIfPresent(Bool.self, forKey: ._hidePointsEarned) ?? false
}
fileprivate static let _numberFormatter: NumberFormatter = NumberFormatter()
@ -291,6 +295,7 @@ class Tournament : ModelObject, Storable {
try container.encode(shouldVerifyGroupStage, forKey: ._shouldVerifyGroupStage)
try container.encode(hideTeamsWeight, forKey: ._hideTeamsWeight)
try container.encode(publishTournament, forKey: ._publishTournament)
try container.encode(hidePointsEarned, forKey: ._hidePointsEarned)
}
fileprivate func _encodePayment(container: inout KeyedEncodingContainer<CodingKeys>) throws {
@ -491,7 +496,7 @@ class Tournament : ModelObject, Storable {
func locationLabel(_ displayStyle: DisplayStyle = .wide) -> String {
if let club = club() {
switch displayStyle {
case .wide:
case .wide, .title:
return club.name
case .short:
return club.acronym
@ -1173,20 +1178,27 @@ class Tournament : ModelObject, Storable {
}
func tournamentTitle(_ displayStyle: DisplayStyle = .wide) -> String {
[tournamentLevel.localizedLabel(displayStyle) + " " + tournamentCategory.localizedLabel(displayStyle), displayStyle == .wide ? name : nil].compactMap({ $0 }).joined(separator: " - ")
if tournamentLevel == .unlisted, displayStyle == .title, let name {
return name
}
return [tournamentLevel.localizedLabel(displayStyle) + " " + tournamentCategory.localizedLabel(), displayStyle == .wide ? name : nil].compactMap({ $0 }).joined(separator: " - ")
}
func hideWeight() -> Bool {
tournamentLevel.hideWeight()
}
func isAnimation() -> Bool {
federalLevelCategory == .unlisted
}
func subtitle(_ displayStyle: DisplayStyle = .wide) -> String {
name ?? ""
}
func formattedDate(_ displayStyle: DisplayStyle = .wide) -> String {
switch displayStyle {
case .wide:
case .wide, .title:
startDate.formatted(date: Date.FormatStyle.DateStyle.complete, time: Date.FormatStyle.TimeStyle.omitted)
case .short:
startDate.formatted(date: .numeric, time: .omitted)

@ -15,6 +15,7 @@ enum DisplayContext {
}
enum DisplayStyle {
case title
case wide
case short
}

@ -210,7 +210,7 @@ enum FederalTournamentAge: Int, Hashable, Codable, CaseIterable, Identifiable {
func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String {
switch self {
case .unlisted:
return displayStyle == .wide ? "Aucune" : ""
return displayStyle == .title ? "Aucune" : ""
case .a11_12:
return "11/12 ans"
case .a13_14:
@ -402,7 +402,7 @@ enum TournamentLevel: Int, Hashable, Codable, CaseIterable, Identifiable {
}
func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String {
if self == .unlisted { return displayStyle == .wide ? "Animation" : "" }
if self == .unlisted { return displayStyle == .title ? "Animation" : "Anim." }
return String(describing: self).capitalized
}
@ -727,7 +727,7 @@ enum TournamentCategory: Int, Hashable, Codable, CaseIterable, Identifiable {
var buildLabel: String {
switch self {
case .unlisted:
return "-"
return ""
case .men:
return "H"
case .women:
@ -740,7 +740,7 @@ enum TournamentCategory: Int, Hashable, Codable, CaseIterable, Identifiable {
var requestLabel: String {
switch self {
case .unlisted:
return "-"
return ""
case .men:
return "DM"
case .women:
@ -753,7 +753,7 @@ enum TournamentCategory: Int, Hashable, Codable, CaseIterable, Identifiable {
var importingRawValue: String {
switch self {
case .unlisted:
return "-"
return ""
case .men:
return "messieurs"
case .women:
@ -766,13 +766,13 @@ enum TournamentCategory: Int, Hashable, Codable, CaseIterable, Identifiable {
func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String {
switch self {
case .unlisted:
return displayStyle == .wide ? "Aucune" : ""
return displayStyle == .title ? "Aucune" : ""
case .men:
return displayStyle == .wide ? "Hommes" : "H"
return displayStyle != .short ? "Hommes" : "H"
case .women:
return displayStyle == .wide ? "Dames" : "D"
return displayStyle != .short ? "Dames" : "D"
case .mix:
return displayStyle == .wide ? "Mixte" : "MX"
return displayStyle != .short ? "Mixte" : "MX"
}
}
@ -864,7 +864,7 @@ enum TeamPosition: Int, Identifiable, Hashable, Codable, CaseIterable {
}
switch displayStyle {
case .wide:
case .wide, .title:
return "Équipe " + shortName
case .short:
return shortName

@ -20,19 +20,25 @@ struct TournamentConfigurationView: View {
@ViewBuilder
var body: some View {
Picker(selection: $tournament.federalCategory, label: Text("Catégorie")) {
ForEach(TournamentCategory.allCases) { type in
Text(type.localizedLabel()).tag(type)
}
}
Picker(selection: $tournament.federalLevelCategory, label: Text("Niveau")) {
ForEach(TournamentLevel.allCases) { type in
Text(type.localizedLabel()).tag(type)
Text(type.localizedLabel(.title)).tag(type)
}
}
.onChange(of: tournament.federalLevelCategory) {
if tournament.federalLevelCategory == .unlisted {
tournament.federalCategory = .unlisted
tournament.federalAgeCategory = .unlisted
}
}
Picker(selection: $tournament.federalCategory, label: Text("Catégorie")) {
ForEach(TournamentCategory.allCases) { type in
Text(type.localizedLabel(.title)).tag(type)
}
}
Picker(selection: $tournament.federalAgeCategory, label: Text("Limite d'âge")) {
ForEach(FederalTournamentAge.allCases) { type in
Text(type.localizedLabel()).tag(type)
Text(type.localizedLabel(.title)).tag(type)
}
}
LabeledContent {

@ -76,6 +76,10 @@ struct DatePickingView: View {
FooterButtonView("retirer l'horaire bloqué") {
currentDate = nil
}
} else {
FooterButtonView("bloquer l'horaire") {
currentDate = startDate
}
}
}
.buttonStyle(.borderless)

@ -21,10 +21,14 @@ struct PlanningSettingsView: View {
init(tournament: Tournament) {
self.tournament = tournament
if let matchScheduler = tournament.matchScheduler() {
if matchScheduler.groupStageChunkCount == nil {
matchScheduler.groupStageChunkCount = tournament.getGroupStageChunkValue()
}
self.matchScheduler = matchScheduler
self._groupStageChunkCount = State(wrappedValue: matchScheduler.groupStageChunkCount ?? tournament.getGroupStageChunkValue())
} else {
self.matchScheduler = MatchScheduler(tournament: tournament.id)
self.matchScheduler = MatchScheduler(tournament: tournament.id, groupStageChunkCount: tournament.getGroupStageChunkValue())
self._groupStageChunkCount = State(wrappedValue: tournament.getGroupStageChunkValue())
}
}
@ -101,6 +105,22 @@ struct PlanningSettingsView: View {
let groupStagesWithDate = allGroupStages.filter({ $0.startDate != nil })
let roundsWithDate = allRounds.filter({ $0.startDate != nil })
if matchesWithDate.isEmpty == false || groupStagesWithDate.isEmpty == false || roundsWithDate.isEmpty == false {
Section {
RowButtonView("Supprimer les horaires des matches", role: .destructive) {
do {
allMatches.forEach({
$0.startDate = nil
$0.confirmed = false
})
try dataStore.matches.addOrUpdate(contentOfs: allMatches)
} catch {
Logger.error(error)
}
}
} footer: {
Text("Garde les horaires définis pour les poules et les manches.")
}
Section {
RowButtonView("Supprimer tous les horaires", role: .destructive) {
do {
@ -183,6 +203,10 @@ struct PlanningSettingsView: View {
}
Section {
Toggle(isOn: $matchScheduler.overrideCourtsUnavailability) {
Text("Ne pas tenir compte des autres épreuves")
}
Toggle(isOn: $matchScheduler.randomizeCourts) {
Text("Distribuer les terrains au hasard")
}

@ -8,20 +8,22 @@
import SwiftUI
struct TeamWeightView: View {
@Environment(Tournament.self) var tournament: Tournament
var team: TeamRegistration
var teamPosition: TeamPosition? = nil
var teamIndex: Int?
var displayWeight: Bool = true
init(team: TeamRegistration, teamPosition: TeamPosition? = nil) {
self.team = team
self.teamPosition = teamPosition
self.teamIndex = team.tournamentObject()?.indexOf(team: team)
let tournament = team.tournamentObject()
self.teamIndex = tournament?.indexOf(team: team)
self.displayWeight = tournament?.hideWeight() == false
}
var body: some View {
VStack(alignment: .trailing, spacing: 0) {
if (teamPosition == .one || teamPosition == nil) && tournament.hideWeight() == false {
if (teamPosition == .one || teamPosition == nil) && displayWeight {
Text(team.weight.formatted())
.monospacedDigit()
.font(.caption)
@ -30,7 +32,7 @@ struct TeamWeightView: View {
Text("#" + (teamIndex + 1).formatted(.number.precision(.integerLength(2...3))))
.monospacedDigit()
}
if teamPosition == .two && tournament.hideWeight() == false {
if teamPosition == .two && displayWeight {
Text(team.weight.formatted())
.monospacedDigit()
.font(.caption)

@ -13,19 +13,27 @@ struct TournamentLevelPickerView: View {
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)
Text(type.localizedLabel(.title)).tag(type)
}
}
.onChange(of: tournament.federalLevelCategory) {
if tournament.federalLevelCategory == .unlisted {
tournament.federalCategory = .unlisted
tournament.federalAgeCategory = .unlisted
}
}
Picker(selection: $tournament.tournamentCategory, label: Text("Catégorie")) {
ForEach(TournamentCategory.allCases) { type in
Text(type.localizedLabel(.title)).tag(type)
}
}
Picker(selection: $tournament.federalTournamentAge, label: Text("Limite d'âge")) {
ForEach(FederalTournamentAge.allCases) { type in
Text(type.localizedLabel()).tag(type)
Text(type.localizedLabel(.title)).tag(type)
}
}
Picker(selection: $tournament.groupStageOrderingMode, label: Text("Répartition en poule")) {

@ -16,7 +16,7 @@ struct TournamentRankView: View {
var body: some View {
List {
@Bindable var tournament = tournament
Section {
let matchs = tournament.runningMatches(tournament.allMatches())
let rankingPublished = tournament.selectedSortedTeams().allSatisfy({ $0.finalRanking != nil })
@ -35,13 +35,24 @@ struct TournamentRankView: View {
} label: {
Text("Classement publié")
}
Toggle(isOn: $tournament.hidePointsEarned) {
Text("Masquer les points gagnés")
}
.onChange(of: tournament.hidePointsEarned) {
do {
try dataStore.tournaments.addOrUpdate(instance: tournament)
} catch {
Logger.error(error)
}
}
RowButtonView(rankingPublished ? "Re-publier le classement" : "Publier le classement", role: .destructive) {
rankings.keys.sorted().forEach { rank in
if let rankedTeams = rankings[rank] {
rankedTeams.forEach { team in
team.finalRanking = rank
team.pointsEarned = tournament.tournamentLevel.points(for: rank - 1, count: tournament.teamCount)
team.pointsEarned = tournament.isAnimation() ? nil : tournament.tournamentLevel.points(for: rank - 1, count: tournament.teamCount)
}
}
}
@ -119,7 +130,7 @@ struct TournamentRankView: View {
}
}
if tournament.hideWeight() == false {
if tournament.isAnimation() == false {
Spacer()
VStack(alignment: .trailing) {
HStack(alignment: .lastTextBaseline, spacing: 0.0) {

@ -46,7 +46,7 @@ struct TournamentCellView: View {
switch displayStyle {
case .short:
DateVerticalView(date: tournament.startDate)
case .wide:
case .wide, .title:
VStack(alignment: .center, spacing: -2.0) {
Text(tournament.startDate.formatted(.dateTime.weekday(.abbreviated)).uppercased())
.font(.system(size: 14.0)).fontWeight(.medium)
@ -79,8 +79,8 @@ struct TournamentCellView: View {
.fontWeight(.semibold)
if displayStyle == .wide {
VStack(alignment: .leading, spacing: 0) {
Text(build.category.localizedLabel(.short))
Text(build.age.localizedLabel(.short))
Text(build.category.localizedLabel())
Text(build.age.localizedLabel())
}
.font(.caption)
}
@ -117,8 +117,8 @@ struct TournamentCellView: View {
}
Text(tournament.subtitleLabel()).lineLimit(1)
} else {
Text(build.category.localizedLabel(.short))
Text(build.age.localizedLabel(.short))
Text(build.category.localizedLabel())
Text(build.age.localizedLabel())
}
}
}

@ -115,7 +115,7 @@ struct TournamentView: View {
if let event = tournament.eventObject() {
Picker(selection: selectedTournamentId) {
ForEach(event.tournaments) { tournament in
Text(tournament.tournamentTitle()).tag(tournament.id as String)
Text(tournament.tournamentTitle(.title)).tag(tournament.id as String)
}
} label: {
@ -127,7 +127,7 @@ struct TournamentView: View {
.toolbar {
ToolbarItem(placement: .principal) {
VStack(spacing: -4.0) {
Text(tournament.tournamentTitle(.short)).font(.headline)
Text(tournament.tournamentTitle(.title)).font(.headline)
Text(tournament.formattedDate())
.font(.subheadline).foregroundStyle(.secondary)
}

@ -99,7 +99,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)
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)
let t = try await Store.main.service().post(tournament)
assert(t.event == tournament.event)
@ -137,6 +137,7 @@ final class ServerDataTests: XCTestCase {
assert(t.shouldVerifyGroupStage == tournament.shouldVerifyGroupStage)
assert(t.hideTeamsWeight == tournament.hideTeamsWeight)
assert(t.publishTournament == tournament.publishTournament)
assert(t.hidePointsEarned == tournament.hidePointsEarned)
}
func testGroupStage() async throws {

Loading…
Cancel
Save