add start date to groupstage

multistore
Razmig Sarkissian 2 years ago
parent bcd71ffa06
commit 16c750f950
  1. 4
      PadelClub.xcodeproj/project.pbxproj
  2. 3
      PadelClub/Data/PlayerRegistration.swift
  3. 18
      PadelClub/Data/Round.swift
  4. 4
      PadelClub/Data/Tournament.swift
  5. 6
      PadelClub/ViewModel/SeedInterval.swift
  6. 7
      PadelClub/Views/Calling/CallSettingsView.swift
  7. 2
      PadelClub/Views/Calling/CallView.swift
  8. 13
      PadelClub/Views/Calling/GroupStageCallingView.swift
  9. 13
      PadelClub/Views/Calling/SeedsCallingView.swift
  10. 74
      PadelClub/Views/Calling/SendToAllView.swift
  11. 8
      PadelClub/Views/Planning/Components/DateUpdateManagerView.swift
  12. 12
      PadelClub/Views/Round/LoserRoundView.swift
  13. 19
      PadelClub/Views/Round/RoundView.swift

@ -122,6 +122,7 @@
FF1DF49B2BD8D23900822FA0 /* BarButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1DF49A2BD8D23900822FA0 /* BarButtonView.swift */; };
FF2BE4872B85E27400592328 /* LeStorage.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C425D4542B6D24E2002A7B48 /* LeStorage.framework */; };
FF2BE4882B85E27400592328 /* LeStorage.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = C425D4542B6D24E2002A7B48 /* LeStorage.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
FF2EFBF02BDE295E0049CE3B /* SendToAllView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF2EFBEF2BDE295E0049CE3B /* SendToAllView.swift */; };
FF3795622B9396D0004EA093 /* PadelClubApp.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = FF3795602B9396D0004EA093 /* PadelClubApp.xcdatamodeld */; };
FF3795662B9399AA004EA093 /* Persistence.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF3795652B9399AA004EA093 /* Persistence.swift */; };
FF3B60A32BC49BBC008C2E66 /* MatchScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF3B60A22BC49BBC008C2E66 /* MatchScheduler.swift */; };
@ -422,6 +423,7 @@
FF1DC5582BAB767000FD8220 /* Tips.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tips.swift; sourceTree = "<group>"; };
FF1DC55A2BAB80C400FD8220 /* DisplayContext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisplayContext.swift; sourceTree = "<group>"; };
FF1DF49A2BD8D23900822FA0 /* BarButtonView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BarButtonView.swift; sourceTree = "<group>"; };
FF2EFBEF2BDE295E0049CE3B /* SendToAllView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SendToAllView.swift; sourceTree = "<group>"; };
FF3795612B9396D0004EA093 /* Model.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = Model.xcdatamodel; sourceTree = "<group>"; };
FF3795652B9399AA004EA093 /* Persistence.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Persistence.swift; sourceTree = "<group>"; };
FF3B60A22BC49BBC008C2E66 /* MatchScheduler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MatchScheduler.swift; sourceTree = "<group>"; };
@ -1084,6 +1086,7 @@
FF9268022BCE94A30080F940 /* GroupStageCallingView.swift */,
FF9268082BCEDC2C0080F940 /* CallView.swift */,
FF1162792BCF8109000C4809 /* CallMessageCustomizationView.swift */,
FF2EFBEF2BDE295E0049CE3B /* SendToAllView.swift */,
);
path = Calling;
sourceTree = "<group>";
@ -1467,6 +1470,7 @@
C4A47DA92B85F82100ADC637 /* ChangePasswordView.swift in Sources */,
FFC83D512BB8087E00750834 /* RoundView.swift in Sources */,
FF6EC8F72B94773200EA7F5A /* RowButtonView.swift in Sources */,
FF2EFBF02BDE295E0049CE3B /* SendToAllView.swift in Sources */,
FF8F263B2BAD528600650388 /* EventCreationView.swift in Sources */,
FFC1E1082BAC29FC008D6F59 /* LocationManager.swift in Sources */,
FFF964552BC266CF00EEF017 /* SchedulerView.swift in Sources */,

@ -305,6 +305,7 @@ class PlayerRegistration: ModelObject, Storable {
case bankTransfer = 5
case clubHouse = 6
case creditCard = 7
case forfeit = 8
func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String {
switch self {
@ -322,6 +323,8 @@ class PlayerRegistration: ModelObject, Storable {
return "Clubhouse"
case .creditCard:
return "CB"
case .forfeit:
return "Forfait"
case .gift:
return "Offert"
}

@ -122,6 +122,10 @@ class Round: ModelObject, Storable {
_matches().compactMap { $0.losingTeamId }.compactMap { Store.main.findById($0) }
}
func teams() -> [TeamRegistration] {
playedMatches().flatMap({ $0.teams() })
}
func roundProjectedTeam(_ team: TeamPosition, inMatch match: Match) -> TeamRegistration? {
if isLoserBracket() == false, let seed = seed(team, inMatchIndex: match.index) {
return seed
@ -199,6 +203,16 @@ class Round: ModelObject, Storable {
Store.main.filter { $0.round == self.id && $0.disabled == false }
}
func displayableMatches() -> [Match] {
if index == 0 && isUpperBracket() {
var matches : [Match?] = [playedMatches().first]
matches.append(loserRounds().first?.playedMatches().first)
return matches.compactMap({ $0 })
} else {
return playedMatches()
}
}
func playedMatches() -> [Match] {
if parent == nil {
enabledMatches()
@ -370,6 +384,10 @@ class Round: ModelObject, Storable {
return loserRounds + loserRounds.flatMap({ $0.loserRoundsAndChildren() })
}
func isUpperBracket() -> Bool {
parent == nil
}
func isLoserBracket() -> Bool {
parent != nil
}

@ -562,13 +562,13 @@ class Tournament : ModelObject, Storable {
}
func isStartDateIsDifferentThanCallDate(_ team: TeamRegistration) -> Bool {
guard let callDate = team.callDate else { return false }
guard let callDate = team.callDate else { return true }
if let groupStageStartDate = team.groupStageObject()?.startDate {
return Calendar.current.compare(callDate, to: groupStageStartDate, toGranularity: .minute) != ComparisonResult.orderedSame
} else if let roundMatchStartDate = team.initialMatch()?.startDate {
return Calendar.current.compare(callDate, to: roundMatchStartDate, toGranularity: .minute) != ComparisonResult.orderedSame
}
return false
return true
}
func availableToStart(_ allMatches: [Match]) -> [Match] {

@ -12,6 +12,12 @@ struct SeedInterval: Hashable, Comparable {
let last: Int
var reduce: Int = 0
func pointsRange(tournamentLevel: TournamentLevel, teamsCount: Int) -> String {
let range = [tournamentLevel.points(for: last - 1 - reduce, count: teamsCount),
tournamentLevel.points(for: first - 1 - reduce, count: teamsCount)]
return range.map { $0.formatted(.number.sign(strategy: .always())) }.joined(separator: " / ") + " pts"
}
static func <(lhs: SeedInterval, rhs: SeedInterval) -> Bool {
return lhs.first < rhs.first
}

@ -10,6 +10,7 @@ import SwiftUI
struct CallSettingsView: View {
@EnvironmentObject var dataStore: DataStore
@Environment(Tournament.self) var tournament: Tournament
@State private var showSendToAllView: Bool = false
var body: some View {
List {
@ -24,7 +25,7 @@ struct CallSettingsView: View {
Section {
RowButtonView("Envoyer un message à tout le monde") {
showSendToAllView = true
}
}
@ -48,6 +49,10 @@ struct CallSettingsView: View {
}
}
}
.sheet(isPresented: $showSendToAllView) {
SendToAllView()
.tint(.master)
}
}
}

@ -20,7 +20,7 @@ struct CallView: View {
if let startDate {
Text(startDate.formattedAsHourMinute())
} else {
Text("Aucun horaire")
Text("Aucun horaire").font(.body)
}
Spacer()
Text(count.formatted() + "/" + total.formatted())

@ -83,6 +83,19 @@ struct GroupStageCallingView: View {
}
}
.overlay {
if groupStage.startDate == nil {
ContentUnavailableView {
Label("Aucun horaire défini", systemImage: "clock.badge.questionmark")
} description: {
Text("Vous n'avez pas encore défini d'horaire pour les différentes phases du tournoi")
} actions: {
// RowButtonView("Horaire intelligent") {
// selectedScheduleDestination = nil
// }
}
}
}
.headerProminence(.increased)
.navigationTitle(groupStage.groupStageTitle())
.navigationBarTitleDisplayMode(.inline)

@ -85,6 +85,19 @@ struct SeedsCallingView: View {
}
}
}
.overlay {
if (keys.isEmpty && displayByMatch == false) || (displayByMatch && roundMatches.isEmpty) {
ContentUnavailableView {
Label("Aucun horaire défini", systemImage: "clock.badge.questionmark")
} description: {
Text("Vous n'avez pas encore défini d'horaire pour les différentes phases du tournoi")
} actions: {
// RowButtonView("Horaire intelligent") {
// selectedScheduleDestination = nil
// }
}
}
}
.headerProminence(.increased)
.navigationTitle(round.roundTitle())
.navigationBarTitleDisplayMode(.inline)

@ -0,0 +1,74 @@
//
// SendToAllView.swift
// PadelClub
//
// Created by Razmig Sarkissian on 28/04/2024.
//
import SwiftUI
import LeStorage
struct SendToAllView: View {
@Environment(Tournament.self) var tournament: Tournament
@State private var contactMethod: Int = 1
@State private var contactRecipients: Set<String> = Set()
var body: some View {
NavigationStack {
List(selection: $contactRecipients) {
Section {
Picker(selection: $contactMethod) {
Text("par sms").tag(0)
Text("par mail").tag(1)
} label: {
Text("méthode")
}
.labelsHidden()
.pickerStyle(.inline)
}
Section {
ForEach(tournament.rounds()) { round in
LabeledContent {
let seeds = round.seeds()
Text(round.seeds().count + " tête" + seeds.count.pluralSuffix + " de série")
} label: {
Text(round.roundTitle())
}
.tag(round.id)
}
}
Section {
RowButtonView("Contacter \(totalString)") {
}
}
}
.environment(\.editMode, Binding.constant(EditMode.active))
.headerProminence(.increased)
.navigationTitle("Réglages")
.navigationBarTitleDisplayMode(.inline)
.toolbarBackground(.visible, for: .navigationBar)
}
}
func _teams() -> [TeamRegistration] {
let rounds : [Round] = contactRecipients.map { Store.main.findById($0) }
return rounds.flatMap({ $0.teams() })
}
func _totalString() -> String {
if contactRecipients.isEmpty {
return "toutes les équipes"
} else {
return
}
}
}
#Preview {
SendToAllView()
}

@ -41,6 +41,14 @@ struct DatePickingView: View {
} else {
HStack {
Menu {
Button("de 30 minutes") {
startDate = startDate.addingTimeInterval(1800)
}
Button("d'une heure") {
startDate = startDate.addingTimeInterval(3600)
}
Button("à 9h") {
startDate = startDate.atNine()
}

@ -9,6 +9,8 @@ import SwiftUI
struct LoserRoundView: View {
@EnvironmentObject var dataStore: DataStore
@Environment(Tournament.self) var tournament: Tournament
let loserRounds: [Round]
@State private var isEditingTournamentSeed: Bool = false
@ -39,7 +41,15 @@ struct LoserRoundView: View {
.disabled(match.disabled)
}
} header: {
Text(loserRound.roundTitle(.wide))
HStack {
Text(loserRound.roundTitle(.wide))
let tournamentTeamCount = tournament.teamCount
if let seedIntervalPointRange = loserRound.seedInterval()?.pointsRange(tournamentLevel: tournament.tournamentLevel, teamsCount: tournamentTeamCount) {
Spacer()
Text(seedIntervalPointRange)
.font(.caption)
}
}
}
}
}

@ -17,8 +17,9 @@ struct RoundView: View {
var body: some View {
List {
let loserRounds = round.loserRounds()
if isEditingTournamentSeed.wrappedValue == false {
let loserRounds = round.loserRounds()
//(where: { $0.isDisabled() == false || isEditingTournamentSeed.wrappedValue })
if loserRounds.isEmpty == false, let first = loserRounds.first {
let correspondingLoserRoundTitle = round.correspondingLoserRoundTitle()
@ -61,11 +62,23 @@ struct RoundView: View {
}
}
ForEach(round.playedMatches()) { match in
ForEach(round.displayableMatches()) { match in
Section {
MatchRowView(match: match, matchViewStyle: .sectionedStandardStyle)
} header: {
Text(round.roundTitle(.wide) + " " + match.matchTitle(.short))
HStack {
Text(round.roundTitle(.wide))
if round.index > 0 {
Text(match.matchTitle(.short))
} else {
let tournamentTeamCount = tournament.teamCount
if let seedIntervalPointRange = loserRounds.first?.seedInterval()?.pointsRange(tournamentLevel: tournament.tournamentLevel, teamsCount: tournamentTeamCount) {
Spacer()
Text(seedIntervalPointRange)
.font(.caption)
}
}
}
}
}
}

Loading…
Cancel
Save