fix bugs with smart planning

fix bug with planning view by court
fix bug with seed deletion
paca_championship
Raz 1 year ago
parent 31f093e60e
commit a5e09d2c06
  1. 8
      PadelClub.xcodeproj/project.pbxproj
  2. 2
      PadelClub/Data/Match.swift
  3. 4
      PadelClub/Data/MatchScheduler.swift
  4. 20
      PadelClub/Views/Match/MatchDetailView.swift
  5. 2
      PadelClub/Views/Match/MatchSetupView.swift
  6. 13
      PadelClub/Views/Planning/PlanningByCourtView.swift
  7. 162
      PadelClub/Views/Planning/PlanningSettingsView.swift
  8. 4
      PadelClub/Views/Round/RoundView.swift

@ -3222,7 +3222,7 @@
CODE_SIGN_ENTITLEMENTS = PadelClub/PadelClub.entitlements; CODE_SIGN_ENTITLEMENTS = PadelClub/PadelClub.entitlements;
CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2; CURRENT_PROJECT_VERSION = 1;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEFINES_MODULE = YES; DEFINES_MODULE = YES;
DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\""; DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\"";
@ -3246,7 +3246,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 1.0.25; MARKETING_VERSION = 1.0.26;
PRODUCT_BUNDLE_IDENTIFIER = app.padelclub; PRODUCT_BUNDLE_IDENTIFIER = app.padelclub;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = ""; PROVISIONING_PROFILE_SPECIFIER = "";
@ -3267,7 +3267,7 @@
CODE_SIGN_ENTITLEMENTS = PadelClub/PadelClub.entitlements; CODE_SIGN_ENTITLEMENTS = PadelClub/PadelClub.entitlements;
CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2; CURRENT_PROJECT_VERSION = 1;
DEFINES_MODULE = YES; DEFINES_MODULE = YES;
DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\""; DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\"";
DEVELOPMENT_TEAM = BQ3Y44M3Q6; DEVELOPMENT_TEAM = BQ3Y44M3Q6;
@ -3290,7 +3290,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 1.0.25; MARKETING_VERSION = 1.0.26;
PRODUCT_BUNDLE_IDENTIFIER = app.padelclub; PRODUCT_BUNDLE_IDENTIFIER = app.padelclub;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = ""; PROVISIONING_PROFILE_SPECIFIER = "";

@ -551,6 +551,8 @@ defer {
} }
if startDate == nil { if startDate == nil {
startDate = endDate?.addingTimeInterval(Double(-getDuration()*60)) startDate = endDate?.addingTimeInterval(Double(-getDuration()*60))
} else if let startDate, let endDate, startDate >= endDate {
self.startDate = endDate.addingTimeInterval(Double(-getDuration()*60))
} }
let teamOne = team(matchDescriptor.winner) let teamOne = team(matchDescriptor.winner)

@ -673,7 +673,7 @@ final class MatchScheduler : ModelObject, Storable {
@discardableResult func updateBracketSchedule(tournament: Tournament, fromRoundId roundId: String?, fromMatchId matchId: String?, startDate: Date) -> Bool { @discardableResult func updateBracketSchedule(tournament: Tournament, fromRoundId roundId: String?, fromMatchId matchId: String?, startDate: Date) -> Bool {
let upperRounds: [Round] = tournament.rounds() let upperRounds: [Round] = tournament.rounds()
let allMatches: [Match] = tournament.allMatches().filter({ $0.hasEnded() == false }) let allMatches: [Match] = tournament.allMatches().filter({ $0.hasEnded() == false && $0.hasStarted() == false })
var rounds = [Round]() var rounds = [Round]()
@ -694,7 +694,7 @@ final class MatchScheduler : ModelObject, Storable {
} }
let flattenedMatches = rounds.flatMap { round in let flattenedMatches = rounds.flatMap { round in
round._matches().filter({ $0.disabled == false && $0.hasEnded() == false }).sorted(by: \.index) round._matches().filter({ $0.disabled == false && $0.hasEnded() == false && $0.hasStarted() == false }).sorted(by: \.index)
} }
flattenedMatches.forEach({ flattenedMatches.forEach({

@ -354,6 +354,26 @@ struct MatchDetailView: View {
Text("Remise-à-zéro") Text("Remise-à-zéro")
} }
if match.teamScores.isEmpty == false {
Divider()
Menu {
ForEach(match.teamScores) { teamScore in
Button(role: .destructive) {
do {
try tournamentStore.teamScores.delete(instance: teamScore)
} catch {
Logger.error(error)
}
match.confirmed = false
_saveMatch()
} label: {
Text(teamScore.team?.teamLabel() ?? "Aucun nom")
}
}
} label: {
Text("Supprimer une équipe")
}
}
} label: { } label: {
LabelOptions() LabelOptions()
} }

@ -205,7 +205,7 @@ struct MatchSetupView: View {
} catch { } catch {
Logger.error(error) Logger.error(error)
} }
match.previousMatches().forEach { previousMatch in if let previousMatch = match.previousMatch(teamPosition) {
if previousMatch.disabled { if previousMatch.disabled {
previousMatch.enableMatch() previousMatch.enableMatch()
do { do {

@ -16,6 +16,7 @@ struct PlanningByCourtView: View {
@State private var viewByCourt: Bool = false @State private var viewByCourt: Bool = false
@State private var selectedDay: Date @State private var selectedDay: Date
@State private var selectedCourt: Int = 0 @State private var selectedCourt: Int = 0
@State private var uuid: UUID = UUID()
var timeSlots: [Date:[Match]] { var timeSlots: [Date:[Match]] {
Dictionary(grouping: matches) { $0.startDate ?? .distantFuture } Dictionary(grouping: matches) { $0.startDate ?? .distantFuture }
@ -48,8 +49,14 @@ struct PlanningByCourtView: View {
let noStartDate = matches.allSatisfy({ $0.startDate == nil }) let noStartDate = matches.allSatisfy({ $0.startDate == nil })
List { List {
_byCourtView(noStartDate: noStartDate) _byCourtView(selectedCourt: selectedCourt, selectedDay: selectedDay, noStartDate: noStartDate)
.id(selectedCourt) .id(uuid)
}
.onChange(of: selectedCourt) {
uuid = UUID()
}
.onChange(of: selectedDay) {
uuid = UUID()
} }
.overlay { .overlay {
if noStartDate { if noStartDate {
@ -98,7 +105,7 @@ struct PlanningByCourtView: View {
} }
@ViewBuilder @ViewBuilder
func _byCourtView(noStartDate: Bool) -> some View { func _byCourtView(selectedCourt: Int, selectedDay: Date, noStartDate: Bool) -> some View {
if let _matches = courtSlots[selectedCourt]?.filter({ $0.startDate?.dayInt == selectedDay.dayInt }) { if let _matches = courtSlots[selectedCourt]?.filter({ $0.startDate?.dayInt == selectedDay.dayInt }) {
let _sortedMatches = _matches.sorted(by: \.computedStartDateForSorting) let _sortedMatches = _matches.sorted(by: \.computedStartDateForSorting)
if _sortedMatches.isEmpty == false { if _sortedMatches.isEmpty == false {

@ -143,7 +143,56 @@ struct PlanningSettingsView: View {
} }
} }
let allMatches = tournament.allMatches() _smartView()
}
.headerProminence(.increased)
.onAppear {
do {
try self.tournamentStore.matchSchedulers.addOrUpdate(instance: matchScheduler)
} catch {
Logger.error(error)
}
}
.overlay(alignment: .bottom) {
if schedulingDone {
if issueFound {
Label("Horaires mis à jour", systemImage: "xmark.circle.fill")
.toastFormatted()
.deferredRendering(for: .seconds(2))
} else {
Label("Horaires mis à jour", systemImage: "checkmark.circle.fill")
.toastFormatted()
.deferredRendering(for: .seconds(2))
}
}
if deletingDone {
Label("Tous les horaires ont été supprimés", systemImage: "clock.badge.xmark")
.toastFormatted()
.deferredRendering(for: .seconds(2))
}
if deletingDateMatchesDone {
Label("Horaires des matchs supprimés", systemImage: "clock.badge.xmark")
.toastFormatted()
.deferredRendering(for: .seconds(2))
}
}
.onChange(of: tournament.startDate) {
_save()
}
.onChange(of: tournament.courtCount) {
matchScheduler.courtsAvailable = Set(tournament.courtsAvailable())
_save()
}
.onChange(of: tournament.dayDuration) {
_save()
}
}
@ViewBuilder
private func _smartView() -> some View {
let allMatches = tournament.allMatches().filter({ $0.hasEnded() == false && $0.hasStarted() == false })
let allGroupStages = tournament.allGroupStages() let allGroupStages = tournament.allGroupStages()
let allRounds = tournament.allRounds() let allRounds = tournament.allRounds()
let matchesWithDate = allMatches.filter({ $0.startDate != nil }) let matchesWithDate = allMatches.filter({ $0.startDate != nil })
@ -156,7 +205,7 @@ struct PlanningSettingsView: View {
let groupStagesWithDate = allGroupStages.filter({ $0.startDate != nil }) let groupStagesWithDate = allGroupStages.filter({ $0.startDate != nil })
let roundsWithDate = allRounds.filter({ $0.startDate != nil }) let roundsWithDate = allRounds.filter({ $0.startDate != nil })
if matchesWithDate.isEmpty == false || groupStagesWithDate.isEmpty == false || roundsWithDate.isEmpty == false { if matchesWithDate.isEmpty == false {
Section { Section {
RowButtonView("Supprimer les horaires des matches", role: .destructive) { RowButtonView("Supprimer les horaires des matches", role: .destructive) {
do { do {
@ -172,9 +221,44 @@ struct PlanningSettingsView: View {
} }
} }
} footer: { } footer: {
Text("Garde les horaires définis pour les poules et les manches.") Text("Supprime les horaires des matchs restants non démarrés. Garde les horaires définis pour les poules et les manches du tableau.")
}
}
if groupStagesWithDate.isEmpty == false {
Section {
RowButtonView("Supprimer les horaires des poules", role: .destructive) {
do {
deletingDone = false
allGroupStages.forEach({ $0.startDate = nil })
try self.tournamentStore.groupStages.addOrUpdate(contentOfs: allGroupStages)
deletingDone = true
} catch {
Logger.error(error)
}
}
}
} }
if roundsWithDate.isEmpty == false {
Section {
RowButtonView("Supprimer les horaires du tableau", role: .destructive) {
do {
deletingDone = false
allRounds.forEach({ $0.startDate = nil })
try self.tournamentStore.rounds.addOrUpdate(contentOfs: allRounds)
deletingDone = true
} catch {
Logger.error(error)
}
}
} footer: {
Text("Supprime les horaires définis pour les manches du tableau.")
}
}
if matchesWithDate.isEmpty == false && groupStagesWithDate.isEmpty == false && roundsWithDate.isEmpty == false {
Section { Section {
RowButtonView("Supprimer tous les horaires", role: .destructive) { RowButtonView("Supprimer tous les horaires", role: .destructive) {
do { do {
@ -195,8 +279,36 @@ struct PlanningSettingsView: View {
Logger.error(error) Logger.error(error)
} }
} }
} footer: {
Text("Supprime les horaires des matchs restants non démarrés, les horaires définis pour les poules et les manches du tableau.")
}
}
#if DEBUG
Section {
RowButtonView("Debug delete all dates", role: .destructive) {
do {
deletingDone = false
tournament.allMatches().forEach({
$0.startDate = nil
$0.endDate = nil
$0.confirmed = false
})
try self.tournamentStore.matches.addOrUpdate(contentOfs: tournament.allMatches())
allGroupStages.forEach({ $0.startDate = nil })
try self.tournamentStore.groupStages.addOrUpdate(contentOfs: allGroupStages)
allRounds.forEach({ $0.startDate = nil })
try self.tournamentStore.rounds.addOrUpdate(contentOfs: allRounds)
deletingDone = true
} catch {
Logger.error(error)
}
} }
} }
#endif
Section { Section {
if groupStagesWithDate.isEmpty == false { if groupStagesWithDate.isEmpty == false {
@ -220,50 +332,6 @@ struct PlanningSettingsView: View {
Text("Padel Club programmera tous les matchs de votre tournoi en fonction de différents paramètres, ") + Text("tout en tenant compte des horaires que vous avez fixé.").underline() Text("Padel Club programmera tous les matchs de votre tournoi en fonction de différents paramètres, ") + Text("tout en tenant compte des horaires que vous avez fixé.").underline()
} }
} }
.headerProminence(.increased)
.onAppear {
do {
try self.tournamentStore.matchSchedulers.addOrUpdate(instance: matchScheduler)
} catch {
Logger.error(error)
}
}
.overlay(alignment: .bottom) {
if schedulingDone {
if issueFound {
Label("Horaires mis à jour", systemImage: "xmark.circle.fill")
.toastFormatted()
.deferredRendering(for: .seconds(2))
} else {
Label("Horaires mis à jour", systemImage: "checkmark.circle.fill")
.toastFormatted()
.deferredRendering(for: .seconds(2))
}
}
if deletingDone {
Label("Tous les horaires ont été supprimés", systemImage: "clock.badge.xmark")
.toastFormatted()
.deferredRendering(for: .seconds(2))
}
if deletingDateMatchesDone {
Label("Horaires des matchs supprimés", systemImage: "clock.badge.xmark")
.toastFormatted()
.deferredRendering(for: .seconds(2))
}
}
.onChange(of: tournament.startDate) {
_save()
}
.onChange(of: tournament.courtCount) {
matchScheduler.courtsAvailable = Set(tournament.courtsAvailable())
_save()
}
.onChange(of: tournament.dayDuration) {
_save()
}
}
@ViewBuilder @ViewBuilder
private func _optionsView() -> some View { private func _optionsView() -> some View {

@ -109,7 +109,11 @@ struct RoundView: View {
LabeledContent { LabeledContent {
let status = upperRound.status() let status = upperRound.status()
if status.0 == status.1 { if status.0 == status.1 {
if status.0 == 0 {
Text("aucun match")
} else {
Image(systemName: "checkmark").foregroundStyle(.green) Image(systemName: "checkmark").foregroundStyle(.green)
}
} else { } else {
Text("\(status.0) terminé\(status.0.pluralSuffix) sur \(status.1)") Text("\(status.0) terminé\(status.0.pluralSuffix) sur \(status.1)")
} }

Loading…
Cancel
Save