You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
PadelClub/PadelClub/Views/Player/PlayerDetailView.swift

428 lines
18 KiB

//
// PlayerDetailView.swift
// PadelClub
//
// Created by Razmig Sarkissian on 17/04/2024.
//
import SwiftUI
import LeStorage
import PadelClubData
struct PlayerDetailView: View {
@Environment(Tournament.self) var tournament: Tournament
@EnvironmentObject var dataStore: DataStore
@Bindable var player: PlayerRegistration
@State private var licenceId: String
@State private var phoneNumber: String
@State private var email: String
@State private var contactName: String
@State private var contactPhoneNumber: String
@State private var contactEmail: String
@FocusState var focusedField: PlayerRegistration.CodingKeys?
var tournamentStore: TournamentStore? {
return self.tournament.tournamentStore
}
init(player: PlayerRegistration) {
self.player = player
_licenceId = .init(wrappedValue: player.licenceId ?? "")
_email = .init(wrappedValue: player.email ?? "")
_phoneNumber = .init(wrappedValue: player.phoneNumber ?? "")
_contactName = .init(wrappedValue: player.contactName ?? "")
_contactEmail = .init(wrappedValue: player.contactEmail ?? "")
_contactPhoneNumber = .init(wrappedValue: player.contactPhoneNumber ?? "")
}
var body: some View {
Form {
Section {
Text(player.localizedSourceLabel().firstCapitalized)
} header: {
Text("Source des informations")
}
if tournament.enableOnlineRegistration || tournament.enableOnlinePayment || player.registeredOnline || player.hasPaidOnline() {
Section {
LabeledContent {
Text(player.registeredOnline ? "Oui" : "Non")
} label: {
Text("Inscription en ligne")
}
if player.captain && (player.hasPaidOnline() || tournament.enableOnlinePayment) {
LabeledContent {
Text(player.hasPaidOnline() ? "Oui" : "Non")
} label: {
Text("A payé pour l'équipe en ligne")
}
}
if tournament.enableTimeToConfirm {
RegistrationStatusPicker(player: player)
.onChange(of: player.registrationStatus) { player.tournamentStore?.playerRegistrations.addOrUpdate(instance: player)
}
}
if let timeToConfirm = player.timeToConfirm {
LabeledContent {
Text(timeToConfirm.formatted())
} label: {
Button("+2h") {
player.timeToConfirm = timeToConfirm.addingTimeInterval(7200)
player.tournamentStore?.playerRegistrations.addOrUpdate(instance: player)
}
}
}
} header: {
Text("Statut de l'inscription en ligne")
}
}
Section {
Toggle("Joueur sur place", isOn: $player.hasArrived)
Toggle("Capitaine", isOn: $player.captain).disabled(player.hasPaidOnline())
//Toggle("Coach", isOn: $player.coach)
Toggle(isOn: $player.clubMember) {
Text("Membre du club")
}
}
Section {
LabeledContent {
TextField("Nom", text: $player.lastName)
.keyboardType(.alphabet)
.multilineTextAlignment(.trailing)
.frame(maxWidth: .infinity)
.onSubmit(of: .text) {
player.lastName = player.lastName.prefixTrimmed(50)
_save()
}
} label: {
Text("Nom")
}
LabeledContent {
TextField("Prénom", text: $player.firstName)
.keyboardType(.alphabet)
.multilineTextAlignment(.trailing)
.frame(maxWidth: .infinity)
.onSubmit(of: .text) {
player.firstName = player.firstName.prefixTrimmed(50)
_save()
}
} label: {
Text("Prénom")
}
PlayerSexPickerView(player: player)
if let birthdate = player.birthdate {
Text(birthdate)
}
}
Section {
LabeledContent {
TextField("Rang", value: $player.rank, format: .number)
.keyboardType(.decimalPad)
.multilineTextAlignment(.trailing)
.frame(maxWidth: .infinity)
.focused($focusedField, equals: ._rank)
} label: {
Text("Rang")
}
} header: {
Text("Classement actuel")
} footer: {
if player.rank == nil {
Text("Classement calculé : " + player.computedRank.formatted())
}
}
let maxMaleUnrankedValue: Int = tournament.maleUnrankedValue ?? 90_415
if player.isMalePlayer() == false && tournament.tournamentCategory == .men && (player.rank == maxMaleUnrankedValue || player.rank == nil) {
Section {
Text("Une joueuse non classée dans un tournoi messieurs aura le rang d'un joueur non classé.")
} header: {
Text("Ré-assimilation")
}
} else if player.isMalePlayer() == false && tournament.tournamentCategory == .men, let rank = player.rank {
Section {
let value = tournament.addon(for: rank, manMax: maxMaleUnrankedValue, womanMax: tournament.femaleUnrankedValue ?? 0)
LabeledContent {
Text(value.formatted())
} label: {
Text("Valeur à rajouter")
}
LabeledContent {
TextField("Rang", value: $player.computedRank, format: .number)
.keyboardType(.decimalPad)
.multilineTextAlignment(.trailing)
.frame(maxWidth: .infinity)
.focused($focusedField, equals: ._computedRank)
} label: {
Text("Poids re-calculé")
}
} header: {
Text("Ré-assimilation")
} footer: {
Text("Calculé en fonction du sexe")
}
}
Section {
LabeledContent {
TextField("Licence", text: $licenceId)
.focused($focusedField, equals: ._licenceId)
.keyboardType(.alphabet)
.textContentType(nil)
.multilineTextAlignment(.trailing)
.autocorrectionDisabled()
.frame(maxWidth: .infinity)
.onSubmit(of: .text) {
player.licenceId = licenceId.prefixTrimmed(50)
_save()
}
} label: {
Menu {
CopyPasteButtonView(pasteValue: player.licenceId)
PasteButtonView(text: $licenceId)
.onChange(of: licenceId) {
player.licenceId = licenceId.prefixTrimmed(50)
_save()
}
} label: {
Text("Licence")
}
}
LabeledContent {
TextField("Téléphone", text: $phoneNumber)
.focused($focusedField, equals: ._phoneNumber)
.keyboardType(.namePhonePad)
.textContentType(nil)
.multilineTextAlignment(.trailing)
.autocorrectionDisabled()
.frame(maxWidth: .infinity)
.onSubmit(of: .text) {
player.phoneNumber = phoneNumber.prefixTrimmed(50)
_save()
}
} label: {
Menu {
if let number = player.phoneNumber?.replacingOccurrences(of: " ", with: "") { if let url = URL(string: "tel:\(number)") {
Link(destination: url) {
Label("\(number)", systemImage: "phone")
}
}
if let url = URL(string: "sms:\(number)") {
Link(destination: url) {
Label("\(number)", systemImage: "message")
}
}
Divider()
}
CopyPasteButtonView(pasteValue: player.phoneNumber)
PasteButtonView(text: $phoneNumber)
} label: {
Text("Téléphone")
}
}
LabeledContent {
TextField("Email", text: $email)
.focused($focusedField, equals: ._email)
.keyboardType(.emailAddress)
.textContentType(nil)
.multilineTextAlignment(.trailing)
.autocorrectionDisabled()
.frame(maxWidth: .infinity)
.onSubmit(of: .text) {
player.email = email.prefixTrimmed(50)
_save()
}
} label: {
Menu {
if let mail = player.email, let mailURL = URL(string: "mail:\(mail)") {
Link(destination: mailURL) {
Label(mail, systemImage: "mail")
}
Divider()
}
CopyPasteButtonView(pasteValue: player.email)
PasteButtonView(text: $email)
} label: {
Text("Email")
}
}
} header: {
Text("Information fédérale")
}
Section {
LabeledContent {
TextField("Contact/tuteur", text: $contactName)
.focused($focusedField, equals: ._contactName)
.keyboardType(.alphabet)
.textContentType(nil)
.multilineTextAlignment(.trailing)
.autocorrectionDisabled()
.frame(maxWidth: .infinity)
.onSubmit(of: .text) {
player.contactName = contactName.prefixTrimmed(200)
_save()
}
} label: {
Text("Contact/tuteur")
}
LabeledContent {
TextField("Téléphone", text: $contactPhoneNumber)
.focused($focusedField, equals: ._contactPhoneNumber)
.keyboardType(.namePhonePad)
.textContentType(nil)
.multilineTextAlignment(.trailing)
.autocorrectionDisabled()
.frame(maxWidth: .infinity)
.onSubmit(of: .text) {
player.contactPhoneNumber = contactPhoneNumber.prefixTrimmed(50)
_save()
}
} label: {
Menu {
if let number = player.contactPhoneNumber?.replacingOccurrences(of: " ", with: "") { if let url = URL(string: "tel:\(number)") {
Link(destination: url) {
Label("\(number)", systemImage: "phone")
}
}
if let url = URL(string: "sms:\(number)") {
Link(destination: url) {
Label(number, systemImage: "message")
}
}
Divider()
}
CopyPasteButtonView(pasteValue: player.contactPhoneNumber)
PasteButtonView(text: $contactPhoneNumber)
} label: {
Text("Téléphone")
}
}
LabeledContent {
TextField("Email", text: $contactEmail)
.focused($focusedField, equals: ._contactEmail)
.keyboardType(.emailAddress)
.textContentType(nil)
.multilineTextAlignment(.trailing)
.autocorrectionDisabled()
.frame(maxWidth: .infinity)
.onSubmit(of: .text) {
player.contactEmail = contactEmail.prefixTrimmed(50)
_save()
}
} label: {
Menu {
if let mail = player.contactEmail, let mailURL = URL(string: "mail:\(mail)") {
Link(destination: mailURL) {
Label(mail, systemImage: "mail")
}
Divider()
}
CopyPasteButtonView(pasteValue: player.contactEmail)
PasteButtonView(text: $contactEmail)
} label: {
Text("Email")
}
}
} header: {
Text("Information de contact")
} footer: {
Text("Ces champs vous permettent de garder les informations de contacts avec le joueur s'ils sont différents de ceux renvoyées par la base fédérale. Cela permet également de garder le contact d'un parent s'il s'agit d'un tournoi enfant.")
}
// Section {
// NavigationLink {
// PlayerStatisticView(player: player)
// } label: {
// Text("Statistiques de participations")
// }
// }
}
.onChange(of: [player.hasArrived, player.captain, player.coach, player.clubMember]) {
_save()
}
.onChange(of: player.sex) {
_save()
}
.navigationBarBackButtonHidden(focusedField != nil)
.headerProminence(.increased)
.navigationTitle("Édition")
.navigationBarTitleDisplayMode(.inline)
.toolbarBackground(.visible, for: .navigationBar)
.toolbar {
ToolbarItem(placement: .topBarTrailing) {
ShareLink(item: player.pasteData(type: .sharing)) {
Label("Partager", systemImage: "square.and.arrow.up")
}
}
if focusedField != nil {
ToolbarItem(placement: .topBarLeading) {
Button("Annuler", role: .cancel) {
focusedField = nil
}
}
}
if focusedField == ._rank || focusedField == ._computedRank || focusedField == ._phoneNumber {
ToolbarItemGroup(placement: .keyboard) {
Spacer()
Button("Valider") {
if focusedField == ._rank {
player.setComputedRank(in: tournament)
player.team()?.updateWeight(inTournamentCategory: tournament.tournamentCategory)
_save()
} else if focusedField == ._computedRank {
player.team()?.updateWeight(inTournamentCategory: tournament.tournamentCategory)
_save()
} else if focusedField == ._phoneNumber {
player.phoneNumber = phoneNumber.prefixTrimmed(50)
_save()
}
focusedField = nil
}
.buttonStyle(.borderedProminent)
}
}
}
}
private func _save() {
do {
try self.tournamentStore?.playerRegistrations.addOrUpdate(instance: player)
} catch {
Logger.error(error)
}
if let team = player.team() {
do {
try self.tournamentStore?.teamRegistrations.addOrUpdate(instance: team)
} catch {
Logger.error(error)
}
}
}
}
//#Preview {
// PlayerDetailView(player: PlayerRegistration.mock())
//}