multistore
Razmig Sarkissian 1 year ago
parent 30affc2de9
commit 11e3995a95
  1. 16
      PadelClub.xcodeproj/project.pbxproj
  2. 4
      PadelClub/Data/Event.swift
  3. 2
      PadelClub/Data/MatchScheduler.swift
  4. 7
      PadelClub/Data/Tournament.swift
  5. 6
      PadelClub/Utils/HtmlService.swift
  6. 2
      PadelClub/Views/Calling/CallMessageCustomizationView.swift
  7. 1
      PadelClub/Views/Cashier/CashierDetailView.swift
  8. 16
      PadelClub/Views/Cashier/Event/EventCreationView.swift
  9. 32
      PadelClub/Views/Cashier/Event/EventSettingsView.swift
  10. 3
      PadelClub/Views/Cashier/Event/EventTournamentsView.swift
  11. 3
      PadelClub/Views/Cashier/Event/EventView.swift
  12. 0
      PadelClub/Views/Cashier/Event/TournamentConfiguratorView.swift
  13. 31
      PadelClub/Views/Club/ClubDetailView.swift
  14. 6
      PadelClub/Views/Planning/PlanningSettingsView.swift
  15. 1
      PadelClub/Views/Planning/PlanningView.swift
  16. 6
      PadelClub/Views/Team/TeamRowView.swift
  17. 1
      PadelClub/Views/Tournament/Screen/TournamentCallView.swift
  18. 1
      PadelClub/Views/Tournament/Screen/TournamentCashierView.swift
  19. 1
      PadelClub/Views/Tournament/Screen/TournamentScheduleView.swift
  20. 25
      PadelClub/Views/Tournament/TournamentBuildView.swift
  21. 32
      PadelClub/Views/Tournament/TournamentInitView.swift
  22. 24
      PadelClub/Views/Tournament/TournamentInscriptionView.swift
  23. 14
      PadelClub/Views/Tournament/TournamentView.swift

@ -748,7 +748,6 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
FF39719B2B8DE04B004C4E75 /* Navigation */, FF39719B2B8DE04B004C4E75 /* Navigation */,
FF8F26392BAD526A00650388 /* Event */,
FF1DC54D2BAB34FA00FD8220 /* Club */, FF1DC54D2BAB34FA00FD8220 /* Club */,
FFC83D4B2BB807C200750834 /* Round */, FFC83D4B2BB807C200750834 /* Round */,
FF967CF92BAEE11500A9A3BD /* GroupStage */, FF967CF92BAEE11500A9A3BD /* GroupStage */,
@ -1120,15 +1119,6 @@
path = Network; path = Network;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
FF8F26392BAD526A00650388 /* Event */ = {
isa = PBXGroup;
children = (
FF8F263A2BAD528600650388 /* EventCreationView.swift */,
FF8F263C2BAD627A00650388 /* TournamentConfiguratorView.swift */,
);
path = Event;
sourceTree = "<group>";
};
FF8F26522BAE0E4E00650388 /* Components */ = { FF8F26522BAE0E4E00650388 /* Components */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@ -1219,6 +1209,8 @@
FFBF41812BF73EB3001B24CB /* EventView.swift */, FFBF41812BF73EB3001B24CB /* EventView.swift */,
FFBF41832BF75ED7001B24CB /* EventTournamentsView.swift */, FFBF41832BF75ED7001B24CB /* EventTournamentsView.swift */,
FFBF41852BF75FDA001B24CB /* EventSettingsView.swift */, FFBF41852BF75FDA001B24CB /* EventSettingsView.swift */,
FF8F263A2BAD528600650388 /* EventCreationView.swift */,
FF8F263C2BAD627A00650388 /* TournamentConfiguratorView.swift */,
); );
name = Event; name = Event;
path = Cashier/Event; path = Cashier/Event;
@ -1927,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 = 11; CURRENT_PROJECT_VERSION = 12;
DEFINES_MODULE = YES; DEFINES_MODULE = YES;
DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\""; DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\"";
DEVELOPMENT_TEAM = BQ3Y44M3Q6; DEVELOPMENT_TEAM = BQ3Y44M3Q6;
@ -1965,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 = 11; CURRENT_PROJECT_VERSION = 12;
DEFINES_MODULE = YES; DEFINES_MODULE = YES;
DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\""; DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\"";
DEVELOPMENT_TEAM = BQ3Y44M3Q6; DEVELOPMENT_TEAM = BQ3Y44M3Q6;

@ -71,8 +71,8 @@ class Event: ModelObject, Storable {
tournaments.first(where: { $0.isSameBuild(build) }) tournaments.first(where: { $0.isSameBuild(build) })
} }
func tournamentsCourtsUsed() -> [DateInterval] { func tournamentsCourtsUsed(exluding tournamentId: String) -> [DateInterval] {
tournaments.flatMap({ tournament in tournaments.filter { $0.id != tournamentId }.flatMap({ tournament in
tournament.getPlayedMatchDateIntervals(in: self) tournament.getPlayedMatchDateIntervals(in: self)
}) })
} }

@ -69,7 +69,7 @@ class MatchScheduler : ModelObject, Storable {
var courtsUnavailability: [DateInterval]? { var courtsUnavailability: [DateInterval]? {
guard let event = tournamentObject()?.eventObject() else { return nil } guard let event = tournamentObject()?.eventObject() else { return nil }
return event.courtsUnavailability + event.tournamentsCourtsUsed() return event.courtsUnavailability + event.tournamentsCourtsUsed(exluding: tournament)
} }
var additionalEstimationDuration : Int { var additionalEstimationDuration : Int {

@ -1398,7 +1398,7 @@ class Tournament : ModelObject, Storable {
} }
private func _matchSchedulers() -> [MatchScheduler] { private func _matchSchedulers() -> [MatchScheduler] {
Store.main.filter(isIncluded: { $0.id == self.id }) Store.main.filter(isIncluded: { $0.tournament == self.id })
} }
func matchScheduler() -> MatchScheduler? { func matchScheduler() -> MatchScheduler? {
@ -1427,6 +1427,11 @@ class Tournament : ModelObject, Storable {
courtNameIfAvailable(atIndex: courtIndex) ?? Court.courtIndexedTitle(atIndex: courtIndex) courtNameIfAvailable(atIndex: courtIndex) ?? Court.courtIndexedTitle(atIndex: courtIndex)
} }
func tournamentWinner() -> TeamRegistration? {
let final : Round? = Store.main.filter(isIncluded: { $0.index == 0 && $0.tournament == id && $0.parent == nil }).first
return final?.playedMatches().first?.winner()
}
// MARK: - Payments & Crypto // MARK: - Payments & Crypto
fileprivate var _currentPayment: TournamentPayment? = nil fileprivate var _currentPayment: TournamentPayment? = nil

@ -199,9 +199,9 @@ enum HtmlService {
} }
var winnerName = "" var winnerName = ""
// if let tournamentWinner = tournament.winnerEntrant { if let tournamentWinner = tournament.tournamentWinner() {
// winnerName = HtmlService.player(entrant: tournamentWinner).html(headName: headName, withRank: withRank, withScore: withScore) winnerName = HtmlService.player(entrant: tournamentWinner).html(headName: headName, withRank: withRank, withScore: withScore)
// } }
let winner = """ let winner = """
<ul class="round" scope="last"> <ul class="round" scope="last">
<li class="spacer">&nbsp;</li> <li class="spacer">&nbsp;</li>

@ -46,7 +46,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()) 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 {

@ -37,7 +37,6 @@ struct CashierDetailView: View {
} }
} }
.headerProminence(.increased) .headerProminence(.increased)
.navigationTitle("Bilan")
} }
private func _tournamentCashierDetailView(_ tournament: Tournament) -> some View { private func _tournamentCashierDetailView(_ tournament: Tournament) -> some View {

@ -20,6 +20,7 @@ struct EventCreationView: View {
@State private var eventName: String = "" @State private var eventName: String = ""
@State var tournaments: [Tournament] = [] @State var tournaments: [Tournament] = []
@State var selectedClub: Club? @State var selectedClub: Club?
@FocusState private var textFieldIsFocus: Bool
let multiTournamentsEventTip = MultiTournamentsEventTip() let multiTournamentsEventTip = MultiTournamentsEventTip()
@ -65,11 +66,10 @@ struct EventCreationView: View {
TextField("Nom de l'événement", text: $eventName, axis: .vertical) TextField("Nom de l'événement", text: $eventName, axis: .vertical)
.lineLimit(2) .lineLimit(2)
.autocorrectionDisabled()
.keyboardType(.alphabet) .keyboardType(.alphabet)
.multilineTextAlignment(.leading) .multilineTextAlignment(.leading)
.frame(maxWidth: .infinity) .frame(maxWidth: .infinity)
.focused($textFieldIsFocus)
LabeledContent { LabeledContent {
Text(tournaments.count.formatted()) Text(tournaments.count.formatted())
} label: { } label: {
@ -125,6 +125,18 @@ struct EventCreationView: View {
} }
} }
.toolbar { .toolbar {
if textFieldIsFocus {
ToolbarItem(placement: .keyboard) {
HStack {
Spacer()
Button("Valider") {
textFieldIsFocus = false
}
.buttonStyle(.bordered)
}
}
}
ToolbarItem(placement: .cancellationAction) { ToolbarItem(placement: .cancellationAction) {
Button("Annuler", role: .cancel) { Button("Annuler", role: .cancel) {
dismiss() dismiss()

@ -12,6 +12,7 @@ struct EventSettingsView: View {
@EnvironmentObject var dataStore: DataStore @EnvironmentObject var dataStore: DataStore
@Bindable var event: Event @Bindable var event: Event
@State private var eventName: String = "" @State private var eventName: String = ""
@FocusState private var textFieldIsFocus: Bool
init(event: Event) { init(event: Event) {
self.event = event self.event = event
@ -23,20 +24,10 @@ struct EventSettingsView: View {
Section { Section {
TextField("Nom de l'événement", text: $eventName, axis: .vertical) TextField("Nom de l'événement", text: $eventName, axis: .vertical)
.lineLimit(2) .lineLimit(2)
.autocorrectionDisabled()
.keyboardType(.alphabet) .keyboardType(.alphabet)
.multilineTextAlignment(.leading) .multilineTextAlignment(.leading)
.frame(maxWidth: .infinity) .frame(maxWidth: .infinity)
.onSubmit { .focused($textFieldIsFocus)
if eventName.trimmed.isEmpty == false {
event.name = eventName.trimmed
} else {
event.name = nil
}
_save()
}
} header: {
Text("Nom de l'événement")
} footer: { } footer: {
if eventName.isEmpty == false { if eventName.isEmpty == false {
FooterButtonView("effacer le nom") { FooterButtonView("effacer le nom") {
@ -47,6 +38,25 @@ struct EventSettingsView: View {
} }
} }
} }
.toolbar {
if textFieldIsFocus {
ToolbarItem(placement: .keyboard) {
HStack {
Spacer()
Button("Valider") {
textFieldIsFocus = false
if eventName.trimmed.isEmpty == false {
event.name = eventName.trimmed
} else {
event.name = nil
}
_save()
}
.buttonStyle(.bordered)
}
}
}
}
} }
private func _save() { private func _save() {

@ -46,9 +46,6 @@ struct EventTournamentsView: View {
} }
} }
.headerProminence(.increased) .headerProminence(.increased)
.toolbarBackground(.visible, for: .navigationBar)
.navigationBarTitleDisplayMode(.inline)
.navigationTitle(event.eventTitle())
.sheet(isPresented: presentTournamentCreationView) { .sheet(isPresented: presentTournamentCreationView) {
if let newTournament { if let newTournament {
NavigationStack { NavigationStack {

@ -58,7 +58,6 @@ struct EventView: View {
switch selectedDestination { switch selectedDestination {
case .none: case .none:
EventSettingsView(event: event) EventSettingsView(event: event)
.navigationTitle("Réglages")
case .some(let selectedEventDestination): case .some(let selectedEventDestination):
switch selectedEventDestination { switch selectedEventDestination {
case .tournaments(let event): case .tournaments(let event):
@ -71,6 +70,6 @@ struct EventView: View {
.headerProminence(.increased) .headerProminence(.increased)
.toolbarBackground(.visible, for: .navigationBar) .toolbarBackground(.visible, for: .navigationBar)
.navigationBarTitleDisplayMode(.inline) .navigationBarTitleDisplayMode(.inline)
.navigationTitle(event.eventTitle()) .navigationTitle("Gestion de l'événement")
} }
} }

@ -39,16 +39,6 @@ struct ClubDetailView: View {
.frame(maxWidth: .infinity) .frame(maxWidth: .infinity)
.focused($focusedField, equals: ._name) .focused($focusedField, equals: ._name)
.submitLabel( displayContext == .addition ? .next : .done) .submitLabel( displayContext == .addition ? .next : .done)
.onSubmit {
if club.acronym.isEmpty {
club.acronym = club.name.canonicalVersion.replaceCharactersFromSet(characterSet: .whitespacesAndNewlines)
focusedField = ._city
}
if displayContext == .addition {
focusedField = ._acronym
}
}
LabeledContent { LabeledContent {
if acronymMode == .automatic || displayContext == .lockedForEditing { if acronymMode == .automatic || displayContext == .lockedForEditing {
Text(club.acronym) Text(club.acronym)
@ -247,6 +237,27 @@ struct ClubDetailView: View {
} }
} }
} }
.toolbar {
if focusedField == ._name {
ToolbarItem(placement: .keyboard) {
HStack {
Spacer()
Button("Valider") {
if club.acronym.isEmpty {
club.acronym = club.name.canonicalVersion.replaceCharactersFromSet(characterSet: .whitespacesAndNewlines)
}
if displayContext == .edition && city.isEmpty == true {
focusedField = ._city
} else {
focusedField = nil
}
}
.buttonStyle(.bordered)
}
}
}
}
} }
} }

@ -140,9 +140,6 @@ struct PlanningSettingsView: View {
.deferredRendering(for: .seconds(2)) .deferredRendering(for: .seconds(2))
} }
} }
.onChange(of: groupStageChunkCount) {
matchScheduler.groupStageChunkCount = groupStageChunkCount
}
.onChange(of: tournament.startDate) { .onChange(of: tournament.startDate) {
_save() _save()
} }
@ -159,6 +156,9 @@ struct PlanningSettingsView: View {
if tournament.groupStages().isEmpty == false { if tournament.groupStages().isEmpty == false {
Section { Section {
TournamentFieldsManagerView(localizedStringKey: "Poule en parallèle", count: $groupStageChunkCount, max: tournament.groupStageCount) TournamentFieldsManagerView(localizedStringKey: "Poule en parallèle", count: $groupStageChunkCount, max: tournament.groupStageCount)
.onChange(of: groupStageChunkCount) {
matchScheduler.groupStageChunkCount = groupStageChunkCount
}
} footer: { } footer: {
Text("Vous pouvez indiquer le nombre de poule démarrant en même temps.") Text("Vous pouvez indiquer le nombre de poule démarrant en même temps.")
} }

@ -82,7 +82,6 @@ struct PlanningView: View {
} }
} }
} }
.navigationTitle("Programmation")
} }
private func _matchesCount(inDayInt dayInt: Int) -> Int { private func _matchesCount(inDayInt dayInt: Int) -> Int {

@ -16,7 +16,11 @@ struct TeamRowView: View {
LabeledContent { LabeledContent {
TeamWeightView(team: team, teamPosition: teamPosition) TeamWeightView(team: team, teamPosition: teamPosition)
} label: { } label: {
Text(team.teamLabel(.wide)) VStack(alignment: .leading) {
ForEach(team.players()) { player in
Text(player.playerLabel())
}
}
if let callDate = team.callDate, displayCallDate { if let callDate = team.callDate, displayCallDate {
Text("Déjà convoquée \(callDate.localizedDate())") Text("Déjà convoquée \(callDate.localizedDate())")
.foregroundStyle(.red) .foregroundStyle(.red)

@ -86,7 +86,6 @@ struct TournamentCallView: View {
switch selectedDestination { switch selectedDestination {
case .none: case .none:
CallSettingsView() CallSettingsView()
.navigationTitle("Réglages")
case .some(let selectedCall): case .some(let selectedCall):
switch selectedCall { switch selectedCall {
case .groupStages: case .groupStages:

@ -113,7 +113,6 @@ struct TournamentCashierView: View {
switch selectedDestination { switch selectedDestination {
case .none: case .none:
CashierSettingsView(tournament: tournament) CashierSettingsView(tournament: tournament)
.navigationTitle("Réglages")
case .some(let selectedCall): case .some(let selectedCall):
switch selectedCall { switch selectedCall {
case .summary: case .summary:

@ -75,7 +75,6 @@ struct TournamentScheduleView: View {
switch selectedScheduleDestination { switch selectedScheduleDestination {
case .none: case .none:
PlanningSettingsView(tournament: tournament) PlanningSettingsView(tournament: tournament)
.navigationTitle("Réglages")
case .some(let selectedSchedule): case .some(let selectedSchedule):
switch selectedSchedule { switch selectedSchedule {
case .scheduleGroupStage: case .scheduleGroupStage:

@ -34,13 +34,24 @@ struct TournamentBuildView: View {
} }
} }
} }
NavigationLink(value: Screen.cashier) {
let tournamentStatus = tournament.cashierStatus() if tournament.state() == .running || tournament.state() == .finished {
LabeledContent { NavigationLink(value: Screen.cashier) {
Text(tournamentStatus.completion) let tournamentStatus = tournament.cashierStatus()
} label: { LabeledContent {
Text("Encaissement") Text(tournamentStatus.completion)
Text(tournamentStatus.label) } label: {
Text("Encaissement")
Text(tournamentStatus.label)
}
}
}
}
if tournament.hasEnded() {
Section {
NavigationLink(value: Screen.rankings) {
Text("Classement")
} }
} }
} }

@ -13,13 +13,6 @@ struct TournamentInitView: View {
@ViewBuilder @ViewBuilder
var body: some View { var body: some View {
Section { Section {
NavigationLink(value: Screen.broadcast) {
LabeledContent {
// Text(tournament.isPrivate ? "privée" : "publique")
} label: {
Text("Publication")
}
}
if let event = tournament.eventObject() { if let event = tournament.eventObject() {
let tournaments = event.tournaments let tournaments = event.tournaments
@ -31,6 +24,16 @@ struct TournamentInitView: View {
} }
} }
} }
NavigationLink(value: Screen.structure) {
LabeledContent {
Text(tournament.structureDescriptionLocalizedLabel())
.tint(.master)
} label: {
LabelStructure()
}
}
NavigationLink(value: Screen.settings) { NavigationLink(value: Screen.settings) {
LabeledContent { LabeledContent {
Text(tournament.settingsDescriptionLocalizedLabel()) Text(tournament.settingsDescriptionLocalizedLabel())
@ -39,8 +42,19 @@ struct TournamentInitView: View {
LabelSettings() LabelSettings()
} }
} }
} footer: {
Text("La date, la catégorie, le niveau, le nombre de terrain, les formats, etc.") NavigationLink(value: Screen.broadcast) {
LabeledContent {
Image(systemName: tournament.isPrivate ? "tv.slash" : "checkmark")
.foregroundStyle(tournament.isPrivate ? .logoRed : .green)
} label: {
Text("Publication")
}
}
NavigationLink(value: Screen.print) {
Text("Imprimer")
}
} }
} }
} }

@ -32,30 +32,6 @@ struct TournamentInscriptionView: View {
Text("Date limite") Text("Date limite")
} }
} }
if tournament.state() != .running {
NavigationLink(value: Screen.structure) {
LabeledContent {
Text(tournament.structureDescriptionLocalizedLabel())
.tint(.master)
} label: {
LabelStructure()
}
}
}
} footer: {
if tournament.inscriptionClosed() == false && tournament.state() == .build && tournament.unsortedTeams().isEmpty == false && tournament.hasStarted() == false {
Button {
tournament.lockRegistration()
_save()
} label: {
Text("clôturer les inscriptions")
.underline()
}
.buttonStyle(.borderless)
} else if tournament.state() != .running {
Text("Nombre d'équipes, de poules, de qualifiés sortant, etc.")
}
} }
} }

@ -62,22 +62,10 @@ struct TournamentView: View {
case .build: case .build:
TournamentInscriptionView(tournament: tournament) TournamentInscriptionView(tournament: tournament)
TournamentInitView(tournament: tournament) TournamentInitView(tournament: tournament)
Section {
NavigationLink(value: Screen.print) {
Label("Imprimer", systemImage: "printer")
}
}
TournamentBuildView(tournament: tournament) TournamentBuildView(tournament: tournament)
case .running, .finished: case .running, .finished:
TournamentInscriptionView(tournament: tournament) TournamentInscriptionView(tournament: tournament)
TournamentBuildView(tournament: tournament) TournamentBuildView(tournament: tournament)
if tournament.hasEnded() {
Section {
NavigationLink(value: Screen.rankings) {
Text("Classement")
}
}
}
TournamentRunningView(tournament: tournament) TournamentRunningView(tournament: tournament)
} }
} }
@ -168,7 +156,7 @@ struct TournamentView: View {
} }
NavigationLink(value: Screen.broadcast) { NavigationLink(value: Screen.broadcast) {
Text("Publication") Label("Publication", systemImage: "airplayvideo")
} }
NavigationLink(value: Screen.print) { NavigationLink(value: Screen.print) {

Loading…
Cancel
Save