From 06fb52ef4351bb0f613001561a566a9dd242c79d Mon Sep 17 00:00:00 2001 From: Razmig Sarkissian Date: Mon, 17 Jun 2024 07:33:30 +0200 Subject: [PATCH] fix ranking view --- PadelClub/Data/Tournament.swift | 7 +- .../Screen/TournamentRankView.swift | 236 +++++++++++------- 2 files changed, 153 insertions(+), 90 deletions(-) diff --git a/PadelClub/Data/Tournament.swift b/PadelClub/Data/Tournament.swift index 301a840..de777c3 100644 --- a/PadelClub/Data/Tournament.swift +++ b/PadelClub/Data/Tournament.swift @@ -1065,7 +1065,12 @@ class Tournament : ModelObject, Storable { let _limit = limit ?? courtCount return Array(allMatches.filter({ $0.hasEnded() }).sorted(by: \.computedEndDateForSorting).reversed().prefix(_limit)) } - + + func teamsRanked() -> [TeamRegistration] { + let selected = selectedSortedTeams().filter({ $0.finalRanking != nil }) + return selected.sorted(by: \.finalRanking!, order: .ascending) + } + func finalRanking() async -> [Int: [String]] { var teams: [Int: [String]] = [:] var ids: Set = Set() diff --git a/PadelClub/Views/Tournament/Screen/TournamentRankView.swift b/PadelClub/Views/Tournament/Screen/TournamentRankView.swift index 4c36727..bc29eb8 100644 --- a/PadelClub/Views/Tournament/Screen/TournamentRankView.swift +++ b/PadelClub/Views/Tournament/Screen/TournamentRankView.swift @@ -14,13 +14,21 @@ struct TournamentRankView: View { @State private var rankings: [Int: [TeamRegistration]] = [:] @State private var calculating = false + @State private var selectedTeam: TeamRegistration? + + var isEditingTeam: Binding { + Binding { + selectedTeam != nil + } set: { value in + } + } var body: some View { List { @Bindable var tournament = tournament + let matchs = tournament.runningMatches(tournament.allMatches()) + let rankingPublished = tournament.selectedSortedTeams().allSatisfy({ $0.finalRanking != nil }) Section { - let matchs = tournament.runningMatches(tournament.allMatches()) - let rankingPublished = tournament.selectedSortedTeams().allSatisfy({ $0.finalRanking != nil }) LabeledContent { Text(matchs.count.formatted()) } label: { @@ -30,8 +38,10 @@ struct TournamentRankView: View { LabeledContent { if rankingPublished { Image(systemName: "checkmark") + .foregroundStyle(.green) } else { Image(systemName: "xmark") + .foregroundStyle(.logoRed) } } label: { Text("Classement publié") @@ -41,107 +51,139 @@ struct TournamentRankView: View { Text("Masquer les points gagnés") } .onChange(of: tournament.hidePointsEarned) { - do { - try dataStore.tournaments.addOrUpdate(instance: tournament) - } catch { - Logger.error(error) - } + dataStore.tournaments.addOrUpdate(instance: tournament) } - 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.isAnimation() ? nil : tournament.tournamentLevel.points(for: rank - 1, count: tournament.teamCount) - } - } + if rankingPublished == false { + RowButtonView("Publier le classement", role: .destructive) { + _publishRankings() } - _save() } - } footer: { - FooterButtonView("masquer le classement", role: .destructive) { - tournament.unsortedTeams().forEach { team in - team.finalRanking = nil - team.pointsEarned = nil + } + + if rankingPublished { + Section { + RowButtonView("Supprimer le classement", role: .destructive) { + tournament.unsortedTeams().forEach { team in + team.finalRanking = nil + team.pointsEarned = nil + } + _save() } - _save() + } footer: { + Text(.init("Masque également le classement sur le site [Padel Club](\(URLs.main.rawValue))")) } } - let keys = rankings.keys.sorted() - ForEach(keys, id: \.self) { key in - if let rankedTeams = rankings[key] { - ForEach(rankedTeams) { team in - HStack { - VStack(alignment: .trailing) { - VStack(alignment: .trailing, spacing: -8.0) { - ZStack(alignment: .trailing) { - Text(tournament.teamCount.formatted()).hidden() - Text(key.formatted()) - } - .monospacedDigit() - .font(.largeTitle) - .fontWeight(.bold) - Text(key.ordinalFormattedSuffix()).font(.caption) - } - if let index = tournament.indexOf(team: team) { - let rankingDifference = index - (key - 1) - if rankingDifference > 0 { - HStack(spacing: 0.0) { - Text(rankingDifference.formatted(.number.sign(strategy: .always()))) - .monospacedDigit() - Image(systemName: "arrowtriangle.up.fill") - .imageScale(.small) + if rankingPublished { + Section { + ForEach(tournament.teamsRanked()) { team in + let key = team.finalRanking! + Button { + selectedTeam = team + } label: { + HStack { + VStack(alignment: .trailing) { + VStack(alignment: .trailing, spacing: -8.0) { + ZStack(alignment: .trailing) { + Text(tournament.teamCount.formatted()).hidden() + Text(key.formatted()) } - .foregroundColor(.green) - } else if rankingDifference < 0 { - HStack(spacing: 0.0) { - Text(rankingDifference.formatted(.number.sign(strategy: .always()))) - .monospacedDigit() - Image(systemName: "arrowtriangle.down.fill") - .imageScale(.small) + .monospacedDigit() + .font(.largeTitle) + .fontWeight(.bold) + Text(key.ordinalFormattedSuffix()).font(.caption) + } + if let index = tournament.indexOf(team: team) { + let rankingDifference = index - (key - 1) + if rankingDifference > 0 { + HStack(spacing: 0.0) { + Text(rankingDifference.formatted(.number.sign(strategy: .always()))) + .monospacedDigit() + Image(systemName: "arrowtriangle.up.fill") + .imageScale(.small) + } + .foregroundColor(.green) + } else if rankingDifference < 0 { + HStack(spacing: 0.0) { + Text(rankingDifference.formatted(.number.sign(strategy: .always()))) + .monospacedDigit() + Image(systemName: "arrowtriangle.down.fill") + .imageScale(.small) + } + .foregroundColor(.red) + } else { + Text("--") } - .foregroundColor(.red) - } else { - Text("--") } } - } - - - Divider() - - VStack(alignment: .leading) { - if let name = team.name { - Text(name).foregroundStyle(.secondary) - } - - ForEach(team.players()) { player in - VStack(alignment: .leading, spacing: -4.0) { - Text(player.playerLabel()).bold() - HStack(alignment: .firstTextBaseline, spacing: 0.0) { - Text(player.rankLabel()) - if let rank = player.getRank() { - Text(rank.ordinalFormattedSuffix()) - .font(.caption) + + + Divider() + + VStack(alignment: .leading) { + if let name = team.name { + Text(name).foregroundStyle(.secondary) + } + + ForEach(team.players()) { player in + VStack(alignment: .leading, spacing: -4.0) { + Text(player.playerLabel()).bold() + HStack(alignment: .firstTextBaseline, spacing: 0.0) { + Text(player.rankLabel()) + if let rank = player.getRank() { + Text(rank.ordinalFormattedSuffix()) + .font(.caption) + } } } } } - } - - if tournament.isAnimation() == false { - Spacer() - VStack(alignment: .trailing) { - HStack(alignment: .lastTextBaseline, spacing: 0.0) { - Text(tournament.tournamentLevel.points(for: key - 1, count: tournament.teamCount).formatted(.number.sign(strategy: .always()))) - Text("pts").font(.caption) + + if tournament.isAnimation() == false { + Spacer() + VStack(alignment: .trailing) { + HStack(alignment: .lastTextBaseline, spacing: 0.0) { + Text(tournament.tournamentLevel.points(for: key - 1, count: tournament.teamCount).formatted(.number.sign(strategy: .always()))) + Text("pts").font(.caption) + } } } } + .frame(maxWidth: .infinity) } + .contentShape(Rectangle()) + .buttonStyle(.plain) } + } footer: { + Text("Vous pouvez appuyer sur une ligne pour éditer manuellement le classement calculé par Padel Club.") + } + } +// let keys = rankings.keys.sorted() +// ForEach(keys, id: \.self) { key in +// if let rankedTeams = rankings[key] { +// ForEach(rankedTeams) { team in +// +// } +// } +// } + } + .alert("Position", isPresented: isEditingTeam) { + if let selectedTeam { + @Bindable var team = selectedTeam + TextField("Position", value: $team.finalRanking, format: .number) + .keyboardType(.numberPad) + .multilineTextAlignment(.trailing) + .frame(maxWidth: .infinity) + + Button("Valider") { + selectedTeam.pointsEarned = tournament.isAnimation() ? nil : tournament.tournamentLevel.points(for: selectedTeam.finalRanking! - 1, count: tournament.teamCount) + dataStore.teamRegistrations.addOrUpdate(instance: selectedTeam) + self.selectedTeam = nil + } + + Button("Annuler", role: .cancel) { + self.selectedTeam = nil } } } @@ -151,10 +193,14 @@ struct TournamentRankView: View { } }) .onAppear { - calculating = true - Task { - await calculateRankings() - calculating = false + let rankingPublished = tournament.selectedSortedTeams().allSatisfy({ $0.finalRanking != nil }) + if rankingPublished == false { + calculating = true + Task { + await _calculateRankings() + _publishRankings() + calculating = false + } } } .navigationTitle("Classement") @@ -163,13 +209,25 @@ struct TournamentRankView: View { .toolbar { ToolbarItem(placement: .topBarTrailing) { if let url = tournament.shareURL(.rankings) { - actionForURL(url) + _actionForURL(url) + } + } + } + } + + private func _publishRankings() { + rankings.keys.sorted().forEach { rank in + if let rankedTeams = rankings[rank] { + rankedTeams.forEach { team in + team.finalRanking = rank + team.pointsEarned = tournament.isAnimation() ? nil : tournament.tournamentLevel.points(for: rank - 1, count: tournament.teamCount) } } } + _save() } - func calculateRankings() async { + private func _calculateRankings() async { let finalRanks = await tournament.finalRanking() finalRanks.keys.sorted().forEach { rank in if let rankedTeamIds = finalRanks[rank] { @@ -179,7 +237,7 @@ struct TournamentRankView: View { } @ViewBuilder - func actionForURL(_ url: URL, removeSource: Bool = false) -> some View { + private func _actionForURL(_ url: URL, removeSource: Bool = false) -> some View { Menu { Button { UIApplication.shared.open(url)