From 3647d20b1f27c86c4695d80a0708ec82a00f7843 Mon Sep 17 00:00:00 2001 From: Razmig Sarkissian Date: Sun, 14 Jul 2024 18:30:15 +0200 Subject: [PATCH] fix stuff --- PadelClub/Data/Club.swift | 6 +- PadelClub/Data/Event.swift | 7 +- PadelClub/Data/TeamRegistration.swift | 17 +- PadelClub/Data/Tournament.swift | 24 ++- PadelClub/Utils/PadelRule.swift | 2 +- .../Components/GroupStageTeamView.swift | 105 ++++++++++-- .../GroupStage/GroupStageSettingsView.swift | 16 ++ .../GroupStageTeamReplacementView.swift | 4 +- .../Components/MatchTeamDetailView.swift | 1 + PadelClub/Views/Match/MatchDetailView.swift | 2 + .../Components/EditablePlayerView.swift | 6 +- .../Player/Components/PlayerPayView.swift | 6 +- .../Views/Tournament/FileImportView.swift | 8 +- .../Components/InscriptionInfoView.swift | 2 +- .../Screen/InscriptionManagerView.swift | 162 +++++++++++------- 15 files changed, 255 insertions(+), 113 deletions(-) diff --git a/PadelClub/Data/Club.swift b/PadelClub/Data/Club.swift index 3c78021..53eca84 100644 --- a/PadelClub/Data/Club.swift +++ b/PadelClub/Data/Club.swift @@ -81,7 +81,11 @@ final class Club : ModelObject, Storable, Hashable { } override func deleteDependencies() throws { - DataStore.shared.courts.deleteDependencies(self.customizedCourts) + let customizedCourts = self.customizedCourts + for customizedCourt in customizedCourts { + try customizedCourt.deleteDependencies() + } + DataStore.shared.courts.deleteDependencies(customizedCourts) } enum CodingKeys: String, CodingKey { diff --git a/PadelClub/Data/Event.swift b/PadelClub/Data/Event.swift index 21a0d91..54c68d8 100644 --- a/PadelClub/Data/Event.swift +++ b/PadelClub/Data/Event.swift @@ -38,7 +38,12 @@ final class Event: ModelObject, Storable { } DataStore.shared.tournaments.deleteDependencies(tournaments) - DataStore.shared.dateIntervals.deleteDependencies(self.courtsUnavailability) + + let courtsUnavailabilities = self.courtsUnavailability + for courtsUnavailability in courtsUnavailabilities { + try courtsUnavailability.deleteDependencies() + } + DataStore.shared.dateIntervals.deleteDependencies(courtsUnavailabilities) } // MARK: - Computed dependencies diff --git a/PadelClub/Data/TeamRegistration.swift b/PadelClub/Data/TeamRegistration.swift index 7dc156e..d38c476 100644 --- a/PadelClub/Data/TeamRegistration.swift +++ b/PadelClub/Data/TeamRegistration.swift @@ -72,8 +72,17 @@ final class TeamRegistration: ModelObject, Storable { // MARK: - override func deleteDependencies() throws { - self.tournamentStore.playerRegistrations.deleteDependencies(self.unsortedPlayers()) - self.tournamentStore.teamScores.deleteDependencies(self.teamScores()) + let unsortedPlayers = unsortedPlayers() + for player in unsortedPlayers { + try player.deleteDependencies() + } + self.tournamentStore.playerRegistrations.deleteDependencies(unsortedPlayers) + + let teamScores = teamScores() + for teamScore in teamScores { + try teamScore.deleteDependencies() + } + self.tournamentStore.teamScores.deleteDependencies(teamScores) } func hasArrived(isHere: Bool = false) { @@ -309,10 +318,6 @@ final class TeamRegistration: ModelObject, Storable { player.teamRegistration = id } } - - func qualifiedFromGroupStage() -> Bool { - groupStagePosition != nil && bracketPosition != nil - } typealias TeamRange = (left: TeamRegistration?, right: TeamRegistration?) diff --git a/PadelClub/Data/Tournament.swift b/PadelClub/Data/Tournament.swift index 8c3ec89..bd0f2fd 100644 --- a/PadelClub/Data/Tournament.swift +++ b/PadelClub/Data/Tournament.swift @@ -530,7 +530,7 @@ defer { func pasteDataForImporting() -> String { let selectedSortedTeams = selectedSortedTeams() - return (selectedSortedTeams.compactMap { $0.pasteData() } + ["Liste d'attente"] + waitingListTeams(in: selectedSortedTeams).compactMap { $0.pasteData() }).joined(separator: "\n\n") + return (selectedSortedTeams.compactMap { $0.pasteData() } + ["Liste d'attente"] + waitingListTeams(in: selectedSortedTeams, includingWalkOuts: true).compactMap { $0.pasteData() }).joined(separator: "\n\n") } func club() -> Club? { @@ -804,12 +804,12 @@ defer { func sortedTeams() -> [TeamRegistration] { let teams = selectedSortedTeams() - return teams + waitingListTeams(in: teams) + return teams + waitingListTeams(in: teams, includingWalkOuts: true) } func waitingListSortedTeams() -> [TeamRegistration] { let teams = selectedSortedTeams() - return waitingListTeams(in: teams) + return waitingListTeams(in: teams, includingWalkOuts: false) } @@ -876,9 +876,15 @@ defer { return _sortedTeams } - func waitingListTeams(in teams: [TeamRegistration]) -> [TeamRegistration] { + func waitingListTeams(in teams: [TeamRegistration], includingWalkOuts: Bool) -> [TeamRegistration] { let waitingList = Set(unsortedTeams()).subtracting(teams) - return waitingList.filter { $0.walkOut == false }.sorted(using: _defaultSorting(), order: .ascending) + waitingList.filter { $0.walkOut == true }.sorted(using: _defaultSorting(), order: .ascending) + let waitings = waitingList.filter { $0.walkOut == false }.sorted(using: _defaultSorting(), order: .ascending) + let walkOuts = waitingList.filter { $0.walkOut == true }.sorted(using: _defaultSorting(), order: .ascending) + if includingWalkOuts { + return waitings + walkOuts + } else { + return waitings + } } func bracketCut(teamCount: Int) -> Int { @@ -1062,7 +1068,7 @@ defer { let inadequatePlayers : [PlayerRegistration] = inadequatePlayers(in: players) let playersWithoutValidLicense : [PlayerRegistration] = playersWithoutValidLicense(in: players) let playersMissing : [TeamRegistration] = selectedTeams.filter({ $0.unsortedPlayers().count < 2 }) - let waitingList : [TeamRegistration] = waitingListTeams(in: selectedTeams) + let waitingList : [TeamRegistration] = waitingListTeams(in: selectedTeams, includingWalkOuts: true) let waitingListInBracket = waitingList.filter({ $0.bracketPosition != nil }) let waitingListInGroupStage = waitingList.filter({ $0.groupStage != nil }) @@ -1382,7 +1388,7 @@ defer { } func qualifiedTeams() -> [TeamRegistration] { - return unsortedTeams().filter({ $0.qualifiedFromGroupStage() }) + return unsortedTeams().filter({ $0.qualified }) } func moreQualifiedToDraw() -> Int { @@ -1392,9 +1398,9 @@ defer { func missingQualifiedFromGroupStages() -> [TeamRegistration] { if groupStageAdditionalQualified > 0 { return groupStages().filter { $0.hasEnded() }.compactMap { groupStage in - groupStage.teams()[qualifiedPerGroupStage] + groupStage.teams(true)[safe: qualifiedPerGroupStage] } - .filter({ $0.qualifiedFromGroupStage() == false }) + .filter({ $0.qualified == false }) } else { return [] } diff --git a/PadelClub/Utils/PadelRule.swift b/PadelClub/Utils/PadelRule.swift index 171662b..b722152 100644 --- a/PadelClub/Utils/PadelRule.swift +++ b/PadelClub/Utils/PadelRule.swift @@ -753,7 +753,7 @@ enum TournamentCategory: Int, Hashable, Codable, CaseIterable, Identifiable { var importingRawValue: String { switch self { case .unlisted: - return "" + return "messieurs" case .men: return "messieurs" case .women: diff --git a/PadelClub/Views/GroupStage/Components/GroupStageTeamView.swift b/PadelClub/Views/GroupStage/Components/GroupStageTeamView.swift index 390b97f..d977498 100644 --- a/PadelClub/Views/GroupStage/Components/GroupStageTeamView.swift +++ b/PadelClub/Views/GroupStage/Components/GroupStageTeamView.swift @@ -12,8 +12,14 @@ struct GroupStageTeamView: View { @EnvironmentObject var dataStore: DataStore @Environment(Tournament.self) var tournament: Tournament + @EnvironmentObject var networkMonitor: NetworkMonitor @Environment(\.dismiss) private var dismiss + + @State private var contactType: ContactType? = nil + @State private var sentError: ContactManagerError? = nil + @State private var showSubscriptionView: Bool = false + let groupStage: GroupStage var team: TeamRegistration @@ -21,6 +27,16 @@ struct GroupStageTeamView: View { return self.tournament.tournamentStore } + var messageSentFailed: Binding { + Binding { + sentError != nil + } set: { newValue in + if newValue == false { + sentError = nil + } + } + } + var body: some View { List { Section { @@ -30,23 +46,6 @@ struct GroupStageTeamView: View { } if groupStage.tournamentObject()?.hasEnded() == false { -// if team.qualified && team.bracketPosition == nil, let tournament = team.tournamentObject() { -// Section { -// NavigationLink { -// SpinDrawView(drawees: [team], segments: tournament.matchesWithSpace()) { results in -// -// } -// } label: { -// Text("Tirage au sort visuel") -// } -// } -// -// Section { -// RowButtonView("Tirage au sort automatique", role: .destructive) { -// } -// } -// } - if team.qualified == false { Section { NavigationLink { @@ -86,10 +85,82 @@ struct GroupStageTeamView: View { } } } + .toolbar { + ToolbarItem(placement: .topBarTrailing) { + MenuWarningView(tournament: tournament, teams: [team], contactType: $contactType) + } + } + .alert("Un problème est survenu", isPresented: messageSentFailed) { + Button("OK") { + } + } message: { + Text(_getErrorMessage()) + } + .sheet(item: $contactType) { contactType in + Group { + switch contactType { + case .message(_, let recipients, let body, _): + if Guard.main.paymentForNewTournament() != nil { + MessageComposeView(recipients: recipients, body: body) { result in + switch result { + case .cancelled: + break + case .failed: + self.sentError = .messageFailed + case .sent: + if networkMonitor.connected == false { + self.sentError = .messageNotSent + } + @unknown default: + break + } + } + } else { + SubscriptionView(isPresented: self.$showSubscriptionView, showLackOfPlanMessage: true) + .environment(\.colorScheme, .light) + } + case .mail(_, let recipients, let bccRecipients, let body, let subject, _): + if Guard.main.paymentForNewTournament() != nil { + MailComposeView(recipients: recipients, bccRecipients: bccRecipients, body: body, subject: subject) { result in + switch result { + case .cancelled, .saved: + self.contactType = nil + case .failed: + self.contactType = nil + self.sentError = .mailFailed + case .sent: + if networkMonitor.connected == false { + self.contactType = nil + self.sentError = .mailNotSent + } + @unknown default: + break + } + } + } else { + SubscriptionView(isPresented: self.$showSubscriptionView, showLackOfPlanMessage: true) + .environment(\.colorScheme, .light) + } + } + } + .tint(.master) + } + + .toolbarBackground(.visible, for: .navigationBar) .navigationTitle("Détail de l'équipe") } + private func _getErrorMessage() -> String { + let m1 : String? = (networkMonitor.connected == false ? "L'appareil n'est pas connecté à internet." : nil) + let m2 : String? = (sentError == .mailNotSent ? "Le mail est dans la boîte d'envoi de l'app Mail. Vérifiez son état dans l'app Mail avant d'essayer de le renvoyer." : nil) + let m3 : String? = ((sentError == .messageFailed || sentError == .messageNotSent) ? "Le SMS n'a pas été envoyé" : nil) + let m4 : String? = (sentError == .mailFailed ? "Le mail n'a pas été envoyé" : nil) + + let message : String = [m1, m2, m3, m4].compacted().joined(separator: "\n") + return message + } + private func _save() { do { try tournamentStore.teamRegistrations.addOrUpdate(instance: team) diff --git a/PadelClub/Views/GroupStage/GroupStageSettingsView.swift b/PadelClub/Views/GroupStage/GroupStageSettingsView.swift index 0900ff9..8b48766 100644 --- a/PadelClub/Views/GroupStage/GroupStageSettingsView.swift +++ b/PadelClub/Views/GroupStage/GroupStageSettingsView.swift @@ -20,6 +20,22 @@ struct GroupStageSettingsView: View { var body: some View { List { + if tournament.moreQualifiedToDraw() > 0 { + let missingQualifiedFromGroupStages = tournament.missingQualifiedFromGroupStages() + let name = "\((tournament.groupStageAdditionalQualified + 1).ordinalFormatted())" + NavigationLink("Tirage au sort d'un \(name)") { + SpinDrawView(drawees: ["Qualification d'un \(name)"], segments: missingQualifiedFromGroupStages) { results in + results.forEach { drawResult in + missingQualifiedFromGroupStages[drawResult.drawIndex].qualified = true + do { + try self.tournamentStore.teamRegistrations.addOrUpdate(instance: missingQualifiedFromGroupStages[drawResult.drawIndex]) + } catch { + Logger.error(error) + } + } + } + } + } if tournament.shouldVerifyGroupStage { Section { diff --git a/PadelClub/Views/GroupStage/Shared/GroupStageTeamReplacementView.swift b/PadelClub/Views/GroupStage/Shared/GroupStageTeamReplacementView.swift index 6a89cd0..df97174 100644 --- a/PadelClub/Views/GroupStage/Shared/GroupStageTeamReplacementView.swift +++ b/PadelClub/Views/GroupStage/Shared/GroupStageTeamReplacementView.swift @@ -89,9 +89,9 @@ struct GroupStageTeamReplacementView: View { } } } header: { - Text("Même position en poule") + Text("Même rang dans la liste") } footer: { - Text("Intervalle de poids d'équipe pour que le remplacement n'affecte pas les poules") + Text("Intervalle de poids d'équipe pour que le remplacement n'affecte rien") } } if let teamRangeExtended { diff --git a/PadelClub/Views/Match/Components/MatchTeamDetailView.swift b/PadelClub/Views/Match/Components/MatchTeamDetailView.swift index eb4ad82..435f181 100644 --- a/PadelClub/Views/Match/Components/MatchTeamDetailView.swift +++ b/PadelClub/Views/Match/Components/MatchTeamDetailView.swift @@ -8,6 +8,7 @@ import SwiftUI struct MatchTeamDetailView: View { + @EnvironmentObject var tournamentStore: TournamentStore let match: Match var body: some View { diff --git a/PadelClub/Views/Match/MatchDetailView.swift b/PadelClub/Views/Match/MatchDetailView.swift index 8207b58..ca7dd70 100644 --- a/PadelClub/Views/Match/MatchDetailView.swift +++ b/PadelClub/Views/Match/MatchDetailView.swift @@ -116,6 +116,7 @@ struct MatchDetailView: View { ForEach(unpaid) { player in LabeledContent { PlayerPayView(player: player) + .environmentObject(tournamentStore) } label: { Text(player.playerLabel()) } @@ -134,6 +135,7 @@ struct MatchDetailView: View { } .sheet(isPresented: $showDetails) { MatchTeamDetailView(match: match).tint(.master) + .environmentObject(tournamentStore) } .sheet(isPresented: self.$showSubscriptionView, content: { NavigationStack { diff --git a/PadelClub/Views/Player/Components/EditablePlayerView.swift b/PadelClub/Views/Player/Components/EditablePlayerView.swift index 4f926ae..e2e6d13 100644 --- a/PadelClub/Views/Player/Components/EditablePlayerView.swift +++ b/PadelClub/Views/Player/Components/EditablePlayerView.swift @@ -17,7 +17,7 @@ struct EditablePlayerView: View { } @EnvironmentObject var dataStore: DataStore - @Environment(Tournament.self) var tournament: Tournament + @EnvironmentObject var tournamentStore: TournamentStore @Bindable var player: PlayerRegistration var editingOptions: [PlayerEditingOption] @@ -25,10 +25,6 @@ struct EditablePlayerView: View { @State private var shouldPresentLicenceIdEdition: Bool = false @State private var presentLastNameUpdate: Bool = false @State private var presentFirstNameUpdate: Bool = false - - var tournamentStore: TournamentStore { - return self.tournament.tournamentStore - } var body: some View { computedPlayerView(player) diff --git a/PadelClub/Views/Player/Components/PlayerPayView.swift b/PadelClub/Views/Player/Components/PlayerPayView.swift index 23f5c1b..4382f14 100644 --- a/PadelClub/Views/Player/Components/PlayerPayView.swift +++ b/PadelClub/Views/Player/Components/PlayerPayView.swift @@ -11,12 +11,8 @@ import LeStorage struct PlayerPayView: View { @Bindable var player: PlayerRegistration - @Environment(Tournament.self) var tournament: Tournament + @EnvironmentObject var tournamentStore: TournamentStore - var tournamentStore: TournamentStore { - return self.tournament.tournamentStore - } - var body: some View { Picker(selection: $player.paymentType) { Text("Non réglé").tag(nil as PlayerRegistration.PlayerPaymentType?) diff --git a/PadelClub/Views/Tournament/FileImportView.swift b/PadelClub/Views/Tournament/FileImportView.swift index dae0ba2..1917e61 100644 --- a/PadelClub/Views/Tournament/FileImportView.swift +++ b/PadelClub/Views/Tournament/FileImportView.swift @@ -232,11 +232,11 @@ struct FileImportView: View { Section { Text("Aucune équipe \(tournament.tournamentCategory.importingRawValue) détectée mais \(teams.count) équipes sont dans le fichier") Picker(selection: $tournament.tournamentCategory) { - ForEach(TournamentCategory.allCases) { category in + ForEach([TournamentCategory.men, TournamentCategory.women, TournamentCategory.mix]) { category in Text(category.importingRawValue).tag(category) } } label: { - Text("Modifier la catégorie du tournoi ?") + Text("Modifier la catégorie") } .onChange(of: tournament.tournamentCategory) { _save() @@ -271,13 +271,11 @@ struct FileImportView: View { let tournamentFilteredTeams = self.filteredTeams(tournament: tournament) Section { - RowButtonView("Valider") { + RowButtonView("Valider les \(tournamentFilteredTeams.count.formatted()) équipe\(tournamentFilteredTeams.count.pluralSuffix)") { await _validate(tournament: tournament) } .disabled(validatedTournamentIds.contains(tournament.id)) } header: { - Text("\(tournamentFilteredTeams.count.formatted()) équipe\(tournamentFilteredTeams.count.pluralSuffix)") - } footer: { Text(tournament.tournamentTitle()) } } diff --git a/PadelClub/Views/Tournament/Screen/Components/InscriptionInfoView.swift b/PadelClub/Views/Tournament/Screen/Components/InscriptionInfoView.swift index 72d5b1d..9514821 100644 --- a/PadelClub/Views/Tournament/Screen/Components/InscriptionInfoView.swift +++ b/PadelClub/Views/Tournament/Screen/Components/InscriptionInfoView.swift @@ -205,7 +205,7 @@ struct InscriptionInfoView: View { players = tournament.unsortedPlayers() selectedTeams = tournament.selectedSortedTeams() callDateIssue = selectedTeams.filter { $0.callDate != nil && tournament.isStartDateIsDifferentThanCallDate($0) } - waitingList = tournament.waitingListTeams(in: selectedTeams) + waitingList = tournament.waitingListTeams(in: selectedTeams, includingWalkOuts: true) duplicates = tournament.duplicates(in: players) problematicPlayers = players.filter({ $0.sex == nil }) inadequatePlayers = tournament.inadequatePlayers(in: players) diff --git a/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift b/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift index eec62b5..3337176 100644 --- a/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift +++ b/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift @@ -166,7 +166,7 @@ struct InscriptionManagerView: View { self.tournament.shouldVerifyBracket = true self.tournament.shouldVerifyGroupStage = true - let waitingList = self.tournament.waitingListTeams(in: selectedSortedTeams) + let waitingList = self.tournament.waitingListTeams(in: selectedSortedTeams, includingWalkOuts: true) waitingList.forEach { team in if team.bracketPosition != nil || team.groupStagePosition != nil { team.resetPositions() @@ -185,7 +185,6 @@ struct InscriptionManagerView: View { var body: some View { VStack(spacing: 0) { - _managementView() if _isEditingTeam() { _buildingTeamView() } else if sortedTeams.isEmpty { @@ -574,7 +573,7 @@ struct InscriptionManagerView: View { Section { TeamDetailView(team: team) } header: { - TeamHeaderView(team: team, teamIndex: teamIndex, tournament: tournament, teamCount: filterMode == .waiting ? 0 : selectedSortedTeams.count) + TeamHeaderView(team: team, teamIndex: filterMode == .waiting ? nil : teamIndex, tournament: tournament, teamCount: filterMode == .waiting ? 0 : selectedSortedTeams.count) } footer: { _teamFooterView(team) } @@ -586,53 +585,31 @@ struct InscriptionManagerView: View { .autocorrectionDisabled() } - @MainActor + @ViewBuilder private func _managementView() -> some View { - HStack { - Button { - presentPlayerCreation = true - } label: { - HStack(spacing: 4) { - Image(systemName: "person.fill.badge.plus") - .resizable() - .scaledToFit() - .frame(width: 20) - Text("Créer") - .font(.headline) - } - .frame(maxWidth: .infinity) - } - - PasteButton(payloadType: String.self) { strings in - guard let first = strings.first else { return } - Task { - await MainActor.run { - fetchPlayers.nsPredicate = Self._pastePredicate(pasteField: first, mostRecentDate: SourceFileManager.shared.mostRecentDateAvailable, filterOption: _filterOption()) - fetchPlayers.nsSortDescriptors = [NSSortDescriptor(keyPath: \ImportedPlayer.rank, ascending: true)] - pasteString = first - autoSelect = true - } - } - } - - Button { - presentPlayerSearch = true - } label: { - HStack(spacing: 4) { - Image(systemName: "person.fill.viewfinder") - .resizable() - .scaledToFit() - .frame(width: 20) - Text("FFT") - .font(.headline) + Button { + presentPlayerSearch = true + } label: { + Text("Rechercher dans la base fédérale") + } + + Button { + presentPlayerCreation = true + } label: { + Text("Créer un non classé / non licencié") + } + + PasteButton(payloadType: String.self) { strings in + guard let first = strings.first else { return } + Task { + await MainActor.run { + fetchPlayers.nsPredicate = Self._pastePredicate(pasteField: first, mostRecentDate: SourceFileManager.shared.mostRecentDateAvailable, filterOption: _filterOption()) + fetchPlayers.nsSortDescriptors = [NSSortDescriptor(keyPath: \ImportedPlayer.rank, ascending: true)] + pasteString = first + autoSelect = true } - .frame(maxWidth: .infinity) } } - .buttonStyle(.borderedProminent) - .tint(.master) - .fixedSize(horizontal: false, vertical: true) - .padding(16) } @@ -690,6 +667,8 @@ struct InscriptionManagerView: View { } } + _informationView() + Section { TipView(fileTip) { action in @@ -758,33 +737,44 @@ struct InscriptionManagerView: View { } } + @ViewBuilder private func _informationView() -> some View { Section { - LabeledContent { - Text(unsortedTeamsWithoutWO.count.formatted() + "/" + tournament.teamCount.formatted()).font(.largeTitle) + Button { + filterMode = .all } label: { - Text("Paire\(unsortedTeamsWithoutWO.count.pluralSuffix) inscrite\(unsortedTeamsWithoutWO.count.pluralSuffix)") + LabeledContent { + Text(unsortedTeamsWithoutWO.count.formatted() + "/" + tournament.teamCount.formatted()) + } label: { + Text("Paire\(unsortedTeamsWithoutWO.count.pluralSuffix) inscrite\(unsortedTeamsWithoutWO.count.pluralSuffix)") + } } + .buttonStyle(.plain) - LabeledContent { - Text(walkoutTeams.count.formatted()).font(.largeTitle) - } label: { - Text("Forfait\(walkoutTeams.count.pluralSuffix)") + HStack { + let waiting: Int = max(0, unsortedTeamsWithoutWO.count - tournament.teamCount) + FooterButtonView("\(waiting.formatted()) équipes en attente\(waiting.pluralSuffix)") { + filterMode = .waiting + } + .disabled(filterMode == .waiting) + + Divider() + + let wo = walkoutTeams.count + FooterButtonView("\(wo.formatted()) équipes forfait\(wo.pluralSuffix)") { + filterMode = .walkOut + } + .disabled(filterMode == .walkOut) } + .fixedSize(horizontal: true, vertical: true) - LabeledContent { - Text(max(0, unsortedTeamsWithoutWO.count - tournament.teamCount).formatted()).font(.largeTitle) - } label: { - Text("Attente") - } - NavigationLink { InscriptionInfoView() .environment(tournament) } label: { LabeledContent { if let registrationIssues { - Text(registrationIssues.formatted()).font(.largeTitle) + Text(registrationIssues.formatted()) } else { ProgressView() } @@ -792,7 +782,28 @@ struct InscriptionManagerView: View { Text("Problèmes détéctés") } } + } header: { + Text("Statut des inscriptions") + } footer: { + HStack { + Menu { + _managementView() + } label: { + Text("Complétez votre liste") + } + Text("ou") + + FooterButtonView("Importez un fichier") { + presentImportView = true + } + } +// if filterMode != .all { +// FooterButtonView("tout afficher") { +// filterMode = .all +// } +// } } + .headerProminence(.increased) } @ViewBuilder @@ -1016,6 +1027,31 @@ struct InscriptionManagerView: View { } } } + } header: { + let _currentSelection = _currentSelection() + let selectedSortedTeams = tournament.selectedSortedTeams() + let rank = _currentSelection.map { + $0.computedRank + }.reduce(0, +) + let teamIndex = selectedSortedTeams.firstIndex(where: { $0.weight >= rank }) ?? selectedSortedTeams.count + if _currentSelection.isEmpty == false, tournament.hideWeight() == false, rank > 0 { + HStack(spacing: 16.0) { + VStack(alignment: .leading, spacing: 0) { + Text("Rang").font(.caption) + Text("#" + (teamIndex + 1).formatted()) + } + + VStack(alignment: .leading, spacing: 0) { + Text("Poids").font(.caption) + Text(rank.formatted()) + } + Spacer() + VStack(alignment: .trailing, spacing: 0) { + Text("").font(.caption) + Text(tournament.cutLabel(index: teamIndex, teamCount: selectedSortedTeams.count)) + } + } + } } if editedTeam == nil { @@ -1163,6 +1199,12 @@ struct InscriptionManagerView: View { private func _teamMenuOptionView(_ team: TeamRegistration) -> some View { Menu { Section { + NavigationLink { + GroupStageTeamReplacementView(team: team) + } label: { + Text("Chercher à remplacer") + } + MenuWarningView(tournament: tournament, teams: [team], contactType: $contactType) //Divider() Button("Copier") {