updated reservation details screen and filter on available screen
This commit is contained in:
+8
-168
@@ -4,22 +4,6 @@
|
||||
type = "1"
|
||||
version = "2.0">
|
||||
<Breakpoints>
|
||||
<BreakpointProxy
|
||||
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
|
||||
<BreakpointContent
|
||||
uuid = "D5681314-32C1-4998-BEBF-D4691C886BC2"
|
||||
shouldBeEnabled = "No"
|
||||
ignoreCount = "0"
|
||||
continueAfterRunningActions = "No"
|
||||
filePath = "Club Portal/ViewModels/DashViewModel.swift"
|
||||
startingColumnNumber = "9223372036854775807"
|
||||
endingColumnNumber = "9223372036854775807"
|
||||
startingLineNumber = "56"
|
||||
endingLineNumber = "56"
|
||||
landmarkName = "getDailySched(clubId:)"
|
||||
landmarkType = "7">
|
||||
</BreakpointContent>
|
||||
</BreakpointProxy>
|
||||
<BreakpointProxy
|
||||
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
|
||||
<BreakpointContent
|
||||
@@ -52,22 +36,6 @@
|
||||
landmarkType = "14">
|
||||
</BreakpointContent>
|
||||
</BreakpointProxy>
|
||||
<BreakpointProxy
|
||||
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
|
||||
<BreakpointContent
|
||||
uuid = "99B40998-6781-4339-ACB5-F75FF0BC38A7"
|
||||
shouldBeEnabled = "No"
|
||||
ignoreCount = "0"
|
||||
continueAfterRunningActions = "No"
|
||||
filePath = "Club Portal/ViewModels/DashViewModel.swift"
|
||||
startingColumnNumber = "9223372036854775807"
|
||||
endingColumnNumber = "9223372036854775807"
|
||||
startingLineNumber = "69"
|
||||
endingLineNumber = "69"
|
||||
landmarkName = "getAvailability()"
|
||||
landmarkType = "7">
|
||||
</BreakpointContent>
|
||||
</BreakpointProxy>
|
||||
<BreakpointProxy
|
||||
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
|
||||
<BreakpointContent
|
||||
@@ -84,38 +52,6 @@
|
||||
landmarkType = "24">
|
||||
</BreakpointContent>
|
||||
</BreakpointProxy>
|
||||
<BreakpointProxy
|
||||
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
|
||||
<BreakpointContent
|
||||
uuid = "9DDEE31F-4183-4E3A-8568-5DBD8C04570A"
|
||||
shouldBeEnabled = "No"
|
||||
ignoreCount = "0"
|
||||
continueAfterRunningActions = "No"
|
||||
filePath = "Club Portal/ViewModels/DashViewModel.swift"
|
||||
startingColumnNumber = "9223372036854775807"
|
||||
endingColumnNumber = "9223372036854775807"
|
||||
startingLineNumber = "84"
|
||||
endingLineNumber = "84"
|
||||
landmarkName = "getProfile()"
|
||||
landmarkType = "7">
|
||||
</BreakpointContent>
|
||||
</BreakpointProxy>
|
||||
<BreakpointProxy
|
||||
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
|
||||
<BreakpointContent
|
||||
uuid = "6A2DA2F1-CA9B-4BCE-8ACA-D6D14E858CDA"
|
||||
shouldBeEnabled = "No"
|
||||
ignoreCount = "0"
|
||||
continueAfterRunningActions = "No"
|
||||
filePath = "Club Portal/ViewModels/DashViewModel.swift"
|
||||
startingColumnNumber = "9223372036854775807"
|
||||
endingColumnNumber = "9223372036854775807"
|
||||
startingLineNumber = "77"
|
||||
endingLineNumber = "77"
|
||||
landmarkName = "getProfile()"
|
||||
landmarkType = "7">
|
||||
</BreakpointContent>
|
||||
</BreakpointProxy>
|
||||
<BreakpointProxy
|
||||
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
|
||||
<BreakpointContent
|
||||
@@ -148,22 +84,6 @@
|
||||
landmarkType = "24">
|
||||
</BreakpointContent>
|
||||
</BreakpointProxy>
|
||||
<BreakpointProxy
|
||||
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
|
||||
<BreakpointContent
|
||||
uuid = "002CCE4B-DBB4-4BE0-82C0-03D8524CC2DA"
|
||||
shouldBeEnabled = "No"
|
||||
ignoreCount = "0"
|
||||
continueAfterRunningActions = "No"
|
||||
filePath = "Club Portal/ViewModels/DashViewModel.swift"
|
||||
startingColumnNumber = "9223372036854775807"
|
||||
endingColumnNumber = "9223372036854775807"
|
||||
startingLineNumber = "34"
|
||||
endingLineNumber = "34"
|
||||
landmarkName = "getStats(completion:)"
|
||||
landmarkType = "7">
|
||||
</BreakpointContent>
|
||||
</BreakpointProxy>
|
||||
<BreakpointProxy
|
||||
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
|
||||
<BreakpointContent
|
||||
@@ -180,38 +100,6 @@
|
||||
landmarkType = "24">
|
||||
</BreakpointContent>
|
||||
</BreakpointProxy>
|
||||
<BreakpointProxy
|
||||
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
|
||||
<BreakpointContent
|
||||
uuid = "C120EA6E-E899-43F2-92E6-438C52F26A83"
|
||||
shouldBeEnabled = "No"
|
||||
ignoreCount = "0"
|
||||
continueAfterRunningActions = "No"
|
||||
filePath = "Club Portal/ViewModels/DashViewModel.swift"
|
||||
startingColumnNumber = "9223372036854775807"
|
||||
endingColumnNumber = "9223372036854775807"
|
||||
startingLineNumber = "98"
|
||||
endingLineNumber = "98"
|
||||
landmarkName = "getClubProfile()"
|
||||
landmarkType = "7">
|
||||
</BreakpointContent>
|
||||
</BreakpointProxy>
|
||||
<BreakpointProxy
|
||||
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
|
||||
<BreakpointContent
|
||||
uuid = "1047CF2A-7B0C-44CA-AB60-933AFFF88D49"
|
||||
shouldBeEnabled = "No"
|
||||
ignoreCount = "0"
|
||||
continueAfterRunningActions = "No"
|
||||
filePath = "Club Portal/ViewModels/DashViewModel.swift"
|
||||
startingColumnNumber = "9223372036854775807"
|
||||
endingColumnNumber = "9223372036854775807"
|
||||
startingLineNumber = "41"
|
||||
endingLineNumber = "41"
|
||||
landmarkName = "getStats(completion:)"
|
||||
landmarkType = "7">
|
||||
</BreakpointContent>
|
||||
</BreakpointProxy>
|
||||
<BreakpointProxy
|
||||
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
|
||||
<BreakpointContent
|
||||
@@ -222,8 +110,8 @@
|
||||
filePath = "Club Portal/UI/Dashboard/DashboardView.swift"
|
||||
startingColumnNumber = "9223372036854775807"
|
||||
endingColumnNumber = "9223372036854775807"
|
||||
startingLineNumber = "65"
|
||||
endingLineNumber = "65"
|
||||
startingLineNumber = "66"
|
||||
endingLineNumber = "66"
|
||||
landmarkName = "body"
|
||||
landmarkType = "24">
|
||||
</BreakpointContent>
|
||||
@@ -260,38 +148,6 @@
|
||||
landmarkType = "7">
|
||||
</BreakpointContent>
|
||||
</BreakpointProxy>
|
||||
<BreakpointProxy
|
||||
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
|
||||
<BreakpointContent
|
||||
uuid = "9F0229B2-427C-4F86-BB4B-02C7B32822AA"
|
||||
shouldBeEnabled = "No"
|
||||
ignoreCount = "0"
|
||||
continueAfterRunningActions = "No"
|
||||
filePath = "Club Portal/Network/Api_Stuff/APIError.swift"
|
||||
startingColumnNumber = "9223372036854775807"
|
||||
endingColumnNumber = "9223372036854775807"
|
||||
startingLineNumber = "60"
|
||||
endingLineNumber = "60"
|
||||
landmarkName = "request(_:responseType:)"
|
||||
landmarkType = "7">
|
||||
</BreakpointContent>
|
||||
</BreakpointProxy>
|
||||
<BreakpointProxy
|
||||
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
|
||||
<BreakpointContent
|
||||
uuid = "D4FFED32-06EF-4269-A4CC-2E38B7C48467"
|
||||
shouldBeEnabled = "No"
|
||||
ignoreCount = "0"
|
||||
continueAfterRunningActions = "No"
|
||||
filePath = "Club Portal/Network/Api_Stuff/APIError.swift"
|
||||
startingColumnNumber = "9223372036854775807"
|
||||
endingColumnNumber = "9223372036854775807"
|
||||
startingLineNumber = "57"
|
||||
endingLineNumber = "57"
|
||||
landmarkName = "request(_:responseType:)"
|
||||
landmarkType = "7">
|
||||
</BreakpointContent>
|
||||
</BreakpointProxy>
|
||||
<BreakpointProxy
|
||||
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
|
||||
<BreakpointContent
|
||||
@@ -311,33 +167,17 @@
|
||||
<BreakpointProxy
|
||||
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
|
||||
<BreakpointContent
|
||||
uuid = "2F83466D-D868-4502-8601-E4770581F3EB"
|
||||
uuid = "C3CB7FC3-4DA4-47C4-88EE-3AB6FBEC9D3D"
|
||||
shouldBeEnabled = "Yes"
|
||||
ignoreCount = "0"
|
||||
continueAfterRunningActions = "No"
|
||||
filePath = "Club Portal/UI/DailySecheduler/ScheduleView.swift"
|
||||
filePath = "Club Portal/UI/Availbility/AvailabilityCardView.swift"
|
||||
startingColumnNumber = "9223372036854775807"
|
||||
endingColumnNumber = "9223372036854775807"
|
||||
startingLineNumber = "241"
|
||||
endingLineNumber = "241"
|
||||
landmarkName = "eventOverlay(for:)"
|
||||
landmarkType = "7">
|
||||
</BreakpointContent>
|
||||
</BreakpointProxy>
|
||||
<BreakpointProxy
|
||||
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
|
||||
<BreakpointContent
|
||||
uuid = "EDD74E11-A7AD-4B5F-919B-B2DEB378DD99"
|
||||
shouldBeEnabled = "Yes"
|
||||
ignoreCount = "0"
|
||||
continueAfterRunningActions = "No"
|
||||
filePath = "Club Portal/UI/DailySecheduler/ScheduleView.swift"
|
||||
startingColumnNumber = "9223372036854775807"
|
||||
endingColumnNumber = "9223372036854775807"
|
||||
startingLineNumber = "239"
|
||||
endingLineNumber = "239"
|
||||
landmarkName = "eventOverlay(for:)"
|
||||
landmarkType = "7">
|
||||
startingLineNumber = "58"
|
||||
endingLineNumber = "58"
|
||||
landmarkName = "body"
|
||||
landmarkType = "24">
|
||||
</BreakpointContent>
|
||||
</BreakpointProxy>
|
||||
</Breakpoints>
|
||||
|
||||
@@ -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"
|
||||
|
||||
BIN
Binary file not shown.
|
After Width: | Height: | Size: 92 KiB |
BIN
Binary file not shown.
|
After Width: | Height: | Size: 92 KiB |
BIN
Binary file not shown.
|
After Width: | Height: | Size: 92 KiB |
@@ -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<T: Decodable>(_ endpoint: Endpoint, responseType: T.Type) async throws -> T {
|
||||
// guard let urlRequest = endpoint.urlRequest else {
|
||||
|
||||
+60
-1
@@ -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 }
|
||||
}
|
||||
|
||||
+1
-1
@@ -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 }
|
||||
|
||||
+1
-1
@@ -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?
|
||||
|
||||
@@ -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"]
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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"]
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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: "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: "Tennis • Indoors • Clay")
|
||||
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()
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
import SwiftUI
|
||||
|
||||
struct PlayersTab: View {
|
||||
@EnvironmentObject var viewModel : DashViewModel
|
||||
|
||||
var body: some View {
|
||||
ScrollView {
|
||||
VStack(alignment: .leading, spacing: 12) {
|
||||
|
||||
@@ -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,12 +48,50 @@ struct ScheduleView: View {
|
||||
}
|
||||
// Time Slots with Events
|
||||
ScrollView {
|
||||
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,6 +279,18 @@ struct ScheduleView: View {
|
||||
}),
|
||||
id: \.id
|
||||
) { event in
|
||||
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)
|
||||
@@ -251,14 +300,15 @@ struct ScheduleView: View {
|
||||
.foregroundColor(.gray)
|
||||
}
|
||||
.padding(8)
|
||||
.frame(height: height)
|
||||
.background(Colorr.greenColor.opacity(0.2))
|
||||
.foregroundColor(Colorr.greenColor.opacity(0.8))
|
||||
.cornerRadius(12)
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
|
||||
.background(Color.clear)
|
||||
.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()
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
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()
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ 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) {
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user