diff --git a/Club_portal/Club Portal/Club Portal.xcodeproj/project.pbxproj b/Club_portal/Club Portal/Club Portal.xcodeproj/project.pbxproj
index 27240df..54b38b2 100644
--- a/Club_portal/Club Portal/Club Portal.xcodeproj/project.pbxproj
+++ b/Club_portal/Club Portal/Club Portal.xcodeproj/project.pbxproj
@@ -267,7 +267,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_STYLE = Automatic;
- CURRENT_PROJECT_VERSION = 1;
+ CURRENT_PROJECT_VERSION = 3;
DEVELOPMENT_ASSET_PATHS = "\"Club Portal/Preview Content\"";
DEVELOPMENT_TEAM = BSGYUG5U29;
ENABLE_PREVIEWS = YES;
@@ -300,7 +300,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_STYLE = Automatic;
- CURRENT_PROJECT_VERSION = 1;
+ CURRENT_PROJECT_VERSION = 3;
DEVELOPMENT_ASSET_PATHS = "\"Club Portal/Preview Content\"";
DEVELOPMENT_TEAM = BSGYUG5U29;
ENABLE_PREVIEWS = YES;
diff --git a/Club_portal/Club Portal/Club Portal.xcodeproj/xcuserdata/umertahir.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/Club_portal/Club Portal/Club Portal.xcodeproj/xcuserdata/umertahir.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist
index 7eb7a6d..39e3c49 100644
--- a/Club_portal/Club Portal/Club Portal.xcodeproj/xcuserdata/umertahir.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist
+++ b/Club_portal/Club Portal/Club Portal.xcodeproj/xcuserdata/umertahir.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist
@@ -100,22 +100,6 @@
landmarkType = "24">
-
-
-
-
ClubStatisticsResponse? {
+ guard let request = endpoint.urlRequest else {
+ throw APIError.invalidURL
+ }
+
+ print("Request URL: \(request.url?.absoluteString ?? "Invalid URL")")
+
+ let (data, response) = try await URLSession.shared.data(for: request)
+
+ guard let httpResponse = response as? HTTPURLResponse else {
+ throw APIError.invalidResponse(0, "No HTTP response received")
+ }
+
+ print("HTTP Status Code: \(httpResponse.statusCode)")
+
+
+
+ if !(200..<300).contains(httpResponse.statusCode) {
+ if let errorResponse = try? JSONDecoder().decode(ErrorResponse.self, from: data) {
+ if errorResponse.message == "TOKEN_EXPIRED" {
+ // Perform logout
+ throw APIError.logout
+ }
+ throw APIError.serverError(errorResponse.message)
+ } else {
+ throw APIError.invalidResponse(httpResponse.statusCode, "Unknown server error")
+ }
+ }
+
+ do {
+ if let responseString = String(data: data, encoding: .utf8) {
+ print("Response: \(responseString)")
+ if let resp = ClubStatisticsResponse(JSONString: responseString) {
+ print(resp)
+ return resp
+ }
+ }
+
+ return nil
+
+
+ } catch {
+ throw APIError.decodingError(error)
+ }
+ }
+
func requestCoach(_ endpoint: Endpoint) async throws -> CoacheList? {
guard let request = endpoint.urlRequest else {
throw APIError.invalidURL
diff --git a/Club_portal/Club Portal/Club Portal/Network/Api_Stuff/Response/ClubStatisticsResponse.swift b/Club_portal/Club Portal/Club Portal/Network/Api_Stuff/Response/ClubStatisticsResponse.swift
index fcd15cd..e6585a4 100644
--- a/Club_portal/Club Portal/Club Portal/Network/Api_Stuff/Response/ClubStatisticsResponse.swift
+++ b/Club_portal/Club Portal/Club Portal/Network/Api_Stuff/Response/ClubStatisticsResponse.swift
@@ -4,65 +4,314 @@
//
// Created by Umer Tahir on 11/04/2025.
//
+import ObjectMapper
-struct ClubStatisticsResponse: Decodable {
- let error: Bool
- let model: ClubStatisticsModel
+class ClubStatisticsResponse : Mappable {
+ var error : Bool?
+ var model : ClubStatisticsModel?
+
+ required init?(map: Map) {
+
+ }
+
+ func mapping(map: Map) {
+
+ error <- map["error"]
+ model <- map["model"]
+ }
+
}
-struct ClubStatisticsModel: Decodable {
- let clinicReservations: [ReservationStats]
- let coachReservations: [ReservationStats]
- let totalReservations: [ReservationStats]
- let revenueHeatMap: [RevenueHeatMap]
- let revenueByDateRange: [RevenueByDateRange]
- let revenueByModule: [RevenueByModule]
- let buddyStatistics: [ReservationStats]
- let totalExpenses: [ExpenseStats]
- let totalRevenue: [RevenueStats]
- let totalProfit: [ProfitStats]
- let courtUtilization: [CourtUtilizationStats]
- let lessonReservations: [ReservationStats]
+class ClubStatisticsModel : Mappable {
+ var clinicReservations : [ClinicReservations]?
+ var coachReservations : [CoachReservations]?
+ var totalReservations : [TotalReservations]?
+ var revenueHeatMap : [RevenueHeatMap]?
+ var revenueByDateRange : [RevenueByDateRange]?
+ var revenueByModule : [RevenueByModule]?
+ var buddyStatistics : [BuddyStatistics]?
+ var totalExpenses : [TotalExpenses]?
+ var totalRevenue : [TotalRevenue]?
+ var totalProfit : [TotalProfit]?
+ var courtUtilization : [CourtUtilization]?
+ var lessonReservations : [LessonReservations]?
+ var revenueByDay : [RevenueByDay]?
+ var revenueBarChart : RevenueBarChart?
+
+ required init?(map: Map) {
+
+ }
+
+ func mapping(map: Map) {
+
+ clinicReservations <- map["clinicReservations"]
+ coachReservations <- map["coachReservations"]
+ totalReservations <- map["totalReservations"]
+ revenueHeatMap <- map["revenueHeatMap"]
+ revenueByDateRange <- map["revenueByDateRange"]
+ revenueByModule <- map["revenueByModule"]
+ buddyStatistics <- map["buddyStatistics"]
+ totalExpenses <- map["totalExpenses"]
+ totalRevenue <- map["totalRevenue"]
+ totalProfit <- map["totalProfit"]
+ courtUtilization <- map["courtUtilization"]
+ lessonReservations <- map["lessonReservations"]
+ revenueByDay <- map["revenueByDay"]
+ revenueBarChart <- map["revenueBarChart"]
+ }
+
}
-struct ReservationStats: Decodable {
- let total_hours: Double?
- let total_revenue: Double?
+class BuddyStatistics : Mappable {
+ var total_hours : String?
+ var total_revenue : String?
+
+ required init?(map: Map) {
+
+ }
+
+ func mapping(map: Map) {
+
+ total_hours <- map["total_hours"]
+ total_revenue <- map["total_revenue"]
+ }
+
}
-struct RevenueHeatMap: Decodable {
- let date: String
- let total_revenue: Double
+class ClinicReservations : Mappable {
+ var total_hours : String?
+ var total_revenue : String?
+
+ required init?(map: Map) {
+
+ }
+
+ func mapping(map: Map) {
+
+ total_hours <- map["total_hours"]
+ total_revenue <- map["total_revenue"]
+ }
+
}
-struct RevenueByDateRange: Decodable {
- // Add fields if needed
+
+
+class CoachReservations : Mappable {
+ var total_hours : String?
+ var total_revenue : String?
+
+ required init?(map: Map) {
+
+ }
+
+ func mapping(map: Map) {
+
+ total_hours <- map["total_hours"]
+ total_revenue <- map["total_revenue"]
+ }
+
}
-struct RevenueByModule: Decodable {
- let module: String
- let revenue: Double?
+class CourtUtilization : Mappable {
+ var utilization_percentage : String?
+ var total_used_hours : String?
+ var total_available_hours : String?
+
+ required init?(map: Map) {
+
+ }
+
+ func mapping(map: Map) {
+
+ utilization_percentage <- map["utilization_percentage"]
+ total_used_hours <- map["total_used_hours"]
+ total_available_hours <- map["total_available_hours"]
+ }
+
}
-struct ExpenseStats: Decodable {
- let total_hours: Double?
- let total_expense: Double?
+class BarData : Mappable {
+ var clinic : [Int]?
+ var coach : [Double]?
+
+ required init?(map: Map) {
+
+ }
+
+ func mapping(map: Map) {
+
+ clinic <- map["clinic"]
+ coach <- map["coach"]
+ }
+
}
-struct RevenueStats: Decodable {
- let total_hours: Double?
- let total_revenue: Double?
+class LessonReservations : Mappable {
+ var total_hours : String?
+ var total_revenue : String?
+
+ required init?(map: Map) {
+
+ }
+
+ func mapping(map: Map) {
+
+ total_hours <- map["total_hours"]
+ total_revenue <- map["total_revenue"]
+ }
+
}
-struct ProfitStats: Decodable {
- let total_profit: Double?
- let total_revenue: Double?
- let total_expense: Double?
+
+class RevenueBarChart : Mappable {
+ var days : [String]?
+ var data : BarData?
+
+ required init?(map: Map) {
+
+ }
+
+ func mapping(map: Map) {
+
+ days <- map["days"]
+ data <- map["data"]
+ }
+
}
-struct CourtUtilizationStats: Decodable {
- let utilization_percentage: Double?
- let total_used_hours: Double?
- let total_available_hours: Double?
-}
\ No newline at end of file
+class RevenueByDateRange : Mappable {
+ var date : String?
+ var clinic_revenue : String?
+ var coach_revenue : String?
+
+ required init?(map: Map) {
+
+ }
+
+ func mapping(map: Map) {
+
+ date <- map["date"]
+ clinic_revenue <- map["clinic_revenue"]
+ coach_revenue <- map["coach_revenue"]
+ }
+
+}
+
+class RevenueByDay : Mappable {
+ var day : String?
+ var module : String?
+ var revenue : Double?
+
+ required init?(map: Map) {
+
+ }
+
+ func mapping(map: Map) {
+
+ day <- map["day"]
+ module <- map["module"]
+ revenue <- map["revenue"]
+ }
+
+}
+
+
+class RevenueByModule : Mappable {
+ var module : String!
+ var revenue : Int!
+
+ required init?(map: Map) {
+
+ }
+
+ func mapping(map: Map) {
+
+ module <- map["module"]
+ revenue <- map["revenue"]
+ }
+
+}
+
+class RevenueHeatMap : Mappable {
+ var date : String?
+ var total_revenue : Double?
+
+ required init?(map: Map) {
+
+ }
+
+ func mapping(map: Map) {
+
+ date <- map["date"]
+ total_revenue <- map["total_revenue"]
+ }
+
+}
+
+class TotalExpenses : Mappable {
+ var total_hours : String?
+ var total_expense : String?
+
+ required init?(map: Map) {
+
+ }
+
+ func mapping(map: Map) {
+
+ total_hours <- map["total_hours"]
+ total_expense <- map["total_expense"]
+ }
+
+}
+
+class TotalProfit : Mappable {
+ var total_profit : Int?
+ var total_revenue : Double?
+ var total_expense : Double?
+
+ required init?(map: Map) {
+
+ }
+
+ func mapping(map: Map) {
+
+ total_profit <- map["total_profit"]
+ total_revenue <- map["total_revenue"]
+ total_expense <- map["total_expense"]
+ }
+
+}
+
+class TotalReservations : Mappable {
+ var total_hours : String?
+ var total_revenue : String?
+
+ required init?(map: Map) {
+
+ }
+
+ func mapping(map: Map) {
+
+ total_hours <- map["total_hours"]
+ total_revenue <- map["total_revenue"]
+ }
+
+}
+
+
+class TotalRevenue : Mappable {
+ var total_hours : String?
+ var total_revenue : String?
+
+ required init?(map: Map) {
+
+ }
+
+ func mapping(map: Map) {
+
+ total_hours <- map["total_hours"]
+ total_revenue <- map["total_revenue"]
+ }
+
+}
diff --git a/Club_portal/Club Portal/Club Portal/Network/Api_Stuff/Response/ReservationResponse.swift b/Club_portal/Club Portal/Club Portal/Network/Api_Stuff/Response/ReservationResponse.swift
index 13ef3a5..c743e1d 100644
--- a/Club_portal/Club Portal/Club Portal/Network/Api_Stuff/Response/ReservationResponse.swift
+++ b/Club_portal/Club Portal/Club Portal/Network/Api_Stuff/Response/ReservationResponse.swift
@@ -101,6 +101,7 @@ class Booking : Mappable, Identifiable {
var create_at : String?
var update_at : String?
var coach_ids : String?
+ var sport_name : String?
required init?(map: Map) {
@@ -144,6 +145,8 @@ class Booking : Mappable, Identifiable {
create_at <- map["create_at"]
update_at <- map["update_at"]
coach_ids <- map["coach_ids"]
+ sport_name <- map["sport_name"]
+
}
}
diff --git a/Club_portal/Club Portal/Club Portal/UI/Availbility/AvailabilityScreen.swift b/Club_portal/Club Portal/Club Portal/UI/Availbility/AvailabilityScreen.swift
index 6db8c29..4ad21b3 100644
--- a/Club_portal/Club Portal/Club Portal/UI/Availbility/AvailabilityScreen.swift
+++ b/Club_portal/Club Portal/Club Portal/UI/Availbility/AvailabilityScreen.swift
@@ -47,6 +47,12 @@ struct AvailabilityScreen: View {
.onChange(of: endDate) { _ in
viewModel.getAvailability(startDate: startDate.toCustomString, endDate: endDate.toCustomString, startTime: startTime.toTimeString, endTime: endTime.toTimeString)
}
+ .onChange(of: startTime) { _ in
+ viewModel.getAvailability(startDate: startDate.toCustomString, endDate: endDate.toCustomString, startTime: startTime.toTimeString, endTime: endTime.toTimeString)
+ }
+ .onChange(of: endTime) { _ in
+ viewModel.getAvailability(startDate: startDate.toCustomString, endDate: endDate.toCustomString, startTime: startTime.toTimeString, endTime: endTime.toTimeString)
+ }
}
}
diff --git a/Club_portal/Club Portal/Club Portal/UI/DailySecheduler/CoachesTab.swift b/Club_portal/Club Portal/Club Portal/UI/DailySecheduler/CoachesTab.swift
index a2af383..5831a5f 100644
--- a/Club_portal/Club Portal/Club Portal/UI/DailySecheduler/CoachesTab.swift
+++ b/Club_portal/Club Portal/Club Portal/UI/DailySecheduler/CoachesTab.swift
@@ -8,29 +8,47 @@
import SwiftUI
struct CoachesTab: View {
+ @EnvironmentObject var viewModel : DashViewModel
+
var body: some View {
ScrollView {
VStack(alignment: .leading, spacing: 12) {
- ForEach(0..<3, id: \.self) { _ in
- HStack(spacing: 12) {
- ProfileImageView(imageUrl: "", size: 40)
-
- VStack(alignment: .leading, spacing: 4) {
- Text("Steve Jobs")
- .font(.headline)
- Text("Hours playing: 9:00 AM – 10:00 AM")
- .font(.caption)
- .foregroundColor(.secondary)
+
+ if let players = viewModel.coaches {
+
+
+ ForEach(players, id: \.id) { index in
+ HStack(spacing: 12) {
+ ProfileImageView(imageUrl: index.user?.photo ?? "", size: 40)
+
+ VStack(alignment: .leading, spacing: 2) {
+ Text("\(index.user?.first_name ?? "") \(index.user?.last_name ?? "" )")
+ .font(.headline)
+
+
+
+ }
+
+ Spacer()
}
-
- Spacer()
+
+
}
+
+
.padding()
- .background(Color(.systemGray6))
- .cornerRadius(10)
+
+
+
}
}
.padding()
+ .onAppear(){
+ if let ids = viewModel.selectedReservation?.booking?.coach_ids?.toCommaSeparated {
+
+ viewModel.getCoaches(playerIds: ids)
+ }
+ }
}
}
}
diff --git a/Club_portal/Club Portal/Club Portal/UI/DailySecheduler/DetailsTab.swift b/Club_portal/Club Portal/Club Portal/UI/DailySecheduler/DetailsTab.swift
index caa956b..950356c 100644
--- a/Club_portal/Club Portal/Club Portal/UI/DailySecheduler/DetailsTab.swift
+++ b/Club_portal/Club Portal/Club Portal/UI/DailySecheduler/DetailsTab.swift
@@ -34,7 +34,7 @@ struct DetailsTab: View {
DetailRow(title: "Reservation date", value: viewModel.selectedReservation?.booking?.date ?? "16 April, 2024")
DetailRow(title: "Time", value: "\(viewModel.selectedReservation?.booking?.start_time ?? "16:00:00" ) - \(viewModel.selectedReservation?.booking?.end_time ?? "16:00:00" ) ")
DetailRow(title: "Event type", value: "Lesson")
- DetailRow(title: "Sport/type", value: "\(viewModel.selectedReservation?.booking?.sport_id ?? 1) • \(viewModel.selectedReservation?.booking?.type ?? "") • \(viewModel.selectedReservation?.booking?.subtype ?? "") ")
+ DetailRow(title: "Sport/type", value: "\(viewModel.selectedReservation?.booking?.sport_name ?? "") • \(viewModel.selectedReservation?.booking?.type ?? "") • \(viewModel.selectedReservation?.booking?.subtype ?? "") ")
DetailRow(title: "Repeating", value: "Every week")
DetailRow(title: "Space assigned", value: "Court #5")
DetailRow(title: "Court fee", value: "$\(viewModel.selectedReservation?.booking?.court_fee ?? "10")")
diff --git a/Club_portal/Club Portal/Club Portal/UI/DailySecheduler/FilterView1.swift b/Club_portal/Club Portal/Club Portal/UI/DailySecheduler/FilterView1.swift
index 4856164..c4a0e95 100644
--- a/Club_portal/Club Portal/Club Portal/UI/DailySecheduler/FilterView1.swift
+++ b/Club_portal/Club Portal/Club Portal/UI/DailySecheduler/FilterView1.swift
@@ -43,7 +43,7 @@ struct ScheduleFilterView: View {
}
.padding(.top, 10)
} label: {
- Text("Club")
+ Text("Courts")
.font(.headline)
}
.padding()
diff --git a/Club_portal/Club Portal/Club Portal/UI/DailySecheduler/PlayersTab.swift b/Club_portal/Club Portal/Club Portal/UI/DailySecheduler/PlayersTab.swift
index 8abaaea..16ff202 100644
--- a/Club_portal/Club Portal/Club Portal/UI/DailySecheduler/PlayersTab.swift
+++ b/Club_portal/Club Portal/Club Portal/UI/DailySecheduler/PlayersTab.swift
@@ -13,34 +13,44 @@ struct PlayersTab: View {
var body: some View {
ScrollView {
VStack(alignment: .leading, spacing: 12) {
- ForEach(0..<5, id: \.self) { index in
- HStack(spacing: 12) {
- ProfileImageView(imageUrl: "", size: 40)
-
- VStack(alignment: .leading, spacing: 2) {
- Text("Steve Jobs")
- .font(.headline)
-
- if index == 0 {
- Text("Guardian sign-in: Steve Jobs")
- .font(.caption)
- .foregroundColor(.gray)
+ if let players = viewModel.players {
+ ForEach(players, id: \.id) { index in
+ HStack(spacing: 12) {
+ ProfileImageView(imageUrl: index.photo, size: 40)
+
+ VStack(alignment: .leading, spacing: 2) {
+ Text("\(index.first_name ?? "") \(index.last_name ?? "" )")
+ .font(.headline)
+
+// if index == 0 {
+// Text("Guardian sign-in: Steve Jobs")
+// .font(.caption)
+// .foregroundColor(.gray)
+// }
}
+
+ Spacer()
+
+// if index == 0 {
+// Image(systemName: "checkmark.seal.fill")
+// .foregroundColor(.green)
+// }
}
-
- Spacer()
-
- if index == 0 {
- Image(systemName: "checkmark.seal.fill")
- .foregroundColor(.green)
- }
+ .padding()
+ .background(Color(.systemGray6))
+ .cornerRadius(10)
}
- .padding()
- .background(Color(.systemGray6))
- .cornerRadius(10)
}
}
.padding()
+ .onAppear(){
+ if let ids = viewModel.selectedReservation?.booking?.player_ids?.toCommaSeparated {
+
+ viewModel.getPlayers(playerIds: ids)
+ }
+ }
}
}
}
+
+
diff --git a/Club_portal/Club Portal/Club Portal/UI/DailySecheduler/ScheduleView.swift b/Club_portal/Club Portal/Club Portal/UI/DailySecheduler/ScheduleView.swift
index 18f19a6..f235e92 100644
--- a/Club_portal/Club Portal/Club Portal/UI/DailySecheduler/ScheduleView.swift
+++ b/Club_portal/Club Portal/Club Portal/UI/DailySecheduler/ScheduleView.swift
@@ -6,12 +6,15 @@ struct ScheduleView: View {
@State var navigationPaths: [String] = []
@State private var selectedDate = Date()
@State private var displayedMonth = Date()
- // @State private var events: [ReservationList] = []
+ // @State private var events: [ReservationList] = []
@Binding var presentSideMenu: Bool // Optional for side menu
@State var courtName = ""
let hours = Array(0...23)
let calendar = Calendar.current
+ @State private var selectedCourtName: String? = nil
+ @State private var selectedCourtId: Int? = nil
+
@EnvironmentObject var viewModel : DashViewModel
var body: some View {
@@ -43,7 +46,28 @@ struct ScheduleView: View {
HStack {
Spacer()
- Text(courtName)
+ if let clubs = self.viewModel.profile?.model?.courts {
+ ScrollView(.horizontal, showsIndicators: false) {
+ HStack(spacing: 16) {
+ ForEach(clubs, id: \.self) { court in
+ if let clubName = court.name {
+ Button(action: {
+ selectedCourtName = clubName
+ selectedCourtId = court.id
+ viewModel.getDailySched(clubId: court.id ?? 0)
+ }) {
+ Text(clubName)
+ .padding(5)
+ .background(selectedCourtName == clubName ? Colorr.themeBlueColor : Color.gray.opacity(0.2))
+ .foregroundColor(selectedCourtName == clubName ? .white : .primary)
+ .cornerRadius(8)
+ }
+ }
+ }
+ }
+ .padding(.horizontal)
+ }
+ }
Spacer()
}
// Time Slots with Events
@@ -221,6 +245,7 @@ struct ScheduleView: View {
}
.onTapGesture {
selectedDate = date
+ viewModel.getDailySched(clubId: selectedCourtId ?? 0)
}
.id(calendar.startOfDay(for: date))
}
diff --git a/Club_portal/Club Portal/Club Portal/UI/Dashboard/AnalyticsView.swift b/Club_portal/Club Portal/Club Portal/UI/Dashboard/AnalyticsView.swift
index f73a4d1..2c80e70 100644
--- a/Club_portal/Club Portal/Club Portal/UI/Dashboard/AnalyticsView.swift
+++ b/Club_portal/Club Portal/Club Portal/UI/Dashboard/AnalyticsView.swift
@@ -20,9 +20,9 @@ struct AnalyticsView: View {
ScrollView {
VStack(spacing: 15) {
PieChartCard(data: self.viewModel.statResponse?.revenueByModule ?? [])
-// BarChartCard(data: self.viewModel.statResponse?.revenueHeatMap ?? [])
+ BarChartCard(data: self.viewModel.statResponse?.revenueHeatMap ?? [])
// PieChartCard1()
- BarChartCard1()
+ // BarChartCard1()
}
.padding(.horizontal)
}
@@ -86,14 +86,14 @@ struct PieChartCard: View {
var body: some View {
let chartData = data.map {
- (name: $0.module, value: $0.revenue ?? 0, color: randomColor())
+ (name: $0.module, value: $0.revenue ?? 0, color: randomColor(module: $0.module))
}
CardView(title: "% made through each module") {
VStack {
HStack {
ForEach(chartData, id: \.name) { item in
- LegendView(color: item.color, text: item.name)
+ LegendView(color: item.color, text: item.name ?? "")
}
.padding(.top, 5)
}
@@ -111,9 +111,16 @@ struct PieChartCard: View {
}
}
- func randomColor() -> Color {
- let colors: [Color] = [Colorr.pieBlue, Colorr.pieGreen, .orange, .purple, .pink, .mint]
- return colors.randomElement() ?? .blue
+ func randomColor(module: String) -> Color {
+ switch(module){
+ case "Clinics":
+ return Colorr.pieBlue
+ case "Coaching":
+ return Colorr.greenColor
+ default:
+ return .orange
+
+ }
}
}
@@ -156,10 +163,10 @@ struct BarChartCard: View {
var body: some View {
let barData: [ModuleRevenue] = data.map {
- ModuleRevenue(day: $0.date.toWeekday()!, module: $0.date.toWeekday()!, value: $0.total_revenue )
+ ModuleRevenue(day: $0.date?.toWeekday()! ?? "", module: $0.date?.toWeekday() ?? "", value: $0.total_revenue ?? 0.0 )
}
- CardView(title: "Revenue from each module") {
+ CardView(title: "Revenue from each day") {
Chart(barData) { data in
BarMark(
x: .value("Day", data.day),
diff --git a/Club_portal/Club Portal/Club Portal/UI/Dashboard/DashboardView.swift b/Club_portal/Club Portal/Club Portal/UI/Dashboard/DashboardView.swift
index 46c3e16..1dd4f70 100644
--- a/Club_portal/Club Portal/Club Portal/UI/Dashboard/DashboardView.swift
+++ b/Club_portal/Club Portal/Club Portal/UI/Dashboard/DashboardView.swift
@@ -27,20 +27,52 @@ struct SummaryView: View {
if selectedTab == 0 {
FilterView(navigationPaths: $navigationPaths, filterCount: $filterCount)
- CourtUtilizationView(utilization: "\(self.viewModel.statResponse?.courtUtilization.first?.utilization_percentage ?? 10.0)%" )
+ CourtUtilizationView(utilization: "\(self.viewModel.statResponse?.courtUtilization?.first?.utilization_percentage ?? "10.0")%" )
ScrollView {
LazyVStack(spacing: 10) {
- ReservationCard(title: "Court reservations", hours: "\(self.viewModel.statResponse?.coachReservations.first?.total_hours ?? 0 )", revenue: "$\(self.viewModel.statResponse?.coachReservations.first?.total_revenue ?? 0 )")
- ReservationCard(title: "Clinic reservations", hours: "\(self.viewModel.statResponse?.clinicReservations.first?.total_hours ?? 0 )", revenue: "$\(self.viewModel.statResponse?.clinicReservations.first?.total_revenue ?? 0 )")
- ReservationCard(title: "Find a Buddy", hours: "\(self.viewModel.statResponse?.buddyStatistics.first?.total_hours ?? 0 )", revenue: "$\(self.viewModel.statResponse?.buddyStatistics.first?.total_revenue ?? 0 )")
- ReservationCard(title: "Lesson reservation", hours: "\(self.viewModel.statResponse?.lessonReservations.first?.total_hours ?? 0 )", revenue: "$\(self.viewModel.statResponse?.lessonReservations.first?.total_revenue ?? 0 )")
+ ReservationCard(
+ title: "Court reservations",
+ hours: String(format: "%.2f", self.viewModel.statResponse?.coachReservations?.first?.total_hours ?? 0),
+ revenue: "$" + String(format: "%.2f", self.viewModel.statResponse?.coachReservations?.first?.total_revenue ?? 0)
+ )
- ReservationCard(title: "Total expenses", hours: "\(self.viewModel.statResponse?.totalExpenses.first?.total_hours ?? 0 )", revenue:"$\(self.viewModel.statResponse?.totalExpenses.first?.total_expense ?? 0 )")
- ReservationCard(title: "Total profit", hours: "\(self.viewModel.statResponse?.totalProfit.first?.total_profit ?? 0 )", revenue: "$\(self.viewModel.statResponse?.totalProfit.first?.total_revenue ?? 0 )")
- ReservationCard(title: "Total revenue", hours: "\(self.viewModel.statResponse?.totalRevenue.first?.total_hours ?? 0 )", revenue: "$\(self.viewModel.statResponse?.totalRevenue.first?.total_revenue ?? 0 )")
+ ReservationCard(
+ title: "Clinic reservations",
+ hours: String(format: "%.2f", self.viewModel.statResponse?.clinicReservations?.first?.total_hours ?? 0),
+ revenue: "$" + String(format: "%.2f", self.viewModel.statResponse?.clinicReservations?.first?.total_revenue ?? 0)
+ )
+ ReservationCard(
+ title: "Find a Buddy",
+ hours: String(format: "%.2f", self.viewModel.statResponse?.buddyStatistics?.first?.total_hours ?? 0),
+ revenue: "$" + String(format: "%.2f", self.viewModel.statResponse?.buddyStatistics?.first?.total_revenue ?? 0)
+ )
+
+ ReservationCard(
+ title: "Lesson reservation",
+ hours: String(format: "%.2f", self.viewModel.statResponse?.lessonReservations?.first?.total_hours ?? 0),
+ revenue: "$" + String(format: "%.2f", self.viewModel.statResponse?.lessonReservations?.first?.total_revenue ?? 0)
+ )
+
+ ReservationCard(
+ title: "Total expenses",
+ hours: String(format: "%.2f", self.viewModel.statResponse?.totalExpenses?.first?.total_hours ?? 0),
+ revenue: "$" + String(format: "%.2f", self.viewModel.statResponse?.totalExpenses?.first?.total_expense ?? 0)
+ )
+
+ ReservationCard(
+ title: "Total profit",
+ hours: String(format: "%.2f", self.viewModel.statResponse?.totalProfit?.first?.total_profit ?? 0),
+ revenue: "$" + String(format: "%.2f", self.viewModel.statResponse?.totalProfit?.first?.total_revenue ?? 0)
+ )
+
+ ReservationCard(
+ title: "Total revenue",
+ hours: String(format: "%.2f", self.viewModel.statResponse?.totalRevenue?.first?.total_hours ?? 0),
+ revenue: "$" + String(format: "%.2f", self.viewModel.statResponse?.totalRevenue?.first?.total_revenue ?? 0)
+ )
}
.padding(.horizontal)
@@ -57,7 +89,7 @@ struct SummaryView: View {
if value == "FilterScreen" {
FilterScreen(navigationPaths: $navigationPaths) { startDate, endDate, startTime, endTime, filterCount in
self.filterCount = filterCount
- viewModel.getStats ( startDate: startDate, endDate: endDate, startTime: startTime, endTime: endTime, completion: {})
+ // viewModel.getStats ( startDate: startDate, endDate: endDate, startTime: startTime, endTime: endTime, completion: {})
}
}
}
@@ -109,7 +141,9 @@ struct HeaderView: View {
AppSettings.token = ""
}
- Button("Cancel", role: .cancel) { }
+ Button("Cancel", role: .cancel) {
+
+ }
}
}
}
diff --git a/Club_portal/Club Portal/Club Portal/UI/Dashboard/FilterScreen.swift b/Club_portal/Club Portal/Club Portal/UI/Dashboard/FilterScreen.swift
index 4432e64..0525c81 100644
--- a/Club_portal/Club Portal/Club Portal/UI/Dashboard/FilterScreen.swift
+++ b/Club_portal/Club Portal/Club Portal/UI/Dashboard/FilterScreen.swift
@@ -14,7 +14,8 @@ struct FilterScreen: View {
@State private var endTime: Date = Date()
@State private var startDate: Date = Date()
@State private var endDate: Date = Date()
-
+ @EnvironmentObject var viewModel : DashViewModel
+
@State private var selectedDuration: String = "This Day"
let durationOptions = ["This Day", "This Week", "This Month", "This Year"]
@@ -33,14 +34,14 @@ struct FilterScreen: View {
DatePicker("End Date", selection: $endDate, displayedComponents: .date)
}
- Section(header: Text("Duration")) {
- Picker("Duration", selection: $selectedDuration) {
- ForEach(durationOptions, id: \.self) { duration in
- Text(duration)
- }
- }
- .pickerStyle(MenuPickerStyle())
- }
+// Section(header: Text("Duration")) {
+// Picker("Duration", selection: $selectedDuration) {
+// ForEach(durationOptions, id: \.self) { duration in
+// Text(duration)
+// }
+// }
+// .pickerStyle(MenuPickerStyle())
+// }
Section {
Button(action: {
@@ -66,17 +67,17 @@ struct FilterScreen: View {
let timeFormatter = DateFormatter()
timeFormatter.dateFormat = "HH:mm:ss"
- let startDateStr = dateFormatter.string(from: startDate)
- let endDateStr = dateFormatter.string(from: endDate)
- let startTimeStr = timeFormatter.string(from: startTime)
- let endTimeStr = timeFormatter.string(from: endTime)
+ viewModel.dashStartDate = dateFormatter.string(from: startDate)
+ viewModel.dashEndDate = dateFormatter.string(from: endDate)
+ viewModel.dashStartTime = timeFormatter.string(from: startTime)
+ viewModel.dashEndTime = timeFormatter.string(from: endTime)
- let query = """
- start_date=\(startDateStr)&\
- end_date=\(endDateStr)&\
- start_time=\(startTimeStr)&\
- end_time=\(endTimeStr)
- """
+// let query = """
+// start_date=\(startDateStr)&\
+// end_date=\(endDateStr)&\
+// start_time=\(startTimeStr)&\
+// end_time=\(endTimeStr)
+// """
// 🔢 Count how many filters are applied
var filterCount = 0
@@ -86,10 +87,10 @@ struct FilterScreen: View {
if !Calendar.current.isDate(endTime, equalTo: Date(), toGranularity: .minute) { filterCount += 1 }
if selectedDuration != "This Day" { filterCount += 1 }
- print("Generated Filter Query: \(query)")
+ // print("Generated Filter Query: \(query)")
print("Filter count: \(filterCount)")
- self.filterBlock?(startDateStr, endDateStr, startTimeStr, endTimeStr, filterCount)
+ // self.filterBlock?(startDateStr, endDateStr, startTimeStr, endTimeStr, filterCount)
self.navigationPaths.removeLast()
}
}
diff --git a/Club_portal/Club Portal/Club Portal/UI/SideMenu/SideMenuView.swift b/Club_portal/Club Portal/Club Portal/UI/SideMenu/SideMenuView.swift
index 5e0300a..5b2eeef 100644
--- a/Club_portal/Club Portal/Club Portal/UI/SideMenu/SideMenuView.swift
+++ b/Club_portal/Club Portal/Club Portal/UI/SideMenu/SideMenuView.swift
@@ -61,6 +61,7 @@ struct SideMenuView: View {
AppSettings.token = ""
presentSideMenu = false
}
+
})
}
diff --git a/Club_portal/Club Portal/Club Portal/Utliz/Extensions.swift b/Club_portal/Club Portal/Club Portal/Utliz/Extensions.swift
index 6c139fe..fbe2c35 100644
--- a/Club_portal/Club Portal/Club Portal/Utliz/Extensions.swift
+++ b/Club_portal/Club Portal/Club Portal/Utliz/Extensions.swift
@@ -368,3 +368,13 @@ extension Date {
return formatter.string(from: self)
}
}
+extension String {
+ var toCommaSeparated: String {
+ self.trimmingCharacters(in: CharacterSet(charactersIn: "[]"))
+ .split(separator: ",")
+ .map { $0.trimmingCharacters(in: .whitespaces) }
+ .joined(separator: ",")
+ }
+}
+
+
diff --git a/Club_portal/Club Portal/Club Portal/ViewModels/DashViewModel.swift b/Club_portal/Club Portal/Club Portal/ViewModels/DashViewModel.swift
index 2292948..2f1f3c8 100644
--- a/Club_portal/Club Portal/Club Portal/ViewModels/DashViewModel.swift
+++ b/Club_portal/Club Portal/Club Portal/ViewModels/DashViewModel.swift
@@ -21,26 +21,29 @@ class DashViewModel: ObservableObject {
@Published var selectedReservation : ReservationList?
@Published var players : [UserMap]?
@Published var coaches : [PlayerList]?
+
+ @Published var dashStartDate : String = "2025-04-01"
+ @Published var dashEndDate : String = "2025-04-30"
+ @Published var dashStartTime : String = "08:30"
+ @Published var dashEndTime : String = "17:00"
func getStats(
- startDate: String? = "2024-10-25",
- endDate: String? = "2024-11-25",
- startTime: String? = "17:30:20",
- endTime: String? = "19:20:30",
+
completion: @escaping () -> ()) {
let endpoint = ClubStatisticsEndpoint(
- startDate: startDate!,
- endDate: endDate!,
- startTime: startTime!,
- endTime: endTime!
+ startDate: dashStartDate,
+ endDate: dashEndDate,
+ startTime: dashStartTime,
+ endTime: dashEndTime
)
Task {
do {
- let response: ClubStatisticsResponse = try await APIService.shared.request(endpoint, responseType: ClubStatisticsResponse.self)
- print("Statistics fetched: \(response.model)")
- if !response.error {
- self.statResponse = response.model
+ let result = try await APIService.shared.requestStatics(endpoint)
+ print("Statistics fetched: \(result?.model)")
+
+ if !result!.error! {
+ self.statResponse = result?.model
}
else {
@@ -49,7 +52,7 @@ class DashViewModel: ObservableObject {
print("Error fetching stats: \(error)")
// if error.localizedDescription == "Token expired" {
sessionExpired = true
- completion()
+ completion()
// }
}
}