multistore
Razmig Sarkissian 2 years ago
parent 5c07386863
commit 6547a8de01
  1. 4
      PadelClub.xcodeproj/project.pbxproj
  2. 3
      PadelClub/Extensions/Array+Extensions.swift
  3. 4
      PadelClub/ViewModel/SeedInterval.swift
  4. 2
      PadelClub/Views/Calling/CallView.swift
  5. 2
      PadelClub/Views/Club/ClubsView.swift
  6. 288
      PadelClub/Views/Components/FortuneWheelView.swift
  7. 50
      PadelClub/Views/Components/GenericDestinationPickerView.swift
  8. 1
      PadelClub/Views/Components/MatchListView.swift
  9. 3
      PadelClub/Views/Event/EventCreationView.swift
  10. 1
      PadelClub/Views/Event/TournamentConfiguratorView.swift
  11. 1
      PadelClub/Views/Match/MatchDetailView.swift
  12. 25
      PadelClub/Views/Match/MatchSetupView.swift
  13. 2
      PadelClub/Views/Navigation/Agenda/ActivityView.swift
  14. 1
      PadelClub/Views/Navigation/Agenda/EmptyActivityView.swift
  15. 4
      PadelClub/Views/Navigation/Toolbox/ToolboxView.swift
  16. 89
      PadelClub/Views/Planning/PlanningSettingsView.swift
  17. 2
      PadelClub/Views/Round/LoserRoundView.swift
  18. 20
      PadelClub/Views/Round/RoundView.swift
  19. 6
      PadelClub/Views/Team/TeamPickerView.swift
  20. 4
      PadelClub/Views/Tournament/Screen/Components/TournamentMatchFormatsSettingsView.swift
  21. 5
      PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift
  22. 8
      PadelClub/Views/Tournament/TournamentRunningView.swift
  23. 19
      PadelClub/Views/Tournament/TournamentView.swift

@ -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 = "<group>"; };
FFC83D4E2BB807D100750834 /* RoundsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoundsView.swift; sourceTree = "<group>"; };
FFC83D502BB8087E00750834 /* RoundView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoundView.swift; sourceTree = "<group>"; };
FFC91AF82BD6A09100B29808 /* FortuneWheelView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FortuneWheelView.swift; sourceTree = "<group>"; };
FFCFC0012BBC39A600B82851 /* EditScoreView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditScoreView.swift; sourceTree = "<group>"; };
FFCFC00D2BBC3D4600B82851 /* PointSelectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PointSelectionView.swift; sourceTree = "<group>"; };
FFCFC0112BBC3E1A00B82851 /* PointView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PointView.swift; sourceTree = "<group>"; };
@ -744,6 +746,7 @@
FF025ADC2BD0C94300A86CF8 /* FooterButtonView.swift */,
FFBF065D2BBD8040009D6715 /* MatchListView.swift */,
FF967CF72BAEDF0000A9A3BD /* Labels.swift */,
FFC91AF82BD6A09100B29808 /* FortuneWheelView.swift */,
);
path = Components;
sourceTree = "<group>";
@ -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 */,

@ -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) }
}
}

@ -16,6 +16,10 @@ struct SeedInterval: Hashable, Comparable {
return lhs.first < rhs.first
}
func isFixed() -> Bool {
first == 1 && last == 2
}
var count: Int {
dimension
}

@ -137,6 +137,7 @@ struct CallView: View {
break
}
}
.tint(.master)
case .mail(_, let recipients, let bccRecipients, let body, let subject, _):
MailComposeView(recipients: recipients, bccRecipients: bccRecipients, body: body, subject: subject) { result in
switch result {
@ -157,6 +158,7 @@ struct CallView: View {
break
}
}
.tint(.master)
}
}
}

@ -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) {

@ -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
}
}

@ -25,8 +25,8 @@ struct GenericDestinationPickerView<T: Identifiable & Selectable>: 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<T: Identifiable & Selectable>: 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()

@ -27,7 +27,6 @@ struct MatchListView: View {
} label: {
LabeledContent {
Text(matches.count.formatted() + " match" + matches.count.pluralSuffix)
.foregroundStyle(.master)
} label: {
Text(section.firstCapitalized)
}

@ -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 {

@ -39,7 +39,6 @@ struct TournamentConfigurationView: View {
StepperView(count: $tournament.teamCount, minimum: minimumTeamsCount, maximum: maximumTeamsCount)
} label: {
Text("Équipes souhaitées")
Text(tournament.teamCount.formatted())
}
}
}

@ -151,6 +151,7 @@ struct MatchDetailView: View {
}
.sheet(isPresented: $showDetails) {
MatchTeamDetailView(match: match)
.tint(.master)
}
.sheet(item: $scoreType, onDismiss: {
if match.hasEnded() {

@ -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()
}
}
}

@ -103,6 +103,8 @@ struct ActivityView: View {
.onDisappear { presentToolbar = false }
.sheet(item: $newTournament) { tournament in
EventCreationView(tournaments: [tournament])
.environment(navigation)
.tint(.master)
}
.refreshable {
if navigation.agendaDestination == .tenup {

@ -29,6 +29,7 @@ struct EmptyActivityView: View {
}
.sheet(item: $newTournament) { tournament in
EventCreationView(tournaments: [tournament])
.tint(.master)
}
}
}

@ -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)

@ -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
@ -85,47 +86,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 {
@ -169,6 +146,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()

@ -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")

@ -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

@ -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)
}
}

@ -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,

@ -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() {

@ -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")
}

@ -37,7 +37,6 @@ struct TournamentView: View {
NavigationLink(value: Screen.inscription) {
LabeledContent {
Text(tournament.unsortedTeams().count.formatted() + "/" + tournament.teamCount.formatted())
.foregroundStyle(.master)
} label: {
Text("Gestion des inscriptions")
if let closedRegistrationDate = tournament.closedRegistrationDate {
@ -48,17 +47,21 @@ struct TournamentView: View {
if let endOfInscriptionDate = tournament.mandatoryRegistrationCloseDate(), tournament.inscriptionClosed() == false && tournament.hasStarted() == false {
LabeledContent {
Text(endOfInscriptionDate.formatted(date: .abbreviated, time: .shortened))
.foregroundStyle(.master)
} label: {
Text("Date limite")
}
if endOfInscriptionDate < Date() {
RowButtonView("Clôturer les inscriptions") {
tournament.lockRegistration()
_save()
}
}
} footer: {
if tournament.inscriptionClosed() {
Button {
tournament.lockRegistration()
_save()
} label: {
Text("clôturer les inscriptions")
.underline()
}
.buttonStyle(.borderless)
}
}

Loading…
Cancel
Save