add match followup picker

paca_championship
Raz 1 year ago
parent c025559b5e
commit ac60d64acc
  1. 8
      PadelClub.xcodeproj/project.pbxproj
  2. 4
      PadelClub/Data/GroupStage.swift
  3. 23
      PadelClub/Data/Match.swift
  4. 4
      PadelClub/Data/TeamRegistration.swift
  5. 4
      PadelClub/Data/Tournament.swift
  6. 7
      PadelClub/Extensions/Date+Extensions.swift
  7. 18
      PadelClub/Utils/DisplayContext.swift
  8. 8
      PadelClub/Views/GroupStage/GroupStageView.swift
  9. 22
      PadelClub/Views/Match/Components/MatchDateView.swift
  10. 16
      PadelClub/Views/Match/Components/PlayerBlockView.swift
  11. 28
      PadelClub/Views/Match/MatchDetailView.swift
  12. 5
      PadelClub/Views/Match/MatchRowView.swift
  13. 10
      PadelClub/Views/Match/MatchSummaryView.swift
  14. 133
      PadelClub/Views/Score/FollowUpMatchView.swift

@ -84,6 +84,9 @@
FF17CA492CB915A1003C7323 /* MultiCourtPickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF17CA482CB915A1003C7323 /* MultiCourtPickerView.swift */; }; FF17CA492CB915A1003C7323 /* MultiCourtPickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF17CA482CB915A1003C7323 /* MultiCourtPickerView.swift */; };
FF17CA4A2CB915A1003C7323 /* MultiCourtPickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF17CA482CB915A1003C7323 /* MultiCourtPickerView.swift */; }; FF17CA4A2CB915A1003C7323 /* MultiCourtPickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF17CA482CB915A1003C7323 /* MultiCourtPickerView.swift */; };
FF17CA4B2CB915A1003C7323 /* MultiCourtPickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF17CA482CB915A1003C7323 /* MultiCourtPickerView.swift */; }; FF17CA4B2CB915A1003C7323 /* MultiCourtPickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF17CA482CB915A1003C7323 /* MultiCourtPickerView.swift */; };
FF17CA4D2CB9243E003C7323 /* FollowUpMatchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF17CA4C2CB9243E003C7323 /* FollowUpMatchView.swift */; };
FF17CA4E2CB9243E003C7323 /* FollowUpMatchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF17CA4C2CB9243E003C7323 /* FollowUpMatchView.swift */; };
FF17CA4F2CB9243E003C7323 /* FollowUpMatchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF17CA4C2CB9243E003C7323 /* FollowUpMatchView.swift */; };
FF1CBC1B2BB53D1F0036DAAB /* FederalTournament.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1CBC182BB53D1F0036DAAB /* FederalTournament.swift */; }; FF1CBC1B2BB53D1F0036DAAB /* FederalTournament.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1CBC182BB53D1F0036DAAB /* FederalTournament.swift */; };
FF1CBC1D2BB53DC10036DAAB /* Calendar+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1CBC1C2BB53DC10036DAAB /* Calendar+Extensions.swift */; }; FF1CBC1D2BB53DC10036DAAB /* Calendar+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1CBC1C2BB53DC10036DAAB /* Calendar+Extensions.swift */; };
FF1CBC1F2BB53E0C0036DAAB /* FederalTournamentSearchScope.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1CBC1E2BB53E0C0036DAAB /* FederalTournamentSearchScope.swift */; }; FF1CBC1F2BB53E0C0036DAAB /* FederalTournamentSearchScope.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1CBC1E2BB53E0C0036DAAB /* FederalTournamentSearchScope.swift */; };
@ -981,6 +984,7 @@
FF1162892BD05247000C4809 /* DateUpdateManagerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateUpdateManagerView.swift; sourceTree = "<group>"; }; FF1162892BD05247000C4809 /* DateUpdateManagerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateUpdateManagerView.swift; sourceTree = "<group>"; };
FF11628B2BD05267000C4809 /* LoserRoundStepScheduleEditorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoserRoundStepScheduleEditorView.swift; sourceTree = "<group>"; }; FF11628B2BD05267000C4809 /* LoserRoundStepScheduleEditorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoserRoundStepScheduleEditorView.swift; sourceTree = "<group>"; };
FF17CA482CB915A1003C7323 /* MultiCourtPickerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultiCourtPickerView.swift; sourceTree = "<group>"; }; FF17CA482CB915A1003C7323 /* MultiCourtPickerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultiCourtPickerView.swift; sourceTree = "<group>"; };
FF17CA4C2CB9243E003C7323 /* FollowUpMatchView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FollowUpMatchView.swift; sourceTree = "<group>"; };
FF1CBC182BB53D1F0036DAAB /* FederalTournament.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FederalTournament.swift; sourceTree = "<group>"; }; FF1CBC182BB53D1F0036DAAB /* FederalTournament.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FederalTournament.swift; sourceTree = "<group>"; };
FF1CBC1C2BB53DC10036DAAB /* Calendar+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Calendar+Extensions.swift"; sourceTree = "<group>"; }; FF1CBC1C2BB53DC10036DAAB /* Calendar+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Calendar+Extensions.swift"; sourceTree = "<group>"; };
FF1CBC1E2BB53E0C0036DAAB /* FederalTournamentSearchScope.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FederalTournamentSearchScope.swift; sourceTree = "<group>"; }; FF1CBC1E2BB53E0C0036DAAB /* FederalTournamentSearchScope.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FederalTournamentSearchScope.swift; sourceTree = "<group>"; };
@ -1860,6 +1864,7 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
FFCFC0012BBC39A600B82851 /* EditScoreView.swift */, FFCFC0012BBC39A600B82851 /* EditScoreView.swift */,
FF17CA4C2CB9243E003C7323 /* FollowUpMatchView.swift */,
FFCFC0152BBC5A4C00B82851 /* SetInputView.swift */, FFCFC0152BBC5A4C00B82851 /* SetInputView.swift */,
FFCFC0172BBC5A6800B82851 /* SetLabelView.swift */, FFCFC0172BBC5A6800B82851 /* SetLabelView.swift */,
FFCFC00D2BBC3D4600B82851 /* PointSelectionView.swift */, FFCFC00D2BBC3D4600B82851 /* PointSelectionView.swift */,
@ -2263,6 +2268,7 @@
FFE103082C353B7600684FC9 /* EventClubSettingsView.swift in Sources */, FFE103082C353B7600684FC9 /* EventClubSettingsView.swift in Sources */,
C4A47DB32B86387500ADC637 /* AccountView.swift in Sources */, C4A47DB32B86387500ADC637 /* AccountView.swift in Sources */,
FFCEDA4C2C2C08EA00F8C0F2 /* PlayersWithoutContactView.swift in Sources */, FFCEDA4C2C2C08EA00F8C0F2 /* PlayersWithoutContactView.swift in Sources */,
FF17CA4F2CB9243E003C7323 /* FollowUpMatchView.swift in Sources */,
FFCD16B32C3E5E590092707B /* TeamsCallingView.swift in Sources */, FFCD16B32C3E5E590092707B /* TeamsCallingView.swift in Sources */,
FF1CBC1D2BB53DC10036DAAB /* Calendar+Extensions.swift in Sources */, FF1CBC1D2BB53DC10036DAAB /* Calendar+Extensions.swift in Sources */,
FF967CF22BAECC0B00A9A3BD /* TeamScore.swift in Sources */, FF967CF22BAECC0B00A9A3BD /* TeamScore.swift in Sources */,
@ -2533,6 +2539,7 @@
FF4CBF672C996C0600151637 /* EventClubSettingsView.swift in Sources */, FF4CBF672C996C0600151637 /* EventClubSettingsView.swift in Sources */,
FF4CBF682C996C0600151637 /* AccountView.swift in Sources */, FF4CBF682C996C0600151637 /* AccountView.swift in Sources */,
FF4CBF692C996C0600151637 /* PlayersWithoutContactView.swift in Sources */, FF4CBF692C996C0600151637 /* PlayersWithoutContactView.swift in Sources */,
FF17CA4D2CB9243E003C7323 /* FollowUpMatchView.swift in Sources */,
FF4CBF6A2C996C0600151637 /* TeamsCallingView.swift in Sources */, FF4CBF6A2C996C0600151637 /* TeamsCallingView.swift in Sources */,
FF4CBF6B2C996C0600151637 /* Calendar+Extensions.swift in Sources */, FF4CBF6B2C996C0600151637 /* Calendar+Extensions.swift in Sources */,
FF4CBF6C2C996C0600151637 /* TeamScore.swift in Sources */, FF4CBF6C2C996C0600151637 /* TeamScore.swift in Sources */,
@ -2782,6 +2789,7 @@
FF70FAE62C90584900129CC2 /* EventClubSettingsView.swift in Sources */, FF70FAE62C90584900129CC2 /* EventClubSettingsView.swift in Sources */,
FF70FAE72C90584900129CC2 /* AccountView.swift in Sources */, FF70FAE72C90584900129CC2 /* AccountView.swift in Sources */,
FF70FAE82C90584900129CC2 /* PlayersWithoutContactView.swift in Sources */, FF70FAE82C90584900129CC2 /* PlayersWithoutContactView.swift in Sources */,
FF17CA4E2CB9243E003C7323 /* FollowUpMatchView.swift in Sources */,
FF70FAE92C90584900129CC2 /* TeamsCallingView.swift in Sources */, FF70FAE92C90584900129CC2 /* TeamsCallingView.swift in Sources */,
FF70FAEA2C90584900129CC2 /* Calendar+Extensions.swift in Sources */, FF70FAEA2C90584900129CC2 /* Calendar+Extensions.swift in Sources */,
FF70FAEB2C90584900129CC2 /* TeamScore.swift in Sources */, FF70FAEB2C90584900129CC2 /* TeamScore.swift in Sources */,

@ -238,7 +238,7 @@ final class GroupStage: ModelObject, Storable {
return _matches().first(where: { matchIndexes.contains($0.index) }) return _matches().first(where: { matchIndexes.contains($0.index) })
} }
func availableToStart(playedMatches: [Match], in runningMatches: [Match]) -> [Match] { func availableToStart(playedMatches: [Match], in runningMatches: [Match], checkCanPlay: Bool = true) -> [Match] {
#if _DEBUG_TIME //DEBUGING TIME #if _DEBUG_TIME //DEBUGING TIME
let start = Date() let start = Date()
defer { defer {
@ -246,7 +246,7 @@ final class GroupStage: ModelObject, Storable {
print("func group stage availableToStart", id, duration.formatted(.units(allowed: [.seconds, .milliseconds]))) print("func group stage availableToStart", id, duration.formatted(.units(allowed: [.seconds, .milliseconds])))
} }
#endif #endif
return playedMatches.filter({ $0.canBeStarted(inMatches: runningMatches) && $0.isRunning() == false }) return playedMatches.filter({ $0.isRunning() == false && $0.canBeStarted(inMatches: runningMatches, checkCanPlay: checkCanPlay) })
} }
func runningMatches(playedMatches: [Match]) -> [Match] { func runningMatches(playedMatches: [Match]) -> [Match] {

@ -9,7 +9,11 @@ import Foundation
import LeStorage import LeStorage
@Observable @Observable
final class Match: ModelObject, Storable { final class Match: ModelObject, Storable, Equatable {
static func == (lhs: Match, rhs: Match) -> Bool {
lhs.id == rhs.id && lhs.startDate == rhs.startDate
}
static func resourceName() -> String { "matches" } static func resourceName() -> String { "matches" }
static func tokenExemptedMethods() -> [HTTPMethod] { return [] } static func tokenExemptedMethods() -> [HTTPMethod] { return [] }
static func filterByStoreIdentifier() -> Bool { return true } static func filterByStoreIdentifier() -> Bool { return true }
@ -684,12 +688,15 @@ defer {
self.courtIndex = courtIndex self.courtIndex = courtIndex
} }
func canBeStarted(inMatches matches: [Match]) -> Bool { func canBeStarted(inMatches matches: [Match], checkCanPlay: Bool) -> Bool {
let teams = teamScores let teams = teamScores
guard teams.count == 2 else { return false } guard teams.count == 2 else {
guard hasEnded() == false else { return false } print("teams.count != 2")
guard hasStarted() == false else { return false } return false
return teams.compactMap({ $0.team }).allSatisfy({ $0.canPlay() && isTeamPlaying($0, inMatches: matches) == false }) }
return teams.compactMap({ $0.team }).allSatisfy({
((checkCanPlay && $0.canPlay()) || checkCanPlay == false) && isTeamPlaying($0, inMatches: matches) == false
})
} }
func isTeamPlaying(_ team: TeamRegistration, inMatches matches: [Match]) -> Bool { func isTeamPlaying(_ team: TeamRegistration, inMatches matches: [Match]) -> Bool {
@ -891,6 +898,10 @@ defer {
} }
} }
var restingTimeForSorting: TimeInterval {
(teams().compactMap({ $0.restingTime() }).max() ?? .distantFuture).timeIntervalSinceNow
}
enum CodingKeys: String, CodingKey { enum CodingKeys: String, CodingKey {
case _id = "id" case _id = "id"
case _round = "round" case _round = "round"

@ -532,6 +532,10 @@ final class TeamRegistration: ModelObject, Storable {
} }
} }
func restingTime() -> Date? {
matches().sorted(by: \.computedEndDateForSorting).last?.endDate
}
enum CodingKeys: String, CodingKey { enum CodingKeys: String, CodingKey {
case _id = "id" case _id = "id"
case _tournament = "tournament" case _tournament = "tournament"

@ -1109,7 +1109,7 @@ defer {
// return Store.main.filter(isIncluded: { $0.groupStage != nil && groupStageIds.contains($0.groupStage!) }) // return Store.main.filter(isIncluded: { $0.groupStage != nil && groupStageIds.contains($0.groupStage!) })
} }
func availableToStart(_ allMatches: [Match], in runningMatches: [Match]) -> [Match] { func availableToStart(_ allMatches: [Match], in runningMatches: [Match], checkCanPlay: Bool = true) -> [Match] {
#if _DEBUG_TIME //DEBUGING TIME #if _DEBUG_TIME //DEBUGING TIME
let start = Date() let start = Date()
defer { defer {
@ -1117,7 +1117,7 @@ defer {
print("func tournament availableToStart", id, duration.formatted(.units(allowed: [.seconds, .milliseconds]))) print("func tournament availableToStart", id, duration.formatted(.units(allowed: [.seconds, .milliseconds])))
} }
#endif #endif
return allMatches.filter({ $0.canBeStarted(inMatches: runningMatches) && $0.isRunning() == false }).sorted(by: \.computedStartDateForSorting) return allMatches.filter({ $0.isRunning() == false && $0.canBeStarted(inMatches: runningMatches, checkCanPlay: checkCanPlay) }).sorted(by: \.computedStartDateForSorting)
} }
func runningMatches(_ allMatches: [Match]) -> [Match] { func runningMatches(_ allMatches: [Match]) -> [Match] {

@ -231,4 +231,11 @@ extension Date {
func localizedWeekDay() -> String { func localizedWeekDay() -> String {
self.formatted(.dateTime.weekday(.wide)) self.formatted(.dateTime.weekday(.wide))
} }
static var hourMinuteFormatter: DateComponentsFormatter = {
let formatter = DateComponentsFormatter()
formatter.allowedUnits = [.hour, .minute] // Customize units
formatter.unitsStyle = .abbreviated // You can choose .abbreviated or .short
return formatter
}()
} }

@ -27,6 +27,24 @@ enum MatchViewStyle {
case feedStyle // vue programmation case feedStyle // vue programmation
case plainStyle // vue detail case plainStyle // vue detail
case tournamentResultStyle //vue resultat tournoi case tournamentResultStyle //vue resultat tournoi
case followUpStyle // vue normal
func displayRestingTime() -> Bool {
switch self {
case .standardStyle:
return false
case .sectionedStandardStyle:
return false
case .feedStyle:
return false
case .plainStyle:
return false
case .tournamentResultStyle:
return false
case .followUpStyle:
return true
}
}
} }
struct DeviceHelper { struct DeviceHelper {

@ -28,6 +28,8 @@ struct GroupStageView: View {
var body: some View { var body: some View {
List { List {
let playedMatches = groupStage.playedMatches()
Section { Section {
GroupStageScoreView(groupStage: groupStage, sortByScore: sortingMode == .score) GroupStageScoreView(groupStage: groupStage, sortByScore: sortingMode == .score)
} header: { } header: {
@ -49,8 +51,12 @@ struct GroupStageView: View {
} }
} }
.headerProminence(.increased) .headerProminence(.increased)
.onChange(of: playedMatches) {
if groupStage.hasEnded() {
sortingMode = .score
}
}
let playedMatches = groupStage.playedMatches()
let runningMatches = groupStage.runningMatches(playedMatches: playedMatches) let runningMatches = groupStage.runningMatches(playedMatches: playedMatches)
MatchListView(section: "en cours", matches: groupStage.runningMatches(playedMatches: playedMatches), hideWhenEmpty: true) MatchListView(section: "en cours", matches: groupStage.runningMatches(playedMatches: playedMatches), hideWhenEmpty: true)
let availableToStart = groupStage.availableToStart(playedMatches: playedMatches, in: runningMatches) let availableToStart = groupStage.availableToStart(playedMatches: playedMatches, in: runningMatches)

@ -17,13 +17,15 @@ struct MatchDateView: View {
private var isReady: Bool private var isReady: Bool
private var hasWalkoutTeam: Bool private var hasWalkoutTeam: Bool
private var hasEnded: Bool private var hasEnded: Bool
private let updatedField: Int?
init(match: Match, showPrefix: Bool) { init(match: Match, showPrefix: Bool, updatedField: Int? = nil) {
self.match = match self.match = match
self.showPrefix = showPrefix self.showPrefix = showPrefix
self.isReady = match.isReady() self.isReady = match.isReady()
self.hasWalkoutTeam = match.hasWalkoutTeam() self.hasWalkoutTeam = match.hasWalkoutTeam()
self.hasEnded = match.hasEnded() self.hasEnded = match.hasEnded()
self.updatedField = updatedField
} }
var body: some View { var body: some View {
@ -34,21 +36,33 @@ struct MatchDateView: View {
let estimatedDuration = match.getDuration() let estimatedDuration = match.getDuration()
if match.startDate == nil && isReady { if match.startDate == nil && isReady {
Button("Démarrer") { Button("Démarrer") {
if let updatedField {
match.setCourt(updatedField)
}
match.startDate = Date() match.startDate = Date()
match.confirmed = true match.confirmed = true
_save() _save()
} }
Button("Démarrer dans 5 minutes") { Button("Démarrer dans 5 minutes") {
if let updatedField {
match.setCourt(updatedField)
}
match.startDate = Calendar.current.date(byAdding: .minute, value: 5, to: Date()) match.startDate = Calendar.current.date(byAdding: .minute, value: 5, to: Date())
match.confirmed = true match.confirmed = true
_save() _save()
} }
Button("Démarrer dans 15 minutes") { Button("Démarrer dans 15 minutes") {
if let updatedField {
match.setCourt(updatedField)
}
match.startDate = Calendar.current.date(byAdding: .minute, value: 15, to: Date()) match.startDate = Calendar.current.date(byAdding: .minute, value: 15, to: Date())
match.confirmed = true match.confirmed = true
_save() _save()
} }
Button("Démarrer dans \(estimatedDuration.formatted()) minutes") { Button("Démarrer dans \(estimatedDuration.formatted()) minutes") {
if let updatedField {
match.setCourt(updatedField)
}
match.startDate = Calendar.current.date(byAdding: .minute, value: estimatedDuration, to: Date()) match.startDate = Calendar.current.date(byAdding: .minute, value: estimatedDuration, to: Date())
match.confirmed = true match.confirmed = true
_save() _save()
@ -56,6 +70,9 @@ struct MatchDateView: View {
} else { } else {
if isReady { if isReady {
Button("Démarrer maintenant") { Button("Démarrer maintenant") {
if let updatedField {
match.setCourt(updatedField)
}
match.startDate = Date() match.startDate = Date()
match.endDate = nil match.endDate = nil
match.confirmed = true match.confirmed = true
@ -63,6 +80,9 @@ struct MatchDateView: View {
} }
} else { } else {
Button("Décaler de \(estimatedDuration) minutes") { Button("Décaler de \(estimatedDuration) minutes") {
if let updatedField {
match.setCourt(updatedField)
}
match.cleanScheduleAndSave(match.startDate?.addingTimeInterval(Double(estimatedDuration) * 60.0)) match.cleanScheduleAndSave(match.startDate?.addingTimeInterval(Double(estimatedDuration) * 60.0))
} }
} }

@ -15,8 +15,9 @@ struct PlayerBlockView: View {
let width: CGFloat let width: CGFloat
let teamScore: TeamScore? let teamScore: TeamScore?
let isWalkOut: Bool let isWalkOut: Bool
let displayRestingTime: Bool
init(match: Match, teamPosition: TeamPosition, color: Color, width: CGFloat) { init(match: Match, teamPosition: TeamPosition, color: Color, width: CGFloat, displayRestingTime: Bool) {
self.match = match self.match = match
self.teamPosition = teamPosition self.teamPosition = teamPosition
let theTeam = match.team(teamPosition) let theTeam = match.team(teamPosition)
@ -26,6 +27,7 @@ struct PlayerBlockView: View {
let theTeamScore = match.teamScore(ofTeam: theTeam) let theTeamScore = match.teamScore(ofTeam: theTeam)
self.teamScore = theTeamScore self.teamScore = theTeamScore
self.isWalkOut = theTeamScore?.isWalkOut() == true self.isWalkOut = theTeamScore?.isWalkOut() == true
self.displayRestingTime = displayRestingTime
} }
var names: [String]? { var names: [String]? {
@ -77,6 +79,18 @@ struct PlayerBlockView: View {
Text(_defaultLabel()).foregroundStyle(.secondary).lineLimit(1) Text(_defaultLabel()).foregroundStyle(.secondary).lineLimit(1)
} }
} }
if displayRestingTime, let restingTime = team?.restingTime()?.timeIntervalSinceNow, let value = Date.hourMinuteFormatter.string(from: restingTime * -1) {
if restingTime > -300 {
Text("vient de finir")
.font(.footnote)
.foregroundStyle(.secondary)
} else {
Text("en repos depuis " + value)
.font(.footnote)
.foregroundStyle(.secondary)
}
}
} }
.bold(hasWon) .bold(hasWon)
Spacer() Spacer()

@ -33,6 +33,8 @@ struct MatchDetailView: View {
@State var showSubscriptionView: Bool = false @State var showSubscriptionView: Bool = false
@State var showUserCreationView: Bool = false @State var showUserCreationView: Bool = false
@State private var presentFollowUpMatch: Bool = false
@State private var dismissWhenPresentFollowUpMatchIsDismissed: Bool = false
var tournamentStore: TournamentStore { var tournamentStore: TournamentStore {
return match.tournamentStore return match.tournamentStore
@ -50,7 +52,7 @@ struct MatchDetailView: View {
var match: Match var match: Match
init(match: Match, matchViewStyle: MatchViewStyle = .standardStyle) { init(match: Match, matchViewStyle: MatchViewStyle = .standardStyle, updatedField: Int? = nil) {
self.match = match self.match = match
self.matchViewStyle = matchViewStyle self.matchViewStyle = matchViewStyle
@ -69,7 +71,7 @@ struct MatchDetailView: View {
_endDate = State(wrappedValue: endDate) _endDate = State(wrappedValue: endDate)
} }
if let courtIndex = match.courtIndex { if let courtIndex = updatedField ?? match.courtIndex {
_fieldSetup = State(wrappedValue: .field(courtIndex)) _fieldSetup = State(wrappedValue: .field(courtIndex))
} }
} }
@ -153,9 +155,17 @@ struct MatchDetailView: View {
} }
} }
}) })
.sheet(isPresented: $presentFollowUpMatch, onDismiss: {
if dismissWhenPresentFollowUpMatchIsDismissed {
dismiss()
}
}) {
FollowUpMatchView(match: match, dismissWhenPresentFollowUpMatchIsDismissed: $dismissWhenPresentFollowUpMatchIsDismissed)
.tint(.master)
}
.sheet(item: $scoreType, onDismiss: { .sheet(item: $scoreType, onDismiss: {
if match.hasEnded() { if match.hasEnded() {
dismiss() presentFollowUpMatch = true
} }
}) { scoreType in }) { scoreType in
let matchDescriptor = MatchDescriptor(match: match) let matchDescriptor = MatchDescriptor(match: match)
@ -402,13 +412,11 @@ struct MatchDetailView: View {
Text("Partage sur les réseaux sociaux") Text("Partage sur les réseaux sociaux")
} }
// if let followUpMatch = match.followUpMatch { Section {
// Section { RowButtonView("Match à suivre") {
// MatchRowView(match: followUpMatch) presentFollowUpMatch = true
// } header: { }
// Text("à suivre terrain \(match.fieldIndex)") }
// }
// }
} }
var editionView: some View { var editionView: some View {

@ -13,6 +13,7 @@ struct MatchRowView: View {
@State var match: Match @State var match: Match
let matchViewStyle: MatchViewStyle let matchViewStyle: MatchViewStyle
var title: String? = nil var title: String? = nil
var updatedField: Int? = nil
@Environment(\.isEditingTournamentSeed) private var isEditingTournamentSeed @Environment(\.isEditingTournamentSeed) private var isEditingTournamentSeed
@ -58,9 +59,9 @@ struct MatchRowView: View {
// }) // })
NavigationLink { NavigationLink {
MatchDetailView(match: match, matchViewStyle: matchViewStyle) MatchDetailView(match: match, matchViewStyle: matchViewStyle, updatedField: updatedField)
} label: { } label: {
MatchSummaryView(match: match, matchViewStyle: matchViewStyle, title: title) MatchSummaryView(match: match, matchViewStyle: matchViewStyle, title: title, updatedField: updatedField)
.contextMenu { .contextMenu {
NavigationLink { NavigationLink {
EditSharingView(match: match) EditSharingView(match: match)

@ -18,14 +18,16 @@ struct MatchSummaryView: View {
let padding: CGFloat let padding: CGFloat
let color: Color let color: Color
let width: CGFloat let width: CGFloat
let updatedField: Int?
init(match: Match, matchViewStyle: MatchViewStyle, title: String? = nil) { init(match: Match, matchViewStyle: MatchViewStyle, title: String? = nil, updatedField: Int? = nil) {
self.match = match self.match = match
self.matchViewStyle = matchViewStyle self.matchViewStyle = matchViewStyle
self.padding = matchViewStyle == .plainStyle ? 0 : 8 self.padding = matchViewStyle == .plainStyle ? 0 : 8
self.spacing = matchViewStyle == .plainStyle ? 8 : 0 self.spacing = matchViewStyle == .plainStyle ? 8 : 0
self.width = matchViewStyle == .plainStyle ? 1 : 2 self.width = matchViewStyle == .plainStyle ? 1 : 2
self.color = Color(white: 0.9) self.color = Color(white: 0.9)
self.updatedField = updatedField
if let groupStage = match.groupStageObject { if let groupStage = match.groupStageObject {
self.roundTitle = groupStage.groupStageTitle(.title) self.roundTitle = groupStage.groupStageTitle(.title)
@ -69,14 +71,14 @@ struct MatchSummaryView: View {
HStack(spacing: 0) { HStack(spacing: 0) {
VStack(alignment: .leading, spacing: spacing) { VStack(alignment: .leading, spacing: spacing) {
PlayerBlockView(match: match, teamPosition: .one, color: color, width: width) PlayerBlockView(match: match, teamPosition: .one, color: color, width: width, displayRestingTime: matchViewStyle.displayRestingTime())
.padding(padding) .padding(padding)
if width == 1 { if width == 1 {
Divider() Divider()
} else { } else {
Divider().frame(height: width).overlay(color) Divider().frame(height: width).overlay(color)
} }
PlayerBlockView(match: match, teamPosition: .two, color: color, width: width) PlayerBlockView(match: match, teamPosition: .two, color: color, width: width, displayRestingTime: matchViewStyle.displayRestingTime())
.padding(padding) .padding(padding)
} }
} }
@ -90,7 +92,7 @@ struct MatchSummaryView: View {
if matchViewStyle != .plainStyle { if matchViewStyle != .plainStyle {
HStack { HStack {
Spacer() Spacer()
MatchDateView(match: match, showPrefix: matchViewStyle == .tournamentResultStyle) MatchDateView(match: match, showPrefix: matchViewStyle == .tournamentResultStyle, updatedField: updatedField)
} }
} }
} }

@ -0,0 +1,133 @@
//
// FollowUpMatchView.swift
// PadelClub
//
// Created by razmig on 11/10/2024.
//
import SwiftUI
struct FollowUpMatchView: View {
@EnvironmentObject var dataStore: DataStore
@Environment(\.dismiss) private var dismiss
let match: Match
let readyMatches: [Match]
let isFree: Bool
@State private var sortingMode: SortingMode = .index
@State private var selectedCourt: Int?
@State private var checkCanPlay: Bool = false
@Binding var dismissWhenPresentFollowUpMatchIsDismissed: Bool
enum SortingMode: Int, Identifiable, CaseIterable {
var id: Int { self.rawValue }
case index
case restingTime
case court
func localizedSortingModeLabel() -> String {
switch self {
case .index:
return "Ordre"
case .court:
return "Terrain"
case .restingTime:
return "Temps de repos"
}
}
}
init(match: Match, dismissWhenPresentFollowUpMatchIsDismissed: Binding<Bool>) {
_dismissWhenPresentFollowUpMatchIsDismissed = dismissWhenPresentFollowUpMatchIsDismissed
self.match = match
_selectedCourt = .init(wrappedValue: match.courtIndex)
let currentTournament = match.currentTournament()
let allMatches = currentTournament?.allMatches() ?? []
let runningMatches = currentTournament?.runningMatches(allMatches) ?? []
let readyMatches = currentTournament?.readyMatches(allMatches) ?? []
self.readyMatches = currentTournament?.availableToStart(readyMatches, in: runningMatches, checkCanPlay: false) ?? []
self.isFree = currentTournament?.isFree() ?? true
}
var sortedMatches: [Match] {
switch sortingMode {
case .index:
return readyMatches
case .restingTime:
return readyMatches.sorted(by: \.restingTimeForSorting)
case .court:
return readyMatches.sorted(using: [.keyPath(\.courtIndexForSorting), .keyPath(\.restingTimeForSorting)], order: .ascending)
}
}
var body: some View {
NavigationStack {
List {
Section {
Picker(selection: $selectedCourt) {
Text("Aucun").tag(nil as Int?)
if let tournament = match.currentTournament() {
ForEach(0..<tournament.courtCount, id: \.self) { courtIndex in
Text(tournament.courtName(atIndex: courtIndex)).tag(courtIndex as Int?)
}
} else {
ForEach(0..<20, id: \.self) { courtIndex in
Text(Court.courtIndexedTitle(atIndex: courtIndex)).tag(courtIndex as Int?)
}
}
} label: {
Text("Sur le terrain")
}
//
// Toggle(isOn: $checkCanPlay) {
// if isFree {
// Text("Vérifier le paiement ou la présence")
// } else {
// Text("Vérifier la présence")
// }
// }
// } footer: {
// if isFree {
// Text("Masque les matchs où un ou plusieurs joueurs qui ne sont pas encore arrivé")
// } else {
// Text("Masque les matchs où un ou plusieurs joueurs n'ont pas encore réglé ou qui ne sont pas encore arrivé")
// }
}
Section {
ForEach(sortedMatches) { match in
MatchRowView(match: match, matchViewStyle: .followUpStyle, updatedField: selectedCourt)
}
} header: {
Picker(selection: $sortingMode) {
ForEach(SortingMode.allCases) { sortingMode in
Text(sortingMode.localizedSortingModeLabel()).tag(sortingMode)
}
} label: {
Text("Méthode de tri")
}
.labelsHidden()
.pickerStyle(.segmented)
.fixedSize()
}
.headerProminence(.increased)
.textCase(nil)
}
.toolbarBackground(.visible, for: .navigationBar)
.navigationTitle("Match à suivre")
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .topBarLeading) {
Button("Retour", role: .cancel) {
dismiss()
}
}
}
}
.onChange(of: readyMatches) {
dismissWhenPresentFollowUpMatchIsDismissed = true
dismiss()
}
}
}
Loading…
Cancel
Save