diff --git a/PadelClub.xcodeproj/project.pbxproj b/PadelClub.xcodeproj/project.pbxproj index 08777bd..e97860c 100644 --- a/PadelClub.xcodeproj/project.pbxproj +++ b/PadelClub.xcodeproj/project.pbxproj @@ -1859,7 +1859,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 50; + CURRENT_PROJECT_VERSION = 53; DEFINES_MODULE = YES; DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\""; DEVELOPMENT_TEAM = BQ3Y44M3Q6; @@ -1882,7 +1882,7 @@ ); MARKETING_VERSION = 0.1; MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; - OTHER_SWIFT_FLAGS = "-Xfrontend -warn-long-function-bodies=5 -Xfrontend -warn-long-expression-type-checking=20 -Xfrontend -warn-long-function-bodies=50"; + OTHER_SWIFT_FLAGS = ""; PRODUCT_BUNDLE_IDENTIFIER = app.padelclub; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = YES; @@ -1897,7 +1897,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 50; + CURRENT_PROJECT_VERSION = 53; DEFINES_MODULE = YES; DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\""; DEVELOPMENT_TEAM = BQ3Y44M3Q6; diff --git a/PadelClub/Data/Tournament.swift b/PadelClub/Data/Tournament.swift index 409f071..2aafb28 100644 --- a/PadelClub/Data/Tournament.swift +++ b/PadelClub/Data/Tournament.swift @@ -867,7 +867,15 @@ class Tournament : ModelObject, Storable { func selectedPlayers() -> [PlayerRegistration] { return self.selectedSortedTeams().flatMap { $0.unsortedPlayers() }.sorted(by: \.computedRank) } - + + func paidSelectedPlayers(type: PlayerRegistration.PlayerPaymentType) -> Double? { + if let entryFee { + return Double(self.selectedSortedTeams().flatMap { $0.unsortedPlayers() }.filter { $0.paymentType == type }.count) * entryFee + } else { + return nil + } + } + func players() -> [PlayerRegistration] { return self.unsortedTeams().flatMap { $0.unsortedPlayers() }.sorted(by: \.computedRank) } @@ -1065,8 +1073,13 @@ class Tournament : ModelObject, Storable { let _limit = limit ?? courtCount return Array(allMatches.filter({ $0.hasEnded() }).sorted(by: \.computedEndDateForSorting).reversed().prefix(_limit)) } - - func finalRanking() -> [Int: [String]] { + + 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() let rounds = rounds() diff --git a/PadelClub/Views/Calling/CallView.swift b/PadelClub/Views/Calling/CallView.swift index fa5f74c..606e61c 100644 --- a/PadelClub/Views/Calling/CallView.swift +++ b/PadelClub/Views/Calling/CallView.swift @@ -85,16 +85,16 @@ struct CallView: View { } } - var finalMessage: String { + func finalMessage(reSummon: Bool) -> String { ContactType.callingMessage(tournament: tournament, startDate: callDate, roundLabel: roundLabel, matchFormat: matchFormat, reSummon: reSummon) } var reSummon: Bool { teams.allSatisfy({ $0.called() }) } - + var body: some View { - let callWord = reSummon ? "Reconvoquer" : "Convoquer" + let callWord : String = (reSummon ? "Reconvoquer" : "Convoquer") HStack { if teams.count == 1 { if let previousCallDate = teams.first?.callDate, Calendar.current.compare(previousCallDate, to: callDate, toGranularity: .minute) != .orderedSame { @@ -105,23 +105,10 @@ struct CallView: View { } else { Text(callWord + " ces \(teams.count) paires par") } - Button { - self._payTournamentAndExecute { - self._contactByMessage() - } - } label: { - Text("sms") - .underline() - } + + _summonMenu(byMessage: true) Text("ou") - Button { - self._payTournamentAndExecute { - self._contactByMail() - } - } label: { - Text("mail") - .underline() - } + _summonMenu(byMessage: false) } .font(.subheadline) .buttonStyle(.borderless) @@ -182,6 +169,39 @@ struct CallView: View { }) } + @ViewBuilder + private func _summonMenu(byMessage: Bool) -> some View { + if reSummon { + Menu { + Button("Convoquer") { + _summon(byMessage: byMessage, reSummon: false) + } + + Button("Re-convoquer") { + _summon(byMessage: byMessage, reSummon: true) + } + + } label: { + Text(byMessage ? "sms" : "mail") + .underline() + } + } else { + Button(byMessage ? "sms" : "mail") { + _summon(byMessage: byMessage, reSummon: false) + } + } + } + + private func _summon(byMessage: Bool, reSummon: Bool) { + self._payTournamentAndExecute { + if byMessage { + self._contactByMessage(reSummon: reSummon) + } else { + self._contactByMail(reSummon: reSummon) + } + } + } + fileprivate func _payTournamentAndExecute(_ handler: () -> ()) { do { try tournament.payIfNecessary() @@ -191,12 +211,12 @@ struct CallView: View { } } - fileprivate func _contactByMessage() { - contactType = .message(date: callDate, recipients: teams.flatMap { $0.getPhoneNumbers() }, body: finalMessage, tournamentBuild: nil) + fileprivate func _contactByMessage(reSummon: Bool) { + contactType = .message(date: callDate, recipients: teams.flatMap { $0.getPhoneNumbers() }, body: finalMessage(reSummon: reSummon), tournamentBuild: nil) } - fileprivate func _contactByMail() { - contactType = .mail(date: callDate, recipients: tournament.umpireMail(), bccRecipients: teams.flatMap { $0.getMail() }, body: finalMessage, subject: tournament.tournamentTitle(), tournamentBuild: nil) + fileprivate func _contactByMail(reSummon: Bool) { + contactType = .mail(date: callDate, recipients: tournament.umpireMail(), bccRecipients: teams.flatMap { $0.getMail() }, body: finalMessage(reSummon: reSummon), subject: tournament.tournamentTitle(), tournamentBuild: nil) } } diff --git a/PadelClub/Views/Cashier/CashierDetailView.swift b/PadelClub/Views/Cashier/CashierDetailView.swift index c262b32..860a0b1 100644 --- a/PadelClub/Views/Cashier/CashierDetailView.swift +++ b/PadelClub/Views/Cashier/CashierDetailView.swift @@ -9,6 +9,8 @@ import SwiftUI struct CashierDetailView: View { var tournaments : [Tournament] + @State private var earnings: Double? = nil + @State private var paidCompletion: Double? = nil init(tournaments: [Tournament]) { self.tournaments = tournaments @@ -20,58 +22,154 @@ struct CashierDetailView: View { var body: some View { List { - ForEach(tournaments) { tournament in + if tournaments.count > 1 { Section { LabeledContent { - Text(tournament.earnings().formatted(.currency(code: "EUR").precision(.fractionLength(0)))) + if let earnings { + Text(earnings.formatted(.currency(code: "EUR").precision(.fractionLength(0)))) + } else { + ProgressView() + } } label: { Text("Encaissement") - Text(tournament.paidCompletion().formatted(.percent.precision(.fractionLength(0)))).foregroundStyle(.secondary) + if let paidCompletion { + Text(paidCompletion.formatted(.percent.precision(.fractionLength(0)))).foregroundStyle(.secondary) + } } - _tournamentCashierDetailView(tournament) + _tournamentsCashierDetailView(tournaments) + } header: { - if tournaments.count > 1 { - Text(tournament.tournamentTitle()) - } + Text("Bilan") } } + + ForEach(tournaments) { tournament in + CashierSectionView(tournament: tournament, showTournamentTitle: tournaments.count > 1) + } } .headerProminence(.increased) + .onAppear { + Task { + if earnings == nil { + _getEarnings() + } + + if paidCompletion == nil { + _getPaidCompletion() + } + } + } } - private func _tournamentCashierDetailView(_ tournament: Tournament) -> some View { + private func _getEarnings() { + earnings = tournaments.map { $0.earnings() }.reduce(0,+) + } + + private func _getPaidCompletion() { + let selectedPlayers = tournaments.flatMap { $0.selectedPlayers() } + if selectedPlayers.isEmpty { paidCompletion = 0 } + paidCompletion = Double(selectedPlayers.filter { $0.hasPaid() }.count) / Double(selectedPlayers.count) + } + + private func _tournamentsCashierDetailView(_ tournaments: [Tournament]) -> some View { DisclosureGroup { ForEach(PlayerRegistration.PlayerPaymentType.allCases) { type in - let count = tournament.selectedPlayers().filter({ $0.paymentType == type }).count - if count > 0 { - LabeledContent { - if let entryFee = tournament.entryFee { - let sum = Double(count) * entryFee - Text(sum.formatted(.currency(code: "EUR"))) - } - } label: { - Text(type.localizedLabel()) - Text(count.formatted()) - } - } + PaymentTypeCashierRowView(tournaments: tournaments, type: type) } } label: { Text("Voir le détail") } + } + + struct CashierSectionView: View { + let tournament: Tournament + let showTournamentTitle: Bool + @State private var earnings: Double? = nil + @State private var paidCompletion: Double? = nil + + var body: some View { + Section { + LabeledContent { + if let earnings { + Text(earnings.formatted(.currency(code: "EUR").precision(.fractionLength(0)))) + } else { + ProgressView() + } + } label: { + Text("Encaissement") + if let paidCompletion { + Text(paidCompletion.formatted(.percent.precision(.fractionLength(0)))).foregroundStyle(.secondary) + } + } + CashierDetailDisclosureView(tournament: tournament) + } header: { + if showTournamentTitle { + Text(tournament.tournamentTitle()) + } + } + .onAppear { + Task { + if earnings == nil { + earnings = tournament.earnings() + } + + if paidCompletion == nil { + paidCompletion = tournament.paidCompletion() + } + } + } + } + } -// -// Section { -// ForEach(tournaments) { tournament in -// } -//// HStack { -//// Text("Total") -//// Spacer() -//// Text(event.earnings.formatted(.currency(code: "EUR").precision(.fractionLength(0)))) -//// Text(event.paidCompletion.formatted(.percent.precision(.fractionLength(0)))).foregroundStyle(.secondary) -//// } -// } header: { -// Text("Encaissement") -// } + struct PaymentTypeCashierRowView: View { + let tournaments: [Tournament] + let type: PlayerRegistration.PlayerPaymentType + @State private var value: Double? + + + var body: some View { + LabeledContent { + if let value { + Text(value.formatted(.currency(code: "EUR"))) + } else { + ProgressView() + } + } label: { + Text(type.localizedLabel()) + } + .onAppear { + Task { + if value == nil { + value = tournaments.compactMap({ $0.paidSelectedPlayers(type: type) }).reduce(0,+) + } + } + } + } + } + + struct CashierDetailDisclosureView: View { + let tournament: Tournament + var body: some View { + DisclosureGroup { + let selectedPlayers = tournament.selectedPlayers() + ForEach(PlayerRegistration.PlayerPaymentType.allCases) { type in + let count = selectedPlayers.filter({ $0.paymentType == type }).count + if count > 0 { + LabeledContent { + if let entryFee = tournament.entryFee { + let sum = Double(count) * entryFee + Text(sum.formatted(.currency(code: "EUR"))) + } + } label: { + Text(type.localizedLabel()) + Text(count.formatted()) + } + } + } + } label: { + Text("Voir le détail") + } + } } } diff --git a/PadelClub/Views/Tournament/Screen/TableStructureView.swift b/PadelClub/Views/Tournament/Screen/TableStructureView.swift index 4fd4a9c..4ca44a6 100644 --- a/PadelClub/Views/Tournament/Screen/TableStructureView.swift +++ b/PadelClub/Views/Tournament/Screen/TableStructureView.swift @@ -6,6 +6,7 @@ // import SwiftUI +import LeStorage struct TableStructureView: View { @Environment(Tournament.self) private var tournament: Tournament @@ -133,6 +134,24 @@ struct TableStructureView: View { Text("Équipes en tableau final") } } + + Section { + RowButtonView("Sauver sans reconstuire l'existant") { + _saveWithoutRebuild() + } + } + + Section { + RowButtonView("Recontruire les poules", role:.destructive) { + _save(rebuildEverything: false) + } + } + + Section { + RowButtonView("Tout refaire", role: .destructive) { + _save(rebuildEverything: true) + } + } } .focused($stepperFieldIsFocused) .onChange(of: stepperFieldIsFocused) { @@ -204,17 +223,17 @@ struct TableStructureView: View { } } .confirmationDialog("Refaire la structure", isPresented: $presentRefreshStructureWarning, actions: { + Button("Sauver sans reconstuire l'existant") { + _saveWithoutRebuild() + } - if requirements.allSatisfy({ $0 == .groupStage }) { - Button("Refaire les poules") { - _save(rebuildEverything: false) - } + Button("Recontruire les poules") { + _save(rebuildEverything: false) } - + Button("Tout refaire", role: .destructive) { _save(rebuildEverything: true) } - }, message: { ForEach(Array(requirements)) { requirement in Text(requirement.rebuildingRequirementMessage) @@ -226,7 +245,22 @@ struct TableStructureView: View { .navigationTitle("Structure") .navigationBarTitleDisplayMode(.inline) } + + private func _saveWithoutRebuild() { + tournament.teamCount = teamCount + tournament.groupStageCount = groupStageCount + tournament.teamsPerGroupStage = teamsPerGroupStage + tournament.qualifiedPerGroupStage = qualifiedPerGroupStage + tournament.groupStageAdditionalQualified = groupStageAdditionalQualified + do { + try dataStore.tournaments.addOrUpdate(instance: tournament) + } catch { + Logger.error(error) + } + dismiss() + } + private func _save(rebuildEverything: Bool = false) { _verifyValueIntegrity() diff --git a/PadelClub/Views/Tournament/Screen/TournamentRankView.swift b/PadelClub/Views/Tournament/Screen/TournamentRankView.swift index 70ed34e..476353b 100644 --- a/PadelClub/Views/Tournament/Screen/TournamentRankView.swift +++ b/PadelClub/Views/Tournament/Screen/TournamentRankView.swift @@ -13,13 +13,22 @@ struct TournamentRankView: View { @EnvironmentObject var dataStore: DataStore @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: { @@ -29,8 +38,10 @@ struct TournamentRankView: View { LabeledContent { if rankingPublished { Image(systemName: "checkmark") + .foregroundStyle(.green) } else { Image(systemName: "xmark") + .foregroundStyle(.logoRed) } } label: { Text("Classement publié") @@ -47,116 +58,227 @@ struct TournamentRankView: View { } } - 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() + } + } else { + RowButtonView("Re-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()) - } + if rankingPublished { + Section { + ForEach(tournament.teamsRanked()) { team in + let key = team.finalRanking ?? 0 + Button { + selectedTeam = team + } label: { + TeamRankCellView(team: team, key: key) + .frame(maxWidth: .infinity) + } + .contentShape(Rectangle()) + .buttonStyle(.plain) + } + } footer: { + Text("Vous pouvez appuyer sur une ligne pour éditer manuellement le classement calculé par Padel Club.") + } + } else { + let keys = rankings.keys.sorted() + ForEach(keys, id: \.self) { key in + if let rankedTeams = rankings[key] { + ForEach(rankedTeams) { team in + TeamRankCellView(team: team, key: key) + } + } + } + } + } + .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) + do { + try dataStore.teamRegistrations.addOrUpdate(instance: selectedTeam) + } catch { + Logger.error(error) + } + + self.selectedTeam = nil + } + + Button("Annuler", role: .cancel) { + self.selectedTeam = nil + } + } + } + .overlay(content: { + if calculating { + ProgressView() + } + }) + .onAppear { + let rankingPublished = tournament.selectedSortedTeams().allSatisfy({ $0.finalRanking != nil }) + if rankingPublished == false { + calculating = true + Task { + await _calculateRankings() + calculating = false + } + } + } + .navigationTitle("Classement") + .navigationBarTitleDisplayMode(.inline) + .toolbarBackground(.visible, for: .navigationBar) + .toolbar { + ToolbarItem(placement: .topBarTrailing) { + if let url = tournament.shareURL(.rankings) { + _actionForURL(url) + } + } + } + } + + struct TeamRankCellView: View { + @Environment(Tournament.self) var tournament: Tournament + let team: TeamRegistration + let key: Int + + var body: some View { + 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() - .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("--") - } - } + Image(systemName: "arrowtriangle.up.fill") + .imageScale(.small) } - - - 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) - } - } - } - } + .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) } - - 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) - } + .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) } } } } } + + if tournament.isAnimation() == false && key > 0 { + 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) + } + } + } } } - .onAppear { - let finalRanks = tournament.finalRanking() - finalRanks.keys.sorted().forEach { rank in - if let rankedTeamIds = finalRanks[rank] { - rankings[rank] = rankedTeamIds.compactMap { Store.main.findById($0) } + } + + 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) } } } - .navigationTitle("Classement") - .navigationBarTitleDisplayMode(.inline) - .toolbarBackground(.visible, for: .navigationBar) + _save() } + private func _calculateRankings() async { + let finalRanks = await tournament.finalRanking() + finalRanks.keys.sorted().forEach { rank in + if let rankedTeamIds = finalRanks[rank] { + rankings[rank] = rankedTeamIds.compactMap { Store.main.findById($0) } + } + } + } + + @ViewBuilder + private func _actionForURL(_ url: URL, removeSource: Bool = false) -> some View { + Menu { + Button { + UIApplication.shared.open(url) + } label: { + Label("Voir", systemImage: "safari") + } + + ShareLink(item: url) { + Label("Partager le lien", systemImage: "link") + } + } label: { + Image(systemName: "square.and.arrow.up") + } + .frame(maxWidth: .infinity) + .buttonStyle(.borderless) + } + + private func _save() { do { try dataStore.teamRegistrations.addOrUpdate(contentOfs: tournament.unsortedTeams()) diff --git a/PadelClub/Views/Tournament/TournamentBuildView.swift b/PadelClub/Views/Tournament/TournamentBuildView.swift index 77ad3d7..976c655 100644 --- a/PadelClub/Views/Tournament/TournamentBuildView.swift +++ b/PadelClub/Views/Tournament/TournamentBuildView.swift @@ -18,85 +18,38 @@ struct TournamentBuildView: View { @ViewBuilder var body: some View { let state = tournament.state() - - if tournament.hasEnded() { - Section { + + Section { + if tournament.hasEnded() { NavigationLink(value: Screen.rankings) { Text("Classement final des équipes") } } - } - - Section { - if tournament.groupStageCount > 0 { - NavigationLink(value: Screen.groupStage) { - LabeledContent { - if let groupStageStatus { - Text(groupStageStatus).lineLimit(1) - .multilineTextAlignment(.trailing) - } else { - ProgressView() - } - } label: { - Text("Poules") - if tournament.shouldVerifyGroupStage { - Text("Vérifier les poules").foregroundStyle(.logoRed) - } - } - } - .task { - groupStageStatus = await tournament.groupStageStatus() - } - } - - if tournament.rounds().isEmpty == false { - NavigationLink(value: Screen.round) { - LabeledContent { - if let bracketStatus { - Text(bracketStatus).lineLimit(1) - .multilineTextAlignment(.trailing) - } else { - ProgressView() - } - } label: { - Text("Tableau") - if tournament.shouldVerifyBracket { - Text("Vérifier la tableau").foregroundStyle(.logoRed) - } - } - } - .task { - bracketStatus = await tournament.bracketStatus() - } - } - } - - Section { + if state == .running || state == .finished { + TournamentInscriptionView(tournament: tournament) TournamentBroadcastRowView(tournament: tournament) } - if state == .running || state == .finished { - NavigationLink(value: Screen.cashier) { - let tournamentStatus = cashierStatus - LabeledContent { - if let tournamentStatus { - Text(tournamentStatus.completion) - } else { - ProgressView() - } - } label: { - Text("Encaissement") - if let tournamentStatus { - Text(tournamentStatus.label).lineLimit(1) - } else { - Text(" ") - } + NavigationLink(value: Screen.cashier) { + let tournamentStatus = cashierStatus + LabeledContent { + if let tournamentStatus { + Text(tournamentStatus.completion) + } else { + ProgressView() + } + } label: { + Text("Encaissement") + if let tournamentStatus { + Text(tournamentStatus.label).lineLimit(1) + } else { + Text(" ") } } - .task { - cashierStatus = await tournament.cashierStatus() - } + } + .task { + cashierStatus = await tournament.cashierStatus() } if state != .finished { @@ -142,11 +95,50 @@ struct TournamentBuildView: View { callStatus = await tournament.callStatus() } } + } + + Section { + if tournament.groupStageCount > 0 { + NavigationLink(value: Screen.groupStage) { + LabeledContent { + if let groupStageStatus { + Text(groupStageStatus).lineLimit(1) + .multilineTextAlignment(.trailing) + } else { + ProgressView() + } + } label: { + Text("Poules") + if tournament.shouldVerifyGroupStage { + Text("Vérifier les poules").foregroundStyle(.logoRed) + } + } + } + .task { + groupStageStatus = await tournament.groupStageStatus() + } + } - if state == .running || state == .finished { - TournamentInscriptionView(tournament: tournament) + if tournament.rounds().isEmpty == false { + NavigationLink(value: Screen.round) { + LabeledContent { + if let bracketStatus { + Text(bracketStatus).lineLimit(1) + .multilineTextAlignment(.trailing) + } else { + ProgressView() + } + } label: { + Text("Tableau") + if tournament.shouldVerifyBracket { + Text("Vérifier la tableau").foregroundStyle(.logoRed) + } + } + } + .task { + bracketStatus = await tournament.bracketStatus() + } } - } } } diff --git a/PadelClub/Views/Tournament/TournamentView.swift b/PadelClub/Views/Tournament/TournamentView.swift index 297cd7b..ebac2b1 100644 --- a/PadelClub/Views/Tournament/TournamentView.swift +++ b/PadelClub/Views/Tournament/TournamentView.swift @@ -67,8 +67,8 @@ struct TournamentView: View { TournamentInitView(tournament: tournament) TournamentBuildView(tournament: tournament) case .running: - TournamentRunningView(tournament: tournament) TournamentBuildView(tournament: tournament) + TournamentRunningView(tournament: tournament) case .finished: TournamentBuildView(tournament: tournament) TournamentRunningView(tournament: tournament) @@ -121,6 +121,12 @@ struct TournamentView: View { } label: { } + + Divider() + + NavigationLink(value: Screen.event) { + Text("Gestion de l'événement") + } } }