multistore
Razmig Sarkissian 1 year ago
parent 92f623c2b4
commit f97531a767
  1. 4
      PadelClub.xcodeproj/project.pbxproj
  2. 8
      PadelClub/Data/Tournament.swift
  3. 12
      PadelClub/Extensions/String+Extensions.swift
  4. 1
      PadelClub/PadelClubApp.swift
  5. 8
      PadelClub/Utils/ContactManager.swift
  6. 8
      PadelClub/Views/Calling/CallMessageCustomizationView.swift
  7. 7
      PadelClub/Views/Calling/CallSettingsView.swift
  8. 4
      PadelClub/Views/Calling/SendToAllView.swift
  9. 2
      PadelClub/Views/Cashier/CashierView.swift
  10. 2
      PadelClub/Views/Navigation/Agenda/CalendarView.swift
  11. 2
      PadelClub/Views/Navigation/Ongoing/OngoingView.swift
  12. 2
      PadelClub/Views/Shared/LearnMoreSheetView.swift
  13. 56
      PadelClub/Views/Tournament/Screen/Components/TournamentGeneralSettingsView.swift
  14. 18
      PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift
  15. 2
      PadelClub/Views/Tournament/Screen/TournamentSettingsView.swift
  16. 2
      PadelClub/Views/Tournament/TournamentView.swift

@ -1919,7 +1919,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 16; CURRENT_PROJECT_VERSION = 17;
DEFINES_MODULE = YES; DEFINES_MODULE = YES;
DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\""; DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\"";
DEVELOPMENT_TEAM = BQ3Y44M3Q6; DEVELOPMENT_TEAM = BQ3Y44M3Q6;
@ -1957,7 +1957,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 16; CURRENT_PROJECT_VERSION = 17;
DEFINES_MODULE = YES; DEFINES_MODULE = YES;
DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\""; DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\"";
DEVELOPMENT_TEAM = BQ3Y44M3Q6; DEVELOPMENT_TEAM = BQ3Y44M3Q6;

@ -762,6 +762,10 @@ class Tournament : ModelObject, Storable {
Store.main.filter { $0.tournament == self.id && $0.walkOut == false } Store.main.filter { $0.tournament == self.id && $0.walkOut == false }
} }
func walkoutTeams() -> [TeamRegistration] {
Store.main.filter { $0.tournament == self.id && $0.walkOut == true }
}
func duplicates(in players: [PlayerRegistration]) -> [PlayerRegistration] { func duplicates(in players: [PlayerRegistration]) -> [PlayerRegistration] {
var duplicates = [PlayerRegistration]() var duplicates = [PlayerRegistration]()
Set(players.compactMap({ $0.licenceId })).forEach { licenceId in Set(players.compactMap({ $0.licenceId })).forEach { licenceId in
@ -1029,7 +1033,7 @@ class Tournament : ModelObject, Storable {
} }
func tournamentTitle(_ displayStyle: DisplayStyle = .wide) -> String { func tournamentTitle(_ displayStyle: DisplayStyle = .wide) -> String {
[tournamentLevel.localizedLabel(displayStyle), tournamentCategory.localizedLabel(displayStyle), name].compactMap({ $0 }).joined(separator: " ") [tournamentLevel.localizedLabel(.wide) + " " + tournamentCategory.localizedLabel(.wide), displayStyle == .wide ? name : nil].compactMap({ $0 }).joined(separator: " - ")
} }
func subtitle(_ displayStyle: DisplayStyle = .wide) -> String { func subtitle(_ displayStyle: DisplayStyle = .wide) -> String {
@ -1723,7 +1727,7 @@ extension Tournament: FederalTournamentHolder {
extension Tournament: TournamentBuildHolder { extension Tournament: TournamentBuildHolder {
func buildHolderTitle() -> String { func buildHolderTitle() -> String {
tournamentTitle() tournamentTitle(.short)
} }
var category: TournamentCategory { var category: TournamentCategory {

@ -174,3 +174,15 @@ extension StringProtocol {
extension LosslessStringConvertible { extension LosslessStringConvertible {
var string: String { .init(self) } var string: String { .init(self) }
} }
extension String {
func createTxtFile(_ withName: String = "temp") -> URL {
let url = FileManager.default.temporaryDirectory
.appendingPathComponent(withName)
.appendingPathExtension("txt")
let string = self
try? FileManager.default.removeItem(at: url)
try? string.write(to: url, atomically: true, encoding: .utf8)
return url
}
}

@ -22,6 +22,7 @@ struct PadelClubApp: App {
.environment(navigationViewModel) .environment(navigationViewModel)
.accentColor(.master) .accentColor(.master)
.onAppear { .onAppear {
networkMonitor.checkConnection()
self._onAppear() self._onAppear()
} }
.task { .task {

@ -31,7 +31,7 @@ enum ContactType: Identifiable {
extension ContactType { extension ContactType {
static let defaultCustomMessage = "Il est conseillé de vous présenter 10 minutes avant de jouer.\nMerci de me confirmer votre présence avec votre nom et de prévenir votre partenaire." static let defaultCustomMessage = "Il est conseillé de vous présenter 10 minutes avant de jouer.\nMerci de me confirmer votre présence avec votre nom et de prévenir votre partenaire."
static let defaultAvailablePaymentMethods = "Règlement possible par chèque ou espèce." static let defaultAvailablePaymentMethods = "Règlement possible par chèque ou espèces."
static func callingCustomMessage(source: String? = nil, tournament: Tournament?, startDate: Date?, roundLabel: String) -> String { static func callingCustomMessage(source: String? = nil, tournament: Tournament?, startDate: Date?, roundLabel: String) -> String {
let tournamentCustomMessage = source ?? DataStore.shared.user.summonsMessageBody ?? defaultCustomMessage let tournamentCustomMessage = source ?? DataStore.shared.user.summonsMessageBody ?? defaultCustomMessage
@ -41,7 +41,7 @@ extension ContactType {
let date = startDate ?? tournament?.startDate ?? Date() let date = startDate ?? tournament?.startDate ?? Date()
if let tournament { if let tournament {
text = text.replacingOccurrences(of: "#titre", with: tournament.tournamentTitle()) text = text.replacingOccurrences(of: "#titre", with: tournament.tournamentTitle(.short))
text = text.replacingOccurrences(of: "#prix", with: tournament.entryFeeMessage) text = text.replacingOccurrences(of: "#prix", with: tournament.entryFeeMessage)
} }
@ -77,11 +77,11 @@ extension ContactType {
} }
var computedMessage: String { var computedMessage: String {
[entryFeeMessage, message].compacted().map { $0.trimmed }.joined(separator: "\n") [entryFeeMessage, message].compacted().map { $0.trimmed }.joined(separator: "\n\n")
} }
if let tournament { if let tournament {
return "Bonjour,\n\nVous êtes \(localizedCalled) pour jouer en \(roundLabel.lowercased()) du \(tournament.tournamentTitle()) au \(clubName) le \(date.formatted(Date.FormatStyle().weekday(.wide).day().month(.wide))) à \(date.formatted(Date.FormatStyle().hour().minute())).\n\n" + computedMessage + "\n\n\(signature)" return "Bonjour,\n\nVous êtes \(localizedCalled) pour jouer en \(roundLabel.lowercased()) du \(tournament.tournamentTitle(.short)) au \(clubName) le \(date.formatted(Date.FormatStyle().weekday(.wide).day().month(.wide))) à \(date.formatted(Date.FormatStyle().hour().minute())).\n\n" + computedMessage + "\n\n\(signature)"
} else { } else {
return "Bonjour,\n\nVous êtes \(localizedCalled) \(roundLabel) au \(clubName) le \(date.formatted(Date.FormatStyle().weekday(.wide).day().month(.wide))) à \(date.formatted(Date.FormatStyle().hour().minute())).\n\nMerci de confirmer en répondant à ce message et de prévenir votre partenaire !\n\n\(signature)" return "Bonjour,\n\nVous êtes \(localizedCalled) \(roundLabel) au \(clubName) le \(date.formatted(Date.FormatStyle().weekday(.wide).day().month(.wide))) à \(date.formatted(Date.FormatStyle().hour().minute())).\n\nMerci de confirmer en répondant à ce message et de prévenir votre partenaire !\n\n\(signature)"
} }

@ -48,7 +48,7 @@ struct CallMessageCustomizationView: View {
var finalMessage: String? { var finalMessage: String? {
let localizedCalled = "convoqué" + (tournament.tournamentCategory == .women ? "e" : "") + "s" let localizedCalled = "convoqué" + (tournament.tournamentCategory == .women ? "e" : "") + "s"
return "Bonjour,\n\nVous êtes \(localizedCalled) pour jouer en \(RoundRule.roundName(fromRoundIndex: 2).lowercased()) du \(tournament.tournamentTitle()) au \(clubName) le \(tournament.startDate.formatted(Date.FormatStyle().weekday(.wide).day().month(.wide))) à \(tournament.startDate.formatted(Date.FormatStyle().hour().minute())).\n\n" + computedMessage + "\n\n\(customCallMessageSignature)" return "Bonjour,\n\nVous êtes \(localizedCalled) pour jouer en \(RoundRule.roundName(fromRoundIndex: 2).lowercased()) du \(tournament.tournamentTitle(.short)) au \(clubName) le \(tournament.startDate.formatted(Date.FormatStyle().weekday(.wide).day().month(.wide))) à \(tournament.startDate.formatted(Date.FormatStyle().hour().minute())).\n\n" + computedMessage + "\n\n\(customCallMessageSignature)"
} }
var body: some View { var body: some View {
@ -103,9 +103,6 @@ struct CallMessageCustomizationView: View {
Spacer() Spacer()
Button { Button {
focusedField = nil focusedField = nil
user.summonsMessageBody = customCallMessageBody
user.summonsMessageSignature = customCallMessageSignature
user.summonsAvailablePaymentMethods = summonsAvailablePaymentMethods
_save() _save()
} label: { } label: {
Text("Valider") Text("Valider")
@ -129,6 +126,9 @@ struct CallMessageCustomizationView: View {
} }
private func _save() { private func _save() {
self.dataStore.user.summonsMessageBody = customCallMessageBody
self.dataStore.user.summonsMessageSignature = customCallMessageSignature
self.dataStore.user.summonsAvailablePaymentMethods = summonsAvailablePaymentMethods
self.dataStore.saveUser() self.dataStore.saveUser()
} }

@ -34,7 +34,6 @@ struct CallSettingsView: View {
Section { Section {
RowButtonView("Envoyer le lien du tournoi") { RowButtonView("Envoyer le lien du tournoi") {
addLink = true addLink = true
showSendToAllView = true
} }
.disabled(tournament.isPrivate) .disabled(tournament.isPrivate)
} footer: { } footer: {
@ -75,7 +74,11 @@ struct CallSettingsView: View {
#endif #endif
} }
.sheet(isPresented: $showSendToAllView) { .sheet(isPresented: $showSendToAllView) {
SendToAllView(addLink: addLink) SendToAllView(addLink: false)
.tint(.master)
}
.sheet(isPresented: $addLink) {
SendToAllView(addLink: true)
.tint(.master) .tint(.master)
} }
} }

@ -72,9 +72,9 @@ struct SendToAllView: View {
Section { Section {
RowButtonView("Contacter \(_totalString())") { RowButtonView("Contacter \(_totalString())") {
if contactMethod == 0 { if contactMethod == 0 {
contactType = .message(date: nil, recipients: _teams().flatMap { $0.unsortedPlayers() }.compactMap { $0.phoneNumber }, body: tournament.shareURL()?.absoluteString , tournamentBuild: nil) contactType = .message(date: nil, recipients: _teams().flatMap { $0.unsortedPlayers() }.compactMap { $0.phoneNumber }, body: addLink ? tournament.shareURL()?.absoluteString : nil, tournamentBuild: nil)
} else { } else {
contactType = .mail(date: nil, recipients: tournament.umpireMail(), bccRecipients: _teams().flatMap { $0.unsortedPlayers() }.compactMap { $0.email }, body: tournament.shareURL()?.absoluteString, subject: tournament.tournamentTitle(), tournamentBuild: nil) contactType = .mail(date: nil, recipients: tournament.umpireMail(), bccRecipients: _teams().flatMap { $0.unsortedPlayers() }.compactMap { $0.email }, body: addLink ? tournament.shareURL()?.absoluteString : nil, subject: tournament.tournamentTitle(), tournamentBuild: nil)
} }
} }
} }

@ -152,7 +152,7 @@ struct CashierView: View {
.searchable(text: $searchText, isPresented: $isSearching, prompt: Text("Chercher un joueur")) .searchable(text: $searchText, isPresented: $isSearching, prompt: Text("Chercher un joueur"))
.toolbar { .toolbar {
ToolbarItem(placement: .topBarTrailing) { ToolbarItem(placement: .topBarTrailing) {
ShareLink(item: _sharedData()) ShareLink(item: _sharedData().createTxtFile("bilan"))
} }
} }
} }

@ -85,7 +85,7 @@ struct CalendarView: View {
ForEach(tournamentsByDay, id: \.holderId) { tournamentHolder in ForEach(tournamentsByDay, id: \.holderId) { tournamentHolder in
if let tournament = tournamentHolder as? Tournament { if let tournament = tournamentHolder as? Tournament {
Section { Section {
Button(tournament.tournamentTitle()) { Button(tournament.tournamentTitle(.short)) {
navigation.path.append(tournament) navigation.path.append(tournament)
} }
} header: { } header: {

@ -32,7 +32,7 @@ struct OngoingView: View {
} header: { } header: {
if let tournament = match.currentTournament() { if let tournament = match.currentTournament() {
HStack { HStack {
Text(tournament.tournamentTitle()) Text(tournament.tournamentTitle(.short))
Spacer() Spacer()
if let club = tournament.club() { if let club = tournament.club() {
Text("@" + club.clubTitle(.short)) Text("@" + club.clubTitle(.short))

@ -28,7 +28,7 @@ struct LearnMoreSheetView: View {
.foregroundStyle(.secondary) .foregroundStyle(.secondary)
ShareLink(item: tournament.pasteDataForImporting()) { ShareLink(item: tournament.pasteDataForImporting().createTxtFile(tournament.tournamentTitle(.short))) {
HStack { HStack {
Spacer() Spacer()
Text("Exporter les inscriptions") Text("Exporter les inscriptions")

@ -9,12 +9,19 @@ import SwiftUI
import LeStorage import LeStorage
struct TournamentGeneralSettingsView: View { struct TournamentGeneralSettingsView: View {
@Environment(Tournament.self) private var tournament: Tournament
@EnvironmentObject var dataStore: DataStore @EnvironmentObject var dataStore: DataStore
var tournament: Tournament
@State private var tournamentName: String = "" @State private var tournamentName: String = ""
@FocusState private var textFieldIsFocus: Bool @State private var entryFee: Double? = nil
@FocusState private var focusedField: Tournament.CodingKeys?
init(tournament: Tournament) {
self.tournament = tournament
_tournamentName = State(wrappedValue: tournament.name ?? "")
_entryFee = State(wrappedValue: tournament.entryFee)
}
var body: some View { var body: some View {
@Bindable var tournament = tournament @Bindable var tournament = tournament
Form { Form {
@ -29,44 +36,43 @@ struct TournamentGeneralSettingsView: View {
Section { Section {
LabeledContent { LabeledContent {
TextField(tournament.isFree() ? "Gratuite" : "Inscription", value: $tournament.entryFee, format: .currency(code: Locale.current.currency?.identifier ?? "EUR")) TextField(tournament.isFree() ? "Gratuite" : "Inscription", value: $entryFee, format: .currency(code: Locale.current.currency?.identifier ?? "EUR"))
.keyboardType(.decimalPad) .keyboardType(.decimalPad)
.multilineTextAlignment(.trailing) .multilineTextAlignment(.trailing)
.frame(maxWidth: .infinity) .frame(maxWidth: .infinity)
.focused($textFieldIsFocus) .focused($focusedField, equals: ._entryFee)
} label: { } label: {
Text("Inscription") Text("Inscription")
} }
} }
Section { Section {
LabeledContent { TextField("Nom du tournoi", text: $tournamentName, axis: .vertical)
TextField("Nom", text: $tournamentName) .lineLimit(2)
.multilineTextAlignment(.trailing) .frame(maxWidth: .infinity)
.frame(maxWidth: .infinity) .keyboardType(.alphabet)
.keyboardType(.alphabet) .autocorrectionDisabled()
.autocorrectionDisabled() .focused($focusedField, equals: ._name)
.onSubmit {
if tournamentName.trimmed.isEmpty {
tournament.name = nil
} else {
tournament.name = tournamentName
}
}
} label: {
Text("Nom du tournoi")
}
} }
} }
.scrollDismissesKeyboard(.immediately) .scrollDismissesKeyboard(.immediately)
.toolbarBackground(.visible, for: .navigationBar) .toolbarBackground(.visible, for: .navigationBar)
.toolbar { .toolbar {
if textFieldIsFocus { if focusedField != nil {
ToolbarItem(placement: .keyboard) { ToolbarItem(placement: .keyboard) {
HStack { HStack {
Spacer() Spacer()
Button("Valider") { Button("Valider") {
textFieldIsFocus = false if focusedField == ._name {
if tournamentName.trimmed.isEmpty {
tournament.name = nil
} else {
tournament.name = tournamentName
}
} else if focusedField == ._entryFee {
tournament.entryFee = entryFee
}
focusedField = nil
} }
.buttonStyle(.bordered) .buttonStyle(.bordered)
} }
@ -115,7 +121,3 @@ struct TournamentGeneralSettingsView: View {
} }
} }
} }
#Preview {
TournamentGeneralSettingsView()
}

@ -249,7 +249,7 @@ struct InscriptionManagerView: View {
Label("Clôturer", systemImage: "lock") Label("Clôturer", systemImage: "lock")
} }
Divider() Divider()
ShareLink(item: tournament.pasteDataForImporting()) { ShareLink(item: tournament.pasteDataForImporting().createTxtFile(self.tournament.tournamentTitle(.short))) {
Label("Exporter les paires", systemImage: "square.and.arrow.up") Label("Exporter les paires", systemImage: "square.and.arrow.up")
} }
Button { Button {
@ -531,15 +531,19 @@ struct InscriptionManagerView: View {
private func _informationView(count: Int) -> some View { private func _informationView(count: Int) -> some View {
Section { Section {
let unsortedTeamsWithoutWO = tournament.unsortedTeamsWithoutWO() let unsortedTeams = tournament.unsortedTeams()
let walkoutTeams = tournament.walkoutTeams()
LabeledContent { LabeledContent {
Text(unsortedTeamsWithoutWO.count.formatted() + "/" + tournament.teamCount.formatted()) Text(unsortedTeams.count.formatted() + "/" + tournament.teamCount.formatted()).font(.largeTitle)
} label: { } label: {
Text("Paires inscrites") Text("Paire\(unsortedTeams.count.pluralSuffix) inscrite\(unsortedTeams.count.pluralSuffix)")
Text("dont \(walkoutTeams.count.pluralSuffix) forfaite\(walkoutTeams.count.pluralSuffix)")
} }
let unsortedTeamsWithoutWO = tournament.unsortedTeamsWithoutWO()
LabeledContent { LabeledContent {
Text(max(0, unsortedTeamsWithoutWO.count - tournament.teamCount).formatted()) Text(max(0, unsortedTeamsWithoutWO.count - tournament.teamCount).formatted()).font(.largeTitle)
} label: { } label: {
Text("Liste d'attente") Text("Liste d'attente")
} }
@ -549,7 +553,7 @@ struct InscriptionManagerView: View {
.environment(tournament) .environment(tournament)
} label: { } label: {
LabeledContent { LabeledContent {
Text(tournament.registrationIssues().formatted()) Text(tournament.registrationIssues().formatted()).font(.largeTitle)
} label: { } label: {
Text("Problèmes détéctés") Text("Problèmes détéctés")
if let closedRegistrationDate = tournament.closedRegistrationDate { if let closedRegistrationDate = tournament.closedRegistrationDate {

@ -67,7 +67,7 @@ struct TournamentSettingsView: View {
case .matchFormats: case .matchFormats:
TournamentMatchFormatsSettingsView() TournamentMatchFormatsSettingsView()
case .general: case .general:
TournamentGeneralSettingsView() TournamentGeneralSettingsView(tournament: tournament)
case .club: case .club:
TournamentClubSettingsView() TournamentClubSettingsView()
} }

@ -121,7 +121,7 @@ struct TournamentView: View {
.toolbar { .toolbar {
ToolbarItem(placement: .principal) { ToolbarItem(placement: .principal) {
VStack(spacing: -4.0) { VStack(spacing: -4.0) {
Text(tournament.tournamentTitle()).font(.headline) Text(tournament.tournamentTitle(.short)).font(.headline)
Text(tournament.formattedDate()) Text(tournament.formattedDate())
.font(.subheadline).foregroundStyle(.secondary) .font(.subheadline).foregroundStyle(.secondary)
} }

Loading…
Cancel
Save