fix ranking view

multistore
Razmig Sarkissian 1 year ago
parent 7c6b22df92
commit 06fb52ef43
  1. 5
      PadelClub/Data/Tournament.swift
  2. 226
      PadelClub/Views/Tournament/Screen/TournamentRankView.swift

@ -1066,6 +1066,11 @@ class Tournament : ModelObject, Storable {
return Array(allMatches.filter({ $0.hasEnded() }).sorted(by: \.computedEndDateForSorting).reversed().prefix(_limit)) 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]] { func finalRanking() async -> [Int: [String]] {
var teams: [Int: [String]] = [:] var teams: [Int: [String]] = [:]
var ids: Set<String> = Set<String>() var ids: Set<String> = Set<String>()

@ -14,13 +14,21 @@ struct TournamentRankView: View {
@State private var rankings: [Int: [TeamRegistration]] = [:] @State private var rankings: [Int: [TeamRegistration]] = [:]
@State private var calculating = false @State private var calculating = false
@State private var selectedTeam: TeamRegistration?
var isEditingTeam: Binding<Bool> {
Binding {
selectedTeam != nil
} set: { value in
}
}
var body: some View { var body: some View {
List { List {
@Bindable var tournament = tournament @Bindable var tournament = tournament
let matchs = tournament.runningMatches(tournament.allMatches())
let rankingPublished = tournament.selectedSortedTeams().allSatisfy({ $0.finalRanking != nil })
Section { Section {
let matchs = tournament.runningMatches(tournament.allMatches())
let rankingPublished = tournament.selectedSortedTeams().allSatisfy({ $0.finalRanking != nil })
LabeledContent { LabeledContent {
Text(matchs.count.formatted()) Text(matchs.count.formatted())
} label: { } label: {
@ -30,8 +38,10 @@ struct TournamentRankView: View {
LabeledContent { LabeledContent {
if rankingPublished { if rankingPublished {
Image(systemName: "checkmark") Image(systemName: "checkmark")
.foregroundStyle(.green)
} else { } else {
Image(systemName: "xmark") Image(systemName: "xmark")
.foregroundStyle(.logoRed)
} }
} label: { } label: {
Text("Classement publié") Text("Classement publié")
@ -41,107 +51,139 @@ struct TournamentRankView: View {
Text("Masquer les points gagnés") Text("Masquer les points gagnés")
} }
.onChange(of: tournament.hidePointsEarned) { .onChange(of: tournament.hidePointsEarned) {
do { dataStore.tournaments.addOrUpdate(instance: tournament)
try dataStore.tournaments.addOrUpdate(instance: tournament)
} catch {
Logger.error(error)
}
} }
RowButtonView(rankingPublished ? "Re-publier le classement" : "Publier le classement", role: .destructive) { if rankingPublished == false {
rankings.keys.sorted().forEach { rank in RowButtonView("Publier le classement", role: .destructive) {
if let rankedTeams = rankings[rank] { _publishRankings()
rankedTeams.forEach { team in
team.finalRanking = rank
team.pointsEarned = tournament.isAnimation() ? nil : tournament.tournamentLevel.points(for: rank - 1, count: tournament.teamCount)
}
}
} }
_save()
} }
} footer: { }
FooterButtonView("masquer le classement", role: .destructive) {
tournament.unsortedTeams().forEach { team in if rankingPublished {
team.finalRanking = nil Section {
team.pointsEarned = nil 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() if rankingPublished {
ForEach(keys, id: \.self) { key in Section {
if let rankedTeams = rankings[key] { ForEach(tournament.teamsRanked()) { team in
ForEach(rankedTeams) { team in let key = team.finalRanking!
HStack { Button {
VStack(alignment: .trailing) { selectedTeam = team
VStack(alignment: .trailing, spacing: -8.0) { } label: {
ZStack(alignment: .trailing) { HStack {
Text(tournament.teamCount.formatted()).hidden() VStack(alignment: .trailing) {
Text(key.formatted()) VStack(alignment: .trailing, spacing: -8.0) {
} ZStack(alignment: .trailing) {
.monospacedDigit() Text(tournament.teamCount.formatted()).hidden()
.font(.largeTitle) Text(key.formatted())
.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) .monospacedDigit()
} else if rankingDifference < 0 { .font(.largeTitle)
HStack(spacing: 0.0) { .fontWeight(.bold)
Text(rankingDifference.formatted(.number.sign(strategy: .always()))) Text(key.ordinalFormattedSuffix()).font(.caption)
.monospacedDigit() }
Image(systemName: "arrowtriangle.down.fill") if let index = tournament.indexOf(team: team) {
.imageScale(.small) 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() Divider()
VStack(alignment: .leading) { VStack(alignment: .leading) {
if let name = team.name { if let name = team.name {
Text(name).foregroundStyle(.secondary) Text(name).foregroundStyle(.secondary)
} }
ForEach(team.players()) { player in ForEach(team.players()) { player in
VStack(alignment: .leading, spacing: -4.0) { VStack(alignment: .leading, spacing: -4.0) {
Text(player.playerLabel()).bold() Text(player.playerLabel()).bold()
HStack(alignment: .firstTextBaseline, spacing: 0.0) { HStack(alignment: .firstTextBaseline, spacing: 0.0) {
Text(player.rankLabel()) Text(player.rankLabel())
if let rank = player.getRank() { if let rank = player.getRank() {
Text(rank.ordinalFormattedSuffix()) Text(rank.ordinalFormattedSuffix())
.font(.caption) .font(.caption)
}
} }
} }
} }
} }
}
if tournament.isAnimation() == false { if tournament.isAnimation() == false {
Spacer() Spacer()
VStack(alignment: .trailing) { VStack(alignment: .trailing) {
HStack(alignment: .lastTextBaseline, spacing: 0.0) { HStack(alignment: .lastTextBaseline, spacing: 0.0) {
Text(tournament.tournamentLevel.points(for: key - 1, count: tournament.teamCount).formatted(.number.sign(strategy: .always()))) Text(tournament.tournamentLevel.points(for: key - 1, count: tournament.teamCount).formatted(.number.sign(strategy: .always())))
Text("pts").font(.caption) 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 { .onAppear {
calculating = true let rankingPublished = tournament.selectedSortedTeams().allSatisfy({ $0.finalRanking != nil })
Task { if rankingPublished == false {
await calculateRankings() calculating = true
calculating = false Task {
await _calculateRankings()
_publishRankings()
calculating = false
}
} }
} }
.navigationTitle("Classement") .navigationTitle("Classement")
@ -163,13 +209,25 @@ struct TournamentRankView: View {
.toolbar { .toolbar {
ToolbarItem(placement: .topBarTrailing) { ToolbarItem(placement: .topBarTrailing) {
if let url = tournament.shareURL(.rankings) { 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() let finalRanks = await tournament.finalRanking()
finalRanks.keys.sorted().forEach { rank in finalRanks.keys.sorted().forEach { rank in
if let rankedTeamIds = finalRanks[rank] { if let rankedTeamIds = finalRanks[rank] {
@ -179,7 +237,7 @@ struct TournamentRankView: View {
} }
@ViewBuilder @ViewBuilder
func actionForURL(_ url: URL, removeSource: Bool = false) -> some View { private func _actionForURL(_ url: URL, removeSource: Bool = false) -> some View {
Menu { Menu {
Button { Button {
UIApplication.shared.open(url) UIApplication.shared.open(url)

Loading…
Cancel
Save