club_update
Razmig Sarkissian 1 year ago
parent 4b5c05dfa3
commit b7dbac40bc
  1. 12
      PadelClub.xcodeproj/project.pbxproj
  2. 2
      PadelClub/Data/Match.swift
  3. 2
      PadelClub/Data/Round.swift
  4. 32
      PadelClub/Data/TeamRegistration.swift
  5. 16
      PadelClub/Data/Tournament.swift
  6. 192
      PadelClub/Views/GroupStage/Components/GroupStageSettingsView.swift
  7. 166
      PadelClub/Views/GroupStage/GroupStageView.swift
  8. 21
      PadelClub/Views/GroupStage/GroupStagesSettingsView.swift
  9. 2
      PadelClub/Views/GroupStage/GroupStagesView.swift
  10. 11
      PadelClub/Views/GroupStage/Shared/GroupStageTeamReplacementView.swift
  11. 2
      PadelClub/Views/Team/TeamRowView.swift
  12. 69
      PadelClub/Views/Tournament/Screen/AddTeamView.swift
  13. 67
      PadelClub/Views/Tournament/Screen/BroadcastView.swift
  14. 464
      PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift
  15. 1
      PadelClub/Views/Tournament/Shared/TournamentCellView.swift

@ -137,7 +137,7 @@
FF5D30512BD94E1000F2B93D /* ImportedPlayer+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5D30502BD94E1000F2B93D /* ImportedPlayer+Extensions.swift */; };
FF5D30532BD94E2E00F2B93D /* PlayerHolder.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5D30522BD94E2E00F2B93D /* PlayerHolder.swift */; };
FF5D30562BD95B1100F2B93D /* OngoingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5D30552BD95B1100F2B93D /* OngoingView.swift */; };
FF5DA18F2BB9268800A33061 /* GroupStageSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5DA18E2BB9268800A33061 /* GroupStageSettingsView.swift */; };
FF5DA18F2BB9268800A33061 /* GroupStagesSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5DA18E2BB9268800A33061 /* GroupStagesSettingsView.swift */; };
FF5DA1932BB9279B00A33061 /* RoundSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5DA1922BB9279B00A33061 /* RoundSettingsView.swift */; };
FF5DA1952BB927E800A33061 /* GenericDestinationPickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5DA1942BB927E800A33061 /* GenericDestinationPickerView.swift */; };
FF5DA19B2BB9662200A33061 /* TournamentSeedEditing.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5DA19A2BB9662200A33061 /* TournamentSeedEditing.swift */; };
@ -229,6 +229,7 @@
FFC91AF92BD6A09100B29808 /* FortuneWheelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFC91AF82BD6A09100B29808 /* FortuneWheelView.swift */; };
FFC91B012BD85C2F00B29808 /* Court.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFC91B002BD85C2F00B29808 /* Court.swift */; };
FFC91B032BD85E2400B29808 /* CourtView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFC91B022BD85E2400B29808 /* CourtView.swift */; };
FFCB74132C4625BB008384D0 /* GroupStageSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFCB74122C4625BB008384D0 /* GroupStageSettingsView.swift */; };
FFCD16B32C3E5E590092707B /* TeamsCallingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFCD16B22C3E5E590092707B /* TeamsCallingView.swift */; };
FFCEDA4C2C2C08EA00F8C0F2 /* PlayersWithoutContactView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFCEDA4B2C2C08EA00F8C0F2 /* PlayersWithoutContactView.swift */; };
FFCF76072C3BE9BC006C8C3D /* CloseDatePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFCF76062C3BE9BC006C8C3D /* CloseDatePicker.swift */; };
@ -474,7 +475,7 @@
FF5D30502BD94E1000F2B93D /* ImportedPlayer+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ImportedPlayer+Extensions.swift"; sourceTree = "<group>"; };
FF5D30522BD94E2E00F2B93D /* PlayerHolder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerHolder.swift; sourceTree = "<group>"; };
FF5D30552BD95B1100F2B93D /* OngoingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OngoingView.swift; sourceTree = "<group>"; };
FF5DA18E2BB9268800A33061 /* GroupStageSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupStageSettingsView.swift; sourceTree = "<group>"; };
FF5DA18E2BB9268800A33061 /* GroupStagesSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupStagesSettingsView.swift; sourceTree = "<group>"; };
FF5DA1922BB9279B00A33061 /* RoundSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoundSettingsView.swift; sourceTree = "<group>"; };
FF5DA1942BB927E800A33061 /* GenericDestinationPickerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GenericDestinationPickerView.swift; sourceTree = "<group>"; };
FF5DA19A2BB9662200A33061 /* TournamentSeedEditing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TournamentSeedEditing.swift; sourceTree = "<group>"; };
@ -567,6 +568,7 @@
FFC91AF82BD6A09100B29808 /* FortuneWheelView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FortuneWheelView.swift; sourceTree = "<group>"; };
FFC91B002BD85C2F00B29808 /* Court.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Court.swift; sourceTree = "<group>"; };
FFC91B022BD85E2400B29808 /* CourtView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CourtView.swift; sourceTree = "<group>"; };
FFCB74122C4625BB008384D0 /* GroupStageSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupStageSettingsView.swift; sourceTree = "<group>"; };
FFCD16B22C3E5E590092707B /* TeamsCallingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TeamsCallingView.swift; sourceTree = "<group>"; };
FFCEDA4B2C2C08EA00F8C0F2 /* PlayersWithoutContactView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayersWithoutContactView.swift; sourceTree = "<group>"; };
FFCF76062C3BE9BC006C8C3D /* CloseDatePicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloseDatePicker.swift; sourceTree = "<group>"; };
@ -1166,7 +1168,7 @@
children = (
FF967CFA2BAEE13800A9A3BD /* GroupStageView.swift */,
FF967CFB2BAEE13900A9A3BD /* GroupStagesView.swift */,
FF5DA18E2BB9268800A33061 /* GroupStageSettingsView.swift */,
FF5DA18E2BB9268800A33061 /* GroupStagesSettingsView.swift */,
FF135BF82C2FCB8300C9247A /* LoserGroupStageSettingsView.swift */,
FF9AC3932BE3625D00C2E883 /* Components */,
FF9AC3922BE3625200C2E883 /* Shared */,
@ -1211,6 +1213,7 @@
isa = PBXGroup;
children = (
FFBF065B2BBD2657009D6715 /* GroupStageTeamView.swift */,
FFCB74122C4625BB008384D0 /* GroupStageSettingsView.swift */,
);
path = Components;
sourceTree = "<group>";
@ -1641,6 +1644,7 @@
FF59FFB92B90EFD70061EFF9 /* ToolboxView.swift in Sources */,
FF8E1CE62C006E0200184680 /* Alphabet.swift in Sources */,
FFF8ACD92B923F3C008466FA /* String+Extensions.swift in Sources */,
FFCB74132C4625BB008384D0 /* GroupStageSettingsView.swift in Sources */,
FF025AE52BD0EBB800A86CF8 /* TournamentGeneralSettingsView.swift in Sources */,
FFC2DCB22BBE75D40046DB9F /* LoserRoundView.swift in Sources */,
FF90FC1D2C44FB3E009339B2 /* AddTeamView.swift in Sources */,
@ -1692,7 +1696,7 @@
FF8F26452BAE0A3400650388 /* TournamentDurationManagerView.swift in Sources */,
FF1DC5532BAB354A00FD8220 /* MockData.swift in Sources */,
FF967D092BAF3D4000A9A3BD /* TeamDetailView.swift in Sources */,
FF5DA18F2BB9268800A33061 /* GroupStageSettingsView.swift in Sources */,
FF5DA18F2BB9268800A33061 /* GroupStagesSettingsView.swift in Sources */,
FF663FBE2BE019EC0031AE83 /* TournamentFilterView.swift in Sources */,
FF1F4B752BFA00FC000B4573 /* HtmlGenerator.swift in Sources */,
FF8F26382BAD523300650388 /* PadelRule.swift in Sources */,

@ -608,8 +608,6 @@ defer {
func canBeStarted(inMatches matches: [Match]) -> Bool {
let teams = teamScores
guard teams.count == 2 else { return false }
guard hasEnded() == false else { return false }
guard hasStarted() == false else { return false }
return teams.compactMap({ $0.team }).allSatisfy({ $0.canPlay() && isTeamPlaying($0, inMatches: matches) == false })
}

@ -511,7 +511,7 @@ defer {
return seedInterval.localizedLabel(displayStyle)
}
print("Round pas trouvé", id, parent, index)
return "--"
return "Match de classement"
}
return RoundRule.roundName(fromRoundIndex: index, displayStyle: displayStyle)
}

@ -71,6 +71,15 @@ final class TeamRegistration: ModelObject, Storable {
// MARK: -
func deleteTeamScores() {
let ts = self.tournamentStore.teamScores.filter({ $0.teamRegistration == id })
do {
try self.tournamentStore.teamScores.delete(contentOfs: ts)
} catch {
Logger.error(error)
}
}
override func deleteDependencies() throws {
let unsortedPlayers = unsortedPlayers()
for player in unsortedPlayers {
@ -264,18 +273,29 @@ final class TeamRegistration: ModelObject, Storable {
}
func resetGroupeStagePosition() {
groupStageObject()?._matches().forEach({ $0.updateTeamScores() })
if let groupStage {
let matches = self.tournamentStore.matches.filter({ $0.groupStage == groupStage }).map { $0.id }
let teamScores = self.tournamentStore.teamScores.filter({ $0.teamRegistration == id && matches.contains($0.match) })
do {
try tournamentStore.teamScores.delete(contentOfs: teamScores)
} catch {
Logger.error(error)
}
}
//groupStageObject()?._matches().forEach({ $0.updateTeamScores() })
groupStage = nil
groupStagePosition = nil
}
func resetBracketPosition() {
guard let bracketPosition else { return }
guard let tournamentObject = tournamentObject() else { return }
if let match = tournamentObject.match(for: bracketPosition) {
let teamScores = match.teamScores.filter({ $0.teamRegistration != self.id })
tournamentObject.resetTeamScores(in: bracketPosition, outsideOf: teamScores)
let matches = self.tournamentStore.matches.filter({ $0.groupStage == nil }).map { $0.id }
let teamScores = self.tournamentStore.teamScores.filter({ $0.teamRegistration == id && matches.contains($0.match) })
do {
try tournamentStore.teamScores.delete(contentOfs: teamScores)
} catch {
Logger.error(error)
}
self.bracketPosition = nil
}

@ -1063,7 +1063,7 @@ defer {
}
}
func registrationIssues() async -> Int {
func registrationIssues() -> Int {
let players : [PlayerRegistration] = unsortedPlayers()
let selectedTeams : [TeamRegistration] = selectedSortedTeams()
let callDateIssue : [TeamRegistration] = selectedTeams.filter { $0.callDate != nil && isStartDateIsDifferentThanCallDate($0) }
@ -1280,6 +1280,20 @@ defer {
}
}
func unlockRegistration() {
closedRegistrationDate = nil
let teams = unsortedTeams()
teams.forEach { team in
team.lockedWeight = nil
}
do {
try self.tournamentStore.teamRegistrations.addOrUpdate(contentOfs: teams)
} catch {
Logger.error(error)
}
}
func updateWeights() {
let teams = self.unsortedTeams()
teams.forEach { team in

@ -0,0 +1,192 @@
//
// GroupStageSettingsView.swift
// PadelClub
//
// Created by Razmig Sarkissian on 16/07/2024.
//
import SwiftUI
import LeStorage
struct GroupStageSettingsView: View {
@Environment(\.dismiss) private var dismiss
@Environment(Tournament.self) private var tournament
@EnvironmentObject var dataStore: DataStore
@Bindable var groupStage: GroupStage
@State private var groupStageName: String
@State private var presentConfirmationButton: Bool = false
@State private var size: Int
@State private var courtIndex: Int
init(groupStage: GroupStage) {
_groupStage = Bindable(groupStage)
_groupStageName = .init(wrappedValue: groupStage.name ?? "")
_size = .init(wrappedValue: groupStage.size)
_courtIndex = .init(wrappedValue: groupStage._matches().first?.courtIndex ?? 0)
}
var tournamentStore: TournamentStore {
return self.tournament.tournamentStore
}
var body: some View {
Form {
Section {
TextField("Nom de la poule", text: $groupStageName)
.keyboardType(.alphabet)
.frame(maxWidth: .infinity)
.onSubmit {
groupStageName = groupStageName.trimmed
if groupStageName.isEmpty == false {
groupStage.name = groupStageName
_save()
dismiss()
}
}
} footer: {
if groupStage.name != nil {
HStack {
Spacer()
FooterButtonView("retirer le nom") {
groupStage.name = nil
groupStageName = ""
_save()
}
}
}
}
Section {
CourtPicker(title: "Terrain dédié", selection: $courtIndex, maxCourt: tournament.courtCount)
RowButtonView("Confirmer", role: .destructive) {
groupStage._matches().forEach { match in
match.setCourt(courtIndex)
}
do {
try tournamentStore.matches.addOrUpdate(contentOfs: groupStage._matches())
} catch {
Logger.error(error)
}
}
}
Section {
LabeledContent {
StepperView(count: $size, minimum: minimumSize(), maximum: maximumSize())
} label: {
Text("Taille de la poule")
}
if presentConfirmationButton {
RowButtonView("Confirmer", role: .destructive, confirmationMessage: "Tous les matchs et les équipes de cette poule seront ré-initialisés") {
let teams = groupStage.teams()
teams.forEach { team in
team.groupStagePosition = nil
team.groupStage = nil
groupStage._matches().forEach({ $0.updateTeamScores() })
}
do {
try tournamentStore.teamRegistrations.addOrUpdate(contentOfs: teams)
} catch {
Logger.error(error)
}
groupStage.size = size
groupStage.buildMatches()
tournament.shouldVerifyGroupStage = true
_save()
presentConfirmationButton = false
}
}
} footer: {
Text("Une poule ne peut pas avoir moins de 3 équipes et plus d'une équipe de différence par rapport aux autres poules.")
}
Section {
RowButtonView("Retirer tous les horaires", role: .destructive) {
groupStage._matches().forEach { match in
match.startDate = nil
match.endDate = nil
}
do {
try tournamentStore.matches.addOrUpdate(contentOfs: groupStage._matches())
} catch {
Logger.error(error)
}
}
}
Section {
RowButtonView("Retirer tout le monde", role: .destructive) {
let teams = groupStage.teams()
teams.forEach { team in
team.groupStagePosition = nil
team.groupStage = nil
groupStage._matches().forEach({ $0.updateTeamScores() })
}
do {
try tournamentStore.teamRegistrations.addOrUpdate(contentOfs: teams)
} catch {
Logger.error(error)
}
}
} footer: {
Text("Toutes les équipes seront retirées et les scores des matchs seront perdus.")
}
Section {
RowButtonView("Recommencer tous les matchs", role: .destructive) {
groupStage.buildMatches()
}
} footer: {
Text("Tous les matchs seront recronstruits, les données des matchs seront perdus.")
}
}
.onChange(of: size) {
if size != groupStage.size {
presentConfirmationButton = true
}
}
.navigationTitle("Paramètres")
.toolbarBackground(.visible, for: .navigationBar)
}
private func maximumSize() -> Int {
return 10
// if groupStage.index == 0 {
// return tournament.teamsPerGroupStage + 1
// }
//
// if let previousGroupStage = tournament.groupStages().first(where: { $0.index == groupStage.index - 1 }), previousGroupStage.size > groupStage.size {
// return tournament.teamsPerGroupStage + 1
// }
//
// return tournament.teamsPerGroupStage
}
private func minimumSize() -> Int {
return 3
// if groupStage.index == tournament.groupStageCount - 1 {
// return max(3, tournament.teamsPerGroupStage - 1)
// }
//
// if let nextGroupStage = tournament.groupStages().first(where: { $0.index == groupStage.index + 1 }), nextGroupStage.size < groupStage.size {
// return max(3, tournament.teamsPerGroupStage - 1)
// }
//
// return tournament.teamsPerGroupStage
}
private func _save() {
do {
try tournamentStore.groupStages.addOrUpdate(instance: groupStage)
} catch {
Logger.error(error)
}
}
}

@ -16,8 +16,6 @@ struct GroupStageView: View {
@Bindable var groupStage: GroupStage
@State private var confirmGroupStageStart: Bool = false
@State private var sortingMode: GroupStageSortingMode = .auto
@State private var confirmRemoveAll: Bool = false
@State private var confirmResetMatch: Bool = false
let playedMatches: [Match]
init(groupStage: GroupStage) {
@ -233,166 +231,20 @@ struct GroupStageView: View {
}
private func _groupStageMenuView() -> some View {
Menu {
NavigationLink {
GroupStageNameEditionView(groupStage: groupStage)
GroupStageSettingsView(groupStage: groupStage)
.environment(tournament)
} label: {
Label("Modifier le nom et la taille", systemImage: "pencil")
Label {
Text("Modifier")
} icon: {
Image(systemName: "gear.circle")
.resizable()
.scaledToFit()
.frame(height: 28)
}
Button("Retirer tout le monde", role: .destructive) {
confirmRemoveAll = true
.labelStyle(.titleOnly)
}
Button("Recommencer tous les matchs", role: .destructive) {
confirmResetMatch = true
}
} label: {
LabelOptions()
}
.confirmationDialog("Êtes-vous sûr de vouloir faire cela ?", isPresented: $confirmRemoveAll, titleVisibility: .visible) {
Button("Oui") {
let teams = groupStage.teams()
teams.forEach { team in
team.groupStagePosition = nil
team.groupStage = nil
groupStage._matches().forEach({ $0.updateTeamScores() })
}
do {
try tournamentStore.teamRegistrations.addOrUpdate(contentOfs: teams)
} catch {
Logger.error(error)
}
}
}
.confirmationDialog("Êtes-vous sûr de vouloir faire cela ?", isPresented: $confirmResetMatch, titleVisibility: .visible) {
Button("Oui") {
groupStage.buildMatches()
}
}
}
private func _save() {
do {
try tournamentStore.groupStages.addOrUpdate(instance: groupStage)
} catch {
Logger.error(error)
}
}
}
struct GroupStageNameEditionView: View {
@Environment(\.dismiss) private var dismiss
@Environment(Tournament.self) private var tournament
@EnvironmentObject var dataStore: DataStore
@Bindable var groupStage: GroupStage
@State private var groupStageName: String
@State private var presentConfirmationButton: Bool = false
@State private var size: Int
init(groupStage: GroupStage) {
_groupStage = Bindable(groupStage)
_groupStageName = .init(wrappedValue: groupStage.name ?? "")
_size = .init(wrappedValue: groupStage.size)
}
var tournamentStore: TournamentStore {
return self.tournament.tournamentStore
}
var body: some View {
Form {
Section {
TextField("Nom de la poule", text: $groupStageName)
.keyboardType(.alphabet)
.frame(maxWidth: .infinity)
.onSubmit {
groupStageName = groupStageName.trimmed
if groupStageName.isEmpty == false {
groupStage.name = groupStageName
_save()
dismiss()
}
}
} footer: {
if groupStage.name != nil {
HStack {
Spacer()
FooterButtonView("retirer le nom") {
groupStage.name = nil
groupStageName = ""
_save()
}
}
}
}
Section {
LabeledContent {
StepperView(count: $size, minimum: minimumSize(), maximum: maximumSize())
} label: {
Text("Taille de la poule")
}
if presentConfirmationButton {
RowButtonView("Confirmer", role: .destructive, confirmationMessage: "Tous les matchs et les équipes de cette poule seront ré-initialisés") {
let teams = groupStage.teams()
teams.forEach { team in
team.groupStagePosition = nil
team.groupStage = nil
groupStage._matches().forEach({ $0.updateTeamScores() })
}
do {
try tournamentStore.teamRegistrations.addOrUpdate(contentOfs: teams)
} catch {
Logger.error(error)
}
groupStage.size = size
groupStage.buildMatches()
tournament.shouldVerifyGroupStage = true
_save()
presentConfirmationButton = false
}
}
} footer: {
Text("Vous ne pouvez réduire la taille qu'à partir de la dernière poule et augmentez la taille qu'à partir de la première poule. Une poule ne peut pas avoir moins de 3 équipes et plus d'une équipe de différence par rapport aux autres poules.")
}
}
.onChange(of: size) {
presentConfirmationButton = true
}
.navigationTitle(groupStage.groupStageTitle())
.toolbarBackground(.visible, for: .navigationBar)
}
private func maximumSize() -> Int {
return 10
// if groupStage.index == 0 {
// return tournament.teamsPerGroupStage + 1
// }
//
// if let previousGroupStage = tournament.groupStages().first(where: { $0.index == groupStage.index - 1 }), previousGroupStage.size > groupStage.size {
// return tournament.teamsPerGroupStage + 1
// }
//
// return tournament.teamsPerGroupStage
}
private func minimumSize() -> Int {
return 3
// if groupStage.index == tournament.groupStageCount - 1 {
// return max(3, tournament.teamsPerGroupStage - 1)
// }
//
// if let nextGroupStage = tournament.groupStages().first(where: { $0.index == groupStage.index + 1 }), nextGroupStage.size < groupStage.size {
// return max(3, tournament.teamsPerGroupStage - 1)
// }
//
// return tournament.teamsPerGroupStage
}
private func _save() {

@ -1,5 +1,5 @@
//
// GroupStageSettingsView.swift
// GroupStagesSettingsView.swift
// PadelClub
//
// Created by Razmig Sarkissian on 31/03/2024.
@ -8,7 +8,7 @@
import SwiftUI
import LeStorage
struct GroupStageSettingsView: View {
struct GroupStagesSettingsView: View {
@EnvironmentObject var dataStore: DataStore
@Environment(Tournament.self) var tournament: Tournament
@ -110,6 +110,23 @@ struct GroupStageSettingsView: View {
// Text("Match de perdant de poules")
// }
Section {
RowButtonView("Retirer tous les horaires", role: .destructive) {
let matches = tournament.groupStages().flatMap({ $0._matches() })
tournament.groupStages().flatMap({ $0._matches() }).forEach { match in
match.startDate = nil
match.endDate = nil
}
do {
try tournamentStore.matches.addOrUpdate(contentOfs: matches)
} catch {
Logger.error(error)
}
}
}
if tournament.unsortedTeams().filter({ $0.groupStagePosition != nil }).isEmpty == false {
Section {
menuBuildAllGroupStages

@ -111,7 +111,7 @@ struct GroupStagesView: View {
case .groupStage(let groupStage):
GroupStageView(groupStage: groupStage).id(groupStage.id)
case nil:
GroupStageSettingsView()
GroupStagesSettingsView()
.navigationTitle("Réglages")
}
}

@ -134,11 +134,11 @@ struct GroupStageTeamReplacementView: View {
@ViewBuilder
var body: some View {
HStack {
TeamRangeSideView(team: teamRange.left, playerWeight: -playerWeight)
TeamRangeSideView(team: teamRange.left, playerWeight: -playerWeight, leftLimit: true)
Spacer()
Image(systemName: "arrowshape.forward.fill")
Spacer()
TeamRangeSideView(team: teamRange.right, playerWeight: -playerWeight)
TeamRangeSideView(team: teamRange.right, playerWeight: -playerWeight, leftLimit: false)
}
}
}
@ -146,13 +146,18 @@ struct GroupStageTeamReplacementView: View {
struct TeamRangeSideView: View {
let team: TeamRegistration?
let playerWeight: Int
let leftLimit: Bool
@ViewBuilder
var body: some View {
if let team, team.weight + playerWeight > 0 {
Text((team.weight + playerWeight).formatted()).font(.largeTitle)
} else {
Text("Aucune limite")
if leftLimit {
Text("1").font(.largeTitle)
} else {
Text("Non classé").font(.largeTitle)
}
}
}
}

@ -32,7 +32,7 @@ struct TeamRowView: View {
.font(.caption)
} else {
Text("Pas encore convoquée")
.foregroundStyle(.logoYellow)
.foregroundStyle(.logoRed)
.italic()
.font(.caption)
}

@ -32,7 +32,6 @@ struct AddTeamView: View {
@State private var pasteString: String?
@State private var selectionSearchField: String?
@State private var autoSelect: Bool = false
@State private var teamsHash: Int?
@State private var presentationCount: Int = 0
@State private var confirmDuplicate: Bool = false
@ -40,7 +39,7 @@ struct AddTeamView: View {
return self.tournament.tournamentStore
}
init(tournament: Tournament, pasteString: String? = nil, editedTeam: TeamRegistration?) {
init(tournament: Tournament, pasteString: String? = nil, editedTeam: TeamRegistration? = nil) {
self.tournament = tournament
_editedTeam = .init(wrappedValue: editedTeam)
if let team = editedTeam {
@ -64,57 +63,6 @@ struct AddTeamView: View {
}
}
// Function to create a simple hash from a list of IDs
private func _simpleHash(ids: [String]) -> Int {
// Combine the hash values of each string
return ids.reduce(0) { $0 ^ $1.hashValue }
}
// Function to check if two lists of IDs produce different hashes
private func _areDifferent(ids1: [String], ids2: [String]) -> Bool {
return _simpleHash(ids: ids1) != _simpleHash(ids: ids2)
}
private func _setHash() async {
#if DEBUG_TIME //DEBUGING TIME
let start = Date()
defer {
let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000)
print("func _setHash", duration.formatted(.units(allowed: [.seconds, .milliseconds])))
}
#endif
let selectedSortedTeams = tournament.selectedSortedTeams()
if self.teamsHash == nil, selectedSortedTeams.isEmpty == false {
self.teamsHash = _simpleHash(ids: selectedSortedTeams.map { $0.id })
}
}
private func _handleHashDiff() async {
let selectedSortedTeams = tournament.selectedSortedTeams()
let newHash = _simpleHash(ids: selectedSortedTeams.map { $0.id })
if (self.teamsHash != nil && newHash != teamsHash!) || (self.teamsHash == nil && selectedSortedTeams.isEmpty == false) {
self.teamsHash = newHash
if self.tournament.shouldVerifyBracket == false || self.tournament.shouldVerifyGroupStage == false {
self.tournament.shouldVerifyBracket = true
self.tournament.shouldVerifyGroupStage = true
let waitingList = self.tournament.waitingListTeams(in: selectedSortedTeams, includingWalkOuts: true)
waitingList.forEach { team in
if team.bracketPosition != nil || team.groupStagePosition != nil {
team.resetPositions()
}
}
do {
try tournamentStore.teamRegistrations.addOrUpdate(contentOfs: waitingList)
try dataStore.tournaments.addOrUpdate(instance: tournament)
} catch {
Logger.error(error)
}
}
}
}
var body: some View {
_buildingTeamView()
.navigationBarBackButtonHidden(true)
@ -198,12 +146,6 @@ struct AddTeamView: View {
tournament.unsortedPlayers()
}
private func _getTeams() {
Task {
await _setHash()
}
}
@ViewBuilder
private func _managementView() -> some View {
Section {
@ -494,15 +436,6 @@ struct AddTeamView: View {
_managementView()
}
}
.onAppear {
_getTeams()
}
.onDisappear {
Task {
await _handleHashDiff()
}
}
.headerProminence(.increased)
.onReceive(fetchPlayers.publisher.count()) { _ in // <-- here
if let pasteString, count == 2, autoSelect == true {

@ -82,6 +82,40 @@ struct BroadcastView: View {
.tipStyle(tint: nil)
}
Section {
TipView(tournamentTVBroadcastTip)
.tipStyle(tint: nil)
}
if let url = tournament.shareURL(.clubBroadcast) {
Section {
Link(destination: url) {
Text(url.absoluteString)
}
.contextMenu {
Button("Copier") {
let pasteboard = UIPasteboard.general
pasteboard.string = url.absoluteString
}
}
} header: {
Text("Lien pour la diffusion TV")
} footer: {
Text("Lien à mettre sur une smart tv ou ordinateur dans le club house par exemple ! Disponible même si le tournoi est privée.")
}
}
Section {
Toggle(isOn: $tournament.isPrivate) {
Text("Tournoi privé")
}
} footer: {
let footerString = "Le tournoi sera masqué sur le site [Padel Club](\(URLs.main.rawValue))"
Text(.init(footerString))
}
if tournament.isPrivate == false {
Section {
let links : [PageLink] = [.teams, .summons, .groupStages, .matches, .rankings, .broadcast, .clubBroadcast]
Picker(selection: $pageLink) {
@ -105,30 +139,6 @@ struct BroadcastView: View {
Text("Autres liens")
}
if tournament.isPrivate == false {
Section {
TipView(tournamentTVBroadcastTip)
.tipStyle(tint: nil)
}
if let url = tournament.shareURL(.clubBroadcast) {
Section {
Link(destination: url) {
Text(url.absoluteString)
}
.contextMenu {
Button("Copier") {
let pasteboard = UIPasteboard.general
pasteboard.string = url.absoluteString
}
}
} header: {
Text("Lien pour la diffusion TV")
} footer: {
Text("Lien à mettre sur une smart tv ou ordinateur dans le club house par exemple !")
}
}
Section {
LabeledContent {
@ -286,15 +296,6 @@ struct BroadcastView: View {
}
//todo waitinglist & info
Section {
Toggle(isOn: $tournament.isPrivate) {
Text("Tournoi privé")
}
} footer: {
let footerString = "Le tournoi sera masqué sur le site [Padel Club](\(URLs.main.rawValue))"
Text(.init(footerString))
}
}
}
.headerProminence(.increased)

@ -9,15 +9,15 @@ import SwiftUI
import TipKit
import LeStorage
let slideToDeleteTip = SlideToDeleteTip()
//let slideToDeleteTip = SlideToDeleteTip()
let inscriptionManagerWomanRankTip = InscriptionManagerWomanRankTip()
let fileTip = InscriptionManagerFileInputTip()
let pasteTip = InscriptionManagerPasteInputTip()
let searchTip = InscriptionManagerSearchInputTip()
let createTip = InscriptionManagerCreateInputTip()
//let fileTip = InscriptionManagerFileInputTip()
//let pasteTip = InscriptionManagerPasteInputTip()
//let searchTip = InscriptionManagerSearchInputTip()
//let createTip = InscriptionManagerCreateInputTip()
let rankUpdateTip = InscriptionManagerRankUpdateTip()
let padelBeachExportTip = PadelBeachExportTip()
let padelBeachImportTip = PadelBeachImportTip()
//let padelBeachExportTip = PadelBeachExportTip()
//let padelBeachImportTip = PadelBeachImportTip()
struct InscriptionManagerView: View {
@ -26,11 +26,6 @@ struct InscriptionManagerView: View {
@EnvironmentObject var networkMonitor: NetworkMonitor
@Environment(\.dismiss) var dismiss
@FetchRequest(
sortDescriptors: [],
animation: .default)
private var fetchPlayers: FetchedResults<ImportedPlayer>
var tournament: Tournament
var cancelShouldDismiss: Bool = false
@ -52,14 +47,11 @@ struct InscriptionManagerView: View {
@State private var contactType: ContactType? = nil
@State private var sentError: ContactManagerError? = nil
@State private var showSubscriptionView: Bool = false
@State private var registrationIssues: Int? = nil
@State private var sortedTeams: [TeamRegistration] = []
@State private var walkoutTeams: [TeamRegistration] = []
@State private var unsortedTeamsWithoutWO: [TeamRegistration] = []
@State private var unsortedPlayers: [PlayerRegistration] = []
@State private var teamPaste: URL?
@State private var confirmDuplicate: Bool = false
@State private var presentAddTeamView: Bool = false
@State private var compactMode: Bool = false
@State private var pasteString: String?
var tournamentStore: TournamentStore {
return self.tournament.tournamentStore
}
@ -95,15 +87,21 @@ struct InscriptionManagerView: View {
case all
case walkOut
case waiting
case bracket
case groupStage
func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String {
switch self {
case .all:
return displayStyle == .wide ? "Équipes inscrites / souhaitées" : "Équipes inscrites"
return displayStyle == .wide ? "Équipes inscrites / souhaitées" : "Paires inscrites"
case .bracket:
return displayStyle == .wide ? "En Tableau" : "Tableau"
case .groupStage:
return displayStyle == .wide ? "En Poule" : "Poule"
case .walkOut:
return "Forfaits"
return displayStyle == .wide ? "Forfaits" : "Forfait"
case .waiting:
return "Liste d'attente"
return displayStyle == .wide ? "Liste d'attente" : "Attente"
}
}
}
@ -113,15 +111,6 @@ struct InscriptionManagerView: View {
_currentRankSourceDate = State(wrappedValue: tournament.rankSourceDate)
}
private func _clearScreen() {
teamPaste = nil
unsortedPlayers.removeAll()
walkoutTeams.removeAll()
unsortedTeamsWithoutWO.removeAll()
sortedTeams.removeAll()
registrationIssues = nil
}
// Function to create a simple hash from a list of IDs
private func _simpleHash(ids: [String]) -> Int {
// Combine the hash values of each string
@ -133,7 +122,7 @@ struct InscriptionManagerView: View {
return _simpleHash(ids: ids1) != _simpleHash(ids: ids2)
}
private func _setHash() async {
private func _setHash() {
#if DEBUG_TIME //DEBUGING TIME
let start = Date()
defer {
@ -147,7 +136,7 @@ struct InscriptionManagerView: View {
}
}
private func _handleHashDiff() async {
private func _handleHashDiff() {
let selectedSortedTeams = tournament.selectedSortedTeams()
let newHash = _simpleHash(ids: selectedSortedTeams.map { $0.id })
if (self.teamsHash != nil && newHash != teamsHash!) || (self.teamsHash == nil && selectedSortedTeams.isEmpty == false) {
@ -200,13 +189,10 @@ struct InscriptionManagerView: View {
}
}
.onAppear {
_getTeams()
_setHash()
}
.onDisappear {
Task {
await _handleHashDiff()
}
_handleHashDiff()
}
.alert("Un problème est survenu", isPresented: messageSentFailed) {
Button("OK") {
@ -268,7 +254,7 @@ struct InscriptionManagerView: View {
.tint(.master)
}
.sheet(isPresented: $presentImportView, onDismiss: {
_getTeams()
_setHash()
}) {
NavigationStack {
FileImportView()
@ -276,14 +262,12 @@ struct InscriptionManagerView: View {
.tint(.master)
}
.onChange(of: tournament.prioritizeClubMembers) {
_clearScreen()
_save()
_getTeams()
_setHash()
}
.onChange(of: tournament.teamSorting) {
_clearScreen()
_save()
_getTeams()
_setHash()
}
.onChange(of: currentRankSourceDate) {
if let currentRankSourceDate, tournament.rankSourceDate != currentRankSourceDate {
@ -297,26 +281,36 @@ struct InscriptionManagerView: View {
.tint(.master)
}
.sheet(isPresented: $presentAddTeamView, onDismiss: {
editedTeam = nil
_getTeams()
_setHash()
}) {
NavigationStack {
AddTeamView(tournament: tournament, editedTeam: editedTeam)
AddTeamView(tournament: tournament)
}
.tint(.master)
}
.onChange(of: filterMode) {
_prepareTeams()
.sheet(item: $editedTeam, onDismiss: {
_setHash()
}) { editedTeam in
NavigationStack {
AddTeamView(tournament: tournament, editedTeam: editedTeam)
}
.onChange(of: sortingMode) {
_prepareTeams()
.tint(.master)
}
.onChange(of: byDecreasingOrdering) {
_prepareTeams()
.sheet(item: $pasteString, onDismiss: {
_setHash()
}) { pasteString in
NavigationStack {
AddTeamView(tournament: tournament, pasteString: pasteString)
}
.tint(.master)
}
.toolbar {
ToolbarItemGroup(placement: .navigationBarTrailing) {
Menu {
Toggle(isOn: $compactMode) {
Text("Vue compact")
}
Divider()
Picker(selection: $filterMode) {
ForEach(FilterMode.allCases) {
Text($0.localizedLabel(.short)).tag($0)
@ -337,6 +331,7 @@ struct InscriptionManagerView: View {
}
} label: {
LabelFilter()
.symbolVariant(filterMode == .all ? .none : .fill)
}
Menu {
if tournament.inscriptionClosed() == false {
@ -372,7 +367,7 @@ struct InscriptionManagerView: View {
}
} else {
Button {
tournament.closedRegistrationDate = nil
tournament.unlockRegistration()
_save()
} label: {
Label("Ré-ouvrir", systemImage: "lock.open")
@ -392,41 +387,42 @@ struct InscriptionManagerView: View {
.navigationBarTitleDisplayMode(.inline)
}
private func _prepareStats() async {
#if DEBUG_TIME //DEBUGING TIME
let start = Date()
defer {
let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000)
print("func _prepareStats", duration.formatted(.units(allowed: [.seconds, .milliseconds])))
var walkoutTeams: [TeamRegistration] {
tournament.walkoutTeams()
}
#endif
unsortedPlayers = tournament.unsortedPlayers()
walkoutTeams = tournament.walkoutTeams()
unsortedTeamsWithoutWO = tournament.unsortedTeamsWithoutWO()
teamPaste = tournament.pasteDataForImporting().createTxtFile(self.tournament.tournamentTitle(.short))
var unsortedTeamsWithoutWO: [TeamRegistration] {
tournament.unsortedTeamsWithoutWO()
}
private func _prepareTeams() {
#if DEBUG_TIME //DEBUGING TIME
let start = Date()
defer {
let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000)
print("func _prepareTeams", duration.formatted(.units(allowed: [.seconds, .milliseconds])))
var teamPaste: URL? {
tournament.pasteDataForImporting().createTxtFile(self.tournament.tournamentTitle(.short))
}
#endif
var unsortedPlayers: [PlayerRegistration] {
tournament.unsortedPlayers()
}
var sortedTeams: [TeamRegistration] {
if filterMode == .waiting {
sortedTeams = tournament.waitingListSortedTeams()
return tournament.waitingListSortedTeams()
} else {
sortedTeams = tournament.sortedTeams()
return tournament.sortedTeams()
}
}
var filteredTeams: [TeamRegistration] {
var teams = sortedTeams
if filterMode == .walkOut {
switch filterMode {
case .walkOut:
teams = teams.filter({ $0.walkOut })
case .bracket:
teams = teams.filter({ $0.inRound() })
case .groupStage:
teams = teams.filter({ $0.inGroupStage() })
default:
break
}
if sortingMode == .registrationDate {
@ -440,53 +436,14 @@ struct InscriptionManagerView: View {
}
}
private func _getIssues() async {
#if DEBUG_TIME //DEBUGING TIME
let start = Date()
defer {
let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000)
print("func _getIssues", duration.formatted(.units(allowed: [.seconds, .milliseconds])))
}
#endif
await registrationIssues = tournament.registrationIssues()
}
private func _getTeams() {
_prepareTeams()
Task {
await _prepareStats()
await _getIssues()
await _setHash()
}
}
private func _teamRegisteredView() -> some View {
List {
_informationView()
let selectedSortedTeams = tournament.selectedSortedTeams()
if let closedRegistrationDate = tournament.closedRegistrationDate {
Section {
CloseDatePicker(closedRegistrationDate: closedRegistrationDate)
} footer: {
Text("Toutes les équipes ayant été inscrites après la date de clôture seront en liste d'attente.")
}
if selectedSortedTeams.isEmpty {
Section {
ContentUnavailableView("Aucune équipe", systemImage: "person.2.slash", description: Text("Vous n'avez aucune équipe inscrite avant la date de clôture."))
}
}
}
if presentSearch == false {
_informationView()
_rankHandlerView()
_relatedTips()
Section {
RowButtonView("Compléter la liste") {
presentAddTeamView = true
}
}
}
let teams = searchField.isEmpty ? filteredTeams : filteredTeams.filter({ $0.contains(searchField.canonicalVersion) })
@ -503,13 +460,7 @@ struct InscriptionManagerView: View {
}
RowButtonView("Créer une équipe") {
// Task {
// await MainActor.run {
// fetchPlayers.nsPredicate = Self._pastePredicate(pasteField: searchField, mostRecentDate: SourceFileManager.shared.mostRecentDateAvailable, filterOption: _filterOption())
// fetchPlayers.nsSortDescriptors = [NSSortDescriptor(keyPath: \ImportedPlayer.rank, ascending: true)]
// pasteString = searchField
// }
// }
pasteString = searchField
}
RowButtonView("D'accord") {
@ -518,6 +469,29 @@ struct InscriptionManagerView: View {
}
}
}
if compactMode {
Section {
ForEach(teams) { team in
let teamIndex = team.index(in: sortedTeams)
NavigationLink {
_teamCompactTeamEditionView(team)
.environment(tournament)
} label: {
TeamRowView(team: team)
}
.swipeActions(edge: .trailing, allowsFullSwipe: true) {
_teamDeleteButtonView(team)
}
}
} header: {
LabeledContent {
Text(teams.count.formatted())
} label: {
Text("Équipe\(teams.count.pluralSuffix)")
}
}
.headerProminence(.increased)
} else {
ForEach(teams) { team in
let teamIndex = team.index(in: sortedTeams)
Section {
@ -530,11 +504,118 @@ struct InscriptionManagerView: View {
.headerProminence(.increased)
}
}
}
.searchable(text: $searchField, isPresented: $presentSearch, prompt: Text("Chercher parmi les équipes inscrites"))
.keyboardType(.alphabet)
.autocorrectionDisabled()
}
func _teamCompactTeamEditionView(_ team: TeamRegistration) -> some View {
List {
Section {
TeamDetailView(team: team)
} footer: {
FooterButtonView("Copier dans le presse-papier") {
let pasteboard = UIPasteboard.general
pasteboard.string = team.playersPasteData()
}
}
Section {
NavigationLink {
EditingTeamView(team: team)
.environment(tournament)
} label: {
Text("Éditer une donnée de l'équipe")
}
NavigationLink {
GroupStageTeamReplacementView(team: team)
.environment(tournament)
} label: {
Text("Chercher à remplacer")
}
RowButtonView("Modifier la composition de l'équipe") {
editedTeam = team
}
}
Section {
Toggle(isOn: .init(get: {
return team.wildCardBracket
}, set: { value in
team.resetPositions()
team.wildCardGroupStage = false
team.walkOut = false
team.wildCardBracket = value
do {
try tournamentStore.teamRegistrations.addOrUpdate(instance: team)
} catch {
Logger.error(error)
}
_setHash()
})) {
Text("Wildcard Tableau")
}
Toggle(isOn: .init(get: {
return team.wildCardGroupStage
}, set: { value in
team.resetPositions()
team.wildCardBracket = false
team.walkOut = false
team.wildCardGroupStage = value
do {
try tournamentStore.teamRegistrations.addOrUpdate(instance: team)
} catch {
Logger.error(error)
}
_setHash()
})) {
Text("Wildcard Poule")
}
}
Section {
Toggle(isOn: .init(get: {
return team.walkOut
}, set: { value in
team.resetPositions()
team.wildCardBracket = false
team.wildCardGroupStage = false
team.walkOut = value
do {
try tournamentStore.teamRegistrations.addOrUpdate(instance: team)
} catch {
Logger.error(error)
}
_setHash()
})) {
Text("Forfait")
}
RowButtonView("Effacer l'équipe", role: .destructive, systemImage: "trash") {
team.deleteTeamScores()
do {
try tournamentStore.teamRegistrations.delete(instance: team)
} catch {
Logger.error(error)
}
_setHash()
}
}
}
.toolbar {
ToolbarItem(placement: .topBarTrailing) {
MenuWarningView(tournament: tournament, teams: [team], contactType: $contactType)
}
}
.toolbarBackground(.visible, for: .navigationBar)
.navigationTitle("Édition de l'équipe")
.navigationBarTitleDisplayMode(.inline)
}
@ViewBuilder
func rankingDateSourcePickerView(showDateInLabel: Bool) -> some View {
Section {
@ -597,6 +678,10 @@ struct InscriptionManagerView: View {
switch filterMode {
case .all:
return unsortedTeamsWithoutWO.count.formatted() + " / " + tournament.teamCount.formatted()
case .bracket:
return tournament.selectedSortedTeams().filter({ $0.inRound() }).count.formatted()
case .groupStage:
return tournament.selectedSortedTeams().filter({ $0.inGroupStage() }).count.formatted()
case .walkOut:
let wo = walkoutTeams.count.formatted()
return wo
@ -609,30 +694,104 @@ struct InscriptionManagerView: View {
@ViewBuilder
private func _informationView() -> some View {
Section {
ForEach(FilterMode.allCases) { filterMode in
LabeledContent {
Text(_teamCountForFilterMode(filterMode: filterMode))
HStack {
VStack(alignment: .leading, spacing: 0) {
Text("Inscriptions").font(.caption)
Text(unsortedTeamsWithoutWO.count.formatted()).font(.largeTitle)
}
.frame(maxWidth: .infinity)
.contentShape(Rectangle())
.onTapGesture {
self.filterMode = .all
}
VStack(alignment: .leading, spacing: 0) {
Text("Paires souhaitées").font(.caption)
Text(tournament.teamCount.formatted()).font(.largeTitle)
}
.frame(maxWidth: .infinity)
Button {
presentAddTeamView = true
} label: {
Text(filterMode.localizedLabel())
Label {
Text("Ajouter une équipe")
} icon: {
Image(systemName: "person.2.fill")
.resizable()
.scaledToFit()
.frame(height:44)
}
.labelStyle(.iconOnly)
.overlay(alignment: .bottomTrailing) {
Image(systemName: "plus.circle.fill")
.foregroundColor(.master)
.background (
Color(.systemBackground)
.clipShape(.circle)
)
}
}
.buttonBorderShape(.roundedRectangle)
.buttonStyle(.borderedProminent)
.frame(maxWidth: .infinity)
}
.fixedSize(horizontal: false, vertical: false)
.padding(.horizontal, -8)
HStack {
ForEach([FilterMode.waiting, FilterMode.walkOut, FilterMode.groupStage, FilterMode.bracket]) { filterMode in
Button {
if self.filterMode == filterMode {
self.filterMode = .all
} else {
self.filterMode = filterMode
}
} label: {
VStack(alignment: .leading, spacing: 0) {
Text(filterMode.localizedLabel(.short)).font(.caption)
Text(_teamCountForFilterMode(filterMode: filterMode)).font(.largeTitle)
}
.frame(maxWidth: .infinity)
.contentShape(Rectangle())
}
.buttonBorderShape(.roundedRectangle)
.buttonStyle(.borderedProminent)
.tint(self.filterMode == filterMode ? .master : .beige)
}
}
.foregroundStyle(.primary)
.fixedSize(horizontal: false, vertical: false)
NavigationLink {
InscriptionInfoView()
.environment(tournament)
} label: {
LabeledContent {
if let registrationIssues {
Text(registrationIssues.formatted())
} else {
ProgressView()
}
Text(tournament.registrationIssues().formatted())
} label: {
Text("Problèmes détéctés")
}
}
if let closedRegistrationDate = tournament.closedRegistrationDate {
CloseDatePicker(closedRegistrationDate: closedRegistrationDate)
}
} header: {
Text("Statut des inscriptions")
HStack {
Spacer()
FooterButtonView(compactMode ? "passer en affichage détaillée" : "passer en affichage compact") {
compactMode.toggle()
}
Spacer()
}
.textCase(nil)
} footer: {
if tournament.closedRegistrationDate != nil {
Text("Toutes les équipes ayant été inscrites après la date de clôture seront en liste d'attente.")
}
}
}
//
@ -821,11 +980,16 @@ struct InscriptionManagerView: View {
Text(formattedRegistrationDate)
}
Spacer()
Menu {
_teamMenuOptionView(team)
} label: {
LabelOptions().labelStyle(.titleOnly)
}
}
}
@ViewBuilder
private func _teamMenuOptionView(_ team: TeamRegistration) -> some View {
Menu {
Section {
NavigationLink {
GroupStageTeamReplacementView(team: team)
@ -842,7 +1006,6 @@ struct InscriptionManagerView: View {
//Divider()
Button("Changer les joueurs") {
editedTeam = team
presentAddTeamView = true
}
Divider()
NavigationLink {
@ -855,9 +1018,6 @@ struct InscriptionManagerView: View {
Toggle(isOn: .init(get: {
return team.wildCardBracket
}, set: { value in
_clearScreen()
Task {
team.resetPositions()
team.wildCardGroupStage = false
team.walkOut = false
@ -867,8 +1027,7 @@ struct InscriptionManagerView: View {
} catch {
Logger.error(error)
}
_getTeams()
}
_setHash()
})) {
Label("Wildcard Tableau", systemImage: team.wildCardBracket ? "circle.inset.filled" : "circle")
}
@ -876,9 +1035,6 @@ struct InscriptionManagerView: View {
Toggle(isOn: .init(get: {
return team.wildCardGroupStage
}, set: { value in
_clearScreen()
Task {
team.resetPositions()
team.wildCardBracket = false
team.walkOut = false
@ -888,8 +1044,7 @@ struct InscriptionManagerView: View {
} catch {
Logger.error(error)
}
_getTeams()
}
_setHash()
})) {
Label("Wildcard Poule", systemImage: team.wildCardGroupStage ? "circle.inset.filled" : "circle")
}
@ -898,8 +1053,6 @@ struct InscriptionManagerView: View {
Toggle(isOn: .init(get: {
return team.walkOut
}, set: { value in
_clearScreen()
Task {
team.resetPositions()
team.wildCardBracket = false
team.wildCardGroupStage = false
@ -909,32 +1062,29 @@ struct InscriptionManagerView: View {
} catch {
Logger.error(error)
}
_getTeams()
}
_setHash()
})) {
Label("WO", systemImage: team.walkOut ? "circle.inset.filled" : "circle")
}
Divider()
_teamDeleteButtonView(team)
// } header: {
// Text(team.teamLabel(.short))
}
}
private func _teamDeleteButtonView(_ team: TeamRegistration) -> some View {
Button(role: .destructive) {
_clearScreen()
Task {
team.deleteTeamScores()
do {
try tournamentStore.teamRegistrations.delete(instance: team)
} catch {
Logger.error(error)
}
_getTeams()
}
_setHash()
} label: {
LabelDelete()
}
// } header: {
// Text(team.teamLabel(.short))
}
} label: {
LabelOptions().labelStyle(.titleOnly)
}
}
private func _getErrorMessage() -> String {

@ -99,6 +99,7 @@ struct TournamentCellView: View {
.resizable()
.scaledToFit()
.frame(height: 28)
.accessibilityLabel("importer ou ouvrir")
.tint(existingTournament != nil ? Color.green : nil)
}
}

Loading…
Cancel
Save