club_update
Razmig Sarkissian 1 year ago
parent 19b58448b2
commit ccb47ff26c
  1. 4
      PadelClub.xcodeproj/project.pbxproj
  2. 9
      PadelClub/Data/PlayerRegistration.swift
  3. 47
      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. 23
      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 */; };
FFF116E12BD2A9B600A33B06 /* DateInterval.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF116E02BD2A9B600A33B06 /* DateInterval.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 */; };
FFF8ACCD2B92367B008466FA /* FederalPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF8ACCC2B92367B008466FA /* FederalPlayer.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>"; };
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>"; };
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>"; };
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>"; };
@ -1323,6 +1325,7 @@
FF0EC51D2BB16F680056B6D1 /* SwiftParser.swift */,
FF1DC5582BAB767000FD8220 /* Tips.swift */,
C49EF01A2BD6A1E80077B5AA /* URLs.swift */,
FFF1D2CA2C4A22B200C8D33D /* ExportFormat.swift */,
);
path = Utils;
sourceTree = "<group>";
@ -1720,6 +1723,7 @@
C493B37E2C10AD3600862481 /* LoadingViewModifier.swift in Sources */,
FF089EBD2BB0287D00F0AEC7 /* PlayerView.swift in Sources */,
FF967D032BAEF0C000A9A3BD /* MatchDetailView.swift in Sources */,
FFF1D2CB2C4A22B200C8D33D /* ExportFormat.swift in Sources */,
FF967D0F2BAF63B000A9A3BD /* PlayerBlockView.swift in Sources */,
C4A47D922B7BBBEC00ADC637 /* StoreItem.swift in Sources */,
FFB9C8712BBADDE200A0EF4F /* Selectable.swift in Sources */,

@ -140,8 +140,13 @@ final class PlayerRegistration: ModelObject, Storable {
return nil
}
func pasteData() -> String {
[firstName.capitalized, lastName.capitalized, licenceId].compactMap({ $0 }).joined(separator: " ")
func pasteData(_ exportFormat: ExportFormat = .rawText) -> String {
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 {

@ -327,24 +327,61 @@ final class TeamRegistration: ModelObject, Storable {
resetBracketPosition()
}
func pasteData() -> String {
[playersPasteData(), formattedInscriptionDate(), name].compactMap({ $0 }).joined(separator: "\n")
func pasteData(_ exportFormat: ExportFormat = .rawText, _ index: Int = 0) -> String {
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 {
return registrationDate ?? .distantFuture
}
func formattedInscriptionDate() -> String? {
func formattedInscriptionDate(_ exportFormat: ExportFormat = .rawText) -> String? {
switch exportFormat {
case .rawText:
if let registrationDate {
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? {
func playersPasteData() -> String {
return players().map { $0.pasteData() }.joined(separator: "\n")
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(_ exportFormat: ExportFormat = .rawText) -> String {
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) {

@ -529,9 +529,19 @@ defer {
return Store.main.findById(event)
}
func pasteDataForImporting() -> String {
func pasteDataForImporting(_ exportFormat: ExportFormat = .rawText) -> String {
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? {

@ -185,10 +185,10 @@ extension LosslessStringConvertible {
}
extension String {
func createTxtFile(_ withName: String = "temp") -> URL {
func createFile(_ withName: String = "temp", _ exportedFormat: ExportFormat = .rawText) -> URL {
let url = FileManager.default.temporaryDirectory
.appendingPathComponent(withName)
.appendingPathExtension("txt")
.appendingPathExtension(exportedFormat.suffix)
let string = self
try? FileManager.default.removeItem(at: url)
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 {
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 {
@Environment(\.colorScheme) var colorScheme
var tint: Color?

@ -12,9 +12,10 @@ struct LearnMoreSheetView: View {
var tournament: Tournament
var body: some View {
VStack(spacing: 20) {
List {
ContentUnavailableView {
Text("Pourquoi cette étape ?")
.font(.title)
} 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.
@ -22,26 +23,18 @@ struct LearnMoreSheetView: View {
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)
ShareLink(item: tournament.pasteDataForImporting().createTxtFile(tournament.tournamentTitle(.short))) {
HStack {
Spacer()
} actions: {
ShareLink(item: tournament.pasteDataForImporting().createFile(tournament.tournamentTitle(.short))) {
Text("Exporter les inscriptions")
Spacer()
}
}
.buttonStyle(.borderedProminent)
Button("J'ai compris") {
RowButtonView("J'ai compris") {
dismiss()
}
}
.padding([.leading, .trailing], 40)
}
}
}

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

Loading…
Cancel
Save