diff --git a/PadelClub.xcodeproj/project.pbxproj b/PadelClub.xcodeproj/project.pbxproj index ea4773d..df590b0 100644 --- a/PadelClub.xcodeproj/project.pbxproj +++ b/PadelClub.xcodeproj/project.pbxproj @@ -118,6 +118,7 @@ FF1DC5572BAB3AED00FD8220 /* ClubsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1DC5562BAB3AED00FD8220 /* ClubsView.swift */; }; FF1DC5592BAB767000FD8220 /* Tips.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1DC5582BAB767000FD8220 /* Tips.swift */; }; FF1DC55B2BAB80C400FD8220 /* DisplayContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1DC55A2BAB80C400FD8220 /* DisplayContext.swift */; }; + FF1DF49B2BD8D23900822FA0 /* BarButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1DF49A2BD8D23900822FA0 /* BarButtonView.swift */; }; FF2BE4872B85E27400592328 /* LeStorage.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C425D4542B6D24E2002A7B48 /* LeStorage.framework */; }; FF2BE4882B85E27400592328 /* LeStorage.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = C425D4542B6D24E2002A7B48 /* LeStorage.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; FF3795622B9396D0004EA093 /* PadelClubApp.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = FF3795602B9396D0004EA093 /* PadelClubApp.xcdatamodeld */; }; @@ -220,6 +221,8 @@ 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 */; }; + FFC91B012BD85C2F00B29808 /* Court.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFC91B002BD85C2F00B29808 /* Court.swift */; }; + FFC91B032BD85E2400B29808 /* CourtView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFC91B022BD85E2400B29808 /* CourtView.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 */; }; @@ -412,6 +415,7 @@ FF1DC5562BAB3AED00FD8220 /* ClubsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClubsView.swift; sourceTree = ""; }; FF1DC5582BAB767000FD8220 /* Tips.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tips.swift; sourceTree = ""; }; FF1DC55A2BAB80C400FD8220 /* DisplayContext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisplayContext.swift; sourceTree = ""; }; + FF1DF49A2BD8D23900822FA0 /* BarButtonView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BarButtonView.swift; sourceTree = ""; }; FF3795612B9396D0004EA093 /* Model.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = Model.xcdatamodel; sourceTree = ""; }; FF3795652B9399AA004EA093 /* Persistence.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Persistence.swift; sourceTree = ""; }; FF3B60A22BC49BBC008C2E66 /* MatchScheduler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MatchScheduler.swift; sourceTree = ""; }; @@ -513,6 +517,8 @@ 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 = ""; }; + FFC91B002BD85C2F00B29808 /* Court.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Court.swift; sourceTree = ""; }; + FFC91B022BD85E2400B29808 /* CourtView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CourtView.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 = ""; }; @@ -671,6 +677,7 @@ FF025AE82BD1307E00A86CF8 /* MonthData.swift */, FF1DC5522BAB354A00FD8220 /* MockData.swift */, FFDB1C6C2BB2A02000F1E467 /* AppSettings.swift */, + FFC91B002BD85C2F00B29808 /* Court.swift */, FF6EC9012B94799200EA7F5A /* Coredata */, FF6EC9022B9479B900EA7F5A /* Federal */, ); @@ -748,6 +755,7 @@ FFBF065D2BBD8040009D6715 /* MatchListView.swift */, FF967CF72BAEDF0000A9A3BD /* Labels.swift */, FFC91AF82BD6A09100B29808 /* FortuneWheelView.swift */, + FF1DF49A2BD8D23900822FA0 /* BarButtonView.swift */, ); path = Components; sourceTree = ""; @@ -869,6 +877,7 @@ FF1DC5542BAB36DD00FD8220 /* CreateClubView.swift */, FFC1E10B2BAC7FB0008D6F59 /* ClubImportView.swift */, FF5D0D882BB4935C005CB568 /* ClubRowView.swift */, + FFC91B022BD85E2400B29808 /* CourtView.swift */, ); path = Club; sourceTree = ""; @@ -1443,6 +1452,7 @@ FFC1E10A2BAC2A77008D6F59 /* NetworkFederalService.swift in Sources */, FF025AEF2BD1AE9400A86CF8 /* DurationSettingsView.swift in Sources */, FF025AED2BD1513700A86CF8 /* AppScreen.swift in Sources */, + FFC91B032BD85E2400B29808 /* CourtView.swift in Sources */, FFCFC00E2BBC3D4600B82851 /* PointSelectionView.swift in Sources */, FF089EB62BB00A3800F0AEC7 /* TeamRowView.swift in Sources */, FF92680B2BCEE3E10080F940 /* ContactManager.swift in Sources */, @@ -1467,6 +1477,7 @@ C4A47D5A2B6D383C00ADC637 /* Tournament.swift in Sources */, C4A47D7B2B73C0F900ADC637 /* TournamentV2.swift in Sources */, FF3795662B9399AA004EA093 /* Persistence.swift in Sources */, + FF1DF49B2BD8D23900822FA0 /* BarButtonView.swift in Sources */, FFF964502BC25E3700EEF017 /* PlanningView.swift in Sources */, FF967CEC2BAECB9900A9A3BD /* Match.swift in Sources */, FF8F264B2BAE0B4100650388 /* TournamentLevelPickerView.swift in Sources */, @@ -1573,6 +1584,7 @@ FFCFC0182BBC5A6800B82851 /* SetLabelView.swift in Sources */, FFF9645B2BC2D53B00EEF017 /* GroupStageScheduleEditorView.swift in Sources */, C4A47DA62B83948E00ADC637 /* LoginView.swift in Sources */, + FFC91B012BD85C2F00B29808 /* Court.swift in Sources */, FF967CF82BAEDF0000A9A3BD /* Labels.swift in Sources */, FF089EB42BB0020000F0AEC7 /* PlayerSexPickerView.swift in Sources */, FF9267FF2BCE94830080F940 /* CallSettingsView.swift in Sources */, diff --git a/PadelClub/Data/Club.swift b/PadelClub/Data/Club.swift index aca7233..f0f7e4f 100644 --- a/PadelClub/Data/Club.swift +++ b/PadelClub/Data/Club.swift @@ -32,8 +32,7 @@ class Club : ModelObject, Storable, Hashable { var zipCode: String? var latitude: Double? var longitude: Double? - var courtCount: Int? - var courtNames: [String]? = nil + //var courtCount: Int? internal init(name: String, acronym: String? = nil, phone: String? = nil, code: String? = nil, address: String? = nil, city: String? = nil, zipCode: String? = nil, latitude: Double? = nil, longitude: Double? = nil) { self.name = name @@ -47,7 +46,21 @@ class Club : ModelObject, Storable, Hashable { self.longitude = longitude } + func clubTitle(_ displayStyle: DisplayStyle = .wide) -> String { + switch displayStyle { + case .wide: + return name + case .short: + return acronym + } + } + + var courts: [Court] { + Store.main.filter { $0.club == self.id }.sorted(by: \.index) + } + override func deleteDependencies() throws { + try Store.main.deleteDependencies(items: self.courts) } enum CodingKeys: String, CodingKey { @@ -61,8 +74,6 @@ class Club : ModelObject, Storable, Hashable { case _zipCode = "zipCode" case _latitude = "latitude" case _longitude = "longitude" - case _courtCount = "courtCount" - case _courtNames = "courtNames" } } diff --git a/PadelClub/Data/Court.swift b/PadelClub/Data/Court.swift new file mode 100644 index 0000000..268f2c2 --- /dev/null +++ b/PadelClub/Data/Court.swift @@ -0,0 +1,64 @@ +// +// Court.swift +// PadelClub +// +// Created by Razmig Sarkissian on 23/04/2024. +// + +import Foundation +import SwiftUI +import LeStorage + +@Observable +class Court : ModelObject, Storable, Hashable { + static func resourceName() -> String { return "courts" } + + static func == (lhs: Court, rhs: Court) -> Bool { + lhs.id == rhs.id + } + + func hash(into hasher: inout Hasher) { + return hasher.combine(id) + } + + var id: String = Store.randomId() + var index: Int + var club: String + var name: String? + var exitAllowed: Bool = false + var indoor: Bool = false + + internal init(club: String, name: String? = nil, index: Int) { + self.club = club + self.name = name + self.index = index + } + + func courtTitle() -> String { + self.name ?? courtIndexTitle() + } + + func courtIndexTitle() -> String { + Self.courtIndexedTitle(atIndex: index) + } + + static func courtIndexedTitle(atIndex index: Int) -> String { + ("Terrain #" + (index + 1).formatted()) + } + + func clubObject() -> Club? { + Store.main.findById(club) + } + + override func deleteDependencies() throws { + } + + enum CodingKeys: String, CodingKey { + case _id = "id" + case _index = "index" + case _club = "club" + case _name = "name" + case _exitAllowed = "exitAllowed" + case _indoor = "indoor" + } +} diff --git a/PadelClub/Data/DataStore.swift b/PadelClub/Data/DataStore.swift index 4d2bf0b..dde41af 100644 --- a/PadelClub/Data/DataStore.swift +++ b/PadelClub/Data/DataStore.swift @@ -17,6 +17,7 @@ class DataStore: ObservableObject { fileprivate(set) var tournaments: StoredCollection fileprivate(set) var clubs: StoredCollection + fileprivate(set) var courts: StoredCollection fileprivate(set) var events: StoredCollection fileprivate(set) var groupStages: StoredCollection fileprivate(set) var matches: StoredCollection @@ -67,6 +68,7 @@ class DataStore: ObservableObject { let indexed : Bool = true self.clubs = store.registerCollection(synchronized: false, indexed: indexed) + self.courts = store.registerCollection(synchronized: false, indexed: indexed) self.tournaments = store.registerCollection(synchronized: false, indexed: indexed) self.events = store.registerCollection(synchronized: false, indexed: indexed) self.groupStages = store.registerCollection(synchronized: false, indexed: indexed) diff --git a/PadelClub/Data/Event.swift b/PadelClub/Data/Event.swift index c3d7a23..e656e9d 100644 --- a/PadelClub/Data/Event.swift +++ b/PadelClub/Data/Event.swift @@ -17,21 +17,21 @@ class Event: ModelObject, Storable { var club: String? var creationDate: Date = Date() var name: String? - var courtCount: Int? + //var courtCount: Int? var tenupId: String? - var groupStageFormat: Int? - var roundFormat: Int? - var loserRoundFormat: Int? +// var groupStageFormat: Int? +// var roundFormat: Int? +// var loserRoundFormat: Int? //var timeslots ? - internal init(club: String? = nil, name: String? = nil, courtCount: Int? = nil, tenupId: String? = nil, groupStageFormat: Int? = nil, roundFormat: Int? = nil, loserRoundFormat: Int? = nil) { + internal init(club: String? = nil, name: String? = nil, tenupId: String? = nil) { self.club = club self.name = name - self.courtCount = courtCount +// self.courtCount = courtCount self.tenupId = tenupId - self.groupStageFormat = groupStageFormat - self.roundFormat = roundFormat - self.loserRoundFormat = loserRoundFormat +// self.groupStageFormat = groupStageFormat +// self.roundFormat = roundFormat +// self.loserRoundFormat = loserRoundFormat } var clubObject: Club? { @@ -58,10 +58,10 @@ extension Event { case _club = "club" case _creationDate = "creationDate" case _name = "name" - case _courtCount = "courtCount" + //case _courtCount = "courtCount" case _tenupId = "tenupId" - case _groupStageFormat = "groupStageFormat" - case _roundFormat = "roundFormat" - case _loserRoundFormat = "loserRoundFormat" +// case _groupStageFormat = "groupStageFormat" +// case _roundFormat = "roundFormat" +// case _loserRoundFormat = "loserRoundFormat" } } diff --git a/PadelClub/Data/Match.swift b/PadelClub/Data/Match.swift index 4650a0e..dce13d2 100644 --- a/PadelClub/Data/Match.swift +++ b/PadelClub/Data/Match.swift @@ -20,30 +20,30 @@ class Match: ModelObject, Storable { var endDate: Date? var index: Int var format: Int? - var court: String? + //var court: String? var servingTeamId: String? var winningTeamId: String? var losingTeamId: String? - var broadcasted: Bool - var name: String? - var order: Int + //var broadcasted: Bool + //var name: String? + //var order: Int var disabled: Bool = false - var courtIndex: Int? + private(set) var courtIndex: Int? - internal init(round: String? = nil, groupStage: String? = nil, startDate: Date? = nil, endDate: Date? = nil, index: Int, matchFormat: MatchFormat? = nil, court: String? = nil, servingTeamId: String? = nil, winningTeamId: String? = nil, losingTeamId: String? = nil, broadcasted: Bool = false, name: String? = nil, order: Int = 0) { + internal init(round: String? = nil, groupStage: String? = nil, startDate: Date? = nil, endDate: Date? = nil, index: Int, matchFormat: MatchFormat? = nil, servingTeamId: String? = nil, winningTeamId: String? = nil, losingTeamId: String? = nil) { self.round = round self.groupStage = groupStage self.startDate = startDate self.endDate = endDate self.index = index self.format = matchFormat?.rawValue - self.court = court + //self.court = court self.servingTeamId = servingTeamId self.winningTeamId = winningTeamId self.losingTeamId = losingTeamId - self.broadcasted = broadcasted - self.name = name - self.order = order +// self.broadcasted = broadcasted +// self.name = name +// self.order = order } func indexInRound() -> Int { @@ -111,7 +111,7 @@ class Match: ModelObject, Storable { losingTeamId = nil winningTeamId = nil endDate = nil - court = nil + removeCourt() servingTeamId = nil } @@ -373,10 +373,11 @@ class Match: ModelObject, Storable { switch fieldSetup { case .random: - let courtName = availableCourts().randomElement() - court = courtName - case .field(let courtName): - court = courtName + if let _courtIndex = availableCourts().randomElement() { + setCourt(_courtIndex) + } + case .field(let _courtIndex): + setCourt(_courtIndex) } } else { @@ -385,10 +386,13 @@ class Match: ModelObject, Storable { } } - func getCourtIndex() -> Int? { - guard let court else { return nil } - if let courtIndex = Int(court) { return courtIndex - 1 } - return nil + func courtName() -> String? { + guard let courtIndex else { return nil } + if let courtName = currentTournament()?.courtName(atIndex: courtIndex) { + return courtName + } else { + return Court.courtIndexedTitle(atIndex: courtIndex) + } } func courtCount() -> Int { @@ -397,7 +401,7 @@ class Match: ModelObject, Storable { func courtIsAvailable(_ courtIndex: Int) -> Bool { let courtUsed = currentTournament()?.courtUsed() ?? [] - return courtUsed.contains(String(courtIndex)) == false + return courtUsed.contains(courtIndex) == false // return Set(availableCourts().map { String($0) }).subtracting(Set(courtUsed)) } @@ -405,18 +409,18 @@ class Match: ModelObject, Storable { false } - func availableCourts() -> [String] { + func availableCourts() -> [Int] { let courtUsed = currentTournament()?.courtUsed() ?? [] - let availableCourts = Array(1...courtCount()) - return Array(Set(availableCourts.map { String($0) }).subtracting(Set(courtUsed))) + let availableCourts = Array(0.. Bool { @@ -605,14 +609,14 @@ class Match: ModelObject, Storable { case _endDate = "endDate" case _index = "index" case _format = "format" - case _court = "court" +// case _court = "court" case _courtIndex = "courtIndex" case _servingTeamId = "servingTeamId" case _winningTeamId = "winningTeamId" case _losingTeamId = "losingTeamId" - case _broadcasted = "broadcasted" - case _name = "name" - case _order = "order" +// case _broadcasted = "broadcasted" +// case _name = "name" +// case _order = "order" case _disabled = "disabled" } } @@ -629,7 +633,7 @@ enum MatchDateSetup: Hashable, Identifiable { enum MatchFieldSetup: Hashable, Identifiable { case random // case firstAvailable - case field(String) + case field(Int) var id: Int { hashValue } } diff --git a/PadelClub/Data/MockData.swift b/PadelClub/Data/MockData.swift index 7cad2cb..110949d 100644 --- a/PadelClub/Data/MockData.swift +++ b/PadelClub/Data/MockData.swift @@ -7,6 +7,12 @@ import Foundation +extension Court { + static func mock() -> Court { + Court(club: "", name: "Test", index: 0) + } +} + extension Club { static func mock() -> Club { Club(name: "AUC", acronym: "AUC") @@ -53,7 +59,7 @@ extension Tournament { extension Match { static func mock() -> Match { - Match(index: 0, broadcasted: false, order: 0) + Match(index: 0) } } diff --git a/PadelClub/Data/Round.swift b/PadelClub/Data/Round.swift index bd96ed2..e88639f 100644 --- a/PadelClub/Data/Round.swift +++ b/PadelClub/Data/Round.swift @@ -273,8 +273,8 @@ class Round: ModelObject, Storable { var cumulativeMatchCount: Int { var totalMatches = playedMatches().count - if let parent = parentRound { - totalMatches += parent.cumulativeMatchCount + if let parentRound { + totalMatches += parentRound.cumulativeMatchCount } return totalMatches } @@ -294,8 +294,8 @@ class Round: ModelObject, Storable { var theoryCumulativeMatchCount: Int { var totalMatches = RoundRule.numberOfMatches(forRoundIndex: index) - if let parent = parentRound { - totalMatches += parent.theoryCumulativeMatchCount + if let parentRound { + totalMatches += parentRound.theoryCumulativeMatchCount } return totalMatches } @@ -329,7 +329,7 @@ class Round: ModelObject, Storable { if let previousRound = previousRound() { return previousRound.seedInterval()?.chunks()?.first - } else if let parentRound = parentRound { + } else if let parentRound { return parentRound.seedInterval()?.chunks()?.last } @@ -398,8 +398,8 @@ class Round: ModelObject, Storable { } var parentRound: Round? { - guard let parentRound = loser else { return nil } - return Store.main.findById(parentRound) + guard let parent = loser else { return nil } + return Store.main.findById(parent) } func updateMatchFormat(_ matchFormat: MatchFormat) { diff --git a/PadelClub/Data/Tournament.swift b/PadelClub/Data/Tournament.swift index 27d72dd..5d236d7 100644 --- a/PadelClub/Data/Tournament.swift +++ b/PadelClub/Data/Tournament.swift @@ -100,15 +100,9 @@ class Tournament : ModelObject, Storable { case build } - func getCourtIndex(_ court: String?) -> Int? { - guard let court else { return nil } - if let courtIndex = Int(court) { return courtIndex - 1 } - return nil - } - - func courtUsed() -> [String] { + func courtUsed() -> [Int] { let runningMatches : [Match] = Store.main.filter(isIncluded: { $0.isRunning() }).filter({ $0.tournamentId() == self.id }) - return Set(runningMatches.compactMap { $0.court }).sorted() + return Set(runningMatches.compactMap { $0.courtIndex }).sorted() } func hasStarted() -> Bool { @@ -1082,6 +1076,15 @@ class Tournament : ModelObject, Storable { var femaleUnrankedValue: Int? { currentMonthData()?.femaleUnrankedValue } + + func courtName(atIndex courtIndex: Int) -> String { + let courts = club()?.courts + if let courts, let court = courts.first(where: { $0.index == courtIndex }) { + return court.courtTitle() + } else { + return Court.courtIndexedTitle(atIndex: courtIndex) + } + } } diff --git a/PadelClub/ViewModel/MatchScheduler.swift b/PadelClub/ViewModel/MatchScheduler.swift index 90ecb2d..d40cd76 100644 --- a/PadelClub/ViewModel/MatchScheduler.swift +++ b/PadelClub/ViewModel/MatchScheduler.swift @@ -260,13 +260,13 @@ class MatchScheduler { ) } - func getAvailableCourts(from matches: [Match]) -> [(String, Date)] { - let validMatches = matches.filter({ $0.court != nil && $0.startDate != nil }) - let byCourt = Dictionary(grouping: validMatches, by: { $0.court! }) + func getAvailableCourts(from matches: [Match]) -> [(Int, Date)] { + let validMatches = matches.filter({ $0.courtIndex != nil && $0.startDate != nil }) + let byCourt = Dictionary(grouping: validMatches, by: { $0.courtIndex! }) return (byCourt.keys.flatMap { court in let matchesByCourt = byCourt[court]?.sorted(by: \.startDate!) let lastMatch = matchesByCourt?.last - var results = [(String, Date)]() + var results = [(Int, Date)]() if let courtFreeDate = lastMatch?.estimatedEndDate(additionalEstimationDuration) { results.append((court, courtFreeDate)) } @@ -290,7 +290,7 @@ class MatchScheduler { rotationIndex += 1 } - let timeMatch = TimeMatch(matchID: match.id, rotationIndex: rotationIndex, courtIndex: match.getCourtIndex() ?? 0, startDate: match.startDate!, durationLeft: match.matchFormat.getEstimatedDuration(additionalEstimationDuration), minimumBreakTime: match.matchFormat.breakTime.breakTime) + let timeMatch = TimeMatch(matchID: match.id, rotationIndex: rotationIndex, courtIndex: match.courtIndex ?? 0, startDate: match.startDate!, durationLeft: match.matchFormat.getEstimatedDuration(additionalEstimationDuration), minimumBreakTime: match.matchFormat.breakTime.breakTime) slots.append(timeMatch) } @@ -463,27 +463,6 @@ class MatchScheduler { let upperRounds = tournament.rounds() let allMatches = tournament.allMatches() - -// if dayOne < tournament.courtCount { -// let startOfDay = startDate.startOfDay -// let endOfday = Calendar.current.dateInterval(of: .day, for: startOfDay)!.end -// let dateInterval = DateInterval(startDate: startOfDay, endDate: endOfday) -// let _courtsUnavailability = courtsUnavailability ?? [:]() -// let data = _courtsUnavailability[courtCount - 1] ?? [DateInterval]() -// data.append(dateInterval) -// courtsUnavailability = _courtsUnavailability -// } -// -// if dayTwo < tournament.courtCount { -// let startOfDay = startDate.startOfDay -// let endOfday = Calendar.current.dateInterval(of: .day, for: startOfDay)!.end -// let dateInterval = DateInterval(startDate: startOfDay, endDate: endOfday) -// let _courtsUnavailability = courtsUnavailability ?? [:]() -// let data = _courtsUnavailability[courtCount - 1] ?? [DateInterval]() -// data.append(dateInterval) -// courtsUnavailability = _courtsUnavailability -// } - let rounds = upperRounds.map { $0 @@ -520,7 +499,7 @@ class MatchScheduler { let usedCourts = getAvailableCourts(from: allMatches.filter({ $0.startDate?.isEarlierThan(startDate) == true && $0.startDate?.dayInt == startDate.dayInt })) let initialCourts = usedCourts.filter { (court, availableDate) in availableDate <= startDate - }.sorted(by: \.1).compactMap { tournament.getCourtIndex($0.0) } + }.sorted(by: \.1).compactMap { $0.0 } let courts : [Int]? = initialCourts.isEmpty ? nil : initialCourts diff --git a/PadelClub/Views/Calling/CallView.swift b/PadelClub/Views/Calling/CallView.swift index 8a3cb22..440adfc 100644 --- a/PadelClub/Views/Calling/CallView.swift +++ b/PadelClub/Views/Calling/CallView.swift @@ -118,62 +118,57 @@ struct CallView: View { Text(message) } .sheet(item: $contactType) { contactType in - switch contactType { - case .message(_, let recipients, let body, _): - - if Guard.main.paymentForNewTournament() != nil { - MessageComposeView(recipients: recipients, body: body) { result in - switch result { - case .cancelled: - _called(true) - break - case .failed: - self.sentError = .messageFailed - case .sent: - if networkMonitor.connected == false { - self.sentError = .messageNotSent - } else { + Group { + switch contactType { + case .message(_, let recipients, let body, _): + if Guard.main.paymentForNewTournament() != nil { + MessageComposeView(recipients: recipients, body: body) { result in + switch result { + case .cancelled: _called(true) + break + case .failed: + self.sentError = .messageFailed + case .sent: + if networkMonitor.connected == false { + self.sentError = .messageNotSent + } else { + _called(true) + } + @unknown default: + break } - @unknown default: - break } + } else { + SubscriptionView(showLackOfPlanMessage: true) } - } else { - SubscriptionView(showLackOfPlanMessage: true) - } -<<<<<<< HEAD - -======= - .tint(.master) ->>>>>>> 6547a8de016666a19407c6cf8b30e91087f201e2 - case .mail(_, let recipients, let bccRecipients, let body, let subject, _): - if Guard.main.paymentForNewTournament() != nil { - - MailComposeView(recipients: recipients, bccRecipients: bccRecipients, body: body, subject: subject) { result in - switch result { - case .cancelled, .saved: - self.contactType = nil - _called(true) - case .failed: - self.contactType = nil - self.sentError = .mailFailed - case .sent: - if networkMonitor.connected == false { + case .mail(_, let recipients, let bccRecipients, let body, let subject, _): + if Guard.main.paymentForNewTournament() != nil { + MailComposeView(recipients: recipients, bccRecipients: bccRecipients, body: body, subject: subject) { result in + switch result { + case .cancelled, .saved: self.contactType = nil - self.sentError = .mailNotSent - } else { _called(true) + case .failed: + self.contactType = nil + self.sentError = .mailFailed + case .sent: + if networkMonitor.connected == false { + self.contactType = nil + self.sentError = .mailNotSent + } else { + _called(true) + } + @unknown default: + break } - @unknown default: - break } + } else { + SubscriptionView(showLackOfPlanMessage: true) } - } else { - SubscriptionView(showLackOfPlanMessage: true) } - .tint(.master) } + .tint(.master) } } } diff --git a/PadelClub/Views/Club/CourtView.swift b/PadelClub/Views/Club/CourtView.swift new file mode 100644 index 0000000..bbcfecf --- /dev/null +++ b/PadelClub/Views/Club/CourtView.swift @@ -0,0 +1,57 @@ +// +// CourtView.swift +// PadelClub +// +// Created by Razmig Sarkissian on 23/04/2024. +// + +import SwiftUI + +struct CourtView: View { + @EnvironmentObject var dataStore: DataStore + @Bindable var court: Court + @State private var name: String = "" + + init(court: Court) { + self.court = court + _name = State(wrappedValue: court.name ?? "") + } + + var body: some View { + Form { + Section { + LabeledContent { + TextField("Nom", text: $name) + .keyboardType(.alphabet) + .multilineTextAlignment(.trailing) + .frame(maxWidth: .infinity) + } label: { + Text("Nom du terrain") + } + } + + Section { + Toggle(isOn: $court.exitAllowed) { + Text("Sortie authorisée") + } + Toggle(isOn: $court.indoor) { + Text("Terrain intérieur") + } + } + } + .onChange(of: name) { + court.name = name + try? dataStore.courts.addOrUpdate(instance: court) + } + .onChange(of: [court.indoor, court.exitAllowed]) { + try? dataStore.courts.addOrUpdate(instance: court) + } + .navigationTitle(court.courtTitle()) + .navigationBarTitleDisplayMode(.inline) + .toolbarBackground(.visible, for: .navigationBar) + } +} + +#Preview { + CourtView(court: Court.mock()) +} diff --git a/PadelClub/Views/Components/BarButtonView.swift b/PadelClub/Views/Components/BarButtonView.swift new file mode 100644 index 0000000..e70d164 --- /dev/null +++ b/PadelClub/Views/Components/BarButtonView.swift @@ -0,0 +1,36 @@ +// +// BarButtonView.swift +// PadelClub +// +// Created by Razmig Sarkissian on 24/04/2024. +// + +import SwiftUI + +struct BarButtonView: View { + let accessibilityLabel: String + let icon: String + let action: () -> () + + init(_ accessibilityLabel: String, icon: String, action: @escaping () -> Void) { + self.accessibilityLabel = accessibilityLabel + self.icon = icon + self.action = action + } + + var body: some View { + Button(action: { + action() + }) { + Label { + Text(accessibilityLabel) + } icon: { + Image(systemName: icon) + .resizable() + .scaledToFit() + .frame(minHeight: 28) + } + .labelStyle(.iconOnly) + } + } +} diff --git a/PadelClub/Views/Components/FooterButtonView.swift b/PadelClub/Views/Components/FooterButtonView.swift index 2d7f99b..3a44a7b 100644 --- a/PadelClub/Views/Components/FooterButtonView.swift +++ b/PadelClub/Views/Components/FooterButtonView.swift @@ -8,11 +8,21 @@ import SwiftUI struct FooterButtonView: View { + let title: String + let action: () -> () + + init(_ title: String, action: @escaping () -> Void) { + self.title = title + self.action = action + } + var body: some View { - Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/) + Button { + action() + } label: { + Text(title) + .underline() + } + .buttonStyle(.borderless) } } - -#Preview { - FooterButtonView() -} diff --git a/PadelClub/Views/Event/EventCreationView.swift b/PadelClub/Views/Event/EventCreationView.swift index 5502e9b..11fa915 100644 --- a/PadelClub/Views/Event/EventCreationView.swift +++ b/PadelClub/Views/Event/EventCreationView.swift @@ -42,7 +42,7 @@ struct EventCreationView: View { if eventType == .approvedTournament { LabeledContent { - StepperView(count: $duration, minimum: 1, maximum: 3) + StepperView(count: $duration, minimum: 1) } label: { Text("Durée") Text("\(duration) jour" + duration.pluralSuffix) @@ -99,6 +99,7 @@ struct EventCreationView: View { } tournaments.forEach { tournament in + tournament.courtCount = selectedClub?.courts.count ?? 2 tournament.startDate = startingDate tournament.dayDuration = duration tournament.setupFederalSettings() diff --git a/PadelClub/Views/Match/MatchDetailView.swift b/PadelClub/Views/Match/MatchDetailView.swift index 4590fac..4285752 100644 --- a/PadelClub/Views/Match/MatchDetailView.swift +++ b/PadelClub/Views/Match/MatchDetailView.swift @@ -29,7 +29,7 @@ struct MatchDetailView: View { self.match = match self.matchViewStyle = matchViewStyle - if match.hasStarted() == false && (match.startDate == nil || match.court == nil) { + if match.hasStarted() == false && (match.startDate == nil || match.courtIndex == nil) { _isEditing = State(wrappedValue: true) } @@ -44,8 +44,8 @@ struct MatchDetailView: View { _endDate = State(wrappedValue: endDate) } - if let court = match.court { - _fieldSetup = State(wrappedValue: .field(court)) + if let courtIndex = match.courtIndex { + _fieldSetup = State(wrappedValue: .field(courtIndex)) } } @@ -57,17 +57,19 @@ struct MatchDetailView: View { match.removeCourt() save() } - ForEach(1...match.courtCount(), id: \.self) { courtIndex in - Button("Terrain #\(courtIndex.formatted())") { - match.setCourt(courtIndex) - save() + if let tournament = match.currentTournament() { + ForEach(0.. 1 { -// ForEach(0.. some View { + if let court = tournamentClub.courts.first(where: { $0.index == index }) { + LabeledContent { + if let name = court.name { + Text(name) + } + } label: { + Text(court.courtIndexTitle()) + HStack { + if court.indoor { + Text("Couvert") + } + if court.exitAllowed { + Text("Sortie autorisée") + } + } + } + } else { + LabeledContent { + FooterButtonView("personnaliser") { + let court = Court(club: tournamentClub.id, index: index) + try? dataStore.courts.addOrUpdate(instance: court) + selectedCourt = court + } + } label: { + Text(_courtName(atIndex: index)) + } + } } + + private func _courtName(atIndex index: Int) -> String { + Court.courtIndexedTitle(atIndex: index) + } + } #Preview { diff --git a/PadelClub/Views/Tournament/Screen/Components/TournamentDurationManagerView.swift b/PadelClub/Views/Tournament/Screen/Components/TournamentDurationManagerView.swift index dd4887d..93bbe59 100644 --- a/PadelClub/Views/Tournament/Screen/Components/TournamentDurationManagerView.swift +++ b/PadelClub/Views/Tournament/Screen/Components/TournamentDurationManagerView.swift @@ -13,7 +13,7 @@ struct TournamentDurationManagerView: View { var body: some View { @Bindable var tournament = tournament LabeledContent { - StepperView(count: $tournament.dayDuration, minimum: 1, maximum: 3) + StepperView(count: $tournament.dayDuration, minimum: 1) } label: { Text("Durée") Text("\(tournament.dayDuration) jour" + tournament.dayDuration.pluralSuffix) diff --git a/PadelClub/Views/Tournament/Screen/Components/TournamentFieldsManagerView.swift b/PadelClub/Views/Tournament/Screen/Components/TournamentFieldsManagerView.swift index 5ada9bf..4faa4e1 100644 --- a/PadelClub/Views/Tournament/Screen/Components/TournamentFieldsManagerView.swift +++ b/PadelClub/Views/Tournament/Screen/Components/TournamentFieldsManagerView.swift @@ -10,7 +10,7 @@ import SwiftUI struct TournamentFieldsManagerView: View { let localizedStringKey: String @Binding var count: Int - let max: Int + var max: Int? = nil var body: some View { LabeledContent { diff --git a/PadelClub/Views/Tournament/TournamentView.swift b/PadelClub/Views/Tournament/TournamentView.swift index 05ba607..eee6759 100644 --- a/PadelClub/Views/Tournament/TournamentView.swift +++ b/PadelClub/Views/Tournament/TournamentView.swift @@ -22,66 +22,9 @@ struct TournamentView: View { } var body: some 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 { - - - // 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 { @@ -101,13 +44,17 @@ struct TournamentView: View { } 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) } }