From d34967df33dc8267e38671a2972067d09ce9ce66 Mon Sep 17 00:00:00 2001 From: Razmig Sarkissian Date: Wed, 17 Apr 2024 15:12:57 +0200 Subject: [PATCH] clean up inscription --- PadelClub/Data/TeamRegistration.swift | 15 +++++ PadelClub/Data/Tournament.swift | 15 +++++ PadelClub/Manager/FileImportManager.swift | 10 ++- PadelClub/Manager/PadelRule.swift | 4 ++ .../ViewModel/TournamentSeedEditing.swift | 13 +--- PadelClub/Views/Calling/CallView.swift | 6 +- .../Views/Calling/GroupStageCallingView.swift | 7 +- .../Views/Calling/SeedsCallingView.swift | 2 +- PadelClub/Views/Match/MatchRowView.swift | 4 +- PadelClub/Views/Round/LoserRoundsView.swift | 8 ++- PadelClub/Views/Round/RoundSettingsView.swift | 4 +- PadelClub/Views/Round/RoundView.swift | 6 +- PadelClub/Views/Round/RoundsView.swift | 6 +- PadelClub/Views/Team/TeamDetailView.swift | 6 +- PadelClub/Views/Team/TeamRowView.swift | 2 +- .../Views/Tournament/FileImportView.swift | 55 ++++++++++------ .../Components/InscriptionInfoView.swift | 64 ++++++++++++------- .../Screen/InscriptionManagerView.swift | 9 ++- 18 files changed, 155 insertions(+), 81 deletions(-) diff --git a/PadelClub/Data/TeamRegistration.swift b/PadelClub/Data/TeamRegistration.swift index 0f42cb0..b0a9453 100644 --- a/PadelClub/Data/TeamRegistration.swift +++ b/PadelClub/Data/TeamRegistration.swift @@ -283,6 +283,21 @@ class TeamRegistration: ModelObject, Storable { return Store.main.findById(groupStage) } + func initialRound() -> Round? { + guard let bracketPosition else { return nil } + let matchIndex = RoundRule.matchIndex(fromBracketPosition: bracketPosition) + let roundIndex = RoundRule.roundIndex(fromMatchIndex: bracketPosition / 2) + return Store.main.filter(isIncluded: { $0.tournament == tournament && $0.index == roundIndex }).first + } + + func initialMatch() -> Match? { + guard let bracketPosition else { return nil } + guard let initialRoundObject = initialRound() else { return nil } + let matchIndex = RoundRule.matchIndex(fromBracketPosition: bracketPosition) + return Store.main.filter(isIncluded: { $0.round == initialRoundObject.id && $0.index == matchIndex }).first + } + + func tournamentObject() -> Tournament? { Store.main.findById(tournament) } diff --git a/PadelClub/Data/Tournament.swift b/PadelClub/Data/Tournament.swift index 50682bd..e2be5d8 100644 --- a/PadelClub/Data/Tournament.swift +++ b/PadelClub/Data/Tournament.swift @@ -506,6 +506,11 @@ class Tournament : ModelObject, Storable { return players.filter({ ($0.isImported() && $0.isValidLicenseNumber(year: licenseYearValidity) == false) || ($0.isImported() == false && ($0.licenceId == nil || $0.licenceId?.isLicenseNumber == false || $0.licenceId?.isEmpty == true)) }) } + func getStartDate(ofSeedIndex seedIndex: Int?) -> Date? { + guard let seedIndex else { return nil } + return selectedSortedTeams()[safe: seedIndex]?.callDate + } + func importTeams(_ teams: [FileImportManager.TeamHolder]) { var teamsToImport = [TeamRegistration]() teams.forEach { team in @@ -523,6 +528,16 @@ class Tournament : ModelObject, Storable { } + func isStartDateIsDifferentThanCallDate(_ team: TeamRegistration) -> Bool { + guard let callDate = team.callDate else { return false } + if let groupStageStartDate = team.groupStageObject()?.startDate { + return Calendar.current.compare(callDate, to: groupStageStartDate, toGranularity: .minute) != ComparisonResult.orderedSame + } else if let roundMatchStartDate = team.initialMatch()?.startDate { + return Calendar.current.compare(callDate, to: roundMatchStartDate, toGranularity: .minute) != ComparisonResult.orderedSame + } + return false + } + func lockRegistration() { closedRegistrationDate = Date() let count = selectedSortedTeams().count diff --git a/PadelClub/Manager/FileImportManager.swift b/PadelClub/Manager/FileImportManager.swift index ac224b4..3810e7e 100644 --- a/PadelClub/Manager/FileImportManager.swift +++ b/PadelClub/Manager/FileImportManager.swift @@ -79,7 +79,15 @@ class FileImportManager { func index(in teams: [TeamHolder]) -> Int? { teams.firstIndex(where: { $0.id == id }) } - + + func formattedSeedIndex(index: Int?) -> String { + if let index { + return "#\(index + 1)" + } else { + return "###" + } + } + func formattedSeed(in teams: [TeamHolder]) -> String { if let index = index(in: teams) { return "#\(index + 1)" diff --git a/PadelClub/Manager/PadelRule.swift b/PadelClub/Manager/PadelRule.swift index cd55595..71a5c96 100644 --- a/PadelClub/Manager/PadelRule.swift +++ b/PadelClub/Manager/PadelRule.swift @@ -1419,6 +1419,10 @@ enum RoundRule { return (1 << roundIndex) - 1 } + static func matchIndex(fromBracketPosition: Int) -> Int { + roundIndex(fromMatchIndex: fromBracketPosition / 2) + fromBracketPosition%2 + } + static func roundIndex(fromMatchIndex matchIndex: Int) -> Int { Int(log2(Double(matchIndex + 1))) } diff --git a/PadelClub/ViewModel/TournamentSeedEditing.swift b/PadelClub/ViewModel/TournamentSeedEditing.swift index 4519355..5f4964b 100644 --- a/PadelClub/ViewModel/TournamentSeedEditing.swift +++ b/PadelClub/ViewModel/TournamentSeedEditing.swift @@ -8,22 +8,13 @@ import Foundation import SwiftUI -// Create an environment key private struct TournamentSeedEditing: EnvironmentKey { - static let defaultValue: Bool = false + static let defaultValue: Binding = .constant(false) } -// ## Introduce new value to EnvironmentValues extension EnvironmentValues { - var isEditingTournamentSeed: Bool { + var isEditingTournamentSeed: Binding { get { self[TournamentSeedEditing.self] } set { self[TournamentSeedEditing.self] = newValue } } } - -// Add a dedicated modifier (Optional) -extension View { - func editTournamentSeed(_ value: Bool) -> some View { - environment(\.isEditingTournamentSeed, value) - } -} diff --git a/PadelClub/Views/Calling/CallView.swift b/PadelClub/Views/Calling/CallView.swift index 4abd713..84282d9 100644 --- a/PadelClub/Views/Calling/CallView.swift +++ b/PadelClub/Views/Calling/CallView.swift @@ -86,7 +86,11 @@ struct CallView: View { let callWord = teams.allSatisfy({ $0.called() }) ? "Reconvoquer" : "Convoquer" HStack { if teams.count == 1 { - Text(callWord + " cette paire par") + if let previousCallDate = teams.first?.callDate, Calendar.current.compare(previousCallDate, to: callDate, toGranularity: .minute) != .orderedSame { + Text("Reconvoquer " + callDate.localizedDate() + " par") + } else { + Text(callWord + " cette paire par") + } } else { Text(callWord + " ces \(teams.count) paires par") } diff --git a/PadelClub/Views/Calling/GroupStageCallingView.swift b/PadelClub/Views/Calling/GroupStageCallingView.swift index c401f3e..7b003c6 100644 --- a/PadelClub/Views/Calling/GroupStageCallingView.swift +++ b/PadelClub/Views/Calling/GroupStageCallingView.swift @@ -19,8 +19,8 @@ struct GroupStageCallingView: View { ForEach(groupStages) { groupStage in let seeds = groupStage.teams() - let callSeeds = seeds.filter({ $0.callDate != nil }) - + let callSeeds = seeds.filter({ tournament.isStartDateIsDifferentThanCallDate($0) == false }) + if seeds.isEmpty == false { Section { NavigationLink { @@ -51,8 +51,9 @@ struct GroupStageCallingView: View { ForEach(keys, id: \.self) { key in if let _groupStages = times[key], _groupStages.count > 1 { let teams = _groupStages.flatMap { $0.teams() } + let callSeeds = teams.filter({ tournament.isStartDateIsDifferentThanCallDate($0) == false }) Section { - CallView.CallStatusView(count: teams.filter({ $0.callDate != nil }).count, total: teams.count, startDate: key) + CallView.CallStatusView(count: callSeeds.count, total: teams.count, startDate: key) } header: { Text(groupStages.map { $0.groupStageTitle() }.joined(separator: ", ")) } footer: { diff --git a/PadelClub/Views/Calling/SeedsCallingView.swift b/PadelClub/Views/Calling/SeedsCallingView.swift index e41e049..d3b0400 100644 --- a/PadelClub/Views/Calling/SeedsCallingView.swift +++ b/PadelClub/Views/Calling/SeedsCallingView.swift @@ -15,7 +15,7 @@ struct SeedsCallingView: View { List { ForEach(tournament.rounds()) { round in let seeds = round.seeds() - let callSeeds = seeds.filter({ $0.callDate != nil }) + let callSeeds = seeds.filter({ tournament.isStartDateIsDifferentThanCallDate($0) == false }) if seeds.isEmpty == false { Section { NavigationLink { diff --git a/PadelClub/Views/Match/MatchRowView.swift b/PadelClub/Views/Match/MatchRowView.swift index 79e1e3b..0251cbb 100644 --- a/PadelClub/Views/Match/MatchRowView.swift +++ b/PadelClub/Views/Match/MatchRowView.swift @@ -10,11 +10,11 @@ import SwiftUI struct MatchRowView: View { var match: Match let matchViewStyle: MatchViewStyle - @Environment(\.editMode) private var editMode + @Environment(\.isEditingTournamentSeed) private var isEditingTournamentSeed @ViewBuilder var body: some View { - if editMode?.wrappedValue.isEditing == true && match.isGroupStage() == false && match.isLoserBracket == false { + if isEditingTournamentSeed.wrappedValue == true && match.isGroupStage() == false && match.isLoserBracket == false { MatchSetupView(match: match) } else { NavigationLink { diff --git a/PadelClub/Views/Round/LoserRoundsView.swift b/PadelClub/Views/Round/LoserRoundsView.swift index fd9dd32..9c86850 100644 --- a/PadelClub/Views/Round/LoserRoundsView.swift +++ b/PadelClub/Views/Round/LoserRoundsView.swift @@ -40,7 +40,7 @@ struct LoserRoundsView: View { struct LoserRoundView: View { @EnvironmentObject var dataStore: DataStore let loserRounds: [Round] - @Environment(\.editMode) private var editMode + @Environment(\.isEditingTournamentSeed) private var isEditingTournamentSeed private func _roundDisabled() -> Bool { loserRounds.allSatisfy({ $0.isDisabled() }) @@ -48,7 +48,7 @@ struct LoserRoundView: View { var body: some View { List { - if editMode?.wrappedValue.isEditing == true { + if isEditingTournamentSeed.wrappedValue == true { _editingView() } @@ -73,7 +73,9 @@ struct LoserRoundView: View { } .headerProminence(.increased) .toolbar { - EditButton() + Button(isEditingTournamentSeed.wrappedValue == true ? "Valider" : "Modifier") { + isEditingTournamentSeed.wrappedValue.toggle() + } } } diff --git a/PadelClub/Views/Round/RoundSettingsView.swift b/PadelClub/Views/Round/RoundSettingsView.swift index 8a412a0..8fd0963 100644 --- a/PadelClub/Views/Round/RoundSettingsView.swift +++ b/PadelClub/Views/Round/RoundSettingsView.swift @@ -9,7 +9,7 @@ import SwiftUI struct RoundSettingsView: View { @EnvironmentObject var dataStore: DataStore - @Environment(\.editMode) private var editMode + @Environment(\.isEditingTournamentSeed) private var isEditingTournamentSeed @Environment(Tournament.self) var tournament: Tournament var body: some View { @@ -21,7 +21,7 @@ struct RoundSettingsView: View { tournament.allRounds().forEach({ round in round.enableRound() }) - editMode?.wrappedValue = .active + self.isEditingTournamentSeed.wrappedValue = true } } diff --git a/PadelClub/Views/Round/RoundView.swift b/PadelClub/Views/Round/RoundView.swift index cc1cb11..ffa25a3 100644 --- a/PadelClub/Views/Round/RoundView.swift +++ b/PadelClub/Views/Round/RoundView.swift @@ -8,7 +8,7 @@ import SwiftUI struct RoundView: View { - @Environment(\.editMode) private var editMode + @Environment(\.isEditingTournamentSeed) private var isEditingTournamentSeed @Environment(Tournament.self) var tournament: Tournament @EnvironmentObject var dataStore: DataStore @@ -17,7 +17,7 @@ struct RoundView: View { var body: some View { List { - if editMode?.wrappedValue.isEditing == false { + if isEditingTournamentSeed.wrappedValue == false { let loserRounds = round.loserRounds() if loserRounds.isEmpty == false, let first = loserRounds.first(where: { $0.isDisabled() == false }) { Section { @@ -37,7 +37,7 @@ struct RoundView: View { try? dataStore.teamRegistrations.addOrUpdate(contentOfs: tournament.seeds()) if tournament.availableSeeds().isEmpty { - editMode?.wrappedValue = .inactive + self.isEditingTournamentSeed.wrappedValue = false } } } diff --git a/PadelClub/Views/Round/RoundsView.swift b/PadelClub/Views/Round/RoundsView.swift index 1134fed..db3b83d 100644 --- a/PadelClub/Views/Round/RoundsView.swift +++ b/PadelClub/Views/Round/RoundsView.swift @@ -10,13 +10,13 @@ import SwiftUI struct RoundsView: View { var tournament: Tournament @State private var selectedRound: Round? - @State var editMode: EditMode = .inactive + @State private var isEditingTournamentSeed = false init(tournament: Tournament) { self.tournament = tournament _selectedRound = State(wrappedValue: tournament.getActiveRound()) if tournament.availableSeeds().isEmpty == false { - _editMode = .init(wrappedValue: .active) + _isEditingTournamentSeed = State(wrappedValue: true) } } @@ -32,7 +32,7 @@ struct RoundsView: View { .navigationTitle(selectedRound.roundTitle()) } } - .environment(\.editMode, $editMode) + .environment(\.isEditingTournamentSeed, $isEditingTournamentSeed) .navigationBarTitleDisplayMode(.inline) .toolbarBackground(.visible, for: .navigationBar) } diff --git a/PadelClub/Views/Team/TeamDetailView.swift b/PadelClub/Views/Team/TeamDetailView.swift index fce9049..cff5a51 100644 --- a/PadelClub/Views/Team/TeamDetailView.swift +++ b/PadelClub/Views/Team/TeamDetailView.swift @@ -16,7 +16,11 @@ struct TeamDetailView: View { Text("Aucun joueur, espace réservé") } else { ForEach(team.players()) { player in - PlayerView(player: player) + NavigationLink { + Text("Hello wolrd") + } label: { + PlayerView(player: player) + } } } } diff --git a/PadelClub/Views/Team/TeamRowView.swift b/PadelClub/Views/Team/TeamRowView.swift index 1b02d36..4eebb7e 100644 --- a/PadelClub/Views/Team/TeamRowView.swift +++ b/PadelClub/Views/Team/TeamRowView.swift @@ -31,7 +31,7 @@ struct TeamRowView: View { } } label: { Text(team.teamLabel(.short)) - if let callDate = team.callDate { + if let callDate = team.callDate, displayCallDate { Text("Déjà convoquée \(callDate.localizedDate())") .foregroundStyle(.red) .italic() diff --git a/PadelClub/Views/Tournament/FileImportView.swift b/PadelClub/Views/Tournament/FileImportView.swift index 0974f39..9eeb438 100644 --- a/PadelClub/Views/Tournament/FileImportView.swift +++ b/PadelClub/Views/Tournament/FileImportView.swift @@ -141,29 +141,16 @@ struct FileImportView: View { } } Section { - ForEach(_filteredTeams) { team in - LabeledContent { - HStack { - if let previousTeam = team.previousTeam { - Text(previousTeam.formattedSeed(in: previousTeams)) - Image(systemName: "arrowshape.forward.fill") - } - Text(team.formattedSeed(in: _filteredTeams)) - } - } label: { - VStack(alignment: .leading) { - Text(team.playerOne.playerLabel()) - Text(team.playerTwo.playerLabel()) - } - } - } - } header: { - HStack { - Text("Équipe\(_filteredTeams.count.pluralSuffix) \(tournament.tournamentCategory.importingRawValue) détectée\(_filteredTeams.count.pluralSuffix)") - Spacer() + LabeledContent { Text(_filteredTeams.count.formatted()) + } label: { + Text("Équipe\(_filteredTeams.count.pluralSuffix) \(tournament.tournamentCategory.importingRawValue) détectée\(_filteredTeams.count.pluralSuffix)") } } + + ForEach(_filteredTeams) { team in + _teamView(team: team, inTeams: _filteredTeams, previousTeams: previousTeams) + } } } .onAppear { @@ -272,6 +259,34 @@ struct FileImportView: View { didImport = true } } + + @ViewBuilder + private func _teamView(team: FileImportManager.TeamHolder, inTeams teams: [FileImportManager.TeamHolder], previousTeams: [TeamRegistration]) -> some View { + + let newIndex = team.index(in: teams) + Section { + HStack { + VStack(alignment: .leading) { + Text(team.playerOne.playerLabel()) + Text(team.playerTwo.playerLabel()) + } + Spacer() + HStack { + if let previousTeam = team.previousTeam { + Text(previousTeam.formattedSeed(in: previousTeams)) + Image(systemName: "arrowshape.forward.fill") + } + Text(team.formattedSeedIndex(index: newIndex)) + } + } + 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) + .italic() + .font(.caption) + } + } + } private func _save() { try? dataStore.tournaments.addOrUpdate(instance: tournament) diff --git a/PadelClub/Views/Tournament/Screen/Components/InscriptionInfoView.swift b/PadelClub/Views/Tournament/Screen/Components/InscriptionInfoView.swift index 02c6d7d..5c9b896 100644 --- a/PadelClub/Views/Tournament/Screen/Components/InscriptionInfoView.swift +++ b/PadelClub/Views/Tournament/Screen/Components/InscriptionInfoView.swift @@ -10,14 +10,21 @@ import SwiftUI struct InscriptionInfoView: View { @EnvironmentObject var dataStore: DataStore @Environment(Tournament.self) var tournament - @State private var duplicates = [PlayerRegistration]() - @State private var problematicPlayers = [PlayerRegistration]() - @State private var inadequatePlayers = [PlayerRegistration]() - @State private var playersWithoutValidLicense = [PlayerRegistration]() - @State private var entriesFromBeachPadel = [TeamRegistration]() - @State private var playersMissing = [TeamRegistration]() - @State private var waitingList = [TeamRegistration]() - @State private var selectedTeams = [TeamRegistration]() + + var players : [PlayerRegistration] { tournament.unsortedPlayers() } + var selectedTeams : [TeamRegistration] { tournament.selectedSortedTeams() } + + var callDateIssue : [TeamRegistration] { + selectedTeams.filter { tournament.isStartDateIsDifferentThanCallDate($0) } + } + + var waitingList : [TeamRegistration] { tournament.waitingListTeams(in: selectedTeams) } + var duplicates : [PlayerRegistration] { tournament.duplicates(in: players) } + var problematicPlayers : [PlayerRegistration] { players.filter({ $0.sex == -1 }) } + var inadequatePlayers : [PlayerRegistration] { tournament.inadequatePlayers(in: players) } + var playersWithoutValidLicense : [PlayerRegistration] { tournament.playersWithoutValidLicense(in: players) } + var entriesFromBeachPadel : [TeamRegistration] { tournament.unsortedTeams().filter({ $0.isImported() }) } + var playersMissing : [TeamRegistration] { selectedTeams.filter({ $0.unsortedPlayers().count < 2 }) } var body: some View { List { @@ -47,6 +54,30 @@ struct InscriptionInfoView: View { .listRowView(color: .green) } + Section { + DisclosureGroup { + ForEach(callDateIssue) { team in + CallView.TeamView(team: team) + if let groupStage = team.groupStageObject(), let callDate = groupStage.startDate { + CallView(teams: [team], callDate: callDate, matchFormat: groupStage.matchFormat, roundLabel: "poule") + } else if let initialRound = team.initialRound(), + let initialMatch = team.initialMatch(), + let callDate = initialMatch.startDate { + CallView(teams: [team], callDate: callDate, matchFormat: initialMatch.matchFormat, roundLabel: initialRound.roundTitle()) + } + } + } label: { + LabeledContent { + Text(callDateIssue.count.formatted()) + } label: { + Text("Erreur de convocation") + } + } + .listRowView(color: .brown) + } footer: { + Text("L'horaire de la convocation est différente du match initial") + } + let waitingListInBracket = waitingList.filter({ $0.bracketPosition != nil }) let waitingListInGroupStage = waitingList.filter({ $0.groupStage != nil }) @@ -166,22 +197,7 @@ struct InscriptionInfoView: View { .navigationTitle("Synthèse") .navigationBarTitleDisplayMode(.inline) .toolbarBackground(.visible, for: .navigationBar) - .onAppear { - _initData() - } - } - - private func _initData() { - let players = tournament.unsortedPlayers() - selectedTeams = tournament.selectedSortedTeams() - waitingList = tournament.waitingListTeams(in: selectedTeams) - duplicates = tournament.duplicates(in: players) - problematicPlayers = players.filter({ $0.sex == -1 }) - inadequatePlayers = tournament.inadequatePlayers(in: players) - playersWithoutValidLicense = tournament.playersWithoutValidLicense(in: players) - entriesFromBeachPadel = tournament.unsortedTeams().filter({ $0.isImported() }) - playersMissing = selectedTeams.filter({ $0.unsortedPlayers().count < 2 }) - } + } } #Preview { diff --git a/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift b/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift index 230fca8..041f9ec 100644 --- a/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift +++ b/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift @@ -739,7 +739,7 @@ struct InscriptionManagerView: View { private func _teamFooterView(_ team: TeamRegistration) -> some View { HStack { if let formattedRegistrationDate = team.formattedInscriptionDate() { - Text(formattedRegistrationDate).font(.caption).foregroundStyle(.secondary) + Text(formattedRegistrationDate).foregroundStyle(.secondary) } Spacer() _teamMenuOptionView(team) @@ -748,7 +748,7 @@ struct InscriptionManagerView: View { private func _teamMenuOptionView(_ team: TeamRegistration) -> some View { Menu { Section { - Button("Éditer les joueurs") { + Button("Modifier l'équipe") { editedTeam = team team.unsortedPlayers().forEach { player in createdPlayers.insert(player) @@ -799,12 +799,11 @@ struct InscriptionManagerView: View { } label: { LabelDelete() } - } header: { - Text(team.teamLabel(.short)) +// } header: { +// Text(team.teamLabel(.short)) } } label: { LabelOptions().labelStyle(.titleOnly) - .font(.caption) } }