fix planning stuff

newoffer2025
Razmig Sarkissian 5 months ago
parent 0a3606916b
commit 87c7c074d3
  1. 4
      PadelClub.xcodeproj/project.pbxproj
  2. 10
      PadelClub/Views/Match/Components/MatchDateView.swift
  3. 6
      PadelClub/Views/Match/MatchDetailView.swift
  4. 70
      PadelClub/Views/Planning/CourtAvailabilitySettingsView.swift
  5. 241
      PadelClub/Views/Planning/PlanningView.swift

@ -3129,7 +3129,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.2.38;
MARKETING_VERSION = 1.2.39;
PRODUCT_BUNDLE_IDENTIFIER = app.padelclub;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
@ -3175,7 +3175,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.2.38;
MARKETING_VERSION = 1.2.39;
PRODUCT_BUNDLE_IDENTIFIER = app.padelclub;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";

@ -46,7 +46,7 @@ struct MatchDateView: View {
if let updatedField {
match.setCourt(updatedField)
}
match.startDate = currentDate
match.updateStartDate(currentDate, keepPlannedStartDate: true)
match.endDate = nil
match.confirmed = true
_save()
@ -55,7 +55,7 @@ struct MatchDateView: View {
if let updatedField {
match.setCourt(updatedField)
}
match.startDate = Calendar.current.date(byAdding: .minute, value: 5, to: currentDate)
match.updateStartDate(Calendar.current.date(byAdding: .minute, value: 5, to: currentDate), keepPlannedStartDate: true)
match.endDate = nil
match.confirmed = true
_save()
@ -64,7 +64,7 @@ struct MatchDateView: View {
if let updatedField {
match.setCourt(updatedField)
}
match.startDate = Calendar.current.date(byAdding: .minute, value: 15, to: currentDate)
match.updateStartDate(Calendar.current.date(byAdding: .minute, value: 15, to: currentDate), keepPlannedStartDate: true)
match.endDate = nil
match.confirmed = true
_save()
@ -73,7 +73,7 @@ struct MatchDateView: View {
if let updatedField {
match.setCourt(updatedField)
}
match.startDate = Calendar.current.date(byAdding: .minute, value: estimatedDuration, to: currentDate)
match.updateStartDate(Calendar.current.date(byAdding: .minute, value: estimatedDuration, to: currentDate), keepPlannedStartDate: true)
match.endDate = nil
match.confirmed = true
_save()
@ -96,7 +96,7 @@ struct MatchDateView: View {
}
Divider()
Button("Retirer l'horaire") {
match.cleanScheduleAndSave()
match.updateStartDate(nil, keepPlannedStartDate: true)
}
} label: {
label

@ -329,10 +329,7 @@ struct MatchDetailView: View {
}
}
Button(role: .destructive) {
match.startDate = nil
match.endDate = nil
match.confirmed = false
save()
match.updateStartDate(nil, keepPlannedStartDate: true)
} label: {
Text("Supprimer l'horaire")
}
@ -348,6 +345,7 @@ struct MatchDetailView: View {
}
Divider()
Button(role: .destructive) {
match.updateStartDate(nil, keepPlannedStartDate: false)
match.resetTeamScores(outsideOf: [])
match.resetMatch()
match.confirmed = false

@ -262,20 +262,29 @@ struct DateAdjusterView: View {
var body: some View {
HStack(spacing: 4) {
if let matchFormat {
_createButton(label: "-\(matchFormat.defaultEstimatedDuration)m", timeOffset: -matchFormat.defaultEstimatedDuration, component: .minute)
_createButton(label: "+\(matchFormat.defaultEstimatedDuration)m", timeOffset: +matchFormat.defaultEstimatedDuration, component: .minute)
_createButton(label: "-\(matchFormat.getEstimatedDuration())m", timeOffset: -matchFormat.getEstimatedDuration(), component: .minute)
Divider()
_createButton(label: "+\(matchFormat.getEstimatedDuration())m", timeOffset: +matchFormat.getEstimatedDuration(), component: .minute)
Divider()
_createButton(label: "-\(matchFormat.estimatedTimeWithBreak)m", timeOffset: -matchFormat.estimatedTimeWithBreak, component: .minute)
Divider()
_createButton(label: "+\(matchFormat.estimatedTimeWithBreak)m", timeOffset: +matchFormat.estimatedTimeWithBreak, component: .minute)
} else if let time {
_createButton(label: "-\(time)m", timeOffset: -time, component: .minute)
Divider()
_createButton(label: "-\(time/2)m", timeOffset: -time/2, component: .minute)
Divider()
_createButton(label: "+\(time/2)m", timeOffset: time/2, component: .minute)
Divider()
_createButton(label: "+\(time)m", timeOffset: time, component: .minute)
} else {
_createButton(label: "-1h", timeOffset: -1, component: .hour)
_createButton(label: "-1h", timeOffset: -60, component: .minute)
Divider()
_createButton(label: "-30m", timeOffset: -30, component: .minute)
Divider()
_createButton(label: "+30m", timeOffset: 30, component: .minute)
_createButton(label: "+1h", timeOffset: 1, component: .hour)
Divider()
_createButton(label: "+1h", timeOffset: 60, component: .minute)
}
}
.font(.headline)
@ -287,11 +296,58 @@ struct DateAdjusterView: View {
}) {
Text(label)
.lineLimit(1)
.font(.footnote)
.underline()
.frame(maxWidth: .infinity) // Make buttons take equal space
}
.buttonStyle(.borderedProminent)
.buttonStyle(.borderless)
.tint(.master)
}
}
struct StepAdjusterView: View {
@Binding var step: Int
var time: Int?
var matchFormat: MatchFormat?
var body: some View {
HStack(spacing: 4) {
if let matchFormat {
_createButton(label: "-\(matchFormat.getEstimatedDuration())m", timeOffset: -matchFormat.getEstimatedDuration(), component: .minute)
Divider()
_createButton(label: "+\(matchFormat.getEstimatedDuration())m", timeOffset: +matchFormat.getEstimatedDuration(), component: .minute)
Divider()
_createButton(label: "-\(matchFormat.estimatedTimeWithBreak)m", timeOffset: -matchFormat.estimatedTimeWithBreak, component: .minute)
Divider()
_createButton(label: "+\(matchFormat.estimatedTimeWithBreak)m", timeOffset: +matchFormat.estimatedTimeWithBreak, component: .minute)
} else if let time {
_createButton(label: "-\(time)m", timeOffset: -time, component: .minute)
Divider()
_createButton(label: "-\(time/2)m", timeOffset: -time/2, component: .minute)
Divider()
_createButton(label: "+\(time/2)m", timeOffset: time/2, component: .minute)
Divider()
_createButton(label: "+\(time)m", timeOffset: time, component: .minute)
} else {
_createButton(label: "-1h", timeOffset: -60, component: .minute)
Divider()
_createButton(label: "-30m", timeOffset: -30, component: .minute)
Divider()
_createButton(label: "+30m", timeOffset: 30, component: .minute)
Divider()
_createButton(label: "+1h", timeOffset: 60, component: .minute)
}
}
.font(.headline)
}
private func _createButton(label: String, timeOffset: Int, component: Calendar.Component) -> some View {
Button(action: {
step += timeOffset
}) {
Text(label)
.lineLimit(1)
.frame(maxWidth: .infinity) // Make buttons take equal space
}
.buttonStyle(.borderless)
.tint(.master)
}
}

@ -110,7 +110,7 @@ struct PlanningView: View {
if _confirmationMode() {
ToolbarItem(placement: .topBarLeading) {
Button("Annuler") {
Button(enableMove ? "Annuler" : "Terminer") {
enableMove = false
enableEditionBinding.wrappedValue = false
}
@ -130,82 +130,86 @@ struct PlanningView: View {
}
}
} else {
if notSlots == false {
ToolbarItemGroup(placement: .bottomBar) {
HStack {
ToolbarItemGroup(placement: .topBarTrailing) {
Menu {
if notSlots == false {
CourtOptionsView(timeSlots: timeSlots, underlined: false)
Spacer()
Toggle(isOn: $enableMove) {
Label {
Text("Déplacer")
Text("Déplacer un créneau")
} icon: {
Image(systemName: "rectangle.2.swap")
}
}
.popoverTip(timeSlotMoveOptionTip)
.disabled(_confirmationMode())
Spacer()
Toggle(isOn: enableEditionBinding) {
Text("Modifier")
Text("Modifier un horaire")
}
.disabled(_confirmationMode())
}
}
}
ToolbarItemGroup(placement: .topBarTrailing) {
Menu {
Section {
Picker(selection: $showFinishedMatches) {
Text("Afficher tous les matchs").tag(true)
Text("Masquer les matchs terminés").tag(false)
} label: {
Divider()
Menu {
Section {
Picker(selection: $showFinishedMatches) {
Text("Afficher tous les matchs").tag(true)
Text("Masquer les matchs terminés").tag(false)
} label: {
Text("Option de filtrage")
}
.labelsHidden()
.pickerStyle(.inline)
} header: {
Text("Option de filtrage")
}
.labelsHidden()
.pickerStyle(.inline)
} header: {
Text("Option de filtrage")
}
Divider()
Divider()
Section {
Picker(selection: $filterOption) {
ForEach(PlanningFilterOption.allCases) {
Text($0.localizedPlanningLabel()).tag($0)
Section {
Picker(selection: $filterOption) {
ForEach(PlanningFilterOption.allCases) {
Text($0.localizedPlanningLabel()).tag($0)
}
} label: {
Text("Option de triage")
}
} label: {
.labelsHidden()
.pickerStyle(.inline)
} header: {
Text("Option de triage")
}
.labelsHidden()
.pickerStyle(.inline)
} header: {
Text("Option de triage")
}
} label: {
Label("Trier", systemImage: "line.3.horizontal.decrease.circle")
.symbolVariant(
filterOption == .byCourt || showFinishedMatches ? .fill : .none)
}
} label: {
Label("Trier", systemImage: "line.3.horizontal.decrease.circle")
.symbolVariant(
filterOption == .byCourt || showFinishedMatches ? .fill : .none)
}
Button("Mettre à jour", systemImage: "arrow.trianglehead.2.clockwise.rotate.90.circle") {
let now = Date()
matches.forEach {
if let startDate = $0.startDate, startDate > now {
$0.plannedStartDate = $0.startDate
Divider()
Button("Mettre à jour", systemImage: "arrow.trianglehead.2.clockwise.rotate.90.circle") {
let now = Date()
matches.forEach {
if let startDate = $0.startDate, startDate > now {
$0.plannedStartDate = $0.startDate
}
}
let groupByTournaments = matches.grouped { match in
match.currentTournament()
}
groupByTournaments.forEach { tournament, matches in
tournament?.tournamentStore?.matches.addOrUpdate(contentOfs: matches)
}
}
let groupByTournaments = matches.grouped { match in
match.currentTournament()
}
groupByTournaments.forEach { tournament, matches in
tournament?.tournamentStore?.matches.addOrUpdate(contentOfs: matches)
}
.popoverTip(updatePlannedDatesTip)
} label: {
LabelOptions()
}
.popoverTip(updatePlannedDatesTip)
}
}
})
@ -235,8 +239,8 @@ struct PlanningView: View {
@Environment(\.editMode) private var editMode
@State private var selectedIds = Set<String>()
@State private var showDateUpdateView: Bool = false
@State private var dateToUpdate: Date = Date()
@State private var matchesToUpdate: [Match] = []
let days: [Date]
let keys: [Date]
let timeSlots: [Date: [Match]]
@ -258,28 +262,34 @@ struct PlanningView: View {
day: day,
keys: keys.filter({ $0.dayInt == day.dayInt }),
timeSlots: timeSlots,
selectedDay: selectedDay
selectedDay: selectedDay,
selectedIds: $selectedIds,
matchesForUpdateSheet: $matchesToUpdate
)
}
}
}
.toolbar(content: {
if editMode?.wrappedValue == .active {
ToolbarItem(placement: .bottomBar) {
ToolbarItem(placement: .topBarTrailing) {
Button {
showDateUpdateView = true
matchesToUpdate = matches.filter({ selectedIds.contains($0.stringId) })
} label: {
Text("Modifier la date des matchs sélectionnés")
Text("Modifier")
}
.buttonStyle(.borderless)
.disabled(selectedIds.isEmpty)
}
}
})
.onChange(of: matchesToUpdate, { oldValue, newValue in
showDateUpdateView = matchesToUpdate.count > 0
})
.sheet(isPresented: $showDateUpdateView, onDismiss: {
selectedIds.removeAll()
matchesToUpdate = []
}) {
let selectedMatches = matches.filter({ selectedIds.contains($0.stringId) })
DateUpdateView(selectedMatches: selectedMatches)
DateUpdateView(selectedMatches: matchesToUpdate)
}
}
}
@ -290,6 +300,7 @@ struct PlanningView: View {
let selectedMatches: [Match]
let selectedFormats: [MatchFormat]
@State private var dateToUpdate: Date
@State private var updateStep: Int = 0
init(selectedMatches: [Match]) {
self.selectedMatches = selectedMatches
@ -306,13 +317,37 @@ struct PlanningView: View {
DatePicker(selection: $dateToUpdate) {
Text(dateToUpdate.formatted(.dateTime.weekday(.wide))).font(.headline)
}
RowButtonView("Définir le nouvel horaire", role: .destructive) {
_setDate()
}
} footer: {
Text("Choisir un nouvel horaire pour tous les matchs sélectionnés")
}
Section {
DateAdjusterView(date: $dateToUpdate)
DateAdjusterView(date: $dateToUpdate, time: 10)
LabeledContent {
StepperView(title: "minutes", count: $updateStep, step: 5)
} label: {
Text("Décalage")
}
RowButtonView("Décaler les horaires", role: .destructive) {
_updateDate()
}
} footer: {
Text("décale CHAQUE horaire du nombre de minutes indiqué")
.foregroundStyle(.logoRed)
}
VStack {
StepAdjusterView(step: $updateStep)
Divider()
StepAdjusterView(step: $updateStep, time: 10)
ForEach(selectedFormats, id: \.self) { matchFormat in
DateAdjusterView(date: $dateToUpdate, matchFormat: matchFormat)
Divider()
StepAdjusterView(step: $updateStep, matchFormat: matchFormat)
}
}
@ -325,7 +360,10 @@ struct PlanningView: View {
}
}
.navigationTitle("Modification de la date")
.onChange(of: updateStep, { oldValue, newValue in
dateToUpdate.addTimeInterval(TimeInterval((newValue - oldValue) * 60))
})
.navigationTitle("Modifier l'horaire")
.navigationBarTitleDisplayMode(.inline)
.toolbarBackground(.visible, for: .navigationBar)
.toolbar(content: {
@ -334,25 +372,34 @@ struct PlanningView: View {
dismiss()
}
}
ToolbarItem(placement: .topBarTrailing) {
Button("Valider") {
_updateDate()
}
}
})
}
}
private func _updateDate() {
selectedMatches.forEach { match in
if match.hasStarted() || match.hasEnded() {
match.plannedStartDate?.addTimeInterval(TimeInterval(updateStep * 60))
} else {
match.startDate?.addTimeInterval(TimeInterval(updateStep * 60))
}
}
let groupByTournaments = selectedMatches.grouped { match in
match.currentTournament()
}
groupByTournaments.forEach { tournament, matches in
tournament?.tournamentStore?.matches.addOrUpdate(contentOfs: matches)
}
dismiss()
}
private func _setDate() {
selectedMatches.forEach { match in
if match.hasStarted() || match.hasEnded() {
match.plannedStartDate = dateToUpdate
} else {
let hasStarted = match.currentTournament()?.hasStarted() == true
match.startDate = dateToUpdate
if hasStarted {
match.plannedStartDate = dateToUpdate
}
}
}
@ -378,7 +425,10 @@ struct PlanningView: View {
let keys: [Date]
let timeSlots: [Date: [Match]]
let selectedDay: Date?
@Binding var selectedIds: Set<String>
@State private var selectAll: Bool = false
@Binding var matchesForUpdateSheet: [Match]
var body: some View {
Section {
ForEach(keys, id: \.self) { key in
@ -386,12 +436,22 @@ struct PlanningView: View {
key: key,
matches: timeSlots[key]?.sorted(
by: filterOption == .byDefault
? \.computedOrder : \.courtIndexForSorting) ?? []
? \.computedOrder : \.courtIndexForSorting) ?? [], matchesForUpdateSheet: $matchesForUpdateSheet
)
}
.onMove(perform: enableMove ? moveSection : nil)
} header: {
HeaderView(day: day, timeSlots: timeSlots)
if editMode?.wrappedValue == .active {
HStack {
Spacer()
FooterButtonView(selectAll ? "Tout desélectionner" : "Tout sélectionner") {
selectAll.toggle()
}
.textCase(nil)
}
} else {
HeaderView(day: day, timeSlots: timeSlots)
}
} footer: {
VStack(alignment: .leading) {
if day.monthYearFormatted == Date.distantFuture.monthYearFormatted {
@ -403,6 +463,16 @@ struct PlanningView: View {
CourtOptionsView(timeSlots: timeSlots, underlined: true)
}
}
.onChange(of: selectAll, { oldValue, newValue in
if oldValue == false, newValue == true {
selectedIds = Set(timeSlots.filter({ keys.contains($0.key) }).values.flatMap({ values in values.compactMap({ match in match.stringId }) }))
} else if oldValue == true, newValue == false {
selectedIds.removeAll()
}
})
.onChange(of: editMode?.wrappedValue) { oldValue, newValue in
selectAll = false
}
}
func moveSection(from source: IndexSet, to destination: Int) {
@ -450,7 +520,7 @@ struct PlanningView: View {
let matches: [Match]
@State private var isExpanded: Bool = false
@State private var showDateUpdateView: Bool = false
@Binding var matchesForUpdateSheet: [Match]
var body: some View {
if !matches.isEmpty {
@ -462,21 +532,23 @@ struct PlanningView: View {
} label: {
TimeSlotHeaderView(key: key, matches: matches)
}
.onChange(of: editMode?.wrappedValue, { oldValue, newValue in
if oldValue == .inactive, newValue == .active, isExpanded == false {
isExpanded = true
} else if oldValue == .active, newValue == .inactive, isExpanded == true {
isExpanded = false
}
})
.contextMenu {
PlanningView.CourtOptionsView(timeSlots: [key: matches], underlined: false)
Button {
showDateUpdateView = true
matchesForUpdateSheet = matches
} label: {
Text("Modifier la date")
}
}
.sheet(isPresented: $showDateUpdateView, onDismiss: {
}) {
PlanningView.DateUpdateView(selectedMatches: matches)
}
// .onChange(of: editMode?.wrappedValue) {
// if editMode?.wrappedValue == .active, isExpanded == false {
// isExpanded = true
@ -500,6 +572,7 @@ struct PlanningView: View {
} label: {
MatchRowView(match: match)
}
.listRowView(isActive: match.hasStarted(), color: .green, hideColorVariation: true)
}
}
}

Loading…
Cancel
Save