fix club and court stuff

multistore
Razmig Sarkissian 1 year ago
parent 6d006d2917
commit 8e3a218979
  1. 12
      PadelClub.xcodeproj/project.pbxproj
  2. 5
      PadelClub/Views/Calling/CallMessageCustomizationView.swift
  3. 23
      PadelClub/Views/Cashier/Event/EventSettingsView.swift
  4. 43
      PadelClub/Views/Club/ClubDetailView.swift
  5. 83
      PadelClub/Views/Club/Shared/ClubCourtSetupView.swift
  6. 26
      PadelClub/Views/Event/EventCreationView.swift
  7. 55
      PadelClub/Views/Planning/PlanningSettingsView.swift
  8. 74
      PadelClub/Views/Tournament/Screen/Components/TournamentClubSettingsView.swift

@ -153,6 +153,7 @@
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 */; };
FF53FBB82BFB302B0051D4C3 /* ClubCourtSetupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF53FBB72BFB302B0051D4C3 /* ClubCourtSetupView.swift */; };
FF59FFB32B90EFAC0061EFF9 /* EventListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF59FFB22B90EFAC0061EFF9 /* EventListView.swift */; };
FF59FFB72B90EFBF0061EFF9 /* MainView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF59FFB62B90EFBF0061EFF9 /* MainView.swift */; };
FF59FFB92B90EFD70061EFF9 /* ToolboxView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF59FFB82B90EFD70061EFF9 /* ToolboxView.swift */; };
@ -469,6 +470,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>"; };
FF53FBB72BFB302B0051D4C3 /* ClubCourtSetupView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClubCourtSetupView.swift; sourceTree = "<group>"; };
FF59FFB22B90EFAC0061EFF9 /* EventListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventListView.swift; sourceTree = "<group>"; };
FF59FFB62B90EFBF0061EFF9 /* MainView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainView.swift; sourceTree = "<group>"; };
FF59FFB82B90EFD70061EFF9 /* ToolboxView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToolboxView.swift; sourceTree = "<group>"; };
@ -927,6 +929,7 @@
FFC1E10B2BAC7FB0008D6F59 /* ClubImportView.swift */,
FF5D0D882BB4935C005CB568 /* ClubRowView.swift */,
FFC91B022BD85E2400B29808 /* CourtView.swift */,
FF53FBB62BFB301A0051D4C3 /* Shared */,
);
path = Club;
sourceTree = "<group>";
@ -1055,6 +1058,14 @@
path = ViewModel;
sourceTree = "<group>";
};
FF53FBB62BFB301A0051D4C3 /* Shared */ = {
isa = PBXGroup;
children = (
FF53FBB72BFB302B0051D4C3 /* ClubCourtSetupView.swift */,
);
path = Shared;
sourceTree = "<group>";
};
FF5D30542BD95AF600F2B93D /* Ongoing */ = {
isa = PBXGroup;
children = (
@ -1533,6 +1544,7 @@
FF8F263F2BAD7D5C00650388 /* Event.swift in Sources */,
FF5D30532BD94E2E00F2B93D /* PlayerHolder.swift in Sources */,
FF11628C2BD05267000C4809 /* LoserRoundStepScheduleEditorView.swift in Sources */,
FF53FBB82BFB302B0051D4C3 /* ClubCourtSetupView.swift in Sources */,
FF089EBF2BB0B14600F0AEC7 /* FileImportView.swift in Sources */,
C4A47D9F2B7D0BCE00ADC637 /* StepperView.swift in Sources */,
FFC83D4F2BB807D100750834 /* RoundsView.swift in Sources */,

@ -192,7 +192,8 @@ struct CallMessageCustomizationView: View {
if let eventClub = tournament.eventObject()?.clubObject() {
let hasBeenCreated: Bool = eventClub.hasBeenCreated(by: dataStore.user.id)
Section {
TextField("Nom du club", text: $customClubName)
TextField("Nom du club", text: $customClubName, axis: .vertical)
.lineLimit(2)
.autocorrectionDisabled()
.focused($focusedField, equals: .clubName)
.onSubmit {
@ -204,8 +205,6 @@ struct CallMessageCustomizationView: View {
}
}
.disabled(hasBeenCreated == false)
} header: {
Text("Nom du club")
} footer: {
if hasBeenCreated == false {
Text("Édition impossible, vous n'êtes pas le créateur de ce club.").foregroundStyle(.logoRed)

@ -12,7 +12,6 @@ struct EventSettingsView: View {
@EnvironmentObject var dataStore: DataStore
@Bindable var event: Event
@State private var eventName: String = ""
@FocusState private var textFieldIsFocus: Bool
init(event: Event) {
self.event = event
@ -22,12 +21,12 @@ struct EventSettingsView: View {
var body: some View {
Form {
Section {
ZStack {
Text(eventName).hidden()
TextEditor(text: $eventName)
.focused($textFieldIsFocus)
TextField("Nom de l'événement", text: $eventName, axis: .vertical)
.lineLimit(2)
.autocorrectionDisabled()
.keyboardType(.alphabet)
.multilineTextAlignment(.leading)
.frame(maxWidth: .infinity)
.onSubmit {
if eventName.trimmed.isEmpty == false {
event.name = eventName.trimmed
@ -36,7 +35,6 @@ struct EventSettingsView: View {
}
_save()
}
}
} header: {
Text("Nom de l'événement")
} footer: {
@ -49,19 +47,6 @@ struct EventSettingsView: View {
}
}
}
.toolbar {
ToolbarItemGroup(placement: .keyboard) {
if textFieldIsFocus {
Spacer()
Button {
textFieldIsFocus = false
} label: {
Text("Valider")
}
.buttonStyle(.bordered)
}
}
}
}
private func _save() {

@ -15,6 +15,7 @@ struct ClubDetailView: View {
@State private var acronymMode: Club.AcronymMode = .automatic
@State private var city: String
@State private var zipCode: String
@State private var selectedCourt: Court?
@Bindable var club: Club
var displayContext: DisplayContext
var selection: ((Club) -> ())? = nil
@ -31,9 +32,8 @@ struct ClubDetailView: View {
var body: some View {
Form {
Section {
ZStack {
Text(club.name).hidden()
TextEditor(text: $club.name)
TextField("Nom du club", text: $club.name, axis: .vertical)
.lineLimit(2)
.autocorrectionDisabled()
.keyboardType(.alphabet)
.frame(maxWidth: .infinity)
@ -48,7 +48,7 @@ struct ClubDetailView: View {
focusedField = ._acronym
}
}
}
LabeledContent {
if acronymMode == .automatic || displayContext == .lockedForEditing {
Text(club.acronym)
@ -94,8 +94,6 @@ struct ClubDetailView: View {
club.acronym = ""
}
}
} header: {
Text("Nom du club")
} footer: {
if displayContext == .lockedForEditing {
Text("Édition impossible, vous n'êtes pas le créateur de ce club.").foregroundStyle(.logoRed)
@ -152,24 +150,7 @@ struct ClubDetailView: View {
.disabled(displayContext == .lockedForEditing)
}
Section {
TournamentFieldsManagerView(localizedStringKey: "Terrains", count: $club.courtCount)
.disabled(displayContext == .lockedForEditing)
.onChange(of: club.courtCount) {
if displayContext != .addition {
do {
try dataStore.clubs.addOrUpdate(instance: club)
} catch {
Logger.error(error)
}
}
}
} footer: {
if displayContext == .lockedForEditing {
Text("Édition impossible, vous n'êtes pas le créateur de ce club.").foregroundStyle(.logoRed)
}
}
ClubCourtSetupView(club: club, displayContext: displayContext, selectedCourt: $selectedCourt)
if let federalLink = club.federalLink() {
Section {
@ -247,18 +228,8 @@ struct ClubDetailView: View {
.toolbar(.visible, for: .navigationBar)
.headerProminence(.increased)
.toolbarBackground(.visible, for: .navigationBar)
.toolbar {
ToolbarItemGroup(placement: .keyboard) {
if focusedField == ._name {
Spacer()
Button {
focusedField = nil
} label: {
Text("Valider")
}
.buttonStyle(.bordered)
}
}
.navigationDestination(item: $selectedCourt) { court in
CourtView(court: court)
}
.onDisappear {
if displayContext == .edition {

@ -0,0 +1,83 @@
//
// ClubCourtSetupView.swift
// PadelClub
//
// Created by Razmig Sarkissian on 20/05/2024.
//
import SwiftUI
import LeStorage
struct ClubCourtSetupView: View {
@EnvironmentObject var dataStore: DataStore
@Bindable var club: Club
let displayContext: DisplayContext
@Binding var selectedCourt: Court?
@ViewBuilder
var body: some View {
Section {
TournamentFieldsManagerView(localizedStringKey: "Terrains", count: $club.courtCount)
.disabled(displayContext == .lockedForEditing)
.onChange(of: club.courtCount) {
if displayContext != .addition {
do {
try dataStore.clubs.addOrUpdate(instance: club)
} catch {
Logger.error(error)
}
}
}
} footer: {
if displayContext == .lockedForEditing {
Text("Édition impossible, vous n'êtes pas le créateur de ce club.").foregroundStyle(.logoRed)
}
}
Section {
ForEach((0..<club.courtCount), id: \.self) { courtIndex in
_courtView(atIndex: courtIndex, tournamentClub: club)
}
}
}
@ViewBuilder
private func _courtView(atIndex index: Int, tournamentClub: Club) -> some View {
let court = tournamentClub.customizedCourts.first(where: { $0.index == index })
LabeledContent {
if displayContext == .edition {
FooterButtonView("personnaliser") {
if let court {
selectedCourt = court
} else {
let newCourt = Court(index: index, club: tournamentClub.id)
do {
try dataStore.courts.addOrUpdate(instance: newCourt)
} catch {
Logger.error(error)
}
selectedCourt = newCourt
}
}
}
} label: {
if let court {
Text(court.courtTitle())
HStack {
if court.indoor {
Text("Couvert")
}
if court.exitAllowed {
Text("Sortie autorisée")
}
}
} else {
Text(_courtName(atIndex: index))
}
}
}
private func _courtName(atIndex index: Int) -> String {
Court.courtIndexedTitle(atIndex: index)
}
}

@ -20,7 +20,6 @@ struct EventCreationView: View {
@State private var eventName: String = ""
@State var tournaments: [Tournament] = []
@State var selectedClub: Club?
@FocusState private var textFieldIsFocus: Bool
let multiTournamentsEventTip = MultiTournamentsEventTip()
@ -64,16 +63,12 @@ struct EventCreationView: View {
}
}
VStack(alignment: .leading) {
Text("Nom de l'événement").font(.footnote)
ZStack {
Text(eventName).hidden()
TextEditor(text: $eventName)
.focused($textFieldIsFocus)
TextField("Nom de l'événement", text: $eventName, axis: .vertical)
.lineLimit(2)
.autocorrectionDisabled()
.keyboardType(.alphabet)
}
}
.multilineTextAlignment(.leading)
.frame(maxWidth: .infinity)
LabeledContent {
Text(tournaments.count.formatted())
@ -143,19 +138,6 @@ struct EventCreationView: View {
}
.popoverTip(multiTournamentsEventTip)
}
ToolbarItemGroup(placement: .keyboard) {
if textFieldIsFocus {
Spacer()
Button {
textFieldIsFocus = false
} label: {
Text("Valider")
}
.buttonStyle(.bordered)
}
}
}
.navigationTitle("Nouvel événement")
.navigationBarTitleDisplayMode(.inline)

@ -34,24 +34,18 @@ struct PlanningSettingsView: View {
SubscriptionInfoView()
Section {
DatePicker(tournament.startDate.formatted(.dateTime.weekday()), selection: $tournament.startDate)
DatePicker(selection: $tournament.startDate) {
Text(tournament.startDate.formatted(.dateTime.weekday(.wide)).capitalized)
}
LabeledContent {
StepperView(count: $tournament.dayDuration, minimum: 1)
} label: {
Text("Durée")
Text("\(tournament.dayDuration) jour" + tournament.dayDuration.pluralSuffix)
}
} header: {
Text("Démarrage et durée du tournoi")
}
Section {
TournamentFieldsManagerView(localizedStringKey: "Terrains maximum", count: $tournament.courtCount)
if tournament.groupStages().isEmpty == false {
TournamentFieldsManagerView(localizedStringKey: "Nombre de poule en même temps", count: $groupStageChunkCount, max: tournament.groupStageCount)
}
if let event = tournament.eventObject() {
NavigationLink {
CourtAvailabilitySettingsView(event: event)
@ -61,14 +55,44 @@ struct PlanningSettingsView: View {
}
}
} footer: {
FooterButtonView((showOptions ? "masquer" : "voir") + " les réglages avancées") {
showOptions.toggle()
if let club = tournament.club() {
if tournament.courtCount < club.courtCount {
let plural = tournament.courtCount.pluralSuffix
let verb = tournament.courtCount > 1 ? "seront" : "sera"
Text("En réduisant les terrains maximum, seul\(plural) le\(plural) \(tournament.courtCount) premier\(plural) terrain\(plural) \(verb) utilisé\(plural)") + Text(", par contre, si vous augmentez le nombre de terrains, vous pourrez plutôt préciser quel terrain n'est pas disponible.")
} else if tournament.courtCount > club.courtCount {
let isCreatedByUser = club.hasBeenCreated(by: dataStore.user.id)
Button {
do {
club.courtCount = tournament.courtCount
try dataStore.clubs.addOrUpdate(instance: club)
} catch {
Logger.error(error)
}
} label: {
if isCreatedByUser {
Text("Vous avez indiqué plus de terrains dans ce tournoi que dans le club.")
+ Text("Mettre à jour le club ?").underline().foregroundStyle(.master)
} else {
Label("Vous avez indiqué plus de terrains dans ce tournoi que dans le club.", systemImage: "exclamationmark.triangle.fill").foregroundStyle(.logoRed)
}
}
.buttonStyle(.plain)
.disabled(isCreatedByUser == false)
}
}
}
if showOptions {
NavigationLink {
List {
_optionsView()
}
.navigationTitle("Options avancées")
.navigationBarTitleDisplayMode(.inline)
.toolbarBackground(.visible, for: .navigationBar)
} label: {
Text("Options avancées")
}
Section {
RowButtonView("Horaire intelligent", role: .destructive) {
@ -132,6 +156,13 @@ struct PlanningSettingsView: View {
@ViewBuilder
private func _optionsView() -> some View {
if tournament.groupStages().isEmpty == false {
Section {
TournamentFieldsManagerView(localizedStringKey: "Poule en parallèle", count: $groupStageChunkCount, max: tournament.groupStageCount)
} footer: {
Text("Vous pouvez indiquer le nombre de poule démarrant en même temps.")
}
}
Section {
Toggle(isOn: $matchScheduler.randomizeCourts) {

@ -11,7 +11,8 @@ import LeStorage
struct TournamentClubSettingsView: View {
@Environment(Tournament.self) private var tournament: Tournament
@EnvironmentObject var dataStore: DataStore
@State var selectedCourt: Court?
@State private var selectedCourt: Court?
@State private var showClubDetail: Club?
var body: some View {
@Bindable var tournament = tournament
@ -19,13 +20,6 @@ struct TournamentClubSettingsView: View {
let event = tournament.eventObject()
let selectedClub = event?.clubObject()
Section {
if let selectedClub {
NavigationLink {
ClubDetailView(club: selectedClub, displayContext: selectedClub.hasBeenCreated(by: dataStore.user.id) ? .edition : .lockedForEditing)
} label: {
ClubRowView(club: selectedClub)
}
} else {
NavigationLink {
ClubsView() { club in
if let event {
@ -38,82 +32,42 @@ struct TournamentClubSettingsView: View {
}
}
} label: {
if let selectedClub {
ClubRowView(club: selectedClub)
} else {
Text("Choisir un club")
}
}
} header: {
Text("Lieu du tournoi")
} footer: {
if let event, selectedClub != nil {
HStack {
Spacer()
Button("modifier", role: .destructive) {
event.club = nil
try? dataStore.events.addOrUpdate(instance: event)
}
FooterButtonView("détails du club") {
showClubDetail = selectedClub
}
}
}
Section {
TournamentFieldsManagerView(localizedStringKey: "Terrains maximum", count: $tournament.courtCount)
}
if let selectedClub {
Section {
ForEach((0..<tournament.courtCount), id: \.self) { courtIndex in
_courtView(atIndex: courtIndex, tournamentClub: selectedClub)
}
}
}
}
.onChange(of: tournament.courtCount) {
ClubCourtSetupView(club: selectedClub, displayContext: selectedClub.hasBeenCreated(by: dataStore.user.id) ? .edition : .lockedForEditing, selectedCourt: $selectedCourt)
.onChange(of: selectedClub.courtCount) {
tournament.courtCount = max(tournament.courtCount, selectedClub.courtCount)
do {
try dataStore.tournaments.addOrUpdate(instance: tournament)
} catch {
Logger.error(error)
}
}
.navigationDestination(item: $selectedCourt) { court in
CourtView(court: court)
}
}
@ViewBuilder
private func _courtView(atIndex index: Int, tournamentClub: Club) -> some View {
if let court = tournamentClub.customizedCourts.first(where: { $0.index == index }) {
LabeledContent {
FooterButtonView("personnaliser") {
selectedCourt = court
}
} label: {
Text(court.courtTitle())
HStack {
if court.indoor {
Text("Couvert")
}
if court.exitAllowed {
Text("Sortie autorisée")
}
}
.navigationDestination(item: $showClubDetail) { club in
ClubDetailView(club: club, displayContext: club.hasBeenCreated(by: dataStore.user.id) ? .edition : .lockedForEditing)
}
} else {
LabeledContent {
FooterButtonView("personnaliser") {
let court = Court(index: index, club: tournamentClub.id)
try? dataStore.courts.addOrUpdate(instance: court)
selectedCourt = court
}
} label: {
Text(_courtName(atIndex: index))
}
}
.navigationDestination(item: $selectedCourt) { court in
CourtView(court: court)
}
private func _courtName(atIndex index: Int) -> String {
Court.courtIndexedTitle(atIndex: index)
}
}
#Preview {

Loading…
Cancel
Save