diff --git a/PadelClub.xcodeproj/project.pbxproj b/PadelClub.xcodeproj/project.pbxproj index 612c14b..ea4773d 100644 --- a/PadelClub.xcodeproj/project.pbxproj +++ b/PadelClub.xcodeproj/project.pbxproj @@ -219,6 +219,7 @@ FFC2DCB42BBE9ECD0046DB9F /* LoserRoundsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFC2DCB32BBE9ECD0046DB9F /* LoserRoundsView.swift */; }; FFC83D4F2BB807D100750834 /* RoundsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFC83D4E2BB807D100750834 /* RoundsView.swift */; }; FFC83D512BB8087E00750834 /* RoundView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFC83D502BB8087E00750834 /* RoundView.swift */; }; + FFC91AF92BD6A09100B29808 /* FortuneWheelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFC91AF82BD6A09100B29808 /* FortuneWheelView.swift */; }; FFCFBFFE2BBBE86600B82851 /* Algorithms in Frameworks */ = {isa = PBXBuildFile; productRef = FFCFBFFD2BBBE86600B82851 /* Algorithms */; }; FFCFC00C2BBC3D1E00B82851 /* EditScoreView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFCFC0012BBC39A600B82851 /* EditScoreView.swift */; }; FFCFC00E2BBC3D4600B82851 /* PointSelectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFCFC00D2BBC3D4600B82851 /* PointSelectionView.swift */; }; @@ -511,6 +512,7 @@ FFC2DCB32BBE9ECD0046DB9F /* LoserRoundsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoserRoundsView.swift; sourceTree = ""; }; FFC83D4E2BB807D100750834 /* RoundsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoundsView.swift; sourceTree = ""; }; FFC83D502BB8087E00750834 /* RoundView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoundView.swift; sourceTree = ""; }; + FFC91AF82BD6A09100B29808 /* FortuneWheelView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FortuneWheelView.swift; sourceTree = ""; }; FFCFC0012BBC39A600B82851 /* EditScoreView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditScoreView.swift; sourceTree = ""; }; FFCFC00D2BBC3D4600B82851 /* PointSelectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PointSelectionView.swift; sourceTree = ""; }; FFCFC0112BBC3E1A00B82851 /* PointView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PointView.swift; sourceTree = ""; }; @@ -745,6 +747,7 @@ FF025ADC2BD0C94300A86CF8 /* FooterButtonView.swift */, FFBF065D2BBD8040009D6715 /* MatchListView.swift */, FF967CF72BAEDF0000A9A3BD /* Labels.swift */, + FFC91AF82BD6A09100B29808 /* FortuneWheelView.swift */, ); path = Components; sourceTree = ""; @@ -1553,6 +1556,7 @@ FF967D0D2BAF3EB300A9A3BD /* MatchDateView.swift in Sources */, FFF964532BC262B000EEF017 /* PlanningSettingsView.swift in Sources */, FFF527D62BC6DDD000FF4EF2 /* MatchScheduleEditorView.swift in Sources */, + FFC91AF92BD6A09100B29808 /* FortuneWheelView.swift in Sources */, FFD784022B91C1B4000F62A6 /* WelcomeView.swift in Sources */, FFF8ACD62B923960008466FA /* URL+Extensions.swift in Sources */, FF089EBD2BB0287D00F0AEC7 /* PlayerView.swift in Sources */, diff --git a/PadelClub/Extensions/Array+Extensions.swift b/PadelClub/Extensions/Array+Extensions.swift index d908a0c..c437d43 100644 --- a/PadelClub/Extensions/Array+Extensions.swift +++ b/PadelClub/Extensions/Array+Extensions.swift @@ -15,7 +15,8 @@ extension Array { } func anySatisfy(_ p: (Element) -> Bool) -> Bool { - return !self.allSatisfy { !p($0) } + return first(where: { p($0) }) != nil + //return !self.allSatisfy { !p($0) } } } diff --git a/PadelClub/ViewModel/SeedInterval.swift b/PadelClub/ViewModel/SeedInterval.swift index d857a99..a4d1af6 100644 --- a/PadelClub/ViewModel/SeedInterval.swift +++ b/PadelClub/ViewModel/SeedInterval.swift @@ -16,6 +16,10 @@ struct SeedInterval: Hashable, Comparable { return lhs.first < rhs.first } + func isFixed() -> Bool { + first == 1 && last == 2 + } + var count: Int { dimension } diff --git a/PadelClub/Views/Calling/CallView.swift b/PadelClub/Views/Calling/CallView.swift index 0ac7364..8a3cb22 100644 --- a/PadelClub/Views/Calling/CallView.swift +++ b/PadelClub/Views/Calling/CallView.swift @@ -142,7 +142,11 @@ struct CallView: View { } else { SubscriptionView(showLackOfPlanMessage: true) } +<<<<<<< HEAD +======= + .tint(.master) +>>>>>>> 6547a8de016666a19407c6cf8b30e91087f201e2 case .mail(_, let recipients, let bccRecipients, let body, let subject, _): if Guard.main.paymentForNewTournament() != nil { @@ -168,6 +172,7 @@ struct CallView: View { } else { SubscriptionView(showLackOfPlanMessage: true) } + .tint(.master) } } } diff --git a/PadelClub/Views/Club/ClubsView.swift b/PadelClub/Views/Club/ClubsView.swift index 16d8cea..dce11a8 100644 --- a/PadelClub/Views/Club/ClubsView.swift +++ b/PadelClub/Views/Club/ClubsView.swift @@ -72,9 +72,11 @@ struct ClubsView: View { .navigationTitle(selection == nil ? "Mes clubs" : "Choisir un club") .sheet(isPresented: $presentClubCreationView) { CreateClubView() + .tint(.master) } .sheet(isPresented: $presentClubSearchView) { ClubImportView() + .tint(.master) } .toolbar { ToolbarItemGroup(placement: .topBarTrailing) { diff --git a/PadelClub/Views/Components/FortuneWheelView.swift b/PadelClub/Views/Components/FortuneWheelView.swift new file mode 100644 index 0000000..ac3a688 --- /dev/null +++ b/PadelClub/Views/Components/FortuneWheelView.swift @@ -0,0 +1,288 @@ +// +// FortuneWheelView.swift +// PadelClub +// +// Created by Razmig Sarkissian on 22/04/2024. +// + +import SwiftUI + +protocol SpinDrawable { + func segmentLabel() -> String +} + +extension String: SpinDrawable { + func segmentLabel() -> String { + self + } +} + +extension Match: SpinDrawable { + func segmentLabel() -> String { + self.matchTitle(.wide) + } +} + +extension TeamRegistration: SpinDrawable { + func segmentLabel() -> String { + self.teamLabel(.short) + } +} + +struct DrawResult: Identifiable { + let id: UUID = UUID() + let drawee: Int + let drawIndex: Int +} + +struct DrawOption: Identifiable, SpinDrawable { + let id: UUID = UUID() + let initialIndex: Int + let option: SpinDrawable + + func segmentLabel() -> String { + option.segmentLabel() + } +} + +struct SpinDrawView: View { + @Environment(\.dismiss) private var dismiss + + let time: Date = Date() + let drawees: [any SpinDrawable] + @State var segments: [any SpinDrawable] + let completion: ([DrawResult]) -> Void // Completion closure + + @State private var drawCount: Int = 0 + @State private var draws: [DrawResult] = [DrawResult]() + @State private var drawOptions: [DrawOption] = [DrawOption]() + + var autoMode: Bool { + drawees.count > 1 + } + + func validationLabel(drawee: Int, result: SpinDrawable) -> String { + let draw = drawees[drawee] + return draw.segmentLabel() + " -> " + result.segmentLabel() + } + + @State private var selectedIndex: Int? + var body: some View { + List { + Section { + Text(time.formatted(date: .complete, time: .complete)) + Text(time, style: .timer) + } + + if let selectedIndex { + Section { + Text(validationLabel(drawee: drawCount, result: segments[draws.last!.drawIndex])) + if autoMode == false || drawCount == drawees.count { + RowButtonView("ok") { + dismiss() + } + } else { + Text("Prochain tirage en préparation") + } + } + } else if drawCount < drawees.count { + Section { + Text(drawees[drawCount].segmentLabel()) + } + + Section { + FortuneWheelTestView(segments: drawOptions, autoMode: autoMode) { index in + self.selectedIndex = index + self.draws.append(DrawResult(drawee: drawCount, drawIndex: drawOptions[index].initialIndex)) + self.drawOptions.remove(at: index) + + if autoMode && drawCount < drawees.count { + DispatchQueue.main.asyncAfter(deadline: .now() + 1) { + self.drawCount += 1 + if drawOptions.count == 1 { + self.draws.append(DrawResult(drawee: self.drawCount, drawIndex: self.drawOptions[0].initialIndex)) + self.drawOptions.remove(at: 0) + self.drawCount += 1 + self.selectedIndex = nil + } else { + self.selectedIndex = nil + } + } + } + } + .listRowBackground(Color.clear) + .listRowSeparator(.hidden) + } + } else { + Section { + Text("Tous les tirages sont terminés") + ForEach(draws) { drawResult in + Text(validationLabel(drawee: drawResult.drawee, result: segments[drawResult.drawIndex])) + } + } + + RowButtonView("Valider les tirages") { + completion(draws) + dismiss() + } + } + + Section { + Text("XXX") + Text("XXX") + Text("XXX") + } header: { + Text("Comité du tournoi") + } + } + .listStyle(.insetGrouped) + .scrollDisabled(true) + .onAppear { + for (index, segment) in segments.enumerated() { + drawOptions.append(DrawOption(initialIndex: index, option: segment)) + } + } + } +} + +struct FortuneWheelTestView: View { + @State private var rotation: Double = 0 + let segments: [any SpinDrawable] + let autoMode: Bool + let completion: (Int) -> Void // Completion closure + + var body: some View { + FortuneWheelView(segments: segments) + .rotationEffect(.degrees(rotation)) + .aspectRatio(contentMode: .fill) + .padding(.top, 5) + .overlay(alignment: .top) { + Triangle() + .fill(Color.red) + .stroke(Color.black, lineWidth: 2) + .frame(width: 20, height: 20) + .rotationEffect(.degrees(180)) + + } + .onAppear { + if autoMode { + DispatchQueue.main.asyncAfter(deadline: .now() + 1) { + rollWheel() + } + } + } + .gesture( + DragGesture() + .onChanged { value in + // Calculate rotation based on the velocity of the drag + let initialVelocity = value.predictedEndTranslation.width / 10 // Adjust sensitivity + rotation += Double(initialVelocity) + } + .onEnded { value in + // Roll the wheel when drag ends + rollWheel() + } + ) + } + + func rollWheel() { + + rotation = 0 + + // Generate a random angle for the wheel to rotate + let randomAngle = Double.random(in: 1440...2880) // Adjust range for more or less rotations + let duration = Double.random(in: 2...4) + + // Apply rotation animation with ease-out + withAnimation(.easeOut(duration: duration)) { + rotation += randomAngle + } + + + // Calculate the angle between the top center and the current rotation position + let segmentAngle = 360.0 / Double(segments.count) + let arrowAngle = 270.0 // Angle of the arrow at the top center of the wheel + let normalizedRotation = (rotation + 360).truncatingRemainder(dividingBy: 360) + let angleToTopCenter = (arrowAngle - normalizedRotation + 360).truncatingRemainder(dividingBy: 360) + + // Determine the selected segment based on the angle + let index = Int(angleToTopCenter / segmentAngle) + + DispatchQueue.main.asyncAfter(deadline: .now() + duration + 1) { + let selectedSegment = index < 0 ? segments.count + index : index // Normalize index + completion(selectedSegment) + } + } + + + +} + +struct FortuneWheelView: View { + let segments: [any SpinDrawable] + let colors: [Color] = [.yellow, .cyan, .green, .blue, .orange, .purple, .mint, .brown] + + func getColor(forIndex index: Int) -> Color { + if index < colors.count { + return colors[index] + } + + let level = Double(index)/Double(colors.count) / 100 + let modulo = colors[index%colors.count].variation(withHueOffset: level) + + return modulo + } + + var body: some View { + GeometryReader { proxy in + let radius = proxy.size.width / 2 + ZStack { + ForEach(segments.indices, id: \.self) { index in + Path { path in + let segmentAngle = 360.0 / Double(segments.count) + let startAngle = Angle(degrees: Double(index) * segmentAngle) + let endAngle = Angle(degrees: Double(index + 1) * segmentAngle) + let center = CGPoint(x: radius, y: radius) + path.move(to: center) + path.addArc(center: center, radius: radius, startAngle: startAngle, endAngle: endAngle, clockwise: false) + path.closeSubpath() + } + .fill(getColor(forIndex:index)) + + Text(segments[index].segmentLabel()).multilineTextAlignment(.trailing) + .rotationEffect(.degrees(Double(index) * (360 / Double(segments.count)) + (360 / Double(segments.count) / 2))) + .foregroundColor(.white) + .position(arcPosition(index: index, radius: radius)) + } + } + } + } + + // Calculate the position for the text in the middle of the arc segment + private func arcPosition(index: Int, radius: Double) -> CGPoint { + let segmentAngle = 360.0 / Double(segments.count) + let startAngle = Double(index) * segmentAngle + let endAngle = Double(index + 1) * segmentAngle + let centerAngle = (startAngle + endAngle) / 2 + let adjustedRadius = radius - 20.0 + let x = radius + (adjustedRadius - 30) * cos(centerAngle * .pi / 180) // Adjusted radius for better fit + let y = radius + (adjustedRadius - 30) * sin(centerAngle * .pi / 180) // Adjusted radius for better fit + return CGPoint(x: x, y: y) + } +} + +struct Triangle: Shape { + func path(in rect: CGRect) -> Path { + var path = Path() + path.move(to: CGPoint(x: rect.midX, y: rect.minY)) + path.addLine(to: CGPoint(x: rect.minX, y: rect.maxY)) + path.addLine(to: CGPoint(x: rect.maxX, y: rect.maxY)) + path.closeSubpath() + return path + } +} + +#Preview { + SpinDrawView(drawees: ["3", "4"], segments: ["1", "2"]) { draws in + } +} diff --git a/PadelClub/Views/Components/GenericDestinationPickerView.swift b/PadelClub/Views/Components/GenericDestinationPickerView.swift index 9bbdbad..93e9c6a 100644 --- a/PadelClub/Views/Components/GenericDestinationPickerView.swift +++ b/PadelClub/Views/Components/GenericDestinationPickerView.swift @@ -25,8 +25,8 @@ struct GenericDestinationPickerView: View { .padding() .background { Circle() - .fill(Color.white) - .opacity(selectedDestination == nil ? 1.0 : 0.4) + .fill(Color.master) + .opacity(selectedDestination == nil ? 1.0 : 0.2) } .buttonStyle(.plain) } @@ -40,31 +40,31 @@ struct GenericDestinationPickerView: View { .padding() .background { Capsule() - .fill(Color.white) - .opacity(selectedDestination?.id == destination.id ? 1.0 : 0.4) + .fill(Color.master) + .opacity(selectedDestination?.id == destination.id ? 1.0 : 0.2) } .buttonStyle(.plain) -// .overlay(alignment: .bottomTrailing) { -// if let badge = destination.badgeImage() { -// Image(systemName: badge.systemName()) -// .foregroundColor(badge.color()) -// .imageScale(.medium) -// .background ( -// Color(.systemBackground) -// .clipShape(.circle) -// ) -// .offset(x: 3, y: 3) -// } else if let count = destination.badgeValue(), count > 0 { -// Image(systemName: count <= 50 ? "\(count).circle.fill" : "plus.circle.fill") -// .foregroundColor(.red) -// .imageScale(.medium) -// .background ( -// Color(.systemBackground) -// .clipShape(.circle) -// ) -// .offset(x: 3, y: 3) -// } -// } + .overlay(alignment: .bottomTrailing) { + if let badge = destination.badgeImage() { + Image(systemName: badge.systemName()) + .foregroundColor(badge.color()) + .imageScale(.medium) + .background ( + Color(.systemBackground) + .clipShape(.circle) + ) + .offset(x: 3, y: 3) + } else if let count = destination.badgeValue(), count > 0 { + Image(systemName: count <= 50 ? "\(count).circle.fill" : "plus.circle.fill") + .foregroundColor(.red) + .imageScale(.medium) + .background ( + Color(.systemBackground) + .clipShape(.circle) + ) + .offset(x: 3, y: 3) + } + } } } .fixedSize() diff --git a/PadelClub/Views/Components/MatchListView.swift b/PadelClub/Views/Components/MatchListView.swift index 1445dcb..f78d29c 100644 --- a/PadelClub/Views/Components/MatchListView.swift +++ b/PadelClub/Views/Components/MatchListView.swift @@ -27,7 +27,6 @@ struct MatchListView: View { } label: { LabeledContent { Text(matches.count.formatted() + " match" + matches.count.pluralSuffix) - .foregroundStyle(.master) } label: { Text(section.firstCapitalized) } diff --git a/PadelClub/Views/Event/EventCreationView.swift b/PadelClub/Views/Event/EventCreationView.swift index 9ee8d7e..5502e9b 100644 --- a/PadelClub/Views/Event/EventCreationView.swift +++ b/PadelClub/Views/Event/EventCreationView.swift @@ -11,6 +11,7 @@ import TipKit struct EventCreationView: View { @Environment(\.dismiss) private var dismiss @EnvironmentObject var dataStore: DataStore + @Environment(NavigationViewModel.self) private var navigation: NavigationViewModel @State private var eventType: EventType = .approvedTournament @State private var animationType: AnimationType = .upAndDown @State private var startingDate: Date = Date().tomorrowAtNine @@ -105,7 +106,9 @@ struct EventCreationView: View { try? dataStore.tournaments.addOrUpdate(contentOfs: tournaments) dismiss() + navigation.path.append(tournaments.first!) } + .disabled(tournaments.isEmpty) } } .toolbar { diff --git a/PadelClub/Views/Event/TournamentConfiguratorView.swift b/PadelClub/Views/Event/TournamentConfiguratorView.swift index 222fdaf..4e5492f 100644 --- a/PadelClub/Views/Event/TournamentConfiguratorView.swift +++ b/PadelClub/Views/Event/TournamentConfiguratorView.swift @@ -39,7 +39,6 @@ struct TournamentConfigurationView: View { StepperView(count: $tournament.teamCount, minimum: minimumTeamsCount, maximum: maximumTeamsCount) } label: { Text("Équipes souhaitées") - Text(tournament.teamCount.formatted()) } } } diff --git a/PadelClub/Views/Match/MatchDetailView.swift b/PadelClub/Views/Match/MatchDetailView.swift index 399f2b3..4590fac 100644 --- a/PadelClub/Views/Match/MatchDetailView.swift +++ b/PadelClub/Views/Match/MatchDetailView.swift @@ -151,6 +151,7 @@ struct MatchDetailView: View { } .sheet(isPresented: $showDetails) { MatchTeamDetailView(match: match) + .tint(.master) } .sheet(item: $scoreType, onDismiss: { if match.hasEnded() { diff --git a/PadelClub/Views/Match/MatchSetupView.swift b/PadelClub/Views/Match/MatchSetupView.swift index c29289a..c13b58c 100644 --- a/PadelClub/Views/Match/MatchSetupView.swift +++ b/PadelClub/Views/Match/MatchSetupView.swift @@ -15,15 +15,7 @@ struct MatchSetupView: View { @ViewBuilder var body: some View { ForEach(TeamPosition.allCases) { teamPosition in - VStack(alignment: .leading) { - if teamPosition == .one { - Text("Branche du haut") - } - _teamView(inTeamPosition: teamPosition) - if teamPosition == .two { - Text("Branche du bas") - } - } + _teamView(inTeamPosition: teamPosition) } } @@ -75,6 +67,7 @@ struct MatchSetupView: View { }) if let tournament = match.currentTournament() { let availableSeedGroups = tournament.availableSeedGroups() + Text("ou") Menu { if walkOutSpot, luckyLosers.isEmpty == false { Button { @@ -99,25 +92,31 @@ struct MatchSetupView: View { } } label: { Text("Tirer au sort").tag(nil as SeedInterval?) + .underline() } .disabled(availableSeedGroups.isEmpty && walkOutSpot == false) - + Spacer() if match.isSeedLocked(atTeamPosition: teamPosition) { - Button("Libérer") { + Button { match.unlockSeedPosition(atTeamPosition: teamPosition) try? dataStore.matches.addOrUpdate(instance: match) + } label: { + Text("Libérer") + .underline() } } else { - Button("Réserver") { + Button { _ = match.lockAndGetSeedPosition(atTeamPosition: teamPosition) try? dataStore.matches.addOrUpdate(instance: match) + } label: { + Text("Réserver") + .underline() } } } } .fixedSize(horizontal: false, vertical: true) .buttonStyle(.borderless) - .underline() } } } diff --git a/PadelClub/Views/Navigation/Agenda/ActivityView.swift b/PadelClub/Views/Navigation/Agenda/ActivityView.swift index dc31d45..14edf88 100644 --- a/PadelClub/Views/Navigation/Agenda/ActivityView.swift +++ b/PadelClub/Views/Navigation/Agenda/ActivityView.swift @@ -104,6 +104,8 @@ struct ActivityView: View { .onDisappear { presentToolbar = false } .sheet(item: $newTournament) { tournament in EventCreationView(tournaments: [tournament]) + .environment(navigation) + .tint(.master) } .refreshable { if navigation.agendaDestination == .tenup { diff --git a/PadelClub/Views/Navigation/Agenda/EmptyActivityView.swift b/PadelClub/Views/Navigation/Agenda/EmptyActivityView.swift index a04b81d..bd0af4f 100644 --- a/PadelClub/Views/Navigation/Agenda/EmptyActivityView.swift +++ b/PadelClub/Views/Navigation/Agenda/EmptyActivityView.swift @@ -29,6 +29,7 @@ struct EmptyActivityView: View { } .sheet(item: $newTournament) { tournament in EventCreationView(tournaments: [tournament]) + .tint(.master) } } } diff --git a/PadelClub/Views/Navigation/Toolbox/ToolboxView.swift b/PadelClub/Views/Navigation/Toolbox/ToolboxView.swift index afb41de..a875a2c 100644 --- a/PadelClub/Views/Navigation/Toolbox/ToolboxView.swift +++ b/PadelClub/Views/Navigation/Toolbox/ToolboxView.swift @@ -36,8 +36,10 @@ struct ToolboxView: View { NavigationLink { DurationSettingsView() } label: { - Label("Estimation des durées moyennes", systemImage: "deskclock") + Label("Définir les durées moyennes", systemImage: "deskclock") } + } footer: { + Text("Vous pouvez définir vos propores estimations de durées de match en fonction du format de jeu.") } } .navigationTitle(TabDestination.toolbox.title) diff --git a/PadelClub/Views/Planning/PlanningSettingsView.swift b/PadelClub/Views/Planning/PlanningSettingsView.swift index c2806e6..16b54d9 100644 --- a/PadelClub/Views/Planning/PlanningSettingsView.swift +++ b/PadelClub/Views/Planning/PlanningSettingsView.swift @@ -21,6 +21,7 @@ struct PlanningSettingsView: View { @State private var shouldHandleUpperRoundSlice: Bool @State private var isScheduling: Bool = false @State private var schedulingDone: Bool = false + @State private var showOptions: Bool = false init(tournament: Tournament) { self.tournament = tournament @@ -87,47 +88,23 @@ struct PlanningSettingsView: View { } 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 } + + if showOptions { + _optionsView() + } + } footer: { + Button { + showOptions.toggle() + } label: { + Text((showOptions ? "masquer" : "voir") + " les réglages avancées") + .underline() + } + .buttonStyle(.borderless) } Section { @@ -171,6 +148,46 @@ struct PlanningSettingsView: View { } } + @ViewBuilder + private func _optionsView() -> some View { + 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 + + } + private func _setupSchedule() async { let groupStageCourtCount = tournament.groupStageCourtCount ?? 1 let groupStages = tournament.groupStages() diff --git a/PadelClub/Views/Round/LoserRoundView.swift b/PadelClub/Views/Round/LoserRoundView.swift index 424b7f4..e050d79 100644 --- a/PadelClub/Views/Round/LoserRoundView.swift +++ b/PadelClub/Views/Round/LoserRoundView.swift @@ -27,7 +27,7 @@ struct LoserRoundView: View { Section { let matches = isEditingTournamentSeed ? loserRound.playedMatches() : loserRound.playedMatches().filter({ $0.disabled == false }) ForEach(matches) { match in - MatchRowView(match: match, matchViewStyle: .standardStyle) + MatchRowView(match: match, matchViewStyle: .sectionedStandardStyle) .overlay { if match.disabled /*&& isEditingTournamentSeed*/ { Image(systemName: "xmark") diff --git a/PadelClub/Views/Round/RoundView.swift b/PadelClub/Views/Round/RoundView.swift index 7d78d83..52186b3 100644 --- a/PadelClub/Views/Round/RoundView.swift +++ b/PadelClub/Views/Round/RoundView.swift @@ -34,13 +34,31 @@ struct RoundView: View { } } else if let availableSeedGroup = tournament.seedGroupAvailable(atRoundIndex: round.index) { - RowButtonView("Placer \(availableSeedGroup.localizedLabel())") { + RowButtonView("Placer \(availableSeedGroup.localizedLabel())" + ((availableSeedGroup.isFixed() == false) ? " au hasard" : "")) { tournament.setSeeds(inRoundIndex: round.index, inSeedGroup: availableSeedGroup) if tournament.availableSeeds().isEmpty { _save() self.isEditingTournamentSeed.wrappedValue = false } } + + if (availableSeedGroup.isFixed() == false) { + let seeds = tournament.seeds(inSeedGroup: availableSeedGroup) + let availableSeedSpot = tournament.availableSeedSpot(inRoundIndex: round.index) + NavigationLink { + SpinDrawView(drawees: seeds, segments: availableSeedSpot) { draws in + draws.forEach { drawResult in + print(seeds[drawResult.drawee].teamLabel()) + print(availableSeedSpot[drawResult.drawIndex].matchTitle()) + seeds[drawResult.drawee].setSeedPosition(inSpot: availableSeedSpot[drawResult.drawIndex], slot: nil, opposingSeeding: false) + } + try? dataStore.matches.addOrUpdate(contentOfs: availableSeedSpot) + try? dataStore.teamRegistrations.addOrUpdate(contentOfs: seeds) + } + } label: { + Text("ou proposer le tirage au sort \(availableSeedGroup.localizedLabel())") + } + } } ForEach(round.playedMatches()) { match in diff --git a/PadelClub/Views/Team/TeamPickerView.swift b/PadelClub/Views/Team/TeamPickerView.swift index 012f95d..6a2459c 100644 --- a/PadelClub/Views/Team/TeamPickerView.swift +++ b/PadelClub/Views/Team/TeamPickerView.swift @@ -17,8 +17,11 @@ struct TeamPickerView: View { let teamPicked: ((TeamRegistration) -> (Void)) var body: some View { - Button("Choisir") { + Button { presentTeamPickerView = true + } label: { + Text("Choisir") + .underline() } .sheet(isPresented: $presentTeamPickerView) { NavigationStack { @@ -65,6 +68,7 @@ struct TeamPickerView: View { .toolbarBackground(.visible, for: .navigationBar) .navigationBarTitleDisplayMode(.inline) } + .tint(.master) } } diff --git a/PadelClub/Views/Tournament/Screen/Components/TournamentMatchFormatsSettingsView.swift b/PadelClub/Views/Tournament/Screen/Components/TournamentMatchFormatsSettingsView.swift index 76c9dce..7bca1b6 100644 --- a/PadelClub/Views/Tournament/Screen/Components/TournamentMatchFormatsSettingsView.swift +++ b/PadelClub/Views/Tournament/Screen/Components/TournamentMatchFormatsSettingsView.swift @@ -38,8 +38,10 @@ struct TournamentMatchFormatsSettingsView: View { NavigationLink { DurationSettingsView() } label: { - Label("Estimation des durées moyennes", systemImage: "deskclock") + Label("Définir les durées moyennes", systemImage: "deskclock") } + } footer: { + Text("Vous pouvez définir vos propores estimations de durées de match en fonction du format de jeu.") } } .onChange(of: [tournament.roundFormat, diff --git a/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift b/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift index 52273e7..3972a59 100644 --- a/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift +++ b/PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift @@ -71,6 +71,7 @@ struct InscriptionManagerView: View { } .sheet(isPresented: $isLearningMore) { LearnMoreSheetView(tournament: tournament) + .tint(.master) } .sheet(isPresented: $presentPlayerSearch, onDismiss: { selectionSearchField = nil @@ -90,17 +91,20 @@ struct InscriptionManagerView: View { presentPlayerCreation = true } } + .tint(.master) } .sheet(isPresented: $presentPlayerCreation) { PlayerPopoverView(source: _searchSource(), sex: _addPlayerSex()) { p in createdPlayers.insert(p) createdPlayerIds.insert(p.id) } + .tint(.master) } .sheet(isPresented: $presentImportView) { NavigationStack { FileImportView(fileContent: nil) } + .tint(.master) } .onChange(of: tournament.prioritizeClubMembers) { _save() @@ -117,6 +121,7 @@ struct InscriptionManagerView: View { currentRankSourceDate = tournament.rankSourceDate }) { UpdateSourceRankDateView(currentRankSourceDate: $currentRankSourceDate, confirmUpdateRank: $confirmUpdateRank, tournament: tournament) + .tint(.master) } .toolbar { if _isEditingTeam() { diff --git a/PadelClub/Views/Tournament/TournamentRunningView.swift b/PadelClub/Views/Tournament/TournamentRunningView.swift index 9250f04..30ea0b0 100644 --- a/PadelClub/Views/Tournament/TournamentRunningView.swift +++ b/PadelClub/Views/Tournament/TournamentRunningView.swift @@ -22,7 +22,7 @@ struct TournamentRunningView: View { NavigationLink(value: Screen.schedule) { let tournamentStatus = tournament.scheduleStatus() LabeledContent { - Text(tournamentStatus.completion).foregroundStyle(.master) + Text(tournamentStatus.completion) } label: { Text("Horaires") Text(tournamentStatus.label) @@ -32,7 +32,7 @@ struct TournamentRunningView: View { NavigationLink(value: Screen.call) { let tournamentStatus = tournament.callStatus() LabeledContent { - Text(tournamentStatus.completion).foregroundStyle(.master) + Text(tournamentStatus.completion) } label: { Text("Convocations") Text(tournamentStatus.label) @@ -42,7 +42,7 @@ struct TournamentRunningView: View { NavigationLink(value: Screen.cashier) { let tournamentStatus = tournament.cashierStatus() LabeledContent { - Text(tournamentStatus.completion).foregroundStyle(.master) + Text(tournamentStatus.completion) } label: { Text("Encaissement") Text(tournamentStatus.label) @@ -55,7 +55,6 @@ struct TournamentRunningView: View { NavigationLink(value: Screen.groupStage) { LabeledContent { Text(tournament.groupStageStatus()) - .foregroundStyle(.master) } label: { Text("Poules") } @@ -66,7 +65,6 @@ struct TournamentRunningView: View { NavigationLink(value: Screen.round) { LabeledContent { Text(tournament.bracketStatus()) - .foregroundStyle(.master) } label: { Text("Tableau") } diff --git a/PadelClub/Views/Tournament/TournamentView.swift b/PadelClub/Views/Tournament/TournamentView.swift index d88e46d..05ba607 100644 --- a/PadelClub/Views/Tournament/TournamentView.swift +++ b/PadelClub/Views/Tournament/TournamentView.swift @@ -25,7 +25,50 @@ struct TournamentView: View { VStack(spacing: 0.0) { +<<<<<<< HEAD OffersHeaderView() +======= +// if tournament.missingUnrankedValue() { +// Button("update NC") { +// tournament.femaleUnrankedValue = SourceFileManager.shared.getUnrankValue(forMale: false, rankSourceDate: tournament.rankSourceDate) +// tournament.maleUnrankedValue = SourceFileManager.shared.getUnrankValue(forMale: true, rankSourceDate: tournament.rankSourceDate) +// try? dataStore.tournaments.addOrUpdate(instance: tournament) +// } +// } +// +// + Section { + NavigationLink(value: Screen.inscription) { + LabeledContent { + Text(tournament.unsortedTeams().count.formatted() + "/" + tournament.teamCount.formatted()) + } label: { + Text("Gestion des inscriptions") + if let closedRegistrationDate = tournament.closedRegistrationDate { + Text("clôturé le " + closedRegistrationDate.formatted(date: .abbreviated, time: .shortened)) + } + } + } + if let endOfInscriptionDate = tournament.mandatoryRegistrationCloseDate(), tournament.inscriptionClosed() == false && tournament.hasStarted() == false { + LabeledContent { + Text(endOfInscriptionDate.formatted(date: .abbreviated, time: .shortened)) + } label: { + Text("Date limite") + } + } + } footer: { + + if tournament.inscriptionClosed() { + Button { + tournament.lockRegistration() + _save() + } label: { + Text("clôturer les inscriptions") + .underline() + } + .buttonStyle(.borderless) + } + } +>>>>>>> 6547a8de016666a19407c6cf8b30e91087f201e2 List {