From b5d77d2426da407dd3e61941fc39bc77cb136734 Mon Sep 17 00:00:00 2001 From: venbaittech Date: Sun, 28 Jul 2024 11:50:54 +0530 Subject: [PATCH] CHANGE_ --- .env | 8 +- .env.production | 6 +- .env.uat | 4 + android/app/build.gradle | 9 +- android/app/google-services.json | 29 ++ android/app/src/debug/google-services.json | 29 ++ android/app/src/main/AndroidManifest.xml | 2 +- android/build.gradle | 11 + android/settings.gradle | 1 + build_web_production.sh | 4 +- build_web_uat.sh | 39 +++ lib/customAppBar/enrollmentAppBar.dart | 82 +++-- lib/main.dart | 14 +- lib/models/environment.dart | 61 +--- lib/pages/enrollment/addons.dart | 108 ++++--- lib/pages/enrollment/empDetails.dart | 295 +++++++++-------- lib/pages/enrollment/empReview.dart | 298 +++++++++++------- lib/pages/firebase-config.dart | 23 ++ lib/pages/login.dart | 156 ++++++++- lib/pages/postEnrollment/policies.dart | 2 +- .../postEnrollment/service/api_service.dart | 46 +-- lib/pages/recaptcha_helper.dart | 10 + lib/pages/verify.dart | 226 ++++++++++--- macos/Flutter/GeneratedPluginRegistrant.swift | 8 + pubspec.yaml | 6 + web/index.html | 34 ++ .../flutter/generated_plugin_registrant.cc | 6 + windows/flutter/generated_plugins.cmake | 2 + 28 files changed, 1079 insertions(+), 440 deletions(-) create mode 100644 .env.uat create mode 100644 android/app/google-services.json create mode 100644 android/app/src/debug/google-services.json create mode 100755 build_web_uat.sh create mode 100644 lib/pages/firebase-config.dart create mode 100644 lib/pages/recaptcha_helper.dart diff --git a/.env b/.env index 70c6f6a..5ea1d81 100644 --- a/.env +++ b/.env @@ -1,4 +1,4 @@ -API_URL=https://venbait.in/nhance/dev/employeeRest/ -TICKET_API_URL=https://venbait.in/nhance/helpdesk/dev/api -BASE_HREF=/nhance/employee/dev/ -ENV=production \ No newline at end of file +API_URL=https://dev.venbait.in/nhance/uat/employeeRest/ +TICKET_API_URL=https://dev.venbait.in/nhance/helpdesk/uat/api +BASE_HREF=/nhance/employee/uat/ +ENV=uat \ No newline at end of file diff --git a/.env.production b/.env.production index 70c6f6a..9a9d739 100644 --- a/.env.production +++ b/.env.production @@ -1,4 +1,4 @@ -API_URL=https://venbait.in/nhance/dev/employeeRest/ -TICKET_API_URL=https://venbait.in/nhance/helpdesk/dev/api -BASE_HREF=/nhance/employee/dev/ +API_URL=https://dev.venbait.in/nhance/uat/employeeRest/ +TICKET_API_URL=https://dev.venbait.in/nhance/helpdesk/uat/api +BASE_HREF=/nhance/employee/uat/ ENV=production \ No newline at end of file diff --git a/.env.uat b/.env.uat new file mode 100644 index 0000000..5ea1d81 --- /dev/null +++ b/.env.uat @@ -0,0 +1,4 @@ +API_URL=https://dev.venbait.in/nhance/uat/employeeRest/ +TICKET_API_URL=https://dev.venbait.in/nhance/helpdesk/uat/api +BASE_HREF=/nhance/employee/uat/ +ENV=uat \ No newline at end of file diff --git a/android/app/build.gradle b/android/app/build.gradle index 282fcfb..9d88b72 100644 --- a/android/app/build.gradle +++ b/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,7 +46,7 @@ android { applicationId "com.example.nhance_app_pwa" // 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 + minSdkVersion 23 targetSdkVersion flutter.targetSdkVersion versionCode flutterVersionCode.toInteger() versionName flutterVersionName @@ -64,4 +65,8 @@ flutter { source '../..' } -dependencies {} +dependencies { + implementation(platform("com.google.firebase:firebase-bom:33.1.2")) + implementation 'com.google.firebase:firebase-messaging:22.0.0' + implementation("com.google.firebase:firebase-auth:23.0.0") +} diff --git a/android/app/google-services.json b/android/app/google-services.json new file mode 100644 index 0000000..7598588 --- /dev/null +++ b/android/app/google-services.json @@ -0,0 +1,29 @@ +{ + "project_info": { + "project_number": "203846206303", + "project_id": "push-notification-enrollment", + "storage_bucket": "push-notification-enrollment.appspot.com" + }, + "client": [ + { + "client_info": { + "mobilesdk_app_id": "1:203846206303:android:01f39a22f39a50539b6cea", + "android_client_info": { + "package_name": "com.example.nhance_app_pwa" + } + }, + "oauth_client": [], + "api_key": [ + { + "current_key": "AIzaSyAdacw5svpqopDCpoEym7aDkI3F2AajEO4" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [] + } + } + } + ], + "configuration_version": "1" +} \ No newline at end of file diff --git a/android/app/src/debug/google-services.json b/android/app/src/debug/google-services.json new file mode 100644 index 0000000..7598588 --- /dev/null +++ b/android/app/src/debug/google-services.json @@ -0,0 +1,29 @@ +{ + "project_info": { + "project_number": "203846206303", + "project_id": "push-notification-enrollment", + "storage_bucket": "push-notification-enrollment.appspot.com" + }, + "client": [ + { + "client_info": { + "mobilesdk_app_id": "1:203846206303:android:01f39a22f39a50539b6cea", + "android_client_info": { + "package_name": "com.example.nhance_app_pwa" + } + }, + "oauth_client": [], + "api_key": [ + { + "current_key": "AIzaSyAdacw5svpqopDCpoEym7aDkI3F2AajEO4" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [] + } + } + } + ], + "configuration_version": "1" +} \ No newline at end of file diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index a6bd867..c33638b 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,4 +1,4 @@ - + diff --git a/android/build.gradle b/android/build.gradle index bc157bd..e7360a9 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -1,3 +1,14 @@ +buildscript { + repositories { + google() + mavenCentral() + } + dependencies { + classpath 'com.android.tools.build:gradle:7.0.2' // Use the appropriate version for your project + classpath 'com.google.gms:google-services:4.4.2' // Correct version for google-services + } +} + allprojects { repositories { google() diff --git a/android/settings.gradle b/android/settings.gradle index 748cace..4aa42d6 100644 --- a/android/settings.gradle +++ b/android/settings.gradle @@ -21,6 +21,7 @@ plugins { id "dev.flutter.flutter-plugin-loader" version "1.0.0" id "com.android.application" version "7.3.0" apply false id "org.jetbrains.kotlin.android" version "1.8.22" apply false + id("com.google.gms.google-services") version "4.4.2" apply false } include ":app" diff --git a/build_web_production.sh b/build_web_production.sh index 2d06463..306147d 100755 --- a/build_web_production.sh +++ b/build_web_production.sh @@ -32,8 +32,8 @@ flutter build web --release --base-href "$BASE_HREF" # Check if the build was successful if [ $? -ne 0 ]; then - echo "Flutter prod build failed" + echo "Flutter PRODUCTION build failed" exit 1 fi -echo "Flutter prod build succeeded" +echo "Flutter PRODUCTION build succeeded" diff --git a/build_web_uat.sh b/build_web_uat.sh new file mode 100755 index 0000000..5cd7ad8 --- /dev/null +++ b/build_web_uat.sh @@ -0,0 +1,39 @@ +#!/bin/bash + +# Define the path to the .env.uat file +ENV_FILE_PATH=".env.uat" + +# Check if the .env.uat file exists +if [ ! -f $ENV_FILE_PATH ]; then + echo "$ENV_FILE_PATH does not exist" + exit 1 +fi + +# Copy the .env.uat file to .env +cp $ENV_FILE_PATH .env +echo "Copied $ENV_FILE_PATH to .env" + +# Source the .env file to load the environment variables +source .env + +# Check if the required variables are set +if [ -z "$BASE_HREF" ] || [ -z "$API_URL" ] || [ -z "$TICKET_API_URL" ]; then + echo "Environment variables not set correctly in .env" + exit 1 +fi + +# Print the loaded variables for verification +echo "Loaded BASE_HREF: $BASE_HREF" +echo "Loaded API_URL: $API_URL" +echo "Loaded TICKET_API_URL: $TICKET_API_URL" + +# Build the web app for UAT with base href +flutter build web --release --base-href "$BASE_HREF" + +# Check if the build was successful +if [ $? -ne 0 ]; then + echo "Flutter UAT build failed" + exit 1 +fi + +echo "Flutter UAT build succeeded" diff --git a/lib/customAppBar/enrollmentAppBar.dart b/lib/customAppBar/enrollmentAppBar.dart index 78b663f..5f78341 100644 --- a/lib/customAppBar/enrollmentAppBar.dart +++ b/lib/customAppBar/enrollmentAppBar.dart @@ -1,8 +1,12 @@ import 'package:flutter/material.dart'; import 'package:adaptive_navbar/adaptive_navbar.dart'; import 'package:nhance_app_pwa/customAppBar/responsive.dart'; +import 'package:nhance_app_pwa/customAppBar/toastHelper.dart'; import 'package:shared_preferences/shared_preferences.dart'; +import '../models/platform_helper_mobile.dart' + if (dart.library.html) '../models/platform_helper_other.dart'; + class CustomAppBar extends StatefulWidget implements PreferredSizeWidget { @override _CustomAppBarState createState() => _CustomAppBarState(); @@ -14,17 +18,46 @@ class CustomAppBar extends StatefulWidget implements PreferredSizeWidget { class _CustomAppBarState extends State { bool showBackToHR = true; bool hideInactiveStatus = true; + dynamic empStatus; @override void initState() { super.initState(); + getEmpStatus(); + } + + Future getEmpStatus() async { + final SharedPreferences prefs = await SharedPreferences.getInstance(); + empStatus = prefs.getString('emp_status'); } Future logout(BuildContext context) async { - final prefs = await SharedPreferences.getInstance(); - final String? token = prefs.getString('token'); + // final prefs = await SharedPreferences.getInstance(); + // final String? token = prefs.getString('token'); + // + // if (token != null && token.isNotEmpty) { + // await prefs.clear(); + // Navigator.pushNamed(context, 'login'); + // } - if (token != null && token.isNotEmpty) { + if (isMobilePlatform()) { + final SharedPreferences prefs = await SharedPreferences.getInstance(); + + // Get the value of the mobile_number key + String? mobileNumber = prefs.getString('empMobileNo'); + + // Clear all keys + await prefs.clear(); + + // Re-set the mobile_number key + if (mobileNumber != null) { + await prefs.setString('empMobileNo', mobileNumber); + } + ToastHelper.showSuccessToast(context, 'logout'); + // Navigate to login page + Navigator.pushNamed(context, 'login'); + } else { + final prefs = await SharedPreferences.getInstance(); await prefs.clear(); Navigator.pushNamed(context, 'login'); } @@ -66,31 +99,32 @@ class _CustomAppBarState extends State { ), ), // AdaptiveNavBar Column - Expanded( - flex: Responsive.isDesktop(context) ? 9 : 3, - child: AdaptiveNavBar( - screenWidth: sw, - backgroundColor: Color(0xFFFFFCE5), - leading: - Container(), // Set an empty container as we have the logo separately - title: Text(''), - navBarItems: [ - if (Responsive.isDesktop(context)) + if (!Responsive.isDesktop(context) && empStatus != 'enrolled') + Expanded( + flex: Responsive.isDesktop(context) ? 9 : 3, + child: AdaptiveNavBar( + screenWidth: sw, + backgroundColor: Color(0xFFFFFCE5), + leading: + Container(), // Set an empty container as we have the logo separately + title: Text(''), + navBarItems: [ + if (Responsive.isDesktop(context)) + NavBarItem( + text: "Inactive Policy", + onTap: () { + Navigator.pushNamed(context, 'oldPolicy'); + }, + ), NavBarItem( - text: "Inactive Policy", - onTap: () { - Navigator.pushNamed(context, 'oldPolicy'); + text: "Logout", + onTap: () async { + logout(context); }, ), - NavBarItem( - text: "Logout", - onTap: () async { - logout(context); - }, - ), - ], + ], + ), ), - ), ], ), ), diff --git a/lib/main.dart b/lib/main.dart index e326504..0b2acba 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,3 +1,4 @@ +import 'package:firebase_core/firebase_core.dart'; import 'package:flutter/material.dart'; import 'package:flutter_dotenv/flutter_dotenv.dart'; import 'package:nhance_app_pwa/pages/enrollment/addons.dart'; @@ -27,6 +28,12 @@ import 'models/environment.dart'; Future main() async { WidgetsFlutterBinding.ensureInitialized(); + await Firebase.initializeApp( + options: const FirebaseOptions( + apiKey: 'AIzaSyAdacw5svpqopDCpoEym7aDkI3F2AajEO4', + appId: '1:203846206303:android:01f39a22f39a50539b6cea', + messagingSenderId: '203846206303', + projectId: 'push-notification-enrollment')); await dotenv.load(fileName: Environment.fileName); final AuthService _authService = AuthService(); @@ -49,7 +56,12 @@ Future main() async { // ), routes: { 'login': (context) => login(), - 'verify': (context) => MyVerify(), + 'verify': (context) => MyVerify( + verificationId: '', + mobileNumber: '', + resendToken: null, + onResendCode: (String, int) {}, + ), 'pinSettingPage': (context) => pinSettingPage(), 'pinPage': (context) => pinPage(), 'changePin': (context) => changePin(), diff --git a/lib/models/environment.dart b/lib/models/environment.dart index b4479ad..e58c64e 100644 --- a/lib/models/environment.dart +++ b/lib/models/environment.dart @@ -1,21 +1,19 @@ -import 'package:flutter/cupertino.dart'; -import 'package:flutter/foundation.dart'; import 'package:flutter_dotenv/flutter_dotenv.dart'; -import '../pages/postEnrollment/home.dart'; - class Environment { static String get fileName { - const bool isProduction = - bool.fromEnvironment('dart.vm.product', defaultValue: false); - const bool isTest = bool.fromEnvironment('ENV', defaultValue: false); + const String env = + String.fromEnvironment('ENV', defaultValue: 'development'); - if (isProduction) { - return '.env.production'; - } else if (isTest) { - return '.env.test'; - } else { - return '.env.development'; + switch (env) { + case 'production': + return '.env.production'; + case 'test': + return '.env.test'; + case 'uat': + return '.env.uat'; + default: + return '.env.development'; } } @@ -32,45 +30,10 @@ class Environment { } static String get apiUrlTicket { - return dotenv.env['TICKET_API_URL'] ?? 'API_URL not found!'; + return dotenv.env['TICKET_API_URL'] ?? 'TICKET_API_URL not found!'; } static String get ticketToken { return 'uncp8FvG310bEyYdV9MmStlo7KDRZ65fLWTeXCI2JzwPrNHjBqQhUiAgxsaO'; } } - -Future main() async { - await dotenv.load(fileName: Environment.fileName); - runApp(MyApp()); -} - -// class Environment { -// static String get fileName { -// if (kReleaseMode) { -// return '.env.production'; -// } else { -// return '.env.development'; -// } -// } -// -// static String get apiUrl { -// return dotenv.env['API_URL'] ?? 'API_URL not found!'; -// } -// -// static String get baseHref { -// return dotenv.env['BASE_HREF'] ?? 'BASE_HREF not found!'; -// } -// -// static String get env { -// return dotenv.env['ENV'] ?? 'ENV not found!'; -// } -// -// static String get apiUrlTicket { -// return dotenv.env['TICKET_API_URL'] ?? 'API_URL not found!'; -// } -// -// static String get ticketToken { -// return 'uncp8FvG310bEyYdV9MmStlo7KDRZ65fLWTeXCI2JzwPrNHjBqQhUiAgxsaO'; -// } -// } diff --git a/lib/pages/enrollment/addons.dart b/lib/pages/enrollment/addons.dart index c158496..fcf50ea 100644 --- a/lib/pages/enrollment/addons.dart +++ b/lib/pages/enrollment/addons.dart @@ -124,6 +124,7 @@ class _addOnsDetailsState extends State { dynamic gpaDataIsEmpty = 1; dynamic empRefId; dynamic empClientBranchId; + dynamic selfEmpStatus; @override void initState() { @@ -168,6 +169,7 @@ class _addOnsDetailsState extends State { // Decode the JWT token received from the API response Map? decodedToken = Jwt.parseJwt(token); print(decodedToken); + selfEmpStatus = prefs.getString('selfEmpStatus'); empClientBranchId = prefs.getString('empClientBranchId'); empCodeString = prefs.getString('empCode'); print(empCodeString); // Check if emp_code is correct @@ -1826,7 +1828,7 @@ class _addOnsDetailsState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Expanded( - flex: 8, + flex: 9, child: Container( alignment: Alignment.centerLeft, child: Column( @@ -1843,7 +1845,7 @@ class _addOnsDetailsState extends State { Responsive.isDesktop( context) ? 18 - : 14, + : 13, ), ), Row( @@ -1857,7 +1859,7 @@ class _addOnsDetailsState extends State { .isDesktop( context) ? 20 - : 16, + : 14, fontWeight: FontWeight.w600, ), @@ -1899,7 +1901,7 @@ class _addOnsDetailsState extends State { ], ))), Expanded( - flex: 4, + flex: 3, child: Container( alignment: Alignment.centerRight, child: Column( @@ -1916,7 +1918,7 @@ class _addOnsDetailsState extends State { Responsive.isDesktop( context) ? 18 - : 14, + : 13, ), ), Text( @@ -1927,7 +1929,7 @@ class _addOnsDetailsState extends State { Responsive.isDesktop( context) ? 20 - : 16, + : 14, fontWeight: FontWeight.w600, ), ), @@ -4151,7 +4153,8 @@ class _addOnsDetailsState extends State { 'assets/nhance-loader.gif'), // Adjust path to your GIF loader ), ), - if (Responsive.isDesktop(context)) + if (Responsive.isDesktop(context) || + (!Responsive.isDesktop(context) && selfEmpStatus != 'enrolled')) Align( alignment: Alignment.bottomCenter, child: Container( @@ -4162,46 +4165,53 @@ class _addOnsDetailsState extends State { ]), floatingActionButton: Responsive.isDesktop(context) ? null - : FloatingActionButton( - onPressed: () => Navigator.push( - context, - MaterialPageRoute(builder: (context) => chatbot()), - ), - child: Icon(Icons.chat), - ), + : selfEmpStatus == 'enrolled' + ? FloatingActionButton( + onPressed: () => Navigator.push( + context, + MaterialPageRoute(builder: (context) => chatbot()), + ), + child: Icon(Icons.chat), + ) + : null, floatingActionButtonLocation: Responsive.isDesktop(context) ? null - : FloatingActionButtonLocation.miniEndFloat, + : selfEmpStatus == 'enrolled' + ? FloatingActionButtonLocation.miniEndFloat + : null, bottomNavigationBar: Responsive.isDesktop(context) ? null - : CustomBottomNavigationBar( - onTabChanged: (index) { - // Add your navigation logic here - // For example: - if (index == 0) { - Navigator.pushNamed(context, 'home'); - } else if (index == 1) { - Navigator.pushNamed(context, 'claims'); - } else if (index == 2) { - Navigator.pushNamed(context, 'profile'); - } else if (index == 3) { - Navigator.pushNamed(context, 'help'); - } - }, - icons: [ - Icons.home_outlined, - Icons.sticky_note_2_outlined, - Icons.person_outline_outlined, - Icons.headset_mic_outlined, - ], - labels: [ - "Home", - "Claim", - "Profile", - "Help", - ], - initialIndex: 0, // Initial index of the bottom navigation bar - ), + : selfEmpStatus == 'enrolled' + ? CustomBottomNavigationBar( + onTabChanged: (index) { + // Add your navigation logic here + // For example: + if (index == 0) { + Navigator.pushNamed(context, 'home'); + } else if (index == 1) { + Navigator.pushNamed(context, 'claims'); + } else if (index == 2) { + Navigator.pushNamed(context, 'profile'); + } else if (index == 3) { + Navigator.pushNamed(context, 'help'); + } + }, + icons: [ + Icons.home_outlined, + Icons.sticky_note_2_outlined, + Icons.person_outline_outlined, + Icons.headset_mic_outlined, + ], + labels: [ + "Home", + "Claim", + "Profile", + "Help", + ], + initialIndex: + 0, // Initial index of the bottom navigation bar + ) + : null, ); } } @@ -4571,7 +4581,7 @@ class _addOnsDetailsState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Expanded( - flex: 8, + flex: 9, child: Container( alignment: Alignment.centerLeft, child: Column( @@ -4586,7 +4596,7 @@ class _addOnsDetailsState extends State { fontSize: Responsive.isDesktop(context) ? 18 - : 14, + : 13, ), ), Row( @@ -4598,7 +4608,7 @@ class _addOnsDetailsState extends State { fontSize: Responsive.isDesktop(context) ? 20 - : 16, + : 14, fontWeight: FontWeight.w600, ), ), @@ -4633,7 +4643,7 @@ class _addOnsDetailsState extends State { ], ))), Expanded( - flex: 4, + flex: 3, child: Container( alignment: Alignment.centerRight, child: Column( @@ -4647,7 +4657,7 @@ class _addOnsDetailsState extends State { fontSize: Responsive.isDesktop(context) ? 18 - : 14, + : 13, ), ), Text( @@ -4657,7 +4667,7 @@ class _addOnsDetailsState extends State { fontSize: Responsive.isDesktop(context) ? 20 - : 16, + : 14, fontWeight: FontWeight.w600, ), ), diff --git a/lib/pages/enrollment/empDetails.dart b/lib/pages/enrollment/empDetails.dart index 6f17f44..01387df 100644 --- a/lib/pages/enrollment/empDetails.dart +++ b/lib/pages/enrollment/empDetails.dart @@ -390,54 +390,83 @@ class _empDetailsState extends State { Future _selectDate(BuildContext context, formType) async { print(formType); var ageValidation = formType['age_validation']; - int min = - int.parse(ageValidation['min']); // Parsing the string to an integer - int max = - int.parse(ageValidation['max']); // Parsing the string to an integer - print(min); - print(max); - late DateTime minDate; - late DateTime maxDate; + print(ageValidation); + // int min = int.parse(ageValidation['min']); + // int max = int.parse(ageValidation['max']); + // Ensure ageValidation is a Map and contains the expected values + if (ageValidation is Map) { + int min = ageValidation.containsKey('min') + ? int.tryParse(ageValidation['min'].toString()) ?? 0 + : 0; + int max = ageValidation.containsKey('max') + ? int.tryParse(ageValidation['max'].toString()) ?? 100 + : 100; - final DateTime now = DateTime.now(); + print('Min age: $min'); + print('Max age: $max'); + late DateTime minDate; + late DateTime maxDate; - maxDate = DateTime(now.year - min, now.month, now.day); - print(maxDate); - minDate = DateTime(now.year - max, now.month, now.day); - print(minDate); + final DateTime now = DateTime.now(); - // if (formType['form_type'] == 'child') { - // // For 'child' form type, allow 0-25 age - // minDate = DateTime(now.year - 25, now.month, now.day); - // maxDate = now; - // } else { - // // For other form types, allow 18-100 age - // minDate = DateTime(1900); - // maxDate = DateTime(now.year - 18, now.month, now.day); - // } + maxDate = DateTime(now.year - min, now.month, now.day); + print(maxDate); + minDate = DateTime(now.year - max, now.month, now.day); + print(minDate); - // Ensure initialDate is within the range of minDate and maxDate - DateTime initialDate = - _selectedDate ?? maxDate; // Use maxDate if _selectedDate is null - initialDate = initialDate.isBefore(minDate) ? minDate : initialDate; - initialDate = initialDate.isAfter(maxDate) ? maxDate : initialDate; + // if (formType['form_type'] == 'child') { + // // For 'child' form type, allow 0-25 age + // minDate = DateTime(now.year - 25, now.month, now.day); + // maxDate = now; + // } else { + // // For other form types, allow 18-100 age + // minDate = DateTime(1900); + // maxDate = DateTime(now.year - 18, now.month, now.day); + // } - final DateTime? picked = await showDatePicker( - context: context, - initialDate: initialDate, - firstDate: minDate, - lastDate: maxDate, - selectableDayPredicate: (DateTime date) { - return date.isAfter(minDate.subtract(const Duration(days: 1))) && - date.isBefore(maxDate.add(const Duration(days: 1))); - }, - ); + // Ensure initialDate is within the range of minDate and maxDate + DateTime initialDate = + _selectedDate ?? maxDate; // Use maxDate if _selectedDate is null + initialDate = initialDate.isBefore(minDate) ? minDate : initialDate; + initialDate = initialDate.isAfter(maxDate) ? maxDate : initialDate; - if (picked != null && picked != _selectedDate) { - setState(() { - _selectedDate = picked; - _dobController.text = DateFormat('dd-MM-yyyy').format(_selectedDate); - }); + final DateTime? picked = await showDatePicker( + context: context, + initialDate: initialDate, + firstDate: minDate, + lastDate: maxDate, + selectableDayPredicate: (DateTime date) { + return date.isAfter(minDate.subtract(const Duration(days: 1))) && + date.isBefore(maxDate.add(const Duration(days: 1))); + }, + builder: (BuildContext context, Widget? child) { + return Theme( + data: ThemeData.light().copyWith( + primaryColor: Color(0xFFE26728), // Header background color + colorScheme: ColorScheme.light( + primary: Color(0xFFE26728), secondary: Color(0xFFE26728)), + buttonTheme: ButtonThemeData(textTheme: ButtonTextTheme.primary), + // dialogBackgroundColor: + // Colors.white, // Background color of the dialog + iconTheme: IconThemeData(color: Color(0xFFE26728)), + textButtonTheme: TextButtonThemeData( + style: TextButton.styleFrom( + foregroundColor: Color(0xFFE26728)), // Button color + ), + ), + child: child!, + ); + }, + ); + + if (picked != null && picked != _selectedDate) { + setState(() { + _selectedDate = picked; + _dobController.text = DateFormat('dd-MM-yyyy').format(_selectedDate); + }); + } + } else { + print('Invalid age validation data'); } } @@ -663,6 +692,7 @@ class _empDetailsState extends State { )); } else { return Scaffold( + backgroundColor: Color(0xFFFFFFFF), appBar: CustomAppBar(), body: Stack(children: [ SingleChildScrollView( @@ -782,7 +812,7 @@ class _empDetailsState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Expanded( - flex: 8, + flex: 9, child: Container( alignment: Alignment.centerLeft, child: Column( @@ -799,13 +829,13 @@ class _empDetailsState extends State { Responsive.isDesktop( context) ? 18 - : 14, + : 13, ), ), Row( children: [ Text( - 'Group Personal Accident Coverage ', + 'Group Personal Accident Coverage', textAlign: TextAlign.left, style: GoogleFonts.poppins( @@ -813,7 +843,7 @@ class _empDetailsState extends State { .isDesktop( context) ? 20 - : 16, + : 14, fontWeight: FontWeight.w600, ), @@ -855,7 +885,7 @@ class _empDetailsState extends State { ], ))), Expanded( - flex: 4, + flex: 3, child: Container( alignment: Alignment.centerRight, child: Column( @@ -872,7 +902,7 @@ class _empDetailsState extends State { Responsive.isDesktop( context) ? 18 - : 14, + : 13, ), ), Text( @@ -883,7 +913,7 @@ class _empDetailsState extends State { Responsive.isDesktop( context) ? 20 - : 16, + : 14, fontWeight: FontWeight.w600, ), ), @@ -1155,7 +1185,8 @@ class _empDetailsState extends State { 'assets/nhance-loader.gif'), // Adjust path to your GIF loader ), ), - if (Responsive.isDesktop(context)) + if (Responsive.isDesktop(context) || + (!Responsive.isDesktop(context) && selfEmpStatus != 'enrolled')) Align( alignment: Alignment.bottomCenter, child: Container( @@ -1166,46 +1197,53 @@ class _empDetailsState extends State { ]), floatingActionButton: Responsive.isDesktop(context) ? null - : FloatingActionButton( - onPressed: () => Navigator.push( - context, - MaterialPageRoute(builder: (context) => chatbot()), - ), - child: Icon(Icons.chat), - ), + : selfEmpStatus == 'enrolled' + ? FloatingActionButton( + onPressed: () => Navigator.push( + context, + MaterialPageRoute(builder: (context) => chatbot()), + ), + child: Icon(Icons.chat), + ) + : null, floatingActionButtonLocation: Responsive.isDesktop(context) ? null - : FloatingActionButtonLocation.miniEndFloat, + : selfEmpStatus == 'enrolled' + ? FloatingActionButtonLocation.miniEndFloat + : null, bottomNavigationBar: Responsive.isDesktop(context) ? null - : CustomBottomNavigationBar( - onTabChanged: (index) { - // Add your navigation logic here - // For example: - if (index == 0) { - Navigator.pushNamed(context, 'home'); - } else if (index == 1) { - Navigator.pushNamed(context, 'claims'); - } else if (index == 2) { - Navigator.pushNamed(context, 'profile'); - } else if (index == 3) { - Navigator.pushNamed(context, 'help'); - } - }, - icons: [ - Icons.home_outlined, - Icons.sticky_note_2_outlined, - Icons.person_outline_outlined, - Icons.headset_mic_outlined, - ], - labels: [ - "Home", - "Claim", - "Profile", - "Help", - ], - initialIndex: 0, // Initial index of the bottom navigation bar - ), + : selfEmpStatus == 'enrolled' + ? CustomBottomNavigationBar( + onTabChanged: (index) { + // Add your navigation logic here + // For example: + if (index == 0) { + Navigator.pushNamed(context, 'home'); + } else if (index == 1) { + Navigator.pushNamed(context, 'claims'); + } else if (index == 2) { + Navigator.pushNamed(context, 'profile'); + } else if (index == 3) { + Navigator.pushNamed(context, 'help'); + } + }, + icons: [ + Icons.home_outlined, + Icons.sticky_note_2_outlined, + Icons.person_outline_outlined, + Icons.headset_mic_outlined, + ], + labels: [ + "Home", + "Claim", + "Profile", + "Help", + ], + initialIndex: + 0, // Initial index of the bottom navigation bar + ) + : null, ); } } @@ -1224,6 +1262,13 @@ class _empDetailsState extends State { ) .toList(); + // Set the dropdown value based on the form_type + String? dropdownValue; + if (floaterData['form_type'] == 'spouse') { + dropdownValue = 'Spouse'; + _relationShipController.text = 'Spouse'; + } + print(selectedRelationships); print(gmcMappedFamilyFloaters); if (action == 'Add') { @@ -1272,6 +1317,7 @@ class _empDetailsState extends State { width: Responsive.isDesktop(context) ? 500 : 800, child: SingleChildScrollView( child: Card( + color: Colors.white, elevation: 0, child: Padding( padding: Responsive.isDesktop(context) @@ -1325,14 +1371,16 @@ class _empDetailsState extends State { hintText: 'Relationship', labelText: 'Relationship', contentPadding: EdgeInsets.symmetric( - vertical: 5, horizontal: 5), + vertical: 10, horizontal: 15), ), - value: action == 'Edit' - ? _relationShipController.text - : null, + value: dropdownValue ?? + (action == 'Edit' + ? _relationShipController.text + : null), onChanged: (value) { _relationShipController.text = value!; }, + dropdownColor: Colors.white, items: selectedRelationships .map((relationship) { final String relationshipName = @@ -1526,6 +1574,7 @@ class _empDetailsState extends State { width: Responsive.isDesktop(context) ? 1000 : 800, child: SingleChildScrollView( child: Card( + color: Colors.white, elevation: 0, child: Padding( padding: Responsive.isDesktop(context) @@ -1630,21 +1679,21 @@ class _empDetailsState extends State { ), ), ), - SizedBox(width: 10), - Expanded( - flex: 4, - child: TextFormField( - readOnly: true, - initialValue: selfEmailPersonal ?? '', - decoration: InputDecoration( - border: OutlineInputBorder(), - hintText: 'Email personal', - labelText: 'Email personal', - contentPadding: EdgeInsets.symmetric( - vertical: 10, horizontal: 15), - ), - ), - ), + // SizedBox(width: 10), + // Expanded( + // flex: 4, + // child: TextFormField( + // readOnly: true, + // initialValue: selfEmailPersonal ?? '', + // decoration: InputDecoration( + // border: OutlineInputBorder(), + // hintText: 'Email personal', + // labelText: 'Email personal', + // contentPadding: EdgeInsets.symmetric( + // vertical: 10, horizontal: 15), + // ), + // ), + // ), SizedBox(width: 10), Expanded( flex: 4, @@ -1765,18 +1814,18 @@ class _empDetailsState extends State { vertical: 10, horizontal: 15), ), ), - SizedBox(height: 15), - TextFormField( - readOnly: true, - initialValue: selfEmailPersonal ?? '', - decoration: InputDecoration( - border: OutlineInputBorder(), - hintText: 'Email personal', - labelText: 'Email personal', - contentPadding: EdgeInsets.symmetric( - vertical: 10, horizontal: 15), - ), - ), + // SizedBox(height: 15), + // TextFormField( + // readOnly: true, + // initialValue: selfEmailPersonal ?? '', + // decoration: InputDecoration( + // border: OutlineInputBorder(), + // hintText: 'Email personal', + // labelText: 'Email personal', + // contentPadding: EdgeInsets.symmetric( + // vertical: 10, horizontal: 15), + // ), + // ), SizedBox(height: 15), TextFormField( readOnly: true, @@ -2164,7 +2213,7 @@ class _empDetailsState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Expanded( - flex: 8, + flex: 9, child: Container( alignment: Alignment.centerLeft, child: Column( @@ -2177,7 +2226,7 @@ class _empDetailsState extends State { style: GoogleFonts.poppins( fontSize: Responsive.isDesktop(context) ? 18 - : 14, + : 13, ), ), Row( @@ -2189,7 +2238,7 @@ class _empDetailsState extends State { fontSize: Responsive.isDesktop(context) ? 20 - : 16, + : 14, fontWeight: FontWeight.w600, ), ), @@ -2224,7 +2273,7 @@ class _empDetailsState extends State { ], ))), Expanded( - flex: 4, + flex: 3, child: Container( alignment: Alignment.centerRight, child: Column( @@ -2237,7 +2286,7 @@ class _empDetailsState extends State { style: GoogleFonts.poppins( fontSize: Responsive.isDesktop(context) ? 18 - : 14, + : 13, ), ), Text( @@ -2246,7 +2295,7 @@ class _empDetailsState extends State { style: GoogleFonts.poppins( fontSize: Responsive.isDesktop(context) ? 20 - : 16, + : 14, fontWeight: FontWeight.w600, ), ), diff --git a/lib/pages/enrollment/empReview.dart b/lib/pages/enrollment/empReview.dart index 8521d38..b688891 100644 --- a/lib/pages/enrollment/empReview.dart +++ b/lib/pages/enrollment/empReview.dart @@ -121,6 +121,7 @@ class _empReviewDetailsState extends State { dynamic gmcDataIsEmpty = 1; dynamic gpaDataIsEmpty = 1; dynamic empClientBranchId; + dynamic selfEmpStatus; @override void initState() { @@ -156,6 +157,7 @@ class _empReviewDetailsState extends State { setState(() { _token = token; }); + selfEmpStatus = prefs.getString('selfEmpStatus'); empClientBranchId = prefs.getString('empClientBranchId'); empCodeString = prefs.getString('empCode'); print(empCodeString); // Check if emp_code is correct @@ -706,12 +708,14 @@ class _empReviewDetailsState extends State { }); print('response.statusCode == 200'); ToastHelper.showSuccessToast(context, 'Saved Successfully...'); + _showSuccessDialog(); } else { setState(() { isLoading = false; }); // Handle other status codes ToastHelper.showErrorToast(context, 'Failed to Save'); + _showErrorDialog(); print('Request failed with status: ${response['code']}'); } } catch (e) { @@ -723,6 +727,67 @@ class _empReviewDetailsState extends State { } } + void _showSuccessDialog() { + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + backgroundColor: Colors.white, + title: Text( + 'Success', + style: GoogleFonts.poppins( + color: Colors.green, + ), + ), + content: Text('Enrollment Completed'), + actions: [ + TextButton( + child: Text( + 'OK', + style: GoogleFonts.poppins( + color: Color(0xFFE26728), + ), + ), + onPressed: () { + Navigator.popAndPushNamed(context, 'home'); + }, + ), + ], + ); + }, + ); + } + + void _showErrorDialog() { + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: Text( + 'Error', + style: GoogleFonts.poppins( + color: Colors.red, + ), + ), + content: Text('Something went wrong. Enrollemnt not completed'), + actions: [ + TextButton( + child: Text( + 'OK', + style: GoogleFonts.poppins( + color: Color(0xFFE26728), + ), + ), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + ], + ); + }, + ); + } + @override Widget build(BuildContext context) { if ((gpaMappedFamilyFloaters == [] && gpaMappedFamilyFloaters == null) && @@ -958,7 +1023,7 @@ class _empReviewDetailsState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Expanded( - flex: 8, + flex: 9, child: Container( alignment: Alignment.centerLeft, child: Column( @@ -975,7 +1040,7 @@ class _empReviewDetailsState extends State { Responsive.isDesktop( context) ? 18 - : 14, + : 13, ), ), Row( @@ -989,7 +1054,7 @@ class _empReviewDetailsState extends State { .isDesktop( context) ? 20 - : 16, + : 14, fontWeight: FontWeight.w600, ), @@ -1031,7 +1096,7 @@ class _empReviewDetailsState extends State { ], ))), Expanded( - flex: 4, + flex: 3, child: Container( alignment: Alignment.centerRight, child: Column( @@ -1048,7 +1113,7 @@ class _empReviewDetailsState extends State { Responsive.isDesktop( context) ? 18 - : 14, + : 13, ), ), Text( @@ -1059,7 +1124,7 @@ class _empReviewDetailsState extends State { Responsive.isDesktop( context) ? 20 - : 16, + : 14, fontWeight: FontWeight.w600, ), ), @@ -1183,7 +1248,7 @@ class _empReviewDetailsState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Expanded( - flex: 8, + flex: 9, child: Container( alignment: Alignment.centerLeft, child: Column( @@ -1200,7 +1265,7 @@ class _empReviewDetailsState extends State { Responsive.isDesktop( context) ? 18 - : 14, + : 13, ), ), Row( @@ -1214,7 +1279,7 @@ class _empReviewDetailsState extends State { .isDesktop( context) ? 20 - : 16, + : 14, fontWeight: FontWeight.w600, ), @@ -1256,7 +1321,7 @@ class _empReviewDetailsState extends State { ], ))), Expanded( - flex: 4, + flex: 3, child: Container( alignment: Alignment.centerRight, child: Column( @@ -1273,7 +1338,7 @@ class _empReviewDetailsState extends State { Responsive.isDesktop( context) ? 18 - : 14, + : 13, ), ), Text( @@ -1284,7 +1349,7 @@ class _empReviewDetailsState extends State { Responsive.isDesktop( context) ? 20 - : 16, + : 14, fontWeight: FontWeight.w600, ), ), @@ -1406,7 +1471,7 @@ class _empReviewDetailsState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Expanded( - flex: 8, + flex: 9, child: Container( alignment: Alignment.centerLeft, child: Column( @@ -1423,63 +1488,74 @@ class _empReviewDetailsState extends State { Responsive.isDesktop( context) ? 18 - : 14, + : 13, ), ), - Row( - children: [ - Text( - topUpParentTypeName ?? '', - textAlign: TextAlign.left, - style: - GoogleFonts.poppins( - fontSize: Responsive - .isDesktop( - context) - ? 20 - : 16, - fontWeight: - FontWeight.w600, - ), - ), - if (topUpParentECardDownload != - null) - GestureDetector( - onTap: () async { - // Convert the URL string to a Uri object - Uri uri = Uri.parse( - topUpParentECardDownload); - - // Check if the URL is valid - if (await canLaunchUrl( - uri)) { - // Open the URL in the default browser - await launchUrl( - uri); - } else { - // Handle the case where the URL cannot be launched - print( - 'Could not launch $topUpParentECardDownload'); - } - }, - child: MouseRegion( - cursor: - SystemMouseCursors - .click, - child: Icon( - Icons.file_download, - color: Color( - 0xFFE26728), - size: 25, + Container( + // padding: EdgeInsets.all(8.0), + child: Row( + children: [ + Flexible( + child: Text( + topUpParentTypeName ?? + '', + textAlign: + TextAlign.left, + style: GoogleFonts + .poppins( + fontSize: Responsive + .isDesktop( + context) + ? 20 + : 14, + fontWeight: + FontWeight.w600, ), + softWrap: true, + overflow: TextOverflow + .visible, ), ), - ], - ), + if (topUpParentECardDownload != + null) + GestureDetector( + onTap: () async { + // Convert the URL string to a Uri object + Uri uri = Uri.parse( + topUpParentECardDownload); + + // Check if the URL is valid + if (await canLaunchUrl( + uri)) { + // Open the URL in the default browser + await launchUrl( + uri); + } else { + // Handle the case where the URL cannot be launched + print( + 'Could not launch $topUpParentECardDownload'); + } + }, + child: MouseRegion( + cursor: + SystemMouseCursors + .click, + child: Icon( + Icons + .file_download, + color: Color( + 0xFFE26728), + size: 25, + ), + ), + ), + ], + ), + ) ], ))), Expanded( - flex: 4, + flex: 3, child: Container( alignment: Alignment.centerRight, child: Column( @@ -1497,7 +1573,7 @@ class _empReviewDetailsState extends State { Responsive.isDesktop( context) ? 18 - : 14, + : 13, ), ), Text( @@ -1508,7 +1584,7 @@ class _empReviewDetailsState extends State { Responsive.isDesktop( context) ? 20 - : 16, + : 14, fontWeight: FontWeight.w600, ), ), @@ -2395,7 +2471,8 @@ class _empReviewDetailsState extends State { 'assets/nhance-loader.gif'), // Adjust path to your GIF loader ), ), - if (Responsive.isDesktop(context)) + if (Responsive.isDesktop(context) || + (!Responsive.isDesktop(context) && selfEmpStatus != 'enrolled')) Align( alignment: Alignment.bottomCenter, child: Container( @@ -2406,46 +2483,53 @@ class _empReviewDetailsState extends State { ]), floatingActionButton: Responsive.isDesktop(context) ? null - : FloatingActionButton( - onPressed: () => Navigator.push( - context, - MaterialPageRoute(builder: (context) => chatbot()), - ), - child: Icon(Icons.chat), - ), + : selfEmpStatus == 'enrolled' + ? FloatingActionButton( + onPressed: () => Navigator.push( + context, + MaterialPageRoute(builder: (context) => chatbot()), + ), + child: Icon(Icons.chat), + ) + : null, floatingActionButtonLocation: Responsive.isDesktop(context) ? null - : FloatingActionButtonLocation.miniEndFloat, + : selfEmpStatus == 'enrolled' + ? FloatingActionButtonLocation.miniEndFloat + : null, bottomNavigationBar: Responsive.isDesktop(context) ? null - : CustomBottomNavigationBar( - onTabChanged: (index) { - // Add your navigation logic here - // For example: - if (index == 0) { - Navigator.pushNamed(context, 'home'); - } else if (index == 1) { - Navigator.pushNamed(context, 'claims'); - } else if (index == 2) { - Navigator.pushNamed(context, 'profile'); - } else if (index == 3) { - Navigator.pushNamed(context, 'help'); - } - }, - icons: [ - Icons.home_outlined, - Icons.sticky_note_2_outlined, - Icons.person_outline_outlined, - Icons.headset_mic_outlined, - ], - labels: [ - "Home", - "Claim", - "Profile", - "Help", - ], - initialIndex: 0, // Initial index of the bottom navigation bar - ), + : selfEmpStatus == 'enrolled' + ? CustomBottomNavigationBar( + onTabChanged: (index) { + // Add your navigation logic here + // For example: + if (index == 0) { + Navigator.pushNamed(context, 'home'); + } else if (index == 1) { + Navigator.pushNamed(context, 'claims'); + } else if (index == 2) { + Navigator.pushNamed(context, 'profile'); + } else if (index == 3) { + Navigator.pushNamed(context, 'help'); + } + }, + icons: [ + Icons.home_outlined, + Icons.sticky_note_2_outlined, + Icons.person_outline_outlined, + Icons.headset_mic_outlined, + ], + labels: [ + "Home", + "Claim", + "Profile", + "Help", + ], + initialIndex: + 0, // Initial index of the bottom navigation bar + ) + : null, ); } } @@ -2510,7 +2594,7 @@ class _empReviewDetailsState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Expanded( - flex: 8, + flex: 9, child: Container( alignment: Alignment.centerLeft, child: Column( @@ -2525,7 +2609,7 @@ class _empReviewDetailsState extends State { fontSize: Responsive.isDesktop(context) ? 18 - : 14, + : 13, ), ), Row( @@ -2537,7 +2621,7 @@ class _empReviewDetailsState extends State { fontSize: Responsive.isDesktop(context) ? 20 - : 16, + : 14, fontWeight: FontWeight.w600, ), ), @@ -2573,7 +2657,7 @@ class _empReviewDetailsState extends State { ], ))), Expanded( - flex: 4, + flex: 3, child: Container( alignment: Alignment.centerRight, child: Column( @@ -2587,7 +2671,7 @@ class _empReviewDetailsState extends State { fontSize: Responsive.isDesktop(context) ? 18 - : 14, + : 13, ), ), Text( @@ -2597,7 +2681,7 @@ class _empReviewDetailsState extends State { fontSize: Responsive.isDesktop(context) ? 20 - : 16, + : 14, fontWeight: FontWeight.w600, ), ), diff --git a/lib/pages/firebase-config.dart b/lib/pages/firebase-config.dart new file mode 100644 index 0000000..0a282fa --- /dev/null +++ b/lib/pages/firebase-config.dart @@ -0,0 +1,23 @@ +import 'package:firebase_core/firebase_core.dart'; + +const firebaseConfig = { + 'apiKey': "AIzaSyAg0e5u7Piy6sWfgKl0C6D2XYyOakGB4wg", + 'authDomain': "push-notification-enrollment.firebaseapp.com", + 'projectId': "push-notification-enrollment", + 'storageBucket': "push-notification-enrollment.appspot.com", + 'messagingSenderId': "203846206303", + 'appId': "1:203846206303:web:292619fd22c24ff99b6cea", +}; + +Future initializeFirebase() async { + await Firebase.initializeApp( + options: FirebaseOptions( + apiKey: firebaseConfig['apiKey']!, + authDomain: firebaseConfig['authDomain']!, + projectId: firebaseConfig['projectId']!, + storageBucket: firebaseConfig['storageBucket']!, + messagingSenderId: firebaseConfig['messagingSenderId']!, + appId: firebaseConfig['appId']!, + ), + ); +} diff --git a/lib/pages/login.dart b/lib/pages/login.dart index 142998d..7431a4c 100644 --- a/lib/pages/login.dart +++ b/lib/pages/login.dart @@ -1,7 +1,9 @@ +import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:http/http.dart' as http; +import 'package:nhance_app_pwa/pages/verify.dart'; import 'dart:convert'; import 'dart:io'; import 'package:shared_preferences/shared_preferences.dart'; @@ -26,6 +28,12 @@ class _loginState extends State { bool _isCheckingToken = false; dynamic empMobileNo; + final FirebaseAuth _auth = FirebaseAuth.instance; + + late String _verificationId; + int? _resendToken; + bool _isLoading = false; + @override void initState() { countryController.text = "+91"; @@ -73,8 +81,13 @@ class _loginState extends State { } Future verifyMobileNumber() async { + // _verifyPhoneNumber(); + // return; try { if (_formKey.currentState!.validate()) { + setState(() { + _isLoading = true; + }); // var enteredMobileNumber = countryController.text + mobileController.text; var enteredMobileNumber = mobileController.text; final response = await http.post( @@ -93,26 +106,142 @@ class _loginState extends State { final SharedPreferences prefs = await SharedPreferences.getInstance(); var enteredMobileNumber = mobileController.text; + var countryCode = countryController.text; prefs.setString('empMobileNo', enteredMobileNumber); - ToastHelper.showSuccessToast(context, message); - Navigator.pushNamed(context, 'verify', - arguments: enteredMobileNumber); + // await FirebaseAuth.instance.verifyPhoneNumber( + // phoneNumber: '${countryCode + enteredMobileNumber}', + // verificationCompleted: (PhoneAuthCredential credential) {}, + // verificationFailed: (FirebaseAuthException e) {}, + // codeSent: (String verificationId, int? resendToken) {}, + // codeAutoRetrievalTimeout: (String verificationId) {}, + // + _verifyPhoneNumber(); + + // ToastHelper.showSuccessToast(context, message); + // Navigator.pushNamed(context, 'verify', + // arguments: enteredMobileNumber); } else { + setState(() { + _isLoading = false; + }); ToastHelper.showErrorToast(context, message); print('Invalid mobile number'); } } else { + setState(() { + _isLoading = false; + }); ToastHelper.showErrorToast(context, 'Something went wrong'); throw Exception('Failed to verify mobile number'); } } } catch (e) { + setState(() { + _isLoading = false; + }); ToastHelper.showErrorToast(context, 'Something went wrong'); print('Error: $e'); } } + Future _verifyPhoneNumber() async { + var enteredMobileNumber = mobileController.text; + var countryCode = countryController.text; + print('${countryCode + enteredMobileNumber}'); + await _auth.verifyPhoneNumber( + phoneNumber: '${countryCode + enteredMobileNumber}', + timeout: const Duration(seconds: 60), + verificationCompleted: (PhoneAuthCredential credential) async { + await _auth.signInWithCredential(credential); + ToastHelper.showSuccessToast(context, 'Verified Successfully!'); + setState(() { + _isLoading = false; + }); + }, + verificationFailed: (FirebaseAuthException e) { + ToastHelper.showSuccessToast(context, 'Verification Failed!'); + if (e.code == 'invalid-phone-number') { + print('The provided phone number is not valid.'); + } + setState(() { + _isLoading = false; + }); + }, + codeSent: (String verificationId, int? resendToken) { + setState(() { + _verificationId = verificationId; + _resendToken = resendToken; + }); + ToastHelper.showSuccessToast( + context, 'Verification code sent to ${enteredMobileNumber}'); + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => MyVerify( + verificationId: _verificationId, + mobileNumber: enteredMobileNumber, + resendToken: _resendToken, + onResendCode: _resendCode, // Pass the phone number + ), + ), + ); + setState(() { + _isLoading = false; + }); + }, + codeAutoRetrievalTimeout: (String verificationId) { + setState(() { + _verificationId = verificationId; + }); + ToastHelper.showSuccessToast(context, 'Code auto-retrieval timed out.'); + setState(() { + _isLoading = false; + }); + }, + ); + } + + void _resendCode(String mobileNumber, int? resendToken) async { + await _auth.verifyPhoneNumber( + phoneNumber: '${countryController.text + mobileNumber}', + timeout: const Duration(seconds: 60), + forceResendingToken: resendToken, + verificationCompleted: (PhoneAuthCredential credential) async { + await _auth.signInWithCredential(credential); + }, + verificationFailed: (FirebaseAuthException e) { + if (e.code == 'invalid-phone-number') { + print('The provided phone number is not valid.'); + } + }, + codeSent: (String verificationId, int? resendToken) { + setState(() { + _verificationId = verificationId; + _resendToken = resendToken; + }); + ToastHelper.showSuccessToast( + context, 'Verification code resent to ${mobileNumber}'); + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => MyVerify( + verificationId: _verificationId, + mobileNumber: mobileNumber, + resendToken: _resendToken, + onResendCode: _resendCode, + ), + ), + ); + }, + codeAutoRetrievalTimeout: (String verificationId) { + setState(() { + _verificationId = verificationId; + }); + }, + ); + } + @override Widget build(BuildContext context) { Size _size = MediaQuery.of(context).size; @@ -414,12 +543,21 @@ class _loginState extends State { BorderRadius.circular(10), ), ), - onPressed: verifyMobileNumber, - child: Text( - "Login With OTP", - style: GoogleFonts.poppins( - color: Color(0xFFFFFFFF)), - ), + onPressed: _isLoading + ? null + : verifyMobileNumber, + child: _isLoading + ? CircularProgressIndicator( + valueColor: + AlwaysStoppedAnimation< + Color>( + Color(0xFF00989E)), + ) + : Text( + "Login With OTP", + style: GoogleFonts.poppins( + color: Color(0xFFFFFFFF)), + ), ), ), ), diff --git a/lib/pages/postEnrollment/policies.dart b/lib/pages/postEnrollment/policies.dart index 63e7578..afc6634 100644 --- a/lib/pages/postEnrollment/policies.dart +++ b/lib/pages/postEnrollment/policies.dart @@ -326,7 +326,7 @@ class _policiesState extends State { width: 8), // Adjust space between icon and text Text( - 'File a Claim', + 'Initiate a Claim', style: GoogleFonts.poppins( fontSize: Responsive.isDesktop( context) diff --git a/lib/pages/postEnrollment/service/api_service.dart b/lib/pages/postEnrollment/service/api_service.dart index a9ba08b..5ce96a6 100644 --- a/lib/pages/postEnrollment/service/api_service.dart +++ b/lib/pages/postEnrollment/service/api_service.dart @@ -118,29 +118,6 @@ class ApiService { return response; } - Future> _makeGetRequest( - Uri url, Map headers) async { - final response = await http.get(url, headers: headers); - return _handleResponse(response); - } - - Future> _makePostRequest( - Uri url, Map body, Map headers) async { - final response = await http.post(url, headers: headers, body: body); - return _handleResponse(response); - } - - Future> _handleResponse(http.Response response) async { - if (response.statusCode == 200) { - return jsonDecode(response.body); - } else if (response.statusCode == 401) { - await _clearLocalStorageAndRedirect(); - return {}; - } else { - throw Exception('Failed to load data'); - } - } - Future _clearLocalStorageAndRedirect() async { final prefs = await SharedPreferences.getInstance(); await prefs.clear(); @@ -176,4 +153,27 @@ class ApiService { final response = await _makeGetRequest(url, headers); return response; } + + Future> _makeGetRequest( + Uri url, Map headers) async { + final response = await http.get(url, headers: headers); + return _handleResponse(response); + } + + Future> _makePostRequest( + Uri url, Map body, Map headers) async { + final response = await http.post(url, headers: headers, body: body); + return _handleResponse(response); + } + + Future> _handleResponse(http.Response response) async { + if (response.statusCode == 200) { + return jsonDecode(response.body); + } else if (response.statusCode == 401) { + await _clearLocalStorageAndRedirect(); + return {}; + } else { + throw Exception('Failed to load data'); + } + } } diff --git a/lib/pages/recaptcha_helper.dart b/lib/pages/recaptcha_helper.dart new file mode 100644 index 0000000..d035c64 --- /dev/null +++ b/lib/pages/recaptcha_helper.dart @@ -0,0 +1,10 @@ +@JS() +library recaptcha_helper; + +import 'package:js/js.dart'; + +@JS('grecaptcha.execute') +external void executeRecaptcha(String siteKey, Function(String) callback); + +@JS('grecaptcha.ready') +external void grecaptchaReady(Function callback); diff --git a/lib/pages/verify.dart b/lib/pages/verify.dart index a415cab..4eaa8e3 100644 --- a/lib/pages/verify.dart +++ b/lib/pages/verify.dart @@ -1,5 +1,8 @@ +import 'package:firebase_auth/firebase_auth.dart'; +import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; +import 'package:nhance_app_pwa/pages/postEnrollment/service/api_service.dart'; import 'package:pinput/pinput.dart'; import 'dart:async'; import 'package:flutter/gestures.dart'; @@ -16,17 +19,30 @@ import '../models/platform_helper_mobile.dart' if (dart.library.html) '../models/platform_helper_other.dart'; class MyVerify extends StatefulWidget { - const MyVerify({Key? key}) : super(key: key); + final String verificationId; + final String mobileNumber; + final int? resendToken; + final Function(String, int?) onResendCode; + + const MyVerify({ + Key? key, + required this.verificationId, + required this.mobileNumber, + required this.resendToken, + required this.onResendCode, + }) : super(key: key); @override State createState() => _MyVerifyState(); } class _MyVerifyState extends State { + TextEditingController countryController = TextEditingController(); + late ApiService apiService; TextEditingController _otpController = TextEditingController(); final _formKey = GlobalKey(); late Timer _timer; - int _secondsRemaining = 30; + int _secondsRemaining = 60; bool _isTimerRunning = false; dynamic empCodeString; dynamic empClientBranchId; @@ -37,12 +53,29 @@ class _MyVerifyState extends State { dynamic clientName; dynamic clientLogo; dynamic emp_status; + dynamic fCMToken; + late String verificationId; + late String mobileNumber; + final FirebaseMessaging _firebaseMessaging = FirebaseMessaging.instance; + final FirebaseAuth _auth = FirebaseAuth.instance; + bool _isLoading = false; + + late String _verificationId; @override void initState() { super.initState(); + countryController.text = "+91"; + apiService = ApiService(context); // checkTokenAvailability(); - // Start the timer when the widget is initialized + verificationId = widget.verificationId; + mobileNumber = widget.mobileNumber; + print('Received verificationId: $verificationId'); + print('Received mobileNumber: $mobileNumber'); + if (verificationId.isEmpty) { + // Handle the case where verificationId is not provided + Navigator.pop(context); + } startTimer(); } @@ -52,6 +85,52 @@ class _MyVerifyState extends State { super.dispose(); } + Future initNotification() async { + await _firebaseMessaging.requestPermission(); + fCMToken = await _firebaseMessaging.getToken(); + print('Token : $fCMToken'); + sendDeviceToken(fCMToken); + FirebaseMessaging.onBackgroundMessage(handleBackgroundMessage); + } + + Future handleBackgroundMessage(RemoteMessage message) async { + print('Title ${message.notification?.title}'); + print('Body ${message.notification?.body}'); + print('Playload ${message.data}'); + } + + void sendDeviceToken(deviceToken) async { + // Retrieve the passed mobile number value + + try { + final response = await http.post( + Uri.parse(Environment.apiUrl + 'storeFireBase'), + body: json + .encode({'mobile': mobileNumber, 'firebase_token': deviceToken}), + headers: { + HttpHeaders.contentTypeHeader: 'application/json', + 'Authorization': 'Bearer $_token', + }, + ); + + if (response.statusCode == 200) { + Map data = json.decode(response.body); + print('data: $data'); + String status = data['status']; + print(status); + if (status == 'success') { + print('Token Store Successfully'); + } else { + print('Please try again'); + } + } else { + throw Exception('Failed to store'); + } + } catch (e) { + print('Error: $e'); + } + } + void startTimer() { _isTimerRunning = true; _timer = Timer.periodic(Duration(seconds: 1), (Timer timer) { @@ -81,59 +160,68 @@ class _MyVerifyState extends State { // } // } - Future resendOTP(String mobileNumber) async { - print(mobileNumber); - // Update the UI as needed - setState(() { - _secondsRemaining = 30; - _isTimerRunning = true; - }); - startTimer(); - try { - final response = await http.post( - Uri.parse(Environment.apiUrl + 'verifyEmployeeNumber'), - body: json.encode({'mobile_number': mobileNumber}), - headers: { - HttpHeaders.contentTypeHeader: 'application/json', - }, - ); + // Future resendOTP(String mobileNumber) async { + // print(mobileNumber); + // // Update the UI as needed + // setState(() { + // _secondsRemaining = 60; + // _isTimerRunning = true; + // }); + // startTimer(); + // try { + // final response = await http.post( + // Uri.parse(Environment.apiUrl + 'verifyEmployeeNumber'), + // body: json.encode({'mobile_number': mobileNumber}), + // headers: { + // HttpHeaders.contentTypeHeader: 'application/json', + // }, + // ); + // + // if (response.statusCode == 200) { + // // Handle successful response + // ToastHelper.showSuccessToast(context, 'OTP resent successfully'); + // } else { + // // Handle other response status codes + // ToastHelper.showErrorToast(context, 'Failed to resend OTP'); + // throw Exception('Failed to resend OTP'); + // } + // } catch (e) { + // // Handle API call errors + // print('Error: $e'); + // ToastHelper.showErrorToast( + // context, 'Failed to resend OTP. Please try again.'); + // } + // } - if (response.statusCode == 200) { - // Handle successful response - ToastHelper.showSuccessToast(context, 'OTP resent successfully'); - } else { - // Handle other response status codes - ToastHelper.showErrorToast(context, 'Failed to resend OTP'); - throw Exception('Failed to resend OTP'); - } - } catch (e) { - // Handle API call errors - print('Error: $e'); - ToastHelper.showErrorToast( - context, 'Failed to resend OTP. Please try again.'); - } - } - - void verifyOTP(String otp) async { + void generateToken(bool otpVerifyStatus) async { // Retrieve the passed mobile number value - final String mobileNumber = - ModalRoute.of(context)!.settings.arguments as String; + // final String mobileNumber = + // ModalRoute.of(context)!.settings.arguments as String; try { final response = await http.post( Uri.parse(Environment.apiUrl + 'getVerifiedUserData'), - body: json.encode({'mobile_number': mobileNumber, 'otp': otp}), + body: json.encode({ + 'mobile_number': mobileNumber, + 'otp_verification': otpVerifyStatus + }), headers: { HttpHeaders.contentTypeHeader: 'application/json', }, ); if (response.statusCode == 200) { + setState(() { + _isLoading = false; + }); Map data = json.decode(response.body); print('data: $data'); _token = data['data']; String status = data['status']; print(status); if (status == 'success') { + setState(() { + _isLoading = false; + }); final SharedPreferences prefs = await SharedPreferences.getInstance(); prefs.setString('token', data['data']); @@ -164,6 +252,7 @@ class _MyVerifyState extends State { if (isMobilePlatform()) { // if (prefs.containsKey('mpin') && mpinText == 'Mpin - exist') { if (token != null && token.isNotEmpty) { + await initNotification(); Navigator.pushReplacementNamed(context, 'pinSettingPage'); } // } @@ -178,16 +267,25 @@ class _MyVerifyState extends State { } } } else { + setState(() { + _isLoading = false; + }); ToastHelper.showErrorToast(context, 'Invalid OTP. Please try again'); // Show a Snackbar if the OTP is invalid // ToastHelper.showErrorToast(context, 'Invalid OTP. Please try again'); print('Invalid OTP. Please try again'); } } else { + setState(() { + _isLoading = false; + }); ToastHelper.showWarningToast(context, 'Something went wrong'); throw Exception('Failed to verify OTP'); } } catch (e) { + setState(() { + _isLoading = false; + }); print('Error: $e'); ToastHelper.showWarningToast(context, 'Something went wrong'); // Show a Snackbar if there's an error while verifying OTP @@ -197,6 +295,50 @@ class _MyVerifyState extends State { } } + void verifyOTP(String otp) async { + try { + setState(() { + _isLoading = true; + }); + PhoneAuthCredential credential = PhoneAuthProvider.credential( + verificationId: verificationId, + smsCode: otp, + ); + + await FirebaseAuth.instance + .signInWithCredential(credential) + .then((user) async { + if (user != null) { + // Handle successful verification + ToastHelper.showSuccessToast(context, 'Successfully Login...!'); + bool otpVerifyStatus = true; + generateToken(otpVerifyStatus); + } else { + setState(() { + _isLoading = false; + }); + ToastHelper.showErrorToast(context, 'Invalid OTP. Please try again'); + } + }); + } catch (e) { + setState(() { + _isLoading = false; + }); + print('Error: $e'); + ToastHelper.showErrorToast( + context, 'Failed to verify OTP. Please try again.'); + } + } + + void _resendOTP() { + setState(() { + _secondsRemaining = 60; + _isTimerRunning = true; + }); + startTimer(); + widget.onResendCode(widget.mobileNumber, widget.resendToken); + } + Future getClientLogoAndDetails() async { var url = Uri.parse(Environment.apiUrl + 'getClientDetails?client_id=$client_id&emp_code=$empCodeString'); @@ -247,8 +389,8 @@ class _MyVerifyState extends State { @override Widget build(BuildContext context) { // Retrieve the passed mobile number value - final String mobileNumber = - ModalRoute.of(context)!.settings.arguments as String; + // final String mobileNumber = + // ModalRoute.of(context)!.settings.arguments as String; Size _size = MediaQuery.of(context).size; EdgeInsets marginInsets = EdgeInsets.zero; if (Responsive.isDesktop(context)) { @@ -524,7 +666,7 @@ class _MyVerifyState extends State { ) : InkWell( onTap: () { - resendOTP(mobileNumber); + _resendOTP(); }, child: Text( "Resend OTP", diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 224c0fd..815fd52 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -5,6 +5,10 @@ import FlutterMacOS import Foundation +import firebase_auth +import firebase_core +import firebase_messaging +import google_sign_in_ios import path_provider_foundation import shared_preferences_foundation import smart_auth @@ -13,6 +17,10 @@ import url_launcher_macos import video_player_avfoundation func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + FLTFirebaseAuthPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseAuthPlugin")) + FLTFirebaseCorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCorePlugin")) + FLTFirebaseMessagingPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseMessagingPlugin")) + FLTGoogleSignInPlugin.register(with: registry.registrar(forPlugin: "FLTGoogleSignInPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) SmartAuthPlugin.register(with: registry.registrar(forPlugin: "SmartAuthPlugin")) diff --git a/pubspec.yaml b/pubspec.yaml index fc21f24..1a0e895 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -57,6 +57,12 @@ dependencies: path_provider: ^2.1.3 dash_chat_2: ^0.0.21 local_auth: ^2.2.0 + firebase_core: ^3.2.0 + firebase_messaging: ^15.0.3 + firebase_auth: ^5.1.2 + google_sign_in: ^6.2.1 + js: ^0.7.1 + firebase_auth_web: ^5.12.4 dev_dependencies: flutter_test: diff --git a/web/index.html b/web/index.html index 65eedfa..9687444 100644 --- a/web/index.html +++ b/web/index.html @@ -72,6 +72,40 @@
+ + + + + + + + + + + + + + +