clean up match view

multistore
Razmig Sarkissian 2 years ago
parent 6d28930359
commit d85e22c9ff
  1. 36
      PadelClub.xcodeproj/project.pbxproj
  2. 3
      PadelClub/Data/GroupStage.swift
  3. 24
      PadelClub/Data/Match.swift
  4. 4
      PadelClub/Data/TeamRegistration.swift
  5. 23
      PadelClub/Data/Tournament.swift
  6. 5
      PadelClub/Extensions/String+Extensions.swift
  7. 4
      PadelClub/ViewModel/MatchDescriptor.swift
  8. 18
      PadelClub/Views/Components/FooterButtonView.swift
  9. 21
      PadelClub/Views/Components/MatchListView.swift
  10. 25
      PadelClub/Views/GroupStage/GroupStageView.swift
  11. 49
      PadelClub/Views/Match/Components/MatchDateView.swift
  12. 45
      PadelClub/Views/Match/Components/MatchTeamDetailView.swift
  13. 4
      PadelClub/Views/Match/Components/PlayerBlockView.swift
  14. 135
      PadelClub/Views/Match/MatchDetailView.swift
  15. 8
      PadelClub/Views/Score/EditScoreView.swift
  16. 10
      PadelClub/Views/Score/PointSelectionView.swift
  17. 2
      PadelClub/Views/Score/PointView.swift
  18. 2
      PadelClub/Views/Score/SetInputView.swift
  19. 43
      PadelClub/Views/Team/Components/TeamHeaderView.swift
  20. 38
      PadelClub/Views/Team/Components/TeamWeightView.swift
  21. 16
      PadelClub/Views/Team/TeamRowView.swift
  22. 23
      PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift
  23. 29
      PadelClub/Views/Tournament/TournamentRunningView.swift

@ -35,6 +35,10 @@
C4A47DAD2B85FCCD00ADC637 /* User.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47DAC2B85FCCD00ADC637 /* User.swift */; };
C4A47DB12B86375E00ADC637 /* MainUserView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47DB02B86375E00ADC637 /* MainUserView.swift */; };
C4A47DB32B86387500ADC637 /* AccountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47DB22B86387500ADC637 /* AccountView.swift */; };
FF025AD82BD0C10F00A86CF8 /* TeamHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF025AD72BD0C10F00A86CF8 /* TeamHeaderView.swift */; };
FF025ADB2BD0C2D000A86CF8 /* MatchTeamDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF025ADA2BD0C2D000A86CF8 /* MatchTeamDetailView.swift */; };
FF025ADD2BD0C94300A86CF8 /* FooterButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF025ADC2BD0C94300A86CF8 /* FooterButtonView.swift */; };
FF025ADF2BD0CE0A00A86CF8 /* TeamWeightView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF025ADE2BD0CE0A00A86CF8 /* TeamWeightView.swift */; };
FF089EB42BB0020000F0AEC7 /* PlayerSexPickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF089EB32BB0020000F0AEC7 /* PlayerSexPickerView.swift */; };
FF089EB62BB00A3800F0AEC7 /* TeamRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF089EB52BB00A3800F0AEC7 /* TeamRowView.swift */; };
FF089EBB2BB0120700F0AEC7 /* PlayerPopoverView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF089EBA2BB0120700F0AEC7 /* PlayerPopoverView.swift */; };
@ -313,6 +317,10 @@
C4A47DAC2B85FCCD00ADC637 /* User.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = User.swift; sourceTree = "<group>"; };
C4A47DB02B86375E00ADC637 /* MainUserView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainUserView.swift; sourceTree = "<group>"; };
C4A47DB22B86387500ADC637 /* AccountView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountView.swift; sourceTree = "<group>"; };
FF025AD72BD0C10F00A86CF8 /* TeamHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TeamHeaderView.swift; sourceTree = "<group>"; };
FF025ADA2BD0C2D000A86CF8 /* MatchTeamDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MatchTeamDetailView.swift; sourceTree = "<group>"; };
FF025ADC2BD0C94300A86CF8 /* FooterButtonView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FooterButtonView.swift; sourceTree = "<group>"; };
FF025ADE2BD0CE0A00A86CF8 /* TeamWeightView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TeamWeightView.swift; sourceTree = "<group>"; };
FF089EB32BB0020000F0AEC7 /* PlayerSexPickerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerSexPickerView.swift; sourceTree = "<group>"; };
FF089EB52BB00A3800F0AEC7 /* TeamRowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TeamRowView.swift; sourceTree = "<group>"; };
FF089EBA2BB0120700F0AEC7 /* PlayerPopoverView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerPopoverView.swift; sourceTree = "<group>"; };
@ -711,12 +719,32 @@
C4A47D9E2B7D0BCE00ADC637 /* StepperView.swift */,
FF5DA1942BB927E800A33061 /* GenericDestinationPickerView.swift */,
FF6EC8F62B94773100EA7F5A /* RowButtonView.swift */,
FF025ADC2BD0C94300A86CF8 /* FooterButtonView.swift */,
FFBF065D2BBD8040009D6715 /* MatchListView.swift */,
FF967CF72BAEDF0000A9A3BD /* Labels.swift */,
);
path = Components;
sourceTree = "<group>";
};
FF025AD62BD0C0FB00A86CF8 /* Components */ = {
isa = PBXGroup;
children = (
FF025AD72BD0C10F00A86CF8 /* TeamHeaderView.swift */,
FF025ADE2BD0CE0A00A86CF8 /* TeamWeightView.swift */,
);
path = Components;
sourceTree = "<group>";
};
FF025AD92BD0C2BD00A86CF8 /* Components */ = {
isa = PBXGroup;
children = (
FF967D0C2BAF3EB200A9A3BD /* MatchDateView.swift */,
FF967D0E2BAF63B000A9A3BD /* PlayerBlockView.swift */,
FF025ADA2BD0C2D000A86CF8 /* MatchTeamDetailView.swift */,
);
path = Components;
sourceTree = "<group>";
};
FF089EB02BB001EA00F0AEC7 /* Components */ = {
isa = PBXGroup;
children = (
@ -1012,8 +1040,7 @@
FF967D052BAF3C4200A9A3BD /* MatchSetupView.swift */,
FF967D002BAEF0B400A9A3BD /* MatchSummaryView.swift */,
FF967D022BAEF0C000A9A3BD /* MatchDetailView.swift */,
FF967D0C2BAF3EB200A9A3BD /* MatchDateView.swift */,
FF967D0E2BAF63B000A9A3BD /* PlayerBlockView.swift */,
FF025AD92BD0C2BD00A86CF8 /* Components */,
);
path = Match;
sourceTree = "<group>";
@ -1025,6 +1052,7 @@
FF967D0A2BAF3D4C00A9A3BD /* TeamPickerView.swift */,
FF089EB52BB00A3800F0AEC7 /* TeamRowView.swift */,
FF1162862BD004AD000C4809 /* EditingTeamView.swift */,
FF025AD62BD0C0FB00A86CF8 /* Components */,
);
path = Team;
sourceTree = "<group>";
@ -1348,6 +1376,7 @@
FFB9C8752BBADDF700A0EF4F /* SeedInterval.swift in Sources */,
FFBF065C2BBD2657009D6715 /* GroupStageTeamView.swift in Sources */,
FF5DA1932BB9279B00A33061 /* RoundSettingsView.swift in Sources */,
FF025ADF2BD0CE0A00A86CF8 /* TeamWeightView.swift in Sources */,
FF9268012BCE94920080F940 /* SeedsCallingView.swift in Sources */,
FF9268092BCEDC2C0080F940 /* CallView.swift in Sources */,
FF5D0D742BB41DF8005CB568 /* Color+Extensions.swift in Sources */,
@ -1404,6 +1433,7 @@
FF1CBC222BB53E590036DAAB /* FederalTournamentHolder.swift in Sources */,
C4A47D5E2B6D38EC00ADC637 /* DataStore.swift in Sources */,
FFCFC01C2BBC5AAA00B82851 /* SetDescriptor.swift in Sources */,
FF025AD82BD0C10F00A86CF8 /* TeamHeaderView.swift in Sources */,
FF82CFC52B911F5B00B0CAF2 /* OrganizedTournamentView.swift in Sources */,
FFF964572BC26B3400EEF017 /* RoundScheduleEditorView.swift in Sources */,
FF59FFB32B90EFAC0061EFF9 /* EventListView.swift in Sources */,
@ -1412,6 +1442,7 @@
FFC1E10C2BAC7FB0008D6F59 /* ClubImportView.swift in Sources */,
FF3B60A32BC49BBC008C2E66 /* MatchScheduler.swift in Sources */,
FF11627A2BCF8109000C4809 /* CallMessageCustomizationView.swift in Sources */,
FF025ADB2BD0C2D000A86CF8 /* MatchTeamDetailView.swift in Sources */,
FF5DA1952BB927E800A33061 /* GenericDestinationPickerView.swift in Sources */,
FF8F26542BAE1E4400650388 /* TableStructureView.swift in Sources */,
C45BAE442BCA753E002EEC8A /* Purchase.swift in Sources */,
@ -1499,6 +1530,7 @@
FF967CF82BAEDF0000A9A3BD /* Labels.swift in Sources */,
FF089EB42BB0020000F0AEC7 /* PlayerSexPickerView.swift in Sources */,
FF9267FF2BCE94830080F940 /* CallSettingsView.swift in Sources */,
FF025ADD2BD0C94300A86CF8 /* FooterButtonView.swift in Sources */,
FF5D0D852BB48997005CB568 /* RankCalculatorView.swift in Sources */,
FF70916A2B90F95E00AB08DA /* DateBoxView.swift in Sources */,
FF5D0D722BB3EFA5005CB568 /* LearnMoreSheetView.swift in Sources */,

@ -136,7 +136,8 @@ class GroupStage: ModelObject, Storable {
}
func availableToStart() -> [Match] {
playedMatches().filter({ $0.canBeStarted() && $0.isRunning() == false })
let runningMatches = runningMatches()
return playedMatches().filter({ $0.canBeStarted(inMatches: runningMatches) && $0.isRunning() == false })
}
func runningMatches() -> [Match] {

@ -349,24 +349,26 @@ class Match: ModelObject, Storable {
court = String(courtIndex)
}
func canBeStarted() -> Bool {
func canBeStarted(inMatches matches: [Match]) -> Bool {
let teams = teams()
guard teams.count == 2 else { return false }
guard hasEnded() == false else { return false }
guard hasStarted() == false else { return false }
return teams.allSatisfy({ $0.canPlay() && isTeamPlaying($0) == false })
return teams.allSatisfy({ $0.canPlay() && isTeamPlaying($0, inMatches: matches) == false })
}
func isTeamPlaying(_ team: TeamRegistration) -> Bool {
if isGroupStage() {
let isPlaying = groupStageObject?.runningMatches().filter({ $0.teams().contains(team) }).isEmpty == false
return isPlaying
} else {
//todo
return false
}
func isTeamPlaying(_ team: TeamRegistration, inMatches matches: [Match]) -> Bool {
matches.filter({ $0.teams().contains(team) }).isEmpty == false
}
var computedStartDateForSorting: Date {
startDate ?? .distantFuture
}
var computedEndDateForSorting: Date {
endDate ?? .distantFuture
}
func isReady() -> Bool {
teams().count == 2
}

@ -128,9 +128,9 @@ class TeamRegistration: ModelObject, Storable {
func teamLabel(_ displayStyle: DisplayStyle = .wide) -> String {
switch displayStyle {
case .wide:
unsortedPlayers().map { $0.playerLabel(displayStyle) }.joined(separator: " & ")
players().map { $0.playerLabel(displayStyle) }.joined(separator: " & ")
case .short:
unsortedPlayers().map { $0.playerLabel(.wide) }.joined(separator: "\n")
players().map { $0.playerLabel(.wide) }.joined(separator: "\n")
}
}

@ -562,6 +562,25 @@ class Tournament : ModelObject, Storable {
return false
}
func availableToStart(_ allMatches: [Match]) -> [Match] {
let runningMatches = allMatches.filter({ $0.isRunning() && $0.isReady() })
return allMatches.filter({ $0.canBeStarted(inMatches: runningMatches) && $0.isRunning() == false }).sorted(by: \.computedStartDateForSorting)
}
func runningMatches(_ allMatches: [Match]) -> [Match] {
allMatches.filter({ $0.isRunning() && $0.isReady() }).sorted(by: \.computedStartDateForSorting)
}
func readyMatches(_ allMatches: [Match]) -> [Match] {
return allMatches.filter({ $0.isReady() && $0.isRunning() == false && $0.hasEnded() == false }).sorted(by: \.computedStartDateForSorting)
}
func finishedMatches(_ allMatches: [Match], limit: Int? = nil) -> [Match] {
let _limit = limit ?? courtCount
return Array(allMatches.filter({ $0.hasEnded() }).sorted(by: \.computedEndDateForSorting).reversed().prefix(_limit))
}
func lockRegistration() {
closedRegistrationDate = Date()
let count = selectedSortedTeams().count
@ -880,6 +899,10 @@ class Tournament : ModelObject, Storable {
entryFee == nil || entryFee == 0
}
func indexOf(team: TeamRegistration) -> Int? {
selectedSortedTeams().firstIndex(where: { $0.id == team.id })
}
func addTeam(_ players: Set<PlayerRegistration>) -> TeamRegistration {
let team = TeamRegistration(tournament: id, registrationDate: Date())
team.tournamentCategory = tournamentCategory

@ -159,3 +159,8 @@ extension String {
}
}
}
extension StringProtocol {
var firstUppercased: String { prefix(1).uppercased() + dropFirst() }
var firstCapitalized: String { prefix(1).capitalized + dropFirst() }
}

@ -29,8 +29,8 @@ class MatchDescriptor: ObservableObject {
}
let teamOne = match?.team(.one)
let teamTwo = match?.team(.two)
self.teamLabelOne = teamOne?.teamLabel() ?? ""
self.teamLabelTwo = teamTwo?.teamLabel() ?? ""
self.teamLabelOne = teamOne?.teamLabel(.short) ?? ""
self.teamLabelTwo = teamTwo?.teamLabel(.short) ?? ""
if let match, let scoresTeamOne = match.teamScore(ofTeam: teamOne)?.score, let scoresTeamTwo = match.teamScore(ofTeam: teamTwo)?.score {

@ -0,0 +1,18 @@
//
// FooterButtonView.swift
// PadelClub
//
// Created by Razmig Sarkissian on 18/04/2024.
//
import SwiftUI
struct FooterButtonView: View {
var body: some View {
Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/)
}
}
#Preview {
FooterButtonView()
}

@ -19,27 +19,20 @@ struct MatchListView: View {
var body: some View {
if matches.isEmpty == false {
Section {
if isExpanded {
DisclosureGroup(isExpanded: $isExpanded) {
ForEach(matches) { match in
MatchRowView(match: match, matchViewStyle: matchViewStyle)
.listRowInsets(EdgeInsets())
}
}
} header: {
Button {
isExpanded.toggle()
} label: {
HStack {
Text(section.capitalized)
Spacer()
Text(matches.count.formatted())
Image(systemName: isExpanded ? "chevron.down.circle" : "chevron.right.circle")
LabeledContent {
Text(matches.count.formatted() + " match" + matches.count.pluralSuffix)
.foregroundStyle(.master)
} label: {
Text(section.firstCapitalized)
}
.contentShape(Rectangle())
}
.buttonStyle(.plain)
.frame(maxWidth: .infinity)
}
.headerProminence(.increased)
}
}
}

@ -34,26 +34,33 @@ struct GroupStageView: View {
Section {
_groupStageView()
} header: {
if let startDate = groupStage.startDate {
Text(startDate.formatted(Date.FormatStyle().weekday(.wide)).capitalized + " à partir de " + startDate.formatted(.dateTime.hour().minute()))
}
} footer: {
HStack {
if let startDate = groupStage.startDate {
Text(startDate.formatted(Date.FormatStyle().weekday(.wide)).capitalized + " à partir de " + startDate.formatted(.dateTime.hour().minute()))
}
Spacer()
Button {
if sortingMode == .weight {
sortingMode = .score
if sortingMode == .auto {
if groupStage.hasEnded() {
sortingMode = .weight
} else {
sortingMode = .score
}
} else if sortingMode == .weight {
sortingMode = .weight
} else {
sortingMode = .weight
}
} label: {
Label(sortByScore ? "tri par score" : "tri par poids", systemImage: "arrow.up.arrow.down").labelStyle(.titleOnly)
.underline()
}
.buttonStyle(.borderless)
}
.buttonStyle(.plain)
}
.headerProminence(.increased)
MatchListView(section: "disponible", matches: groupStage.availableToStart()).id(UUID())
MatchListView(section: "en cours", matches: groupStage.runningMatches()).id(UUID())
MatchListView(section: "à lancer", matches: groupStage.readyMatches()).id(UUID())

@ -8,13 +8,14 @@
import SwiftUI
struct MatchDateView: View {
@EnvironmentObject var dataStore: DataStore
var match: Match
var showPrefix: Bool = false
var body: some View {
Menu {
if match.startDate == nil {
Button("Commencer") {
if match.startDate == nil && match.isReady() {
Button("Démarrer") {
match.startDate = Date()
save()
}
@ -23,12 +24,20 @@ struct MatchDateView: View {
save()
}
} else {
Button("Recommencer") {
match.startDate = Date()
match.endDate = nil
save()
if match.isReady() {
Button("Démarrer maintenant") {
match.startDate = Date()
match.endDate = nil
save()
}
} else {
Button("Décaler de \(match.matchFormat.estimatedDuration) minutes") {
match.startDate = match.startDate?.addingTimeInterval(Double(match.matchFormat.estimatedDuration) * 60.0)
match.endDate = nil
save()
}
}
Button("Remise à zéro") {
Button("Retirer l'horaire") {
match.startDate = nil
match.endDate = nil
save()
@ -50,8 +59,16 @@ struct MatchDateView: View {
if showPrefix {
Text("en cours").font(.footnote).foregroundStyle(.secondary)
}
Text(startDate, style: .timer)
.monospacedDigit()
if match.isReady() {
Text(startDate, style: .timer)
.monospacedDigit()
.foregroundStyle(Color.master)
.underline()
} else {
Text("en retard")
.foregroundStyle(Color.master)
.underline()
}
} else if startDate.timeIntervalSinceNow <= 7200 && showPrefix {
if showPrefix {
Text("démarre dans")
@ -59,15 +76,21 @@ struct MatchDateView: View {
}
Text(startDate, style: .timer)
.monospacedDigit()
.foregroundStyle(Color.master)
.underline()
} else {
if showPrefix {
Text("le " + startDate.formatted(date: .abbreviated, time: .omitted))
.font(.footnote).foregroundStyle(.secondary)
Text("à " + startDate.formatted(date: .omitted, time: .shortened))
.monospacedDigit()
.foregroundStyle(Color.master)
.underline()
} else {
Text(startDate.formatted(date: .abbreviated, time: .shortened))
.monospacedDigit()
.foregroundStyle(Color.master)
.underline()
}
}
}
@ -81,11 +104,15 @@ struct MatchDateView: View {
}
Text(duration)
.monospacedDigit()
.foregroundStyle(Color.master)
.underline()
}
if match.startDate == nil && match.hasEnded() == false {
Text("démarrage").font(.footnote).foregroundStyle(.secondary)
Text("non défini")
.foregroundStyle(Color.master)
.underline()
}
}
}
@ -94,9 +121,7 @@ struct MatchDateView: View {
func save() {
do {
// match.currentTournament?.objectWillChange.send()
// match.objectWillChange.send()
// try viewContext.save()
try dataStore.matches.addOrUpdate(instance: match)
} catch {
// Replace this implementation with code to handle the error appropriately.
// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.

@ -0,0 +1,45 @@
//
// MatchTeamDetailView.swift
// PadelClub
//
// Created by Razmig Sarkissian on 18/04/2024.
//
import SwiftUI
struct MatchTeamDetailView: View {
let match: Match
var body: some View {
NavigationStack {
let tournament = match.currentTournament()
List {
if let teamOne = match.team(.one) {
_teamDetailView(teamOne, inTournament: tournament)
}
if let teamTwo = match.team(.two) {
_teamDetailView(teamTwo, inTournament: tournament)
}
}
.headerProminence(.increased)
.tint(.master)
}
.presentationDetents([.fraction(0.66)])
}
@ViewBuilder
private func _teamDetailView(_ team: TeamRegistration, inTournament tournament: Tournament?) -> some View {
Section {
ForEach(team.players()) { player in
EditablePlayerView(player: player, editingOptions: [.licenceId, .payment])
}
} header: {
TeamHeaderView(team: team, teamIndex: tournament?.indexOf(team: team), tournament: nil)
}
}
}
#Preview {
MatchTeamDetailView(match: Match.mock())
}

@ -79,7 +79,7 @@ struct PlayerBlockView: View {
Text("WO")
}
if hideScore == false {
if hideScore == false && scores.isEmpty == false {
ForEach(scores.indices, id: \.self) { index in
let string = scores[index]
if string.isEmpty == false {
@ -96,6 +96,8 @@ struct PlayerBlockView: View {
.lineLimit(1)
}
}
} else if let team {
TeamWeightView(team: team, teamPosition: teamPosition)
}
}
}

@ -48,86 +48,40 @@ struct MatchDetailView: View {
_fieldSetup = State(wrappedValue: .field(court))
}
}
// @ViewBuilder
// func entrantView(_ entrant: Entrant) -> some View {
// Section {
// ForEach(entrant.orderedPlayers) { player in
// if player.isPlaying(in: match) {
// playerView(player)
// }
// }
// } header: {
// LabeledContent {
// if let tournament = match.currentTournament, let index = tournament.indexOfEntrant(entrant) {
// Text("#\(index + 1)")
// }
// } label: {
// if let title = entrant.brand?.title {
// Text(title)
// }
// }
// } footer: {
// LabeledContent {
// let weight = entrant.orderedPlayers.filter { $0.isPlaying(in: match) }.map { $0.tournamentRank }.reduce(0, +)
// Text(weight.formatted())
// } label: {
// Text("Poids de la paire")
// }
// }
// .headerProminence(.increased)
// }
// @ViewBuilder
// func playerView(_ player: Player) -> some View {
// VStack(alignment: .leading) {
// HStack {
// Text(player.longLabel)
// Text(player.localizedAge)
// Spacer()
// Text(player.formattedRank)
// }
//
// if let computedClubName = player.computedClubName {
// Text(computedClubName).foregroundStyle(.secondary).font(.caption)
// }
// if let computedLicense = player.computedLicense {
// Text(computedLicense).foregroundStyle(.secondary).font(.caption)
// }
// }
// }
var quickLookHeader: some View {
Section {
HStack {
if match.hasEnded() == false {
Menu {
Button("Non défini") {
match.removeCourt()
Menu {
Button("Non défini") {
match.removeCourt()
save()
}
ForEach(1...match.courtCount(), id: \.self) { courtIndex in
Button("Terrain #\(courtIndex.formatted())") {
match.setCourt(courtIndex)
save()
}
ForEach(1...match.courtCount(), id: \.self) { courtIndex in
Button("Terrain #\(courtIndex.formatted())") {
match.setCourt(courtIndex)
save()
}
}
} label: {
VStack(alignment: .leading) {
Text("terrain").font(.footnote).foregroundStyle(.secondary)
if let court = match.court {
Text("#" + court)
} else {
Text("Choisir")
}
}
} label: {
VStack(alignment: .leading) {
Text("terrain").font(.footnote).foregroundStyle(.secondary)
if let court = match.court {
Text("#" + court)
.foregroundStyle(Color.master)
.underline()
} else {
Text("Choisir")
.foregroundStyle(Color.master)
.underline()
}
}
.buttonStyle(.plain)
}
Spacer()
MatchDateView(match: match, showPrefix: true)
}
.font(.title)
.buttonStyle(.plain)
} footer: {
// if match.hasWalkoutTeam() == false {
// if let weatherData = match.weatherData {
@ -151,7 +105,6 @@ struct MatchDetailView: View {
Section {
MatchSummaryView(match: match, matchViewStyle: .plainStyle)
} header: {
} footer: {
if match.isEmpty() == false {
HStack {
@ -171,34 +124,34 @@ struct MatchDetailView: View {
}
}
Section {
ForEach(match.teams()) { team in
ForEach(team.players().filter({ $0.hasPaid() == false })) { player in
HStack {
Text(player.playerLabel())
Spacer()
//PlayerPayView(player: player)
let players = match.teams().flatMap { $0.players() }
let unpaid = players.filter({ $0.hasPaid() == false })
if unpaid.isEmpty == false {
Section {
DisclosureGroup {
ForEach(unpaid) { player in
LabeledContent {
PlayerPayView(player: player)
} label: {
Text(player.playerLabel())
}
}
} label: {
LabeledContent {
Text(unpaid.count.formatted() + " / " + players.count.formatted())
} label: {
Text("Encaissement manquant")
}
}
}
}
menuView
}
// .sheet(isPresented: $showDetails) {
// NavigationStack {
// List {
// if let entrantOne = match.entrantOne() {
// entrantView(entrantOne)
// }
// if let entrantTwo = match.entrantTwo() {
// entrantView(entrantTwo)
// }
// }
// }
// .presentationDetents([.fraction(0.66)])
// }
.sheet(isPresented: $showDetails) {
MatchTeamDetailView(match: match)
}
.sheet(item: $scoreType, onDismiss: {
if match.hasEnded() {
dismiss()
@ -206,6 +159,7 @@ struct MatchDetailView: View {
}) { scoreType in
let matchDescriptor = MatchDescriptor(match: match)
EditScoreView(matchDescriptor: matchDescriptor)
.tint(.master)
// switch scoreType {
// case .edition:
@ -305,7 +259,8 @@ struct MatchDetailView: View {
// }
// }
.navigationTitle(match.matchTitle())
.navigationBarTitleDisplayMode(.large)
.navigationBarTitleDisplayMode(.inline)
.toolbarBackground(.visible, for: .navigationBar)
}
enum ScoreType: Int, Identifiable, Hashable {

@ -22,7 +22,10 @@ struct EditScoreView: View {
Form {
Section {
Text(matchDescriptor.teamLabelOne)
Text(matchDescriptor.teamLabelTwo)
HStack {
Spacer()
Text(matchDescriptor.teamLabelTwo).multilineTextAlignment(.trailing)
}
} footer: {
HStack {
Menu {
@ -37,7 +40,8 @@ struct EditScoreView: View {
Text(matchDescriptor.teamLabelTwo)
}
} label: {
Text("Forfait")
Text("Forfait d'une équipe ?")
.underline()
}
Spacer()

@ -13,8 +13,8 @@ struct PointSelectionView: View {
var possibleValues: [Int]
var disableValues: [Int] = []
var deleteAction: () -> ()
let gridItems: [GridItem] = [GridItem(.adaptive(minimum: 65), spacing: 20)]
let columns = Array(repeating: GridItem(.flexible()), count: 3)
init(valueSelected: Binding<Int?>, values: [Int], possibleValues: [Int], disableValues: [Int], deleteAction: @escaping () -> Void) {
_valueSelected = valueSelected
@ -26,12 +26,13 @@ struct PointSelectionView: View {
var body: some View {
LazyVGrid(columns: gridItems, alignment: .center, spacing: 20) {
LazyVGrid(columns: columns, alignment: .center, spacing: 8) {
ForEach(possibleValues, id: \.self) { value in
Button {
valueSelected = value
} label: {
PointView(value: "\(value).circle.fill")
.frame(maxWidth: .infinity)
}
.buttonStyle(.borderedProminent)
.controlSize(.large)
@ -41,10 +42,11 @@ struct PointSelectionView: View {
deleteAction()
} label: {
PointView(value: "delete.left.fill")
.frame(maxWidth: .infinity)
}
.buttonStyle(.borderedProminent)
.controlSize(.large)
}
.padding()
.padding(8)
}
}

@ -15,7 +15,7 @@ struct PointView: View {
.resizable()
.aspectRatio(contentMode: .fit)
.font(.largeTitle)
.frame(height: 40)
.frame(height: 36)
}
}

@ -124,12 +124,14 @@ struct SetInputView: View {
Section {
DisclosureGroup(isExpanded: $showSetInputView) {
PointSelectionView(valueSelected: currentValue, values: possibleValues(), possibleValues: setFormat.possibleValues, disableValues: disableValues, deleteAction: deleteLastValue)
.listRowInsets(EdgeInsets(top: -8, leading: -20, bottom: 0, trailing: 0))
} label: {
SetLabelView(initialValueLeft: $setDescriptor.valueTeamOne, initialValueRight: $setDescriptor.valueTeamTwo, shouldDisplaySteppers: isMainViewTieBreakView)
}
if showTieBreakView {
DisclosureGroup(isExpanded: $showTieBreakInputView) {
PointSelectionView(valueSelected: currentTiebreakValue, values: tieBreakPossibleValues(), possibleValues: SetFormat.six.possibleValues, disableValues: disableTieBreakValues, deleteAction: deleteLastTiebreakValue)
.listRowInsets(EdgeInsets(top: -8, leading: -20, bottom: 0, trailing: 0))
} label: {
SetLabelView(initialValueLeft: $setDescriptor.tieBreakValueTeamOne, initialValueRight: $setDescriptor.tieBreakValueTeamTwo, shouldDisplaySteppers: showTieBreakInputView, isTieBreak: true)
}

@ -0,0 +1,43 @@
//
// TeamHeaderView.swift
// PadelClub
//
// Created by Razmig Sarkissian on 18/04/2024.
//
import SwiftUI
struct TeamHeaderView: View {
var team: TeamRegistration
var teamIndex: Int?
var tournament: Tournament?
var body: some View {
_teamHeaderView(team, teamIndex: teamIndex)
}
private func _teamHeaderView(_ team: TeamRegistration, teamIndex: Int?) -> some View {
HStack {
if let teamIndex {
Text("#" + (teamIndex + 1).formatted())
}
if team.unsortedPlayers().isEmpty == false {
Text(team.weight.formatted())
}
if team.isWildCard() {
Text("wildcard").italic().font(.caption)
}
Spacer()
if team.walkOut {
Text("WO")
} else if let teamIndex, let tournament {
Text(tournament.cutLabel(index: teamIndex))
}
}
}
}
#Preview {
TeamHeaderView(team: TeamRegistration.mock(), teamIndex: 1, tournament: nil)
}

@ -0,0 +1,38 @@
//
// TeamWeightView.swift
// PadelClub
//
// Created by Razmig Sarkissian on 18/04/2024.
//
import SwiftUI
struct TeamWeightView: View {
var team: TeamRegistration
var teamPosition: TeamPosition? = nil
var body: some View {
VStack(alignment: .trailing, spacing: 0) {
if teamPosition == .one || teamPosition == nil {
Text(team.weight.formatted())
.monospacedDigit()
.font(.caption)
}
if let teams = team.tournamentObject()?.selectedSortedTeams(), let index = team.index(in: teams) {
Text("#" + (index + 1).formatted(.number.precision(.integerLength(2...3))))
.monospacedDigit()
.font(.title)
}
if teamPosition == .two {
Text(team.weight.formatted())
.monospacedDigit()
.font(.caption)
}
}
}
}
#Preview {
TeamWeightView(team: TeamRegistration.mock(), teamPosition: .one)
}

@ -14,21 +14,7 @@ struct TeamRowView: View {
var body: some View {
LabeledContent {
VStack(alignment: .trailing, spacing: 0) {
if teamPosition == .one || teamPosition == nil {
Text(team.weight.formatted())
.font(.caption)
}
if let teams = team.tournamentObject()?.selectedSortedTeams(), let index = team.index(in: teams) {
Text("#" + (index + 1).formatted())
.font(.title)
}
if teamPosition == .two {
Text(team.weight.formatted())
.font(.caption)
}
}
TeamWeightView(team: team, teamPosition: teamPosition)
} label: {
Text(team.teamLabel(.short))
if let callDate = team.callDate, displayCallDate {

@ -239,7 +239,7 @@ struct InscriptionManagerView: View {
Section {
TeamDetailView(team: team)
} header: {
_teamHeaderView(team, teamIndex: teamIndex)
TeamHeaderView(team: team, teamIndex: teamIndex, tournament: tournament)
} footer: {
_teamFooterView(team)
}
@ -717,27 +717,6 @@ struct InscriptionManagerView: View {
}
}
private func _teamHeaderView(_ team: TeamRegistration, teamIndex: Int?) -> some View {
HStack {
if let teamIndex {
Text("#" + (teamIndex + 1).formatted())
}
if team.unsortedPlayers().isEmpty == false {
Text(team.weight.formatted())
}
if team.isWildCard() {
Text("wildcard").italic().font(.caption)
}
Spacer()
if team.walkOut {
Text("WO")
} else if let teamIndex {
Text(tournament.cutLabel(index: teamIndex))
}
}
}
private func _teamFooterView(_ team: TeamRegistration) -> some View {
HStack {
if let formattedRegistrationDate = team.formattedInscriptionDate() {

@ -44,8 +44,8 @@ struct TournamentRunningView: View {
}
}
if tournament.groupStages().isEmpty == false {
Section {
Section {
if tournament.groupStages().isEmpty == false {
NavigationLink(value: Screen.groupStage) {
LabeledContent {
Text(tournament.groupStageStatus())
@ -55,19 +55,24 @@ struct TournamentRunningView: View {
}
}
}
}
Section {
NavigationLink(value: Screen.round) {
LabeledContent {
Text(tournament.bracketStatus())
.foregroundStyle(.master)
} label: {
Text("Tableau")
if tournament.rounds().isEmpty == false {
NavigationLink(value: Screen.round) {
LabeledContent {
Text(tournament.bracketStatus())
.foregroundStyle(.master)
} label: {
Text("Tableau")
}
}
}
}
let allMatches = tournament.allMatches()
MatchListView(section: "en cours", matches: tournament.runningMatches(allMatches)).id(UUID())
MatchListView(section: "disponible", matches: tournament.availableToStart(allMatches)).id(UUID())
MatchListView(section: "à lancer", matches: tournament.readyMatches(allMatches)).id(UUID())
MatchListView(section: "terminés", matches: tournament.finishedMatches(allMatches)).id(UUID())
}
}

Loading…
Cancel
Save