fix wildcard management

fix p500 dates and schedule
add format aide memoire
optimize selectedsorted teams calls
sync2
Raz 9 months ago
parent a3459ba57f
commit 119d7e53c9
  1. 12
      PadelClub.xcodeproj/project.pbxproj
  2. 8
      PadelClub/Data/TeamRegistration.swift
  3. 26
      PadelClub/Data/Tournament.swift
  4. 34
      PadelClub/Utils/PadelRule.swift
  5. 7
      PadelClub/Views/Calling/SendToAllView.swift
  6. 2
      PadelClub/Views/Navigation/Toolbox/MatchFormatStorageView.swift
  7. 72
      PadelClub/Views/Planning/MatchFormatGuideView.swift
  8. 23
      PadelClub/Views/Planning/PlanningSettingsView.swift
  9. 7
      PadelClub/Views/Team/Components/TeamWeightView.swift
  10. 3
      PadelClub/Views/Team/EditingTeamView.swift
  11. 3
      PadelClub/Views/Team/TeamRowView.swift
  12. 2
      PadelClub/Views/Tournament/FileImportView.swift
  13. 2
      PadelClub/Views/Tournament/Screen/Components/TournamentMatchFormatsSettingsView.swift
  14. 119
      PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift

@ -798,6 +798,9 @@
FFA6D7852BB0B795003A31F3 /* FileImportManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFA6D7842BB0B795003A31F3 /* FileImportManager.swift */; }; FFA6D7852BB0B795003A31F3 /* FileImportManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFA6D7842BB0B795003A31F3 /* FileImportManager.swift */; };
FFA6D7872BB0B7A2003A31F3 /* CloudConvert.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFA6D7862BB0B7A2003A31F3 /* CloudConvert.swift */; }; FFA6D7872BB0B7A2003A31F3 /* CloudConvert.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFA6D7862BB0B7A2003A31F3 /* CloudConvert.swift */; };
FFB1C98B2C10255100B154A7 /* TournamentBroadcastRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFB1C98A2C10255100B154A7 /* TournamentBroadcastRowView.swift */; }; FFB1C98B2C10255100B154A7 /* TournamentBroadcastRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFB1C98A2C10255100B154A7 /* TournamentBroadcastRowView.swift */; };
FFB378342D672ED200EE82E9 /* MatchFormatGuideView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFB378332D672ED100EE82E9 /* MatchFormatGuideView.swift */; };
FFB378352D672ED200EE82E9 /* MatchFormatGuideView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFB378332D672ED100EE82E9 /* MatchFormatGuideView.swift */; };
FFB378362D672ED200EE82E9 /* MatchFormatGuideView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFB378332D672ED100EE82E9 /* MatchFormatGuideView.swift */; };
FFB9C8712BBADDE200A0EF4F /* Selectable.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFB9C8702BBADDE200A0EF4F /* Selectable.swift */; }; FFB9C8712BBADDE200A0EF4F /* Selectable.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFB9C8702BBADDE200A0EF4F /* Selectable.swift */; };
FFB9C8752BBADDF700A0EF4F /* SeedInterval.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFB9C8742BBADDF700A0EF4F /* SeedInterval.swift */; }; FFB9C8752BBADDF700A0EF4F /* SeedInterval.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFB9C8742BBADDF700A0EF4F /* SeedInterval.swift */; };
FFBA2D2D2CA2CE9E00D5BBDD /* CodingContainer+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C33F752C9B1EC5006316DE /* CodingContainer+Extensions.swift */; }; FFBA2D2D2CA2CE9E00D5BBDD /* CodingContainer+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C33F752C9B1EC5006316DE /* CodingContainer+Extensions.swift */; };
@ -1201,6 +1204,7 @@
FFA6D7862BB0B7A2003A31F3 /* CloudConvert.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CloudConvert.swift; sourceTree = "<group>"; }; FFA6D7862BB0B7A2003A31F3 /* CloudConvert.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CloudConvert.swift; sourceTree = "<group>"; };
FFA6D78A2BB0BEB3003A31F3 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; }; FFA6D78A2BB0BEB3003A31F3 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
FFB1C98A2C10255100B154A7 /* TournamentBroadcastRowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TournamentBroadcastRowView.swift; sourceTree = "<group>"; }; FFB1C98A2C10255100B154A7 /* TournamentBroadcastRowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TournamentBroadcastRowView.swift; sourceTree = "<group>"; };
FFB378332D672ED100EE82E9 /* MatchFormatGuideView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MatchFormatGuideView.swift; sourceTree = "<group>"; };
FFB9C8702BBADDE200A0EF4F /* Selectable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Selectable.swift; sourceTree = "<group>"; }; FFB9C8702BBADDE200A0EF4F /* Selectable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Selectable.swift; sourceTree = "<group>"; };
FFB9C8742BBADDF700A0EF4F /* SeedInterval.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SeedInterval.swift; sourceTree = "<group>"; }; FFB9C8742BBADDF700A0EF4F /* SeedInterval.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SeedInterval.swift; sourceTree = "<group>"; };
FFBE62042CE9DA0900815D33 /* MatchViewStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MatchViewStyle.swift; sourceTree = "<group>"; }; FFBE62042CE9DA0900815D33 /* MatchViewStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MatchViewStyle.swift; sourceTree = "<group>"; };
@ -2073,6 +2077,7 @@
FFF527D52BC6DDD000FF4EF2 /* MatchScheduleEditorView.swift */, FFF527D52BC6DDD000FF4EF2 /* MatchScheduleEditorView.swift */,
FFF9645A2BC2D53B00EEF017 /* GroupStageScheduleEditorView.swift */, FFF9645A2BC2D53B00EEF017 /* GroupStageScheduleEditorView.swift */,
FFF116E22BD2AF4800A33B06 /* CourtAvailabilitySettingsView.swift */, FFF116E22BD2AF4800A33B06 /* CourtAvailabilitySettingsView.swift */,
FFB378332D672ED100EE82E9 /* MatchFormatGuideView.swift */,
FF1162882BD0523B000C4809 /* Components */, FF1162882BD0523B000C4809 /* Components */,
); );
path = Planning; path = Planning;
@ -2494,6 +2499,7 @@
FFBFC3962CF05CBB000EBD8D /* DateMenuView.swift in Sources */, FFBFC3962CF05CBB000EBD8D /* DateMenuView.swift in Sources */,
FF089EBB2BB0120700F0AEC7 /* PlayerPopoverView.swift in Sources */, FF089EBB2BB0120700F0AEC7 /* PlayerPopoverView.swift in Sources */,
FF70916E2B9108C600AB08DA /* InscriptionManagerView.swift in Sources */, FF70916E2B9108C600AB08DA /* InscriptionManagerView.swift in Sources */,
FFB378352D672ED200EE82E9 /* MatchFormatGuideView.swift in Sources */,
FF77CE542CCCD1B200CBCBB4 /* MatchFormatPickingView.swift in Sources */, FF77CE542CCCD1B200CBCBB4 /* MatchFormatPickingView.swift in Sources */,
FF82CFC92B9132AF00B0CAF2 /* ActivityView.swift in Sources */, FF82CFC92B9132AF00B0CAF2 /* ActivityView.swift in Sources */,
FFDB1C732BB2CFE900F1E467 /* MySortDescriptor.swift in Sources */, FFDB1C732BB2CFE900F1E467 /* MySortDescriptor.swift in Sources */,
@ -2785,6 +2791,7 @@
FFBFC3972CF05CBB000EBD8D /* DateMenuView.swift in Sources */, FFBFC3972CF05CBB000EBD8D /* DateMenuView.swift in Sources */,
FF4CBFDA2C996C0600151637 /* PlayerPopoverView.swift in Sources */, FF4CBFDA2C996C0600151637 /* PlayerPopoverView.swift in Sources */,
FF4CBFDB2C996C0600151637 /* InscriptionManagerView.swift in Sources */, FF4CBFDB2C996C0600151637 /* InscriptionManagerView.swift in Sources */,
FFB378362D672ED200EE82E9 /* MatchFormatGuideView.swift in Sources */,
FF77CE522CCCD1B200CBCBB4 /* MatchFormatPickingView.swift in Sources */, FF77CE522CCCD1B200CBCBB4 /* MatchFormatPickingView.swift in Sources */,
FF4CBFDC2C996C0600151637 /* ActivityView.swift in Sources */, FF4CBFDC2C996C0600151637 /* ActivityView.swift in Sources */,
FF4CBFDD2C996C0600151637 /* MySortDescriptor.swift in Sources */, FF4CBFDD2C996C0600151637 /* MySortDescriptor.swift in Sources */,
@ -3055,6 +3062,7 @@
FFBFC3952CF05CBB000EBD8D /* DateMenuView.swift in Sources */, FFBFC3952CF05CBB000EBD8D /* DateMenuView.swift in Sources */,
FF70FB592C90584900129CC2 /* PlayerPopoverView.swift in Sources */, FF70FB592C90584900129CC2 /* PlayerPopoverView.swift in Sources */,
FF70FB5A2C90584900129CC2 /* InscriptionManagerView.swift in Sources */, FF70FB5A2C90584900129CC2 /* InscriptionManagerView.swift in Sources */,
FFB378342D672ED200EE82E9 /* MatchFormatGuideView.swift in Sources */,
FF77CE532CCCD1B200CBCBB4 /* MatchFormatPickingView.swift in Sources */, FF77CE532CCCD1B200CBCBB4 /* MatchFormatPickingView.swift in Sources */,
FF70FB5B2C90584900129CC2 /* ActivityView.swift in Sources */, FF70FB5B2C90584900129CC2 /* ActivityView.swift in Sources */,
FF70FB5C2C90584900129CC2 /* MySortDescriptor.swift in Sources */, FF70FB5C2C90584900129CC2 /* MySortDescriptor.swift in Sources */,
@ -3315,7 +3323,6 @@
DEFINES_MODULE = YES; DEFINES_MODULE = YES;
DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\""; DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\"";
DEVELOPMENT_TEAM = BQ3Y44M3Q6; DEVELOPMENT_TEAM = BQ3Y44M3Q6;
GCC_OPTIMIZATION_LEVEL = 0;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = PadelClub/Info.plist; INFOPLIST_FILE = PadelClub/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = "Padel Club"; INFOPLIST_KEY_CFBundleDisplayName = "Padel Club";
@ -3344,7 +3351,6 @@
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
SUPPORTS_MACCATALYST = NO; SUPPORTS_MACCATALYST = NO;
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2"; TARGETED_DEVICE_FAMILY = "1,2";
}; };
@ -3362,7 +3368,6 @@
DEFINES_MODULE = YES; DEFINES_MODULE = YES;
DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\""; DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\"";
DEVELOPMENT_TEAM = BQ3Y44M3Q6; DEVELOPMENT_TEAM = BQ3Y44M3Q6;
GCC_OPTIMIZATION_LEVEL = 0;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = PadelClub/Info.plist; INFOPLIST_FILE = PadelClub/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = "Padel Club"; INFOPLIST_KEY_CFBundleDisplayName = "Padel Club";
@ -3391,7 +3396,6 @@
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
SUPPORTS_MACCATALYST = NO; SUPPORTS_MACCATALYST = NO;
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2"; TARGETED_DEVICE_FAMILY = "1,2";
}; };

@ -632,14 +632,18 @@ final class TeamRegistration: ModelObject, Storable {
func isDifferentPosition(_ drawMatchIndex: Int?) -> Bool { func isDifferentPosition(_ drawMatchIndex: Int?) -> Bool {
if let bracketPosition, let drawMatchIndex { if let bracketPosition, let drawMatchIndex {
return drawMatchIndex != bracketPosition return drawMatchIndex != bracketPosition
} else if let bracketPosition { } else if bracketPosition != nil {
return true return true
} else if let drawMatchIndex { } else if drawMatchIndex != nil {
return true return true
} }
return false return false
} }
func shouldDisplayRankAndWeight() -> Bool {
unsortedPlayers().count > 0
}
enum CodingKeys: String, CodingKey { enum CodingKeys: String, CodingKey {
case _id = "id" case _id = "id"
case _tournament = "tournament" case _tournament = "tournament"

@ -428,6 +428,11 @@ final class Tournament : ModelObject, Storable {
return Array(self.tournamentStore.teamRegistrations) return Array(self.tournamentStore.teamRegistrations)
} }
func unsortedTeamsCount() -> Int {
return self.tournamentStore.teamRegistrations.count
}
func groupStages(atStep step: Int = 0) -> [GroupStage] { func groupStages(atStep step: Int = 0) -> [GroupStage] {
let groupStages: [GroupStage] = self.tournamentStore.groupStages.filter { $0.tournament == self.id && $0.step == step } let groupStages: [GroupStage] = self.tournamentStore.groupStages.filter { $0.tournament == self.id && $0.step == step }
return groupStages.sorted(by: \.index) return groupStages.sorted(by: \.index)
@ -581,7 +586,8 @@ defer {
} }
func pasteDataForImporting(_ exportFormat: ExportFormat = .rawText) -> String { func pasteDataForImporting(_ exportFormat: ExportFormat = .rawText) -> String {
let selectedSortedTeams = selectedSortedTeams() + waitingListSortedTeams() let _selectedSortedTeams = selectedSortedTeams()
let selectedSortedTeams = _selectedSortedTeams + waitingListSortedTeams(selectedSortedTeams: _selectedSortedTeams)
switch exportFormat { switch exportFormat {
case .rawText: case .rawText:
return (selectedSortedTeams.compactMap { $0.pasteData(exportFormat) } + ["Liste d'attente"] + waitingListTeams(in: selectedSortedTeams, includingWalkOuts: true).compactMap { $0.pasteData(exportFormat) }).joined(separator: exportFormat.newLineSeparator(2)) return (selectedSortedTeams.compactMap { $0.pasteData(exportFormat) } + ["Liste d'attente"] + waitingListTeams(in: selectedSortedTeams, includingWalkOuts: true).compactMap { $0.pasteData(exportFormat) }).joined(separator: exportFormat.newLineSeparator(2))
@ -878,13 +884,13 @@ defer {
return rounds.sorted(by: \.index).reversed() return rounds.sorted(by: \.index).reversed()
} }
func sortedTeams() -> [TeamRegistration] { func sortedTeams(selectedSortedTeams: [TeamRegistration]) -> [TeamRegistration] {
let teams = selectedSortedTeams() let teams = selectedSortedTeams
return teams + waitingListTeams(in: teams, includingWalkOuts: true) return teams + waitingListTeams(in: teams, includingWalkOuts: true)
} }
func waitingListSortedTeams() -> [TeamRegistration] { func waitingListSortedTeams(selectedSortedTeams: [TeamRegistration]) -> [TeamRegistration] {
let teams = selectedSortedTeams() let teams = selectedSortedTeams
return waitingListTeams(in: teams, includingWalkOuts: false) return waitingListTeams(in: teams, includingWalkOuts: false)
} }
@ -1183,9 +1189,8 @@ defer {
} }
} }
func registrationIssues() -> Int { func registrationIssues(selectedTeams: [TeamRegistration]) -> Int {
let players : [PlayerRegistration] = unsortedPlayers() let players : [PlayerRegistration] = unsortedPlayers()
let selectedTeams : [TeamRegistration] = selectedSortedTeams()
let callDateIssue : [TeamRegistration] = selectedTeams.filter { $0.callDate != nil && isStartDateIsDifferentThanCallDate($0) } let callDateIssue : [TeamRegistration] = selectedTeams.filter { $0.callDate != nil && isStartDateIsDifferentThanCallDate($0) }
let duplicates : [PlayerRegistration] = duplicates(in: players) let duplicates : [PlayerRegistration] = duplicates(in: players)
let problematicPlayers : [PlayerRegistration] = players.filter({ $0.sex == nil }) let problematicPlayers : [PlayerRegistration] = players.filter({ $0.sex == nil })
@ -1852,6 +1857,7 @@ defer {
team.wildCardGroupStage = true team.wildCardGroupStage = true
} }
team.setWeight(from: [], inTournamentCategory: self.tournamentCategory)
return team return team
} }
@ -1865,6 +1871,7 @@ defer {
func addEmptyTeamRegistration(_ count: Int) { func addEmptyTeamRegistration(_ count: Int) {
let teams = (0..<count).map { _ in let teams = (0..<count).map { _ in
let team = TeamRegistration(tournament: id) let team = TeamRegistration(tournament: id)
team.setWeight(from: [], inTournamentCategory: self.tournamentCategory)
return team return team
} }
@ -2820,10 +2827,7 @@ extension Tournament {
func deadline(for type: TournamentDeadlineType) -> Date? { func deadline(for type: TournamentDeadlineType) -> Date? {
guard [.p500, .p1000, .p1500, .p2000].contains(tournamentLevel) else { return nil } guard [.p500, .p1000, .p1500, .p2000].contains(tournamentLevel) else { return nil }
var daysOffset = type.daysOffset var daysOffset = type.daysOffset(level: tournamentLevel)
if tournamentLevel == .p500 {
daysOffset += 7
}
if let date = Calendar.current.date(byAdding: .day, value: daysOffset, to: startDate) { if let date = Calendar.current.date(byAdding: .day, value: daysOffset, to: startDate) {
let startOfDay = Calendar.current.startOfDay(for: date) let startOfDay = Calendar.current.startOfDay(for: date)
return Calendar.current.date(byAdding: type.timeOffset, to: startOfDay) return Calendar.current.date(byAdding: type.timeOffset, to: startOfDay)

@ -1996,16 +1996,30 @@ enum TournamentDeadlineType: String, CaseIterable {
case wildcardLicensePurchase = "Prise de licence des WC" case wildcardLicensePurchase = "Prise de licence des WC"
case definitiveBroadcastList = "Publication définitive" case definitiveBroadcastList = "Publication définitive"
var daysOffset: Int { func daysOffset(level: TournamentLevel) -> Int {
switch self { if level == .p500 {
case .inscription: switch self {
return -13 case .inscription:
case .broadcastList: return -6
return -12 case .broadcastList:
case .wildcardRequest: return -6
return -9 case .wildcardRequest:
case .wildcardLicensePurchase, .definitiveBroadcastList: return -4
return -8 case .wildcardLicensePurchase, .definitiveBroadcastList:
return -4
}
} else {
switch self {
case .inscription:
return -13
case .broadcastList:
return -12
case .wildcardRequest:
return -9
case .wildcardLicensePurchase, .definitiveBroadcastList:
return -8
}
} }
} }

@ -223,13 +223,14 @@ struct SendToAllView: View {
} }
func _teams() -> [TeamRegistration] { func _teams() -> [TeamRegistration] {
let selectedSortedTeams = tournament.selectedSortedTeams()
if onlyWaitingList { if onlyWaitingList {
return tournament.waitingListSortedTeams() return tournament.waitingListSortedTeams(selectedSortedTeams: selectedSortedTeams)
} }
if _roundTeams().isEmpty && _groupStagesTeams().isEmpty { if _roundTeams().isEmpty && _groupStagesTeams().isEmpty {
return tournament.selectedSortedTeams() + (includeWaitingList ? tournament.waitingListSortedTeams() : []) return tournament.selectedSortedTeams() + (includeWaitingList ? tournament.waitingListSortedTeams(selectedSortedTeams: selectedSortedTeams) : [])
} }
return _roundTeams() + _groupStagesTeams() + (includeWaitingList ? tournament.waitingListSortedTeams() : []) return _roundTeams() + _groupStagesTeams() + (includeWaitingList ? tournament.waitingListSortedTeams(selectedSortedTeams: selectedSortedTeams) : [])
} }
func _roundTeams() -> [TeamRegistration] { func _roundTeams() -> [TeamRegistration] {

@ -22,7 +22,7 @@ struct MatchFormatStorageView: View {
var body: some View { var body: some View {
Section { Section {
LabeledContent { LabeledContent {
StepperView(title: "minutes", count: $estimatedDuration, step: 5) StepperView(title: "minute", count: $estimatedDuration, step: 5)
} label: { } label: {
MatchFormatRowView(matchFormat: matchFormat, hideDuration: true) MatchFormatRowView(matchFormat: matchFormat, hideDuration: true)
} }

@ -0,0 +1,72 @@
//
// MatchFormatGuideView.swift
// PadelClub
//
// Created by razmig on 20/02/2025.
//
import SwiftUI
struct MatchFormatGuideView: View {
let matchCounts = Array(2...7)
let formats: [MatchFormat] = [
.twoSets, .twoSetsDecisivePoint,
.twoSetsSuperTie, .twoSetsDecisivePointSuperTie,
.twoSetsOfFourGames, .twoSetsOfFourGamesDecisivePoint,
.nineGames, .nineGamesDecisivePoint,
.superTie
]
func getFormatDescription(for matchCount: Int) -> String {
var description = ""
// Group formats by their behavior
let formatGroups = Dictionary(grouping: formats) { format in
format.maximumMatchPerDay(for: matchCount)
}
// Sort by maximum matches allowed (descending)
let sortedMaxMatches = formatGroups.keys.sorted(by: >)
for maxMatches in sortedMaxMatches {
if let formatsForMax = formatGroups[maxMatches] {
let formatStrings = formatsForMax.map { $0.format }.joined(separator: "/")
if maxMatches > 0 && maxMatches <= matchCount {
description += "Maximum \(maxMatches) matchs en format \(formatStrings)\n"
} else if maxMatches == 0 {
description += "Aucun match au format \(formatStrings)\n"
}
}
}
if matchCount >= 7 {
description += "Format \(MatchFormat.superTie.format) principalement"
}
return description.isEmpty ? "Aucun match possible" : description
}
var body: some View {
List {
Section {
ForEach(matchCounts, id: \.self) { count in
VStack {
Text("\(count) matchs par jour")
.font(.headline)
Text(getFormatDescription(for: count))
}
}
// Special case for 7+ matches
VStack {
Text("7+ matchs par jour")
.font(.headline)
Text("Tournois P 25 uniquement (soirée/demi-journée/journée)")
}
}
}
.navigationTitle("Guide des Formats de Match")
.navigationBarTitleDisplayMode(.inline)
.toolbarBackground(.visible, for: .navigationBar)
}
}

@ -23,6 +23,7 @@ struct PlanningSettingsView: View {
@State private var parallelType: Bool = false @State private var parallelType: Bool = false
@State private var deletingDateMatchesDone: Bool = false @State private var deletingDateMatchesDone: Bool = false
@State private var deletingDone: Bool = false @State private var deletingDone: Bool = false
@State private var presentFormatHelperView: Bool = false
var tournamentStore: TournamentStore { var tournamentStore: TournamentStore {
return self.tournament.tournamentStore return self.tournament.tournamentStore
@ -145,6 +146,28 @@ struct PlanningSettingsView: View {
_smartView() _smartView()
} }
.navigationTitle("Réglages")
.toolbar {
ToolbarItem(placement: .topBarTrailing) {
Button {
presentFormatHelperView = true
} label: {
Text("Aide-mémoire")
}
}
}
.sheet(isPresented: $presentFormatHelperView) {
NavigationStack {
MatchFormatGuideView()
.toolbar {
ToolbarItem(placement: .topBarLeading) {
Button("Retour", role: .cancel) {
presentFormatHelperView = false
}
}
}
}
}
.headerProminence(.increased) .headerProminence(.increased)
.onAppear { .onAppear {
do { do {

@ -12,16 +12,15 @@ struct TeamWeightView: View {
let team: TeamRegistration let team: TeamRegistration
var teamPosition: TeamPosition? = nil var teamPosition: TeamPosition? = nil
var teamIndex: Int? { var teamIndex: Int?
team.tournamentObject()?.indexOf(team: team)
}
var displayWeight: Bool { var displayWeight: Bool {
team.tournamentObject()?.hideWeight() == false team.shouldDisplayRankAndWeight() && team.tournamentObject()?.hideWeight() == false
} }
var body: some View { var body: some View {
VStack(alignment: .trailing, spacing: 0) { VStack(alignment: .trailing, spacing: 0) {
let displayWeight = self.displayWeight
if (teamPosition == .one || teamPosition == nil) && displayWeight { if (teamPosition == .one || teamPosition == nil) && displayWeight {
Text(team.weight.formatted()) Text(team.weight.formatted())
.monospacedDigit() .monospacedDigit()

@ -47,7 +47,8 @@ struct EditingTeamView: View {
} }
private func _resetTeam() { private func _resetTeam() {
self.currentWaitingList = tournament.waitingListSortedTeams().filter({ $0.hasRegisteredOnline() }).first let selectedSortedTeams = tournament.selectedSortedTeams()
self.currentWaitingList = tournament.waitingListSortedTeams(selectedSortedTeams: selectedSortedTeams).filter({ $0.hasRegisteredOnline() }).first
team.resetPositions() team.resetPositions()
team.wildCardGroupStage = false team.wildCardGroupStage = false
team.walkOut = false team.walkOut = false

@ -13,10 +13,11 @@ struct TeamRowView: View {
var teamPosition: TeamPosition? = nil var teamPosition: TeamPosition? = nil
var displayCallDate: Bool = false var displayCallDate: Bool = false
var displayRestingTime: Bool = false var displayRestingTime: Bool = false
var teamIndex: Int?
var body: some View { var body: some View {
LabeledContent { LabeledContent {
TeamWeightView(team: team, teamPosition: teamPosition) TeamWeightView(team: team, teamPosition: teamPosition, teamIndex: teamIndex)
} label: { } label: {
VStack(alignment: .leading) { VStack(alignment: .leading) {
TeamHeadlineView(team: team) TeamHeadlineView(team: team)

@ -312,7 +312,7 @@ struct FileImportView: View {
} }
} else if didImport { } else if didImport {
let _filteredTeams = filteredTeams let _filteredTeams = filteredTeams
let previousTeams = tournament.sortedTeams() let previousTeams = tournament.sortedTeams(selectedSortedTeams: tournament.selectedSortedTeams())
if previousTeams.isEmpty == false { if previousTeams.isEmpty == false {
Section { Section {

@ -34,7 +34,7 @@ struct TournamentMatchFormatsSettingsView: View {
Section { Section {
LabeledContent { LabeledContent {
StepperView(title: "minutes", count: $tournament.additionalEstimationDuration, step: 5, minimum: -10) StepperView(title: "minute", count: $tournament.additionalEstimationDuration, step: 5, minimum: -10)
} label: { } label: {
Text("Modifier les durées moyennes") Text("Modifier les durées moyennes")
} }

@ -180,7 +180,7 @@ struct InscriptionManagerView: View {
return _simpleHash(ids: ids1) != _simpleHash(ids: ids2) return _simpleHash(ids: ids1) != _simpleHash(ids: ids2)
} }
private func _setHash() { private func _setHash(currentSelectedSortedTeams: [TeamRegistration]? = nil) {
#if _DEBUG_TIME //DEBUGING TIME #if _DEBUG_TIME //DEBUGING TIME
let start = Date() let start = Date()
defer { defer {
@ -188,18 +188,17 @@ struct InscriptionManagerView: View {
print("func _setHash", duration.formatted(.units(allowed: [.seconds, .milliseconds]))) print("func _setHash", duration.formatted(.units(allowed: [.seconds, .milliseconds])))
} }
#endif #endif
let selectedSortedTeams = tournament.selectedSortedTeams() let selectedSortedTeams = currentSelectedSortedTeams == nil ? tournament.selectedSortedTeams() : currentSelectedSortedTeams!
if self.teamsHash == nil, selectedSortedTeams.isEmpty == false { if self.teamsHash == nil, selectedSortedTeams.isEmpty == false {
self.teamsHash = _simpleHash(ids: selectedSortedTeams.map { $0.id }) self.teamsHash = _simpleHash(ids: selectedSortedTeams.map { $0.id })
} }
self.registrationIssues = nil self.registrationIssues = nil
DispatchQueue.main.async { DispatchQueue.main.async {
self.registrationIssues = tournament.registrationIssues() self.registrationIssues = tournament.registrationIssues(selectedTeams: selectedSortedTeams)
} }
} }
private func _handleHashDiff() { private func _handleHashDiff(selectedSortedTeams: [TeamRegistration]) {
let selectedSortedTeams = tournament.selectedSortedTeams()
let newHash = _simpleHash(ids: selectedSortedTeams.map { $0.id }) let newHash = _simpleHash(ids: selectedSortedTeams.map { $0.id })
if (self.teamsHash != nil && newHash != teamsHash!) || (self.teamsHash == nil && selectedSortedTeams.isEmpty == false) { if (self.teamsHash != nil && newHash != teamsHash!) || (self.teamsHash == nil && selectedSortedTeams.isEmpty == false) {
self.teamsHash = newHash self.teamsHash = newHash
@ -225,9 +224,10 @@ struct InscriptionManagerView: View {
} }
var body: some View { var body: some View {
Group { let selectedSortedTeams = tournament.selectedSortedTeams()
if tournament.unsortedTeams().isEmpty == false { return Group {
_teamRegisteredView() if tournament.unsortedTeamsCount() > 0 {
_teamRegisteredView(selectedSortedTeams: selectedSortedTeams)
} else { } else {
List { List {
@ -263,10 +263,10 @@ struct InscriptionManagerView: View {
await _refreshList() await _refreshList()
} }
.onAppear { .onAppear {
_setHash() _setHash(currentSelectedSortedTeams: selectedSortedTeams)
} }
.onDisappear { .onDisappear {
_handleHashDiff() _handleHashDiff(selectedSortedTeams: selectedSortedTeams)
} }
.sheet(isPresented: $isLearningMore) { .sheet(isPresented: $isLearningMore) {
LearnMoreSheetView(tournament: tournament) LearnMoreSheetView(tournament: tournament)
@ -490,47 +490,43 @@ struct InscriptionManagerView: View {
tournament.unsortedPlayers() tournament.unsortedPlayers()
} }
var sortedTeams: [TeamRegistration] { func sortedTeams(selectedSortedTeams: [TeamRegistration]) -> [TeamRegistration] {
if filterMode == .waiting { if filterMode == .waiting {
return tournament.waitingListSortedTeams() return tournament.waitingListSortedTeams(selectedSortedTeams: selectedSortedTeams)
} else { } else {
return tournament.sortedTeams() return tournament.sortedTeams(selectedSortedTeams: selectedSortedTeams)
} }
} }
var filteredTeams: [TeamRegistration] { func filteredTeams(sortedTeams: [TeamRegistration]) -> [TeamRegistration] {
let filtered = sortedTeams.lazy.filter { team in
var teams = sortedTeams switch filterMode {
switch filterMode { case .wildcardBracket:
case .wildcardBracket: return team.wildCardBracket
teams = teams.filter({ $0.wildCardBracket }) case .wildcardGroupStage:
case .wildcardGroupStage: return team.wildCardGroupStage
teams = teams.filter({ $0.wildCardGroupStage }) case .walkOut:
case .walkOut: return team.walkOut
teams = teams.filter({ $0.walkOut }) case .bracket:
case .bracket: return team.inRound() && !team.inGroupStage()
teams = teams.filter({ $0.inRound() && $0.inGroupStage() == false }) case .groupStage:
case .groupStage: return team.inGroupStage()
teams = teams.filter({ $0.inGroupStage() }) case .notImported:
case .notImported: return !team.isImported()
teams = teams.filter({ $0.isImported() == false }) case .registeredLocally:
case .registeredLocally: return !team.hasRegisteredOnline()
teams = teams.filter({ $0.hasRegisteredOnline() == false }) case .registeredOnline:
case .registeredOnline: return team.hasRegisteredOnline()
teams = teams.filter({ $0.hasRegisteredOnline() == true }) default:
default: return true
break }
} }
if sortingMode == .registrationDate { let sorted = sortingMode == .registrationDate
teams = teams.sorted(by: \.computedRegistrationDate) ? filtered.sorted(by: { $0.computedRegistrationDate < $1.computedRegistrationDate })
} : Array(filtered)
if byDecreasingOrdering { return byDecreasingOrdering ? sorted.reversed() : sorted
return teams.reversed()
} else {
return teams
}
} }
// private func _fixModel() { // private func _fixModel() {
@ -572,12 +568,10 @@ struct InscriptionManagerView: View {
} }
} }
private func _teamRegisteredView() -> some View { private func _teamRegisteredView(selectedSortedTeams: [TeamRegistration]) -> some View {
List { List {
let selectedSortedTeams = tournament.selectedSortedTeams()
if presentSearch == false { if presentSearch == false {
_informationView() _informationView(for: selectedSortedTeams)
if tournament.isAnimation() == false { if tournament.isAnimation() == false {
_rankHandlerView() _rankHandlerView()
@ -585,7 +579,8 @@ struct InscriptionManagerView: View {
} }
} }
let teams = searchField.isEmpty ? filteredTeams : filteredTeams.filter({ $0.contains(searchField.canonicalVersion) }) let sortedTeams = sortedTeams(selectedSortedTeams: selectedSortedTeams)
let teams = searchField.isEmpty ? filteredTeams(sortedTeams: sortedTeams) : filteredTeams(sortedTeams: sortedTeams).filter({ $0.contains(searchField.canonicalVersion) })
if teams.isEmpty && searchField.isEmpty == false { if teams.isEmpty && searchField.isEmpty == false {
ContentUnavailableView { ContentUnavailableView {
@ -622,7 +617,7 @@ struct InscriptionManagerView: View {
EditingTeamView(team: team) EditingTeamView(team: team)
.environment(tournament) .environment(tournament)
} label: { } label: {
TeamRowView(team: team) TeamRowView(team: team, teamIndex: teamIndex)
} }
.swipeActions(edge: .trailing, allowsFullSwipe: true) { .swipeActions(edge: .trailing, allowsFullSwipe: true) {
if tournament.enableOnlineRegistration == false { if tournament.enableOnlineRegistration == false {
@ -735,18 +730,18 @@ struct InscriptionManagerView: View {
} }
} }
private func _teamCountForFilterMode(filterMode: FilterMode) -> String { private func _teamCountForFilterMode(filterMode: FilterMode, in teams: [TeamRegistration]) -> String {
switch filterMode { switch filterMode {
case .wildcardBracket: case .wildcardBracket:
return tournament.selectedSortedTeams().filter({ $0.wildCardBracket }).count.formatted() return teams.filter({ $0.wildCardBracket }).count.formatted()
case .wildcardGroupStage: case .wildcardGroupStage:
return tournament.selectedSortedTeams().filter({ $0.wildCardGroupStage }).count.formatted() return teams.filter({ $0.wildCardGroupStage }).count.formatted()
case .all: case .all:
return unsortedTeamsWithoutWO.count.formatted() return unsortedTeamsWithoutWO.count.formatted()
case .bracket: case .bracket:
return tournament.selectedSortedTeams().filter({ $0.inRound() && $0.inGroupStage() == false }).count.formatted() return teams.filter({ $0.inRound() && $0.inGroupStage() == false }).count.formatted()
case .groupStage: case .groupStage:
return tournament.selectedSortedTeams().filter({ $0.inGroupStage() }).count.formatted() return teams.filter({ $0.inGroupStage() }).count.formatted()
case .walkOut: case .walkOut:
let wo = walkoutTeams.count.formatted() let wo = walkoutTeams.count.formatted()
return wo return wo
@ -754,20 +749,20 @@ struct InscriptionManagerView: View {
let waiting: Int = max(0, unsortedTeamsWithoutWO.count - tournament.teamCount) let waiting: Int = max(0, unsortedTeamsWithoutWO.count - tournament.teamCount)
return waiting.formatted() return waiting.formatted()
case .notImported: case .notImported:
let notImported: Int = max(0, sortedTeams.filter({ $0.isImported() == false }).count) let notImported: Int = max(0, sortedTeams(selectedSortedTeams: teams).filter({ $0.isImported() == false }).count)
return notImported.formatted() return notImported.formatted()
case .registeredLocally: case .registeredLocally:
let registeredLocally: Int = max(0, sortedTeams.filter({ $0.hasRegisteredOnline() == false }).count) let registeredLocally: Int = max(0, sortedTeams(selectedSortedTeams: teams).filter({ $0.hasRegisteredOnline() == false }).count)
return registeredLocally.formatted() return registeredLocally.formatted()
case .registeredOnline: case .registeredOnline:
let registeredOnline: Int = max(0, sortedTeams.filter({ $0.hasRegisteredOnline() }).count) let registeredOnline: Int = max(0, sortedTeams(selectedSortedTeams: teams).filter({ $0.hasRegisteredOnline() }).count)
return registeredOnline.formatted() return registeredOnline.formatted()
} }
} }
@ViewBuilder @ViewBuilder
private func _informationView() -> some View { private func _informationView(for teams: [TeamRegistration]) -> some View {
Section { Section {
HStack { HStack {
// VStack(alignment: .leading, spacing: 0) { // VStack(alignment: .leading, spacing: 0) {
@ -781,7 +776,7 @@ struct InscriptionManagerView: View {
// } // }
// //
ForEach([FilterMode.all, FilterMode.waiting, FilterMode.walkOut]) { filterMode in ForEach([FilterMode.all, FilterMode.waiting, FilterMode.walkOut]) { filterMode in
_filterModeView(filterMode: filterMode) _filterModeView(filterMode: filterMode, in: teams)
} }
Button { Button {
@ -809,7 +804,7 @@ struct InscriptionManagerView: View {
.listRowSeparator(.hidden) .listRowSeparator(.hidden)
HStack { HStack {
ForEach([FilterMode.groupStage, FilterMode.bracket, FilterMode.wildcardGroupStage, FilterMode.wildcardBracket]) { filterMode in ForEach([FilterMode.groupStage, FilterMode.bracket, FilterMode.wildcardGroupStage, FilterMode.wildcardBracket]) { filterMode in
_filterModeView(filterMode: filterMode) _filterModeView(filterMode: filterMode, in: teams)
} }
} }
.padding(.bottom, -4) .padding(.bottom, -4)
@ -883,7 +878,7 @@ struct InscriptionManagerView: View {
} }
} }
private func _filterModeView(filterMode: FilterMode) -> some View { private func _filterModeView(filterMode: FilterMode, in teams: [TeamRegistration]) -> some View {
Button { Button {
if self.filterMode == filterMode { if self.filterMode == filterMode {
@ -894,7 +889,7 @@ struct InscriptionManagerView: View {
} label: { } label: {
VStack(alignment: .center, spacing: -2) { VStack(alignment: .center, spacing: -2) {
Text(filterMode.localizedLabel(.short)).font(.caption).padding(.horizontal, -8) Text(filterMode.localizedLabel(.short)).font(.caption).padding(.horizontal, -8)
Text(_teamCountForFilterMode(filterMode: filterMode)).font(.largeTitle) Text(_teamCountForFilterMode(filterMode: filterMode, in: teams)).font(.largeTitle)
} }
.frame(maxWidth: .infinity) .frame(maxWidth: .infinity)
.contentShape(Rectangle()) .contentShape(Rectangle())

Loading…
Cancel
Save