diff --git a/Backup/File.swift b/Backup/File.swift new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/Backup/File.swift @@ -0,0 +1 @@ + diff --git a/Backup/ProductListView.swift b/Backup/ProductListView.swift new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/Backup/ProductListView.swift @@ -0,0 +1 @@ + diff --git a/ContentView.swift b/ContentView.swift new file mode 100644 index 0000000..5fc49b8 --- /dev/null +++ b/ContentView.swift @@ -0,0 +1,17 @@ +// +// ContentView.swift +// Vogue Vista +// +// Created by SMD Thiranjaya on BE 2567-03-14. +// + +import SwiftUI + +struct ContentView: View { + var body: some View { + NavigationView { + StartupPageViewController() + .navigationBarHidden(true) // Hide navigation bar if you don't want it + } + } +} diff --git a/README.md b/README.md new file mode 100644 index 0000000..284dc76 --- /dev/null +++ b/README.md @@ -0,0 +1,77 @@ +# Vogue Vista + +Vogue Vista is an iOS application designed for an online clothing brand. It allows users to browse, search, and purchase clothing items directly from their iOS devices. The app utilizes Auth0 for user authentication and Supabase for backend services. + +## Features + +- User Authentication with Auth0 +- Product Listings +- Product Detail Views +- Shopping Cart Management +- Checkout Functionality +- Responsive and Intuitive User Interface + +## Installation + +Before you can run the project, you need to follow these setup steps: + +### Prerequisites + +- Xcode 12 or later +- CocoaPods or Swift Package Manager +- An Auth0 account +- A Supabase project + +### Setting Up Auth0 + +1. Create a new application in your Auth0 dashboard. +2. Note down your Client ID and Domain. +3. Configure callback URLs, logout URLs, and allowed web origins as per your iOS app's settings. + +### Setting Up Supabase + +1. Create a new project in Supabase. +2. Note down the API URL and anon key from the project settings. + +### Configuring the iOS Project + +1. Clone the repository: + +```bash +git clone https://github.com/smdthiranjaya/Vogue-Vista.git +cd voguevista +```` +If using CocoaPods, run: +```bash +pod install +```` + +Open the .xcworkspace file in Xcode. + +2. Add your Auth0 ClientId and Domain to the Auth0.plist file. + +3. Update the Supabase URL and anon key in the application's network configuration. + +### Usage + +To run the application, open the project in Xcode and run it on a simulator or a physical device. + +- **Login/Signup:** Users can sign up or log in using the Auth0 integration. +- **Browse Products:** Users can browse the list of products available for purchase. +- **Product Details:** Users can view detailed information about a product. +- **Add to Cart:** Users can add products to their shopping cart. +- **Checkout:** Users can proceed to checkout to complete their purchase. + +### Contributing + +Contributions to Vogue Vista are welcome! Please read the CONTRIBUTING.md for guidelines on how to contribute to this project. + +### License + +This project is licensed under the MIT License - see the LICENSE file for details. + +### Acknowledgments + +- Auth0 for authentication services. +- Supabase for providing the backend as a service. +- All the open-source libraries and tools used in this project. diff --git a/Vogue Vista.xcodeproj/project.pbxproj b/Vogue Vista.xcodeproj/project.pbxproj new file mode 100644 index 0000000..ab3fedf --- /dev/null +++ b/Vogue Vista.xcodeproj/project.pbxproj @@ -0,0 +1,533 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 56; + objects = { + +/* Begin PBXBuildFile section */ + 1B1D29BD2BA9BBB0003A5AC6 /* StartupPageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B1D29BC2BA9BBB0003A5AC6 /* StartupPageView.swift */; }; + 1B1D29BF2BA9BBD2003A5AC6 /* SignUpView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B1D29BE2BA9BBD2003A5AC6 /* SignUpView.swift */; }; + 1B1D29C12BA9BC06003A5AC6 /* LoginView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B1D29C02BA9BC06003A5AC6 /* LoginView.swift */; }; + 1B1D29C32BA9BE73003A5AC6 /* UIView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B1D29C22BA9BE72003A5AC6 /* UIView.swift */; }; + 1B1D29C62BA9E167003A5AC6 /* File.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B1D29C42BA9C04D003A5AC6 /* File.swift */; }; + 1B208C332BAE8FF4009A361C /* CartManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B208C322BAE8FF4009A361C /* CartManager.swift */; }; + 1B208C352BAE919F009A361C /* CartModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B208C342BAE919F009A361C /* CartModel.swift */; }; + 1B208C372BAE91D2009A361C /* CartItemModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B208C362BAE91D2009A361C /* CartItemModel.swift */; }; + 1B2CB2E02BB4925900575E4C /* ProfileView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B2CB2DF2BB4925900575E4C /* ProfileView.swift */; }; + 1B2CB2E22BB4978300575E4C /* ProfileModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B2CB2E12BB4978300575E4C /* ProfileModel.swift */; }; + 1B2DCA5B2BB6B20D004D2790 /* OrderModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B2DCA5A2BB6B20D004D2790 /* OrderModel.swift */; }; + 1B2DCA5D2BB8539B004D2790 /* OrderManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B2DCA5C2BB8539B004D2790 /* OrderManager.swift */; }; + 1B2DCA5F2BB85530004D2790 /* Configuration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B2DCA5E2BB85530004D2790 /* Configuration.swift */; }; + 1B3778822BA433E20037B375 /* HomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B3778812BA433E20037B375 /* HomeView.swift */; }; + 1BD6DB0C2BA23A5F00E42988 /* Vogue_VistaApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BD6DB0B2BA23A5F00E42988 /* Vogue_VistaApp.swift */; }; + 1BD6DB102BA23A6100E42988 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 1BD6DB0F2BA23A6100E42988 /* Assets.xcassets */; }; + 1BD6DB142BA23A6100E42988 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 1BD6DB132BA23A6100E42988 /* Preview Assets.xcassets */; }; + 1BD6DB222BA2401900E42988 /* ProductModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BD6DB212BA2401900E42988 /* ProductModel.swift */; }; + 1BD6DB242BA2402900E42988 /* UsersModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BD6DB232BA2402900E42988 /* UsersModel.swift */; }; + 1BD6DB2B2BA2407600E42988 /* ProductListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BD6DB2A2BA2407600E42988 /* ProductListView.swift */; }; + 1BD6DB2D2BA2408400E42988 /* ProductDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BD6DB2C2BA2408400E42988 /* ProductDetailView.swift */; }; + 1BD6DB2F2BA2408E00E42988 /* ShoppingCartView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BD6DB2E2BA2408E00E42988 /* ShoppingCartView.swift */; }; + 1BD6DB312BA2409700E42988 /* CheckoutView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BD6DB302BA2409700E42988 /* CheckoutView.swift */; }; + 1BD6DB342BA240BE00E42988 /* ProductListViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BD6DB332BA240BE00E42988 /* ProductListViewModel.swift */; }; + 1BD6DB382BA240DC00E42988 /* ShoppingCartViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BD6DB372BA240DC00E42988 /* ShoppingCartViewModel.swift */; }; + 1BD6DB3A2BA240E500E42988 /* CheckoutViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BD6DB392BA240E500E42988 /* CheckoutViewModel.swift */; }; + 1BD6DB462BA2413600E42988 /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BD6DB452BA2413600E42988 /* Extensions.swift */; }; + 1BD6DB502BA2460B00E42988 /* Auth0 in Frameworks */ = {isa = PBXBuildFile; productRef = 1BD6DB4F2BA2460B00E42988 /* Auth0 */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 1B1D29BC2BA9BBB0003A5AC6 /* StartupPageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StartupPageView.swift; sourceTree = ""; }; + 1B1D29BE2BA9BBD2003A5AC6 /* SignUpView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignUpView.swift; sourceTree = ""; }; + 1B1D29C02BA9BC06003A5AC6 /* LoginView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginView.swift; sourceTree = ""; }; + 1B1D29C22BA9BE72003A5AC6 /* UIView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIView.swift; sourceTree = ""; }; + 1B1D29C42BA9C04D003A5AC6 /* File.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = File.swift; sourceTree = ""; }; + 1B208C322BAE8FF4009A361C /* CartManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CartManager.swift; sourceTree = ""; }; + 1B208C342BAE919F009A361C /* CartModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CartModel.swift; sourceTree = ""; }; + 1B208C362BAE91D2009A361C /* CartItemModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CartItemModel.swift; sourceTree = ""; }; + 1B2CB2DF2BB4925900575E4C /* ProfileView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileView.swift; sourceTree = ""; }; + 1B2CB2E12BB4978300575E4C /* ProfileModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileModel.swift; sourceTree = ""; }; + 1B2DCA5A2BB6B20D004D2790 /* OrderModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OrderModel.swift; sourceTree = ""; }; + 1B2DCA5C2BB8539B004D2790 /* OrderManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OrderManager.swift; sourceTree = ""; }; + 1B2DCA5E2BB85530004D2790 /* Configuration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Configuration.swift; sourceTree = ""; }; + 1B3778812BA433E20037B375 /* HomeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeView.swift; sourceTree = ""; }; + 1BD6DB082BA23A5F00E42988 /* Vogue Vista.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Vogue Vista.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + 1BD6DB0B2BA23A5F00E42988 /* Vogue_VistaApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Vogue_VistaApp.swift; sourceTree = ""; }; + 1BD6DB0F2BA23A6100E42988 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 1BD6DB112BA23A6100E42988 /* Vogue_Vista.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Vogue_Vista.entitlements; sourceTree = ""; }; + 1BD6DB132BA23A6100E42988 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; + 1BD6DB212BA2401900E42988 /* ProductModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProductModel.swift; sourceTree = ""; }; + 1BD6DB232BA2402900E42988 /* UsersModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UsersModel.swift; sourceTree = ""; }; + 1BD6DB2A2BA2407600E42988 /* ProductListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProductListView.swift; sourceTree = ""; }; + 1BD6DB2C2BA2408400E42988 /* ProductDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProductDetailView.swift; sourceTree = ""; }; + 1BD6DB2E2BA2408E00E42988 /* ShoppingCartView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShoppingCartView.swift; sourceTree = ""; }; + 1BD6DB302BA2409700E42988 /* CheckoutView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckoutView.swift; sourceTree = ""; }; + 1BD6DB332BA240BE00E42988 /* ProductListViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProductListViewModel.swift; sourceTree = ""; }; + 1BD6DB372BA240DC00E42988 /* ShoppingCartViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShoppingCartViewModel.swift; sourceTree = ""; }; + 1BD6DB392BA240E500E42988 /* CheckoutViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckoutViewModel.swift; sourceTree = ""; }; + 1BD6DB452BA2413600E42988 /* Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Extensions.swift; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 1BD6DB052BA23A5F00E42988 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 1BD6DB502BA2460B00E42988 /* Auth0 in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 1B1D29C52BA9E15D003A5AC6 /* Backup */ = { + isa = PBXGroup; + children = ( + 1BD6DB2A2BA2407600E42988 /* ProductListView.swift */, + 1B1D29C42BA9C04D003A5AC6 /* File.swift */, + ); + path = Backup; + sourceTree = ""; + }; + 1BD6DAFF2BA23A5F00E42988 = { + isa = PBXGroup; + children = ( + 1B1D29C52BA9E15D003A5AC6 /* Backup */, + 1BD6DB0A2BA23A5F00E42988 /* Vogue Vista */, + 1BD6DB092BA23A5F00E42988 /* Products */, + ); + sourceTree = ""; + }; + 1BD6DB092BA23A5F00E42988 /* Products */ = { + isa = PBXGroup; + children = ( + 1BD6DB082BA23A5F00E42988 /* Vogue Vista.app */, + ); + name = Products; + sourceTree = ""; + }; + 1BD6DB0A2BA23A5F00E42988 /* Vogue Vista */ = { + isa = PBXGroup; + children = ( + 1BD6DB0B2BA23A5F00E42988 /* Vogue_VistaApp.swift */, + 1B1D29C22BA9BE72003A5AC6 /* UIView.swift */, + 1B1D29BC2BA9BBB0003A5AC6 /* StartupPageView.swift */, + 1BD6DB202BA2400900E42988 /* Models */, + 1BD6DB252BA2403A00E42988 /* Views */, + 1BD6DB322BA240A600E42988 /* ViewModels */, + 1BD6DB3B2BA240EB00E42988 /* Services */, + 1BD6DB422BA2412300E42988 /* Utilities */, + 1BD6DB0F2BA23A6100E42988 /* Assets.xcassets */, + 1BD6DB112BA23A6100E42988 /* Vogue_Vista.entitlements */, + 1BD6DB122BA23A6100E42988 /* Preview Content */, + ); + path = "Vogue Vista"; + sourceTree = ""; + }; + 1BD6DB122BA23A6100E42988 /* Preview Content */ = { + isa = PBXGroup; + children = ( + 1BD6DB132BA23A6100E42988 /* Preview Assets.xcassets */, + ); + path = "Preview Content"; + sourceTree = ""; + }; + 1BD6DB202BA2400900E42988 /* Models */ = { + isa = PBXGroup; + children = ( + 1BD6DB212BA2401900E42988 /* ProductModel.swift */, + 1BD6DB232BA2402900E42988 /* UsersModel.swift */, + 1B208C342BAE919F009A361C /* CartModel.swift */, + 1B2CB2E12BB4978300575E4C /* ProfileModel.swift */, + 1B2DCA5A2BB6B20D004D2790 /* OrderModel.swift */, + 1B208C362BAE91D2009A361C /* CartItemModel.swift */, + ); + path = Models; + sourceTree = ""; + }; + 1BD6DB252BA2403A00E42988 /* Views */ = { + isa = PBXGroup; + children = ( + 1B1D29BE2BA9BBD2003A5AC6 /* SignUpView.swift */, + 1B1D29C02BA9BC06003A5AC6 /* LoginView.swift */, + 1B3778812BA433E20037B375 /* HomeView.swift */, + 1B2CB2DF2BB4925900575E4C /* ProfileView.swift */, + 1BD6DB2C2BA2408400E42988 /* ProductDetailView.swift */, + 1BD6DB2E2BA2408E00E42988 /* ShoppingCartView.swift */, + 1BD6DB302BA2409700E42988 /* CheckoutView.swift */, + ); + path = Views; + sourceTree = ""; + }; + 1BD6DB322BA240A600E42988 /* ViewModels */ = { + isa = PBXGroup; + children = ( + 1BD6DB332BA240BE00E42988 /* ProductListViewModel.swift */, + 1BD6DB372BA240DC00E42988 /* ShoppingCartViewModel.swift */, + 1BD6DB392BA240E500E42988 /* CheckoutViewModel.swift */, + ); + path = ViewModels; + sourceTree = ""; + }; + 1BD6DB3B2BA240EB00E42988 /* Services */ = { + isa = PBXGroup; + children = ( + 1B208C322BAE8FF4009A361C /* CartManager.swift */, + 1B2DCA5C2BB8539B004D2790 /* OrderManager.swift */, + 1B2DCA5E2BB85530004D2790 /* Configuration.swift */, + ); + path = Services; + sourceTree = ""; + }; + 1BD6DB422BA2412300E42988 /* Utilities */ = { + isa = PBXGroup; + children = ( + 1BD6DB452BA2413600E42988 /* Extensions.swift */, + ); + path = Utilities; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 1BD6DB072BA23A5F00E42988 /* Vogue Vista */ = { + isa = PBXNativeTarget; + buildConfigurationList = 1BD6DB172BA23A6100E42988 /* Build configuration list for PBXNativeTarget "Vogue Vista" */; + buildPhases = ( + 1BD6DB042BA23A5F00E42988 /* Sources */, + 1BD6DB052BA23A5F00E42988 /* Frameworks */, + 1BD6DB062BA23A5F00E42988 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "Vogue Vista"; + packageProductDependencies = ( + 1BD6DB4F2BA2460B00E42988 /* Auth0 */, + ); + productName = "Vogue Vista"; + productReference = 1BD6DB082BA23A5F00E42988 /* Vogue Vista.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 1BD6DB002BA23A5F00E42988 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 1510; + LastUpgradeCheck = 1510; + TargetAttributes = { + 1BD6DB072BA23A5F00E42988 = { + CreatedOnToolsVersion = 15.1; + }; + }; + }; + buildConfigurationList = 1BD6DB032BA23A5F00E42988 /* Build configuration list for PBXProject "Vogue Vista" */; + compatibilityVersion = "Xcode 14.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 1BD6DAFF2BA23A5F00E42988; + packageReferences = ( + 1BD6DB4E2BA2460A00E42988 /* XCRemoteSwiftPackageReference "Auth0.swift" */, + ); + productRefGroup = 1BD6DB092BA23A5F00E42988 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 1BD6DB072BA23A5F00E42988 /* Vogue Vista */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 1BD6DB062BA23A5F00E42988 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 1BD6DB142BA23A6100E42988 /* Preview Assets.xcassets in Resources */, + 1BD6DB102BA23A6100E42988 /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 1BD6DB042BA23A5F00E42988 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 1B1D29C62BA9E167003A5AC6 /* File.swift in Sources */, + 1BD6DB2D2BA2408400E42988 /* ProductDetailView.swift in Sources */, + 1BD6DB382BA240DC00E42988 /* ShoppingCartViewModel.swift in Sources */, + 1B1D29BF2BA9BBD2003A5AC6 /* SignUpView.swift in Sources */, + 1B208C332BAE8FF4009A361C /* CartManager.swift in Sources */, + 1B208C352BAE919F009A361C /* CartModel.swift in Sources */, + 1B208C372BAE91D2009A361C /* CartItemModel.swift in Sources */, + 1B2CB2E22BB4978300575E4C /* ProfileModel.swift in Sources */, + 1BD6DB2B2BA2407600E42988 /* ProductListView.swift in Sources */, + 1B1D29BD2BA9BBB0003A5AC6 /* StartupPageView.swift in Sources */, + 1BD6DB462BA2413600E42988 /* Extensions.swift in Sources */, + 1B2DCA5D2BB8539B004D2790 /* OrderManager.swift in Sources */, + 1B3778822BA433E20037B375 /* HomeView.swift in Sources */, + 1B2CB2E02BB4925900575E4C /* ProfileView.swift in Sources */, + 1B1D29C12BA9BC06003A5AC6 /* LoginView.swift in Sources */, + 1BD6DB312BA2409700E42988 /* CheckoutView.swift in Sources */, + 1BD6DB0C2BA23A5F00E42988 /* Vogue_VistaApp.swift in Sources */, + 1BD6DB342BA240BE00E42988 /* ProductListViewModel.swift in Sources */, + 1BD6DB222BA2401900E42988 /* ProductModel.swift in Sources */, + 1B2DCA5F2BB85530004D2790 /* Configuration.swift in Sources */, + 1BD6DB3A2BA240E500E42988 /* CheckoutViewModel.swift in Sources */, + 1B2DCA5B2BB6B20D004D2790 /* OrderModel.swift in Sources */, + 1BD6DB2F2BA2408E00E42988 /* ShoppingCartView.swift in Sources */, + 1BD6DB242BA2402900E42988 /* UsersModel.swift in Sources */, + 1B1D29C32BA9BE73003A5AC6 /* UIView.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 1BD6DB152BA23A6100E42988 /* 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; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 1BD6DB162BA23A6100E42988 /* 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; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SWIFT_COMPILATION_MODE = wholemodule; + }; + name = Release; + }; + 1BD6DB182BA23A6100E42988 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_ENTITLEMENTS = "Vogue Vista/Vogue_Vista.entitlements"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"Vogue Vista/Preview Content\""; + DEVELOPMENT_TEAM = S36S3PC342; + ENABLE_HARDENED_RUNTIME = YES; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = "Vogue-Vista-Info.plist"; + "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES; + "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphonesimulator*]" = YES; + "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphoneos*]" = YES; + "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphonesimulator*]" = YES; + "INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphoneos*]" = YES; + "INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphonesimulator*]" = YES; + "INFOPLIST_KEY_UIStatusBarStyle[sdk=iphoneos*]" = UIStatusBarStyleDefault; + "INFOPLIST_KEY_UIStatusBarStyle[sdk=iphonesimulator*]" = UIStatusBarStyleDefault; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + IPHONEOS_DEPLOYMENT_TARGET = 17.2; + LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks"; + "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 14.2; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = "com.thiranjaya.Vogue-Vista"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = auto; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 1BD6DB192BA23A6100E42988 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_ENTITLEMENTS = "Vogue Vista/Vogue_Vista.entitlements"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"Vogue Vista/Preview Content\""; + DEVELOPMENT_TEAM = S36S3PC342; + ENABLE_HARDENED_RUNTIME = YES; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = "Vogue-Vista-Info.plist"; + "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES; + "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphonesimulator*]" = YES; + "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphoneos*]" = YES; + "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphonesimulator*]" = YES; + "INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphoneos*]" = YES; + "INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphonesimulator*]" = YES; + "INFOPLIST_KEY_UIStatusBarStyle[sdk=iphoneos*]" = UIStatusBarStyleDefault; + "INFOPLIST_KEY_UIStatusBarStyle[sdk=iphonesimulator*]" = UIStatusBarStyleDefault; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + IPHONEOS_DEPLOYMENT_TARGET = 17.2; + LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks"; + "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 14.2; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = "com.thiranjaya.Vogue-Vista"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = auto; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 1BD6DB032BA23A5F00E42988 /* Build configuration list for PBXProject "Vogue Vista" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1BD6DB152BA23A6100E42988 /* Debug */, + 1BD6DB162BA23A6100E42988 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 1BD6DB172BA23A6100E42988 /* Build configuration list for PBXNativeTarget "Vogue Vista" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1BD6DB182BA23A6100E42988 /* Debug */, + 1BD6DB192BA23A6100E42988 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + +/* Begin XCRemoteSwiftPackageReference section */ + 1BD6DB4E2BA2460A00E42988 /* XCRemoteSwiftPackageReference "Auth0.swift" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/auth0/Auth0.swift.git"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 2.6.0; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 1BD6DB4F2BA2460B00E42988 /* Auth0 */ = { + isa = XCSwiftPackageProductDependency; + package = 1BD6DB4E2BA2460A00E42988 /* XCRemoteSwiftPackageReference "Auth0.swift" */; + productName = Auth0; + }; +/* End XCSwiftPackageProductDependency section */ + }; + rootObject = 1BD6DB002BA23A5F00E42988 /* Project object */; +} diff --git a/Vogue Vista.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Vogue Vista.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/Vogue Vista.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/Vogue Vista.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Vogue Vista.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/Vogue Vista.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Vogue Vista.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Vogue Vista.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved new file mode 100644 index 0000000..f6253d1 --- /dev/null +++ b/Vogue Vista.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -0,0 +1,32 @@ +{ + "pins" : [ + { + "identity" : "auth0.swift", + "kind" : "remoteSourceControl", + "location" : "https://github.com/auth0/Auth0.swift.git", + "state" : { + "revision" : "a4da5b3f8c962357b67620dae8d5e2a4f9ca02fd", + "version" : "2.6.0" + } + }, + { + "identity" : "jwtdecode.swift", + "kind" : "remoteSourceControl", + "location" : "https://github.com/auth0/JWTDecode.swift.git", + "state" : { + "revision" : "58af7278797871e460d79496621b3e5366b865b2", + "version" : "3.1.0" + } + }, + { + "identity" : "simplekeychain", + "kind" : "remoteSourceControl", + "location" : "https://github.com/auth0/SimpleKeychain.git", + "state" : { + "revision" : "f082494aab8056139a4fe234c920a9f92f681aff", + "version" : "1.1.0" + } + } + ], + "version" : 2 +} diff --git a/Vogue Vista.xcodeproj/project.xcworkspace/xcuserdata/thiranjaya.xcuserdatad/Bookmarks/bookmarks.plist b/Vogue Vista.xcodeproj/project.xcworkspace/xcuserdata/thiranjaya.xcuserdatad/Bookmarks/bookmarks.plist new file mode 100644 index 0000000..405f488 --- /dev/null +++ b/Vogue Vista.xcodeproj/project.xcworkspace/xcuserdata/thiranjaya.xcuserdatad/Bookmarks/bookmarks.plist @@ -0,0 +1,28 @@ + + + + + top-level-items + + + destination + + rebasable-url + + base + workspace + payload + + relative-path + Backup/File.swift + + + type + DVTDocumentLocation + + type + bookmark + + + + diff --git a/Vogue Vista.xcodeproj/project.xcworkspace/xcuserdata/thiranjaya.xcuserdatad/IDEFindNavigatorScopes.plist b/Vogue Vista.xcodeproj/project.xcworkspace/xcuserdata/thiranjaya.xcuserdatad/IDEFindNavigatorScopes.plist new file mode 100644 index 0000000..5dd5da8 --- /dev/null +++ b/Vogue Vista.xcodeproj/project.xcworkspace/xcuserdata/thiranjaya.xcuserdatad/IDEFindNavigatorScopes.plist @@ -0,0 +1,5 @@ + + + + + diff --git a/Vogue Vista.xcodeproj/project.xcworkspace/xcuserdata/thiranjaya.xcuserdatad/UserInterfaceState.xcuserstate b/Vogue Vista.xcodeproj/project.xcworkspace/xcuserdata/thiranjaya.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 0000000..2f56274 Binary files /dev/null and b/Vogue Vista.xcodeproj/project.xcworkspace/xcuserdata/thiranjaya.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/Vogue Vista.xcodeproj/xcuserdata/thiranjaya.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/Vogue Vista.xcodeproj/xcuserdata/thiranjaya.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist new file mode 100644 index 0000000..7533ef2 --- /dev/null +++ b/Vogue Vista.xcodeproj/xcuserdata/thiranjaya.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + diff --git a/Vogue Vista.xcodeproj/xcuserdata/thiranjaya.xcuserdatad/xcschemes/xcschememanagement.plist b/Vogue Vista.xcodeproj/xcuserdata/thiranjaya.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000..a6e48cf --- /dev/null +++ b/Vogue Vista.xcodeproj/xcuserdata/thiranjaya.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,35 @@ + + + + + SchemeUserState + + JWTDecode (Playground) 1.xcscheme + + isShown + + orderHint + 2 + + JWTDecode (Playground) 2.xcscheme + + isShown + + orderHint + 3 + + JWTDecode (Playground).xcscheme + + isShown + + orderHint + 0 + + Vogue Vista.xcscheme_^#shared#^_ + + orderHint + 0 + + + + diff --git a/Vogue Vista/Assets.xcassets/AccentColor.colorset/Contents.json b/Vogue Vista/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000..eb87897 --- /dev/null +++ b/Vogue Vista/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Vogue Vista/Assets.xcassets/AppIcon.appiconset/Contents.json b/Vogue Vista/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..6875061 --- /dev/null +++ b/Vogue Vista/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,64 @@ +{ + "images" : [ + { + "filename" : "logo.jpg", + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "16x16" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "16x16" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "32x32" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "32x32" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "128x128" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "128x128" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "256x256" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "256x256" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "512x512" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "512x512" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Vogue Vista/Assets.xcassets/AppIcon.appiconset/logo.jpg b/Vogue Vista/Assets.xcassets/AppIcon.appiconset/logo.jpg new file mode 100644 index 0000000..c12a785 Binary files /dev/null and b/Vogue Vista/Assets.xcassets/AppIcon.appiconset/logo.jpg differ diff --git a/Vogue Vista/Assets.xcassets/Contents.json b/Vogue Vista/Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/Vogue Vista/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Vogue Vista/Assets.xcassets/checkoutProcessImage.imageset/1024 1.png b/Vogue Vista/Assets.xcassets/checkoutProcessImage.imageset/1024 1.png new file mode 100644 index 0000000..0c18f42 Binary files /dev/null and b/Vogue Vista/Assets.xcassets/checkoutProcessImage.imageset/1024 1.png differ diff --git a/Vogue Vista/Assets.xcassets/checkoutProcessImage.imageset/1024 2.png b/Vogue Vista/Assets.xcassets/checkoutProcessImage.imageset/1024 2.png new file mode 100644 index 0000000..0c18f42 Binary files /dev/null and b/Vogue Vista/Assets.xcassets/checkoutProcessImage.imageset/1024 2.png differ diff --git a/Vogue Vista/Assets.xcassets/checkoutProcessImage.imageset/1024.png b/Vogue Vista/Assets.xcassets/checkoutProcessImage.imageset/1024.png new file mode 100644 index 0000000..0c18f42 Binary files /dev/null and b/Vogue Vista/Assets.xcassets/checkoutProcessImage.imageset/1024.png differ diff --git a/Vogue Vista/Assets.xcassets/checkoutProcessImage.imageset/Contents.json b/Vogue Vista/Assets.xcassets/checkoutProcessImage.imageset/Contents.json new file mode 100644 index 0000000..5d4e30a --- /dev/null +++ b/Vogue Vista/Assets.xcassets/checkoutProcessImage.imageset/Contents.json @@ -0,0 +1,35 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + }, + { + "filename" : "1024.png", + "idiom" : "iphone", + "scale" : "1x" + }, + { + "filename" : "1024 1.png", + "idiom" : "iphone", + "scale" : "2x" + }, + { + "filename" : "1024 2.png", + "idiom" : "iphone", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Vogue Vista/Assets.xcassets/logoPng.imageset/Contents.json b/Vogue Vista/Assets.xcassets/logoPng.imageset/Contents.json new file mode 100644 index 0000000..c3e8329 --- /dev/null +++ b/Vogue Vista/Assets.xcassets/logoPng.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "logo png.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "logo png 1.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "logo png 2.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Vogue Vista/Assets.xcassets/logoPng.imageset/logo png 1.png b/Vogue Vista/Assets.xcassets/logoPng.imageset/logo png 1.png new file mode 100644 index 0000000..3a82d68 Binary files /dev/null and b/Vogue Vista/Assets.xcassets/logoPng.imageset/logo png 1.png differ diff --git a/Vogue Vista/Assets.xcassets/logoPng.imageset/logo png 2.png b/Vogue Vista/Assets.xcassets/logoPng.imageset/logo png 2.png new file mode 100644 index 0000000..3a82d68 Binary files /dev/null and b/Vogue Vista/Assets.xcassets/logoPng.imageset/logo png 2.png differ diff --git a/Vogue Vista/Assets.xcassets/logoPng.imageset/logo png.png b/Vogue Vista/Assets.xcassets/logoPng.imageset/logo png.png new file mode 100644 index 0000000..3a82d68 Binary files /dev/null and b/Vogue Vista/Assets.xcassets/logoPng.imageset/logo png.png differ diff --git a/Vogue Vista/Models/CartItemModel.swift b/Vogue Vista/Models/CartItemModel.swift new file mode 100644 index 0000000..5744cc1 --- /dev/null +++ b/Vogue Vista/Models/CartItemModel.swift @@ -0,0 +1,20 @@ + +import Foundation + +struct CartItem: Codable, Identifiable { + let id: Int + let product_id: Int + let color: String + let size: String + let quantity: Int + let price: String + let name: String + let created_at: String + let cart_id: Int + let imageUrl: String + + private enum CodingKeys: String, CodingKey { + case id, product_id, color, size, quantity, price, name, created_at, cart_id + case imageUrl = "imageurl" + } +} diff --git a/Vogue Vista/Models/CartModel.swift b/Vogue Vista/Models/CartModel.swift new file mode 100644 index 0000000..d2002d5 --- /dev/null +++ b/Vogue Vista/Models/CartModel.swift @@ -0,0 +1,8 @@ +import Foundation + +struct Cart: Codable { + let id: Int + let user_id: Int + let created_at: String + let items: [CartItem] +} diff --git a/Vogue Vista/Models/OrderModel.swift b/Vogue Vista/Models/OrderModel.swift new file mode 100644 index 0000000..49eddc7 --- /dev/null +++ b/Vogue Vista/Models/OrderModel.swift @@ -0,0 +1,23 @@ +import Foundation + +struct Order: Codable, Identifiable { + var id: Int + var userId: Int + var items: [CartItem] + var address: String + var cardNumber: String + var totalAmount: Double + var createdAt: String + var status: String + + private enum CodingKeys: String, CodingKey { + case id + case userId = "userId" + case items + case address + case cardNumber = "cardNumber" + case totalAmount = "totalAmount" + case createdAt = "createdAt" + case status + } +} diff --git a/Vogue Vista/Models/ProductModel.swift b/Vogue Vista/Models/ProductModel.swift new file mode 100644 index 0000000..b077817 --- /dev/null +++ b/Vogue Vista/Models/ProductModel.swift @@ -0,0 +1,23 @@ +import Foundation + +struct Product: Identifiable, Decodable { + var id: Int + var name: String + var description: String + var price: String + var category: String + var color: String + var size: String + var imageUrl: String + var isSpecialOffer: Bool? + var specialOfferSince: String? + var createdAt: String? + + enum CodingKeys: String, CodingKey { + case id, name, description, price, category, color, size + case imageUrl = "imageurl" + case isSpecialOffer = "is_special_offer" + case specialOfferSince = "special_offer_since" + case createdAt = "created_at" + } +} diff --git a/Vogue Vista/Models/ProfileModel.swift b/Vogue Vista/Models/ProfileModel.swift new file mode 100644 index 0000000..15f877d --- /dev/null +++ b/Vogue Vista/Models/ProfileModel.swift @@ -0,0 +1,57 @@ + +import Foundation + +struct Profile: Codable, Equatable { + var id: String + var name: String + var email: String + var address: String? +} + +class ProfileModel: ObservableObject { + @Published var profile: Profile? + let baseURL = URL(string: AppConfiguration.serverURL)! + + func fetchProfileData(userId: Int) { + guard let url = URL(string: "\(baseURL)/users/\(userId)") else { return } + + URLSession.shared.dataTask(with: url) { data, response, error in + if let data = data { + do { + let decodedData = try JSONDecoder().decode(Profile.self, from: data) + DispatchQueue.main.async { + self.profile = decodedData + } + print("Fetched Profile: \(decodedData)") + } catch { + print("Failed to decode JSON: \(error)") + } + } else if let error = error { + print("Failed to fetch data: \(error)") + } + }.resume() + } + + func updateProfileData() { + guard let profile = profile, + let url = URL(string: "\(baseURL)/users/\(profile.id)") else { return } + + var request = URLRequest(url: url) + request.httpMethod = "PUT" + request.addValue("application/json", forHTTPHeaderField: "Content-Type") + do { + let jsonData = try JSONEncoder().encode(profile) + request.httpBody = jsonData + + URLSession.shared.dataTask(with: request) { data, response, error in + if let error = error { + print("Failed to update profile: \(error)") + } else { + print("Profile successfully updated.") + } + }.resume() + } catch { + print("Failed to encode profile data: \(error)") + } + } +} diff --git a/Vogue Vista/Models/UsersModel.swift b/Vogue Vista/Models/UsersModel.swift new file mode 100644 index 0000000..cf909d3 --- /dev/null +++ b/Vogue Vista/Models/UsersModel.swift @@ -0,0 +1,60 @@ +import Foundation + +struct User: Codable { + var id: Int? + var email: String + var name: String? +} + +struct LoginResponse: Codable { + let token: String + let userId: Int +} + +class UsersModel { + let baseURL = URL(string: AppConfiguration.serverURL)! + + func registerUser(email: String, password: String, name: String, completion: @escaping (Bool, Error?) -> Void) { + guard let url = URL(string: "\(baseURL)/users/register") else { return } // Corrected endpoint + var request = URLRequest(url: url) + request.httpMethod = "POST" + request.addValue("application/json", forHTTPHeaderField: "Content-Type") + let body = ["email": email, "password": password, "name": name] + guard let httpBody = try? JSONEncoder().encode(body) else { return } + request.httpBody = httpBody + URLSession.shared.dataTask(with: request) { data, response, error in + if let error = error { + completion(false, error) + return + } + // Decoding response to ensure user creation was successful + guard let data = data, let _ = try? JSONDecoder().decode(User.self, from: data) else { + completion(false, NSError(domain: "", code: 0, userInfo: [NSLocalizedDescriptionKey: "Failed to decode response"])) + return + } + completion(true, nil) + }.resume() + } + + func loginUser(email: String, password: String, completion: @escaping (LoginResponse?, Error?) -> Void) { + guard let url = URL(string: "\(baseURL)/users/login") else { return } // Ensure this matches your actual login endpoint + var request = URLRequest(url: url) + request.httpMethod = "POST" + request.addValue("application/json", forHTTPHeaderField: "Content-Type") + let body = ["email": email, "password": password] + guard let httpBody = try? JSONEncoder().encode(body) else { return } + request.httpBody = httpBody + URLSession.shared.dataTask(with: request) { data, response, error in + if let error = error { + completion(nil, error) + return + } + guard let data = data, let loginResponse = try? JSONDecoder().decode(LoginResponse.self, from: data) else { + completion(nil, NSError(domain: "", code: 0, userInfo: [NSLocalizedDescriptionKey: "Invalid response"])) + return + } + completion(loginResponse, nil) + + }.resume() + } +} diff --git a/Vogue Vista/Preview Content/Preview Assets.xcassets/Contents.json b/Vogue Vista/Preview Content/Preview Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/Vogue Vista/Preview Content/Preview Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Vogue Vista/README.md b/Vogue Vista/README.md new file mode 100644 index 0000000..284dc76 --- /dev/null +++ b/Vogue Vista/README.md @@ -0,0 +1,77 @@ +# Vogue Vista + +Vogue Vista is an iOS application designed for an online clothing brand. It allows users to browse, search, and purchase clothing items directly from their iOS devices. The app utilizes Auth0 for user authentication and Supabase for backend services. + +## Features + +- User Authentication with Auth0 +- Product Listings +- Product Detail Views +- Shopping Cart Management +- Checkout Functionality +- Responsive and Intuitive User Interface + +## Installation + +Before you can run the project, you need to follow these setup steps: + +### Prerequisites + +- Xcode 12 or later +- CocoaPods or Swift Package Manager +- An Auth0 account +- A Supabase project + +### Setting Up Auth0 + +1. Create a new application in your Auth0 dashboard. +2. Note down your Client ID and Domain. +3. Configure callback URLs, logout URLs, and allowed web origins as per your iOS app's settings. + +### Setting Up Supabase + +1. Create a new project in Supabase. +2. Note down the API URL and anon key from the project settings. + +### Configuring the iOS Project + +1. Clone the repository: + +```bash +git clone https://github.com/smdthiranjaya/Vogue-Vista.git +cd voguevista +```` +If using CocoaPods, run: +```bash +pod install +```` + +Open the .xcworkspace file in Xcode. + +2. Add your Auth0 ClientId and Domain to the Auth0.plist file. + +3. Update the Supabase URL and anon key in the application's network configuration. + +### Usage + +To run the application, open the project in Xcode and run it on a simulator or a physical device. + +- **Login/Signup:** Users can sign up or log in using the Auth0 integration. +- **Browse Products:** Users can browse the list of products available for purchase. +- **Product Details:** Users can view detailed information about a product. +- **Add to Cart:** Users can add products to their shopping cart. +- **Checkout:** Users can proceed to checkout to complete their purchase. + +### Contributing + +Contributions to Vogue Vista are welcome! Please read the CONTRIBUTING.md for guidelines on how to contribute to this project. + +### License + +This project is licensed under the MIT License - see the LICENSE file for details. + +### Acknowledgments + +- Auth0 for authentication services. +- Supabase for providing the backend as a service. +- All the open-source libraries and tools used in this project. diff --git a/Vogue Vista/Services/CartManager.swift b/Vogue Vista/Services/CartManager.swift new file mode 100644 index 0000000..f962181 --- /dev/null +++ b/Vogue Vista/Services/CartManager.swift @@ -0,0 +1,41 @@ +import Foundation + +class CartManager { + let baseURL = URL(string: AppConfiguration.serverURL)! + + func addToCart(userId: Int, productId: Int, color: String, size: String, quantity: Int, price: Double, name: String , imageUrl: String, completion: @escaping (Bool, String) -> Void) { + guard let url = URL(string: "\(baseURL)/cart/add") else { + completion(false, "Invalid URL") + return + } + + let body: [String: Any] = [ + "userId": userId, + "productId": productId, + "color": color, + "size": size, + "quantity": quantity, + "price": price, + "name": name, + "imageUrl": imageUrl + ] + + var request = URLRequest(url: url) + request.httpMethod = "POST" + request.addValue("application/json", forHTTPHeaderField: "Content-Type") + request.httpBody = try? JSONSerialization.data(withJSONObject: body, options: []) + + URLSession.shared.dataTask(with: request) { data, response, error in + guard error == nil else { + completion(false, "Network error: \(error!.localizedDescription)") + return + } + + if let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 { + completion(true, "Product added to cart successfully") + } else { + completion(false, "Failed to add product to cart") + } + }.resume() + } +} diff --git a/Vogue Vista/Services/Configuration.swift b/Vogue Vista/Services/Configuration.swift new file mode 100644 index 0000000..5313718 --- /dev/null +++ b/Vogue Vista/Services/Configuration.swift @@ -0,0 +1,4 @@ + +struct AppConfiguration { + static let serverURL = "https://ancient-taiga-27787-c7cd95aba2be.herokuapp.com" +} diff --git a/Vogue Vista/Services/OrderManager.swift b/Vogue Vista/Services/OrderManager.swift new file mode 100644 index 0000000..158a3da --- /dev/null +++ b/Vogue Vista/Services/OrderManager.swift @@ -0,0 +1,31 @@ +import Foundation + +class OrderManager { + let baseURL = URL(string: AppConfiguration.serverURL)! + + func sendOrderToServer(_ orderDetails: Order) { + guard let url = URL(string: "\(baseURL)/order/create") else { return } + var request = URLRequest(url: url) + request.httpMethod = "POST" + request.addValue("application/json", forHTTPHeaderField: "Content-Type") + + do { + let jsonData = try JSONEncoder().encode(orderDetails) + request.httpBody = jsonData + + URLSession.shared.dataTask(with: request) { data, response, error in + if let error = error { + print("Error sending order: \(error)") + return + } + guard let data = data else { + print("No data received") + return + } + }.resume() + } catch { + print("Error encoding order details: \(error)") + } + } + +} diff --git a/Vogue Vista/StartupPageView.swift b/Vogue Vista/StartupPageView.swift new file mode 100644 index 0000000..f202b96 --- /dev/null +++ b/Vogue Vista/StartupPageView.swift @@ -0,0 +1,52 @@ +import SwiftUI + + +struct StartupPageView: View { + @State private var showingSignUp = false + @State private var showingLogin = false + + var body: some View { + VStack(spacing: 20) { + Image("logoPng") + .resizable() + .aspectRatio(contentMode: .fit) + .frame(width: 200, height: 200) + + Text("Welcome to Vogue Vista!") + .font(.system(size: 24, weight: .medium)) + + Text("Your style, your way.") + .font(.system(size: 18, weight: .light)) + + Divider() + + Button("Sign Up") { + showingSignUp = true + } + .foregroundColor(.white) + .frame(minWidth: 0, maxWidth: .infinity) + .padding() + .background(AppColor.appPrimary) + .cornerRadius(10) + .padding() + + Button("Login") { + showingLogin = true + } + .foregroundColor(.white) + .frame(minWidth: 0, maxWidth: .infinity) + .padding() + .background(AppColor.appPrimary) + .cornerRadius(10) + .padding() + + } + .sheet(isPresented: $showingSignUp) { + SignUpView() + } + .sheet(isPresented: $showingLogin) { + LoginView() + } + } +} + diff --git a/Vogue Vista/UIView.swift b/Vogue Vista/UIView.swift new file mode 100644 index 0000000..21e14fa --- /dev/null +++ b/Vogue Vista/UIView.swift @@ -0,0 +1,15 @@ +import SwiftUI + +struct SwiftUIWrapperView: UIViewControllerRepresentable { + typealias UIViewControllerType = UIHostingController + + func makeUIViewController(context: Context) -> UIHostingController { + + let hostingController = UIHostingController(rootView: StartupPageView()) + return hostingController + } + + func updateUIViewController(_ uiViewController: UIHostingController, context: Context) { + + } +} diff --git a/Vogue Vista/Utilities/Extensions.swift b/Vogue Vista/Utilities/Extensions.swift new file mode 100644 index 0000000..e06a53e --- /dev/null +++ b/Vogue Vista/Utilities/Extensions.swift @@ -0,0 +1,24 @@ + +import SwiftUI + +enum AppColor { + static let appPrimary = Color(red: 38 / 255, green: 34 / 255, blue: 97 / 255) +} + +extension String { + var isValidEmail: Bool { + let emailRegEx = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}" + let emailPred = NSPredicate(format:"SELF MATCHES %@", emailRegEx) + return emailPred.evaluate(with: self) + } +} + +extension View { + func iconPrefix(systemName: String) -> some View { + HStack { + Image(systemName: systemName) + .foregroundColor(AppColor.appPrimary.opacity(0.8)) + self + } + } +} diff --git a/Vogue Vista/ViewModels/CheckoutViewModel.swift b/Vogue Vista/ViewModels/CheckoutViewModel.swift new file mode 100644 index 0000000..c7f5af9 --- /dev/null +++ b/Vogue Vista/ViewModels/CheckoutViewModel.swift @@ -0,0 +1,49 @@ +import Foundation + +class CheckoutViewModel: ObservableObject { + let baseURL = URL(string: AppConfiguration.serverURL)! + + + @Published var totalAmount: Double + + init(totalAmount: Double) { + self.totalAmount = totalAmount + } + + func checkoutCart() { + guard let userId = UserDefaults.standard.object(forKey: "userId") as? Int else { + print("User ID not found") + return + } + + let url = URL(string: "\(baseURL)/checkout/\(userId)")! + var request = URLRequest(url: url) + request.httpMethod = "POST" + request.addValue("application/json", forHTTPHeaderField: "Content-Type") + + URLSession.shared.dataTask(with: request) { data, response, error in + if let error = error { + DispatchQueue.main.async { + print("Checkout error: \(error.localizedDescription)") + } + return + } + guard let data = data else { + DispatchQueue.main.async { + print("No data received") + } + return + } + do { + let jsonResponse = try JSONSerialization.jsonObject(with: data, options: []) + DispatchQueue.main.async { + print("Checkout successful: \(jsonResponse)") + } + } catch { + DispatchQueue.main.async { + print("Failed to decode checkout response") + } + } + }.resume() + } +} diff --git a/Vogue Vista/ViewModels/ProductListViewModel.swift b/Vogue Vista/ViewModels/ProductListViewModel.swift new file mode 100644 index 0000000..2d61c27 --- /dev/null +++ b/Vogue Vista/ViewModels/ProductListViewModel.swift @@ -0,0 +1,87 @@ +import Foundation + +class ProductViewModel: ObservableObject { + @Published var products: [Product] = [] + @Published var specialOffers: [Product] = [] + + let baseURL = URL(string: AppConfiguration.serverURL)! + + func loadSpecialOffers() { + + guard let url = URL(string: "\(baseURL)/special-offers") else { + print("Invalid URL for special offers") + return + } + + let task = URLSession.shared.dataTask(with: url) { data, response, error in + if let error = error { + print("Error fetching data: \(error.localizedDescription)") + return + } + + guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else { + print("Error with the response, unexpected status code: \((response as? HTTPURLResponse)?.statusCode ?? -1)") + return + } + + if let data = data { + do { + let decodedResponse = try JSONDecoder().decode([Product].self, from: data) + DispatchQueue.main.async { + self.specialOffers = decodedResponse + print("Special offers loaded: \(self.specialOffers.count)") + } + } catch let jsonError { + print("Failed to decode JSON for special offers: \(jsonError.localizedDescription)") + } + } + } + + task.resume() + } + + func loadProducts(searchText: String = "") { + var components = URLComponents(string: "\(baseURL)/products") + + var queryItems = [URLQueryItem]() + if !searchText.isEmpty { + queryItems.append(URLQueryItem(name: "search", value: searchText)) + } + components?.queryItems = queryItems + + guard let url = components?.url else { + print("Invalid URL") + return + } + + let task = URLSession.shared.dataTask(with: url) { data, response, error in + if let error = error { + + print("Error fetching products: \(error.localizedDescription)") + return + } + + guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else { + + print("Error with the response, unexpected status code: \((response as? HTTPURLResponse)?.statusCode ?? -1)") + return + } + + if let data = data { + do { + + let decodedResponse = try JSONDecoder().decode([Product].self, from: data) + + DispatchQueue.main.async { + self.products = decodedResponse + } + } catch let jsonError { + + print("Failed to decode JSON: \(jsonError.localizedDescription)") + } + } + } + + task.resume() + } +} diff --git a/Vogue Vista/ViewModels/ShoppingCartViewModel.swift b/Vogue Vista/ViewModels/ShoppingCartViewModel.swift new file mode 100644 index 0000000..a3eb1d2 --- /dev/null +++ b/Vogue Vista/ViewModels/ShoppingCartViewModel.swift @@ -0,0 +1,87 @@ +import SwiftUI + +class ShoppingCartViewModel: ObservableObject { + @Published var cart: Cart? + @Published var items: [CartItem] = [] + + let baseURL = URL(string: AppConfiguration.serverURL)! + + struct RemoveItemResponse: Decodable { + let message: String + let item: RemovedItem? + } + + struct RemovedItem: Decodable { + let id: Int + } + + + var totalPrice: String { + let total = items.reduce(0.0) { $0 + (Double($1.price) ?? 0.0) * Double($1.quantity) } + return String(format: "%.2f", total) + } + + + func fetchCart() { + guard let userId = UserDefaults.standard.object(forKey: "userId") as? Int else { + print("User ID not found") + return + } + + guard let url = URL(string: "\(baseURL)/cart/\(userId)") else { return } + + var request = URLRequest(url: url) + request.addValue("application/json", forHTTPHeaderField: "Accept") + URLSession.shared.dataTask(with: request) { [weak self] data, response, error in + guard let data = data, error == nil else { + DispatchQueue.main.async { + print("Error fetching cart: \(error?.localizedDescription ?? "Unknown error")") + } + return + } + + do { + let decoder = JSONDecoder() + let decodedResponse = try decoder.decode(Cart.self, from: data) + DispatchQueue.main.async { + self?.cart = decodedResponse + self?.items = decodedResponse.items + } + } catch { + DispatchQueue.main.async { + print("Failed to decode cart: \(error.localizedDescription)") + } + } + }.resume() + } + + func removeItemFromCart(itemId: Int) { + guard let url = URL(string: "\(baseURL)/cart/item/\(itemId)") else { return } + + var request = URLRequest(url: url) + request.httpMethod = "DELETE" + + URLSession.shared.dataTask(with: request) { [weak self] data, response, error in + guard let data = data, error == nil else { + DispatchQueue.main.async { + print("Error removing item: \(error?.localizedDescription ?? "Unknown error")") + } + return + } + + do { + let removedItemResponse = try JSONDecoder().decode(RemoveItemResponse.self, from: data) + DispatchQueue.main.async { + print("Item removed: \(removedItemResponse.message)") + self?.fetchCart() + } + } catch { + DispatchQueue.main.async { + print("Failed to decode response: \(error.localizedDescription)") + } + } + }.resume() + } + + +} diff --git a/Vogue Vista/Views/CheckoutView.swift b/Vogue Vista/Views/CheckoutView.swift new file mode 100644 index 0000000..58a42ca --- /dev/null +++ b/Vogue Vista/Views/CheckoutView.swift @@ -0,0 +1,208 @@ +import SwiftUI + +struct CheckoutView: View { + @ObservedObject var checkoutViewModel: CheckoutViewModel + @ObservedObject var viewModel = ShoppingCartViewModel() + + private let OrderviewModel = OrderManager() + + let deliveryFee = 5.00 + let serviceFee = 2.00 + @State private var address: String = "" + @State private var cardNumber: String = "" + @State private var expiryDate: String = "" + @State private var cvv: String = "" + @State private var showingPopup = false + @Environment(\.presentationMode) var presentationMode + @State private var promoCode: String = "" + + + var body: some View { + + ScrollView (showsIndicators: false){ + VStack(alignment: .leading, spacing: 15) { + Text("Checkout") + .font(.title) + .bold() + .padding(.top, 20) + + TextField("Address", text: $address) + .textFieldStyle(RoundedBorderTextFieldStyle()) + .padding(.top, 5) + .iconPrefix(systemName: "location.fill") + + TextField("Card Number", text: $cardNumber) + .textFieldStyle(RoundedBorderTextFieldStyle()) + .iconPrefix(systemName: "creditcard.fill") + + TextField("Expiry Date", text: $expiryDate) + .textFieldStyle(RoundedBorderTextFieldStyle()) + .iconPrefix(systemName: "calendar") + + TextField("CVV", text: $cvv) + .textFieldStyle(RoundedBorderTextFieldStyle()) + .iconPrefix(systemName: "lock.fill") + + PromoCodeEntryField(promoCode: $promoCode) + + FeeRowView(title: "Subtotal", icon: "cart.fill", amount: checkoutViewModel.totalAmount) + FeeRowView(title: "Delivery Fee", icon: "bicycle", amount: deliveryFee) + FeeRowView(title: "Service Fee", icon: "wrench.and.screwdriver.fill", amount: serviceFee) + + TotalView(totalAmount: checkoutViewModel.totalAmount + deliveryFee + serviceFee) + + Text("By completing your purchase, you agree to our payment terms: All sales are final, payments are processed securely, and personal data is handled in accordance with our privacy policy. For support, please contact customer service.") + .font(.caption2) + .padding(.top, 20) + + } + .padding() + + Button("Place Order") { + if validateInputs() { + placeOrder() + } + } + .frame(minWidth: 0, maxWidth: .infinity) + .padding() + .foregroundColor(.white) + .background(validateInputs() ? AppColor.appPrimary : AppColor.appPrimary.opacity(0.1)) + .cornerRadius(10) + .padding() + }.onAppear { + viewModel.fetchCart() + } + .padding() + .navigationTitle("Checkout") + .navigationBarTitleDisplayMode(.inline) + .overlay( + showingPopup ? PopupMessageView(showingPopup: $showingPopup) : nil + ) + } + + private func validateInputs() -> Bool { + return !address.isEmpty && !cardNumber.isEmpty && !expiryDate.isEmpty && !cvv.isEmpty + } + + private func placeOrder() { + guard let userId = UserDefaults.standard.object(forKey: "userId") as? Int else { + print("User ID not found") + return + } + let orderDetails = Order( id: 1, userId: userId, items: viewModel.items, address: address, cardNumber: cardNumber, totalAmount: checkoutViewModel.totalAmount + deliveryFee + serviceFee, createdAt: getCurrentDate(), status: "Pending") + + OrderviewModel.sendOrderToServer(orderDetails) + + showingPopup = true + DispatchQueue.main.asyncAfter(deadline: .now() + 2) { + showingPopup = false + presentationMode.wrappedValue.dismiss() + } + } +} + + +private func getCurrentDate() -> String { + let formatter = DateFormatter() + formatter.dateFormat = "yyyy-MM-dd HH:mm:ss" + return formatter.string(from: Date()) +} + + +struct FeeRowView: View { + let title: String + let icon: String + let amount: Double + var body: some View { + HStack { + Image(systemName: icon) + .foregroundColor(AppColor.appPrimary) + Text(title) + Spacer() + Text("$\(amount, specifier: "%.2f")") + } + .padding() + .background(Color.gray.opacity(0.1)) + .cornerRadius(10) + } +} + +struct PrimaryButtonStyle: ButtonStyle { + func makeBody(configuration: Configuration) -> some View { + configuration.label + .padding() + .background(AppColor.appPrimary) + .foregroundColor(.white) + .clipShape(RoundedRectangle(cornerRadius: 10)) + .scaleEffect(configuration.isPressed ? 0.95 : 1) + } +} +struct TotalView: View { + let totalAmount: Double + + var body: some View { + HStack { + Image(systemName: "sum") + .foregroundColor(AppColor.appPrimary) + Text("Total") + .font(.title2) + .bold() + Spacer() + Text("$\(totalAmount, specifier: "%.2f")") + .font(.title2) + .bold() + } + .padding() + .background(AppColor.appPrimary.opacity(0.1)) + .cornerRadius(10) + } +} + + + + +struct PopupMessageView: View { + @Binding var showingPopup: Bool + @State private var isAnimating = false + + var body: some View { + VStack { + Image(systemName: "checkmark.circle.fill") + .resizable() + .frame(width: 100, height: 100) + .foregroundColor(.green) + .scaleEffect(isAnimating ? 1.1 : 1.0) + .onAppear { + withAnimation(.easeInOut(duration: 0.8).repeatForever(autoreverses: true)) { + isAnimating = true + } + } + Text("Order Placed!") + .font(.headline) + .foregroundColor(.white) + } + .padding() + .background(Color.black.opacity(0.7)) + .cornerRadius(20) + .padding(100) + .onAppear { + DispatchQueue.main.asyncAfter(deadline: .now() + 2) { + showingPopup = false + } + } + } +} + +struct PromoCodeEntryField: View { + @Binding var promoCode: String + + var body: some View { + VStack(alignment: .leading) { + Text("Have a promo code?") + .font(.headline) + TextField("Promo Code", text: $promoCode) + .textFieldStyle(RoundedBorderTextFieldStyle()) + .padding(.top, 5) + } + } +} diff --git a/Vogue Vista/Views/HomeView.swift b/Vogue Vista/Views/HomeView.swift new file mode 100644 index 0000000..463b5ef --- /dev/null +++ b/Vogue Vista/Views/HomeView.swift @@ -0,0 +1,143 @@ + +import SwiftUI + +struct HomeView: View { + @State private var searchText = "" + @State private var products: [Product] = [] + @State private var specialOffers: [Product] = [] + @State private var navigateToProfile = false + @State private var navigateToCart = false + @ObservedObject var viewModel = ProductViewModel() + + var body: some View { + NavigationView { + ZStack { + Color.white.edgesIgnoringSafeArea(.all) + ScrollView (showsIndicators: false){ + VStack { + + HStack{ + Image("checkoutProcessImage") + .resizable() + .scaledToFit() + .frame(width: 30, height: 30) + Text("Welcome to Vogue Vista!") + .font(.headline) + .fontWeight(.bold) + .padding() + + } + + Text("Discover your unique style with us. Explore the latest trends and timeless pieces that make you feel at home in the world of fashion.") + .font(.subheadline) + .multilineTextAlignment(.center) + .padding([.leading, .trailing, .bottom]) + + TextField("Search...", text: $searchText) + .padding(7) + .background(Color(.systemGray6)) + .cornerRadius(8) + .padding(.top, 20) + .onSubmit { + viewModel.loadProducts(searchText: searchText) + } + + if !viewModel.specialOffers.isEmpty { + VStack(alignment: .leading) { + Text("Special Offers") + .font(.headline) + .padding() + + ScrollView(.horizontal, showsIndicators: false) { + HStack(spacing: 20) { + ForEach(viewModel.specialOffers) { offer in + VStack(alignment: .leading) { + AsyncImage(url: URL(string: offer.imageUrl)) { image in + image.resizable() + } placeholder: { + Color.gray + } + .frame(width: 100, height: 100) + .cornerRadius(5) + + Text(offer.name) + .font(.headline) + Text("$\(offer.price)") + .font(.subheadline) + .bold() + .foregroundStyle(AppColor.appPrimary) + } + .padding(.leading, 10) + } + } + .padding() + } + } + } + + Divider() + + Text("New Arrivals") + .font(.headline) + .padding() + + ForEach(viewModel.products) { product in + NavigationLink(destination: ProductDetailView(products: [product])) { + HStack { + AsyncImage(url: URL(string: product.imageUrl)) { image in + image.resizable() + } placeholder: { + ProgressView() + } + .frame(width: 100, height: 100) + .cornerRadius(5) + + VStack(alignment: .leading) { + Text(product.name) + .font(.headline) + .foregroundStyle(Color.black) + Text(product.description) + .font(.subheadline) + .lineLimit(2) + .foregroundStyle(Color.black) + Text("$\(product.price)") + .font(.subheadline) + .bold() + .foregroundStyle(AppColor.appPrimary) + } + } + .padding(.vertical) + } + } + } + } + .padding(.horizontal) + + NavigationLink(destination: ShoppingCartView(), isActive: $navigateToCart) { EmptyView() }.hidden() + NavigationLink(destination: ProfileView(), isActive: $navigateToProfile) { EmptyView() }.hidden() + } + .navigationBarTitle(Text("Home"), displayMode: .inline) + .navigationBarItems( + leading: HStack { + Button(action: { + self.navigateToProfile = true + }) { + Image(systemName: "person.crop.circle.fill") + .imageScale(.large) + .foregroundStyle(AppColor.appPrimary) + } + }, + trailing: Button(action: { + self.navigateToCart = true + }) { + Image(systemName: "cart.fill") + .foregroundStyle(AppColor.appPrimary) + } + ) + .onAppear { + viewModel.loadProducts() + viewModel.loadSpecialOffers() + } + } + } +} diff --git a/Vogue Vista/Views/LoginView.swift b/Vogue Vista/Views/LoginView.swift new file mode 100644 index 0000000..95bc503 --- /dev/null +++ b/Vogue Vista/Views/LoginView.swift @@ -0,0 +1,142 @@ + +import SwiftUI + +struct LoginView: View { + @State private var email: String = "" + @State private var password: String = "" + @State private var showingAlert = false + @State private var alertTitle: String = "" + @State private var alertMessage: String = "" + @State private var navigateToHome = false + @State private var showingSignup = false + + var body: some View { + VStack { + Spacer() + + Image(systemName: "person.fill") + .resizable() + .aspectRatio(contentMode: .fill) + .frame(width: 120, height: 120) + .clipped() + .padding(.bottom, 50) + .foregroundColor(AppColor.appPrimary) + + HStack { + Image(systemName: "envelope") + .foregroundColor(.gray) + TextField("Email", text: $email) + .autocapitalization(.none) + } + .padding() + .background(Color(.systemGray6)) + .cornerRadius(10) + .padding(.horizontal) + + + HStack { + Image(systemName: "lock") + .foregroundColor(.gray) + SecureField("Password", text: $password) + } + .padding() + .background(Color(.systemGray6)) + .cornerRadius(10) + .padding(.horizontal) + .padding(.top, 10) + + + Button("Log In") { + loginButtonTapped() + } + .padding() + .frame(maxWidth: .infinity) + .background(AppColor.appPrimary) + .foregroundColor(.white) + .cornerRadius(10) + .padding(.horizontal) + .padding(.top, 20) + + Spacer() + + + HStack { + Text("Don't have an account?") + .foregroundColor(.gray) + + Button("Sign Up") { + self.showingSignup = true + } + .foregroundColor(AppColor.appPrimary) + } + .padding(.bottom) + .sheet(isPresented: $showingSignup) { + SignUpView() + } + Text("By signing in, you agree to our Terms of Service and Privacy Policy.") + .font(.footnote) + .foregroundColor(.gray) + .multilineTextAlignment(.center) + .padding(.horizontal) + + Spacer() + } + .alert(isPresented: $showingAlert) { + Alert(title: Text(alertTitle), message: Text(alertMessage), dismissButton: .default(Text("OK")) { + if self.alertTitle == "Login Success" { + self.navigateToHome = true + } + }) + } + .fullScreenCover(isPresented: $navigateToHome) { + HomeView() + } + } + + private func loginButtonTapped() { + guard validateInputs() else { + showingAlert = true + return + } + + let usersModel = UsersModel() + usersModel.loginUser(email: email, password: password) { loginResponse, error in + DispatchQueue.main.async { + if let error = error { + self.alertTitle = "Login Error" + self.alertMessage = "Failed to log in: \(error.localizedDescription)" + self.showingAlert = true + } else if loginResponse != nil { + + UserDefaults.standard.set(loginResponse?.userId, forKey: "userId") + UserDefaults.standard.set(loginResponse?.token, forKey: "userToken") + + self.alertTitle = "Login Success" + self.alertMessage = "You're now logged in!" + self.showingAlert = true + + } else { + self.alertTitle = "Login Error" + self.alertMessage = "An unknown error occurred." + self.showingAlert = true + } + } + } + } + + private func validateInputs() -> Bool { + if email.isEmpty || password.isEmpty { + alertTitle = "Input Error" + alertMessage = "Please enter your email and password." + return false + } + + if !email.isValidEmail { + alertTitle = "Invalid Email" + alertMessage = "Please enter a valid email address." + return false + } + + return true + } +} diff --git a/Vogue Vista/Views/ProductDetailView.swift b/Vogue Vista/Views/ProductDetailView.swift new file mode 100644 index 0000000..3f61095 --- /dev/null +++ b/Vogue Vista/Views/ProductDetailView.swift @@ -0,0 +1,152 @@ + +import SwiftUI + +struct ProductDetailView: View { + var products: [Product] + @State private var selectedColor: String + @State private var selectedSize: String + @State private var selectedProduct: Product? + @State private var quantity: Int = 1 + @State private var showingAddToCartAlert = false + @State private var addToCartMessage = "" + @State private var navigateToCart = false + + private let cartManager = CartManager() + + private var colors: [String] { + Set(products.map { $0.color }).sorted() + } + + private var sizes: [String] { + Set(products.map { $0.size }).sorted() + } + + init(products: [Product]) { + self.products = products + _selectedColor = State(initialValue: products.first?.color ?? "") + _selectedSize = State(initialValue: products.first?.size ?? "") + _selectedProduct = State(initialValue: products.first) + + } + + var body: some View { + ScrollView { + VStack(alignment: .leading, spacing: 20) { + AsyncImage(url: URL(string: selectedProduct?.imageUrl ?? "")) { image in + image.resizable() + } placeholder: { + ProgressView() + } + .aspectRatio(contentMode: .fit) + + Text(selectedProduct?.name ?? "") + .font(.title) + .bold() + + Text(selectedProduct?.description ?? "") + .font(.body) + + Text("Color") + .font(.headline) + .bold() + .multilineTextAlignment(.center) + + Picker("Color", selection: $selectedColor) { + ForEach(colors, id: \.self) { color in + Text(color).tag(color) + } + } + .pickerStyle(.segmented) + .onChange(of: selectedColor) { newValue in + selectProduct() + } + + Text("Size") + .font(.headline) + .bold() + .multilineTextAlignment(.center) + + Picker("Size", selection: $selectedSize) { + ForEach(sizes, id: \.self) { size in + Text(size).tag(size) + } + } + .pickerStyle(.segmented) + + Text("Quantity") + .font(.headline) + .bold() + .multilineTextAlignment(.center) + + Picker("Quantity", selection: $quantity) { + ForEach(1..<10) { count in + Text("\(count)").tag(count) + } + } + .pickerStyle(.segmented) + + .onChange(of: selectedSize) { newValue in + selectProduct() + } + + + Spacer() + HStack { + if let price = selectedProduct?.price { + Text("Price: \(price)") + .font(.headline) + .bold() + } + + } + + Button("Add to Cart") { + if let userId = UserDefaults.standard.object(forKey: "userId") as? Int { + guard let product = selectedProduct, + let price = Double(product.price) else { return } + addToCart(userId: userId, productId: product.id, color: selectedColor, size: selectedSize, quantity: quantity, price: price, name: product.name, imageUrl: product.imageUrl) + } else { + self.addToCartMessage = "Please login to add items to cart." + self.showingAddToCartAlert = true + } + } + .frame(minWidth: 0, maxWidth: .infinity) + .padding() + .foregroundColor(.white) + .background(AppColor.appPrimary) + .cornerRadius(10) + + + .alert(isPresented: $showingAddToCartAlert) { + Alert(title: Text("Cart Update"), message: Text(addToCartMessage), dismissButton: .default(Text("OK"))) + } + .padding() + + + } + .padding() + NavigationLink(destination: ShoppingCartView(), isActive: $navigateToCart) { EmptyView() } + } + .navigationBarTitle(Text(selectedProduct?.name ?? ""), displayMode: .inline) + .navigationBarItems(trailing: Button(action: { + self.navigateToCart = true + }) { + Image(systemName: "cart.fill") + .foregroundStyle(AppColor.appPrimary) + }) + } + + private func selectProduct() { + selectedProduct = products.first { $0.color == selectedColor && $0.size == selectedSize } + } + + private func addToCart(userId: Int, productId: Int, color: String, size: String, quantity: Int, price: Double, name: String, imageUrl: String) { + cartManager.addToCart(userId: userId, productId: productId, color: color, size: size, quantity: quantity, price: price, name: name, imageUrl: imageUrl) { success, message in + DispatchQueue.main.async { + self.addToCartMessage = message + self.showingAddToCartAlert = true + } + } + } + +} diff --git a/Vogue Vista/Views/ProfileView.swift b/Vogue Vista/Views/ProfileView.swift new file mode 100644 index 0000000..4bbafee --- /dev/null +++ b/Vogue Vista/Views/ProfileView.swift @@ -0,0 +1,109 @@ +import SwiftUI + +struct ProfileView: View { + @ObservedObject var profileModel = ProfileModel() + @State private var password: String = "" + @State private var showingAlert = false + @State private var navigateToLogin = false + @State private var showingProfileAlert = false + + + var body: some View { + VStack { + Text("Profile") + .font(.largeTitle) + .bold() + .padding(.top, 15) + + + Image(systemName: "person.crop.circle.fill") + .resizable() + .scaledToFit() + .frame(width: 90, height: 90) + .padding() + .foregroundColor(AppColor.appPrimary) + } + .onAppear { + guard let userId = UserDefaults.standard.object(forKey: "userId") as? Int else { + print("User ID not found") + return + } + profileModel.fetchProfileData(userId: userId) + } + Form { + Section(header: Text("Profile Information").font(.headline)) { + HStack { + Image(systemName: "person.fill") + .foregroundColor(AppColor.appPrimary) + TextField("Name", text: Binding(get: { self.profileModel.profile?.name ?? "" }, set: { self.profileModel.profile?.name = $0 })) + .padding(10) + } + + HStack { + Image(systemName: "envelope.fill") + .foregroundColor(AppColor.appPrimary) + TextField("Email", text: Binding(get: { self.profileModel.profile?.email ?? "" }, set: { self.profileModel.profile?.email = $0 })) + .padding(10) + } + + HStack { + Image(systemName: "house.fill") + .foregroundColor(AppColor.appPrimary) + TextField("Address", text: Binding(get: { self.profileModel.profile?.address ?? "" }, set: { self.profileModel.profile?.address = $0 })) + .padding(10) + } + } + + HStack { + Spacer() + Button("Update Profile") { + self.profileModel.updateProfileData() + self.showingProfileAlert = true + } + .buttonStyle(EqualWidthButtonStyle()) + + Spacer() + + Button("Sign Out") { + showingAlert = true + } + .buttonStyle(EqualWidthButtonStyle()) + + Spacer() + } + .alert(isPresented: $showingProfileAlert) { + Alert(title: Text("Profile Details Updated"), message: Text("Profile Updated Successfully!"), dismissButton: .default(Text("OK"))) + } + }.background(Color.white) + .alert(isPresented: $showingAlert) { + Alert( + title: Text("Log Out"), + message: Text("Are you sure you want to log out?"), + primaryButton: .destructive(Text("Yes")) { + UserDefaults.standard.removeObject(forKey: "userToken") + UserDefaults.standard.removeObject(forKey: "userId") + navigateToLogin = true + }, + secondaryButton: .cancel() + ) + } + .fullScreenCover(isPresented: $navigateToLogin) { + SwiftUIWrapperView() + } + .background(Color.white) + .edgesIgnoringSafeArea(.all) + } +} + + +struct EqualWidthButtonStyle: ButtonStyle { + func makeBody(configuration: Self.Configuration) -> some View { + configuration.label + .padding() + .frame(minWidth: 0, maxWidth: .infinity) + .background(AppColor.appPrimary) + .foregroundColor(.white) + .clipShape(RoundedRectangle(cornerRadius: 10)) + .scaleEffect(configuration.isPressed ? 0.95 : 1) + } +} diff --git a/Vogue Vista/Views/ShoppingCartView.swift b/Vogue Vista/Views/ShoppingCartView.swift new file mode 100644 index 0000000..b2e8564 --- /dev/null +++ b/Vogue Vista/Views/ShoppingCartView.swift @@ -0,0 +1,82 @@ + +import SwiftUI + +struct ShoppingCartView: View { + @ObservedObject var viewModel = ShoppingCartViewModel() + @State private var showingCheckout = false + + var body: some View { + NavigationView { + VStack { + List(viewModel.items) { item in + HStack { + AsyncImage(url: URL(string: item.imageUrl)) { phase in + switch phase { + case .empty: + ProgressView() + case .success(let image): + image.resizable() + case .failure: + Image(systemName: "photo") + @unknown default: + EmptyView() + } + } + .frame(width: 70, height: 70) + .cornerRadius(5) + + VStack(alignment: .leading) { + Text(item.name) + .font(.headline) + Text("\(item.quantity) x $\(item.price) - \(item.color), \(item.size)") + .font(.subheadline) + } + + Spacer() + Text("$\(String(format: "%.2f", Double(item.price)! * Double(item.quantity)))") + .bold() + .foregroundStyle(AppColor.appPrimary) + + Button(action: { + viewModel.removeItemFromCart(itemId: item.id) + }) { + Image(systemName: "trash") + .foregroundColor(.red) + } + + } + + } + + HStack { + Text("Subtotal: ") + .font(.headline) + Spacer() + + Text("$\(viewModel.totalPrice)") + .font(.headline) + .bold() + + } + .padding() + + Button("Checkout") { + showingCheckout = true + } + .frame(minWidth: 0, maxWidth: .infinity) + .padding() + .foregroundColor(.white) + .background(AppColor.appPrimary) + .cornerRadius(10) + .padding() + } + .navigationBarTitle("Shopping Cart", displayMode: .inline) + .sheet(isPresented: $showingCheckout) { + CheckoutView(checkoutViewModel: CheckoutViewModel(totalAmount: Double(viewModel.totalPrice) ?? 0.0)) + } + } + .onAppear { + viewModel.fetchCart() + } + } +} diff --git a/Vogue Vista/Views/SignUpView.swift b/Vogue Vista/Views/SignUpView.swift new file mode 100644 index 0000000..f19013d --- /dev/null +++ b/Vogue Vista/Views/SignUpView.swift @@ -0,0 +1,142 @@ + + +import SwiftUI + +struct SignUpView: View { + @State private var email: String = "" + @State private var password: String = "" + @State private var name: String = "" + @State private var showingAlert = false + @State private var alertTitle: String = "" + @State private var alertMessage: String = "" + @State private var showingLogin = false + + private let usersModel = UsersModel() + + var body: some View { + VStack { + Spacer() + + Image(systemName: "person.crop.circle.badge.plus") + .resizable() + .aspectRatio(contentMode: .fill) + .frame(width: 120, height: 120) + .padding(.bottom, 50) + .foregroundColor(AppColor.appPrimary) + + + HStack { + Image(systemName: "person") + .foregroundColor(.gray) + TextField("Name", text: $name) + .autocapitalization(.none) + } + .padding() + .background(Color(.systemGray6)) + .cornerRadius(10) + .padding(.horizontal) + + HStack { + Image(systemName: "envelope") + .foregroundColor(.gray) + TextField("Email", text: $email) + .keyboardType(.emailAddress) + .autocapitalization(.none) + } + .padding() + .background(Color(.systemGray6)) + .cornerRadius(10) + .padding(.horizontal) + .padding(.top, 10) + + + HStack { + Image(systemName: "lock") + .foregroundColor(.gray) + SecureField("Password", text: $password) + } + .padding() + .background(Color(.systemGray6)) + .cornerRadius(10) + .padding(.horizontal) + .padding(.top, 10) + + Button("Sign Up") { + signUpButtonTapped() + } + .padding() + .frame(maxWidth: .infinity) + .background(AppColor.appPrimary) + .foregroundColor(.white) + .cornerRadius(10) + .padding(.horizontal) + .padding(.top, 20) + + Spacer() + + HStack { + Text("Already have an account?") + .foregroundColor(.gray) + + Button("Log In") { + self.showingLogin = true + } + .foregroundColor(AppColor.appPrimary) + } + .padding(.bottom) + .sheet(isPresented: $showingLogin) { + LoginView() + } + Spacer() + } + .alert(isPresented: $showingAlert) { + Alert(title: Text(alertTitle), message: Text(alertMessage), dismissButton: .default(Text("OK"))) + } + } + + private func signUpButtonTapped() { + guard validateInputs() else { + self.showingAlert = true + return + } + + usersModel.registerUser(email: email, password: password, name: name) { success, error in + DispatchQueue.main.async { + if success { + self.alertTitle = "Success" + self.alertMessage = "You have successfully registered." + self.showingAlert = true + } else if let error = error { + self.alertTitle = "Registration Error" + self.alertMessage = error.localizedDescription + self.showingAlert = true + } + } + } + } + + private func validateInputs() -> Bool { + if name.isEmpty || email.isEmpty || password.isEmpty { + self.alertTitle = "Missing Information" + self.alertMessage = "Please fill out all fields." + return false + } + + if !email.isValidEmail { + self.alertTitle = "Invalid Email" + self.alertMessage = "Please enter a valid email address." + return false + } + + if password.count < 6 { + self.alertTitle = "Password Too Short" + self.alertMessage = "Password must be at least 6 characters." + return false + } + + return true + } + +} + + diff --git a/Vogue Vista/Vogue_Vista.entitlements b/Vogue Vista/Vogue_Vista.entitlements new file mode 100644 index 0000000..f2ef3ae --- /dev/null +++ b/Vogue Vista/Vogue_Vista.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.files.user-selected.read-only + + + diff --git a/Vogue Vista/Vogue_VistaApp.swift b/Vogue Vista/Vogue_VistaApp.swift new file mode 100644 index 0000000..14af7ed --- /dev/null +++ b/Vogue Vista/Vogue_VistaApp.swift @@ -0,0 +1,15 @@ +import SwiftUI + +@main +struct Vogue_VistaApp: App { + var body: some Scene { + WindowGroup { + if UserDefaults.standard.string(forKey: "userToken") != nil { + HomeView() + } else { + SwiftUIWrapperView() + } + } + } +} + diff --git a/Vogue-Vista-Info.plist b/Vogue-Vista-Info.plist new file mode 100644 index 0000000..c33428a --- /dev/null +++ b/Vogue-Vista-Info.plist @@ -0,0 +1,10 @@ + + + + + CFBundleURLSchemes + https://dev-6x7bcpok.us.auth0.com/macos/com.thiranjaya.Vogue-Vista/callback + Scene configuration + + +