Merge branch 'main'

sync3
Razmig Sarkissian 1 month ago
commit b8680eeea1
  1. 24
      PadelClub.xcodeproj/project.pbxproj
  2. 41
      PadelClub/Utils/Network/PaymentService.swift
  3. 2
      PadelClub/Views/Calling/CallMessageCustomizationView.swift
  4. 64
      PadelClub/Views/Cashier/CashierSettingsView.swift
  5. 12
      PadelClub/Views/Cashier/Event/EventCreationView.swift
  6. 32
      PadelClub/Views/Cashier/Event/EventSettingsView.swift
  7. 11
      PadelClub/Views/Club/ClubDetailView.swift
  8. 13
      PadelClub/Views/Components/StepperView.swift
  9. 40
      PadelClub/Views/Navigation/Umpire/UmpireView.swift
  10. 28
      PadelClub/Views/Player/Components/PlayerPopoverView.swift
  11. 5
      PadelClub/Views/Player/PlayerDetailView.swift
  12. 4
      PadelClub/Views/Team/EditingTeamView.swift
  13. 51
      PadelClub/Views/Team/PaymentRequestButton.swift
  14. 24
      PadelClub/Views/Team/PaymentService.swift
  15. 3
      PadelClub/Views/Tournament/Screen/AddTeamView.swift
  16. 120
      PadelClub/Views/Tournament/Screen/Components/TournamentGeneralSettingsView.swift
  17. 99
      PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift
  18. 24
      PadelClub/Views/Tournament/Screen/RegistrationSetupView.swift

@ -155,6 +155,12 @@
FF2B51612C7E302C00FFF126 /* local.sqlite in Resources */ = {isa = PBXBuildFile; fileRef = FF2B51602C7E302C00FFF126 /* local.sqlite */; };
FF2B6F5E2C036A1500835EE7 /* EventLinksView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF2B6F5D2C036A1400835EE7 /* EventLinksView.swift */; };
FF2EFBF02BDE295E0049CE3B /* SendToAllView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF2EFBEF2BDE295E0049CE3B /* SendToAllView.swift */; };
FF30ACED2E8D700B008B6006 /* PaymentService.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF30ACEC2E8D700B008B6006 /* PaymentService.swift */; };
FF30ACEE2E8D700B008B6006 /* PaymentService.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF30ACEC2E8D700B008B6006 /* PaymentService.swift */; };
FF30ACEF2E8D700B008B6006 /* PaymentService.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF30ACEC2E8D700B008B6006 /* PaymentService.swift */; };
FF30ACF12E8D7078008B6006 /* PaymentRequestButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF30ACF02E8D7078008B6006 /* PaymentRequestButton.swift */; };
FF30ACF22E8D7078008B6006 /* PaymentRequestButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF30ACF02E8D7078008B6006 /* PaymentRequestButton.swift */; };
FF30ACF32E8D7078008B6006 /* PaymentRequestButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF30ACF02E8D7078008B6006 /* PaymentRequestButton.swift */; };
FF3795622B9396D0004EA093 /* PadelClubApp.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = FF3795602B9396D0004EA093 /* PadelClubApp.xcdatamodeld */; };
FF3795662B9399AA004EA093 /* Persistence.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF3795652B9399AA004EA093 /* Persistence.swift */; };
FF39B6152DC8825E004E10CE /* PadelClubData.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C49770202DC25A23005CD239 /* PadelClubData.framework */; };
@ -717,6 +723,9 @@
FFA97C9E2E8A7C080089EA22 /* View+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFA97C9D2E8A7C080089EA22 /* View+Extensions.swift */; };
FFA97C9F2E8A7C080089EA22 /* View+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFA97C9D2E8A7C080089EA22 /* View+Extensions.swift */; };
FFA97CA02E8A7C080089EA22 /* View+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFA97C9D2E8A7C080089EA22 /* View+Extensions.swift */; };
FFA97CA22E8BAC880089EA22 /* RankingGroupStageSetupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFA97CA12E8BAC880089EA22 /* RankingGroupStageSetupView.swift */; };
FFA97CA32E8BAC880089EA22 /* RankingGroupStageSetupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFA97CA12E8BAC880089EA22 /* RankingGroupStageSetupView.swift */; };
FFA97CA42E8BAC880089EA22 /* RankingGroupStageSetupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFA97CA12E8BAC880089EA22 /* RankingGroupStageSetupView.swift */; };
FFB0FF672E81B671009EDEAC /* OnboardingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFB0FF662E81B671009EDEAC /* OnboardingView.swift */; };
FFB0FF682E81B671009EDEAC /* OnboardingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFB0FF662E81B671009EDEAC /* OnboardingView.swift */; };
FFB0FF692E81B671009EDEAC /* OnboardingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFB0FF662E81B671009EDEAC /* OnboardingView.swift */; };
@ -1019,6 +1028,8 @@
FF2B51622C7F073100FFF126 /* Model_1_1.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = Model_1_1.xcdatamodel; sourceTree = "<group>"; };
FF2B6F5D2C036A1400835EE7 /* EventLinksView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventLinksView.swift; sourceTree = "<group>"; };
FF2EFBEF2BDE295E0049CE3B /* SendToAllView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SendToAllView.swift; sourceTree = "<group>"; };
FF30ACEC2E8D700B008B6006 /* PaymentService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaymentService.swift; sourceTree = "<group>"; };
FF30ACF02E8D7078008B6006 /* PaymentRequestButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaymentRequestButton.swift; sourceTree = "<group>"; };
FF3795612B9396D0004EA093 /* Model.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = Model.xcdatamodel; sourceTree = "<group>"; };
FF3795652B9399AA004EA093 /* Persistence.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Persistence.swift; sourceTree = "<group>"; };
FF39B60F2DC87FEB004E10CE /* PadelClubData.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = PadelClubData.framework; sourceTree = BUILT_PRODUCTS_DIR; };
@ -1126,6 +1137,7 @@
FFA6D78A2BB0BEB3003A31F3 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
FFA97C8C2E8A59D00089EA22 /* TournamentPickerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TournamentPickerView.swift; sourceTree = "<group>"; };
FFA97C9D2E8A7C080089EA22 /* View+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+Extensions.swift"; sourceTree = "<group>"; };
FFA97CA12E8BAC880089EA22 /* RankingGroupStageSetupView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RankingGroupStageSetupView.swift; sourceTree = "<group>"; };
FFB0FF662E81B671009EDEAC /* OnboardingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingView.swift; sourceTree = "<group>"; };
FFB0FF722E841042009EDEAC /* WeekdaySelectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeekdaySelectionView.swift; sourceTree = "<group>"; };
FFB1C98A2C10255100B154A7 /* TournamentBroadcastRowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TournamentBroadcastRowView.swift; sourceTree = "<group>"; };
@ -1762,6 +1774,7 @@
FF4AB6B42B9248200002987F /* NetworkManager.swift */,
FFC1E1092BAC2A77008D6F59 /* NetworkFederalService.swift */,
FFE8B5BA2DA9896800BDE966 /* RefundService.swift */,
FF30ACEC2E8D700B008B6006 /* PaymentService.swift */,
FFE8B5C62DAA390000BDE966 /* StripeValidationService.swift */,
FFE8B5CA2DAA429E00BDE966 /* XlsToCsvService.swift */,
FFE8B6392DACEAEC00BDE966 /* ConfigurationService.swift */,
@ -1814,6 +1827,7 @@
FF967CFB2BAEE13900A9A3BD /* GroupStagesView.swift */,
FF5DA18E2BB9268800A33061 /* GroupStagesSettingsView.swift */,
FF6525C22C8C61B400B9498E /* LoserBracketFromGroupStageView.swift */,
FFA97CA12E8BAC880089EA22 /* RankingGroupStageSetupView.swift */,
FF9AC3932BE3625D00C2E883 /* Components */,
FF9AC3922BE3625200C2E883 /* Shared */,
);
@ -1842,6 +1856,7 @@
FF1162862BD004AD000C4809 /* EditingTeamView.swift */,
FF17CA562CC02FEA003C7323 /* CoachListView.swift */,
FF7DCD382CC330260041110C /* TeamRestingView.swift */,
FF30ACF02E8D7078008B6006 /* PaymentRequestButton.swift */,
FF025AD62BD0C0FB00A86CF8 /* Components */,
);
path = Team;
@ -2280,6 +2295,7 @@
C4A47DB32B86387500ADC637 /* AccountView.swift in Sources */,
FFCEDA4C2C2C08EA00F8C0F2 /* PlayersWithoutContactView.swift in Sources */,
FF17CA4F2CB9243E003C7323 /* FollowUpMatchView.swift in Sources */,
FF30ACEF2E8D700B008B6006 /* PaymentService.swift in Sources */,
FFCD16B32C3E5E590092707B /* TeamsCallingView.swift in Sources */,
FF1162832BCFBE4E000C4809 /* EditablePlayerView.swift in Sources */,
FF1162852BD00279000C4809 /* PlayerDetailView.swift in Sources */,
@ -2314,6 +2330,7 @@
FF025AED2BD1513700A86CF8 /* AppScreen.swift in Sources */,
FFC91B032BD85E2400B29808 /* CourtView.swift in Sources */,
FFCFC00E2BBC3D4600B82851 /* PointSelectionView.swift in Sources */,
FF30ACF22E8D7078008B6006 /* PaymentRequestButton.swift in Sources */,
FF089EB62BB00A3800F0AEC7 /* TeamRowView.swift in Sources */,
C40CD2F32C412681000DBD9A /* AppDelegate.swift in Sources */,
FFCFC00C2BBC3D1E00B82851 /* EditScoreView.swift in Sources */,
@ -2450,6 +2467,7 @@
FF967D032BAEF0C000A9A3BD /* MatchDetailView.swift in Sources */,
FFE8B5B32DA848D300BDE966 /* OnlineWaitingListFaqSheetView.swift in Sources */,
FF967D0F2BAF63B000A9A3BD /* PlayerBlockView.swift in Sources */,
FFA97CA32E8BAC880089EA22 /* RankingGroupStageSetupView.swift in Sources */,
FFCFC0122BBC3E1A00B82851 /* PointView.swift in Sources */,
FF1CBC232BB53E590036DAAB /* ClubHolder.swift in Sources */,
FFBF41862BF75FDA001B24CB /* EventSettingsView.swift in Sources */,
@ -2549,6 +2567,7 @@
FFA252AF2CDB734A0074E63F /* UmpireStatisticView.swift in Sources */,
FF4CBF6F2C996C0600151637 /* ListRowViewModifier.swift in Sources */,
FF4CBF702C996C0600151637 /* PresentationContext.swift in Sources */,
FF30ACEE2E8D700B008B6006 /* PaymentService.swift in Sources */,
FF4CBF722C996C0600151637 /* SwiftParser.swift in Sources */,
FF4CBF732C996C0600151637 /* ChangePasswordView.swift in Sources */,
FF4CBF742C996C0600151637 /* TournamentSubscriptionView.swift in Sources */,
@ -2583,6 +2602,7 @@
FF4CBF882C996C0600151637 /* TeamRowView.swift in Sources */,
FF4CBF8A2C996C0600151637 /* AppDelegate.swift in Sources */,
FF4CBF8C2C996C0600151637 /* EditScoreView.swift in Sources */,
FF30ACF12E8D7078008B6006 /* PaymentRequestButton.swift in Sources */,
FF4CBF8D2C996C0600151637 /* TournamentOrganizerView.swift in Sources */,
FF4CBF8F2C996C0600151637 /* TournamentRunningView.swift in Sources */,
FF4CBF902C996C0600151637 /* TournamentDatePickerView.swift in Sources */,
@ -2719,6 +2739,7 @@
FF4CC0182C996C0600151637 /* ClubHolder.swift in Sources */,
FF4CC0192C996C0600151637 /* EventSettingsView.swift in Sources */,
C49771E72DC25F04005CD239 /* Color+Extensions.swift in Sources */,
FFA97CA22E8BAC880089EA22 /* RankingGroupStageSetupView.swift in Sources */,
C49771E82DC25F04005CD239 /* Badge+Extensions.swift in Sources */,
C49771E92DC25F04005CD239 /* SpinDrawable+Extensions.swift in Sources */,
FF4CC01A2C996C0600151637 /* InscriptionInfoView.swift in Sources */,
@ -2796,6 +2817,7 @@
FFA252AD2CDB734A0074E63F /* UmpireStatisticView.swift in Sources */,
FF70FAEE2C90584900129CC2 /* ListRowViewModifier.swift in Sources */,
FF70FAEF2C90584900129CC2 /* PresentationContext.swift in Sources */,
FF30ACED2E8D700B008B6006 /* PaymentService.swift in Sources */,
FF70FAF12C90584900129CC2 /* SwiftParser.swift in Sources */,
FF70FAF22C90584900129CC2 /* ChangePasswordView.swift in Sources */,
FF70FAF32C90584900129CC2 /* TournamentSubscriptionView.swift in Sources */,
@ -2830,6 +2852,7 @@
FF70FB072C90584900129CC2 /* TeamRowView.swift in Sources */,
FF70FB092C90584900129CC2 /* AppDelegate.swift in Sources */,
FF70FB0B2C90584900129CC2 /* EditScoreView.swift in Sources */,
FF30ACF32E8D7078008B6006 /* PaymentRequestButton.swift in Sources */,
FF70FB0C2C90584900129CC2 /* TournamentOrganizerView.swift in Sources */,
FF70FB0E2C90584900129CC2 /* TournamentRunningView.swift in Sources */,
FF70FB0F2C90584900129CC2 /* TournamentDatePickerView.swift in Sources */,
@ -2966,6 +2989,7 @@
FF70FB972C90584900129CC2 /* ClubHolder.swift in Sources */,
FF70FB982C90584900129CC2 /* EventSettingsView.swift in Sources */,
C49771E42DC25F04005CD239 /* Color+Extensions.swift in Sources */,
FFA97CA42E8BAC880089EA22 /* RankingGroupStageSetupView.swift in Sources */,
C49771E52DC25F04005CD239 /* Badge+Extensions.swift in Sources */,
C49771E62DC25F04005CD239 /* SpinDrawable+Extensions.swift in Sources */,
FF70FB992C90584900129CC2 /* InscriptionInfoView.swift in Sources */,

@ -0,0 +1,41 @@
//
// PaymentService.swift
// PadelClub
//
// Created by Razmig Sarkissian on 01/10/2025.
//
import Foundation
import LeStorage
import PadelClubData
class PaymentService {
static func resendPaymentEmail(teamRegistrationId: String) async throws -> SimpleResponse {
let service = try StoreCenter.main.service()
let urlRequest = try service._baseRequest(
servicePath: "resend-payment-email/\(teamRegistrationId)/",
method: .post,
requiresToken: true
)
let (data, response) = try await URLSession.shared.data(for: urlRequest)
guard let httpResponse = response as? HTTPURLResponse,
httpResponse.statusCode == 200 else {
throw PaymentError.requestFailed
}
return try JSON.decoder.decode(SimpleResponse.self, from: data)
}
}
enum PaymentError: Error {
case requestFailed
case unauthorized
case unknown
}
struct SimpleResponse: Codable {
let success: Bool
let message: String
}

@ -126,7 +126,7 @@ struct CallMessageCustomizationView: View {
} label: {
Text("Valider")
}
.buttonStyle(.bordered)
.buttonStyle(.borderedProminent)
}
}
}

@ -127,49 +127,47 @@ struct CashierSettingsView: View {
}
}
ToolbarItem(placement: .keyboard) {
HStack {
if focusedField == ._entryFee {
if tournament.isFree() {
ForEach(priceTags, id: \.self) { priceTag in
Button(priceTag.formatted(.currency(code: tournament.defaultCurrency()))) {
entryFee = priceTag
tournament.entryFee = priceTag
focusedField = nil
}
.buttonStyle(.bordered)
}
} else {
Button("Gratuit") {
entryFee = nil
tournament.entryFee = nil
ToolbarItemGroup(placement: .keyboard) {
if focusedField == ._entryFee {
if tournament.isFree() {
ForEach(priceTags, id: \.self) { priceTag in
Button(priceTag.formatted(.currency(code: tournament.defaultCurrency()))) {
entryFee = priceTag
tournament.entryFee = priceTag
focusedField = nil
}
.buttonStyle(.bordered)
}
} else if focusedField == ._clubMemberFeeDeduction {
ForEach(deductionTags, id: \.self) { deductionTag in
Button(deductionTag.formatted(.currency(code: tournament.defaultCurrency()).precision(.fractionLength(0)))) {
clubMemberFeeDeduction = deductionTag
tournament.clubMemberFeeDeduction = deductionTag
focusedField = nil
}
.buttonStyle(.bordered)
.buttonStyle(.borderedProminent)
}
} else {
Button("Gratuit") {
clubMemberFeeDeduction = entryFee
tournament.clubMemberFeeDeduction = clubMemberFeeDeduction
entryFee = nil
tournament.entryFee = nil
focusedField = nil
}
.buttonStyle(.bordered)
.buttonStyle(.borderedProminent)
}
Spacer()
Button("Valider") {
} else if focusedField == ._clubMemberFeeDeduction {
ForEach(deductionTags, id: \.self) { deductionTag in
Button(deductionTag.formatted(.currency(code: tournament.defaultCurrency()).precision(.fractionLength(0)))) {
clubMemberFeeDeduction = deductionTag
tournament.clubMemberFeeDeduction = deductionTag
focusedField = nil
}
.buttonStyle(.borderedProminent)
}
Button("Gratuit") {
clubMemberFeeDeduction = entryFee
tournament.clubMemberFeeDeduction = clubMemberFeeDeduction
focusedField = nil
}
.buttonStyle(.bordered)
.buttonStyle(.borderedProminent)
}
Spacer()
Button("Valider") {
focusedField = nil
}
.buttonStyle(.borderedProminent)
}
}
}

@ -73,14 +73,12 @@ struct EventCreationView: View {
.focused($textFieldIsFocus)
.toolbar {
if textFieldIsFocus {
ToolbarItem(placement: .keyboard) {
HStack {
Spacer()
Button("Valider") {
textFieldIsFocus = false
}
.buttonStyle(.bordered)
ToolbarItemGroup(placement: .keyboard) {
Spacer()
Button("Valider") {
textFieldIsFocus = false
}
.buttonStyle(.borderedProminent)
}
}
}

@ -187,26 +187,24 @@ struct EventSettingsView: View {
}
if focusedField != nil {
ToolbarItem(placement: .keyboard) {
HStack {
if focusedField == ._name, eventName.isEmpty == false {
Button("Effacer") {
event.name = nil
eventName = ""
}
.buttonStyle(.borderless)
} else if focusedField == ._information, tournamentInformation.isEmpty == false {
Button("Effacer") {
tournamentInformation = ""
}
.buttonStyle(.borderless)
ToolbarItemGroup(placement: .keyboard) {
if focusedField == ._name, eventName.isEmpty == false {
Button("Effacer") {
event.name = nil
eventName = ""
}
Spacer()
Button("Valider") {
focusedField = nil
.buttonStyle(.borderedProminent)
} else if focusedField == ._information, tournamentInformation.isEmpty == false {
Button("Effacer") {
tournamentInformation = ""
}
.buttonStyle(.bordered)
.buttonStyle(.borderedProminent)
}
Spacer()
Button("Valider") {
focusedField = nil
}
.buttonStyle(.borderedProminent)
}
}
}

@ -223,13 +223,12 @@ struct ClubDetailView: View {
.navigationBarBackButtonHidden(focusedField != nil)
.toolbar(content: {
if focusedField != nil {
ToolbarItem(placement: .keyboard) {
HStack {
Button("Fermer", role: .cancel) {
focusedField = nil
}
Spacer()
ToolbarItemGroup(placement: .keyboard) {
Button("Fermer", role: .cancel) {
focusedField = nil
}
.buttonStyle(.borderedProminent)
Spacer()
}
}
})

@ -67,15 +67,14 @@ struct StepperView: View {
}
.multilineTextAlignment(.trailing)
.toolbar {
ToolbarItem(placement: .keyboard) {
ToolbarItemGroup(placement: .keyboard) {
if amountIsFocused {
HStack {
Spacer()
Button("Confirmer") {
amountIsFocused = false
_validate()
}
Spacer()
Button("Confirmer") {
amountIsFocused = false
_validate()
}
.buttonStyle(.borderedProminent)
}
}
}

@ -327,30 +327,28 @@ struct UmpireView: View {
})
.toolbar {
if focusedField != nil {
ToolbarItem(placement: .keyboard) {
HStack {
if focusedField == ._umpireCustomMail, umpireCustomMail.isEmpty == false {
Button("Effacer") {
_deleteUmpireMail()
}
.buttonStyle(.borderless)
} else if focusedField == ._umpireCustomPhone, umpireCustomPhone.isEmpty == false {
Button("Effacer") {
_deleteUmpirePhone()
}
.buttonStyle(.borderless)
} else if focusedField == ._umpireCustomContact, umpireCustomContact.isEmpty == false {
Button("Effacer") {
_deleteUmpireContact()
}
.buttonStyle(.borderless)
ToolbarItemGroup(placement: .keyboard) {
if focusedField == ._umpireCustomMail, umpireCustomMail.isEmpty == false {
Button("Effacer") {
_deleteUmpireMail()
}
Spacer()
Button("Valider") {
focusedField = nil
.buttonStyle(.borderedProminent)
} else if focusedField == ._umpireCustomPhone, umpireCustomPhone.isEmpty == false {
Button("Effacer") {
_deleteUmpirePhone()
}
.buttonStyle(.bordered)
.buttonStyle(.borderedProminent)
} else if focusedField == ._umpireCustomContact, umpireCustomContact.isEmpty == false {
Button("Effacer") {
_deleteUmpireContact()
}
.buttonStyle(.borderedProminent)
}
Spacer()
Button("Valider") {
focusedField = nil
}
.buttonStyle(.borderedProminent)
}
}
}

@ -202,27 +202,25 @@ struct PlayerPopoverView: View {
}
if licenseIsFocused || amountIsFocused {
ToolbarItem(placement: .keyboard) {
HStack {
Spacer()
Button("Confirmer") {
if licenseIsFocused {
license = license.trimmedMultiline
if requiredField.contains(.license) {
if license.isLicenseNumber {
amountIsFocused = true
} else {
displayWrongLicenceError = true
}
} else {
ToolbarItemGroup(placement: .keyboard) {
Spacer()
Button("Confirmer") {
if licenseIsFocused {
license = license.trimmedMultiline
if requiredField.contains(.license) {
if license.isLicenseNumber {
amountIsFocused = true
} else {
displayWrongLicenceError = true
}
} else {
amountIsFocused = false
amountIsFocused = true
}
} else {
amountIsFocused = false
}
.buttonStyle(.bordered)
}
.buttonStyle(.borderedProminent)
}
}
}

@ -286,7 +286,7 @@ struct PlayerDetailView: View {
}
LabeledContent {
TextField("Téléphone contact", text: $contactPhoneNumber)
TextField("Téléphone", text: $contactPhoneNumber)
.focused($focusedField, equals: ._contactPhoneNumber)
.keyboardType(.namePhonePad)
.textContentType(nil)
@ -320,7 +320,7 @@ struct PlayerDetailView: View {
}
LabeledContent {
TextField("Email contact", text: $contactEmail)
TextField("Email", text: $contactEmail)
.focused($focusedField, equals: ._contactEmail)
.keyboardType(.emailAddress)
.textContentType(nil)
@ -401,6 +401,7 @@ struct PlayerDetailView: View {
}
focusedField = nil
}
.buttonStyle(.borderedProminent)
}
}
}

@ -140,6 +140,10 @@ struct EditingTeamView: View {
} label: {
Text("Payé en ligne")
}
if team.hasPaidOnline() == false {
PaymentRequestButton(teamRegistration: team)
}
}
if let refundMessage, refundMessage.isEmpty == false {

@ -0,0 +1,51 @@
//
// PaymentRequestButton.swift
// PadelClub
//
// Created by Razmig Sarkissian on 01/10/2025.
//
import SwiftUI
import PadelClubData
struct PaymentRequestButton: View {
let teamRegistration: TeamRegistration
@State private var isLoading = false
@State private var showAlert = false
@State private var alertMessage = ""
var body: some View {
Button("Renvoyer email de paiement") {
resendEmail()
}
.disabled(isLoading)
.alert("Résultat", isPresented: $showAlert) {
Button("OK") { }
} message: {
Text(alertMessage)
}
}
private func resendEmail() {
isLoading = true
Task {
do {
let response = try await PaymentService.resendPaymentEmail(
teamRegistrationId: teamRegistration.id
)
await MainActor.run {
isLoading = false
alertMessage = response.message
showAlert = true
}
} catch {
await MainActor.run {
isLoading = false
alertMessage = "Erreur lors de l'envoi"
showAlert = true
}
}
}
}
}

@ -0,0 +1,24 @@
class PaymentService {
static func resendPaymentEmail(teamRegistrationId: String) async throws -> SimpleResponse {
let service = try StoreCenter.main.service()
let urlRequest = try service._baseRequest(
servicePath: "resend-payment-email/\(teamRegistrationId)/",
method: .post,
requiresToken: true
)
let (data, response) = try await URLSession.shared.data(for: urlRequest)
guard let httpResponse = response as? HTTPURLResponse,
httpResponse.statusCode == 200 else {
throw PaymentError.requestFailed
}
return try JSON.decoder.decode(SimpleResponse.self, from: data)
}
}
struct SimpleResponse: Codable {
let success: Bool
let message: String
}

@ -461,6 +461,7 @@ struct AddTeamView: View {
self.editableTextField = pasteString
self.focusedField = nil
}
.buttonStyle(.borderedProminent)
Spacer()
Button("Chercher") {
if editableTextField.count > 1 {
@ -470,7 +471,7 @@ struct AddTeamView: View {
self.displayWarningNotEnoughCharacter = true
}
}
.buttonStyle(.bordered)
.buttonStyle(.borderedProminent)
}
}
} header: {

@ -217,78 +217,76 @@ struct TournamentGeneralSettingsView: View {
.toolbarBackground(.visible, for: .navigationBar)
.toolbar {
if focusedField != nil {
ToolbarItem(placement: .keyboard) {
HStack {
if focusedField == ._entryFee {
if tournament.isFree() {
ForEach(priceTags, id: \.self) { priceTag in
Button(priceTag.formatted(.currency(code: tournament.defaultCurrency()).precision(.fractionLength(0)))) {
entryFee = priceTag
tournament.entryFee = priceTag
focusedField = nil
}
.buttonStyle(.bordered)
}
} else {
Button("Gratuit") {
entryFee = nil
tournament.entryFee = nil
focusedField = nil
}
.buttonStyle(.bordered)
}
} else if focusedField == ._clubMemberFeeDeduction {
ForEach(deductionTags, id: \.self) { deductionTag in
Button(deductionTag.formatted(.currency(code: tournament.defaultCurrency()).precision(.fractionLength(0)))) {
clubMemberFeeDeduction = deductionTag
tournament.clubMemberFeeDeduction = deductionTag
ToolbarItemGroup(placement: .keyboard) {
if focusedField == ._entryFee {
if tournament.isFree() {
ForEach(priceTags, id: \.self) { priceTag in
Button(priceTag.formatted(.currency(code: tournament.defaultCurrency()).precision(.fractionLength(0)))) {
entryFee = priceTag
tournament.entryFee = priceTag
focusedField = nil
}
.buttonStyle(.bordered)
.buttonStyle(.borderedProminent)
}
} else {
Button("Gratuit") {
clubMemberFeeDeduction = entryFee
tournament.clubMemberFeeDeduction = clubMemberFeeDeduction
entryFee = nil
tournament.entryFee = nil
focusedField = nil
}
.buttonStyle(.bordered)
} else {
if focusedField == ._name, tournamentName.isEmpty == false {
Button("Effacer") {
tournament.name = nil
tournamentName = ""
}
.buttonStyle(.borderless)
} else if focusedField == ._information, tournamentInformation.isEmpty == false {
Button("Effacer") {
tournament.information = nil
tournamentInformation = ""
}
.buttonStyle(.borderless)
} else if focusedField == ._umpireCustomMail, umpireCustomMail.isEmpty == false {
Button("Effacer") {
_deleteUmpireMail()
}
.buttonStyle(.borderless)
} else if focusedField == ._umpireCustomPhone, umpireCustomPhone.isEmpty == false {
Button("Effacer") {
_deleteUmpirePhone()
}
.buttonStyle(.borderless)
} else if focusedField == ._umpireCustomContact, umpireCustomContact.isEmpty == false {
Button("Effacer") {
_deleteUmpireContact()
}
.buttonStyle(.borderless)
.buttonStyle(.borderedProminent)
}
} else if focusedField == ._clubMemberFeeDeduction {
ForEach(deductionTags, id: \.self) { deductionTag in
Button(deductionTag.formatted(.currency(code: tournament.defaultCurrency()).precision(.fractionLength(0)))) {
clubMemberFeeDeduction = deductionTag
tournament.clubMemberFeeDeduction = deductionTag
focusedField = nil
}
.buttonStyle(.borderedProminent)
}
Spacer()
Button("Valider") {
Button("Gratuit") {
clubMemberFeeDeduction = entryFee
tournament.clubMemberFeeDeduction = clubMemberFeeDeduction
focusedField = nil
}
.buttonStyle(.bordered)
.buttonStyle(.borderedProminent)
} else {
if focusedField == ._name, tournamentName.isEmpty == false {
Button("Effacer") {
tournament.name = nil
tournamentName = ""
}
.buttonStyle(.borderless)
} else if focusedField == ._information, tournamentInformation.isEmpty == false {
Button("Effacer") {
tournament.information = nil
tournamentInformation = ""
}
.buttonStyle(.borderedProminent)
} else if focusedField == ._umpireCustomMail, umpireCustomMail.isEmpty == false {
Button("Effacer") {
_deleteUmpireMail()
}
.buttonStyle(.borderedProminent)
} else if focusedField == ._umpireCustomPhone, umpireCustomPhone.isEmpty == false {
Button("Effacer") {
_deleteUmpirePhone()
}
.buttonStyle(.borderedProminent)
} else if focusedField == ._umpireCustomContact, umpireCustomContact.isEmpty == false {
Button("Effacer") {
_deleteUmpireContact()
}
.buttonStyle(.borderedProminent)
}
}
Spacer()
Button("Valider") {
focusedField = nil
}
.buttonStyle(.borderedProminent)
}
}
}

@ -54,7 +54,10 @@ struct InscriptionManagerView: View {
@State private var refreshResult: String? = nil
@State private var refreshStatus: Bool?
@State private var showLegendView: Bool = false
@State private var isLoading = false
@State private var showAlert = false
@State private var alertMessage = ""
var tournamentStore: TournamentStore? {
return self.tournament.tournamentStore
}
@ -335,6 +338,11 @@ struct InscriptionManagerView: View {
}
.tint(.master)
}
.alert("Requête de paiement", isPresented: $showAlert) {
Button("OK") { }
} message: {
Text(alertMessage)
}
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Menu {
@ -373,6 +381,40 @@ struct InscriptionManagerView: View {
ToolbarItem(placement: .navigationBarTrailing) {
Menu {
#if PRODTEST
if tournament.enableOnlinePayment {
Button {
isLoading = true
Task {
do {
try await selectedSortedTeams.filter { team in
team.hasPaidOnline() == false && team.hasPaid() == false
}.concurrentForEach { team in
_ = try await PaymentService.resendPaymentEmail(teamRegistrationId: team.id)
}
await MainActor.run {
isLoading = false
alertMessage = "Relance effectuée avec succès"
showAlert = true
}
} catch {
Logger.error(error)
await MainActor.run {
isLoading = false
alertMessage = "Erreur lors de la requête"
showAlert = true
}
}
}
} label: {
Text("Requête de paiement")
}
.disabled(isLoading)
}
#endif
if tournament.inscriptionClosed() == false {
Menu {
_sortingTypePickerView()
@ -396,7 +438,7 @@ struct InscriptionManagerView: View {
if tournament.isAnimation() == false {
Divider()
Section {
Menu {
Button("+1 en tableau") {
tournament.addWildCard(1, .bracket)
_setHash()
@ -408,7 +450,7 @@ struct InscriptionManagerView: View {
_setHash()
}
}
} header: {
} label: {
Text("Ajout de wildcards")
}
@ -421,21 +463,29 @@ struct InscriptionManagerView: View {
}
Divider()
Button {
presentImportView = true
Menu {
Button {
presentImportView = true
} label: {
Label("Importer un fichier", systemImage: "square.and.arrow.down")
}
Link(destination: URLs.beachPadel.url) {
Label("beach-padel.app.fft.fr", systemImage: "safari")
}
} label: {
Label("Importer beach-padel", systemImage: "square.and.arrow.down")
}
Link(destination: URLs.beachPadel.url) {
Label("beach-padel.app.fft.fr", systemImage: "safari")
Text("Beach Padel")
}
if tournament.inscriptionClosed() == false {
Divider()
_importTeamsMenuView(title: "Importer des paires")
_sharingTeamsMenuView()
Menu {
_importTeamsMenuView(title: "Importer des paires d'un autre tournoi")
_sharingTeamsMenuView()
} label: {
Text("Importer / Exporter")
}
} else {
_sharingTeamsMenuView()
@ -459,19 +509,20 @@ struct InscriptionManagerView: View {
}
//rankingDateSourcePickerView(showDateInLabel: true)
Divider()
_sharingTeamsMenuView()
Divider()
_importTeamsMenuView(title: "Importer des paires")
Button {
presentImportView = true
Menu {
_sharingTeamsMenuView()
_importTeamsMenuView(title: "Importer des paires d'un autre tournoi")
Button {
presentImportView = true
} label: {
Label("Importer un fichier", systemImage: "square.and.arrow.down")
}
} label: {
Label("Importer un fichier", systemImage: "square.and.arrow.down")
Text("Importer / Exporter")
}
}
} label: {
@ -741,7 +792,7 @@ struct InscriptionManagerView: View {
.pickerStyle(.menu)
if currentRankSourceDate == SourceFileManager.shared.mostRecentDateAvailable {
Button("Rafraîchir") {
Button("Rafraîchir le rang des joueurs") {
confirmUpdateRank = true
}
}

@ -331,20 +331,18 @@ struct RegistrationSetupView: View {
})
.toolbar {
if focusedField == ._stripeAccountId, stripeAccountId.isEmpty == false {
ToolbarItem(placement: .keyboard) {
HStack {
Button("Effacer") {
stripeAccountId = ""
stripeAccountIdIsInvalid = nil
tournament.stripeAccountId = nil
}
.buttonStyle(.borderless)
Spacer()
Button("Valider") {
focusedField = nil
}
.buttonStyle(.bordered)
ToolbarItemGroup(placement: .keyboard) {
Button("Effacer") {
stripeAccountId = ""
stripeAccountIdIsInvalid = nil
tournament.stripeAccountId = nil
}
.buttonStyle(.borderedProminent)
Spacer()
Button("Valider") {
focusedField = nil
}
.buttonStyle(.borderedProminent)
}
}
}

Loading…
Cancel
Save