Laurent 2 years ago
commit 4253ba2c31
  1. 12
      PadelClub/Data/Match.swift
  2. 13
      PadelClub/Data/MatchScheduler.swift
  3. 7
      PadelClub/Data/Tournament.swift
  4. 7
      PadelClub/Utils/Tips.swift
  5. 46
      PadelClub/Views/Calling/CallMessageCustomizationView.swift
  6. 19
      PadelClub/Views/Club/CourtView.swift
  7. 17
      PadelClub/Views/Components/FortuneWheelView.swift
  8. 2
      PadelClub/Views/Components/MatchListView.swift
  9. 2
      PadelClub/Views/GroupStage/Shared/GroupStageTeamReplacementView.swift
  10. 8
      PadelClub/Views/Match/MatchSummaryView.swift
  11. 41
      PadelClub/Views/Planning/CourtAvailabilitySettingsView.swift
  12. 4
      PadelClub/Views/Planning/PlanningSettingsView.swift
  13. 4
      PadelClub/Views/Player/Components/PlayerSexPickerView.swift
  14. 54
      PadelClub/Views/Round/RoundView.swift
  15. 2
      PadelClub/Views/Score/EditScoreView.swift
  16. 2
      PadelClub/Views/Team/TeamRowView.swift
  17. 9
      PadelClub/Views/Tournament/FileImportView.swift
  18. 12
      PadelClub/Views/Tournament/Screen/BroadcastView.swift
  19. 6
      PadelClub/Views/Tournament/Screen/Components/TournamentClubSettingsView.swift
  20. 2
      PadelClub/Views/Tournament/Screen/Components/TournamentMatchFormatsSettingsView.swift
  21. 8
      PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift
  22. 10
      PadelClub/Views/Tournament/TournamentInitView.swift
  23. 11
      PadelClub/Views/Tournament/TournamentView.swift

@ -27,12 +27,12 @@ class Match: ModelObject, Storable {
var winningTeamId: String? var winningTeamId: String?
var losingTeamId: String? var losingTeamId: String?
//var broadcasted: Bool //var broadcasted: Bool
//var name: String? private var name: String?
//var order: Int //var order: Int
var disabled: Bool = false var disabled: Bool = false
private(set) 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, servingTeamId: String? = nil, winningTeamId: String? = nil, losingTeamId: String? = nil, disabled: Bool = false, courtIndex: Int? = nil) { 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, name: String? = nil, disabled: Bool = false, courtIndex: Int? = nil) {
self.round = round self.round = round
self.groupStage = groupStage self.groupStage = groupStage
self.startDate = startDate self.startDate = startDate
@ -44,11 +44,9 @@ class Match: ModelObject, Storable {
self.winningTeamId = winningTeamId self.winningTeamId = winningTeamId
self.losingTeamId = losingTeamId self.losingTeamId = losingTeamId
self.disabled = disabled self.disabled = disabled
self.name = name
self.courtIndex = courtIndex self.courtIndex = courtIndex
// self.broadcasted = broadcasted // self.broadcasted = broadcasted
// self.name = name
// self.order = order // self.order = order
} }
@ -271,7 +269,7 @@ class Match: ModelObject, Storable {
} }
func roundTitle() -> String? { func roundTitle() -> String? {
if groupStage != nil { return "Poule" } if groupStage != nil { return groupStageObject?.groupStageTitle() }
else if let roundObject { return roundObject.roundTitle() } else if let roundObject { return roundObject.roundTitle() }
else { return nil } else { return nil }
} }
@ -630,7 +628,7 @@ class Match: ModelObject, Storable {
case _winningTeamId = "winningTeamId" case _winningTeamId = "winningTeamId"
case _losingTeamId = "losingTeamId" case _losingTeamId = "losingTeamId"
// case _broadcasted = "broadcasted" // case _broadcasted = "broadcasted"
// case _name = "name" case _name = "name"
// case _order = "order" // case _order = "order"
case _disabled = "disabled" case _disabled = "disabled"
} }

@ -111,7 +111,7 @@ class MatchScheduler : ModelObject, Storable {
return lastDate return lastDate
} }
func groupStageDispatcher(numberOfCourtsAvailablePerRotation: Int, groupStages: [GroupStage], startingDate: Date?) -> GroupStageMatchDispatcher { func groupStageDispatcher(numberOfCourtsAvailablePerRotation: Int, groupStages: [GroupStage], startingDate: Date) -> GroupStageMatchDispatcher {
let _groupStages = groupStages let _groupStages = groupStages
@ -156,7 +156,16 @@ class MatchScheduler : ModelObject, Storable {
(0..<numberOfCourtsAvailablePerRotation).forEach { courtIndex in (0..<numberOfCourtsAvailablePerRotation).forEach { courtIndex in
//print(mt.map { ($0.bracket!.index.intValue, counts[$0.bracket!.index.intValue]) }) //print(mt.map { ($0.bracket!.index.intValue, counts[$0.bracket!.index.intValue]) })
if let first = rotationMatches.first(where: { match in if let first = rotationMatches.first(where: { match in
teamsPerRotation[rotationIndex]!.allSatisfy({ match.containsTeamId($0) == false }) == true let estimatedDuration = match.matchFormat.getEstimatedDuration(additionalEstimationDuration)
let timeIntervalToAdd = (Double(rotationIndex)) * Double(estimatedDuration) * 60
let rotationStartDate: Date = startingDate.addingTimeInterval(timeIntervalToAdd)
let courtsUnavailable = courtsUnavailable(startDate: rotationStartDate, duration: match.matchFormat.getEstimatedDuration(additionalEstimationDuration))
if courtIndex >= numberOfCourtsAvailablePerRotation - courtsUnavailable {
return false
} else {
return teamsPerRotation[rotationIndex]!.allSatisfy({ match.containsTeamId($0) == false }) == true
}
}) { }) {
let timeMatch = GroupStageTimeMatch(matchID: first.id, rotationIndex: rotationIndex, courtIndex: courtIndex, groupIndex: first.groupStageObject!.index) let timeMatch = GroupStageTimeMatch(matchID: first.id, rotationIndex: rotationIndex, courtIndex: courtIndex, groupIndex: first.groupStageObject!.index)
slots.append(timeMatch) slots.append(timeMatch)

@ -96,7 +96,7 @@ class Tournament : ModelObject, Storable {
case _publishBrackets = "publishBrackets" case _publishBrackets = "publishBrackets"
} }
internal init(event: String? = nil, name: String? = nil, startDate: Date = Date(), endDate: Date? = nil, creationDate: Date = Date(), isPrivate: Bool = true, groupStageFormat: MatchFormat? = nil, roundFormat: MatchFormat? = nil, loserRoundFormat: MatchFormat? = nil, groupStageSortMode: GroupStageOrderingMode, groupStageCount: Int = 4, rankSourceDate: Date? = nil, dayDuration: Int = 1, teamCount: Int = 24, teamSorting: TeamSortingType? = nil, federalCategory: TournamentCategory, federalLevelCategory: TournamentLevel, federalAgeCategory: FederalTournamentAge, groupStageCourtCount: Int? = nil, closedRegistrationDate: Date? = nil, groupStageAdditionalQualified: Int = 0, courtCount: Int = 2, prioritizeClubMembers: Bool = false, qualifiedPerGroupStage: Int = 1, teamsPerGroupStage: Int = 4, entryFee: Double? = nil, additionalEstimationDuration: Int = 0, isDeleted: Bool = false, publishManually: Bool = false, publishTeams: Bool = false, publishWaitingList: Bool = false, publishSummons: Bool = false, publishGroupStages: Bool = false, publishBrackets: Bool = false) { internal init(event: String? = nil, name: String? = nil, startDate: Date = Date(), endDate: Date? = nil, creationDate: Date = Date(), isPrivate: Bool = false, groupStageFormat: MatchFormat? = nil, roundFormat: MatchFormat? = nil, loserRoundFormat: MatchFormat? = nil, groupStageSortMode: GroupStageOrderingMode, groupStageCount: Int = 4, rankSourceDate: Date? = nil, dayDuration: Int = 1, teamCount: Int = 24, teamSorting: TeamSortingType? = nil, federalCategory: TournamentCategory, federalLevelCategory: TournamentLevel, federalAgeCategory: FederalTournamentAge, groupStageCourtCount: Int? = nil, closedRegistrationDate: Date? = nil, groupStageAdditionalQualified: Int = 0, courtCount: Int = 2, prioritizeClubMembers: Bool = false, qualifiedPerGroupStage: Int = 1, teamsPerGroupStage: Int = 4, entryFee: Double? = nil, additionalEstimationDuration: Int = 0, isDeleted: Bool = false, publishManually: Bool = false, publishTeams: Bool = false, publishWaitingList: Bool = false, publishSummons: Bool = false, publishGroupStages: Bool = false, publishBrackets: Bool = false) {
self.event = event self.event = event
self.name = name self.name = name
self.startDate = startDate self.startDate = startDate
@ -363,8 +363,9 @@ class Tournament : ModelObject, Storable {
} }
func seeds() -> [TeamRegistration] { func seeds() -> [TeamRegistration] {
let seeds = max(teamCount - groupStageCount * teamsPerGroupStage, 0) let selectedSortedTeams = selectedSortedTeams()
return Array(selectedSortedTeams().prefix(seeds)) let seeds = max(selectedSortedTeams.count - groupStageCount * teamsPerGroupStage, 0)
return Array(selectedSortedTeams.prefix(seeds))
} }
func availableSeeds() -> [TeamRegistration] { func availableSeeds() -> [TeamRegistration] {

@ -339,9 +339,12 @@ struct TournamentPublishingTip: Tip {
Text("Gestion de la publication") Text("Gestion de la publication")
} }
enum ActionKey: String {
case showPadelClub = "show-padel-club"
}
var message: Text? { var message: Text? {
let messageString = "Padel Club vous permet de publier votre tournoi et rendre accessible à tous les résultats des matchs et l'évolution de l'événement. Les informations seront accessible sur le site [Padel Club](\(URLs.padelClub))." Text("Padel Club vous permet de publier votre tournoi et rendre accessible à tous les résultats des matchs et l'évolution de l'événement. Les informations seront accessible sur le site Padel Club.")
return Text(.init(messageString))
} }
var image: Image? { var image: Image? {

@ -46,17 +46,18 @@ struct CallMessageCustomizationView: View {
var finalMessage: String? { var finalMessage: String? {
let localizedCalled = "convoqué" + (tournament.tournamentCategory == .women ? "e" : "") + "s" let localizedCalled = "convoqué" + (tournament.tournamentCategory == .women ? "e" : "") + "s"
return "Bonjour,\n\nVous êtes \(localizedCalled) pour jouer en \(RoundRule.roundName(fromRoundIndex: 2).lowercased()) du \(tournament.tournamentTitle()) au \(clubName) le \(Date().formatted(Date.FormatStyle().weekday(.wide).day().month(.wide))) à \(Date().formatted(Date.FormatStyle().hour().minute())).\n\n" + computedMessage + "\n\n\(customCallMessageSignature)" return "Bonjour,\n\nVous êtes \(localizedCalled) pour jouer en \(RoundRule.roundName(fromRoundIndex: 2).lowercased()) du \(tournament.tournamentTitle()) au \(clubName) le \(tournament.startDate).formatted(Date.FormatStyle().weekday(.wide).day().month(.wide))) à \(tournament.startDate.formatted(Date.FormatStyle().hour().minute())).\n\n" + computedMessage + "\n\n\(customCallMessageSignature)"
} }
var body: some View { var body: some View {
@Bindable var user = dataStore.user @Bindable var user = dataStore.user
List { List {
_renderingView() _renderingView()
.disabled(true)
_optionsView() _optionsView()
_editorView() _editorView()
if user.summonsUseFullCustomMessage { if user.summonsUseFullCustomMessage && tournament.isFree() == false {
_paymentMethodsView() _paymentMethodsView()
} }
@ -138,9 +139,19 @@ struct CallMessageCustomizationView: View {
Text("Information supplémentaire") Text("Information supplémentaire")
} }
} footer: { } footer: {
if user.summonsUseFullCustomMessage { if user.summonsUseFullCustomMessage == false {
LazyVStack(alignment: .leading) { HStack {
Text("Utilisez ces balises") Spacer()
FooterButtonView("éditer") {
focusedField = .body
}
}
}
}
if user.summonsUseFullCustomMessage {
Section {
LazyHStack {
FooterButtonView("#titre") { FooterButtonView("#titre") {
customCallMessageBody.append("#titre") customCallMessageBody.append("#titre")
focusedField = .body focusedField = .body
@ -170,13 +181,8 @@ struct CallMessageCustomizationView: View {
focusedField = .body focusedField = .body
} }
} }
} else { } header: {
HStack { Text("Utilisez ces balises")
Spacer()
FooterButtonView("éditer") {
focusedField = .body
}
}
} }
} }
} }
@ -219,12 +225,14 @@ struct CallMessageCustomizationView: View {
private func _renderingView() -> some View { private func _renderingView() -> some View {
@Bindable var user = dataStore.user @Bindable var user = dataStore.user
Section { Section {
if user.summonsUseFullCustomMessage { Group {
Text(self.computedFullCustomMessage()) if user.summonsUseFullCustomMessage {
} Text(self.computedFullCustomMessage())
else if let finalMessage { }
Text(finalMessage) else if let finalMessage {
} Text(finalMessage)
}
}.italic().foregroundStyle(.gray)
} header: { } header: {
Text("Rendu généré automatiquement") Text("Rendu généré automatiquement")
} }
@ -248,7 +256,7 @@ struct CallMessageCustomizationView: View {
Text("Options") Text("Options")
} }
if user.summonsDisplayEntryFee && user.summonsUseFullCustomMessage == false { if user.summonsDisplayEntryFee && user.summonsUseFullCustomMessage == false && tournament.isFree() == false {
_paymentMethodsView() _paymentMethodsView()
} }
} }

@ -25,9 +25,24 @@ struct CourtView: View {
.keyboardType(.alphabet) .keyboardType(.alphabet)
.multilineTextAlignment(.trailing) .multilineTextAlignment(.trailing)
.frame(maxWidth: .infinity) .frame(maxWidth: .infinity)
.onSubmit {
court.name = name
if name.isEmpty {
court.name = nil
}
try? dataStore.courts.addOrUpdate(instance: court)
}
} label: { } label: {
Text("Nom du terrain") Text("Nom du terrain")
} }
} footer: {
if court.name?.isEmpty == false {
FooterButtonView("nom par défaut") {
name = ""
court.name = nil
try? dataStore.courts.addOrUpdate(instance: court)
}
}
} }
Section { Section {
@ -39,10 +54,6 @@ struct CourtView: View {
} }
} }
} }
.onChange(of: name) {
court.name = name
try? dataStore.courts.addOrUpdate(instance: court)
}
.onChange(of: [court.indoor, court.exitAllowed]) { .onChange(of: [court.indoor, court.exitAllowed]) {
try? dataStore.courts.addOrUpdate(instance: court) try? dataStore.courts.addOrUpdate(instance: court)
} }

@ -128,13 +128,14 @@ struct SpinDrawView: View {
} }
} }
Section { //todo
Text("XXX") // Section {
Text("XXX") // Text("XXX")
Text("XXX") // Text("XXX")
} header: { // Text("XXX")
Text("Comité du tournoi") // } header: {
} // Text("Comité du tournoi")
// }
} }
.toolbar { .toolbar {
ToolbarItem(placement: .cancellationAction) { ToolbarItem(placement: .cancellationAction) {
@ -286,7 +287,7 @@ struct FortuneWheelView: View {
Text(string).bold() Text(string).bold()
} }
} }
.padding(.trailing, 30) .padding(.trailing, 40)
.rotationEffect(.degrees(Double(index) * (360 / Double(segments.count)) + (360 / Double(segments.count) / 2))) .rotationEffect(.degrees(Double(index) * (360 / Double(segments.count)) + (360 / Double(segments.count) / 2)))
.foregroundColor(.white) .foregroundColor(.white)
.position(arcPosition(index: index, radius: radius)) .position(arcPosition(index: index, radius: radius))

@ -11,7 +11,7 @@ struct MatchListView: View {
@EnvironmentObject var dataStore: DataStore @EnvironmentObject var dataStore: DataStore
let section: String let section: String
let matches: [Match] let matches: [Match]
var matchViewStyle: MatchViewStyle = .sectionedStandardStyle var matchViewStyle: MatchViewStyle = .standardStyle
@State var isExpanded: Bool = true @State var isExpanded: Bool = true

@ -72,6 +72,8 @@ struct GroupStageTeamReplacementView: View {
} }
.labelsHidden() .labelsHidden()
.pickerStyle(.inline) .pickerStyle(.inline)
} header: {
Text("Remplacer")
} footer: { } footer: {
Text("Remplacement de l'équipe ou d'un joueur particulier") Text("Remplacement de l'équipe ou d'un joueur particulier")
} }

@ -76,10 +76,12 @@ struct MatchSummaryView: View {
Text(tournament.tournamentTitle()) Text(tournament.tournamentTitle())
} }
HStack { HStack {
if let roundTitle { if matchViewStyle != .sectionedStandardStyle {
Text(roundTitle) if let roundTitle {
Text(roundTitle)
}
Text(matchTitle)
} }
Text(matchTitle)
Spacer() Spacer()
if let courtName { if let courtName {
Spacer() Spacer()

@ -33,24 +33,7 @@ struct CourtAvailabilitySettingsView: View {
if let dates = courtsUnavailability[key] { if let dates = courtsUnavailability[key] {
Section { Section {
ForEach(dates) { dateInterval in ForEach(dates) { dateInterval in
HStack { Menu {
VStack(alignment: .leading, spacing: 0) {
Text(dateInterval.startDate.localizedTime()).font(.largeTitle)
Text(dateInterval.startDate.localizedDay()).font(.caption)
}
Spacer()
VStack {
Image(systemName: "arrowshape.forward.fill")
.tint(.master)
Text("indisponible").foregroundStyle(.red).font(.caption)
}
Spacer()
VStack(alignment: .trailing, spacing: 0) {
Text(dateInterval.endDate.localizedTime()).font(.largeTitle)
Text(dateInterval.endDate.localizedDay()).font(.caption)
}
}
.contextMenu(menuItems: {
Button("dupliquer") { Button("dupliquer") {
let duplicatedDateInterval = DateInterval(event: event.id, courtIndex: (courtIndex+1)%tournament.courtCount, startDate: dateInterval.startDate, endDate: dateInterval.endDate) let duplicatedDateInterval = DateInterval(event: event.id, courtIndex: (courtIndex+1)%tournament.courtCount, startDate: dateInterval.startDate, endDate: dateInterval.endDate)
try? dataStore.dateIntervals.addOrUpdate(instance: duplicatedDateInterval) try? dataStore.dateIntervals.addOrUpdate(instance: duplicatedDateInterval)
@ -65,7 +48,25 @@ struct CourtAvailabilitySettingsView: View {
Button("effacer", role: .destructive) { Button("effacer", role: .destructive) {
try? dataStore.dateIntervals.delete(instance: dateInterval) try? dataStore.dateIntervals.delete(instance: dateInterval)
} }
}) } label: {
HStack {
VStack(alignment: .leading, spacing: 0) {
Text(dateInterval.startDate.localizedTime()).font(.largeTitle)
Text(dateInterval.startDate.localizedDay()).font(.caption)
}
Spacer()
VStack {
Image(systemName: "arrowshape.forward.fill")
.tint(.master)
Text("indisponible").foregroundStyle(.red).font(.caption)
}
Spacer()
VStack(alignment: .trailing, spacing: 0) {
Text(dateInterval.endDate.localizedTime()).font(.largeTitle)
Text(dateInterval.endDate.localizedDay()).font(.caption)
}
}
}
.swipeActions { .swipeActions {
Button(role: .destructive) { Button(role: .destructive) {
try? dataStore.dateIntervals.delete(instance: dateInterval) try? dataStore.dateIntervals.delete(instance: dateInterval)
@ -95,6 +96,8 @@ struct CourtAvailabilitySettingsView: View {
Text("Vous pouvez précisez l'indisponibilité d'une ou plusieurs terrains, que ce soit pour une journée entière ou un créneau précis.") Text("Vous pouvez précisez l'indisponibilité d'une ou plusieurs terrains, que ce soit pour une journée entière ou un créneau précis.")
} actions: { } actions: {
RowButtonView("Ajouter une indisponibilité", systemImage: "plus.circle.fill") { RowButtonView("Ajouter une indisponibilité", systemImage: "plus.circle.fill") {
startDate = tournament.startDate
endDate = tournament.startDate.addingTimeInterval(5400)
showingPopover = true showingPopover = true
} }
} }

@ -42,15 +42,13 @@ struct PlanningSettingsView: View {
} }
} header: { } header: {
Text("Démarrage et durée du tournoi") Text("Démarrage et durée du tournoi")
} footer: {
Text("todo: Expliquer ce que ca fait")
} }
Section { Section {
TournamentFieldsManagerView(localizedStringKey: "Terrains maximum", count: $tournament.courtCount) TournamentFieldsManagerView(localizedStringKey: "Terrains maximum", count: $tournament.courtCount)
if tournament.groupStages().isEmpty == false { if tournament.groupStages().isEmpty == false {
TournamentFieldsManagerView(localizedStringKey: "Terrains par poule", count: $groupStageCourtCount, max: tournament.maximumCourtsPerGroupSage()) TournamentFieldsManagerView(localizedStringKey: "Nombre de poule en même temps", count: $groupStageCourtCount, max: tournament.groupStageCount)
} }
if let event = tournament.eventObject() { if let event = tournament.eventObject() {

@ -17,8 +17,8 @@ struct PlayerSexPickerView: View {
Text(player.playerLabel()) Text(player.playerLabel())
Spacer() Spacer()
Picker(selection: $player.sex) { Picker(selection: $player.sex) {
Text("Homme").tag(PlayerRegistration.PlayerSexType.male) Text("Homme").tag(PlayerRegistration.PlayerSexType.male as PlayerRegistration.PlayerSexType?)
Text("Femme").tag(PlayerRegistration.PlayerSexType.female) Text("Femme").tag(PlayerRegistration.PlayerSexType.female as PlayerRegistration.PlayerSexType?)
} label: { } label: {
} }

@ -11,6 +11,15 @@ struct RoundView: View {
@Environment(\.isEditingTournamentSeed) private var isEditingTournamentSeed @Environment(\.isEditingTournamentSeed) private var isEditingTournamentSeed
@Environment(Tournament.self) var tournament: Tournament @Environment(Tournament.self) var tournament: Tournament
@EnvironmentObject var dataStore: DataStore @EnvironmentObject var dataStore: DataStore
@State private var selectedSeedGroup: SeedInterval?
var showVisualDrawView: Binding<Bool> { Binding(
get: { selectedSeedGroup != nil },
set: {
if $0 == false {
selectedSeedGroup = nil
}
}
)}
var round: Round var round: Round
@ -50,29 +59,21 @@ struct RoundView: View {
} }
} else if let availableSeedGroup = tournament.seedGroupAvailable(atRoundIndex: round.index) { } else if let availableSeedGroup = tournament.seedGroupAvailable(atRoundIndex: round.index) {
RowButtonView("Placer \(availableSeedGroup.localizedLabel())" + ((availableSeedGroup.isFixed() == false) ? " au hasard" : "")) { Section {
tournament.setSeeds(inRoundIndex: round.index, inSeedGroup: availableSeedGroup) RowButtonView("Placer \(availableSeedGroup.localizedLabel())" + ((availableSeedGroup.isFixed() == false) ? " au hasard" : "")) {
if tournament.availableSeeds().isEmpty { tournament.setSeeds(inRoundIndex: round.index, inSeedGroup: availableSeedGroup)
_save() if tournament.availableSeeds().isEmpty {
self.isEditingTournamentSeed.wrappedValue = false _save()
self.isEditingTournamentSeed.wrappedValue = false
}
} }
} }
if (availableSeedGroup.isFixed() == false) { if (availableSeedGroup.isFixed() == false) {
let seeds = tournament.seeds(inSeedGroup: availableSeedGroup) Section {
let availableSeedSpot = tournament.availableSeedSpot(inRoundIndex: round.index) RowButtonView("Tirage au sort \(availableSeedGroup.localizedLabel()) visuel") {
NavigationLink { self.selectedSeedGroup = availableSeedGroup
SpinDrawView(drawees: seeds, segments: availableSeedSpot) { draws in
draws.forEach { drawResult in
print(seeds[drawResult.drawee].teamLabel())
print(availableSeedSpot[drawResult.drawIndex].matchTitle())
seeds[drawResult.drawee].setSeedPosition(inSpot: availableSeedSpot[drawResult.drawIndex], slot: nil, opposingSeeding: false)
}
try? dataStore.matches.addOrUpdate(contentOfs: availableSeedSpot)
try? dataStore.teamRegistrations.addOrUpdate(contentOfs: seeds)
} }
} label: {
Text("ou proposer le tirage au sort \(availableSeedGroup.localizedLabel())")
} }
} }
} }
@ -97,6 +98,23 @@ struct RoundView: View {
} }
} }
} }
.sheet(isPresented: showVisualDrawView) {
if let availableSeedGroup = selectedSeedGroup {
let seeds = tournament.seeds(inSeedGroup: availableSeedGroup)
let availableSeedSpot = tournament.availableSeedSpot(inRoundIndex: round.index)
NavigationStack {
SpinDrawView(drawees: seeds, segments: availableSeedSpot) { draws in
draws.forEach { drawResult in
print(seeds[drawResult.drawee].teamLabel())
print(availableSeedSpot[drawResult.drawIndex].matchTitle())
seeds[drawResult.drawee].setSeedPosition(inSpot: availableSeedSpot[drawResult.drawIndex], slot: nil, opposingSeeding: false)
}
try? dataStore.matches.addOrUpdate(contentOfs: availableSeedSpot)
try? dataStore.teamRegistrations.addOrUpdate(contentOfs: seeds)
}
}
}
}
.headerProminence(.increased) .headerProminence(.increased)
.toolbar { .toolbar {
ToolbarItem(placement: .topBarTrailing) { ToolbarItem(placement: .topBarTrailing) {

@ -95,7 +95,7 @@ struct EditScoreView: View {
dismiss() dismiss()
} }
} footer: { } footer: {
Text("Met à jour le score pour la diffusion, ne termine pas la rencontre") Text("Met à jour le score, ne termine pas la rencontre")
} }
} }
} }

@ -16,7 +16,7 @@ struct TeamRowView: View {
LabeledContent { LabeledContent {
TeamWeightView(team: team, teamPosition: teamPosition) TeamWeightView(team: team, teamPosition: teamPosition)
} label: { } label: {
Text(team.teamLabel(.short)) Text(team.teamLabel(.wide))
if let callDate = team.callDate, displayCallDate { if let callDate = team.callDate, displayCallDate {
Text("Déjà convoquée \(callDate.localizedDate())") Text("Déjà convoquée \(callDate.localizedDate())")
.foregroundStyle(.red) .foregroundStyle(.red)

@ -56,7 +56,7 @@ struct FileImportView: View {
await _startImport(fileContent: fileContent) await _startImport(fileContent: fileContent)
} }
} }
.disabled(fileContent == nil) .disabled(fileContent == nil || convertingFile)
} footer: { } footer: {
if fileProvider == .frenchFederation { if fileProvider == .frenchFederation {
let footerString = "Fichier provenant de [beach-padel.app.fft.fr](\(URLs.beachPadel.rawValue))" let footerString = "Fichier provenant de [beach-padel.app.fft.fr](\(URLs.beachPadel.rawValue))"
@ -95,7 +95,7 @@ struct FileImportView: View {
LabeledContent { LabeledContent {
ProgressView() ProgressView()
} label: { } label: {
Text("Importation en cours") Text("Conversion du fichier en cours")
} }
} }
} }
@ -171,13 +171,14 @@ struct FileImportView: View {
case .success(let fileurls): case .success(let fileurls):
if let selectedFile = fileurls.first { if let selectedFile = fileurls.first {
if selectedFile.startAccessingSecurityScopedResource() { if selectedFile.startAccessingSecurityScopedResource() {
convertingFile = true
errorMessage = nil errorMessage = nil
teams.removeAll() teams.removeAll()
Task { Task {
do { do {
if selectedFile.lastPathComponent.hasSuffix("xls") { if selectedFile.lastPathComponent.hasSuffix("xls") {
convertingFile = true
fileContent = try await CloudConvert.manager.uploadFile(selectedFile) fileContent = try await CloudConvert.manager.uploadFile(selectedFile)
convertingFile = false
} else { } else {
fileContent = try String(contentsOf: selectedFile) fileContent = try String(contentsOf: selectedFile)
} }
@ -242,7 +243,6 @@ struct FileImportView: View {
func _startImport(fileContent: String) async { func _startImport(fileContent: String) async {
await MainActor.run { await MainActor.run {
convertingFile = true
errorMessage = nil errorMessage = nil
teams.removeAll() teams.removeAll()
} }
@ -252,7 +252,6 @@ struct FileImportView: View {
self.teams = await FileImportManager.shared.createTeams(from: fileContent, tournament: tournament, fileProvider: fileProvider) self.teams = await FileImportManager.shared.createTeams(from: fileContent, tournament: tournament, fileProvider: fileProvider)
await MainActor.run { await MainActor.run {
convertingFile = false
didImport = true didImport = true
} }
} }

@ -28,10 +28,16 @@ struct BroadcastView: View {
var body: some View { var body: some View {
@Bindable var tournament = tournament @Bindable var tournament = tournament
List { List {
TipView(tournamentPublishingTip) Section {
.tipStyle(tint: nil) TipView(tournamentPublishingTip) { action in
TipView(tournamentTVBroadcastTip) UIApplication.shared.open(URLs.padelClub.url)
}
.tipStyle(tint: nil) .tipStyle(tint: nil)
}
Section {
TipView(tournamentTVBroadcastTip)
.tipStyle(tint: nil)
}
if tournament.publishManually == false { if tournament.publishManually == false {
Section { Section {

@ -83,11 +83,11 @@ struct TournamentClubSettingsView: View {
private func _courtView(atIndex index: Int, tournamentClub: Club) -> some View { private func _courtView(atIndex index: Int, tournamentClub: Club) -> some View {
if let court = tournamentClub.courts.first(where: { $0.index == index }) { if let court = tournamentClub.courts.first(where: { $0.index == index }) {
LabeledContent { LabeledContent {
if let name = court.name { FooterButtonView("personnaliser") {
Text(name) selectedCourt = court
} }
} label: { } label: {
Text(court.courtIndexTitle()) Text(court.courtTitle())
HStack { HStack {
if court.indoor { if court.indoor {
Text("Couvert") Text("Couvert")

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

@ -374,7 +374,13 @@ struct InscriptionManagerView: View {
TipView(pasteTip) { action in TipView(pasteTip) { action in
if let paste = UIPasteboard.general.string { if let paste = UIPasteboard.general.string {
self.pasteString = paste Task {
await MainActor.run {
fetchPlayers.nsPredicate = _pastePredicate(pasteField: paste, mostRecentDate: SourceFileManager.shared.mostRecentDateAvailable)
pasteString = paste
autoSelect = true
}
}
} }
} }
.tipStyle(tint: nil) .tipStyle(tint: nil)

@ -25,6 +25,16 @@ struct TournamentInitView: View {
Text("La date, la catégorie, le niveau, le nombre de terrain, les formats, etc.") Text("La date, la catégorie, le niveau, le nombre de terrain, les formats, etc.")
} }
Section {
NavigationLink(value: Screen.broadcast) {
LabeledContent {
// Text(tournament.isPrivate ? "privée" : "publique")
} label: {
Text("Publication")
}
}
}
Section { Section {
NavigationLink(value: Screen.structure) { NavigationLink(value: Screen.structure) {
LabeledContent { LabeledContent {

@ -72,15 +72,6 @@ struct TournamentView: View {
} }
case .initial: case .initial:
TournamentInitView() TournamentInitView()
NavigationLink(value: Screen.broadcast) {
LabeledContent {
Text(tournament.publishManually ? "manuel" : "auto.")
} label: {
Text("Publication")
}
}
case .build: case .build:
TournamentRunningView(tournament: tournament) TournamentRunningView(tournament: tournament)
@ -153,7 +144,7 @@ struct TournamentView: View {
Text("Classement") Text("Classement")
} }
NavigationLink(value: Screen.broadcast) { NavigationLink(value: Screen.broadcast) {
Text("Diffusion") Text("Publication")
} }
} }
} label: { } label: {

Loading…
Cancel
Save