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 7f0b0e8..7eb7a6d 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
@@ -4,22 +4,6 @@
type = "1"
version = "2.0">
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -260,38 +148,6 @@
landmarkType = "7">
-
-
-
-
-
-
-
-
-
-
-
-
+ startingLineNumber = "58"
+ endingLineNumber = "58"
+ landmarkName = "body"
+ landmarkType = "24">
diff --git a/Club_portal/Club Portal/Club Portal/Assets.xcassets/AppIcon.appiconset/Contents.json b/Club_portal/Club Portal/Club Portal/Assets.xcassets/AppIcon.appiconset/Contents.json
index 2305880..6623abc 100644
--- a/Club_portal/Club Portal/Club Portal/Assets.xcassets/AppIcon.appiconset/Contents.json
+++ b/Club_portal/Club Portal/Club Portal/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -1,6 +1,7 @@
{
"images" : [
{
+ "filename" : "WhatsApp Image 2024-11-29 at 00.34.09 1.jpeg",
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
@@ -12,6 +13,7 @@
"value" : "dark"
}
],
+ "filename" : "WhatsApp Image 2024-11-29 at 00.34.09 2.jpeg",
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
@@ -23,6 +25,7 @@
"value" : "tinted"
}
],
+ "filename" : "WhatsApp Image 2024-11-29 at 00.34.09.jpeg",
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
diff --git a/Club_portal/Club Portal/Club Portal/Assets.xcassets/AppIcon.appiconset/WhatsApp Image 2024-11-29 at 00.34.09 1.jpeg b/Club_portal/Club Portal/Club Portal/Assets.xcassets/AppIcon.appiconset/WhatsApp Image 2024-11-29 at 00.34.09 1.jpeg
new file mode 100644
index 0000000..6dd7c01
Binary files /dev/null and b/Club_portal/Club Portal/Club Portal/Assets.xcassets/AppIcon.appiconset/WhatsApp Image 2024-11-29 at 00.34.09 1.jpeg differ
diff --git a/Club_portal/Club Portal/Club Portal/Assets.xcassets/AppIcon.appiconset/WhatsApp Image 2024-11-29 at 00.34.09 2.jpeg b/Club_portal/Club Portal/Club Portal/Assets.xcassets/AppIcon.appiconset/WhatsApp Image 2024-11-29 at 00.34.09 2.jpeg
new file mode 100644
index 0000000..6dd7c01
Binary files /dev/null and b/Club_portal/Club Portal/Club Portal/Assets.xcassets/AppIcon.appiconset/WhatsApp Image 2024-11-29 at 00.34.09 2.jpeg differ
diff --git a/Club_portal/Club Portal/Club Portal/Assets.xcassets/AppIcon.appiconset/WhatsApp Image 2024-11-29 at 00.34.09.jpeg b/Club_portal/Club Portal/Club Portal/Assets.xcassets/AppIcon.appiconset/WhatsApp Image 2024-11-29 at 00.34.09.jpeg
new file mode 100644
index 0000000..6dd7c01
Binary files /dev/null and b/Club_portal/Club Portal/Club Portal/Assets.xcassets/AppIcon.appiconset/WhatsApp Image 2024-11-29 at 00.34.09.jpeg differ
diff --git a/Club_portal/Club Portal/Club Portal/Network/Api_Stuff/APIError.swift b/Club_portal/Club Portal/Club Portal/Network/Api_Stuff/APIError.swift
index 823afa4..55cde16 100644
--- a/Club_portal/Club Portal/Club Portal/Network/Api_Stuff/APIError.swift
+++ b/Club_portal/Club Portal/Club Portal/Network/Api_Stuff/APIError.swift
@@ -210,6 +210,98 @@ final class APIService {
}
}
+ func requestPlayers(_ endpoint: Endpoint) async throws -> Players? {
+ 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 = Players(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
+ }
+
+ 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 = CoacheList(JSONString: responseString) {
+ print(resp)
+ return resp
+ }
+ }
+
+ return nil
+
+
+ } catch {
+ throw APIError.decodingError(error)
+ }
+ }
+
// func requestWithAlamofire(_ endpoint: Endpoint, responseType: T.Type) async throws -> T {
// guard let urlRequest = endpoint.urlRequest else {
@@ -235,7 +327,7 @@ final class APIService {
// } else {
// print("Failed to convert response data to string")
// }
-//
+//
// } else {
// throw APIError.invalidResponse(response.response?.statusCode ?? 0, error.localizedDescription)
// }
diff --git a/Club_portal/Club Portal/Club Portal/Network/Api_Stuff/EndPoints/AvailabilityListEndpoint.swift b/Club_portal/Club Portal/Club Portal/Network/Api_Stuff/EndPoints/AvailabilityListEndpoint.swift
index 435dfdf..86139b8 100644
--- a/Club_portal/Club Portal/Club Portal/Network/Api_Stuff/EndPoints/AvailabilityListEndpoint.swift
+++ b/Club_portal/Club Portal/Club Portal/Network/Api_Stuff/EndPoints/AvailabilityListEndpoint.swift
@@ -8,7 +8,6 @@
import SwiftUI
struct AvailabilityListEndpoint: Endpoint {
- var urlQueryItems: [URLQueryItem] = []
var path: String {
"/v3/api/custom/courtmatchup/club/reservations/availability"
@@ -16,7 +15,19 @@ struct AvailabilityListEndpoint: Endpoint {
var method: HTTPMethod { .get }
-
+ let startDate: String
+ let endDate: String
+ let startTime: String
+ let endTime: String
+
+ var urlQueryItems: [URLQueryItem] {
+ [
+ URLQueryItem(name: "from_time", value: startTime),
+ URLQueryItem(name: "until_time", value: endTime),
+ URLQueryItem(name: "from_date", value: startDate),
+ URLQueryItem(name: "until_date", value: endDate)
+ ]
+ }
var customHeaders: [String: String]? { nil }
@@ -24,3 +35,51 @@ struct AvailabilityListEndpoint: Endpoint {
var isMultipart: Bool { false }
}
+
+
+struct GetPlayerListEndpoint: Endpoint {
+ var path: String {
+ "/v4/api/records/user?order=id,desc&filter=id,in,\(playerIds)&"
+ }
+
+ var method: HTTPMethod { .get }
+
+
+ var customHeaders: [String: String]? { nil }
+
+ var urlQueryItems: [URLQueryItem] {
+ [
+
+ ]
+ }
+ var playerIds = ""
+
+ var body: Data? { nil }
+
+ var isMultipart: Bool { false }
+}
+
+
+
+struct GetCoachListEndpoint: Endpoint {
+ var path: String {
+ "/v4/api/records/coach?join=user|user_id&order=id,desc&filter=id,in,\(playerIds)&"
+ }
+
+ var method: HTTPMethod { .get }
+
+
+ var customHeaders: [String: String]? { nil }
+
+ var urlQueryItems: [URLQueryItem] {
+ [
+
+ ]
+ }
+
+ var playerIds = ""
+
+ var body: Data? { nil }
+
+ var isMultipart: Bool { false }
+}
diff --git a/Club_portal/Club Portal/Club Portal/Network/Api_Stuff/EndPoints/ReservationListEndpoint.swift b/Club_portal/Club Portal/Club Portal/Network/Api_Stuff/EndPoints/ReservationListEndpoint.swift
index 1a22caa..a948cdd 100644
--- a/Club_portal/Club Portal/Club Portal/Network/Api_Stuff/EndPoints/ReservationListEndpoint.swift
+++ b/Club_portal/Club Portal/Club Portal/Network/Api_Stuff/EndPoints/ReservationListEndpoint.swift
@@ -9,7 +9,7 @@ import SwiftUI
struct ReservationListEndpoint: Endpoint {
var urlQueryItems: [URLQueryItem] = []
var path: String {
- "/v4/api/records/reservation?join=booking|booking_id&join=user|user_id&join=buddy|buddy_id&join=clubs|club_id&order=id,desc&filter=courtmatchup_booking.court_id,eq,163"
+ "/v4/api/records/reservation?join=booking|booking_id&join=user|user_id&join=buddy|buddy_id&join=clubs|club_id&order=id,desc&filter=courtmatchup_booking.court_id,eq,\(clubID)"
}
var method: HTTPMethod { .get }
diff --git a/Club_portal/Club Portal/Club Portal/Network/Api_Stuff/Response/AvailabliltyRsp.swift b/Club_portal/Club Portal/Club Portal/Network/Api_Stuff/Response/AvailabliltyRsp.swift
index 8d435ad..d3ec5ce 100644
--- a/Club_portal/Club Portal/Club Portal/Network/Api_Stuff/Response/AvailabliltyRsp.swift
+++ b/Club_portal/Club Portal/Club Portal/Network/Api_Stuff/Response/AvailabliltyRsp.swift
@@ -31,7 +31,7 @@ struct Coaches: Codable {
// MARK: - CoachesAvailable
struct CoachesAvailable: Codable , Identifiable, Hashable {
- let id: Int?
+ let id = UUID().uuidString
let coachID, userID: Int?
let bio, hourlyRate, availability, firstName: String?
let lastName, phone, email: String?
diff --git a/Club_portal/Club Portal/Club Portal/Network/Api_Stuff/Response/Model/CoacheList.swift b/Club_portal/Club Portal/Club Portal/Network/Api_Stuff/Response/Model/CoacheList.swift
new file mode 100644
index 0000000..a3b7d97
--- /dev/null
+++ b/Club_portal/Club Portal/Club Portal/Network/Api_Stuff/Response/Model/CoacheList.swift
@@ -0,0 +1,27 @@
+//
+// CoacheList.swift
+// Club Portal
+//
+// Created by Umer Tahir on 07/05/2025.
+//
+
+
+import Foundation
+import ObjectMapper
+
+class CoacheList : Mappable {
+ var list : [PlayerList]?
+
+ required init?(map: Map) {
+
+ }
+
+ func mapping(map: Map) {
+
+ list <- map["list"]
+ }
+
+}
+
+
+
diff --git a/Club_portal/Club Portal/Club Portal/Network/Api_Stuff/Response/Model/Players.swift b/Club_portal/Club Portal/Club Portal/Network/Api_Stuff/Response/Model/Players.swift
new file mode 100644
index 0000000..3d8faa6
--- /dev/null
+++ b/Club_portal/Club Portal/Club Portal/Network/Api_Stuff/Response/Model/Players.swift
@@ -0,0 +1,90 @@
+//
+// Players.swift
+// Club Portal
+//
+// Created by Umer Tahir on 07/05/2025.
+//
+
+
+//
+// Json4Swift.swift
+// Club Portal
+//
+// Created by Umer Tahir on 06/05/2025.
+//
+
+
+import Foundation
+import ObjectMapper
+
+class Players : Mappable {
+ var list : [UserMap]?
+
+ required init?(map: Map) {
+
+ }
+
+ func mapping(map: Map) {
+
+ list <- map["list"]
+ }
+
+}
+
+
+
+struct PlayerList : Mappable {
+ var id : Int?
+ var user_id : Int?
+ var name : String?
+ var club_id : Int?
+ var hourly_rate : String?
+ var status : Int?
+ var not_paid_through_platform : Int?
+ var completed : Int?
+ var type : Int?
+ var experience : String?
+ var surface_id : Int?
+ var bio : String?
+ var sport_id : String?
+ var availability : String?
+ var default_availability : String?
+ var week_specific_availability : String?
+ var account_details : String?
+ var photo : String?
+ var is_public : Int?
+ var create_at : String?
+ var update_at : String?
+ var user : UserMap?
+
+ init?(map: Map) {
+
+ }
+
+ mutating func mapping(map: Map) {
+
+ id <- map["id"]
+ user_id <- map["user_id"]
+ name <- map["name"]
+ club_id <- map["club_id"]
+ hourly_rate <- map["hourly_rate"]
+ status <- map["status"]
+ not_paid_through_platform <- map["not_paid_through_platform"]
+ completed <- map["completed"]
+ type <- map["type"]
+ experience <- map["experience"]
+ surface_id <- map["surface_id"]
+ bio <- map["bio"]
+ sport_id <- map["sport_id"]
+ availability <- map["availability"]
+ default_availability <- map["default_availability"]
+ week_specific_availability <- map["week_specific_availability"]
+ account_details <- map["account_details"]
+ photo <- map["photo"]
+ is_public <- map["is_public"]
+ create_at <- map["create_at"]
+ update_at <- map["update_at"]
+ user <- map["user"]
+ }
+
+}
diff --git a/Club_portal/Club Portal/Club Portal/UI/Availbility/AvailabilityCardView.swift b/Club_portal/Club Portal/Club Portal/UI/Availbility/AvailabilityCardView.swift
index a9db5ba..39e88cc 100644
--- a/Club_portal/Club Portal/Club Portal/UI/Availbility/AvailabilityCardView.swift
+++ b/Club_portal/Club Portal/Club Portal/UI/Availbility/AvailabilityCardView.swift
@@ -51,16 +51,16 @@ struct AvailabilityCardView: View {
Divider()
}
case .coaches:
- ForEach(coaches ?? [], id: \.self) { coach in
+ ForEach(self.coaches ?? [], id: \.id) { coach in
HStack {
ProfileImageView(imageUrl: coach.photo ?? "", size: 30)
- Text(coach.firstName ?? "")
+ Text("\(coach.firstName ?? "") \(coach.lastName ?? "") ")
Spacer()
Image("send")
.foregroundColor(.gray)
.onTapGesture {
- name = coach.firstName ?? ""
+ name = "\(coach.firstName ?? "") \(coach.lastName ?? "") "
email = coach.email ?? ""
phone = coach.phone ?? ""
showSheet.toggle()
@@ -70,10 +70,10 @@ struct AvailabilityCardView: View {
Divider()
}
case .staff:
- ForEach(self.stsff ?? [], id: \.self) { coach in
+ ForEach(self.stsff ?? [], id: \.id) { coach in
HStack {
ProfileImageView(imageUrl: coach.photo ?? "", size: 30)
- Text( coach.firstName ?? "")
+ Text("\(coach.firstName ?? "") \(coach.lastName ?? "") ")
Spacer()
Image("send")
.foregroundColor(.gray)
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 50766cb..6db8c29 100644
--- a/Club_portal/Club Portal/Club Portal/UI/Availbility/AvailabilityScreen.swift
+++ b/Club_portal/Club Portal/Club Portal/UI/Availbility/AvailabilityScreen.swift
@@ -13,13 +13,16 @@ struct AvailabilityScreen: View {
@Binding var presentSideMenu: Bool // Optional for side menu
@EnvironmentObject var viewModel : DashViewModel
-
+ @State var startDate: Date = Date()
+ @State var endDate: Date = Calendar.current.date(byAdding: .day, value: 1, to: Date()) ?? Date()
+ @State var startTime: Date = Date()
+ @State var endTime: Date = Calendar.current.date(byAdding: .hour, value: 1, to: Date()) ?? Date()
var body: some View {
VStack(spacing: 16) {
HeaderView(presentSideMenu: $presentSideMenu)
- DateSelectorView()
+ DateSelectorView(startDate: $startDate, endDate: $endDate, startTime: $startTime, endTime: $endTime)
TabSelectorView(selectedTab: $selectedTab)
ScrollView {
@@ -36,7 +39,14 @@ struct AvailabilityScreen: View {
}
.background(Color(.systemGray6).ignoresSafeArea())
.onAppear(){
- viewModel.getAvailability()
+ viewModel.getAvailability(startDate: startDate.toCustomString, endDate: endDate.toCustomString, startTime: startTime.toTimeString, endTime: endTime.toTimeString)
}
+ .onChange(of: startDate) { _ in
+ viewModel.getAvailability(startDate: startDate.toCustomString, endDate: endDate.toCustomString, startTime: startTime.toTimeString, endTime: endTime.toTimeString)
+ }
+ .onChange(of: endDate) { _ 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/Availbility/DateSelectorView.swift b/Club_portal/Club Portal/Club Portal/UI/Availbility/DateSelectorView.swift
index 22b54ea..dc8f9b5 100644
--- a/Club_portal/Club Portal/Club Portal/UI/Availbility/DateSelectorView.swift
+++ b/Club_portal/Club Portal/Club Portal/UI/Availbility/DateSelectorView.swift
@@ -9,11 +9,11 @@ import SwiftUI
struct DateSelectorView: View {
- @State private var startDate: Date = Date()
- @State private var endDate: Date = Calendar.current.date(byAdding: .day, value: 1, to: Date()) ?? Date()
+ @Binding var startDate: Date
+ @Binding var endDate: Date
- @State private var startTime: Date = Date()
- @State private var endTime: Date = Calendar.current.date(byAdding: .hour, value: 1, to: Date()) ?? Date()
+ @Binding var startTime: Date
+ @Binding var endTime: Date
var body: some View {
VStack(spacing: 12) {
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 2c72330..caa956b 100644
--- a/Club_portal/Club Portal/Club Portal/UI/DailySecheduler/DetailsTab.swift
+++ b/Club_portal/Club Portal/Club Portal/UI/DailySecheduler/DetailsTab.swift
@@ -25,19 +25,21 @@ struct DetailRow: View {
}
struct DetailsTab: View {
+ @EnvironmentObject var viewModel : DashViewModel
+
var body: some View {
ScrollView {
VStack(alignment: .leading, spacing: 16) {
Group {
- DetailRow(title: "Reservation date", value: "16 April, 2024")
- DetailRow(title: "Time", value: "9:00 AM – 10:00 AM")
- DetailRow(title: "Event type", value: "Lesson")
- DetailRow(title: "Sport/type", value: "Tennis • Indoors • Clay")
+ 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: "Repeating", value: "Every week")
DetailRow(title: "Space assigned", value: "Court #5")
- DetailRow(title: "Court fee", value: "$50.0")
+ DetailRow(title: "Court fee", value: "$\(viewModel.selectedReservation?.booking?.court_fee ?? "10")")
DetailRow(title: "Court fee status", value: "Paid")
- DetailRow(title: "Note", value: "{content.note}")
+ DetailRow(title: "Note", value: "\(viewModel.selectedReservation?.booking?.notes ?? "--")")
}
}
.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 c180c3d..8abaaea 100644
--- a/Club_portal/Club Portal/Club Portal/UI/DailySecheduler/PlayersTab.swift
+++ b/Club_portal/Club Portal/Club Portal/UI/DailySecheduler/PlayersTab.swift
@@ -8,6 +8,8 @@
import SwiftUI
struct PlayersTab: View {
+ @EnvironmentObject var viewModel : DashViewModel
+
var body: some View {
ScrollView {
VStack(alignment: .leading, spacing: 12) {
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 3e89262..18f19a6 100644
--- a/Club_portal/Club Portal/Club Portal/UI/DailySecheduler/ScheduleView.swift
+++ b/Club_portal/Club Portal/Club Portal/UI/DailySecheduler/ScheduleView.swift
@@ -9,7 +9,7 @@ struct ScheduleView: View {
// @State private var events: [ReservationList] = []
@Binding var presentSideMenu: Bool // Optional for side menu
@State var courtName = ""
- let hours = Array(4...20)
+ let hours = Array(0...23)
let calendar = Calendar.current
@EnvironmentObject var viewModel : DashViewModel
@@ -48,11 +48,49 @@ struct ScheduleView: View {
}
// Time Slots with Events
ScrollView {
- VStack(spacing: 0) {
- ForEach(hours, id: \.self) { hour in
- timeSlotRow(hour: hour)
-
+ ZStack(alignment: .topLeading) {
+ VStack(spacing: 0) {
+ ForEach(hours, id: \.self) { hour in
+ timeSlotRow(hour: hour)
+ }
}
+
+ ForEach(viewModel.dailyReservations.filter { event in
+ let selectedDateStr = DateFormatter.bookingDateFormatter.string(from: selectedDate)
+ return event.booking?.date == selectedDateStr
+ }, id: \.id) { event in
+ if let startTime = DateFormatter.timeOnlyFormatter.date(from: event.booking?.start_time ?? ""),
+ let endTime = DateFormatter.timeOnlyFormatter.date(from: event.booking?.end_time ?? "") {
+
+ let startHour = calendar.component(.hour, from: startTime)
+ let endHour = calendar.component(.hour, from: endTime)
+ let yOffset = CGFloat(startHour - hours.first!) * 70
+ let height = max(CGFloat(endHour - startHour), 1) * 70
+
+ VStack(alignment: .leading, spacing: 2) {
+ HStack {
+ Text(event.booking?.type ?? "- -")
+ .font(.subheadline)
+ .bold()
+ Spacer()
+ }
+ Text("\(event.booking!.start_time!.to12HourFormat()) - \(event.booking!.end_time!.to12HourFormat())")
+ .font(.caption)
+ .foregroundColor(.gray)
+ }
+ .padding(8)
+ .frame(height: height)
+ .background(Colorr.greenColor.opacity(0.2))
+ .cornerRadius(12)
+ .foregroundColor(Colorr.greenColor.opacity(0.8))
+ .offset(y: yOffset)
+ .onTapGesture {
+ viewModel.selectedReservation = event
+ self.navigationPaths.append("ReservationDetailsView")
+ }
+ }
+ }
+ .padding(.leading, 60) // leave space for time labels
}
.padding(.horizontal)
.padding(.bottom, 20)
@@ -202,14 +240,13 @@ struct ScheduleView: View {
HStack(alignment: .top) {
Text(timeLabel(for: hour))
.font(.caption)
- .frame(width: 50, alignment: .leading)
+ .frame(width: 55, alignment: .leading)
.padding(.top)
ZStack(alignment: .topLeading) {
Rectangle()
.fill(Color(.systemGray6))
.frame(height: 60)
- eventOverlay(for: hour)
}
}
@@ -242,23 +279,36 @@ struct ScheduleView: View {
}),
id: \.id
) { event in
- VStack(alignment: .leading, spacing: 2) {
- Text(event.booking?.type ?? "- -")
- .font(.subheadline)
- .bold()
- Text("\(event.booking!.start_time!.to12HourFormat()) - \(event.booking!.end_time!.to12HourFormat())")
- .font(.caption)
- .foregroundColor(.gray)
- }
- .padding(8)
- .background(Colorr.greenColor.opacity(0.2))
- .foregroundColor(Colorr.greenColor.opacity(0.8))
- .cornerRadius(12)
- .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
- .background(Color.clear)
- .onTapGesture {
- self.navigationPaths.append("ReservationDetailsView")
- }
+ let height: CGFloat = {
+ if let start = DateFormatter.timeOnlyFormatter.date(from: event.booking!.start_time!),
+ let end = DateFormatter.timeOnlyFormatter.date(from: event.booking!.end_time!) {
+ let startHour = calendar.component(.hour, from: start)
+ let endHour = calendar.component(.hour, from: end)
+ let duration = max(endHour - startHour, 1)
+ return CGFloat(duration) * 60
+ }
+ return 60
+ }()
+ Color.clear
+ .overlay(
+ VStack(alignment: .leading, spacing: 2) {
+ Text(event.booking?.type ?? "- -")
+ .font(.subheadline)
+ .bold()
+ Text("\(event.booking!.start_time!.to12HourFormat()) - \(event.booking!.end_time!.to12HourFormat())")
+ .font(.caption)
+ .foregroundColor(.gray)
+ }
+ .padding(8)
+ .frame(height: height)
+ .background(Colorr.greenColor.opacity(0.2))
+ .cornerRadius(12)
+ .foregroundColor(Colorr.greenColor.opacity(0.8))
+ .onTapGesture {
+ self.navigationPaths.append("ReservationDetailsView")
+ },
+ alignment: .topLeading
+ )
}
}
.padding(.top, 4)
@@ -345,6 +395,14 @@ extension DateFormatter {
}
}
+extension DateFormatter {
+ static var timeOnlyFormatter: DateFormatter {
+ let formatter = DateFormatter()
+ formatter.dateFormat = "HH:mm:ss"
+ return formatter
+ }
+}
+
extension String {
func to12HourFormat() -> String {
let inputFormatter = DateFormatter()
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 031f095..f73a4d1 100644
--- a/Club_portal/Club Portal/Club Portal/UI/Dashboard/AnalyticsView.swift
+++ b/Club_portal/Club Portal/Club Portal/UI/Dashboard/AnalyticsView.swift
@@ -19,9 +19,9 @@ struct AnalyticsView: View {
// SegmentedControlView()
ScrollView {
VStack(spacing: 15) {
-// PieChartCard(data: self.viewModel.statResponse?.revenueByModule ?? [])
+ PieChartCard(data: self.viewModel.statResponse?.revenueByModule ?? [])
// BarChartCard(data: self.viewModel.statResponse?.revenueHeatMap ?? [])
- PieChartCard1()
+ // PieChartCard1()
BarChartCard1()
}
.padding(.horizontal)
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 d1c3876..46c3e16 100644
--- a/Club_portal/Club Portal/Club Portal/UI/Dashboard/DashboardView.swift
+++ b/Club_portal/Club Portal/Club Portal/UI/Dashboard/DashboardView.swift
@@ -15,6 +15,7 @@ struct SummaryView: View {
@EnvironmentObject var viewModel : DashViewModel
@Binding var presentSideMenu: Bool // Optional for side menu
@State var filterCount = 0
+
var body: some View {
NavigationStack(path: $navigationPaths) {
VStack {
@@ -26,7 +27,7 @@ struct SummaryView: View {
if selectedTab == 0 {
FilterView(navigationPaths: $navigationPaths, filterCount: $filterCount)
- CourtUtilizationView()
+ CourtUtilizationView(utilization: "\(self.viewModel.statResponse?.courtUtilization.first?.utilization_percentage ?? 10.0)%" )
ScrollView {
LazyVStack(spacing: 10) {
@@ -54,9 +55,9 @@ struct SummaryView: View {
.navigationBarHidden(true)
.navigationDestination(for: String.self) { value in
if value == "FilterScreen" {
- FilterScreen(navigationPaths: $navigationPaths) { filter, filterCount in
+ FilterScreen(navigationPaths: $navigationPaths) { startDate, endDate, startTime, endTime, filterCount in
self.filterCount = filterCount
- viewModel.getStats(completion: {})
+ viewModel.getStats ( startDate: startDate, endDate: endDate, startTime: startTime, endTime: endTime, completion: {})
}
}
}
@@ -68,7 +69,7 @@ struct SummaryView: View {
}
}
// viewModel.getClubProfile()
- viewModel.getProfile()
+ // viewModel.getProfile()
}
@@ -83,16 +84,14 @@ struct HeaderView: View {
var body: some View {
HStack {
- Image(systemName: "person.circle.fill")
- .resizable()
- .frame(width: 32, height: 32)
- .onTapGesture {
+ ProfileImageView(imageUrl: AppSettings.clubLogo, size: 32)
+ .onTapGesture {
presentSideMenu.toggle()
}
Spacer()
- Text("The Royal Club")
+ Text(AppSettings.clubName)
.font(.headline)
Spacer()
@@ -199,13 +198,14 @@ struct FilterView: View {
// MARK: - Court Utilization
struct CourtUtilizationView: View {
+ @State var utilization: String
var body: some View {
HStack {
Text("Court utilization")
.font(.subheadline)
.bold()
Spacer()
- Text("66%")
+ Text(utilization)
.font(.subheadline)
.bold()
}
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 52fb97b..4432e64 100644
--- a/Club_portal/Club Portal/Club Portal/UI/Dashboard/FilterScreen.swift
+++ b/Club_portal/Club Portal/Club Portal/UI/Dashboard/FilterScreen.swift
@@ -18,7 +18,7 @@ struct FilterScreen: View {
@State private var selectedDuration: String = "This Day"
let durationOptions = ["This Day", "This Week", "This Month", "This Year"]
- @State var filterBlock : ((String, Int) ->Void)?
+ @State var filterBlock : ((String, String, String, String, Int) ->Void)?
var body: some View {
NavigationView {
@@ -89,7 +89,7 @@ struct FilterScreen: View {
print("Generated Filter Query: \(query)")
print("Filter count: \(filterCount)")
- self.filterBlock?(query, filterCount)
+ self.filterBlock?(startDateStr, endDateStr, startTimeStr, endTimeStr, filterCount)
self.navigationPaths.removeLast()
}
}
diff --git a/Club_portal/Club Portal/Club Portal/UI/OnBoarding/SigninView.swift b/Club_portal/Club Portal/Club Portal/UI/OnBoarding/SigninView.swift
index 932a4db..9dfa617 100644
--- a/Club_portal/Club Portal/Club Portal/UI/OnBoarding/SigninView.swift
+++ b/Club_portal/Club Portal/Club Portal/UI/OnBoarding/SigninView.swift
@@ -16,7 +16,8 @@ struct SigninView: View {
@State private var keepLoggedIn = false
@State private var showPassword = false
@EnvironmentObject var viewModel: AuthViewModel
-
+ @EnvironmentObject var dashViewModel : DashViewModel
+
var body: some View {
NavigationStack(path: $navigationPaths) {
ScrollView {
@@ -106,6 +107,7 @@ struct SigninView: View {
Button(action: {
viewModel.login {_ in
// Navigate to the Dashboard on successful login
+ dashViewModel.getProfile()
navigationPaths.removeAll()
navigationPaths.append("Dashboard")
}
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 51d8e77..5e0300a 100644
--- a/Club_portal/Club Portal/Club Portal/UI/SideMenu/SideMenuView.swift
+++ b/Club_portal/Club Portal/Club Portal/UI/SideMenu/SideMenuView.swift
@@ -138,12 +138,12 @@ struct SideMenuView: View {
}
- Text("John Smith")
+ Text("\(AppSettings.fName) \(AppSettings.lastName)")
.padding(.leading)
.font(.system(size: 18, weight: .bold))
.foregroundColor(.black)
- Text("John@gmail.com")
+ Text(AppSettings.email)
.padding(.leading)
.font(.system(size: 14, weight: .semibold))
.foregroundColor(.gray)
diff --git a/Club_portal/Club Portal/Club Portal/Utliz/Extensions.swift b/Club_portal/Club Portal/Club Portal/Utliz/Extensions.swift
index 9a89438..6c139fe 100644
--- a/Club_portal/Club Portal/Club Portal/Utliz/Extensions.swift
+++ b/Club_portal/Club Portal/Club Portal/Utliz/Extensions.swift
@@ -353,3 +353,18 @@ extension String {
return formatter.string(from: date)
}
}
+extension Date {
+ var toCustomString: String {
+ let formatter = DateFormatter()
+ formatter.dateFormat = "dd-MMM-yyyy"
+ return formatter.string(from: self)
+ }
+}
+
+extension Date {
+ var toTimeString: String {
+ let formatter = DateFormatter()
+ formatter.dateFormat = "hh:mm a"
+ return formatter.string(from: self)
+ }
+}
diff --git a/Club_portal/Club Portal/Club Portal/ViewModels/AuthViewModel1.swift b/Club_portal/Club Portal/Club Portal/ViewModels/AuthViewModel1.swift
index ec53ffb..f640464 100644
--- a/Club_portal/Club Portal/Club Portal/ViewModels/AuthViewModel1.swift
+++ b/Club_portal/Club Portal/Club Portal/ViewModels/AuthViewModel1.swift
@@ -71,7 +71,8 @@ class AuthViewModel: ObservableObject {
// Save the token and user ID for the session
AppSettings.token = response.token
AppSettings.userID = response.userId
- AppSettings.lastName
+
+
// Update login state
self.isLoggedIn = true
completion(true) // Proceed to Dashboard
diff --git a/Club_portal/Club Portal/Club Portal/ViewModels/DashViewModel.swift b/Club_portal/Club Portal/Club Portal/ViewModels/DashViewModel.swift
index 73e63f3..2292948 100644
--- a/Club_portal/Club Portal/Club Portal/ViewModels/DashViewModel.swift
+++ b/Club_portal/Club Portal/Club Portal/ViewModels/DashViewModel.swift
@@ -18,13 +18,21 @@ class DashViewModel: ObservableObject {
@Published var clubDetail : ClubDetailsResponse?
@Published var profile : ProfileDetailResponse?
@Published var sessionExpired = false
+ @Published var selectedReservation : ReservationList?
+ @Published var players : [UserMap]?
+ @Published var coaches : [PlayerList]?
- func getStats(completion: @escaping () -> ()) {
+ 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: "2024-10-25",
- endDate: "2024-11-25",
- startTime: "17:30:20",
- endTime: "19:20:30"
+ startDate: startDate!,
+ endDate: endDate!,
+ startTime: startTime!,
+ endTime: endTime!
)
Task {
@@ -60,8 +68,19 @@ class DashViewModel: ObservableObject {
}
}
- func getAvailability(){
- let endpoint = AvailabilityListEndpoint()
+ func getAvailability(
+ startDate: String? = "2024-10-25",
+ endDate: String? = "2024-11-25",
+ startTime: String? = "17:30:20",
+ endTime: String? = "19:20:30"
+ ){
+ let endpoint = AvailabilityListEndpoint(
+ startDate: startDate!,
+ endDate: endDate!,
+ startTime: startTime!,
+ endTime: endTime!
+
+ )
Task {
do {
let result = try await APIService.shared.request(endpoint, responseType: AvailabliltyRsp.self)
@@ -82,7 +101,11 @@ class DashViewModel: ObservableObject {
profile = result
AppSettings.clubId = result?.model?.club?.id ?? 0
AppSettings.clubName = result?.model?.club?.name ?? ""
- AppSettings.clubName = result?.model?.user?.email ?? ""
+ AppSettings.email = result?.model?.user?.email ?? ""
+ AppSettings.photo = result?.model?.user?.photo ?? ""
+ AppSettings.fName = result?.model?.user?.first_name ?? ""
+ AppSettings.lastName = result?.model?.user?.last_name ?? ""
+ AppSettings.clubLogo = result?.model?.club?.club_logo ?? ""
} catch {
print("Error fetching reservations:", error)
@@ -101,4 +124,29 @@ class DashViewModel: ObservableObject {
}
}
}
+
+
+ func getPlayers(playerIds: String){
+ let endpoint = GetPlayerListEndpoint(playerIds: playerIds)
+ Task {
+ do {
+ let result = try await APIService.shared.requestPlayers(endpoint)
+ players = result?.list ?? []
+ } catch {
+ print("Error fetching reservations:", error)
+ }
+ }
+ }
+
+ func getCoaches(playerIds: String){
+ let endpoint = GetCoachListEndpoint(playerIds: playerIds)
+ Task {
+ do {
+ let result = try await APIService.shared.requestCoach(endpoint)
+ coaches = result?.list
+ } catch {
+ print("Error fetching reservations:", error)
+ }
+ }
+ }
}