You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
PadelClub/PadelClub/Views/Planning/PlanningSettingsView.swift

220 lines
9.2 KiB

//
// PlanningSettingsView.swift
// PadelClub
//
// Created by Razmig Sarkissian on 07/04/2024.
//
import SwiftUI
struct PlanningSettingsView: View {
@EnvironmentObject var dataStore: DataStore
var tournament: Tournament
@State private var randomCourtDistribution: Bool
@State private var groupStageCourtCount: Int
@State private var upperBracketBreakTime: Bool
@State private var loserBracketBreakTime: Bool
@State private var rotationDifferenceIsImportant: Bool
@State private var loserBracketRotationDifference: Int
@State private var upperBracketRotationDifference: Int
@State private var timeDifferenceLimit: Double
@State private var shouldHandleUpperRoundSlice: Bool
@State private var isScheduling: Bool = false
@State private var schedulingDone: Bool = false
init(tournament: Tournament) {
self.tournament = tournament
self._groupStageCourtCount = State(wrappedValue: tournament.groupStageCourtCount ?? 1)
self._loserBracketRotationDifference = State(wrappedValue: MatchScheduler.shared.loserBracketRotationDifference)
self._upperBracketRotationDifference = State(wrappedValue: MatchScheduler.shared.upperBracketRotationDifference)
self._timeDifferenceLimit = State(wrappedValue: MatchScheduler.shared.timeDifferenceLimit)
self._rotationDifferenceIsImportant = State(wrappedValue: MatchScheduler.shared.rotationDifferenceIsImportant())
self._randomCourtDistribution = State(wrappedValue: MatchScheduler.shared.randomizeCourts())
self._upperBracketBreakTime = State(wrappedValue: MatchScheduler.shared.accountUpperBracketBreakTime())
self._loserBracketBreakTime = State(wrappedValue: MatchScheduler.shared.accountLoserBracketBreakTime())
self._shouldHandleUpperRoundSlice = State(wrappedValue: MatchScheduler.shared.shouldHandleUpperRoundSlice())
}
var body: some View {
@Bindable var tournament = tournament
List {
Section {
DatePicker(tournament.startDate.formatted(.dateTime.weekday()), selection: $tournament.startDate)
LabeledContent {
StepperView(count: $tournament.dayDuration, minimum: 1, maximum: 1_000)
} label: {
Text("Durée")
Text("\(tournament.dayDuration) jour" + tournament.dayDuration.pluralSuffix)
}
} header: {
Text("Démarrage et durée du tournoi")
} footer: {
Text("todo: Expliquer ce que ca fait")
}
Section {
TournamentFieldsManagerView(localizedStringKey: "Terrains maximum", count: $tournament.courtCount, max: 100)
if tournament.groupStages().isEmpty == false {
TournamentFieldsManagerView(localizedStringKey: "Terrains par poule", count: $groupStageCourtCount, max: tournament.maximumCourtsPerGroupSage())
}
NavigationLink {
} label: {
Text("Disponibilité des terrains")
}
}
Section {
Toggle(isOn: $randomCourtDistribution) {
Text("Distribuer les terrains au hasard")
}
Toggle(isOn: $shouldHandleUpperRoundSlice) {
Text("Équilibrer les matchs d'une manche sur plusieurs tours")
}
Toggle(isOn: $upperBracketBreakTime) {
Text("Tableau : tenir compte des pauses")
}
Toggle(isOn: $loserBracketBreakTime) {
Text("Classement : tenir compte des pauses")
}
Toggle(isOn: $rotationDifferenceIsImportant) {
Text("Forcer un créneau supplémentaire entre 2 phases")
}
LabeledContent {
StepperView(count: $upperBracketRotationDifference, minimum: 0, maximum: 2)
} label: {
Text("Tableau")
}
.disabled(rotationDifferenceIsImportant == false)
LabeledContent {
StepperView(count: $loserBracketRotationDifference, minimum: 0, maximum: 2)
} label: {
Text("Classement")
}
.disabled(rotationDifferenceIsImportant == false)
//timeDifferenceLimit
RowButtonView("Horaire intelligent", role: .destructive) {
schedulingDone = false
await _setupSchedule()
schedulingDone = true
}
}
Section {
RowButtonView("Supprimer tous les horaires", role: .destructive) {
let allMatches = tournament.allMatches()
allMatches.forEach({ $0.startDate = nil })
try? dataStore.matches.addOrUpdate(contentOfs: allMatches)
let allGroupStages = tournament.groupStages()
allGroupStages.forEach({ $0.startDate = nil })
try? dataStore.groupStages.addOrUpdate(contentOfs: allGroupStages)
let allRounds = tournament.allRounds()
allRounds.forEach({ $0.startDate = nil })
try? dataStore.rounds.addOrUpdate(contentOfs: allRounds)
}
}
}
.overlay(alignment: .bottom) {
if schedulingDone {
Label("Horaires mis à jour", systemImage: "checkmark.circle.fill")
.toastFormatted()
.deferredRendering(for: .seconds(2))
}
}
.onChange(of: groupStageCourtCount) {
tournament.groupStageCourtCount = groupStageCourtCount
_save()
}
.onChange(of: tournament.startDate) {
_save()
}
.onChange(of: tournament.courtCount) {
_save()
}
.onChange(of: tournament.groupStageCourtCount) {
_save()
}
.onChange(of: tournament.dayDuration) {
_save()
}
}
private func _setupSchedule() async {
let groupStageCourtCount = tournament.groupStageCourtCount ?? 1
let groupStages = tournament.groupStages()
let numberOfCourtsAvailablePerRotation: Int = tournament.courtCount
let matchScheduler = MatchScheduler.shared
matchScheduler.options.removeAll()
if randomCourtDistribution {
matchScheduler.options.insert(.randomizeCourts)
}
if shouldHandleUpperRoundSlice {
matchScheduler.options.insert(.shouldHandleUpperRoundSlice)
}
if upperBracketBreakTime {
matchScheduler.options.insert(.accountUpperBracketBreakTime)
}
if loserBracketBreakTime {
matchScheduler.options.insert(.accountLoserBracketBreakTime)
}
if rotationDifferenceIsImportant {
matchScheduler.options.insert(.rotationDifferenceIsImportant)
}
matchScheduler.loserBracketRotationDifference = loserBracketRotationDifference
matchScheduler.upperBracketRotationDifference = upperBracketRotationDifference
matchScheduler.timeDifferenceLimit = timeDifferenceLimit
let matches = tournament.groupStages().flatMap({ $0._matches() })
matches.forEach({ $0.startDate = nil })
var lastDate : Date = tournament.startDate
groupStages.chunked(into: groupStageCourtCount).forEach { groups in
groups.forEach({ $0.startDate = lastDate })
try? dataStore.groupStages.addOrUpdate(contentOfs: groups)
let dispatch = matchScheduler.groupStageDispatcher(numberOfCourtsAvailablePerRotation: numberOfCourtsAvailablePerRotation, groupStages: groups, startingDate: lastDate)
dispatch.timedMatches.forEach { matchSchedule in
if let match = matches.first(where: { $0.id == matchSchedule.matchID }) {
let estimatedDuration = match.matchFormat.getEstimatedDuration(tournament.additionalEstimationDuration)
let timeIntervalToAdd = (Double(matchSchedule.rotationIndex)) * Double(estimatedDuration) * 60
if let startDate = match.groupStageObject?.startDate {
let matchStartDate = startDate.addingTimeInterval(timeIntervalToAdd)
match.startDate = matchStartDate
lastDate = matchStartDate.addingTimeInterval(Double(estimatedDuration) * 60)
}
match.setCourt(matchSchedule.courtIndex + 1)
}
}
}
try? dataStore.matches.addOrUpdate(contentOfs: matches)
matchScheduler.updateSchedule(tournament: tournament, fromRoundId: nil, fromMatchId: nil, startDate: lastDate)
}
private func _save() {
try? dataStore.tournaments.addOrUpdate(instance: tournament)
}
}
#Preview {
PlanningSettingsView(tournament: Tournament.mock())
}