From ca4ce044b5c6d26f5164c9d627032d65c21a7aeb Mon Sep 17 00:00:00 2001 From: kituNew Date: Sat, 12 Jul 2025 01:25:20 +0300 Subject: [PATCH 1/2] add baze --- SportApp/HomeView.swift | 9 +- SportApp/Navigation/Coordinator.swift | 1 + SportApp/Statistics/View/StatisticsView.swift | 88 +++++++++++++- .../ViewModel/StatisticViewModel.swift | 115 ++++++++++++++++++ .../ViewModel/StatisticsViewModel.swift | 16 --- SportApp/Tournirs/Model/Tournir.swift | 15 ++- SportApp/Tournirs/View/TournirMaker.swift | 2 +- SportApp/Tournirs/View/TournirsDetail.swift | 8 ++ SportApp/Tournirs/View/TournirsListView.swift | 1 + .../ViewModel/TournirsViewModel.swift | 19 ++- 10 files changed, 247 insertions(+), 27 deletions(-) create mode 100644 SportApp/Statistics/ViewModel/StatisticViewModel.swift delete mode 100644 SportApp/Statistics/ViewModel/StatisticsViewModel.swift diff --git a/SportApp/HomeView.swift b/SportApp/HomeView.swift index 9c8a0b8..7a597d1 100644 --- a/SportApp/HomeView.swift +++ b/SportApp/HomeView.swift @@ -65,8 +65,13 @@ struct HomeView: View { } .tag(0) // исправить потом - NavigationStack() { - EmptyView() + NavigationStack(path: $coordinator.statListPath) { + StatisticsView(viewModelOfStatistic: StatisticViewModel(tournirsVM: viewModelOfTournirs, coordinator: coordinator)) + .environmentObject(coordinator) + .navigationDestination(for: Tournir.self) { detail in + TournirsDetail() + .environmentObject(coordinator) + } } .tabItem { Label("Мои матчи", systemImage: "pawprint.circle.fill") diff --git a/SportApp/Navigation/Coordinator.swift b/SportApp/Navigation/Coordinator.swift index 1f62930..5e2dceb 100644 --- a/SportApp/Navigation/Coordinator.swift +++ b/SportApp/Navigation/Coordinator.swift @@ -10,6 +10,7 @@ import Combine class Coordinator: ObservableObject { @Published var tournirListPath = NavigationPath() + @Published var statListPath = NavigationPath() @Published var presentedSheet: ModalSheet? @Published var user: User = User(id: UUID(), phio: "", password: "", email: "", isAdmin: false) @Published var currentTournir: Tournir? diff --git a/SportApp/Statistics/View/StatisticsView.swift b/SportApp/Statistics/View/StatisticsView.swift index b3d00dc..ff494a9 100644 --- a/SportApp/Statistics/View/StatisticsView.swift +++ b/SportApp/Statistics/View/StatisticsView.swift @@ -9,13 +9,93 @@ import SwiftUI struct StatisticsView: View { @EnvironmentObject var coordinator: Coordinator + @StateObject var viewModelOfStatistic: StatisticViewModel + @State var tournirs: [Tournir] = [] + + init(viewModelOfStatistic: StatisticViewModel) { + _viewModelOfStatistic = StateObject(wrappedValue: viewModelOfStatistic) + } var body: some View { - if coordinator.user.isAdmin == true { - + VStack { + ScrollView { + VStack { + Text("openedRegistrationTournaments") + + ForEach(tournirs, id: \.id) { tournir in + if tournir.tournirInstaseState == .openedRegistrationTournaments { + TournirCell(tournir: tournir) + .padding(.horizontal, 16) + .padding(.vertical, 3) + .onTapGesture { + coordinator.tournirListPath = NavigationPath() + coordinator.currentTournir = tournir + coordinator.statListPath.append(tournir) + } + } + } + } + + VStack { + Text("closedRegistrationTournaments") + + ForEach(tournirs, id: \.id) { tournir in + if tournir.tournirInstaseState == .closedRegistrationTournaments { + TournirCell(tournir: tournir) + .padding(.horizontal, 16) + .padding(.vertical, 3) + .onTapGesture { + coordinator.tournirListPath = NavigationPath() + coordinator.currentTournir = tournir + coordinator.statListPath.append(tournir) + } + } + } + } + + VStack { + Text("ongoingTournaments") + + ForEach(tournirs, id: \.id) { tournir in + if tournir.tournirInstaseState == .ongoingTournaments { + TournirCell(tournir: tournir) + .padding(.horizontal, 16) + .padding(.vertical, 3) + .onTapGesture { + coordinator.tournirListPath = NavigationPath() + coordinator.currentTournir = tournir + coordinator.statListPath.append(tournir) + } + } + } + } + + VStack { + Text("pastTournament") + + ForEach(tournirs, id: \.id) { tournir in + if tournir.tournirInstaseState == .pastTournament { + TournirCell(tournir: tournir) + .padding(.horizontal, 16) + .padding(.vertical, 3) + .onTapGesture { + coordinator.tournirListPath = NavigationPath() + coordinator.currentTournir = tournir + coordinator.statListPath.append(tournir) + } + } + } + } + } } - ScrollView{ - + .onAppear { + if coordinator.user.isAdmin == true { + tournirs = viewModelOfStatistic.tournirs4Admin() + } else { + tournirs = viewModelOfStatistic.tournirs4User() + } } + .frame(maxWidth: .infinity, maxHeight: .infinity) + .background(Color(red: 248/255, green: 247/255, blue: 255/255)) } } diff --git a/SportApp/Statistics/ViewModel/StatisticViewModel.swift b/SportApp/Statistics/ViewModel/StatisticViewModel.swift new file mode 100644 index 0000000..7e4ad66 --- /dev/null +++ b/SportApp/Statistics/ViewModel/StatisticViewModel.swift @@ -0,0 +1,115 @@ +// +// StatisticViewModel.swift +// SportApp +// +// Created by Zaitsev Vladislav on 11.07.2025. +// + +import Foundation +import Combine + +class StatisticViewModel: ObservableObject { + @Published var allTournirs: [Tournir] = [] + @Published var user: User = User(id: UUID(), phio: "", password: "", email: "", isAdmin: false) + + private var cancellables = Set() + + init(tournirsVM: TournirsViewModel, coordinator: Coordinator) { + coordinator.$user + .assign(to: \.user, on: self) + .store(in: &cancellables) + + tournirsVM.$tournirs + .assign(to: \.allTournirs, on: self) + .store(in: &cancellables) + } + + func tournirs4User() -> [Tournir] { + let mockTournirs: [Tournir] = [ + // 1. Прошедший турнир + Tournir( + id: UUID(), + title: "Cупер кубок 2023", + description: "Главный футбольный турнир года, прошедший в Москве.", + sport: "Футбол", + type_group: .olympic, + type_tournir: .team, + start_time: Calendar.current.date(byAdding: .day, value: -10, to: Date())!, + created_at: Calendar.current.date(byAdding: .month, value: -6, to: Date())!, + entry_cost: 0.0, + is_team_based: true, + place: "Стадион 'Динамо', Москва", + max_participants: 32, + organizer_id: UUID(), + users: [user], + requirements: Requirements(), + tournirInstaseState: .pastTournament + ), + + // 2. Турнир идёт + Tournir( + id: UUID(), + title: "Чемпионат города", + description: "Ежегодный баскетбольный чемпионат среди любительских команд.", + sport: "Баскетбол", + type_group: .olympic, + type_tournir: .team, + start_time: Calendar.current.date(byAdding: .hour, value: -2, to: Date())!, + created_at: Calendar.current.date(byAdding: .month, value: -3, to: Date())!, + entry_cost: 500.0, + is_team_based: true, + place: "Спортивный зал 'Арена'", + max_participants: 16, + organizer_id: UUID(), + users: [user], + requirements: Requirements(), + tournirInstaseState: .ongoingTournaments + ), + + // 3. Регистрация закрыта + Tournir( + id: UUID(), + title: "Турнир чемпионов", + description: "Волейбольный турнир для профессиональных клубов.", + sport: "Волейбол", + type_group: .olympic, + type_tournir: .team, + start_time: Calendar.current.date(byAdding: .day, value: 3, to: Date())!, + created_at: Calendar.current.date(byAdding: .month, value: -4, to: Date())!, + entry_cost: 1000.0, + is_team_based: true, + place: "Крытый спортивный комплекс", + max_participants: 8, + organizer_id: UUID(), + users: [user], + requirements: Requirements(), + tournirInstaseState: .closedRegistrationTournaments + ), + + // 4. Регистрация открыта + Tournir( + id: UUID(), + title: "Молодёжный кубок", + description: "Хоккейный турнир для молодых игроков до 21 года.", + sport: "Хоккей", + type_group: .olympic, + type_tournir: .team, + start_time: Calendar.current.date(byAdding: .weekOfYear, value: 2, to: Date())!, + created_at: Calendar.current.date(byAdding: .month, value: -1, to: Date())!, + entry_cost: 200.0, + is_team_based: true, + place: "Ледовая арена 'Звезда'", + max_participants: 12, + organizer_id: UUID(), + users: [user], + requirements: Requirements(), + tournirInstaseState: .openedRegistrationTournaments + ) + ] + return mockTournirs + } + + func tournirs4Admin() -> [Tournir] { + allTournirs.filter { $0.organizer_id == user.id } + } +} diff --git a/SportApp/Statistics/ViewModel/StatisticsViewModel.swift b/SportApp/Statistics/ViewModel/StatisticsViewModel.swift deleted file mode 100644 index 8c9d8cf..0000000 --- a/SportApp/Statistics/ViewModel/StatisticsViewModel.swift +++ /dev/null @@ -1,16 +0,0 @@ -// -// StatisticsViewModel.swift -// SportApp -// -// Created by user on 11.07.2025. -// - -import Combine -import Foundation - -@MainActor -class StatisticsViewModel: ObservableObject { - @Published var tournirs: [ReggedDTO] = [] - - -} diff --git a/SportApp/Tournirs/Model/Tournir.swift b/SportApp/Tournirs/Model/Tournir.swift index 8ac2213..1db1116 100644 --- a/SportApp/Tournirs/Model/Tournir.swift +++ b/SportApp/Tournirs/Model/Tournir.swift @@ -26,7 +26,7 @@ struct Tournir: Codable, Identifiable, Hashable { var tournirInstaseState: TournirInstaseState = .openedRegistrationTournaments } -enum TournirInstaseState: Codable, Identifiable, Hashable { +enum TournirInstaseState: String, Codable, Identifiable, Hashable { case pastTournament case ongoingTournaments case closedRegistrationTournaments @@ -44,6 +44,19 @@ enum TournirInstaseState: Codable, Identifiable, Hashable { return 4 } } + + static func nextState(_ state: TournirInstaseState) -> TournirInstaseState { + switch state { + case .openedRegistrationTournaments: + return .closedRegistrationTournaments + case .closedRegistrationTournaments: + return .ongoingTournaments + case .ongoingTournaments: + return .pastTournament + case .pastTournament: + return .pastTournament + } + } } struct Requirements: Codable, Hashable { diff --git a/SportApp/Tournirs/View/TournirMaker.swift b/SportApp/Tournirs/View/TournirMaker.swift index 94c95e2..08620b7 100644 --- a/SportApp/Tournirs/View/TournirMaker.swift +++ b/SportApp/Tournirs/View/TournirMaker.swift @@ -177,7 +177,7 @@ struct TournirMaker: View { Spacer() Button(action: { - let tourinr: Tournir = Tournir(id: UUID(), title: name, description: description, sport: Sport.toStringEng(Sport.allSports[selectedSport]), type_group: .olympic, type_tournir: .solo, start_time: combinedDateTime, created_at: Date(), entry_cost: Double(cost) ?? 0, is_team_based: true, place: "", max_participants: 10, organizer_id: UUID(), requirements: Requirements()) + let tourinr: Tournir = Tournir(id: UUID(), title: name, description: description, sport: Sport.toStringEng(Sport.allSports[selectedSport]), type_group: .olympic, type_tournir: .solo, start_time: combinedDateTime, created_at: Date(), entry_cost: Double(cost) ?? 0, is_team_based: true, place: "", max_participants: 10, organizer_id: coordinator.user.id, requirements: Requirements()) viewModel.tournirs.append(tourinr) viewModel.giveTournir(tournir: tourinr) coordinator.dismissSheet() diff --git a/SportApp/Tournirs/View/TournirsDetail.swift b/SportApp/Tournirs/View/TournirsDetail.swift index e97c4f0..5d3fd2c 100644 --- a/SportApp/Tournirs/View/TournirsDetail.swift +++ b/SportApp/Tournirs/View/TournirsDetail.swift @@ -97,6 +97,14 @@ struct TournirsDetail: View { Text("Зарегистрироваться") .padding() } + } else { + Button(action: { + // сохранять в нормальное место + coordinator.currentTournir!.tournirInstaseState = TournirInstaseState.nextState(coordinator.currentTournir!.tournirInstaseState) + }, label: { + Text(coordinator.currentTournir!.tournirInstaseState.rawValue) + .padding() + }) } Spacer() } diff --git a/SportApp/Tournirs/View/TournirsListView.swift b/SportApp/Tournirs/View/TournirsListView.swift index 332f93c..16c1da5 100644 --- a/SportApp/Tournirs/View/TournirsListView.swift +++ b/SportApp/Tournirs/View/TournirsListView.swift @@ -41,6 +41,7 @@ struct TournirsListView: View { .padding(.horizontal, 16) .padding(.vertical, 3) .onTapGesture { + coordinator.statListPath = NavigationPath() coordinator.currentTournir = tournir coordinator.tournirListPath.append(tournir) } diff --git a/SportApp/Tournirs/ViewModel/TournirsViewModel.swift b/SportApp/Tournirs/ViewModel/TournirsViewModel.swift index 8e92e52..dda844f 100644 --- a/SportApp/Tournirs/ViewModel/TournirsViewModel.swift +++ b/SportApp/Tournirs/ViewModel/TournirsViewModel.swift @@ -8,7 +8,6 @@ import Combine import Foundation -@MainActor class TournirsViewModel: ObservableObject { @Published var tournirs: [Tournir] = [] @@ -20,7 +19,11 @@ class TournirsViewModel: ObservableObject { func loadTournirs() { Task { do { - tournirs = try await fetchTournirs() + let tournirsss = try await fetchTournirs() + + await MainActor.run { + self.tournirs = tournirsss + } } catch { tournirs = [] } @@ -42,7 +45,7 @@ class TournirsViewModel: ObservableObject { } let mappedEvents = tournirResponses.map { dto -> Tournir in - Tournir(id: UUID(uuidString: dto.id) ?? UUID(), title: dto.title ?? "Unnamed", description: dto.description ?? "", sport: dto.sport ?? "Chess", type_group: TypeTournir.fromString(dto.typeGroup ?? ""), type_tournir: TypeIsTeam.fromString(dto.typeTournament ?? ""), start_time: Date(), created_at: Date(), entry_cost: Double(dto.entryCost ?? 0), is_team_based: true, place: dto.place ?? "", max_participants: Int(dto.maxParticipants ?? 0), organizer_id: UUID(), requirements: Requirements()) + Tournir(id: UUID(uuidString: dto.id) ?? UUID(), title: dto.title ?? "Unnamed", description: dto.description ?? "", sport: dto.sport ?? "Chess", type_group: TypeTournir.fromString(dto.typeGroup ?? ""), type_tournir: TypeIsTeam.fromString(dto.typeTournament ?? ""), start_time: Date(), created_at: Date(), entry_cost: Double(dto.entryCost ?? 0), is_team_based: true, place: dto.place ?? "", max_participants: Int(dto.maxParticipants ?? 0), organizer_id: UUID(uuidString: dto.organizedId ?? "") ?? UUID(), requirements: Requirements()) } return mappedEvents } @@ -63,6 +66,16 @@ class TournirsViewModel: ObservableObject { } } } + +// func tournirs4User() -> [Tournir] { +// let x: [Tournir] = [] +// return x +// } +// +// func tournirs4Admin() -> [Tournir] { +// let x: [Tournir] = tournirs.filter { $0.organizer_id == } +// return x +// } /* func loadMockTournirs() -> [Tournir] { let x = [ From 242a3fdf781a9dd2660da99226af206ea3dd3bc0 Mon Sep 17 00:00:00 2001 From: kituNew Date: Sat, 12 Jul 2025 20:50:55 +0300 Subject: [PATCH 2/2] Add Tourinr rope --- SportApp/HomeView.swift | 2 + SportApp/Registration/Model/User.swift | 7 + SportApp/Statistics/View/StatisticsView.swift | 79 +---- .../ViewModel/StatisticViewModel.swift | 2 +- SportApp/Tournirs/Model/Tournir.swift | 61 +++- SportApp/Tournirs/View/TournirMaker.swift | 2 +- SportApp/Tournirs/View/TournirsDetail.swift | 170 +++++++++-- .../ViewModel/ParticipantsViewModel.swift | 289 +++++++++++++++++- 8 files changed, 517 insertions(+), 95 deletions(-) diff --git a/SportApp/HomeView.swift b/SportApp/HomeView.swift index 7a597d1..c8f6f85 100644 --- a/SportApp/HomeView.swift +++ b/SportApp/HomeView.swift @@ -58,6 +58,7 @@ struct HomeView: View { .navigationDestination(for: Tournir.self) { detail in TournirsDetail() .environmentObject(coordinator) + .environmentObject(viewModelOfTournirs) } } .tabItem { @@ -71,6 +72,7 @@ struct HomeView: View { .navigationDestination(for: Tournir.self) { detail in TournirsDetail() .environmentObject(coordinator) + .environmentObject(viewModelOfTournirs) } } .tabItem { diff --git a/SportApp/Registration/Model/User.swift b/SportApp/Registration/Model/User.swift index 037e1d9..83e2c54 100644 --- a/SportApp/Registration/Model/User.swift +++ b/SportApp/Registration/Model/User.swift @@ -96,3 +96,10 @@ enum UserCodingKeys: String { case mmr case bio } + +struct PairAxoroms: Identifiable, Hashable, Codable { + var id = UUID() + var first: User + var second: User + var isFirstWinner: Bool? +} diff --git a/SportApp/Statistics/View/StatisticsView.swift b/SportApp/Statistics/View/StatisticsView.swift index ff494a9..c3325ba 100644 --- a/SportApp/Statistics/View/StatisticsView.swift +++ b/SportApp/Statistics/View/StatisticsView.swift @@ -19,70 +19,21 @@ struct StatisticsView: View { var body: some View { VStack { ScrollView { - VStack { - Text("openedRegistrationTournaments") - - ForEach(tournirs, id: \.id) { tournir in - if tournir.tournirInstaseState == .openedRegistrationTournaments { - TournirCell(tournir: tournir) - .padding(.horizontal, 16) - .padding(.vertical, 3) - .onTapGesture { - coordinator.tournirListPath = NavigationPath() - coordinator.currentTournir = tournir - coordinator.statListPath.append(tournir) - } - } - } - } - - VStack { - Text("closedRegistrationTournaments") - - ForEach(tournirs, id: \.id) { tournir in - if tournir.tournirInstaseState == .closedRegistrationTournaments { - TournirCell(tournir: tournir) - .padding(.horizontal, 16) - .padding(.vertical, 3) - .onTapGesture { - coordinator.tournirListPath = NavigationPath() - coordinator.currentTournir = tournir - coordinator.statListPath.append(tournir) - } - } - } - } - - VStack { - Text("ongoingTournaments") - - ForEach(tournirs, id: \.id) { tournir in - if tournir.tournirInstaseState == .ongoingTournaments { - TournirCell(tournir: tournir) - .padding(.horizontal, 16) - .padding(.vertical, 3) - .onTapGesture { - coordinator.tournirListPath = NavigationPath() - coordinator.currentTournir = tournir - coordinator.statListPath.append(tournir) - } - } - } - } - - VStack { - Text("pastTournament") - - ForEach(tournirs, id: \.id) { tournir in - if tournir.tournirInstaseState == .pastTournament { - TournirCell(tournir: tournir) - .padding(.horizontal, 16) - .padding(.vertical, 3) - .onTapGesture { - coordinator.tournirListPath = NavigationPath() - coordinator.currentTournir = tournir - coordinator.statListPath.append(tournir) - } + ForEach(TournirInstaseState.list, id: \.id) { state in + VStack { + Text(state.rawValue) + + ForEach(tournirs, id: \.id) { tournir in + if tournir.tournirInstanteState == state { + TournirCell(tournir: tournir) + .padding(.horizontal, 16) + .padding(.vertical, 3) + .onTapGesture { + coordinator.tournirListPath = NavigationPath() + coordinator.currentTournir = tournir + coordinator.statListPath.append(tournir) + } + } } } } diff --git a/SportApp/Statistics/ViewModel/StatisticViewModel.swift b/SportApp/Statistics/ViewModel/StatisticViewModel.swift index 7e4ad66..9842fcc 100644 --- a/SportApp/Statistics/ViewModel/StatisticViewModel.swift +++ b/SportApp/Statistics/ViewModel/StatisticViewModel.swift @@ -43,7 +43,7 @@ class StatisticViewModel: ObservableObject { organizer_id: UUID(), users: [user], requirements: Requirements(), - tournirInstaseState: .pastTournament + tournirInstaseState: .endedTournaments ), // 2. Турнир идёт diff --git a/SportApp/Tournirs/Model/Tournir.swift b/SportApp/Tournirs/Model/Tournir.swift index 1db1116..1ff9ce5 100644 --- a/SportApp/Tournirs/Model/Tournir.swift +++ b/SportApp/Tournirs/Model/Tournir.swift @@ -23,18 +23,65 @@ struct Tournir: Codable, Identifiable, Hashable { var organizer_id: UUID var users: [User] = [] var requirements: Requirements - var tournirInstaseState: TournirInstaseState = .openedRegistrationTournaments + var tournirInstanteState: TournirInstaseState + var currentMatch: Int = 0 + let matchs: Int + + init( + id: UUID = UUID(), + title: String, + description: String, + sport: String, + type_group: TypeTournir, + type_tournir: TypeIsTeam, + start_time: Date, + created_at: Date, + entry_cost: Double, + is_team_based: Bool, + place: String, + max_participants: Int, + organizer_id: UUID, + users: [User] = [], + requirements: Requirements, + tournirInstaseState: TournirInstaseState = .openedRegistrationTournaments + ) { + self.id = id + self.title = title + self.description = description + self.sport = sport + self.type_group = type_group + self.type_tournir = type_tournir + self.start_time = start_time + self.created_at = created_at + self.entry_cost = entry_cost + self.is_team_based = is_team_based + self.place = place + self.max_participants = max_participants + self.organizer_id = organizer_id + self.users = users + self.requirements = requirements + self.matchs = Int(ceil(log2(Double(max_participants)))) + self.tournirInstanteState = tournirInstaseState + + self.max_participants = nextPowerOfTwo(max_participants) + } + + func nextPowerOfTwo(_ number: Int) -> Int { + guard number > 0 else { return 1 } + let exponent = log2(Double(number - 1)).rounded(.up) + return Int(pow(2.0, exponent)) + } } enum TournirInstaseState: String, Codable, Identifiable, Hashable { - case pastTournament + case endedTournaments case ongoingTournaments case closedRegistrationTournaments case openedRegistrationTournaments var id: Int { switch self { - case .pastTournament: + case .endedTournaments: return 1 case .ongoingTournaments: return 2 @@ -52,11 +99,13 @@ enum TournirInstaseState: String, Codable, Identifiable, Hashable { case .closedRegistrationTournaments: return .ongoingTournaments case .ongoingTournaments: - return .pastTournament - case .pastTournament: - return .pastTournament + return .endedTournaments + case .endedTournaments: + return .endedTournaments } } + + static let list: [TournirInstaseState] = [.openedRegistrationTournaments, .closedRegistrationTournaments, .ongoingTournaments, .endedTournaments] } struct Requirements: Codable, Hashable { diff --git a/SportApp/Tournirs/View/TournirMaker.swift b/SportApp/Tournirs/View/TournirMaker.swift index 08620b7..347c13f 100644 --- a/SportApp/Tournirs/View/TournirMaker.swift +++ b/SportApp/Tournirs/View/TournirMaker.swift @@ -177,7 +177,7 @@ struct TournirMaker: View { Spacer() Button(action: { - let tourinr: Tournir = Tournir(id: UUID(), title: name, description: description, sport: Sport.toStringEng(Sport.allSports[selectedSport]), type_group: .olympic, type_tournir: .solo, start_time: combinedDateTime, created_at: Date(), entry_cost: Double(cost) ?? 0, is_team_based: true, place: "", max_participants: 10, organizer_id: coordinator.user.id, requirements: Requirements()) + let tourinr: Tournir = Tournir(id: UUID(), title: name, description: description, sport: Sport.toStringEng(Sport.allSports[selectedSport]), type_group: .olympic, type_tournir: .solo, start_time: combinedDateTime, created_at: Date(), entry_cost: Double(cost) ?? 0, is_team_based: true, place: "", max_participants: 16, organizer_id: coordinator.user.id, requirements: Requirements()) viewModel.tournirs.append(tourinr) viewModel.giveTournir(tournir: tourinr) coordinator.dismissSheet() diff --git a/SportApp/Tournirs/View/TournirsDetail.swift b/SportApp/Tournirs/View/TournirsDetail.swift index 5d3fd2c..95b64df 100644 --- a/SportApp/Tournirs/View/TournirsDetail.swift +++ b/SportApp/Tournirs/View/TournirsDetail.swift @@ -9,8 +9,12 @@ import SwiftUI struct TournirsDetail: View { @EnvironmentObject var coordinator: Coordinator + @EnvironmentObject var viewModel2: TournirsViewModel @StateObject var viewModel = ParticipantsViewModel() + @State var matchs: Int = 0 + @State var participants: [User] = [] + var body: some View { VStack { Text(coordinator.currentTournir!.title) @@ -69,20 +73,23 @@ struct TournirsDetail: View { .frame(height: 1) .padding(.horizontal, 12) - - List { - HStack { - Text("Участники") - .padding(.leading) - - Spacer() - } - ScrollView { - ForEach(viewModel.participants, id: \.id) { participant in - Text(participant.name) - .padding() + if coordinator.user.isAdmin == false { + List { + HStack { + Text("Участники") + .padding(.leading) + + Spacer() + } + ScrollView { + ForEach(viewModel.participants, id: \.id) { participant in + Text(participant.phio) + .padding() + } } } + } else { + scale } if coordinator.user.isAdmin == false { @@ -98,20 +105,143 @@ struct TournirsDetail: View { .padding() } } else { - Button(action: { - // сохранять в нормальное место - coordinator.currentTournir!.tournirInstaseState = TournirInstaseState.nextState(coordinator.currentTournir!.tournirInstaseState) - }, label: { - Text(coordinator.currentTournir!.tournirInstaseState.rawValue) + if coordinator.currentTournir!.tournirInstanteState == .endedTournaments { + Text(coordinator.currentTournir!.tournirInstanteState.rawValue) .padding() - }) + } else { + Button(action: { + // сохранять в нормальное место + guard var currentTournir = coordinator.currentTournir else { + return + } + + let newState = TournirInstaseState.nextState(currentTournir.tournirInstanteState) + + if let index = viewModel2.tournirs.firstIndex(of: currentTournir) { + viewModel2.tournirs[index].tournirInstanteState = newState + } + + currentTournir.tournirInstanteState = newState + coordinator.currentTournir = currentTournir + }, label: { + Text(coordinator.currentTournir!.tournirInstanteState.rawValue) + .padding() + }) + } } Spacer() } .navigationTitle("О соревновании") .onAppear() { - viewModel.loadParticipants(for: coordinator.currentTournir!.id.uuidString) + //viewModel.loadParticipants(for: coordinator.currentTournir!.id.uuidString) + matchs = coordinator.currentTournir!.matchs + viewModel.setParticipants() + viewModel.setAxoroms() + } + } + + var scale: some View { + Group { + if coordinator.currentTournir!.currentMatch <= matchs { + VStack { + HStack { + ForEach(0.. String { + switch number { + case 1: + return "Финал" + default: + return "1/\(number)" } - } } diff --git a/SportApp/Tournirs/ViewModel/ParticipantsViewModel.swift b/SportApp/Tournirs/ViewModel/ParticipantsViewModel.swift index e24efb9..900c2c5 100644 --- a/SportApp/Tournirs/ViewModel/ParticipantsViewModel.swift +++ b/SportApp/Tournirs/ViewModel/ParticipantsViewModel.swift @@ -10,7 +10,16 @@ import Combine @MainActor class ParticipantsViewModel: ObservableObject { - @Published var participants: [UserDTO] = [] + @Published var participants: [User] = [] + @Published var axoroms: [PairAxoroms] = [] + + var winner: User? { + if axoroms.count == 1 { + return axoroms[0].isFirstWinner! ? axoroms[0].first : axoroms[0].second + } else { + return nil + } + } func loadParticipants(for tournamentID: String) { Task { @@ -23,7 +32,7 @@ class ParticipantsViewModel: ObservableObject { } } - func fetchParticipants(tournamentID: String) async throws -> [UserDTO] { + func fetchParticipants(tournamentID: String) async throws -> [User] { let participantEndpoint = ParticipantEndpoint(tournamentID: tournamentID) let participantRequest = ParticipantRequest() @@ -33,7 +42,281 @@ class ParticipantsViewModel: ObservableObject { requestDTO: participantRequest ) - return participantResponses + return participantResponses.map { User(dto: $0) } + } + + func setParticipants() { + participants = [ + User( + id: UUID(), + phio: "Иванов Иван", + phone: "+79001234567", + password: "pass123", + email: "ivanov@example.com", + dateOfBirth: Date().addingTimeInterval(-25 * 365 * 86400), + age: 25, + sexIsMan: true, + weight: 75.5, + height: 180.0, + mmr: 1500.0, + bio: "Профессиональный игрок в CS:GO.", + isAdmin: false + ), + User( + id: UUID(), + phio: "Петров Петр", + phone: "+79007654321", + password: "pass123", + email: "petrov@example.com", + dateOfBirth: Date().addingTimeInterval(-28 * 365 * 86400), + age: 28, + sexIsMan: true, + weight: 80.0, + height: 185.0, + mmr: 1450.0, + bio: nil, + isAdmin: false + ), + User( + id: UUID(), + phio: "Смирнова Ольга", + phone: nil, + password: "pass123", + email: "smirnova@example.com", + dateOfBirth: Date().addingTimeInterval(-22 * 365 * 86400), + age: 22, + sexIsMan: false, + weight: 60.0, + height: 170.0, + mmr: 1300.0, + bio: "Любитель шахмат и настольных игр.", + isAdmin: false + ), + User( + id: UUID(), + phio: "Кузнецов Алексей", + phone: "+79012345678", + password: "pass123", + email: "kuznetsov@example.com", + dateOfBirth: Date().addingTimeInterval(-30 * 365 * 86400), + age: 30, + sexIsMan: true, + weight: 90.0, + height: 190.0, + mmr: 1600.0, + bio: "Мастер по Dota 2.", + isAdmin: false + ), + User( + id: UUID(), + phio: "Соколов Дмитрий", + phone: nil, + password: "pass123", + email: "sokolov@example.com", + dateOfBirth: Date().addingTimeInterval(-19 * 365 * 86400), + age: 19, + sexIsMan: true, + weight: 70.0, + height: 175.0, + mmr: 1200.0, + bio: nil, + isAdmin: false + ), + User( + id: UUID(), + phio: "Попова Екатерина", + phone: "+79011122334", + password: "pass123", + email: "popova@example.com", + dateOfBirth: Date().addingTimeInterval(-24 * 365 * 86400), + age: 24, + sexIsMan: false, + weight: 55.0, + height: 165.0, + mmr: 1350.0, + bio: "Увлекаюсь киберспортом и программированием.", + isAdmin: false + ), + User( + id: UUID(), + phio: "Васильев Максим", + phone: "+79022233445", + password: "pass123", + email: "vasilev@example.com", + dateOfBirth: Date().addingTimeInterval(-27 * 365 * 86400), + age: 27, + sexIsMan: true, + weight: 85.0, + height: 188.0, + mmr: 1400.0, + bio: nil, + isAdmin: false + ), + User( + id: UUID(), + phio: "Алексеева Наталья", + phone: nil, + password: "pass123", + email: "alekseeva@example.com", + dateOfBirth: Date().addingTimeInterval(-23 * 365 * 86400), + age: 23, + sexIsMan: false, + weight: 62.0, + height: 172.0, + mmr: 1250.0, + bio: nil, + isAdmin: false + ), + User( + id: UUID(), + phio: "Михайлов Андрей", + phone: "+79033344556", + password: "pass123", + email: "mikhailov@example.com", + dateOfBirth: Date().addingTimeInterval(-26 * 365 * 86400), + age: 26, + sexIsMan: true, + weight: 78.0, + height: 178.0, + mmr: 1550.0, + bio: "Профессиональный игрок в League of Legends.", + isAdmin: false + ), + User( + id: UUID(), + phio: "Новикова Татьяна", + phone: "+79044455667", + password: "pass123", + email: "novikova@example.com", + dateOfBirth: Date().addingTimeInterval(-21 * 365 * 86400), + age: 21, + sexIsMan: false, + weight: 58.0, + height: 168.0, + mmr: 1320.0, + bio: nil, + isAdmin: false + ), + User( + id: UUID(), + phio: "Федоров Сергей", + phone: nil, + password: "pass123", + email: "fedorov@example.com", + dateOfBirth: Date().addingTimeInterval(-29 * 365 * 86400), + age: 29, + sexIsMan: true, + weight: 82.0, + height: 183.0, + mmr: 1480.0, + bio: nil, + isAdmin: false + ), + User( + id: UUID(), + phio: "Егорова Полина", + phone: "+79055566778", + password: "pass123", + email: "egorova@example.com", + dateOfBirth: Date().addingTimeInterval(-20 * 365 * 86400), + age: 20, + sexIsMan: false, + weight: 57.0, + height: 167.0, + mmr: 1280.0, + bio: nil, + isAdmin: false + ), + User( + id: UUID(), + phio: "Беляев Роман", + phone: "+79066677889", + password: "pass123", + email: "belyaev@example.com", + dateOfBirth: Date().addingTimeInterval(-31 * 365 * 86400), + age: 31, + sexIsMan: true, + weight: 88.0, + height: 192.0, + mmr: 1620.0, + bio: "Опытный игрок в Apex Legends.", + isAdmin: false + ), + User( + id: UUID(), + phio: "Комарова Анна", + phone: nil, + password: "pass123", + email: "komarova@example.com", + dateOfBirth: Date().addingTimeInterval(-22 * 365 * 86400), + age: 22, + sexIsMan: false, + weight: 59.0, + height: 170.0, + mmr: 1310.0, + bio: nil, + isAdmin: false + ), + User( + id: UUID(), + phio: "Орлов Виталий", + phone: "+79077788990", + password: "pass123", + email: "orlov@example.com", + dateOfBirth: Date().addingTimeInterval(-33 * 365 * 86400), + age: 33, + sexIsMan: true, + weight: 92.0, + height: 195.0, + mmr: 1700.0, + bio: "Тренер по киберспорту.", + isAdmin: false + ), + User( + id: UUID(), + phio: "Лазарева Юлия", + phone: "+79088899001", + password: "pass123", + email: "lazareva@example.com", + dateOfBirth: Date().addingTimeInterval(-24 * 365 * 86400), + age: 24, + sexIsMan: false, + weight: 61.0, + height: 173.0, + mmr: 1370.0, + bio: nil, + isAdmin: false + ) + ] + } + + func setAxoroms() { + axoroms.removeAll() + + guard !participants.isEmpty else { return } + + for i in stride(from: 0, to: participants.count, by: 2) { + guard i + 1 < participants.count else { + break + } + let pair = PairAxoroms(first: participants[i], second: participants[i + 1]) + axoroms.append(pair) + } + } + + func remakeAxoroms() { + if axoroms.count == 1 { return } + var axoromss: [PairAxoroms] = [] + for i in stride(from: 0, to: axoroms.count, by: 2) { + guard i + 1 < axoroms.count else { + break + } + let first = axoroms[i].isFirstWinner! ? axoroms[i].first : axoroms[i].second + let second = axoroms[i+1].isFirstWinner! ? axoroms[i+1].first : axoroms[i+1].second + let pair = PairAxoroms(first: first, second: second) + axoromss.append(pair) + } + axoroms = axoromss } /* func giveParticipant(participant: Participant) {