|
|
|
@ -49,7 +49,7 @@ struct InscriptionManagerView: View { |
|
|
|
@State private var showSubscriptionView: Bool = false |
|
|
|
@State private var showSubscriptionView: Bool = false |
|
|
|
@State private var confirmDuplicate: Bool = false |
|
|
|
@State private var confirmDuplicate: Bool = false |
|
|
|
@State private var presentAddTeamView: Bool = false |
|
|
|
@State private var presentAddTeamView: Bool = false |
|
|
|
@State private var compactMode: Bool = false |
|
|
|
@State private var compactMode: Bool = true |
|
|
|
@State private var pasteString: String? |
|
|
|
@State private var pasteString: String? |
|
|
|
|
|
|
|
|
|
|
|
var tournamentStore: TournamentStore { |
|
|
|
var tournamentStore: TournamentStore { |
|
|
|
@ -89,19 +89,63 @@ struct InscriptionManagerView: View { |
|
|
|
case waiting |
|
|
|
case waiting |
|
|
|
case bracket |
|
|
|
case bracket |
|
|
|
case groupStage |
|
|
|
case groupStage |
|
|
|
|
|
|
|
case wildcardGroupStage |
|
|
|
|
|
|
|
case wildcardBracket |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func emptyLocalizedLabelDescription() -> String { |
|
|
|
|
|
|
|
switch self { |
|
|
|
|
|
|
|
case .wildcardBracket: |
|
|
|
|
|
|
|
return "Vous n'avez aucune wildcard en tableau." |
|
|
|
|
|
|
|
case .wildcardGroupStage: |
|
|
|
|
|
|
|
return "Vous n'avez aucune wildcard en poule." |
|
|
|
|
|
|
|
case .all: |
|
|
|
|
|
|
|
return "Vous n'avez encore aucune équipe inscrite." |
|
|
|
|
|
|
|
case .walkOut: |
|
|
|
|
|
|
|
return "Vous n'avez aucune équipe forfait." |
|
|
|
|
|
|
|
case .waiting: |
|
|
|
|
|
|
|
return "Vous n'avez aucune équipe en liste d'attente." |
|
|
|
|
|
|
|
case .bracket: |
|
|
|
|
|
|
|
return "Vous n'avez placé aucune équipe dans le tableau." |
|
|
|
|
|
|
|
case .groupStage: |
|
|
|
|
|
|
|
return "Vous n'avez placé aucune équipe en poule." |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func emptyLocalizedLabelTitle() -> String { |
|
|
|
|
|
|
|
switch self { |
|
|
|
|
|
|
|
case .wildcardBracket: |
|
|
|
|
|
|
|
return "Aucune wildcard en tableau" |
|
|
|
|
|
|
|
case .wildcardGroupStage: |
|
|
|
|
|
|
|
return "Aucune wildcard en poule" |
|
|
|
|
|
|
|
case .all: |
|
|
|
|
|
|
|
return "Aucune équipe inscrite" |
|
|
|
|
|
|
|
case .walkOut: |
|
|
|
|
|
|
|
return "Aucune équipe forfait" |
|
|
|
|
|
|
|
case .waiting: |
|
|
|
|
|
|
|
return "Aucune équipe en attente" |
|
|
|
|
|
|
|
case .bracket: |
|
|
|
|
|
|
|
return "Aucune équipe dans le tableau" |
|
|
|
|
|
|
|
case .groupStage: |
|
|
|
|
|
|
|
return "Aucune équipe en poule" |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String { |
|
|
|
func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String { |
|
|
|
switch self { |
|
|
|
switch self { |
|
|
|
|
|
|
|
case .wildcardBracket: |
|
|
|
|
|
|
|
return displayStyle == .wide ? "Wildcard Tableau" : "wc tableau" |
|
|
|
|
|
|
|
case .wildcardGroupStage: |
|
|
|
|
|
|
|
return displayStyle == .wide ? "Wildcard Poule" : "wc poule" |
|
|
|
case .all: |
|
|
|
case .all: |
|
|
|
return displayStyle == .wide ? "Équipes inscrites / souhaitées" : "Paires inscrites" |
|
|
|
return displayStyle == .wide ? "Équipes inscrites" : "inscris" |
|
|
|
case .bracket: |
|
|
|
case .bracket: |
|
|
|
return displayStyle == .wide ? "En Tableau" : "Tableau" |
|
|
|
return displayStyle == .wide ? "En Tableau" : "tableau" |
|
|
|
case .groupStage: |
|
|
|
case .groupStage: |
|
|
|
return displayStyle == .wide ? "En Poule" : "Poule" |
|
|
|
return displayStyle == .wide ? "En Poule" : "poule" |
|
|
|
case .walkOut: |
|
|
|
case .walkOut: |
|
|
|
return displayStyle == .wide ? "Forfaits" : "Forfait" |
|
|
|
return displayStyle == .wide ? "Forfaits" : "forfait" |
|
|
|
case .waiting: |
|
|
|
case .waiting: |
|
|
|
return displayStyle == .wide ? "Liste d'attente" : "Attente" |
|
|
|
return displayStyle == .wide ? "Liste d'attente" : "attente" |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
@ -313,7 +357,7 @@ struct InscriptionManagerView: View { |
|
|
|
Divider() |
|
|
|
Divider() |
|
|
|
Picker(selection: $filterMode) { |
|
|
|
Picker(selection: $filterMode) { |
|
|
|
ForEach(FilterMode.allCases) { |
|
|
|
ForEach(FilterMode.allCases) { |
|
|
|
Text($0.localizedLabel(.short)).tag($0) |
|
|
|
Text($0.localizedLabel()).tag($0) |
|
|
|
} |
|
|
|
} |
|
|
|
} label: { |
|
|
|
} label: { |
|
|
|
} |
|
|
|
} |
|
|
|
@ -415,10 +459,14 @@ struct InscriptionManagerView: View { |
|
|
|
|
|
|
|
|
|
|
|
var teams = sortedTeams |
|
|
|
var teams = sortedTeams |
|
|
|
switch filterMode { |
|
|
|
switch filterMode { |
|
|
|
|
|
|
|
case .wildcardBracket: |
|
|
|
|
|
|
|
teams = teams.filter({ $0.wildCardBracket }) |
|
|
|
|
|
|
|
case .wildcardGroupStage: |
|
|
|
|
|
|
|
teams = teams.filter({ $0.wildCardGroupStage }) |
|
|
|
case .walkOut: |
|
|
|
case .walkOut: |
|
|
|
teams = teams.filter({ $0.walkOut }) |
|
|
|
teams = teams.filter({ $0.walkOut }) |
|
|
|
case .bracket: |
|
|
|
case .bracket: |
|
|
|
teams = teams.filter({ $0.inRound() }) |
|
|
|
teams = teams.filter({ $0.inRound() && $0.inGroupStage() == false }) |
|
|
|
case .groupStage: |
|
|
|
case .groupStage: |
|
|
|
teams = teams.filter({ $0.inGroupStage() }) |
|
|
|
teams = teams.filter({ $0.inGroupStage() }) |
|
|
|
default: |
|
|
|
default: |
|
|
|
@ -469,6 +517,8 @@ struct InscriptionManagerView: View { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if teams.isEmpty == false { |
|
|
|
if compactMode { |
|
|
|
if compactMode { |
|
|
|
Section { |
|
|
|
Section { |
|
|
|
ForEach(teams) { team in |
|
|
|
ForEach(teams) { team in |
|
|
|
@ -482,12 +532,13 @@ struct InscriptionManagerView: View { |
|
|
|
.swipeActions(edge: .trailing, allowsFullSwipe: true) { |
|
|
|
.swipeActions(edge: .trailing, allowsFullSwipe: true) { |
|
|
|
_teamDeleteButtonView(team) |
|
|
|
_teamDeleteButtonView(team) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
.listRowView(isActive: true, color: team.initialRoundColor() ?? tournament.cutLabelColor(index: teamIndex, teamCount: selectedSortedTeams.count), hideColorVariation: true) |
|
|
|
} |
|
|
|
} |
|
|
|
} header: { |
|
|
|
} header: { |
|
|
|
LabeledContent { |
|
|
|
if filterMode == .all { |
|
|
|
Text(teams.count.formatted()) |
|
|
|
Text("\(teams.count.formatted()) équipe\(teams.count.pluralSuffix) dont \(walkoutTeams.count.formatted()) forfait\(walkoutTeams.count.pluralSuffix)") |
|
|
|
} label: { |
|
|
|
} else { |
|
|
|
Text("Équipe\(teams.count.pluralSuffix)") |
|
|
|
Text("\(teams.count.formatted()) équipe\(teams.count.pluralSuffix)") |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
.headerProminence(.increased) |
|
|
|
.headerProminence(.increased) |
|
|
|
@ -504,6 +555,17 @@ struct InscriptionManagerView: View { |
|
|
|
.headerProminence(.increased) |
|
|
|
.headerProminence(.increased) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
} else if filterMode != .all { |
|
|
|
|
|
|
|
ContentUnavailableView { |
|
|
|
|
|
|
|
Label(filterMode.emptyLocalizedLabelTitle(), systemImage: "person.2.slash") |
|
|
|
|
|
|
|
} description: { |
|
|
|
|
|
|
|
Text(filterMode.emptyLocalizedLabelDescription()) |
|
|
|
|
|
|
|
} actions: { |
|
|
|
|
|
|
|
RowButtonView("Supprimer le filtre") { |
|
|
|
|
|
|
|
filterMode = .all |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
.searchable(text: $searchField, isPresented: $presentSearch, prompt: Text("Chercher parmi les équipes inscrites")) |
|
|
|
.searchable(text: $searchField, isPresented: $presentSearch, prompt: Text("Chercher parmi les équipes inscrites")) |
|
|
|
.keyboardType(.alphabet) |
|
|
|
.keyboardType(.alphabet) |
|
|
|
@ -526,7 +588,8 @@ struct InscriptionManagerView: View { |
|
|
|
EditingTeamView(team: team) |
|
|
|
EditingTeamView(team: team) |
|
|
|
.environment(tournament) |
|
|
|
.environment(tournament) |
|
|
|
} label: { |
|
|
|
} label: { |
|
|
|
Text("Éditer une donnée de l'équipe") |
|
|
|
Text("Modifier le statut de l'équipe") |
|
|
|
|
|
|
|
Text("Nom de l'équipe, date d'inscription, présence, position") |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
NavigationLink { |
|
|
|
NavigationLink { |
|
|
|
@ -676,10 +739,14 @@ struct InscriptionManagerView: View { |
|
|
|
|
|
|
|
|
|
|
|
private func _teamCountForFilterMode(filterMode: FilterMode) -> String { |
|
|
|
private func _teamCountForFilterMode(filterMode: FilterMode) -> String { |
|
|
|
switch filterMode { |
|
|
|
switch filterMode { |
|
|
|
|
|
|
|
case .wildcardBracket: |
|
|
|
|
|
|
|
return tournament.selectedSortedTeams().filter({ $0.wildCardBracket }).count.formatted() |
|
|
|
|
|
|
|
case .wildcardGroupStage: |
|
|
|
|
|
|
|
return tournament.selectedSortedTeams().filter({ $0.wildCardGroupStage }).count.formatted() |
|
|
|
case .all: |
|
|
|
case .all: |
|
|
|
return unsortedTeamsWithoutWO.count.formatted() + " / " + tournament.teamCount.formatted() |
|
|
|
return unsortedTeamsWithoutWO.count.formatted() |
|
|
|
case .bracket: |
|
|
|
case .bracket: |
|
|
|
return tournament.selectedSortedTeams().filter({ $0.inRound() }).count.formatted() |
|
|
|
return tournament.selectedSortedTeams().filter({ $0.inRound() && $0.inGroupStage() == false }).count.formatted() |
|
|
|
case .groupStage: |
|
|
|
case .groupStage: |
|
|
|
return tournament.selectedSortedTeams().filter({ $0.inGroupStage() }).count.formatted() |
|
|
|
return tournament.selectedSortedTeams().filter({ $0.inGroupStage() }).count.formatted() |
|
|
|
case .walkOut: |
|
|
|
case .walkOut: |
|
|
|
@ -695,85 +762,59 @@ struct InscriptionManagerView: View { |
|
|
|
private func _informationView() -> some View { |
|
|
|
private func _informationView() -> some View { |
|
|
|
Section { |
|
|
|
Section { |
|
|
|
HStack { |
|
|
|
HStack { |
|
|
|
VStack(alignment: .leading, spacing: 0) { |
|
|
|
// VStack(alignment: .leading, spacing: 0) { |
|
|
|
Text("Inscriptions").font(.caption) |
|
|
|
// Text("Inscriptions").font(.caption) |
|
|
|
Text(unsortedTeamsWithoutWO.count.formatted()).font(.largeTitle) |
|
|
|
// Text(unsortedTeamsWithoutWO.count.formatted()).font(.largeTitle) |
|
|
|
} |
|
|
|
// } |
|
|
|
.frame(maxWidth: .infinity) |
|
|
|
// .frame(maxWidth: .infinity) |
|
|
|
.contentShape(Rectangle()) |
|
|
|
// .contentShape(Rectangle()) |
|
|
|
.onTapGesture { |
|
|
|
// .onTapGesture { |
|
|
|
self.filterMode = .all |
|
|
|
// self.filterMode = .all |
|
|
|
} |
|
|
|
// } |
|
|
|
|
|
|
|
// |
|
|
|
VStack(alignment: .leading, spacing: 0) { |
|
|
|
ForEach([FilterMode.all, FilterMode.waiting, FilterMode.walkOut]) { filterMode in |
|
|
|
Text("Paires souhaitées").font(.caption) |
|
|
|
_filterModeView(filterMode: filterMode) |
|
|
|
Text(tournament.teamCount.formatted()).font(.largeTitle) |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
.frame(maxWidth: .infinity) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Button { |
|
|
|
Button { |
|
|
|
presentAddTeamView = true |
|
|
|
presentAddTeamView = true |
|
|
|
} label: { |
|
|
|
} label: { |
|
|
|
Label { |
|
|
|
Image(systemName: "plus.circle.fill") |
|
|
|
Text("Ajouter une équipe") |
|
|
|
|
|
|
|
} icon: { |
|
|
|
|
|
|
|
Image(systemName: "person.2.fill") |
|
|
|
|
|
|
|
.resizable() |
|
|
|
.resizable() |
|
|
|
.scaledToFit() |
|
|
|
.scaledToFit() |
|
|
|
.frame(height:44) |
|
|
|
.frame(maxWidth: .infinity) |
|
|
|
} |
|
|
|
} |
|
|
|
.labelStyle(.iconOnly) |
|
|
|
.labelStyle(.iconOnly) |
|
|
|
.overlay(alignment: .bottomTrailing) { |
|
|
|
.buttonStyle(.borderless) |
|
|
|
Image(systemName: "plus.circle.fill") |
|
|
|
.padding(10) |
|
|
|
.foregroundColor(.master) |
|
|
|
|
|
|
|
.background ( |
|
|
|
|
|
|
|
Color(.systemBackground) |
|
|
|
|
|
|
|
.clipShape(.circle) |
|
|
|
|
|
|
|
) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
.buttonBorderShape(.roundedRectangle) |
|
|
|
|
|
|
|
.buttonStyle(.borderedProminent) |
|
|
|
|
|
|
|
.frame(maxWidth: .infinity) |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
.padding(.bottom, -4) |
|
|
|
.fixedSize(horizontal: false, vertical: false) |
|
|
|
.fixedSize(horizontal: false, vertical: false) |
|
|
|
.padding(.horizontal, -8) |
|
|
|
.listRowSeparator(.hidden) |
|
|
|
|
|
|
|
|
|
|
|
HStack { |
|
|
|
HStack { |
|
|
|
ForEach([FilterMode.waiting, FilterMode.walkOut, FilterMode.groupStage, FilterMode.bracket]) { filterMode in |
|
|
|
ForEach([FilterMode.groupStage, FilterMode.bracket, FilterMode.wildcardGroupStage, FilterMode.wildcardBracket]) { filterMode in |
|
|
|
|
|
|
|
_filterModeView(filterMode: filterMode) |
|
|
|
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) |
|
|
|
.padding(.bottom, -4) |
|
|
|
.buttonStyle(.borderedProminent) |
|
|
|
|
|
|
|
.tint(self.filterMode == filterMode ? .master : .beige) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
.foregroundStyle(.primary) |
|
|
|
|
|
|
|
.fixedSize(horizontal: false, vertical: false) |
|
|
|
.fixedSize(horizontal: false, vertical: false) |
|
|
|
|
|
|
|
.listRowSeparator(.hidden) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let registrationIssues = tournament.registrationIssues() |
|
|
|
|
|
|
|
if registrationIssues > 0 { |
|
|
|
NavigationLink { |
|
|
|
NavigationLink { |
|
|
|
InscriptionInfoView() |
|
|
|
InscriptionInfoView() |
|
|
|
.environment(tournament) |
|
|
|
.environment(tournament) |
|
|
|
} label: { |
|
|
|
} label: { |
|
|
|
LabeledContent { |
|
|
|
LabeledContent { |
|
|
|
Text(tournament.registrationIssues().formatted()) |
|
|
|
Text(tournament.registrationIssues().formatted()) |
|
|
|
|
|
|
|
.foregroundStyle(.logoRed) |
|
|
|
|
|
|
|
.fontWeight(.bold) |
|
|
|
} label: { |
|
|
|
} label: { |
|
|
|
Text("Problèmes détéctés") |
|
|
|
Text("Problèmes détéctés") |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if let closedRegistrationDate = tournament.closedRegistrationDate { |
|
|
|
if let closedRegistrationDate = tournament.closedRegistrationDate { |
|
|
|
CloseDatePicker(closedRegistrationDate: closedRegistrationDate) |
|
|
|
CloseDatePicker(closedRegistrationDate: closedRegistrationDate) |
|
|
|
@ -794,6 +835,29 @@ struct InscriptionManagerView: View { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private func _filterModeView(filterMode: FilterMode) -> some View { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Button { |
|
|
|
|
|
|
|
if self.filterMode == filterMode { |
|
|
|
|
|
|
|
self.filterMode = .all |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
self.filterMode = filterMode |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} label: { |
|
|
|
|
|
|
|
VStack(alignment: .center, spacing: -2) { |
|
|
|
|
|
|
|
Text(filterMode.localizedLabel(.short)).font(.caption).padding(.horizontal, -8) |
|
|
|
|
|
|
|
Text(_teamCountForFilterMode(filterMode: filterMode)).font(.largeTitle) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
.frame(maxWidth: .infinity) |
|
|
|
|
|
|
|
.contentShape(Rectangle()) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
.buttonBorderShape(.roundedRectangle) |
|
|
|
|
|
|
|
.buttonStyle(.borderedProminent) |
|
|
|
|
|
|
|
.foregroundStyle(.primary) |
|
|
|
|
|
|
|
.tint(self.filterMode == filterMode ? .master : .beige) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
// |
|
|
|
// |
|
|
|
// @ViewBuilder |
|
|
|
// @ViewBuilder |
|
|
|
// private func _informationView() -> some View { |
|
|
|
// private func _informationView() -> some View { |
|
|
|
@ -1012,7 +1076,7 @@ struct InscriptionManagerView: View { |
|
|
|
EditingTeamView(team: team) |
|
|
|
EditingTeamView(team: team) |
|
|
|
.environment(tournament) |
|
|
|
.environment(tournament) |
|
|
|
} label: { |
|
|
|
} label: { |
|
|
|
Text("Éditer une donnée de l'équipe") |
|
|
|
Text("Modifier une donnée de l'équipe") |
|
|
|
} |
|
|
|
} |
|
|
|
Divider() |
|
|
|
Divider() |
|
|
|
Toggle(isOn: .init(get: { |
|
|
|
Toggle(isOn: .init(get: { |
|
|
|
|