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.
220 lines
9.2 KiB
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())
|
|
}
|
|
|