club_update
Razmig Sarkissian 1 year ago
parent fd92b3950e
commit 03069d9505
  1. 4
      PadelClub/Data/Tournament.swift
  2. 2
      PadelClub/Utils/URLs.swift
  3. 9
      PadelClub/Views/GroupStage/GroupStageView.swift
  4. 26
      PadelClub/Views/GroupStage/GroupStagesSettingsView.swift
  5. 45
      PadelClub/Views/GroupStage/GroupStagesView.swift
  6. 69
      PadelClub/Views/Navigation/Toolbox/ToolboxView.swift
  7. 41
      PadelClub/Views/Navigation/Umpire/UmpireView.swift
  8. 2
      PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift
  9. 5
      PadelClub/Views/Tournament/TournamentBuildView.swift

@ -1065,7 +1065,7 @@ defer {
if state() == .build && groupStageCount > 0 && groupStageTeams().isEmpty { if state() == .build && groupStageCount > 0 && groupStageTeams().isEmpty {
setGroupStageTeams(randomize: groupStageSortMode == .random) setGroupStage(randomize: groupStageSortMode == .random)
} }
} }
@ -1417,7 +1417,7 @@ defer {
} }
func missingQualifiedFromGroupStages() -> [TeamRegistration] { func missingQualifiedFromGroupStages() -> [TeamRegistration] {
if groupStageAdditionalQualified > 0 { if groupStageAdditionalQualified > 0 && groupStagesAreOver() {
return groupStages().filter { $0.hasEnded() }.compactMap { groupStage in return groupStages().filter { $0.hasEnded() }.compactMap { groupStage in
groupStage.teams(true)[safe: qualifiedPerGroupStage] groupStage.teams(true)[safe: qualifiedPerGroupStage]
} }

@ -17,7 +17,7 @@ enum URLs: String, Identifiable {
//case padelClub = "https://padelclub.app" //case padelClub = "https://padelclub.app"
case tenup = "https://tenup.fft.fr" case tenup = "https://tenup.fft.fr"
case padelRules = "https://fft-site.cdn.prismic.io/fft-site/ZgLn3McYqOFdyF7n_LEGUIDEDELACOMPETITIONDEPADEL-MAJDECEMBRE2023.pdf" case padelRules = "https://fft-site.cdn.prismic.io/fft-site/ZgLn3McYqOFdyF7n_LEGUIDEDELACOMPETITIONDEPADEL-MAJDECEMBRE2023.pdf"
case restingDischarge = "https://club.fft.fr/tennisfirmidecazeville/60120370_d/data_1/pdf/fo/formlairededechargederesponsabilitetournoidepadel.pdf"
var id: String { return self.rawValue } var id: String { return self.rawValue }
var url: URL { var url: URL {

@ -16,11 +16,9 @@ struct GroupStageView: View {
@Bindable var groupStage: GroupStage @Bindable var groupStage: GroupStage
@State private var confirmGroupStageStart: Bool = false @State private var confirmGroupStageStart: Bool = false
@State private var sortingMode: GroupStageSortingMode @State private var sortingMode: GroupStageSortingMode
let playedMatches: [Match]
init(groupStage: GroupStage) { init(groupStage: GroupStage) {
self.groupStage = groupStage self.groupStage = groupStage
self.playedMatches = groupStage.playedMatches()
_sortingMode = .init(wrappedValue: groupStage.hasEnded() ? .score : .weight) _sortingMode = .init(wrappedValue: groupStage.hasEnded() ? .score : .weight)
} }
@ -52,6 +50,7 @@ struct GroupStageView: View {
} }
.headerProminence(.increased) .headerProminence(.increased)
let playedMatches = groupStage.playedMatches()
let runningMatches = groupStage.runningMatches(playedMatches: playedMatches) let runningMatches = groupStage.runningMatches(playedMatches: playedMatches)
MatchListView(section: "en cours", matches: groupStage.runningMatches(playedMatches: playedMatches), hideWhenEmpty: true) MatchListView(section: "en cours", matches: groupStage.runningMatches(playedMatches: playedMatches), hideWhenEmpty: true)
let availableToStart = groupStage.availableToStart(playedMatches: playedMatches, in: runningMatches) let availableToStart = groupStage.availableToStart(playedMatches: playedMatches, in: runningMatches)
@ -59,6 +58,12 @@ struct GroupStageView: View {
.listRowView(isActive: availableToStart.isEmpty == false, color: .green, hideColorVariation: true) .listRowView(isActive: availableToStart.isEmpty == false, color: .green, hideColorVariation: true)
MatchListView(section: "à lancer", matches: groupStage.readyMatches(playedMatches: playedMatches), hideWhenEmpty: true) MatchListView(section: "à lancer", matches: groupStage.readyMatches(playedMatches: playedMatches), hideWhenEmpty: true)
MatchListView(section: "terminés", matches: groupStage.finishedMatches(playedMatches: playedMatches), isExpanded: false) MatchListView(section: "terminés", matches: groupStage.finishedMatches(playedMatches: playedMatches), isExpanded: false)
if playedMatches.isEmpty {
RowButtonView("Créer les matchs de poules") {
groupStage.buildMatches()
}
}
} }
.toolbar { .toolbar {
ToolbarItem(placement: .topBarTrailing) { ToolbarItem(placement: .topBarTrailing) {

@ -20,32 +20,6 @@ struct GroupStagesSettingsView: View {
var body: some View { var body: some View {
List { List {
if tournament.groupStageAdditionalQualified > 0 {
let missingQualifiedFromGroupStages = tournament.missingQualifiedFromGroupStages()
Section {
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)
}
}
}
}
.disabled(tournament.moreQualifiedToDraw() == 0 || missingQualifiedFromGroupStages.isEmpty)
} footer: {
if tournament.moreQualifiedToDraw() == 0 {
Text("Aucune équipe supplémentaire à qualifier. Vous pouvez en rajouter en modifier le paramètre dans structure.")
} else if missingQualifiedFromGroupStages.isEmpty {
Text("Aucune équipe supplémentaire à tirer au sort. Attendez la fin des poules.")
}
}
}
if tournament.shouldVerifyGroupStage { if tournament.shouldVerifyGroupStage {
Section { Section {
let issues = tournament.groupStageTeamPlacementIssue() let issues = tournament.groupStageTeamPlacementIssue()

@ -6,6 +6,7 @@
// //
import SwiftUI import SwiftUI
import LeStorage
struct GroupStagesView: View { struct GroupStagesView: View {
var tournament: Tournament var tournament: Tournament
@ -16,7 +17,7 @@ struct GroupStagesView: View {
lhs.id == rhs.id lhs.id == rhs.id
} }
case all case all(Tournament)
case groupStage(GroupStage) case groupStage(GroupStage)
var id: String { var id: String {
@ -52,8 +53,12 @@ struct GroupStagesView: View {
func badgeImage() -> Badge? { func badgeImage() -> Badge? {
switch self { switch self {
case .all: case .all(let tournament):
if tournament.groupStageAdditionalQualified > 0 && tournament.moreQualifiedToDraw() > 0 && tournament.missingQualifiedFromGroupStages().isEmpty == false {
return .custom(systemName: "exclamationmark.circle.fill", color: .logoBackground)
} else {
return nil return nil
}
case .groupStage(let groupStage): case .groupStage(let groupStage):
return groupStage.badgeImage() return groupStage.badgeImage()
} }
@ -80,7 +85,7 @@ struct GroupStagesView: View {
} }
func allDestinations() -> [GroupStageDestination] { func allDestinations() -> [GroupStageDestination] {
var allDestinations : [GroupStageDestination] = [.all] var allDestinations : [GroupStageDestination] = [.all(tournament)]
let groupStageDestinations : [GroupStageDestination] = tournament.groupStages().map { GroupStageDestination.groupStage($0) } let groupStageDestinations : [GroupStageDestination] = tournament.groupStages().map { GroupStageDestination.groupStage($0) }
allDestinations.append(contentsOf: groupStageDestinations) allDestinations.append(contentsOf: groupStageDestinations)
return allDestinations return allDestinations
@ -94,6 +99,40 @@ struct GroupStagesView: View {
let finishedMatches = tournament.finishedMatches(allMatches) let finishedMatches = tournament.finishedMatches(allMatches)
List { List {
if tournament.groupStageAdditionalQualified > 0 {
let missingQualifiedFromGroupStages = tournament.missingQualifiedFromGroupStages()
Section {
let name = "\((tournament.qualifiedPerGroupStage + 1).ordinalFormatted())"
NavigationLink {
SpinDrawView(drawees: ["Qualification d'un \(name) de poule"], segments: missingQualifiedFromGroupStages) { results in
results.forEach { drawResult in
missingQualifiedFromGroupStages[drawResult.drawIndex].qualified = true
do {
try self.tournament.tournamentStore.teamRegistrations.addOrUpdate(instance: missingQualifiedFromGroupStages[drawResult.drawIndex])
} catch {
Logger.error(error)
}
}
}
} label: {
Label {
Text("Qualifier un \(name) de poule par tirage au sort")
} icon: {
Image(systemName: "exclamationmark.circle.fill")
.foregroundStyle(.logoBackground)
}
}
.disabled(tournament.moreQualifiedToDraw() == 0 || missingQualifiedFromGroupStages.isEmpty)
} footer: {
if tournament.moreQualifiedToDraw() == 0 {
Text("Aucune équipe supplémentaire à qualifier. Vous pouvez en rajouter en modifier le paramètre dans structure.")
} else if missingQualifiedFromGroupStages.isEmpty {
Text("Aucune équipe supplémentaire à tirer au sort. Attendez la fin des poules.")
}
}
}
let runningMatches = tournament.runningMatches(allMatches) let runningMatches = tournament.runningMatches(allMatches)
MatchListView(section: "en cours", matches: runningMatches, matchViewStyle: .standardStyle, isExpanded: false) MatchListView(section: "en cours", matches: runningMatches, matchViewStyle: .standardStyle, isExpanded: false)
MatchListView(section: "prêt à démarrer", matches: tournament.availableToStart(allMatches, in: runningMatches), matchViewStyle: .standardStyle, isExpanded: false) MatchListView(section: "prêt à démarrer", matches: tournament.availableToStart(allMatches, in: runningMatches), matchViewStyle: .standardStyle, isExpanded: false)

@ -14,6 +14,19 @@ struct ToolboxView: View {
@Environment(NavigationViewModel.self) private var navigation: NavigationViewModel @Environment(NavigationViewModel.self) private var navigation: NavigationViewModel
@State private var didResetApiCalls: Bool = false @State private var didResetApiCalls: Bool = false
var lastDataSource: String? {
dataStore.appSettings.lastDataSource
}
var _mostRecentDateAvailable: Date? {
SourceFileManager.shared.mostRecentDateAvailable
}
var _lastDataSourceDate: Date? {
guard let lastDataSource else { return nil }
return URL.importDateFormatter.date(from: lastDataSource)
}
var body: some View { var body: some View {
@Bindable var navigation = navigation @Bindable var navigation = navigation
NavigationStack(path: $navigation.toolboxPath) { NavigationStack(path: $navigation.toolboxPath) {
@ -27,9 +40,15 @@ struct ToolboxView: View {
} }
SupportButtonView(contentIsUnavailable: false) SupportButtonView(contentIsUnavailable: false)
Link(destination: URLs.main.url) {
Text("Accéder au site Padel Club")
}
.contextMenu {
ShareLink(item: URLs.main.url)
}
} }
#if DEBUG #if DEBUG
Section { Section {
@ -107,20 +126,37 @@ struct ToolboxView: View {
#endif #endif
Section { Section {
Link(destination: URLs.main.url) { NavigationLink {
Text("Accéder au site Padel Club") PadelClubView()
} label: {
if let _lastDataSourceDate {
LabeledContent {
Image(systemName: "checkmark.circle.fill")
.foregroundStyle(.green)
} label: {
Text(_lastDataSourceDate.monthYearFormatted)
Text("Classement mensuel utilisé")
}
} else {
LabeledContent {
Image(systemName: "xmark.circle.fill")
.tint(.logoRed)
} label: {
if let _mostRecentDateAvailable {
Text(_mostRecentDateAvailable.monthYearFormatted)
} else {
Text("Aucun")
}
Text("Classement mensuel disponible")
}
} }
} }
Section {
NavigationLink { NavigationLink {
SelectablePlayerListView() SelectablePlayerListView()
} label: { } label: {
Label("Rechercher un joueur", systemImage: "person.fill.viewfinder") Label("Rechercher un joueur", systemImage: "person.fill.viewfinder")
} }
}
Section {
NavigationLink { NavigationLink {
RankCalculatorView() RankCalculatorView()
} label: { } label: {
@ -129,23 +165,12 @@ struct ToolboxView: View {
} }
Section { Section {
NavigationLink { Link("Accéder au guide de la compétition", destination: URLs.padelRules.url)
GlobalSettingsView() Link("Formulaire de décharge des temps de repos", destination: URLs.restingDischarge.url)
} label: { } header: {
Label("Formats de jeu par défaut", systemImage: "megaphone") Text("Documents fédéraux")
}
NavigationLink {
DurationSettingsView()
} label: {
Label("Définir les durées moyennes", systemImage: "deskclock")
}
} footer: {
Text("Vous pouvez définir vos propres estimations de durées de match en fonction du format de jeu.")
} }
Section {
Link("Accéder au guide de la compétition de la FFT", destination: URLs.padelRules.url)
}
} }
.overlay(alignment: .bottom) { .overlay(alignment: .bottom) {
if didResetApiCalls { if didResetApiCalls {

@ -19,19 +19,6 @@ struct UmpireView: View {
// @State var isConnected: Bool = false // @State var isConnected: Bool = false
var lastDataSource: String? {
dataStore.appSettings.lastDataSource
}
var _mostRecentDateAvailable: Date? {
SourceFileManager.shared.mostRecentDateAvailable
}
var _lastDataSourceDate: Date? {
guard let lastDataSource else { return nil }
return URL.importDateFormatter.date(from: lastDataSource)
}
enum UmpireScreen { enum UmpireScreen {
case login case login
} }
@ -119,32 +106,20 @@ struct UmpireView: View {
Text("Il s'agit des clubs qui sont utilisés pour récupérer les tournois tenup.") Text("Il s'agit des clubs qui sont utilisés pour récupérer les tournois tenup.")
} }
Section { Section {
NavigationLink { NavigationLink {
PadelClubView() GlobalSettingsView()
} label: {
if let _lastDataSourceDate {
LabeledContent {
Image(systemName: "checkmark.circle.fill")
.foregroundStyle(.green)
} label: { } label: {
Text(_lastDataSourceDate.monthYearFormatted) Label("Formats de jeu par défaut", systemImage: "megaphone")
Text("Classement mensuel utilisé")
} }
} else { NavigationLink {
LabeledContent { DurationSettingsView()
Image(systemName: "xmark.circle.fill")
.tint(.logoRed)
} label: { } label: {
if let _mostRecentDateAvailable { Label("Définir les durées moyennes", systemImage: "deskclock")
Text(_mostRecentDateAvailable.monthYearFormatted)
} else {
Text("Aucun")
}
Text("Classement mensuel disponible")
}
}
} }
} footer: {
Text("Vous pouvez définir vos propres estimations de durées de match en fonction du format de jeu.")
} }
// Section { // Section {

@ -532,7 +532,7 @@ 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) .listRowView(isActive: true, color: team.initialRoundColor() ?? tournament.cutLabelColor(index: teamIndex, teamCount: filterMode == .waiting ? 0 : selectedSortedTeams.count), hideColorVariation: true)
} }
} header: { } header: {
if filterMode == .all { if filterMode == .all {

@ -31,7 +31,10 @@ struct TournamentBuildView: View {
} }
} label: { } label: {
Text("Poules") Text("Poules")
if tournament.shouldVerifyGroupStage { if tournament.groupStagesAreOver(), tournament.moreQualifiedToDraw() > 0 {
let moreQualifiedToDraw = tournament.moreQualifiedToDraw()
Text("Qualifié\(moreQualifiedToDraw.pluralSuffix) sortant\(moreQualifiedToDraw.pluralSuffix) manquant\(moreQualifiedToDraw.pluralSuffix)").foregroundStyle(.logoRed)
} else if tournament.shouldVerifyGroupStage {
Text("Vérifier les poules").foregroundStyle(.logoRed) Text("Vérifier les poules").foregroundStyle(.logoRed)
} else if let range = groupStageStatus?.1 { } else if let range = groupStageStatus?.1 {
HStack { HStack {

Loading…
Cancel
Save