sync2
Laurent 1 year ago
commit 32577b810b
  1. 719
      PadelClub.xcodeproj/project.pbxproj
  2. 78
      PadelClub.xcodeproj/xcshareddata/xcschemes/PadelClub ProdTest.xcscheme
  3. 66
      PadelClub/Data/AppSettings.swift
  4. 4
      PadelClub/Data/Coredata/ImportedPlayer+Extensions.swift
  5. 17
      PadelClub/Data/Federal/FederalTournament.swift
  6. 7
      PadelClub/Data/MatchScheduler.swift
  7. 4
      PadelClub/Data/PlayerRegistration.swift
  8. 72
      PadelClub/Data/Round.swift
  9. 13
      PadelClub/Data/Tournament.swift
  10. 41
      PadelClub/Data/User.swift
  11. 4
      PadelClub/PadelClubApp.swift
  12. 3
      PadelClub/Utils/ContactManager.swift
  13. 4
      PadelClub/Utils/URLs.swift
  14. 4
      PadelClub/Views/Club/ClubSearchView.swift
  15. 2
      PadelClub/Views/Club/ClubsView.swift
  16. 2
      PadelClub/Views/Components/Labels.swift
  17. 1
      PadelClub/Views/GroupStage/LoserBracketFromGroupStageView.swift
  18. 3
      PadelClub/Views/Match/MatchSetupView.swift
  19. 12
      PadelClub/Views/Navigation/Agenda/ActivityView.swift
  20. 102
      PadelClub/Views/Navigation/Agenda/TournamentLookUpView.swift
  21. 206
      PadelClub/Views/Navigation/Agenda/TournamentSubscriptionView.swift
  22. 16
      PadelClub/Views/Navigation/Umpire/UmpireView.swift
  23. 190
      PadelClub/Views/Planning/Components/DateUpdateManagerView.swift
  24. 1
      PadelClub/Views/Planning/CourtAvailabilitySettingsView.swift
  25. 3
      PadelClub/Views/Planning/GroupStageScheduleEditorView.swift
  26. 2
      PadelClub/Views/Planning/LoserRoundScheduleEditorView.swift
  27. 13
      PadelClub/Views/Planning/LoserRoundStepScheduleEditorView.swift
  28. 4
      PadelClub/Views/Planning/MatchScheduleEditorView.swift
  29. 1
      PadelClub/Views/Planning/PlanningByCourtView.swift
  30. 22
      PadelClub/Views/Planning/PlanningSettingsView.swift
  31. 1
      PadelClub/Views/Planning/PlanningView.swift
  32. 10
      PadelClub/Views/Player/Components/PlayerPopoverView.swift
  33. 2
      PadelClub/Views/Player/PlayerDetailView.swift
  34. 27
      PadelClub/Views/Round/LoserRoundSettingsView.swift
  35. 591
      PadelClub/Views/Shared/SelectablePlayerListView.swift
  36. 4
      PadelClub/Views/Team/EditingTeamView.swift
  37. 10
      PadelClub/Views/Team/TeamPickerView.swift
  38. 25
      PadelClub/Views/Tournament/FileImportView.swift
  39. 136
      PadelClub/Views/Tournament/Screen/AddTeamView.swift
  40. 47
      PadelClub/Views/Tournament/Screen/Components/TournamentGeneralSettingsView.swift
  41. 9
      PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift
  42. 2
      PadelClub/Views/Tournament/Screen/TournamentSettingsView.swift
  43. 4
      PadelClub/Views/Tournament/Subscription/Guard.swift
  44. 4
      PadelClub/Views/Tournament/TournamentInitView.swift

@ -123,6 +123,268 @@
FF4AB6BD2B9256E10002987F /* SelectablePlayerListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF4AB6BC2B9256E10002987F /* SelectablePlayerListView.swift */; };
FF4AB6BF2B92577A0002987F /* ImportedPlayerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF4AB6BE2B92577A0002987F /* ImportedPlayerView.swift */; };
FF4C7F022BBBD7150031B6A3 /* TabItemModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF4C7F012BBBD7150031B6A3 /* TabItemModifier.swift */; };
FF4CBF442C996C0600151637 /* UserCreationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47D862B7BA36D00ADC637 /* UserCreationView.swift */; };
FF4CBF452C996C0600151637 /* TabDestination.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF7091652B90F0B000AB08DA /* TabDestination.swift */; };
FF4CBF462C996C0600151637 /* CashierView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF9267F72BCE78C70080F940 /* CashierView.swift */; };
FF4CBF472C996C0600151637 /* Event.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8F263E2BAD7D5C00650388 /* Event.swift */; };
FF4CBF482C996C0600151637 /* PlayerHolder.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5D30522BD94E2E00F2B93D /* PlayerHolder.swift */; };
FF4CBF492C996C0600151637 /* LoserRoundStepScheduleEditorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF11628B2BD05267000C4809 /* LoserRoundStepScheduleEditorView.swift */; };
FF4CBF4A2C996C0600151637 /* ClubCourtSetupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF53FBB72BFB302B0051D4C3 /* ClubCourtSetupView.swift */; };
FF4CBF4B2C996C0600151637 /* Patcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B3A1542C2581DA0078EAA8 /* Patcher.swift */; };
FF4CBF4C2C996C0600151637 /* FileImportView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF089EBE2BB0B14600F0AEC7 /* FileImportView.swift */; };
FF4CBF4D2C996C0600151637 /* StepperView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47D9E2B7D0BCE00ADC637 /* StepperView.swift */; };
FF4CBF4E2C996C0600151637 /* RoundsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFC83D4E2BB807D100750834 /* RoundsView.swift */; };
FF4CBF4F2C996C0600151637 /* FederalTournament.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1CBC182BB53D1F0036DAAB /* FederalTournament.swift */; };
FF4CBF502C996C0600151637 /* TournamentInitView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8F26402BADFC8700650388 /* TournamentInitView.swift */; };
FF4CBF512C996C0600151637 /* SubscriptionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47D892B7BBB6500ADC637 /* SubscriptionView.swift */; };
FF4CBF522C996C0600151637 /* TournamentLookUpView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFD655D72C8DE27400E5B35E /* TournamentLookUpView.swift */; };
FF4CBF532C996C0600151637 /* ClubsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1DC5562BAB3AED00FD8220 /* ClubsView.swift */; };
FF4CBF542C996C0600151637 /* ImagePickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFE103112C366E5900684FC9 /* ImagePickerView.swift */; };
FF4CBF552C996C0600151637 /* MatchTypeSelectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8F264E2BAE0B9600650388 /* MatchTypeSelectionView.swift */; };
FF4CBF562C996C0600151637 /* MatchSetupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967D052BAF3C4200A9A3BD /* MatchSetupView.swift */; };
FF4CBF572C996C0600151637 /* NetworkManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF4AB6B42B9248200002987F /* NetworkManager.swift */; };
FF4CBF582C996C0600151637 /* EventLinksView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF2B6F5D2C036A1400835EE7 /* EventLinksView.swift */; };
FF4CBF592C996C0600151637 /* SeedInterval.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFB9C8742BBADDF700A0EF4F /* SeedInterval.swift */; };
FF4CBF5A2C996C0600151637 /* TournamentClubSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF025AE02BD0EB9000A86CF8 /* TournamentClubSettingsView.swift */; };
FF4CBF5B2C996C0600151637 /* GroupStageTeamView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFBF065B2BBD2657009D6715 /* GroupStageTeamView.swift */; };
FF4CBF5C2C996C0600151637 /* RoundSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5DA1922BB9279B00A33061 /* RoundSettingsView.swift */; };
FF4CBF5D2C996C0600151637 /* SupportButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFE2D2E12C231BEE00D0C7BE /* SupportButtonView.swift */; };
FF4CBF5E2C996C0600151637 /* TournamentBroadcastRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFB1C98A2C10255100B154A7 /* TournamentBroadcastRowView.swift */; };
FF4CBF5F2C996C0600151637 /* TeamWeightView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF025ADE2BD0CE0A00A86CF8 /* TeamWeightView.swift */; };
FF4CBF602C996C0600151637 /* SeedsCallingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF9268002BCE94920080F940 /* SeedsCallingView.swift */; };
FF4CBF612C996C0600151637 /* CallView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF9268082BCEDC2C0080F940 /* CallView.swift */; };
FF4CBF622C996C0600151637 /* Color+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5D0D732BB41DF8005CB568 /* Color+Extensions.swift */; };
FF4CBF632C996C0600151637 /* EditSharingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFE1030F2C366DCD00684FC9 /* EditSharingView.swift */; };
FF4CBF642C996C0600151637 /* TournamentCellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF7091672B90F79F00AB08DA /* TournamentCellView.swift */; };
FF4CBF652C996C0600151637 /* Sequence+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF6EC9032B9479F500EA7F5A /* Sequence+Extensions.swift */; };
FF4CBF662C996C0600151637 /* CashierDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF9267F92BCE78EB0080F940 /* CashierDetailView.swift */; };
FF4CBF672C996C0600151637 /* EventClubSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFE103072C353B7600684FC9 /* EventClubSettingsView.swift */; };
FF4CBF682C996C0600151637 /* AccountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47DB22B86387500ADC637 /* AccountView.swift */; };
FF4CBF692C996C0600151637 /* PlayersWithoutContactView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFCEDA4B2C2C08EA00F8C0F2 /* PlayersWithoutContactView.swift */; };
FF4CBF6A2C996C0600151637 /* TeamsCallingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFCD16B22C3E5E590092707B /* TeamsCallingView.swift */; };
FF4CBF6B2C996C0600151637 /* Calendar+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1CBC1C2BB53DC10036DAAB /* Calendar+Extensions.swift */; };
FF4CBF6C2C996C0600151637 /* TeamScore.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967CEF2BAECC0A00A9A3BD /* TeamScore.swift */; };
FF4CBF6D2C996C0600151637 /* EditablePlayerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1162822BCFBE4E000C4809 /* EditablePlayerView.swift */; };
FF4CBF6E2C996C0600151637 /* PlayerDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1162842BD00279000C4809 /* PlayerDetailView.swift */; };
FF4CBF6F2C996C0600151637 /* ListRowViewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5D0D752BB428B2005CB568 /* ListRowViewModifier.swift */; };
FF4CBF702C996C0600151637 /* PresentationContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF6EC8FF2B94794700EA7F5A /* PresentationContext.swift */; };
FF4CBF712C996C0600151637 /* AppSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFDB1C6C2BB2A02000F1E467 /* AppSettings.swift */; };
FF4CBF722C996C0600151637 /* SwiftParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF0EC51D2BB16F680056B6D1 /* SwiftParser.swift */; };
FF4CBF732C996C0600151637 /* ChangePasswordView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47DA82B85F82100ADC637 /* ChangePasswordView.swift */; };
FF4CBF742C996C0600151637 /* TournamentSubscriptionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8044AB2C8F676D00A49A52 /* TournamentSubscriptionView.swift */; };
FF4CBF752C996C0600151637 /* RoundView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFC83D502BB8087E00750834 /* RoundView.swift */; };
FF4CBF762C996C0600151637 /* RowButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF6EC8F62B94773100EA7F5A /* RowButtonView.swift */; };
FF4CBF772C996C0600151637 /* SendToAllView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF2EFBEF2BDE295E0049CE3B /* SendToAllView.swift */; };
FF4CBF782C996C0600151637 /* EventCreationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8F263A2BAD528600650388 /* EventCreationView.swift */; };
FF4CBF792C996C0600151637 /* LocationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFC1E1072BAC29FC008D6F59 /* LocationManager.swift */; };
FF4CBF7A2C996C0600151637 /* CapsuleViewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C01D972C481C0C0059087C /* CapsuleViewModifier.swift */; };
FF4CBF7B2C996C0600151637 /* BroadcastView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF6087EB2BE26A2F004E1E47 /* BroadcastView.swift */; };
FF4CBF7C2C996C0600151637 /* SchedulerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF964542BC266CF00EEF017 /* SchedulerView.swift */; };
FF4CBF7D2C996C0600151637 /* PadelClubButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFA1B1282BB71773006CE248 /* PadelClubButtonView.swift */; };
FF4CBF7E2C996C0600151637 /* TournamentSeedEditing.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5DA19A2BB9662200A33061 /* TournamentSeedEditing.swift */; };
FF4CBF7F2C996C0600151637 /* TournamentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF70916B2B91005400AB08DA /* TournamentView.swift */; };
FF4CBF802C996C0600151637 /* OngoingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5D30552BD95B1100F2B93D /* OngoingView.swift */; };
FF4CBF812C996C0600151637 /* CreateClubView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1DC5542BAB36DD00FD8220 /* CreateClubView.swift */; };
FF4CBF822C996C0600151637 /* APICallsListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4607A7C2C04DDE2004CB781 /* APICallsListView.swift */; };
FF4CBF832C996C0600151637 /* NetworkFederalService.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFC1E1092BAC2A77008D6F59 /* NetworkFederalService.swift */; };
FF4CBF842C996C0600151637 /* DurationSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF025AEE2BD1AE9400A86CF8 /* DurationSettingsView.swift */; };
FF4CBF852C996C0600151637 /* AppScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF025AEC2BD1513700A86CF8 /* AppScreen.swift */; };
FF4CBF862C996C0600151637 /* CourtView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFC91B022BD85E2400B29808 /* CourtView.swift */; };
FF4CBF872C996C0600151637 /* PointSelectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFCFC00D2BBC3D4600B82851 /* PointSelectionView.swift */; };
FF4CBF882C996C0600151637 /* TeamRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF089EB52BB00A3800F0AEC7 /* TeamRowView.swift */; };
FF4CBF892C996C0600151637 /* ContactManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF92680A2BCEE3E10080F940 /* ContactManager.swift */; };
FF4CBF8A2C996C0600151637 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40CD2F22C412681000DBD9A /* AppDelegate.swift */; };
FF4CBF8B2C996C0600151637 /* SubscriptionInfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49EF0252BD80AE80077B5AA /* SubscriptionInfoView.swift */; };
FF4CBF8C2C996C0600151637 /* EditScoreView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFCFC0012BBC39A600B82851 /* EditScoreView.swift */; };
FF4CBF8D2C996C0600151637 /* TournamentOrganizerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF7091612B90F04300AB08DA /* TournamentOrganizerView.swift */; };
FF4CBF8E2C996C0600151637 /* NetworkMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF92680C2BCEE5EA0080F940 /* NetworkMonitor.swift */; };
FF4CBF8F2C996C0600151637 /* TournamentRunningView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967CF52BAED51600A9A3BD /* TournamentRunningView.swift */; };
FF4CBF902C996C0600151637 /* TournamentDatePickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8F264A2BAE0B4100650388 /* TournamentDatePickerView.swift */; };
FF4CBF912C996C0600151637 /* CourtAvailabilitySettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF116E22BD2AF4800A33B06 /* CourtAvailabilitySettingsView.swift */; };
FF4CBF922C996C0600151637 /* ConfirmButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFE8C2BF2C7601E80046B243 /* ConfirmButtonView.swift */; };
FF4CBF932C996C0600151637 /* PasswordField.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4FC2E262C2AABC90021F3BF /* PasswordField.swift */; };
FF4CBF942C996C0600151637 /* MatchRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967CFF2BAEEF6400A9A3BD /* MatchRowView.swift */; };
FF4CBF952C996C0600151637 /* Locale+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44B79102BBDA63A00906534 /* Locale+Extensions.swift */; };
FF4CBF962C996C0600151637 /* HtmlService.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1F4B732BFA00FC000B4573 /* HtmlService.swift */; };
FF4CBF972C996C0600151637 /* GroupStage.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967CE72BAEC70100A9A3BD /* GroupStage.swift */; };
FF4CBF982C996C0600151637 /* TournamentCashierView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1162802BCF945C000C4809 /* TournamentCashierView.swift */; };
FF4CBF992C996C0600151637 /* StoreManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47D8D2B7BBBEC00ADC637 /* StoreManager.swift */; };
FF4CBF9A2C996C0600151637 /* SearchViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF4AB6BA2B9256D50002987F /* SearchViewModel.swift */; };
FF4CBF9B2C996C0600151637 /* PlayerRegistration.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967CF12BAECC0B00A9A3BD /* PlayerRegistration.swift */; };
FF4CBF9C2C996C0600151637 /* ImportedPlayerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF4AB6BE2B92577A0002987F /* ImportedPlayerView.swift */; };
FF4CBF9D2C996C0600151637 /* EditingTeamView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1162862BD004AD000C4809 /* EditingTeamView.swift */; };
FF4CBF9E2C996C0600151637 /* NetworkManagerError.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF6EC9052B947A1000EA7F5A /* NetworkManagerError.swift */; };
FF4CBF9F2C996C0600151637 /* Tournament.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47D592B6D383C00ADC637 /* Tournament.swift */; };
FF4CBFA02C996C0600151637 /* TournamentStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4FC2E2A2C2C0E4D0021F3BF /* TournamentStore.swift */; };
FF4CBFA12C996C0600151637 /* LoserRoundSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5647122C0B6F380081F995 /* LoserRoundSettingsView.swift */; };
FF4CBFA22C996C0600151637 /* Persistence.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF3795652B9399AA004EA093 /* Persistence.swift */; };
FF4CBFA32C996C0600151637 /* CloseDatePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFCF76062C3BE9BC006C8C3D /* CloseDatePicker.swift */; };
FF4CBFA42C996C0600151637 /* BarButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1DF49A2BD8D23900822FA0 /* BarButtonView.swift */; };
FF4CBFA52C996C0600151637 /* PlanningView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF9644F2BC25E3700EEF017 /* PlanningView.swift */; };
FF4CBFA62C996C0600151637 /* Match.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967CEB2BAECB9900A9A3BD /* Match.swift */; };
FF4CBFA72C996C0600151637 /* TournamentLevelPickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8F26492BAE0B4100650388 /* TournamentLevelPickerView.swift */; };
FF4CBFA82C996C0600151637 /* FederalTournamentHolder.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1CBC212BB53E590036DAAB /* FederalTournamentHolder.swift */; };
FF4CBFA92C996C0600151637 /* DataStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47D5D2B6D38EC00ADC637 /* DataStore.swift */; };
FF4CBFAA2C996C0600151637 /* SetDescriptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFCFC01B2BBC5AAA00B82851 /* SetDescriptor.swift */; };
FF4CBFAB2C996C0600151637 /* TeamHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF025AD72BD0C10F00A86CF8 /* TeamHeaderView.swift */; };
FF4CBFAC2C996C0600151637 /* OrganizedTournamentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF82CFC42B911F5B00B0CAF2 /* OrganizedTournamentView.swift */; };
FF4CBFAD2C996C0600151637 /* RoundScheduleEditorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF964562BC26B3400EEF017 /* RoundScheduleEditorView.swift */; };
FF4CBFAE2C996C0600151637 /* EventListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF59FFB22B90EFAC0061EFF9 /* EventListView.swift */; };
FF4CBFAF2C996C0600151637 /* TournamentConfiguratorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8F263C2BAD627A00650388 /* TournamentConfiguratorView.swift */; };
FF4CBFB02C996C0600151637 /* ClubImportView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFC1E10B2BAC7FB0008D6F59 /* ClubImportView.swift */; };
FF4CBFB12C996C0600151637 /* UnderlineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF558C622C6CDD020071F9AE /* UnderlineView.swift */; };
FF4CBFB22C996C0600151637 /* MatchScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF3B60A22BC49BBC008C2E66 /* MatchScheduler.swift */; };
FF4CBFB32C996C0600151637 /* CallMessageCustomizationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1162792BCF8109000C4809 /* CallMessageCustomizationView.swift */; };
FF4CBFB42C996C0600151637 /* TournamentStatusView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF6087E92BE25EF1004E1E47 /* TournamentStatusView.swift */; };
FF4CBFB52C996C0600151637 /* MatchTeamDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF025ADA2BD0C2D000A86CF8 /* MatchTeamDetailView.swift */; };
FF4CBFB62C996C0600151637 /* GenericDestinationPickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5DA1942BB927E800A33061 /* GenericDestinationPickerView.swift */; };
FF4CBFB72C996C0600151637 /* DateInterval.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF116E02BD2A9B600A33B06 /* DateInterval.swift */; };
FF4CBFB82C996C0600151637 /* TableStructureView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8F26532BAE1E4400650388 /* TableStructureView.swift */; };
FF4CBFB92C996C0600151637 /* Purchase.swift in Sources */ = {isa = PBXBuildFile; fileRef = C45BAE432BCA753E002EEC8A /* Purchase.swift */; };
FF4CBFBA2C996C0600151637 /* Screen.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF6EC8FD2B94792300EA7F5A /* Screen.swift */; };
FF4CBFBB2C996C0600151637 /* Round.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967CED2BAECBD700A9A3BD /* Round.swift */; };
FF4CBFBC2C996C0600151637 /* FederalDataViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5BAF6D2BE0B3C8008B4B7E /* FederalDataViewModel.swift */; };
FF4CBFBD2C996C0600151637 /* AgendaDestination.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF3F74FE2B91A2D4004CFE0E /* AgendaDestination.swift */; };
FF4CBFBE2C996C0600151637 /* PadelClubApp.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = FF3795602B9396D0004EA093 /* PadelClubApp.xcdatamodeld */; };
FF4CBFBF2C996C0600151637 /* SetInputView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFCFC0152BBC5A4C00B82851 /* SetInputView.swift */; };
FF4CBFC02C996C0600151637 /* ButtonValidateView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF03C932BD91D0C00B516FC /* ButtonValidateView.swift */; };
FF4CBFC12C996C0600151637 /* ClubRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5D0D882BB4935C005CB568 /* ClubRowView.swift */; };
FF4CBFC22C996C0600151637 /* ClubDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1DC5502BAB351300FD8220 /* ClubDetailView.swift */; };
FF4CBFC32C996C0600151637 /* GroupStageCallingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF9268022BCE94A30080F940 /* GroupStageCallingView.swift */; };
FF4CBFC42C996C0600151637 /* Key.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49EF0432BE286780077B5AA /* Key.swift */; };
FF4CBFC52C996C0600151637 /* CashierSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF11627C2BCF941A000C4809 /* CashierSettingsView.swift */; };
FF4CBFC62C996C0600151637 /* LoserRoundScheduleEditorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFFCDE0D2BCC833600317DEF /* LoserRoundScheduleEditorView.swift */; };
FF4CBFC72C996C0600151637 /* Club.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47D622B6D3D6500ADC637 /* Club.swift */; };
FF4CBFC82C996C0600151637 /* Array+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF6EC90A2B947AC000EA7F5A /* Array+Extensions.swift */; };
FF4CBFC92C996C0600151637 /* ToolboxView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF59FFB82B90EFD70061EFF9 /* ToolboxView.swift */; };
FF4CBFCA2C996C0600151637 /* Alphabet.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8E1CE52C006E0200184680 /* Alphabet.swift */; };
FF4CBFCB2C996C0600151637 /* String+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF8ACD82B923F3C008466FA /* String+Extensions.swift */; };
FF4CBFCC2C996C0600151637 /* GroupStageSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFCB74122C4625BB008384D0 /* GroupStageSettingsView.swift */; };
FF4CBFCD2C996C0600151637 /* TournamentGeneralSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF025AE42BD0EBB800A86CF8 /* TournamentGeneralSettingsView.swift */; };
FF4CBFCE2C996C0600151637 /* LoserRoundView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFC2DCB12BBE75D40046DB9F /* LoserRoundView.swift */; };
FF4CBFCF2C996C0600151637 /* AddTeamView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF90FC1C2C44FB3E009339B2 /* AddTeamView.swift */; };
FF4CBFD02C996C0600151637 /* PlayerPayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF9267FB2BCE84870080F940 /* PlayerPayView.swift */; };
FF4CBFD12C996C0600151637 /* PlanningByCourtView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF2B51542C7A4DAF00FFF126 /* PlanningByCourtView.swift */; };
FF4CBFD22C996C0600151637 /* FileImportManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFA6D7842BB0B795003A31F3 /* FileImportManager.swift */; };
FF4CBFD32C996C0600151637 /* TournamentButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF6EC8FA2B94788600EA7F5A /* TournamentButtonView.swift */; };
FF4CBFD42C996C0600151637 /* FederalPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF8ACCC2B92367B008466FA /* FederalPlayer.swift */; };
FF4CBFD52C996C0600151637 /* NavigationViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFBF065F2BBD9F6D009D6715 /* NavigationViewModel.swift */; };
FF4CBFD62C996C0600151637 /* FixedWidthInteger+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF6EC9082B947A5300EA7F5A /* FixedWidthInteger+Extensions.swift */; };
FF4CBFD72C996C0600151637 /* LoserBracketFromGroupStageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF6525C22C8C61B400B9498E /* LoserBracketFromGroupStageView.swift */; };
FF4CBFD82C996C0600151637 /* ImportedPlayer+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5D30502BD94E1000F2B93D /* ImportedPlayer+Extensions.swift */; };
FF4CBFD92C996C0600151637 /* ClubSearchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFC1E1032BAC28C6008D6F59 /* ClubSearchView.swift */; };
FF4CBFDA2C996C0600151637 /* PlayerPopoverView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF089EBA2BB0120700F0AEC7 /* PlayerPopoverView.swift */; };
FF4CBFDB2C996C0600151637 /* InscriptionManagerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF70916D2B9108C600AB08DA /* InscriptionManagerView.swift */; };
FF4CBFDC2C996C0600151637 /* ActivityView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF82CFC82B9132AF00B0CAF2 /* ActivityView.swift */; };
FF4CBFDD2C996C0600151637 /* MySortDescriptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFDB1C722BB2CFE900F1E467 /* MySortDescriptor.swift */; };
FF4CBFDE2C996C0600151637 /* CalendarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5D0D8A2BB4D1E3005CB568 /* CalendarView.swift */; };
FF4CBFDF2C996C0600151637 /* FederalTournamentSearchScope.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1CBC1E2BB53E0C0036DAAB /* FederalTournamentSearchScope.swift */; };
FF4CBFE02C996C0600151637 /* TournamentFieldsManagerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8F26462BAE0ACB00650388 /* TournamentFieldsManagerView.swift */; };
FF4CBFE12C996C0600151637 /* PrintSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1F4B812BFA0124000B4573 /* PrintSettingsView.swift */; };
FF4CBFE22C996C0600151637 /* TournamentMatchFormatsSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF025AE22BD0EBA900A86CF8 /* TournamentMatchFormatsSettingsView.swift */; };
FF4CBFE32C996C0600151637 /* DateUpdateManagerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1162892BD05247000C4809 /* DateUpdateManagerView.swift */; };
FF4CBFE42C996C0600151637 /* MatchTypeSmallSelectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFCFC0192BBC5A8500B82851 /* MatchTypeSmallSelectionView.swift */; };
FF4CBFE52C996C0600151637 /* MonthData.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF025AE82BD1307E00A86CF8 /* MonthData.swift */; };
FF4CBFE62C996C0600151637 /* MenuWarningView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFEF7F4D2BDE69130033D0F0 /* MenuWarningView.swift */; };
FF4CBFE72C996C0600151637 /* TournamentBuildView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1F4B6C2BF9E60B000B4573 /* TournamentBuildView.swift */; };
FF4CBFE82C996C0600151637 /* TeamPickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967D0A2BAF3D4C00A9A3BD /* TeamPickerView.swift */; };
FF4CBFE92C996C0600151637 /* CloudConvert.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFA6D7862BB0B7A2003A31F3 /* CloudConvert.swift */; };
FF4CBFEA2C996C0600151637 /* EventTournamentsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFBF41832BF75ED7001B24CB /* EventTournamentsView.swift */; };
FF4CBFEB2C996C0600151637 /* DisplayContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1DC55A2BAB80C400FD8220 /* DisplayContext.swift */; };
FF4CBFEC2C996C0600151637 /* TournamentCallView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF9268062BCE94D90080F940 /* TournamentCallView.swift */; };
FF4CBFED2C996C0600151637 /* LoserRoundsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFC2DCB32BBE9ECD0046DB9F /* LoserRoundsView.swift */; };
FF4CBFEE2C996C0600151637 /* GroupStagesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967CFB2BAEE13900A9A3BD /* GroupStagesView.swift */; };
FF4CBFEF2C996C0600151637 /* PadelClubView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFD783FE2B91BA42000F62A6 /* PadelClubView.swift */; };
FF4CBFF02C996C0600151637 /* URLs.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49EF01A2BD6A1E80077B5AA /* URLs.swift */; };
FF4CBFF12C996C0600151637 /* MatchDescriptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFCFC0132BBC59FC00B82851 /* MatchDescriptor.swift */; };
FF4CBFF22C996C0600151637 /* TournamentFormatSelectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8F26482BAE0B4100650388 /* TournamentFormatSelectionView.swift */; };
FF4CBFF32C996C0600151637 /* MatchListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFBF065D2BBD8040009D6715 /* MatchListView.swift */; };
FF4CBFF42C996C0600151637 /* PadelClubApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = C425D4002B6D249D002A7B48 /* PadelClubApp.swift */; };
FF4CBFF52C996C0600151637 /* TournamentSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8F26422BADFE5B00650388 /* TournamentSettingsView.swift */; };
FF4CBFF62C996C0600151637 /* String+Crypto.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49EF03B2BE15AF80077B5AA /* String+Crypto.swift */; };
FF4CBFF72C996C0600151637 /* GroupStageTeamReplacementView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF9AC3942BE3627B00C2E883 /* GroupStageTeamReplacementView.swift */; };
FF4CBFF82C996C0600151637 /* TabItemModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF4C7F012BBBD7150031B6A3 /* TabItemModifier.swift */; };
FF4CBFF92C996C0600151637 /* DeferredViewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFDDD40B2B93B2BB00C91A49 /* DeferredViewModifier.swift */; };
FF4CBFFA2C996C0600151637 /* TournamentScheduleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF0E0B6C2BC254C6005F00A9 /* TournamentScheduleView.swift */; };
FF4CBFFB2C996C0600151637 /* MatchFormatStorageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF025AF02BD1AEBD00A86CF8 /* MatchFormatStorageView.swift */; };
FF4CBFFC2C996C0600151637 /* UmpireView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF3F74F52B919E45004CFE0E /* UmpireView.swift */; };
FF4CBFFD2C996C0600151637 /* User.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47DAC2B85FCCD00ADC637 /* User.swift */; };
FF4CBFFE2C996C0600151637 /* MatchSummaryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967D002BAEF0B400A9A3BD /* MatchSummaryView.swift */; };
FF4CBFFF2C996C0600151637 /* TournamentDurationManagerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8F26442BAE0A3400650388 /* TournamentDurationManagerView.swift */; };
FF4CC0002C996C0600151637 /* MockData.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1DC5522BAB354A00FD8220 /* MockData.swift */; };
FF4CC0012C996C0600151637 /* TeamDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967D082BAF3D4000A9A3BD /* TeamDetailView.swift */; };
FF4CC0022C996C0600151637 /* GroupStagesSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5DA18E2BB9268800A33061 /* GroupStagesSettingsView.swift */; };
FF4CC0032C996C0600151637 /* TournamentFilterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF663FBD2BE019EC0031AE83 /* TournamentFilterView.swift */; };
FF4CC0042C996C0600151637 /* HtmlGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1F4B722BFA00FB000B4573 /* HtmlGenerator.swift */; };
FF4CC0052C996C0600151637 /* PadelRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8F26352BAD523300650388 /* PadelRule.swift */; };
FF4CC0062C996C0600151637 /* TeamRegistration.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967CF02BAECC0B00A9A3BD /* TeamRegistration.swift */; };
FF4CC0072C996C0600151637 /* Date+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF8ACDA2B923F48008466FA /* Date+Extensions.swift */; };
FF4CC0082C996C0600151637 /* GroupStageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967CFA2BAEE13800A9A3BD /* GroupStageView.swift */; };
FF4CC0092C996C0600151637 /* Tips.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1DC5582BAB767000FD8220 /* Tips.swift */; };
FF4CC00A2C996C0600151637 /* MainView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF59FFB62B90EFBF0061EFF9 /* MainView.swift */; };
FF4CC00B2C996C0600151637 /* MatchDateView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967D0C2BAF3EB200A9A3BD /* MatchDateView.swift */; };
FF4CC00C2C996C0600151637 /* PlanningSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF964522BC262B000EEF017 /* PlanningSettingsView.swift */; };
FF4CC00D2C996C0600151637 /* MatchScheduleEditorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF527D52BC6DDD000FF4EF2 /* MatchScheduleEditorView.swift */; };
FF4CC00E2C996C0600151637 /* FortuneWheelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFC91AF82BD6A09100B29808 /* FortuneWheelView.swift */; };
FF4CC00F2C996C0600151637 /* URL+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF8ACD52B923960008466FA /* URL+Extensions.swift */; };
FF4CC0102C996C0600151637 /* LoadingViewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = C493B37D2C10AD3600862481 /* LoadingViewModifier.swift */; };
FF4CC0112C996C0600151637 /* PlayerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF089EBC2BB0287D00F0AEC7 /* PlayerView.swift */; };
FF4CC0122C996C0600151637 /* MatchDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967D022BAEF0C000A9A3BD /* MatchDetailView.swift */; };
FF4CC0132C996C0600151637 /* ExportFormat.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF1D2CA2C4A22B200C8D33D /* ExportFormat.swift */; };
FF4CC0142C996C0600151637 /* PlayerBlockView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967D0E2BAF63B000A9A3BD /* PlayerBlockView.swift */; };
FF4CC0152C996C0600151637 /* StoreItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47D8F2B7BBBEC00ADC637 /* StoreItem.swift */; };
FF4CC0162C996C0600151637 /* Selectable.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFB9C8702BBADDE200A0EF4F /* Selectable.swift */; };
FF4CC0172C996C0600151637 /* PointView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFCFC0112BBC3E1A00B82851 /* PointView.swift */; };
FF4CC0182C996C0600151637 /* ClubHolder.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1CBC202BB53E590036DAAB /* ClubHolder.swift */; };
FF4CC0192C996C0600151637 /* EventSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFBF41852BF75FDA001B24CB /* EventSettingsView.swift */; };
FF4CC01A2C996C0600151637 /* InscriptionInfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5D0D772BB42C5B005CB568 /* InscriptionInfoView.swift */; };
FF4CC01B2C996C0600151637 /* SelectablePlayerListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF4AB6BC2B9256E10002987F /* SelectablePlayerListView.swift */; };
FF4CC01C2C996C0600151637 /* MatchFormatPickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8F26502BAE0BAD00650388 /* MatchFormatPickerView.swift */; };
FF4CC01D2C996C0600151637 /* TournamentRankView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5BAF712BE19274008B4B7E /* TournamentRankView.swift */; };
FF4CC01E2C996C0600151637 /* NumberFormatter+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5D0D862BB48AFD005CB568 /* NumberFormatter+Extensions.swift */; };
FF4CC01F2C996C0600151637 /* SetLabelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFCFC0172BBC5A6800B82851 /* SetLabelView.swift */; };
FF4CC0202C996C0600151637 /* DebugSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4489BE12C05BF5000043F3D /* DebugSettingsView.swift */; };
FF4CC0212C996C0600151637 /* GroupStageScheduleEditorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF9645A2BC2D53B00EEF017 /* GroupStageScheduleEditorView.swift */; };
FF4CC0222C996C0600151637 /* EventView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFBF41812BF73EB3001B24CB /* EventView.swift */; };
FF4CC0232C996C0600151637 /* LoginView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47DA52B83948E00ADC637 /* LoginView.swift */; };
FF4CC0242C996C0600151637 /* Court.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFC91B002BD85C2F00B29808 /* Court.swift */; };
FF4CC0252C996C0600151637 /* Labels.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967CF72BAEDF0000A9A3BD /* Labels.swift */; };
FF4CC0262C996C0600151637 /* CopyPasteButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFCB74162C480411008384D0 /* CopyPasteButtonView.swift */; };
FF4CC0272C996C0600151637 /* PlayerSexPickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF089EB32BB0020000F0AEC7 /* PlayerSexPickerView.swift */; };
FF4CC0282C996C0600151637 /* TournamentInscriptionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1F4B702BF9EFE9000B4573 /* TournamentInscriptionView.swift */; };
FF4CC0292C996C0600151637 /* CallSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF9267FE2BCE94830080F940 /* CallSettingsView.swift */; };
FF4CC02A2C996C0600151637 /* FooterButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF025ADC2BD0C94300A86CF8 /* FooterButtonView.swift */; };
FF4CC02B2C996C0600151637 /* RankCalculatorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5D0D822BB48997005CB568 /* RankCalculatorView.swift */; };
FF4CC02C2C996C0600151637 /* DateBoxView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF7091692B90F95E00AB08DA /* DateBoxView.swift */; };
FF4CC02D2C996C0600151637 /* LearnMoreSheetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5D0D6F2BB3EFA5005CB568 /* LearnMoreSheetView.swift */; };
FF4CC02E2C996C0600151637 /* SourceFileManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFF8ACD32B92392C008466FA /* SourceFileManager.swift */; };
FF4CC02F2C996C0600151637 /* PListReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EC6F582BE92D88000CEAB4 /* PListReader.swift */; };
FF4CC0302C996C0600151637 /* UpdateSourceRankDateView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF0EC5212BB173E70056B6D1 /* UpdateSourceRankDateView.swift */; };
FF4CC0312C996C0600151637 /* GlobalSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF025AE62BD1111000A86CF8 /* GlobalSettingsView.swift */; };
FF4CC0322C996C0600151637 /* Guard.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47D8E2B7BBBEC00ADC637 /* Guard.swift */; };
FF4CC0332C996C0600151637 /* PurchaseListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49EF0182BD694290077B5AA /* PurchaseListView.swift */; };
FF4CC0352C996C0600151637 /* Algorithms in Frameworks */ = {isa = PBXBuildFile; productRef = FF4CBF3F2C996C0600151637 /* Algorithms */; };
FF4CC0362C996C0600151637 /* Zip in Frameworks */ = {isa = PBXBuildFile; productRef = FF4CBF412C996C0600151637 /* Zip */; };
FF4CC0372C996C0600151637 /* LeStorage.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C49EF0372BDFF3000077B5AA /* LeStorage.framework */; };
FF4CC0392C996C0600151637 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C425D4072B6D249E002A7B48 /* Preview Assets.xcassets */; };
FF4CC03A2C996C0600151637 /* Launch Screen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = FFD784002B91BF79000F62A6 /* Launch Screen.storyboard */; };
FF4CC03B2C996C0600151637 /* tournament-template.html in Resources */ = {isa = PBXBuildFile; fileRef = FF1F4B7F2BFA0105000B4573 /* tournament-template.html */; };
FF4CC03C2C996C0600151637 /* local.sqlite in Resources */ = {isa = PBXBuildFile; fileRef = FF2B51602C7E302C00FFF126 /* local.sqlite */; };
FF4CC03D2C996C0600151637 /* groupstagescore-template.html in Resources */ = {isa = PBXBuildFile; fileRef = FF1F4B7B2BFA0105000B4573 /* groupstagescore-template.html */; };
FF4CC03E2C996C0600151637 /* player-template.html in Resources */ = {isa = PBXBuildFile; fileRef = FF1F4B7E2BFA0105000B4573 /* player-template.html */; };
FF4CC03F2C996C0600151637 /* groupstagerow-template.html in Resources */ = {isa = PBXBuildFile; fileRef = FF1F4B7A2BFA0105000B4573 /* groupstagerow-template.html */; };
FF4CC0402C996C0600151637 /* hiddenplayer-template.html in Resources */ = {isa = PBXBuildFile; fileRef = FF1F4B7C2BFA0105000B4573 /* hiddenplayer-template.html */; };
FF4CC0412C996C0600151637 /* bracket-template.html in Resources */ = {isa = PBXBuildFile; fileRef = FF1F4B762BFA0105000B4573 /* bracket-template.html */; };
FF4CC0422C996C0600151637 /* groupstagecol-template.html in Resources */ = {isa = PBXBuildFile; fileRef = FF1F4B782BFA0105000B4573 /* groupstagecol-template.html */; };
FF4CC0432C996C0600151637 /* groupstage-template.html in Resources */ = {isa = PBXBuildFile; fileRef = FF1F4B772BFA0105000B4573 /* groupstage-template.html */; };
FF4CC0442C996C0600151637 /* groupstageentrant-template.html in Resources */ = {isa = PBXBuildFile; fileRef = FF1F4B792BFA0105000B4573 /* groupstageentrant-template.html */; };
FF4CC0452C996C0600151637 /* match-template.html in Resources */ = {isa = PBXBuildFile; fileRef = FF1F4B7D2BFA0105000B4573 /* match-template.html */; };
FF4CC0462C996C0600151637 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = FFF0241D2BF48B15001F14B4 /* Localizable.strings */; };
FF4CC0472C996C0600151637 /* SyncedProducts.storekit in Resources */ = {isa = PBXBuildFile; fileRef = C45BAE3A2BC6DF10002EEC8A /* SyncedProducts.storekit */; };
FF4CC0482C996C0600151637 /* local.plist in Resources */ = {isa = PBXBuildFile; fileRef = C4EC6F562BE92CAC000CEAB4 /* local.plist */; };
FF4CC0492C996C0600151637 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = FF0CA5742BDA4AE10080E843 /* PrivacyInfo.xcprivacy */; };
FF4CC04A2C996C0600151637 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C425D4042B6D249E002A7B48 /* Assets.xcassets */; };
FF4CC04C2C996C0600151637 /* LeStorage.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = C49EF0372BDFF3000077B5AA /* LeStorage.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
FF53FBB82BFB302B0051D4C3 /* ClubCourtSetupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF53FBB72BFB302B0051D4C3 /* ClubCourtSetupView.swift */; };
FF558C632C6CDD020071F9AE /* UnderlineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF558C622C6CDD020071F9AE /* UnderlineView.swift */; };
FF5647132C0B6F390081F995 /* LoserRoundSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5647122C0B6F380081F995 /* LoserRoundSettingsView.swift */; };
@ -571,6 +833,17 @@
name = "Embed Frameworks";
runOnlyForDeploymentPostprocessing = 0;
};
FF4CC04B2C996C0600151637 /* Embed Frameworks */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 10;
files = (
FF4CC04C2C996C0600151637 /* LeStorage.framework in Embed Frameworks */,
);
name = "Embed Frameworks";
runOnlyForDeploymentPostprocessing = 0;
};
FF70FBCA2C90584900129CC2 /* Embed Frameworks */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
@ -745,6 +1018,7 @@
FF4AB6BC2B9256E10002987F /* SelectablePlayerListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectablePlayerListView.swift; sourceTree = "<group>"; };
FF4AB6BE2B92577A0002987F /* ImportedPlayerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImportedPlayerView.swift; sourceTree = "<group>"; };
FF4C7F012BBBD7150031B6A3 /* TabItemModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabItemModifier.swift; sourceTree = "<group>"; };
FF4CC0502C996C0600151637 /* ProdTest PadelClub.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "ProdTest PadelClub.app"; sourceTree = BUILT_PRODUCTS_DIR; };
FF53FBB72BFB302B0051D4C3 /* ClubCourtSetupView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClubCourtSetupView.swift; sourceTree = "<group>"; };
FF558C622C6CDD020071F9AE /* UnderlineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnderlineView.swift; sourceTree = "<group>"; };
FF5647122C0B6F380081F995 /* LoserRoundSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoserRoundSettingsView.swift; sourceTree = "<group>"; };
@ -930,6 +1204,16 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
FF4CC0342C996C0600151637 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
FF4CC0352C996C0600151637 /* Algorithms in Frameworks */,
FF4CC0362C996C0600151637 /* Zip in Frameworks */,
FF4CC0372C996C0600151637 /* LeStorage.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
FF70FBB32C90584900129CC2 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
@ -961,6 +1245,7 @@
C425D40D2B6D249E002A7B48 /* PadelClubTests.xctest */,
C425D4172B6D249E002A7B48 /* PadelClubUITests.xctest */,
FF70FBCF2C90584900129CC2 /* PadelClub TestFlight.app */,
FF4CC0502C996C0600151637 /* ProdTest PadelClub.app */,
);
name = Products;
sourceTree = "<group>";
@ -1743,6 +2028,28 @@
productReference = C425D4172B6D249E002A7B48 /* PadelClubUITests.xctest */;
productType = "com.apple.product-type.bundle.ui-testing";
};
FF4CBF3E2C996C0600151637 /* ProdTest PadelClub */ = {
isa = PBXNativeTarget;
buildConfigurationList = FF4CC04D2C996C0600151637 /* Build configuration list for PBXNativeTarget "ProdTest PadelClub" */;
buildPhases = (
FF4CBF432C996C0600151637 /* Sources */,
FF4CC0342C996C0600151637 /* Frameworks */,
FF4CC0382C996C0600151637 /* Resources */,
FF4CC04B2C996C0600151637 /* Embed Frameworks */,
);
buildRules = (
);
dependencies = (
);
name = "ProdTest PadelClub";
packageProductDependencies = (
FF4CBF3F2C996C0600151637 /* Algorithms */,
FF4CBF412C996C0600151637 /* Zip */,
);
productName = PadelClub;
productReference = FF4CC0502C996C0600151637 /* ProdTest PadelClub.app */;
productType = "com.apple.product-type.application";
};
FF70FABD2C90584900129CC2 /* PadelClub TestFlight */ = {
isa = PBXNativeTarget;
buildConfigurationList = FF70FBCC2C90584900129CC2 /* Build configuration list for PBXNativeTarget "PadelClub TestFlight" */;
@ -1810,6 +2117,7 @@
C425D40C2B6D249E002A7B48 /* PadelClubTests */,
C425D4162B6D249E002A7B48 /* PadelClubUITests */,
FF70FABD2C90584900129CC2 /* PadelClub TestFlight */,
FF4CBF3E2C996C0600151637 /* ProdTest PadelClub */,
);
};
/* End PBXProject section */
@ -1854,6 +2162,31 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
FF4CC0382C996C0600151637 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
FF4CC0392C996C0600151637 /* Preview Assets.xcassets in Resources */,
FF4CC03A2C996C0600151637 /* Launch Screen.storyboard in Resources */,
FF4CC03B2C996C0600151637 /* tournament-template.html in Resources */,
FF4CC03C2C996C0600151637 /* local.sqlite in Resources */,
FF4CC03D2C996C0600151637 /* groupstagescore-template.html in Resources */,
FF4CC03E2C996C0600151637 /* player-template.html in Resources */,
FF4CC03F2C996C0600151637 /* groupstagerow-template.html in Resources */,
FF4CC0402C996C0600151637 /* hiddenplayer-template.html in Resources */,
FF4CC0412C996C0600151637 /* bracket-template.html in Resources */,
FF4CC0422C996C0600151637 /* groupstagecol-template.html in Resources */,
FF4CC0432C996C0600151637 /* groupstage-template.html in Resources */,
FF4CC0442C996C0600151637 /* groupstageentrant-template.html in Resources */,
FF4CC0452C996C0600151637 /* match-template.html in Resources */,
FF4CC0462C996C0600151637 /* Localizable.strings in Resources */,
FF4CC0472C996C0600151637 /* SyncedProducts.storekit in Resources */,
FF4CC0482C996C0600151637 /* local.plist in Resources */,
FF4CC0492C996C0600151637 /* PrivacyInfo.xcprivacy in Resources */,
FF4CC04A2C996C0600151637 /* Assets.xcassets in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
FF70FBB72C90584900129CC2 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
@ -2151,6 +2484,253 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
FF4CBF432C996C0600151637 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
FF4CBF442C996C0600151637 /* UserCreationView.swift in Sources */,
FF4CBF452C996C0600151637 /* TabDestination.swift in Sources */,
FF4CBF462C996C0600151637 /* CashierView.swift in Sources */,
FF4CBF472C996C0600151637 /* Event.swift in Sources */,
FF4CBF482C996C0600151637 /* PlayerHolder.swift in Sources */,
FF4CBF492C996C0600151637 /* LoserRoundStepScheduleEditorView.swift in Sources */,
FF4CBF4A2C996C0600151637 /* ClubCourtSetupView.swift in Sources */,
FF4CBF4B2C996C0600151637 /* Patcher.swift in Sources */,
FF4CBF4C2C996C0600151637 /* FileImportView.swift in Sources */,
FF4CBF4D2C996C0600151637 /* StepperView.swift in Sources */,
FF4CBF4E2C996C0600151637 /* RoundsView.swift in Sources */,
FF4CBF4F2C996C0600151637 /* FederalTournament.swift in Sources */,
FF4CBF502C996C0600151637 /* TournamentInitView.swift in Sources */,
FF4CBF512C996C0600151637 /* SubscriptionView.swift in Sources */,
FF4CBF522C996C0600151637 /* TournamentLookUpView.swift in Sources */,
FF4CBF532C996C0600151637 /* ClubsView.swift in Sources */,
FF4CBF542C996C0600151637 /* ImagePickerView.swift in Sources */,
FF4CBF552C996C0600151637 /* MatchTypeSelectionView.swift in Sources */,
FF4CBF562C996C0600151637 /* MatchSetupView.swift in Sources */,
FF4CBF572C996C0600151637 /* NetworkManager.swift in Sources */,
FF4CBF582C996C0600151637 /* EventLinksView.swift in Sources */,
FF4CBF592C996C0600151637 /* SeedInterval.swift in Sources */,
FF4CBF5A2C996C0600151637 /* TournamentClubSettingsView.swift in Sources */,
FF4CBF5B2C996C0600151637 /* GroupStageTeamView.swift in Sources */,
FF4CBF5C2C996C0600151637 /* RoundSettingsView.swift in Sources */,
FF4CBF5D2C996C0600151637 /* SupportButtonView.swift in Sources */,
FF4CBF5E2C996C0600151637 /* TournamentBroadcastRowView.swift in Sources */,
FF4CBF5F2C996C0600151637 /* TeamWeightView.swift in Sources */,
FF4CBF602C996C0600151637 /* SeedsCallingView.swift in Sources */,
FF4CBF612C996C0600151637 /* CallView.swift in Sources */,
FF4CBF622C996C0600151637 /* Color+Extensions.swift in Sources */,
FF4CBF632C996C0600151637 /* EditSharingView.swift in Sources */,
FF4CBF642C996C0600151637 /* TournamentCellView.swift in Sources */,
FF4CBF652C996C0600151637 /* Sequence+Extensions.swift in Sources */,
FF4CBF662C996C0600151637 /* CashierDetailView.swift in Sources */,
FF4CBF672C996C0600151637 /* EventClubSettingsView.swift in Sources */,
FF4CBF682C996C0600151637 /* AccountView.swift in Sources */,
FF4CBF692C996C0600151637 /* PlayersWithoutContactView.swift in Sources */,
FF4CBF6A2C996C0600151637 /* TeamsCallingView.swift in Sources */,
FF4CBF6B2C996C0600151637 /* Calendar+Extensions.swift in Sources */,
FF4CBF6C2C996C0600151637 /* TeamScore.swift in Sources */,
FF4CBF6D2C996C0600151637 /* EditablePlayerView.swift in Sources */,
FF4CBF6E2C996C0600151637 /* PlayerDetailView.swift in Sources */,
FF4CBF6F2C996C0600151637 /* ListRowViewModifier.swift in Sources */,
FF4CBF702C996C0600151637 /* PresentationContext.swift in Sources */,
FF4CBF712C996C0600151637 /* AppSettings.swift in Sources */,
FF4CBF722C996C0600151637 /* SwiftParser.swift in Sources */,
FF4CBF732C996C0600151637 /* ChangePasswordView.swift in Sources */,
FF4CBF742C996C0600151637 /* TournamentSubscriptionView.swift in Sources */,
FF4CBF752C996C0600151637 /* RoundView.swift in Sources */,
FF4CBF762C996C0600151637 /* RowButtonView.swift in Sources */,
FF4CBF772C996C0600151637 /* SendToAllView.swift in Sources */,
FF4CBF782C996C0600151637 /* EventCreationView.swift in Sources */,
FF4CBF792C996C0600151637 /* LocationManager.swift in Sources */,
FF4CBF7A2C996C0600151637 /* CapsuleViewModifier.swift in Sources */,
FF4CBF7B2C996C0600151637 /* BroadcastView.swift in Sources */,
FF4CBF7C2C996C0600151637 /* SchedulerView.swift in Sources */,
FF4CBF7D2C996C0600151637 /* PadelClubButtonView.swift in Sources */,
FF4CBF7E2C996C0600151637 /* TournamentSeedEditing.swift in Sources */,
FF4CBF7F2C996C0600151637 /* TournamentView.swift in Sources */,
FF4CBF802C996C0600151637 /* OngoingView.swift in Sources */,
FF4CBF812C996C0600151637 /* CreateClubView.swift in Sources */,
FF4CBF822C996C0600151637 /* APICallsListView.swift in Sources */,
FF4CBF832C996C0600151637 /* NetworkFederalService.swift in Sources */,
FF4CBF842C996C0600151637 /* DurationSettingsView.swift in Sources */,
FF4CBF852C996C0600151637 /* AppScreen.swift in Sources */,
FF4CBF862C996C0600151637 /* CourtView.swift in Sources */,
FF4CBF872C996C0600151637 /* PointSelectionView.swift in Sources */,
FF4CBF882C996C0600151637 /* TeamRowView.swift in Sources */,
FF4CBF892C996C0600151637 /* ContactManager.swift in Sources */,
FF4CBF8A2C996C0600151637 /* AppDelegate.swift in Sources */,
FF4CBF8B2C996C0600151637 /* SubscriptionInfoView.swift in Sources */,
FF4CBF8C2C996C0600151637 /* EditScoreView.swift in Sources */,
FF4CBF8D2C996C0600151637 /* TournamentOrganizerView.swift in Sources */,
FF4CBF8E2C996C0600151637 /* NetworkMonitor.swift in Sources */,
FF4CBF8F2C996C0600151637 /* TournamentRunningView.swift in Sources */,
FF4CBF902C996C0600151637 /* TournamentDatePickerView.swift in Sources */,
FF4CBF912C996C0600151637 /* CourtAvailabilitySettingsView.swift in Sources */,
FF4CBF922C996C0600151637 /* ConfirmButtonView.swift in Sources */,
FF4CBF932C996C0600151637 /* PasswordField.swift in Sources */,
FF4CBF942C996C0600151637 /* MatchRowView.swift in Sources */,
FF4CBF952C996C0600151637 /* Locale+Extensions.swift in Sources */,
FF4CBF962C996C0600151637 /* HtmlService.swift in Sources */,
FF4CBF972C996C0600151637 /* GroupStage.swift in Sources */,
FF4CBF982C996C0600151637 /* TournamentCashierView.swift in Sources */,
FF4CBF992C996C0600151637 /* StoreManager.swift in Sources */,
FF4CBF9A2C996C0600151637 /* SearchViewModel.swift in Sources */,
FF4CBF9B2C996C0600151637 /* PlayerRegistration.swift in Sources */,
FF4CBF9C2C996C0600151637 /* ImportedPlayerView.swift in Sources */,
FF4CBF9D2C996C0600151637 /* EditingTeamView.swift in Sources */,
FF4CBF9E2C996C0600151637 /* NetworkManagerError.swift in Sources */,
FF4CBF9F2C996C0600151637 /* Tournament.swift in Sources */,
FF4CBFA02C996C0600151637 /* TournamentStore.swift in Sources */,
FF4CBFA12C996C0600151637 /* LoserRoundSettingsView.swift in Sources */,
FF4CBFA22C996C0600151637 /* Persistence.swift in Sources */,
FF4CBFA32C996C0600151637 /* CloseDatePicker.swift in Sources */,
FF4CBFA42C996C0600151637 /* BarButtonView.swift in Sources */,
FF4CBFA52C996C0600151637 /* PlanningView.swift in Sources */,
FF4CBFA62C996C0600151637 /* Match.swift in Sources */,
FF4CBFA72C996C0600151637 /* TournamentLevelPickerView.swift in Sources */,
FF4CBFA82C996C0600151637 /* FederalTournamentHolder.swift in Sources */,
FF4CBFA92C996C0600151637 /* DataStore.swift in Sources */,
FF4CBFAA2C996C0600151637 /* SetDescriptor.swift in Sources */,
FF4CBFAB2C996C0600151637 /* TeamHeaderView.swift in Sources */,
FF4CBFAC2C996C0600151637 /* OrganizedTournamentView.swift in Sources */,
FF4CBFAD2C996C0600151637 /* RoundScheduleEditorView.swift in Sources */,
FF4CBFAE2C996C0600151637 /* EventListView.swift in Sources */,
FF4CBFAF2C996C0600151637 /* TournamentConfiguratorView.swift in Sources */,
FF4CBFB02C996C0600151637 /* ClubImportView.swift in Sources */,
FF4CBFB12C996C0600151637 /* UnderlineView.swift in Sources */,
FF4CBFB22C996C0600151637 /* MatchScheduler.swift in Sources */,
FF4CBFB32C996C0600151637 /* CallMessageCustomizationView.swift in Sources */,
FF4CBFB42C996C0600151637 /* TournamentStatusView.swift in Sources */,
FF4CBFB52C996C0600151637 /* MatchTeamDetailView.swift in Sources */,
FF4CBFB62C996C0600151637 /* GenericDestinationPickerView.swift in Sources */,
FF4CBFB72C996C0600151637 /* DateInterval.swift in Sources */,
FF4CBFB82C996C0600151637 /* TableStructureView.swift in Sources */,
FF4CBFB92C996C0600151637 /* Purchase.swift in Sources */,
FF4CBFBA2C996C0600151637 /* Screen.swift in Sources */,
FF4CBFBB2C996C0600151637 /* Round.swift in Sources */,
FF4CBFBC2C996C0600151637 /* FederalDataViewModel.swift in Sources */,
FF4CBFBD2C996C0600151637 /* AgendaDestination.swift in Sources */,
FF4CBFBE2C996C0600151637 /* PadelClubApp.xcdatamodeld in Sources */,
FF4CBFBF2C996C0600151637 /* SetInputView.swift in Sources */,
FF4CBFC02C996C0600151637 /* ButtonValidateView.swift in Sources */,
FF4CBFC12C996C0600151637 /* ClubRowView.swift in Sources */,
FF4CBFC22C996C0600151637 /* ClubDetailView.swift in Sources */,
FF4CBFC32C996C0600151637 /* GroupStageCallingView.swift in Sources */,
FF4CBFC42C996C0600151637 /* Key.swift in Sources */,
FF4CBFC52C996C0600151637 /* CashierSettingsView.swift in Sources */,
FF4CBFC62C996C0600151637 /* LoserRoundScheduleEditorView.swift in Sources */,
FF4CBFC72C996C0600151637 /* Club.swift in Sources */,
FF4CBFC82C996C0600151637 /* Array+Extensions.swift in Sources */,
FF4CBFC92C996C0600151637 /* ToolboxView.swift in Sources */,
FF4CBFCA2C996C0600151637 /* Alphabet.swift in Sources */,
FF4CBFCB2C996C0600151637 /* String+Extensions.swift in Sources */,
FF4CBFCC2C996C0600151637 /* GroupStageSettingsView.swift in Sources */,
FF4CBFCD2C996C0600151637 /* TournamentGeneralSettingsView.swift in Sources */,
FF4CBFCE2C996C0600151637 /* LoserRoundView.swift in Sources */,
FF4CBFCF2C996C0600151637 /* AddTeamView.swift in Sources */,
FF4CBFD02C996C0600151637 /* PlayerPayView.swift in Sources */,
FF4CBFD12C996C0600151637 /* PlanningByCourtView.swift in Sources */,
FF4CBFD22C996C0600151637 /* FileImportManager.swift in Sources */,
FF4CBFD32C996C0600151637 /* TournamentButtonView.swift in Sources */,
FF4CBFD42C996C0600151637 /* FederalPlayer.swift in Sources */,
FF4CBFD52C996C0600151637 /* NavigationViewModel.swift in Sources */,
FF4CBFD62C996C0600151637 /* FixedWidthInteger+Extensions.swift in Sources */,
FF4CBFD72C996C0600151637 /* LoserBracketFromGroupStageView.swift in Sources */,
FF4CBFD82C996C0600151637 /* ImportedPlayer+Extensions.swift in Sources */,
FF4CBFD92C996C0600151637 /* ClubSearchView.swift in Sources */,
FF4CBFDA2C996C0600151637 /* PlayerPopoverView.swift in Sources */,
FF4CBFDB2C996C0600151637 /* InscriptionManagerView.swift in Sources */,
FF4CBFDC2C996C0600151637 /* ActivityView.swift in Sources */,
FF4CBFDD2C996C0600151637 /* MySortDescriptor.swift in Sources */,
FF4CBFDE2C996C0600151637 /* CalendarView.swift in Sources */,
FF4CBFDF2C996C0600151637 /* FederalTournamentSearchScope.swift in Sources */,
FF4CBFE02C996C0600151637 /* TournamentFieldsManagerView.swift in Sources */,
FF4CBFE12C996C0600151637 /* PrintSettingsView.swift in Sources */,
FF4CBFE22C996C0600151637 /* TournamentMatchFormatsSettingsView.swift in Sources */,
FF4CBFE32C996C0600151637 /* DateUpdateManagerView.swift in Sources */,
FF4CBFE42C996C0600151637 /* MatchTypeSmallSelectionView.swift in Sources */,
FF4CBFE52C996C0600151637 /* MonthData.swift in Sources */,
FF4CBFE62C996C0600151637 /* MenuWarningView.swift in Sources */,
FF4CBFE72C996C0600151637 /* TournamentBuildView.swift in Sources */,
FF4CBFE82C996C0600151637 /* TeamPickerView.swift in Sources */,
FF4CBFE92C996C0600151637 /* CloudConvert.swift in Sources */,
FF4CBFEA2C996C0600151637 /* EventTournamentsView.swift in Sources */,
FF4CBFEB2C996C0600151637 /* DisplayContext.swift in Sources */,
FF4CBFEC2C996C0600151637 /* TournamentCallView.swift in Sources */,
FF4CBFED2C996C0600151637 /* LoserRoundsView.swift in Sources */,
FF4CBFEE2C996C0600151637 /* GroupStagesView.swift in Sources */,
FF4CBFEF2C996C0600151637 /* PadelClubView.swift in Sources */,
FF4CBFF02C996C0600151637 /* URLs.swift in Sources */,
FF4CBFF12C996C0600151637 /* MatchDescriptor.swift in Sources */,
FF4CBFF22C996C0600151637 /* TournamentFormatSelectionView.swift in Sources */,
FF4CBFF32C996C0600151637 /* MatchListView.swift in Sources */,
FF4CBFF42C996C0600151637 /* PadelClubApp.swift in Sources */,
FF4CBFF52C996C0600151637 /* TournamentSettingsView.swift in Sources */,
FF4CBFF62C996C0600151637 /* String+Crypto.swift in Sources */,
FF4CBFF72C996C0600151637 /* GroupStageTeamReplacementView.swift in Sources */,
FF4CBFF82C996C0600151637 /* TabItemModifier.swift in Sources */,
FF4CBFF92C996C0600151637 /* DeferredViewModifier.swift in Sources */,
FF4CBFFA2C996C0600151637 /* TournamentScheduleView.swift in Sources */,
FF4CBFFB2C996C0600151637 /* MatchFormatStorageView.swift in Sources */,
FF4CBFFC2C996C0600151637 /* UmpireView.swift in Sources */,
FF4CBFFD2C996C0600151637 /* User.swift in Sources */,
FF4CBFFE2C996C0600151637 /* MatchSummaryView.swift in Sources */,
FF4CBFFF2C996C0600151637 /* TournamentDurationManagerView.swift in Sources */,
FF4CC0002C996C0600151637 /* MockData.swift in Sources */,
FF4CC0012C996C0600151637 /* TeamDetailView.swift in Sources */,
FF4CC0022C996C0600151637 /* GroupStagesSettingsView.swift in Sources */,
FF4CC0032C996C0600151637 /* TournamentFilterView.swift in Sources */,
FF4CC0042C996C0600151637 /* HtmlGenerator.swift in Sources */,
FF4CC0052C996C0600151637 /* PadelRule.swift in Sources */,
FF4CC0062C996C0600151637 /* TeamRegistration.swift in Sources */,
FF4CC0072C996C0600151637 /* Date+Extensions.swift in Sources */,
FF4CC0082C996C0600151637 /* GroupStageView.swift in Sources */,
FF4CC0092C996C0600151637 /* Tips.swift in Sources */,
FF4CC00A2C996C0600151637 /* MainView.swift in Sources */,
FF4CC00B2C996C0600151637 /* MatchDateView.swift in Sources */,
FF4CC00C2C996C0600151637 /* PlanningSettingsView.swift in Sources */,
FF4CC00D2C996C0600151637 /* MatchScheduleEditorView.swift in Sources */,
FF4CC00E2C996C0600151637 /* FortuneWheelView.swift in Sources */,
FF4CC00F2C996C0600151637 /* URL+Extensions.swift in Sources */,
FF4CC0102C996C0600151637 /* LoadingViewModifier.swift in Sources */,
FF4CC0112C996C0600151637 /* PlayerView.swift in Sources */,
FF4CC0122C996C0600151637 /* MatchDetailView.swift in Sources */,
FF4CC0132C996C0600151637 /* ExportFormat.swift in Sources */,
FF4CC0142C996C0600151637 /* PlayerBlockView.swift in Sources */,
FF4CC0152C996C0600151637 /* StoreItem.swift in Sources */,
FF4CC0162C996C0600151637 /* Selectable.swift in Sources */,
FF4CC0172C996C0600151637 /* PointView.swift in Sources */,
FF4CC0182C996C0600151637 /* ClubHolder.swift in Sources */,
FF4CC0192C996C0600151637 /* EventSettingsView.swift in Sources */,
FF4CC01A2C996C0600151637 /* InscriptionInfoView.swift in Sources */,
FF4CC01B2C996C0600151637 /* SelectablePlayerListView.swift in Sources */,
FF4CC01C2C996C0600151637 /* MatchFormatPickerView.swift in Sources */,
FF4CC01D2C996C0600151637 /* TournamentRankView.swift in Sources */,
FF4CC01E2C996C0600151637 /* NumberFormatter+Extensions.swift in Sources */,
FF4CC01F2C996C0600151637 /* SetLabelView.swift in Sources */,
FF4CC0202C996C0600151637 /* DebugSettingsView.swift in Sources */,
FF4CC0212C996C0600151637 /* GroupStageScheduleEditorView.swift in Sources */,
FF4CC0222C996C0600151637 /* EventView.swift in Sources */,
FF4CC0232C996C0600151637 /* LoginView.swift in Sources */,
FF4CC0242C996C0600151637 /* Court.swift in Sources */,
FF4CC0252C996C0600151637 /* Labels.swift in Sources */,
FF4CC0262C996C0600151637 /* CopyPasteButtonView.swift in Sources */,
FF4CC0272C996C0600151637 /* PlayerSexPickerView.swift in Sources */,
FF4CC0282C996C0600151637 /* TournamentInscriptionView.swift in Sources */,
FF4CC0292C996C0600151637 /* CallSettingsView.swift in Sources */,
FF4CC02A2C996C0600151637 /* FooterButtonView.swift in Sources */,
FF4CC02B2C996C0600151637 /* RankCalculatorView.swift in Sources */,
FF4CC02C2C996C0600151637 /* DateBoxView.swift in Sources */,
FF4CC02D2C996C0600151637 /* LearnMoreSheetView.swift in Sources */,
FF4CC02E2C996C0600151637 /* SourceFileManager.swift in Sources */,
FF4CC02F2C996C0600151637 /* PListReader.swift in Sources */,
FF4CC0302C996C0600151637 /* UpdateSourceRankDateView.swift in Sources */,
FF4CC0312C996C0600151637 /* GlobalSettingsView.swift in Sources */,
FF4CC0322C996C0600151637 /* Guard.swift in Sources */,
FF4CC0332C996C0600151637 /* PurchaseListView.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
FF70FAC22C90584900129CC2 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
@ -2552,7 +3132,7 @@
CODE_SIGN_ENTITLEMENTS = PadelClub/PadelClub.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 5;
CURRENT_PROJECT_VERSION = 3;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEFINES_MODULE = YES;
DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\"";
@ -2562,6 +3142,7 @@
INFOPLIST_KEY_CFBundleDisplayName = "Padel Club";
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.sports";
INFOPLIST_KEY_LSSupportsOpeningDocumentsInPlace = YES;
INFOPLIST_KEY_NSCalendarsUsageDescription = "Padel Club a besoin d'avoir accès à votre calendrier pour pouvoir y inscrire ce tournoi";
INFOPLIST_KEY_NSCameraUsageDescription = "En autorisant l'application à utiliser la caméra, vous pourrez prendre des photos des rencontres";
INFOPLIST_KEY_NSLocationWhenInUseUsageDescription = "Padel Club a besoin de votre position pour rechercher les clubs autour de vous.";
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
@ -2574,13 +3155,14 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.0.8;
MARKETING_VERSION = 1.0.9;
PRODUCT_BUNDLE_IDENTIFIER = app.padelclub;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
SUPPORTS_MACCATALYST = NO;
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
@ -2594,7 +3176,7 @@
CODE_SIGN_ENTITLEMENTS = PadelClub/PadelClub.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 5;
CURRENT_PROJECT_VERSION = 3;
DEFINES_MODULE = YES;
DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\"";
DEVELOPMENT_TEAM = BQ3Y44M3Q6;
@ -2603,6 +3185,7 @@
INFOPLIST_KEY_CFBundleDisplayName = "Padel Club";
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.sports";
INFOPLIST_KEY_LSSupportsOpeningDocumentsInPlace = YES;
INFOPLIST_KEY_NSCalendarsUsageDescription = "Padel Club a besoin d'avoir accès à votre calendrier pour pouvoir y inscrire ce tournoi";
INFOPLIST_KEY_NSCameraUsageDescription = "En autorisant l'application à utiliser la caméra, vous pourrez prendre des photos des rencontres";
INFOPLIST_KEY_NSLocationWhenInUseUsageDescription = "Padel Club a besoin de votre position pour rechercher les clubs autour de vous.";
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
@ -2615,13 +3198,14 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.0.8;
MARKETING_VERSION = 1.0.9;
PRODUCT_BUNDLE_IDENTIFIER = app.padelclub;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
SUPPORTS_MACCATALYST = NO;
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
@ -2699,6 +3283,90 @@
};
name = Release;
};
FF4CC04E2C996C0600151637 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = PadelClub/PadelClub.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 3;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEFINES_MODULE = YES;
DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\"";
DEVELOPMENT_TEAM = BQ3Y44M3Q6;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = PadelClub/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = "Padel Club (ProdTest)";
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.sports";
INFOPLIST_KEY_LSSupportsOpeningDocumentsInPlace = YES;
INFOPLIST_KEY_NSCameraUsageDescription = "En autorisant l'application à utiliser la caméra, vous pourrez prendre des photos des rencontres";
INFOPLIST_KEY_NSLocationWhenInUseUsageDescription = "Padel Club a besoin de votre position pour rechercher les clubs autour de vous.";
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
INFOPLIST_KEY_UILaunchStoryboardName = "Launch Screen";
INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait;
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
IPHONEOS_DEPLOYMENT_TARGET = 17.1;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.0.9;
PRODUCT_BUNDLE_IDENTIFIER = app.padelclub;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
SUPPORTS_MACCATALYST = NO;
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
FF4CC04F2C996C0600151637 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = PadelClub/PadelClub.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 3;
DEFINES_MODULE = YES;
DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\"";
DEVELOPMENT_TEAM = BQ3Y44M3Q6;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = PadelClub/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = "Padel Club (ProdTest)";
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.sports";
INFOPLIST_KEY_LSSupportsOpeningDocumentsInPlace = YES;
INFOPLIST_KEY_NSCameraUsageDescription = "En autorisant l'application à utiliser la caméra, vous pourrez prendre des photos des rencontres";
INFOPLIST_KEY_NSLocationWhenInUseUsageDescription = "Padel Club a besoin de votre position pour rechercher les clubs autour de vous.";
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
INFOPLIST_KEY_UILaunchStoryboardName = "Launch Screen";
INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait;
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
IPHONEOS_DEPLOYMENT_TARGET = 17.1;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.0.9;
PRODUCT_BUNDLE_IDENTIFIER = app.padelclub;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
SUPPORTS_MACCATALYST = NO;
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = PRODTEST;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Release;
};
FF70FBCD2C90584900129CC2 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
@ -2707,7 +3375,7 @@
CODE_SIGN_ENTITLEMENTS = PadelClub/PadelClub.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 4;
CURRENT_PROJECT_VERSION = 3;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEFINES_MODULE = YES;
DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\"";
@ -2729,7 +3397,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.0.8;
MARKETING_VERSION = 1.0.9;
PRODUCT_BUNDLE_IDENTIFIER = app.padelclub.beta;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
@ -2749,7 +3417,7 @@
CODE_SIGN_ENTITLEMENTS = PadelClub/PadelClub.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 4;
CURRENT_PROJECT_VERSION = 3;
DEFINES_MODULE = YES;
DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\"";
DEVELOPMENT_TEAM = BQ3Y44M3Q6;
@ -2770,7 +3438,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.0.8;
MARKETING_VERSION = 1.0.9;
PRODUCT_BUNDLE_IDENTIFIER = app.padelclub.beta;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
@ -2822,6 +3490,15 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
FF4CC04D2C996C0600151637 /* Build configuration list for PBXNativeTarget "ProdTest PadelClub" */ = {
isa = XCConfigurationList;
buildConfigurations = (
FF4CC04E2C996C0600151637 /* Debug */,
FF4CC04F2C996C0600151637 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
FF70FBCC2C90584900129CC2 /* Build configuration list for PBXNativeTarget "PadelClub TestFlight" */ = {
isa = XCConfigurationList;
buildConfigurations = (
@ -2842,6 +3519,22 @@
minimumVersion = 1.2.0;
};
};
FF4CBF402C996C0600151637 /* XCRemoteSwiftPackageReference "swift-algorithms" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/apple/swift-algorithms.git";
requirement = {
kind = upToNextMajorVersion;
minimumVersion = 1.2.0;
};
};
FF4CBF422C996C0600151637 /* XCRemoteSwiftPackageReference "Zip" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/marmelroy/Zip";
requirement = {
kind = upToNextMajorVersion;
minimumVersion = 2.1.2;
};
};
FF70FABF2C90584900129CC2 /* XCRemoteSwiftPackageReference "swift-algorithms" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/apple/swift-algorithms.git";
@ -2869,6 +3562,16 @@
/* End XCRemoteSwiftPackageReference section */
/* Begin XCSwiftPackageProductDependency section */
FF4CBF3F2C996C0600151637 /* Algorithms */ = {
isa = XCSwiftPackageProductDependency;
package = FF4CBF402C996C0600151637 /* XCRemoteSwiftPackageReference "swift-algorithms" */;
productName = Algorithms;
};
FF4CBF412C996C0600151637 /* Zip */ = {
isa = XCSwiftPackageProductDependency;
package = FF4CBF422C996C0600151637 /* XCRemoteSwiftPackageReference "Zip" */;
productName = Zip;
};
FF70FABE2C90584900129CC2 /* Algorithms */ = {
isa = XCSwiftPackageProductDependency;
package = FF70FABF2C90584900129CC2 /* XCRemoteSwiftPackageReference "swift-algorithms" */;

@ -0,0 +1,78 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1600"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES"
buildArchitectures = "Automatic">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "FF4CBF3E2C996C0600151637"
BuildableName = "ProdTest PadelClub.app"
BlueprintName = "ProdTest PadelClub"
ReferencedContainer = "container:PadelClub.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
shouldAutocreateTestPlan = "YES">
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "FF4CBF3E2C996C0600151637"
BuildableName = "ProdTest PadelClub.app"
BlueprintName = "ProdTest PadelClub"
ReferencedContainer = "container:PadelClub.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "FF4CBF3E2C996C0600151637"
BuildableName = "ProdTest PadelClub.app"
BlueprintName = "ProdTest PadelClub"
ReferencedContainer = "container:PadelClub.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

@ -16,8 +16,48 @@ final class AppSettings: MicroStorable {
var didCreateAccount: Bool = false
var didRegisterAccount: Bool = false
//search tournament stuff
var tournamentCategories: Set<TournamentCategory.ID>
var tournamentLevels: Set<TournamentLevel.ID>
var tournamentAges: Set<FederalTournamentAge.ID>
var tournamentTypes: Set<FederalTournamentType.ID>
var startDate: Date
var endDate: Date
var city: String
var distance: Double
var sortingOption: String
var nationalCup: Bool
var dayDuration: Int?
var dayPeriod: DayPeriod
func resetSearch() {
tournamentAges = Set()
tournamentTypes = Set()
tournamentLevels = Set()
tournamentCategories = Set()
city = ""
distance = 30
startDate = Date()
endDate = Calendar.current.date(byAdding: .month, value: 3, to: Date())!
sortingOption = "dateDebut+asc"
nationalCup = false
dayDuration = nil
dayPeriod = .all
}
required init() {
tournamentAges = Set()
tournamentTypes = Set()
tournamentLevels = Set()
tournamentCategories = Set()
city = ""
distance = 30
startDate = Date()
endDate = Calendar.current.date(byAdding: .month, value: 3, to: Date())!
sortingOption = "dateDebut+asc"
nationalCup = false
dayDuration = nil
dayPeriod = .all
}
required init(from decoder: Decoder) throws {
@ -25,11 +65,35 @@ final class AppSettings: MicroStorable {
lastDataSource = try container.decodeIfPresent(String.self, forKey: ._lastDataSource)
didCreateAccount = try container.decodeIfPresent(Bool.self, forKey: ._didCreateAccount) ?? false
didRegisterAccount = try container.decodeIfPresent(Bool.self, forKey: ._didRegisterAccount) ?? false
tournamentCategories = try container.decodeIfPresent(Set<TournamentCategory.ID>.self, forKey: ._tournamentCategories) ?? Set()
tournamentLevels = try container.decodeIfPresent(Set<TournamentLevel.ID>.self, forKey: ._tournamentLevels) ?? Set()
tournamentAges = try container.decodeIfPresent(Set<FederalTournamentAge.ID>.self, forKey: ._tournamentAges) ?? Set()
tournamentTypes = try container.decodeIfPresent(Set<FederalTournamentType.ID>.self, forKey: ._tournamentTypes) ?? Set()
startDate = try container.decodeIfPresent(Date.self, forKey: ._startDate) ?? Date()
endDate = try container.decodeIfPresent(Date.self, forKey: ._endDate) ?? Calendar.current.date(byAdding: .month, value: 3, to: Date())!
city = try container.decodeIfPresent(String.self, forKey: ._city) ?? ""
distance = try container.decodeIfPresent(Double.self, forKey: ._distance) ?? 30
sortingOption = try container.decodeIfPresent(String.self, forKey: ._sortingOption) ?? "dateDebut+asc"
nationalCup = try container.decodeIfPresent(Bool.self, forKey: ._nationalCup) ?? false
dayDuration = try container.decodeIfPresent(Int.self, forKey: ._dayDuration)
dayPeriod = try container.decodeIfPresent(DayPeriod.self, forKey: ._dayPeriod) ?? .all
}
enum CodingKeys: String, CodingKey {
case _lastDataSource = "lastDataSource"
case _didCreateAccount = "didCreateAccount"
case _didRegisterAccount = "didRegisterAccount"
case _tournamentCategories = "tournamentCategories"
case _tournamentLevels = "tournamentLevels"
case _tournamentAges = "tournamentAges"
case _tournamentTypes = "tournamentTypes"
case _startDate = "startDate"
case _endDate = "endDate"
case _city = "city"
case _distance = "distance"
case _sortingOption = "sortingOption"
case _nationalCup = "nationalCup"
case _dayDuration = "dayDuration"
case _dayPeriod = "dayPeriod"
}
}

@ -70,6 +70,10 @@ extension ImportedPlayer: PlayerHolder {
}
}
func contains(_ searchField: String) -> Bool {
firstName?.localizedCaseInsensitiveContains(searchField) == true || lastName?.localizedCaseInsensitiveContains(searchField) == true
}
func hitForSearch(_ searchText: String) -> Int {
var trimmedSearchText = searchText.lowercased().trimmingCharacters(in: .whitespaces).folding(options: .diacriticInsensitive, locale: .current)
trimmedSearchText = trimmedSearchText.replaceCharactersFromSet(characterSet: .punctuationCharacters, replacementString: " ")

@ -7,8 +7,8 @@ import Foundation
import CoreLocation
import LeStorage
enum DayPeriod: CaseIterable, Identifiable {
var id: Self { self }
enum DayPeriod: Int, CaseIterable, Identifiable, Codable {
var id: Int { self.rawValue }
case all
case weekend
@ -151,6 +151,19 @@ struct FederalTournament: Identifiable, Codable {
[libelle, dateDebut?.formatted(date: .complete, time: .omitted)].compactMap({$0}).joined(separator: "\n") + "\n"
}
var sharePartnerMessage: String {
["Je nous ai inscris au tournoi suivant : ",
libelle,
dateDebut?.formatted(date: .complete, time: .omitted),
"message preparé par Padel Club",
URLs.appStore.rawValue
].compactMap({$0}).joined(separator: "\n") + "\n"
}
func calendarNoteMessage() -> String {
[jugeArbitre?.nom, jugeArbitre?.prenom, courrielEngagement, installation?.telephone].compactMap({$0}).joined(separator: "\n")
}
var japMessage: String {
[nomClub, jugeArbitre?.nom, jugeArbitre?.prenom, courrielEngagement, installation?.telephone].compactMap({$0}).joined(separator: ";")
}

@ -93,9 +93,12 @@ final class MatchScheduler : ModelObject, Storable {
}
@discardableResult
func updateGroupStageSchedule(tournament: Tournament) -> Date {
func updateGroupStageSchedule(tournament: Tournament, specificGroupStage: GroupStage? = nil) -> Date {
let computedGroupStageChunkCount = groupStageChunkCount ?? tournament.getGroupStageChunkValue()
let groupStages: [GroupStage] = tournament.groupStages()
var groupStages: [GroupStage] = tournament.groupStages()
if let specificGroupStage {
groupStages = [specificGroupStage]
}
let numberOfCourtsAvailablePerRotation: Int = tournament.courtCount
let matches = groupStages.flatMap { $0._matches() }

@ -160,8 +160,8 @@ final class PlayerRegistration: ModelObject, Storable {
}
func isSameAs(_ player: PlayerRegistration) -> Bool {
firstName.localizedCaseInsensitiveCompare(player.firstName) == .orderedSame &&
lastName.localizedCaseInsensitiveCompare(player.lastName) == .orderedSame
firstName.trimmedMultiline.localizedCaseInsensitiveCompare(player.firstName.trimmedMultiline) == .orderedSame &&
lastName.trimmedMultiline.localizedCaseInsensitiveCompare(player.lastName.trimmedMultiline) == .orderedSame
}
func tournament() -> Tournament? {

@ -23,14 +23,16 @@ final class Round: ModelObject, Storable {
private(set) var format: MatchFormat?
var startDate: Date?
var groupStageLoserBracket: Bool = false
internal init(tournament: String, index: Int, parent: String? = nil, matchFormat: MatchFormat? = nil, startDate: Date? = nil, groupStageLoserBracket: Bool = false) {
var loserBracketMode: LoserBracketMode = .automatic
internal init(tournament: String, index: Int, parent: String? = nil, matchFormat: MatchFormat? = nil, startDate: Date? = nil, groupStageLoserBracket: Bool = false, loserBracketMode: LoserBracketMode = .automatic) {
self.tournament = tournament
self.index = index
self.parent = parent
self.format = matchFormat
self.startDate = startDate
self.groupStageLoserBracket = groupStageLoserBracket
self.loserBracketMode = loserBracketMode
}
// MARK: - Computed dependencies
@ -153,7 +155,12 @@ final class Round: ModelObject, Storable {
let teamIds: [String] = self._matches().compactMap { $0.losingTeamId }
return teamIds.compactMap { self.tournamentStore.teamRegistrations.findById($0) }
}
func winners() -> [TeamRegistration] {
let teamIds: [String] = self._matches().compactMap { $0.winningTeamId }
return teamIds.compactMap { self.tournamentStore.teamRegistrations.findById($0) }
}
func teams() -> [TeamRegistration] {
return playedMatches().flatMap({ $0.teams() })
}
@ -186,13 +193,13 @@ defer {
case .two:
if let luckyLoser = match.teamScores.first(where: { $0.luckyLoser == match.index * 2 + 1 }) {
return luckyLoser.team
} else if groupStageLoserBracket == false, let previousMatch = bottomPreviousRoundMatch(ofMatch: match, previousRound: previousRound) {
} else if let previousMatch = bottomPreviousRoundMatch(ofMatch: match, previousRound: previousRound) {
if let teamId = previousMatch.winningTeamId {
return self.tournamentStore.teamRegistrations.findById(teamId)
} else if previousMatch.disabled {
return previousMatch.teams().first
}
} else if groupStageLoserBracket == false, let parent = upperBracketBottomMatch(ofMatchIndex: match.index, previousRound: previousRound)?.losingTeamId {
} else if let parent = upperBracketBottomMatch(ofMatchIndex: match.index, previousRound: previousRound)?.losingTeamId {
return tournamentStore.findById(parent)
}
}
@ -208,8 +215,13 @@ defer {
print("func upperBracketTopMatch", matchIndex, duration.formatted(.units(allowed: [.seconds, .milliseconds])))
}
#endif
let parentRound = parentRound
if let parentRound, parentRound.parent == nil, groupStageLoserBracket == false, parentRound.loserBracketMode != .automatic {
return nil
}
let indexInRound = RoundRule.matchIndexWithinRound(fromMatchIndex: matchIndex)
if isLoserBracket(), previousRound == nil, let parentRound = parentRound, let upperBracketTopMatch = parentRound.getMatch(atMatchIndexInRound: indexInRound * 2) {
if isLoserBracket(), previousRound == nil, let upperBracketTopMatch = parentRound?.getMatch(atMatchIndexInRound: indexInRound * 2) {
return upperBracketTopMatch
}
return nil
@ -224,8 +236,13 @@ defer {
}
#endif
let parentRound = parentRound
if let parentRound, parentRound.parent == nil, groupStageLoserBracket == false, parentRound.loserBracketMode != .automatic {
return nil
}
let indexInRound = RoundRule.matchIndexWithinRound(fromMatchIndex: matchIndex)
if isLoserBracket(), previousRound == nil, let parentRound = parentRound, let upperBracketBottomMatch = parentRound.getMatch(atMatchIndexInRound: indexInRound * 2 + 1) {
if isLoserBracket(), previousRound == nil, let upperBracketBottomMatch = parentRound?.getMatch(atMatchIndexInRound: indexInRound * 2 + 1) {
return upperBracketBottomMatch
}
return nil
@ -420,6 +437,10 @@ defer {
return _matches().filter({ $0.disabled })
}
func allLoserRoundMatches() -> [Match] {
loserRoundsAndChildren().flatMap({ $0._matches() })
}
var theoryCumulativeMatchCount: Int {
var totalMatches = RoundRule.numberOfMatches(forRoundIndex: index)
if let parentRound {
@ -576,7 +597,11 @@ defer {
func deleteLoserBracket() {
do {
try self.tournamentStore.rounds.delete(contentOfs: loserRounds())
let loserRounds = loserRounds()
for loserRound in loserRounds {
try loserRound.deleteDependencies()
}
try self.tournamentStore.rounds.delete(contentOfs: loserRounds)
} catch {
Logger.error(error)
}
@ -681,6 +706,7 @@ defer {
case _format = "format"
case _startDate = "startDate"
case _groupStageLoserBracket = "groupStageLoserBracket"
case _loserBracketMode = "loserBracketMode"
}
required init(from decoder: Decoder) throws {
@ -692,6 +718,7 @@ defer {
format = try container.decodeIfPresent(MatchFormat.self, forKey: ._format)
startDate = try container.decodeIfPresent(Date.self, forKey: ._startDate)
groupStageLoserBracket = try container.decodeIfPresent(Bool.self, forKey: ._groupStageLoserBracket) ?? false
loserBracketMode = try container.decodeIfPresent(LoserBracketMode.self, forKey: ._loserBracketMode) ?? .automatic
}
func encode(to encoder: Encoder) throws {
@ -701,7 +728,8 @@ defer {
try container.encode(tournament, forKey: ._tournament)
try container.encode(index, forKey: ._index)
try container.encode(groupStageLoserBracket, forKey: ._groupStageLoserBracket)
try container.encode(loserBracketMode, forKey: ._loserBracketMode)
if let parent = parent {
try container.encode(parent, forKey: ._parent)
} else {
@ -776,3 +804,29 @@ extension Round: Selectable, Equatable {
return hasEnded() ? .checkmark : nil
}
}
enum LoserBracketMode: Int, CaseIterable, Identifiable, Codable {
var id: Int { self.rawValue }
case automatic
case manual
func localizedLoserBracketMode() -> String {
switch self {
case .automatic:
"Automatique"
case .manual:
"Manuelle"
}
}
func localizedLoserBracketModeDescription() -> String {
switch self {
case .automatic:
"Les perdants du tableau principal sont placés à leur place prévue."
case .manual:
"Aucun placement automatique n'est fait. Vous devez choisir les perdants qui se jouent."
}
}
}

@ -57,7 +57,8 @@ final class Tournament : ModelObject, Storable {
var publishTournament: Bool = false
var hidePointsEarned: Bool = false
var publishRankings: Bool = false
var loserBracketMode: LoserBracketMode = .automatic
@ObservationIgnored
var navigationPath: [Screen] = []
@ -105,9 +106,10 @@ final class Tournament : ModelObject, Storable {
case _publishTournament = "publishTournament"
case _hidePointsEarned = "hidePointsEarned"
case _publishRankings = "publishRankings"
case _loserBracketMode = "loserBracketMode"
}
internal init(event: String? = nil, name: String? = nil, startDate: Date = Date(), endDate: Date? = nil, creationDate: Date = Date(), isPrivate: Bool = false, groupStageFormat: MatchFormat? = nil, roundFormat: MatchFormat? = nil, loserRoundFormat: MatchFormat? = nil, groupStageSortMode: GroupStageOrderingMode, groupStageCount: Int = 4, rankSourceDate: Date? = nil, dayDuration: Int = 1, teamCount: Int = 24, teamSorting: TeamSortingType? = nil, federalCategory: TournamentCategory, federalLevelCategory: TournamentLevel, federalAgeCategory: FederalTournamentAge, closedRegistrationDate: Date? = nil, groupStageAdditionalQualified: Int = 0, courtCount: Int = 2, prioritizeClubMembers: Bool = false, qualifiedPerGroupStage: Int = 1, teamsPerGroupStage: Int = 4, entryFee: Double? = nil, additionalEstimationDuration: Int = 0, isDeleted: Bool = false, publishTeams: Bool = false, publishSummons: Bool = false, publishGroupStages: Bool = false, publishBrackets: Bool = false, shouldVerifyBracket: Bool = false, shouldVerifyGroupStage: Bool = false, hideTeamsWeight: Bool = false, publishTournament: Bool = false, hidePointsEarned: Bool = false, publishRankings: Bool = false) {
internal init(event: String? = nil, name: String? = nil, startDate: Date = Date(), endDate: Date? = nil, creationDate: Date = Date(), isPrivate: Bool = false, groupStageFormat: MatchFormat? = nil, roundFormat: MatchFormat? = nil, loserRoundFormat: MatchFormat? = nil, groupStageSortMode: GroupStageOrderingMode, groupStageCount: Int = 4, rankSourceDate: Date? = nil, dayDuration: Int = 1, teamCount: Int = 24, teamSorting: TeamSortingType? = nil, federalCategory: TournamentCategory, federalLevelCategory: TournamentLevel, federalAgeCategory: FederalTournamentAge, closedRegistrationDate: Date? = nil, groupStageAdditionalQualified: Int = 0, courtCount: Int = 2, prioritizeClubMembers: Bool = false, qualifiedPerGroupStage: Int = 1, teamsPerGroupStage: Int = 4, entryFee: Double? = nil, additionalEstimationDuration: Int = 0, isDeleted: Bool = false, publishTeams: Bool = false, publishSummons: Bool = false, publishGroupStages: Bool = false, publishBrackets: Bool = false, shouldVerifyBracket: Bool = false, shouldVerifyGroupStage: Bool = false, hideTeamsWeight: Bool = false, publishTournament: Bool = false, hidePointsEarned: Bool = false, publishRankings: Bool = false, loserBracketMode: LoserBracketMode = .automatic) {
self.event = event
self.name = name
self.startDate = startDate
@ -145,6 +147,7 @@ final class Tournament : ModelObject, Storable {
self.publishTournament = publishTournament
self.hidePointsEarned = hidePointsEarned
self.publishRankings = publishRankings
self.loserBracketMode = loserBracketMode
}
required init(from decoder: Decoder) throws {
@ -189,6 +192,7 @@ final class Tournament : ModelObject, Storable {
publishTournament = try container.decodeIfPresent(Bool.self, forKey: ._publishTournament) ?? false
hidePointsEarned = try container.decodeIfPresent(Bool.self, forKey: ._hidePointsEarned) ?? false
publishRankings = try container.decodeIfPresent(Bool.self, forKey: ._publishRankings) ?? false
loserBracketMode = try container.decodeIfPresent(LoserBracketMode.self, forKey: ._loserBracketMode) ?? .automatic
}
fileprivate static let _numberFormatter: NumberFormatter = NumberFormatter()
@ -1633,7 +1637,7 @@ defer {
let roundCount = RoundRule.numberOfRounds(forTeams: bracketTeamCount())
let rounds = (0..<roundCount).map { //index 0 is the final
return Round(tournament: id, index: $0, matchFormat: roundSmartMatchFormat($0))
return Round(tournament: id, index: $0, matchFormat: roundSmartMatchFormat($0), loserBracketMode: loserBracketMode)
}
do {
@ -2181,7 +2185,7 @@ extension Tournament {
let tournamentCategory = TournamentCategory.mostUsed(inTournaments: tournaments)
let federalTournamentAge = FederalTournamentAge.mostUsed(inTournaments: tournaments)
//creator: DataStore.shared.user?.id
return Tournament(groupStageSortMode: .snake, rankSourceDate: rankSourceDate, teamSorting: tournamentLevel.defaultTeamSortingType, federalCategory: tournamentCategory, federalLevelCategory: tournamentLevel, federalAgeCategory: federalTournamentAge)
return Tournament(groupStageSortMode: .snake, rankSourceDate: rankSourceDate, teamSorting: tournamentLevel.defaultTeamSortingType, federalCategory: tournamentCategory, federalLevelCategory: tournamentLevel, federalAgeCategory: federalTournamentAge, loserBracketMode: DataStore.shared.user.loserBracketMode)
}
static func fake() -> Tournament {
@ -2189,3 +2193,4 @@ extension Tournament {
}
}

@ -43,16 +43,18 @@ class User: ModelObject, UserBase, Storable {
var bracketMatchFormatPreference: MatchFormat?
var groupStageMatchFormatPreference: MatchFormat?
var loserBracketMatchFormatPreference: MatchFormat?
var loserBracketMode: LoserBracketMode = .automatic
var deviceId: String?
init(username: String, email: String, firstName: String, lastName: String, phone: String?, country: String?) {
init(username: String, email: String, firstName: String, lastName: String, phone: String?, country: String?, loserBracketMode: LoserBracketMode = .automatic) {
self.username = username
self.firstName = firstName
self.lastName = lastName
self.email = email
self.phone = phone
self.country = country
self.loserBracketMode = loserBracketMode
}
public func uuid() throws -> UUID {
@ -139,8 +141,42 @@ class User: ModelObject, UserBase, Storable {
case _groupStageMatchFormatPreference = "groupStageMatchFormatPreference"
case _loserBracketMatchFormatPreference = "loserBracketMatchFormatPreference"
case _deviceId = "deviceId"
case _loserBracketMode = "loserBracketMode"
}
public required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
// Required properties
id = try container.decodeIfPresent(String.self, forKey: ._id) ?? Store.randomId()
username = try container.decode(String.self, forKey: ._username)
email = try container.decode(String.self, forKey: ._email)
firstName = try container.decode(String.self, forKey: ._firstName)
lastName = try container.decode(String.self, forKey: ._lastName)
// Optional properties
clubs = try container.decodeIfPresent([String].self, forKey: ._clubs) ?? []
umpireCode = try container.decodeIfPresent(String.self, forKey: ._umpireCode)
licenceId = try container.decodeIfPresent(String.self, forKey: ._licenceId)
phone = try container.decodeIfPresent(String.self, forKey: ._phone)
country = try container.decodeIfPresent(String.self, forKey: ._country)
// Summons-related properties
summonsMessageBody = try container.decodeIfPresent(String.self, forKey: ._summonsMessageBody)
summonsMessageSignature = try container.decodeIfPresent(String.self, forKey: ._summonsMessageSignature)
summonsAvailablePaymentMethods = try container.decodeIfPresent(String.self, forKey: ._summonsAvailablePaymentMethods)
summonsDisplayFormat = try container.decodeIfPresent(Bool.self, forKey: ._summonsDisplayFormat) ?? false
summonsDisplayEntryFee = try container.decodeIfPresent(Bool.self, forKey: ._summonsDisplayEntryFee) ?? false
summonsUseFullCustomMessage = try container.decodeIfPresent(Bool.self, forKey: ._summonsUseFullCustomMessage) ?? false
// Match-related properties
matchFormatsDefaultDuration = try container.decodeIfPresent([MatchFormat: Int].self, forKey: ._matchFormatsDefaultDuration)
bracketMatchFormatPreference = try container.decodeIfPresent(MatchFormat.self, forKey: ._bracketMatchFormatPreference)
groupStageMatchFormatPreference = try container.decodeIfPresent(MatchFormat.self, forKey: ._groupStageMatchFormatPreference)
loserBracketMatchFormatPreference = try container.decodeIfPresent(MatchFormat.self, forKey: ._loserBracketMatchFormatPreference)
loserBracketMode = try container.decodeIfPresent(LoserBracketMode.self, forKey: ._loserBracketMode) ?? .automatic
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
@ -228,6 +264,7 @@ class User: ModelObject, UserBase, Storable {
try container.encodeNil(forKey: ._deviceId)
}
try container.encode(loserBracketMode, forKey: ._loserBracketMode)
}
static func placeHolder() -> User {

@ -52,6 +52,8 @@ struct PadelClubApp: App {
return "\(version) (\(build)) Debug"
#elseif TESTFLIGHT
return "\(version) (\(build)) TestFlight"
#elseif PRODTEST
return "\(version) (\(build)) ProdTest"
#else
return "\(version) (\(build))"
#endif
@ -84,6 +86,8 @@ struct PadelClubApp: App {
print("Running in Debug mode")
#elseif TESTFLIGHT
print("Running in TestFlight mode")
#elseif PRODTEST
print("Running in ProdTest mode")
#else
print("Running in Release mode")
#endif

@ -15,6 +15,9 @@ enum ContactManagerError: LocalizedError {
case mailNotSent //no network no error
case messageFailed
case messageNotSent //no network no error
case calendarAccessDenied
case calendarEventSaveFailed
case noCalendarAvailable
}
enum ContactType: Identifiable {

@ -17,6 +17,10 @@ enum URLs: String, Identifiable {
case activationHost = "xlr.alwaysdata.net"
case main = "https://xlr.alwaysdata.net/"
case api = "https://xlr.alwaysdata.net/roads/"
#elseif PRODTEST
case activationHost = "padelclub.app"
case main = "https://padelclub.app/"
case api = "https://padelclub.app/roads/"
#else
case activationHost = "padelclub.app"
case main = "https://padelclub.app/"

@ -140,7 +140,6 @@ struct ClubSearchView: View {
RowButtonView("D'accord") {
locationManager.lastError = nil
}
.padding(.horizontal)
}
} else if clubMarkers.isEmpty == false && searching == false && _filteredClubs().isEmpty {
ContentUnavailableView.search(text: searchedCity)
@ -172,7 +171,6 @@ struct ClubSearchView: View {
locationManager.requestLocation()
}
}
.padding(.horizontal)
}
if error != nil {
@ -184,13 +182,11 @@ struct ClubSearchView: View {
RowButtonView("Chercher une ville ou un code postal") {
searchPresented = true
}
.padding(.horizontal)
if searchAttempted {
RowButtonView("Créer un club manuellement") {
newClub = club ?? Club.newEmptyInstance()
}
.padding(.horizontal)
}
}
}

@ -159,11 +159,9 @@ struct ClubsView: View {
RowButtonView("Créer un nouveau club", systemImage: "plus.circle.fill") {
newClub = Club.newEmptyInstance()
}
.padding(.horizontal)
RowButtonView("Chercher un club", systemImage: "magnifyingglass.circle.fill") {
presentClubSearchView = true
}
.padding(.horizontal)
}
}

@ -21,7 +21,7 @@ struct LabelStructure: View {
struct LabelSettings: View {
var body: some View {
Label("Réglages", systemImage: "slider.horizontal.3").labelStyle(.titleOnly)
Label("Réglages du tournoi", systemImage: "slider.horizontal.3").labelStyle(.titleOnly)
}
}

@ -81,7 +81,6 @@ struct LoserBracketFromGroupStageView: View {
isEditingLoserBracketGroupStage = true
_addNewMatch()
}
.padding(.horizontal)
}
}
}

@ -63,7 +63,7 @@ struct MatchSetupView: View {
}
HStack {
let luckyLosers = walkOutSpot ? match.luckyLosers() : []
TeamPickerView(shouldConfirm: shouldConfirm, groupStagePosition: nil, matchTypeContext: matchTypeContext, luckyLosers: luckyLosers, teamPicked: { team in
TeamPickerView(shouldConfirm: shouldConfirm, round: match.roundObject, matchTypeContext: matchTypeContext, luckyLosers: luckyLosers, teamPicked: { team in
print(team.pasteData())
if walkOutSpot || team.bracketPosition != nil || matchTypeContext == .loserBracket {
match.setLuckyLoser(team: team, teamPosition: teamPosition)
@ -86,6 +86,7 @@ struct MatchSetupView: View {
}
}
})
.environment(match)
if matchTypeContext == .bracket, let tournament = match.currentTournament() {
let availableQualifiedTeams = tournament.availableQualifiedTeams()
let availableSeedGroups = tournament.availableSeedGroups()

@ -122,7 +122,6 @@ struct ActivityView: View {
RowButtonView("D'accord.") {
self.error = nil
}
.padding(.horizontal)
}
} else if isGatheringFederalTournaments {
ProgressView()
@ -139,11 +138,9 @@ struct ActivityView: View {
FooterButtonView("supprimer vos filtres") {
federalDataViewModel.removeFilters()
}
.padding(.horizontal)
FooterButtonView("modifier vos filtres") {
presentFilterView = true
}
.padding(.horizontal)
}
} else {
_dataEmptyView()
@ -153,8 +150,8 @@ struct ActivityView: View {
}
}
//.searchable(text: $searchText)
.onAppear { presentToolbar = true }
.onDisappear { presentToolbar = false }
// .onAppear { presentToolbar = true }
// .onDisappear { presentToolbar = false }
.refreshable {
if navigation.agendaDestination == .tenup {
federalDataViewModel.federalTournaments.removeAll()
@ -234,7 +231,7 @@ struct ActivityView: View {
}
}
if presentToolbar, tournaments.isEmpty == false, federalDataViewModel.areFiltersEnabled() || navigation.agendaDestination == .around {
if tournaments.isEmpty == false, federalDataViewModel.areFiltersEnabled() || navigation.agendaDestination == .around {
ToolbarItemGroup(placement: .bottomBar) {
VStack(spacing: 0) {
let searchStatus = _searchStatus()
@ -403,13 +400,10 @@ struct ActivityView: View {
RowButtonView("Créer un nouvel événement") {
newTournament = Tournament.newEmptyInstance()
}
.padding(.horizontal)
RowButtonView("Importer via Tenup") {
navigation.agendaDestination = .tenup
}
.padding(.horizontal)
SupportButtonView(contentIsUnavailable: true)
.padding(.horizontal)
}
}

@ -10,6 +10,7 @@ import CoreLocation
import CoreLocationUI
struct TournamentLookUpView: View {
@EnvironmentObject var dataStore: DataStore
@Environment(FederalDataViewModel.self) var federalDataViewModel: FederalDataViewModel
@StateObject var locationManager = LocationManager()
@Environment(\.dismiss) private var dismiss
@ -18,19 +19,8 @@ struct TournamentLookUpView: View {
@State var page: Int = 0
@State var total: Int = 0
@State private var tournamentCategories = Set<TournamentCategory.ID>()
@State private var tournamentLevels = Set<TournamentLevel.ID>()
@State private var tournamentAges = Set<FederalTournamentAge.ID>()
@State private var tournamentTypes = Set<FederalTournamentType.ID>()
@State private var searching: Bool = false
@State private var startDate: Date = Date()
@State private var endDate: Date = Calendar.current.date(byAdding: .month, value: 3, to: Date())!
@AppStorage("lastCity") private var city: String = ""
@State private var ligue: String = ""
@State private var distance: Double = 30
@State private var sortingOption: String = "dateDebut+asc"
@State private var requestedToGetAllPages: Bool = false
@State private var nationalCup: Bool = false
@State private var revealSearchParameters: Bool = true
@State private var presentAlert: Bool = false
@ -61,14 +51,18 @@ struct TournamentLookUpView: View {
presentAlert = false
}
}, message: {
Text("Il y a beacoup de tournois pour cette requête, êtes-vous sûr de vouloir tout récupérer ? Sinon essayez d'affiner votre recherche.")
if dataStore.appSettings.city.isEmpty {
Text("Il est préférable de se localiser ou d'indiquer une ville pour réduire le nombre de résultat.")
} else {
Text("Il y a beacoup de tournois pour cette requête, êtes-vous sûr de vouloir tout récupérer ? Sinon essayez d'affiner votre recherche.")
}
})
.toolbarBackground(.visible, for: .bottomBar, .navigationBar)
.navigationTitle("Chercher un tournoi")
.navigationBarTitleDisplayMode(.inline)
.onChange(of: locationManager.city) {
if let newValue = locationManager.city, city.isEmpty {
city = newValue
if let newValue = locationManager.city, dataStore.appSettings.city.isEmpty {
dataStore.appSettings.city = newValue
}
}
.toolbarTitleDisplayMode(.large)
@ -113,15 +107,9 @@ struct TournamentLookUpView: View {
#endif
Button(role: .destructive) {
tournamentLevels = Set()
tournamentCategories = Set()
city = ""
dataStore.appSettings.resetSearch()
locationManager.location = nil
locationManager.city = nil
distance = 30
startDate = Date()
endDate = Calendar.current.date(byAdding: .month, value: 3, to: Date())!
sortingOption = "dateDebut+asc"
revealSearchParameters = true
federalDataViewModel.searchedFederalTournaments = []
federalDataViewModel.searchAttemptCount = 0
@ -151,6 +139,7 @@ struct TournamentLookUpView: View {
}
private func runSearch() {
dataStore.appSettingsStorage.write()
revealSearchParameters = false
total = 0
page = 0
@ -158,6 +147,9 @@ struct TournamentLookUpView: View {
searching = true
requestedToGetAllPages = false
federalDataViewModel.searchAttemptCount += 1
federalDataViewModel.dayPeriod = dataStore.appSettings.dayPeriod
federalDataViewModel.dayDuration = dataStore.appSettings.dayDuration
Task {
await getNewPage()
searching = false
@ -170,7 +162,7 @@ struct TournamentLookUpView: View {
}
private var distanceLimit: Measurement<UnitLength> {
distanceLimit(distance: distance)
distanceLimit(distance: dataStore.appSettings.distance)
}
private func distanceLimit(distance: Double) -> Measurement<UnitLength> {
@ -178,19 +170,19 @@ struct TournamentLookUpView: View {
}
private var categories: [TournamentCategory] {
tournamentCategories.compactMap { TournamentCategory(rawValue: $0) }
dataStore.appSettings.tournamentCategories.compactMap { TournamentCategory(rawValue: $0) }
}
private var levels: [TournamentLevel] {
tournamentLevels.compactMap { TournamentLevel(rawValue: $0) }
dataStore.appSettings.tournamentLevels.compactMap { TournamentLevel(rawValue: $0) }
}
private var ages: [FederalTournamentAge] {
tournamentAges.compactMap { FederalTournamentAge(rawValue: $0) }
dataStore.appSettings.tournamentAges.compactMap { FederalTournamentAge(rawValue: $0) }
}
private var types: [FederalTournamentType] {
tournamentTypes.compactMap { FederalTournamentType(rawValue: $0) }
dataStore.appSettings.tournamentTypes.compactMap { FederalTournamentType(rawValue: $0) }
}
func getNewPage() async {
@ -198,7 +190,7 @@ struct TournamentLookUpView: View {
if NetworkFederalService.shared.formId.isEmpty {
await getNewBuildForm()
} else {
let commands = try await NetworkFederalService.shared.getAllFederalTournaments(sortingOption: sortingOption, page: page, startDate: startDate, endDate: endDate, city: city, distance: distance, categories: categories, levels: levels, lat: locationManager.location?.coordinate.latitude.formatted(.number.locale(Locale(identifier: "us"))), lng: locationManager.location?.coordinate.longitude.formatted(.number.locale(Locale(identifier: "us"))), ages: ages, types: types, nationalCup: nationalCup)
let commands = try await NetworkFederalService.shared.getAllFederalTournaments(sortingOption: dataStore.appSettings.sortingOption, page: page, startDate: dataStore.appSettings.startDate, endDate: dataStore.appSettings.endDate, city: dataStore.appSettings.city, distance: dataStore.appSettings.distance, categories: categories, levels: levels, lat: locationManager.location?.coordinate.latitude.formatted(.number.locale(Locale(identifier: "us"))), lng: locationManager.location?.coordinate.longitude.formatted(.number.locale(Locale(identifier: "us"))), ages: ages, types: types, nationalCup: dataStore.appSettings.nationalCup)
let resultCommand = commands.first(where: { $0.results != nil })
if let newTournaments = resultCommand?.results?.items {
newTournaments.forEach { ft in
@ -253,15 +245,9 @@ struct TournamentLookUpView: View {
}
}
Button {
tournamentLevels = Set()
tournamentCategories = Set()
city = ""
dataStore.appSettings.resetSearch()
locationManager.location = nil
locationManager.city = nil
distance = 30
startDate = Date()
endDate = Calendar.current.date(byAdding: .month, value: 3, to: Date())!
sortingOption = "dateDebut+asc"
revealSearchParameters = true
} label: {
Label("Ré-initialiser la recherche", systemImage: "xmark.circle")
@ -271,12 +257,12 @@ struct TournamentLookUpView: View {
@ViewBuilder
var searchParametersView: some View {
@Bindable var federalDataViewModel = federalDataViewModel
@Bindable var appSettings = dataStore.appSettings
Section {
DatePicker("Début", selection: $startDate, displayedComponents: .date)
DatePicker("Fin", selection: $endDate, displayedComponents: .date)
Picker(selection: $federalDataViewModel.dayDuration) {
Text("aucune").tag(nil as Int?)
DatePicker("Début", selection: $appSettings.startDate, displayedComponents: .date)
DatePicker("Fin", selection: $appSettings.endDate, displayedComponents: .date)
Picker(selection: $appSettings.dayDuration) {
Text("Aucune").tag(nil as Int?)
Text(1.formatted()).tag(1 as Int?)
Text(2.formatted()).tag(2 as Int?)
Text(3.formatted()).tag(3 as Int?)
@ -284,16 +270,16 @@ struct TournamentLookUpView: View {
Text("Durée max (en jours)")
}
Picker(selection: $federalDataViewModel.dayPeriod) {
Picker(selection: $appSettings.dayPeriod) {
ForEach(DayPeriod.allCases) {
Text($0.localizedDayPeriodLabel()).tag($0)
Text($0.localizedDayPeriodLabel().capitalized).tag($0)
}
} label: {
Text("En semaine ou week-end")
}
HStack {
TextField("Ville", text: $city)
TextField("Ville", text: $appSettings.city)
if let city = locationManager.city {
Divider()
Text(city).italic()
@ -311,7 +297,7 @@ struct TournamentLookUpView: View {
}
}
Picker(selection: $distance) {
Picker(selection: $appSettings.distance) {
Text(distanceLimit(distance:30).formatted()).tag(30.0)
Text(distanceLimit(distance:50).formatted()).tag(50.0)
Text(distanceLimit(distance:60).formatted()).tag(60.0)
@ -324,7 +310,7 @@ struct TournamentLookUpView: View {
Text("Distance max")
}
Picker(selection: $sortingOption) {
Picker(selection: $appSettings.sortingOption) {
Text("Distance").tag("_DIST_")
Text("Date de début").tag("dateDebut+asc")
Text("Date de fin").tag("dateFin+asc")
@ -333,7 +319,7 @@ struct TournamentLookUpView: View {
}
NavigationLink {
List(TournamentCategory.allCases, selection: $tournamentCategories) { type in
List([TournamentCategory.men, TournamentCategory.women, TournamentCategory.mix], selection: $appSettings.tournamentCategories) { type in
Text(type.localizedLabel())
}
.navigationTitle("Catégories")
@ -348,7 +334,7 @@ struct TournamentLookUpView: View {
}
NavigationLink {
List(TournamentLevel.allCases, selection: $tournamentLevels) { type in
List([TournamentLevel.p25, TournamentLevel.p100, TournamentLevel.p250, TournamentLevel.p500, TournamentLevel.p1000, TournamentLevel.p1500, TournamentLevel.p2000], selection: $appSettings.tournamentLevels) { type in
Text(type.localizedLabel())
}
.navigationTitle("Niveaux")
@ -363,7 +349,7 @@ struct TournamentLookUpView: View {
}
NavigationLink {
List(FederalTournamentAge.allCases, selection: $tournamentAges) { type in
List([FederalTournamentAge.senior, FederalTournamentAge.a45, FederalTournamentAge.a55, FederalTournamentAge.a17_18, FederalTournamentAge.a15_16, FederalTournamentAge.a13_14, FederalTournamentAge.a11_12], selection: $appSettings.tournamentAges) { type in
Text(type.localizedLabel())
}
.navigationTitle("Limites d'âge")
@ -372,7 +358,7 @@ struct TournamentLookUpView: View {
HStack {
Text("Limite d'âge")
Spacer()
if tournamentAges.isEmpty || tournamentAges.count == FederalTournamentAge.allCases.count {
if dataStore.appSettings.tournamentAges.isEmpty || dataStore.appSettings.tournamentAges.count == FederalTournamentAge.allCases.count {
Text("Tous les âges")
.foregroundStyle(.secondary)
} else {
@ -383,7 +369,7 @@ struct TournamentLookUpView: View {
}
NavigationLink {
List(FederalTournamentType.allCases, selection: $tournamentTypes) { type in
List(FederalTournamentType.allCases, selection: $appSettings.tournamentTypes) { type in
Text(type.localizedLabel())
}
.navigationTitle("Types de tournoi")
@ -392,7 +378,7 @@ struct TournamentLookUpView: View {
HStack {
Text("Type de tournoi")
Spacer()
if tournamentTypes.isEmpty || tournamentTypes.count == FederalTournamentType.allCases.count {
if dataStore.appSettings.tournamentTypes.isEmpty || dataStore.appSettings.tournamentTypes.count == FederalTournamentType.allCases.count {
Text("Tous les types")
.foregroundStyle(.secondary)
} else {
@ -402,7 +388,7 @@ struct TournamentLookUpView: View {
}
}
Picker(selection: $nationalCup) {
Picker(selection: $appSettings.nationalCup) {
Text("N'importe").tag(false)
Text("Uniquement").tag(true)
} label: {
@ -416,7 +402,7 @@ struct TournamentLookUpView: View {
}
var categoriesLabel: some View {
if tournamentCategories.isEmpty || tournamentCategories.count == TournamentCategory.allCases.count {
if dataStore.appSettings.tournamentCategories.isEmpty || dataStore.appSettings.tournamentCategories.count == TournamentCategory.allCases.count {
Text("Toutes les catégories")
} else {
Text(categories.map({ $0.localizedLabel() }).joined(separator: ", "))
@ -424,7 +410,7 @@ struct TournamentLookUpView: View {
}
var levelsLabel: some View {
if tournamentLevels.isEmpty || tournamentLevels.count == TournamentLevel.allCases.count {
if dataStore.appSettings.tournamentLevels.isEmpty || dataStore.appSettings.tournamentLevels.count == TournamentLevel.allCases.count {
Text("Tous les niveaux")
} else {
Text(levels.map({ $0.localizedLabel() }).joined(separator: ", "))
@ -437,8 +423,8 @@ struct TournamentLookUpView: View {
HStack {
Text("Lieu")
Spacer()
Text(city)
if distance >= 3000 {
Text(dataStore.appSettings.city)
if dataStore.appSettings.distance >= 3000 {
Text("sans limite de distance")
} else {
Text("à moins de " + distanceLimit.formatted())
@ -448,9 +434,9 @@ struct TournamentLookUpView: View {
Text("Période")
Spacer()
Text("Du")
Text(startDate.twoDigitsYearFormatted)
Text(dataStore.appSettings.startDate.twoDigitsYearFormatted)
Text("Au")
Text(endDate.twoDigitsYearFormatted)
Text(dataStore.appSettings.endDate.twoDigitsYearFormatted)
}
HStack {
Text("Niveau")
@ -472,7 +458,7 @@ struct TournamentLookUpView: View {
}
var sortingOptionLabel: String {
switch sortingOption {
switch dataStore.appSettings.sortingOption {
case "_DIST_": return "Distance"
case "dateDebut+asc": return "Date de début"
case "dateFin+asc": return "Date de fin"

@ -6,6 +6,7 @@
//
import SwiftUI
import EventKit
struct TournamentSubscriptionView: View {
@EnvironmentObject var networkMonitor: NetworkMonitor
@ -17,6 +18,8 @@ struct TournamentSubscriptionView: View {
@State private var selectedPlayers: [ImportedPlayer]
@State private var contactType: ContactType? = nil
@State private var sentError: ContactManagerError? = nil
@State private var didSendMessage: Bool = false
@State private var didSaveInCalendar: Bool = false
init(federalTournament: FederalTournament, build: any TournamentBuildHolder, user: User) {
self.federalTournament = federalTournament
@ -25,6 +28,64 @@ struct TournamentSubscriptionView: View {
_selectedPlayers = .init(wrappedValue: [user.currentPlayerData()].compactMap({ $0 }))
}
func hasPartner() -> Bool {
selectedPlayers.count == 2
}
func inscriptionSent() -> Bool {
didSendMessage
}
func addEvent() {
let eventStore = EKEventStore()
eventStore.requestWriteOnlyAccessToEvents { (granted, error) in
if granted && error == nil {
print("Access granted")
let startDate = federalTournament.startDate
let endDate = federalTournament.dateFin ?? federalTournament.startDate.endOfDay()
addEventToCalendar(title: messageSubject, startDate: startDate, endDate: endDate)
didSaveInCalendar = true
} else {
print("Access denied or error occurred: \(String(describing: error?.localizedDescription))")
sentError = .calendarAccessDenied
}
}
}
func addEventToCalendar(title: String, startDate: Date, endDate: Date) {
let eventStore = EKEventStore()
if eventStore.defaultCalendarForNewEvents == nil {
sentError = .noCalendarAvailable
return
}
eventStore.requestWriteOnlyAccessToEvents { (granted, error) in
if granted && error == nil {
let event = EKEvent(eventStore: eventStore)
event.title = title
event.isAllDay = true
event.startDate = startDate
event.endDate = endDate
event.calendar = eventStore.defaultCalendarForNewEvents
event.notes = noteCalendar
event.location = federalTournament.clubLabel()
do {
try eventStore.save(event, span: .thisEvent)
didSaveInCalendar = true
print("Event saved")
} catch let error {
print("Failed to save event: \(error.localizedDescription)")
sentError = .calendarEventSaveFailed
}
} else {
print("Access denied or error occurred: \(String(describing: error?.localizedDescription))")
sentError = .calendarAccessDenied
}
}
}
var body: some View {
List {
Section {
@ -86,54 +147,91 @@ struct TournamentSubscriptionView: View {
}
}
if let courrielEngagement = federalTournament.courrielEngagement {
Section {
RowButtonView("S'inscrire par email") {
contactType = .mail(date: nil, recipients: [courrielEngagement], bccRecipients: nil, body: messageBody, subject: messageSubject, tournamentBuild: build as? TournamentBuild)
}
}
Section {
Text(messageBody)
} header: {
Text("Message preparé par Padel Club")
} footer: {
CopyPasteButtonView(pasteValue: messageBody)
}
if let installation = federalTournament.installation, let telephone = installation.telephone {
if telephone.isMobileNumber() {
Section {
RowButtonView("S'inscrire par message") {
contactType = .message(date: nil, recipients: [telephone], body: messageBodyShort, tournamentBuild: build as? TournamentBuild)
}
.toolbarBackground(.visible, for: .bottomBar)
.toolbarBackground(.visible, for: .navigationBar)
.overlay(alignment: .bottom) {
if didSaveInCalendar {
Label("Ajouté dans votre calendrier par défaut", systemImage: "checkmark")
.toastFormatted()
.deferredRendering(for: .seconds(3))
}
}
.toolbar(content: {
ToolbarItem(placement: .status) {
Menu {
if let courrielEngagement = federalTournament.courrielEngagement {
Section {
RowButtonView("S'inscrire par email", systemImage: "envelope") {
contactType = .mail(date: nil, recipients: [courrielEngagement], bccRecipients: nil, body: messageBody, subject: messageSubject, tournamentBuild: build as? TournamentBuild)
}
}
}
}
let number = telephone.replacingOccurrences(of: " ", with: "")
if let url = URL(string: "tel:\(number)") {
Link(destination: url) {
Label("Appeler", systemImage: "phone")
if let installation = federalTournament.installation, let telephone = installation.telephone {
if telephone.isMobileNumber() {
Section {
RowButtonView("S'inscrire par message", systemImage: "message") {
contactType = .message(date: nil, recipients: [telephone], body: messageBodyShort, tournamentBuild: build as? TournamentBuild)
}
}
}
let number = telephone.replacingOccurrences(of: " ", with: "")
if let url = URL(string: "tel:\(number)") {
Link(destination: url) {
Label("Appeler", systemImage: "phone")
}
}
}
} label: {
Text("Contact et inscription")
}
Section {
Text(messageBody)
} header: {
Text("Message preparé par Padel Club")
} footer: {
CopyPasteButtonView(pasteValue: messageBody)
}
.menuStyle(.button)
.buttonStyle(.borderedProminent)
.offset(y:-2)
}
}
.toolbar(content: {
Menu {
Link(destination: URL(string:"https://tenup.fft.fr/tournoi/\(federalTournament.id)")!) {
Label("Voir sur Tenup", systemImage: "tennisball")
}
ShareLink(item: federalTournament.shareMessage) {
Label("Partager les infos", systemImage: "info")
ToolbarItem(placement: .topBarTrailing) {
Menu {
ShareLink(item: federalTournament.sharePartnerMessage) {
Label("Prévenir votre partenaire", systemImage: "person.2")
}
Button("Ajouter à votre agenda") {
addEvent()
}
ShareLink(item: federalTournament.shareMessage) {
Label("Partager les infos", systemImage: "info")
}
Link(destination: URL(string:"https://tenup.fft.fr/tournoi/\(federalTournament.id)")!) {
Label("Voir sur Tenup", systemImage: "tennisball")
}
ShareLink(item: federalTournament.shareMessage) {
Label("Partager les infos", systemImage: "info")
}
} label: {
LabelOptions()
}
} label: {
LabelOptions()
}
})
.alert("Un problème est survenu", isPresented: messageSentFailed) {
Button("OK") {
}
if sentError == .calendarAccessDenied || sentError == .noCalendarAvailable {
Button("Voir vos réglages") {
openAppSettings()
}
}
} message: {
Text(_networkErrorMessage)
}
@ -150,6 +248,8 @@ struct TournamentSubscriptionView: View {
case .sent:
if networkMonitor.connected == false {
self.sentError = .messageNotSent
} else {
self.didSendMessage = true
}
@unknown default:
break
@ -167,6 +267,8 @@ struct TournamentSubscriptionView: View {
if networkMonitor.connected == false {
self.contactType = nil
self.sentError = .mailNotSent
} else {
self.didSendMessage = true
}
@unknown default:
break
@ -188,12 +290,19 @@ struct TournamentSubscriptionView: View {
var messageBody: String {
let bonjourOuBonsoir = Date().timeOfDay.hello
let bonneSoireeOuBonneJournee = Date().timeOfDay.goodbye
let body = [["\(bonjourOuBonsoir),\n\nJe souhaiterais inscrire mon équipe au tournoi : ", build.buildHolderTitle(), "du", federalTournament.computedStartDate, "au", federalTournament.clubLabel() + ".\n"].compacted().joined(separator: " "), teamsString, "\nCordialement,\n", user.fullName() ?? bonneSoireeOuBonneJournee, "----------------------------------\nCe message a été préparé grâce à l'application Padel Club !\nVotre tournoi n'est pas encore dessus ? \(URLs.main.rawValue)", "Téléchargez l'app : \(URLs.appStore.rawValue)", "En savoir plus : \(URLs.appDescription.rawValue)"].compactMap { $0 }.joined(separator: "\n") + "\n"
let body = [["\(bonjourOuBonsoir),\n\nJe souhaiterais inscrire mon équipe au tournoi : ", build.buildHolderTitle(), "du", federalTournament.computedStartDate, "au", federalTournament.clubLabel() + ".\n"].compacted().joined(separator: " "), teamsString, "\nCordialement,\n", user.fullName() ?? bonneSoireeOuBonneJournee, "----------------------------------\nCe message a été préparé grâce à l'application Padel Club !"].compactMap { $0 }.joined(separator: "\n") + "\n"
return body
}
var messageBodyShort: String {
let body = [[build.buildHolderTitle(), federalTournament.clubLabel()].compacted().joined(separator: " "), federalTournament.computedStartDate, teamsString].compacted().joined(separator: "\n") + "\n"
let bonjourOuBonsoir = Date().timeOfDay.hello
let bonneSoireeOuBonneJournee = Date().timeOfDay.goodbye
let body = [["\(bonjourOuBonsoir),\n\nJe souhaiterais inscrire mon équipe au tournoi : ", build.buildHolderTitle(), "du", federalTournament.computedStartDate, "au", federalTournament.clubLabel() + ".\n"].compacted().joined(separator: " "), teamsString, "\nCordialement,\n", user.fullName() ?? bonneSoireeOuBonneJournee].compactMap { $0 }.joined(separator: "\n") + "\n"
return body
}
var noteCalendar: String {
let body = [[build.buildHolderTitle(), "du", federalTournament.computedStartDate, "au", federalTournament.clubLabel() + ".\n"].compacted().joined(separator: " "), teamsString, federalTournament.calendarNoteMessage()].compactMap { $0 }.joined(separator: "\n") + "\n"
return body
}
@ -227,6 +336,29 @@ struct TournamentSubscriptionView: View {
if sentError == .mailFailed {
errors.append("Le mail n'a pas été envoyé")
}
if sentError == .calendarAccessDenied {
errors.append("Padel Club n'a pas accès à votre calendrier")
}
if sentError == .calendarEventSaveFailed {
errors.append("Padel Club n'a pas réussi à sauver ce tournoi dans votre calendrier")
}
if sentError == .noCalendarAvailable {
errors.append("Padel Club n'a pas réussi à trouver un calendrier pour y inscrire ce tournoi")
}
return errors.joined(separator: "\n")
}
func openAppSettings() {
if let appSettings = URL(string: UIApplication.openSettingsURLString) {
if UIApplication.shared.canOpenURL(appSettings) {
UIApplication.shared.open(appSettings, options: [:], completionHandler: nil)
}
}
}
}

@ -122,6 +122,22 @@ struct UmpireView: View {
}
Section {
@Bindable var user = dataStore.user
Picker(selection: $user.loserBracketMode) {
ForEach(LoserBracketMode.allCases) {
Text($0.localizedLoserBracketMode()).tag($0)
}
} label: {
Text("Position des perdants")
}
.onChange(of: user.loserBracketMode) {
dataStore.saveUser()
}
} header: {
Text("Matchs de classement")
}
Section {
NavigationLink {
GlobalSettingsView()

@ -93,6 +93,7 @@ struct DatePickingView: View {
}
struct MatchFormatPickingView: View {
var title: String? = nil
@Binding var matchFormat: MatchFormat
var validateAction: (() async -> ())
@ -110,6 +111,10 @@ struct MatchFormatPickingView: View {
confirmScheduleUpdate = false
}
}
} header: {
if let title {
Text(title)
}
} footer: {
if confirmScheduleUpdate && updatingInProgress == false {
FooterButtonView("non, ne pas modifier les horaires") {
@ -123,3 +128,188 @@ struct MatchFormatPickingView: View {
}
}
}
struct DatePickingViewWithFormat: View {
@Binding var matchFormat: MatchFormat
let title: String
@Binding var startDate: Date
@Binding var currentDate: Date?
var duration: Int?
var validateAction: ((Bool) async -> ())
@State private var confirmScheduleUpdate: Bool = false
@State private var updatingInProgress : Bool = false
var body: some View {
Section {
MatchFormatPickerView(headerLabel: "Format", matchFormat: $matchFormat)
DatePicker(selection: $startDate) {
Text(startDate.formatted(.dateTime.weekday(.wide))).font(.headline)
}
if confirmScheduleUpdate {
RowButtonView("Sauver et modifier la suite") {
updatingInProgress = true
await validateAction(true)
updatingInProgress = false
confirmScheduleUpdate = false
}
}
} header: {
Text(title)
} footer: {
if confirmScheduleUpdate && updatingInProgress == false {
HStack {
FooterButtonView("sauver sans modifier la suite") {
Task {
await validateAction(false)
confirmScheduleUpdate = false
}
}
Text("ou")
FooterButtonView("annuler") {
confirmScheduleUpdate = false
}
}
} else {
HStack {
Menu {
Button("de 30 minutes") {
startDate = startDate.addingTimeInterval(1800)
}
Button("d'une heure") {
startDate = startDate.addingTimeInterval(3600)
}
Button("à 9h") {
startDate = startDate.atNine()
}
Button("à demain 9h") {
startDate = startDate.tomorrowAtNine
}
if let duration {
Button("à la prochaine rotation") {
startDate = startDate.addingTimeInterval(Double(duration) * 60)
}
Button("à la précédente rotation") {
startDate = startDate.addingTimeInterval(Double(duration) * -60)
}
}
} label: {
Text("décaler")
.underline()
}
.buttonStyle(.borderless)
Spacer()
if currentDate != nil {
FooterButtonView("retirer l'horaire bloqué") {
currentDate = nil
}
} else {
FooterButtonView("bloquer l'horaire") {
currentDate = startDate
}
}
}
.buttonStyle(.borderless)
}
}
.headerProminence(.increased)
.onChange(of: matchFormat) {
confirmScheduleUpdate = true
}
.onChange(of: startDate) {
confirmScheduleUpdate = true
}
}
}
struct GroupStageDatePickingView: View {
let title: String
@Binding var startDate: Date
@Binding var currentDate: Date?
var duration: Int?
var validateAction: (() async -> ())
@State private var confirmFollowingScheduleUpdate: Bool = false
@State private var updatingInProgress: Bool = false
var body: some View {
Section {
DatePicker(selection: $startDate) {
Text(startDate.formatted(.dateTime.weekday(.wide))).font(.headline)
}
if confirmFollowingScheduleUpdate {
RowButtonView("Confirmer et modifier les matchs") {
updatingInProgress = true
await validateAction()
updatingInProgress = false
confirmFollowingScheduleUpdate = false
}
}
} header: {
Text(title)
} footer: {
if confirmFollowingScheduleUpdate && updatingInProgress == false {
FooterButtonView("Modifier juste l'horaire de la poule") {
currentDate = startDate
confirmFollowingScheduleUpdate = false
}
} else {
HStack {
Menu {
Button("de 30 minutes") {
startDate = startDate.addingTimeInterval(1800)
}
Button("d'une heure") {
startDate = startDate.addingTimeInterval(3600)
}
Button("à 9h") {
startDate = startDate.atNine()
}
Button("à demain 9h") {
startDate = startDate.tomorrowAtNine
}
if let duration {
Button("à la prochaine rotation") {
startDate = startDate.addingTimeInterval(Double(duration) * 60)
}
Button("à la précédente rotation") {
startDate = startDate.addingTimeInterval(Double(duration) * -60)
}
}
} label: {
Text("décaler")
.underline()
}
.buttonStyle(.borderless)
Spacer()
if currentDate != nil {
FooterButtonView("retirer l'horaire bloqué") {
currentDate = nil
}
} else {
FooterButtonView("bloquer l'horaire") {
currentDate = startDate
}
}
}
.buttonStyle(.borderless)
}
}
.onChange(of: startDate) {
confirmFollowingScheduleUpdate = true
}
.headerProminence(.increased)
}
}

@ -114,7 +114,6 @@ struct CourtAvailabilitySettingsView: View {
endDate = tournament.startDate.addingTimeInterval(5400)
showingPopover = true
}
.padding(.horizontal)
}
}
}

@ -27,8 +27,9 @@ struct GroupStageScheduleEditorView: View {
}
var body: some View {
DatePickingView(title: groupStage.groupStageTitle(), startDate: $startDate, currentDate: $groupStage.startDate, duration: groupStage.matchFormat.getEstimatedDuration(tournament.additionalEstimationDuration)) {
GroupStageDatePickingView(title: groupStage.groupStageTitle(), startDate: $startDate, currentDate: $groupStage.startDate, duration: groupStage.matchFormat.getEstimatedDuration(tournament.additionalEstimationDuration)) {
groupStage.startDate = startDate
tournament.matchScheduler()?.updateGroupStageSchedule(tournament: tournament, specificGroupStage: groupStage)
_save()
}
}

@ -33,7 +33,7 @@ struct LoserRoundScheduleEditorView: View {
var body: some View {
List {
MatchFormatPickingView(matchFormat: $matchFormat) {
MatchFormatPickingView(title: "Format des tours par défault", matchFormat: $matchFormat) {
await _updateSchedule()
}

@ -38,8 +38,11 @@ struct LoserRoundStepScheduleEditorView: View {
var body: some View {
@Bindable var round = round
DatePickingView(title: "Tour #\(stepIndex + 1)", startDate: $startDate, currentDate: .constant(nil), duration: round.matchFormat.getEstimatedDuration(tournament.additionalEstimationDuration)) {
await _updateSchedule()
DatePickingViewWithFormat(matchFormat: $round.matchFormat, title: "Tour #\(stepIndex + 1)", startDate: $startDate, currentDate: .constant(nil), duration: round.matchFormat.getEstimatedDuration(tournament.additionalEstimationDuration)) { update in
for match in matches {
match.matchFormat = round.matchFormat
}
await _updateSchedule(update: update)
}
Section {
@ -64,8 +67,10 @@ struct LoserRoundStepScheduleEditorView: View {
}
}
private func _updateSchedule() async {
tournament.matchScheduler()?.updateBracketSchedule(tournament: tournament, fromRoundId: round.id, fromMatchId: nil, startDate: startDate)
private func _updateSchedule(update: Bool) async {
if update {
tournament.matchScheduler()?.updateBracketSchedule(tournament: tournament, fromRoundId: round.id, fromMatchId: nil, startDate: startDate)
}
upperRound.loserRounds(forRoundIndex: round.index).forEach({ round in
round.startDate = startDate
})

@ -27,6 +27,10 @@ struct MatchScheduleEditorView: View {
}
var body: some View {
MatchFormatPickingView(matchFormat: $match.matchFormat) {
await _updateSchedule()
}
DatePickingView(title: title, startDate: $startDate, currentDate: .constant(nil), duration: match.matchFormat.getEstimatedDuration(tournament.additionalEstimationDuration)) {
await _updateSchedule()
}

@ -51,7 +51,6 @@ struct PlanningByCourtView: View {
RowButtonView("Horaire intelligent") {
selectedScheduleDestination = nil
}
.padding(.horizontal)
}
}
}

@ -21,7 +21,9 @@ struct PlanningSettingsView: View {
@State private var showOptions: Bool = false
@State private var issueFound: Bool = false
@State private var parallelType: Bool = false
@State private var deletingDateMatchesDone: Bool = false
@State private var deletingDone: Bool = false
var tournamentStore: TournamentStore {
return self.tournament.tournamentStore
}
@ -121,11 +123,13 @@ struct PlanningSettingsView: View {
Section {
RowButtonView("Supprimer les horaires des matches", role: .destructive) {
do {
deletingDateMatchesDone = false
allMatches.forEach({
$0.startDate = nil
$0.confirmed = false
})
try self.tournamentStore.matches.addOrUpdate(contentOfs: allMatches)
deletingDateMatchesDone = true
} catch {
Logger.error(error)
}
@ -137,7 +141,8 @@ struct PlanningSettingsView: View {
Section {
RowButtonView("Supprimer tous les horaires", role: .destructive) {
do {
allMatches.forEach({
deletingDone = false
allMatches.forEach({
$0.startDate = nil
$0.confirmed = false
})
@ -148,6 +153,7 @@ struct PlanningSettingsView: View {
allRounds.forEach({ $0.startDate = nil })
try self.tournamentStore.rounds.addOrUpdate(contentOfs: allRounds)
deletingDone = true
} catch {
Logger.error(error)
}
@ -197,6 +203,18 @@ struct PlanningSettingsView: View {
.deferredRendering(for: .seconds(2))
}
}
if deletingDone {
Label("Tous les horaires ont été supprimés", systemImage: "clock.badge.xmark")
.toastFormatted()
.deferredRendering(for: .seconds(2))
}
if deletingDateMatchesDone {
Label("Horaires des matchs supprimés", systemImage: "clock.badge.xmark")
.toastFormatted()
.deferredRendering(for: .seconds(2))
}
}
.onChange(of: tournament.startDate) {
_save()

@ -40,7 +40,6 @@ struct PlanningView: View {
RowButtonView("Horaire intelligent") {
selectedScheduleDestination = nil
}
.padding(.horizontal)
}
}
}

@ -99,7 +99,7 @@ struct PlayerPopoverView: View {
.textInputAutocapitalization(.words)
.focused($firstNameIsFocused)
.onSubmit {
firstName = firstName.trimmed
firstName = firstName.trimmedMultiline
lastNameIsFocused = true
}
.fixedSize()
@ -114,7 +114,7 @@ struct PlayerPopoverView: View {
.keyboardType(.alphabet)
.focused($lastNameIsFocused)
.onSubmit {
lastName = lastName.trimmed
lastName = lastName.trimmedMultiline
licenseIsFocused = true
}
.fixedSize()
@ -128,7 +128,7 @@ struct PlayerPopoverView: View {
.keyboardType(.numberPad)
.submitLabel(.next)
.onSubmit {
license = license.trimmed
license = license.trimmedMultiline
if requiredField.contains(.license) {
if license.isLicenseNumber {
amountIsFocused = true
@ -206,7 +206,7 @@ struct PlayerPopoverView: View {
Spacer()
Button("Confirmer") {
if licenseIsFocused {
license = license.trimmed
license = license.trimmedMultiline
if requiredField.contains(.license) {
if license.isLicenseNumber {
amountIsFocused = true
@ -251,7 +251,7 @@ struct PlayerPopoverView: View {
}
func createManualPlayer() {
let playerRegistration = PlayerRegistration(firstName: firstName, lastName: lastName, licenceId: license.trimmed.isEmpty ? nil : license, rank: rank, sex: PlayerRegistration.PlayerSexType(rawValue: sex))
let playerRegistration = PlayerRegistration(firstName: firstName.trimmedMultiline, lastName: lastName.trimmedMultiline, licenceId: license.trimmedMultiline.isEmpty ? nil : license, rank: rank, sex: PlayerRegistration.PlayerSexType(rawValue: sex))
self.creationCompletionHandler(playerRegistration)
}

@ -41,6 +41,7 @@ struct PlayerDetailView: View {
.multilineTextAlignment(.trailing)
.frame(maxWidth: .infinity)
.onSubmit(of: .text) {
player.lastName = player.lastName.trimmedMultiline
_save()
}
} label: {
@ -53,6 +54,7 @@ struct PlayerDetailView: View {
.multilineTextAlignment(.trailing)
.frame(maxWidth: .infinity)
.onSubmit(of: .text) {
player.firstName = player.firstName.trimmedMultiline
_save()
}
} label: {

@ -22,6 +22,28 @@ struct LoserRoundSettingsView: View {
}
}
Section {
@Bindable var round: Round = upperBracketRound.round
Picker(selection: $round.loserBracketMode) {
ForEach(LoserBracketMode.allCases) {
Text($0.localizedLoserBracketMode()).tag($0)
}
} label: {
Text("Position des perdants")
}
.onChange(of: round.loserBracketMode) {
do {
try self.tournament.tournamentStore.rounds.addOrUpdate(instance: upperBracketRound.round)
} catch {
Logger.error(error)
}
}
} header: {
Text("Matchs de classement")
} footer: {
Text(upperBracketRound.round.loserBracketMode.localizedLoserBracketModeDescription())
}
Section {
RowButtonView("Synchroniser les noms des matchs") {
let allRoundMatches = upperBracketRound.loserRounds.flatMap({ $0.allMatches
@ -48,6 +70,11 @@ struct LoserRoundSettingsView: View {
upperBracketRound.round.disabledMatches().forEach { match in
match.disableMatch()
}
do {
try self.tournament.tournamentStore.matches.addOrUpdate(contentOfs: upperBracketRound.round.allLoserRoundMatches())
} catch {
Logger.error(error)
}
}
}
.disabled(upperBracketRound.round.loserRounds().isEmpty == false)

@ -172,7 +172,7 @@ struct SelectablePlayerListView: View {
}
.scrollDismissesKeyboard(.immediately)
.navigationBarBackButtonHidden(searchViewModel.allowMultipleSelection)
.toolbarBackground(.visible, for: .bottomBar)
//.toolbarBackground(.visible, for: .bottomBar)
// .toolbarRole(searchViewModel.allowMultipleSelection ? .navigationStack : .editor)
.interactiveDismissDisabled(searchViewModel.selectedPlayers.isEmpty == false)
.navigationTitle(searchViewModel.label(forDataSet: searchViewModel.dataSet))
@ -358,13 +358,109 @@ struct MySearchView: View {
@ViewBuilder
var playersView: some View {
let showProgression = true
let showFemaleInMaleAssimilation = searchViewModel.showFemaleInMaleAssimilation
if searchViewModel.allowMultipleSelection {
List(selection: $searchViewModel.selectedPlayers) {
if searchViewModel.filterSelectionEnabled {
let array = Array(searchViewModel.selectedPlayers)
Section {
ForEach(array) { player in
ImportedPlayerView(player: player, showFemaleInMaleAssimilation: searchViewModel.showFemaleInMaleAssimilation, showProgression: true)
let index : Int? = nil
VStack(alignment: .leading) {
HStack {
if player.isAnonymous() {
Text("Joueur Anonyme")
} else {
Text(player.getLastName().capitalized)
Text(player.getFirstName().capitalized)
}
if index == nil {
Text(player.male ? "" : "")
}
Spacer()
if let index {
HStack(alignment: .top, spacing: 0) {
Text(index.formatted())
.foregroundStyle(.secondary)
.font(.title3)
Text(index.ordinalFormattedSuffix())
.foregroundStyle(.secondary)
.font(.caption)
}
}
}
.font(.title3)
.lineLimit(1)
HStack {
HStack(alignment: .top, spacing: 0) {
Text(player.formattedRank()).italic(player.isAssimilated)
.font(.title3)
.background {
if player.isNotFromCurrentDate() {
UnderlineView()
}
}
if let rank = player.getRank() {
Text(rank.ordinalFormattedSuffix()).italic(player.isAssimilated)
.font(.caption)
}
}
if showProgression, player.getProgression() != 0 {
HStack(alignment: .top, spacing: 2) {
Text("(")
Text(player.getProgression().formatted(.number.sign(strategy: .always())))
.foregroundStyle(player.getProgressionColor(progression: player.getProgression()))
Text(")")
}.font(.title3)
}
if let pts = player.getPoints(), pts > 0 {
HStack(alignment: .lastTextBaseline, spacing: 0) {
Text(pts.formatted()).font(.title3)
Text(" pts").font(.caption)
}
}
if let tournamentPlayed = player.tournamentPlayed, tournamentPlayed > 0 {
HStack(alignment: .lastTextBaseline, spacing: 0) {
Text(tournamentPlayed.formatted()).font(.title3)
Text(" tournoi" + tournamentPlayed.pluralSuffix).font(.caption)
}
}
}
.lineLimit(1)
if showFemaleInMaleAssimilation, let assimilatedAsMaleRank = player.getAssimilatedAsMaleRank() {
HStack(alignment: .top, spacing: 2) {
Text("(")
Text(assimilatedAsMaleRank.formatted())
VStack(alignment: .leading, spacing: 0) {
Text("équivalence")
Text("messieurs")
}
.font(.caption)
Text(")").font(.title3)
}
}
HStack {
Text(player.formattedLicense())
if let computedAge = player.computedAge {
Text(computedAge.formatted() + " ans")
}
}
.font(.caption)
if let clubName = player.clubName {
Text(clubName)
.font(.caption)
}
if let ligueName = player.ligueName {
Text(ligueName)
.font(.caption)
}
}
}
.onDelete { indexSet in
for index in indexSet {
@ -379,7 +475,102 @@ struct MySearchView: View {
} else {
Section {
ForEach(players, id: \.self) { player in
ImportedPlayerView(player: player, index: nil, showFemaleInMaleAssimilation: searchViewModel.showFemaleInMaleAssimilation, showProgression: true)
let index : Int? = nil
VStack(alignment: .leading) {
HStack {
if player.isAnonymous() {
Text("Joueur Anonyme")
} else {
Text(player.getLastName().capitalized)
Text(player.getFirstName().capitalized)
}
if index == nil {
Text(player.male ? "" : "")
}
Spacer()
if let index {
HStack(alignment: .top, spacing: 0) {
Text(index.formatted())
.foregroundStyle(.secondary)
.font(.title3)
Text(index.ordinalFormattedSuffix())
.foregroundStyle(.secondary)
.font(.caption)
}
}
}
.font(.title3)
.lineLimit(1)
HStack {
HStack(alignment: .top, spacing: 0) {
Text(player.formattedRank()).italic(player.isAssimilated)
.font(.title3)
.background {
if player.isNotFromCurrentDate() {
UnderlineView()
}
}
if let rank = player.getRank() {
Text(rank.ordinalFormattedSuffix()).italic(player.isAssimilated)
.font(.caption)
}
}
if showProgression, player.getProgression() != 0 {
HStack(alignment: .top, spacing: 2) {
Text("(")
Text(player.getProgression().formatted(.number.sign(strategy: .always())))
.foregroundStyle(player.getProgressionColor(progression: player.getProgression()))
Text(")")
}.font(.title3)
}
if let pts = player.getPoints(), pts > 0 {
HStack(alignment: .lastTextBaseline, spacing: 0) {
Text(pts.formatted()).font(.title3)
Text(" pts").font(.caption)
}
}
if let tournamentPlayed = player.tournamentPlayed, tournamentPlayed > 0 {
HStack(alignment: .lastTextBaseline, spacing: 0) {
Text(tournamentPlayed.formatted()).font(.title3)
Text(" tournoi" + tournamentPlayed.pluralSuffix).font(.caption)
}
}
}
.lineLimit(1)
if showFemaleInMaleAssimilation, let assimilatedAsMaleRank = player.getAssimilatedAsMaleRank() {
HStack(alignment: .top, spacing: 2) {
Text("(")
Text(assimilatedAsMaleRank.formatted())
VStack(alignment: .leading, spacing: 0) {
Text("équivalence")
Text("messieurs")
}
.font(.caption)
Text(")").font(.title3)
}
}
HStack {
Text(player.formattedLicense())
if let computedAge = player.computedAge {
Text(computedAge.formatted() + " ans")
}
}
.font(.caption)
if let clubName = player.clubName {
Text(clubName)
.font(.caption)
}
if let ligueName = player.ligueName {
Text(ligueName)
.font(.caption)
}
}
}
} header: {
if players.isEmpty == false {
@ -395,10 +586,105 @@ struct MySearchView: View {
if searchViewModel.allowSingleSelection {
Section {
ForEach(players) { player in
let index : Int? = nil
Button {
searchViewModel.selectedPlayers.insert(player)
} label: {
ImportedPlayerView(player: player, showFemaleInMaleAssimilation: searchViewModel.showFemaleInMaleAssimilation, showProgression: true)
VStack(alignment: .leading) {
HStack {
if player.isAnonymous() {
Text("Joueur Anonyme")
} else {
Text(player.getLastName().capitalized)
Text(player.getFirstName().capitalized)
}
if index == nil {
Text(player.male ? "" : "")
}
Spacer()
if let index {
HStack(alignment: .top, spacing: 0) {
Text(index.formatted())
.foregroundStyle(.secondary)
.font(.title3)
Text(index.ordinalFormattedSuffix())
.foregroundStyle(.secondary)
.font(.caption)
}
}
}
.font(.title3)
.lineLimit(1)
HStack {
HStack(alignment: .top, spacing: 0) {
Text(player.formattedRank()).italic(player.isAssimilated)
.font(.title3)
.background {
if player.isNotFromCurrentDate() {
UnderlineView()
}
}
if let rank = player.getRank() {
Text(rank.ordinalFormattedSuffix()).italic(player.isAssimilated)
.font(.caption)
}
}
if showProgression, player.getProgression() != 0 {
HStack(alignment: .top, spacing: 2) {
Text("(")
Text(player.getProgression().formatted(.number.sign(strategy: .always())))
.foregroundStyle(player.getProgressionColor(progression: player.getProgression()))
Text(")")
}.font(.title3)
}
if let pts = player.getPoints(), pts > 0 {
HStack(alignment: .lastTextBaseline, spacing: 0) {
Text(pts.formatted()).font(.title3)
Text(" pts").font(.caption)
}
}
if let tournamentPlayed = player.tournamentPlayed, tournamentPlayed > 0 {
HStack(alignment: .lastTextBaseline, spacing: 0) {
Text(tournamentPlayed.formatted()).font(.title3)
Text(" tournoi" + tournamentPlayed.pluralSuffix).font(.caption)
}
}
}
.lineLimit(1)
if showFemaleInMaleAssimilation, let assimilatedAsMaleRank = player.getAssimilatedAsMaleRank() {
HStack(alignment: .top, spacing: 2) {
Text("(")
Text(assimilatedAsMaleRank.formatted())
VStack(alignment: .leading, spacing: 0) {
Text("équivalence")
Text("messieurs")
}
.font(.caption)
Text(")").font(.title3)
}
}
HStack {
Text(player.formattedLicense())
if let computedAge = player.computedAge {
Text(computedAge.formatted() + " ans")
}
}
.font(.caption)
if let clubName = player.clubName {
Text(clubName)
.font(.caption)
}
if let ligueName = player.ligueName {
Text(ligueName)
.font(.caption)
}
}
}
.buttonStyle(.plain)
}
@ -410,9 +696,104 @@ struct MySearchView: View {
.id(UUID())
} else {
Section {
ForEach(players.indices, id: \.self) { index in
let player = players[index]
ImportedPlayerView(player: player, index: searchViewModel.showIndex() ? (index + 1) : nil, showFemaleInMaleAssimilation: searchViewModel.showFemaleInMaleAssimilation, showProgression: true)
ForEach(players.indices, id: \.self) { playerIndex in
let player = players[playerIndex]
let index: Int? = searchViewModel.showIndex() ? (playerIndex + 1) : nil
VStack(alignment: .leading) {
HStack {
if player.isAnonymous() {
Text("Joueur Anonyme")
} else {
Text(player.getLastName().capitalized)
Text(player.getFirstName().capitalized)
}
if index == nil {
Text(player.male ? "" : "")
}
Spacer()
if let index {
HStack(alignment: .top, spacing: 0) {
Text(index.formatted())
.foregroundStyle(.secondary)
.font(.title3)
Text(index.ordinalFormattedSuffix())
.foregroundStyle(.secondary)
.font(.caption)
}
}
}
.font(.title3)
.lineLimit(1)
HStack {
HStack(alignment: .top, spacing: 0) {
Text(player.formattedRank()).italic(player.isAssimilated)
.font(.title3)
.background {
if player.isNotFromCurrentDate() {
UnderlineView()
}
}
if let rank = player.getRank() {
Text(rank.ordinalFormattedSuffix()).italic(player.isAssimilated)
.font(.caption)
}
}
if showProgression, player.getProgression() != 0 {
HStack(alignment: .top, spacing: 2) {
Text("(")
Text(player.getProgression().formatted(.number.sign(strategy: .always())))
.foregroundStyle(player.getProgressionColor(progression: player.getProgression()))
Text(")")
}.font(.title3)
}
if let pts = player.getPoints(), pts > 0 {
HStack(alignment: .lastTextBaseline, spacing: 0) {
Text(pts.formatted()).font(.title3)
Text(" pts").font(.caption)
}
}
if let tournamentPlayed = player.tournamentPlayed, tournamentPlayed > 0 {
HStack(alignment: .lastTextBaseline, spacing: 0) {
Text(tournamentPlayed.formatted()).font(.title3)
Text(" tournoi" + tournamentPlayed.pluralSuffix).font(.caption)
}
}
}
.lineLimit(1)
if showFemaleInMaleAssimilation, let assimilatedAsMaleRank = player.getAssimilatedAsMaleRank() {
HStack(alignment: .top, spacing: 2) {
Text("(")
Text(assimilatedAsMaleRank.formatted())
VStack(alignment: .leading, spacing: 0) {
Text("équivalence")
Text("messieurs")
}
.font(.caption)
Text(")").font(.title3)
}
}
HStack {
Text(player.formattedLicense())
if let computedAge = player.computedAge {
Text(computedAge.formatted() + " ans")
}
}
.font(.caption)
if let clubName = player.clubName {
Text(clubName)
.font(.caption)
}
if let ligueName = player.ligueName {
Text(ligueName)
.font(.caption)
}
}
}
} header: {
if players.isEmpty == false {
@ -423,19 +804,205 @@ struct MySearchView: View {
}
} else {
Section {
ForEach(players.indices, id: \.self) { index in
let player = players[index]
ForEach(players.indices, id: \.self) { playerIndex in
let player = players[playerIndex]
let index: Int? = searchViewModel.showIndex() ? (playerIndex + 1) : nil
if searchViewModel.allowSingleSelection {
Button {
searchViewModel.selectedPlayers.insert(player)
} label: {
ImportedPlayerView(player: player, index: searchViewModel.showIndex() ? (index + 1) : nil, showFemaleInMaleAssimilation: searchViewModel.showFemaleInMaleAssimilation, showProgression: true)
.contentShape(Rectangle())
VStack(alignment: .leading) {
HStack {
if player.isAnonymous() {
Text("Joueur Anonyme")
} else {
Text(player.getLastName().capitalized)
Text(player.getFirstName().capitalized)
}
if index == nil {
Text(player.male ? "" : "")
}
Spacer()
if let index {
HStack(alignment: .top, spacing: 0) {
Text(index.formatted())
.foregroundStyle(.secondary)
.font(.title3)
Text(index.ordinalFormattedSuffix())
.foregroundStyle(.secondary)
.font(.caption)
}
}
}
.font(.title3)
.lineLimit(1)
HStack {
HStack(alignment: .top, spacing: 0) {
Text(player.formattedRank()).italic(player.isAssimilated)
.font(.title3)
.background {
if player.isNotFromCurrentDate() {
UnderlineView()
}
}
if let rank = player.getRank() {
Text(rank.ordinalFormattedSuffix()).italic(player.isAssimilated)
.font(.caption)
}
}
if showProgression, player.getProgression() != 0 {
HStack(alignment: .top, spacing: 2) {
Text("(")
Text(player.getProgression().formatted(.number.sign(strategy: .always())))
.foregroundStyle(player.getProgressionColor(progression: player.getProgression()))
Text(")")
}.font(.title3)
}
if let pts = player.getPoints(), pts > 0 {
HStack(alignment: .lastTextBaseline, spacing: 0) {
Text(pts.formatted()).font(.title3)
Text(" pts").font(.caption)
}
}
if let tournamentPlayed = player.tournamentPlayed, tournamentPlayed > 0 {
HStack(alignment: .lastTextBaseline, spacing: 0) {
Text(tournamentPlayed.formatted()).font(.title3)
Text(" tournoi" + tournamentPlayed.pluralSuffix).font(.caption)
}
}
}
.lineLimit(1)
if showFemaleInMaleAssimilation, let assimilatedAsMaleRank = player.getAssimilatedAsMaleRank() {
HStack(alignment: .top, spacing: 2) {
Text("(")
Text(assimilatedAsMaleRank.formatted())
VStack(alignment: .leading, spacing: 0) {
Text("équivalence")
Text("messieurs")
}
.font(.caption)
Text(")").font(.title3)
}
}
HStack {
Text(player.formattedLicense())
if let computedAge = player.computedAge {
Text(computedAge.formatted() + " ans")
}
}
.font(.caption)
if let clubName = player.clubName {
Text(clubName)
.font(.caption)
}
if let ligueName = player.ligueName {
Text(ligueName)
.font(.caption)
}
}
}
.frame(maxWidth: .infinity)
.buttonStyle(.plain)
} else {
ImportedPlayerView(player: player, index: searchViewModel.showIndex() ? (index + 1) : nil, showFemaleInMaleAssimilation: searchViewModel.showFemaleInMaleAssimilation, showProgression: true)
VStack(alignment: .leading) {
HStack {
if player.isAnonymous() {
Text("Joueur Anonyme")
} else {
Text(player.getLastName().capitalized)
Text(player.getFirstName().capitalized)
}
if index == nil {
Text(player.male ? "" : "")
}
Spacer()
if let index {
HStack(alignment: .top, spacing: 0) {
Text(index.formatted())
.foregroundStyle(.secondary)
.font(.title3)
Text(index.ordinalFormattedSuffix())
.foregroundStyle(.secondary)
.font(.caption)
}
}
}
.font(.title3)
.lineLimit(1)
HStack {
HStack(alignment: .top, spacing: 0) {
Text(player.formattedRank()).italic(player.isAssimilated)
.font(.title3)
.background {
if player.isNotFromCurrentDate() {
UnderlineView()
}
}
if let rank = player.getRank() {
Text(rank.ordinalFormattedSuffix()).italic(player.isAssimilated)
.font(.caption)
}
}
if showProgression, player.getProgression() != 0 {
HStack(alignment: .top, spacing: 2) {
Text("(")
Text(player.getProgression().formatted(.number.sign(strategy: .always())))
.foregroundStyle(player.getProgressionColor(progression: player.getProgression()))
Text(")")
}.font(.title3)
}
if let pts = player.getPoints(), pts > 0 {
HStack(alignment: .lastTextBaseline, spacing: 0) {
Text(pts.formatted()).font(.title3)
Text(" pts").font(.caption)
}
}
if let tournamentPlayed = player.tournamentPlayed, tournamentPlayed > 0 {
HStack(alignment: .lastTextBaseline, spacing: 0) {
Text(tournamentPlayed.formatted()).font(.title3)
Text(" tournoi" + tournamentPlayed.pluralSuffix).font(.caption)
}
}
}
.lineLimit(1)
if showFemaleInMaleAssimilation, let assimilatedAsMaleRank = player.getAssimilatedAsMaleRank() {
HStack(alignment: .top, spacing: 2) {
Text("(")
Text(assimilatedAsMaleRank.formatted())
VStack(alignment: .leading, spacing: 0) {
Text("équivalence")
Text("messieurs")
}
.font(.caption)
Text(")").font(.title3)
}
}
HStack {
Text(player.formattedLicense())
if let computedAge = player.computedAge {
Text(computedAge.formatted() + " ans")
}
}
.font(.caption)
if let clubName = player.clubName {
Text(clubName)
.font(.caption)
}
if let ligueName = player.ligueName {
Text(ligueName)
.font(.caption)
}
}
}
}
} header: {

@ -147,7 +147,7 @@ struct EditingTeamView: View {
.frame(maxWidth: .infinity)
.submitLabel(.done)
.onSubmit(of: .text) {
let trimmed = name.trimmed
let trimmed = name.trimmedMultiline
if trimmed.isEmpty {
team.name = nil
} else {
@ -243,7 +243,7 @@ struct EditingTeamView: View {
}
.tint(.master)
}
.sheet(item: $editedTeam) { editedTeam in
.fullScreenCover(item: $editedTeam) { editedTeam in
NavigationStack {
AddTeamView(tournament: tournament, editedTeam: editedTeam)
}

@ -18,6 +18,7 @@ struct TeamPickerView: View {
var shouldConfirm: Bool = false
var groupStagePosition: Int? = nil
var round: Round? = nil
var matchTypeContext: MatchType = .bracket
var luckyLosers: [TeamRegistration] = []
let teamPicked: ((TeamRegistration) -> (Void))
@ -39,6 +40,14 @@ struct TeamPickerView: View {
.sheet(isPresented: $presentTeamPickerView) {
NavigationStack {
List {
if matchTypeContext == .loserBracket, let losers = round?.parentRound?.losers() {
_sectionView(losers.sorted(by: \.weight, order: sortOrder), title: "Perdant du tour précédent")
}
if matchTypeContext == .loserBracket, let losers = round?.previousRound()?.winners() {
_sectionView(losers.sorted(by: \.weight, order: sortOrder), title: "Gagnant du tour précédent")
}
if let groupStagePosition, let replacementRangeExtended = tournament.replacementRangeExtended(groupStagePosition: groupStagePosition) {
Section {
GroupStageTeamReplacementView.TeamRangeView(teamRange: replacementRangeExtended, playerWeight: 0)
@ -130,6 +139,7 @@ struct TeamPickerView: View {
}
.frame(maxWidth: .infinity)
.buttonStyle(.plain)
.listRowView(isActive: matchTypeContext == .loserBracket && round?.teams().map({ $0.id }).contains(team.id) == true, color: .green, hideColorVariation: true)
// .confirmationDialog("Attention", isPresented: confirmationRequest, titleVisibility: .visible) {
// Button("Retirer du tableau", role: .destructive) {
// teamPicked(confirmTeam!)

@ -317,6 +317,20 @@ struct FileImportView: View {
}
}
let unfound = _getUnfound(tournament: tournament, fromTeams: _filteredTeams)
if unfound.isEmpty == false {
Section {
LabeledContent {
Text(unfound.count.formatted())
} label: {
Text("Équipe\(unfound.count.pluralSuffix) précédente\(unfound.count.pluralSuffix) introuvable\(unfound.count.pluralSuffix)")
}
} footer: {
Text("Équipes de votre liste précédente non détectées dans ce nouveau fichier")
}
}
ForEach(_filteredTeams) { team in
_teamView(team: team, inTeams: _filteredTeams, previousTeams: previousTeams)
}
@ -442,11 +456,16 @@ struct FileImportView: View {
.disabled(validationInProgress)
}
private func _getUnfound(tournament: Tournament, fromTeams filteredTeams: [FileImportManager.TeamHolder]) -> Set<TeamRegistration> {
let previousTeams = filteredTeams.compactMap({ $0.previousTeam })
let unfound = Set(tournament.unsortedTeams()).subtracting(Set(previousTeams))
return unfound
}
private func _validate(tournament: Tournament) async {
let filteredTeams = filteredTeams(tournament: tournament)
let previousTeams = filteredTeams.compactMap({ $0.previousTeam })
let unfound = Set(tournament.unsortedTeams()).subtracting(Set(previousTeams))
let unfound = _getUnfound(tournament: tournament, fromTeams: filteredTeams)
unfound.forEach { team in
team.resetPositions()
team.wildCardBracket = false
@ -455,7 +474,7 @@ struct FileImportView: View {
}
do {
try self.tournamentStore.teamRegistrations.addOrUpdate(contentOfs: unfound)
try tournament.tournamentStore.teamRegistrations.addOrUpdate(contentOfs: unfound)
} catch {
Logger.error(error)
}

@ -20,7 +20,11 @@ struct AddTeamView: View {
var tournament: Tournament
var cancelShouldDismiss: Bool = false
enum FocusField: Hashable {
case pasteField
}
@FocusState private var focusedField: FocusField?
@State private var searchField: String = ""
@State private var presentSearch: Bool = false
@State private var presentPlayerSearch: Bool = false
@ -36,7 +40,8 @@ struct AddTeamView: View {
@State private var confirmDuplicate: Bool = false
@State private var homonyms: [PlayerRegistration] = []
@State private var confirmHomonym: Bool = false
@State private var editableTextField: String = ""
var tournamentStore: TournamentStore {
return self.tournament.tournamentStore
}
@ -61,13 +66,32 @@ struct AddTeamView: View {
_pasteString = .init(wrappedValue: pasteString)
_fetchPlayers = FetchRequest<ImportedPlayer>(sortDescriptors: [NSSortDescriptor(keyPath: \ImportedPlayer.rank, ascending: true)], predicate: SearchViewModel.pastePredicate(pasteField: pasteString, mostRecentDate: tournament.rankSourceDate, filterOption: tournament.tournamentCategory.playerFilterOption))
_autoSelect = .init(wrappedValue: true)
_editableTextField = .init(wrappedValue: pasteString)
cancelShouldDismiss = true
}
}
var body: some View {
_buildingTeamView()
.navigationBarBackButtonHidden(true)
if pasteString != nil, fetchPlayers.isEmpty == false {
computedBody
.searchable(text: $searchField, placement: .navigationBarDrawer(displayMode: .always), prompt: Text("Chercher dans les résultats"))
} else {
computedBody
}
}
var computedBody: some View {
List(selection: $createdPlayerIds) {
_buildingTeamView()
}
.onReceive(fetchPlayers.publisher.count()) { _ in // <-- here
if let pasteString, count == 2, autoSelect == true {
fetchPlayers.filter { $0.hitForSearch(pasteString) >= hitTarget }.sorted(by: { $0.hitForSearch(pasteString) > $1.hitForSearch(pasteString) }).forEach { player in
createdPlayerIds.insert(player.license!)
}
autoSelect = false
}
}
.alert("Présence d'homonyme", isPresented: $confirmHomonym) {
Button("Créer l'équipe quand même") {
_createTeam(checkDuplicates: false, checkHomonym: false)
@ -121,7 +145,6 @@ struct AddTeamView: View {
}
.tint(.master)
}
.navigationTitle(editedTeam == nil ? "Ajouter une équipe" : "Modifier l'équipe")
.toolbar {
ToolbarItem(placement: .cancellationAction) {
Button("Annuler", role: .cancel) {
@ -130,27 +153,23 @@ struct AddTeamView: View {
}
if pasteString == nil {
ToolbarItem(placement: .topBarTrailing) {
ToolbarItem(placement: .bottomBar) {
PasteButton(payloadType: String.self) { strings in
guard let first = strings.first else { return }
Task {
await MainActor.run {
fetchPlayers.nsPredicate = SearchViewModel.pastePredicate(pasteField: first, mostRecentDate: SourceFileManager.shared.mostRecentDateAvailable, filterOption: _filterOption())
fetchPlayers.nsSortDescriptors = [NSSortDescriptor(keyPath: \ImportedPlayer.rank, ascending: true)]
pasteString = first
autoSelect = true
}
}
handlePasteString(first)
}
.foregroundStyle(.master)
.labelStyle(.iconOnly)
.labelStyle(.titleAndIcon)
.buttonBorderShape(.capsule)
}
}
}
.navigationBarBackButtonHidden(_isEditingTeam())
.navigationBarBackButtonHidden(true)
.toolbarBackground(.visible, for: .navigationBar)
.toolbarBackground(.visible, for: .bottomBar)
.navigationBarTitleDisplayMode(.inline)
.navigationTitle(editedTeam == nil ? "Ajouter une équipe" : "Modifier l'équipe")
.environment(\.editMode, Binding.constant(EditMode.active))
}
private func _isEditingTeam() -> Bool {
@ -275,6 +294,8 @@ struct AddTeamView: View {
createdPlayers.removeAll()
createdPlayerIds.removeAll()
pasteString = nil
editableTextField = ""
if team.players().count > 1 {
dismiss()
}
@ -302,28 +323,50 @@ struct AddTeamView: View {
createdPlayers.removeAll()
createdPlayerIds.removeAll()
pasteString = nil
editableTextField = ""
self.editedTeam = nil
if editedTeam.players().count > 1 {
dismiss()
}
}
@ViewBuilder
private func _buildingTeamView() -> some View {
List(selection: $createdPlayerIds) {
if let pasteString {
Section {
Text(pasteString)
TextEditor(text: $editableTextField)
.frame(minHeight: 120, maxHeight: .infinity)
.focused($focusedField, equals: .pasteField)
.toolbar {
ToolbarItemGroup(placement: .keyboard) {
Button("Fermer", role: .cancel) {
self.editableTextField = pasteString
self.focusedField = nil
}
Spacer()
Button("Chercher") {
self.handlePasteString(editableTextField)
self.focusedField = nil
}
.buttonStyle(.bordered)
}
}
} header: {
Text("Contenu du presse-papier")
} footer: {
HStack {
Text("contenu du presse-papier")
FooterButtonView("éditer") {
self.focusedField = .pasteField
}
Spacer()
Button("effacer", role: .destructive) {
FooterButtonView("effacer", role: .destructive) {
self.focusedField = nil
self.editableTextField = ""
self.pasteString = nil
self.createdPlayers.removeAll()
self.createdPlayerIds.removeAll()
}
.buttonStyle(.borderless)
}
}
}
@ -387,6 +430,8 @@ struct AddTeamView: View {
Text(tournament.cutLabel(index: teamIndex, teamCount: selectedSortedTeams.count))
}
}
// } else {
// Text("Préparation de l'équipe")
}
}
@ -404,33 +449,16 @@ struct AddTeamView: View {
RowButtonView("Effacer cette recherche") {
self.pasteString = nil
self.editableTextField = ""
}
}
} else {
Section {
let sortedPlayers = fetchPlayers.sorted(by: { $0.hitForSearch(pasteString) > $1.hitForSearch(pasteString) })
ForEach(sortedPlayers) { player in
ImportedPlayerView(player: player).tag(player.license!)
}
} header: {
Text(fetchPlayers.count.formatted() + " résultat" + fetchPlayers.count.pluralSuffix)
}
_listOfPlayers(pasteString: pasteString)
}
} else {
_managementView()
}
}
.headerProminence(.increased)
.onReceive(fetchPlayers.publisher.count()) { _ in // <-- here
if let pasteString, count == 2, autoSelect == true {
fetchPlayers.filter { $0.hitForSearch(pasteString) >= hitTarget }.sorted(by: { $0.hitForSearch(pasteString) > $1.hitForSearch(pasteString) }).forEach { player in
createdPlayerIds.insert(player.license!)
}
autoSelect = false
}
}
.environment(\.editMode, Binding.constant(EditMode.active))
}
private var count: Int {
@ -453,4 +481,32 @@ struct AddTeamView: View {
Logger.error(error)
}
}
private func handlePasteString(_ first: String) {
Task {
await MainActor.run {
fetchPlayers.nsPredicate = SearchViewModel.pastePredicate(pasteField: first, mostRecentDate: SourceFileManager.shared.mostRecentDateAvailable, filterOption: _filterOption())
fetchPlayers.nsSortDescriptors = [NSSortDescriptor(keyPath: \ImportedPlayer.rank, ascending: true)]
pasteString = first
editableTextField = first
autoSelect = true
}
}
}
@ViewBuilder
private func _listOfPlayers(pasteString: String) -> some View {
let sortedPlayers = fetchPlayers.filter({ $0.contains(searchField) || searchField.isEmpty }).sorted(by: { $0.hitForSearch(pasteString) > $1.hitForSearch(pasteString) })
Section {
ForEach(sortedPlayers) { player in
ImportedPlayerView(player: player).tag(player.license!)
}
} header: {
Text(sortedPlayers.count.formatted() + " résultat" + sortedPlayers.count.pluralSuffix)
}
}
}

@ -11,7 +11,7 @@ import LeStorage
struct TournamentGeneralSettingsView: View {
@EnvironmentObject var dataStore: DataStore
var tournament: Tournament
@Bindable var tournament: Tournament
@State private var tournamentName: String = ""
@State private var entryFee: Double? = nil
@FocusState private var focusedField: Tournament.CodingKeys?
@ -24,7 +24,7 @@ struct TournamentGeneralSettingsView: View {
var body: some View {
@Bindable var tournament = tournament
Form {
Form {
Section {
TournamentDatePickerView()
TournamentDurationManagerView()
@ -34,6 +34,43 @@ struct TournamentGeneralSettingsView: View {
TournamentLevelPickerView()
}
Section {
Picker(selection: $tournament.loserBracketMode) {
ForEach(LoserBracketMode.allCases) {
Text($0.localizedLoserBracketMode()).tag($0)
}
} label: {
Text("Position des perdants")
}
.onChange(of: tournament.loserBracketMode) {
_save()
let rounds = tournament.rounds()
rounds.forEach { round in
round.loserBracketMode = tournament.loserBracketMode
}
do {
try self.tournament.tournamentStore.rounds.addOrUpdate(contentOfs: rounds)
} catch {
Logger.error(error)
}
}
} header: {
Text("Matchs de classement")
} footer: {
if dataStore.user.loserBracketMode != tournament.loserBracketMode {
_footerView()
.onTapGesture(perform: {
self.dataStore.user.loserBracketMode = tournament.loserBracketMode
self.dataStore.saveUser()
})
} else {
Text(tournament.loserBracketMode.localizedLoserBracketModeDescription())
}
}
Section {
LabeledContent {
TextField(tournament.isFree() ? "Gratuite" : "Inscription", value: $entryFee, format: .currency(code: Locale.current.currency?.identifier ?? "EUR"))
@ -118,4 +155,10 @@ struct TournamentGeneralSettingsView: View {
Logger.error(error)
}
}
private func _footerView() -> some View {
Text(tournament.loserBracketMode.localizedLoserBracketModeDescription())
+
Text(" Modifier le réglage par défaut pour tous vos tournois").foregroundStyle(.blue)
}
}

@ -224,14 +224,11 @@ struct InscriptionManagerView: View {
RowButtonView("Ajouter une équipe") {
presentAddTeamView = true
}
.padding(.horizontal)
RowButtonView("Importer un fichier") {
presentImportView = true
}
.padding(.horizontal)
}
.padding()
}
}
}
@ -327,7 +324,7 @@ struct InscriptionManagerView: View {
UpdateSourceRankDateView(currentRankSourceDate: $currentRankSourceDate, confirmUpdateRank: $confirmUpdateRank, tournament: tournament)
.tint(.master)
}
.sheet(isPresented: $presentAddTeamView, onDismiss: {
.fullScreenCover(isPresented: $presentAddTeamView, onDismiss: {
_setHash()
}) {
NavigationStack {
@ -335,7 +332,7 @@ struct InscriptionManagerView: View {
}
.tint(.master)
}
.sheet(item: $editedTeam, onDismiss: {
.fullScreenCover(item: $editedTeam, onDismiss: {
_setHash()
}) { editedTeam in
NavigationStack {
@ -343,7 +340,7 @@ struct InscriptionManagerView: View {
}
.tint(.master)
}
.sheet(item: $pasteString, onDismiss: {
.fullScreenCover(item: $pasteString, onDismiss: {
_setHash()
}) { pasteString in
NavigationStack {

@ -74,7 +74,7 @@ struct TournamentSettingsView: View {
}
.navigationBarTitleDisplayMode(.inline)
.toolbarBackground(.visible, for: .navigationBar)
.navigationTitle("Réglages")
.navigationTitle("Réglages du tournoi")
}
}

@ -178,7 +178,9 @@ import LeStorage
#if DEBUG
return .monthlyUnlimited
#elseif TESTFLIGHT
return .monthlyUnlimited
return .monthlyUnlimited
#elseif PRODTEST
return .monthlyUnlimited
#else
if let currentBestPurchase = self.currentBestPurchase, let plan = StoreItem(rawValue: currentBestPurchase.productId) {
return plan

@ -40,7 +40,7 @@ struct TournamentInitView: View {
Image(systemName: "checkmark").foregroundStyle(.green)
}
} label: {
LabelStructure()
Text("Structure")
Text(tournament.structureDescriptionLocalizedLabel())
}
}
@ -49,7 +49,7 @@ struct TournamentInitView: View {
LabeledContent {
Text(tournament.localizedTournamentType())
} label: {
LabelSettings()
Text("Réglages du tournoi")
Text("Formats, terrains, prix et plus")
}
}

Loading…
Cancel
Save