From 6ff21edba811a09c2c867e32e000b047377c8cc9 Mon Sep 17 00:00:00 2001 From: Raz Date: Sun, 23 Mar 2025 07:03:36 +0100 Subject: [PATCH] fix pdf rules add descritpion sharing system fix slots setup --- PadelClub/Data/Tournament.swift | 49 ++++++++---- PadelClub/Utils/URLs.swift | 7 +- PadelClub/Views/Match/MatchSetupView.swift | 27 +++++++ .../Navigation/Agenda/EventListView.swift | 77 +++++++++++++++---- .../Navigation/Toolbox/ToolboxView.swift | 7 +- PadelClub/Views/Round/RoundView.swift | 21 +++-- 6 files changed, 146 insertions(+), 42 deletions(-) diff --git a/PadelClub/Data/Tournament.swift b/PadelClub/Data/Tournament.swift index a7769eb..ceaa9b3 100644 --- a/PadelClub/Data/Tournament.swift +++ b/PadelClub/Data/Tournament.swift @@ -702,16 +702,31 @@ defer { return getRound(atRoundIndex: roundIndex)?.playedMatches().filter { $0.hasSpaceLeft() } ?? [] } - func availableSeedGroups() -> [SeedInterval] { + func availableSeedGroups(includeAll: Bool = false) -> [SeedInterval] { let seeds = seeds() var availableSeedGroup = Set() for (index, seed) in seeds.enumerated() { if seed.isSeedable(), let seedGroup = seedGroup(for: index) { - availableSeedGroup.insert(seedGroup) + if includeAll { + if let chunks = seedGroup.chunks() { + chunksBy(in: chunks, availableSeedGroup: &availableSeedGroup) + } + } else { + availableSeedGroup.insert(seedGroup) + } } } return availableSeedGroup.sorted(by: <) } + + func chunksBy(in chunks: [SeedInterval], availableSeedGroup: inout Set) { + chunks.forEach { chunk in + availableSeedGroup.insert(chunk) + if let moreChunk = chunk.chunks() { + self.chunksBy(in: moreChunk, availableSeedGroup: &availableSeedGroup) + } + } + } func seedGroup(for alreadySetupSeeds: Int) -> SeedInterval? { switch alreadySetupSeeds { @@ -771,20 +786,24 @@ defer { let maxSpots = max(availableSeedSpot.count, availableSeedOpponentSpot.count) if availableSeedGroup == SeedInterval(first: 3, last: 4), availableSeedSpot.count == 6 { return availableSeedGroup - } else if availableSeedGroup == SeedInterval(first: 5, last: 8), maxSpots == 6, availableSeeds.count == 2 { - return SeedInterval(first: 7, last: 12) } if availableSeeds.count == availableSeedSpot.count && availableSeedGroup.count == availableSeeds.count { return availableSeedGroup } else if availableSeeds.count == availableSeedOpponentSpot.count && availableSeedGroup.count == availableSeedOpponentSpot.count { return availableSeedGroup } else if let chunks = availableSeedGroup.chunks() { + let seededTeamsCount = self.seededTeams().count if let chunk = chunks.first(where: { seedInterval in - seedInterval.first >= self.seededTeams().count + return seedInterval.first == seededTeamsCount }) { - return seedGroupAvailable(atRoundIndex: roundIndex, availableSeedGroup: chunk) + return seedGroupAvailable(atRoundIndex: roundIndex + 1, availableSeedGroup: chunk) + } else { + let seeds = seeds() + if let firstIndex = seeds.firstIndex(where: { $0.isSeedable() }) { + return SeedInterval(first: firstIndex + 1, last: firstIndex + maxSpots) + } } - } else if fullLeftSeeds.count % maxSpots == 0 { + } else if fullLeftSeeds.count % maxSpots == 0 || fullLeftSeeds.count >= maxSpots { let seeds = seeds() if let firstIndex = seeds.firstIndex(where: { $0.isSeedable() }) { return SeedInterval(first: firstIndex + 1, last: firstIndex + maxSpots) @@ -820,14 +839,14 @@ defer { for (index, seed) in availableSeeds.enumerated() { seed.setSeedPosition(inSpot: spots[index], slot: nil, opposingSeeding: false) } - } else if seedGroup == SeedInterval(first: 5, last: 6), availableSeedSpot.count == 4 { - var spots = [Match]() - spots.append(availableSeedSpot[1]) - spots.append(availableSeedSpot[2]) - spots = spots.shuffled() - for (index, seed) in availableSeeds.enumerated() { - seed.setSeedPosition(inSpot: spots[index], slot: nil, opposingSeeding: false) - } +// } else if seedGroup == SeedInterval(first: 5, last: 6), availableSeedSpot.count == 4 { +// var spots = [Match]() +// spots.append(availableSeedSpot[1]) +// spots.append(availableSeedSpot[2]) +// spots = spots.shuffled() +// for (index, seed) in availableSeeds.enumerated() { +// seed.setSeedPosition(inSpot: spots[index], slot: nil, opposingSeeding: false) +// } } else { if availableSeeds.count <= availableSeedSpot.count { let spots = availableSeedSpot.shuffled() diff --git a/PadelClub/Utils/URLs.swift b/PadelClub/Utils/URLs.swift index 2b9e746..452e35d 100644 --- a/PadelClub/Utils/URLs.swift +++ b/PadelClub/Utils/URLs.swift @@ -31,9 +31,10 @@ enum URLs: String, Identifiable { case beachPadel = "https://beach-padel.app.fft.fr/beachja/index/" //case padelClub = "https://padelclub.app" case tenup = "https://tenup.fft.fr" - case padelCompetitionGeneralGuide = "https://fft-site.cdn.prismic.io/fft-site/Z2mH0ZbqstJ98yso_CHAPITREIRèglesgénérales.pdf" - case padelCompetitionSpecificGuide = "https://fft-site.cdn.prismic.io/fft-site/Z2mHz5bqstJ98ysm_CHAPITREIIICahierdeschargesdestournois.pdf" - case padelRules = "https://xlr.alwaysdata.net/static/rules/padel-rules-2024.pdf" + case padelCompetitionGeneralGuide = "https://padelclub.app/static/rules/padel-guide-general.pdf" + case padelCompetitionSpecificGuide = "https://padelclub.app/static/rules/padel-guide-cdc.pdf" + case padelCompetitionRankingGuide = "https://padelclub.app/static/rules/padel-guide-rankings.pdf" + case padelRules = "https://padelclub.app/static/rules/padel-rules.pdf" case restingDischarge = "https://club.fft.fr/tennisfirmidecazeville/60120370_d/data_1/pdf/fo/formlairededechargederesponsabilitetournoidepadel.pdf" case appReview = "https://apps.apple.com/app/padel-club/id6484163558?mt=8&action=write-review" case appDescription = "https://padelclub.app/download/" diff --git a/PadelClub/Views/Match/MatchSetupView.swift b/PadelClub/Views/Match/MatchSetupView.swift index dc932bd..72b5b19 100644 --- a/PadelClub/Views/Match/MatchSetupView.swift +++ b/PadelClub/Views/Match/MatchSetupView.swift @@ -146,6 +146,33 @@ struct MatchSetupView: View { Label(seedGroup.localizedInterval(), systemImage: "dice") } } + + Divider() + + Menu { + ForEach(tournament.availableSeedGroups(includeAll: true), id: \.self) { seedGroup in + ConfirmButtonView(shouldConfirm: shouldConfirm, message: MatchSetupView.confirmationMessage) { + if let randomTeam = tournament.randomSeed(fromSeedGroup: seedGroup) { + randomTeam.setSeedPosition(inSpot: match, slot: teamPosition, opposingSeeding: false) + do { + try tournamentStore.matches.addOrUpdate(instance: match) + } catch { + Logger.error(error) + } + do { + try tournamentStore.teamRegistrations.addOrUpdate(instance: randomTeam) + } catch { + Logger.error(error) + } + } + } label: { + Label(seedGroup.localizedInterval(), systemImage: "dice") + } + } + } label: { + Text("plus d'options") + } + } label: { Text("Tirer au sort").tag(nil as SeedInterval?) .underline() diff --git a/PadelClub/Views/Navigation/Agenda/EventListView.swift b/PadelClub/Views/Navigation/Agenda/EventListView.swift index f9c0ed5..dc869dc 100644 --- a/PadelClub/Views/Navigation/Agenda/EventListView.swift +++ b/PadelClub/Views/Navigation/Agenda/EventListView.swift @@ -107,7 +107,7 @@ struct EventListView: View { @ViewBuilder private func _options(_ pcTournaments: [Tournament]) -> some View { if let lastDataSource, pcTournaments.anySatisfy({ $0.rankSourceShouldBeRefreshed() != nil && $0.hasEnded() == false }) { - Section { + Menu { Button { Task { do { @@ -134,14 +134,14 @@ struct EventListView: View { } } } label: { - Text("Rafraîchir les classements") + Text("M-à-j des classements") } - } header: { - Text("Source disponible : \(lastDataSource.monthYearFormatted)") + } label: { + Text("Classement \(lastDataSource.monthYearFormatted)") } Divider() } - Section { + Menu { if pcTournaments.anySatisfy({ $0.isPrivate == true }) { Button { pcTournaments.forEach { tournament in @@ -153,7 +153,7 @@ struct EventListView: View { Logger.error(error) } } label: { - Text("Afficher ce\(pcTournaments.count.pluralSuffix) tournoi\(pcTournaments.count.pluralSuffix) sur Padel Club") + Text("Afficher sur Padel Club") } } @@ -168,15 +168,15 @@ struct EventListView: View { Logger.error(error) } } label: { - Text("Masquer ce\(pcTournaments.count.pluralSuffix) tournoi\(pcTournaments.count.pluralSuffix) sur Padel Club") + Text("Masquer sur Padel Club") } } - } header: { + } label: { Text("Visibilité sur Padel Club") } Divider() if pcTournaments.anySatisfy({ $0.hasEnded() == false && $0.enableOnlineRegistration == false && $0.onlineRegistrationCanBeEnabled() }) || pcTournaments.anySatisfy({ $0.enableOnlineRegistration == true && $0.hasEnded() == false }) { - Section { + Menu { if pcTournaments.anySatisfy({ $0.hasEnded() == false && $0.enableOnlineRegistration == false && $0.onlineRegistrationCanBeEnabled() }) { Button { pcTournaments.forEach { tournament in @@ -188,7 +188,7 @@ struct EventListView: View { Logger.error(error) } } label: { - Text("Activer l'inscription en ligne") + Text("Activer") } } @@ -201,7 +201,7 @@ struct EventListView: View { } } } label: { - Text("Rafraîchir la liste des équipes inscrites en ligne") + Text("M-à-j des inscriptions") } @@ -215,13 +215,64 @@ struct EventListView: View { Logger.error(error) } } label: { - Text("Désactiver l'inscription en ligne") + Text("Désactiver") } } - } header: { + } label: { Text("Inscription en ligne") } } + Divider() + Menu { + Button { + pcTournaments.forEach { tournament in + tournament.information = nil + } + do { + try dataStore.tournaments.addOrUpdate(contentOfs: pcTournaments) + } catch { + Logger.error(error) + } + } label: { + Text("Effacer les descriptions") + } + + let info = Set(pcTournaments.compactMap { tournament in + tournament.information?.trimmedMultiline + }).joined(separator: "\n") + + if info.isEmpty == false { + Button { + pcTournaments.forEach { tournament in + tournament.information = info + } + do { + try dataStore.tournaments.addOrUpdate(contentOfs: pcTournaments) + } catch { + Logger.error(error) + } + + } label: { + Text("Mettre '\(info.trunc(length: 12))'") + } + } + + PasteButton(payloadType: String.self) { strings in + if let pasteboard = strings.first { + pcTournaments.forEach { tournament in + tournament.information = pasteboard + } + do { + try dataStore.tournaments.addOrUpdate(contentOfs: pcTournaments) + } catch { + Logger.error(error) + } + } + } + } label: { + Text("Description des tournois") + } + } private func _nextMonths() -> [Date] { diff --git a/PadelClub/Views/Navigation/Toolbox/ToolboxView.swift b/PadelClub/Views/Navigation/Toolbox/ToolboxView.swift index 487f80c..c6b2177 100644 --- a/PadelClub/Views/Navigation/Toolbox/ToolboxView.swift +++ b/PadelClub/Views/Navigation/Toolbox/ToolboxView.swift @@ -170,9 +170,10 @@ struct ToolboxView: View { } Section { - Link("Accéder au guide de la compétition", destination: URLs.padelCompetitionGeneralGuide.url) - Link("Accéder aux CDC des tournois", destination: URLs.padelCompetitionSpecificGuide.url) - Link("Accéder aux règles du jeu", destination: URLs.padelRules.url) + Link("Guide de la compétition", destination: URLs.padelCompetitionGeneralGuide.url) + Link("CDC des tournois", destination: URLs.padelCompetitionSpecificGuide.url) + Link("Barèmes et assimilations", destination: URLs.padelCompetitionRankingGuide.url) + Link("Règles du jeu", destination: URLs.padelRules.url) Link("Décharge des temps de repos", destination: URLs.restingDischarge.url) } header: { Text("Documents fédéraux") diff --git a/PadelClub/Views/Round/RoundView.swift b/PadelClub/Views/Round/RoundView.swift index 3ffe945..343c330 100644 --- a/PadelClub/Views/Round/RoundView.swift +++ b/PadelClub/Views/Round/RoundView.swift @@ -132,9 +132,6 @@ struct RoundView: View { if availableSeeds.isEmpty == false, let availableSeedGroup { _seedGroupSection(availableSeeds: availableSeeds, availableSeedGroup: availableSeedGroup) - if upperRound.round.index == 3, availableSeedGroup.first == 5, availableSeedGroup.last == 8, availableSeeds.count > 4, let half = availableSeedGroup.chunks()?.first { - _seedGroupSection(availableSeeds: Array(availableSeeds.prefix(2)), availableSeedGroup: half) - } } if availableQualifiedTeams.isEmpty == false { @@ -295,11 +292,6 @@ struct RoundView: View { array.append(spots[1]) array.append(spots[4]) return array - } else if availableSeedGroup == SeedInterval(first: 5, last: 6), spots.count == 4 { - var array = [Match]() - array.append(spots[1]) - array.append(spots[2]) - return array } else { return spots } @@ -394,6 +386,14 @@ struct RoundView: View { _save(seeds: availableSeeds) } } +// if upperRound.round.index == 3, availableSeedGroup.first == 5, availableSeedGroup.last == 8, availableSeeds.count > 1, let half = availableSeedGroup.chunks()?.first { +// FooterButtonView("ou placer \(half.localizedInterval())" + ((half.isFixed() == false) ? " au hasard" : "")) { +// Task { +// tournament.setSeeds(inRoundIndex: upperRound.round.index, inSeedGroup: half) +// _save(seeds: availableSeeds) +// } +// } +// } } footer: { if availableSeedGroup.isFixed() == false { Text("Le tirage au sort ne sera pas visuel. Toutes les équipes de ce chapeau seront tirées.") @@ -411,6 +411,11 @@ struct RoundView: View { RowButtonView("Tirage au sort \(availableSeedGroup.localizedInterval()) visuel") { self.selectedSeedGroup = availableSeedGroup } +// if upperRound.round.index == 3, availableSeedGroup.first == 5, availableSeedGroup.last == 8, availableSeeds.count > 1, let half = availableSeedGroup.chunks()?.first { +// FooterButtonView("ou tirage au sort \(half.localizedInterval()) visuel") { +// self.selectedSeedGroup = half +// } +// } } footer: { Text("Le tirage au sort sera visuel et automatique, n'hésitez pas à enregistrer une vidéo de votre écran. Toutes les équipes de ce chapeau seront tirées les unes après les autres.") }