From 88b392e5e2024f6690c812b09d41de06b1f05ff5 Mon Sep 17 00:00:00 2001 From: umer Date: Wed, 23 Apr 2025 20:14:15 +0500 Subject: [PATCH] updated code --- .../Club Portal.xcodeproj/project.pbxproj | 365 ++++++++++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/swiftpm/Package.resolved | 15 + .../xcdebugger/Breakpoints_v2.xcbkptlist | 280 +++++++++++ .../AccentColor.colorset/Contents.json | 11 + .../AppIcon.appiconset/Contents.json | 35 ++ .../Assets.xcassets/Color/Contents.json | 6 + .../Color/blue.colorset/Contents.json | 38 ++ .../Color/green.colorset/Contents.json | 38 ++ .../Club Portal/Assets.xcassets/Contents.json | 6 + .../Assets.xcassets/icons/Contents.json | 6 + .../icons/ball.imageset/Contents.json | 22 + .../icons/ball.imageset/Custom Icon.png | Bin 0 -> 21790 bytes .../icons/ball.imageset/Custom Icon@2x.png | Bin 0 -> 12085 bytes .../icons/cash.imageset/Contents.json | 12 + .../icons/cash.imageset/Icon (1).pdf | Bin 0 -> 4394 bytes .../icons/clock.imageset/Contents.json | 12 + .../icons/clock.imageset/clock.pdf | Bin 0 -> 4111 bytes .../icons/filter 1.imageset/Contents.json | 12 + .../icons/filter 1.imageset/Frame 11.pdf | Bin 0 -> 23852 bytes .../icons/filter.imageset/Contents.json | 21 + .../icons/filter.imageset/filter.png | Bin 0 -> 428 bytes .../Compact Button [1.0] (1).pdf | Bin 0 -> 15563 bytes .../icons/left_arrow.imageset/Contents.json | 12 + .../icons/lock.imageset/Contents.json | 21 + .../icons/lock.imageset/Custom Icon (1).png | Bin 0 -> 20935 bytes .../icons/logout.imageset/Contents.json | 12 + .../icons/logout.imageset/Icon-right.pdf | Bin 0 -> 4222 bytes .../Compact Button [1.0].pdf | Bin 0 -> 15563 bytes .../icons/right_arrow.imageset/Contents.json | 12 + .../icons/send.imageset/Contents.json | 12 + .../icons/send.imageset/Frame 4 (1).pdf | Bin 0 -> 4482 bytes .../icons/tab1.imageset/Contents.json | 12 + .../icons/tab1.imageset/Frame 595 (2).pdf | Bin 0 -> 5535 bytes .../icons/tab1_.imageset/Contents.json | 12 + .../icons/tab1_.imageset/Frame 595.pdf | Bin 0 -> 4305 bytes .../icons/tab2.imageset/Contents.json | 12 + .../calendar-clock, date-time.pdf | Bin 0 -> 4742 bytes .../icons/tab2_.imageset/Contents.json | 12 + .../calendar-clock, date-time (1).pdf | Bin 0 -> 4136 bytes .../icons/tab3.imageset/Contents.json | 12 + .../icons/tab3.imageset/Frame 595 (1).pdf | Bin 0 -> 4710 bytes .../icons/tab3_.imageset/Contents.json | 21 + .../icons/tab3_.imageset/Frame 595.png | Bin 0 -> 3369 bytes .../Network/Api_Stuff/APIConstants.swift | 36 ++ .../Network/Api_Stuff/APIError.swift | 122 +++++ .../EndPoints/AvailabilityListEndpoint.swift | 26 + .../EndPoints/ClubStatisticsEndpoint.swift | 73 +++ .../EndPoints/ForgotPasswordEndpoint.swift | 29 ++ .../Api_Stuff/EndPoints/LoginEndpoint.swift | 33 ++ .../EndPoints/ReservationListEndpoint.swift | 38 ++ .../EndPoints/ResetPasswordEndpoint.swift | 31 ++ .../Network/Api_Stuff/Endpoint.swift | 47 ++ .../Api_Stuff/Response/AvailabliltyRsp.swift | 444 ++++++++++++++++++ .../Response/ClubDetailsResponse.swift | 62 +++ .../Api_Stuff/Response/ClubResponse.swift | 200 ++++++++ .../Response/ClubStatisticsResponse.swift | 68 +++ .../Response/ForgotPasswordResponse.swift | 12 + .../Api_Stuff/Response/LoginResponse.swift | 31 ++ .../Api_Stuff/Response/ProfileRsp.swift | 332 +++++++++++++ .../Response/ReservationResponse.swift | 146 ++++++ .../DataModels/AppleLoginRequest.swift | 16 + .../Preview Assets.xcassets/Contents.json | 6 + .../UI/Availbility/AvailabilityCardView.swift | 112 +++++ .../UI/Availbility/AvailabilityScreen.swift | 42 ++ .../UI/Availbility/AvailabilityTab.swift | 31 ++ .../UI/Availbility/DateSelectorView.swift | 58 +++ .../UI/Availbility/HeaderView.swift | 24 + .../Sheet/ContactInfoBottomSheet.swift | 68 +++ .../UI/Availbility/TabSelectorView.swift | 67 +++ .../UI/DailySecheduler/CalendarKitView.swift | 35 ++ .../UI/DailySecheduler/CoachesTab.swift | 36 ++ .../UI/DailySecheduler/DetailsTab.swift | 52 ++ .../DailySecheduler/EventCalendarView.swift | 54 +++ .../UI/DailySecheduler/FilterView.swift | 101 ++++ .../UI/DailySecheduler/FilterView1.swift | 106 +++++ .../UI/DailySecheduler/PlayersTab.swift | 44 ++ .../ReservationDetailsView.swift | 34 ++ .../UI/DailySecheduler/ScheduleView.swift | 306 ++++++++++++ .../UI/Dashboard/AnalyticsView.swift | 241 ++++++++++ .../UI/Dashboard/DashboardView.swift | 280 +++++++++++ .../UI/Dashboard/FilterScreen.swift | 95 ++++ .../Club Portal/UI/Main/Club_PortalApp.swift | 23 + .../Club Portal/UI/Main/ContentView.swift | 24 + .../UI/OnBoarding/ForgotPasswordView.swift | 105 +++++ .../Club Portal/UI/OnBoarding/OtpView.swift | 159 +++++++ .../UI/OnBoarding/SetNewPasswordView.swift | 167 +++++++ .../UI/OnBoarding/SigninView.swift | 164 +++++++ .../Club Portal/UI/SideMenu/SideMenu.swift | 32 ++ .../UI/SideMenu/SideMenuView.swift | 206 ++++++++ .../Club Portal/UI/Tabbar/WelcomeView.swift | 107 +++++ .../Club Portal/Utliz/AppSettings.swift | 346 ++++++++++++++ .../Club Portal/Utliz/Colorr.swift | 57 +++ .../Club Portal/Utliz/CustomTextField.swift | 32 ++ .../Club Portal/Utliz/Extension.swift | 18 + .../Club Portal/Utliz/Extensions.swift | 355 ++++++++++++++ .../Club Portal/Utliz/LoadingView.swift | 21 + .../Club Portal/Utliz/ProfileImageView.swift | 36 ++ .../Club Portal/Utliz/SignInButton.swift | 40 ++ .../Club Portal/Utliz/ToastModifier.swift | 48 ++ .../ViewModels/AuthViewModel1.swift | 154 ++++++ .../ViewModels/CalendarEventStore.swift | 28 ++ .../ViewModels/DashViewModel.swift | 104 ++++ .../ViewModels/LoginViewModel.swift | 79 ++++ 104 files changed, 6879 insertions(+) create mode 100644 Club_portal/Club Portal/Club Portal.xcodeproj/project.pbxproj create mode 100644 Club_portal/Club Portal/Club Portal.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 Club_portal/Club Portal/Club Portal.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved create mode 100644 Club_portal/Club Portal/Club Portal.xcodeproj/xcuserdata/umertahir.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist create mode 100644 Club_portal/Club Portal/Club Portal/Assets.xcassets/AccentColor.colorset/Contents.json create mode 100644 Club_portal/Club Portal/Club Portal/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 Club_portal/Club Portal/Club Portal/Assets.xcassets/Color/Contents.json create mode 100644 Club_portal/Club Portal/Club Portal/Assets.xcassets/Color/blue.colorset/Contents.json create mode 100644 Club_portal/Club Portal/Club Portal/Assets.xcassets/Color/green.colorset/Contents.json create mode 100644 Club_portal/Club Portal/Club Portal/Assets.xcassets/Contents.json create mode 100644 Club_portal/Club Portal/Club Portal/Assets.xcassets/icons/Contents.json create mode 100644 Club_portal/Club Portal/Club Portal/Assets.xcassets/icons/ball.imageset/Contents.json create mode 100644 Club_portal/Club Portal/Club Portal/Assets.xcassets/icons/ball.imageset/Custom Icon.png create mode 100644 Club_portal/Club Portal/Club Portal/Assets.xcassets/icons/ball.imageset/Custom Icon@2x.png create mode 100644 Club_portal/Club Portal/Club Portal/Assets.xcassets/icons/cash.imageset/Contents.json create mode 100644 Club_portal/Club Portal/Club Portal/Assets.xcassets/icons/cash.imageset/Icon (1).pdf create mode 100644 Club_portal/Club Portal/Club Portal/Assets.xcassets/icons/clock.imageset/Contents.json create mode 100644 Club_portal/Club Portal/Club Portal/Assets.xcassets/icons/clock.imageset/clock.pdf create mode 100644 Club_portal/Club Portal/Club Portal/Assets.xcassets/icons/filter 1.imageset/Contents.json create mode 100644 Club_portal/Club Portal/Club Portal/Assets.xcassets/icons/filter 1.imageset/Frame 11.pdf create mode 100644 Club_portal/Club Portal/Club Portal/Assets.xcassets/icons/filter.imageset/Contents.json create mode 100644 Club_portal/Club Portal/Club Portal/Assets.xcassets/icons/filter.imageset/filter.png create mode 100644 Club_portal/Club Portal/Club Portal/Assets.xcassets/icons/left_arrow.imageset/Compact Button [1.0] (1).pdf create mode 100644 Club_portal/Club Portal/Club Portal/Assets.xcassets/icons/left_arrow.imageset/Contents.json create mode 100644 Club_portal/Club Portal/Club Portal/Assets.xcassets/icons/lock.imageset/Contents.json create mode 100644 Club_portal/Club Portal/Club Portal/Assets.xcassets/icons/lock.imageset/Custom Icon (1).png create mode 100644 Club_portal/Club Portal/Club Portal/Assets.xcassets/icons/logout.imageset/Contents.json create mode 100644 Club_portal/Club Portal/Club Portal/Assets.xcassets/icons/logout.imageset/Icon-right.pdf create mode 100644 Club_portal/Club Portal/Club Portal/Assets.xcassets/icons/right_arrow.imageset/Compact Button [1.0].pdf create mode 100644 Club_portal/Club Portal/Club Portal/Assets.xcassets/icons/right_arrow.imageset/Contents.json create mode 100644 Club_portal/Club Portal/Club Portal/Assets.xcassets/icons/send.imageset/Contents.json create mode 100644 Club_portal/Club Portal/Club Portal/Assets.xcassets/icons/send.imageset/Frame 4 (1).pdf create mode 100644 Club_portal/Club Portal/Club Portal/Assets.xcassets/icons/tab1.imageset/Contents.json create mode 100644 Club_portal/Club Portal/Club Portal/Assets.xcassets/icons/tab1.imageset/Frame 595 (2).pdf create mode 100644 Club_portal/Club Portal/Club Portal/Assets.xcassets/icons/tab1_.imageset/Contents.json create mode 100644 Club_portal/Club Portal/Club Portal/Assets.xcassets/icons/tab1_.imageset/Frame 595.pdf create mode 100644 Club_portal/Club Portal/Club Portal/Assets.xcassets/icons/tab2.imageset/Contents.json create mode 100644 Club_portal/Club Portal/Club Portal/Assets.xcassets/icons/tab2.imageset/calendar-clock, date-time.pdf create mode 100644 Club_portal/Club Portal/Club Portal/Assets.xcassets/icons/tab2_.imageset/Contents.json create mode 100644 Club_portal/Club Portal/Club Portal/Assets.xcassets/icons/tab2_.imageset/calendar-clock, date-time (1).pdf create mode 100644 Club_portal/Club Portal/Club Portal/Assets.xcassets/icons/tab3.imageset/Contents.json create mode 100644 Club_portal/Club Portal/Club Portal/Assets.xcassets/icons/tab3.imageset/Frame 595 (1).pdf create mode 100644 Club_portal/Club Portal/Club Portal/Assets.xcassets/icons/tab3_.imageset/Contents.json create mode 100644 Club_portal/Club Portal/Club Portal/Assets.xcassets/icons/tab3_.imageset/Frame 595.png create mode 100644 Club_portal/Club Portal/Club Portal/Network/Api_Stuff/APIConstants.swift create mode 100644 Club_portal/Club Portal/Club Portal/Network/Api_Stuff/APIError.swift create mode 100644 Club_portal/Club Portal/Club Portal/Network/Api_Stuff/EndPoints/AvailabilityListEndpoint.swift create mode 100644 Club_portal/Club Portal/Club Portal/Network/Api_Stuff/EndPoints/ClubStatisticsEndpoint.swift create mode 100644 Club_portal/Club Portal/Club Portal/Network/Api_Stuff/EndPoints/ForgotPasswordEndpoint.swift create mode 100644 Club_portal/Club Portal/Club Portal/Network/Api_Stuff/EndPoints/LoginEndpoint.swift create mode 100644 Club_portal/Club Portal/Club Portal/Network/Api_Stuff/EndPoints/ReservationListEndpoint.swift create mode 100644 Club_portal/Club Portal/Club Portal/Network/Api_Stuff/EndPoints/ResetPasswordEndpoint.swift create mode 100644 Club_portal/Club Portal/Club Portal/Network/Api_Stuff/Endpoint.swift create mode 100644 Club_portal/Club Portal/Club Portal/Network/Api_Stuff/Response/AvailabliltyRsp.swift create mode 100644 Club_portal/Club Portal/Club Portal/Network/Api_Stuff/Response/ClubDetailsResponse.swift create mode 100644 Club_portal/Club Portal/Club Portal/Network/Api_Stuff/Response/ClubResponse.swift create mode 100644 Club_portal/Club Portal/Club Portal/Network/Api_Stuff/Response/ClubStatisticsResponse.swift create mode 100644 Club_portal/Club Portal/Club Portal/Network/Api_Stuff/Response/ForgotPasswordResponse.swift create mode 100644 Club_portal/Club Portal/Club Portal/Network/Api_Stuff/Response/LoginResponse.swift create mode 100644 Club_portal/Club Portal/Club Portal/Network/Api_Stuff/Response/ProfileRsp.swift create mode 100644 Club_portal/Club Portal/Club Portal/Network/Api_Stuff/Response/ReservationResponse.swift create mode 100644 Club_portal/Club Portal/Club Portal/Network/DataModels/AppleLoginRequest.swift create mode 100644 Club_portal/Club Portal/Club Portal/Preview Content/Preview Assets.xcassets/Contents.json create mode 100644 Club_portal/Club Portal/Club Portal/UI/Availbility/AvailabilityCardView.swift create mode 100644 Club_portal/Club Portal/Club Portal/UI/Availbility/AvailabilityScreen.swift create mode 100644 Club_portal/Club Portal/Club Portal/UI/Availbility/AvailabilityTab.swift create mode 100644 Club_portal/Club Portal/Club Portal/UI/Availbility/DateSelectorView.swift create mode 100644 Club_portal/Club Portal/Club Portal/UI/Availbility/HeaderView.swift create mode 100644 Club_portal/Club Portal/Club Portal/UI/Availbility/Sheet/ContactInfoBottomSheet.swift create mode 100644 Club_portal/Club Portal/Club Portal/UI/Availbility/TabSelectorView.swift create mode 100644 Club_portal/Club Portal/Club Portal/UI/DailySecheduler/CalendarKitView.swift create mode 100644 Club_portal/Club Portal/Club Portal/UI/DailySecheduler/CoachesTab.swift create mode 100644 Club_portal/Club Portal/Club Portal/UI/DailySecheduler/DetailsTab.swift create mode 100644 Club_portal/Club Portal/Club Portal/UI/DailySecheduler/EventCalendarView.swift create mode 100644 Club_portal/Club Portal/Club Portal/UI/DailySecheduler/FilterView.swift create mode 100644 Club_portal/Club Portal/Club Portal/UI/DailySecheduler/FilterView1.swift create mode 100644 Club_portal/Club Portal/Club Portal/UI/DailySecheduler/PlayersTab.swift create mode 100644 Club_portal/Club Portal/Club Portal/UI/DailySecheduler/ReservationDetailsView.swift create mode 100644 Club_portal/Club Portal/Club Portal/UI/DailySecheduler/ScheduleView.swift create mode 100644 Club_portal/Club Portal/Club Portal/UI/Dashboard/AnalyticsView.swift create mode 100644 Club_portal/Club Portal/Club Portal/UI/Dashboard/DashboardView.swift create mode 100644 Club_portal/Club Portal/Club Portal/UI/Dashboard/FilterScreen.swift create mode 100644 Club_portal/Club Portal/Club Portal/UI/Main/Club_PortalApp.swift create mode 100644 Club_portal/Club Portal/Club Portal/UI/Main/ContentView.swift create mode 100644 Club_portal/Club Portal/Club Portal/UI/OnBoarding/ForgotPasswordView.swift create mode 100644 Club_portal/Club Portal/Club Portal/UI/OnBoarding/OtpView.swift create mode 100644 Club_portal/Club Portal/Club Portal/UI/OnBoarding/SetNewPasswordView.swift create mode 100644 Club_portal/Club Portal/Club Portal/UI/OnBoarding/SigninView.swift create mode 100644 Club_portal/Club Portal/Club Portal/UI/SideMenu/SideMenu.swift create mode 100644 Club_portal/Club Portal/Club Portal/UI/SideMenu/SideMenuView.swift create mode 100644 Club_portal/Club Portal/Club Portal/UI/Tabbar/WelcomeView.swift create mode 100644 Club_portal/Club Portal/Club Portal/Utliz/AppSettings.swift create mode 100644 Club_portal/Club Portal/Club Portal/Utliz/Colorr.swift create mode 100644 Club_portal/Club Portal/Club Portal/Utliz/CustomTextField.swift create mode 100644 Club_portal/Club Portal/Club Portal/Utliz/Extension.swift create mode 100644 Club_portal/Club Portal/Club Portal/Utliz/Extensions.swift create mode 100644 Club_portal/Club Portal/Club Portal/Utliz/LoadingView.swift create mode 100644 Club_portal/Club Portal/Club Portal/Utliz/ProfileImageView.swift create mode 100644 Club_portal/Club Portal/Club Portal/Utliz/SignInButton.swift create mode 100644 Club_portal/Club Portal/Club Portal/Utliz/ToastModifier.swift create mode 100644 Club_portal/Club Portal/Club Portal/ViewModels/AuthViewModel1.swift create mode 100644 Club_portal/Club Portal/Club Portal/ViewModels/CalendarEventStore.swift create mode 100644 Club_portal/Club Portal/Club Portal/ViewModels/DashViewModel.swift create mode 100644 Club_portal/Club Portal/Club Portal/ViewModels/LoginViewModel.swift diff --git a/Club_portal/Club Portal/Club Portal.xcodeproj/project.pbxproj b/Club_portal/Club Portal/Club Portal.xcodeproj/project.pbxproj new file mode 100644 index 0000000..19e74b6 --- /dev/null +++ b/Club_portal/Club Portal/Club Portal.xcodeproj/project.pbxproj @@ -0,0 +1,365 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 77; + objects = { + +/* Begin PBXBuildFile section */ + ACAC2DEB2D9F2C1900869E5C /* CalendarKit in Frameworks */ = {isa = PBXBuildFile; productRef = ACAC2DEA2D9F2C1900869E5C /* CalendarKit */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + ACAC2DCF2D9F271300869E5C /* Club Portal.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Club Portal.app"; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFileSystemSynchronizedRootGroup section */ + ACAC2DD12D9F271300869E5C /* Club Portal */ = { + isa = PBXFileSystemSynchronizedRootGroup; + path = "Club Portal"; + sourceTree = ""; + }; +/* End PBXFileSystemSynchronizedRootGroup section */ + +/* Begin PBXFrameworksBuildPhase section */ + ACAC2DCC2D9F271300869E5C /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ACAC2DEB2D9F2C1900869E5C /* CalendarKit in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + ACAC2DC62D9F271300869E5C = { + isa = PBXGroup; + children = ( + ACAC2DD12D9F271300869E5C /* Club Portal */, + ACAC2DD02D9F271300869E5C /* Products */, + ); + sourceTree = ""; + }; + ACAC2DD02D9F271300869E5C /* Products */ = { + isa = PBXGroup; + children = ( + ACAC2DCF2D9F271300869E5C /* Club Portal.app */, + ); + name = Products; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + ACAC2DCE2D9F271300869E5C /* Club Portal */ = { + isa = PBXNativeTarget; + buildConfigurationList = ACAC2DDD2D9F271500869E5C /* Build configuration list for PBXNativeTarget "Club Portal" */; + buildPhases = ( + ACAC2DCB2D9F271300869E5C /* Sources */, + ACAC2DCC2D9F271300869E5C /* Frameworks */, + ACAC2DCD2D9F271300869E5C /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + fileSystemSynchronizedGroups = ( + ACAC2DD12D9F271300869E5C /* Club Portal */, + ); + name = "Club Portal"; + packageProductDependencies = ( + ACAC2DEA2D9F2C1900869E5C /* CalendarKit */, + ); + productName = "Club Portal"; + productReference = ACAC2DCF2D9F271300869E5C /* Club Portal.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + ACAC2DC72D9F271300869E5C /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 1600; + LastUpgradeCheck = 1600; + TargetAttributes = { + ACAC2DCE2D9F271300869E5C = { + CreatedOnToolsVersion = 16.0; + }; + }; + }; + buildConfigurationList = ACAC2DCA2D9F271300869E5C /* Build configuration list for PBXProject "Club Portal" */; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = ACAC2DC62D9F271300869E5C; + minimizedProjectReferenceProxies = 1; + packageReferences = ( + ACAC2DE92D9F2C1900869E5C /* XCRemoteSwiftPackageReference "CalendarKit" */, + ); + preferredProjectObjectVersion = 77; + productRefGroup = ACAC2DD02D9F271300869E5C /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + ACAC2DCE2D9F271300869E5C /* Club Portal */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + ACAC2DCD2D9F271300869E5C /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + ACAC2DCB2D9F271300869E5C /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + ACAC2DDB2D9F271500869E5C /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 18.0; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + ACAC2DDC2D9F271500869E5C /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 18.0; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + ACAC2DDE2D9F271500869E5C /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"Club Portal/Preview Content\""; + DEVELOPMENT_TEAM = BSGYUG5U29; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = "com.app.Club-Portal"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + ACAC2DDF2D9F271500869E5C /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"Club Portal/Preview Content\""; + DEVELOPMENT_TEAM = BSGYUG5U29; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = "com.app.Club-Portal"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + ACAC2DCA2D9F271300869E5C /* Build configuration list for PBXProject "Club Portal" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + ACAC2DDB2D9F271500869E5C /* Debug */, + ACAC2DDC2D9F271500869E5C /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + ACAC2DDD2D9F271500869E5C /* Build configuration list for PBXNativeTarget "Club Portal" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + ACAC2DDE2D9F271500869E5C /* Debug */, + ACAC2DDF2D9F271500869E5C /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + +/* Begin XCRemoteSwiftPackageReference section */ + ACAC2DE92D9F2C1900869E5C /* XCRemoteSwiftPackageReference "CalendarKit" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/richardtop/CalendarKit"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 1.1.11; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + ACAC2DEA2D9F2C1900869E5C /* CalendarKit */ = { + isa = XCSwiftPackageProductDependency; + package = ACAC2DE92D9F2C1900869E5C /* XCRemoteSwiftPackageReference "CalendarKit" */; + productName = CalendarKit; + }; +/* End XCSwiftPackageProductDependency section */ + }; + rootObject = ACAC2DC72D9F271300869E5C /* Project object */; +} diff --git a/Club_portal/Club Portal/Club Portal.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Club_portal/Club Portal/Club Portal.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/Club_portal/Club Portal/Club Portal.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/Club_portal/Club Portal/Club Portal.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Club_portal/Club Portal/Club Portal.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved new file mode 100644 index 0000000..45d1f78 --- /dev/null +++ b/Club_portal/Club Portal/Club Portal.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -0,0 +1,15 @@ +{ + "originHash" : "67a1c5e835fc1360427d7e9a548368960eed766e429f7c9f11a95a0e99d682d9", + "pins" : [ + { + "identity" : "calendarkit", + "kind" : "remoteSourceControl", + "location" : "https://github.com/richardtop/CalendarKit", + "state" : { + "revision" : "89e02a1472e9ad815ae72b62fd15430144fd1c4c", + "version" : "1.1.11" + } + } + ], + "version" : 3 +} 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 new file mode 100644 index 0000000..19e5bda --- /dev/null +++ b/Club_portal/Club Portal/Club Portal.xcodeproj/xcuserdata/umertahir.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -0,0 +1,280 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Club_portal/Club Portal/Club Portal/Assets.xcassets/AccentColor.colorset/Contents.json b/Club_portal/Club Portal/Club Portal/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000..eb87897 --- /dev/null +++ b/Club_portal/Club Portal/Club Portal/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} 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 new file mode 100644 index 0000000..2305880 --- /dev/null +++ b/Club_portal/Club Portal/Club Portal/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,35 @@ +{ + "images" : [ + { + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "tinted" + } + ], + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Club_portal/Club Portal/Club Portal/Assets.xcassets/Color/Contents.json b/Club_portal/Club Portal/Club Portal/Assets.xcassets/Color/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/Club_portal/Club Portal/Club Portal/Assets.xcassets/Color/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Club_portal/Club Portal/Club Portal/Assets.xcassets/Color/blue.colorset/Contents.json b/Club_portal/Club Portal/Club Portal/Assets.xcassets/Color/blue.colorset/Contents.json new file mode 100644 index 0000000..fb61520 --- /dev/null +++ b/Club_portal/Club Portal/Club Portal/Assets.xcassets/Color/blue.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0x64", + "green" : "0x26", + "red" : "0x16" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0x64", + "green" : "0x26", + "red" : "0x16" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Club_portal/Club Portal/Club Portal/Assets.xcassets/Color/green.colorset/Contents.json b/Club_portal/Club Portal/Club Portal/Assets.xcassets/Color/green.colorset/Contents.json new file mode 100644 index 0000000..95e1e06 --- /dev/null +++ b/Club_portal/Club Portal/Club Portal/Assets.xcassets/Color/green.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0x48", + "green" : "0x64", + "red" : "0x17" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0x48", + "green" : "0x64", + "red" : "0x17" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Club_portal/Club Portal/Club Portal/Assets.xcassets/Contents.json b/Club_portal/Club Portal/Club Portal/Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/Club_portal/Club Portal/Club Portal/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Club_portal/Club Portal/Club Portal/Assets.xcassets/icons/Contents.json b/Club_portal/Club Portal/Club Portal/Assets.xcassets/icons/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/Club_portal/Club Portal/Club Portal/Assets.xcassets/icons/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Club_portal/Club Portal/Club Portal/Assets.xcassets/icons/ball.imageset/Contents.json b/Club_portal/Club Portal/Club Portal/Assets.xcassets/icons/ball.imageset/Contents.json new file mode 100644 index 0000000..a6874b6 --- /dev/null +++ b/Club_portal/Club Portal/Club Portal/Assets.xcassets/icons/ball.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "filename" : "Custom Icon.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Custom Icon@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Club_portal/Club Portal/Club Portal/Assets.xcassets/icons/ball.imageset/Custom Icon.png b/Club_portal/Club Portal/Club Portal/Assets.xcassets/icons/ball.imageset/Custom Icon.png new file mode 100644 index 0000000000000000000000000000000000000000..7410d5988689e6c437714aff3c9e84e124fa2ca1 GIT binary patch literal 21790 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 R>q`It002ovPDHLkV1fl&y9)pS literal 0 HcmV?d00001 diff --git a/Club_portal/Club Portal/Club Portal/Assets.xcassets/icons/ball.imageset/Custom Icon@2x.png b/Club_portal/Club Portal/Club Portal/Assets.xcassets/icons/ball.imageset/Custom Icon@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..f51125a6dbdf86775f04c2c985c3a22890b01aaf GIT binary patch literal 12085 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 literal 0 HcmV?d00001 diff --git a/Club_portal/Club Portal/Club Portal/Assets.xcassets/icons/cash.imageset/Contents.json b/Club_portal/Club Portal/Club Portal/Assets.xcassets/icons/cash.imageset/Contents.json new file mode 100644 index 0000000..8124221 --- /dev/null +++ b/Club_portal/Club Portal/Club Portal/Assets.xcassets/icons/cash.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "Icon (1).pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Club_portal/Club Portal/Club Portal/Assets.xcassets/icons/cash.imageset/Icon (1).pdf b/Club_portal/Club Portal/Club Portal/Assets.xcassets/icons/cash.imageset/Icon (1).pdf new file mode 100644 index 0000000000000000000000000000000000000000..2a59d27371983467241942eedd8b18e63098c694 GIT binary patch literal 4394 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 literal 0 HcmV?d00001 diff --git a/Club_portal/Club Portal/Club Portal/Assets.xcassets/icons/clock.imageset/Contents.json b/Club_portal/Club Portal/Club Portal/Assets.xcassets/icons/clock.imageset/Contents.json new file mode 100644 index 0000000..e899d63 --- /dev/null +++ b/Club_portal/Club Portal/Club Portal/Assets.xcassets/icons/clock.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "clock.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Club_portal/Club Portal/Club Portal/Assets.xcassets/icons/clock.imageset/clock.pdf b/Club_portal/Club Portal/Club Portal/Assets.xcassets/icons/clock.imageset/clock.pdf new file mode 100644 index 0000000000000000000000000000000000000000..f4b41852ed8ba2556db26c3d3b8934a708398695 GIT binary patch literal 4111 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 HTWbFS2Ph~x literal 0 HcmV?d00001 diff --git a/Club_portal/Club Portal/Club Portal/Assets.xcassets/icons/filter 1.imageset/Contents.json b/Club_portal/Club Portal/Club Portal/Assets.xcassets/icons/filter 1.imageset/Contents.json new file mode 100644 index 0000000..7d33427 --- /dev/null +++ b/Club_portal/Club Portal/Club Portal/Assets.xcassets/icons/filter 1.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "Frame 11.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Club_portal/Club Portal/Club Portal/Assets.xcassets/icons/filter 1.imageset/Frame 11.pdf b/Club_portal/Club Portal/Club Portal/Assets.xcassets/icons/filter 1.imageset/Frame 11.pdf new file mode 100644 index 0000000000000000000000000000000000000000..f69967205b4eed7ab4d8829c7b6a52be619ac2ac GIT binary patch literal 23852 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%0A~e6M5^<{B5frbQMzrsH#4N?M4PT9pq2 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 literal 0 HcmV?d00001 diff --git a/Club_portal/Club Portal/Club Portal/Assets.xcassets/icons/left_arrow.imageset/Contents.json b/Club_portal/Club Portal/Club Portal/Assets.xcassets/icons/left_arrow.imageset/Contents.json new file mode 100644 index 0000000..4d29721 --- /dev/null +++ b/Club_portal/Club Portal/Club Portal/Assets.xcassets/icons/left_arrow.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "Compact Button [1.0] (1).pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Club_portal/Club Portal/Club Portal/Assets.xcassets/icons/lock.imageset/Contents.json b/Club_portal/Club Portal/Club Portal/Assets.xcassets/icons/lock.imageset/Contents.json new file mode 100644 index 0000000..2636b8f --- /dev/null +++ b/Club_portal/Club Portal/Club Portal/Assets.xcassets/icons/lock.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Custom Icon (1).png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Club_portal/Club Portal/Club Portal/Assets.xcassets/icons/lock.imageset/Custom Icon (1).png b/Club_portal/Club Portal/Club Portal/Assets.xcassets/icons/lock.imageset/Custom Icon (1).png new file mode 100644 index 0000000000000000000000000000000000000000..1fc358eb9485af184c7ba50e3bf009e029fc372c GIT binary patch literal 20935 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 RA3GrdmxKWM_*Ac{{Rc*xTUh`A literal 0 HcmV?d00001 diff --git a/Club_portal/Club Portal/Club Portal/Assets.xcassets/icons/right_arrow.imageset/Compact Button [1.0].pdf b/Club_portal/Club Portal/Club Portal/Assets.xcassets/icons/right_arrow.imageset/Compact Button [1.0].pdf new file mode 100644 index 0000000000000000000000000000000000000000..c69e01fbb8546e04a3c6319584b3f2613724888d GIT binary patch literal 15563 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 literal 0 HcmV?d00001 diff --git a/Club_portal/Club Portal/Club Portal/Assets.xcassets/icons/right_arrow.imageset/Contents.json b/Club_portal/Club Portal/Club Portal/Assets.xcassets/icons/right_arrow.imageset/Contents.json new file mode 100644 index 0000000..97e6326 --- /dev/null +++ b/Club_portal/Club Portal/Club Portal/Assets.xcassets/icons/right_arrow.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "Compact Button [1.0].pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Club_portal/Club Portal/Club Portal/Assets.xcassets/icons/send.imageset/Contents.json b/Club_portal/Club Portal/Club Portal/Assets.xcassets/icons/send.imageset/Contents.json new file mode 100644 index 0000000..59f885f --- /dev/null +++ b/Club_portal/Club Portal/Club Portal/Assets.xcassets/icons/send.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "Frame 4 (1).pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Club_portal/Club Portal/Club Portal/Assets.xcassets/icons/send.imageset/Frame 4 (1).pdf b/Club_portal/Club Portal/Club Portal/Assets.xcassets/icons/send.imageset/Frame 4 (1).pdf new file mode 100644 index 0000000000000000000000000000000000000000..06c0b4cc9e622c972e4539701107fed372f261b8 GIT binary patch literal 4482 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 Vf*{?o-zy;@CJF`d@~Y{p{~yl8 literal 0 HcmV?d00001 diff --git a/Club_portal/Club Portal/Club Portal/Assets.xcassets/icons/tab1.imageset/Contents.json b/Club_portal/Club Portal/Club Portal/Assets.xcassets/icons/tab1.imageset/Contents.json new file mode 100644 index 0000000..813d5fd --- /dev/null +++ b/Club_portal/Club Portal/Club Portal/Assets.xcassets/icons/tab1.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "Frame 595 (2).pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Club_portal/Club Portal/Club Portal/Assets.xcassets/icons/tab1.imageset/Frame 595 (2).pdf b/Club_portal/Club Portal/Club Portal/Assets.xcassets/icons/tab1.imageset/Frame 595 (2).pdf new file mode 100644 index 0000000000000000000000000000000000000000..385d468e8cc01e458f71d5040105ef1bd1105b57 GIT binary patch literal 5535 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 literal 0 HcmV?d00001 diff --git a/Club_portal/Club Portal/Club Portal/Assets.xcassets/icons/tab1_.imageset/Contents.json b/Club_portal/Club Portal/Club Portal/Assets.xcassets/icons/tab1_.imageset/Contents.json new file mode 100644 index 0000000..020b3f9 --- /dev/null +++ b/Club_portal/Club Portal/Club Portal/Assets.xcassets/icons/tab1_.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "Frame 595.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Club_portal/Club Portal/Club Portal/Assets.xcassets/icons/tab1_.imageset/Frame 595.pdf b/Club_portal/Club Portal/Club Portal/Assets.xcassets/icons/tab1_.imageset/Frame 595.pdf new file mode 100644 index 0000000000000000000000000000000000000000..fcf8e2bc0930bde55c2fa7694d26ee42fc95f23e GIT binary patch literal 4305 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 S+|Nu%NWh^WUS3r_wf_N8)kg;a literal 0 HcmV?d00001 diff --git a/Club_portal/Club Portal/Club Portal/Assets.xcassets/icons/tab2_.imageset/Contents.json b/Club_portal/Club Portal/Club Portal/Assets.xcassets/icons/tab2_.imageset/Contents.json new file mode 100644 index 0000000..a44bfc3 --- /dev/null +++ b/Club_portal/Club Portal/Club Portal/Assets.xcassets/icons/tab2_.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "calendar-clock, date-time (1).pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Club_portal/Club Portal/Club Portal/Assets.xcassets/icons/tab2_.imageset/calendar-clock, date-time (1).pdf b/Club_portal/Club Portal/Club Portal/Assets.xcassets/icons/tab2_.imageset/calendar-clock, date-time (1).pdf new file mode 100644 index 0000000000000000000000000000000000000000..14430d2f243a46a51928870ede493a5bb337df52 GIT binary patch literal 4136 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 literal 0 HcmV?d00001 diff --git a/Club_portal/Club Portal/Club Portal/Assets.xcassets/icons/tab3_.imageset/Contents.json b/Club_portal/Club Portal/Club Portal/Assets.xcassets/icons/tab3_.imageset/Contents.json new file mode 100644 index 0000000..bfa0c83 --- /dev/null +++ b/Club_portal/Club Portal/Club Portal/Assets.xcassets/icons/tab3_.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Frame 595.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Club_portal/Club Portal/Club Portal/Assets.xcassets/icons/tab3_.imageset/Frame 595.png b/Club_portal/Club Portal/Club Portal/Assets.xcassets/icons/tab3_.imageset/Frame 595.png new file mode 100644 index 0000000000000000000000000000000000000000..f0762e2216c801c7611e25df8eb17db70d40be3c GIT binary patch literal 3369 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 literal 0 HcmV?d00001 diff --git a/Club_portal/Club Portal/Club Portal/Network/Api_Stuff/APIConstants.swift b/Club_portal/Club Portal/Club Portal/Network/Api_Stuff/APIConstants.swift new file mode 100644 index 0000000..e752b5f --- /dev/null +++ b/Club_portal/Club Portal/Club Portal/Network/Api_Stuff/APIConstants.swift @@ -0,0 +1,36 @@ +// +// APIConstants.swift +// Club Portal +// +// Created by Umer Tahir on 09/04/2025. +// + + +enum APIConstants { + static let baseURL = "https://courtmatchup.mkdlabs.com" + static let xProject = "Y291cnRtYXRjaHVwOmczbmp1OTlpZjR3enk4Z3V0bHEwYmUwZDI1Nzk1MTNsbTg=" + static var commonHeaders: [String: String] { + var headers = ["Content-Type": "application/json", + "x-project": "Y291cnRtYXRjaHVwOmczbmp1OTlpZjR3enk4Z3V0bHEwYmUwZDI1Nzk1MTNsbTg=" + ] + if let token = AuthToken.current { + headers["Authorization"] = "Bearer \(token)" + } + return headers + } +} + +struct AuthToken { + static var current: String? { + // Replace with Keychain token + return AppSettings.token + } +} + + +enum HTTPMethod: String { + case get = "GET" + case post = "POST" +} + + 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 new file mode 100644 index 0000000..7f5e094 --- /dev/null +++ b/Club_portal/Club Portal/Club Portal/Network/Api_Stuff/APIError.swift @@ -0,0 +1,122 @@ +import Foundation + +import SwiftUI + +enum APIError: Error, CustomStringConvertible { + case invalidURL + case invalidResponse(Int, String?) // Include status code and optional error message + case decodingError(Error) + case serverError(String) + case unknownError + case logout + + var description: String { + switch self { + case .invalidURL: + return "Invalid URL" + case .invalidResponse(let statusCode, let message): + return "Invalid response with status code: \(statusCode)\nMessage: \(message ?? "No additional message")" + case .decodingError(let error): + return "Decoding error: \(error.localizedDescription)" + case .serverError(let message): + return "Server error: \(message)" + case .unknownError: + return "An unknown error occurred" + case .logout: + return "Token expired" + } + } +} + +//struct Endpoint { +// let urlRequest: URLRequest? +// let isMultipart: Bool +// let body: Data? +//} + +final class APIService { + static let shared = APIService() + private init() {} + + func request(_ endpoint: Endpoint, responseType: T.Type) async throws -> T { + 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 let responseString = String(data: data, encoding: .utf8) { + print("Response: \(responseString)") + } else { + print("Failed to convert response data to string") + } + + 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 { + return try JSONDecoder().decode(T.self, from: data) + } catch { + throw APIError.decodingError(error) + } + } + + private func uploadMultipart(endpoint: Endpoint, responseType: T.Type) async throws -> T { + guard var request = endpoint.urlRequest else { throw APIError.invalidURL } + + let boundary = "Boundary-\(UUID().uuidString)" + request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type") + request.httpBody = endpoint.body + + 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 let responseString = String(data: data, encoding: .utf8) { + print("Response: \(responseString)") + } else { + print("Failed to convert response data to string") + } + + guard (200..<300).contains(httpResponse.statusCode) else { + if let errorResponse = try? JSONDecoder().decode(ErrorResponse.self, from: data) { + throw APIError.serverError(errorResponse.message) + } else { + throw APIError.invalidResponse(httpResponse.statusCode, "Unknown server error") + } + } + + do { + return try JSONDecoder().decode(T.self, from: data) + } catch { + throw APIError.decodingError(error) + } + } + + +} + +struct ErrorResponse: Codable { + let message: String +} 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 new file mode 100644 index 0000000..435dfdf --- /dev/null +++ b/Club_portal/Club Portal/Club Portal/Network/Api_Stuff/EndPoints/AvailabilityListEndpoint.swift @@ -0,0 +1,26 @@ +// +// AvailabilityListEndpoint.swift +// Club Portal +// +// Created by Umer Tahir on 15/04/2025. +// + +import SwiftUI + +struct AvailabilityListEndpoint: Endpoint { + var urlQueryItems: [URLQueryItem] = [] + + var path: String { + "/v3/api/custom/courtmatchup/club/reservations/availability" + } + + var method: HTTPMethod { .get } + + + + var customHeaders: [String: String]? { nil } + + var body: Data? { nil } + + var isMultipart: Bool { false } +} diff --git a/Club_portal/Club Portal/Club Portal/Network/Api_Stuff/EndPoints/ClubStatisticsEndpoint.swift b/Club_portal/Club Portal/Club Portal/Network/Api_Stuff/EndPoints/ClubStatisticsEndpoint.swift new file mode 100644 index 0000000..8382931 --- /dev/null +++ b/Club_portal/Club Portal/Club Portal/Network/Api_Stuff/EndPoints/ClubStatisticsEndpoint.swift @@ -0,0 +1,73 @@ +// +// ClubStatisticsEndpoint.swift +// Club Portal +// +// Created by Umer Tahir on 11/04/2025. +// +import SwiftUI + +struct ClubStatisticsEndpoint: Endpoint { + var path: String { + "/v3/api/custom/courtmatchup/club/statistics" + } + + var method: HTTPMethod { .get } + + let startDate: String + let endDate: String + let startTime: String + let endTime: String + + var customHeaders: [String: String]? { nil } + + var urlQueryItems: [URLQueryItem] { + [ + URLQueryItem(name: "start_date", value: startDate), + URLQueryItem(name: "end_date", value: endDate), + URLQueryItem(name: "start_time", value: startTime), + URLQueryItem(name: "end_time", value: endTime) + ] + } + + var body: Data? { nil } + + var isMultipart: Bool { false } +} +struct GetClubEndpoint: Endpoint { + var path: String { + "/v3/api/custom/courtmatchup/user/club/\(clubId)" + } + + var method: HTTPMethod { .get } + + let clubId: String + + var customHeaders: [String: String]? { nil } + + var urlQueryItems: [URLQueryItem] { [] } // no query items needed + + var body: Data? { nil } + + var isMultipart: Bool { false } +} + +struct GetprofileEndpoint: Endpoint { + var path: String { + "/v3/api/custom/courtmatchup/club/profile" + } + + var method: HTTPMethod { .get } + + + var customHeaders: [String: String]? { nil } + + var urlQueryItems: [URLQueryItem] { + [ + + ] + } + + var body: Data? { nil } + + var isMultipart: Bool { false } +} diff --git a/Club_portal/Club Portal/Club Portal/Network/Api_Stuff/EndPoints/ForgotPasswordEndpoint.swift b/Club_portal/Club Portal/Club Portal/Network/Api_Stuff/EndPoints/ForgotPasswordEndpoint.swift new file mode 100644 index 0000000..3341629 --- /dev/null +++ b/Club_portal/Club Portal/Club Portal/Network/Api_Stuff/EndPoints/ForgotPasswordEndpoint.swift @@ -0,0 +1,29 @@ +// +// ForgotPasswordEndpoint.swift +// Club Portal +// +// Created by Umer Tahir on 10/04/2025. +// + +import SwiftUI + +struct ForgotPasswordEndpoint: Endpoint { + var urlQueryItems: [URLQueryItem] = [] + + var path: String { "/v2/api/lambda/mobile/forgot" } + var method: HTTPMethod { .post } + + let email: String + let role: String + + var customHeaders: [String: String]? { nil } + + var body: Data? { + try? JSONEncoder().encode([ + "email": email, + "role": role + ]) + } + + var isMultipart: Bool { false } +} diff --git a/Club_portal/Club Portal/Club Portal/Network/Api_Stuff/EndPoints/LoginEndpoint.swift b/Club_portal/Club Portal/Club Portal/Network/Api_Stuff/EndPoints/LoginEndpoint.swift new file mode 100644 index 0000000..9703de7 --- /dev/null +++ b/Club_portal/Club Portal/Club Portal/Network/Api_Stuff/EndPoints/LoginEndpoint.swift @@ -0,0 +1,33 @@ +// +// LoginEndpoint.swift +// Club Portal +// +// Created by Umer Tahir on 09/04/2025. +// + +import SwiftUI + +struct LoginEndpoint: Endpoint { + var urlQueryItems: [URLQueryItem] = [] + + var path: String { "/v2/api/lambda/login" } + var method: HTTPMethod { .post } + + let email: String + let password: String + let role: String = "club" + let isRefresh: Bool + + var customHeaders: [String: String]? { nil } + + var body: Data? { + try? JSONEncoder().encode([ + "email": email, + "password": password, + "role": role, + "is_refresh": isRefresh ? "true" : "false" // Convert Bool to String + ]) + } + + 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 new file mode 100644 index 0000000..108887a --- /dev/null +++ b/Club_portal/Club Portal/Club Portal/Network/Api_Stuff/EndPoints/ReservationListEndpoint.swift @@ -0,0 +1,38 @@ +// +// ReservationListEndpoint.swift +// Club Portal +// +// Created by Umer Tahir on 12/04/2025. +// +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.date,cs,\(clubID)" + } + + var method: HTTPMethod { .get } + + //let date: String + let clubID : Int + +// var queryItems: [URLQueryItem]? { +// [ +// URLQueryItem(name: "join", value: "booking|booking_id"), +// URLQueryItem(name: "join", value: "user|user_id"), +// URLQueryItem(name: "join", value: "buddy|buddy_id"), +// URLQueryItem(name: "join", value: "clubs|club_id"), +// URLQueryItem(name: "order", value: "id,desc"), +// URLQueryItem(name: "filter", value: "courtmatchup_booking.date,cs,\(date)") +// ] +// } + + var customHeaders: [String: String]? { nil } + + var body: Data? { nil } + + var isMultipart: Bool { false } +} + diff --git a/Club_portal/Club Portal/Club Portal/Network/Api_Stuff/EndPoints/ResetPasswordEndpoint.swift b/Club_portal/Club Portal/Club Portal/Network/Api_Stuff/EndPoints/ResetPasswordEndpoint.swift new file mode 100644 index 0000000..4908a46 --- /dev/null +++ b/Club_portal/Club Portal/Club Portal/Network/Api_Stuff/EndPoints/ResetPasswordEndpoint.swift @@ -0,0 +1,31 @@ +// +// ResetPasswordEndpoint.swift +// Club Portal +// +// Created by Umer Tahir on 10/04/2025. +// + +import SwiftUI + +struct ResetPasswordEndpoint: Endpoint { + var urlQueryItems: [URLQueryItem] = [] + + var path: String { "/v2/api/lambda/mobile/reset" } + var method: HTTPMethod { .post } + + let email: String + let code: String + let password: String + + var customHeaders: [String: String]? { nil } + + var body: Data? { + try? JSONEncoder().encode([ + "email": email, + "code": code, + "password": password + ]) + } + + var isMultipart: Bool { false } +} diff --git a/Club_portal/Club Portal/Club Portal/Network/Api_Stuff/Endpoint.swift b/Club_portal/Club Portal/Club Portal/Network/Api_Stuff/Endpoint.swift new file mode 100644 index 0000000..4692659 --- /dev/null +++ b/Club_portal/Club Portal/Club Portal/Network/Api_Stuff/Endpoint.swift @@ -0,0 +1,47 @@ +// +// Endpoint.swift +// Club Portal +// +// Created by Umer Tahir on 09/04/2025. +// + +import SwiftUI + +protocol Endpoint { + var path: String { get } + var method: HTTPMethod { get } + var customHeaders: [String: String]? { get } + var body: Data? { get } + var isMultipart: Bool { get } + + var urlQueryItems: [URLQueryItem] { get } // 👈 Add this + var urlRequest: URLRequest? { get } +} + +extension Endpoint { + var urlRequest: URLRequest? { + // Build full URL with query parameters + guard var components = URLComponents(string: APIConstants.baseURL + path) else { + return nil + } + + if !urlQueryItems.isEmpty { + components.queryItems = urlQueryItems + } + + guard let url = components.url else { + return nil + } + + var request = URLRequest(url: url) + request.httpMethod = method.rawValue + + // Merge headers + var headers = APIConstants.commonHeaders + customHeaders?.forEach { headers[$0] = $1 } + request.allHTTPHeaderFields = headers + + request.httpBody = body + return request + } +} 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 new file mode 100644 index 0000000..8d435ad --- /dev/null +++ b/Club_portal/Club Portal/Club Portal/Network/Api_Stuff/Response/AvailabliltyRsp.swift @@ -0,0 +1,444 @@ +// This file was generated from JSON Schema using quicktype, do not modify it directly. +// To parse the JSON, add this file to your project and do: +// +// let availabliltyRsp = try? JSONDecoder().decode(AvailabliltyRsp.self, from: jsonData) + +import Foundation + +// MARK: - AvailabliltyRsp +struct AvailabliltyRsp: Codable { + let error: Bool? + let courts: Courts? + let hours: Hours? + let coaches: Coaches? + let staff: Staff? + let sports: [JSONAny]? + let dateRange, timeRange: Range? + + enum CodingKeys: String, CodingKey { + case error, courts, hours, coaches, staff, sports + case dateRange = "date_range" + case timeRange = "time_range" + } +} + +// MARK: - Coaches +struct Coaches: Codable { + let available: [CoachesAvailable]? + let unavailable: [CoachesAvailable]? + let total: Int? +} + +// MARK: - CoachesAvailable +struct CoachesAvailable: Codable , Identifiable, Hashable { + let id: Int? + let coachID, userID: Int? + let bio, hourlyRate, availability, firstName: String? + let lastName, phone, email: String? + let photo: String? + let coachSports: String? + let sports: [Sport]? + let availabilityData: AvailabilityData? + + enum CodingKeys: String, CodingKey { + case coachID = "coach_id" + case userID = "user_id" + case bio + case hourlyRate = "hourly_rate" + case availability + case firstName = "first_name" + case lastName = "last_name" + case email, photo, phone + case coachSports = "coach_sports" + case sports, availabilityData + case id + } + + func hash(into hasher: inout Hasher) { + hasher.combine(id) + } + + static func == (lhs: CoachesAvailable, rhs: CoachesAvailable) -> Bool { + lhs.id == rhs.id + } +} + +// MARK: - AvailabilityData +struct AvailabilityData: Codable, Identifiable, Hashable { + + let id: Int? + let availability: [Availability]? + let unavailability: [JSONAny]? + + func hash(into hasher: inout Hasher) { + hasher.combine(id) + } + + static func == (lhs: AvailabilityData, rhs: AvailabilityData) -> Bool { + lhs.id == rhs.id + } + +} + +// MARK: - Availability +struct Availability: Codable, Identifiable, Hashable { + let start, end, date, time: String? + let id: Int? + +} + +// MARK: - Sport +struct Sport: Codable, Identifiable, Hashable{ + let id: Int? + let sportID: Int? + let type, subType: String? + let price: Int? + let sportName: String? + + enum CodingKeys: String, CodingKey { + case sportID = "sport_id" + case type + case subType = "sub_type" + case price + case sportName = "sport_name" + case id + } + func hash(into hasher: inout Hasher) { + hasher.combine(id) + } + + static func == (lhs: Sport, rhs: Sport) -> Bool { + lhs.id == rhs.id + } +} + +// MARK: - Courts +struct Courts: Codable { + let available: [CourtsAvailable]? + let unavailable: [JSONAny]? + let total: Int? +} + +// MARK: - CourtsAvailable +struct CourtsAvailable: Codable, Identifiable , Hashable{ + let id: Int? + let name, type, subtype: String? + let sportID, surfaceID: Int? + let availability, sportName: String? + let surfaceName: String? + let availabilityData: AvailabilityData? + + enum CodingKeys: String, CodingKey { + case id, name, type, subtype + case sportID = "sport_id" + case surfaceID = "surface_id" + case availability + case sportName = "sport_name" + case surfaceName = "surface_name" + case availabilityData + } + func hash(into hasher: inout Hasher) { + hasher.combine(id) + } + + static func == (lhs: CourtsAvailable, rhs: CourtsAvailable) -> Bool { + lhs.id == rhs.id + } +} + +// MARK: - Range +struct Range: Codable { + let from, until: String? +} + +// MARK: - Hours +struct Hours: Codable { + let available: [String]? + let total: Int? + let range: Range? + let byDate: [ByDate]? +} + +// MARK: - ByDate +struct ByDate: Codable { + let date: String? + let slots: [String]? + let from, until: String? +} + +// MARK: - Staff +struct Staff: Codable, Identifiable, Hashable { + var id: Int? + let available: [StaffAvailable]? + let unavailable: [JSONAny]? + let total: Int? + + static func == (lhs: Staff, rhs: Staff) -> Bool { + lhs.id == rhs.id + } + + func hash(into hasher: inout Hasher) { + hasher.combine(id) + } +} + +// MARK: - StaffAvailable +struct StaffAvailable: Codable, Identifiable, Hashable { + var id: Int? + let staffID, userID: Int? + let availability, staffRole, firstName, lastName: String? + let email, phone, photo: String? + let availabilityData: AvailabilityData? + + enum CodingKeys: String, CodingKey { + case staffID = "staff_id" + case userID = "user_id" + case availability + case staffRole = "staff_role" + case firstName = "first_name" + case lastName = "last_name" + case email, photo, availabilityData, phone + } +} + +// MARK: - Encode/decode helpers + +class JSONNull: Codable, Hashable { + + public static func == (lhs: JSONNull, rhs: JSONNull) -> Bool { + return true + } + + public var hashValue: Int { + return 0 + } + + public init() {} + + public required init(from decoder: Decoder) throws { + let container = try decoder.singleValueContainer() + if !container.decodeNil() { + throw DecodingError.typeMismatch(JSONNull.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for JSONNull")) + } + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + try container.encodeNil() + } +} + +class JSONCodingKey: CodingKey { + let key: String + + required init?(intValue: Int) { + return nil + } + + required init?(stringValue: String) { + key = stringValue + } + + var intValue: Int? { + return nil + } + + var stringValue: String { + return key + } +} + +class JSONAny: Codable { + + let value: Any + + static func decodingError(forCodingPath codingPath: [CodingKey]) -> DecodingError { + let context = DecodingError.Context(codingPath: codingPath, debugDescription: "Cannot decode JSONAny") + return DecodingError.typeMismatch(JSONAny.self, context) + } + + static func encodingError(forValue value: Any, codingPath: [CodingKey]) -> EncodingError { + let context = EncodingError.Context(codingPath: codingPath, debugDescription: "Cannot encode JSONAny") + return EncodingError.invalidValue(value, context) + } + + static func decode(from container: SingleValueDecodingContainer) throws -> Any { + if let value = try? container.decode(Bool.self) { + return value + } + if let value = try? container.decode(Int64.self) { + return value + } + if let value = try? container.decode(Double.self) { + return value + } + if let value = try? container.decode(String.self) { + return value + } + if container.decodeNil() { + return JSONNull() + } + throw decodingError(forCodingPath: container.codingPath) + } + + static func decode(from container: inout UnkeyedDecodingContainer) throws -> Any { + if let value = try? container.decode(Bool.self) { + return value + } + if let value = try? container.decode(Int64.self) { + return value + } + if let value = try? container.decode(Double.self) { + return value + } + if let value = try? container.decode(String.self) { + return value + } + if let value = try? container.decodeNil() { + if value { + return JSONNull() + } + } + if var container = try? container.nestedUnkeyedContainer() { + return try decodeArray(from: &container) + } + if var container = try? container.nestedContainer(keyedBy: JSONCodingKey.self) { + return try decodeDictionary(from: &container) + } + throw decodingError(forCodingPath: container.codingPath) + } + + static func decode(from container: inout KeyedDecodingContainer, forKey key: JSONCodingKey) throws -> Any { + if let value = try? container.decode(Bool.self, forKey: key) { + return value + } + if let value = try? container.decode(Int64.self, forKey: key) { + return value + } + if let value = try? container.decode(Double.self, forKey: key) { + return value + } + if let value = try? container.decode(String.self, forKey: key) { + return value + } + if let value = try? container.decodeNil(forKey: key) { + if value { + return JSONNull() + } + } + if var container = try? container.nestedUnkeyedContainer(forKey: key) { + return try decodeArray(from: &container) + } + if var container = try? container.nestedContainer(keyedBy: JSONCodingKey.self, forKey: key) { + return try decodeDictionary(from: &container) + } + throw decodingError(forCodingPath: container.codingPath) + } + + static func decodeArray(from container: inout UnkeyedDecodingContainer) throws -> [Any] { + var arr: [Any] = [] + while !container.isAtEnd { + let value = try decode(from: &container) + arr.append(value) + } + return arr + } + + static func decodeDictionary(from container: inout KeyedDecodingContainer) throws -> [String: Any] { + var dict = [String: Any]() + for key in container.allKeys { + let value = try decode(from: &container, forKey: key) + dict[key.stringValue] = value + } + return dict + } + + static func encode(to container: inout UnkeyedEncodingContainer, array: [Any]) throws { + for value in array { + if let value = value as? Bool { + try container.encode(value) + } else if let value = value as? Int64 { + try container.encode(value) + } else if let value = value as? Double { + try container.encode(value) + } else if let value = value as? String { + try container.encode(value) + } else if value is JSONNull { + try container.encodeNil() + } else if let value = value as? [Any] { + var container = container.nestedUnkeyedContainer() + try encode(to: &container, array: value) + } else if let value = value as? [String: Any] { + var container = container.nestedContainer(keyedBy: JSONCodingKey.self) + try encode(to: &container, dictionary: value) + } else { + throw encodingError(forValue: value, codingPath: container.codingPath) + } + } + } + + static func encode(to container: inout KeyedEncodingContainer, dictionary: [String: Any]) throws { + for (key, value) in dictionary { + let key = JSONCodingKey(stringValue: key)! + if let value = value as? Bool { + try container.encode(value, forKey: key) + } else if let value = value as? Int64 { + try container.encode(value, forKey: key) + } else if let value = value as? Double { + try container.encode(value, forKey: key) + } else if let value = value as? String { + try container.encode(value, forKey: key) + } else if value is JSONNull { + try container.encodeNil(forKey: key) + } else if let value = value as? [Any] { + var container = container.nestedUnkeyedContainer(forKey: key) + try encode(to: &container, array: value) + } else if let value = value as? [String: Any] { + var container = container.nestedContainer(keyedBy: JSONCodingKey.self, forKey: key) + try encode(to: &container, dictionary: value) + } else { + throw encodingError(forValue: value, codingPath: container.codingPath) + } + } + } + + static func encode(to container: inout SingleValueEncodingContainer, value: Any) throws { + if let value = value as? Bool { + try container.encode(value) + } else if let value = value as? Int64 { + try container.encode(value) + } else if let value = value as? Double { + try container.encode(value) + } else if let value = value as? String { + try container.encode(value) + } else if value is JSONNull { + try container.encodeNil() + } else { + throw encodingError(forValue: value, codingPath: container.codingPath) + } + } + + public required init(from decoder: Decoder) throws { + if var arrayContainer = try? decoder.unkeyedContainer() { + self.value = try JSONAny.decodeArray(from: &arrayContainer) + } else if var container = try? decoder.container(keyedBy: JSONCodingKey.self) { + self.value = try JSONAny.decodeDictionary(from: &container) + } else { + let container = try decoder.singleValueContainer() + self.value = try JSONAny.decode(from: container) + } + } + + public func encode(to encoder: Encoder) throws { + if let arr = self.value as? [Any] { + var container = encoder.unkeyedContainer() + try JSONAny.encode(to: &container, array: arr) + } else if let dict = self.value as? [String: Any] { + var container = encoder.container(keyedBy: JSONCodingKey.self) + try JSONAny.encode(to: &container, dictionary: dict) + } else { + var container = encoder.singleValueContainer() + try JSONAny.encode(to: &container, value: self.value) + } + } +} diff --git a/Club_portal/Club Portal/Club Portal/Network/Api_Stuff/Response/ClubDetailsResponse.swift b/Club_portal/Club Portal/Club Portal/Network/Api_Stuff/Response/ClubDetailsResponse.swift new file mode 100644 index 0000000..625aa0c --- /dev/null +++ b/Club_portal/Club Portal/Club Portal/Network/Api_Stuff/Response/ClubDetailsResponse.swift @@ -0,0 +1,62 @@ +// +// ClubDetailsResponse.swift +// Club Portal +// +// Created by Umer Tahir on 19/04/2025. +// + + +import Foundation + +// MARK: - Main Response +struct ClubDetailsResponse: Codable { + let error: Bool + let model: ClubDetailsModel +} + +// MARK: - Club Details Model +struct ClubDetailsModel: Codable { + let user: ClubUser + let club: Club + let courts: [Court] + let sports: [Sport] + let pricing: [Pricing] +} + +// MARK: - Club User +struct ClubUser: Codable { + let id: Int + let oauth: String? + let role: String + let firstName, lastName, email, password: String + let type: Int + let alternativePhone, ageGroup, familyRole, defaultPaymentMethod: String? + let guardian, passwordLogin, verify: Int + let clubID: Int? + let phone, photo, bio, refer, stripeUID, paypalUID, twoFactorAuthentication: String? + let status: Int + let createAt, updateAt: String + + enum CodingKeys: String, CodingKey { + case id, oauth, role + case firstName = "first_name" + case lastName = "last_name" + case email, password, type + case alternativePhone = "alternative_phone" + case ageGroup = "age_group" + case familyRole = "family_role" + case defaultPaymentMethod = "default_payment_method" + case guardian + case passwordLogin = "password_login" + case verify + case clubID = "club_id" + case phone, photo, bio, refer + case stripeUID = "stripe_uid" + case paypalUID = "paypal_uid" + case twoFactorAuthentication = "two_factor_authentication" + case status + case createAt = "create_at" + case updateAt = "update_at" + } +} + diff --git a/Club_portal/Club Portal/Club Portal/Network/Api_Stuff/Response/ClubResponse.swift b/Club_portal/Club Portal/Club Portal/Network/Api_Stuff/Response/ClubResponse.swift new file mode 100644 index 0000000..148558d --- /dev/null +++ b/Club_portal/Club Portal/Club Portal/Network/Api_Stuff/Response/ClubResponse.swift @@ -0,0 +1,200 @@ +// This file was generated from JSON Schema using quicktype, do not modify it directly. +// To parse the JSON, add this file to your project and do: +// +// let clubDetail = try? JSONDecoder().decode(ClubDetail.self, from: jsonData) + +import Foundation + +// MARK: - ClubDetail +struct ClubDetail: Codable { + let error: Bool? + let model: Model? + let sports: [Club]? + let courts: [Court]? + let pricing: [Pricing]? +} + +// MARK: - Court +struct Court: Codable, Identifiable, Hashable { + let id, clubID: Int? + let name: String? + let sportID: Int? + let type: String? + let surfaceID: Int? + let subType, availability: String? + let createAt, updateAt: String? + + enum CodingKeys: String, CodingKey { + case id + case clubID = "club_id" + case name + case sportID = "sport_id" + case type + case surfaceID = "surface_id" + case subType = "sub_type" + case availability + case createAt = "create_at" + case updateAt = "update_at" + } +} + +enum TypeEnum: String, Codable { + case empty = "" + case indoor = "Indoor" + case outdoor = "Outdoor" + case typeOutdoor = "outdoor" +} + +// MARK: - Model +struct Model: Codable { + let id: Int? + let name, slug, title, description: String? + let userID, completed: Int? + let bio: JSONNull? + let openingTime, closingTime, times, splashScreen: String? + let showClinic, showNotification: Int? + let clubLogo: String? + let feeSettings: String? + let customFields: [CustomField]? + let homeImage: String? + let showBuddy: Int? + let exceptions, clubLocation: String? + let showCoach, showGroups, showCourt: Int? + let buddyDescription, courtDescription, coachDescription, clinicDescription: String? + let lessonDescription: String? + let customRequestThreshold, clubFee, serviceFee, maxPlayers: Int? + let accountDetails, accountSettings, membershipSettings, dailyBreaks: String? + let daysOff, createAt, updateAt: String? + + enum CodingKeys: String, CodingKey { + case id, name, slug, title, description + case userID = "user_id" + case completed, bio + case openingTime = "opening_time" + case closingTime = "closing_time" + case times + case splashScreen = "splash_screen" + case showClinic = "show_clinic" + case showNotification = "show_notification" + case clubLogo = "club_logo" + case feeSettings = "fee_settings" + case customFields = "custom_fields" + case homeImage = "home_image" + case showBuddy = "show_buddy" + case exceptions + case clubLocation = "club_location" + case showCoach = "show_coach" + case showGroups = "show_groups" + case showCourt = "show_court" + case buddyDescription = "buddy_description" + case courtDescription = "court_description" + case coachDescription = "coach_description" + case clinicDescription = "clinic_description" + case lessonDescription = "lesson_description" + case customRequestThreshold = "custom_request_threshold" + case clubFee = "club_fee" + case serviceFee = "service_fee" + case maxPlayers = "max_players" + case accountDetails = "account_details" + case accountSettings = "account_settings" + case membershipSettings = "membership_settings" + case dailyBreaks = "daily_breaks" + case daysOff = "days_off" + case createAt = "create_at" + case updateAt = "update_at" + } +} + +// MARK: - CustomField +struct CustomField: Codable { + let value, label, type: String? + let customFieldRequired: Bool? + let options: [Option]? + let min, max, step: String? + + enum CodingKeys: String, CodingKey { + case value, label, type + case customFieldRequired = "required" + case options, min, max, step + } +} + +enum Option: Codable { + case double(Double) + case string(String) + + init(from decoder: Decoder) throws { + let container = try decoder.singleValueContainer() + if let x = try? container.decode(Double.self) { + self = .double(x) + return + } + if let x = try? container.decode(String.self) { + self = .string(x) + return + } + throw DecodingError.typeMismatch(Option.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for Option")) + } + + func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + switch self { + case .double(let x): + try container.encode(x) + case .string(let x): + try container.encode(x) + } + } +} + +// MARK: - Pricing +struct Pricing: Codable { + let id: Int? + let surfaceID: Int? + let clubID: Int? + let type, subtype: String? + let sportID: Int? + let status, isGeneral, generalRate: Int? + let priceByHours, createAt, updateAt: String? + + enum CodingKeys: String, CodingKey { + case id + case surfaceID = "surface_id" + case clubID = "club_id" + case type, subtype + case sportID = "sport_id" + case status + case isGeneral = "is_general" + case generalRate = "general_rate" + case priceByHours = "price_by_hours" + case createAt = "create_at" + case updateAt = "update_at" + } +} + +// MARK: - Sport +struct ClubSport: Codable { + let id, status: Int? + let name: String? + let clubID: Int? + let sportTypes: [SportType]? + + enum CodingKeys: String, CodingKey { + case id, status, name + case clubID = "club_id" + case sportTypes = "sport_types" + } +} + +// MARK: - SportType +struct SportType: Codable { + let type: String? + let subtype: [String]? + let clubSportTypeID: String? + + enum CodingKeys: String, CodingKey { + case type, subtype + case clubSportTypeID = "club_sport_type_id" + } +} + 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 new file mode 100644 index 0000000..fcd15cd --- /dev/null +++ b/Club_portal/Club Portal/Club Portal/Network/Api_Stuff/Response/ClubStatisticsResponse.swift @@ -0,0 +1,68 @@ +// +// ClubStatisticsResponse.swift +// Club Portal +// +// Created by Umer Tahir on 11/04/2025. +// + + +struct ClubStatisticsResponse: Decodable { + let error: Bool + let model: ClubStatisticsModel +} + +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] +} + +struct ReservationStats: Decodable { + let total_hours: Double? + let total_revenue: Double? +} + +struct RevenueHeatMap: Decodable { + let date: String + let total_revenue: Double +} + +struct RevenueByDateRange: Decodable { + // Add fields if needed +} + +struct RevenueByModule: Decodable { + let module: String + let revenue: Double? +} + +struct ExpenseStats: Decodable { + let total_hours: Double? + let total_expense: Double? +} + +struct RevenueStats: Decodable { + let total_hours: Double? + let total_revenue: Double? +} + +struct ProfitStats: Decodable { + let total_profit: Double? + let total_revenue: Double? + let total_expense: Double? +} + +struct CourtUtilizationStats: Decodable { + let utilization_percentage: Double? + let total_used_hours: Double? + let total_available_hours: Double? +} \ No newline at end of file diff --git a/Club_portal/Club Portal/Club Portal/Network/Api_Stuff/Response/ForgotPasswordResponse.swift b/Club_portal/Club Portal/Club Portal/Network/Api_Stuff/Response/ForgotPasswordResponse.swift new file mode 100644 index 0000000..9dd8704 --- /dev/null +++ b/Club_portal/Club Portal/Club Portal/Network/Api_Stuff/Response/ForgotPasswordResponse.swift @@ -0,0 +1,12 @@ +// +// ForgotPasswordResponse.swift +// Club Portal +// +// Created by Umer Tahir on 10/04/2025. +// + + +struct ForgotPasswordResponse: Codable { + let error: Bool + let message: String +} \ No newline at end of file diff --git a/Club_portal/Club Portal/Club Portal/Network/Api_Stuff/Response/LoginResponse.swift b/Club_portal/Club Portal/Club Portal/Network/Api_Stuff/Response/LoginResponse.swift new file mode 100644 index 0000000..c254d80 --- /dev/null +++ b/Club_portal/Club Portal/Club Portal/Network/Api_Stuff/Response/LoginResponse.swift @@ -0,0 +1,31 @@ +// +// LoginResponse.swift +// Club Portal +// +// Created by Umer Tahir on 09/04/2025. +// + + +struct LoginResponse: Decodable { + let error: Bool + let role: String + let token: String + let expire_at: Int + let userId: Int + let first_name: String? + let last_name: String? + let photo: String? + let two_factor_enabled: Bool + + enum CodingKeys: String, CodingKey { + case error + case role + case token + case expire_at + case userId = "user_id" // Mapping JSON key "user_id" to "userId" + case first_name + case last_name + case photo + case two_factor_enabled + } +} diff --git a/Club_portal/Club Portal/Club Portal/Network/Api_Stuff/Response/ProfileRsp.swift b/Club_portal/Club Portal/Club Portal/Network/Api_Stuff/Response/ProfileRsp.swift new file mode 100644 index 0000000..9137fe0 --- /dev/null +++ b/Club_portal/Club Portal/Club Portal/Network/Api_Stuff/Response/ProfileRsp.swift @@ -0,0 +1,332 @@ +// +// Profile.swift +// Club Portal +// +// Created by Umer Tahir on 22/04/2025. +// + + +// This file was generated from JSON Schema using quicktype, do not modify it directly. +// To parse the JSON, add this file to your project and do: +// +// let profile = try? JSONDecoder().decode(Profile.self, from: jsonData) + +import Foundation + + +// MARK: - ProfileRsp +class ProfileRsp: Codable { + let error: Bool? + let model: ProfileModel? + + init(error: Bool?, model: ProfileModel?) { + self.error = error + self.model = model + } +} + +// MARK: - Model +class ProfileModel: Codable { + let user: User1? + let club: Club1? + let courts: [Court1]? + let sports: [Sport1]? + let pricing: [Pricing1]? + + init(user: User1?, club: Club1?, courts: [Court1]?, sports: [Sport1]?, pricing: [Pricing1]?) { + self.user = user + self.club = club + self.courts = courts + self.sports = sports + self.pricing = pricing + } +} + +// MARK: - Club +class Club1: Codable { + let id: Int? + let name, slug: String? + let title, description: String? + let userID, completed: Int? + let bio: String? + let openingTime, closingTime, times, splashScreen: String? + let showClinic, showNotification: Int? + let clubLogo, feeSettings, customFields, homeImage: String? + let showBuddy: Int? + let exceptions: String? + let clubLocation: String? + let showCoach, showGroups, showCourt: Int? + let buddyDescription, courtDescription, coachDescription, clinicDescription: String? + let lessonDescription: String? + let customRequestThreshold, clubFee, serviceFee, maxPlayers: Int? + let accountDetails: String? + let accountSettings, membershipSettings, dailyBreaks: String? + let daysOff, createAt, updateAt: String? + + enum CodingKeys: String, CodingKey { + case id, name, slug, title, description + case userID = "user_id" + case completed, bio + case openingTime = "opening_time" + case closingTime = "closing_time" + case times + case splashScreen = "splash_screen" + case showClinic = "show_clinic" + case showNotification = "show_notification" + case clubLogo = "club_logo" + case feeSettings = "fee_settings" + case customFields = "custom_fields" + case homeImage = "home_image" + case showBuddy = "show_buddy" + case exceptions + case clubLocation = "club_location" + case showCoach = "show_coach" + case showGroups = "show_groups" + case showCourt = "show_court" + case buddyDescription = "buddy_description" + case courtDescription = "court_description" + case coachDescription = "coach_description" + case clinicDescription = "clinic_description" + case lessonDescription = "lesson_description" + case customRequestThreshold = "custom_request_threshold" + case clubFee = "club_fee" + case serviceFee = "service_fee" + case maxPlayers = "max_players" + case accountDetails = "account_details" + case accountSettings = "account_settings" + case membershipSettings = "membership_settings" + case dailyBreaks = "daily_breaks" + case daysOff = "days_off" + case createAt = "create_at" + case updateAt = "update_at" + } + + init(id: Int?, name: String?, slug: String?, title: String?, description: String?, userID: Int?, completed: Int?, bio: String?, openingTime: String?, closingTime: String?, times: String?, splashScreen: String?, showClinic: Int?, showNotification: Int?, clubLogo: String?, feeSettings: String?, customFields: String?, homeImage: String?, showBuddy: Int?, exceptions: String?, clubLocation: String?, showCoach: Int?, showGroups: Int?, showCourt: Int?, buddyDescription: String?, courtDescription: String?, coachDescription: String?, clinicDescription: String?, lessonDescription: String?, customRequestThreshold: Int?, clubFee: Int?, serviceFee: Int?, maxPlayers: Int?, accountDetails: String?, accountSettings: String?, membershipSettings: String?, dailyBreaks: String?, daysOff: String?, createAt: String?, updateAt: String?) { + self.id = id + self.name = name + self.slug = slug + self.title = title + self.description = description + self.userID = userID + self.completed = completed + self.bio = bio + self.openingTime = openingTime + self.closingTime = closingTime + self.times = times + self.splashScreen = splashScreen + self.showClinic = showClinic + self.showNotification = showNotification + self.clubLogo = clubLogo + self.feeSettings = feeSettings + self.customFields = customFields + self.homeImage = homeImage + self.showBuddy = showBuddy + self.exceptions = exceptions + self.clubLocation = clubLocation + self.showCoach = showCoach + self.showGroups = showGroups + self.showCourt = showCourt + self.buddyDescription = buddyDescription + self.courtDescription = courtDescription + self.coachDescription = coachDescription + self.clinicDescription = clinicDescription + self.lessonDescription = lessonDescription + self.customRequestThreshold = customRequestThreshold + self.clubFee = clubFee + self.serviceFee = serviceFee + self.maxPlayers = maxPlayers + self.accountDetails = accountDetails + self.accountSettings = accountSettings + self.membershipSettings = membershipSettings + self.dailyBreaks = dailyBreaks + self.daysOff = daysOff + self.createAt = createAt + self.updateAt = updateAt + } +} + +// MARK: - Court +class Court1: Codable { + let id, clubID: Int? + let name: String? + let sportID: Int? + let type: String? + let surfaceID: Int? + let subType: String? + let availability: String? + let createAt, updateAt: String? + + enum CodingKeys: String, CodingKey { + case id + case clubID = "club_id" + case name + case sportID = "sport_id" + case type + case surfaceID = "surface_id" + case subType = "sub_type" + case availability + case createAt = "create_at" + case updateAt = "update_at" + } + + init(id: Int?, clubID: Int?, name: String?, sportID: Int?, type: String?, surfaceID: Int?, subType: String?, availability: String?, createAt: String?, updateAt: String?) { + self.id = id + self.clubID = clubID + self.name = name + self.sportID = sportID + self.type = type + self.surfaceID = surfaceID + self.subType = subType + self.availability = availability + self.createAt = createAt + self.updateAt = updateAt + } +} + +// MARK: - Pricing +class Pricing1: Codable { + let id: Int? + let surfaceID: String? + let clubID: Int? + let type, subtype: String? + let sportID, status, isGeneral, generalRate: Int? + let priceByHours, createAt, updateAt: String? + + enum CodingKeys: String, CodingKey { + case id + case surfaceID = "surface_id" + case clubID = "club_id" + case type, subtype + case sportID = "sport_id" + case status + case isGeneral = "is_general" + case generalRate = "general_rate" + case priceByHours = "price_by_hours" + case createAt = "create_at" + case updateAt = "update_at" + } + + init(id: Int?, surfaceID: String?, clubID: Int?, type: String?, subtype: String?, sportID: Int?, status: Int?, isGeneral: Int?, generalRate: Int?, priceByHours: String?, createAt: String?, updateAt: String?) { + self.id = id + self.surfaceID = surfaceID + self.clubID = clubID + self.type = type + self.subtype = subtype + self.sportID = sportID + self.status = status + self.isGeneral = isGeneral + self.generalRate = generalRate + self.priceByHours = priceByHours + self.createAt = createAt + self.updateAt = updateAt + } +} + +// MARK: - Sport +class Sport1: Codable { + let id, status: Int? + let name: String? + let clubID: Int? + let sportTypes: [SportType]? + + enum CodingKeys: String, CodingKey { + case id, status, name + case clubID = "club_id" + case sportTypes = "sport_types" + } + + init(id: Int?, status: Int?, name: String?, clubID: Int?, sportTypes: [SportType]?) { + self.id = id + self.status = status + self.name = name + self.clubID = clubID + self.sportTypes = sportTypes + } +} + +// MARK: - SportType +class SportType1: Codable { + let type: String? + let subtype: [String]? + let clubSportTypeID: String? + + enum CodingKeys: String, CodingKey { + case type, subtype + case clubSportTypeID = "club_sport_type_id" + } + + init(type: String?, subtype: [String]?, clubSportTypeID: String?) { + self.type = type + self.subtype = subtype + self.clubSportTypeID = clubSportTypeID + } +} + +// MARK: - User +class User1: Codable { + let id: Int? + let oauth: String? + let role, firstName, lastName, email: String? + let password: String? + let type: Int? + let alternativePhone, ageGroup, familyRole, defaultPaymentMethod: String? + let guardian, passwordLogin, verify: Int? + let clubID, phone, photo, bio: String? + let refer, stripeUid, paypalUid, twoFactorAuthentication: String? + let status: Int? + let createAt, updateAt: String? + + enum CodingKeys: String, CodingKey { + case id, oauth, role + case firstName = "first_name" + case lastName = "last_name" + case email, password, type + case alternativePhone = "alternative_phone" + case ageGroup = "age_group" + case familyRole = "family_role" + case defaultPaymentMethod = "default_payment_method" + case guardian + case passwordLogin = "password_login" + case verify + case clubID = "club_id" + case phone, photo, bio, refer + case stripeUid = "stripe_uid" + case paypalUid = "paypal_uid" + case twoFactorAuthentication = "two_factor_authentication" + case status + case createAt = "create_at" + case updateAt = "update_at" + } + + init(id: Int?, oauth: String?, role: String?, firstName: String?, lastName: String?, email: String?, password: String?, type: Int?, alternativePhone: String?, ageGroup: String?, familyRole: String?, defaultPaymentMethod: String?, guardian: Int?, passwordLogin: Int?, verify: Int?, clubID: String?, phone: String?, photo: String?, bio: String?, refer: String?, stripeUid: String?, paypalUid: String?, twoFactorAuthentication: String?, status: Int?, createAt: String?, updateAt: String?) { + self.id = id + self.oauth = oauth + self.role = role + self.firstName = firstName + self.lastName = lastName + self.email = email + self.password = password + self.type = type + self.alternativePhone = alternativePhone + self.ageGroup = ageGroup + self.familyRole = familyRole + self.defaultPaymentMethod = defaultPaymentMethod + self.guardian = guardian + self.passwordLogin = passwordLogin + self.verify = verify + self.clubID = clubID + self.phone = phone + self.photo = photo + self.bio = bio + self.refer = refer + self.stripeUid = stripeUid + self.paypalUid = paypalUid + self.twoFactorAuthentication = twoFactorAuthentication + self.status = status + self.createAt = createAt + self.updateAt = updateAt + } +} + +// MARK: - Encode/decode helpers + 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 new file mode 100644 index 0000000..f60f7ac --- /dev/null +++ b/Club_portal/Club Portal/Club Portal/Network/Api_Stuff/Response/ReservationResponse.swift @@ -0,0 +1,146 @@ +import Foundation + +// MARK: - Root +struct ReservationRsp: Codable { + let list: [Reservation] +} + +// MARK: - Reservation +struct Reservation: Codable { + let id: Int + let bookingID: Int + let buddyID: Int? + let spaceAssigned: String + let userID: Int + let clubID: Int + let reservationType: Int? + let status: Int + let checkIn: Int + let recurring: Int + let notes: String + let createAt: String + let updateAt: String + let booking: Booking + let user: User + let buddy: [String?] + let clubs: Club +} + +// MARK: - Booking +struct Booking: Codable { + let id: Int + let userID: Int + let sportID: Int + let type: String + let receiptID: String + let subtype: String + let status: Int + let clinicID: Int? + let minNtrp: Int + let maxNtrp: Int + let coachID: Int? + let reservationType: Int + let courtID: Int + let courtIDs: String + let customRequest: Int + let price: Double + let clubFee: Double + let serviceFee: Double + let clinicFee: Double + let last4: Int + let clubID: Int? + let coachFee: Double + let lessonID: Int? + let duration: Int + let paymentIntent: String? + let notes: String? + let spaceAssigned: String? + let playerIDs: String + let courtFee: Double? + let playerID: Int? + let date: String + let startTime: String + let endTime: String + let createAt: String + let updateAt: String + let coachIDs: String +} + +// MARK: - User +struct User: Codable { + let id: Int + let oauth: String? + let role: String + let firstName: String + let lastName: String + let email: String + let password: String + let type: Int + let alternativePhone: String? + let ageGroup: String? + let familyRole: String? + let defaultPaymentMethod: String? + let guardian: Int + let passwordLogin: String + let verify: Int + let clubID: Int? + let phone: String? + let photo: String? + let bio: String? + let refer: String? + let stripeUID: String? + let paypalUID: String? + let twoFactorAuthentication: String? + let status: Int + let createAt: String + let updateAt: String +} + +// MARK: - Buddy +struct Buddy: Codable { + // Add properties if needed +} + +// MARK: - Club +struct Club: Codable { + let id: Int + let name: String + let slug: String + let title: String + let description: String + let userID: Int + let completed: Int + let bio: String? + let openingTime: String + let closingTime: String + let times: String + let splashScreen: String + let showClinic: Int + let showNotification: Int + let clubLogo: String + let feeSettings: String + let customFields: String + let homeImage: String + let showBuddy: Int + let exceptions: String + let clubLocation: String + let showCoach: Int + let showGroups: Int + let showCourt: Int + let buddyDescription: String + let courtDescription: String + let coachDescription: String + let clinicDescription: String + let lessonDescription: String + let customRequestThreshold: Int + let clubFee: Double + let serviceFee: Double + let maxPlayers: Int + let accountDetails: String + let accountSettings: String + let membershipSettings: String + let dailyBreaks: String + let daysOff: String + let createAt: String + let updateAt: String +} diff --git a/Club_portal/Club Portal/Club Portal/Network/DataModels/AppleLoginRequest.swift b/Club_portal/Club Portal/Club Portal/Network/DataModels/AppleLoginRequest.swift new file mode 100644 index 0000000..07f6a6b --- /dev/null +++ b/Club_portal/Club Portal/Club Portal/Network/DataModels/AppleLoginRequest.swift @@ -0,0 +1,16 @@ +// +// AppleLoginRequest.swift +// Club Portal +// +// Created by Umer Tahir on 09/04/2025. +// + + +struct AppleLoginRequest: Encodable { + let first_name : String + let last_name : String + let identityToken : String + let apple_id : String + let role: String + +} diff --git a/Club_portal/Club Portal/Club Portal/Preview Content/Preview Assets.xcassets/Contents.json b/Club_portal/Club Portal/Club Portal/Preview Content/Preview Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/Club_portal/Club Portal/Club Portal/Preview Content/Preview Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Club_portal/Club Portal/Club Portal/UI/Availbility/AvailabilityCardView.swift b/Club_portal/Club Portal/Club Portal/UI/Availbility/AvailabilityCardView.swift new file mode 100644 index 0000000..a9db5ba --- /dev/null +++ b/Club_portal/Club Portal/Club Portal/UI/Availbility/AvailabilityCardView.swift @@ -0,0 +1,112 @@ +// +// AvailabilityCardView.swift +// Club Portal +// +// Created by Umer Tahir on 12/04/2025. +// +import SwiftUI + +struct AvailabilityCardView: View { + var tab: AvailabilityTab + var courts: [CourtsAvailable] = [] + var hours: [String]? + var coaches: [CoachesAvailable]? + var stsff: [StaffAvailable]? + @State var name : String = "" + @State var email : String = "" + @State var phone : String = "" + + @State var showSheet = false + var body: some View { + VStack(alignment: .leading, spacing: 12) { + HStack { + Label("Available \(getCount())", systemImage: "calendar") + Spacer() + Text("\(getCount())") + .font(.subheadline) + .padding(.horizontal, 10) + .padding(.vertical, 4) + .background(Colorr.themeBlueColor) + .foregroundColor(.white) + .clipShape(Capsule()) + } + + switch tab { + case .courts: + ForEach(courts , id: \.self) { court in + HStack { + Text("Court") + Text("\(courts.count)") + .foregroundColor(.gray) + Spacer() + } + Divider() + } + case .hours: + ForEach(hours ?? [] , id: \.self) { hour in + Text("\(hour)") + .onTapGesture { + showSheet.toggle() + } + Divider() + } + case .coaches: + ForEach(coaches ?? [], id: \.self) { coach in + HStack { + ProfileImageView(imageUrl: coach.photo ?? "", size: 30) + + Text(coach.firstName ?? "") + Spacer() + Image("send") + .foregroundColor(.gray) + .onTapGesture { + name = coach.firstName ?? "" + email = coach.email ?? "" + phone = coach.phone ?? "" + showSheet.toggle() + + } + } + Divider() + } + case .staff: + ForEach(self.stsff ?? [], id: \.self) { coach in + HStack { + ProfileImageView(imageUrl: coach.photo ?? "", size: 30) + Text( coach.firstName ?? "") + Spacer() + Image("send") + .foregroundColor(.gray) + .onTapGesture { + name = coach.firstName ?? "" + email = coach.email ?? "" + phone = coach.phone ?? "" + showSheet.toggle() + } + } + Divider() + } + + } + } + .padding() + .background(Color.white) + .cornerRadius(14) + .shadow(color: Color.black.opacity(0.05), radius: 5, x: 0, y: 2) + .padding(.horizontal) + .sheet(isPresented: $showSheet) { + ContactInfoBottomSheet(name: $name, email: $email, phone: $phone) + .presentationDetents([.height(250)]) + .presentationDragIndicator(.visible) + } + } + + func getCount() -> Int { + switch tab { + case .courts: return courts.count + case .hours: return hours?.count ?? 0 + case .coaches: return coaches?.count ?? 0 + case .staff: return stsff?.count ?? 0 + } + } +} diff --git a/Club_portal/Club Portal/Club Portal/UI/Availbility/AvailabilityScreen.swift b/Club_portal/Club Portal/Club Portal/UI/Availbility/AvailabilityScreen.swift new file mode 100644 index 0000000..50766cb --- /dev/null +++ b/Club_portal/Club Portal/Club Portal/UI/Availbility/AvailabilityScreen.swift @@ -0,0 +1,42 @@ +// +// AvailabilityScreen.swift +// Club Portal +// +// Created by Umer Tahir on 12/04/2025. +// + + +import SwiftUI + +struct AvailabilityScreen: View { + @State private var selectedTab: AvailabilityTab = .courts + @Binding var presentSideMenu: Bool // Optional for side menu + + @EnvironmentObject var viewModel : DashViewModel + + + var body: some View { + VStack(spacing: 16) { + HeaderView(presentSideMenu: $presentSideMenu) + + DateSelectorView() + + TabSelectorView(selectedTab: $selectedTab) + ScrollView { + AvailabilityCardView( + tab: selectedTab, + courts: viewModel.availRsp?.courts?.available ?? [] , + hours: viewModel.availRsp?.hours?.available ?? [] , + coaches: viewModel.availRsp?.coaches?.available ?? [], + stsff: viewModel.availRsp?.staff?.available ?? [] + ) + } + + Spacer() + } + .background(Color(.systemGray6).ignoresSafeArea()) + .onAppear(){ + viewModel.getAvailability() + } + } +} diff --git a/Club_portal/Club Portal/Club Portal/UI/Availbility/AvailabilityTab.swift b/Club_portal/Club Portal/Club Portal/UI/Availbility/AvailabilityTab.swift new file mode 100644 index 0000000..3f12237 --- /dev/null +++ b/Club_portal/Club Portal/Club Portal/UI/Availbility/AvailabilityTab.swift @@ -0,0 +1,31 @@ +// +// AvailabilityTab.swift +// Club Portal +// +// Created by Umer Tahir on 12/04/2025. +// + +import SwiftUI +enum AvailabilityTab: String, CaseIterable { + case courts = "Courts" + case hours = "Hours" + case coaches = "Coaches" + case staff = "Staff" +} + +// +//struct Court: Identifiable { +// var id = UUID() +// var number: String +//} +// +//struct Hour: Identifiable { +// var id = UUID() +// var hour: String +//} +// +//struct Coach: Identifiable { +// var id = UUID() +// var name: String +// var image: String // image name in Assets +//} diff --git a/Club_portal/Club Portal/Club Portal/UI/Availbility/DateSelectorView.swift b/Club_portal/Club Portal/Club Portal/UI/Availbility/DateSelectorView.swift new file mode 100644 index 0000000..22b54ea --- /dev/null +++ b/Club_portal/Club Portal/Club Portal/UI/Availbility/DateSelectorView.swift @@ -0,0 +1,58 @@ +// +// DateSelectorView.swift +// Club Portal +// +// Created by Umer Tahir on 12/04/2025. +// + +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() + + @State private var startTime: Date = Date() + @State private var endTime: Date = Calendar.current.date(byAdding: .hour, value: 1, to: Date()) ?? Date() + + var body: some View { + VStack(spacing: 12) { + VStack(alignment: .leading, spacing: 4) { + HStack { + Image(systemName: "calendar") + Text("Date Range") + .fontWeight(.semibold) + Spacer() + } + HStack { + DatePicker("From", selection: $startDate, displayedComponents: [.date]) + .labelsHidden() + DatePicker("Until", selection: $endDate, in: startDate..., displayedComponents: [.date]) + .labelsHidden() + } + } + .padding() + .background(Color(.white)) + .cornerRadius(10) + + VStack(alignment: .leading, spacing: 4) { + HStack { + Image(systemName: "clock") + Text("Time Range") + .fontWeight(.semibold) + Spacer() + } + HStack { + DatePicker("From", selection: $startTime, displayedComponents: [.hourAndMinute]) + .labelsHidden() + DatePicker("Until", selection: $endTime, displayedComponents: [.hourAndMinute]) + .labelsHidden() + } + } + .padding() + .background(Color(.white)) + .cornerRadius(10) + } + .padding(.horizontal) + } +} diff --git a/Club_portal/Club Portal/Club Portal/UI/Availbility/HeaderView.swift b/Club_portal/Club Portal/Club Portal/UI/Availbility/HeaderView.swift new file mode 100644 index 0000000..b9d0af6 --- /dev/null +++ b/Club_portal/Club Portal/Club Portal/UI/Availbility/HeaderView.swift @@ -0,0 +1,24 @@ +// +// HeaderView.swift +// Club Portal +// +// Created by Umer Tahir on 12/04/2025. +// + +import SwiftUI + +struct HeaderViewAv: View { + var body: some View { + HStack { + Image(systemName: "person.crop.circle.fill") + .resizable() + .frame(width: 30, height: 30) + Spacer() + Text("The Royal Club") + .font(.headline) + Spacer() + Image(systemName: "arrowshape.turn.up.backward") + } + .padding(.horizontal) + } +} diff --git a/Club_portal/Club Portal/Club Portal/UI/Availbility/Sheet/ContactInfoBottomSheet.swift b/Club_portal/Club Portal/Club Portal/UI/Availbility/Sheet/ContactInfoBottomSheet.swift new file mode 100644 index 0000000..39e82b2 --- /dev/null +++ b/Club_portal/Club Portal/Club Portal/UI/Availbility/Sheet/ContactInfoBottomSheet.swift @@ -0,0 +1,68 @@ +// +// ContactInfoBottomSheet.swift +// Club Portal +// +// Created by Umer Tahir on 16/04/2025. +// + + +import SwiftUI + +struct ContactInfoBottomSheet: View { + @Binding var name : String + @Binding var email : String + @Binding var phone : String + + @Environment(\.dismiss) var dismiss + + var body: some View { + VStack(alignment: .leading, spacing: 20) { + Text(name) + .font(.title3.bold()) + + VStack(alignment: .leading, spacing: 8) { + Text("EMAIL") + .font(.caption) + .foregroundColor(.gray) + + HStack { + Text(email) + .underline() + .lineLimit(1) + .truncationMode(.middle) + + Spacer() + + Button { + UIPasteboard.general.string = email + } label: { + Image(systemName: "doc.on.doc") + } + } + } + + VStack(alignment: .leading, spacing: 8) { + Text("PHONE") + .font(.caption) + .foregroundColor(.gray) + + HStack { + Text(phone) + .underline() + + Spacer() + + Button { + UIPasteboard.general.string = phone + } label: { + Image(systemName: "doc.on.doc") + } + } + } + } + .padding() + .background(Color(.systemBackground)) + .cornerRadius(20) + .padding() + } +} diff --git a/Club_portal/Club Portal/Club Portal/UI/Availbility/TabSelectorView.swift b/Club_portal/Club Portal/Club Portal/UI/Availbility/TabSelectorView.swift new file mode 100644 index 0000000..b495840 --- /dev/null +++ b/Club_portal/Club Portal/Club Portal/UI/Availbility/TabSelectorView.swift @@ -0,0 +1,67 @@ +// +// TabSelectorView.swift +// Club Portal +// +// Created by Umer Tahir on 12/04/2025. +// + +import SwiftUI + +struct TabSelectorView: View { + @Binding var selectedTab: AvailabilityTab + + var body: some View { + HStack(spacing: 0) { + // ForEach(AvailabilityTab.allCases, id: \.self) { tab in + // Button(action: { + // selectedTab = tab + // }) { + // Text(tab.rawValue) + // .fontWeight(selectedTab == tab ? .bold : .regular) + // .foregroundColor(.black) + // .frame(maxWidth: .infinity) + // .padding(.vertical, 10) + // .background(selectedTab == tab ? Color.white : Color.clear) + // } + // } + // } + // .background(Color(.systemGray5)) + // .clipShape(Capsule()) + // .padding(.horizontal) + + Picker(selection: $selectedTab, label: Text("Options")) { + ForEach(AvailabilityTab.allCases, id: \.self) { index in + Text(index.rawValue).tag(index) + } + + } + .pickerStyle(SegmentedPickerStyle()) + .padding(.horizontal) + .tint(.gray.opacity(0.5)) + + } + } +} + +//struct SegmentedControlView: View { +// @Binding var selectedTab : Int +// let options = ["Summary", "Analytics"] +// +// @EnvironmentObject var viewModel : DashViewModel +// +// var body: some View { +// Picker(selection: $selectedTab, label: Text("Options")) { +// ForEach(options.indices, id: \.self) { index in +// Text(self.options[index]).tag(index) +// } +// +// } +// .pickerStyle(SegmentedPickerStyle()) +// .padding(.horizontal) +// .tint(.gray.opacity(0.5)) +// +// Divider() +// +// +// } +//} diff --git a/Club_portal/Club Portal/Club Portal/UI/DailySecheduler/CalendarKitView.swift b/Club_portal/Club Portal/Club Portal/UI/DailySecheduler/CalendarKitView.swift new file mode 100644 index 0000000..ebca9ab --- /dev/null +++ b/Club_portal/Club Portal/Club Portal/UI/DailySecheduler/CalendarKitView.swift @@ -0,0 +1,35 @@ +// +// CalendarKitView.swift +// Club Portal +// +// Created by Umer Tahir on 04/04/2025. +// + + +import SwiftUI +import CalendarKit + +//struct CalendarKitView: UIViewControllerRepresentable { +// func makeUIViewController(context: Context) -> DayViewController { +// let dayViewController = DayViewController() +// dayViewController.dataSource = context.coordinator +// return dayViewController +// } +// +// func updateUIViewController(_ uiViewController: DayViewController, context: Context) {} +// +// func makeCoordinator() -> Coordinator { +// return Coordinator() +// } +// +// class Coordinator: NSObject, EventDataSource { +// func eventsForDate(_ date: Date) -> [EventDescriptor] { +// let event = Event() +// // event.startDate = date +// // event.endDate = Calendar.current.date(byAdding: .hour, value: 1, to: date)! +// event.text = "Sample Event" +// event.color = .blue +// return [event] +// } +// } +//} diff --git a/Club_portal/Club Portal/Club Portal/UI/DailySecheduler/CoachesTab.swift b/Club_portal/Club Portal/Club Portal/UI/DailySecheduler/CoachesTab.swift new file mode 100644 index 0000000..a2af383 --- /dev/null +++ b/Club_portal/Club Portal/Club Portal/UI/DailySecheduler/CoachesTab.swift @@ -0,0 +1,36 @@ +// +// CoachesTab.swift +// Club Portal +// +// Created by Umer Tahir on 16/04/2025. +// + +import SwiftUI + +struct CoachesTab: View { + 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) + } + + Spacer() + } + .padding() + .background(Color(.systemGray6)) + .cornerRadius(10) + } + } + .padding() + } + } +} diff --git a/Club_portal/Club Portal/Club Portal/UI/DailySecheduler/DetailsTab.swift b/Club_portal/Club Portal/Club Portal/UI/DailySecheduler/DetailsTab.swift new file mode 100644 index 0000000..2c72330 --- /dev/null +++ b/Club_portal/Club Portal/Club Portal/UI/DailySecheduler/DetailsTab.swift @@ -0,0 +1,52 @@ +// +// DetailsTab.swift +// Club Portal +// +// Created by Umer Tahir on 16/04/2025. +// + +import SwiftUI + +struct DetailRow: View { + let title: String + let value: String + + var body: some View { + VStack(alignment: .leading, spacing: 4) { + Text(title) + .font(.subheadline) + .foregroundColor(.secondary) + Text(value) + .font(.body) + .foregroundColor(.primary) + Divider() + } + } +} + +struct DetailsTab: View { + 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: "Repeating", value: "Every week") + DetailRow(title: "Space assigned", value: "Court #5") + DetailRow(title: "Court fee", value: "$50.0") + DetailRow(title: "Court fee status", value: "Paid") + DetailRow(title: "Note", value: "{content.note}") + } + } + .padding() + .background( + RoundedRectangle(cornerRadius: 12) + .fill(Color(.systemBackground)) + .shadow(color: Color.black.opacity(0.05), radius: 5, x: 0, y: 2) + ) + .padding() + } + } +} diff --git a/Club_portal/Club Portal/Club Portal/UI/DailySecheduler/EventCalendarView.swift b/Club_portal/Club Portal/Club Portal/UI/DailySecheduler/EventCalendarView.swift new file mode 100644 index 0000000..52b9003 --- /dev/null +++ b/Club_portal/Club Portal/Club Portal/UI/DailySecheduler/EventCalendarView.swift @@ -0,0 +1,54 @@ +// +// EventCalendarView.swift +// Club Portal +// +// Created by Umer Tahir on 16/04/2025. +// + + +import SwiftUI +import CalendarKit + +//struct EventCalendarView: View { +// @Binding var selectedDate: Date +// @EnvironmentObject var viewModel: DashViewModel +// @StateObject private var eventStore = CalendarEventStore() +// +// var body: some View { +// CalendarView(eventStore: eventStore) +// .frame(height: 400) +// .cornerRadius(12) +// .onAppear { +// loadEvents() +// } +// .onChange(of: selectedDate) { _ in +// loadEvents() +// } +// } +// +// private func loadEvents() { +// guard let stats = viewModel.dailyReservations?.first else { return } +// eventStore.events.removeAll() +// +// func addEvent(title: String, duration: Double) { +// guard duration > 0 else { return } +// +// let start = Calendar.current.date(bySettingHour: 9, minute: 0, second: 0, of: selectedDate)! +// let end = Calendar.current.date(byAdding: .minute, value: Int(duration * 60), to: start)! +// +// let event = Event() +// event.dateInterval = DateInterval(start: start, end: end) +// event.text = "\(title)" +// event.color = .blue.withAlphaComponent(0.6) +// event.lineBreakMode = .byTruncatingTail +// +// eventStore.events.append(event) +// } +// +// if let lesson = stats.booking { +// addEvent(title: "Lesson", duration: lesson.duration) +// } +// +// eventStore.objectWillChange.send() +// } +//} diff --git a/Club_portal/Club Portal/Club Portal/UI/DailySecheduler/FilterView.swift b/Club_portal/Club Portal/Club Portal/UI/DailySecheduler/FilterView.swift new file mode 100644 index 0000000..a0e40bf --- /dev/null +++ b/Club_portal/Club Portal/Club Portal/UI/DailySecheduler/FilterView.swift @@ -0,0 +1,101 @@ +// +// FilterView.swift +// Club Portal +// +// Created by Umer Tahir on 04/04/2025. +// + + +import SwiftUI + +struct DailyFilterView: View { + @Environment(\.presentationMode) var presentationMode + @State private var selectedSport: String? = "Tennis" + @State private var isDropdownOpen = false + + let sports = ["Tennis", "Pickleball", "Sport 3", "Sport 4"] + + var body: some View { + VStack { + // Close and Title + HStack { + Button("Close") { + presentationMode.wrappedValue.dismiss() + } + Spacer() + Text("Filter") + .font(.headline) + Spacer() + } + .padding() + + // Dropdown Menu + VStack(alignment: .leading, spacing: 10) { + Button(action: { + withAnimation { + isDropdownOpen.toggle() + } + }) { + HStack { + Text("Sport") + .font(.subheadline) + Spacer() + Image(systemName: isDropdownOpen ? "chevron.up" : "chevron.down") + } + .padding() + } + .background(Color(.systemGray6)) + .cornerRadius(8) + + if isDropdownOpen { + VStack(spacing: 8) { + ForEach(sports, id: \..self) { sport in + HStack { + Text(sport) + .padding() + .frame(maxWidth: .infinity, alignment: .leading) + + if selectedSport == sport { + Image(systemName: "checkmark.square.fill") + .foregroundColor(.blue) + } else { + Image(systemName: "square") + } + } + .background(Color.white) + .cornerRadius(8) + .onTapGesture { + selectedSport = sport + } + } + } + .padding(.horizontal) + } + } + .padding() + + Spacer() + + // Apply Button + Button(action: { + presentationMode.wrappedValue.dismiss() + }) { + Text("Apply and close") + .frame(maxWidth: .infinity) + .padding() + .background(Color.blue) + .foregroundColor(.white) + .cornerRadius(10) + } + .padding() + } + .background(Color(.systemGray6).opacity(0.2)) + .edgesIgnoringSafeArea(.bottom) + } +} + +//struct FilterView_Previews: PreviewProvider { +// static var previews: some View { +// FilterView() +// } +//} diff --git a/Club_portal/Club Portal/Club Portal/UI/DailySecheduler/FilterView1.swift b/Club_portal/Club Portal/Club Portal/UI/DailySecheduler/FilterView1.swift new file mode 100644 index 0000000..705da9b --- /dev/null +++ b/Club_portal/Club Portal/Club Portal/UI/DailySecheduler/FilterView1.swift @@ -0,0 +1,106 @@ +// +// FilterScreen.swift +// Club Portal +// +// Created by Umer Tahir on 15/04/2025. +// + + +import SwiftUI + + + +struct ScheduleFilterView: View { + @Environment(\.dismiss) var dismiss + @Binding var navigationPaths: [String] + + @State private var isClubExpanded = true + @State private var selectedClubs: Set = [] // For storing selected clubs + + @State var filterBlock: ((Int) -> Void)? + @EnvironmentObject var dasModel: DashViewModel + + var body: some View { + VStack(spacing: 0) { + ScrollView { + VStack(alignment: .leading, spacing: 20) { + // Club Filter Section + DisclosureGroup(isExpanded: $isClubExpanded) { + VStack(spacing: 10) { + if let clubs = dasModel.club?.courts { + ForEach(clubs, id: \.self) { court in + if let clubName = court.name { + ClubCheckboxRow(clubName: clubName, isSelected: selectedClubs.contains(clubName)) { + if selectedClubs.contains(clubName) { + selectedClubs.remove(clubName) + } else { + selectedClubs.insert(clubName) + } + } + } + } + } + } + .padding(.top, 10) + } label: { + Text("Club") + .font(.headline) + } + .padding() + .background(RoundedRectangle(cornerRadius: 10).fill(Color(.systemGray6))) + .overlay( + RoundedRectangle(cornerRadius: 10) + .stroke(Color(.systemGray4), lineWidth: 1) + ) + .padding(.horizontal) + } + .padding(.top) + } + + Spacer() + + Button(action: { + // Handle apply logic + self.navigationPaths.removeLast() + }) { + Text("Apply and close") + .fontWeight(.semibold) + .frame(maxWidth: .infinity) + .padding() + .background(Colorr.themeBlueColor) + .foregroundColor(.white) + .cornerRadius(10) + .padding(.horizontal) + } + .padding(.vertical, 10) + .background(Color.white) + } + .navigationTitle("Filter") + .navigationBarTitleDisplayMode(.inline) + .background(Color(.systemGray6).ignoresSafeArea()) + } +} + +// MARK: - Club Checkbox Row +struct ClubCheckboxRow: View { + let clubName: String + let isSelected: Bool + let onTap: () -> Void + + var body: some View { + Button(action: onTap) { + HStack { + Text(clubName) + .foregroundColor(.primary) + Spacer() + Image(systemName: isSelected ? "checkmark.square.fill" : "square") + .foregroundColor(isSelected ? Colorr.themeBlueColor : .gray) + } + .padding() + .background( + RoundedRectangle(cornerRadius: 8) + .stroke(isSelected ? Colorr.themeBlueColor : Color.gray.opacity(0.3), lineWidth: 1) + ) + } + } +} diff --git a/Club_portal/Club Portal/Club Portal/UI/DailySecheduler/PlayersTab.swift b/Club_portal/Club Portal/Club Portal/UI/DailySecheduler/PlayersTab.swift new file mode 100644 index 0000000..c180c3d --- /dev/null +++ b/Club_portal/Club Portal/Club Portal/UI/DailySecheduler/PlayersTab.swift @@ -0,0 +1,44 @@ +// +// PlayersTab.swift +// Club Portal +// +// Created by Umer Tahir on 16/04/2025. +// + +import SwiftUI + +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) + } + } + + Spacer() + + if index == 0 { + Image(systemName: "checkmark.seal.fill") + .foregroundColor(.green) + } + } + .padding() + .background(Color(.systemGray6)) + .cornerRadius(10) + } + } + .padding() + } + } +} diff --git a/Club_portal/Club Portal/Club Portal/UI/DailySecheduler/ReservationDetailsView.swift b/Club_portal/Club Portal/Club Portal/UI/DailySecheduler/ReservationDetailsView.swift new file mode 100644 index 0000000..88b8a0a --- /dev/null +++ b/Club_portal/Club Portal/Club Portal/UI/DailySecheduler/ReservationDetailsView.swift @@ -0,0 +1,34 @@ +import SwiftUI + +struct ReservationDetailsView: View { + @State private var selectedTab = "Details" + let tabs = ["Details", "Players", "Coaches"] + + var body: some View { + VStack(spacing: 0) { + Picker("", selection: $selectedTab) { + ForEach(tabs, id: \.self) { tab in + Text(tab).tag(tab) + } + } + .pickerStyle(SegmentedPickerStyle()) + .padding() + + switch selectedTab { + case "Details": + DetailsTab() + case "Players": + PlayersTab() + case "Coaches": + CoachesTab() + default: + Spacer() + Text("Unknown Tab") + Spacer() + } + } + .navigationBarTitle("Reservation details", displayMode: .inline) + } +} + + diff --git a/Club_portal/Club Portal/Club Portal/UI/DailySecheduler/ScheduleView.swift b/Club_portal/Club Portal/Club Portal/UI/DailySecheduler/ScheduleView.swift new file mode 100644 index 0000000..0ad0fa5 --- /dev/null +++ b/Club_portal/Club Portal/Club Portal/UI/DailySecheduler/ScheduleView.swift @@ -0,0 +1,306 @@ +import SwiftUI + +struct Event: Identifiable { + let id = UUID() + let title: String + let startTime: Date + let endTime: Date + let color: Color +} + +struct ScheduleView: View { + @State var navigationPaths: [String] = [] + @State private var selectedDate = Date() + @State private var displayedMonth = Date() + @State private var events: [Event] = [] + @Binding var presentSideMenu: Bool // Optional for side menu + + let hours = Array(8...20) + let calendar = Calendar.current + + @EnvironmentObject var viewModel : DashViewModel + + var body: some View { + NavigationStack(path: $navigationPaths) { + + VStack(spacing: 0) { + // Top Header + HeaderView(presentSideMenu: $presentSideMenu) + Divider() + VStack { + // Search + Filter + searchBar + .padding(.top, 5) + + + // Month + Horizontal Dates + VStack(spacing: 10) { + Divider() + + monthHeader + Divider() + + dateScrollView + } + .padding(.horizontal) + .padding(.bottom) + + Divider() + + // Time Slots with Events + ScrollView { + VStack(spacing: 0) { + ForEach(hours, id: \.self) { hour in + timeSlotRow(hour: hour) + } + } + .padding(.horizontal) + .padding(.bottom, 20) + } + + } + } + .onAppear { + // loadDummyEvents() + viewModel.getDailySched(clubId: 10) + + if let club = self.viewModel.club { + print(club.courts?.count) + }else { + viewModel.getClubProfile() + } + } + .navigationDestination(for: String.self) { value in + if value == "DailyFilterView" { + ScheduleFilterView(navigationPaths: $navigationPaths) { filter in + viewModel.getDailySched(clubId: filter) + } + }else if value == "ReservationDetailsView" { + ReservationDetailsView() + } + + } + } + } + + // MARK: - Components + + var headerBar: some View { + HStack { + Image(systemName: "person.circle.fill") + .resizable() + .frame(width: 36, height: 36) + + Spacer() + + Text("The Royal Club") + .font(.headline) + + Spacer() + + Image(systemName: "arrow.right") + } + .padding() + } + + var searchBar: some View { + HStack { + HStack { + Image(systemName: "magnifyingglass") + .foregroundColor(.gray) + + TextField("search player", text: .constant("")) + .foregroundColor(.primary) + } + .padding(5) + .background(Color(.white)) + .cornerRadius(10) + .overlay( + RoundedRectangle(cornerRadius: 10) + .stroke(Color(.systemGray4), lineWidth: 1) + ) + //.padding(.horizontal) + + Button(action: { + + navigationPaths.append("DailyFilterView") + }) { + HStack(spacing: 4) { + Image("filter 1") + + } + } + } + .padding(.horizontal) + } + + var monthHeader: some View { + HStack { + Button(action: { + displayedMonth = calendar.date(byAdding: .month, value: -1, to: displayedMonth) ?? displayedMonth + }) { + Image("left_arrow") + } + + Spacer() + + Text(formattedMonth(displayedMonth)) + .font(.subheadline) + + Spacer() + + Button(action: { + displayedMonth = calendar.date(byAdding: .month, value: 1, to: displayedMonth) ?? displayedMonth + }) { + Image("right_arrow") + } + } + .padding(.horizontal) + .background(Colorr.lightColor) + + } + + var dateScrollView: some View { + let days = getDatesForMonth(from: displayedMonth) + + return ScrollViewReader { scrollProxy in + ScrollView(.horizontal, showsIndicators: false) { + HStack(spacing: 12) { + ForEach(days, id: \.self) { date in + VStack(spacing: 4) { + Text(shortDayName(from: date)) + .font(.caption) + .foregroundColor(.gray) + + Text(dayNumber(from: date)) + .fontWeight(isSameDay(date, selectedDate) ? .bold : .regular) + .frame(width: 36, height: 36) + .background(isSameDay(date, selectedDate) ? Colorr.themeBlueColor : Color.clear) + .foregroundColor(isSameDay(date, selectedDate) ? .white : .primary) + .clipShape(RoundedCorner()) + } + .onTapGesture { + selectedDate = date + } + .id(calendar.startOfDay(for: date)) + } + } + .padding(.vertical, 4) + .onAppear { + DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { + scrollProxy.scrollTo(calendar.startOfDay(for: selectedDate), anchor: .center) + } + } + } + } + } + + func timeSlotRow(hour: Int) -> some View { + VStack(alignment: .leading, spacing: 4) { + HStack(alignment: .top) { + Text(timeLabel(for: hour)) + .font(.caption) + .frame(width: 50, alignment: .leading) + + ZStack(alignment: .topLeading) { + Rectangle() + .fill(Color(.systemGray6)) + .frame(height: 60) + eventOverlay(for: hour) + } + } + + Divider() + } + } + + func eventOverlay(for hour: Int) -> some View { + VStack(spacing: 4) { + ForEach(events.filter { + calendar.isDate($0.startTime, inSameDayAs: selectedDate) + && calendar.component(.hour, from: $0.startTime) == hour + }) { event in + VStack(alignment: .leading, spacing: 2) { + Text(event.title) + .font(.subheadline) + .bold() + Text("\(timeOnly(event.startTime)) - \(timeOnly(event.endTime))") + .font(.caption) + .foregroundColor(.gray) + } + .padding(8) + .background(event.color.opacity(0.2)) + .foregroundColor(event.color) + .cornerRadius(12) + .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading) + .background(Color.clear) + .onTapGesture { + self.navigationPaths.append("ReservationDetailsView") + } + } + } + .padding(.top, 4) + .frame(maxWidth: .infinity) + } + + // MARK: - Utility + + func loadDummyEvents() { + let baseDate = calendar.startOfDay(for: Date()) + events = [ + Event( + title: "Court reserved", + startTime: calendar.date(bySettingHour: 9, minute: 0, second: 0, of: baseDate)!, + endTime: calendar.date(bySettingHour: 10, minute: 0, second: 0, of: baseDate)!, + color: .green + ), + Event( + title: "Clinic", + startTime: calendar.date(bySettingHour: 11, minute: 30, second: 0, of: baseDate)!, + endTime: calendar.date(bySettingHour: 13, minute: 0, second: 0, of: baseDate)!, + color: .purple + ) + ] + } + + func getDatesForMonth(from date: Date) -> [Date] { + let range = calendar.range(of: .day, in: .month, for: date)! + let start = calendar.date(from: calendar.dateComponents([.year, .month], from: date))! + return range.compactMap { calendar.date(byAdding: .day, value: $0 - 1, to: start) } + } + + func formattedMonth(_ date: Date) -> String { + let formatter = DateFormatter() + formatter.dateFormat = "MMMM yyyy" + return formatter.string(from: date) + } + + func dayNumber(from date: Date) -> String { + let formatter = DateFormatter() + formatter.dateFormat = "dd" + return formatter.string(from: date) + } + + func shortDayName(from date: Date) -> String { + let formatter = DateFormatter() + formatter.dateFormat = "EEE" + return formatter.string(from: date) + } + + func timeOnly(_ date: Date) -> String { + let formatter = DateFormatter() + formatter.dateFormat = "h:mm a" + return formatter.string(from: date) + } + + func timeLabel(for hour: Int) -> String { + let date = calendar.date(bySettingHour: hour, minute: 0, second: 0, of: Date())! + let formatter = DateFormatter() + formatter.dateFormat = "h a" + return formatter.string(from: date) + } + + func isSameDay(_ a: Date, _ b: Date) -> Bool { + calendar.isDate(a, inSameDayAs: b) + } +} diff --git a/Club_portal/Club Portal/Club Portal/UI/Dashboard/AnalyticsView.swift b/Club_portal/Club Portal/Club Portal/UI/Dashboard/AnalyticsView.swift new file mode 100644 index 0000000..031f095 --- /dev/null +++ b/Club_portal/Club Portal/Club Portal/UI/Dashboard/AnalyticsView.swift @@ -0,0 +1,241 @@ +// +// AnalyticsView.swift +// Club Portal +// +// Created by Umer Tahir on 04/04/2025. +// + + +import SwiftUI +import Charts + +struct AnalyticsView: View { + @State private var selectedTab = 1 + @EnvironmentObject var viewModel : DashViewModel + var body: some View { + NavigationView { + VStack { + //HeaderView() + // SegmentedControlView() + ScrollView { + VStack(spacing: 15) { +// PieChartCard(data: self.viewModel.statResponse?.revenueByModule ?? []) +// BarChartCard(data: self.viewModel.statResponse?.revenueHeatMap ?? []) + PieChartCard1() + BarChartCard1() + } + .padding(.horizontal) + } + } + .background(Color(.systemGray6)) + .navigationBarHidden(true) + .onAppear() { + if let data = self.viewModel.statResponse?.revenueHeatMap { + print(data.count) + } + } + } + } +} + +// MARK: - Pie Chart Card +struct PieChartCard1: View { + var pieData: [RevenueByModule]? + + let data: [(name: String, value: Double, color: Color)] = [ + ("Module A", 30, Colorr.pieBlue), + ("Module B", 70, Colorr.pieGreen) + ] + + var body: some View { + CardView(title: "% made through each module") { + VStack { + HStack { + LegendView(color: Colorr.pieBlue, text: "Module A") + LegendView(color: Colorr.pieGreen, text: "Module B") + } + .padding(.top, 10) + + Chart(data, id: \.name) { item in + SectorMark( + angle: .value("Value", item.value), + innerRadius: .ratio(0.5), + angularInset: 2 + ) + .foregroundStyle(item.color) + } + .chartLegend(.hidden) + .frame(height: 180) + + + } + } + .onAppear() { + if let d = self.pieData { + + d.forEach { r in + // let data = (r.module.) + } + } + } + } +} + +struct PieChartCard: View { + let data: [RevenueByModule] + + var body: some View { + let chartData = data.map { + (name: $0.module, value: $0.revenue ?? 0, color: randomColor()) + } + + CardView(title: "% made through each module") { + VStack { + HStack { + ForEach(chartData, id: \.name) { item in + LegendView(color: item.color, text: item.name) + } + .padding(.top, 5) + } + Chart(chartData, id: \.name) { item in + SectorMark( + angle: .value("Value", item.value), + innerRadius: .ratio(0.5), + angularInset: 2 + ) + .foregroundStyle(item.color) + } + .chartLegend(.hidden) + .frame(height: 180) + } + } + } + + func randomColor() -> Color { + let colors: [Color] = [Colorr.pieBlue, Colorr.pieGreen, .orange, .purple, .pink, .mint] + return colors.randomElement() ?? .blue + } +} + +struct BarChartCard1: View { + var data: [RevenueByModule]? + + let barData: [ModuleRevenue] = [ + ModuleRevenue(day: "Wed", module: "Module A", value: 200), + ModuleRevenue(day: "Wed", module: "Module B", value: 500), + ModuleRevenue(day: "Thu", module: "Module A", value: 50), + ModuleRevenue(day: "Thu", module: "Module B", value: 300), + ModuleRevenue(day: "Fri", module: "Module A", value: 30), + ModuleRevenue(day: "Fri", module: "Module B", value: 200), + ModuleRevenue(day: "Sat", module: "Module A", value: 400), + ModuleRevenue(day: "Sat", module: "Module B", value: 250), + ModuleRevenue(day: "Sun", module: "Module A", value: 180), + ModuleRevenue(day: "Sun", module: "Module B", value: 600) + ] + + var body: some View { + CardView(title: "Revenue from each module") { + Chart(barData) { data in + BarMark( + x: .value("Day", data.day), + y: .value("Revenue", data.value) + ) + .foregroundStyle(data.module == "Module A" ? Colorr.pieBlue : Colorr.pieGreen) + .position(by: .value("Module", data.module)) + } + .chartYAxis { + AxisMarks(position: .leading) + } + .frame(height: 200) + } + } +} + +struct BarChartCard: View { + let data: [RevenueHeatMap] + + var body: some View { + let barData: [ModuleRevenue] = data.map { + ModuleRevenue(day: $0.date.toWeekday()!, module: $0.date.toWeekday()!, value: $0.total_revenue ) + } + + CardView(title: "Revenue from each module") { + Chart(barData) { data in + BarMark( + x: .value("Day", data.day), + y: .value("Revenue", data.value) + ) + .foregroundStyle( + data.module == "Module A" ? Colorr.pieBlue : Colorr.pieGreen + ) + } + .chartYAxis { + AxisMarks(position: .leading) + } + .frame(height: 200) + } + + } +} + +// MARK: - Common Components + +// Reusable card for charts +struct CardView: View { + let title: String + @ViewBuilder let content: Content + + var body: some View { + VStack(alignment: .leading) { + HStack { + Text(title) + .font(.subheadline) + .bold() + Spacer() + // Image(systemName: "arrow.up.right.square") + } + .padding(.bottom, 5) + + content + } + .padding() + .background(Color.white) + .cornerRadius(10) + .shadow(color: .gray.opacity(0.2), radius: 5) + } +} + +// Legend for pie chart +struct LegendView: View { + let color: Color + let text: String + + var body: some View { + HStack { + Circle() + .fill(color) + .frame(width: 12, height: 12) + Text(text) + .font(.footnote) + } + } +} + + + + + +// MARK: - Preview +struct AnalyticsView_Previews: PreviewProvider { + static var previews: some View { + AnalyticsView() + } +} + +// MARK: - Model +struct ModuleRevenue: Identifiable, Hashable { + var id = UUID() + var day: String + var module: String + var value: Double +} diff --git a/Club_portal/Club Portal/Club Portal/UI/Dashboard/DashboardView.swift b/Club_portal/Club Portal/Club Portal/UI/Dashboard/DashboardView.swift new file mode 100644 index 0000000..5fa6771 --- /dev/null +++ b/Club_portal/Club Portal/Club Portal/UI/Dashboard/DashboardView.swift @@ -0,0 +1,280 @@ +// +// SummaryView.swift +// Club Portal +// +// Created by Umer Tahir on 04/04/2025. +// + + +import SwiftUI + +struct SummaryView: View { + //@Binding var selectedTab: Int + @State var navigationPaths: [String] = [] + @State var selectedTab = 0 + @EnvironmentObject var viewModel : DashViewModel + @Binding var presentSideMenu: Bool // Optional for side menu + @State var filterCount = 0 + var body: some View { + NavigationStack(path: $navigationPaths) { + VStack { + VStack { + HeaderView(presentSideMenu: $presentSideMenu) + SegmentedControlView(selectedTab: $selectedTab) + } + .background(Color(.white)) + + if selectedTab == 0 { + FilterView(navigationPaths: $navigationPaths, filterCount: $filterCount) + CourtUtilizationView() + + 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: "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 )") + + + } + .padding(.horizontal) + } + }else { + AnalyticsView() + + } + // .background(Color(UIColor.systemGroupedBackground)) + } + .background(Color(.systemGray6)) + .navigationBarHidden(true) + .navigationDestination(for: String.self) { value in + if value == "FilterScreen" { + FilterScreen(navigationPaths: $navigationPaths) { filter, filterCount in + self.filterCount = filterCount + viewModel.getStats(completion: {}) + } + } + } + .onAppear(){ + viewModel.getStats { + if viewModel.sessionExpired { + AppSettings.token = "" + presentSideMenu = false + } + } + // viewModel.getClubProfile() + // viewModel.getProfile() + + } + + } + } +} + +// MARK: - Header +struct HeaderView: View { + @Binding var presentSideMenu: Bool + @State private var showLogoutConfirmation = false + + var body: some View { + HStack { + Image(systemName: "person.circle.fill") + .resizable() + .frame(width: 32, height: 32) + .onTapGesture { + presentSideMenu.toggle() + } + + Spacer() + + Text("The Royal Club") + .font(.headline) + + Spacer() + + Image("logout") + .onTapGesture { + showLogoutConfirmation = true + } + } + .padding() + .alert("Are you sure you want to logout?", isPresented: $showLogoutConfirmation) { + Button("Logout", role: .destructive) { + // Your logout logic here + print("User logged out") + AppSettings.token = "" + + } + Button("Cancel", role: .cancel) { } + } + } +} + +// MARK: - Segmented Control +struct SegmentedControlView: View { + @Binding var selectedTab : Int + let options = ["Summary", "Analytics"] + + @EnvironmentObject var viewModel : DashViewModel + + var body: some View { + Picker(selection: $selectedTab, label: Text("Options")) { + ForEach(options.indices, id: \.self) { index in + Text(self.options[index]).tag(index) + } + + } + .pickerStyle(SegmentedPickerStyle()) + .padding(.horizontal) + .tint(.gray.opacity(0.5)) + + Divider() + + + } +} + + + +// MARK: - Filter View +struct FilterView: View { + @Binding var navigationPaths: [String] + @State var isFilterScreenPresented = false + @Binding var filterCount : Int + @EnvironmentObject var viewModel : DashViewModel + + var body: some View { + + VStack(alignment: .leading) { + HStack { + Spacer() + .frame(width: 20) + Button(action: { isFilterScreenPresented = true }) { + HStack(spacing: 5) { + Image("filter") + Text("Filter") + .font(.footnote) + .foregroundColor(.gray) + + Text("\(self.filterCount ) ") + .font(.caption) + .foregroundColor(.white) + .padding(.horizontal, 8) // Horizontal padding for better width + .padding(.vertical, 4) // Vertical padding for height + .background( + Capsule() + .fill(Colorr.themeBlueColor) // Use your custom color + ) + } + .padding(8) // Padding inside the button + .background(Color.white) + .cornerRadius(10) + .shadow(radius: 2) + .onTapGesture { + self.navigationPaths.append("FilterScreen") + } + + + } + Text("Clear all") + .onTapGesture { + filterCount = 0 + viewModel.dashboardfilters = "" + } + + Spacer() + + } + + .padding(.bottom, 5) + } + Divider() + } +} + +// MARK: - Court Utilization +struct CourtUtilizationView: View { + var body: some View { + HStack { + Text("Court utilization") + .font(.subheadline) + .bold() + Spacer() + Text("66%") + .font(.subheadline) + .bold() + } + .padding() + .background(Color.white) + .cornerRadius(10) + .padding(.horizontal) + } +} + +// MARK: - Reservation Card +struct ReservationCard: View { + let title: String + let hours: String + let revenue: String + + var body: some View { + VStack(alignment: .leading, spacing: 10) { + Text(title) + .font(.body) + // .padding(.top) + + HStack { + Spacer() + InfoView(icon: "clock", label: "Hours", value: hours) + .padding(.leading) + Spacer() + .frame(width: 150) + InfoView(icon: "cash", label: "Revenue", value: revenue) + .padding(.trailing) + Spacer() + + + } + .padding(.bottom) + } + .padding() + .background(Color.white) + .cornerRadius(10) + .shadow(color: .gray.opacity(0.2), radius: 5) + } +} + +// MARK: - Info View +struct InfoView: View { + let icon: String + let label: String + let value: String + + var body: some View { + VStack { + Image( icon) + .font(.title2) + .foregroundColor(.blue) + + Text(label) + .font(.footnote) + .foregroundColor(.gray) + + Text(value) + .font(.headline) + .bold() + } + } +} + +// MARK: - Preview +//struct SummaryView_Previews: PreviewProvider { +// static var previews: some View { +// SummaryView() +// } +//} diff --git a/Club_portal/Club Portal/Club Portal/UI/Dashboard/FilterScreen.swift b/Club_portal/Club Portal/Club Portal/UI/Dashboard/FilterScreen.swift new file mode 100644 index 0000000..52fb97b --- /dev/null +++ b/Club_portal/Club Portal/Club Portal/UI/Dashboard/FilterScreen.swift @@ -0,0 +1,95 @@ +// +// FilterScreen.swift +// Club Portal +// +// Created by Umer Tahir on 14/04/2025. +// + + +import SwiftUI + +struct FilterScreen: View { + @Binding var navigationPaths: [String] + @State private var startTime: Date = Date() + @State private var endTime: Date = Date() + @State private var startDate: Date = Date() + @State private var endDate: Date = Date() + + @State private var selectedDuration: String = "This Day" + let durationOptions = ["This Day", "This Week", "This Month", "This Year"] + + @State var filterBlock : ((String, Int) ->Void)? + + var body: some View { + NavigationView { + Form { + Section(header: Text("Time Range")) { + DatePicker("Start Time", selection: $startTime, displayedComponents: .hourAndMinute) + DatePicker("End Time", selection: $endTime, displayedComponents: .hourAndMinute) + } + + Section(header: Text("Date Range")) { + DatePicker("Start Date", selection: $startDate, displayedComponents: .date) + 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 { + Button(action: { + applyFilter() + }) { + HStack { + Spacer() + Text("Apply") + .fontWeight(.bold) + Spacer() + } + } + } + } + .navigationTitle("Filter") + } + } + + private func applyFilter() { + let dateFormatter = DateFormatter() + dateFormatter.dateFormat = "yyyy-MM-dd" + + 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) + + let query = """ + start_date=\(startDateStr)&\ + end_date=\(endDateStr)&\ + start_time=\(startTimeStr)&\ + end_time=\(endTimeStr) + """ + + // 🔢 Count how many filters are applied + var filterCount = 0 + if !Calendar.current.isDateInToday(startDate) { filterCount += 1 } + if !Calendar.current.isDateInToday(endDate) { filterCount += 1 } + if !Calendar.current.isDate(startTime, equalTo: Date(), toGranularity: .minute) { filterCount += 1 } + if !Calendar.current.isDate(endTime, equalTo: Date(), toGranularity: .minute) { filterCount += 1 } + if selectedDuration != "This Day" { filterCount += 1 } + + print("Generated Filter Query: \(query)") + print("Filter count: \(filterCount)") + + self.filterBlock?(query, filterCount) + self.navigationPaths.removeLast() + } +} diff --git a/Club_portal/Club Portal/Club Portal/UI/Main/Club_PortalApp.swift b/Club_portal/Club Portal/Club Portal/UI/Main/Club_PortalApp.swift new file mode 100644 index 0000000..e7de98a --- /dev/null +++ b/Club_portal/Club Portal/Club Portal/UI/Main/Club_PortalApp.swift @@ -0,0 +1,23 @@ +// +// Club_PortalApp.swift +// Club Portal +// +// Created by Umer Tahir on 04/04/2025. +// + +import SwiftUI + +@main +struct Club_PortalApp: App { + + var body: some Scene { + WindowGroup { + WelcomeView() + .environmentObject(AuthViewModel()) + .environmentObject(DashViewModel()) + .preferredColorScheme(.light) // or `.light or` `nil` to use the current scheme + + } + + } +} diff --git a/Club_portal/Club Portal/Club Portal/UI/Main/ContentView.swift b/Club_portal/Club Portal/Club Portal/UI/Main/ContentView.swift new file mode 100644 index 0000000..b378abb --- /dev/null +++ b/Club_portal/Club Portal/Club Portal/UI/Main/ContentView.swift @@ -0,0 +1,24 @@ +// +// ContentView.swift +// Club Portal +// +// Created by Umer Tahir on 04/04/2025. +// + +import SwiftUI + +struct ContentView: View { + var body: some View { + VStack { + Image(systemName: "globe") + .imageScale(.large) + .foregroundStyle(.tint) + Text("Hello, world!") + } + .padding() + } +} + +#Preview { + ContentView() +} diff --git a/Club_portal/Club Portal/Club Portal/UI/OnBoarding/ForgotPasswordView.swift b/Club_portal/Club Portal/Club Portal/UI/OnBoarding/ForgotPasswordView.swift new file mode 100644 index 0000000..a51752b --- /dev/null +++ b/Club_portal/Club Portal/Club Portal/UI/OnBoarding/ForgotPasswordView.swift @@ -0,0 +1,105 @@ +// +// ForgotPasswordView.swift +// Club Portal +// +// Created by Umer Tahir on 10/04/2025. +// + + +import SwiftUI + +struct ForgotPasswordView: View { + @Binding var navigationPaths: [String] + + @State private var showAlert = false + @Environment(\.presentationMode) var presentationMode + @EnvironmentObject var viewModel: AuthViewModel + @State private var navigateToOtp = false + @State var showError = false + + var body: some View { + // NavigationStack { // ✅ Change NavigationView to NavigationStack + HStack(spacing: 10) { + Image(systemName: "arrow.left") + .foregroundColor(.black) + .onTapGesture { + presentationMode.wrappedValue.dismiss() + } + Spacer() + Text("Forgot password") + .bold() + Spacer() + } + .padding() + ScrollView { + VStack(spacing: 20) { + // Title + Image("lock") + .resizable() + .frame(width: 80, height: 80) + .foregroundColor(.green) + .padding(.top, 40) + + // Phone Number Input + VStack(alignment: .leading) { + Text("Email") + .foregroundColor(.black) + .padding(.leading, 10) + + TextField("Email", text: $viewModel.email) + .keyboardType(.emailAddress) + .autocapitalization(.none) + .padding(12) + .overlay( + RoundedRectangle(cornerRadius: 8) + .stroke(viewModel.email.isEmpty || viewModel.isEmailValid ? Color.gray : Color.red, lineWidth: 1) + ) + .cornerRadius(8) + } + + // Description Text + Text("Enter the email associated with your account and we will send you a code to reset your password.") + .font(.footnote) + .foregroundColor(.gray) + .padding(.top, 5) + } + .padding(.horizontal, 10) + + // Request Password Reset Code Button + Button(action: { + viewModel.forgotPassword { isSuccess in + if isSuccess { + self.navigationPaths.append("OtpView") + } else { + showError = true + } + } + }) { + Text("Request password reset code") + .font(.headline) + .foregroundColor(.white) + .frame(maxWidth: .infinity) + .padding() + .background(Color("green")) + .cornerRadius(8) + } + .disabled(!viewModel.isEmailValid) + .opacity(viewModel.isEmailValid ? 1 : 0.5) + + Spacer() + } + .padding(.horizontal, 20) + .toast(message: viewModel.toastMessage, isShowing: $viewModel.showToast) + .overlay( + Group { + if viewModel.isLoading { + LoadingView() + } + } + ) + .navigationBarBackButtonHidden(true) + } + + + } + diff --git a/Club_portal/Club Portal/Club Portal/UI/OnBoarding/OtpView.swift b/Club_portal/Club Portal/Club Portal/UI/OnBoarding/OtpView.swift new file mode 100644 index 0000000..9824588 --- /dev/null +++ b/Club_portal/Club Portal/Club Portal/UI/OnBoarding/OtpView.swift @@ -0,0 +1,159 @@ +// +// OtpView.swift +// Club Portal +// +// Created by Umer Tahir on 10/04/2025. +// + + + + +import SwiftUI + +struct OtpView: View { + @EnvironmentObject var viewModel : AuthViewModel + @Binding var navPaths : [String] + @State private var code = ["", "", "", "", "",""] // Array to hold each digit of the OTP + // Accept phone number as a parameter + @State private var showAlert = false + @State private var navigateToNext = false // State to control navigation + @Environment(\.presentationMode) var presentationMode + // @State var otpCode = "" + @FocusState private var focusedField: Int? + + var body: some View { + HStack(spacing: 10) { + Image(systemName: "arrow.left") + .foregroundColor(.black) + .onTapGesture { + presentationMode.wrappedValue.dismiss() + } + Spacer() + Text("Verify security code") + .bold() + Spacer() + } + .padding() + ScrollView { + VStack(spacing: 20) { + // Title + Image("otp") + .resizable() + .frame(width: 80, height: 80) + .foregroundColor(.green) + .padding(.top, 40) + + // Security Code Description + VStack(alignment: .leading) { + Text("Security code") + .foregroundColor(.black) + .padding(.leading, 10) + + Text("We’ve sent a verification code to: \(viewModel.email)") + .font(.footnote) + .foregroundColor(.gray) + .padding(.top, 5) + } + .padding(.horizontal, 20) +// if viewModel.isFetching { +// ProgressView("Login...") +// } + // OTP Input Fields + HStack(spacing: 10) { + ForEach(0..<6) { index in + TextField("", text: $code[index]) + .keyboardType(.numberPad) + .frame(width: 50, height: 50) + //.background(Color.gray.opacity(0.2)) + .overlay( + RoundedRectangle(cornerRadius: 8) + .stroke(Color.gray, lineWidth: 1) + ) + .cornerRadius(8) + .multilineTextAlignment(.center) + .focused($focusedField, equals: index) // Bind focus state + .onChange(of: code[index]) { newValue in + if newValue.count > 1 { + code[index] = String(newValue.prefix(1)) // Limit to one character + } + + if !newValue.isEmpty && index < 5 { + // Move to the next field if not the last one + focusedField = index + 1 + } else if newValue.isEmpty && index > 0 { + // Move focus back if the user deletes the input + focusedField = index - 1 + } + } + } + } + .padding(.horizontal, 20) + .onAppear { + // Set initial focus to the first text field + focusedField = 0 + } + + // Resend Code Link + HStack { + Text("Didn’t get the text?") + .foregroundColor(.gray) + + Button(action: { + // Handle resend code action +// viewModel.forgotUsingPhoneNumber { isSuccess in +// if isSuccess { +// DispatchQueue.main.async { +// // navigateToOtp = true +// // viewModel.phoneNumber = "\(viewModel.countryCode) \(viewModel.phone)" +// } +// }else { +// } +// } + }) { + Text("Resend") + .foregroundColor(Color("green")) + .underline() // Underline the text + } + } + .padding(.leading, 10) + + // Continue Button + Button(action: { + let otpCode = code.joined() + viewModel.otpCode = otpCode + navPaths.append("SetNewPasswordView") + }) { + Text("Continue") + .font(.headline) + .foregroundColor(.white) + .frame(maxWidth: .infinity) + .padding() + .background(Color("green")) + .cornerRadius(8) + } + .disabled(code.joined().count != 6) + .opacity(code.joined().count == 6 ? 1 : 0.5) + .padding( 10) + .alert(isPresented: $showAlert) { + Alert(title: Text("Incomplete OTP"), + message: Text("Please enter all digits of the OTP."), + dismissButton: .default(Text("OK"))) + } + + Spacer() + } + .padding(.horizontal, 20) + } + .navigationTitle("") + // .toast(isPresented: $viewModel.isError, message: viewModel.errorMessage) + .navigationBarBackButtonHidden(true) + } + func verifyCode(){ +// viewModel.reset { +// if viewModel.isError == false { +// navPaths.append("SetNewPasswordView") +// } +// } + } +} + diff --git a/Club_portal/Club Portal/Club Portal/UI/OnBoarding/SetNewPasswordView.swift b/Club_portal/Club Portal/Club Portal/UI/OnBoarding/SetNewPasswordView.swift new file mode 100644 index 0000000..b0406e1 --- /dev/null +++ b/Club_portal/Club Portal/Club Portal/UI/OnBoarding/SetNewPasswordView.swift @@ -0,0 +1,167 @@ +// +// SetNewPasswordView.swift +// Club Portal +// +// Created by Umer Tahir on 10/04/2025. +// + + + + +import SwiftUI + +struct SetNewPasswordView: View { + @Binding var navigationPaths: [String] + + + @State private var showNewPassword = false + @State private var showConfirmPassword = false + @Environment(\.presentationMode) var presentationMode + @EnvironmentObject var viewModel: AuthViewModel + @State private var showAlert = false + @State private var navigateToSignIn = false // State to control navigation to SignInView + + + + var body: some View { + HStack(spacing: 10) { + Image(systemName: "arrow.left") + .foregroundColor(.black) + .onTapGesture { + presentationMode.wrappedValue.dismiss() + } + Spacer() + Text("Set new password") + .bold() + Spacer() + } + .padding() + ScrollView { + VStack(spacing: 20) { + // Title + Image("lock") + .resizable() + .frame(width: 80, height: 80) + .foregroundColor(.green) + .padding(.top, 40) + + // New Password Field + VStack(alignment: .leading) { + Text("Set new password") + .foregroundColor(.black) + .padding(.leading, 10) + + ZStack { + if showNewPassword { + TextField("New Password", text: $viewModel.newPassword) + } else { + SecureField("New Password", text: $viewModel.newPassword) + } + } + .padding(12) + .overlay( + RoundedRectangle(cornerRadius: 8) + .stroke(Color.gray, lineWidth: 1) + ) + .cornerRadius(8) + .overlay( + Button(action: { + showNewPassword.toggle() + }) { + Image(systemName: showNewPassword ? "eye.slash" : "eye") + .foregroundColor(.gray) + .padding(.trailing, 10) + }, alignment: .trailing + ) + } + .padding(.horizontal, 20) + + // Confirm Password Field + VStack(alignment: .leading) { + Text("Confirm new password") + .foregroundColor(.black) + .padding(.leading, 10) + + ZStack { + if showConfirmPassword { + TextField("Confirm Password", text: $viewModel.confirmPassword) + + } else { + SecureField("Confirm Password", text: $viewModel.confirmPassword) + + } + } + .padding(12) + .overlay( + RoundedRectangle(cornerRadius: 8) + .stroke(Color.gray, lineWidth: 1) + ) + .cornerRadius(8) + .overlay( + Button(action: { + showConfirmPassword.toggle() + }) { + Image(systemName: showConfirmPassword ? "eye.slash" : "eye") + .foregroundColor(.gray) + .padding(.trailing, 10) + }, alignment: .trailing + ) + } + .padding(.horizontal, 20) + + Button(action: { + viewModel.resetPassword { success in + if success { + self.navigationPaths.removeAll() + } + } + }) { + Text("Set new password") + .font(.headline) + .foregroundColor(.white) + .frame(maxWidth: .infinity) + .padding() + .background(Color("green")) + .cornerRadius(8) + } + .disabled(!viewModel.canSubmitNewPassword) + .opacity(viewModel.canSubmitNewPassword ? 1 : 0.5) + + .padding(.vertical, 10) + + Spacer() + } + .padding(.horizontal, 20) +// if viewModel.isFetching { +// ProgressView("Loading...") +// } + } + .navigationTitle("") + .navigationBarTitleDisplayMode(.inline) + .navigationBarBackButtonHidden(true) // Hide the back button + .alert(isPresented: $showAlert) { + Alert( + title: Text("Alert"), + message: Text(viewModel.showToast ? viewModel.toastMessage : viewModel.toastMessage), + dismissButton: .default(Text("OK")) { + if !viewModel.showToast { + // navigateToSignIn = true // Navigate to SignInView on success + self.navigationPaths.removeAll() + } + } + ) + } + .toast(message: viewModel.toastMessage, isShowing: $viewModel.showToast) + .overlay( + Group { + if viewModel.isLoading { + LoadingView() + } + } + ) + } +} + +//#Preview { +// SetNewPasswordView() +//} diff --git a/Club_portal/Club Portal/Club Portal/UI/OnBoarding/SigninView.swift b/Club_portal/Club Portal/Club Portal/UI/OnBoarding/SigninView.swift new file mode 100644 index 0000000..932a4db --- /dev/null +++ b/Club_portal/Club Portal/Club Portal/UI/OnBoarding/SigninView.swift @@ -0,0 +1,164 @@ +// +// SigninView.swift +// Club Portal +// +// Created by Umer Tahir on 09/04/2025. +// + + + +import SwiftUI +import LocalAuthentication + +struct SigninView: View { + @State var navigationPaths: [String] = [] + + @State private var keepLoggedIn = false + @State private var showPassword = false + @EnvironmentObject var viewModel: AuthViewModel + + var body: some View { + NavigationStack(path: $navigationPaths) { + ScrollView { + + + VStack(spacing: 20) { + // Welcome Text + Image("ball") + .resizable() + .frame(width: 80, height: 80) + .foregroundColor(.green) + .padding(.top, 40) + + + // Email and Password Fields + CustomTextField(placeholder: "Email", text: $viewModel.email, keyboardType: .emailAddress) + + VStack(alignment: .leading) { + Text("Password") + .foregroundColor(.black) + .padding(.leading, 10) + + + + ZStack { + if showPassword { + TextField("Password", text: $viewModel.password) + + } else { + SecureField("Password", text: $viewModel.password) + + } + } + .padding(12) + .overlay( + RoundedRectangle(cornerRadius: 8) + .stroke(Color.gray, lineWidth: 1) + ) + .cornerRadius(8) + + .overlay( + Button(action: { + showPassword.toggle() + }) { + Image(systemName: showPassword ? "eye.slash" : "eye") + .foregroundColor(.gray) + .padding(.trailing, 10) + }, alignment: .trailing + ) + } + .padding(0) + + // Forgot Password Link + HStack { + + Button(action: { + // Push SignUp destination onto the navigation path + navigationPaths.append("ForgotPassword") + }) { + Text("Forgot Password") + .foregroundColor(.black) + .underline() + } + + Spacer() + } + + // Keep me logged in Checkbox + HStack { + Button(action: { + keepLoggedIn.toggle() + viewModel.isRefresh.toggle() + AppSettings.keepLoginIn.toggle() + }) { + Image(systemName: AppSettings.keepLoginIn ? "checkmark.square" : "square") + .foregroundColor(Color("blue")) + } + Text("Keep me logged in for 30 days") + .foregroundColor(.black) + .onTapGesture { + viewModel.isRefresh.toggle() + + } + } + + // Log In Button + Button(action: { + viewModel.login {_ in + // Navigate to the Dashboard on successful login + navigationPaths.removeAll() + navigationPaths.append("Dashboard") + } + }) { + Text("Log in") + .font(.headline) + .foregroundColor(.white) + .frame(maxWidth: .infinity) + .padding() + .background(viewModel.canLogin ? Color("green") : Color.gray) + .cornerRadius(8) + } + .disabled(!viewModel.canLogin) + .padding(.vertical, 10) + + // Sign Up Link + + } + .padding(.horizontal, 20) + .onAppear(){ + + } + } + .navigationTitle("Welcome back") + .navigationBarTitleDisplayMode(.inline) + .navigationBarBackButtonHidden(true) + .navigationBarBackButtonHidden(true) + .navigationDestination(for: String.self) { value in + if value == "ForgotPassword" { + ForgotPasswordView(navigationPaths: $navigationPaths) + } else if value == "OtpView" { + OtpView(navPaths: $navigationPaths) + } else if value == "SetNewPasswordView" { + SetNewPasswordView(navigationPaths: $navigationPaths) + } else if value == "Dashboard" { + WelcomeView() // Navigate to the Dashboard screen + } + } + .toast(message: viewModel.toastMessage, isShowing: $viewModel.showToast) + .overlay( + Group { + if viewModel.isLoading { + LoadingView() + } + } + ) + } + } + + +} + + +#Preview { + SigninView() +} diff --git a/Club_portal/Club Portal/Club Portal/UI/SideMenu/SideMenu.swift b/Club_portal/Club Portal/Club Portal/UI/SideMenu/SideMenu.swift new file mode 100644 index 0000000..578fbbc --- /dev/null +++ b/Club_portal/Club Portal/Club Portal/UI/SideMenu/SideMenu.swift @@ -0,0 +1,32 @@ +// +// SideMenu.swift +// +// + +import SwiftUI + +struct SideMenu: View { + @Binding var isShowing: Bool + var content: AnyView + var edgeTransition: AnyTransition = .move(edge: .leading) + var body: some View { + ZStack(alignment: .bottom) { + if (isShowing) { + Color.black + .opacity(0.3) + .ignoresSafeArea() + .onTapGesture { + isShowing.toggle() + } + content + .transition(edgeTransition) + .background( + Color.clear + ) + } + } + .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .bottom) + .ignoresSafeArea() + .animation(.easeInOut, value: isShowing) + } +} diff --git a/Club_portal/Club Portal/Club Portal/UI/SideMenu/SideMenuView.swift b/Club_portal/Club Portal/Club Portal/UI/SideMenu/SideMenuView.swift new file mode 100644 index 0000000..51d8e77 --- /dev/null +++ b/Club_portal/Club Portal/Club Portal/UI/SideMenu/SideMenuView.swift @@ -0,0 +1,206 @@ +// +// SideMenuView.swift +// +// + +import SwiftUI + +enum SideMenuRowType: Int, CaseIterable { + case logout + + + var title: String { + switch self { + case .logout: + return "Logout" + + } + } + + var iconName: String { + switch self { + case .logout: + return "logout" + + } + } +} + +struct SideMenuView: View { + @Binding var selectedSideMenuTab: Int? + @Binding var presentSideMenu: Bool + @EnvironmentObject var userAuth: AuthViewModel + @State private var isActive = false // Track active navigation state + @State var nav = [String]() + @State var isUpgrde = false + + var body: some View { + HStack { + ZStack { + Rectangle() + .fill(.white) + .frame(width: 320) + .shadow(color: .purple.opacity(0.1), radius: 5, x: 0, y: 3) + + VStack(alignment: .leading, spacing: 0) { + HStack { + ProfileHead() + .frame(height: 50) + .padding(.bottom, 30) + + } + + Spacer() + .frame(height: 40) + ForEach(SideMenuRowType.allCases, id: \.self) { row in + + RowView(isSelected: selectedSideMenuTab == row.rawValue, imageName: row.iconName, title: row.title, action: { + + if row.title == "Logout" { + // userAuth.logout() + AppSettings.token = "" + presentSideMenu = false + } + }) + + } + + Spacer() + + HStack{ + Button(action: + { + ConstantData.openLink(urlString: ConstantData.termsLink) + }) { + Text("Terms and Conditions") + .font(.footnote) + .foregroundColor(.gray.opacity(0.7)) + .padding(.leading) + } + + Spacer() + + Button(action: { + ConstantData.openLink(urlString: ConstantData.privacyLink) + }) { + Text("Privacy Policy") + .font(.footnote) + .foregroundColor(.gray.opacity(0.7)) + .padding(.trailing) + } + + } + Text("v.\(appVersion)") + .font(.footnote) + .padding(.leading) + .foregroundColor(.gray.opacity(0.7)) + Spacer() + .frame(height: 50) + } + .padding(.top, 75) + .frame(width: 320) + .background(Color.white) + .background( + NavigationLink( + destination: self.handleMenuSelection(for: selectedSideMenuTab ?? 0), + isActive: $isActive // Bind to the state + ) { + EmptyView() + } + ) + + } + + Spacer() + } + .background(.clear) + } + private var appVersion: String { + let version = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "N/A" + let build = Bundle.main.infoDictionary?["CFBundleVersion"] as? String ?? "N/A" + return "\(version) (\(build))" + } + + + func ProfileHead() -> some View { + VStack(alignment: .leading) { + HStack { + ProfileImageView(imageUrl: AppSettings.photo, size: 50) + .padding(.leading) + + Spacer() + Image("menuLeft") + .padding(.trailing) + .onTapGesture { + presentSideMenu.toggle() + + } + + } + + Text("John Smith") + .padding(.leading) + .font(.system(size: 18, weight: .bold)) + .foregroundColor(.black) + + Text("John@gmail.com") + .padding(.leading) + .font(.system(size: 14, weight: .semibold)) + .foregroundColor(.gray) + } + .padding(.leading) + } + + func RowView(isSelected: Bool, imageName: String, title: String, action: @escaping (() -> ())) -> some View { + Button { + action() + } label: { + VStack(alignment: .leading, spacing: 0) { + HStack(spacing: 20) { + Rectangle() + .fill(isSelected ? .gray : .white) + .frame(width: 5) + + ZStack { + Image(imageName) + .resizable() + .renderingMode(.template) + .foregroundColor(Colorr.lableColor) + .frame(width: 26, height: 26) + } + .frame(width: 30, height: 30) + + Text(title) + .font(.system(size: 14, weight: .regular)) + .foregroundColor(.black) + + // Spacer() + } + .frame(height: 40) + + // Move Divider outside HStack to be a full-width line + if title.lowercased() == "billing" { + Divider() + .background(Colorr.lightColor) + .padding(.horizontal) + .padding(.vertical) + + } + } + } + } + + private func handleMenuSelection(for tab: Int) -> some View { + switch tab { + case 0: + print("Navigate to ProfileView") + return AnyView(EmptyView()) + + default: + return AnyView(EmptyView()) + } + } + + +} + diff --git a/Club_portal/Club Portal/Club Portal/UI/Tabbar/WelcomeView.swift b/Club_portal/Club Portal/Club Portal/UI/Tabbar/WelcomeView.swift new file mode 100644 index 0000000..534d08b --- /dev/null +++ b/Club_portal/Club Portal/Club Portal/UI/Tabbar/WelcomeView.swift @@ -0,0 +1,107 @@ +// +// WelcomeView.swift +// Club Portal +// +// Created by Umer Tahir on 11/04/2025. +// + + + +import SwiftUI + +struct WelcomeView: View { + + @EnvironmentObject var userAuth: AuthViewModel + + @State private var isAccountViewPresented = false + @State private var selectedTab = 0 // Track the selected tab + @State private var presentSideMenu = false + @State private var selectedSideMenuTab: Int? = nil // Optional for side menu + + @AppStorage("token") var token: String = "" + + var body: some View { + // NavigationView { + + ZStack(alignment: .topTrailing) { + if token != "" { + TabView(selection: $selectedTab) { + SummaryView(presentSideMenu: $presentSideMenu) + .tabItem { + Image(selectedTab == 0 ? "tab1_" : "tab1") + Text("Home") + } + .tag(0) + //.environmentObject(treeqlViewModel) + + + ScheduleView(presentSideMenu: $presentSideMenu) + .tabItem { + Image(selectedTab == 1 ? "tab2_" : "tab2") + Text("Daily scheduler") + } + .tag(1) + //.environmentObject(treeqlViewModel) + + AvailabilityScreen(presentSideMenu: $presentSideMenu) + .tabItem { + Image(selectedTab == 2 ? "tab3_" : "tab3") + Text("Availability") + } + .tag(2) + // .environmentObject(treeqlViewModel) + + + + } + .accentColor(Color(hex: "#162664")) + .toolbar { + ToolbarItem(placement: .navigationBarLeading) { + Button { + withAnimation { + presentSideMenu.toggle() + } + } label: { + Image("menu") + .resizable() + .frame(width: 32, height: 32) + } + } + } + .background(Color.red) + .ignoresSafeArea(.all) + + SideMenu(isShowing: $presentSideMenu, content: AnyView( + SideMenuView(selectedSideMenuTab: $selectedSideMenuTab, presentSideMenu: $presentSideMenu) + )) + + + } else { + SigninView() + .environmentObject(userAuth) + } + } + .navigationBarBackButtonHidden() + .navigationBarBackButtonHidden(true) + .onChange(of: token) { newVal in + print("Token changed: \(newVal)") + } + // } + .onAppear { + // treeqlViewModel.getSportsList() + print(AppSettings.token) + } + + .navigationBarBackButtonHidden(true) + .navigationBarBackButtonHidden() + + } + +} + +struct WelcomeView_Previews: PreviewProvider { + static var previews: some View { + WelcomeView() + .environmentObject(AuthViewModel()) + } +} diff --git a/Club_portal/Club Portal/Club Portal/Utliz/AppSettings.swift b/Club_portal/Club Portal/Club Portal/Utliz/AppSettings.swift new file mode 100644 index 0000000..e601324 --- /dev/null +++ b/Club_portal/Club Portal/Club Portal/Utliz/AppSettings.swift @@ -0,0 +1,346 @@ +// +// AppSettings.swift +// Club Portal +// +// Created by Umer Tahir on 09/04/2025. +// + + + +import Foundation +import SwiftUI + +final class AppSettings { + enum SettingKey: String { + case userID + case email + case fName + case lastName + case role + case token + case photo + case password + case clubId + case ntrp + case keepLoginIn + case clubLogo + case clubName + case clubFee + case serviceFee + case clubPlans + case activePlanID + + case plan_name + case price + case allow_clinic + case allow_buddy + case allow_coach + case allow_groups + case allow_court + case lastReservationID + case lastReserveTime + case lastReservationTotal + case isLoggedin + + + + + } + static var lastReservationID: Int { + get { + return USERDEFAULTS_GET_INT_KEY(key: SettingKey.lastReservationID.rawValue) + } + set { + USERDEFAULTS_SET_INT_KEY(object: newValue, key: SettingKey.lastReservationID.rawValue) + } + } + + static var lastReserveTime: Double { + get { + return USERDEFAULTS_GET_DOUBLE_KEY(key: SettingKey.lastReserveTime.rawValue) + } + set { + USERDEFAULTS_SET_DOUBLE_KEY(object: newValue, key: SettingKey.lastReserveTime.rawValue) + } + } + + static var lastReservationTotal: Double { + get { + return USERDEFAULTS_GET_DOUBLE_KEY(key: SettingKey.lastReservationTotal.rawValue) + } + set { + USERDEFAULTS_SET_DOUBLE_KEY(object: newValue, key: SettingKey.lastReservationTotal.rawValue) + } + } + + static var plan_name: String { + get { + return USERDEFAULTS_GET_STRING_KEY(key: SettingKey.plan_name.rawValue) + } + set { + USERDEFAULTS_SET_STRING_KEY(object: newValue, key: SettingKey.plan_name.rawValue) + } + } + static var price: Double { + get { + return USERDEFAULTS_GET_DOUBLE_KEY(key: SettingKey.price.rawValue) + } + set { + USERDEFAULTS_SET_DOUBLE_KEY(object: newValue, key: SettingKey.price.rawValue) + } + } + + static var allow_clinic: Bool { + get { + return USERDEFAULTS_GET_BOOL_KEY(key: SettingKey.allow_clinic.rawValue) + } + set { + USERDEFAULTS_SET_BOOL_KEY(object: newValue, key: SettingKey.allow_clinic.rawValue) + } + } + static var allow_buddy: Bool { + get { + return USERDEFAULTS_GET_BOOL_KEY(key: SettingKey.allow_buddy.rawValue) + } + set { + USERDEFAULTS_SET_BOOL_KEY(object: newValue, key: SettingKey.allow_buddy.rawValue) + } + } + static var allow_coach: Bool { + get { + return USERDEFAULTS_GET_BOOL_KEY(key: SettingKey.allow_coach.rawValue) + } + set { + USERDEFAULTS_SET_BOOL_KEY(object: newValue, key: SettingKey.allow_coach.rawValue) + } + } + static var allow_groups: Bool { + get { + return USERDEFAULTS_GET_BOOL_KEY(key: SettingKey.allow_groups.rawValue) + } + set { + USERDEFAULTS_SET_BOOL_KEY(object: newValue, key: SettingKey.allow_groups.rawValue) + } + } + static var allow_court: Bool { + get { + return USERDEFAULTS_GET_BOOL_KEY(key: SettingKey.allow_court.rawValue) + } + set { + USERDEFAULTS_SET_BOOL_KEY(object: newValue, key: SettingKey.allow_court.rawValue) + } + } + + + static var clubFee: Double { + get { + return USERDEFAULTS_GET_DOUBLE_KEY(key: SettingKey.clubFee.rawValue) + } + set { + USERDEFAULTS_SET_DOUBLE_KEY(object: newValue, key: SettingKey.clubFee.rawValue) + } + } + static var serviceFee: Double { + get { + return USERDEFAULTS_GET_DOUBLE_KEY(key: SettingKey.serviceFee.rawValue) + } + set { + USERDEFAULTS_SET_DOUBLE_KEY(object: newValue, key: SettingKey.serviceFee.rawValue) + } + } + + + static var keepLoginIn: Bool { + get { + return USERDEFAULTS_GET_BOOL_KEY(key: SettingKey.keepLoginIn.rawValue) + } + set { + USERDEFAULTS_SET_BOOL_KEY(object: newValue, key: SettingKey.keepLoginIn.rawValue) + } + } + static var clubLogo: String { + get { + return USERDEFAULTS_GET_STRING_KEY(key: SettingKey.clubLogo.rawValue) + } + set { + USERDEFAULTS_SET_STRING_KEY(object: newValue, key: SettingKey.clubLogo.rawValue) + } + } + static var clubPlans: String { + get { + return USERDEFAULTS_GET_STRING_KEY(key: SettingKey.clubPlans.rawValue) + } + set { + USERDEFAULTS_SET_STRING_KEY(object: newValue, key: SettingKey.clubPlans.rawValue) + } + } + static var clubName: String { + get { + return USERDEFAULTS_GET_STRING_KEY(key: SettingKey.clubName.rawValue) + } + set { + USERDEFAULTS_SET_STRING_KEY(object: newValue, key: SettingKey.clubName.rawValue) + } + } + + static var photo: String { + get { + return USERDEFAULTS_GET_STRING_KEY(key: SettingKey.photo.rawValue) + } + set { + USERDEFAULTS_SET_STRING_KEY(object: newValue, key: SettingKey.photo.rawValue) + } + } + + static var password: String { + get { + return USERDEFAULTS_GET_STRING_KEY(key: SettingKey.password.rawValue) + } + set { + USERDEFAULTS_SET_STRING_KEY(object: newValue, key: SettingKey.password.rawValue) + } + } + + + static var lastName: String { + get { + return USERDEFAULTS_GET_STRING_KEY(key: SettingKey.lastName.rawValue) + } + set { + USERDEFAULTS_SET_STRING_KEY(object: newValue, key: SettingKey.lastName.rawValue) + } + } + + + static var email: String { + get { + return USERDEFAULTS_GET_STRING_KEY(key: SettingKey.email.rawValue) + } + set { + USERDEFAULTS_SET_STRING_KEY(object: newValue, key: SettingKey.email.rawValue) + } + } + + + static var fName: String { + get { + return USERDEFAULTS_GET_STRING_KEY(key: SettingKey.fName.rawValue) + } + set { + USERDEFAULTS_SET_STRING_KEY(object: newValue, key: SettingKey.fName.rawValue) + } + } + + + static var role: String { + get { + return USERDEFAULTS_GET_STRING_KEY(key: SettingKey.role.rawValue) + } + set { + USERDEFAULTS_SET_STRING_KEY(object: newValue, key: SettingKey.role.rawValue) + } + } + + @AppStorage("token") static var token: String = "" + +// static var token: String { +// get { +// return USERDEFAULTS_GET_STRING_KEY(key: SettingKey.token.rawValue) +// } +// set { +// USERDEFAULTS_SET_STRING_KEY(object: newValue, key: SettingKey.token.rawValue) +// } +// } + + static var userID: Int { + get { + return USERDEFAULTS_GET_INT_KEY(key: SettingKey.userID.rawValue) + } + set { + USERDEFAULTS_SET_INT_KEY(object: newValue, key: SettingKey.userID.rawValue) + } + } + + static var clubId: Int { + get { + return USERDEFAULTS_GET_INT_KEY(key: SettingKey.clubId.rawValue) + } + set { + USERDEFAULTS_SET_INT_KEY(object: newValue, key: SettingKey.clubId.rawValue) + } + } + + static var activePlanID: Int { + get { + return USERDEFAULTS_GET_INT_KEY(key: SettingKey.activePlanID.rawValue) + } + set { + USERDEFAULTS_SET_INT_KEY(object: newValue, key: SettingKey.activePlanID.rawValue) + } + } + + static var ntrp: Int { + get { + return USERDEFAULTS_GET_INT_KEY(key: SettingKey.ntrp.rawValue) + } + set { + USERDEFAULTS_SET_INT_KEY(object: newValue, key: SettingKey.ntrp.rawValue) + } + } + +} +enum USERDEFAULTS_KEYS: String { + case user + +} + +let USERDEFAULTS = UserDefaults.standard + +func USERDEFAULTS_SET_STRING_KEY(object:String, key:String) -> Void { + USERDEFAULTS .set(object, forKey: key) + USERDEFAULTS.synchronize() +} +func USERDEFAULTS_GET_STRING_KEY(key:String) -> String { + return USERDEFAULTS.object(forKey: key) as? String == nil ? "" : USERDEFAULTS.object(forKey: key) as! String +} +func USERDEFAULTS_SET_BOOL_KEY(object:Bool, key:String) -> Void { + USERDEFAULTS .set(object, forKey: key) + USERDEFAULTS.synchronize() +} +func USERDEFAULTS_GET_BOOL_KEY(key:String) -> Bool { + return USERDEFAULTS.object(forKey: key) as? Bool == nil ? false : USERDEFAULTS.object(forKey: key) as! Bool + +} +func USERDEFAULTS_SET_INT_KEY(object:Int, key:String) -> Void { + USERDEFAULTS .set(object, forKey: key) + USERDEFAULTS.synchronize() +} +func USERDEFAULTS_GET_INT_KEY(key:String) -> Int { + return USERDEFAULTS.object(forKey: key) as? Int == nil ? 0 : USERDEFAULTS.object(forKey: key) as! Int +} +func USERDEFAULTS_SET_DOUBLE_KEY(object:Double, key:String) -> Void { + USERDEFAULTS .set(object, forKey: key) + USERDEFAULTS.synchronize() +} +func USERDEFAULTS_GET_DOUBLE_KEY(key:String) -> Double { + return USERDEFAULTS.object(forKey: key) as? Double == nil ? 0.0 : USERDEFAULTS.object(forKey: key) as! Double +} +//func USERDEFAULTS_SET_USER_OBJECT(object:User) -> Void { +// USERDEFAULTS .set(try? PropertyListEncoder().encode(object), forKey: USERDEFAULTS_KEYS.user.rawValue) +// USERDEFAULTS.synchronize() +//} +//func USERDEFAULTS_GET_USER_OBJECT() -> User? { +// +// guard let data = USERDEFAULTS.object(forKey: USERDEFAULTS_KEYS.user.rawValue) as? Data else{ +// return nil +// } +// let user = try? PropertyListDecoder().decode(User.self, from: data) +// return user +//} +func USERDEFAULTS_REMOVE_OBJECT(key:String){ + USERDEFAULTS.removeObject(forKey: key) + USERDEFAULTS.synchronize() +} + + + diff --git a/Club_portal/Club Portal/Club Portal/Utliz/Colorr.swift b/Club_portal/Club Portal/Club Portal/Utliz/Colorr.swift new file mode 100644 index 0000000..14e4d5b --- /dev/null +++ b/Club_portal/Club Portal/Club Portal/Utliz/Colorr.swift @@ -0,0 +1,57 @@ +// +// Colorr.swift +// Club Portal +// +// Created by Umer Tahir on 09/04/2025. +// + + +// +// extension.swift +// courtmatch +// +// Created by Umer Tahir on 13/11/2024. +// + + +import SwiftUI + +struct Colorr { + static let themeBlueColor = Color(hex: "112a8c") + static let reservationBackgroundColor = Color(hex: "C2D6FF").opacity(0.1) + static let requestSentBg = Color( hex: "38C793") + static let requestJoinAlertOrangeBg = Color(hex: "F17B2C") + static let greenColor = Color(hex: "176448") + static let lableColor = Color(hex: "525866") + static let lightColor = Color(hex: "F6F8FA") + static let findBuddyColor = Color(hex: "CBF5E5") + static let redColor = Color(hex: "DF1C41") + static let pieGreen = Color(hex: "2D9F75") + static let pieBlue = Color(hex: "253EA7") + + +} +struct ConstantData { + + static let privacyLink = "https://courtmatchup.manaknightdigital.com/privacy-policy" + static let termsLink = "https://courtmatchup.manaknightdigital.com/terms-and-conditions" + + static let stripeTestKey = "pk_test_51Ll5ukBgOlWo0lDUrBhA2W7EX2MwUH9AR5Y3KQoujf7PTQagZAJylWP1UOFbtH4UwxoufZbInwehQppWAq53kmNC00UIKSmebO" + static let appleMarchantId = "merchant.com.courtmatch.app" + static func openLink(urlString: String) { + guard let url = URL(string: urlString), UIApplication.shared.canOpenURL(url) else { + print("Invalid URL") + return + } + UIApplication.shared.open(url) + } + + static let NTRPVALUES = ["not set", "8.0","7.5", "7.0", "6.5", "6.0", "5.5", "5.0", "4.5", "4.0", "3.5", "3.0", "2.5", "2.0"] + static let genders = ["Male", "Female", "Other"] + + static var isCustomRequest = 0 +} + + + + diff --git a/Club_portal/Club Portal/Club Portal/Utliz/CustomTextField.swift b/Club_portal/Club Portal/Club Portal/Utliz/CustomTextField.swift new file mode 100644 index 0000000..11cdc9e --- /dev/null +++ b/Club_portal/Club Portal/Club Portal/Utliz/CustomTextField.swift @@ -0,0 +1,32 @@ +// +// CustomTextField.swift +// Club Portal +// +// Created by Umer Tahir on 09/04/2025. +// + + +import SwiftUI +// Custom TextField Component +struct CustomTextField: View { + let placeholder: String + @Binding var text: String + var keyboardType: UIKeyboardType = .default + + var body: some View { + VStack(alignment: .leading) { + Text(placeholder) + .foregroundColor(.black) + .padding(.leading, 10) + + TextField( placeholder == "Date of birth" ? "MM/DD/YYY" : placeholder, text: $text) + .keyboardType(keyboardType) + .padding(12) + .overlay( + RoundedRectangle(cornerRadius: 8) + .stroke(Color.gray, lineWidth: 1) + ) + .cornerRadius(8) + } + } +} diff --git a/Club_portal/Club Portal/Club Portal/Utliz/Extension.swift b/Club_portal/Club Portal/Club Portal/Utliz/Extension.swift new file mode 100644 index 0000000..48f82c7 --- /dev/null +++ b/Club_portal/Club Portal/Club Portal/Utliz/Extension.swift @@ -0,0 +1,18 @@ +// +// Extension.swift +// Club Portal +// +// Created by Umer Tahir on 10/04/2025. +// + +import Foundation + +extension String { + var isValidEmail: Bool { + let emailPredicate = NSPredicate( + format: "SELF MATCHES %@", + "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}" + ) + return emailPredicate.evaluate(with: self) + } +} diff --git a/Club_portal/Club Portal/Club Portal/Utliz/Extensions.swift b/Club_portal/Club Portal/Club Portal/Utliz/Extensions.swift new file mode 100644 index 0000000..1611911 --- /dev/null +++ b/Club_portal/Club Portal/Club Portal/Utliz/Extensions.swift @@ -0,0 +1,355 @@ + + + + import Foundation + import SwiftUI + + +extension Color { + init(hex: String) { + let scanner = Scanner(string: hex) + var rgbValue: UInt64 = 0 + + scanner.scanHexInt64(&rgbValue) + + let r = Double((rgbValue & 0xff0000) >> 16) / 255.0 + let g = Double((rgbValue & 0xff00) >> 8) / 255.0 + let b = Double(rgbValue & 0xff) / 255.0 + + self.init(red: r, green: g, blue: b) + } +} + +extension Data { + mutating func append(_ string: String) { + if let data = string.data(using: .utf8) { + append(data) + } + } + + +} +import Foundation + +extension String { + /// Converts a date string from one format to another. + /// - Parameters: + /// - inputFormat: The format of the input date string. + /// - outputFormat: The desired format for the output date string. + /// - Returns: A formatted date string, or `nil` if conversion fails. + func convertDateFormat(from inputFormat: String? = "yyyy-MM-dd", to outputFormat: String? = "EEEE, MMM d") -> String? { + let inputFormatter = DateFormatter() + inputFormatter.dateFormat = inputFormat + + guard let date = inputFormatter.date(from: self) else { + return nil + } + + let outputFormatter = DateFormatter() + outputFormatter.dateFormat = outputFormat + return outputFormatter.string(from: date) + } + + func convertDateForReq(from inputFormat: String? = "yyyy-MM-dd", to outputFormat: String? = "EEEE, MMM d") -> String? { + let inputFormatter = DateFormatter() + inputFormatter.dateFormat = inputFormat + inputFormatter.locale = Locale.current + inputFormatter.timeZone = TimeZone.current + + guard let date = inputFormatter.date(from: self) else { + return nil + } + + let calendar = Calendar.current + let now = Date() + + let components = calendar.dateComponents([.day], from: calendar.startOfDay(for: now), to: calendar.startOfDay(for: date)) + + let dayFormatter = DateFormatter() + dayFormatter.dateFormat = "EEEE, MMM d" + + let formattedDate = dayFormatter.string(from: date) + + switch components.day { + case 0: + return "Today (\(formattedDate))" + case 1: + return "Tomorrow (\(formattedDate))" + default: + let outputFormatter = DateFormatter() + outputFormatter.dateFormat = outputFormat + return outputFormatter.string(from: date) + } + } + + func isDateForTmr() ->Bool { + let inputFormatter = DateFormatter() + inputFormatter.dateFormat = "yyyy-MM-dd" + inputFormatter.locale = Locale.current + inputFormatter.timeZone = TimeZone.current + + guard let date = inputFormatter.date(from: self) else { + return false + } + + let calendar = Calendar.current + let now = Date() + + let components = calendar.dateComponents([.day], from: calendar.startOfDay(for: now), to: calendar.startOfDay(for: date)) + + let dayFormatter = DateFormatter() + dayFormatter.dateFormat = "EEEE, MMM d" + + + if components.day == 1 { + return true + } + + return false + } + + + func getStringArray() -> [String] { + if let data = self.data(using: .utf8) { + do { + let stringArray = try JSONDecoder().decode([String].self, from: data) + print(stringArray) // Output: ["Indoor", "Outdoor"] + return stringArray + } catch { + print("Decoding error: \(error)") + } + } + return [] + } +} +extension Double { + /// Rounds the double to 2 decimal places + var roundedTo2Decimals: Double { + var decimalValue = Decimal(self) + var roundedValue = Decimal() + NSDecimalRound(&roundedValue, &decimalValue, 2, .plain) // Rounds to 2 decimal places + return (roundedValue as NSDecimalNumber).doubleValue + } + + func formattedString(decimalPlaces: Int) -> String { + return String(format: "%.\(decimalPlaces)f", self) + } + + +} + +extension String { + + func fromTimeIntervaldDate(format: String = "MMM dd, yyyy") -> String? { + guard let timestamp = TimeInterval(self) else { return nil } + let date = Date(timeIntervalSince1970: timestamp) + + let dateFormatter = DateFormatter() + dateFormatter.dateFormat = format + dateFormatter.timeZone = TimeZone.current // Adjust to device's timezone + let s = dateFormatter.string(from: date) + return s + } + + /// Converts a time string from 24-hour format to 12-hour format with AM/PM + func to12HourFormat1() -> String? { + let inputDateFormatter = DateFormatter() + inputDateFormatter.dateFormat = "HH:mm:ss" // Input format + + let outputDateFormatter = DateFormatter() + outputDateFormatter.dateFormat = "hh:mm a" // Output format + + // Convert the string to a `Date` object + if let date = inputDateFormatter.date(from: self) { + return outputDateFormatter.string(from: date) // Convert back to string in desired format + } else { + return nil // Return nil if conversion fails + } + } + + func to12HourFormat() -> String? { + let inputDateFormatter = DateFormatter() + inputDateFormatter.dateFormat = "HH:mm:ss" // Input format (24-hour) + + let outputDateFormatter = DateFormatter() + outputDateFormatter.dateFormat = "hh:mm a" // Output format (12-hour with AM/PM) + + // Split the range into start and end times + let timeComponents = self.components(separatedBy: "-") + + if timeComponents.count == 2 { + let startTime = inputDateFormatter.date(from: timeComponents[0]) + let endTime = inputDateFormatter.date(from: timeComponents[1]) + + let start12Hour = outputDateFormatter.string(from: startTime!) + let end12Hour = outputDateFormatter.string(from: endTime!) + + return "\(start12Hour) - \(end12Hour)" + }else { + let startTime = inputDateFormatter.date(from: self) + let start12Hour = outputDateFormatter.string(from: startTime!) + + return start12Hour + } + + // Return nil if conversion fails + } + + + + + func convertTo24HourFormat() -> String? { + // Initialize date formatter + let inputFormatter = DateFormatter() + inputFormatter.dateFormat = "hh:mm a" // Input format (12-hour with AM/PM) + + // Convert string to date + if let date = inputFormatter.date(from: self) { + // Initialize another date formatter for 24-hour format + let outputFormatter = DateFormatter() + outputFormatter.dateFormat = "HH:mm" // 24-hour format + + // Convert date to 24-hour string + return outputFormatter.string(from: date) + } + return nil + } + + + func timeAgo() -> String? { + // Set up the date formatter + let dateFormatter = DateFormatter() + dateFormatter.dateFormat = "yyyy-MM-dd" + dateFormatter.locale = Locale.current + dateFormatter.timeZone = TimeZone.current + + // Convert the string into a Date object + guard let date = dateFormatter.date(from: self) else { return nil } + + // Get the current date and calculate the time interval + let calendar = Calendar.current + let now = Date() + let components = calendar.dateComponents([.year, .month, .day], from: date, to: now) + + // Check the components to return the correct "time ago" string + if let years = components.year, years > 0 { + return "\(years) year\(years > 1 ? "s" : "") ago" + } else if let months = components.month, months > 0 { + return "\(months) month\(months > 1 ? "s" : "") ago" + } else if let days = components.day, days > 0 { + return "\(days) day\(days > 1 ? "s" : "") ago" + } else { + return "Just now" + } + } + + + +} +extension Date { + func dayOfWeek( ) -> String { + let dateFormatter = DateFormatter() + dateFormatter.dateFormat = "EEEE" // Full day name + return dateFormatter.string(from: self) + } + + func getStringDate() -> String { + let dateFormatter = DateFormatter() + dateFormatter.dateFormat = "yyyy-MM-dd" // Specify the desired format + return dateFormatter.string(from: self) + } + + func currentDateOfMonth() ->Int { + let day = Calendar.current.component(.day, from: Date()) + return day + } +} + +extension UIImage { + func resize(to targetSize: CGSize) -> UIImage? { + let size = self.size + + let widthRatio = targetSize.width / size.width + let heightRatio = targetSize.height / size.height + + // Figure out what our orientation is, and use that to form the rectangle + var newSize: CGSize + if(widthRatio > heightRatio) { + newSize = CGSize(width: size.width * heightRatio, height: size.height * heightRatio) + } else { + newSize = CGSize(width: size.width * widthRatio, height: size.height * widthRatio) + } + + // This is the rect that we've calculated out and this is what is actually used below + let rect = CGRect(origin: .zero, size: newSize) + + // Actually do the resizing to the rect using the ImageContext stuff + UIGraphicsBeginImageContextWithOptions(newSize, false, 1.0) + self.draw(in: rect) + let newImage = UIGraphicsGetImageFromCurrentImageContext() + UIGraphicsEndImageContext() + + return newImage + } +} + +struct RoundedCorner: Shape { + + var radius: CGFloat = .infinity + var corners: UIRectCorner = .allCorners + + func path(in rect: CGRect) -> Path { + let path = UIBezierPath(roundedRect: rect, byRoundingCorners: corners, cornerRadii: CGSize(width: radius, height: radius)) + return Path(path.cgPath) + } +} + +extension View { + func cornerRadius(_ radius: CGFloat, corners: UIRectCorner) -> some View { + clipShape( RoundedCorner(radius: radius, corners: corners) ) + } +} + +extension String { + func toIntArray() -> [Int] { + // Remove brackets and split the string by commas + let trimmed = self.trimmingCharacters(in: CharacterSet(charactersIn: "[]")) + let stringArray = trimmed.split(separator: ",") + + // Convert each substring to an integer + return stringArray.compactMap { Int($0) } + } + func removeFlag() -> String { + let filteredString = self.filter { !$0.isFlag } + return filteredString.trimmingCharacters(in: .whitespaces) + } + +} + +extension Character { + // Check if a character is a flag emoji + var isFlag: Bool { + return unicodeScalars.allSatisfy { $0.properties.isEmoji && ($0.value >= 0x1F1E6 && $0.value <= 0x1F1FF) } + } +} + +extension Double { + func formatNTRP () -> String { + return String(format: "%.1f", self ) + } +} + +extension String { + func toWeekday() -> String? { + let formatter = DateFormatter() + formatter.dateFormat = "yyyy-MM-dd" + formatter.locale = Locale(identifier: "en_US_POSIX") + + guard let date = formatter.date(from: self) else { + return nil + } + + formatter.dateFormat = "E" // Short weekday like Mon, Tue, etc. + return formatter.string(from: date) + } +} diff --git a/Club_portal/Club Portal/Club Portal/Utliz/LoadingView.swift b/Club_portal/Club Portal/Club Portal/Utliz/LoadingView.swift new file mode 100644 index 0000000..36dd6be --- /dev/null +++ b/Club_portal/Club Portal/Club Portal/Utliz/LoadingView.swift @@ -0,0 +1,21 @@ +// +// LoadingView.swift +// Club Portal +// +// Created by Umer Tahir on 10/04/2025. +// + + +import SwiftUI + +struct LoadingView: View { + var body: some View { + ZStack { + Color.black.opacity(0.3).ignoresSafeArea() + ProgressView("Loading...") + .padding() + .background(Color.white) + .cornerRadius(12) + } + } +} \ No newline at end of file diff --git a/Club_portal/Club Portal/Club Portal/Utliz/ProfileImageView.swift b/Club_portal/Club Portal/Club Portal/Utliz/ProfileImageView.swift new file mode 100644 index 0000000..e8d7b03 --- /dev/null +++ b/Club_portal/Club Portal/Club Portal/Utliz/ProfileImageView.swift @@ -0,0 +1,36 @@ +// +// ProfileImageView.swift +// Club Portal +// +// Created by Umer Tahir on 15/04/2025. +// + + + +import SwiftUI + +struct ProfileImageView: View { + let imageUrl: String? + let size: CGFloat + + var body: some View { + if let urlString = imageUrl, let url = URL(string: urlString) { + AsyncImage(url: url) { image in + image + .resizable() + .scaledToFill() + .frame(width: size, height: size) + .clipShape(Circle()) + } placeholder: { + Circle() + .fill(Color.gray) + .frame(width: size, height: size) + } + } else { + Circle() + .fill(Color.gray) + .frame(width: size, height: size) + + } + } +} diff --git a/Club_portal/Club Portal/Club Portal/Utliz/SignInButton.swift b/Club_portal/Club Portal/Club Portal/Utliz/SignInButton.swift new file mode 100644 index 0000000..6aa2ab4 --- /dev/null +++ b/Club_portal/Club Portal/Club Portal/Utliz/SignInButton.swift @@ -0,0 +1,40 @@ +// +// SignInButton.swift +// Club Portal +// +// Created by Umer Tahir on 09/04/2025. +// + + + +import SwiftUI + +// Social Sign-In Button Component +struct SignInButton: View { + let title: String + let iconName: String + var action: (() -> ())? + + var body: some View { + Button(action: { + // Handle sign-in + action?() + }) { + HStack { + Image(iconName) + .font(.headline) + Text(title) + .font(.headline) + } + .foregroundColor(.black) + .frame(maxWidth: .infinity) + .padding() + .overlay( + RoundedRectangle(cornerRadius: 8) + .stroke(Color.gray.opacity(0.5), lineWidth: 1) + ) + .cornerRadius(8) + } + } +} + diff --git a/Club_portal/Club Portal/Club Portal/Utliz/ToastModifier.swift b/Club_portal/Club Portal/Club Portal/Utliz/ToastModifier.swift new file mode 100644 index 0000000..6e96b35 --- /dev/null +++ b/Club_portal/Club Portal/Club Portal/Utliz/ToastModifier.swift @@ -0,0 +1,48 @@ +// +// ToastModifier.swift +// Club Portal +// +// Created by Umer Tahir on 09/04/2025. +// + + +import SwiftUI + +struct ToastModifier: ViewModifier { + let message: String + let backgroundColor: Color + + @Binding var isShowing: Bool + + func body(content: Content) -> some View { + ZStack { + content + + if isShowing { + VStack { + Spacer() + Text(message) + .padding() + .background(backgroundColor) + .foregroundColor(.white) + .cornerRadius(10) + .transition(.move(edge: .bottom).combined(with: .opacity)) + .padding(.bottom, 40) + } + .animation(.easeInOut, value: isShowing) + .onAppear { + // Hide toast automatically after 3 seconds + DispatchQueue.main.asyncAfter(deadline: .now() + 3) { + isShowing = false + } + } + } + } + } +} + +extension View { + func toast(message: String, isShowing: Binding, backgroundColor: Color = .green) -> some View { + self.modifier(ToastModifier(message: message, backgroundColor: backgroundColor, isShowing: isShowing)) + } +} diff --git a/Club_portal/Club Portal/Club Portal/ViewModels/AuthViewModel1.swift b/Club_portal/Club Portal/Club Portal/ViewModels/AuthViewModel1.swift new file mode 100644 index 0000000..ec53ffb --- /dev/null +++ b/Club_portal/Club Portal/Club Portal/ViewModels/AuthViewModel1.swift @@ -0,0 +1,154 @@ +// +// AuthViewModel.swift +// Club Portal +// +// Created by Umer Tahir on 09/04/2025. +// + + +import Foundation +import LocalAuthentication + +@MainActor +class AuthViewModel: ObservableObject { + @Published var isLoggedIn = false + @Published var showToast = false + @Published var toastMessage = "" + @Published var email = "" + @Published var password = "" + @Published var newPassword = "" + @Published var confirmPassword = "" + @Published var otpCode = "" // Code received via email + @Published var isLoading = false + @Published var isRefresh: Bool = false + + var isEmailValid: Bool { + email.isValidEmail + } + + var isNewPasswordValid: Bool { + let pattern = "^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!@#$%^&*])[A-Za-z\\d!@#$%^&*]{6,}$" + return NSPredicate(format: "SELF MATCHES %@", pattern).evaluate(with: newPassword) + } + + var isConfirmPasswordMatching: Bool { + return !confirmPassword.isEmpty && confirmPassword == newPassword + } + + var canSubmitNewPassword: Bool { + return isNewPasswordValid && isConfirmPasswordMatching + } + var isPasswordValid: Bool { + return !password.isEmpty && password.count >= 6 + } + + var canLogin: Bool { + return isEmailValid && isPasswordValid + } + + func login(completion: @escaping (Bool) -> Void) { + guard canLogin else { + toastMessage = "Please enter a valid email and password." + showToast = true + return + } + + isLoading = true + + let endpoint = LoginEndpoint(email: email, password: password, isRefresh: AppSettings.keepLoginIn) + print("endpoint: ", endpoint) + + Task { + do { + let response: LoginResponse = try await APIService.shared.request(endpoint, responseType: LoginResponse.self) + // Handle the successful login response + print("response: ", response ) + + if response.error { + toastMessage = "Login failed. Please try again." + showToast = true + } else { + // 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 + } + } catch { + toastMessage = "Error: \(error.localizedDescription)" + showToast = true + } + + isLoading = false + } + } + func forgotPassword(role: String = "club", completion: @escaping (Bool) -> Void) { + self.isLoading = true + + Task { + let endpoint = ForgotPasswordEndpoint(email: email, role: role) + print("endpoint: ", endpoint) + do { + let response: ForgotPasswordResponse = try await APIService.shared.request(endpoint, responseType: ForgotPasswordResponse.self) + toastMessage = response.message + print("response: ", response ) + showToast = true + DispatchQueue.main.asyncAfter(deadline: .now() + 2) { + self.showToast = false + self.isLoading = false + + completion(true) + } + } catch { + toastMessage = error.localizedDescription + showToast = true + DispatchQueue.main.asyncAfter(deadline: .now() + 2) { + self.showToast = false + self.isLoading = false + + completion(false) + } + } + } + } + + + func resetPassword(completion: @escaping (Bool) -> Void) { + isLoading = true + + Task { + let endpoint = ResetPasswordEndpoint( + email: email, + code: otpCode, + password: newPassword + ) + print("endpoint: ", endpoint) + + do { + let response: ForgotPasswordResponse = try await APIService.shared.request(endpoint, responseType: ForgotPasswordResponse.self) + toastMessage = response.message + print("response: ", response ) + showToast = true + DispatchQueue.main.asyncAfter(deadline: .now() + 2) { + self.showToast = false + self.isLoading = false + + completion(true) + } + } catch { + toastMessage = error.localizedDescription + showToast = true + DispatchQueue.main.asyncAfter(deadline: .now() + 2) { + self.showToast = false + self.isLoading = false + completion(false) + } + } + } + } + + + +} diff --git a/Club_portal/Club Portal/Club Portal/ViewModels/CalendarEventStore.swift b/Club_portal/Club Portal/Club Portal/ViewModels/CalendarEventStore.swift new file mode 100644 index 0000000..5eac224 --- /dev/null +++ b/Club_portal/Club Portal/Club Portal/ViewModels/CalendarEventStore.swift @@ -0,0 +1,28 @@ +// +// CalendarEventStore.swift +// Club Portal +// +// Created by Umer Tahir on 04/04/2025. +// + +import SwiftUI +import CalendarKit + +//class CalendarEventStore: ObservableObject, EventDataSource { +// @Published var events: [EventDescriptor] = [] +// +// func eventsForDate(_ date: Date) -> [EventDescriptor] { +// return events +// } +// +// func addDummyEvent() { +// let event = Event() +// let startDate = Date() +// let endDate = Calendar.current.date(byAdding: .hour, value: 1, to: startDate)! +// event.dateInterval = DateInterval(start: startDate, end: endDate) +// event.text = "Dummy Event" +// event.color = .blue +// events.append(event) +// } +//} +// diff --git a/Club_portal/Club Portal/Club Portal/ViewModels/DashViewModel.swift b/Club_portal/Club Portal/Club Portal/ViewModels/DashViewModel.swift new file mode 100644 index 0000000..de63924 --- /dev/null +++ b/Club_portal/Club Portal/Club Portal/ViewModels/DashViewModel.swift @@ -0,0 +1,104 @@ +// +// DashViewModel.swift +// Club Portal +// +// Created by Umer Tahir on 11/04/2025. +// + +import SwiftUI + +@MainActor +class DashViewModel: ObservableObject { + + @Published var dashboardfilters = "" + @Published var statResponse : ClubStatisticsModel? + @Published var dailyReservations : [Reservation]? + @Published var availRsp : AvailabliltyRsp? + @Published var club : ClubDetail? + @Published var clubDetail : ClubDetailsResponse? + @Published var profile : ProfileRsp? + @Published var sessionExpired = false + + func getStats(completion: @escaping () -> ()) { + let endpoint = ClubStatisticsEndpoint( + startDate: "2024-10-25", + endDate: "2024-11-25", + startTime: "17:30:20", + endTime: "19:20:30" + ) + + 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 + } + else { + + } + } catch { + print("Error fetching stats: \(error)") + // if error.localizedDescription == "Token expired" { + sessionExpired = true + completion() + // } + } + } + } + + func getDailySched(clubId: Int){ + let endpoint = ReservationListEndpoint(clubID: clubId) + Task { + do { + let result = try await APIService.shared.request(endpoint, responseType: ReservationRsp.self) + dailyReservations = result.list + print("Reservations:", result.list) + } catch { + print("Error fetching reservations:", error) + } + } + } + + func getAvailability(){ + let endpoint = AvailabilityListEndpoint() + Task { + do { + let result = try await APIService.shared.request(endpoint, responseType: AvailabliltyRsp.self) + availRsp = result + print("avail:", result) + } catch { + print("Error fetching reservations:", error) + } + } + } + + func getProfile(){ + let endpoint = GetprofileEndpoint() + Task { + do { + + let result = try await APIService.shared.request(endpoint, responseType: ProfileRsp.self) + profile = result + AppSettings.clubId = result.model?.club?.id ?? 0 + AppSettings.clubName = result.model?.club?.name ?? "" + AppSettings.clubName = result.model?.user?.email ?? "" + + } catch { + print("Error fetching reservations:", error) + } + } + } + + func getClubProfile(){ + let endpoint = GetClubEndpoint(clubId: "10") + Task { + do { + let result = try await APIService.shared.request(endpoint, responseType: ClubDetail.self) + club = result + } catch { + print("Error fetching reservations:", error) + } + } + } +} diff --git a/Club_portal/Club Portal/Club Portal/ViewModels/LoginViewModel.swift b/Club_portal/Club Portal/Club Portal/ViewModels/LoginViewModel.swift new file mode 100644 index 0000000..7907e09 --- /dev/null +++ b/Club_portal/Club Portal/Club Portal/ViewModels/LoginViewModel.swift @@ -0,0 +1,79 @@ +// +// LoginViewModel.swift +// Club Portal +// +// Created by Umer Tahir on 09/04/2025. +// + + +// +// LoginViewModel.swift +// courtmatch +// +// Created by Umer Tahir on 03/12/2024. +// + +import AuthenticationServices +import SwiftUI + +class LoginViewModel: NSObject { + + var appleRes : ((AppleLoginRequest?, String?)->Void)? + @Published var appleLoginRequest: AppleLoginRequest? = nil + +} + +extension LoginViewModel: ASAuthorizationControllerDelegate { + func authorizationController(controller: ASAuthorizationController, + didCompleteWithAuthorization authorization: ASAuthorization) { + if let appleIdCredential = authorization.credential as? ASAuthorizationAppleIDCredential { + guard let token = appleIdCredential.identityToken?.base64EncodedString() else { + return + } + + // MARK: TODO + /// 1. Set token here + /// 2. Perform tasks to do after login + var firstName = "" + var lastName = "" + + + if appleIdCredential.authorizedScopes.contains(.fullName) { + print(appleIdCredential.fullName?.givenName ?? "No given name") + firstName = appleIdCredential.fullName?.givenName ?? "" + lastName = appleIdCredential.fullName?.givenName ?? "" + + } + + if appleIdCredential.authorizedScopes.contains(.email) { + print(appleIdCredential.email ?? "No email") + } + + let userIdentifier = appleIdCredential.user + let identityTokenData = appleIdCredential.identityToken + let authCode = appleIdCredential.authorizationCode + let realUserStatus = appleIdCredential.realUserStatus + let identityTokenString = String(data: identityTokenData!, encoding: .utf8) ?? "" + + + let request = AppleLoginRequest(first_name: firstName, last_name: lastName, identityToken: identityTokenString, apple_id: userIdentifier, role: "user") + + appleRes?(request, nil) + + } + } + + func authorizationController(controller: ASAuthorizationController, didCompleteWithError error: Error) { + print(error) + } + + func performAppleSignIn(comppletion : @escaping ((AppleLoginRequest?, String?)->Void)) { + let provider = ASAuthorizationAppleIDProvider() + let request = provider.createRequest() + request.requestedScopes = [.fullName, .email] + let controller = ASAuthorizationController(authorizationRequests: [request]) + controller.delegate = self + self.appleRes = comppletion + controller.performRequests() + } +}