Laurent 1 year ago
commit 78b71710c0
  1. 4
      PadelClub.xcodeproj/project.pbxproj
  2. 6
      PadelClub/Assets.xcassets/logoRed.colorset/Contents.json
  3. 7
      PadelClub/Data/Event.swift
  4. 6
      PadelClub/Data/GroupStage.swift
  5. 6
      PadelClub/Data/Match.swift
  6. 79
      PadelClub/Data/Round.swift
  7. 73
      PadelClub/Data/Tournament.swift
  8. 2
      PadelClub/Utils/PadelRule.swift
  9. 27
      PadelClub/ViewModel/SeedInterval.swift
  10. 2
      PadelClub/ViewModel/Selectable.swift
  11. 46
      PadelClub/Views/Components/FortuneWheelView.swift
  12. 4
      PadelClub/Views/Components/GenericDestinationPickerView.swift
  13. 2
      PadelClub/Views/Components/RowButtonView.swift
  14. 2
      PadelClub/Views/Match/Components/MatchTeamDetailView.swift
  15. 2
      PadelClub/Views/Navigation/Umpire/UmpireView.swift
  16. 2
      PadelClub/Views/Planning/CourtAvailabilitySettingsView.swift
  17. 100
      PadelClub/Views/Round/RoundView.swift
  18. 3
      PadelClub/Views/Team/Components/TeamHeaderView.swift
  19. 2
      PadelClub/Views/Team/TeamRowView.swift
  20. 2
      PadelClub/Views/Tournament/FileImportView.swift
  21. 8
      PadelClub/Views/Tournament/Screen/Components/InscriptionInfoView.swift
  22. 20
      PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift
  23. 2
      PadelClub/Views/Tournament/Screen/TournamentRankView.swift
  24. 4
      PadelClub/Views/Tournament/TournamentBuildView.swift
  25. 2
      PadelClub/Views/User/ChangePasswordView.swift
  26. 2
      PadelClub/Views/User/LoginView.swift

@ -1906,7 +1906,7 @@
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = PadelClub/PadelClub.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 88;
CURRENT_PROJECT_VERSION = 89;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEFINES_MODULE = YES;
DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\"";
@ -1950,7 +1950,7 @@
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = PadelClub/PadelClub.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 88;
CURRENT_PROJECT_VERSION = 89;
DEFINES_MODULE = YES;
DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\"";
DEVELOPMENT_TEAM = BQ3Y44M3Q6;

@ -23,9 +23,9 @@
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "1.000",
"green" : "1.000",
"red" : "1.000"
"blue" : "0.220",
"green" : "0.251",
"red" : "0.910"
}
},
"idiom" : "universal"

@ -32,7 +32,12 @@ final class Event: ModelObject, Storable {
}
override func deleteDependencies() throws {
DataStore.shared.tournaments.deleteDependencies(self.tournaments)
let tournaments = self.tournaments
for tournament in tournaments {
try tournament.deleteDependencies()
}
DataStore.shared.tournaments.deleteDependencies(tournaments)
DataStore.shared.dateIntervals.deleteDependencies(self.courtsUnavailability)
}

@ -381,7 +381,11 @@ final class GroupStage: ModelObject, Storable {
}
override func deleteDependencies() throws {
self.tournamentStore.matches.deleteDependencies(self._matches())
let matches = self._matches()
for match in matches {
try match.deleteDependencies()
}
self.tournamentStore.matches.deleteDependencies(matches)
}
func encode(to encoder: Encoder) throws {

@ -78,7 +78,11 @@ final class Match: ModelObject, Storable {
guard let tournament = self.currentTournament() else {
return
}
tournament.tournamentStore.teamScores.deleteDependencies(self.teamScores)
let teamScores = self.teamScores
for teamScore in teamScores {
try teamScore.deleteDependencies()
}
tournament.tournamentStore.teamScores.deleteDependencies(teamScores)
}
func indexInRound(in matches: [Match]? = nil) -> Int {

@ -455,12 +455,12 @@ defer {
return nextRound()?.isRankDisabled() == false
}
func seedInterval(expanded: Bool = false) -> SeedInterval? {
func seedInterval(initialMode: Bool = false) -> SeedInterval? {
#if DEBUG_TIME //DEBUGING TIME
let start = Date()
defer {
let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000)
print("func seedInterval(expanded: Bool = false)", duration.formatted(.units(allowed: [.seconds, .milliseconds])))
print("func seedInterval(initialMode)", initialMode, duration.formatted(.units(allowed: [.seconds, .milliseconds])))
}
#endif
@ -477,24 +477,24 @@ defer {
}
if let previousRound = previousRound() {
if previousRound.enabledMatches().isEmpty == false && expanded == false {
return previousRound.seedInterval()?.chunks()?.first
if (previousRound.enabledMatches().isEmpty == false || initialMode) {
return previousRound.seedInterval(initialMode: initialMode)?.chunks()?.first
} else {
return previousRound.previousRound()?.seedInterval()
return previousRound.previousRound()?.seedInterval(initialMode: initialMode)
}
} else if let parentRound {
if parentRound.parent == nil && expanded == false {
return parentRound.seedInterval()
if parentRound.parent == nil {
return parentRound.seedInterval(initialMode: initialMode)
}
return parentRound.seedInterval()?.chunks()?.last
return parentRound.seedInterval(initialMode: initialMode)?.chunks()?.last
}
return nil
}
func roundTitle(_ displayStyle: DisplayStyle = .wide) -> String {
func roundTitle(_ displayStyle: DisplayStyle = .wide, initialMode: Bool = false) -> String {
if parent != nil {
if let seedInterval = seedInterval() {
if let seedInterval = seedInterval(initialMode: initialMode) {
return seedInterval.localizedLabel(displayStyle)
}
print("Round pas trouvé", id, parent, index)
@ -556,7 +556,11 @@ defer {
let currentRoundMatchCount = RoundRule.numberOfMatches(forRoundIndex: index)
guard currentRoundMatchCount > 1 else { return }
let roundCount = RoundRule.numberOfRounds(forTeams: currentRoundMatchCount)
let loserBracketMatchFormat = tournamentObject()?.loserBracketMatchFormat
var loserBracketMatchFormat = tournamentObject()?.loserBracketMatchFormat
if let parentRound {
loserBracketMatchFormat = tournamentObject()?.loserBracketSmartMatchFormat(parentRound.index)
}
let rounds = (0..<roundCount).map { //index 0 is the final
let round = Round(tournament: tournament, index: $0, matchFormat: loserBracketMatchFormat)
@ -574,7 +578,8 @@ defer {
let matches = (0..<matchCount).map { //0 is final match
let roundIndex = RoundRule.roundIndex(fromMatchIndex: $0)
let round = rounds[roundIndex]
return Match(round: round.id, index: $0, matchFormat: loserBracketMatchFormat)
return Match(round: round.id, index: $0, matchFormat: loserBracketMatchFormat, name: round.roundTitle(initialMode: true))
//initial mode let the roundTitle give a name without considering the playable match
}
do {
@ -582,45 +587,10 @@ defer {
} catch {
Logger.error(error)
}
matches.forEach { //0 is final match
let roundIndex = RoundRule.roundIndex(fromMatchIndex: $0.index)
let round = rounds[roundIndex]
$0.name = round.roundTitle()
}
do {
try self.tournamentStore.matches.addOrUpdate(contentOfs: matches)
} catch {
Logger.error(error)
}
loserRounds().forEach { round in
round.buildLoserBracket()
}
/*
return Match(round: round.id, index: $0, matchFormat: loserBracketMatchFormat)
}
do {
try DataStore.shared.matches.addOrUpdate(contentOfs: matches)
} catch {
Logger.error(error)
}
matches.forEach {
$0.name = $0.roundObject?.roundTitle()
}
do {
try DataStore.shared.matches.addOrUpdate(contentOfs: matches)
} catch {
Logger.error(error)
}
*/
}
var parentRound: Round? {
@ -657,8 +627,19 @@ defer {
}
override func deleteDependencies() throws {
self.tournamentStore.matches.deleteDependencies(self._matches())
self.tournamentStore.rounds.deleteDependencies(self.loserRoundsAndChildren())
let matches = self._matches()
for match in matches {
try match.deleteDependencies()
}
self.tournamentStore.matches.deleteDependencies(matches)
let loserRounds = self.loserRounds()
for round in loserRounds {
try round.deleteDependencies()
}
self.tournamentStore.rounds.deleteDependencies(loserRounds)
}
enum CodingKeys: String, CodingKey {

@ -347,9 +347,24 @@ final class Tournament : ModelObject, Storable {
override func deleteDependencies() throws {
let store = self.tournamentStore
store.teamRegistrations.deleteDependencies(self.unsortedTeams())
store.groupStages.deleteDependencies(self.groupStages())
store.rounds.deleteDependencies(self.rounds())
let teams = self.unsortedTeams()
for team in teams {
try team.deleteDependencies()
}
store.teamRegistrations.deleteDependencies(teams)
let groups = self.groupStages()
for group in groups {
try group.deleteDependencies()
}
store.groupStages.deleteDependencies(groups)
let rounds = self.rounds()
for round in rounds {
try round.deleteDependencies()
}
store.rounds.deleteDependencies(rounds)
store.matchSchedulers.deleteDependencies(self._matchSchedulers())
}
@ -792,6 +807,12 @@ defer {
return teams + waitingListTeams(in: teams)
}
func waitingListSortedTeams() -> [TeamRegistration] {
let teams = selectedSortedTeams()
return waitingListTeams(in: teams)
}
func selectedSortedTeams() -> [TeamRegistration] {
#if DEBUG_TIME //DEBUGING TIME
let start = Date()
@ -860,7 +881,7 @@ defer {
return waitingList.filter { $0.walkOut == false }.sorted(using: _defaultSorting(), order: .ascending) + waitingList.filter { $0.walkOut == true }.sorted(using: _defaultSorting(), order: .ascending)
}
func bracketCut() -> Int {
func bracketCut(teamCount: Int) -> Int {
return max(0, teamCount - groupStageCut())
}
@ -868,10 +889,12 @@ defer {
return groupStageSpots()
}
func cutLabel(index: Int) -> String {
if index < bracketCut() {
func cutLabel(index: Int, teamCount: Int?) -> String {
let _teamCount = teamCount ?? selectedSortedTeams().count
let bracketCut = bracketCut(teamCount: _teamCount)
if index < bracketCut {
return "Tableau"
} else if index - bracketCut() < groupStageCut() {
} else if index - bracketCut < groupStageCut() && _teamCount > 0 {
return "Poule"
} else {
return "Attente"
@ -1172,12 +1195,12 @@ defer {
let disabledIds = playedMatches.flatMap({ $0.teamScores.compactMap({ $0.teamRegistration }) }).filter({ ids.contains($0) == false })
if disabledIds.isEmpty == false {
_removeStrings(from: &teams, stringsToRemove: disabledIds)
teams[interval.computedLast] = disabledIds
teams[interval.last] = disabledIds
let teamNames : [String] = disabledIds.compactMap {
let t : TeamRegistration? = Store.main.findById($0)
return t
}.map { $0.canonicalName }
print("winners.isEmpty", "\(interval.computedLast) : ", teamNames)
print("winners.isEmpty", "\(interval.last) : ", teamNames)
disabledIds.forEach {
ids.insert($0)
}
@ -1185,23 +1208,23 @@ defer {
} else {
if winners.isEmpty == false {
_removeStrings(from: &teams, stringsToRemove: winners)
teams[interval.computedFirst + winners.count - 1] = winners
teams[interval.first + winners.count - 1] = winners
let teamNames : [String] = winners.compactMap {
let t: TeamRegistration? = Store.main.findById($0)
return t
}.map { $0.canonicalName }
print("winners", "\(interval.computedFirst + winners.count - 1) : ", teamNames)
print("winners", "\(interval.last + winners.count - 1) : ", teamNames)
winners.forEach { ids.insert($0) }
}
if losers.isEmpty == false {
_removeStrings(from: &teams, stringsToRemove: losers)
teams[interval.computedLast] = losers
teams[interval.last] = losers
let loserTeamNames : [String] = losers.compactMap {
let t: TeamRegistration? = Store.main.findById($0)
return t
}.map { $0.canonicalName }
print("losers", "\(interval.computedLast) : ", loserTeamNames)
print("losers", "\(interval.last) : ", loserTeamNames)
losers.forEach { ids.insert($0) }
}
}
@ -1514,6 +1537,7 @@ defer {
}
func deleteAndBuildEverything() {
resetBracketPosition()
deleteStructure()
deleteGroupStages()
buildGroupStages()
@ -1527,7 +1551,7 @@ defer {
var _groupStages = [GroupStage]()
for index in 0..<groupStageCount {
let groupStage = GroupStage(tournament: id, index: index, size: teamsPerGroupStage, matchFormat: groupStageMatchFormat)
let groupStage = GroupStage(tournament: id, index: index, size: teamsPerGroupStage, matchFormat: groupStageSmartMatchFormat())
_groupStages.append(groupStage)
}
@ -1550,7 +1574,7 @@ defer {
let roundCount = RoundRule.numberOfRounds(forTeams: bracketTeamCount())
let rounds = (0..<roundCount).map { //index 0 is the final
Round(tournament: id, index: $0)
return Round(tournament: id, index: $0, matchFormat: roundSmartMatchFormat($0))
}
do {
@ -1563,7 +1587,7 @@ defer {
let matches = (0..<matchCount).map { //0 is final match
let roundIndex = RoundRule.roundIndex(fromMatchIndex: $0)
let round = rounds[roundIndex]
return Match(round: round.id, index: $0, matchFormat: matchFormat, name: Match.setServerTitle(upperRound: round, matchIndex: RoundRule.matchIndexWithinRound(fromMatchIndex: $0)))
return Match(round: round.id, index: $0, matchFormat: round.matchFormat, name: Match.setServerTitle(upperRound: round, matchIndex: RoundRule.matchIndexWithinRound(fromMatchIndex: $0)))
}
print(matches.map {
@ -1576,7 +1600,7 @@ defer {
Logger.error(error)
}
self.rounds().forEach { round in
rounds.forEach { round in
round.buildLoserBracket()
}
}
@ -1609,6 +1633,9 @@ defer {
} catch {
Logger.error(error)
}
}
func resetBracketPosition() {
unsortedTeams().forEach({ $0.bracketPosition = nil })
do {
try self.tournamentStore.teamRegistrations.addOrUpdate(contentOfs: unsortedTeams())
@ -1623,16 +1650,6 @@ defer {
} catch {
Logger.error(error)
}
do {
unsortedTeams().forEach({
$0.groupStage = nil
$0.groupStagePosition = nil
})
try self.tournamentStore.teamRegistrations.addOrUpdate(contentOfs: unsortedTeams())
} catch {
Logger.error(error)
}
}
func refreshGroupStages() {
@ -1774,7 +1791,7 @@ defer {
teamSorting = newValue.defaultTeamSortingType
groupStageMatchFormat = groupStageSmartMatchFormat()
loserBracketMatchFormat = loserBracketSmartMatchFormat(1)
matchFormat = roundSmartMatchFormat(1)
matchFormat = roundSmartMatchFormat(5)
}
}

@ -329,7 +329,7 @@ enum TournamentLevel: Int, Hashable, Codable, CaseIterable, Identifiable {
case .p500:
if roundIndex == 0 { //finale
return .twoSetsDecisivePoint
return .twoSetsDecisivePointSuperTie
} else if roundIndex == 1 { //demi-finale
return .twoSetsDecisivePointSuperTie
} else {

@ -10,11 +10,10 @@ import Foundation
struct SeedInterval: Hashable, Comparable {
let first: Int
let last: Int
var reduce: Int = 0
func pointsRange(tournamentLevel: TournamentLevel, teamsCount: Int) -> String {
let range = [tournamentLevel.points(for: last - 1 - reduce, count: teamsCount),
tournamentLevel.points(for: first - 1 - reduce, count: teamsCount)]
let range = [tournamentLevel.points(for: last - 1, count: teamsCount),
tournamentLevel.points(for: first - 1, count: teamsCount)]
return range.map { $0.formatted(.number.sign(strategy: .always())) }.joined(separator: " / ") + " pts"
}
@ -38,32 +37,24 @@ struct SeedInterval: Hashable, Comparable {
if dimension > 3 {
let split = dimension / 2
if split%2 == 0 {
let firstHalf = SeedInterval(first: first, last: first + split - 1, reduce: reduce)
let secondHalf = SeedInterval(first: first + split, last: last, reduce: reduce)
let firstHalf = SeedInterval(first: first, last: first + split - 1)
let secondHalf = SeedInterval(first: first + split, last: last)
return [firstHalf, secondHalf]
} else {
let firstHalf = SeedInterval(first: first, last: first + split, reduce: reduce)
let secondHalf = SeedInterval(first: first + split + 1, last: last, reduce: reduce)
let firstHalf = SeedInterval(first: first, last: first + split)
let secondHalf = SeedInterval(first: first + split + 1, last: last)
return [firstHalf, secondHalf]
}
} else {
return nil
}
}
var computedLast: Int {
last - reduce
}
var computedFirst: Int {
first - reduce
}
func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String {
if dimension < 2 {
return "#\(first - reduce) / #\(last - reduce)"
return "#\(first) / #\(last)"
} else {
return "#\(first - reduce) à #\(last - reduce)"
return "#\(first) à #\(last)"
}
}
}

@ -43,7 +43,7 @@ enum Badge {
case .checkmark:
.green
case .xmark:
.red
.logoRed
case .custom(_, let color):
color
}

@ -62,26 +62,29 @@ struct SpinDrawView: View {
let drawees: [any SpinDrawable]
@State var segments: [any SpinDrawable]
var autoMode: Bool = false
let completion: ([DrawResult]) async -> Void // Completion closure
let completion: ([DrawResult]) -> Void // Completion closure
@State private var drawCount: Int = 0
@State private var draws: [DrawResult] = [DrawResult]()
@State private var drawOptions: [DrawOption] = [DrawOption]()
@State private var selectedIndex: Int?
@State private var disabled: Bool = false
@State private var validating: Bool = false
var scrollDisabled: Bool {
drawCount < drawees.count || selectedIndex != nil
}
var body: some View {
List {
if selectedIndex != nil {
Section {
_validationLabelView(drawee: drawCount, result: segments[draws.last!.drawIndex])
_validationLabelView(drawee: drawCount, result: segments[draws.last!.drawIndex])
if autoMode == false || drawCount == drawees.count {
RowButtonView("Valider le tirage") {
await completion(draws)
completion(draws)
dismiss()
}
} else {
Text("Prochain tirage en préparation")
}
}
} else if drawCount < drawees.count {
@ -135,16 +138,17 @@ struct SpinDrawView: View {
}
}
} else {
Section {
Text("Tous les tirages sont terminés")
ForEach(draws) { drawResult in
ForEach(draws) { drawResult in
Section {
_validationLabelView(drawee: drawResult.drawee, result: segments[drawResult.drawIndex])
}
}
RowButtonView("Valider les tirages") {
await completion(draws)
dismiss()
Section {
RowButtonView("Valider les tirages") {
await completion(draws)
dismiss()
}
}
}
@ -164,6 +168,22 @@ struct SpinDrawView: View {
}
.disabled(disabled || autoMode)
}
if scrollDisabled == false {
ToolbarItem(placement: .topBarTrailing) {
Button {
validating = true
completion(draws)
dismiss()
} label: {
Text("Tout valider")
}
}
ToolbarItem(placement: .status) {
Text("Tous les tirages sont terminés")
}
}
}
.navigationBarBackButtonHidden()
.navigationTitle("Tirage au sort")
@ -171,7 +191,7 @@ struct SpinDrawView: View {
.toolbarBackground(.visible, for: .navigationBar)
.toolbar(.hidden, for: .tabBar)
.listStyle(.insetGrouped)
.scrollDisabled(true)
.scrollDisabled(scrollDisabled)
.interactiveDismissDisabled()
.onAppear {
for (index, segment) in segments.enumerated() {
@ -219,7 +239,7 @@ struct FortuneWheelContainerView: View {
.padding(.top, 5)
.overlay(alignment: .top) {
Triangle()
.fill(Color.red)
.fill(Color.logoRed)
.stroke(Color.black, lineWidth: 2)
.frame(width: 20, height: 20)
.rotationEffect(.degrees(180))

@ -62,7 +62,7 @@ struct GenericDestinationPickerView<T: Identifiable & Selectable & Equatable >:
.offset(x: 3, y: 3)
} else if let count, count > 0 {
Image(systemName: count <= 50 ? "\(String(count)).circle.fill" : "plus.circle.fill")
.foregroundColor(destination.badgeValueColor() ?? .red)
.foregroundColor(destination.badgeValueColor() ?? .logoRed)
.imageScale(.medium)
.background (
Color(.systemBackground)
@ -82,7 +82,7 @@ struct GenericDestinationPickerView<T: Identifiable & Selectable & Equatable >:
.offset(x: 3, y: 3)
} else if let count = destination.badgeValue(), count > 0 {
Image(systemName: count <= 50 ? "\(String(count)).circle.fill" : "plus.circle.fill")
.foregroundColor(destination.badgeValueColor() ?? .red)
.foregroundColor(destination.badgeValueColor() ?? .logoRed)
.imageScale(.medium)
.background (
Color(.systemBackground)

@ -87,7 +87,7 @@ struct RowButtonView: View {
.disabled(isLoading)
.frame(maxWidth: .infinity)
.buttonStyle(.borderedProminent)
.tint(role == .destructive ? Color.red : Color.master)
.tint(role == .destructive ? Color.logoRed : Color.master)
.listRowBackground(Color.clear)
.listRowInsets(EdgeInsets(.zero))
.confirmationDialog("Confirmation",

@ -34,7 +34,7 @@ struct MatchTeamDetailView: View {
EditablePlayerView(player: player, editingOptions: [.licenceId, .name, .payment])
}
} header: {
TeamHeaderView(team: team, teamIndex: tournament?.indexOf(team: team), tournament: nil)
TeamHeaderView(team: team, teamIndex: tournament?.indexOf(team: team))
}
}

@ -134,7 +134,7 @@ struct UmpireView: View {
} else {
LabeledContent {
Image(systemName: "xmark.circle.fill")
.tint(.red)
.tint(.logoRed)
} label: {
if let _mostRecentDateAvailable {
Text(_mostRecentDateAvailable.monthYearFormatted)

@ -67,7 +67,7 @@ struct CourtAvailabilitySettingsView: View {
VStack {
Image(systemName: "arrowshape.forward.fill")
.tint(.master)
Text("indisponible").foregroundStyle(.red).font(.caption)
Text("indisponible").foregroundStyle(.logoRed).font(.caption)
}
Spacer()
VStack(alignment: .trailing, spacing: 0) {

@ -28,7 +28,7 @@ struct RoundView: View {
return self.tournament.tournamentStore
}
private func _getAvailableSeedGroup() async {
private func _getAvailableSeedGroup() {
#if DEBUG_TIME //DEBUGING TIME
let start = Date()
defer {
@ -40,7 +40,7 @@ struct RoundView: View {
availableSeedGroup = tournament.seedGroupAvailable(atRoundIndex: upperRound.round.index)
}
private func _getSpaceLeft() async {
private func _getSpaceLeft() {
#if DEBUG_TIME //DEBUGING TIME
let start = Date()
defer {
@ -130,12 +130,11 @@ struct RoundView: View {
Section {
RowButtonView("Placer \(availableSeedGroup.localizedLabel())" + ((availableSeedGroup.isFixed() == false) ? " au hasard" : "")) {
tournament.setSeeds(inRoundIndex: upperRound.round.index, inSeedGroup: availableSeedGroup)
await _save()
_save()
if tournament.availableSeeds().isEmpty && tournament.availableQualifiedTeams().isEmpty {
self.isEditingTournamentSeed.wrappedValue = false
}
await _getSpaceLeft()
await _getAvailableSeedGroup()
_prepareRound()
}
} footer: {
if availableSeedGroup.isFixed() == false {
@ -161,17 +160,14 @@ struct RoundView: View {
ForEach(availableQualifiedTeams) { team in
NavigationLink {
SpinDrawView(drawees: [team], segments: spaceLeft) { results in
Task {
results.forEach { drawResult in
team.setSeedPosition(inSpot: spaceLeft[drawResult.drawIndex], slot: nil, opposingSeeding: true)
}
await _save()
if tournament.availableSeeds().isEmpty && tournament.availableQualifiedTeams().isEmpty {
self.isEditingTournamentSeed.wrappedValue = false
}
await _getSpaceLeft()
await _getAvailableSeedGroup()
results.forEach { drawResult in
team.setSeedPosition(inSpot: spaceLeft[drawResult.drawIndex], slot: nil, opposingSeeding: true)
}
_save()
if tournament.availableSeeds().isEmpty && tournament.availableQualifiedTeams().isEmpty {
self.isEditingTournamentSeed.wrappedValue = false
}
_prepareRound()
}
} label: {
TeamRowView(team: team, displayCallDate: false)
@ -193,17 +189,14 @@ struct RoundView: View {
ForEach(availableSeeds) { team in
NavigationLink {
SpinDrawView(drawees: [team], segments: seedSpaceLeft) { results in
Task {
results.forEach { drawResult in
team.setSeedPosition(inSpot: seedSpaceLeft[drawResult.drawIndex], slot: nil, opposingSeeding: false)
}
await _save()
if tournament.availableSeeds().isEmpty && tournament.availableQualifiedTeams().isEmpty {
self.isEditingTournamentSeed.wrappedValue = false
}
await _getSpaceLeft()
await _getAvailableSeedGroup()
results.forEach { drawResult in
team.setSeedPosition(inSpot: seedSpaceLeft[drawResult.drawIndex], slot: nil, opposingSeeding: false)
}
_save()
if tournament.availableSeeds().isEmpty && tournament.availableQualifiedTeams().isEmpty {
self.isEditingTournamentSeed.wrappedValue = false
}
_prepareRound()
}
} label: {
TeamRowView(team: team, displayCallDate: false)
@ -221,17 +214,14 @@ struct RoundView: View {
ForEach(availableSeeds) { team in
NavigationLink {
SpinDrawView(drawees: [team], segments: spaceLeft) { results in
Task {
results.forEach { drawResult in
team.setSeedPosition(inSpot: spaceLeft[drawResult.drawIndex], slot: nil, opposingSeeding: true)
}
await _save()
if tournament.availableSeeds().isEmpty && tournament.availableQualifiedTeams().isEmpty {
self.isEditingTournamentSeed.wrappedValue = false
}
await _getSpaceLeft()
await _getAvailableSeedGroup()
results.forEach { drawResult in
team.setSeedPosition(inSpot: spaceLeft[drawResult.drawIndex], slot: nil, opposingSeeding: true)
}
_save()
if tournament.availableSeeds().isEmpty && tournament.availableQualifiedTeams().isEmpty {
self.isEditingTournamentSeed.wrappedValue = false
}
_prepareRound()
}
} label: {
TeamRowView(team: team, displayCallDate: false)
@ -283,9 +273,7 @@ struct RoundView: View {
PrintSettingsView(tournament: tournament)
}
.onAppear {
Task {
await _prepareRound()
}
_prepareRound()
let seeds = upperRound.round.seeds()
SlideToDeleteSeedTip.seeds = seeds.count
PrintTip.seeds = seeds.count
@ -294,17 +282,19 @@ struct RoundView: View {
.fullScreenCover(isPresented: showVisualDrawView) {
if let availableSeedGroup = selectedSeedGroup {
let seeds = tournament.seeds(inSeedGroup: availableSeedGroup)
let availableSeedSpot = tournament.availableSeedSpot(inRoundIndex: upperRound.round.index)
let opposingSeeding = seedSpaceLeft.isEmpty ? true : false
let availableSeedSpot = opposingSeeding ? spaceLeft : seedSpaceLeft
NavigationStack {
SpinDrawView(drawees: seeds, segments: availableSeedSpot, autoMode: true) { draws in
Task {
draws.forEach { drawResult in
seeds[drawResult.drawee].setSeedPosition(inSpot: availableSeedSpot[drawResult.drawIndex], slot: nil, opposingSeeding: false)
}
await _save()
if tournament.availableSeeds().isEmpty && tournament.availableQualifiedTeams().isEmpty {
self.isEditingTournamentSeed.wrappedValue = false
}
draws.forEach { drawResult in
seeds[drawResult.drawee].setSeedPosition(inSpot: availableSeedSpot[drawResult.drawIndex], slot: nil, opposingSeeding: opposingSeeding)
}
_save()
_prepareRound()
if tournament.availableSeeds().isEmpty && tournament.availableQualifiedTeams().isEmpty {
self.isEditingTournamentSeed.wrappedValue = false
}
}
}
@ -315,9 +305,7 @@ struct RoundView: View {
ToolbarItem(placement: .topBarTrailing) {
Button(isEditingTournamentSeed.wrappedValue == true ? "Valider" : "Modifier") {
if isEditingTournamentSeed.wrappedValue == true {
Task {
await _save()
}
_save()
}
isEditingTournamentSeed.wrappedValue.toggle()
}
@ -325,7 +313,7 @@ struct RoundView: View {
}
}
private func _save() async {
private func _save() {
do {
try self.tournamentStore.teamRegistrations.addOrUpdate(contentOfs: tournament.unsortedTeams())
} catch {
@ -348,13 +336,9 @@ struct RoundView: View {
}
}
private func _prepareRound() async {
Task {
await _getSpaceLeft()
}
Task {
await _getAvailableSeedGroup()
}
private func _prepareRound() {
_getSpaceLeft()
_getAvailableSeedGroup()
}
}

@ -11,6 +11,7 @@ struct TeamHeaderView: View {
var team: TeamRegistration
var teamIndex: Int?
var tournament: Tournament?
var teamCount: Int?
var body: some View {
HStack(spacing: 16.0) {
@ -46,7 +47,7 @@ struct TeamHeaderView: View {
} else {
Text("").font(.caption)
}
Text(tournament.cutLabel(index: teamIndex))
Text(tournament.cutLabel(index: teamIndex, teamCount: teamCount))
}
}
}

@ -26,7 +26,7 @@ struct TeamRowView: View {
}
if let callDate = team.callDate, displayCallDate {
Text("Déjà convoquée \(callDate.localizedDate())")
.foregroundStyle(.red)
.foregroundStyle(.logoRed)
.italic()
.font(.caption)
}

@ -492,7 +492,7 @@ struct FileImportView: View {
}
if let callDate = team.previousTeam?.callDate, let newDate = tournament.getStartDate(ofSeedIndex: newIndex), callDate != newDate {
Text("Attention, cette paire a déjà été convoquée à \(callDate.localizedDate())")
.foregroundStyle(.red)
.foregroundStyle(.logoRed)
.italic()
.font(.caption)
}

@ -91,7 +91,7 @@ struct InscriptionInfoView: View {
Text("Dans le tableau")
}
}
.listRowView(color: .red)
.listRowView(color: .logoRed)
DisclosureGroup {
ForEach(waitingListInGroupStage) { team in
@ -104,7 +104,7 @@ struct InscriptionInfoView: View {
Text("En poule")
}
}
.listRowView(color: .red)
.listRowView(color: .logoRed)
} header: {
Text("Équipes ne devant plus être sélectionnées")
} footer: {
@ -123,7 +123,7 @@ struct InscriptionInfoView: View {
Text("Doublons")
}
}
.listRowView(color: .red)
.listRowView(color: .logoRed)
}
Section {
@ -155,7 +155,7 @@ struct InscriptionInfoView: View {
Text("Joueurs trop bien classés")
}
}
.listRowView(color: .red)
.listRowView(color: .logoRed)
} footer: {
Text("Il s'agit des joueurs ou joueuses dont le rang est inférieur à la limite fédérale.")
}

@ -98,6 +98,7 @@ struct InscriptionManagerView: View {
var id: Int { self.rawValue }
case all
case walkOut
case waiting
func localizedLabel() -> String {
switch self {
@ -105,6 +106,8 @@ struct InscriptionManagerView: View {
return "Toutes les équipes"
case .walkOut:
return "Voir les WOs"
case .waiting:
return "Liste d'attente"
}
}
}
@ -468,7 +471,11 @@ struct InscriptionManagerView: View {
print("func _prepareTeams", duration.formatted(.units(allowed: [.seconds, .milliseconds])))
}
#endif
sortedTeams = tournament.sortedTeams()
if filterMode == .waiting {
sortedTeams = tournament.waitingListSortedTeams()
} else {
sortedTeams = tournament.sortedTeams()
}
}
var filteredTeams: [TeamRegistration] {
@ -511,12 +518,19 @@ struct InscriptionManagerView: View {
private func _teamRegisteredView() -> some View {
List {
let selectedSortedTeams = tournament.selectedSortedTeams()
if let closedRegistrationDate = tournament.closedRegistrationDate {
Section {
CloseDatePicker(closedRegistrationDate: closedRegistrationDate)
} footer: {
Text("Toutes les équipes ayant été inscrites après la date de clôture seront en liste d'attente.")
}
if selectedSortedTeams.isEmpty {
Section {
ContentUnavailableView("Aucune équipe", systemImage: "person.2.slash", description: Text("Vous n'avez aucune équipe inscrite avant la date de clôture."))
}
}
}
if presentSearch == false {
@ -560,7 +574,7 @@ struct InscriptionManagerView: View {
Section {
TeamDetailView(team: team)
} header: {
TeamHeaderView(team: team, teamIndex: teamIndex, tournament: tournament)
TeamHeaderView(team: team, teamIndex: teamIndex, tournament: tournament, teamCount: filterMode == .waiting ? 0 : selectedSortedTeams.count)
} footer: {
_teamFooterView(team)
}
@ -722,7 +736,7 @@ struct InscriptionManagerView: View {
}
Section {
ContentUnavailableView("Aucune équipe", systemImage: "person.2.slash", description: Text("Vous n'avez encore aucune équipe dans votre liste d'attente."))
ContentUnavailableView("Aucune équipe", systemImage: "person.2.slash", description: Text("Vous n'avez encore aucune équipe inscrite dans votre tournoi."))
}
_rankHandlerView()

@ -213,7 +213,7 @@ struct TournamentRankView: View {
Image(systemName: "arrowtriangle.down.fill")
.imageScale(.small)
}
.foregroundColor(.red)
.foregroundColor(.logoRed)
} else {
Text("--")
}

@ -102,7 +102,7 @@ struct TournamentBuildView: View {
} label: {
Text("Classement final des équipes")
if tournament.publishRankings == false {
Text("Vérifiez le classement avant de publier").foregroundStyle(.red)
Text("Vérifiez le classement avant de publier").foregroundStyle(.logoRed)
}
}
}
@ -120,7 +120,7 @@ struct TournamentBuildView: View {
} label: {
Text("Classement final des équipes")
if tournament.publishRankings == false {
Text("Vérifiez le classement avant de publier").foregroundStyle(.red)
Text("Vérifiez le classement avant de publier").foregroundStyle(.logoRed)
}
}
}

@ -46,7 +46,7 @@ struct ChangePasswordView: View {
.frame(maxWidth: .infinity)
} footer: {
if self.errorMessage.count > 0 {
Text(self.errorMessage).foregroundStyle(.red)
Text(self.errorMessage).foregroundStyle(.logoRed)
}
}
}

@ -131,7 +131,7 @@ struct LoginView: View {
}
.disabled(password.isEmpty || username.isEmpty)
// if let error = self.errorText {
// Text(error).font(.callout).foregroundStyle(.red)
// Text(error).font(.callout).foregroundStyle(.logoRed)
// }
}

Loading…
Cancel
Save