From 2e8399f3b78244fafc7b159c874849ab7ae65dd7 Mon Sep 17 00:00:00 2001 From: jaimeyfrank Date: Thu, 17 Oct 2024 04:37:48 -0400 Subject: [PATCH 1/8] implemented google oauth, redesigned sign in page --- .idea/caches/deviceStreaming.xml | 329 +++++++++ .idea/misc.xml | 1 - .idea/modules.xml | 8 + .idea/se-bu-passport-arts.iml | 9 + bu_passport/android/app/build.gradle | 8 +- bu_passport/android/build.gradle | 5 + bu_passport/android/google-services.json | 55 ++ .../gradle/wrapper/gradle-wrapper.properties | 2 +- bu_passport/firebase.json | 1 + bu_passport/ios/Flutter/Flutter.podspec | 18 - bu_passport/ios/Flutter/Generated.xcconfig | 9 +- .../ios/Flutter/flutter_export_environment.sh | 9 +- .../ios/Runner/GeneratedPluginRegistrant.m | 7 + bu_passport/ios/Runner/Info.plist | 11 + bu_passport/lib/pages/login_page.dart | 91 +-- bu_passport/lib/pages/signup_page.dart | 2 +- .../Flutter/GeneratedPluginRegistrant.swift | 2 + .../macos/GoogleService-Info (1).plist | 38 + bu_passport/package-lock.json | 693 ++++++++++++++++++ bu_passport/package.json | 5 + bu_passport/pubspec.lock | 80 +- bu_passport/pubspec.yaml | 1 + 22 files changed, 1269 insertions(+), 115 deletions(-) create mode 100644 .idea/caches/deviceStreaming.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/se-bu-passport-arts.iml create mode 100644 bu_passport/android/google-services.json create mode 100644 bu_passport/firebase.json delete mode 100644 bu_passport/ios/Flutter/Flutter.podspec create mode 100644 bu_passport/macos/GoogleService-Info (1).plist create mode 100644 bu_passport/package-lock.json create mode 100644 bu_passport/package.json diff --git a/.idea/caches/deviceStreaming.xml b/.idea/caches/deviceStreaming.xml new file mode 100644 index 0000000..821dc38 --- /dev/null +++ b/.idea/caches/deviceStreaming.xml @@ -0,0 +1,329 @@ + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index ade1ecd..69b2821 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,4 +1,3 @@ - diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..8d138cd --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/se-bu-passport-arts.iml b/.idea/se-bu-passport-arts.iml new file mode 100644 index 0000000..d6ebd48 --- /dev/null +++ b/.idea/se-bu-passport-arts.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/bu_passport/android/app/build.gradle b/bu_passport/android/app/build.gradle index ef3cfb2..5dd9ce6 100644 --- a/bu_passport/android/app/build.gradle +++ b/bu_passport/android/app/build.gradle @@ -2,6 +2,7 @@ plugins { id "com.android.application" id "kotlin-android" id "dev.flutter.flutter-gradle-plugin" + id 'com.google.gms.google-services' } def localProperties = new Properties() @@ -45,12 +46,14 @@ android { applicationId "com.example.bu_passport" // You can update the following values to match your application needs. // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration. - minSdkVersion flutter.minSdkVersion targetSdkVersion flutter.targetSdkVersion versionCode flutterVersionCode.toInteger() versionName flutterVersionName multiDexEnabled true - minSdkVersion 20 + minSdkVersion 23 + manifestPlaceholders += [ + 'appAuthRedirectScheme': 'com.example.bu_passport' + ] } buildTypes { @@ -68,4 +71,5 @@ flutter { dependencies { implementation 'androidx.multidex:multidex:2.0.1' + implementation platform('com.google.firebase:firebase-bom:33.4.0') } diff --git a/bu_passport/android/build.gradle b/bu_passport/android/build.gradle index bc157bd..008c4f7 100644 --- a/bu_passport/android/build.gradle +++ b/bu_passport/android/build.gradle @@ -1,3 +1,8 @@ +plugins { + // Add the dependency for the Google services Gradle plugin + id 'com.google.gms.google-services' version '4.4.2' apply false +} + allprojects { repositories { google() diff --git a/bu_passport/android/google-services.json b/bu_passport/android/google-services.json new file mode 100644 index 0000000..893ae4e --- /dev/null +++ b/bu_passport/android/google-services.json @@ -0,0 +1,55 @@ +{ + "project_info": { + "project_number": "429645705382", + "firebase_url": "https://se-bu-passport-default-rtdb.firebaseio.com", + "project_id": "se-bu-passport", + "storage_bucket": "se-bu-passport.appspot.com" + }, + "client": [ + { + "client_info": { + "mobilesdk_app_id": "1:429645705382:android:9f0f8af5edb9341147c467", + "android_client_info": { + "package_name": "com.example.bu_passport" + } + }, + "oauth_client": [ + { + "client_id": "429645705382-eaea9sqfmj8st3mn8o2t9jgnbacih67m.apps.googleusercontent.com", + "client_type": 1, + "android_info": { + "package_name": "com.example.bu_passport", + "certificate_hash": "d20f358562edf29f6d34f9d5ab57697734bc4157" + } + }, + { + "client_id": "429645705382-qdv5dcrft8ifi50gs2d1e7psu7e22nfq.apps.googleusercontent.com", + "client_type": 3 + } + ], + "api_key": [ + { + "current_key": "AIzaSyCoIG5QDtwfePSWyYQWOBZQgMLy3RPetc4" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [ + { + "client_id": "429645705382-qdv5dcrft8ifi50gs2d1e7psu7e22nfq.apps.googleusercontent.com", + "client_type": 3 + }, + { + "client_id": "429645705382-1j4106od8lrq0ceoetqln2c928v18850.apps.googleusercontent.com", + "client_type": 2, + "ios_info": { + "bundle_id": "com.example.buPassport.RunnerTests" + } + } + ] + } + } + } + ], + "configuration_version": "1" +} \ No newline at end of file diff --git a/bu_passport/android/gradle/wrapper/gradle-wrapper.properties b/bu_passport/android/gradle/wrapper/gradle-wrapper.properties index e1ca574..6cb8454 100644 --- a/bu_passport/android/gradle/wrapper/gradle-wrapper.properties +++ b/bu_passport/android/gradle/wrapper/gradle-wrapper.properties @@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.3-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-all.zip diff --git a/bu_passport/firebase.json b/bu_passport/firebase.json new file mode 100644 index 0000000..0bf5976 --- /dev/null +++ b/bu_passport/firebase.json @@ -0,0 +1 @@ +{"flutter":{"platforms":{"android":{"default":{"projectId":"crypto-monolith-417321","appId":"1:368645643837:android:9762661e16a81671965454","fileOutput":"android/app/google-services.json"}},"ios":{"default":{"projectId":"crypto-monolith-417321","appId":"1:368645643837:ios:259eeb31aac82760965454","uploadDebugSymbols":false,"fileOutput":"ios/Runner/GoogleService-Info.plist"}},"macos":{"default":{"projectId":"crypto-monolith-417321","appId":"1:368645643837:ios:259eeb31aac82760965454","uploadDebugSymbols":false,"fileOutput":"macos/Runner/GoogleService-Info.plist"}},"dart":{"lib/firebase_options.dart":{"projectId":"crypto-monolith-417321","configurations":{"android":"1:368645643837:android:9762661e16a81671965454","ios":"1:368645643837:ios:259eeb31aac82760965454","macos":"1:368645643837:ios:259eeb31aac82760965454","web":"1:368645643837:web:0c7de7191eb84a8a965454","windows":"1:368645643837:web:c8ea8678917e5ca3965454"}}}}}} \ No newline at end of file diff --git a/bu_passport/ios/Flutter/Flutter.podspec b/bu_passport/ios/Flutter/Flutter.podspec deleted file mode 100644 index 98e1633..0000000 --- a/bu_passport/ios/Flutter/Flutter.podspec +++ /dev/null @@ -1,18 +0,0 @@ -# -# This podspec is NOT to be published. It is only used as a local source! -# This is a generated file; do not edit or check into version control. -# - -Pod::Spec.new do |s| - s.name = 'Flutter' - s.version = '1.0.0' - s.summary = 'A UI toolkit for beautiful and fast apps.' - s.homepage = 'https://flutter.dev' - s.license = { :type => 'BSD' } - s.author = { 'Flutter Dev Team' => 'flutter-dev@googlegroups.com' } - s.source = { :git => 'https://github.com/flutter/engine', :tag => s.version.to_s } - s.ios.deployment_target = '12.0' - # Framework linking is handled by Flutter tooling, not CocoaPods. - # Add a placeholder to satisfy `s.dependency 'Flutter'` plugin podspecs. - s.vendored_frameworks = 'path/to/nothing' -end diff --git a/bu_passport/ios/Flutter/Generated.xcconfig b/bu_passport/ios/Flutter/Generated.xcconfig index 9309e6d..fd56f3d 100644 --- a/bu_passport/ios/Flutter/Generated.xcconfig +++ b/bu_passport/ios/Flutter/Generated.xcconfig @@ -1,15 +1,14 @@ // This is a generated file; do not edit or check into version control. -FLUTTER_ROOT=/Users/saisriram/development/flutter -FLUTTER_APPLICATION_PATH=/Users/saisriram/Desktop/BUPassport/se-bu-passport-arts/bu_passport +FLUTTER_ROOT=C:\Users\franj\flutter\flutter_windows_3.24.3-stable\flutter +FLUTTER_APPLICATION_PATH=C:\Users\franj\se-bu-passport-arts\bu_passport COCOAPODS_PARALLEL_CODE_SIGN=true -FLUTTER_TARGET=/Users/saisriram/Desktop/BUPassport/se-bu-passport-arts/bu_passport/lib/main.dart +FLUTTER_TARGET=lib\main.dart FLUTTER_BUILD_DIR=build FLUTTER_BUILD_NAME=1.0.0 FLUTTER_BUILD_NUMBER=1 EXCLUDED_ARCHS[sdk=iphonesimulator*]=i386 arm64 EXCLUDED_ARCHS[sdk=iphoneos*]=armv7 -DART_DEFINES=RkxVVFRFUl9XRUJfQVVUT19ERVRFQ1Q9dHJ1ZQ==,RkxVVFRFUl9XRUJfQ0FOVkFTS0lUX1VSTD1odHRwczovL3d3dy5nc3RhdGljLmNvbS9mbHV0dGVyLWNhbnZhc2tpdC8wNDgxN2M5OWM5ZmQ0OTU2ZjI3NTA1MjA0ZjdlMzQ0MzM1ODEwYWVkLw== DART_OBFUSCATION=false TRACK_WIDGET_CREATION=true TREE_SHAKE_ICONS=false -PACKAGE_CONFIG=/Users/saisriram/Desktop/BUPassport/se-bu-passport-arts/bu_passport/.dart_tool/package_config.json +PACKAGE_CONFIG=.dart_tool/package_config.json diff --git a/bu_passport/ios/Flutter/flutter_export_environment.sh b/bu_passport/ios/Flutter/flutter_export_environment.sh index b1b041f..3b69bc7 100755 --- a/bu_passport/ios/Flutter/flutter_export_environment.sh +++ b/bu_passport/ios/Flutter/flutter_export_environment.sh @@ -1,14 +1,13 @@ #!/bin/sh # This is a generated file; do not edit or check into version control. -export "FLUTTER_ROOT=/Users/saisriram/development/flutter" -export "FLUTTER_APPLICATION_PATH=/Users/saisriram/Desktop/BUPassport/se-bu-passport-arts/bu_passport" +export "FLUTTER_ROOT=C:\Users\franj\flutter\flutter_windows_3.24.3-stable\flutter" +export "FLUTTER_APPLICATION_PATH=C:\Users\franj\se-bu-passport-arts\bu_passport" export "COCOAPODS_PARALLEL_CODE_SIGN=true" -export "FLUTTER_TARGET=/Users/saisriram/Desktop/BUPassport/se-bu-passport-arts/bu_passport/lib/main.dart" +export "FLUTTER_TARGET=lib\main.dart" export "FLUTTER_BUILD_DIR=build" export "FLUTTER_BUILD_NAME=1.0.0" export "FLUTTER_BUILD_NUMBER=1" -export "DART_DEFINES=RkxVVFRFUl9XRUJfQVVUT19ERVRFQ1Q9dHJ1ZQ==,RkxVVFRFUl9XRUJfQ0FOVkFTS0lUX1VSTD1odHRwczovL3d3dy5nc3RhdGljLmNvbS9mbHV0dGVyLWNhbnZhc2tpdC8wNDgxN2M5OWM5ZmQ0OTU2ZjI3NTA1MjA0ZjdlMzQ0MzM1ODEwYWVkLw==" export "DART_OBFUSCATION=false" export "TRACK_WIDGET_CREATION=true" export "TREE_SHAKE_ICONS=false" -export "PACKAGE_CONFIG=/Users/saisriram/Desktop/BUPassport/se-bu-passport-arts/bu_passport/.dart_tool/package_config.json" +export "PACKAGE_CONFIG=.dart_tool/package_config.json" diff --git a/bu_passport/ios/Runner/GeneratedPluginRegistrant.m b/bu_passport/ios/Runner/GeneratedPluginRegistrant.m index 8eb66a7..2853a60 100644 --- a/bu_passport/ios/Runner/GeneratedPluginRegistrant.m +++ b/bu_passport/ios/Runner/GeneratedPluginRegistrant.m @@ -42,6 +42,12 @@ @import google_maps_flutter_ios; #endif +#if __has_include() +#import +#else +@import google_sign_in_ios; +#endif + #if __has_include() #import #else @@ -69,6 +75,7 @@ + (void)registerWithRegistry:(NSObject*)registry { [FLTFirebaseStoragePlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTFirebaseStoragePlugin"]]; [GeolocatorPlugin registerWithRegistrar:[registry registrarForPlugin:@"GeolocatorPlugin"]]; [FLTGoogleMapsPlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTGoogleMapsPlugin"]]; + [FLTGoogleSignInPlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTGoogleSignInPlugin"]]; [FLTImagePickerPlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTImagePickerPlugin"]]; [PermissionHandlerPlugin registerWithRegistrar:[registry registrarForPlugin:@"PermissionHandlerPlugin"]]; [URLLauncherPlugin registerWithRegistrar:[registry registrarForPlugin:@"URLLauncherPlugin"]]; diff --git a/bu_passport/ios/Runner/Info.plist b/bu_passport/ios/Runner/Info.plist index 0c64889..6e3e9a4 100644 --- a/bu_passport/ios/Runner/Info.plist +++ b/bu_passport/ios/Runner/Info.plist @@ -54,5 +54,16 @@ We need your location to check you into events near you. NSLocationAlwaysAndWhenInUseUsageDescription We need your location to provide notifications and information about events near you. + CFBundleURLTypes + + + CFBundleTypeRole + Editor + CFBundleURLSchemes + + com.example.bu_passport + + + diff --git a/bu_passport/lib/pages/login_page.dart b/bu_passport/lib/pages/login_page.dart index 3efd246..344837a 100644 --- a/bu_passport/lib/pages/login_page.dart +++ b/bu_passport/lib/pages/login_page.dart @@ -1,18 +1,41 @@ import 'package:bu_passport/pages/signup_page.dart'; import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutter/material.dart'; +import 'package:google_sign_in/google_sign_in.dart'; class LoginPage extends StatefulWidget { const LoginPage({Key? key}) : super(key: key); + @override State createState() => _LoginPageState(); } class _LoginPageState extends State { - TextEditingController _emailController = TextEditingController(); - TextEditingController _passwordController = TextEditingController(); String? _errorMessage; + Future _signInWithGoogle() async { + try { + final GoogleSignInAccount? googleUser = await GoogleSignIn().signIn(); + if (googleUser == null) { + return null; // The user canceled the sign-in + } + + final GoogleSignInAuthentication googleAuth = await googleUser.authentication; + final AuthCredential credential = GoogleAuthProvider.credential( + accessToken: googleAuth.accessToken, + idToken: googleAuth.idToken, + ); + + final UserCredential userCredential = await FirebaseAuth.instance.signInWithCredential(credential); + return userCredential.user; + } catch (e) { + setState(() { + _errorMessage = 'Google Sign-In failed. Please try again.'; + }); + return null; + } + } + @override Widget build(BuildContext context) { double screenWidth = MediaQuery.of(context).size.width; @@ -32,20 +55,6 @@ class _LoginPageState extends State { "assets/images/onboarding/BU art logo.png", fit: BoxFit.contain, ), - TextField( - controller: _emailController, - decoration: const InputDecoration( - labelText: 'Email', - ), - ), - SizedBox(height: sizedBoxHeight), - TextField( - controller: _passwordController, - decoration: const InputDecoration( - labelText: 'Password', - ), - obscureText: true, - ), if (_errorMessage != null) Text( _errorMessage!, @@ -54,60 +63,18 @@ class _LoginPageState extends State { SizedBox(height: sizedBoxHeight), ElevatedButton( onPressed: () async { - // Reset error message - setState(() { - _errorMessage = null; - }); - String email = _emailController.text; - String password = _passwordController.text; - try { - await FirebaseAuth.instance.signInWithEmailAndPassword( - email: email, - password: password, - ); - // Navigate to home page + User? user = await _signInWithGoogle(); + if (user != null) { Navigator.pushNamed(context, '/home'); - } catch (e) { - // Handle login errors - print('Login error: $e'); - setState(() { - _errorMessage = - 'Login failed. Please check your email and password.'; - }); } }, - child: const Text('Sign In'), + child: const Text('Sign In with Google'), ), SizedBox(height: sizedBoxHeight), - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Text('Don\'t have an account?'), - TextButton( - onPressed: () { - // Navigate to sign up page - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => SignUpPage(), - ), - ); - }, - child: const Text('Sign Up'), - ), - ], - ) ], ), ), ), ); } - - @override - void dispose() { - _emailController.dispose(); - _passwordController.dispose(); - super.dispose(); - } -} +} \ No newline at end of file diff --git a/bu_passport/lib/pages/signup_page.dart b/bu_passport/lib/pages/signup_page.dart index 3cbc289..45a966c 100644 --- a/bu_passport/lib/pages/signup_page.dart +++ b/bu_passport/lib/pages/signup_page.dart @@ -108,7 +108,7 @@ class _SignUpPageState extends State { String lastName = _lastNameController.text; String buID = _buIDController.text; String school = _userSchool.text; - String year_text = _userYear.toString().trim(); + String year_text = _userYear.text.trim(); // Check if any of the fields are empty. if (email.isEmpty || diff --git a/bu_passport/macos/Flutter/GeneratedPluginRegistrant.swift b/bu_passport/macos/Flutter/GeneratedPluginRegistrant.swift index dbd9bdd..5c91395 100644 --- a/bu_passport/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/bu_passport/macos/Flutter/GeneratedPluginRegistrant.swift @@ -11,6 +11,7 @@ import firebase_auth import firebase_core import firebase_storage import geolocator_apple +import google_sign_in_ios import url_launcher_macos func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { @@ -20,5 +21,6 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { FLTFirebaseCorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCorePlugin")) FLTFirebaseStoragePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseStoragePlugin")) GeolocatorPlugin.register(with: registry.registrar(forPlugin: "GeolocatorPlugin")) + FLTGoogleSignInPlugin.register(with: registry.registrar(forPlugin: "FLTGoogleSignInPlugin")) UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) } diff --git a/bu_passport/macos/GoogleService-Info (1).plist b/bu_passport/macos/GoogleService-Info (1).plist new file mode 100644 index 0000000..0ea15da --- /dev/null +++ b/bu_passport/macos/GoogleService-Info (1).plist @@ -0,0 +1,38 @@ + + + + + CLIENT_ID + 429645705382-1j4106od8lrq0ceoetqln2c928v18850.apps.googleusercontent.com + REVERSED_CLIENT_ID + com.googleusercontent.apps.429645705382-1j4106od8lrq0ceoetqln2c928v18850 + ANDROID_CLIENT_ID + 429645705382-eaea9sqfmj8st3mn8o2t9jgnbacih67m.apps.googleusercontent.com + API_KEY + AIzaSyDB-0rxfn8BysrqjdSsWddOiJseJlvvIyo + GCM_SENDER_ID + 429645705382 + PLIST_VERSION + 1 + BUNDLE_ID + com.example.buPassport.RunnerTests + PROJECT_ID + se-bu-passport + STORAGE_BUCKET + se-bu-passport.appspot.com + IS_ADS_ENABLED + + IS_ANALYTICS_ENABLED + + IS_APPINVITE_ENABLED + + IS_GCM_ENABLED + + IS_SIGNIN_ENABLED + + GOOGLE_APP_ID + 1:429645705382:ios:cd4b6c01aab761b947c467 + DATABASE_URL + https://se-bu-passport-default-rtdb.firebaseio.com + + \ No newline at end of file diff --git a/bu_passport/package-lock.json b/bu_passport/package-lock.json new file mode 100644 index 0000000..f0fb6bc --- /dev/null +++ b/bu_passport/package-lock.json @@ -0,0 +1,693 @@ +{ + "name": "bu_passport", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "dependencies": { + "@clerk/nextjs": "^5.7.3" + } + }, + "node_modules/@clerk/backend": { + "version": "1.13.10", + "resolved": "https://registry.npmjs.org/@clerk/backend/-/backend-1.13.10.tgz", + "integrity": "sha512-1pastJJ+LpEPxKynseJVsZhOwn4bO8Mg+ekCeoDbQC4b4jxSM4kOGriYAXy8xe4q2K+pufe/pTPeKxQT/jkxtg==", + "license": "MIT", + "dependencies": { + "@clerk/shared": "2.9.1", + "@clerk/types": "4.25.1", + "cookie": "0.7.0", + "snakecase-keys": "5.4.4", + "tslib": "2.4.1" + }, + "engines": { + "node": ">=18.17.0" + } + }, + "node_modules/@clerk/clerk-react": { + "version": "5.11.1", + "resolved": "https://registry.npmjs.org/@clerk/clerk-react/-/clerk-react-5.11.1.tgz", + "integrity": "sha512-m7x681y6MasiXsfNaTbo5c8JQKtWlZMbDRKf75Cmoo7naDbccWBZNPY1OYbK0uxpnxY24zi/2xfKWTufW8q5RA==", + "license": "MIT", + "dependencies": { + "@clerk/shared": "2.9.1", + "@clerk/types": "4.25.1", + "tslib": "2.4.1" + }, + "engines": { + "node": ">=18.17.0" + }, + "peerDependencies": { + "react": ">=18 || >=19.0.0-beta", + "react-dom": ">=18 || >=19.0.0-beta" + } + }, + "node_modules/@clerk/nextjs": { + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/@clerk/nextjs/-/nextjs-5.7.3.tgz", + "integrity": "sha512-9HwiK43waReG4q9ADbG0k3IzFwV3+pSB8i1p3fieM6wf5iOpbrWQAbNtStx0rRqZ/x9WJTp3xavqStmETgeUqQ==", + "license": "MIT", + "dependencies": { + "@clerk/backend": "1.13.10", + "@clerk/clerk-react": "5.11.1", + "@clerk/shared": "2.9.1", + "@clerk/types": "4.25.1", + "crypto-js": "4.2.0", + "server-only": "0.0.1", + "tslib": "2.4.1" + }, + "engines": { + "node": ">=18.17.0" + }, + "peerDependencies": { + "next": "^13.5.4 || ^14.0.3 || >=15.0.0-rc", + "react": ">=18 || >=19.0.0-beta", + "react-dom": ">=18 || >=19.0.0-beta" + } + }, + "node_modules/@clerk/shared": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/@clerk/shared/-/shared-2.9.1.tgz", + "integrity": "sha512-NOO6gLRKvEMmz/3pjDiG/rE7ZSmLbZFaU1yTyx94Wb33v0sAaF4v8uypbXSswgAsPWVKu+fgkWmmbhBJP5Us7A==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "@clerk/types": "4.25.1", + "glob-to-regexp": "0.4.1", + "js-cookie": "3.0.5", + "std-env": "^3.7.0", + "swr": "^2.2.0" + }, + "engines": { + "node": ">=18.17.0" + }, + "peerDependencies": { + "react": ">=18 || >=19.0.0-beta", + "react-dom": ">=18 || >=19.0.0-beta" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, + "node_modules/@clerk/types": { + "version": "4.25.1", + "resolved": "https://registry.npmjs.org/@clerk/types/-/types-4.25.1.tgz", + "integrity": "sha512-ILvR2YXz6BSGXDoozBAd2BGj8ZF/FQrfWQd0FtLz1JXt1vurkgRncAhEcC427OiXNRnq5R2Pn++urfFEa0PqYA==", + "license": "MIT", + "dependencies": { + "csstype": "3.1.1" + }, + "engines": { + "node": ">=18.17.0" + } + }, + "node_modules/@next/env": { + "version": "14.2.15", + "resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.15.tgz", + "integrity": "sha512-S1qaj25Wru2dUpcIZMjxeMVSwkt8BK4dmWHHiBuRstcIyOsMapqT4A4jSB6onvqeygkSSmOkyny9VVx8JIGamQ==", + "license": "MIT", + "peer": true + }, + "node_modules/@next/swc-darwin-arm64": { + "version": "14.2.15", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.15.tgz", + "integrity": "sha512-Rvh7KU9hOUBnZ9TJ28n2Oa7dD9cvDBKua9IKx7cfQQ0GoYUwg9ig31O2oMwH3wm+pE3IkAQ67ZobPfEgurPZIA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "peer": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-darwin-x64": { + "version": "14.2.15", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.15.tgz", + "integrity": "sha512-5TGyjFcf8ampZP3e+FyCax5zFVHi+Oe7sZyaKOngsqyaNEpOgkKB3sqmymkZfowy3ufGA/tUgDPPxpQx931lHg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "peer": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-gnu": { + "version": "14.2.15", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.15.tgz", + "integrity": "sha512-3Bwv4oc08ONiQ3FiOLKT72Q+ndEMyLNsc/D3qnLMbtUYTQAmkx9E/JRu0DBpHxNddBmNT5hxz1mYBphJ3mfrrw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-musl": { + "version": "14.2.15", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.15.tgz", + "integrity": "sha512-k5xf/tg1FBv/M4CMd8S+JL3uV9BnnRmoe7F+GWC3DxkTCD9aewFRH1s5rJ1zkzDa+Do4zyN8qD0N8c84Hu96FQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-gnu": { + "version": "14.2.15", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.15.tgz", + "integrity": "sha512-kE6q38hbrRbKEkkVn62reLXhThLRh6/TvgSP56GkFNhU22TbIrQDEMrO7j0IcQHcew2wfykq8lZyHFabz0oBrA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-musl": { + "version": "14.2.15", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.15.tgz", + "integrity": "sha512-PZ5YE9ouy/IdO7QVJeIcyLn/Rc4ml9M2G4y3kCM9MNf1YKvFY4heg3pVa/jQbMro+tP6yc4G2o9LjAz1zxD7tQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-arm64-msvc": { + "version": "14.2.15", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.15.tgz", + "integrity": "sha512-2raR16703kBvYEQD9HNLyb0/394yfqzmIeyp2nDzcPV4yPjqNUG3ohX6jX00WryXz6s1FXpVhsCo3i+g4RUX+g==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "peer": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-ia32-msvc": { + "version": "14.2.15", + "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.15.tgz", + "integrity": "sha512-fyTE8cklgkyR1p03kJa5zXEaZ9El+kDNM5A+66+8evQS5e/6v0Gk28LqA0Jet8gKSOyP+OTm/tJHzMlGdQerdQ==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "peer": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-x64-msvc": { + "version": "14.2.15", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.15.tgz", + "integrity": "sha512-SzqGbsLsP9OwKNUG9nekShTwhj6JSB9ZLMWQ8g1gG6hdE5gQLncbnbymrwy2yVmH9nikSLYRYxYMFu78Ggp7/g==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "peer": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@swc/counter": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", + "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==", + "license": "Apache-2.0", + "peer": true + }, + "node_modules/@swc/helpers": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.5.tgz", + "integrity": "sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@swc/counter": "^0.1.3", + "tslib": "^2.4.0" + } + }, + "node_modules/busboy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", + "peer": true, + "dependencies": { + "streamsearch": "^1.1.0" + }, + "engines": { + "node": ">=10.16.0" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001669", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001669.tgz", + "integrity": "sha512-DlWzFDJqstqtIVx1zeSpIMLjunf5SmwOw0N2Ck/QSQdS8PLS4+9HrLaYei4w8BIAL7IB/UEDu889d8vhCTPA0w==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0", + "peer": true + }, + "node_modules/client-only": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", + "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", + "license": "MIT" + }, + "node_modules/cookie": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.0.tgz", + "integrity": "sha512-qCf+V4dtlNhSRXGAZatc1TasyFO6GjohcOul807YOb5ik3+kQSnb4d7iajeCL8QHaJ4uZEjCgiCJerKXwdRVlQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/crypto-js": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz", + "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==", + "license": "MIT" + }, + "node_modules/csstype": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.1.tgz", + "integrity": "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==", + "license": "MIT" + }, + "node_modules/dot-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", + "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", + "license": "MIT", + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "license": "BSD-2-Clause" + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC", + "peer": true + }, + "node_modules/js-cookie": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz", + "integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==", + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT", + "peer": true + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "license": "MIT", + "peer": true, + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.3" + } + }, + "node_modules/map-obj": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz", + "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/nanoid": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "peer": true, + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/next": { + "version": "14.2.15", + "resolved": "https://registry.npmjs.org/next/-/next-14.2.15.tgz", + "integrity": "sha512-h9ctmOokpoDphRvMGnwOJAedT6zKhwqyZML9mDtspgf4Rh3Pn7UTYKqePNoDvhsWBAO5GoPNYshnAUGIazVGmw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@next/env": "14.2.15", + "@swc/helpers": "0.5.5", + "busboy": "1.6.0", + "caniuse-lite": "^1.0.30001579", + "graceful-fs": "^4.2.11", + "postcss": "8.4.31", + "styled-jsx": "5.1.1" + }, + "bin": { + "next": "dist/bin/next" + }, + "engines": { + "node": ">=18.17.0" + }, + "optionalDependencies": { + "@next/swc-darwin-arm64": "14.2.15", + "@next/swc-darwin-x64": "14.2.15", + "@next/swc-linux-arm64-gnu": "14.2.15", + "@next/swc-linux-arm64-musl": "14.2.15", + "@next/swc-linux-x64-gnu": "14.2.15", + "@next/swc-linux-x64-musl": "14.2.15", + "@next/swc-win32-arm64-msvc": "14.2.15", + "@next/swc-win32-ia32-msvc": "14.2.15", + "@next/swc-win32-x64-msvc": "14.2.15" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.1.0", + "@playwright/test": "^1.41.2", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "sass": "^1.3.0" + }, + "peerDependenciesMeta": { + "@opentelemetry/api": { + "optional": true + }, + "@playwright/test": { + "optional": true + }, + "sass": { + "optional": true + } + } + }, + "node_modules/no-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", + "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", + "license": "MIT", + "dependencies": { + "lower-case": "^2.0.2", + "tslib": "^2.0.3" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC", + "peer": true + }, + "node_modules/postcss": { + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "nanoid": "^3.3.6", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/react": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "license": "MIT", + "peer": true, + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.2" + }, + "peerDependencies": { + "react": "^18.3.1" + } + }, + "node_modules/scheduler": { + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "loose-envify": "^1.1.0" + } + }, + "node_modules/server-only": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/server-only/-/server-only-0.0.1.tgz", + "integrity": "sha512-qepMx2JxAa5jjfzxG79yPPq+8BuFToHd1hm7kI+Z4zAq1ftQiP7HcxMhDDItrbtwVeLg/cY2JnKnrcFkmiswNA==", + "license": "MIT" + }, + "node_modules/snake-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz", + "integrity": "sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==", + "license": "MIT", + "dependencies": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/snakecase-keys": { + "version": "5.4.4", + "resolved": "https://registry.npmjs.org/snakecase-keys/-/snakecase-keys-5.4.4.tgz", + "integrity": "sha512-YTywJG93yxwHLgrYLZjlC75moVEX04LZM4FHfihjHe1FCXm+QaLOFfSf535aXOAd0ArVQMWUAe8ZPm4VtWyXaA==", + "license": "MIT", + "dependencies": { + "map-obj": "^4.1.0", + "snake-case": "^3.0.4", + "type-fest": "^2.5.2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/std-env": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.7.0.tgz", + "integrity": "sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==", + "license": "MIT" + }, + "node_modules/streamsearch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", + "peer": true, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/styled-jsx": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.1.tgz", + "integrity": "sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==", + "license": "MIT", + "peer": true, + "dependencies": { + "client-only": "0.0.1" + }, + "engines": { + "node": ">= 12.0.0" + }, + "peerDependencies": { + "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/swr": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/swr/-/swr-2.2.5.tgz", + "integrity": "sha512-QtxqyclFeAsxEUeZIYmsaQ0UjimSq1RZ9Un7I68/0ClKK/U3LoyQunwkQfJZr2fc22DfIXLNDc2wFyTEikCUpg==", + "license": "MIT", + "dependencies": { + "client-only": "^0.0.1", + "use-sync-external-store": "^1.2.0" + }, + "peerDependencies": { + "react": "^16.11.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/tslib": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz", + "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==", + "license": "0BSD" + }, + "node_modules/type-fest": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/use-sync-external-store": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.2.tgz", + "integrity": "sha512-PElTlVMwpblvbNqQ82d2n6RjStvdSoNe9FG28kNfz3WiXilJm4DdNkEzRhCZuIDwY8U08WVihhGR5iRqAwfDiw==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + } + } +} diff --git a/bu_passport/package.json b/bu_passport/package.json new file mode 100644 index 0000000..5f56a3d --- /dev/null +++ b/bu_passport/package.json @@ -0,0 +1,5 @@ +{ + "dependencies": { + "@clerk/nextjs": "^5.7.3" + } +} diff --git a/bu_passport/pubspec.lock b/bu_passport/pubspec.lock index 957d053..0cb447b 100644 --- a/bu_passport/pubspec.lock +++ b/bu_passport/pubspec.lock @@ -544,6 +544,46 @@ packages: url: "https://pub.dev" source: hosted version: "0.5.6+2" + google_sign_in: + dependency: "direct main" + description: + name: google_sign_in + sha256: "821f354c053d51a2d417b02d42532a19a6ea8057d2f9ebb8863c07d81c98aaf9" + url: "https://pub.dev" + source: hosted + version: "5.4.4" + google_sign_in_android: + dependency: transitive + description: + name: google_sign_in_android + sha256: "0928059d2f0840f63c7b07a30cf73b593ae872cdd0dbd46d1b9ba878d2599c01" + url: "https://pub.dev" + source: hosted + version: "6.1.33" + google_sign_in_ios: + dependency: transitive + description: + name: google_sign_in_ios + sha256: "83f015169102df1ab2905cf8abd8934e28f87db9ace7a5fa676998842fed228a" + url: "https://pub.dev" + source: hosted + version: "5.7.8" + google_sign_in_platform_interface: + dependency: transitive + description: + name: google_sign_in_platform_interface + sha256: "1f6e5787d7a120cc0359ddf315c92309069171306242e181c09472d1b00a2971" + url: "https://pub.dev" + source: hosted + version: "2.4.5" + google_sign_in_web: + dependency: transitive + description: + name: google_sign_in_web + sha256: "75cc41ebc53b1756320ee14d9c3018ad3e6cea298147dbcd86e9d0c8d6720b40" + url: "https://pub.dev" + source: hosted + version: "0.10.2+1" graphs: dependency: transitive description: @@ -692,26 +732,26 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa" + sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05" url: "https://pub.dev" source: hosted - version: "10.0.0" + version: "10.0.5" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0 + sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "3.0.5" leak_tracker_testing: dependency: transitive description: name: leak_tracker_testing - sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47 + sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "3.0.1" lints: dependency: transitive description: @@ -748,18 +788,18 @@ packages: dependency: transitive description: name: material_color_utilities - sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" + sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec url: "https://pub.dev" source: hosted - version: "0.8.0" + version: "0.11.1" meta: dependency: transitive description: name: meta - sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 + sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "1.15.0" mime: dependency: transitive description: @@ -1065,26 +1105,26 @@ packages: dependency: "direct dev" description: name: test - sha256: a1f7595805820fcc05e5c52e3a231aedd0b72972cb333e8c738a8b1239448b6f + sha256: "7ee44229615f8f642b68120165ae4c2a75fe77ae2065b1e55ae4711f6cf0899e" url: "https://pub.dev" source: hosted - version: "1.24.9" + version: "1.25.7" test_api: dependency: transitive description: name: test_api - sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" + sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb" url: "https://pub.dev" source: hosted - version: "0.6.1" + version: "0.7.2" test_core: dependency: transitive description: name: test_core - sha256: a757b14fc47507060a162cc2530d9a4a2f92f5100a952c7443b5cad5ef5b106a + sha256: "55ea5a652e38a1dfb32943a7973f3681a60f872f8c3a05a14664ad54ef9c6696" url: "https://pub.dev" source: hosted - version: "0.5.9" + version: "0.6.4" timezone: dependency: "direct main" description: @@ -1201,10 +1241,10 @@ packages: dependency: transitive description: name: vm_service - sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957 + sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d" url: "https://pub.dev" source: hosted - version: "13.0.0" + version: "14.2.5" watcher: dependency: transitive description: @@ -1246,5 +1286,5 @@ packages: source: hosted version: "3.1.2" sdks: - dart: ">=3.3.0 <4.0.0" - flutter: ">=3.19.0" + dart: ">=3.5.0 <4.0.0" + flutter: ">=3.24.0" diff --git a/bu_passport/pubspec.yaml b/bu_passport/pubspec.yaml index 74f1a16..6d9d140 100644 --- a/bu_passport/pubspec.yaml +++ b/bu_passport/pubspec.yaml @@ -48,6 +48,7 @@ dependencies: timezone: ^0.8.0 permission_handler: ^11.3.1 url_launcher: ^6.2.5 + google_sign_in: ^5.2.1 dev_dependencies: flutter_test: From f31fb3571113dad2adf6ce7624d5b49fe38734fc Mon Sep 17 00:00:00 2001 From: Jaimey Frank <104035227+jaimeyfrank@users.noreply.github.com> Date: Mon, 21 Oct 2024 04:44:29 -0400 Subject: [PATCH 2/8] Delete .idea/caches directory --- .idea/caches/deviceStreaming.xml | 329 ------------------------------- 1 file changed, 329 deletions(-) delete mode 100644 .idea/caches/deviceStreaming.xml diff --git a/.idea/caches/deviceStreaming.xml b/.idea/caches/deviceStreaming.xml deleted file mode 100644 index 821dc38..0000000 --- a/.idea/caches/deviceStreaming.xml +++ /dev/null @@ -1,329 +0,0 @@ - - - - - - \ No newline at end of file From 340f7f9b57ed65b4135d0861d4dbf56224df92e4 Mon Sep 17 00:00:00 2001 From: jaimeyfrank Date: Mon, 21 Oct 2024 04:52:57 -0400 Subject: [PATCH 3/8] fixed login page styles --- .idea/caches/deviceStreaming.xml | 329 -------------------------- .idea/misc.xml | 1 + .idea/modules.xml | 8 - .idea/se-bu-passport-arts.iml | 9 - bu_passport/lib/pages/login_page.dart | 28 ++- bu_passport/pubspec.yaml | 2 + 6 files changed, 24 insertions(+), 353 deletions(-) delete mode 100644 .idea/caches/deviceStreaming.xml delete mode 100644 .idea/modules.xml delete mode 100644 .idea/se-bu-passport-arts.iml diff --git a/.idea/caches/deviceStreaming.xml b/.idea/caches/deviceStreaming.xml deleted file mode 100644 index 821dc38..0000000 --- a/.idea/caches/deviceStreaming.xml +++ /dev/null @@ -1,329 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index 69b2821..ade1ecd 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,3 +1,4 @@ + diff --git a/.idea/modules.xml b/.idea/modules.xml deleted file mode 100644 index 8d138cd..0000000 --- a/.idea/modules.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/se-bu-passport-arts.iml b/.idea/se-bu-passport-arts.iml deleted file mode 100644 index d6ebd48..0000000 --- a/.idea/se-bu-passport-arts.iml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/bu_passport/lib/pages/login_page.dart b/bu_passport/lib/pages/login_page.dart index 344837a..9d51539 100644 --- a/bu_passport/lib/pages/login_page.dart +++ b/bu_passport/lib/pages/login_page.dart @@ -38,9 +38,6 @@ class _LoginPageState extends State { @override Widget build(BuildContext context) { - double screenWidth = MediaQuery.of(context).size.width; - double screenHeight = MediaQuery.of(context).size.height; - double sizedBoxHeight = (MediaQuery.of(context).size.height * 0.05); double edgeInsets = (MediaQuery.of(context).size.width * 0.02); @@ -58,17 +55,33 @@ class _LoginPageState extends State { if (_errorMessage != null) Text( _errorMessage!, - style: TextStyle(color: Colors.red), + style: const TextStyle(color: Color(0xFFCC0000)), ), SizedBox(height: sizedBoxHeight), - ElevatedButton( - onPressed: () async { + GestureDetector( + onTap: () async { User? user = await _signInWithGoogle(); if (user != null) { Navigator.pushNamed(context, '/home'); } }, - child: const Text('Sign In with Google'), + child: Container( + width: 244.14, + height: 43.89, + decoration: BoxDecoration( + color: const Color(0xFFCC0000), + borderRadius: BorderRadius.circular(66.75), + ), + child: const Center( + child: Text( + 'Sign In with Google', + style: TextStyle( + color: Colors.white, + fontSize: 16, // Adjust the font size if needed + ), + ), + ), + ), ), SizedBox(height: sizedBoxHeight), ], @@ -76,5 +89,6 @@ class _LoginPageState extends State { ), ), ); + } } \ No newline at end of file diff --git a/bu_passport/pubspec.yaml b/bu_passport/pubspec.yaml index 6d9d140..38cc869 100644 --- a/bu_passport/pubspec.yaml +++ b/bu_passport/pubspec.yaml @@ -30,6 +30,8 @@ environment: dependencies: flutter: sdk: flutter + assets: + - bu_passport\assets\images\onboarding\BU art logo.png # The following adds the Cupertino Icons font to your application. From 6e35932c1b32b3acc5395db027d6a014abfa4f13 Mon Sep 17 00:00:00 2001 From: jaimeyfrank Date: Mon, 21 Oct 2024 06:45:00 -0400 Subject: [PATCH 4/8] working on firebase auth to firestore doc. issues with collecting buid, classyear, etc. --- bu_passport/lib/pages/login_page.dart | 71 ++++++- bu_passport/lib/pages/signup_page.dart | 265 +++++++++---------------- bu_passport/pubspec.yaml | 2 - 3 files changed, 156 insertions(+), 182 deletions(-) diff --git a/bu_passport/lib/pages/login_page.dart b/bu_passport/lib/pages/login_page.dart index 9d51539..043f7d8 100644 --- a/bu_passport/lib/pages/login_page.dart +++ b/bu_passport/lib/pages/login_page.dart @@ -2,6 +2,7 @@ import 'package:bu_passport/pages/signup_page.dart'; import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutter/material.dart'; import 'package:google_sign_in/google_sign_in.dart'; +import 'package:cloud_firestore/cloud_firestore.dart'; class LoginPage extends StatefulWidget { const LoginPage({Key? key}) : super(key: key); @@ -27,11 +28,70 @@ class _LoginPageState extends State { ); final UserCredential userCredential = await FirebaseAuth.instance.signInWithCredential(credential); - return userCredential.user; + final User? user = userCredential.user; + + if (user != null) { + // Check if the user document exists in Firestore + final userDoc = FirebaseFirestore.instance.collection('users').doc(user.uid); + final docSnapshot = await userDoc.get(); + if (docSnapshot.exists) { + print("Firestore doc data: ${docSnapshot.data()}"); + } + if (!docSnapshot.exists) { + // Create a new user document + final name = user.displayName?.split(' ') ?? []; + await userDoc.set({ + 'userUID': user.uid, + 'userEmail': user.email, + 'firstName': name.isNotEmpty ? name[0] : '', + 'lastName': name.length > 1 ? name[1] : '', + 'userProfileURL': user.photoURL, + 'userPoints': 0, + 'userSavedEvents': {}, + // Additional fields to be filled by the user + 'userBUID': '', + 'userSchool': '', + 'userYear': 0, + }); + // Navigate to SignUpPage to collect additional information + if (mounted) { + print("Navigating to SignUpPage"); + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => const SignUpPage(), + ), + ); + } + } else { + // Check if additional fields are set + final data = docSnapshot.data(); + if (data != null && (data['userBUID'] == '' || data['userSchool'] == '' || data['userYear'] == 0)) { + if (mounted) { + print("Navigating to SignUpPage"); + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => const SignUpPage(), + ), + ); + } + } else { + // Navigate to home page if user document is complete + if (mounted) { + Navigator.pushNamed(context, '/home'); + } + } + } + } + + return user; } catch (e) { - setState(() { - _errorMessage = 'Google Sign-In failed. Please try again.'; - }); + if (mounted) { + setState(() { + _errorMessage = 'Google Sign-In failed. Please try again. Error: $e'; + }); + } return null; } } @@ -77,7 +137,7 @@ class _LoginPageState extends State { 'Sign In with Google', style: TextStyle( color: Colors.white, - fontSize: 16, // Adjust the font size if needed + fontSize: 16, ), ), ), @@ -89,6 +149,5 @@ class _LoginPageState extends State { ), ), ); - } } \ No newline at end of file diff --git a/bu_passport/lib/pages/signup_page.dart b/bu_passport/lib/pages/signup_page.dart index 45a966c..2b4d8d2 100644 --- a/bu_passport/lib/pages/signup_page.dart +++ b/bu_passport/lib/pages/signup_page.dart @@ -1,194 +1,111 @@ -import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/widgets.dart'; +import 'package:cloud_firestore/cloud_firestore.dart'; -// SignUpPage allows new users to create an account. class SignUpPage extends StatefulWidget { const SignUpPage({Key? key}) : super(key: key); + @override State createState() => _SignUpPageState(); } -// State class for SignUpPage handling user sign-up logic. class _SignUpPageState extends State { - // Text controllers to manage the input fields for user registration. - TextEditingController _emailController = TextEditingController(); - TextEditingController _passwordController = TextEditingController(); - TextEditingController _firstNameController = TextEditingController(); - TextEditingController _lastNameController = TextEditingController(); - TextEditingController _buIDController = TextEditingController(); - TextEditingController _userSchool = TextEditingController(); - TextEditingController _userYear = TextEditingController(); + final _formKey = GlobalKey(); + final _firstNameController = TextEditingController(); + final _lastNameController = TextEditingController(); + final _userBUIDController = TextEditingController(); + final _userSchoolController = TextEditingController(); + final _userYearController = TextEditingController(); - final db = FirebaseFirestore.instance; // Firestore instance for data storage. + Future _saveUserInfo() async { + if (_formKey.currentState!.validate()) { + final user = FirebaseAuth.instance.currentUser; + if (user != null) { + final userDoc = FirebaseFirestore.instance.collection('users').doc(user.uid); + await userDoc.update({ + 'firstName': _firstNameController.text, + 'lastName': _lastNameController.text, + 'userBUID': _userBUIDController.text, + 'userSchool': _userSchoolController.text, + 'userYear': int.parse(_userYearController.text), + }); + Navigator.pushNamed(context, '/home'); + } + } + } @override Widget build(BuildContext context) { - double screenWidth = MediaQuery.of(context).size.width; - double screenHeight = MediaQuery.of(context).size.height; - - // Variables for consistent spacing and padding in the UI. - double sizedBoxHeight = screenHeight * 0.02; - double edgeInsets = screenWidth * 0.02; - return Scaffold( - appBar: AppBar(), // Simple AppBar for the layout. - body: Center( - child: SingleChildScrollView( - child: Padding( - padding: EdgeInsets.all(edgeInsets), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - SizedBox(height: sizedBoxHeight), - // Input field for first name. - TextField( - controller: _firstNameController, - decoration: const InputDecoration( - labelText: 'First Name', - ), - ), - SizedBox(height: sizedBoxHeight), - // Input field for last name. - TextField( - controller: _lastNameController, - decoration: const InputDecoration( - labelText: 'Last Name', - ), - ), - SizedBox(height: sizedBoxHeight), - // Input field for email. - TextField( - controller: _emailController, - decoration: const InputDecoration( - labelText: 'Email', - ), - ), - SizedBox(height: sizedBoxHeight), - // Input field for university ID. - TextField( - controller: _buIDController, - decoration: const InputDecoration( - labelText: 'BU ID', - ), - ), - SizedBox(height: sizedBoxHeight), - // Input field for the school within the university. - TextField( - controller: _userSchool, - decoration: const InputDecoration( - labelText: 'School', - ), - ), - SizedBox(height: sizedBoxHeight), - // Input field for the year in school. - TextField( - controller: _userYear, - decoration: const InputDecoration( - labelText: 'Year', - ), - ), - SizedBox(height: sizedBoxHeight), - // Input field for password, obscured for security. - TextField( - controller: _passwordController, - decoration: const InputDecoration( - labelText: 'Password', - ), - obscureText: true, - ), - SizedBox(height: sizedBoxHeight), - // Button to submit the sign-up form. - ElevatedButton( - onPressed: () async { - String email = _emailController.text; - String password = _passwordController.text; - String firstName = _firstNameController.text; - String lastName = _lastNameController.text; - String buID = _buIDController.text; - String school = _userSchool.text; - String year_text = _userYear.text.trim(); - - // Check if any of the fields are empty. - if (email.isEmpty || - password.isEmpty || - firstName.isEmpty || - lastName.isEmpty || - buID.isEmpty || - school.isEmpty || - year_text.isEmpty) { - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar( - content: Text('Please fill out all fields.')), - ); - return; - } - int year = int.parse(year_text); - - try { - // Firebase Auth call to create a new user with email and password. - final userCredential = await FirebaseAuth.instance - .createUserWithEmailAndPassword( - email: email, - password: password, - ); - - // Preparing user data for Firestore. - final user = { - 'firstName': firstName, - 'lastName': lastName, - 'userEmail': userCredential.user!.email, - 'userUID': userCredential.user!.uid, - 'userSchool': school, - 'userYear': year, - 'userBUID': buID, - 'userPoints': 0, - 'userProfileURL': '', - 'userSavedEvents': Map(), - }; - // Saving user data to Firestore. - await db - .collection('users') - .doc(userCredential.user!.uid) - .set(user); - Navigator.pushNamed(context, '/onboarding'); - } catch (e) { - if (e is FirebaseAuthException) { - if (e.code == 'weak-password') { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: - Text('The password provided is too weak.')), - ); - } else if (e.code == 'email-already-in-use') { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text( - 'The account already exists for that email.')), - ); - } else { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text( - 'Error: ${e.message ?? 'An unknown error occurred.'}')), - ); - } - } else { - // Handle other types of exceptions - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text('Error: $e')), - ); - } - } - }, - child: const Text('Sign Up'), - ), - ], - ), + appBar: AppBar( + title: Text('Complete Your Profile'), + ), + body: Padding( + padding: const EdgeInsets.all(16.0), + child: Form( + key: _formKey, + child: Column( + children: [ + TextFormField( + controller: _firstNameController, + decoration: InputDecoration(labelText: 'First Name'), + validator: (value) { + if (value == null || value.isEmpty) { + return 'Please enter your first name'; + } + return null; + }, + ), + TextFormField( + controller: _lastNameController, + decoration: InputDecoration(labelText: 'Last Name'), + validator: (value) { + if (value == null || value.isEmpty) { + return 'Please enter your last name'; + } + return null; + }, + ), + TextFormField( + controller: _userBUIDController, + decoration: InputDecoration(labelText: 'BUID'), + validator: (value) { + if (value == null || value.isEmpty) { + return 'Please enter your BUID'; + } + return null; + }, + ), + TextFormField( + controller: _userSchoolController, + decoration: InputDecoration(labelText: 'School'), + validator: (value) { + if (value == null || value.isEmpty) { + return 'Please enter your school'; + } + return null; + }, + ), + TextFormField( + controller: _userYearController, + decoration: InputDecoration(labelText: 'Year'), + keyboardType: TextInputType.number, + validator: (value) { + if (value == null || value.isEmpty) { + return 'Please enter your year'; + } + return null; + }, + ), + SizedBox(height: 20), + ElevatedButton( + onPressed: _saveUserInfo, + child: Text('Save'), + ), + ], ), ), ), ); } -} +} \ No newline at end of file diff --git a/bu_passport/pubspec.yaml b/bu_passport/pubspec.yaml index 38cc869..6d9d140 100644 --- a/bu_passport/pubspec.yaml +++ b/bu_passport/pubspec.yaml @@ -30,8 +30,6 @@ environment: dependencies: flutter: sdk: flutter - assets: - - bu_passport\assets\images\onboarding\BU art logo.png # The following adds the Cupertino Icons font to your application. From a1d7acddea04338dd6d7fec4eb086e295dc939c2 Mon Sep 17 00:00:00 2001 From: jaimeyfrank Date: Tue, 22 Oct 2024 20:51:02 -0400 Subject: [PATCH 5/8] finished login implementation --- bu_passport/lib/pages/login_page.dart | 62 +++++++++++++------------- bu_passport/lib/pages/signup_page.dart | 54 +++++++++++++++++++++- 2 files changed, 85 insertions(+), 31 deletions(-) diff --git a/bu_passport/lib/pages/login_page.dart b/bu_passport/lib/pages/login_page.dart index 043f7d8..f37fb22 100644 --- a/bu_passport/lib/pages/login_page.dart +++ b/bu_passport/lib/pages/login_page.dart @@ -13,6 +13,26 @@ class LoginPage extends StatefulWidget { class _LoginPageState extends State { String? _errorMessage; + static bool newUser = false; + + // Navigate to the signup page + void _navigateToSignUp() { + if (mounted) { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => const SignUpPage(), + ), + ); + } + } + + // Navigate to the home page + void _navigateToHome() { + if (mounted) { + Navigator.pushNamed(context, '/home'); + } + } Future _signInWithGoogle() async { try { @@ -34,9 +54,7 @@ class _LoginPageState extends State { // Check if the user document exists in Firestore final userDoc = FirebaseFirestore.instance.collection('users').doc(user.uid); final docSnapshot = await userDoc.get(); - if (docSnapshot.exists) { - print("Firestore doc data: ${docSnapshot.data()}"); - } + if (!docSnapshot.exists) { // Create a new user document final name = user.displayName?.split(' ') ?? []; @@ -52,35 +70,15 @@ class _LoginPageState extends State { 'userBUID': '', 'userSchool': '', 'userYear': 0, + 'admin': false, }); - // Navigate to SignUpPage to collect additional information - if (mounted) { - print("Navigating to SignUpPage"); - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => const SignUpPage(), - ), - ); - } + // Set newUser flag + newUser = true; } else { // Check if additional fields are set final data = docSnapshot.data(); if (data != null && (data['userBUID'] == '' || data['userSchool'] == '' || data['userYear'] == 0)) { - if (mounted) { - print("Navigating to SignUpPage"); - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => const SignUpPage(), - ), - ); - } - } else { - // Navigate to home page if user document is complete - if (mounted) { - Navigator.pushNamed(context, '/home'); - } + newUser = true; } } } @@ -121,9 +119,13 @@ class _LoginPageState extends State { GestureDetector( onTap: () async { User? user = await _signInWithGoogle(); - if (user != null) { - Navigator.pushNamed(context, '/home'); - } + if(user != null) { + if (newUser) { + _navigateToSignUp(); + } else { + _navigateToHome(); + } + }; }, child: Container( width: 244.14, diff --git a/bu_passport/lib/pages/signup_page.dart b/bu_passport/lib/pages/signup_page.dart index 2b4d8d2..2996230 100644 --- a/bu_passport/lib/pages/signup_page.dart +++ b/bu_passport/lib/pages/signup_page.dart @@ -38,7 +38,15 @@ class _SignUpPageState extends State { Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: Text('Complete Your Profile'), + title: const Text('Complete Your Profile', + style: TextStyle( + color: Colors.black, + fontSize: 17, + fontFamily: 'Inter', + fontWeight: FontWeight.w500, + height: 0, + ), + ), ), body: Padding( padding: const EdgeInsets.all(16.0), @@ -49,6 +57,13 @@ class _SignUpPageState extends State { TextFormField( controller: _firstNameController, decoration: InputDecoration(labelText: 'First Name'), + style: const TextStyle( + color: Colors.black, + fontSize: 14.93, + fontFamily: 'Inter', + fontWeight: FontWeight.w400, + height: 0, + ), validator: (value) { if (value == null || value.isEmpty) { return 'Please enter your first name'; @@ -59,6 +74,13 @@ class _SignUpPageState extends State { TextFormField( controller: _lastNameController, decoration: InputDecoration(labelText: 'Last Name'), + style: const TextStyle( + color: Colors.black, + fontSize: 14.93, + fontFamily: 'Inter', + fontWeight: FontWeight.w400, + height: 0, + ), validator: (value) { if (value == null || value.isEmpty) { return 'Please enter your last name'; @@ -69,6 +91,13 @@ class _SignUpPageState extends State { TextFormField( controller: _userBUIDController, decoration: InputDecoration(labelText: 'BUID'), + style: const TextStyle( + color: Colors.black, + fontSize: 14.93, + fontFamily: 'Inter', + fontWeight: FontWeight.w400, + height: 0, + ), validator: (value) { if (value == null || value.isEmpty) { return 'Please enter your BUID'; @@ -79,6 +108,13 @@ class _SignUpPageState extends State { TextFormField( controller: _userSchoolController, decoration: InputDecoration(labelText: 'School'), + style: const TextStyle( + color: Colors.black, + fontSize: 14.93, + fontFamily: 'Inter', + fontWeight: FontWeight.w400, + height: 0, + ), validator: (value) { if (value == null || value.isEmpty) { return 'Please enter your school'; @@ -89,6 +125,13 @@ class _SignUpPageState extends State { TextFormField( controller: _userYearController, decoration: InputDecoration(labelText: 'Year'), + style: const TextStyle( + color: Colors.black, + fontSize: 14.93, + fontFamily: 'Inter', + fontWeight: FontWeight.w400, + height: 0, + ), keyboardType: TextInputType.number, validator: (value) { if (value == null || value.isEmpty) { @@ -99,6 +142,15 @@ class _SignUpPageState extends State { ), SizedBox(height: 20), ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: const Color(0xFFCC0000), + textStyle: const TextStyle( + color: Color(0xFFFFFFFF), + fontSize: 22.32, + fontFamily: 'Inter', + fontWeight: FontWeight.w500, + ), + ), onPressed: _saveUserInfo, child: Text('Save'), ), From 3cae266bd346b706f397743bb33a25af8e4caa88 Mon Sep 17 00:00:00 2001 From: jaimeyfrank Date: Fri, 25 Oct 2024 19:32:30 -0400 Subject: [PATCH 6/8] tested with non bu email --- bu_passport/lib/classes/user.dart | 2 + bu_passport/lib/pages/login_page.dart | 52 ++++++++++++++----- .../lib/services/firebase_service.dart | 2 + 3 files changed, 43 insertions(+), 13 deletions(-) diff --git a/bu_passport/lib/classes/user.dart b/bu_passport/lib/classes/user.dart index 2953645..97fadc5 100644 --- a/bu_passport/lib/classes/user.dart +++ b/bu_passport/lib/classes/user.dart @@ -9,6 +9,7 @@ class Users { final int userPoints; final String userProfileURL; final Map userSavedEvents; + final bool admin; Users({ required this.firstName, @@ -21,5 +22,6 @@ class Users { required this.userSavedEvents, required this.userPoints, required this.userProfileURL, + required this.admin, }); } diff --git a/bu_passport/lib/pages/login_page.dart b/bu_passport/lib/pages/login_page.dart index f37fb22..9dd7941 100644 --- a/bu_passport/lib/pages/login_page.dart +++ b/bu_passport/lib/pages/login_page.dart @@ -14,6 +14,7 @@ class LoginPage extends StatefulWidget { class _LoginPageState extends State { String? _errorMessage; static bool newUser = false; + static bool BUemail = false; // Navigate to the signup page void _navigateToSignUp() { @@ -36,7 +37,10 @@ class _LoginPageState extends State { Future _signInWithGoogle() async { try { - final GoogleSignInAccount? googleUser = await GoogleSignIn().signIn(); + final GoogleSignIn googleSignIn = GoogleSignIn( + hostedDomain: 'bu.edu', + ); + final GoogleSignInAccount? googleUser = await googleSignIn.signIn(); if (googleUser == null) { return null; // The user canceled the sign-in } @@ -51,18 +55,27 @@ class _LoginPageState extends State { final User? user = userCredential.user; if (user != null) { + if (user.email != null && !user.email!.contains("bu.edu")) { + BUemail = false; + await user.delete(); + setState(() { + _errorMessage = 'Please sign in with a BU email address.'; + }); + return null; + } else { + BUemail = true; + } // Check if the user document exists in Firestore final userDoc = FirebaseFirestore.instance.collection('users').doc(user.uid); final docSnapshot = await userDoc.get(); if (!docSnapshot.exists) { // Create a new user document - final name = user.displayName?.split(' ') ?? []; await userDoc.set({ 'userUID': user.uid, 'userEmail': user.email, - 'firstName': name.isNotEmpty ? name[0] : '', - 'lastName': name.length > 1 ? name[1] : '', + 'firstName': userCredential.additionalUserInfo?.profile?['given_name'], + 'lastName': userCredential.additionalUserInfo?.profile?['family_name'], 'userProfileURL': user.photoURL, 'userPoints': 0, 'userSavedEvents': {}, @@ -77,15 +90,25 @@ class _LoginPageState extends State { } else { // Check if additional fields are set final data = docSnapshot.data(); + if (data != null && (data['userBUID'] == '' || data['userSchool'] == '' || data['userYear'] == 0)) { newUser = true; } } + } else { + setState(() { + _errorMessage = 'Sign in failed. Please sign in with a BU email address.'; + }); + return null; } return user; } catch (e) { - if (mounted) { + if (_errorMessage != null) { + setState(() { + _errorMessage = 'Please sign in with a BU email address. Error: $e'; + }); + } else if (mounted) { setState(() { _errorMessage = 'Google Sign-In failed. Please try again. Error: $e'; }); @@ -110,22 +133,19 @@ class _LoginPageState extends State { "assets/images/onboarding/BU art logo.png", fit: BoxFit.contain, ), - if (_errorMessage != null) - Text( - _errorMessage!, - style: const TextStyle(color: Color(0xFFCC0000)), - ), SizedBox(height: sizedBoxHeight), GestureDetector( onTap: () async { User? user = await _signInWithGoogle(); - if(user != null) { + if (user != null) { if (newUser) { + debugPrint("navigate to signup"); _navigateToSignUp(); } else { + debugPrint("navigate to home"); _navigateToHome(); } - }; + } }, child: Container( width: 244.14, @@ -136,7 +156,7 @@ class _LoginPageState extends State { ), child: const Center( child: Text( - 'Sign In with Google', + 'Sign In with BU Gmail', style: TextStyle( color: Colors.white, fontSize: 16, @@ -146,6 +166,12 @@ class _LoginPageState extends State { ), ), SizedBox(height: sizedBoxHeight), + if (_errorMessage != null || !BUemail) + Text( + 'Please sign in with a BU email address.', + style: const TextStyle(color: Color(0xFFCC0000)), + ), + SizedBox(height: sizedBoxHeight), ], ), ), diff --git a/bu_passport/lib/services/firebase_service.dart b/bu_passport/lib/services/firebase_service.dart index 2ff75cd..ee751f5 100644 --- a/bu_passport/lib/services/firebase_service.dart +++ b/bu_passport/lib/services/firebase_service.dart @@ -83,6 +83,7 @@ class FirebaseService { userPoints: userData['userPoints'], userSavedEvents: Map.from(userData['userSavedEvents'] ?? {}), + admin: userData['admin'], ); return user; } else { @@ -340,6 +341,7 @@ class FirebaseService { Map.from(userData['userSavedEvents'] ?? {}), userPoints: userData['userPoints'], userProfileURL: userData['userProfileURL'], + admin: userData['admin'], ); users.add(user); }); From ab5207b6833a7988c21bebb8d4d046972cdc6129 Mon Sep 17 00:00:00 2001 From: heyadi Date: Tue, 29 Oct 2024 17:30:05 -0400 Subject: [PATCH 7/8] Update Info.plist to enable google sign-in on ios --- bu_passport/ios/Runner/Info.plist | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/bu_passport/ios/Runner/Info.plist b/bu_passport/ios/Runner/Info.plist index 6e3e9a4..d0aace8 100644 --- a/bu_passport/ios/Runner/Info.plist +++ b/bu_passport/ios/Runner/Info.plist @@ -54,6 +54,8 @@ We need your location to check you into events near you. NSLocationAlwaysAndWhenInUseUsageDescription We need your location to provide notifications and information about events near you. + GIDClientID + 429645705382-neovegus18m295gn11dpo2g6mi85tv3j.apps.googleusercontent.com CFBundleURLTypes @@ -61,9 +63,9 @@ Editor CFBundleURLSchemes - com.example.bu_passport + com.googleusercontent.apps.429645705382-neovegus18m295gn11dpo2g6mi85tv3j - + From 660b0b897828bd3664fb08c58301623aeb8d8c17 Mon Sep 17 00:00:00 2001 From: jaimeyfrank Date: Wed, 30 Oct 2024 14:48:14 -0400 Subject: [PATCH 8/8] small update to initial state --- bu_passport/lib/pages/login_page.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bu_passport/lib/pages/login_page.dart b/bu_passport/lib/pages/login_page.dart index 9dd7941..2c8d80d 100644 --- a/bu_passport/lib/pages/login_page.dart +++ b/bu_passport/lib/pages/login_page.dart @@ -14,7 +14,7 @@ class LoginPage extends StatefulWidget { class _LoginPageState extends State { String? _errorMessage; static bool newUser = false; - static bool BUemail = false; + static bool BUemail = true; // Navigate to the signup page void _navigateToSignUp() {