multistore
Razmig Sarkissian 1 year ago
parent 8dffc0a3e6
commit cd730f3f03
  1. 2
      PadelClub/Data/Tournament.swift
  2. 64
      PadelClub/Views/Calling/CallView.swift
  3. 44
      PadelClub/Views/Tournament/Screen/TableStructureView.swift
  4. 51
      PadelClub/Views/Tournament/Screen/TournamentRankView.swift
  5. 128
      PadelClub/Views/Tournament/TournamentBuildView.swift
  6. 2
      PadelClub/Views/Tournament/TournamentView.swift

@ -1066,7 +1066,7 @@ class Tournament : ModelObject, Storable {
return Array(allMatches.filter({ $0.hasEnded() }).sorted(by: \.computedEndDateForSorting).reversed().prefix(_limit)) return Array(allMatches.filter({ $0.hasEnded() }).sorted(by: \.computedEndDateForSorting).reversed().prefix(_limit))
} }
func finalRanking() -> [Int: [String]] { func finalRanking() async -> [Int: [String]] {
var teams: [Int: [String]] = [:] var teams: [Int: [String]] = [:]
var ids: Set<String> = Set<String>() var ids: Set<String> = Set<String>()
let rounds = rounds() let rounds = rounds()

@ -85,7 +85,7 @@ struct CallView: View {
} }
} }
var finalMessage: String { func finalMessage(reSummon: Bool) -> String {
ContactType.callingMessage(tournament: tournament, startDate: callDate, roundLabel: roundLabel, matchFormat: matchFormat, reSummon: reSummon) ContactType.callingMessage(tournament: tournament, startDate: callDate, roundLabel: roundLabel, matchFormat: matchFormat, reSummon: reSummon)
} }
@ -94,7 +94,7 @@ struct CallView: View {
} }
var body: some View { var body: some View {
let callWord = reSummon ? "Reconvoquer" : "Convoquer" let callWord : String = (reSummon ? "Reconvoquer" : "Convoquer")
HStack { HStack {
if teams.count == 1 { if teams.count == 1 {
if let previousCallDate = teams.first?.callDate, Calendar.current.compare(previousCallDate, to: callDate, toGranularity: .minute) != .orderedSame { if let previousCallDate = teams.first?.callDate, Calendar.current.compare(previousCallDate, to: callDate, toGranularity: .minute) != .orderedSame {
@ -105,23 +105,10 @@ struct CallView: View {
} else { } else {
Text(callWord + " ces \(teams.count) paires par") Text(callWord + " ces \(teams.count) paires par")
} }
Button {
self._payTournamentAndExecute { _summonMenu(byMessage: true)
self._contactByMessage()
}
} label: {
Text("sms")
.underline()
}
Text("ou") Text("ou")
Button { _summonMenu(byMessage: false)
self._payTournamentAndExecute {
self._contactByMail()
}
} label: {
Text("mail")
.underline()
}
} }
.font(.subheadline) .font(.subheadline)
.buttonStyle(.borderless) .buttonStyle(.borderless)
@ -182,6 +169,39 @@ struct CallView: View {
}) })
} }
@ViewBuilder
private func _summonMenu(byMessage: Bool) -> some View {
if reSummon {
Menu {
Button("Convoquer") {
_summon(byMessage: byMessage, reSummon: false)
}
Button("Re-convoquer") {
_summon(byMessage: byMessage, reSummon: true)
}
} label: {
Text(byMessage ? "sms" : "mail")
.underline()
}
} else {
Button(byMessage ? "sms" : "mail") {
_summon(byMessage: byMessage, reSummon: false)
}
}
}
private func _summon(byMessage: Bool, reSummon: Bool) {
self._payTournamentAndExecute {
if byMessage {
self._contactByMessage(reSummon: reSummon)
} else {
self._contactByMail(reSummon: reSummon)
}
}
}
fileprivate func _payTournamentAndExecute(_ handler: () -> ()) { fileprivate func _payTournamentAndExecute(_ handler: () -> ()) {
do { do {
try tournament.payIfNecessary() try tournament.payIfNecessary()
@ -191,12 +211,12 @@ struct CallView: View {
} }
} }
fileprivate func _contactByMessage() { fileprivate func _contactByMessage(reSummon: Bool) {
contactType = .message(date: callDate, recipients: teams.flatMap { $0.getPhoneNumbers() }, body: finalMessage, tournamentBuild: nil) contactType = .message(date: callDate, recipients: teams.flatMap { $0.getPhoneNumbers() }, body: finalMessage(reSummon: reSummon), tournamentBuild: nil)
} }
fileprivate func _contactByMail() { fileprivate func _contactByMail(reSummon: Bool) {
contactType = .mail(date: callDate, recipients: tournament.umpireMail(), bccRecipients: teams.flatMap { $0.getMail() }, body: finalMessage, subject: tournament.tournamentTitle(), tournamentBuild: nil) contactType = .mail(date: callDate, recipients: tournament.umpireMail(), bccRecipients: teams.flatMap { $0.getMail() }, body: finalMessage(reSummon: reSummon), subject: tournament.tournamentTitle(), tournamentBuild: nil)
} }
} }

@ -6,6 +6,7 @@
// //
import SwiftUI import SwiftUI
import LeStorage
struct TableStructureView: View { struct TableStructureView: View {
@Environment(Tournament.self) private var tournament: Tournament @Environment(Tournament.self) private var tournament: Tournament
@ -133,6 +134,24 @@ struct TableStructureView: View {
Text("Équipes en tableau final") Text("Équipes en tableau final")
} }
} }
Section {
RowButtonView("Sauver sans reconstuire l'existant") {
_saveWithoutRebuild()
}
}
Section {
RowButtonView("Recontruire les poules", role:.destructive) {
_save(rebuildEverything: false)
}
}
Section {
RowButtonView("Tout refaire", role: .destructive) {
_save(rebuildEverything: true)
}
}
} }
.focused($stepperFieldIsFocused) .focused($stepperFieldIsFocused)
.onChange(of: stepperFieldIsFocused) { .onChange(of: stepperFieldIsFocused) {
@ -204,17 +223,17 @@ struct TableStructureView: View {
} }
} }
.confirmationDialog("Refaire la structure", isPresented: $presentRefreshStructureWarning, actions: { .confirmationDialog("Refaire la structure", isPresented: $presentRefreshStructureWarning, actions: {
Button("Sauver sans reconstuire l'existant") {
_saveWithoutRebuild()
}
if requirements.allSatisfy({ $0 == .groupStage }) { Button("Recontruire les poules") {
Button("Refaire les poules") { _save(rebuildEverything: false)
_save(rebuildEverything: false)
}
} }
Button("Tout refaire", role: .destructive) { Button("Tout refaire", role: .destructive) {
_save(rebuildEverything: true) _save(rebuildEverything: true)
} }
}, message: { }, message: {
ForEach(Array(requirements)) { requirement in ForEach(Array(requirements)) { requirement in
Text(requirement.rebuildingRequirementMessage) Text(requirement.rebuildingRequirementMessage)
@ -227,6 +246,21 @@ struct TableStructureView: View {
.navigationBarTitleDisplayMode(.inline) .navigationBarTitleDisplayMode(.inline)
} }
private func _saveWithoutRebuild() {
tournament.teamCount = teamCount
tournament.groupStageCount = groupStageCount
tournament.teamsPerGroupStage = teamsPerGroupStage
tournament.qualifiedPerGroupStage = qualifiedPerGroupStage
tournament.groupStageAdditionalQualified = groupStageAdditionalQualified
do {
try dataStore.tournaments.addOrUpdate(instance: tournament)
} catch {
Logger.error(error)
}
dismiss()
}
private func _save(rebuildEverything: Bool = false) { private func _save(rebuildEverything: Bool = false) {
_verifyValueIntegrity() _verifyValueIntegrity()

@ -13,6 +13,7 @@ struct TournamentRankView: View {
@EnvironmentObject var dataStore: DataStore @EnvironmentObject var dataStore: DataStore
@State private var rankings: [Int: [TeamRegistration]] = [:] @State private var rankings: [Int: [TeamRegistration]] = [:]
@State private var calculating = false
var body: some View { var body: some View {
List { List {
@ -144,19 +145,59 @@ struct TournamentRankView: View {
} }
} }
} }
.overlay(content: {
if calculating {
ProgressView()
}
})
.onAppear { .onAppear {
let finalRanks = tournament.finalRanking() calculating = true
finalRanks.keys.sorted().forEach { rank in Task {
if let rankedTeamIds = finalRanks[rank] { await calculateRankings()
rankings[rank] = rankedTeamIds.compactMap { Store.main.findById($0) } calculating = false
}
} }
} }
.navigationTitle("Classement") .navigationTitle("Classement")
.navigationBarTitleDisplayMode(.inline) .navigationBarTitleDisplayMode(.inline)
.toolbarBackground(.visible, for: .navigationBar) .toolbarBackground(.visible, for: .navigationBar)
.toolbar {
ToolbarItem(placement: .topBarTrailing) {
if let url = tournament.shareURL(.rankings) {
actionForURL(url)
}
}
}
}
func calculateRankings() async {
let finalRanks = await tournament.finalRanking()
finalRanks.keys.sorted().forEach { rank in
if let rankedTeamIds = finalRanks[rank] {
rankings[rank] = rankedTeamIds.compactMap { Store.main.findById($0) }
}
}
}
@ViewBuilder
func actionForURL(_ url: URL, removeSource: Bool = false) -> some View {
Menu {
Button {
UIApplication.shared.open(url)
} label: {
Label("Voir", systemImage: "safari")
}
ShareLink(item: url) {
Label("Partager le lien", systemImage: "link")
}
} label: {
Image(systemName: "square.and.arrow.up")
}
.frame(maxWidth: .infinity)
.buttonStyle(.borderless)
} }
private func _save() { private func _save() {
do { do {
try dataStore.teamRegistrations.addOrUpdate(contentOfs: tournament.unsortedTeams()) try dataStore.teamRegistrations.addOrUpdate(contentOfs: tournament.unsortedTeams())

@ -19,66 +19,63 @@ struct TournamentBuildView: View {
var body: some View { var body: some View {
let state = tournament.state() let state = tournament.state()
if tournament.hasEnded() { Section {
Section { if tournament.hasEnded() {
NavigationLink(value: Screen.rankings) { NavigationLink(value: Screen.rankings) {
Text("Classement final des équipes") Text("Classement final des équipes")
} }
} }
}
Section { if state == .running || state == .finished {
if tournament.groupStageCount > 0 { TournamentInscriptionView(tournament: tournament)
NavigationLink(value: Screen.groupStage) { TournamentBroadcastRowView(tournament: tournament)
LabeledContent { }
if let groupStageStatus {
Text(groupStageStatus).lineLimit(1) NavigationLink(value: Screen.cashier) {
.multilineTextAlignment(.trailing) let tournamentStatus = cashierStatus
} else { LabeledContent {
ProgressView() if let tournamentStatus {
} Text(tournamentStatus.completion)
} label: { } else {
Text("Poules") ProgressView()
if tournament.shouldVerifyGroupStage { }
Text("Vérifier les poules").foregroundStyle(.logoRed) } label: {
} Text("Encaissement")
if let tournamentStatus {
Text(tournamentStatus.label).lineLimit(1)
} else {
Text(" ")
} }
} }
.task { }
groupStageStatus = await tournament.groupStageStatus() .task {
} cashierStatus = await tournament.cashierStatus()
} }
if tournament.rounds().isEmpty == false { if state != .finished {
NavigationLink(value: Screen.round) { NavigationLink(value: Screen.schedule) {
let tournamentStatus = scheduleStatus
LabeledContent { LabeledContent {
if let bracketStatus { if let tournamentStatus {
Text(bracketStatus).lineLimit(1) Text(tournamentStatus.completion)
.multilineTextAlignment(.trailing)
} else { } else {
ProgressView() ProgressView()
} }
} label: { } label: {
Text("Tableau") Text("Horaires")
if tournament.shouldVerifyBracket { if let tournamentStatus {
Text("Vérifier la tableau").foregroundStyle(.logoRed) Text(tournamentStatus.label).lineLimit(1)
} else {
Text(" ")
} }
} }
} }
.task { .task {
bracketStatus = await tournament.bracketStatus() scheduleStatus = await tournament.scheduleStatus()
} }
}
}
Section {
if state == .running || state == .finished {
TournamentBroadcastRowView(tournament: tournament)
}
if state == .running || state == .finished { NavigationLink(value: Screen.call) {
NavigationLink(value: Screen.cashier) { let tournamentStatus = callStatus
let tournamentStatus = cashierStatus
LabeledContent { LabeledContent {
if let tournamentStatus { if let tournamentStatus {
Text(tournamentStatus.completion) Text(tournamentStatus.completion)
@ -86,7 +83,7 @@ struct TournamentBuildView: View {
ProgressView() ProgressView()
} }
} label: { } label: {
Text("Encaissement") Text("Convocations")
if let tournamentStatus { if let tournamentStatus {
Text(tournamentStatus.label).lineLimit(1) Text(tournamentStatus.label).lineLimit(1)
} else { } else {
@ -95,58 +92,53 @@ struct TournamentBuildView: View {
} }
} }
.task { .task {
cashierStatus = await tournament.cashierStatus() callStatus = await tournament.callStatus()
} }
} }
}
if state != .finished { Section {
NavigationLink(value: Screen.schedule) { if tournament.groupStageCount > 0 {
let tournamentStatus = scheduleStatus NavigationLink(value: Screen.groupStage) {
LabeledContent { LabeledContent {
if let tournamentStatus { if let groupStageStatus {
Text(tournamentStatus.completion) Text(groupStageStatus).lineLimit(1)
.multilineTextAlignment(.trailing)
} else { } else {
ProgressView() ProgressView()
} }
} label: { } label: {
Text("Horaires") Text("Poules")
if let tournamentStatus { if tournament.shouldVerifyGroupStage {
Text(tournamentStatus.label).lineLimit(1) Text("Vérifier les poules").foregroundStyle(.logoRed)
} else {
Text(" ")
} }
} }
} }
.task { .task {
scheduleStatus = await tournament.scheduleStatus() groupStageStatus = await tournament.groupStageStatus()
} }
}
NavigationLink(value: Screen.call) { if tournament.rounds().isEmpty == false {
let tournamentStatus = callStatus NavigationLink(value: Screen.round) {
LabeledContent { LabeledContent {
if let tournamentStatus { if let bracketStatus {
Text(tournamentStatus.completion) Text(bracketStatus).lineLimit(1)
.multilineTextAlignment(.trailing)
} else { } else {
ProgressView() ProgressView()
} }
} label: { } label: {
Text("Convocations") Text("Tableau")
if let tournamentStatus { if tournament.shouldVerifyBracket {
Text(tournamentStatus.label).lineLimit(1) Text("Vérifier la tableau").foregroundStyle(.logoRed)
} else {
Text(" ")
} }
} }
} }
.task { .task {
callStatus = await tournament.callStatus() bracketStatus = await tournament.bracketStatus()
} }
} }
if state == .running || state == .finished {
TournamentInscriptionView(tournament: tournament)
}
} }
} }
} }

@ -67,8 +67,8 @@ struct TournamentView: View {
TournamentInitView(tournament: tournament) TournamentInitView(tournament: tournament)
TournamentBuildView(tournament: tournament) TournamentBuildView(tournament: tournament)
case .running: case .running:
TournamentRunningView(tournament: tournament)
TournamentBuildView(tournament: tournament) TournamentBuildView(tournament: tournament)
TournamentRunningView(tournament: tournament)
case .finished: case .finished:
TournamentBuildView(tournament: tournament) TournamentBuildView(tournament: tournament)
TournamentRunningView(tournament: tournament) TournamentRunningView(tournament: tournament)

Loading…
Cancel
Save