club_update
Razmig Sarkissian 1 year ago
parent 19b58448b2
commit ccb47ff26c
  1. 4
      PadelClub.xcodeproj/project.pbxproj
  2. 9
      PadelClub/Data/PlayerRegistration.swift
  3. 55
      PadelClub/Data/TeamRegistration.swift
  4. 14
      PadelClub/Data/Tournament.swift
  5. 4
      PadelClub/Extensions/String+Extensions.swift
  6. 37
      PadelClub/Utils/ExportFormat.swift
  7. 9
      PadelClub/Utils/PadelRule.swift
  8. 15
      PadelClub/Utils/Tips.swift
  9. 29
      PadelClub/Views/Shared/LearnMoreSheetView.swift
  10. 49
      PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift

@ -257,6 +257,7 @@
FFF03C942BD91D0C00B516FC /* ButtonValidateView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF03C932BD91D0C00B516FC /* ButtonValidateView.swift */; }; FFF03C942BD91D0C00B516FC /* ButtonValidateView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF03C932BD91D0C00B516FC /* ButtonValidateView.swift */; };
FFF116E12BD2A9B600A33B06 /* DateInterval.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF116E02BD2A9B600A33B06 /* DateInterval.swift */; }; FFF116E12BD2A9B600A33B06 /* DateInterval.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF116E02BD2A9B600A33B06 /* DateInterval.swift */; };
FFF116E32BD2AF4800A33B06 /* CourtAvailabilitySettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF116E22BD2AF4800A33B06 /* CourtAvailabilitySettingsView.swift */; }; FFF116E32BD2AF4800A33B06 /* CourtAvailabilitySettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF116E22BD2AF4800A33B06 /* CourtAvailabilitySettingsView.swift */; };
FFF1D2CB2C4A22B200C8D33D /* ExportFormat.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF1D2CA2C4A22B200C8D33D /* ExportFormat.swift */; };
FFF527D62BC6DDD000FF4EF2 /* MatchScheduleEditorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF527D52BC6DDD000FF4EF2 /* MatchScheduleEditorView.swift */; }; FFF527D62BC6DDD000FF4EF2 /* MatchScheduleEditorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF527D52BC6DDD000FF4EF2 /* MatchScheduleEditorView.swift */; };
FFF8ACCD2B92367B008466FA /* FederalPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF8ACCC2B92367B008466FA /* FederalPlayer.swift */; }; FFF8ACCD2B92367B008466FA /* FederalPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF8ACCC2B92367B008466FA /* FederalPlayer.swift */; };
FFF8ACD42B92392C008466FA /* SourceFileManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF8ACD32B92392C008466FA /* SourceFileManager.swift */; }; FFF8ACD42B92392C008466FA /* SourceFileManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF8ACD32B92392C008466FA /* SourceFileManager.swift */; };
@ -599,6 +600,7 @@
FFF03C932BD91D0C00B516FC /* ButtonValidateView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonValidateView.swift; sourceTree = "<group>"; }; FFF03C932BD91D0C00B516FC /* ButtonValidateView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonValidateView.swift; sourceTree = "<group>"; };
FFF116E02BD2A9B600A33B06 /* DateInterval.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateInterval.swift; sourceTree = "<group>"; }; FFF116E02BD2A9B600A33B06 /* DateInterval.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateInterval.swift; sourceTree = "<group>"; };
FFF116E22BD2AF4800A33B06 /* CourtAvailabilitySettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CourtAvailabilitySettingsView.swift; sourceTree = "<group>"; }; FFF116E22BD2AF4800A33B06 /* CourtAvailabilitySettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CourtAvailabilitySettingsView.swift; sourceTree = "<group>"; };
FFF1D2CA2C4A22B200C8D33D /* ExportFormat.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExportFormat.swift; sourceTree = "<group>"; };
FFF527D52BC6DDD000FF4EF2 /* MatchScheduleEditorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MatchScheduleEditorView.swift; sourceTree = "<group>"; }; FFF527D52BC6DDD000FF4EF2 /* MatchScheduleEditorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MatchScheduleEditorView.swift; sourceTree = "<group>"; };
FFF8ACCC2B92367B008466FA /* FederalPlayer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FederalPlayer.swift; sourceTree = "<group>"; }; FFF8ACCC2B92367B008466FA /* FederalPlayer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FederalPlayer.swift; sourceTree = "<group>"; };
FFF8ACD32B92392C008466FA /* SourceFileManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SourceFileManager.swift; sourceTree = "<group>"; }; FFF8ACD32B92392C008466FA /* SourceFileManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SourceFileManager.swift; sourceTree = "<group>"; };
@ -1323,6 +1325,7 @@
FF0EC51D2BB16F680056B6D1 /* SwiftParser.swift */, FF0EC51D2BB16F680056B6D1 /* SwiftParser.swift */,
FF1DC5582BAB767000FD8220 /* Tips.swift */, FF1DC5582BAB767000FD8220 /* Tips.swift */,
C49EF01A2BD6A1E80077B5AA /* URLs.swift */, C49EF01A2BD6A1E80077B5AA /* URLs.swift */,
FFF1D2CA2C4A22B200C8D33D /* ExportFormat.swift */,
); );
path = Utils; path = Utils;
sourceTree = "<group>"; sourceTree = "<group>";
@ -1720,6 +1723,7 @@
C493B37E2C10AD3600862481 /* LoadingViewModifier.swift in Sources */, C493B37E2C10AD3600862481 /* LoadingViewModifier.swift in Sources */,
FF089EBD2BB0287D00F0AEC7 /* PlayerView.swift in Sources */, FF089EBD2BB0287D00F0AEC7 /* PlayerView.swift in Sources */,
FF967D032BAEF0C000A9A3BD /* MatchDetailView.swift in Sources */, FF967D032BAEF0C000A9A3BD /* MatchDetailView.swift in Sources */,
FFF1D2CB2C4A22B200C8D33D /* ExportFormat.swift in Sources */,
FF967D0F2BAF63B000A9A3BD /* PlayerBlockView.swift in Sources */, FF967D0F2BAF63B000A9A3BD /* PlayerBlockView.swift in Sources */,
C4A47D922B7BBBEC00ADC637 /* StoreItem.swift in Sources */, C4A47D922B7BBBEC00ADC637 /* StoreItem.swift in Sources */,
FFB9C8712BBADDE200A0EF4F /* Selectable.swift in Sources */, FFB9C8712BBADDE200A0EF4F /* Selectable.swift in Sources */,

@ -140,8 +140,13 @@ final class PlayerRegistration: ModelObject, Storable {
return nil return nil
} }
func pasteData() -> String { func pasteData(_ exportFormat: ExportFormat = .rawText) -> String {
[firstName.capitalized, lastName.capitalized, licenceId].compactMap({ $0 }).joined(separator: " ") switch exportFormat {
case .rawText:
return [firstName.capitalized, lastName.capitalized, licenceId].compactMap({ $0 }).joined(separator: exportFormat.separator())
case .csv:
return [lastName.uppercased() + " " + firstName.capitalized].joined(separator: exportFormat.separator())
}
} }
func isPlaying() -> Bool { func isPlaying() -> Bool {

@ -327,24 +327,61 @@ final class TeamRegistration: ModelObject, Storable {
resetBracketPosition() resetBracketPosition()
} }
func pasteData() -> String { func pasteData(_ exportFormat: ExportFormat = .rawText, _ index: Int = 0) -> String {
[playersPasteData(), formattedInscriptionDate(), name].compactMap({ $0 }).joined(separator: "\n") switch exportFormat {
case .rawText:
return [playersPasteData(exportFormat), formattedInscriptionDate(exportFormat), name].compactMap({ $0 }).joined(separator: exportFormat.newLineSeparator())
case .csv:
return [index.formatted(), playersPasteData(exportFormat), isWildCard() ? "WC" : weight.formatted()].joined(separator: exportFormat.separator())
}
} }
var computedRegistrationDate: Date { var computedRegistrationDate: Date {
return registrationDate ?? .distantFuture return registrationDate ?? .distantFuture
} }
func formattedInscriptionDate() -> String? { func formattedInscriptionDate(_ exportFormat: ExportFormat = .rawText) -> String? {
if let registrationDate { switch exportFormat {
return "Inscrit le " + registrationDate.formatted(.dateTime.weekday().day().month().hour().minute()) case .rawText:
} else { if let registrationDate {
return nil return "Inscrit le " + registrationDate.formatted(.dateTime.weekday().day().month().hour().minute())
} else {
return nil
}
case .csv:
if let registrationDate {
return registrationDate.formatted(.dateTime.weekday().day().month().hour().minute())
} else {
return nil
}
}
}
func formattedSummonDate(_ exportFormat: ExportFormat = .rawText) -> String? {
switch exportFormat {
case .rawText:
if let callDate {
return "Convoqué le " + callDate.formatted(.dateTime.weekday().day().month().hour().minute())
} else {
return nil
}
case .csv:
if let callDate {
return callDate.formatted(.dateTime.weekday().day().month().hour().minute())
} else {
return nil
}
} }
} }
func playersPasteData() -> String { func playersPasteData(_ exportFormat: ExportFormat = .rawText) -> String {
return players().map { $0.pasteData() }.joined(separator: "\n") switch exportFormat {
case .rawText:
return players().map { $0.pasteData(exportFormat) }.joined(separator: exportFormat.newLineSeparator())
case .csv:
return players().map { [$0.pasteData(exportFormat), isWildCard() ? "WC" : $0.computedRank.formatted() ].joined(separator: exportFormat.separator()) }.joined(separator: exportFormat.separator())
}
} }
func updatePlayers(_ players: Set<PlayerRegistration>, inTournamentCategory tournamentCategory: TournamentCategory) { func updatePlayers(_ players: Set<PlayerRegistration>, inTournamentCategory tournamentCategory: TournamentCategory) {

@ -529,9 +529,19 @@ defer {
return Store.main.findById(event) return Store.main.findById(event)
} }
func pasteDataForImporting() -> String { func pasteDataForImporting(_ exportFormat: ExportFormat = .rawText) -> String {
let selectedSortedTeams = selectedSortedTeams() let selectedSortedTeams = selectedSortedTeams()
return (selectedSortedTeams.compactMap { $0.pasteData() } + ["Liste d'attente"] + waitingListTeams(in: selectedSortedTeams, includingWalkOuts: true).compactMap { $0.pasteData() }).joined(separator: "\n\n") switch exportFormat {
case .rawText:
return (selectedSortedTeams.compactMap { $0.pasteData(exportFormat) } + ["Liste d'attente"] + waitingListTeams(in: selectedSortedTeams, includingWalkOuts: true).compactMap { $0.pasteData(exportFormat) }).joined(separator: exportFormat.newLineSeparator(2))
case .csv:
let headers = ["", "Nom Prénom", "rang", "Nom Prénom", "rang", "poids"].joined(separator: exportFormat.separator())
var teamPaste = [headers]
for (index, team) in selectedSortedTeams.enumerated() {
teamPaste.append(team.pasteData(exportFormat, index + 1))
}
return teamPaste.joined(separator: exportFormat.newLineSeparator())
}
} }
func club() -> Club? { func club() -> Club? {

@ -185,10 +185,10 @@ extension LosslessStringConvertible {
} }
extension String { extension String {
func createTxtFile(_ withName: String = "temp") -> URL { func createFile(_ withName: String = "temp", _ exportedFormat: ExportFormat = .rawText) -> URL {
let url = FileManager.default.temporaryDirectory let url = FileManager.default.temporaryDirectory
.appendingPathComponent(withName) .appendingPathComponent(withName)
.appendingPathExtension("txt") .appendingPathExtension(exportedFormat.suffix)
let string = self let string = self
try? FileManager.default.removeItem(at: url) try? FileManager.default.removeItem(at: url)
try? string.write(to: url, atomically: true, encoding: .utf8) try? string.write(to: url, atomically: true, encoding: .utf8)

@ -0,0 +1,37 @@
//
// ExportFormat.swift
// PadelClub
//
// Created by Razmig Sarkissian on 19/07/2024.
//
import Foundation
enum ExportFormat: Int, Identifiable, CaseIterable {
var id: Int { self.rawValue }
case rawText
case csv
var suffix: String {
switch self {
case .rawText:
return "txt"
case .csv:
return "csv"
}
}
func separator() -> String {
switch self {
case .rawText:
return " "
case .csv:
return ";"
}
}
func newLineSeparator(_ count: Int = 1) -> String {
return Array(repeating: "\n", count: count).joined()
}
}

@ -264,6 +264,15 @@ enum TournamentLevel: Int, Hashable, Codable, CaseIterable, Identifiable {
} }
} }
func shouldShareTeams() -> Bool {
switch self {
case .p500, .p1000, .p1500, .p2000:
return true
default:
return false
}
}
static func mostRecent(inTournaments tournaments: [Tournament]) -> Self { static func mostRecent(inTournaments tournaments: [Tournament]) -> Self {
return tournaments.first?.tournamentLevel ?? .p100 return tournaments.first?.tournamentLevel ?? .p100
} }

@ -534,6 +534,21 @@ struct BracketEditTip: Tip {
} }
} }
struct TeamsExportTip: Tip {
var title: Text {
Text("Exporter les paires")
}
var message: Text? {
Text("Partager les paires comme indiqué dans le guide de la compétition à J-6 avant midi.")
}
var image: Image? {
Image(systemName: "square.and.arrow.up")
}
}
struct TipStyleModifier: ViewModifier { struct TipStyleModifier: ViewModifier {
@Environment(\.colorScheme) var colorScheme @Environment(\.colorScheme) var colorScheme
var tint: Color? var tint: Color?

@ -12,36 +12,29 @@ struct LearnMoreSheetView: View {
var tournament: Tournament var tournament: Tournament
var body: some View { var body: some View {
VStack(spacing: 20) { List {
Text("Pourquoi cette étape ?") ContentUnavailableView {
.font(.title) Text("Pourquoi cette étape ?")
Text(""" } description: {
Text("""
Pour terminer la préparation de votre tournoi et pouvoir commencer à convoquer vos joueurs, vous devez inscrire les paires que vous avez préparé dans Padel Club sur le site beach-padel.app.fft.fr. Pour terminer la préparation de votre tournoi et pouvoir commencer à convoquer vos joueurs, vous devez inscrire les paires que vous avez préparé dans Padel Club sur le site beach-padel.app.fft.fr.
Padel Club ne peut pas, pour l'instant, faire cette manipulation automatiquement. Padel Club ne peut pas, pour l'instant, faire cette manipulation automatiquement.
Par contre, vous pouvez exporter les paires que vous avez préparé en un simple fichier texte vous permettant ainsi d'accélérer un peu plus la saisie sur le site fédéral. Par contre, vous pouvez exporter les paires que vous avez préparé en un simple fichier texte vous permettant ainsi d'accélérer un peu plus la saisie sur le site fédéral.
Une fois vos que vos paires seront inscrites sur beach-padel.app.fft.fr, vous pourrez les importer à nouveau dans Padel Club en un instant, vous donnant accès aux emails et téléphones des joueurs dans le but de les convoquer. Une fois que vos paires seront inscrites sur beach-padel.app.fft.fr, vous pourrez les importer à nouveau dans Padel Club en un instant, vous donnant accès aux emails et téléphones des joueurs dans le but de les convoquer.
""") """)
.foregroundStyle(.secondary) } actions: {
ShareLink(item: tournament.pasteDataForImporting().createFile(tournament.tournamentTitle(.short))) {
ShareLink(item: tournament.pasteDataForImporting().createTxtFile(tournament.tournamentTitle(.short))) {
HStack {
Spacer()
Text("Exporter les inscriptions") Text("Exporter les inscriptions")
Spacer()
} }
}
.buttonStyle(.borderedProminent)
RowButtonView("J'ai compris") {
Button("J'ai compris") { dismiss()
dismiss() }
} }
} }
.padding([.leading, .trailing], 40)
} }
} }

@ -16,8 +16,9 @@ let inscriptionManagerWomanRankTip = InscriptionManagerWomanRankTip()
//let searchTip = InscriptionManagerSearchInputTip() //let searchTip = InscriptionManagerSearchInputTip()
//let createTip = InscriptionManagerCreateInputTip() //let createTip = InscriptionManagerCreateInputTip()
let rankUpdateTip = InscriptionManagerRankUpdateTip() let rankUpdateTip = InscriptionManagerRankUpdateTip()
//let padelBeachExportTip = PadelBeachExportTip() let padelBeachExportTip = PadelBeachExportTip()
//let padelBeachImportTip = PadelBeachImportTip() let padelBeachImportTip = PadelBeachImportTip()
let teamsExportTip = TeamsExportTip()
struct InscriptionManagerView: View { struct InscriptionManagerView: View {
@ -396,11 +397,7 @@ struct InscriptionManagerView: View {
Label("Clôturer", systemImage: "lock") Label("Clôturer", systemImage: "lock")
} }
Divider() Divider()
if let teamPaste { _sharingTeamsMenuView()
ShareLink(item: teamPaste) {
Label("Exporter les paires", systemImage: "square.and.arrow.up")
}
}
Button { Button {
presentImportView = true presentImportView = true
} label: { } label: {
@ -410,6 +407,11 @@ struct InscriptionManagerView: View {
Label("beach-padel.app.fft.fr", systemImage: "safari") Label("beach-padel.app.fft.fr", systemImage: "safari")
} }
} else { } else {
_sharingTeamsMenuView()
Divider()
Button { Button {
tournament.unlockRegistration() tournament.unlockRegistration()
_save() _save()
@ -431,6 +433,23 @@ struct InscriptionManagerView: View {
.navigationBarTitleDisplayMode(.inline) .navigationBarTitleDisplayMode(.inline)
} }
private func _sharingTeamsMenuView() -> some View {
Menu {
if let teamPaste = teamPaste() {
ShareLink(item: teamPaste) {
Text("En texte")
}
}
if let teamPaste = teamPaste(.csv) {
ShareLink(item: teamPaste) {
Text("En csv")
}
}
} label: {
Label("Exporter les paires", systemImage: "square.and.arrow.up")
}
}
var walkoutTeams: [TeamRegistration] { var walkoutTeams: [TeamRegistration] {
tournament.walkoutTeams() tournament.walkoutTeams()
} }
@ -439,8 +458,8 @@ struct InscriptionManagerView: View {
tournament.unsortedTeamsWithoutWO() tournament.unsortedTeamsWithoutWO()
} }
var teamPaste: URL? { func teamPaste(_ exportFormat: ExportFormat = .rawText) -> URL? {
tournament.pasteDataForImporting().createTxtFile(self.tournament.tournamentTitle(.short)) tournament.pasteDataForImporting(exportFormat).createFile(self.tournament.tournamentTitle(.short), exportFormat)
} }
var unsortedPlayers: [PlayerRegistration] { var unsortedPlayers: [PlayerRegistration] {
@ -535,7 +554,7 @@ struct InscriptionManagerView: View {
.listRowView(isActive: true, color: team.initialRoundColor() ?? tournament.cutLabelColor(index: teamIndex, teamCount: filterMode == .waiting ? 0 : 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 && walkoutTeams.isEmpty == false {
Text("\(teams.count.formatted()) équipe\(teams.count.pluralSuffix) dont \(walkoutTeams.count.formatted()) forfait\(walkoutTeams.count.pluralSuffix)") Text("\(teams.count.formatted()) équipe\(teams.count.pluralSuffix) dont \(walkoutTeams.count.formatted()) forfait\(walkoutTeams.count.pluralSuffix)")
} else { } else {
Text("\(teams.count.formatted()) équipe\(teams.count.pluralSuffix)") Text("\(teams.count.formatted()) équipe\(teams.count.pluralSuffix)")
@ -823,9 +842,13 @@ struct InscriptionManagerView: View {
@ViewBuilder @ViewBuilder
private func _relatedTips() -> some View { private func _relatedTips() -> some View {
// if pasteString == nil // if tournament.inscriptionClosed() && tournament.tournamentLevel.shouldShareTeams() {
// && createdPlayerIds.isEmpty // Section {
// && tournament.unsortedTeams().count >= tournament.teamCount // TipView(teamsExportTip)
// .tipStyle(tint: nil)
// }
// }
// if tournament.unsortedTeams().count >= tournament.teamCount
// && tournament.unsortedPlayers().filter({ $0.source == .beachPadel }).isEmpty { // && tournament.unsortedPlayers().filter({ $0.source == .beachPadel }).isEmpty {
// Section { // Section {
// TipView(padelBeachExportTip) { action in // TipView(padelBeachExportTip) { action in

Loading…
Cancel
Save