enhance tournament settings screen

multistore
Razmig Sarkissian 2 years ago
parent d85e22c9ff
commit f13a71675d
  1. 12
      PadelClub.xcodeproj/project.pbxproj
  2. 4
      PadelClub/Data/GroupStage.swift
  3. 4
      PadelClub/Data/Round.swift
  4. 2
      PadelClub/ViewModel/AgendaDestination.swift
  5. 31
      PadelClub/ViewModel/Selectable.swift
  6. 6
      PadelClub/Views/Components/GenericDestinationPickerView.swift
  7. 2
      PadelClub/Views/GroupStage/GroupStagesView.swift
  8. 68
      PadelClub/Views/Tournament/Screen/Components/TournamentClubSettingsView.swift
  9. 78
      PadelClub/Views/Tournament/Screen/Components/TournamentGeneralSettingsView.swift
  10. 20
      PadelClub/Views/Tournament/Screen/Components/TournamentMatchFormatsSettingsView.swift
  11. 6
      PadelClub/Views/Tournament/Screen/TournamentCallView.swift
  12. 8
      PadelClub/Views/Tournament/Screen/TournamentCashierView.swift
  13. 2
      PadelClub/Views/Tournament/Screen/TournamentScheduleView.swift
  14. 153
      PadelClub/Views/Tournament/Screen/TournamentSettingsView.swift

@ -39,6 +39,9 @@
FF025ADB2BD0C2D000A86CF8 /* MatchTeamDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF025ADA2BD0C2D000A86CF8 /* MatchTeamDetailView.swift */; };
FF025ADD2BD0C94300A86CF8 /* FooterButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF025ADC2BD0C94300A86CF8 /* FooterButtonView.swift */; };
FF025ADF2BD0CE0A00A86CF8 /* TeamWeightView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF025ADE2BD0CE0A00A86CF8 /* TeamWeightView.swift */; };
FF025AE12BD0EB9000A86CF8 /* TournamentClubSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF025AE02BD0EB9000A86CF8 /* TournamentClubSettingsView.swift */; };
FF025AE32BD0EBA900A86CF8 /* TournamentMatchFormatsSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF025AE22BD0EBA900A86CF8 /* TournamentMatchFormatsSettingsView.swift */; };
FF025AE52BD0EBB800A86CF8 /* TournamentGeneralSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF025AE42BD0EBB800A86CF8 /* TournamentGeneralSettingsView.swift */; };
FF089EB42BB0020000F0AEC7 /* PlayerSexPickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF089EB32BB0020000F0AEC7 /* PlayerSexPickerView.swift */; };
FF089EB62BB00A3800F0AEC7 /* TeamRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF089EB52BB00A3800F0AEC7 /* TeamRowView.swift */; };
FF089EBB2BB0120700F0AEC7 /* PlayerPopoverView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF089EBA2BB0120700F0AEC7 /* PlayerPopoverView.swift */; };
@ -321,6 +324,9 @@
FF025ADA2BD0C2D000A86CF8 /* MatchTeamDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MatchTeamDetailView.swift; sourceTree = "<group>"; };
FF025ADC2BD0C94300A86CF8 /* FooterButtonView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FooterButtonView.swift; sourceTree = "<group>"; };
FF025ADE2BD0CE0A00A86CF8 /* TeamWeightView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TeamWeightView.swift; sourceTree = "<group>"; };
FF025AE02BD0EB9000A86CF8 /* TournamentClubSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TournamentClubSettingsView.swift; sourceTree = "<group>"; };
FF025AE22BD0EBA900A86CF8 /* TournamentMatchFormatsSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TournamentMatchFormatsSettingsView.swift; sourceTree = "<group>"; };
FF025AE42BD0EBB800A86CF8 /* TournamentGeneralSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TournamentGeneralSettingsView.swift; sourceTree = "<group>"; };
FF089EB32BB0020000F0AEC7 /* PlayerSexPickerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerSexPickerView.swift; sourceTree = "<group>"; };
FF089EB52BB00A3800F0AEC7 /* TeamRowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TeamRowView.swift; sourceTree = "<group>"; };
FF089EBA2BB0120700F0AEC7 /* PlayerPopoverView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerPopoverView.swift; sourceTree = "<group>"; };
@ -1006,6 +1012,9 @@
FF8F26492BAE0B4100650388 /* TournamentLevelPickerView.swift */,
FF0EC5212BB173E70056B6D1 /* UpdateSourceRankDateView.swift */,
FF5D0D772BB42C5B005CB568 /* InscriptionInfoView.swift */,
FF025AE02BD0EB9000A86CF8 /* TournamentClubSettingsView.swift */,
FF025AE22BD0EBA900A86CF8 /* TournamentMatchFormatsSettingsView.swift */,
FF025AE42BD0EBB800A86CF8 /* TournamentGeneralSettingsView.swift */,
);
path = Components;
sourceTree = "<group>";
@ -1374,6 +1383,7 @@
FF967D062BAF3C4200A9A3BD /* MatchSetupView.swift in Sources */,
FF4AB6B52B9248200002987F /* NetworkManager.swift in Sources */,
FFB9C8752BBADDF700A0EF4F /* SeedInterval.swift in Sources */,
FF025AE12BD0EB9000A86CF8 /* TournamentClubSettingsView.swift in Sources */,
FFBF065C2BBD2657009D6715 /* GroupStageTeamView.swift in Sources */,
FF5DA1932BB9279B00A33061 /* RoundSettingsView.swift in Sources */,
FF025ADF2BD0CE0A00A86CF8 /* TeamWeightView.swift in Sources */,
@ -1460,6 +1470,7 @@
FF6EC90B2B947AC000EA7F5A /* Array+Extensions.swift in Sources */,
FF59FFB92B90EFD70061EFF9 /* ToolboxView.swift in Sources */,
FFF8ACD92B923F3C008466FA /* String+Extensions.swift in Sources */,
FF025AE52BD0EBB800A86CF8 /* TournamentGeneralSettingsView.swift in Sources */,
FFC2DCB22BBE75D40046DB9F /* LoserBracketView.swift in Sources */,
FF9267FC2BCE84870080F940 /* PlayerPayView.swift in Sources */,
FFA6D7852BB0B795003A31F3 /* FileImportManager.swift in Sources */,
@ -1475,6 +1486,7 @@
FF5D0D8B2BB4D1E3005CB568 /* CalendarView.swift in Sources */,
FF1CBC1F2BB53E0C0036DAAB /* FederalTournamentSearchScope.swift in Sources */,
FF8F26472BAE0ACB00650388 /* TournamentFieldsManagerView.swift in Sources */,
FF025AE32BD0EBA900A86CF8 /* TournamentMatchFormatsSettingsView.swift in Sources */,
FF11628A2BD05247000C4809 /* DateUpdateManagerView.swift in Sources */,
FFCFC01A2BBC5A8500B82851 /* MatchTypeSmallSelectionView.swift in Sources */,
FF967D0B2BAF3D4C00A9A3BD /* TeamPickerView.swift in Sources */,

@ -281,7 +281,7 @@ extension GroupStage: Selectable {
runningMatches().count
}
func badgeImage() -> String? {
hasEnded() ? "checkmark.circle.fill" : nil
func badgeImage() -> Badge? {
hasEnded() ? .checkmark : nil
}
}

@ -419,7 +419,7 @@ extension Round: Selectable {
}
}
func badgeImage() -> String? {
hasEnded() ? "checkmark.circle.fill" : nil
func badgeImage() -> Badge? {
hasEnded() ? .checkmark : nil
}
}

@ -56,7 +56,7 @@ enum AgendaDestination: CaseIterable, Identifiable, Selectable {
}
}
func badgeImage() -> String? {
func badgeImage() -> Badge? {
nil
}
}

@ -6,9 +6,38 @@
//
import Foundation
import SwiftUI
protocol Selectable {
func selectionLabel() -> String
func badgeValue() -> Int?
func badgeImage() -> String?
func badgeImage() -> Badge?
}
enum Badge {
case checkmark
case xmark
case custom(systemName: String, color: Color)
func systemName() -> String {
switch self {
case .checkmark:
return "checkmark.circle.fill"
case .xmark:
return "xmark.circle.fill"
case .custom(let systemName, _):
return systemName
}
}
func color() -> Color {
switch self {
case .checkmark:
.green
case .xmark:
.red
case .custom(_, let color):
color
}
}
}

@ -45,9 +45,9 @@ struct GenericDestinationPickerView<T: Identifiable & Selectable>: View {
}
.buttonStyle(.plain)
.overlay(alignment: .bottomTrailing) {
if let image = destination.badgeImage() {
Image(systemName: image)
.foregroundColor(.green)
if let badge = destination.badgeImage() {
Image(systemName: badge.systemName())
.foregroundColor(badge.color())
.imageScale(.medium)
.background (
Color(.systemBackground)

@ -42,7 +42,7 @@ struct GroupStagesView: View {
}
}
func badgeImage() -> String? {
func badgeImage() -> Badge? {
nil
}
}

@ -0,0 +1,68 @@
//
// TournamentClubSettingsView.swift
// PadelClub
//
// Created by Razmig Sarkissian on 18/04/2024.
//
import SwiftUI
struct TournamentClubSettingsView: View {
@Environment(Tournament.self) private var tournament: Tournament
@EnvironmentObject var dataStore: DataStore
var body: some View {
@Bindable var tournament = tournament
List {
let event = tournament.eventObject
let selectedClub = event?.clubObject
Section {
if let selectedClub {
NavigationLink {
ClubDetailView(club: selectedClub, displayContext: .edition)
} label: {
ClubRowView(club: selectedClub)
}
} else {
NavigationLink {
ClubsView() { club in
if let event {
event.club = club.id
try? dataStore.events.addOrUpdate(instance: event)
} else {
let event = Event(club: club.id)
tournament.event = event.id
try? dataStore.events.addOrUpdate(instance: event)
}
}
} label: {
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)
}
}
}
}
Section {
TournamentFieldsManagerView(localizedStringKey: "Terrains maximum", count: $tournament.courtCount, max: 100)
}
}
.onDisappear {
try? dataStore.tournaments.addOrUpdate(instance: tournament)
}
}
}
#Preview {
TournamentClubSettingsView()
}

@ -0,0 +1,78 @@
//
// TournamentGeneralSettingsView.swift
// PadelClub
//
// Created by Razmig Sarkissian on 18/04/2024.
//
import SwiftUI
struct TournamentGeneralSettingsView: View {
@Environment(Tournament.self) private var tournament: Tournament
@EnvironmentObject var dataStore: DataStore
@State private var tournamentName: String = ""
@FocusState private var textFieldIsFocus: Bool
var body: some View {
@Bindable var tournament = tournament
Form {
Section {
TournamentDatePickerView()
TournamentDurationManagerView()
}
Section {
TournamentLevelPickerView()
}
Section {
LabeledContent {
TextField(tournament.isFree() ? "Gratuite" : "Inscription", value: $tournament.entryFee, format: .currency(code: Locale.current.currency?.identifier ?? "EUR"))
.keyboardType(.decimalPad)
.multilineTextAlignment(.trailing)
.frame(maxWidth: .infinity)
} label: {
Text("Inscription")
}
}
Section {
LabeledContent {
TextField("Nom", text: $tournamentName)
.multilineTextAlignment(.trailing)
.frame(maxWidth: .infinity)
.keyboardType(.alphabet)
.autocorrectionDisabled()
.onSubmit {
if tournamentName.trimmed.isEmpty {
tournament.name = nil
} else {
tournament.name = tournamentName
}
}
} label: {
Text("Nom du tournoi")
}
}
}
.focused($textFieldIsFocus)
.scrollDismissesKeyboard(.immediately)
.navigationTitle("Réglages")
.toolbarBackground(.visible, for: .navigationBar)
.toolbar {
ToolbarItem(placement: .keyboard) {
Button("Valider") {
textFieldIsFocus = false
}
}
}
.onDisappear {
try? dataStore.tournaments.addOrUpdate(instance: tournament)
}
}
}
#Preview {
TournamentGeneralSettingsView()
}

@ -0,0 +1,20 @@
//
// TournamentMatchFormatsSettingsView.swift
// PadelClub
//
// Created by Razmig Sarkissian on 18/04/2024.
//
import SwiftUI
struct TournamentMatchFormatsSettingsView: View {
var body: some View {
List {
TournamentFormatSelectionView()
}
}
}
#Preview {
TournamentMatchFormatsSettingsView()
}

@ -40,14 +40,14 @@ enum CallDestination: Identifiable, Selectable {
}
}
func badgeImage() -> String? {
func badgeImage() -> Badge? {
switch self {
case .seeds(let tournament):
let allSeedCalled = tournament.seeds().allSatisfy({ tournament.isStartDateIsDifferentThanCallDate($0) == false })
return allSeedCalled ? "checkmark.circle.fill" : nil
return allSeedCalled ? .checkmark : nil
case .groupStages(let tournament):
let allSeedCalled = tournament.groupStageTeams().allSatisfy({ tournament.isStartDateIsDifferentThanCallDate($0) == false })
return allSeedCalled ? "checkmark.circle.fill" : nil
return allSeedCalled ? .checkmark : nil
}
}

@ -50,16 +50,16 @@ enum CashierDestination: Identifiable, Selectable {
}
}
func badgeImage() -> String? {
func badgeImage() -> Badge? {
switch self {
case .summary:
return nil
case .groupStage(let groupStage):
return groupStage.unsortedPlayers().allSatisfy({ $0.hasPaid() }) ? "checkmark.circle.fill" : nil
return groupStage.unsortedPlayers().allSatisfy({ $0.hasPaid() }) ? .checkmark : nil
case .bracket(let round):
return round.seeds().flatMap { $0.unsortedPlayers() }.allSatisfy({ $0.hasPaid() }) ? "checkmark.circle.fill" : nil
return round.seeds().flatMap { $0.unsortedPlayers() }.allSatisfy({ $0.hasPaid() }) ? .checkmark : nil
case .all(let tournament):
return tournament.selectedPlayers().allSatisfy({ $0.hasPaid() }) ? "checkmark.circle.fill" : nil
return tournament.selectedPlayers().allSatisfy({ $0.hasPaid() }) ? .checkmark : nil
}
}

@ -42,7 +42,7 @@ enum ScheduleDestination: String, Identifiable, Selectable {
nil
}
func badgeImage() -> String? {
func badgeImage() -> Badge? {
nil
}

@ -7,112 +7,69 @@
import SwiftUI
struct TournamentSettingsView: View {
@Environment(Tournament.self) private var tournament: Tournament
@EnvironmentObject var dataStore: DataStore
@State private var tournamentName: String = ""
@FocusState private var textFieldIsFocus: Bool
var body: some View {
@Bindable var tournament = tournament
Form {
LabeledContent {
TextField(tournament.isFree() ? "Gratuite" : "Inscription", value: $tournament.entryFee, format: .currency(code: Locale.current.currency?.identifier ?? "EUR"))
.keyboardType(.decimalPad)
.multilineTextAlignment(.trailing)
.frame(maxWidth: .infinity)
} label: {
Text("Inscription")
}
LabeledContent {
TextField("Nom", text: $tournamentName)
.multilineTextAlignment(.trailing)
.frame(maxWidth: .infinity)
.keyboardType(.alphabet)
.autocorrectionDisabled()
.onSubmit {
if tournamentName.trimmed.isEmpty {
tournament.name = nil
} else {
tournament.name = tournamentName
}
}
} label: {
Text("Nom du tournoi")
}
TournamentLevelPickerView()
TournamentDurationManagerView()
TournamentFieldsManagerView(localizedStringKey: "Terrains maximum", count: $tournament.courtCount, max: 100)
TournamentDatePickerView()
enum TournamentSettings: Identifiable, Selectable {
case general
case club(Tournament)
case matchFormats
let event = tournament.eventObject
let selectedClub = event?.clubObject
Section {
if let selectedClub {
NavigationLink {
ClubDetailView(club: selectedClub, displayContext: .edition)
} label: {
ClubRowView(club: selectedClub)
}
} else {
NavigationLink {
ClubsView() { club in
if let event {
event.club = club.id
try? dataStore.events.addOrUpdate(instance: event)
} else {
let event = Event(club: club.id)
tournament.event = event.id
try? dataStore.events.addOrUpdate(instance: event)
}
}
} label: {
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)
}
}
}
}
TournamentFormatSelectionView()
var id: String { String(describing: self) }
func selectionLabel() -> String {
switch self {
case .matchFormats:
return "Formats de jeu"
case .general:
return "Général"
case .club:
return "Club"
}
.focused($textFieldIsFocus)
.scrollDismissesKeyboard(.immediately)
.navigationTitle("Réglages")
.toolbarBackground(.visible, for: .navigationBar)
.toolbar {
ToolbarItem(placement: .keyboard) {
Button("Valider") {
textFieldIsFocus = false
}
}
func badgeValue() -> Int? {
nil
}
func badgeImage() -> Badge? {
switch self {
case .club(let tournament):
if tournament.club() != nil {
return .checkmark
} else {
return .xmark
}
default:
return nil
}
.onDisappear {
try? dataStore.tournaments.addOrUpdate(instance: tournament)
}
}
struct TournamentSettingsView: View {
@State private var selectedDestination: TournamentSettings? = .general
@Environment(Tournament.self) var tournament: Tournament
private func destinations() -> [TournamentSettings] {
[.general, .club(tournament), .matchFormats]
}
var body: some View {
VStack(spacing: 0) {
GenericDestinationPickerView(selectedDestination: $selectedDestination, destinations: destinations(), nilDestinationIsValid: false)
switch selectedDestination! {
case .matchFormats:
TournamentMatchFormatsSettingsView()
case .general:
TournamentGeneralSettingsView()
case .club:
TournamentClubSettingsView()
}
}
.navigationBarTitleDisplayMode(.inline)
.toolbarBackground(.visible, for: .navigationBar)
.navigationTitle("Réglages")
}
}
#Preview {
Group {
TournamentSettingsView()
.environmentObject(DataStore.shared)
.environment(Tournament.mock())
}
TournamentSettingsView()
}

Loading…
Cancel
Save