diff --git a/.env b/.env deleted file mode 100644 index 1e4e6c0..0000000 --- a/.env +++ /dev/null @@ -1,6 +0,0 @@ -API_URL=https://venbait.in/nhance/dev/employeeRest/ -chatbot_Url=https://venbait.in/nhance/dev/chatbot -API_URL_Enrollment=https://venbait.in/enrollment/employeeRest/ -TICKET_API_URL=https://venbait.in/nhance/dev/api -BASE_HREF=/nhance/app/dev/ -ENV=development \ No newline at end of file diff --git a/.env.development b/.env.development deleted file mode 100644 index 1e4e6c0..0000000 --- a/.env.development +++ /dev/null @@ -1,6 +0,0 @@ -API_URL=https://venbait.in/nhance/dev/employeeRest/ -chatbot_Url=https://venbait.in/nhance/dev/chatbot -API_URL_Enrollment=https://venbait.in/enrollment/employeeRest/ -TICKET_API_URL=https://venbait.in/nhance/dev/api -BASE_HREF=/nhance/app/dev/ -ENV=development \ No newline at end of file diff --git a/.env.production b/.env.production deleted file mode 100644 index b341532..0000000 --- a/.env.production +++ /dev/null @@ -1,6 +0,0 @@ -API_URL=https://app.nhanceindia.in/zenith/employeeRest/ -API_URL_Enrollment=https://app.nhanceindia.in/enrolment/employeeRest/ -TICKET_API_URL=https://app.nhanceindia.in/zenith/api -chatbot_Url=https://app.nhanceindia.in/zenith/chatbot -BASE_HREF=/app/ -ENV=production diff --git a/.env.test b/.env.test deleted file mode 100644 index 3c01a91..0000000 --- a/.env.test +++ /dev/null @@ -1,6 +0,0 @@ -API_URL=https://app.nhanceindia.in/zenith/employeeRest/ -API_URL_Enrollment=https://app.nhanceindia.in/enrolment/employeeRest/ -TICKET_API_URL=https://app.nhanceindia.in/zenith/api -chatbot_Url=https://app.nhanceindia.in/zenith/chatbot -BASE_HREF=/app/ -ENV=production \ No newline at end of file diff --git a/.env.uat b/.env.uat deleted file mode 100644 index 3c01a91..0000000 --- a/.env.uat +++ /dev/null @@ -1,6 +0,0 @@ -API_URL=https://app.nhanceindia.in/zenith/employeeRest/ -API_URL_Enrollment=https://app.nhanceindia.in/enrolment/employeeRest/ -TICKET_API_URL=https://app.nhanceindia.in/zenith/api -chatbot_Url=https://app.nhanceindia.in/zenith/chatbot -BASE_HREF=/app/ -ENV=production \ No newline at end of file diff --git a/android/app/build.gradle b/android/app/build.gradle index 07d735e..f4a69b6 100755 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -19,12 +19,12 @@ if (project.hasProperty('google-services.json')) { def flutterVersionCode = localProperties.getProperty('flutter.versionCode') if (flutterVersionCode == null) { - flutterVersionCode = '44' + flutterVersionCode = '45' } def flutterVersionName = localProperties.getProperty('flutter.versionName') if (flutterVersionName == null) { - flutterVersionName = '2.0.6' + flutterVersionName = '2.0.7' } def keystoreProperties = new Properties() @@ -104,6 +104,23 @@ android { enableSplit = true // Ensures a proper split per architecture } } + + flavorDimensions "app" + productFlavors { + dev { + dimension "app" + // no suffix + } + uat { + dimension "app" + // no suffix + } + prod { + dimension "app" + // no suffix + } + } + } flutter { diff --git a/assets/cardBG.png b/assets/cardBG.png new file mode 100644 index 0000000..962c523 Binary files /dev/null and b/assets/cardBG.png differ diff --git a/assets/nhance.png b/assets/nhance.png new file mode 100644 index 0000000..65a3a7d Binary files /dev/null and b/assets/nhance.png differ diff --git a/assets/retail.png b/assets/retail.png new file mode 100644 index 0000000..8e3d1c3 Binary files /dev/null and b/assets/retail.png differ diff --git a/build_dev_apk.sh b/build_dev_apk.sh deleted file mode 100755 index 3eeb65f..0000000 --- a/build_dev_apk.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/bash - -# Define the path to the .env file for development -ENV_FILE_PATH=".env.development" - -# Check if the .env file exists -if [ ! -f $ENV_FILE_PATH ]; then - echo "$ENV_FILE_PATH does not exist" - exit 1 -fi - -# Copy the .env file for development to .env -cp $ENV_FILE_PATH .env - -# Build the APK for development -flutter build apk --debug - -# Optionally, remove the .env file after the build -# rm .env diff --git a/build_prod_apk.sh b/build_prod_apk.sh deleted file mode 100755 index 9261516..0000000 --- a/build_prod_apk.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/bash - -# Define the path to the .env file for production -ENV_FILE_PATH=".env.production" - -# Check if the .env file exists -if [ ! -f $ENV_FILE_PATH ]; then - echo "$ENV_FILE_PATH does not exist" - exit 1 -fi - -# Copy the .env file for production to .env -cp $ENV_FILE_PATH .env - -# Build the APK for production -flutter build apk --release - -# Optionally, remove the .env file after the build -# rm .env diff --git a/build_test_apk.sh b/build_test_apk.sh deleted file mode 100755 index ff990f3..0000000 --- a/build_test_apk.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/bash - -# Define the path to the .env file for testing -ENV_FILE_PATH=".env.test" - -# Check if the .env file exists -if [ ! -f $ENV_FILE_PATH ]; then - echo "$ENV_FILE_PATH does not exist" - exit 1 -fi - -# Copy the .env file for testing to .env -cp $ENV_FILE_PATH .env - -# Build the APK for testing -flutter build apk --debug - -# Optionally, remove the .env file after the build -# rm .env diff --git a/build_web_dev.sh b/build_web_dev.sh deleted file mode 100755 index b4cdfeb..0000000 --- a/build_web_dev.sh +++ /dev/null @@ -1,40 +0,0 @@ -#!/bin/bash - -# Define the path to the .env.development file -ENV_FILE_PATH=".env.development" - -# Check if the .env.development file exists -if [ ! -f $ENV_FILE_PATH ]; then - echo "$ENV_FILE_PATH does not exist" - exit 1 -fi - -# Copy the .env.development 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" -echo "Loaded ticketToken: $ticketToken" - -# Build the web app for development with base href -flutter build web --release --base-href "$BASE_HREF" - -# Check if the build was successful -if [ $? -ne 0 ]; then - echo "Flutter dev build failed" - exit 1 -fi - -echo "Flutter dev build succeeded" diff --git a/build_web_production.sh b/build_web_production.sh deleted file mode 100755 index 306147d..0000000 --- a/build_web_production.sh +++ /dev/null @@ -1,39 +0,0 @@ -#!/bin/bash - -# Define the path to the .env.production file -ENV_FILE_PATH=".env.production" - -# Check if the .env.production file exists -if [ ! -f $ENV_FILE_PATH ]; then - echo "$ENV_FILE_PATH does not exist" - exit 1 -fi - -# Copy the .env.production 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 production with base href -flutter build web --release --base-href "$BASE_HREF" - -# Check if the build was successful -if [ $? -ne 0 ]; then - echo "Flutter PRODUCTION build failed" - exit 1 -fi - -echo "Flutter PRODUCTION build succeeded" diff --git a/build_web_test.sh b/build_web_test.sh deleted file mode 100755 index b764d8c..0000000 --- a/build_web_test.sh +++ /dev/null @@ -1,39 +0,0 @@ -#!/bin/bash - -# Define the path to the .env.test file -ENV_FILE_PATH=".env.test" - -# Check if the .env.test file exists -if [ ! -f $ENV_FILE_PATH ]; then - echo "$ENV_FILE_PATH does not exist" - exit 1 -fi - -# Copy the .env.test 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 the test environment with base href -flutter build web --release --base-href "$BASE_HREF" - -# Check if the build was successful -if [ $? -ne 0 ]; then - echo "Flutter test build failed" - exit 1 -fi - -echo "Flutter test build succeeded" diff --git a/build_web_uat.sh b/build_web_uat.sh deleted file mode 100755 index 5cd7ad8..0000000 --- a/build_web_uat.sh +++ /dev/null @@ -1,39 +0,0 @@ -#!/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/ios/Runner/Configs/Config.dev.xcconfig b/ios/Runner/Configs/Config.dev.xcconfig new file mode 100644 index 0000000..e69de29 diff --git a/ios/Runner/Configs/Config.prod.xcconfig b/ios/Runner/Configs/Config.prod.xcconfig new file mode 100644 index 0000000..e69de29 diff --git a/ios/Runner/Configs/Config.uat.xcconfig b/ios/Runner/Configs/Config.uat.xcconfig new file mode 100644 index 0000000..e69de29 diff --git a/lib/config/environment.dart b/lib/config/environment.dart new file mode 100644 index 0000000..42c398a --- /dev/null +++ b/lib/config/environment.dart @@ -0,0 +1,69 @@ +// lib/environment.dart +enum Flavor { dev, uat, prod, prod1 } + +class Environment { + static Flavor flavor = Flavor.dev; // overwritten by each main_*.dart + + static bool get isProd => flavor == Flavor.prod || flavor == Flavor.prod1; + + static String get apiUrl { + switch (flavor) { + case Flavor.dev: + return "https://venbait.in/nhance/dev/employeeRest/"; + case Flavor.uat: + return "https://appstage.nhanceindia.in/nexus/employeeRest/"; + case Flavor.prod: + return "https://app.nhanceindia.in/zenith/employeeRest/"; + case Flavor.prod1: + return "https://app.nhanceindia.in/zenith/employeeRest/"; + } + } + + // static String get ticketApiUrl { + // switch (flavor) { + // case Flavor.dev: + // return "https://venbait.in/nhance/dev/api"; + // case Flavor.uat: + // return "https://uat.nhanceindia.in/zenith/api"; + // case Flavor.prod: + // return "https://app.nhanceindia.in/zenith/api"; + // } + // } + + static String get apiUrlEnrollment { + switch (flavor) { + case Flavor.dev: + return "https://venbait.in/enrollment/employeeRest/"; + case Flavor.uat: + return "https://appstage.nhanceindia.in/apex/employeeRest/"; + case Flavor.prod: + return "https://app.nhanceindia.in/enrolment/employeeRest/"; + case Flavor.prod1: + return "https://app.nhanceindia.in/enrolment/employeeRest/"; + } + } + + // static String get chatbotUrl { + // switch (flavor) { + // case Flavor.dev: + // return "https://venbait.in/nhance/dev/chatbot"; + // case Flavor.uat: + // return "https://uat.nhanceindia.in/zenith/chatbot"; + // case Flavor.prod: + // return "https://app.nhanceindia.in/zenith/chatbot"; + // } + // } + + static String get baseHref { + switch (flavor) { + case Flavor.dev: + return "/nhance/app/dev/"; + case Flavor.uat: + return "/"; + case Flavor.prod: + return "/app/"; + case Flavor.prod1: + return "/"; + } + } +} diff --git a/lib/config/main_dev.dart b/lib/config/main_dev.dart new file mode 100644 index 0000000..19e2474 --- /dev/null +++ b/lib/config/main_dev.dart @@ -0,0 +1,9 @@ + +import '../main.dart'; +import 'environment.dart'; + +Future main() async { + Environment.flavor = Flavor.dev; + await startApp(); +} + diff --git a/lib/config/main_prod.dart b/lib/config/main_prod.dart new file mode 100644 index 0000000..44580d0 --- /dev/null +++ b/lib/config/main_prod.dart @@ -0,0 +1,10 @@ + + +import '../main.dart'; +import 'environment.dart'; + +Future main() async { + Environment.flavor = Flavor.prod; + await startApp(); +} + diff --git a/lib/config/main_prod1.dart b/lib/config/main_prod1.dart new file mode 100644 index 0000000..f2eae0f --- /dev/null +++ b/lib/config/main_prod1.dart @@ -0,0 +1,10 @@ + + +import '../main.dart'; +import 'environment.dart'; + +Future main() async { + Environment.flavor = Flavor.prod1; + await startApp(); +} + diff --git a/lib/config/main_uat.dart b/lib/config/main_uat.dart new file mode 100644 index 0000000..1d0484a --- /dev/null +++ b/lib/config/main_uat.dart @@ -0,0 +1,10 @@ + + +import '../main.dart'; +import 'environment.dart'; + +Future main() async { + Environment.flavor = Flavor.uat; + await startApp(); +} + diff --git a/lib/customAppBar/customAppBar.dart b/lib/customAppBar/customAppBar.dart index d9b5b91..9a0a308 100755 --- a/lib/customAppBar/customAppBar.dart +++ b/lib/customAppBar/customAppBar.dart @@ -20,20 +20,37 @@ class _CustomAppBarState extends State { Size get preferredSize => Size.fromHeight(kToolbarHeight); late ApiService apiService; final session = SessionManager(); + bool isRetailLoggedIn = false; @override void initState() { super.initState(); apiService = ApiService(context); + checkRetailOrNot(); } + checkRetailOrNot() async { + final SharedPreferences prefs = await SharedPreferences.getInstance(); + isRetailLoggedIn = prefs.getBool('isRetailLoggedIn') ?? false; + print('isRetailLoggedIn Navbar $isRetailLoggedIn'); + } + + void handleMenuTap(VoidCallback? action) { + if (isRetailLoggedIn) { + + return; + } + action?.call(); + } + + Future logout(BuildContext context) async { final prefs = await SharedPreferences.getInstance(); - final String? token = prefs.getString('_postToken'); + final String? token = prefs.getString('post_token'); if (token != null && token.isNotEmpty) { await prefs.clear(); - context.go('/phone'); + context.go('/login'); // Navigator.pushNamed(context, 'phone'); } } @@ -41,6 +58,7 @@ class _CustomAppBarState extends State { @override Widget build(BuildContext context) { final sw = MediaQuery.of(context).size.width; + return Scaffold( backgroundColor: Color(0xFFFFFCE5), // Set background color for AppBar appBar: PreferredSize( @@ -89,38 +107,42 @@ class _CustomAppBarState extends State { children: [ NavBarItem( text: "Home", - onTap: () { + onTap: () => handleMenuTap(() { context.push('/home'); - }, + }), ), NavBarItem( text: "Claims", - onTap: () { + onTap: () => handleMenuTap(() { context.push('/claims'); - }, + }), ), NavBarItem( text: "Help", - onTap: () { + onTap: () => handleMenuTap(() { context.push('/help'); - }, - ), - NavBarItem( - text: "Wellness", - onTap: () { - // context.push('/wellness'); - // Wellness tab clicked → show popup - PopupHelper.showRedirectPopup( - context: context, - apiService: apiService, - empPrimaryId: session.empPrimaryId, - ); - }, + }), ), + // NavBarItem( + // text: "Wellness", + // onTap: () => handleMenuTap(() { + // PopupHelper.showRedirectPopup( + // context: context, + // apiService: apiService, + // empPrimaryId: session.empPrimaryId, + // ); + // }), + // ), NavBarItem( text: "Profile", - onTap: () { + onTap: () => handleMenuTap(() { context.push('/profile'); + }), + ), + NavBarItem( + text: "Logout", + onTap: () { + logout(context); }, ), ], diff --git a/lib/customAppBar/enrollmentAppBar.dart b/lib/customAppBar/enrollmentAppBar.dart index 6fcc8aa..502b3fe 100755 --- a/lib/customAppBar/enrollmentAppBar.dart +++ b/lib/customAppBar/enrollmentAppBar.dart @@ -9,7 +9,8 @@ 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/environment.dart'; + +import '../config/environment.dart'; import '../models/platform_helper_mobile.dart' if (dart.library.html) '../models/platform_helper_other.dart'; import '../pages/postEnrollment/service/api_service.dart'; @@ -179,107 +180,85 @@ class _CustomAppBarState extends State { preferredSize: widget.preferredSize, child: SafeArea( child: Container( - padding: Responsive.isDesktop(context) - ? EdgeInsets.symmetric(horizontal: 16.0) - : EdgeInsets.symmetric(horizontal: 0), + padding: EdgeInsets.symmetric( + horizontal: MediaQuery.of(context).size.width * + (Responsive.isDesktop(context) ? 0.03 : 0.02), + ), child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ // Logo Column - Expanded( - flex: Responsive.isDesktop(context) ? 3 : 9, - child: Row( - mainAxisAlignment: Responsive.isDesktop(context) - ? MainAxisAlignment.spaceEvenly - : MainAxisAlignment.start, - children: [ - Container( - margin: EdgeInsets.only(top: 10, bottom: 10), - width: Responsive.isDesktop(context) ? 150 : 130, - height: Responsive.isDesktop(context) ? 150 : 130, - child: Image.asset( - 'assets/nhance_client_logo.png', - fit: BoxFit.contain, - ), + Row( + mainAxisAlignment: Responsive.isDesktop(context) + ? MainAxisAlignment.spaceEvenly + : MainAxisAlignment.start, + children: [ + Container( + margin: EdgeInsets.only(top: 10, bottom: 10), + width: 150, + height: 150, + child: Image.asset( + 'assets/nhance_client_logo.png', + fit: BoxFit.contain, ), - ], - ), + ), + ], ), // AdaptiveNavBar Column if (!isTokenAvailable) - 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: "", - onTap: () { - // Navigator.pushNamed(context, 'oldPolicy'); - }, - ), - if (!isTokenAvailable) - NavBarItem( - text: "Logout", - onTap: () async { - logout(context); - }, - ), - ], + Row( + mainAxisSize: MainAxisSize.min, + children: [ + NavBarItem( + text: "Logout", + onTap: () async { + logout(context); + }, ), - ), + ], + ), // if (empStatus == 'enrolled') if (isTokenAvailable) - Expanded( - flex: Responsive.isDesktop(context) ? 9 : 3, - child: AdaptiveNavBar( - screenWidth: sw, - backgroundColor: const Color(0xFFFFFBDE), - leading: - Container(), // Set an empty container as we have the logo separately - title: Text(''), - navBarItems: [ - NavBarItem( - text: "Home", - onTap: () { - context.push('/home'); - }, - ), - NavBarItem( - text: "Claims", - onTap: () { - context.push('/claims'); - }, - ), - NavBarItem( - text: "Help", - onTap: () { - context.push('/help'); - }, - ), - NavBarItem( - text: "Wellness", - onTap: () { - // Wellness tab clicked → show popup - PopupHelper.showRedirectPopup( - context: context, - apiService: apiService, - empPrimaryId: session.empPrimaryId, - ); - }, - ), - NavBarItem( - text: "Profile", - onTap: () { - context.push('/profile'); - }, - ), - ], - ), + Row( + mainAxisSize: MainAxisSize.min, + children: [ + NavBarItem( + text: "Home", + onTap: () { + context.push('/home'); + }, + ), + NavBarItem( + text: "Claims", + onTap: () { + context.push('/claims'); + }, + ), + NavBarItem( + text: "Help", + onTap: () { + context.push('/help'); + }, + ), + NavBarItem( + text: "Wellness", + onTap: () { + // context.push('/wellness'); + // Wellness tab clicked → show popup + PopupHelper.showRedirectPopup( + context: context, + apiService: apiService, + empPrimaryId: session.empPrimaryId, + ); + }, + ), + NavBarItem( + text: "Profile", + onTap: () { + context.push('/profile'); + }, + ), + ], ), ], ), @@ -289,3 +268,33 @@ class _CustomAppBarState extends State { ); } } +class NavBarItem extends StatelessWidget { + final String text; + final VoidCallback? onTap; + + const NavBarItem({ + Key? key, + required this.text, + this.onTap, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return InkWell( + borderRadius: BorderRadius.circular(6), + onTap: onTap, + hoverColor: const Color(0xFFF4F3E7), // subtle hover background + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6), + child: Text( + text, + style: const TextStyle( + fontSize: 14, + fontWeight: FontWeight.w500, + color: Colors.black87, + ), + ), + ), + ); + } +} \ No newline at end of file diff --git a/lib/main.dart b/lib/main.dart index 7e36671..d1325bd 100755 --- a/lib/main.dart +++ b/lib/main.dart @@ -10,6 +10,7 @@ import 'package:nhance_app_pwa/pages/enrollment/addons.dart'; import 'package:nhance_app_pwa/pages/enrollment/empDetails.dart'; import 'package:nhance_app_pwa/pages/enrollment/empReview.dart'; import 'package:nhance_app_pwa/pages/login.dart'; +import 'package:nhance_app_pwa/pages/postEnrollment/AddPolicyScreen.dart'; import 'package:nhance_app_pwa/pages/postEnrollment/botman_chat.dart'; import 'package:nhance_app_pwa/pages/postEnrollment/claimprocess.dart'; import 'package:nhance_app_pwa/pages/postEnrollment/claims.dart'; @@ -21,6 +22,7 @@ import 'package:nhance_app_pwa/pages/postEnrollment/policies.dart'; import 'package:nhance_app_pwa/pages/postEnrollment/privacypolicy.dart'; import 'package:nhance_app_pwa/pages/postEnrollment/profile.dart'; import 'package:nhance_app_pwa/pages/postEnrollment/raisedTicketList.dart'; +import 'package:nhance_app_pwa/pages/postEnrollment/retailClaimForm.dart'; import 'package:nhance_app_pwa/pages/postEnrollment/service/api_service.dart'; import 'package:nhance_app_pwa/pages/postEnrollment/termsofuse.dart'; import 'package:nhance_app_pwa/pages/postEnrollment/tickets.dart'; @@ -37,9 +39,10 @@ import 'package:nhance_app_pwa/pages/setPassword.dart'; import 'package:nhance_app_pwa/pages/verify.dart'; import 'package:shared_preferences/shared_preferences.dart'; -import 'models/environment.dart'; import 'package:flutter/foundation.dart'; import 'package:go_router/go_router.dart'; + +import 'config/environment.dart'; // import 'dart:html' as html; // === Splash (only for mobile) === @@ -118,69 +121,15 @@ Future tokenRedirectLogic( // ✅ Restore session (decode JWT etc.) await SessionManager().restoreSession(); - // ✅ Mobile mpin check - // if (!kIsWeb) { - // final prefs = await SharedPreferences.getInstance(); - // final isMpinSkipped = prefs.getString('is_mpin_skipped'); - // if (isMpinSkipped == '0' && state.uri.toString() != '/pinPage') { - // return '/pinPage'; - // } - // } - return null; // no redirect } -// Future _firebaseMessagingBackgroundHandler(RemoteMessage message) async { -// await Firebase.initializeApp(); -// print("Handling background message: ${message.messageId}"); -// } -Future main() async { +Future startApp() async { WidgetsFlutterBinding.ensureInitialized(); - // final SharedPreferences prefs = await SharedPreferences.getInstance(); - // prefs.clear(); - // if (kIsWeb) { - // await Firebase.initializeApp( - // options: const FirebaseOptions( - // // apiKey: 'AIzaSyC4yHbCX4mQu0jO81pJrDwxKLQlTQWofrc', - // // appId: '1:1084115316849:android:68b14c4ff37f4bbc6c6bd0', - // // messagingSenderId: '1084115316849', - // // projectId: 'nhance-ee8d1' - // apiKey: "AIzaSyC4yHbCX4mQu0jO81pJrDwxKLQlTQWofrc", - // authDomain: "nhance-ee8d1.firebaseapp.com", - // projectId: "nhance-ee8d1", - // storageBucket: "nhance-ee8d1.firebasestorage.app", - // messagingSenderId: "1084115316849", - // appId: "1:1084115316849:web:8fc3b1c886349ae86c6bd0")); - // } else { - // await Firebase.initializeApp(); - // await FirebaseAppCheck.instance.activate( - // // webProvider: ReCaptchaV3Provider('recaptcha-v3-site-key'), - // androidProvider: AndroidProvider.playIntegrity, - // appleProvider: AppleProvider.appAttest, - // ); - // FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler); - // - // FirebaseMessaging messaging = FirebaseMessaging.instance; - // // NotificationSettings settings = await messaging.requestPermission(); - // NotificationSettings settings = - // await FirebaseMessaging.instance.requestPermission( - // alert: true, - // badge: true, - // sound: true, - // ); - // print('User granted permission: ${settings.authorizationStatus}'); - // - // FirebaseMessaging.onMessage.listen((RemoteMessage message) { - // print('Foreground Message: ${message.notification?.title}'); - // }); - // - // FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) { - // print('User tapped on notification'); - // }); - // } - await dotenv.load(fileName: Environment.fileName); + + // await dotenv.load(fileName: Environment.fileName); // Initialize prefs before router final prefs = await SharedPreferences.getInstance(); @@ -195,32 +144,7 @@ Future main() async { GoRoute( path: '/splash', builder: (context, state) => const SplashScreen()), GoRoute(path: '/login', builder: (context, state) => login()), - // GoRoute( - // path: '/verify', - // builder: (context, state) { - // final extras = state.extra as Map?; - // - // return MyVerify( - // verificationId: extras?['verificationId'] ?? '', - // mobileNumber: extras?['mobileNumber'] ?? '', - // resendToken: extras?['resendToken'], - // onResendCode: extras?['onResendCode'], - // ); - // }, - // ), - // GoRoute( - // path: '/chatbot', - // builder: (context, state) { - // final extras = state.extra as Map?; - // return ChatbotWebViewPage( - // client_branch_id: extras?['client_branch_id'] ?? "", - // empCodeString: extras?['empCodeString'] ?? "", - // empName: extras?['empName'] ?? "", - // empPrimaryId: extras?['empPrimaryId'] ?? "", - // client_id: extras?['client_id'] ?? "", - // ); - // }, - // ), + GoRoute( path: '/mailVerify', @@ -233,14 +157,7 @@ Future main() async { }, ), - // GoRoute( - // path: '/mailVerify', - // builder: (context, state) { - // final email = state.extra as String; - // return MyEmailVerify(email: email); - // }, - // ), - // GoRoute(path: '/mailVerify', builder: (context, state) => MyEmailVerify(email: '')), + GoRoute(path: '/pinPage', builder: (context, state) => pinPage()), GoRoute( path: '/pinSettingPage', @@ -261,9 +178,7 @@ Future main() async { GoRoute( path: '/generalExclusionsDeductibles', builder: (context, state) => generalExclusionsDeductibles()), - GoRoute( - path: '/raisedTicketHistory', - builder: (context, state) => raisedTicketHistory()), + GoRoute(path: '/tickets', builder: (context, state) => tickets()), GoRoute( path: '/policies', @@ -288,12 +203,29 @@ Future main() async { }, ), GoRoute( - path: '/tickettracklist', + path: '/retailClaimForm', builder: (context, state) { - final ticketID = state.extra as String; // cast to correct type + final details = state.extra as Map?; + return retailClaimForm(details: details); + }, + ), + GoRoute( + path: '/raisedTicketHistory', + builder: (context, state) => raisedTicketHistory()), + GoRoute( + path: '/tickettracklist/:ticketID', + builder: (context, state) { + final ticketID = state.pathParameters['ticketID']!; return tickettracklist(ticketID: ticketID); }, ), + // GoRoute( + // path: '/tickettracklist', + // builder: (context, state) { + // final ticketID = state.extra as String; // cast to correct type + // return tickettracklist(ticketID: ticketID); + // }, + // ), GoRoute( path: '/empDetails', builder: (context, state) => empDetails(), @@ -303,6 +235,9 @@ Future main() async { GoRoute( path: '/empReviewDetails', builder: (context, state) => empReviewDetails()), + GoRoute( + path: '/AddPolicyScreen', + builder: (context, state) => AddPolicyScreen()), GoRoute( path: '/changePassword', name: 'changePassword', @@ -344,18 +279,7 @@ Future main() async { redirect: (context, state) async => await tokenRedirectLogic(context, state), ); -// ✅ Load API data once at startup -// runApp( -// Builder( -// builder: (context) { -// final apiService = ApiService(context); // ✅ context is available here -// DataManager().init(apiService); -// DataManager().loadAdvertisementImages(); // load once -// -// return MyApp(router: router); -// }, -// ), -// ); + runApp(MyApp(router: router)); } @@ -376,7 +300,7 @@ class _MyAppState extends State { WidgetsBinding.instance.addPostFrameCallback((_) { final apiService = ApiService(context); DataManager().init(apiService); - DataManager().loadAdvertisementImages(); + // DataManager().loadAdvertisementImages(); }); } diff --git a/lib/models/environment.dart b/lib/models/environment.dart deleted file mode 100755 index 541b7e7..0000000 --- a/lib/models/environment.dart +++ /dev/null @@ -1,52 +0,0 @@ -import 'package:flutter_dotenv/flutter_dotenv.dart'; - -class Environment { - static String get fileName { - const String env = - String.fromEnvironment('ENV', defaultValue: 'development'); - - switch (env) { - case 'production': - return '.env.production'; - case 'test': - return '.env.test'; - case 'uat': - return '.env.uat'; - default: - return '.env.development'; - } - } - - static String get apiUrl { - return dotenv.env['API_URL'] ?? 'API_URL not found!'; - } - - static String get apiUrlEnrollment { - return dotenv.env['API_URL_Enrollment'] ?? 'API_URL not found!'; - } - - static String get chatBotUrl { - return dotenv.env['chatbot_Url'] ?? 'CHAT_BOT_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'] ?? 'TICKET_API_URL not found!'; - } - -//Live - static String get ticketToken { - return dotenv.env['ticketToken'] ?? 'TICKET_API_URL not found!'; - } - //Dev - // static String get ticketToken { - // return 'uncp8FvG310bEyYdV9MmStlo7KDRZ65fLWTeXCI2JzwPrNHjBqQhUiAgxsaO'; - // } -} diff --git a/lib/pages/changePassword.dart b/lib/pages/changePassword.dart index 77fec03..c66c339 100644 --- a/lib/pages/changePassword.dart +++ b/lib/pages/changePassword.dart @@ -10,9 +10,9 @@ import 'package:nhance_app_pwa/pages/service/SessionManager.dart'; import 'dart:io'; import 'package:google_fonts/google_fonts.dart'; import 'package:shared_preferences/shared_preferences.dart'; +import '../config/environment.dart'; import '../customAppBar/responsive.dart'; import '../customAppBar/toastHelper.dart'; -import '../models/environment.dart'; class changesPassword extends StatefulWidget { final String email; @@ -91,6 +91,7 @@ class _changesPasswordState extends State { body: json.encode(payload), headers: { HttpHeaders.contentTypeHeader: 'application/json', + 'APP-SIGNATURE': 'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y', }, ); diff --git a/lib/pages/email_verify.dart b/lib/pages/email_verify.dart index 777a6fc..380480e 100755 --- a/lib/pages/email_verify.dart +++ b/lib/pages/email_verify.dart @@ -8,6 +8,7 @@ import 'package:google_fonts/google_fonts.dart'; import 'package:nhance_app_pwa/pages/postEnrollment/service/api_service.dart'; import 'package:nhance_app_pwa/pages/service/SessionManager.dart'; import 'package:nhance_app_pwa/pages/service/TokenService.dart'; +import 'package:nhance_app_pwa/pages/service/data_manager.dart'; import 'package:pinput/pinput.dart'; import 'dart:async'; import 'package:flutter/gestures.dart'; @@ -17,10 +18,10 @@ import 'dart:io'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:jwt_decode/jwt_decode.dart'; +import '../config/environment.dart'; import '../customAppBar/responsive.dart'; import '../customAppBar/toastHelper.dart'; -import '../models/environment.dart'; -import '../models/platform_helper_mobile.dart' + import '../models/platform_helper_mobile.dart' if (dart.library.html) '../models/platform_helper_other.dart'; // import 'dart:html' as html; @@ -75,6 +76,7 @@ class _MyEmailVerifyState extends State { dynamic empMobileNo; dynamic empEmailid; late SessionManager session; + final dataManager = DataManager(); @override void initState() { @@ -165,6 +167,7 @@ class _MyEmailVerifyState extends State { headers: { HttpHeaders.contentTypeHeader: 'application/json', 'Authorization': 'Bearer $_preToken', + 'APP-SIGNATURE': 'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y', }, ); @@ -212,6 +215,7 @@ class _MyEmailVerifyState extends State { body: json.encode(payload), headers: { HttpHeaders.contentTypeHeader: 'application/json', + 'APP-SIGNATURE': 'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y', }, ); print('response : ${response.statusCode}'); @@ -231,6 +235,14 @@ class _MyEmailVerifyState extends State { return; // stop execution } + if (data['status'] == 'OTP is required') { + final SharedPreferences prefs = await SharedPreferences.getInstance(); + prefs.clear(); + ToastHelper.showErrorToast(context, data['message'] ?? 'OTP is required'); + print('API error → ${data['message']}'); + return; // stop execution + } + // --- Extract tokens safely --- String? preToken; if (data['data'] is String) { @@ -327,6 +339,13 @@ class _MyEmailVerifyState extends State { await SessionManager().initializeFromPostToken(post['data']); session = await SessionManager(); + if (session.client_id != null && session.client_id!.isNotEmpty) { + await dataManager.loadSelfEmployeeProfile( + clientId: session.client_id!, + empCode: session.empCodeString!, + clientBranchId: session.empClientBranchId!, + ); + } // // Decode the JWT token received from the API response // Map? decodedToken = Jwt.parseJwt(post['data']); @@ -424,6 +443,8 @@ class _MyEmailVerifyState extends State { // int skipStatus = prefs.getInt('skipStatus') ?? 1; // final mpinText = prefs.getString('mpinText'); // print(isMobilePlatform()); + + if (isMobilePlatform()) { // if (prefs.containsKey('mpin') && mpinText == 'Mpin - exist') { if (_postToken != null && _postToken.isNotEmpty) { @@ -439,10 +460,20 @@ class _MyEmailVerifyState extends State { // } } else { if (_postToken != null && _postToken.isNotEmpty) { - // final SharedPreferences prefs = await SharedPreferences.getInstance(); + final SharedPreferences prefs = await SharedPreferences.getInstance(); // prefs.setString('empEmailid', widget.email); - ToastHelper.showSuccessToast(context, 'Successfully Login'); - checkPassword(context,session.empEmailCorporate,session.client_id,'home'); + ToastHelper.showSuccessToast(context, 'Successfully Logged In'); + if (session.client_id != null && session.client_id!.isNotEmpty) { + prefs.setBool('isRetailLoggedIn', false); + checkPassword(context, session.empEmailCorporate, session.client_id, 'home'); + } else { + if (session.client_id == null || session.client_id!.isEmpty || + session.empCodeString == null || session.empCodeString!.isEmpty || + session.empClientBranchId == null || session.empClientBranchId!.isEmpty) { + prefs.setBool('isRetailLoggedIn', true); + } + context.go('/home'); + } // if (emp_status == 'enrolled' || emp_status == 'active') { // context.go('/home'); // Navigator.pushReplacementNamed(context, 'home'); @@ -498,7 +529,7 @@ class _MyEmailVerifyState extends State { // } } else { if (_preToken != null && _preToken.isNotEmpty) { - ToastHelper.showSuccessToast(context, 'Successfully Login'); + ToastHelper.showSuccessToast(context, 'Successfully Logged In'); checkPassword(context,session.enrollmentEmailCorporate,session.enrollmentClient_id,'empDetails'); // if (emp_status == 'enrolled' || emp_status == 'active') { // Navigator.pushReplacementNamed(context, 'home'); @@ -536,6 +567,7 @@ class _MyEmailVerifyState extends State { body: json.encode(payload), headers: { HttpHeaders.contentTypeHeader: 'application/json', + 'APP-SIGNATURE': 'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y', }, ); @@ -588,6 +620,7 @@ class _MyEmailVerifyState extends State { body: json.encode(params), headers: { HttpHeaders.contentTypeHeader: 'application/json', + 'APP-SIGNATURE': 'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y', }, ); @@ -612,7 +645,7 @@ class _MyEmailVerifyState extends State { } if (_postToken != null && _postToken.isNotEmpty) { - ToastHelper.showSuccessToast(context, 'Successfully Login'); + ToastHelper.showSuccessToast(context, 'Successfully Logged In'); context.go('/home'); // Navigator.pushReplacementNamed(context, 'home'); } else { @@ -660,6 +693,7 @@ class _MyEmailVerifyState extends State { body: json.encode(params), headers: { HttpHeaders.contentTypeHeader: 'application/json', + 'APP-SIGNATURE': 'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y', }, ); @@ -697,6 +731,7 @@ class _MyEmailVerifyState extends State { headers: { 'Authorization': 'Bearer $_preToken', // Add token to the Authorization header + 'APP-SIGNATURE': 'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y', }, ); if (response.statusCode == 200) { @@ -918,7 +953,7 @@ class _MyEmailVerifyState extends State { child: Align( alignment: Responsive .isDesktop(context) - ? Alignment.centerLeft + ? Alignment.center : Alignment .bottomCenter, // Align to the start child: _size.width <= 1100 diff --git a/lib/pages/enrollment/addons.dart b/lib/pages/enrollment/addons.dart index b5c074d..cd02221 100755 --- a/lib/pages/enrollment/addons.dart +++ b/lib/pages/enrollment/addons.dart @@ -62,27 +62,11 @@ class _addOnsDetailsState extends State { dynamic gpaSelfRelationship; dynamic gpaSelfDetails; dynamic gpaECardDownload; - dynamic gpaGstValue; - dynamic gpaSiPremiumValue; - dynamic gpaTotalableValue; - dynamic gmcGstValue; - dynamic gmcSiPremiumValue; - dynamic gmcTotalableValue; - late bool gmcIsValueValid; - dynamic gmcECardDownload; dynamic gmcClintPolicyId; - dynamic gmcMappedFamilyFloaters; dynamic gmcSelfName; dynamic gmcSelfDob; dynamic gmcSelfRelationship; dynamic gmcSelfDetails; - dynamic gmcPolicyName; - dynamic gmcPolicyType; - dynamic gmcNotes; - dynamic gmcSumInsured; - dynamic gmcFloaterTextDescription; - dynamic gmcFloaterTextHeading; - dynamic gmcTypeName; dynamic clientName; dynamic clientLogo; bool gpahasPremiumSummary = false; @@ -128,6 +112,7 @@ class _addOnsDetailsState extends State { dynamic addOnsDependentPolicyType; dynamic addOnsDependentECardDownload; dynamic addOnsDependentIsPremiumSummery; + dynamic addOnsDependentRelationShip; int addOnsDependentOpenForEnrollment = 0; dynamic siValue; dynamic addOnDependentPremiumValue; @@ -760,6 +745,8 @@ class _addOnsDetailsState extends State { addOnsDependentIsPremiumSummery = addOnsDependentPolicies['gmc_dependent_addon']['is_premium_summery']; print('addOnsDependentIsPremiumSummery $addOnsDependentIsPremiumSummery'); + + addOnsDependentRelationShip = addOnsDependentPolicies['gmc_dependent_addon']['relationship']; // if(addOnsDependentIsPremiumSummery == 1){ // print('isPremiumSummery true'); // isPremiumSummery = true; @@ -808,48 +795,78 @@ class _addOnsDetailsState 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 - late DateTime minDate; - late DateTime maxDate; + print(ageValidation); + 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); - minDate = DateTime(now.year - max, now.month, now.day); + final DateTime now = DateTime.now(); - // 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; + maxDate = DateTime(now.year - min, now.month, now.day); + print(maxDate); + minDate = DateTime(now.year - max, now.month, now.day); + print(minDate); - 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), + + 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'); } } Future deleteItem(Map deletedItem) async { try { var id = deletedItem['employee_id']; - final response = await apiService.deleteItemToApi(id); + final response = await apiService.deleteItemToApi(id,'',''); if (response['status'] == 'success') { formDataList.clear(); @@ -1750,172 +1767,127 @@ class _addOnsDetailsState extends State { ), ), ), - if (addOnsDependentOpenForEnrollment != 0) + if (addOnsDependentOpenForEnrollment != 0)...[ if (addOnsDependentECardDownload == null) - if (isValueExist && - floaterData['relationship'] != 'Self') - Expanded( - flex: Responsive.isDesktop(context) ? 1 : 2, - child: Container( - alignment: Alignment.topRight, - child: Column( - mainAxisAlignment: MainAxisAlignment.end, - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - MouseRegion( - cursor: SystemMouseCursors.click, - child: GestureDetector( - onTap: () { - deleteItem(floaterData); - }, - child: Text( - 'Delete', - textAlign: TextAlign.left, - style: GoogleFonts.poppins( - fontSize: - Responsive.isDesktop(context) - ? 16 - : 15, - color: Color( - 0xFFE26728), // Add underline decoration - ), - ), - ), - ) - ], + if (isValueExist && floaterData['relationship'] != 'Self') + MouseRegion( + cursor: SystemMouseCursors.click, + child: GestureDetector( + onTap: () => deleteItem(floaterData), + child: Tooltip( + message: 'Delete', + child: Icon( + Icons.delete, + color: Color(0xFFE26728), + ), ), ), ), - if (addOnsDependentOpenForEnrollment != 0) if (addOnsDependentECardDownload == null) - Expanded( - flex: (isValueExist && - floaterData['relationship'] != 'Self') - ? Responsive.isDesktop(context) - ? 1 - : 2 - : Responsive.isDesktop(context) - ? 2 - : 4, - child: Container( - alignment: Alignment.topRight, - child: Column( - mainAxisAlignment: MainAxisAlignment.end, - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - MouseRegion( - cursor: SystemMouseCursors.click, - child: GestureDetector( - onTap: () { - openForm(floaterData, 'Edit'); - }, - child: Text( - floaterData['relationship'] == 'Self' - ? 'View' - : 'Edit', - textAlign: TextAlign.left, - style: GoogleFonts.poppins( - fontSize: Responsive.isDesktop(context) - ? 16 - : 15, - color: Color( - 0xFFE26728), // Add underline decoration - ), - ), - ), - ) - ], + MouseRegion( + cursor: SystemMouseCursors.click, + child: GestureDetector( + onTap: () { + openAddFamilyMemberPopup('Edit',floaterData); + }, + child: Tooltip( + message: 'Edit', + child: Icon( + Icons.edit, + color: Color(0xFFE26728), + ), ), ), ), + ] ], ), ), ); } else { - familyFloaterContainers.add(MouseRegion( - cursor: SystemMouseCursors.click, // Set cursor to pointer - child: GestureDetector( - onTap: () { - // Handle the click event to open the form - if (addOnsSelectedDependent != null) { - if (addOnsDependentOpenForEnrollment != 0) { - if (addOnsDependentECardDownload == null) { - openForm(floaterData, 'Add'); - } - } - } else { - ToastHelper.showWarningToast( - context, 'Please Select Sum Insured Amount'); - } - }, - child: Container( - decoration: BoxDecoration( - border: Border.all( - color: Color(0xFF000000), - width: 1, - ), - borderRadius: BorderRadius.circular(5), - ), - padding: Responsive.isDesktop(context) - ? EdgeInsets.all(12) - : EdgeInsets.all(10), - child: Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Expanded( - flex: Responsive.isDesktop(context) ? 1 : 4, - child: Container( - alignment: Alignment.centerRight, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Container( - padding: EdgeInsets.all(5), - decoration: BoxDecoration( - color: Color(0xFF6A6A6A), - borderRadius: BorderRadius.circular(5), - ), - child: Icon( - Icons.person_add, - color: Colors.white, - size: 20, - ), - ), - ], - ), - ), - ), - SizedBox(width: Responsive.isDesktop(context) ? 5 : 10), - Expanded( - flex: Responsive.isDesktop(context) ? 11 : 8, - child: Container( - alignment: Alignment.centerLeft, - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - floaterData['button_name'] ?? '', - textAlign: TextAlign.left, - style: GoogleFonts.poppins( - fontSize: - Responsive.isDesktop(context) ? 22 : 18, - fontWeight: FontWeight.w500, - ), - ), - ], - ), - ), - ), - ], - ), - ), // Replace YourChildWidget with the widget you want to wrap - ), - )); + // familyFloaterContainers.add(MouseRegion( + // cursor: SystemMouseCursors.click, // Set cursor to pointer + // child: GestureDetector( + // onTap: () { + // // Handle the click event to open the form + // if (addOnsSelectedDependent != null) { + // if (addOnsDependentOpenForEnrollment != 0) { + // if (addOnsDependentECardDownload == null) { + // openForm(floaterData, 'Add'); + // } + // } + // } else { + // ToastHelper.showWarningToast( + // context, 'Please Select Sum Insured Amount'); + // } + // }, + // child: Container( + // decoration: BoxDecoration( + // border: Border.all( + // color: Color(0xFF000000), + // width: 1, + // ), + // borderRadius: BorderRadius.circular(5), + // ), + // padding: Responsive.isDesktop(context) + // ? EdgeInsets.all(12) + // : EdgeInsets.all(10), + // child: Row( + // crossAxisAlignment: CrossAxisAlignment.start, + // children: [ + // Expanded( + // flex: Responsive.isDesktop(context) ? 1 : 4, + // child: Container( + // alignment: Alignment.centerRight, + // child: Column( + // mainAxisAlignment: MainAxisAlignment.center, + // crossAxisAlignment: CrossAxisAlignment.center, + // children: [ + // Container( + // padding: EdgeInsets.all(5), + // decoration: BoxDecoration( + // color: Color(0xFF6A6A6A), + // borderRadius: BorderRadius.circular(5), + // ), + // child: Icon( + // Icons.person_add, + // color: Colors.white, + // size: 20, + // ), + // ), + // ], + // ), + // ), + // ), + // SizedBox(width: Responsive.isDesktop(context) ? 5 : 10), + // Expanded( + // flex: Responsive.isDesktop(context) ? 11 : 8, + // child: Container( + // alignment: Alignment.centerLeft, + // child: Column( + // mainAxisAlignment: MainAxisAlignment.start, + // crossAxisAlignment: CrossAxisAlignment.start, + // children: [ + // Text( + // floaterData['button_name'] ?? '', + // textAlign: TextAlign.left, + // style: GoogleFonts.poppins( + // fontSize: + // Responsive.isDesktop(context) ? 22 : 18, + // fontWeight: FontWeight.w500, + // ), + // ), + // ], + // ), + // ), + // ), + // ], + // ), + // ), // Replace YourChildWidget with the widget you want to wrap + // ), + // )); } - familyFloaterContainers.add(SizedBox(height: 15)); + familyFloaterContainers.add(SizedBox(height: 10)); } } @@ -1993,40 +1965,36 @@ class _addOnsDetailsState extends State { : CustomBottomNavigationBar( onTabChanged: (index) { // Add your navigation logic here - // For example: + // repositionBotman(); if (index == 0) { - context.go('/home'); - // Navigator.pushNamed(context, 'home'); + context.push('/home'); } else if (index == 1) { - context.go('/claims'); - // Navigator.pushNamed(context, 'claims'); + context.push('/claims'); } else if (index == 2) { - context.go('/profile'); - // Navigator.pushNamed(context, 'profile'); + context.push('/profile'); } else if (index == 3) { - context.go('/help'); - // Navigator.pushNamed(context, 'help'); - } else if (index == 4) { - context.go('/wellness'); - // Navigator.pushNamed(context, 'wellness'); + context.push('/help'); } + // else if (index == 4) { + // // context.push('/wellness'); + // // Wellness tab clicked → show popup + // if(!isRetailLoggedIn) + // PopupHelper.showRedirectPopup( + // context: context, + // apiService: apiService, + // empPrimaryId: session.empPrimaryId, + // ); + // } }, icons: [ Icons.home_outlined, Icons.sticky_note_2_outlined, Icons.person_outline_outlined, Icons.headset_mic_outlined, - Icons.health_and_safety_outlined, + // Icons.health_and_safety_outlined, ], - labels: [ - "Home", - "Claims", - "Profile", - "Help", - "wellness", - ], - initialIndex: - 0, // Initial index of the bottom navigation bar + labels: ["Home", "Claims", "Profile", "Help"], + initialIndex: 0, // Initial index of the bottom navigation bar ), ) ) : PopScope( canPop: false, @@ -2448,10 +2416,47 @@ class _addOnsDetailsState extends State { ], ), SizedBox(height: 20), - if (_addOnsDependentSwitcher == true) + if (_addOnsDependentSwitcher == true)...[ Column( children: familyFloaterContainers, ), + SizedBox(height: 10), + GestureDetector( + onTap: () { + dynamic getFalseObjects = addOnsDependentMappedFamilyFloatersDependent + .where((element) => element['is_value_exist'] == false) + .toList(); + if (getFalseObjects.length == 0) { + ToastHelper.showWarningToast(context, "No family member to add"); + return; + } + openAddFamilyMemberPopup("Add", null); + }, + child: Container( + margin: EdgeInsets.only(top: 10), + padding: EdgeInsets.all(15), + decoration: BoxDecoration( + border: Border.all(color: Colors.black, width: 1), + borderRadius: BorderRadius.circular(8), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon(Icons.person_add, color: Colors.black), + SizedBox(width: 10), + Text( + "Add Family Member", + style: GoogleFonts.poppins( + fontSize: Responsive.isDesktop(context) ? 20 : 16, + fontWeight: FontWeight.w500, + ), + ), + ], + ), + ), + ), + ], + if (_addOnsDependentSwitcher) SizedBox(height: 20), if (Responsive.isDesktop(context) && @@ -2479,7 +2484,7 @@ class _addOnsDetailsState extends State { TextAlign.start, style: GoogleFonts .poppins( - fontSize: 20, + fontSize: Responsive.isDesktop(context) ? 16 : 13, fontWeight: FontWeight .w500, @@ -2494,7 +2499,7 @@ class _addOnsDetailsState extends State { TextAlign.start, style: GoogleFonts .poppins( - fontSize: 18, + fontSize: Responsive.isDesktop(context) ? 14 : 12, ), ), ], @@ -2518,7 +2523,7 @@ class _addOnsDetailsState extends State { TextAlign.start, style: GoogleFonts .poppins( - fontSize: 20, + fontSize: Responsive.isDesktop(context) ? 16 : 13, fontWeight: FontWeight .w500, @@ -2533,7 +2538,7 @@ class _addOnsDetailsState extends State { TextAlign.start, style: GoogleFonts .poppins( - fontSize: 18, + fontSize: Responsive.isDesktop(context) ? 14 : 12, ), ), ], @@ -2557,7 +2562,7 @@ class _addOnsDetailsState extends State { TextAlign.start, style: GoogleFonts .poppins( - fontSize: 20, + fontSize: Responsive.isDesktop(context) ? 16 : 13, fontWeight: FontWeight .w500, @@ -2572,7 +2577,7 @@ class _addOnsDetailsState extends State { TextAlign.start, style: GoogleFonts .poppins( - fontSize: 18, + fontSize: Responsive.isDesktop(context) ? 14 : 12, ), ), ], @@ -2611,7 +2616,7 @@ class _addOnsDetailsState extends State { style: GoogleFonts .poppins( fontSize: - 18, + Responsive.isDesktop(context) ? 16 : 13, fontWeight: FontWeight .w500, @@ -2643,7 +2648,7 @@ class _addOnsDetailsState extends State { style: GoogleFonts .poppins( fontSize: - 16, + Responsive.isDesktop(context) ? 14 : 12, ), ), ], @@ -2675,7 +2680,7 @@ class _addOnsDetailsState extends State { style: GoogleFonts .poppins( fontSize: - 18, + Responsive.isDesktop(context) ? 16 : 13, fontWeight: FontWeight .w500, @@ -2707,7 +2712,7 @@ class _addOnsDetailsState extends State { style: GoogleFonts .poppins( fontSize: - 16, + Responsive.isDesktop(context) ? 14 : 12, ), ), ], @@ -2739,7 +2744,7 @@ class _addOnsDetailsState extends State { style: GoogleFonts .poppins( fontSize: - 18, + Responsive.isDesktop(context) ? 16 : 13, fontWeight: FontWeight .w500, @@ -2771,7 +2776,7 @@ class _addOnsDetailsState extends State { style: GoogleFonts .poppins( fontSize: - 16, + Responsive.isDesktop(context) ? 14 : 12, ), ), ], @@ -3120,7 +3125,7 @@ class _addOnsDetailsState extends State { TextAlign.start, style: GoogleFonts .poppins( - fontSize: 20, + fontSize: Responsive.isDesktop(context) ? 16 : 13, fontWeight: FontWeight.w500, ), @@ -3134,7 +3139,7 @@ class _addOnsDetailsState extends State { TextAlign.start, style: GoogleFonts .poppins( - fontSize: 18, + fontSize: Responsive.isDesktop(context) ? 14 : 12, ), ), ], @@ -3157,7 +3162,7 @@ class _addOnsDetailsState extends State { TextAlign.start, style: GoogleFonts .poppins( - fontSize: 20, + fontSize: Responsive.isDesktop(context) ? 16 : 13, fontWeight: FontWeight.w500, ), @@ -3171,7 +3176,7 @@ class _addOnsDetailsState extends State { TextAlign.start, style: GoogleFonts .poppins( - fontSize: 18, + fontSize: Responsive.isDesktop(context) ? 14 : 12, ), ), ], @@ -3194,7 +3199,7 @@ class _addOnsDetailsState extends State { TextAlign.start, style: GoogleFonts .poppins( - fontSize: 20, + fontSize: Responsive.isDesktop(context) ? 16 : 13, fontWeight: FontWeight.w500, ), @@ -3208,7 +3213,7 @@ class _addOnsDetailsState extends State { TextAlign.start, style: GoogleFonts .poppins( - fontSize: 18, + fontSize: Responsive.isDesktop(context) ? 14 : 12, ), ), ], @@ -3247,7 +3252,7 @@ class _addOnsDetailsState extends State { style: GoogleFonts .poppins( - fontSize: 18, + fontSize: Responsive.isDesktop(context) ? 16 : 13, fontWeight: FontWeight .w500, @@ -3279,7 +3284,7 @@ class _addOnsDetailsState extends State { style: GoogleFonts .poppins( - fontSize: 16, + fontSize: Responsive.isDesktop(context) ? 14 : 12, ), ), ], @@ -3311,7 +3316,7 @@ class _addOnsDetailsState extends State { style: GoogleFonts .poppins( - fontSize: 18, + fontSize: Responsive.isDesktop(context) ? 16 : 13, fontWeight: FontWeight .w500, @@ -3343,7 +3348,7 @@ class _addOnsDetailsState extends State { style: GoogleFonts .poppins( - fontSize: 16, + fontSize: Responsive.isDesktop(context) ? 14 : 12, ), ), ], @@ -3375,7 +3380,7 @@ class _addOnsDetailsState extends State { style: GoogleFonts .poppins( - fontSize: 18, + fontSize: Responsive.isDesktop(context) ? 16 : 13, fontWeight: FontWeight .w500, @@ -3407,7 +3412,7 @@ class _addOnsDetailsState extends State { style: GoogleFonts .poppins( - fontSize: 16, + fontSize: Responsive.isDesktop(context) ? 14 : 12, ), ), ], @@ -3756,7 +3761,7 @@ class _addOnsDetailsState extends State { TextAlign.start, style: GoogleFonts .poppins( - fontSize: 20, + fontSize: Responsive.isDesktop(context) ? 16 : 13, fontWeight: FontWeight.w500, ), @@ -3770,7 +3775,7 @@ class _addOnsDetailsState extends State { TextAlign.start, style: GoogleFonts .poppins( - fontSize: 18, + fontSize: Responsive.isDesktop(context) ? 14 : 12, ), ), ], @@ -3793,7 +3798,7 @@ class _addOnsDetailsState extends State { TextAlign.start, style: GoogleFonts .poppins( - fontSize: 20, + fontSize: Responsive.isDesktop(context) ? 16 : 13, fontWeight: FontWeight.w500, ), @@ -3807,7 +3812,7 @@ class _addOnsDetailsState extends State { TextAlign.start, style: GoogleFonts .poppins( - fontSize: 18, + fontSize: Responsive.isDesktop(context) ? 14 : 12, ), ), ], @@ -3830,7 +3835,7 @@ class _addOnsDetailsState extends State { TextAlign.start, style: GoogleFonts .poppins( - fontSize: 20, + fontSize: Responsive.isDesktop(context) ? 16 : 13, fontWeight: FontWeight.w500, ), @@ -3844,7 +3849,7 @@ class _addOnsDetailsState extends State { TextAlign.start, style: GoogleFonts .poppins( - fontSize: 18, + fontSize: Responsive.isDesktop(context) ? 14 : 12, ), ), ], @@ -3883,7 +3888,7 @@ class _addOnsDetailsState extends State { style: GoogleFonts .poppins( - fontSize: 18, + fontSize: Responsive.isDesktop(context) ? 16 : 13, fontWeight: FontWeight .w500, @@ -3915,7 +3920,7 @@ class _addOnsDetailsState extends State { style: GoogleFonts .poppins( - fontSize: 16, + fontSize: Responsive.isDesktop(context) ? 14 : 12, ), ), ], @@ -3947,7 +3952,7 @@ class _addOnsDetailsState extends State { style: GoogleFonts .poppins( - fontSize: 18, + fontSize: Responsive.isDesktop(context) ? 16 : 13, fontWeight: FontWeight .w500, @@ -3979,7 +3984,7 @@ class _addOnsDetailsState extends State { style: GoogleFonts .poppins( - fontSize: 16, + fontSize: Responsive.isDesktop(context) ? 14 : 12, ), ), ], @@ -4011,7 +4016,7 @@ class _addOnsDetailsState extends State { style: GoogleFonts .poppins( - fontSize: 18, + fontSize: Responsive.isDesktop(context) ? 16 : 13, fontWeight: FontWeight .w500, @@ -4043,7 +4048,7 @@ class _addOnsDetailsState extends State { style: GoogleFonts .poppins( - fontSize: 16, + fontSize: Responsive.isDesktop(context) ? 14 : 12, ), ), ], @@ -4131,7 +4136,7 @@ class _addOnsDetailsState extends State { padding: EdgeInsets.all(8.0), child: Text( - 'Policy ', + 'Policy', textAlign: TextAlign.start, style: GoogleFonts.poppins( @@ -4619,6 +4624,225 @@ class _addOnsDetailsState extends State { } } + void openAddFamilyMemberPopup(String action, Map? floaterData) { + print('gmcRelationShip : $addOnsDependentRelationShip'); + print('editGmcRelationShip : $floaterData'); + + Map? selectedFloaterData; + String? dropdownValue; + + // ============================ + // NORMALIZE RELATIONSHIP LIST + // ============================ + + List> relationshipObjects = []; + + // Case 1: List → convert to full object + if (addOnsDependentRelationShip.isNotEmpty && addOnsDependentRelationShip.first is String) { + relationshipObjects = addOnsDependentRelationShip.map((r) { + return { + "relationship": r, + "client_policy_id":r, + "age_validation": r, // default fallback + }; + }).toList(); + } + + // Case 2: List> + else if (addOnsDependentRelationShip.isNotEmpty && addOnsDependentRelationShip.first is Map) { + relationshipObjects = List>.from(addOnsDependentRelationShip); + } + + print("Normalized relationship list: $relationshipObjects"); + + // Reset UI Fields + _relationShipController.clear(); + _memberNameController.clear(); + _dobController.clear(); + + // ============================ + // EDIT MODE + // ============================ + if (action == "Edit" && floaterData != null) { + String rel = floaterData["relationship"]; + + dropdownValue = rel; + + // Check if the relationship exists in normalized list + var exists = relationshipObjects.any((e) => e["relationship"] == rel); + + // If not found (rare case), insert it for dropdown display + if (!exists) { + relationshipObjects.insert(0, { + "relationship": rel, + "client_policy_id":floaterData["client_policy_id"], + "employee_id":floaterData["employee_id"], + "age_validation": floaterData["age_validation"] ?? {"min": "0", "max": "99"}, + }); + } + + selectedFloaterData = relationshipObjects.firstWhere( + (e) => e["relationship"] == rel, + orElse: () => { + "relationship": rel, + "client_policy_id":floaterData["client_policy_id"], + "employee_id":floaterData["employee_id"], + "age_validation": floaterData["age_validation"] ?? {"min": "0", "max": "99"}, + }); + + _relationShipController.text = rel; + _memberNameController.text = floaterData["name"]; + _dobController.text = floaterData["dob"]; + } + + // ============================ + // SHOW POPUP + // ============================ + showDialog( + context: context, + barrierDismissible: false, + builder: (context) { + return AlertDialog( + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)), + content: SizedBox( + width: Responsive.isDesktop(context) ? 500 : 350, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + action == "Edit" ? "Edit Family Member" : "Add Family Member", + style: GoogleFonts.poppins( + fontSize: 18, + fontWeight: FontWeight.bold, + color: Color(0xFFE26728), + ), + ), + SizedBox(height: 20), + + // RELATIONSHIP DROPDOWN + DropdownButtonFormField( + value: dropdownValue, + decoration: InputDecoration( + border: OutlineInputBorder(), + labelText: "Relationship", + ), + items: relationshipObjects.map((item) { + return DropdownMenuItem( + value: item["relationship"], + child: Text(item["relationship"]), + ); + }).toList(), + + onChanged: action == "Edit" + ? null + : (value) { + dropdownValue = value!; + _relationShipController.text = value; + + // assign selected relationship FULL OBJECT + selectedFloaterData = relationshipObjects.firstWhere( + (item) => item["relationship"] == value); + print('selectedFloaterData1234 $selectedFloaterData'); + }, + ), + + SizedBox(height: 15), + + // FULL NAME FIELD + TextFormField( + controller: _memberNameController, + decoration: InputDecoration( + border: OutlineInputBorder(), + labelText: "Member Full Name", + ), + ), + + SizedBox(height: 15), + + // DOB → passes FULL SELECTED OBJECT + TextFormField( + controller: _dobController, + readOnly: true, + onTap: () { + _selectDate(context, selectedFloaterData ?? floaterData); + }, + decoration: InputDecoration( + border: OutlineInputBorder(), + labelText: "Date of Birth", + suffixIcon: IconButton( + icon: Icon(Icons.calendar_today), + onPressed: () { + _selectDate(context, selectedFloaterData ?? floaterData); + }, + ), + ), + ), + + SizedBox(height: 20), + + // ACTION BUTTONS + Row( + children: [ + Expanded( + child: OutlinedButton( + onPressed: () => Navigator.pop(context), + child: Text("Cancel"), + ), + ), + SizedBox(width: 10), + Expanded( + child: ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: Color(0xFFE26728), + ), + onPressed: () { + if (_relationShipController + .text.isEmpty || + _memberNameController + .text.isEmpty || + _dobController.text.isEmpty) { + ToastHelper.showWarningToast(context, + 'All fields are required'); + } else { + // Create a map to hold the form data + Map formData = { + 'relationship': + _relationShipController.text, + 'memberName': + _memberNameController.text, + 'dateOfBirth': _dobController.text, + // Add more form fields as needed + }; + + // Call the function to send form data to API + saveAddOnsDetails( + formData, + selectedFloaterData ?? floaterData ?? {}, + action + ); + + // Close the dialog + + context.pop(); + // Navigator.of(context).pop(); + } + }, + child: Text( + "Save", + style: TextStyle(color: Colors.white), + ), + ), + ), + ], + ), + ], + ), + ), + ); + }, + ); + } + void openForm(Map floaterData, action) { dynamic popupName = floaterData['button_name']; // Check if the action is 'Edit' @@ -4910,40 +5134,53 @@ class _addOnsDetailsState extends State { ); } + double toDoubleSafe(dynamic value) { + if (value == null) return 0.0; + if (value is num) return value.toDouble(); + if (value is String) return double.tryParse(value) ?? 0.0; + return 0.0; + } + + List generateGmcCards(List data) { List cards = []; for (var item in data) { - setState(() { - gmcPolicyName = item['Policy_Name']; - gmcPolicyType = item['type']; - gmcFloaterTextHeading = item['floter_text_heading']; - gmcFloaterTextDescription = item['floter_text_description']; - gmcNotes = item['notes']; - gmcECardDownload = item['eCardDownload']; - gmcGstValue = item['family_floaters_of_dependent_and_gst_value']; - gmcSiPremiumValue = - item['family_floaters_of_dependent_and_si_premium_value']; - gmcTotalableValue = gmcGstValue + gmcSiPremiumValue; - gmcIsValueValid = (gmcSiPremiumValue != 0 && gmcGstValue != 0); - // gmcSumInsured = item['Policy_Terms']['sum_insured']; - gmcMappedFamilyFloaters = item['mapped_family_floaters']; - print('gmcMappedFamilyFloaters checking $gmcMappedFamilyFloaters'); - dynamic getTrueObjects = gmcMappedFamilyFloaters + + String gmcPolicyName = item['Policy_Name'] ?? ''; + String gmcPolicyType = item['type'] ?? ''; + String gmcFloaterTextHeading = item['floter_text_heading'] ?? ''; + String gmcFloaterTextDescription = item['floter_text_description'] ?? ''; + String gmcNotes = item['notes'] ?? ''; + String gmcECardDownload = item['eCardDownload'] ?? ''; + + double gmcGstValue = toDoubleSafe(item['family_floaters_of_dependent_and_gst_value']); + double gmcSiPremiumValue = toDoubleSafe(item['family_floaters_of_dependent_and_si_premium_value']); + + + double gmcTotalableValue = gmcSiPremiumValue + gmcGstValue; + + bool gmcIsValueValid = (gmcSiPremiumValue != 0 && gmcGstValue != 0); + + List gmcMappedFamilyFloaters = item['mapped_family_floaters'] ?? []; + + // get true objects + List trueObjects = gmcMappedFamilyFloaters .where((element) => element['is_value_exist'] == true) .toList(); - if (getTrueObjects.length > 0) { - gmcSumInsured = gmcMappedFamilyFloaters[0]["data"]["basic_cover_si"]; - } else { - gmcSumInsured = item['Policy_Terms']['sum_insured']; - } - gmcTypeName = item['type']; - }); - dynamic getTrueObjects = gmcMappedFamilyFloaters - .where((element) => element['is_value_exist'] == true) - .toList(); - Widget card = getTrueObjects.length > 0 + double gmcSumInsured; + + if (trueObjects.isNotEmpty) { + gmcSumInsured = toDoubleSafe(trueObjects[0]["data"]["basic_cover_si"]); + } else { + gmcSumInsured = toDoubleSafe(item['Policy_Terms']?['sum_insured']); + + } + + String gmcTypeName = gmcPolicyType; + + Widget card = trueObjects.length > 0 ? Card( elevation: 0, color: Colors.white, @@ -5111,7 +5348,7 @@ class _addOnsDetailsState extends State { 'Additional Premium', textAlign: TextAlign.start, style: GoogleFonts.poppins( - fontSize: 20, + fontSize: Responsive.isDesktop(context) ? 16 : 13, fontWeight: FontWeight.w500, ), ), @@ -5119,7 +5356,7 @@ class _addOnsDetailsState extends State { (gmcSiPremiumValue ?? '').toString(), textAlign: TextAlign.start, style: GoogleFonts.poppins( - fontSize: 18, + fontSize: Responsive.isDesktop(context) ? 14 : 12, ), ), ], @@ -5137,7 +5374,7 @@ class _addOnsDetailsState extends State { 'GST', textAlign: TextAlign.start, style: GoogleFonts.poppins( - fontSize: 20, + fontSize: Responsive.isDesktop(context) ? 16 : 13, fontWeight: FontWeight.w500, ), ), @@ -5145,7 +5382,7 @@ class _addOnsDetailsState extends State { (gmcGstValue ?? '').toString(), textAlign: TextAlign.start, style: GoogleFonts.poppins( - fontSize: 18, + fontSize: Responsive.isDesktop(context) ? 14 : 12, ), ), ], @@ -5163,7 +5400,7 @@ class _addOnsDetailsState extends State { 'Total Payable', textAlign: TextAlign.start, style: GoogleFonts.poppins( - fontSize: 20, + fontSize: Responsive.isDesktop(context) ? 16 : 13, fontWeight: FontWeight.w500, ), ), @@ -5171,7 +5408,7 @@ class _addOnsDetailsState extends State { (gmcTotalableValue ?? '').toString(), textAlign: TextAlign.start, style: GoogleFonts.poppins( - fontSize: 18, + fontSize: Responsive.isDesktop(context) ? 14 : 12, ), ), ], @@ -5200,7 +5437,7 @@ class _addOnsDetailsState extends State { 'Additional Premium', textAlign: TextAlign.start, style: GoogleFonts.poppins( - fontSize: 18, + fontSize: Responsive.isDesktop(context) ? 16 : 13, fontWeight: FontWeight.w500, ), ), @@ -5221,7 +5458,7 @@ class _addOnsDetailsState extends State { .toString(), textAlign: TextAlign.start, style: GoogleFonts.poppins( - fontSize: 16, + fontSize: Responsive.isDesktop(context) ? 14 : 12, ), ), ], @@ -5245,7 +5482,7 @@ class _addOnsDetailsState extends State { 'GST', textAlign: TextAlign.start, style: GoogleFonts.poppins( - fontSize: 18, + fontSize: Responsive.isDesktop(context) ? 16 : 13, fontWeight: FontWeight.w500, ), ), @@ -5265,7 +5502,7 @@ class _addOnsDetailsState extends State { (gmcGstValue ?? '').toString(), textAlign: TextAlign.start, style: GoogleFonts.poppins( - fontSize: 16, + fontSize: Responsive.isDesktop(context) ? 14 : 12, ), ), ], @@ -5289,7 +5526,7 @@ class _addOnsDetailsState extends State { 'Total Payable', textAlign: TextAlign.start, style: GoogleFonts.poppins( - fontSize: 18, + fontSize: Responsive.isDesktop(context) ? 16 : 13, fontWeight: FontWeight.w500, ), ), @@ -5310,7 +5547,7 @@ class _addOnsDetailsState extends State { .toString(), textAlign: TextAlign.start, style: GoogleFonts.poppins( - fontSize: 16, + fontSize: Responsive.isDesktop(context) ? 14 : 12, ), ), ], @@ -5337,27 +5574,47 @@ class _addOnsDetailsState extends State { String? gpaPolicyType = item['type']; String? gpaECardDownload = item['eCardDownload']; String? gpaSumInsured; - gpaGstValue = item['si_gst_value']; - gpaSiPremiumValue = item['si_premium_value']; - gpaTotalableValue = gpaSiPremiumValue + gpaGstValue; - bool isValueValid = (gpaSiPremiumValue != 0 && gpaGstValue != 0); - List gpaMappedFamilyFloaters = []; + // Safe numeric conversion + double gpaGstValue = toDoubleSafe(item['si_gst_value']); + double gpaSiPremiumValue = toDoubleSafe(item['si_premium_value']); - if (item['mapped_family_floaters'] != null) { - gpaMappedFamilyFloaters = [item['mapped_family_floaters']]; - } + double gpaTotalableValue = gpaSiPremiumValue + gpaGstValue; - dynamic getTrueObjects = gpaMappedFamilyFloaters + bool isValueValid = gpaSiPremiumValue != 0 && gpaGstValue != 0; + + // ------------------------------- +// FIX: mapped_family_floaters is ALREADY a list +// ------------------------------- + List gpaMappedFamilyFloaters = + item['mapped_family_floaters'] is List + ? item['mapped_family_floaters'] + : []; + +// ------------------------------- +// FIX: get true objects +// ------------------------------- + List getTrueObjects = gpaMappedFamilyFloaters .where((element) => element['is_value_exist'] == true) .toList(); - if (getTrueObjects.length > 0) { +// ------------------------------- +// FIX: safe Sum Insured check +// ------------------------------- + if (getTrueObjects.isNotEmpty) { gpaSumInsured = getTrueObjects[0]["data"]["basic_cover_si"]; } else { - gpaSumInsured = item['Policy_Terms']['sumInsured2']; + gpaSumInsured = item['Policy_Terms']?['sumInsured2']; } - int gpaOpenForEnrollment = int.parse(item['OpenForEnrollment']); +// ------------------------------- +// FIX: Safe parsing of OpenForEnrollment +// backend could send "1", 1, null +// ------------------------------- + int gpaOpenForEnrollment = 0; + dynamic enroll = item['OpenForEnrollment']; + + if (enroll is int) gpaOpenForEnrollment = enroll; + else if (enroll is String) gpaOpenForEnrollment = int.tryParse(enroll) ?? 0; List familyFloaterContainers = []; for (var floater in gpaMappedFamilyFloaters) { @@ -5526,7 +5783,7 @@ class _addOnsDetailsState extends State { 'Additional Premium', textAlign: TextAlign.start, style: GoogleFonts.poppins( - fontSize: 20, + fontSize: Responsive.isDesktop(context) ? 16 : 13, fontWeight: FontWeight.w500, ), ), @@ -5534,7 +5791,7 @@ class _addOnsDetailsState extends State { (gpaSiPremiumValue ?? '').toString(), textAlign: TextAlign.start, style: GoogleFonts.poppins( - fontSize: 18, + fontSize: Responsive.isDesktop(context) ? 14 : 12, ), ), ], @@ -5551,7 +5808,7 @@ class _addOnsDetailsState extends State { 'GST', textAlign: TextAlign.start, style: GoogleFonts.poppins( - fontSize: 20, + fontSize: Responsive.isDesktop(context) ? 16 : 13, fontWeight: FontWeight.w500, ), ), @@ -5559,7 +5816,7 @@ class _addOnsDetailsState extends State { (gpaGstValue ?? '').toString(), textAlign: TextAlign.start, style: GoogleFonts.poppins( - fontSize: 18, + fontSize: Responsive.isDesktop(context) ? 14 : 12, ), ), ], @@ -5576,7 +5833,7 @@ class _addOnsDetailsState extends State { 'Total Payable', textAlign: TextAlign.start, style: GoogleFonts.poppins( - fontSize: 20, + fontSize: Responsive.isDesktop(context) ? 16 : 13, fontWeight: FontWeight.w500, ), ), @@ -5584,7 +5841,7 @@ class _addOnsDetailsState extends State { (gpaTotalableValue ?? '').toString(), textAlign: TextAlign.start, style: GoogleFonts.poppins( - fontSize: 18, + fontSize: Responsive.isDesktop(context) ? 14 : 12, ), ), ], @@ -5612,7 +5869,7 @@ class _addOnsDetailsState extends State { 'Additional Premium', textAlign: TextAlign.start, style: GoogleFonts.poppins( - fontSize: 18, + fontSize: Responsive.isDesktop(context) ? 16 : 13, fontWeight: FontWeight.w500, ), ), @@ -5631,7 +5888,7 @@ class _addOnsDetailsState extends State { (gpaSiPremiumValue ?? '').toString(), textAlign: TextAlign.start, style: GoogleFonts.poppins( - fontSize: 16, + fontSize: Responsive.isDesktop(context) ? 14 : 12, ), ), ], @@ -5654,7 +5911,7 @@ class _addOnsDetailsState extends State { 'GST', textAlign: TextAlign.start, style: GoogleFonts.poppins( - fontSize: 18, + fontSize: Responsive.isDesktop(context) ? 16 : 13, fontWeight: FontWeight.w500, ), ), @@ -5673,7 +5930,7 @@ class _addOnsDetailsState extends State { (gpaGstValue ?? '').toString(), textAlign: TextAlign.start, style: GoogleFonts.poppins( - fontSize: 16, + fontSize: Responsive.isDesktop(context) ? 14 : 12, ), ), ], @@ -5696,7 +5953,7 @@ class _addOnsDetailsState extends State { 'Total Payable', textAlign: TextAlign.start, style: GoogleFonts.poppins( - fontSize: 18, + fontSize: Responsive.isDesktop(context) ? 16 : 13, fontWeight: FontWeight.w500, ), ), @@ -5715,7 +5972,7 @@ class _addOnsDetailsState extends State { (gpaTotalableValue ?? '').toString(), textAlign: TextAlign.start, style: GoogleFonts.poppins( - fontSize: 16, + fontSize: Responsive.isDesktop(context) ? 14 : 12, ), ), ], diff --git a/lib/pages/enrollment/empDetails.dart b/lib/pages/enrollment/empDetails.dart index fb186da..e5385b1 100755 --- a/lib/pages/enrollment/empDetails.dart +++ b/lib/pages/enrollment/empDetails.dart @@ -49,30 +49,15 @@ class _empDetailsState extends State { dynamic gpaOpenForEnrollment; dynamic gpaECardDownload; dynamic gpaSumInsured; - dynamic gpaGstValue; - dynamic gpaSiPremiumValue; - dynamic gpaTotalableValue; - dynamic gmcECardDownload; - dynamic gmcGstValue; - dynamic gmcSiPremiumValue; - dynamic gmcTotalableValue; - late bool gmcIsValueValid; - int gmcOpenForEnrollment = 0; dynamic gmcPolicies; dynamic gmcSlabRates = []; - dynamic gmcMappedFamilyFloaters = []; + dynamic getFalseObjects = []; dynamic gmcSelfName; dynamic gmcSelfDob; dynamic gmcSelfRelationship; dynamic gmcSelfDetails; - dynamic gmcPolicyName; - dynamic gmcPolicyType; - dynamic gmcNotes; + dynamic gmcGridMaster; - dynamic gmcSumInsured; - dynamic gmcFloaterTextDescription; - dynamic gmcFloaterTextHeading; - dynamic gmcTypeName; dynamic clientName; dynamic clientLogo; late TextEditingController _relationShipController; @@ -411,8 +396,10 @@ class _empDetailsState extends State { for (var item in response['data']) { String relationshipName = item['relationship_name']; String dependent = item['dependent']; + final id = item["id"]; - relationshipOptions.add({ + relationshipOptions.add({ + "id": id, 'relationship_name': relationshipName, 'dependent': dependent, }); @@ -504,11 +491,11 @@ class _empDetailsState extends State { return formattedDate; } - Future deleteItem(Map deletedItem) async { + Future deleteItem(Map deletedItem,copyStatus,gmcClientPolicyId) async { print(deletedItem); try { var id = deletedItem['employee_id']; - final response = await apiService.deleteItemToApi(id); + final response = await apiService.deleteItemToApi(id,copyStatus,gmcClientPolicyId); // Check if the request was successful (status code 200) if (response['status'] == 'success') { @@ -536,7 +523,7 @@ class _empDetailsState extends State { } void saveFamilyMemberDetails(Map formData, - Map floatedData, action, gmcSumInsured) async { + Map floatedData, String action, gmcSumInsured) async { // Construct the array of objects print(formData); print('floatedData $floatedData'); @@ -591,6 +578,12 @@ class _empDetailsState extends State { ToastHelper.showSuccessToast(context, response['message']); print('Form data sent successfully.'); } else { + formDataList.clear(); + selectedRelationships.clear(); + _relationShipController.clear(); + _memberNameController.clear(); + _dobController.clear(); + relationshipOptions.clear(); // Request failed, handle error ToastHelper.showErrorToast(context, response['message']); print('Failed to send form data. Error: ${response}'); @@ -1105,317 +1098,268 @@ class _empDetailsState extends State { } } - void openForm(Map floaterData, action, gmcSumInsured) { - print(action); - print(floaterData); - print(relationshipOptions); - print('test'); - // Check if the action is 'Edit' - dynamic popupName = floaterData['button_name']; + // List> getMatchedRelationships() { + // // 1. Get all empty floater slots + // List available = gmcMappedFamilyFloaters + // .where((e) => e['is_value_exist'] == false) + // .map((e) => e['data']) + // .toList(); + // + // List> matched = []; + // + // for (var item in relationshipOptions) { + // String? dependent = item["dependent"]; + // + // bool exists = available.any((floater) => floater["form_type"] == dependent); + // + // if (exists) { + // matched.add({ + // "id": item["id"], + // "relationship_name": item["relationship_name"], + // "dependent": dependent, + // }); + // } + // } + // + // return matched; + // } - selectedRelationships = relationshipOptions - .where( - (option) => option['dependent'] == floaterData['form_type'], - ) - .toList(); + void openAddFamilyMemberPopup( + String action, + Map? floaterData, + String clientPolicyId, + List relationshipList, + dynamic sumInsured, + ) { + print('gmcRelationShip : $relationshipList'); + print('editGmcRelationShip : $floaterData'); - // Set the dropdown value based on the form_type + Map? selectedFloaterData; String? dropdownValue; - if (floaterData['form_type'] == 'spouse') { - dropdownValue = 'Spouse'; - _relationShipController.text = 'Spouse'; + + // ============================ + // NORMALIZE RELATIONSHIP LIST + // ============================ + + List> relationshipObjects = []; + + // Case 1: List → convert to full object + if (relationshipList.isNotEmpty && relationshipList.first is String) { + relationshipObjects = relationshipList.map((r) { + return { + "relationship": r, + "client_policy_id":r, + "age_validation": {"min": "0", "max": "99"}, // default fallback + }; + }).toList(); } - print(selectedRelationships); - print(gmcMappedFamilyFloaters); - if (action == 'Add') { - if (floaterData['form_type'] == 'parent_in_law' || - floaterData['form_type'] == 'parent') { - print('Add'); - // Filter the addOnsDependentMappedFamilyFloatersDependent list to get only the objects where is_value_exist is true - dynamic getTrueObjects = gmcMappedFamilyFloaters - .where((element) => element['is_value_exist'] == true) - .toList(); + // Case 2: List> + else if (relationshipList.isNotEmpty && relationshipList.first is Map) { + relationshipObjects = List>.from(relationshipList); + } - print(getTrueObjects); + print("Normalized relationship list: $relationshipObjects"); - List> getTrueDataObjects = getTrueObjects - .map>( - (element) => element['data'] as Map) - .toList(); + // Reset UI Fields + _relationShipController.clear(); + _memberNameController.clear(); + _dobController.clear(); - print(getTrueDataObjects); + // ============================ + // EDIT MODE + // ============================ + if (action == "Edit" && floaterData != null) { + print('Edit Floater'); + print(floaterData); - selectedRelationships.removeWhere((relationship) => - getTrueDataObjects.any((dataObject) => - dataObject['relationship'] == - relationship['relationship_name'])); + dropdownValue = floaterData["relationship"]; - print(selectedRelationships); + // Use REAL floaterData as selected data (IMPORTANT) + selectedFloaterData = floaterData; + + // Insert into dropdown list only if needed + if (!relationshipObjects.any((e) => e["relationship"] == dropdownValue)) { + relationshipObjects.insert(0, { + "relationship": dropdownValue, + "client_policy_id": floaterData["client_policy_id"], + }); } - } else { - // Set values to all fields - _relationShipController.text = floaterData['relationship'] ?? ''; - _memberNameController.text = floaterData['name'] ?? ''; - _dobController.text = floaterData['dob'] ?? ''; - // Add more fields as needed - } + _relationShipController.text = floaterData["relationship"]; + _memberNameController.text = floaterData["name"]; + _dobController.text = floaterData["dob"]; + } + // if (action == "Edit" && floaterData != null) { + // print('Edit Floater'); + // print(floaterData); + // String rel = floaterData["relationship"]; + // + // dropdownValue = rel; + // + // // Check if the relationship exists in normalized list + // var exists = relationshipObjects.any((e) => e["relationship"] == rel); + // + // // If not found (rare case), insert it for dropdown display + // if (!exists) { + // relationshipObjects.insert(0, { + // "relationship": rel, + // "client_policy_id":floaterData["client_policy_id"], + // "employee_id":floaterData["employee_id"], + // "age_validation": floaterData["age_validation"] ?? {"min": "0", "max": "99"}, + // }); + // } + // + // selectedFloaterData = relationshipObjects.firstWhere( + // (e) => e["relationship"] == rel, + // orElse: () => { + // "relationship": rel, + // "client_policy_id":floaterData["client_policy_id"], + // "employee_id":floaterData["employee_id"], + // "age_validation": floaterData["age_validation"] ?? {"min": "0", "max": "99"}, + // }); + // + // _relationShipController.text = rel; + // _memberNameController.text = floaterData["name"]; + // _dobController.text = floaterData["dob"]; + // } + + // ============================ + // SHOW POPUP + // ============================ showDialog( context: context, barrierDismissible: false, - builder: (BuildContext context) { + builder: (context) { return AlertDialog( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(30.0), - ), - elevation: 0, - backgroundColor: Colors.transparent, + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)), content: SizedBox( - width: Responsive.isDesktop(context) ? 500 : 800, - child: SingleChildScrollView( - child: Card( - color: Colors.white, - elevation: 0, - child: Padding( - padding: Responsive.isDesktop(context) - ? EdgeInsets.all(30) - : EdgeInsets.all(15), - child: Column( - children: [ - Container( - child: Column( - children: [ - Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Expanded( - flex: 12, - child: Container( - alignment: Alignment.centerLeft, - child: Column( - mainAxisAlignment: - MainAxisAlignment.center, - crossAxisAlignment: - CrossAxisAlignment.center, - children: [ - Text(popupName ?? '', - style: GoogleFonts.poppins( - fontSize: 16, - fontWeight: FontWeight.w500, - color: Color(0xFFE26728), - )), - ], - ), - ), - ), - ], - ), - SizedBox(height: 15), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Container( - width: double.infinity, - alignment: Alignment.center, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: - CrossAxisAlignment.center, - children: [ - DropdownButtonFormField( - decoration: InputDecoration( - border: OutlineInputBorder(), - hintText: 'Relationship', - labelText: 'Relationship', - contentPadding: EdgeInsets.symmetric( - vertical: 10, horizontal: 15), - ), - value: dropdownValue ?? - (action == 'Edit' - ? _relationShipController.text - : null), - onChanged: (value) { - _relationShipController.text = value!; - }, - dropdownColor: Colors.white, - items: selectedRelationships - .map((relationship) { - final String relationshipName = - relationship[ - 'relationship_name'] ?? - ''; - return DropdownMenuItem( - value: relationshipName, - child: Text(relationshipName), - ); - }).toList(), - ), - ], - ), - ), - SizedBox(height: 10), - Container( - width: double.infinity, - alignment: Alignment.centerLeft, - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - Container( - child: TextFormField( - controller: _memberNameController, - onChanged: (value) => null, - decoration: InputDecoration( - border: OutlineInputBorder(), - hintText: 'Member Full Name', - labelText: 'Member Full Name', - contentPadding: - EdgeInsets.symmetric( - vertical: 10, - horizontal: 15), - ), - validator: (value) { - if (value == null || - value.isEmpty) { - return 'Member Full Name is required'; - } - return null; - }, - ), - ), - ], - ), - ), - SizedBox(height: 10), - Container( - width: double.infinity, - alignment: Alignment.centerRight, - child: Column( - mainAxisAlignment: MainAxisAlignment.end, - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - Container( - child: TextFormField( - controller: _dobController, - onTap: () { - _selectDate(context, floaterData); - }, - readOnly: true, - decoration: InputDecoration( - border: OutlineInputBorder(), - hintText: 'DD-MM-YYYY', - labelText: 'Date of Birth', - suffixIcon: IconButton( - icon: Icon(Icons.calendar_today), - onPressed: () { - _selectDate( - context, floaterData); - }, - ), - contentPadding: - EdgeInsets.symmetric( - vertical: 10, - horizontal: 15, - ), - ), - ), - ), - ], - ), - ), - ], - ), - SizedBox(height: 15), - Row( - children: [ - Expanded( - flex: 6, - child: Container( - alignment: Alignment.centerLeft, - child: ElevatedButton( - onPressed: () { - // Close the dialog without sending data - cancelPopUp(); - context.pop(); - // Navigator.of(context).pop(); - }, - child: Text( - 'Cancel', - style: GoogleFonts.poppins( - color: Colors.white), - ), - style: ElevatedButton.styleFrom( - backgroundColor: Color(0xFFE26728), - shape: RoundedRectangleBorder( - borderRadius: - BorderRadius.circular(5), - ), - ), - ), - )), - Expanded( - flex: 6, - child: Container( - alignment: Alignment.centerRight, - child: ElevatedButton( - onPressed: () { - if (_relationShipController - .text.isEmpty || - _memberNameController - .text.isEmpty || - _dobController.text.isEmpty) { - ToastHelper.showWarningToast( - context, - 'All fields are required'); - } else { - Map formData = { - 'relationship': - _relationShipController.text, - 'memberName': - _memberNameController.text, - 'dateOfBirth': - _dobController.text, - - // Add more form fields as needed - }; - - // Call the function to send form data to API - saveFamilyMemberDetails( - formData, - floaterData, - action, - gmcSumInsured); - - // Close the dialog - context.pop(); - // Navigator.of(context).pop(); - } - }, - child: Text( - 'Save', - style: GoogleFonts.poppins( - color: Colors.white), - ), - style: ElevatedButton.styleFrom( - backgroundColor: Color(0xFFE26728), - shape: RoundedRectangleBorder( - borderRadius: - BorderRadius.circular(5), - ), - ), - ), - )), - ], - ), - ], - ), - ), - ], + width: Responsive.isDesktop(context) ? 500 : 350, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + action == "Edit" ? "Edit Family Member" : "Add Family Member", + style: GoogleFonts.poppins( + fontSize: 18, + fontWeight: FontWeight.bold, + color: Color(0xFFE26728), ), ), - ), + SizedBox(height: 20), + + // RELATIONSHIP DROPDOWN + DropdownButtonFormField( + value: dropdownValue, + decoration: InputDecoration( + border: OutlineInputBorder(), + labelText: "Relationship", + ), + items: relationshipObjects.map((item) { + return DropdownMenuItem( + value: item["relationship"], + child: Text(item["relationship"]), + ); + }).toList(), + + onChanged: action == "Edit" + ? null + : (value) { + dropdownValue = value!; + _relationShipController.text = value; + + // assign selected relationship FULL OBJECT + selectedFloaterData = relationshipObjects.firstWhere( + (item) => item["relationship"] == value); + }, + ), + + SizedBox(height: 15), + + // FULL NAME FIELD + TextFormField( + controller: _memberNameController, + decoration: InputDecoration( + border: OutlineInputBorder(), + labelText: "Member Full Name", + ), + ), + + SizedBox(height: 15), + + // DOB → passes FULL SELECTED OBJECT + TextFormField( + controller: _dobController, + readOnly: true, + onTap: () { + _selectDate(context, selectedFloaterData ?? floaterData); + }, + decoration: InputDecoration( + border: OutlineInputBorder(), + labelText: "Date of Birth", + suffixIcon: IconButton( + icon: Icon(Icons.calendar_today), + onPressed: () { + _selectDate(context, selectedFloaterData ?? floaterData); + }, + ), + ), + ), + + SizedBox(height: 20), + + // ACTION BUTTONS + Row( + children: [ + Expanded( + child: OutlinedButton( + onPressed: () => Navigator.pop(context), + child: Text("Cancel"), + ), + ), + SizedBox(width: 10), + Expanded( + child: ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: Color(0xFFE26728), + ), + onPressed: () { + if (_memberNameController.text.isEmpty || + _dobController.text.isEmpty || + dropdownValue == null) { + ToastHelper.showWarningToast( + context, "All fields are required"); + return; + } + + Map formData = { + "relationship": dropdownValue!, + "memberName": _memberNameController.text, + "dateOfBirth": _dobController.text, + }; + + print('floaterDatafloaterData'); + print(floaterData); + print(selectedFloaterData); + + saveFamilyMemberDetails( + formData, + selectedFloaterData ?? floaterData ?? {}, + action, + sumInsured, + ); + + Navigator.pop(context); + }, + child: Text( + "Save", + style: TextStyle(color: Colors.white), + ), + ), + ), + ], + ), + ], ), ), ); @@ -1423,6 +1367,7 @@ class _empDetailsState extends State { ); } + void openSelfForm(action) { showDialog( context: context, @@ -1792,10 +1737,13 @@ class _empDetailsState extends State { String? gpaPolicyType = item['type']; String? gpaECardDownload = item['eCardDownload']; String? gpaSumInsured = item['si_value']; - gpaGstValue = item['si_gst_value']; - gpaSiPremiumValue = item['si_premium_value']; - gpaTotalableValue = gpaSiPremiumValue + gpaGstValue; - bool isValueValid = (gpaSiPremiumValue != 0 && gpaGstValue != 0); + double gpaGstValue = (item['si_gst_value'] ?? 0).toDouble(); + double gpaSiPremiumValue = (item['si_premium_value'] ?? 0).toDouble(); + + double gpaTotalableValue = gpaSiPremiumValue + gpaGstValue; + + bool isValueValid = gpaSiPremiumValue != 0 && gpaGstValue != 0; + List gpaMappedFamilyFloaters = []; @@ -2274,34 +2222,44 @@ class _empDetailsState extends State { for (var item in data) { print('item'); print(item); - setState(() { - gmcPolicyName = item['Policy_Name']; + + String gmcPolicyName = item['Policy_Name']; print('gmcPolicyName $gmcPolicyName'); - gmcPolicyType = item['type']; + String gmcPolicyType = item['type']; print('gmcPolicyType $gmcPolicyType'); - gmcFloaterTextHeading = item['floter_text_heading']; + String gmcClientPolicyId = item['ClientPolicyId']; + print('gmcClientPolicyId $gmcClientPolicyId'); + String gmcFloaterTextHeading = item['floter_text_heading']; print('gmcFloaterTextHeading $gmcFloaterTextHeading'); - gmcFloaterTextDescription = item['floter_text_description']; + String gmcFloaterTextDescription = item['floter_text_description']; print('gmcFloaterTextDescription $gmcFloaterTextDescription'); - gmcNotes = item['notes']; + String gmcNotes = item['notes']; print('gmcNotes $gmcNotes'); - gmcECardDownload = item['eCardDownload']; + dynamic gmcECardDownload = item['eCardDownload']; print('gmcECardDownload $gmcECardDownload'); - gmcGstValue = item['family_floaters_of_dependent_and_gst_value']; + bool gmcCopyDependenceDataEnable = item['copy_dependence_data_enable']; + print('gmcCopyDependenceDataEnable $gmcCopyDependenceDataEnable'); + double gmcGstValue = item['family_floaters_of_dependent_and_gst_value']; print('gmcGstValue $gmcGstValue'); - gmcSiPremiumValue = + double gmcSiPremiumValue = item['family_floaters_of_dependent_and_si_premium_value']; print('gmcSiPremiumValue $gmcSiPremiumValue'); - gmcTotalableValue = gmcGstValue + gmcSiPremiumValue; - gmcIsValueValid = (gmcSiPremiumValue != 0 && gmcGstValue != 0); + double gmcTotalableValue = gmcGstValue + gmcSiPremiumValue; + bool gmcIsValueValid = (gmcSiPremiumValue != 0 && gmcGstValue != 0); - gmcMappedFamilyFloaters = item['mapped_family_floaters']; + List gmcRelationShip = item['relationship']; + List gmcMappedFamilyFloaters = item['mapped_family_floaters']; print('gmcMappedFamilyFloaters $gmcMappedFamilyFloaters'); - dynamic getTrueObjects = gmcMappedFamilyFloaters + List getTrueObjects = gmcMappedFamilyFloaters .where((element) => element['is_value_exist'] == true) .toList(); + + List getFalseObjects = gmcMappedFamilyFloaters + .where((element) => element['is_value_exist'] == false) + .toList(); print('getTrueObjects'); print(getTrueObjects); + dynamic gmcSumInsured; if (getTrueObjects.length > 0) { print('true'); gmcSumInsured = gmcMappedFamilyFloaters[0]["data"]["basic_cover_si"]; @@ -2310,15 +2268,17 @@ class _empDetailsState extends State { gmcSumInsured = item['Policy_Terms']['sum_insured']; } String intOpenForEnrollment = item['OpenForEnrollment']; - gmcOpenForEnrollment = int.parse(intOpenForEnrollment); + int gmcOpenForEnrollment = int.parse(intOpenForEnrollment); print('gmcOpenForEnrollment $gmcOpenForEnrollment'); - gmcTypeName = item['type']; - }); + String gmcTypeName = item['type']; + + List familyFloaterContainers = []; for (var floater in gmcMappedFamilyFloaters) { bool isValueExist = floater['is_value_exist']; Map floaterData = floater['data']; + print('floaterData123456 $floaterData'); final dobDate = floaterData['dob'] ?? ''; if (isValueExist) { @@ -2391,193 +2351,84 @@ class _empDetailsState extends State { ), ), ), - if (gmcOpenForEnrollment != 0) - if (gmcECardDownload == null) - if (isValueExist && floaterData['relationship'] != 'Self') - Expanded( - flex: Responsive.isDesktop(context) ? 1 : 2, - child: Container( - alignment: Alignment.topRight, - child: Column( - mainAxisAlignment: MainAxisAlignment.end, - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - MouseRegion( - cursor: SystemMouseCursors.click, - child: GestureDetector( - onTap: () { - deleteItem(floaterData); - }, - child: Tooltip( - message: 'Delete', - child: Icon( - Icons.delete, - color: Color(0xFFE26728), - ), + // === RIGHT SIDE ACTION ICONS === + if (gmcOpenForEnrollment != 0 && gmcECardDownload == null) + Expanded( + flex: Responsive.isDesktop(context) ? 2 : 3, + child: Container( + alignment: Alignment.topRight, + child: Column( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + // VIEW (Self Only) + if (floaterData['relationship'] == 'Self') + MouseRegion( + cursor: SystemMouseCursors.click, + child: GestureDetector( + onTap: () => openSelfForm('View'), + child: Tooltip( + message: 'View', + child: Icon( + Icons.remove_red_eye, + color: Color(0xFFE26728), ), - // child: Text( - // 'Delete', - // textAlign: TextAlign.left, - // style: GoogleFonts.poppins( - // fontSize: Responsive.isDesktop(context) - // ? 16 - // : 15, - // color: Color( - // 0xFFE26728), // Add underline decoration - // ), - // ), ), - ) - ], - ), - ), - ), - if (gmcOpenForEnrollment != 0) - if (gmcECardDownload == null) - Expanded( - flex: (isValueExist && - floaterData['relationship'] != 'Self') - ? Responsive.isDesktop(context) - ? 1 - : 4 - : Responsive.isDesktop(context) - ? 1 - : 4, - child: Container( - // alignment: Alignment.topRight, - // color: Colors.yellow, - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ + ), + ), + + // DELETE + EDIT (For others) + if (floaterData['relationship'] != 'Self') ...[ + // DELETE ICON + MouseRegion( + cursor: SystemMouseCursors.click, + child: GestureDetector( + onTap: () => deleteItem(floaterData,gmcCopyDependenceDataEnable,gmcClientPolicyId), + child: Tooltip( + message: 'Delete', + child: Icon( + Icons.delete, + color: Color(0xFFE26728), + ), + ), + ), + ), + SizedBox(height: 8), + + // EDIT ICON MouseRegion( cursor: SystemMouseCursors.click, child: GestureDetector( onTap: () { - if (floaterData['relationship'] == 'Self') { - openSelfForm('View'); - } else { - openForm( - floaterData, 'Edit', gmcSumInsured); - } - }, - // child: Text( - // floaterData['relationship'] == 'Self' - // ? 'View' - // : 'Edit', - // textAlign: TextAlign.left, - // style: GoogleFonts.poppins( - // fontSize: Responsive.isDesktop(context) - // ? 16 - // : 15, - // color: Color( - // 0xFFE26728), // Add underline decoration - // ), - // ), - child: floaterData['relationship'] == 'Self' - ? Tooltip( - message: 'View', - child: Icon( - Icons.remove_red_eye, - color: const Color(0xFFE26728), - ), - ) - : Tooltip( - message: 'Edit', - child: Icon( - Icons.edit, - color: const Color(0xFFE26728), - ), - ), + openAddFamilyMemberPopup( + "Edit", + floaterData, + gmcClientPolicyId, + gmcRelationShip, + gmcSumInsured, + ); + }, + child: Tooltip( + message: 'Edit', + child: Icon( + Icons.edit, + color: Color(0xFFE26728), + ), + ), ), - ) - ], - ), + ), + ] + ], ), ), + ), + + // SizedBox(height: 10), ], ), ), ); - } else { - familyFloaterContainers.add(MouseRegion( - cursor: SystemMouseCursors.click, // Set cursor to pointer - child: GestureDetector( - onTap: () { - // Handle the click event to open the form - if (gmcOpenForEnrollment != 0) { - if (gmcECardDownload == null) { - openForm(floaterData, 'Add', gmcSumInsured); - } - } - }, - child: Container( - decoration: BoxDecoration( - border: Border.all( - color: Color(0xFF000000), - width: 1, - ), - borderRadius: BorderRadius.circular(5), - ), - padding: Responsive.isDesktop(context) - ? EdgeInsets.all(12) - : EdgeInsets.all(10), - child: Row( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Expanded( - flex: Responsive.isDesktop(context) ? 1 : 4, - child: Container( - alignment: Alignment.centerRight, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Container( - padding: EdgeInsets.all(5), - decoration: BoxDecoration( - color: Color(0xFF6A6A6A), - borderRadius: BorderRadius.circular(5), - ), - child: Icon( - Icons.person_add, - color: Colors.white, - size: 20, - ), - ), - ], - ), - ), - ), - SizedBox(width: Responsive.isDesktop(context) ? 5 : 10), - Expanded( - flex: Responsive.isDesktop(context) ? 11 : 8, - child: Container( - alignment: Alignment.centerLeft, - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - floaterData['button_name'] ?? '', - textAlign: TextAlign.left, - style: GoogleFonts.poppins( - fontSize: - Responsive.isDesktop(context) ? 22 : 18, - fontWeight: FontWeight.w500, - ), - ), - ], - ), - ), - ), - ], - ), - ), // Replace YourChildWidget with the widget you want to wrap - ), - )); } - familyFloaterContainers.add(SizedBox(height: 15)); + familyFloaterContainers.add(SizedBox(height: 10)); } Widget card = Card( @@ -2745,20 +2596,77 @@ class _empDetailsState extends State { ), ), SizedBox(height: 15), - Container( - padding: - EdgeInsets.only(top: 0, bottom: 0, left: 15, right: 15), - child: Text('Please review your enrolled dependents.', - style: GoogleFonts.poppins( - fontSize: Responsive.isDesktop(context) ? 18 : 16, - color: Color(0xFF181818), - fontWeight: FontWeight.w500, - )), + Row( + children: [ + Expanded( + child: Text( + 'Please review your enrolled dependents. ', + style: GoogleFonts.poppins( + fontSize: Responsive.isDesktop(context) ? 18 : 16, + color: Color(0xFF181818), + fontWeight: FontWeight.w500, + ), + ), + ), + if(item['copy_dependence_data_enable']) + GestureDetector( + onTap:() => copyPreviousPolicy(item['mapped_family_floaters'],item['ClientPolicyId'],gmcSumInsured), + child: Text( + '( Copy From Previous Policy )', + style: GoogleFonts.poppins( + fontSize: Responsive.isDesktop(context) ? 18 : 16, + color: Color(0xFFE26728), + fontWeight: FontWeight.w600, + ), + ), + ), + ], ), SizedBox(height: 15), Column( children: familyFloaterContainers, ), + // Add Family Member Button HERE + if (gmcOpenForEnrollment != 0 && gmcECardDownload == null)...[ + GestureDetector( + onTap: () { + if (getFalseObjects.length == 0) { + ToastHelper.showWarningToast(context, "No family member to add"); + return; + } + openAddFamilyMemberPopup( + "Add", + null, + gmcClientPolicyId, + gmcRelationShip, + gmcSumInsured, + ); + }, + child: Container( + margin: EdgeInsets.only(top: 10), + padding: EdgeInsets.all(15), + decoration: BoxDecoration( + border: Border.all(color: Colors.black, width: 1), + borderRadius: BorderRadius.circular(8), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon(Icons.person_add, color: Colors.black), + SizedBox(width: 10), + Text( + "Add Family Member", + style: GoogleFonts.poppins( + fontSize: Responsive.isDesktop(context) ? 20 : 16, + fontWeight: FontWeight.w500, + ), + ), + ], + ), + ), + ), + ], + SizedBox(height: 15), if (Responsive.isDesktop(context) && gmcIsValueValid) Row( crossAxisAlignment: CrossAxisAlignment.start, @@ -2987,4 +2895,349 @@ class _empDetailsState extends State { return cards; } + + Future copyPreviousPolicy(gmcMappedFamilyFloaters,gmcClientPolicyId,sumInsured) async { + print('gmcClientPolicyId1234 $gmcClientPolicyId'); + try { + final response = await apiService.copyActivePolicy(enrollmentClient_id,enrollmentEmpCodeString,gmcClientPolicyId); + print('ABCDEF ${response['status']}'); + if (response['status'] == 'success') { + List dependents = response['data']; + + /// FILTER is_value_exist == true + List getTrueObjects = gmcMappedFamilyFloaters + .where((element) => element['is_value_exist'] == true) + .toList(); + + /// MERGE & REMOVE DUPLICATES & REMOVE SELF + List uniqueList = getNonMatchedDependents(getTrueObjects, dependents); + + ToastHelper.showSuccessToast(context, "Previous policy copied!"); + + /// OPEN POPUP + showCopyPolicyPopup(context, uniqueList,sumInsured); + + } else { + throw Exception('Failed to fetch relationship list'); + } + } catch (error) { + print('Error fetching relationship list: $error'); + } + + } + + void showCopyPolicyPopup(BuildContext context, List uniqueList,sumInsured) { + Map selected = {}; + List> selectedDependents = []; + + for (int i = 0; i < uniqueList.length; i++) { + selected[i] = false; + } + + showDialog( + context: context, + barrierDismissible: true, + builder: (context) { + return StatefulBuilder( + builder: (context, setState) { + return Dialog( + backgroundColor: Colors.white, + insetPadding: EdgeInsets.symmetric( + horizontal: Responsive.isDesktop(context) ? 350 : 20, + vertical: 100, + ), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + child: Container( + padding: const EdgeInsets.all(20), + width: double.maxFinite, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + "Copy From Previous Policy", + style: TextStyle( + fontSize: Responsive.isDesktop(context) ? 24 : 18, + fontWeight: FontWeight.bold, + ), + ), + + const SizedBox(height: 20), + + SizedBox( + height: 300, + child: ListView.builder( + itemCount: uniqueList.length, + itemBuilder: (context, index) { + final item = uniqueList[index]; + final data = item["data"]; + final relationship = data["relationship"].toString().toLowerCase(); + + bool isSelf = relationship == "self"; + + return Card( + elevation: 3, + margin: const EdgeInsets.symmetric(vertical: 8), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + ), + child: ListTile( + leading: isSelf + ? const SizedBox(width: 24) // Empty space instead of checkbox + : Checkbox( + value: selected[index], + onChanged: (val) { + setState(() { + selected[index] = val ?? false; + + if (val == true) { + selectedDependents.add(data); + } else { + selectedDependents.removeWhere( + (d) => + d["employee_id"] == + data["employee_id"]); + } + }); + }, + ), + + title: Text( + "${data['relationship']} - ${data['name']}", + style: const TextStyle(fontWeight: FontWeight.w600), + ), + + subtitle: Text("DOB: ${data['dob']}"), + ), + ); + }, + ), + ), + + const SizedBox(height: 20), + + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + // ❌ CANCEL BUTTON + TextButton( + onPressed: () => Navigator.pop(context), + child: Text( + "Cancel", + style: TextStyle( + fontSize: 16, + color: Colors.red, + fontWeight: FontWeight.w600), + ), + ), + + // ✔ SUBMIT BUTTON + ElevatedButton( + onPressed: () async { + + if (selectedDependents.isEmpty) { + ToastHelper.showWarningToast(context, "Please select at least one member"); + return; + } + + Navigator.pop(context); + + // Send the full list in one API call + await saveMultipleFamilyMembers(selectedDependents,sumInsured); + }, + child: Text( + "Submit", + style: TextStyle(fontSize: 16), + ), + ), + ], + ) + ] + ), + ), + ); + }, + ); + }, + ); + } + + // List getNonMatchedDependents(List list1, List list2) { + // List merged = [...list1, ...list2]; + // + // List unique = []; + // + // for (var item in merged) { + // final data = item['data']; + // + // // ❌ SKIP SELF + // // if (data['relationship'].toString().toLowerCase() == "self") { + // // continue; + // // } + // + // bool exists = unique.any((u) { + // final ud = u['data']; + // return ud['relationship'] == data['relationship'] && + // ud['name'] == data['name'] && + // ud['dob'] == data['dob']; + // }); + // + // if (!exists) { + // unique.add(item); + // } + // } + // + // return unique; + // } + + List getNonMatchedDependents(List list1, List list2) { + List nonMatched = []; + + for (var item2 in list2) { + var d2 = item2['data']; + + // Always keep SELF — do NOT compare + if (d2['relationship'].toString().toLowerCase() == "self") { + nonMatched.add(item2); + continue; + } + + bool existsInList1 = false; + + for (var item1 in list1) { + var d1 = item1['data']; + + // skip self in list1 also + if (d1['relationship'].toString().toLowerCase() == "self") { + continue; + } + + if (d1['relationship'] == d2['relationship'] && + d1['name'] == d2['name'] && + d1['dob'] == d2['dob']) { + existsInList1 = true; + break; + } + } + + // If not found in list1 → keep the record + if (!existsInList1) { + nonMatched.add(item2); + } + } + + return nonMatched; + } + + Future saveMultipleFamilyMembers( + List> members, sumInsured) async { + + final SharedPreferences prefs = await SharedPreferences.getInstance(); + + dynamic primaryId; + dynamic is_createdby_hr; + + if (prefs.containsKey('hrtoken')) { + primaryId = hrPrimaryId; + is_createdby_hr = 1; + } else { + primaryId = enrollmentEmpPrimaryId; + is_createdby_hr = 0; + } + + // LOOP and send ONE BY ONE + for (var m in members) { + Map body = { + "client_branch_id": enrollmentEmpClientBranchId, + "relationship": m["relationship"], + "name": m["name"], + "dob": m["dob"], + "emp_code": enrollmentEmpCodeString, + "client_id": enrollmentClient_id, + "client_policy_id": m["client_policy_id"], + "id": m["employee_id"], + "created_by": primaryId, + "is_createdby_hr": is_createdby_hr, + "unit": selfUnit, + "basic_cover_si": sumInsured, + }; + + print("SENDING TO API → $body"); + + final response = + await apiService.saveFamilyMemberDetailsToApi([body]); // API expects a list + + if (response['status'] != 'success') { + ToastHelper.showErrorToast(context, "Failed to save ${m['name']}"); + return; // stop further calls on failure + } + } + + // ToastHelper.showSuccessToast(context, "Dependents copied successfully!"); + + formDataList.clear(); + selectedRelationships.clear(); + _relationShipController.clear(); + _memberNameController.clear(); + _dobController.clear(); + relationshipOptions.clear(); + + // Refresh screens + getGpaEmpPolicyDetails(enrollmentEmpPrimaryId); + getGmcEmpPolicyDetails(enrollmentEmpPrimaryId); + fetchRelationshipList(); + } + + +// Future saveMultipleFamilyMembers(List> members,sumInsured) async { + // final SharedPreferences prefs = await SharedPreferences.getInstance(); + // + // dynamic primaryId; + // dynamic is_createdby_hr; + // + // if (prefs.containsKey('hrtoken')) { + // primaryId = hrPrimaryId; + // is_createdby_hr = 1; + // } else { + // primaryId = enrollmentEmpPrimaryId; + // is_createdby_hr = 0; + // } + // + // // Add extra fields to each object + // List> finalList = members.map((m) { + // return { + // "client_branch_id": enrollmentEmpClientBranchId, + // "relationship": m["relationship"], + // "name": m["name"], + // "dob": m["dob"], + // "emp_code": enrollmentEmpCodeString, + // "client_id": enrollmentClient_id, + // "id":m['employee_id'], + // "client_policy_id": m["client_policy_id"], + // "created_by": primaryId, + // "is_createdby_hr": is_createdby_hr, + // "unit": selfUnit, + // "basic_cover_si": sumInsured, + // }; + // }).toList(); + // + // print("FINAL JSON TO API:"); + // print(finalList); + // + // final response = await apiService.saveFamilyMemberDetailsToApi(finalList); + // + // if (response['status'] == 'success') { + // ToastHelper.showSuccessToast(context, response['message']); + // getGpaEmpPolicyDetails(enrollmentEmpPrimaryId); + // getGmcEmpPolicyDetails(enrollmentEmpPrimaryId); + // fetchRelationshipList(); + // } else { + // ToastHelper.showErrorToast(context, response['message']); + // } + // } + + } + + diff --git a/lib/pages/enrollment/empReview.dart b/lib/pages/enrollment/empReview.dart index 89aee41..0d69bf6 100755 --- a/lib/pages/enrollment/empReview.dart +++ b/lib/pages/enrollment/empReview.dart @@ -61,39 +61,20 @@ class _empReviewDetailsState extends State { dynamic gpaSelfRelationship; dynamic gpaSelfDetails; dynamic gpaECardDownload; - dynamic gpaGstValue; - dynamic gpaSiPremiumValue; - dynamic gpaIsPremiumSummery; - dynamic gpaTotalableValue; - late bool isValueValid; dynamic gpaTotalAmt; dynamic gmcTotalAmt; - dynamic gmcGstValue; - dynamic gmcSiPremiumValue; - dynamic gmcTotalableValue; - late bool gmcIsValueValid; - dynamic gmcECardDownload; - dynamic gmcIsPremiumSummery; dynamic gmcDisclaimer; dynamic gmcClintPolicyId = 0; - dynamic gmcMappedFamilyFloaters = []; dynamic gmcSelfName; dynamic gmcSelfDob; dynamic gmcSelfRelationship; dynamic gmcSelfDetails; - dynamic gmcPolicyName; - dynamic gmcPolicyType; - dynamic gmcNotes; - dynamic gmcSumInsured; - dynamic gmcTypeName; dynamic clientName; dynamic clientLogo; dynamic addOnsDependentMappedFamilyFloatersArray; dynamic addOnsDependentClientPolicyId; dynamic gmcAddOnsselectedSI; dynamic gmcAddOnsselectedDependent; - dynamic gmcFloaterTextDescription; - dynamic gmcFloaterTextHeading; dynamic dependentValue; dynamic siValue; late int addOnsDependentSumInsured; @@ -169,7 +150,6 @@ class _empReviewDetailsState extends State { bool gmchasPremiumSummary = false; List> allDisclaimer = []; // Declare this to store all disclaimers - late List checkboxValues; bool isTokenAvailable = false; bool isEnrollCompletedStatus = false; final session = SessionManager(); @@ -184,7 +164,7 @@ class _empReviewDetailsState extends State { isLoading = false; }); }); - checkboxValues = List.filled(allDisclaimer.length, false); + // checkboxValues = List.filled(allDisclaimer.length, false); getTokenStatus(); } @@ -1368,8 +1348,7 @@ class _empReviewDetailsState extends State { @override Widget build(BuildContext context) { - if ((gpaMappedFamilyFloaters == [] && gpaMappedFamilyFloaters == null) && - (gmcMappedFamilyFloaters == [] && gmcMappedFamilyFloaters == null)) { + if (gpaDataIsEmpty == 0 && gmcDataIsEmpty == 0) { return PopScope( canPop: false, onPopInvokedWithResult: (didPop, result) { @@ -3380,6 +3359,14 @@ class _empReviewDetailsState extends State { } } + double toDoubleSafe(dynamic value) { + if (value == null) return 0.0; + if (value is num) return value.toDouble(); + if (value is String) return double.tryParse(value) ?? 0.0; + return 0.0; + } + + List generateGpaCards(List data) { List cards = []; @@ -3390,37 +3377,70 @@ class _empReviewDetailsState extends State { String? gpaPolicyName = item['Policy_Name']; String? gpaPolicyType = item['type']; String? gpaECardDownload = item['eCardDownload']; - gpaGstValue = item['si_gst_value']; - gpaSiPremiumValue = item['si_premium_value']; - setState(() { - gpaIsPremiumSummery = item['is_premium_summery']; - }); - gpaTotalableValue = gpaSiPremiumValue + gpaGstValue; - isValueValid = (gpaSiPremiumValue != 0 && gpaGstValue != 0); - String? gpaSumInsured; - print('GPA Disclaimer $allDisclaimer'); - setState(() { - checkboxValues = List.generate(allDisclaimer.length, (_) => false); - }); + double gpaGstValue = toDoubleSafe(item['si_gst_value']); + double gpaSiPremiumValue = toDoubleSafe(item['si_premium_value']); - List gpaMappedFamilyFloaters = []; + // bool gpaIsPremiumSummery = item['is_premium_summery'] ?? false; + bool gpaIsPremiumSummery = false; + dynamic summery = item['is_premium_summery']; - if (item['mapped_family_floaters'] != null) { - gpaMappedFamilyFloaters = [item['mapped_family_floaters']]; + if (summery is bool) { + gpaIsPremiumSummery = summery; + } else if (summery is String) { + gpaIsPremiumSummery = (summery == "1"); + } else if (summery is int) { + gpaIsPremiumSummery = (summery == 1); } - dynamic getTrueObjects = gpaMappedFamilyFloaters + + double gpaTotalableValue = gpaSiPremiumValue + gpaGstValue; + + bool isValueValid = (gpaSiPremiumValue != 0 && gpaGstValue != 0); + + String? gpaSumInsured; + + print('GPA Disclaimer $allDisclaimer'); + +// local checkbox list + List checkboxValues = + List.generate(allDisclaimer.length, (_) => false); + +// --------------------------------------------- +// FIX: mapped_family_floaters is ALREADY A LIST +// --------------------------------------------- + List gpaMappedFamilyFloaters = + item['mapped_family_floaters'] is List + ? item['mapped_family_floaters'] + : []; + +// --------------------------------------------- +// Filter TRUE objects +// --------------------------------------------- + List getTrueObjects = gpaMappedFamilyFloaters .where((element) => element['is_value_exist'] == true) .toList(); - if (getTrueObjects.length > 0) { +// --------------------------------------------- +// FIX: safe selection of Sum Insured +// --------------------------------------------- + if (getTrueObjects.isNotEmpty) { gpaSumInsured = getTrueObjects[0]["data"]["basic_cover_si"]; } else { - gpaSumInsured = item['Policy_Terms']['sumInsured2']; + gpaSumInsured = item['Policy_Terms']?['sumInsured2']; } - int gpaOpenForEnrollment = int.parse(item['OpenForEnrollment']); +// --------------------------------------------- +// FIX: OpenForEnrollment can be int or String +// --------------------------------------------- + int gpaOpenForEnrollment = 0; + dynamic enroll = item['OpenForEnrollment']; + + if (enroll is int) { + gpaOpenForEnrollment = enroll; + } else if (enroll is String) { + gpaOpenForEnrollment = int.tryParse(enroll) ?? 0; + } List familyFloaterContainers = []; for (var floater in gpaMappedFamilyFloaters) { @@ -3809,51 +3829,71 @@ class _empReviewDetailsState extends State { List generateGmcCards(List data) { List cards = []; - gmcEnrollmentStatus = isEnrollmentOpen(data); + // gmcEnrollmentStatus = isEnrollmentOpen(data); for (var item in data) { - setState(() { - gmcPolicyName = item['Policy_Name']; - gmcPolicyType = item['type']; - gmcFloaterTextHeading = item['floter_text_heading']; - gmcFloaterTextDescription = item['floter_text_description']; - gmcNotes = item['notes']; - gmcECardDownload = item['eCardDownload']; - gmcGstValue = item['family_floaters_of_dependent_and_gst_value']; - gmcSiPremiumValue = - item['family_floaters_of_dependent_and_si_premium_value']; - gmcTotalableValue = gmcGstValue + gmcSiPremiumValue; - gmcIsValueValid = (gmcSiPremiumValue != 0 && gmcGstValue != 0); - setState(() { - gmcIsPremiumSummery = item['is_premium_summery']; - }); + String gmcPolicyName = item['Policy_Name'] ?? ''; + String gmcPolicyType = item['type'] ?? ''; + String gmcFloaterTextHeading = item['floter_text_heading'] ?? ''; + String gmcFloaterTextDescription = item['floter_text_description'] ?? ''; + String gmcNotes = item['notes'] ?? ''; + String gmcECardDownload = item['eCardDownload'] ?? ''; - // gmcSumInsured = item['Policy_Terms']['sum_insured']; - gmcMappedFamilyFloaters = item['mapped_family_floaters']; - // allDisclaimer = allDisclaimer.toSet().toList(); - print('GPA Disclaimer $allDisclaimer'); - dynamic getTrueObjects = gmcMappedFamilyFloaters - .where((element) => element['is_value_exist'] == true) - .toList(); - print('getTrueObjects'); - print(getTrueObjects); - if (getTrueObjects.length > 0) { - print('true'); - gmcSumInsured = gmcMappedFamilyFloaters[0]["data"]["basic_cover_si"]; - } else { - print('false'); - gmcSumInsured = item['Policy_Terms']['sum_insured']; - } - gmcTypeName = item['type']; - }); - print('forEach Card'); - print(gmcMappedFamilyFloaters); - dynamic getTrueObjects = gmcMappedFamilyFloaters + // bool gmcIsPremiumSummery = item['is_premium_summery'] ?? false; + bool gmcIsPremiumSummery = false; + dynamic summery = item['is_premium_summery']; + + if (summery is bool) { + gmcIsPremiumSummery = summery; + } else if (summery is String) { + gmcIsPremiumSummery = (summery == "1"); + } else if (summery is int) { + gmcIsPremiumSummery = (summery == 1); + } + + +// ------------------------------- +// SAFE numeric conversion +// ------------------------------- + double gmcGstValue = toDoubleSafe(item['family_floaters_of_dependent_and_gst_value']); + double gmcSiPremiumValue = toDoubleSafe(item['family_floaters_of_dependent_and_si_premium_value']); + + double gmcTotalableValue = gmcSiPremiumValue + gmcGstValue; + + bool gmcIsValueValid = (gmcSiPremiumValue != 0 && gmcGstValue != 0); + +// ------------------------------- +// SAFE list handling +// ------------------------------- + List gmcMappedFamilyFloaters = item['mapped_family_floaters'] is List + ? item['mapped_family_floaters'] + : []; + + print('GPA Disclaimer $allDisclaimer'); + +// ------------------------------- +// Find objects where is_value_exist == true +// ------------------------------- + List trueObjects = gmcMappedFamilyFloaters .where((element) => element['is_value_exist'] == true) .toList(); - print(getTrueObjects); +// ------------------------------- +// SAFE Suinsured calculation +// ------------------------------- + double gmcSumInsured; - Widget card = getTrueObjects.length > 0 + if (trueObjects.isNotEmpty) { + gmcSumInsured = toDoubleSafe(trueObjects[0]["data"]["basic_cover_si"]); + } else { + gmcSumInsured = toDoubleSafe(item['Policy_Terms']?['sum_insured']); + } + + String gmcTypeName = gmcPolicyType; + + print("gmcMappedFamilyFloaters: $gmcMappedFamilyFloaters"); + print("trueObjects: $trueObjects"); + + Widget card = trueObjects.length > 0 ? Card( elevation: 0, color: Colors.white, diff --git a/lib/pages/enrollment/oldPolicy.dart b/lib/pages/enrollment/oldPolicy.dart deleted file mode 100755 index 787bd4f..0000000 --- a/lib/pages/enrollment/oldPolicy.dart +++ /dev/null @@ -1,388 +0,0 @@ -import 'dart:convert'; -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/widgets.dart'; -import 'package:google_fonts/google_fonts.dart'; -import 'package:intl/intl.dart'; -import 'package:jwt_decode/jwt_decode.dart'; -import 'package:nhance_app_pwa/customAppBar/enrollmentAppBar.dart'; -import 'package:shared_preferences/shared_preferences.dart'; -import 'package:http/http.dart' as http; - -import '../../customAppBar/responsive.dart'; -import '../../customAppBar/toastHelper.dart'; -import '../../models/environment.dart'; - -class oldPolicy extends StatefulWidget { - const oldPolicy({Key? key}) : super(key: key); - - @override - State createState() => _oldPolicyState(); -} - -class _oldPolicyState extends State { - dynamic _token; - dynamic empCodeString; - dynamic empPrimaryId; - dynamic client_id; - dynamic gpaEmpName; - dynamic oldPolicyList; - dynamic oldPolicyName; - dynamic oldPolicyClientId; - dynamic oldPolicyClientPolicyId; - dynamic oldPolicySiGstValue; - dynamic oldPolicySiPremiumValue; - dynamic oldPolicySiValue; - dynamic oldPolicyType; - dynamic oldPolicyEmployeePolicy; - dynamic oldPolicyDataIsEmpty = 1; - dynamic oldPolicyTypeHeading; - dynamic oldPolicyFloaterTextHeading; - dynamic oldPolicyStartDate; - dynamic oldPolicyEndDate; - dynamic empClientBranchId; - - @override - void initState() { - super.initState(); - _loadToken(); - } - - @override - void dispose() { - super.dispose(); - } - - Future _loadToken() async { - print('_loadToken'); - final SharedPreferences prefs = await SharedPreferences.getInstance(); - final String? token = prefs.getString('token'); - if (token != null && token.isNotEmpty) { - setState(() { - _token = token; - }); - // Decode the JWT token received from the API response - Map? decodedToken = Jwt.parseJwt(token); - print(decodedToken); - empClientBranchId = prefs.getString('empClientBranchId'); - empCodeString = prefs.getString('empCode'); - print(empCodeString); // Check if emp_code is correct - empPrimaryId = prefs.getString('empPrimaryId'); - gpaEmpName = prefs.getString('gpaEmpName'); - client_id = prefs.getString('client_id'); - print(client_id); - getOldPolicyDetails(); - } else { - // Token is empty or null, handle accordingly (e.g., navigate to login screen) - // For now, let's navigate to the login screen - ToastHelper.showErrorToast(context, 'Session Out'); - Navigator.pushReplacementNamed(context, 'phone'); - } - } - - Future getOldPolicyDetails() async { - var url = Uri.parse(Environment.apiUrl + - 'getEmployeeActiveOrInactivePolicy?client_id=$client_id&emp_code=$empCodeString&type=Inactive&client_branch_id=$empClientBranchId'); - try { - var response = await http.get( - url, - headers: { - 'Authorization': - 'Bearer $_token', // Add token to the Authorization header - }, - ); - if (response.statusCode == 200) { - Map data = json.decode(response.body); - if (data['status'] == 'success') { - setState(() { - oldPolicyList = data['data']; - print('gmcPolicies'); - }); - // Assuming data is a List - print(oldPolicyList); - } else { - setState(() { - oldPolicyDataIsEmpty = 0; - }); - // Handle other status messages if needed - // ToastHelper.showErrorToast( - // context, 'API request failed with status: ${data['status']}'); - print('API request failed with status: ${data['status']}'); - } - } else { - setState(() { - oldPolicyDataIsEmpty = 0; - }); - // Handle other status codes - // ToastHelper.showErrorToast( - // context, 'Request failed with status: ${response.statusCode}'); - print('Request failed with status: ${response.statusCode}'); - } - } catch (e) { - // Handle exceptions - print('Exception occurred: $e'); - } - } - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: CustomAppBar(), - body: _buildBody(), - ); - } - - Widget _buildBody() { - if (oldPolicyList == null) { - return Center(child: _buildNoDataWidget()); - } else if (oldPolicyList!.isEmpty) { - return Center(child: _buildNoDataWidget()); - } else { - return _buildPolicyListWidget(); - } - } - - Widget _buildNoDataWidget() { - return SingleChildScrollView( - child: Container( - color: Color(0xFFEFF3F6), - child: Column( - children: [ - Center( - child: Text('No Data Available'), - ) - ], - ), - ), - ); - } - - Widget _buildPolicyListWidget() { - return Stack( - children: [ - SingleChildScrollView( - child: Container( - padding: Responsive.isDesktop(context) - ? EdgeInsets.symmetric( - horizontal: MediaQuery.of(context).size.width * - 0.2, // 30% of screen width as horizontal padding - vertical: MediaQuery.of(context).size.height * - 0.03, // 5% of screen height as vertical padding - ) - : EdgeInsets.all(10), - color: Color(0xFFEFF3F6), - child: Column( - children: [ - if (oldPolicyDataIsEmpty == 1) - ...generateGmcCards(oldPolicyList), - ], - ), - ), - ) - ], - ); - } - - List generateGmcCards(List data) { - List cards = []; - - print('data'); - print(data); - - for (var item in data) { - setState(() { - oldPolicyName = item['policy_name']; - oldPolicyTypeHeading = item['heading']; - oldPolicyFloaterTextHeading = item['floter_text_heading']; - oldPolicyClientId = item['client_id']; - oldPolicyClientPolicyId = item['client_policy_id']; - oldPolicySiGstValue = item['si_gst_value']; - oldPolicySiPremiumValue = item['si_premium_value']; - oldPolicySiValue = item['si_value']; - oldPolicyType = item['policy_type']; - oldPolicyStartDate = item['policy_start_date']; - oldPolicyEndDate = item['policy_end_date']; - oldPolicyEmployeePolicy = item['EmployeePolicy']; - }); - - Widget card = Card( - elevation: 0, - child: Padding( - padding: Responsive.isDesktop(context) - ? EdgeInsets.all(20) - : EdgeInsets.all(10), - child: Column( - children: [ - Container( - decoration: BoxDecoration( - color: Color( - 0xFFFFF1DD), // Set background color for the container - borderRadius: BorderRadius.circular( - 5), // Set border radius for the container - ), - padding: Responsive.isDesktop(context) - ? EdgeInsets.only( - top: 15, bottom: 15, left: 25, right: 25) - : EdgeInsets.only( - top: 10, bottom: 10, left: 10, right: 10), - child: Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Expanded( - flex: 8, - child: Container( - alignment: Alignment.centerLeft, - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - oldPolicyName ?? '', - textAlign: TextAlign.left, - style: GoogleFonts.poppins( - fontSize: Responsive.isDesktop(context) - ? 18 - : 14, - ), - ), - Text( - oldPolicyTypeHeading ?? '', - textAlign: TextAlign.left, - style: GoogleFonts.poppins( - fontSize: Responsive.isDesktop(context) - ? 20 - : 16, - fontWeight: FontWeight.w600, - ), - ), - ], - ))), - Expanded( - flex: 4, - child: Container( - alignment: Alignment.centerRight, - child: Column( - mainAxisAlignment: MainAxisAlignment.end, - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - Text( - oldPolicyFloaterTextHeading ?? '', - textAlign: TextAlign.right, - style: GoogleFonts.poppins( - fontSize: Responsive.isDesktop(context) - ? 18 - : 14, - ), - ), - Text( - '₹ ${oldPolicySiValue != null ? oldPolicySiValue : 'NA'}', - textAlign: TextAlign.right, - style: GoogleFonts.poppins( - fontSize: Responsive.isDesktop(context) - ? 20 - : 16, - fontWeight: FontWeight.w600, - ), - ), - ], - ))), - ], - ), - ), - SizedBox(height: 15), - Container( - padding: Responsive.isDesktop(context) - ? EdgeInsets.only(top: 0, bottom: 0, left: 15, right: 15) - : EdgeInsets.only(top: 0, bottom: 0, left: 5, right: 5), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: oldPolicyEmployeePolicy.map((data) { - String formattedDate = formatDate(data['dob']); - return Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Icon(Icons.arrow_right, - color: Color(0xFFE26728)), // Arrow icon - SizedBox( - width: Responsive.isDesktop(context) - ? 10 - : 5), // Space between icon and text - Expanded( - child: Text( - '${data['name']} ~ ${data['relationship']} ~ DOB : $formattedDate', - style: GoogleFonts.poppins( - fontSize: - Responsive.isDesktop(context) ? 18 : 16, - color: Color(0xFF232526), - ), - ), - ), - ], - ); - }).toList(), - ), - ), - SizedBox(height: 15), - Container( - padding: Responsive.isDesktop(context) - ? EdgeInsets.only(top: 0, bottom: 0, left: 25, right: 25) - : EdgeInsets.only(top: 0, bottom: 0, left: 10, right: 10), - child: Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Expanded( - flex: 12, - child: Container( - alignment: Alignment.centerLeft, - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - RichText( - text: TextSpan( - text: 'Policy Period : ', - style: GoogleFonts.poppins( - fontSize: 16, - fontWeight: FontWeight.w500, - color: Color(0xFF232526), - ), - children: [ - TextSpan( - text: - '$oldPolicyStartDate To $oldPolicyEndDate', - style: GoogleFonts.poppins( - fontSize: 14, - color: Color(0xFF232526), - ), - ), - ], - ), - ), - ], - ), - ), - ), - ], - ), - ), - ], - ), - )); - - cards.add(card); - cards.add(SizedBox(height: 10)); - } - - return cards; - } - - String formatDate(String inputDate) { - // Parse the input date string - DateTime dateTime = DateTime.parse(inputDate); - - // Format the date to 'dd MMMM yyyy' - String formattedDate = DateFormat('dd MMMM yyyy').format(dateTime); - - return formattedDate; - } -} diff --git a/lib/pages/enrollment/service/api_service.dart b/lib/pages/enrollment/service/api_service.dart index f40715f..5c21bf1 100755 --- a/lib/pages/enrollment/service/api_service.dart +++ b/lib/pages/enrollment/service/api_service.dart @@ -3,8 +3,9 @@ import 'package:http/http.dart' as http; import 'package:shared_preferences/shared_preferences.dart'; import 'dart:convert'; +import '../../../config/environment.dart'; import '../../../customAppBar/toastHelper.dart'; -import '../../../models/environment.dart'; + import '../../service/TokenService.dart'; class ApiService { @@ -186,13 +187,27 @@ class ApiService { return response; } - Future> deleteItemToApi(id) async { + Future> deleteItemToApi(id,copyStatus,gmcClientPolicyId) async { print(_token); if (_token == null) { await _initializeToken(); } final url = - Uri.parse('${Environment.apiUrlEnrollment}deleteDependence?id=$id'); + Uri.parse('${Environment.apiUrlEnrollment}deleteDependence?id=$id&is_from_copy=$copyStatus&client_policy_id=$gmcClientPolicyId'); + final headers = { + 'Authorization': 'Bearer $_token' ?? '', + }; + final response = await _makeGetRequest(url, headers); + return response; + } + + Future> copyActivePolicy(enrollmentClient_id,enrollmentEmpCodeString,newClientPolicy) async { + print(_token); + if (_token == null) { + await _initializeToken(); + } + final url = + Uri.parse('${Environment.apiUrlEnrollment}copyActiveEmployeeAndDependentDetails?client_id=$enrollmentClient_id&emp_code=$enrollmentEmpCodeString&new_client_policy_id=$newClientPolicy'); final headers = { 'Authorization': 'Bearer $_token' ?? '', }; @@ -470,12 +485,16 @@ class ApiService { Future> _makeGetRequest( Uri url, Map headers) async { + // Add global APP_SIGNATURE header + headers['APP-SIGNATURE'] = 'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y'; final response = await http.get(url, headers: headers); return _handleResponse(response); } Future> _makePostRequest( Uri url, String body, Map headers) async { + // Add global APP_SIGNATURE header + headers['APP-SIGNATURE'] = 'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y'; final response = await http.post(url, headers: headers, body: body); return _handleResponse(response); } diff --git a/lib/pages/login.dart b/lib/pages/login.dart index e0d52ab..d3c3671 100755 --- a/lib/pages/login.dart +++ b/lib/pages/login.dart @@ -13,9 +13,9 @@ import 'dart:convert'; import 'dart:io'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:google_fonts/google_fonts.dart'; +import '../config/environment.dart'; import '../customAppBar/responsive.dart'; import '../customAppBar/toastHelper.dart'; -import '../models/environment.dart'; import '../models/platform_helper_mobile.dart' if (dart.library.html) '../models/platform_helper_other.dart'; import 'package:pinput/pinput.dart'; @@ -138,6 +138,7 @@ class _loginState extends State { body: json.encode(params), headers: { HttpHeaders.contentTypeHeader: 'application/json', + 'APP-SIGNATURE': 'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y', }, ); @@ -232,6 +233,7 @@ class _loginState extends State { body: json.encode(payload), headers: { HttpHeaders.contentTypeHeader: 'application/json', + 'APP-SIGNATURE': 'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y', }, ); @@ -460,6 +462,7 @@ class _loginState extends State { body: json.encode(payload), headers: { HttpHeaders.contentTypeHeader: 'application/json', + 'APP-SIGNATURE': 'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y', }, ); print('response : ${response.statusCode}'); @@ -571,7 +574,7 @@ class _loginState extends State { getClientLogoAndDetails(); } if (_postToken != null && _postToken.isNotEmpty) { - ToastHelper.showSuccessToast(context, 'Successfully Login'); + ToastHelper.showSuccessToast(context, 'Successfully Logged In'); context.go('/home'); // Navigator.pushReplacementNamed(context, 'home'); } else { @@ -588,7 +591,7 @@ class _loginState extends State { print('Successfully Login'); print(isMobilePlatform()); if (_preToken != null && _preToken.isNotEmpty) { - ToastHelper.showSuccessToast(context, 'Successfully Login'); + ToastHelper.showSuccessToast(context, 'Successfully Logged In'); context.go('/home'); // Navigator.pushReplacementNamed(context, 'home'); } else { @@ -606,6 +609,7 @@ class _loginState extends State { headers: { 'Authorization': 'Bearer $_preToken', // Add token to the Authorization header + 'APP-SIGNATURE': 'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y', }, ); if (response.statusCode == 200) { @@ -675,6 +679,7 @@ class _loginState extends State { body: json.encode(payload), headers: { HttpHeaders.contentTypeHeader: 'application/json', + 'APP-SIGNATURE': 'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y', }, ); @@ -740,6 +745,7 @@ class _loginState extends State { body: json.encode(payload), headers: { HttpHeaders.contentTypeHeader: 'application/json', + 'APP-SIGNATURE': 'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y', }, ); @@ -816,6 +822,7 @@ class _loginState extends State { body: json.encode(payload), headers: { HttpHeaders.contentTypeHeader: 'application/json', + 'APP-SIGNATURE': 'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y', }, ); @@ -897,6 +904,7 @@ class _loginState extends State { body: json.encode(payload), headers: { HttpHeaders.contentTypeHeader: 'application/json', + 'APP-SIGNATURE': 'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y', }, ); @@ -1130,8 +1138,7 @@ class _loginState extends State { Expanded( flex: 12, child: Align( - alignment: Alignment - .topLeft, // ✅ Always top-left + alignment: Alignment.center, child: _size.width <= 1100 ? Image.asset( 'assets/nhance_app_logo.png', @@ -1155,7 +1162,7 @@ class _loginState extends State { ), SizedBox( height: Responsive.isDesktop(context) - ? _size.height * 0.1 + ? _size.height * 0.0 : 10, ), SizedBox(height: 10), @@ -1985,117 +1992,9 @@ class _loginState extends State { SizedBox( height: _size.width <= 1100 ? 0 : 0, ), - // _size.width > 1100 - // ? Container( - // margin: EdgeInsets.symmetric( - // horizontal: 150), - // child: Column( - // children: [ - // SizedBox(height: 30), - // Text( - // "Benefits of Login", - // style: - // GoogleFonts.poppins( - // fontSize: 20, - // fontWeight: - // FontWeight.bold, - // ), - // ), - // SizedBox(height: 15), - // ], - // )) - // : SizedBox(), - // _size.width > 1100 - // ? Container( - // margin: EdgeInsets.symmetric( - // horizontal: 150), - // child: Row( - // mainAxisAlignment: - // MainAxisAlignment.center, - // children: [ - // Expanded( - // flex: 6, - // child: Container( - // padding: EdgeInsets - // .symmetric( - // vertical: 8), - // child: Row( - // mainAxisAlignment: - // MainAxisAlignment - // .center, - // children: [ - // Expanded( - // child: Container( - // padding: EdgeInsets - // .symmetric( - // vertical: - // 12), - // decoration: - // BoxDecoration( - // border: - // Border( - // right: - // BorderSide( - // width: 1, - // color: Colors - // .black, - // ), - // ), - // ), - // child: Column( - // children: [ - // Icon( - // Icons - // .policy, - // color: Color( - // 0xFFE26728)), - // SizedBox( - // height: - // 10), - // Text( - // "View Policy"), - // ], - // ), - // ), - // ), - // Expanded( - // child: Container( - // padding: EdgeInsets - // .symmetric( - // vertical: - // 12), - // child: Column( - // children: [ - // Icon( - // Icons - // .edit, - // color: Color( - // 0xFFE26728)), - // SizedBox( - // height: - // 10), - // Text( - // "Manage Claims"), - // ], - // ), - // ), - // ), - // ], - // ), - // ), - // ), - // ], - // ), - // ) - // : SizedBox( - // height: Responsive.isDesktop( - // context) - // ? _size.height * 0.1 - // : _size.height * 0.2, - // ), SizedBox( height: Responsive.isDesktop(context) - ? _size.height * 0.3 + ? _size.height * 0.1 : _size.height * 0.2, ), // SizedBox( diff --git a/lib/pages/postEnrollment/AddPolicyScreen.dart b/lib/pages/postEnrollment/AddPolicyScreen.dart new file mode 100644 index 0000000..2c5a4a4 --- /dev/null +++ b/lib/pages/postEnrollment/AddPolicyScreen.dart @@ -0,0 +1,386 @@ +import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:intl/intl.dart'; +import 'package:jwt_decode/jwt_decode.dart'; +import 'package:nhance_app_pwa/pages/postEnrollment/service/api_service.dart'; + +import '../../customAppBar/customAppBar.dart'; +import '../../customAppBar/customFooter.dart'; +import '../../customAppBar/responsive.dart'; +import '../../customAppBar/tabs.dart'; +import '../../customAppBar/toastHelper.dart'; +import '../service/SessionManager.dart'; +import '../service/TokenService.dart'; + +class AddPolicyScreen extends StatefulWidget { + const AddPolicyScreen({super.key}); + + @override + State createState() => _AddPolicyScreenState(); +} + +class _AddPolicyScreenState extends State { + final _formKey = GlobalKey(); + bool isLoading = false; + + dynamic _token; + final session = SessionManager(); + late ApiService apiService; + + dynamic empCodeString; + dynamic empPrimaryId; + dynamic client_id; + dynamic mobileNo; + dynamic client_branch_id; + dynamic decodedToken; + + // Form controllers + final TextEditingController policyNoController = TextEditingController(); + final TextEditingController expDateController = TextEditingController(); + + String? selectedPolicyType; + String? selectedInsurer; + + List> policyTypeData = []; + List> insurerData = []; + + @override + void initState() { + super.initState(); + apiService = ApiService(context); + _loadToken(); + } + + Future _loadToken() async { + final String? token = await TokenService.getPostToken(); + if (token != null && token.isNotEmpty) { + setState(() => _token = token); + + decodedToken = Jwt.parseJwt(token); + mobileNo = session.mobileNo; + client_branch_id = session.empClientBranchId; + empCodeString = session.empCodeString; + empPrimaryId = session.empPrimaryId; + client_id = session.client_id; + + getPolicyTypeAndInsurerMaster(); + } + } + + Future pickExpiryDate() async { + DateTime today = DateTime.now(); + + DateTime? picked = await showDatePicker( + context: context, + initialDate: today.add(const Duration(days: 1)), // default tomorrow + firstDate: DateTime(today.year, today.month, today.day), // no past + lastDate: DateTime(2100), + ); + + if (picked != null) { + expDateController.text = DateFormat("dd MMM yyyy").format(picked); + setState(() {}); + } + } + + Future getPolicyTypeAndInsurerMaster() async { + setState(() => isLoading = true); + + try { + final response = await apiService.fetchPolicyTypeAndInsurer(); + if (response['status'] == 'success') { + policyTypeData = + List>.from(response['data']['policy_type']); + insurerData = + List>.from(response['data']['insurer']); + + setState(() => isLoading = false); + } else { + throw Exception('Failed to fetch master data'); + } + } catch (error) { + print('Error fetching list: $error'); + } + } + + Future submitForm() async { + try { + if (_formKey.currentState!.validate()) { + setState(() => isLoading = true); + Map formData = { + 'emp_id': empPrimaryId, + 'policy_type_id': selectedPolicyType, + 'insurer_id': selectedInsurer, + 'policy_no': policyNoController.text, + 'policy_end_date': expDateController.text, + 'policy_start_date': '', + }; + + final response = await apiService.sendRetailPolicyDetails(formData); + + if (response['status'] == 'success') { + ToastHelper.showSuccessToast(context, response['message']); + + selectedPolicyType = null; + selectedInsurer = null; + policyNoController.clear(); + expDateController.clear(); + + setState(() => isLoading = false); + + context.go('/home'); + } + } + } catch (e) { + print('Submit error: $e'); + } + } + + // ------------------------------------------------------ + // WIDGETS + // ------------------------------------------------------ + + Widget dropdownFieldMap({ + required String? value, + required String hint, + required List> items, + required String validatorMsg, + bool isMandatory = true, + required Function(String?) onChanged, + required String labelKey, + required String valueKey, + }) { + return DropdownButtonFormField( + value: value, + decoration: inputDecoration(hint), + items: items.map((item) { + return DropdownMenuItem( + value: item[valueKey].toString(), // send ID + child: Text(item[labelKey].toString()), // show name + ); + }).toList(), + validator: (value) { + if (!isMandatory) return null; + return value == null ? validatorMsg : null; + }, + onChanged: onChanged, + ); + } + + TextStyle labelStyle() => GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w500, + color: Colors.black87, + ); + + InputDecoration inputDecoration(String hint, {Widget? suffix}) { + return InputDecoration( + suffixIcon: suffix, + hintText: hint, + contentPadding: const EdgeInsets.all(14), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(10), + ), + ); + } + + @override + Widget build(BuildContext context) { + return PopScope( + canPop: false, + onPopInvokedWithResult: (didPop, result) { + if (didPop) return; + context.go('/home'); + }, + child: Scaffold( + backgroundColor: Colors.white, + appBar: CustomAppBar(), + body: Stack(children: [ + SingleChildScrollView( + child: Container( + padding: Responsive.isDesktop(context) + ? EdgeInsets.symmetric( + horizontal: MediaQuery.of(context).size.width * 0.2, + vertical: MediaQuery.of(context).size.height * 0.05, + ) + : const EdgeInsets.all(10), + color: Colors.white, + child: Column(children: [ + Card( + elevation: 5, + color: Colors.white, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(15)), + child: Padding( + padding: const EdgeInsets.all(15), + child: Column( + children: [ + Row( + children: [ + InkWell( + onTap: () => context.go('/home'), + child: const Icon(Icons.chevron_left, size: 30), + ), + const SizedBox(width: 10), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("Add Your Insurance Policy", + style: GoogleFonts.poppins( + fontSize: 16, + fontWeight: FontWeight.w600, + )), + const SizedBox(height: 2), + Text( + 'Add and manage all your insurance policies taken anywhere for value added services and renewal reminders', + maxLines: 3, + overflow: TextOverflow.ellipsis, + style: GoogleFonts.poppins(fontSize: 12), + ), + ], + ), + ) + ], + ), + const SizedBox(height: 20), + + /// FORM + Form( + key: _formKey, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("Type of Policy", style: labelStyle()), + const SizedBox(height: 5), + dropdownFieldMap( + value: selectedPolicyType, + hint: "Select policy type", + items: policyTypeData, + validatorMsg: "Please select a policy type", + labelKey: "policy_type", + valueKey: "policy_type_id", + onChanged: (val) => + setState(() => selectedPolicyType = val), + ), + const SizedBox(height: 20), + + Text("Insurer", style: labelStyle()), + const SizedBox(height: 5), + dropdownFieldMap( + value: selectedInsurer, + hint: "Select insurer", + items: insurerData, + validatorMsg: "Please select an insurer", + labelKey: "insurer_name", + valueKey: "insurer_id", + onChanged: (val) => + setState(() => selectedInsurer = val), + ), + const SizedBox(height: 20), + + Text("Policy Number", style: labelStyle()), + const SizedBox(height: 5), + TextFormField( + controller: policyNoController, + decoration: + inputDecoration("Enter policy number"), + validator: (value) => null, // NOT MANDATORY + ), + const SizedBox(height: 20), + + Text("Expiry Date", style: labelStyle()), + const SizedBox(height: 5), + GestureDetector( + onTap: pickExpiryDate, + child: AbsorbPointer( + child: TextFormField( + controller: expDateController, + decoration: inputDecoration( + "Select expiry date", + suffix: const Icon(Icons.calendar_month), + ), + validator: (value) => value == null || + value.isEmpty + ? "Expiry date required" + : null, + ), + ), + ), + const SizedBox(height: 30), + + SizedBox( + width: double.infinity, + height: 50, + child: ElevatedButton( + onPressed: submitForm, + style: ElevatedButton.styleFrom( + backgroundColor: const Color(0xFFE26828), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + ), + ), + child: Text( + "Save Policy", + style: GoogleFonts.poppins( + fontSize: 16, + color: Colors.white, + fontWeight: FontWeight.w600, + ), + ), + ), + ), + ], + ), + ), + ], + ), + ), + ), + const SizedBox(height: 60), + ]), + ), + ), + + if (isLoading) + Container( + color: Colors.white70, + child: Center( + child: Image.asset( + 'assets/nhance-loader.gif', + height: 60, + width: 60, + ), + ), + ), + + if (Responsive.isDesktop(context)) + Align( + alignment: Alignment.bottomCenter, + child: SizedBox( + width: double.infinity, child: CustomFooter())), + ]), + bottomNavigationBar: Responsive.isDesktop(context) + ? null + : CustomBottomNavigationBar( + icons: [ + Icons.home_outlined, + Icons.sticky_note_2_outlined, + Icons.person_outline_outlined, + Icons.headset_mic_outlined, + Icons.health_and_safety_outlined, + ], + labels: ["Home", "Claims", "Profile", "Help", "Wellness"], + initialIndex: 3, + onTabChanged: (index) { + if (index == 0) context.go('/home'); + if (index == 1) context.go('/claims'); + if (index == 2) context.go('/profile'); + if (index == 3) context.go('/help'); + }, + ), + ), + ); + } +} diff --git a/lib/pages/postEnrollment/botman_chat.dart b/lib/pages/postEnrollment/botman_chat.dart index fd2789e..dda4a1f 100644 --- a/lib/pages/postEnrollment/botman_chat.dart +++ b/lib/pages/postEnrollment/botman_chat.dart @@ -1,98 +1,98 @@ -import 'package:flutter/material.dart'; -import 'package:nhance_app_pwa/models/environment.dart'; -import 'package:webview_flutter/webview_flutter.dart'; -import 'package:url_launcher/url_launcher.dart'; - -class ChatbotWebViewPage extends StatefulWidget { - final String client_branch_id; - final String empCodeString; - final String empName; - final String empPrimaryId; - final String client_id; - - const ChatbotWebViewPage({ - Key? key, - required this.client_branch_id, - required this.empCodeString, - required this.empName, - required this.empPrimaryId, - required this.client_id, - }) : super(key: key); - - @override - State createState() => _ChatbotWebViewPageState(); -} - -class _ChatbotWebViewPageState extends State { - late final WebViewController _controller; - late final String chatbotDomain; - late final String initialUrl; - - @override - void initState() { - super.initState(); - final chatbotURL = Environment.chatBotUrl; - print('chatbotURL $chatbotURL'); - - initialUrl = Uri.parse( - '$chatbotURL' - '?employee_id=${widget.empPrimaryId}' - '&emp_code=${widget.empCodeString}' - '&origin=mob' - '&name=${Uri.encodeComponent(widget.empName)}' - '&client_id=${widget.client_id}' - '&client_branch_id=${widget.client_branch_id}', - ).toString(); - - // Extract the domain for comparison - final uri = Uri.parse(chatbotURL); - chatbotDomain = '${uri.scheme}://${uri.host}'; - - _controller = WebViewController() - ..setJavaScriptMode(JavaScriptMode.unrestricted) - ..setBackgroundColor(Colors.transparent) - ..setNavigationDelegate( - NavigationDelegate( - onNavigationRequest: (NavigationRequest request) { - debugPrint("Navigation requested: ${request.url}"); - debugPrint("Is main frame: ${request.isMainFrame}"); - - // Allow navigation within the chatbot domain - if (request.url.startsWith(chatbotDomain)) { - return NavigationDecision.navigate; - } else { - // Open external links in browser - _launchInExternalBrowser(Uri.parse(request.url)); - return NavigationDecision.prevent; - } - }, - onWebResourceError: (error) { - debugPrint("WebView error: ${error.description}"); - }, - onPageStarted: (String url) { - debugPrint("Page started loading: $url"); - }, - onPageFinished: (String url) { - debugPrint("Page finished loading: $url"); - }, - ), - ) - ..loadRequest(Uri.parse(initialUrl)); - } - - Future _launchInExternalBrowser(Uri url) async { - if (await canLaunchUrl(url)) { - await launchUrl(url, mode: LaunchMode.externalApplication); - } else { - debugPrint("Could not launch $url"); - } - } - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar(title: const Text('Ask ILA')), - body: WebViewWidget(controller: _controller), - ); - } -} \ No newline at end of file +// import 'package:flutter/material.dart'; +// import 'package:nhance_app_pwa/models/environment.dart'; +// import 'package:webview_flutter/webview_flutter.dart'; +// import 'package:url_launcher/url_launcher.dart'; +// +// class ChatbotWebViewPage extends StatefulWidget { +// final String client_branch_id; +// final String empCodeString; +// final String empName; +// final String empPrimaryId; +// final String client_id; +// +// const ChatbotWebViewPage({ +// Key? key, +// required this.client_branch_id, +// required this.empCodeString, +// required this.empName, +// required this.empPrimaryId, +// required this.client_id, +// }) : super(key: key); +// +// @override +// State createState() => _ChatbotWebViewPageState(); +// } +// +// class _ChatbotWebViewPageState extends State { +// late final WebViewController _controller; +// late final String chatbotDomain; +// late final String initialUrl; +// +// @override +// void initState() { +// super.initState(); +// final chatbotURL = Environment.chatBotUrl; +// print('chatbotURL $chatbotURL'); +// +// initialUrl = Uri.parse( +// '$chatbotURL' +// '?employee_id=${widget.empPrimaryId}' +// '&emp_code=${widget.empCodeString}' +// '&origin=mob' +// '&name=${Uri.encodeComponent(widget.empName)}' +// '&client_id=${widget.client_id}' +// '&client_branch_id=${widget.client_branch_id}', +// ).toString(); +// +// // Extract the domain for comparison +// final uri = Uri.parse(chatbotURL); +// chatbotDomain = '${uri.scheme}://${uri.host}'; +// +// _controller = WebViewController() +// ..setJavaScriptMode(JavaScriptMode.unrestricted) +// ..setBackgroundColor(Colors.transparent) +// ..setNavigationDelegate( +// NavigationDelegate( +// onNavigationRequest: (NavigationRequest request) { +// debugPrint("Navigation requested: ${request.url}"); +// debugPrint("Is main frame: ${request.isMainFrame}"); +// +// // Allow navigation within the chatbot domain +// if (request.url.startsWith(chatbotDomain)) { +// return NavigationDecision.navigate; +// } else { +// // Open external links in browser +// _launchInExternalBrowser(Uri.parse(request.url)); +// return NavigationDecision.prevent; +// } +// }, +// onWebResourceError: (error) { +// debugPrint("WebView error: ${error.description}"); +// }, +// onPageStarted: (String url) { +// debugPrint("Page started loading: $url"); +// }, +// onPageFinished: (String url) { +// debugPrint("Page finished loading: $url"); +// }, +// ), +// ) +// ..loadRequest(Uri.parse(initialUrl)); +// } +// +// Future _launchInExternalBrowser(Uri url) async { +// if (await canLaunchUrl(url)) { +// await launchUrl(url, mode: LaunchMode.externalApplication); +// } else { +// debugPrint("Could not launch $url"); +// } +// } +// +// @override +// Widget build(BuildContext context) { +// return Scaffold( +// appBar: AppBar(title: const Text('Ask ILA')), +// body: WebViewWidget(controller: _controller), +// ); +// } +// } \ No newline at end of file diff --git a/lib/pages/postEnrollment/claimprocess.dart b/lib/pages/postEnrollment/claimprocess.dart index f7f50f8..dbb5aba 100755 --- a/lib/pages/postEnrollment/claimprocess.dart +++ b/lib/pages/postEnrollment/claimprocess.dart @@ -815,39 +815,39 @@ class _claimprocessState extends State { bottomNavigationBar: Responsive.isDesktop(context) ? null : CustomBottomNavigationBar( - onTabChanged: (index) { - if (index == 0) { - context.go('/home'); - } else if (index == 1) { - context.go('/claims'); - } else if (index == 2) { - context.go('/profile'); - } else if (index == 3) { - context.go('/help'); - } else if (index == 4) { - // Wellness tab clicked → show popup - PopupHelper.showRedirectPopup( - context: context, - apiService: apiService, - empPrimaryId: session.empPrimaryId, - ); - } - }, - icons: [ - Icons.home_outlined, - Icons.sticky_note_2_outlined, - Icons.person_outline_outlined, - Icons.headset_mic_outlined, - Icons.health_and_safety_outlined, - ], - labels: [ - "Home", - "Claims", - "Profile", - "Help", - "Wellness", - ], - ), + onTabChanged: (index) { + // Add your navigation logic here + // repositionBotman(); + if (index == 0) { + context.push('/home'); + } else if (index == 1) { + context.push('/claims'); + } else if (index == 2) { + context.push('/profile'); + } else if (index == 3) { + context.push('/help'); + } + // else if (index == 4) { + // // context.push('/wellness'); + // // Wellness tab clicked → show popup + // if(!isRetailLoggedIn) + // PopupHelper.showRedirectPopup( + // context: context, + // apiService: apiService, + // empPrimaryId: session.empPrimaryId, + // ); + // } + }, + icons: [ + Icons.home_outlined, + Icons.sticky_note_2_outlined, + Icons.person_outline_outlined, + Icons.headset_mic_outlined, + // Icons.health_and_safety_outlined, + ], + labels: ["Home", "Claims", "Profile", "Help"], + initialIndex: 0, // Initial index of the bottom navigation bar + ), )); } } diff --git a/lib/pages/postEnrollment/claims.dart b/lib/pages/postEnrollment/claims.dart index a5d0cfb..f556b15 100755 --- a/lib/pages/postEnrollment/claims.dart +++ b/lib/pages/postEnrollment/claims.dart @@ -8,6 +8,7 @@ import 'package:jwt_decode/jwt_decode.dart'; import 'package:nhance_app_pwa/customAppBar/customAppBar.dart'; import 'package:nhance_app_pwa/pages/postEnrollment/service/api_service.dart'; import 'package:nhance_app_pwa/pages/postEnrollment/service/svg_service.dart'; +import 'package:ribbon_widget/ribbon_widget.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:http/http.dart' as http; @@ -29,6 +30,19 @@ class claims extends StatefulWidget { State createState() => _claimsState(); } +const TextStyle _kTextStyle = TextStyle( + color: Color(0xFFFFFFFF), + fontSize: 12, + fontWeight: FontWeight.w900, + height: 1.0); + +enum RibbonLocation { + topStart, + topEnd, + bottomStart, + bottomEnd, +} + class _claimsState extends State { bool isLoading = false; late ApiService apiService; @@ -46,6 +60,7 @@ class _claimsState extends State { dynamic policyHeading; dynamic policyName; dynamic EmployeePolicy; + dynamic emailId; List> employeeDetails = []; dynamic argumentsData; dynamic empMobileNo; @@ -57,6 +72,7 @@ class _claimsState extends State { dynamic client_branch_id; dynamic mobileNo; final session = SessionManager(); + List retailPolicyDetails = []; List> getDepartmentItems() { return [ @@ -108,13 +124,14 @@ class _claimsState extends State { final String? token = await TokenService.getPostToken(); if (token != null && token.isNotEmpty) { // Decode the JWT token received from the API response - mobileNo = session.mobileNo; + mobileNo = session.mobileNo ?? ''; client_branch_id = session.empClientBranchId; empCodeString = session.empCodeString; empName = session.gpaEmpName; print(empCodeString); // Check if emp_code is correct empPrimaryId = session.empPrimaryId; client_id = session.client_id; + emailId = session.empEmailCorporate ?? ''; print(client_id); getActiveAndInactivePolicyDetails(); getTrackClaimsList(); @@ -133,11 +150,11 @@ class _claimsState extends State { try { // Call both APIs final responseActive = await apiService.getActiveAndInactivePolicyDetails( - client_id!, empCodeString!, 'Active', client_branch_id, mobileNo); + client_id!, empCodeString!, 'Active', client_branch_id, mobileNo,emailId); final responseInactive = await apiService.getActiveAndInactivePolicyDetails(client_id!, - empCodeString!, 'Inactive', client_branch_id, mobileNo); + empCodeString!, 'Inactive', client_branch_id, mobileNo,emailId); final bool isActiveSuccess = responseActive['status'] == 'success' && responseActive['data'] != null; @@ -152,7 +169,14 @@ class _claimsState extends State { ? List>.from(responseInactive['data']) : []; - final combinedPolicies = [...activeData, ...inactiveData]; + retailPolicyDetails = List.from(responseActive['retail_policy_data'] ?? []); + + // Remove retail objects without policy_transaction_id + final filteredRetail = retailPolicyDetails + .where((item) => item.containsKey('policy_transaction_id')) + .toList(); + + final combinedPolicies = [...activeData, ...inactiveData, ...filteredRetail]; setState(() { policyList = combinedPolicies; @@ -177,7 +201,7 @@ class _claimsState extends State { return; } print('check 1'); - final response = await apiService.getTrackClaimsList(empPrimaryId!); + final response = await apiService.getTrackClaimsList(empPrimaryId!,mobileNo,emailId); print('check 1'); // if (response['success'] == true) { setState(() { @@ -734,207 +758,353 @@ class _claimsState extends State { bottomNavigationBar: Responsive.isDesktop(context) ? null : CustomBottomNavigationBar( - onTabChanged: (index) { - // Add your navigation logic here - // For example: - if (index == 0) { - context.push('/home'); - } else if (index == 1) { - context.push('/claims'); - } else if (index == 2) { - context.push('/profile'); - } else if (index == 3) { - context.push('/help'); - } else if (index == 4) { - // Wellness tab clicked → show popup - PopupHelper.showRedirectPopup( - context: context, - apiService: apiService, - empPrimaryId: session.empPrimaryId, - ); - } - }, - icons: [ - Icons.home_outlined, - Icons.sticky_note_2_outlined, - Icons.person_outline_outlined, - Icons.headset_mic_outlined, - Icons.health_and_safety_outlined, - ], - labels: [ - "Home", - "Claims", - "Profile", - "Help", - "Wellness", - ], - initialIndex: 1, // Initial index of the bottom navigation bar - ), + onTabChanged: (index) { + // Add your navigation logic here + // repositionBotman(); + if (index == 0) { + context.push('/home'); + } else if (index == 1) { + context.push('/claims'); + } else if (index == 2) { + context.push('/profile'); + } else if (index == 3) { + context.push('/help'); + } + // else if (index == 4) { + // // context.push('/wellness'); + // // Wellness tab clicked → show popup + // if(!isRetailLoggedIn) + // PopupHelper.showRedirectPopup( + // context: context, + // apiService: apiService, + // empPrimaryId: session.empPrimaryId, + // ); + // } + }, + icons: [ + Icons.home_outlined, + Icons.sticky_note_2_outlined, + Icons.person_outline_outlined, + Icons.headset_mic_outlined, + // Icons.health_and_safety_outlined, + ], + labels: ["Home", "Claims", "Profile", "Help"], + initialIndex: 0, // Initial index of the bottom navigation bar + ), )); } + // List generateYourPlanList(List data) { + // return [ + // ListView.builder( + // shrinkWrap: true, + // physics: NeverScrollableScrollPhysics(), + // itemCount: data.length, + // itemBuilder: (BuildContext context, int index) { + // var item = data[index]; + // String policyHeading = item['heading']; + // String policyName = item['policy_name']; + // String policyStatus = item['policy_status']; + // String si_value = item['si_value']; + // String policyEndDate = item['policy_end_date']; + // String policyNo = item['policy_no']; + // String clientPolicyId = item['client_policy_id']; + // String sumInsuredLabel = item['sum_insured_label']; + // List employeePolicy = item['EmployeePolicy']; + // + // return GestureDetector( + // onTap: () { + // var details = {'claimsDetails': item, 'fromClaimPage': 0}; + // context.go('/planclaimsform', extra: details); + // }, + // child: MouseRegion( + // cursor: SystemMouseCursors.click, + // child: Card( + // elevation: 5, + // shape: RoundedRectangleBorder( + // borderRadius: BorderRadius.circular(15.0), + // ), + // child: Container( + // decoration: BoxDecoration( + // color: Colors.white, + // borderRadius: BorderRadius.circular(15.0), + // ), + // padding: EdgeInsets.symmetric(vertical: 15, horizontal: 15), + // child: Row( + // mainAxisAlignment: MainAxisAlignment + // .spaceBetween, // Center content horizontally + // crossAxisAlignment: + // CrossAxisAlignment.center, // Center content vertically + // children: [ + // Expanded( + // flex: 4, + // child: Container( + // alignment: Alignment.centerLeft, + // child: Column( + // mainAxisAlignment: MainAxisAlignment.start, + // crossAxisAlignment: CrossAxisAlignment.start, + // children: [ + // Text( + // 'Insurer', + // textAlign: TextAlign.left, + // style: GoogleFonts.poppins( + // fontSize: + // Responsive.isDesktop(context) ? 16 : 12, + // fontWeight: FontWeight.w400, + // color: Color(0xFF979797), + // ), + // ), + // Text( + // policyName ?? '', + // textAlign: TextAlign.left, + // style: GoogleFonts.poppins( + // fontSize: + // Responsive.isDesktop(context) ? 18 : 14, + // fontWeight: FontWeight.w600, + // color: Color(0xFF000000), + // ), + // ), + // ], + // ), + // ), + // ), + // Expanded( + // flex: 3, + // child: Container( + // alignment: Alignment.centerLeft, + // child: Column( + // mainAxisAlignment: MainAxisAlignment.start, + // crossAxisAlignment: CrossAxisAlignment.start, + // children: [ + // Text( + // 'Status', + // textAlign: TextAlign.center, + // style: GoogleFonts.poppins( + // fontSize: + // Responsive.isDesktop(context) ? 16 : 12, + // fontWeight: FontWeight.w400, + // color: Color(0xFF979797), + // ), + // ), + // Text( + // policyStatus ?? '', + // textAlign: TextAlign.center, + // style: GoogleFonts.poppins( + // fontSize: + // Responsive.isDesktop(context) ? 18 : 14, + // fontWeight: FontWeight.w600, + // color: policyStatus == 'Active' + // ? Colors.green + // : Colors.red, + // ), + // ), + // ], + // ), + // ), + // ), + // Expanded( + // flex: 4, + // child: Container( + // alignment: Alignment.centerRight, + // child: Column( + // mainAxisAlignment: MainAxisAlignment.end, + // crossAxisAlignment: CrossAxisAlignment.end, + // children: [ + // Text( + // sumInsuredLabel ?? '', + // textAlign: TextAlign.end, + // style: GoogleFonts.poppins( + // fontSize: + // Responsive.isDesktop(context) ? 16 : 12, + // fontWeight: FontWeight.w400, + // color: Color(0xFF979797), + // ), + // ), + // Text( + // '₹ $si_value', + // textAlign: TextAlign.end, + // style: GoogleFonts.poppins( + // fontSize: + // Responsive.isDesktop(context) ? 18 : 14, + // fontWeight: FontWeight.w600, + // color: Color(0xFF000000), + // ), + // ), + // ], + // ), + // ), + // ), + // Expanded( + // flex: 1, + // child: Container( + // alignment: Alignment.centerRight, + // child: Icon( + // Icons.chevron_right, + // color: Color(0xFFE26728), + // size: Responsive.isDesktop(context) ? 30 : 26, + // ), + // ), + // ), + // ], + // ), + // ), + // ), + // ), + // ); + // }, + // ), + // ]; + // } + List generateYourPlanList(List data) { return [ ListView.builder( shrinkWrap: true, - physics: NeverScrollableScrollPhysics(), + physics: const NeverScrollableScrollPhysics(), itemCount: data.length, itemBuilder: (BuildContext context, int index) { var item = data[index]; - String policyHeading = item['heading']; - String policyName = item['policy_name']; - String policyStatus = item['policy_status']; - String si_value = item['si_value']; - String policyEndDate = item['policy_end_date']; - String policyNo = item['policy_no']; - String clientPolicyId = item['client_policy_id']; - String sumInsuredLabel = item['sum_insured_label']; - List employeePolicy = item['EmployeePolicy']; + + // **CHECK IF THIS IS A RETAIL POLICY** + bool isRetail = item.containsKey('policy_transaction_id'); + + // ---------------------------- + // NORMAL POLICY DATA (GMC/GPA) + // ---------------------------- + String policyHeading = item['heading'] ?? ''; + String policyName = item['policy_name'] ?? ''; + String policyStatus = item['policy_status'] ?? ''; + String siValue = item['si_value'] ?? ''; + String sumInsuredLabel = item['sum_insured_label'] ?? ''; + + // ---------------------------- + // RETAIL POLICY DATA + // ---------------------------- + String insurerShortName = item['insurer_short_name'] ?? ''; + String policyType = item['policy_type'] ?? ''; + String vehicleNo = item['vehicle_no'] ?? ''; return GestureDetector( - onTap: () { - var details = {'claimsDetails': item, 'fromClaimPage': 0}; - context.go('/planclaimsform', extra: details); - }, - child: MouseRegion( - cursor: SystemMouseCursors.click, - child: Card( - elevation: 5, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(15.0), - ), - child: Container( - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(15.0), - ), - padding: EdgeInsets.symmetric(vertical: 15, horizontal: 15), - child: Row( - mainAxisAlignment: MainAxisAlignment - .spaceBetween, // Center content horizontally - crossAxisAlignment: - CrossAxisAlignment.center, // Center content vertically - children: [ - Expanded( - flex: 4, - child: Container( - alignment: Alignment.centerLeft, - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - 'Insurer', - textAlign: TextAlign.left, - style: GoogleFonts.poppins( - fontSize: - Responsive.isDesktop(context) ? 16 : 12, - fontWeight: FontWeight.w400, - color: Color(0xFF979797), - ), - ), - Text( - policyName ?? '', - textAlign: TextAlign.left, - style: GoogleFonts.poppins( - fontSize: - Responsive.isDesktop(context) ? 18 : 14, - fontWeight: FontWeight.w600, - color: Color(0xFF000000), - ), - ), - ], - ), - ), + onTap: () { + if (isRetail) { + // Retail policy click – allow clicking + var details = { + "retailDetails": item, + }; + context.go('/retailClaimForm', extra: details); + } else { + // Normal claim policy click + var details = { + "claimsDetails": item, + "fromClaimPage": 0, + }; + context.go('/planclaimsform', extra: details); + } + }, + child: MouseRegion( + cursor: SystemMouseCursors.click, + child: Card( + elevation: 5, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(15.0), + ), + child: Container( + padding: const EdgeInsets.symmetric(vertical: 15, horizontal: 15), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(15.0), ), - Expanded( - flex: 3, - child: Container( - alignment: Alignment.centerLeft, - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - 'Status', - textAlign: TextAlign.center, - style: GoogleFonts.poppins( - fontSize: - Responsive.isDesktop(context) ? 16 : 12, - fontWeight: FontWeight.w400, - color: Color(0xFF979797), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + // LEFT SECTION + Expanded( + flex: 4, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + isRetail ? "Insurer" : "Insurer", + style: GoogleFonts.poppins( + fontSize: Responsive.isDesktop(context) ? 16 : 12, + color: const Color(0xFF979797)), ), - ), - Text( - policyStatus ?? '', - textAlign: TextAlign.center, - style: GoogleFonts.poppins( - fontSize: - Responsive.isDesktop(context) ? 18 : 14, - fontWeight: FontWeight.w600, - color: policyStatus == 'Active' - ? Colors.green - : Colors.red, + Text( + isRetail ? insurerShortName : policyName, + style: GoogleFonts.poppins( + fontSize: Responsive.isDesktop(context) ? 18 : 14, + fontWeight: FontWeight.w600), ), - ), - ], + ], + ), ), - ), - ), - Expanded( - flex: 4, - child: Container( - alignment: Alignment.centerRight, - child: Column( - mainAxisAlignment: MainAxisAlignment.end, - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - Text( - sumInsuredLabel ?? '', - textAlign: TextAlign.end, - style: GoogleFonts.poppins( - fontSize: - Responsive.isDesktop(context) ? 16 : 12, - fontWeight: FontWeight.w400, - color: Color(0xFF979797), + + // CENTER (Status or Policy Type) + Expanded( + flex: 3, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + isRetail ? "Policy Type" : "Status", + style: GoogleFonts.poppins( + fontSize: Responsive.isDesktop(context) ? 16 : 12, + color: const Color(0xFF979797)), ), - ), - Text( - '₹ $si_value', - textAlign: TextAlign.end, - style: GoogleFonts.poppins( - fontSize: - Responsive.isDesktop(context) ? 18 : 14, - fontWeight: FontWeight.w600, - color: Color(0xFF000000), + Text( + isRetail ? policyType : policyStatus, + style: GoogleFonts.poppins( + fontSize: Responsive.isDesktop(context) ? 18 : 14, + fontWeight: FontWeight.w600, + color: isRetail + ? Colors.black + : (policyStatus == "Active" + ? Colors.green + : Colors.red), + ), ), - ), - ], + ], + ), ), - ), - ), - Expanded( - flex: 1, - child: Container( - alignment: Alignment.centerRight, - child: Icon( - Icons.chevron_right, - color: Color(0xFFE26728), - size: Responsive.isDesktop(context) ? 30 : 26, + + // RIGHT (Insured Name OR Sum Insured) + Expanded( + flex: 4, + child: Column( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Text( + isRetail ? "Vehicle No" : sumInsuredLabel, + style: GoogleFonts.poppins( + fontSize: Responsive.isDesktop(context) ? 16 : 12, + color: const Color(0xFF979797)), + ), + Text( + isRetail ? vehicleNo : "₹ $siValue", + style: GoogleFonts.poppins( + fontSize: Responsive.isDesktop(context) ? 18 : 14, + fontWeight: FontWeight.w600), + ), + ], + ), ), - ), + + // ARROW + const Icon(Icons.chevron_right, + color: Color(0xFFE26728), size: 26), + ], ), - ], + ), ), ), - ), - ), - ); + ); + }, ), ]; } + List generateTrackList(List data) { return [ ListView.builder( @@ -949,6 +1119,11 @@ class _claimsState extends State { String claimsDate = item['created_at'] ?? ''; String claimStatus = item['claim_status'] ?? ''; String claimsDepartmentName = item['ticket_policy_type'] ?? ''; + String rawClaimNumber = item['claim_number']?.toString().trim() ?? ''; + + String claimNumber = rawClaimNumber.isNotEmpty + ? ' - ($rawClaimNumber)' + : ''; // String claimsReplies = item['replies']; // List messageList = item['message_list']; @@ -989,6 +1164,16 @@ class _claimsState extends State { statusColor = Color(0xFF979797); // Default color if status is null } + bool isRetailPolicy = false; + + String? policyTxId = item['policy_transaction_id']; + + if (policyTxId != null && policyTxId.toString().trim().isNotEmpty) { + isRetailPolicy = true; // Retail + } else { + isRetailPolicy = false; // Nhance GMC/GPA + } + return GestureDetector( onTap: () { print("Tappp - $ticketId"); @@ -1003,16 +1188,13 @@ class _claimsState extends State { insetPadding: EdgeInsets.all(16), child: ClaimHistoryPopup( ticket_id: item['id'], - empName: item['emp_name'], // example - empCode: item['emp_code'], // example + empName: isRetailPolicy ? '' : item['emp_name'], + empCode: isRetailPolicy ? '' : item['emp_code'], policyType: item['ticket_policy_type']!, - // policyType: item['policy_type'], - // clientPolicyNo: item['client_policy_no'], clientPolicyNo: item['policy_no']?.toString(), claimAmount: item['claim_amount']?.toString(), - // claimNo: item['claim_no'], claimNo: item['claim_number']?.toString(), - // postToken: widget.postToken, + isRetailPolicy: isRetailPolicy, // ⬅️ NEW ), ); }, @@ -1047,17 +1229,32 @@ class _claimsState extends State { mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text( - claimsDepartmentName ?? '', - textAlign: TextAlign.left, - style: GoogleFonts.poppins( - fontSize: Responsive.isDesktop(context) - ? 18 - : 16, - fontWeight: FontWeight.w600, - color: Color(0xFF000000), + RichText( + text: TextSpan( + children: [ + // First Part: claimsDepartmentName → e.g., "GMC" + TextSpan( + text: claimsDepartmentName ?? '', + style: GoogleFonts.poppins( + fontSize: Responsive.isDesktop(context) ? 18 : 16, + fontWeight: FontWeight.w700, + color: const Color(0xFF000000), + ), + ), + + // Second Part: claimNumber → e.g., "(54534543)" + TextSpan( + text: '${claimNumber ?? ''}', + style: GoogleFonts.poppins( + fontSize: Responsive.isDesktop(context) ? 16 : 12, + fontWeight: FontWeight.w500, + color: const Color(0xFF555555), // lighter color + ), + ), + ], ), ), + SizedBox(height: 5), Tooltip( message: diff --git a/lib/pages/postEnrollment/claimshistory.dart b/lib/pages/postEnrollment/claimshistory.dart index 4324445..04ef615 100644 --- a/lib/pages/postEnrollment/claimshistory.dart +++ b/lib/pages/postEnrollment/claimshistory.dart @@ -1,10 +1,20 @@ +import 'dart:convert'; + +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:google_fonts/google_fonts.dart'; +import 'package:http/http.dart' as http; import 'package:nhance_app_pwa/pages/postEnrollment/service/api_service.dart'; +import 'package:nhance_app_pwa/pages/postEnrollment/service/file_upload_service.dart'; +import 'package:nhance_app_pwa/pages/postEnrollment/service/multi_file_upload_widget.dart'; import 'package:url_launcher/url_launcher.dart'; - +import 'package:file_picker/file_picker.dart'; +import 'package:pdf/widgets.dart' as pw; +import '../../config/environment.dart'; import '../../customAppBar/responsive.dart'; +import '../../customAppBar/toastHelper.dart'; +import '../service/TokenService.dart'; class ClaimHistoryPopup extends StatefulWidget { final String ticket_id; @@ -14,7 +24,7 @@ class ClaimHistoryPopup extends StatefulWidget { final String? clientPolicyNo; final String? claimAmount; final String? claimNo; - // final String postToken; + final bool isRetailPolicy; const ClaimHistoryPopup({ Key? key, @@ -25,49 +35,50 @@ class ClaimHistoryPopup extends StatefulWidget { this.clientPolicyNo, this.claimAmount, this.claimNo, - // required this.postToken, + required this.isRetailPolicy, }) : super(key: key); @override State createState() => _ClaimHistoryPopupState(); } -class _ClaimHistoryPopupState extends State { +class _ClaimHistoryPopupState extends State + with TickerProviderStateMixin { late ApiService apiService; List> getClaimsHistoryList = []; List stepKeys = []; late Map stepMap; - int _index = 4; bool isLoading = false; + dynamic _token; + + String? selectedFileNames; + // html.File? uploadedFile; + // List uploadedFiles = []; + List uploadedFiles = []; + final FileUploadService fileService = FileUploadService(); + List claimFiles = []; - // final List> claimFiles = [ - // { - // "claim_file_id": "23", - // "claim_file_name": "1757420176_c2c92a7393b75677a5bb.png", - // "url": "https://venbait.in/nhance/dev/downloadClaimFile/23" - // }, - // { - // "claim_file_id": "24", - // "claim_file_name": "1757420176_7ff43ded77125ae390d0.png", - // "url": "https://venbait.in/nhance/dev/downloadClaimFile/24" - // }, - // { - // "claim_file_id": "25", - // "claim_file_name": "1757420176_193139b931f3bbf792f2.png", - // "url": "https://venbait.in/nhance/dev/downloadClaimFile/25" - // }, - // { - // "claim_file_id": "26", - // "claim_file_name": "1757420176_f66188d9ebf2580ed0f1.png", - // "url": "https://venbait.in/nhance/dev/downloadClaimFile/26" - // } + List> requiredDocsList = []; + List> requiredDocsListBackup = []; + bool isSubmitting = false; + + bool isActionFreeze = false; + + // IR Docs state + bool showIRDocs = false; + // final List> irDocList = [ + // {"title": "Hospital Bill", "checked": false}, + // {"title": "Discharge Summary", "checked": false}, + // {"title": "Prescription", "checked": false}, // ]; + final Map _assignedFiles = {}; @override void initState() { super.initState(); apiService = ApiService(context); - // print(widget.postToken); + + // debug prints kept print("CLAIMHISTORY"); print(widget.claimAmount); print(widget.claimNo); @@ -83,56 +94,191 @@ class _ClaimHistoryPopupState extends State { super.dispose(); } + // ------------------------- + // File pick (web + mobile) + // ------------------------- + Future pickFile() async { + final result = await FilePicker.platform + .pickFiles(withData: true, allowMultiple: false); + if (result != null && result.files.isNotEmpty) { + return result.files.first; + } + return null; + } + + // ------------------------- + // Assign same picked file to all checked docs + // ------------------------- + Future handleUploadForSelected() async { + final checked = + requiredDocsList.where((d) => d['document_received'] == true).toList(); + if (checked.isEmpty) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text("Please select at least one document"))); + return; + } + + final picked = await pickFile(); + if (picked == null) return; + + setState(() { + for (var d in checked) { + final title = d['document_name'] as String; + _assignedFiles[title] = picked; + } + }); + } + + + + // ------------------------- + // Remove assignment + // ------------------------- + void removeAssignedFile(String title) { + setState(() { + _assignedFiles[title] = null; + }); + } + + // ------------------------- + // Submit IR Docs (placeholder) + // Replace with real API upload logic + // ------------------------- + Future submitIRDocs() async { + setState(() => isSubmitting = true); // 🔥 start loader + + try { + print('enter'); + _token = await TokenService.getPostToken(); + + // STEP 1: Must select at least one document type + final selectedDocs = requiredDocsList + .where((d) => d['document_received'] == true) + .toList(); + if (selectedDocs.isEmpty) { + ToastHelper.showErrorToast( + context, "Please select at least one IR document type"); + return; + } + + // STEP 2: Must upload at least one file + if (!MultiFileUploadWidget.hasFiles || fileService.files.isEmpty) { + ToastHelper.showErrorToast( + context, "Please upload at least one document"); + return; + } + + // STEP 3: All uploaded files must have a label + for (final uf in fileService.files) { + if ((uf.label ?? "").trim().isEmpty) { + ToastHelper.showErrorToast( + context, "Please enter name for all uploaded documents"); + return; + } + } + + // STEP 4: Prepare multipart request + final url = Uri.parse("${Environment.apiUrl}/uploadIRDocs"); + final request = http.MultipartRequest('POST', url); + + request.headers['Authorization'] = "Bearer $_token"; + request.headers['APP-SIGNATURE'] = 'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y'; + + final requiredDocsPayload = { + "is_action_freeze": isActionFreeze, + "docs": requiredDocsList.map((d) { + return { + "document_name": d["document_name"], + "document_received": d["document_received"], + }; + }).toList(), + }; + + request.fields["required_docs"] = jsonEncode(requiredDocsPayload); + + // Add mapped fields if required + request.fields['ticket_id'] = widget.ticket_id; + + // STEP 5: Convert image → PDF and attach files + for (var uf in fileService.files) { + final pf = uf.file; + final ext = pf.extension?.toLowerCase() ?? ""; + + Uint8List fileBytes = pf.bytes!; + + // if (['jpg', 'jpeg', 'png', 'heic'].contains(ext)) { + // final pdf = pw.Document(); + // final image = pw.MemoryImage(fileBytes); + // + // pdf.addPage( + // pw.Page( + // build: (context) => pw.Center(child: pw.Image(image)), + // ), + // ); + // + // fileBytes = await pdf.save(); + // + // final pdfName = pf.name.replaceAll(RegExp(r'\.\w+$'), '.pdf'); + // + // request.files.add(http.MultipartFile.fromBytes( + // "claim_docs[]", + // fileBytes, + // filename: pdfName, + // )); + // } else { + request.files.add(http.MultipartFile.fromBytes( + "claim_docs[]", + fileBytes, + filename: pf.name, + )); + // } + } + + // STEP 6: Add names for these IR docs + final labels = fileService.files.map((f) => f.label.trim()).toList(); + request.fields['claim_doc_names'] = jsonEncode(labels); + + // Debug + print( + "Files uploaded: ${fileService.files.map((e) => e.file.name).toList()}"); + print("Labels: $labels"); + + // STEP 7: Send the request + final response = await request.send(); + final responseBody = await response.stream.bytesToString(); + final decoded = jsonDecode(responseBody); + + if (decoded["status"] == true) { + ToastHelper.showSuccessToast(context, decoded["message"]); + + _resetIRDocs(); + await getClaimsHistoryDetails(); // <--- refresh checkbox state from API + setState(() => showIRDocs = false); + } else { + ToastHelper.showErrorToast( + context, "Upload failed: ${decoded['message']}"); + } + } catch (e) { + ToastHelper.showErrorToast(context, "Upload failed"); + } finally { + setState(() => isSubmitting = false); // 🔥 stop loader + } + } + + // ------------------------- + // Existing API call (unchanged) + // ------------------------- Future getClaimsHistoryDetails() async { setState(() { isLoading = true; }); try { - print('10'); - // final ticketID = widget.ticket_id; - // if (ticketID != '' || ticketID != null) { - // return; - // } final response = await apiService.getClaimsHistoryToApi(widget.ticket_id); if (response['status'] == 'success') { setState(() { isLoading = false; - print('Claims History List1'); final claimsDocs = response['data']['claim_files']; - print("TC1 - $claimsDocs"); - claimFiles = List>.from(claimsDocs); - print('claimFiles $claimFiles'); - // final List ticketDataList = response['data']['ticket_data']; - // final Map ticketDataMap = - // response['data']['ticket_data']; - // print('Claims History List12'); - // print('Claims History List1- $ticketDataMap'); - - // getClaimsHistoryList = ticketDataList - // .map((item) => Map.from(item)) - // .toList(); - - // getClaimsHistoryList = ticketDataMap.entries.map((entry) { - // return { - // 'status': entry.key, - // ...Map.from(entry.value), - // }; - // }).toList(); - // - // getClaimsHistoryList = ticketDataMap.entries.map((entry) { - // return { - // 'status': entry.key, - // ...Map.from(entry.value), - // }; - // }).toList(); - - // if (getClaimsHistoryList.isNotEmpty) { - // stepMap = getClaimsHistoryList[0]; - // stepKeys = stepMap.keys.toList(); - // } - - print('Claims History List: $getClaimsHistoryList'); getClaimsHistoryList = [ Map.from(response['data']['ticket_data']) @@ -140,118 +286,161 @@ class _ClaimHistoryPopupState extends State { stepMap = getClaimsHistoryList[0]; stepKeys = stepMap.keys.toList(); - // originalData = getCDPolicies; - // filteredData = List.from(originalData); - // print('filteredData'); - // print(filteredData); + final isFreeze = + response['data']['required_docs']['is_action_freeze'] ?? false; + setState(() { + isActionFreeze = isFreeze; + }); + final requiredDocs = response['data']['required_docs']['docs']; + requiredDocsList = List>.from(requiredDocs); + +// ⭐ Make a backup copy to restore later + requiredDocsListBackup = requiredDocsList + .map((doc) => { + "document_name": doc["document_name"], + "document_received": doc["document_received"], + }) + .toList(); + + print(isActionFreeze); + print('requiredDocsList $requiredDocsList'); + + for (var d in requiredDocsList) { + _assignedFiles[d['document_name']] = null; + } }); } else { - setState(() { - isLoading = false; - }); - - // ToastHelper.showWarningToast( - // context, 'Request failed with status: ${response.statusCode}'); + setState(() => isLoading = false); print('Request failed with status: ${response['code']}'); } } catch (e) { - setState(() { - isLoading = false; - }); + setState(() => isLoading = false); print('Exception occurred: $e'); - } finally { - setState(() { - // _isLoading = false; - }); } } Future _launchURL(String url) async { - final Uri uri = Uri.parse(url); // Parse the URL properly - print('_launchURL $uri'); - if (uri != null) { - print('If $uri'); + final Uri uri = Uri.parse(url); + try { await launchUrl(uri, mode: LaunchMode.externalApplication); - } else { - print('else $uri'); - throw 'Could not launch $url'; + } catch (e) { + print('Could not launch URL: $e'); } } + void _resetIRDocs() { + // Restore checkbox values from API backup + requiredDocsList = requiredDocsListBackup + .map((doc) => { + "document_name": doc["document_name"], + "document_received": doc["document_received"], + }) + .toList(); + + // Clear assigned uploaded files + _assignedFiles.updateAll((key, value) => null); + + // Reset multi-file upload widget service + fileService.clearAll(); + MultiFileUploadWidget.hasFiles = false; + + setState(() {}); + } + + // ------------------------- + // Build + // ------------------------- @override Widget build(BuildContext context) { - // if (getClaimsHistoryList.isEmpty) { - // return SizedBox( - // height: 50, - // child: Center(child: Text('No available Claims')), - // ); - // } + // responsive decisions + final mediaW = MediaQuery.of(context).size.width; + final isMobile = mediaW < 600; + final panelWidth = + isMobile ? MediaQuery.of(context).size.width * 0.95 : 320.0; + final keyValueWidgets = [ _buildKeyValue( - 'Name', - '${widget.empName ?? ''} (${widget.empCode ?? ''})', - ), + 'Name', '${widget.empName ?? ''} (${widget.empCode ?? ''})'), SizedBox( width: Responsive.isDesktop(context) ? 16 : 0, height: Responsive.isDesktop(context) ? 0 : 8), - _buildKeyValue( - 'Policy Name', - '${widget.policyType ?? ''} - ${widget.clientPolicyNo ?? ''}', - ), + _buildKeyValue('Policy Name', + '${widget.policyType ?? ''} - ${widget.clientPolicyNo ?? ''}'), ]; final keyValueWidgetRow = [ _buildKeyValue( - 'Claims Amount', - widget.claimAmount == null || widget.claimAmount!.trim().isEmpty - ? 'N/A' - : '₹${widget.claimAmount}', - ), + 'Claims Amount', + widget.claimAmount == null || widget.claimAmount!.trim().isEmpty + ? 'N/A' + : '₹${widget.claimAmount}'), SizedBox( width: Responsive.isDesktop(context) ? 16 : 0, height: Responsive.isDesktop(context) ? 0 : 8), _buildKeyValue( - 'Claims Number', - widget.claimNo == null || widget.claimNo!.trim().isEmpty - ? 'N/A' - : widget.claimNo!, - ), + 'Claims Number', + widget.claimNo == null || widget.claimNo!.trim().isEmpty + ? 'N/A' + : widget.claimNo!), ]; return PopScope( - canPop: false, - onPopInvokedWithResult: (didPop, result) { - if (didPop) return; - context.pop(); - }, - child: Container( - constraints: BoxConstraints(maxWidth: 800, maxHeight: 800), - padding: EdgeInsets.all(20), - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(12), - ), - child: SingleChildScrollView( + canPop: false, + onPopInvokedWithResult: (didPop, result) { + if (didPop) return; + context.pop(); + }, + child: Container( + // keep popup sized; adjust if required + constraints: BoxConstraints(maxWidth: 900, maxHeight: 820), + padding: EdgeInsets.all(20), + decoration: BoxDecoration( + color: Colors.white, borderRadius: BorderRadius.circular(12)), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ + // Header row (keeps IR docs icon + close inside popup header) Row( children: [ Expanded( - child: Align( - alignment: Alignment.centerLeft, - child: Text( - 'Claims History', - style: GoogleFonts.poppins( - fontSize: Responsive.isDesktop(context) ? 20 : 16, - fontWeight: FontWeight.w500, - color: Color(0xFF101010), - ), + child: Text( + 'Claims History', + style: GoogleFonts.poppins( + fontSize: Responsive.isDesktop(context) ? 20 : 16, + fontWeight: FontWeight.w500, + color: Color(0xFF101010), ), ), ), + + // IR Docs icon + if (!isActionFreeze && requiredDocsList.isNotEmpty) + MouseRegion( + cursor: SystemMouseCursors.click, + child: GestureDetector( + onTap: () { + setState(() { + showIRDocs = true; + }); + }, + child: Container( + height: 34, + width: 34, + margin: EdgeInsets.only(right: 8), + decoration: BoxDecoration( + color: Colors.white, + border: Border.all(color: Color(0xFFBCBCBC)), + borderRadius: BorderRadius.circular(6), + ), + child: Icon(Icons.folder_open, + size: 20, color: Color(0xFF00A5A8)), + ), + ), + ), + + // Close popup MouseRegion( - cursor: SystemMouseCursors.click, // Show pointer cursor + cursor: SystemMouseCursors.click, child: GestureDetector( onTap: () => context.pop(), child: Container( @@ -262,283 +451,811 @@ class _ClaimHistoryPopupState extends State { border: Border.all(color: Color(0xFFBCBCBC)), borderRadius: BorderRadius.circular(6), ), - child: Icon( - Icons.close, - size: 25, - color: Color(0xFFBCBCBC), - ), + child: + Icon(Icons.close, size: 25, color: Color(0xFFBCBCBC)), ), ), ) ], ), - SizedBox(height: Responsive.isDesktop(context) ? 10 : 5), + + SizedBox(height: Responsive.isDesktop(context) ? 10 : 6), + + // BODY: for mobile we will show either the Claim content or full IR Docs content (switch) + // for desktop we show Row with left content and optional right panel + // Expanded( child: isMobile ? AnimatedSwitcher( duration: Duration(milliseconds: 300), transitionBuilder: (child, animation) { final offsetAnimation = Tween( begin: Offset(0, 1), end: Offset(0, 0)) .animate(animation); return SlideTransition( position: offsetAnimation, child: child); }, child: showIRDocs ? _buildMobileFullIrDocs(panelWidth, key: ValueKey('mobile_ir')) : _buildMainLeftContent(key: ValueKey('main_left')), ) : Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ // LEFT: main content - scrollable Expanded(child: _buildMainLeftContent()), // RIGHT: IR panel - desktop inline (only visible on wide screens) AnimatedContainer( duration: Duration(milliseconds: 300), curve: Curves.easeInOut, width: showIRDocs ? panelWidth : 0, child: showIRDocs ? _buildIrDocsPanel(panelWidth, isMobile: false) : const SizedBox.shrink(), ), ], ), ), + Expanded( + child: isMobile + ? AnimatedSwitcher( + duration: Duration(milliseconds: 300), + transitionBuilder: (child, animation) { + final offsetAnimation = Tween( + begin: Offset(0, 1), end: Offset(0, 0)) + .animate(animation); + return SlideTransition( + position: offsetAnimation, child: child); + }, + child: showIRDocs + ? buildIRDocsContent( + isDesktop: false, + key: ValueKey('mobile_ir'), + ) + : _buildMainLeftContent(key: ValueKey('main_left')), + ) + : Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // LEFT: main content - scrollable + Expanded(child: _buildMainLeftContent()), + + // RIGHT: IR panel - desktop inline (only visible on wide screens) + AnimatedContainer( + duration: Duration(milliseconds: 300), + curve: Curves.easeInOut, + width: showIRDocs ? panelWidth : 0, + child: showIRDocs + ? buildIRDocsContent( + isDesktop: true, + panelWidth: panelWidth, + key: ValueKey('desktop_ir'), + ) + : SizedBox.shrink(), + ), + ], + ), + ), + ], + ), + ), + ); + } + + // ------------------------- + // Main left content extracted to keep code tidy + // ------------------------- + Widget _buildMainLeftContent({Key? key}) { + return SingleChildScrollView( + key: key, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Claim Details card (unchanged) + if(!widget.isRetailPolicy) Container( - margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 20), + margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 6), decoration: BoxDecoration( - color: Color(0xFFFFFFFF), // White background + color: Colors.white, borderRadius: BorderRadius.circular(12), boxShadow: [ BoxShadow( - color: Color(0xFFEBEBEB), // Shadow color - blurRadius: 14, // How soft the shadow is - spreadRadius: 2, // How much it spreads - offset: Offset(0, 1), // X and Y offset + color: const Color(0xFFEBEBEB), + blurRadius: 14, + spreadRadius: 2, + offset: const Offset(0, 1), ), ], ), padding: const EdgeInsets.all(16), - child: Center( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Responsive.isDesktop(context) - ? Row( - children: keyValueWidgets - .map((w) => Expanded(child: w)) - .toList(), - ) - : Container( - width: MediaQuery.of(context).size.width, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: keyValueWidgets), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + + /// ----------------------------------------- + /// NAME + POLICY NAME SECTION + /// ----------------------------------------- + Responsive.isDesktop(context) + ? Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: _buildKeyValue( + 'Name', + '${widget.empName ?? ''} (${widget.empCode ?? ''})', + ), + ), + const SizedBox(width: 24), + Expanded( + child: _buildKeyValue( + 'Policy Name', + widget.clientPolicyNo == null || + widget.clientPolicyNo!.trim().isEmpty + ? (widget.policyType ?? '') + : '${widget.policyType ?? ''} - ${widget.clientPolicyNo}', + ), + ), + + // Expanded( + // child: _buildKeyValue( + // 'Policy Name', + // '${widget.policyType ?? ''} - ${widget.clientPolicyNo ?? ''}', + // ), + // ), + ], + ) + : SizedBox( + width: double.infinity, // ⬅️ forces full width + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _buildKeyValue( + 'Name', + '${widget.empName ?? ''} (${widget.empCode ?? ''})', + ), + + const SizedBox(height: 12), + + _buildKeyValue( + 'Policy Name', + '${widget.policyType ?? ''} - ${widget.clientPolicyNo ?? ''}', + ), + ], ), - // Row( - // children: [ - // Expanded( - // child: _buildKeyValue('Name', - // '${widget.empName ?? ''} (${widget.empCode ?? ''})'), - // ), - // SizedBox(width: 16), - // Expanded( - // child: _buildKeyValue('Policy Name', - // '${widget.policyType ?? ''} - ${widget.clientPolicyNo ?? ''}')), - // ], - // ), - SizedBox(height: Responsive.isDesktop(context) ? 16 : 8), - Responsive.isDesktop(context) - ? Row( - children: keyValueWidgetRow - .map((w) => Expanded(child: w)) - .toList(), - ) - : Container( - width: MediaQuery.of(context).size.width, - child: Column( + ), + + const SizedBox(height: 24), + + /// ----------------------------------------- + /// CLAIM AMOUNT + CLAIM NUMBER SECTION + /// ----------------------------------------- + Responsive.isDesktop(context) + ? Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: _buildKeyValue( + 'Claims Amount', + (widget.claimAmount == null || + widget.claimAmount!.trim().isEmpty) + ? 'N/A' + : '₹${widget.claimAmount}', + ), + ), + const SizedBox(width: 24), + Expanded( + child: _buildKeyValue( + 'Claims Number', + (widget.claimNo == null || + widget.claimNo!.trim().isEmpty) + ? 'N/A' + : widget.claimNo!, + ), + ), + ], + ) + : Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _buildKeyValue( + 'Claims Amount', + (widget.claimAmount == null || + widget.claimAmount!.trim().isEmpty) + ? 'N/A' + : '₹${widget.claimAmount}', + ), + const SizedBox(height: 8), + _buildKeyValue( + 'Claims Number', + (widget.claimNo == null || + widget.claimNo!.trim().isEmpty) + ? 'N/A' + : widget.claimNo!, + ), + ], + ), + ], + ), + ), + + SizedBox(height: 10), + + // Loading or step content + isLoading + ? Container( + child: Center( + child: Image.asset( + height: 60, width: 60, 'assets/nhance-loader.gif'))) + : Container( + child: getClaimsHistoryList.isNotEmpty + ? Column( crossAxisAlignment: CrossAxisAlignment.start, - children: keyValueWidgetRow), + children: List.generate(stepKeys.length, (index) { + String stepTitleKey = stepKeys[index]; + Map stepData = + stepMap[stepTitleKey]; + Widget content = _getStepContentFromApi(stepData); + return _buildStep( + stepNumber: index + 1, + title: + _getStepTitleFromApi(stepTitleKey, stepData), + content: content, + isLast: index == stepKeys.length - 1, + ); + }), + ) + : Container( + height: MediaQuery.of(context).size.height * 0.4, + child: Center( + child: Column( + children: [ + Image.asset('assets/searchData.jpg', + width: 200, height: 200, fit: BoxFit.cover), + Text('No Claims History', + style: TextStyle( + fontWeight: FontWeight.w500, + fontSize: 15)), + ], + ), + ), + ), + ), + + // Claim files list + if(!widget.isRetailPolicy) + Container( + margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 20), + child: claimFiles.isEmpty + ? const Center(child: Text("No files available")) + : SizedBox( + height: 300, + child: ListView.builder( + itemCount: claimFiles.length, + itemBuilder: (context, index) { + final file = claimFiles[index]; + return Card( + child: ListTile( + leading: const Icon(Icons.insert_drive_file, + color: Colors.blue), + title: Text(file['claim_file_name']), + trailing: + const Icon(Icons.download, color: Colors.green), + onTap: () => _launchURL(file['url']), + ), + ); + }, + ), + ), + ), + ], + ), + ); + } + + Widget buildIRDocsContent({ + required bool isDesktop, + Key? key, + double? panelWidth, + }) { + return Container( + key: key ?? ValueKey("ir_docs_unified"), + width: isDesktop ? panelWidth ?? 320 : double.infinity, + height: double.infinity, + padding: EdgeInsets.all(12), + decoration: BoxDecoration( + color: Colors.white, + border: Border( + left: BorderSide( + color: Responsive.isDesktop(context) + ? Colors.grey.shade300 + : Colors.transparent, + width: Responsive.isDesktop(context) ? 1 : 0)), + ), + // color: Colors.white, + child: SafeArea( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // ----------------------------- + // HEADER (same for mobile + web) + // ----------------------------- + Row( + children: [ + Expanded( + child: Text( + "IR Docs", + style: GoogleFonts.poppins( + fontSize: isDesktop ? 16 : 18, + fontWeight: FontWeight.w600, + ), + ), + ), + IconButton( + icon: Icon(Icons.close), + onPressed: () { + _resetIRDocs(); + setState(() => showIRDocs = false); + }, + ), + ], + ), + + SizedBox(height: isDesktop ? 8 : 8), + + Text( + "Select documents, click Upload to assign the same file to all selected items.", + style: GoogleFonts.poppins( + fontSize: isDesktop ? 12 : 13, + color: Colors.grey.shade700, + ), + ), + + SizedBox(height: 12), + + // ----------------------------- + // SCROLLABLE BODY + // ----------------------------- + Expanded( + child: SingleChildScrollView( + child: Column( + children: [ + // Checkbox list (same UI for both) + ...requiredDocsList.map((d) { + final title = d["document_name"] as String; + return CheckboxListTile( + value: d["document_received"] as bool, + onChanged: (v) => + setState(() => d["document_received"] = v), + title: Text(title), + controlAffinity: isDesktop + ? ListTileControlAffinity.leading + : ListTileControlAffinity.trailing, + contentPadding: EdgeInsets.symmetric(horizontal: 8), + ); + }).toList(), + + SizedBox(height: 12), + + // Multi uploader (same UI) + Padding( + padding: const EdgeInsets.all(16.0), + child: const MultiFileUploadWidget(forceMobile: true), + ), + + SizedBox(height: 12), + + // Assigned file preview + ..._assignedFiles.entries + .where((e) => e.value != null) + .map((e) { + final title = e.key; + final file = e.value!; + return Card( + margin: + EdgeInsets.symmetric(horizontal: 8, vertical: 6), + child: ListTile( + title: Text(title), + subtitle: Text( + file.name, + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + trailing: IconButton( + icon: Icon(Icons.cancel, color: Colors.red), + onPressed: () => removeAssignedFile(title), + ), + ), + ); + }).toList(), + + SizedBox(height: 16), + ], + ), + ), + ), + + SizedBox(height: 8), + + // ----------------------------- + // SUBMIT BUTTON + // ----------------------------- + ElevatedButton( + onPressed: () => submitIRDocs(), + style: ElevatedButton.styleFrom( + backgroundColor: const Color(0xFFE26728), + minimumSize: Size(double.infinity, 50), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(5), + ), + ), + child: isSubmitting + ? SizedBox( + height: 22, + width: 22, + child: CircularProgressIndicator( + strokeWidth: 2, + color: Colors.white, + ), + ) + : Text( + 'Submit IR Docs', + style: GoogleFonts.poppins( + color: Colors.white, + fontWeight: FontWeight.w500, + ), + ), + ), + SizedBox(height: 10), + + // ----------------------------- + // CANCEL BUTTON + // ----------------------------- + ElevatedButton( + onPressed: () { + _resetIRDocs(); + setState(() => showIRDocs = false); + }, + style: ElevatedButton.styleFrom( + backgroundColor: Colors.white, + minimumSize: Size(double.infinity, 50), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(5), + side: const BorderSide(color: Color(0xFFE26728)), + ), + ), + child: Text( + "Cancel", + style: GoogleFonts.poppins( + color: Color(0xFFE26728), + fontWeight: FontWeight.w500, + ), + ), + ), + ], + ), + ), + ); + } + + // ------------------------- + // Mobile full IR Docs panel (replaces the main content) + // ------------------------- + Widget _buildMobileFullIrDocs(double panelWidth, {Key? key}) { + return Container( + key: key ?? ValueKey('mobile_full_ir'), + width: double.infinity, + height: double.infinity, + padding: EdgeInsets.all(12), + color: Colors.white, + child: SafeArea( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // header row with close (keeps popup header outside) + Row( + children: [ + Expanded( + child: Text("IR Docs", + style: GoogleFonts.poppins( + fontSize: 18, fontWeight: FontWeight.w600))), + IconButton( + icon: Icon(Icons.close), + onPressed: () { + _resetIRDocs(); + setState(() => showIRDocs = false); + }, + ), + ], + ), + SizedBox(height: 8), + Text( + "Select documents, click Upload to assign the same file to all selected items.", + style: GoogleFonts.poppins( + fontSize: 13, color: Colors.grey.shade700)), + SizedBox(height: 12), + + Expanded( + child: SingleChildScrollView( + child: Column( + children: [ + ...requiredDocsList.map((d) { + final title = d['document_name'] as String; + return CheckboxListTile( + value: d['document_received'] as bool, + onChanged: (v) => + setState(() => d['document_received'] = v), + title: Text(title), + controlAffinity: ListTileControlAffinity.trailing, + contentPadding: EdgeInsets.symmetric(horizontal: 8.0), + ); + }).toList(), + + SizedBox(height: 12), + Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + children: [ + const MultiFileUploadWidget(forceMobile: true), + ], + ), + ), + // Padding( + // padding: const EdgeInsets.symmetric(horizontal: 8.0), + // child: ElevatedButton.icon( + // onPressed: handleUploadForSelected, + // icon: Icon(Icons.upload_file), + // label: Text("Upload for selected"), + // style: ElevatedButton.styleFrom( + // backgroundColor: Color(0xFF00A5A8), + // minimumSize: Size(double.infinity, 50), + // shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), + // ), + // ), + // ), + + SizedBox(height: 12), + + ..._assignedFiles.entries + .where((e) => e.value != null) + .map((e) { + final title = e.key; + final file = e.value!; + return Card( + margin: + EdgeInsets.symmetric(horizontal: 8, vertical: 6), + child: ListTile( + title: Text(title), + subtitle: Text(file.name, + maxLines: 1, overflow: TextOverflow.ellipsis), + trailing: IconButton( + icon: Icon(Icons.cancel, color: Colors.red), + onPressed: () => removeAssignedFile(title)), + ), + ); + }).toList(), + + SizedBox(height: 16), + ], + ), + ), + ), + + Container( + alignment: Alignment.center, + child: ElevatedButton( + onPressed: () { + submitIRDocs(); + }, + style: ElevatedButton.styleFrom( + backgroundColor: const Color(0xFFE26728), // Orange button + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(5), + ), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + 'Submit IR Docs', + style: GoogleFonts.poppins( + color: Colors.white, + fontWeight: FontWeight.w500, + ), ), ], ), ), ), SizedBox(height: 10), - // Container( - // padding: const EdgeInsets.all(16), - // child: Stepper( - // currentStep: _index, - // onStepCancel: () { - // if (_index > 0) { - // setState(() { - // _index -= 1; - // }); - // } - // }, - // onStepContinue: () { - // if (_index < 4) { - // setState(() { - // _index += 1; - // }); - // } - // }, - // onStepTapped: (int index) { - // setState(() { - // _index = index; - // }); - // }, - // steps: [ - // Step( - // title: Text('Step 1: PAYMENT INITIATED'), - // content: Text('Pay Initiate Date: 05-05-2025'), - // isActive: true, - // state: StepState.complete, - // ), - // Step( - // title: Text('Step 2: INFORMATION REQUIRED'), - // content: Text('Raised Date: 05-05-2025'), - // isActive: true, - // state: StepState.complete, - // ), - // Step( - // title: Text('Step 3: CLAIM NO. UPDATION'), - // content: Column( - // crossAxisAlignment: CrossAxisAlignment.start, - // children: [ - // Text('Claim Number: 4315440'), - // Text('Registration Date: 05-05-2025'), - // ], - // ), - // isActive: true, - // state: StepState.complete, - // ), - // Step( - // title: Text('Step 4: QUERY DOCUMENT REQUIRED'), - // content: Text('Query Received Date: 05-05-2025'), - // isActive: true, - // state: StepState.complete, - // ), - // Step( - // title: Text('Step 5: APPROVED'), - // content: Column( - // crossAxisAlignment: CrossAxisAlignment.start, - // children: [ - // Text('Approved Amount: 4315440'), - // Text('Approved Date: 05-05-2025'), - // Text('Approved Letter: Lorem ipsum...'), - // Text('Description: Lorem ipsum...'), - // ], - // ), - // isActive: true, - // state: StepState.complete, - // ), - // ], - // ), - // ) - // Column( - // crossAxisAlignment: CrossAxisAlignment.start, - // children: List.generate(5, (index) { - // return _buildStep( - // stepNumber: index + 1, - // title: _getStepTitle(index), - // content: _getStepContent(index), - // isLast: index == 4, - // ); - // }), - // ) - - isLoading - ? Container( - // color: Color(0x98FFFCE5), // Semi-transparent background - child: Center( - child: // Your GIF loader widget - Image.asset( - height: 60, - width: 60, - 'assets/nhance-loader.gif'), // Adjust path to your GIF loader - ), - ) - : Container( - child: getClaimsHistoryList.isNotEmpty - ? Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: List.generate(stepKeys.length, (index) { - String stepTitleKey = stepKeys[index]; - Map stepData = - stepMap[stepTitleKey]; - - print('stepTitleKey'); - print(stepTitleKey); - print('stepData'); - print(stepData); - - Widget content = _getStepContentFromApi(stepData); - - return _buildStep( - stepNumber: index + 1, - title: _getStepTitleFromApi( - stepTitleKey, stepData), - content: content, - // content: _getStepContentFromApi(stepData), - isLast: index == stepKeys.length - 1, - ); - }), - ) - : Container( - height: MediaQuery.of(context).size.height * 0.4, - // color: Colors.red, - child: Center( - child: Column( - children: [ - Image.asset( - 'assets/searchData.jpg', // Replace 'default_image.png' with your default image asset path - width: 200, - height: 200, - fit: BoxFit.cover, - ), - Text( - 'No Claims History', - style: TextStyle( - fontWeight: FontWeight.w500, - fontSize: 15), - ), - ], - )), - ), - ), - Container( - margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 20), - child: claimFiles.isEmpty - ? const Center(child: Text("No files available")) - : SizedBox( // ✅ Give ListView a fixed height - height: 300, - child: ListView.builder( - itemCount: claimFiles.length, - itemBuilder: (context, index) { - final file = claimFiles[index]; - return Card( - child: ListTile( - leading: const Icon(Icons.insert_drive_file, color: Colors.blue), - title: Text(file['claim_file_name']), - trailing: const Icon(Icons.download, color: Colors.green), - onTap: () => _launchURL(file['url']), - ), - ); - }, + alignment: Alignment.center, + child: ElevatedButton( + onPressed: () { + _resetIRDocs(); + setState(() => showIRDocs = false); + }, + style: ElevatedButton.styleFrom( + backgroundColor: Colors.white, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(5), + side: const BorderSide(color: Color(0xFFE26728)), + ), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + 'Cancel', + style: + GoogleFonts.poppins(color: const Color(0xFFE26728)), + ), + ], ), ), - ) ], ), ), - ) ); } + // ------------------------- + // Builds IR Docs panel (desktop) + // ------------------------- + Widget _buildIrDocsPanel(double panelWidth, {required bool isMobile}) { + // The panel has internal scrolling so it won't overflow + return Container( + width: panelWidth, + padding: EdgeInsets.all(12), + decoration: BoxDecoration( + color: Colors.white, + border: Border(left: BorderSide(color: Colors.grey.shade300, width: 1)), + ), + child: SafeArea( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // header row + Row( + children: [ + Expanded( + child: Text("IR Docs", + style: GoogleFonts.poppins( + fontSize: 16, fontWeight: FontWeight.w600))), + IconButton( + icon: Icon(Icons.close), + onPressed: () { + _resetIRDocs(); + setState(() => showIRDocs = false); + }, + ) + ], + ), + SizedBox(height: 8), + Text( + "Select documents, click Upload to assign the same file to all selected items.", + style: GoogleFonts.poppins( + fontSize: 12, color: Colors.grey.shade700)), + SizedBox(height: 12), + + // list + upload + preview inside scroll + Expanded( + child: SingleChildScrollView( + child: Column( + children: [ + // checkboxes + ...requiredDocsList.map((d) { + final title = d['document_name'] as String; + return CheckboxListTile( + value: d['document_received'] as bool, + onChanged: (v) => + setState(() => d['document_received'] = v), + title: Text(title), + ); + }).toList(), + + SizedBox(height: 8), + + // upload button + // Padding( + // padding: const EdgeInsets.symmetric(horizontal: 4.0), + // child: ElevatedButton.icon( + // onPressed: handleUploadForSelected, + // icon: Icon(Icons.upload_file), + // label: Text("Upload for selected"), + // style: ElevatedButton.styleFrom( + // backgroundColor: Color(0xFF00A5A8), + // minimumSize: Size(double.infinity, 44), + // shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(22)), + // ), + // ), + // ), + Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + children: [ + const MultiFileUploadWidget(forceMobile: true), + ], + ), + ), + + SizedBox(height: 12), + + // assigned files preview + ..._assignedFiles.entries + .where((e) => e.value != null) + .map((e) { + final title = e.key; + final file = e.value!; + return Card( + child: ListTile( + title: Text(title), + subtitle: Text(file.name, + maxLines: 1, overflow: TextOverflow.ellipsis), + trailing: IconButton( + icon: Icon(Icons.cancel, color: Colors.red), + onPressed: () => removeAssignedFile(title)), + ), + ); + }).toList(), + + SizedBox(height: 16), + ], + ), + ), + ), + + // Submit button (keeps at bottom) + + Container( + alignment: Alignment.center, + child: ElevatedButton( + onPressed: () { + submitIRDocs(); + }, + style: ElevatedButton.styleFrom( + backgroundColor: const Color(0xFFE26728), // Orange button + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(5), + ), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + 'Submit IR Docs', + style: GoogleFonts.poppins( + color: Colors.white, + fontWeight: FontWeight.w500, + ), + ), + ], + ), + ), + ), + SizedBox(height: 10), + Container( + alignment: Alignment.center, + child: ElevatedButton( + onPressed: () { + _resetIRDocs(); + setState(() => showIRDocs = false); + }, + style: ElevatedButton.styleFrom( + backgroundColor: Colors.white, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(5), + side: const BorderSide(color: Color(0xFFE26728)), + ), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + 'Cancel', + style: + GoogleFonts.poppins(color: const Color(0xFFE26728)), + ), + ], + ), + ), + ) + ], + ), + ), + ); + } + + // ------------------------- + // UI Helpers & existing functions below (unchanged) + // ------------------------- Widget _buildStep({ required int stepNumber, - // required String title, required Widget title, required Widget content, bool isLast = false, }) { - print(title); - final noContent; - if ((content as Column).children.isEmpty) { - noContent = 0; - } else { - noContent = 1; - } + final noContent = (content as Column).children.isEmpty ? 0 : 1; return Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ - // Left Column with circle + line Column( children: [ - // Add top spacing before circle SizedBox(height: stepNumber == 1 ? 0 : 4), - - // Step number circle Container( height: 28, width: 28, decoration: BoxDecoration( - color: Color(0xFF00A5A8), - shape: BoxShape.circle, - ), + color: Color(0xFF00A5A8), shape: BoxShape.circle), alignment: Alignment.center, child: Text( '$stepNumber', @@ -549,24 +1266,16 @@ class _ClaimHistoryPopupState extends State { ), ), ), - - // Dotted line below circle (except for last step) if (!isLast) Container( height: noContent == 1 ? 50 : 20, - // increase to extend line width: 2, margin: EdgeInsets.only(top: 4, bottom: 4), - child: CustomPaint( - painter: DottedLinePainter(), - ), + child: CustomPaint(painter: DottedLinePainter()), ), ], ), - SizedBox(width: 12), - - // Right Side: Step title and content Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -592,16 +1301,11 @@ class _ClaimHistoryPopupState extends State { ); } - // String _getStepTitleFromApi(String status, Map data) { - // final modifiedBy = data['modified_by'] ?? ''; - // final modifiedAt = data['modified_at'] ?? ''; - // return '$status ($modifiedBy – $modifiedAt)'; - // } - Widget _getStepTitleFromApi(String status, Map data) { final modifiedBy = data['modified_by'] ?? ''; final modifiedAt = data['modified_at'] ?? ''; - final symbol = (data['modified_by'] != null && data['modified_by'] != '') ? ' - ' : ''; + final symbol = + (data['modified_by'] != null && data['modified_by'] != '') ? ' - ' : ''; final isDesktop = Responsive.isDesktop(context); return RichText( @@ -610,18 +1314,16 @@ class _ClaimHistoryPopupState extends State { TextSpan( text: status + (isDesktop ? ' ' : '\n'), style: GoogleFonts.poppins( - fontSize: Responsive.isDesktop(context) ? 15 : 11, - fontWeight: FontWeight.w500, - color: Color(0xFF212120), // Status color - ), + fontSize: Responsive.isDesktop(context) ? 15 : 11, + fontWeight: FontWeight.w500, + color: Color(0xFF212120)), ), TextSpan( text: ' ($modifiedBy$symbol$modifiedAt)', style: GoogleFonts.poppins( - fontSize: Responsive.isDesktop(context) ? 14 : 11, - fontWeight: FontWeight.w400, - color: Color(0xFF565656), // Subtitle color - ), + fontSize: Responsive.isDesktop(context) ? 14 : 11, + fontWeight: FontWeight.w400, + color: Color(0xFF565656)), ), ], ), @@ -630,43 +1332,58 @@ class _ClaimHistoryPopupState extends State { Widget _getStepContentFromApi(Map data) { List rows = []; - data.forEach((key, value) { - // Skip metadata fields if (key == 'modified_by' || key == 'modified_at') return; - String displayName = value['display_name'] ?? key; String displayValue = value['display_value'] ?? 'N/A'; - rows.add(_buildHistoryListData(displayName, displayValue)); rows.add(SizedBox(height: 1)); }); - - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: rows, - ); + return Column(crossAxisAlignment: CrossAxisAlignment.start, children: rows); } - Widget _buildKeyValue(String title, String value) { + // Widget _buildKeyValue(String title, String value) { + // final displayValue = + // (value == null || value.trim().isEmpty) ? 'N/A' : value; + // return Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ + // Text(title, + // style: GoogleFonts.poppins( + // color: Color(0xFF747474), + // fontWeight: FontWeight.w400, + // fontSize: Responsive.isDesktop(context) ? 16 : 13)), + // SizedBox(height: 4), + // Text(displayValue, + // style: GoogleFonts.poppins( + // color: Color(0xFF000000), + // fontWeight: FontWeight.w500, + // fontSize: Responsive.isDesktop(context) ? 16 : 13)), + // ]); + // } + + Widget _buildKeyValue(String title, String? value) { final displayValue = - (value == null || value.trim().isEmpty) ? 'N/A' : value; + (value == null || value.trim().isEmpty) ? 'N/A' : value.trim(); + return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( title, style: GoogleFonts.poppins( - color: Color(0xFF747474), + color: const Color(0xFF747474), fontWeight: FontWeight.w400, fontSize: Responsive.isDesktop(context) ? 16 : 13, ), ), - SizedBox(height: 4), + const SizedBox(height: 4), + + /// Prevent overflow everywhere (web & mobile) Text( displayValue, + overflow: TextOverflow.ellipsis, + maxLines: 1, style: GoogleFonts.poppins( - color: Color(0xFF000000), + color: const Color(0xFF000000), fontWeight: FontWeight.w500, fontSize: Responsive.isDesktop(context) ? 16 : 13, ), @@ -677,80 +1394,36 @@ class _ClaimHistoryPopupState extends State { Widget _buildHistoryListData(String title, String value) { final displayValue = - (value == null || value.trim().isEmpty) ? 'N/A' : value; - - final titleText = Text( - title, - style: GoogleFonts.poppins( - color: const Color(0xFF747474), - fontWeight: FontWeight.w400, - fontSize: Responsive.isDesktop(context) ? 14 : 12, - ), - ); - - final valueText = Text( - displayValue, - style: GoogleFonts.poppins( - color: const Color(0xFF000000), - fontWeight: FontWeight.w400, - fontSize: Responsive.isDesktop(context) ? 14 : 12, - ), - textAlign: TextAlign.right, - ); + (value == null || value.trim().isEmpty) ? 'N/A' : value; + final titleText = Text(title, + style: GoogleFonts.poppins( + color: const Color(0xFF747474), + fontWeight: FontWeight.w400, + fontSize: Responsive.isDesktop(context) ? 14 : 12)); + final valueText = Text(displayValue, + style: GoogleFonts.poppins( + color: const Color(0xFF000000), + fontWeight: FontWeight.w400, + fontSize: Responsive.isDesktop(context) ? 14 : 12), + textAlign: TextAlign.right); return Padding( - padding: const EdgeInsets.symmetric(vertical: 1), // optional spacing + padding: const EdgeInsets.symmetric(vertical: 1), child: Responsive.isDesktop(context) ? Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Expanded( - child: Align( - alignment: Alignment.centerLeft, child: titleText)), - Expanded( - child: Align( - alignment: Alignment.centerRight, child: valueText)), - - // Title - Align to center left - // Expanded( - // child: Align( - // alignment: Alignment.centerLeft, - // child: Text( - // title, - // style: GoogleFonts.poppins( - // color: const Color(0xFF747474), - // fontWeight: FontWeight.w400, - // fontSize: 14, - // ), - // ), - // ), - // ), - - // Value - Align to center right - // Expanded( - // child: Align( - // alignment: Alignment.centerRight, - // child: Text( - // displayValue, - // style: GoogleFonts.poppins( - // color: const Color(0xFF000000), - // fontWeight: FontWeight.w400, - // fontSize: 14, - // ), - // ), - // ), - // ), - ], - ) + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Expanded( + child: Align( + alignment: Alignment.centerLeft, child: titleText)), + Expanded( + child: Align( + alignment: Alignment.centerRight, child: valueText)), + ]) : Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - titleText, - SizedBox(height: 4), - valueText, - ], - ), + crossAxisAlignment: CrossAxisAlignment.start, + children: [titleText, SizedBox(height: 4), valueText]), ); } } @@ -764,17 +1437,12 @@ class DottedLinePainter extends CustomPainter { final paint = Paint() ..color = Colors.grey.shade400 ..strokeWidth = 1; - while (startY < size.height) { - canvas.drawLine( - Offset(0, startY), - Offset(0, startY + dashHeight), - paint, - ); + canvas.drawLine(Offset(0, startY), Offset(0, startY + dashHeight), paint); startY += dashHeight + dashSpace; } } @override bool shouldRepaint(CustomPainter oldDelegate) => false; -} \ No newline at end of file +} diff --git a/lib/pages/postEnrollment/generalexclusionsdeductibles.dart b/lib/pages/postEnrollment/generalexclusionsdeductibles.dart index 8b05d12..700ce2b 100755 --- a/lib/pages/postEnrollment/generalexclusionsdeductibles.dart +++ b/lib/pages/postEnrollment/generalexclusionsdeductibles.dart @@ -807,39 +807,39 @@ class _generalExclusionsDeductiblesState bottomNavigationBar: Responsive.isDesktop(context) ? null : CustomBottomNavigationBar( - onTabChanged: (index) { - if (index == 0) { - context.go('/home'); - } else if (index == 1) { - context.go('/claims'); - } else if (index == 2) { - context.go('/profile'); - } else if (index == 3) { - context.go('/help'); - } else if (index == 4) { - // Wellness tab clicked → show popup - PopupHelper.showRedirectPopup( - context: context, - apiService: apiService, - empPrimaryId: session.empPrimaryId, - ); - } - }, - icons: [ - Icons.home_outlined, - Icons.sticky_note_2_outlined, - Icons.person_outline_outlined, - Icons.headset_mic_outlined, - Icons.health_and_safety_outlined, - ], - labels: [ - "Home", - "Claims", - "Profile", - "Help", - "Wellness", - ], - ), + onTabChanged: (index) { + // Add your navigation logic here + // repositionBotman(); + if (index == 0) { + context.push('/home'); + } else if (index == 1) { + context.push('/claims'); + } else if (index == 2) { + context.push('/profile'); + } else if (index == 3) { + context.push('/help'); + } + // else if (index == 4) { + // // context.push('/wellness'); + // // Wellness tab clicked → show popup + // if(!isRetailLoggedIn) + // PopupHelper.showRedirectPopup( + // context: context, + // apiService: apiService, + // empPrimaryId: session.empPrimaryId, + // ); + // } + }, + icons: [ + Icons.home_outlined, + Icons.sticky_note_2_outlined, + Icons.person_outline_outlined, + Icons.headset_mic_outlined, + // Icons.health_and_safety_outlined, + ], + labels: ["Home", "Claims", "Profile", "Help"], + initialIndex: 0, // Initial index of the bottom navigation bar + ), )); } } diff --git a/lib/pages/postEnrollment/help.dart b/lib/pages/postEnrollment/help.dart index 1ba3281..bb2da32 100755 --- a/lib/pages/postEnrollment/help.dart +++ b/lib/pages/postEnrollment/help.dart @@ -42,6 +42,7 @@ class _helpState extends State { dynamic _token; dynamic empCodeString; dynamic empPrimaryId; + dynamic empEmailID; dynamic client_id; dynamic policyList; dynamic policyDataIsEmpty = 1; @@ -135,6 +136,8 @@ class _helpState extends State { // Decode the JWT token received from the API response Map? decodedToken = Jwt.parseJwt(token); print(decodedToken); + empMobileNo = session.mobileNo ?? ''; + empEmailID = session.empEmailCorporate ?? ''; client_branch_id = session.empClientBranchId; empCodeString = session.empCodeString; print(empCodeString); // Check if emp_code is correct @@ -189,7 +192,7 @@ class _helpState extends State { isLoading = true; }); print('check 1'); - final response = await apiService.getTrackClaimsList(empPrimaryId!); + final response = await apiService.getTrackClaimsList(empPrimaryId!,empMobileNo,empEmailID); print('check 2'); setState(() { isLoading = false; @@ -1043,40 +1046,39 @@ class _helpState extends State { bottomNavigationBar: Responsive.isDesktop(context) ? null : CustomBottomNavigationBar( - onTabChanged: (index) { - if (index == 0) { - context.push('/home'); - } else if (index == 1) { - context.push('/claims'); - } else if (index == 2) { - context.push('/profile'); - } else if (index == 3) { - context.push('/help'); - } else if (index == 4) { - // Wellness tab clicked → show popup - PopupHelper.showRedirectPopup( - context: context, - apiService: apiService, - empPrimaryId: session.empPrimaryId, - ); - } - }, - icons: [ - Icons.home_outlined, - Icons.sticky_note_2_outlined, - Icons.person_outline_outlined, - Icons.headset_mic_outlined, - Icons.health_and_safety_outlined, - ], - labels: [ - "Home", - "Claims", - "Profile", - "Help", - "Wellness", - ], - initialIndex: 3, // Initial index of the bottom navigation bar - ), + onTabChanged: (index) { + // Add your navigation logic here + // repositionBotman(); + if (index == 0) { + context.push('/home'); + } else if (index == 1) { + context.push('/claims'); + } else if (index == 2) { + context.push('/profile'); + } else if (index == 3) { + context.push('/help'); + } + // else if (index == 4) { + // // context.push('/wellness'); + // // Wellness tab clicked → show popup + // if(!isRetailLoggedIn) + // PopupHelper.showRedirectPopup( + // context: context, + // apiService: apiService, + // empPrimaryId: session.empPrimaryId, + // ); + // } + }, + icons: [ + Icons.home_outlined, + Icons.sticky_note_2_outlined, + Icons.person_outline_outlined, + Icons.headset_mic_outlined, + // Icons.health_and_safety_outlined, + ], + labels: ["Home", "Claims", "Profile", "Help"], + initialIndex: 0, // Initial index of the bottom navigation bar + ), )); } diff --git a/lib/pages/postEnrollment/home.dart b/lib/pages/postEnrollment/home.dart index febddc8..a98265d 100755 --- a/lib/pages/postEnrollment/home.dart +++ b/lib/pages/postEnrollment/home.dart @@ -5,21 +5,27 @@ import 'dart:math'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:flutter_animate/flutter_animate.dart'; import 'package:flutter_image_slideshow/flutter_image_slideshow.dart'; +import 'package:flutter_svg/svg.dart'; import 'package:go_router/go_router.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:intl/intl.dart'; import 'package:jwt_decode/jwt_decode.dart'; import 'package:nhance_app_pwa/customAppBar/customAppBar.dart'; import 'package:nhance_app_pwa/pages/postEnrollment/service/api_service.dart'; +import 'package:nhance_app_pwa/pages/postEnrollment/service/svg_service.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:http/http.dart' as http; +import 'package:url_launcher/url_launcher.dart'; import 'package:webview_flutter/webview_flutter.dart'; import '../../customAppBar/customFooter.dart'; import '../../customAppBar/responsive.dart'; import '../../customAppBar/tabs.dart'; import '../../customAppBar/toastHelper.dart'; +import '../service/BlinkingCard.dart'; +import '../service/popup_widget.dart'; import '../service/SessionManager.dart'; import '../service/TokenService.dart'; import '../service/data_manager.dart'; @@ -28,13 +34,16 @@ import 'botman_chat.dart'; import 'chatbot.dart'; // import 'dart:js' as js; // import 'dart:html' as html; +import 'package:carousel_slider/carousel_slider.dart'; + import '/pages/helpers/botman_stub.dart' -if (dart.library.html) '/pages/helpers/botman_web.dart'; + if (dart.library.html) '/pages/helpers/botman_web.dart'; import '/../pages/helpers/mobile_helpers.dart' -if (dart.library.html) '/../pages/helpers/web_helpers.dart'; + if (dart.library.html) '/../pages/helpers/web_helpers.dart'; import 'package:webview_flutter_android/webview_flutter_android.dart'; +import 'package:carousel_slider/carousel_slider.dart'; class Home extends StatefulWidget { const Home({Key? key}) : super(key: key); @@ -44,8 +53,11 @@ class Home extends StatefulWidget { } class _HomeState extends State { + final CarouselSliderController _carouselController = CarouselSliderController(); + bool _dialogShown = false; bool isLoadingGif = false; + bool showProfileAlert = false; late ApiService apiService; int _currentIndex = 0; bool isActive = true; @@ -54,7 +66,9 @@ class _HomeState extends State { dynamic empName; dynamic empPrimaryId; dynamic client_id; - dynamic policyList; + dynamic emailId; + List policyList = []; + List retailPolicyDetails = []; dynamic activePoliciesDetails; dynamic policyDataIsEmpty = 1; dynamic policyHeading; @@ -62,10 +76,15 @@ class _HomeState extends State { dynamic EmployeePolicy; dynamic emp_name; dynamic pre_policy_count; + dynamic emp_not_enrolled_count; + dynamic wellnessURL; dynamic client_branch_id; dynamic mobileNo; List advertisementImages = []; bool isLoading = true; + int currentIndex = 0; + bool isRetailLoggedIn = false; + // dynamic policyCount; bool isTokenAvailable = false; // Default: button disabled @@ -78,55 +97,70 @@ class _HomeState extends State { final session = SessionManager(); final dataManager = DataManager(); late final WebViewController _controller; + dynamic selfDetails; @override void initState() { super.initState(); apiService = ApiService(context); // Initialize ApiService here - _loadToken(); - checkEnrollToken(); - // 🔹 Load ads - // dataManager.loadAdvertisementImages(); - if(kIsWeb){ - getAdvertisementSliderImage(); - } else { - advertisementImages = DataManager().advertisementImages; + + if (session.client_id == null || session.client_id!.isEmpty || + session.empCodeString == null || session.empCodeString!.isEmpty || + session.empClientBranchId == null || session.empClientBranchId!.isEmpty) { + setState(() { + isRetailLoggedIn = true; + }); } - - print('starts'); - print(advertisementImages); - print('Ends'); - - // // 🔹 Load policies (Active ones) - activePoliciesDetails = dataManager.loadPolicies( - clientId: session.client_id, - empCode: session.empCodeString, - status: "Active", - clientBranchId: session.empClientBranchId, - mobileNo: session.mobileNo, - ); - - print('activePoliciesDetails'); - print(activePoliciesDetails); - - // 🔹 Load self employee profile - dataManager.loadSelfEmployeeProfile( - clientId: session.client_id!, - empCode: session.empCodeString!, - clientBranchId: session.empClientBranchId!, - ); - print('loadSelfEmployeeProfile'); - - - - - - + _loadToken(); // if (kIsWeb) { - // openBotmanChat(); + getAdvertisementSliderImage(); + // } else { + // advertisementImages = DataManager().advertisementImages; // } + if(!isRetailLoggedIn){ + checkEnrollToken(); + + // 🔹 Load ads + // dataManager.loadAdvertisementImages(); + + // Load self profile → AFTER loading, check mobile number + dataManager + .loadSelfEmployeeProfile( + clientId: session.client_id!, + empCode: session.empCodeString!, + clientBranchId: session.empClientBranchId!, + ) + .then((_) { + final selfDetails = dataManager.selfProfile; + final mobile = selfDetails?["mobile"]?.toString().trim() ?? ''; + + setState(() { + showProfileAlert = mobile.isEmpty; + }); + }); + + print('starts'); + print(advertisementImages); + print('Ends'); + + // // 🔹 Load policies (Active ones) + // activePoliciesDetails = dataManager.loadPolicies( + // clientId: session.client_id, + // empCode: session.empCodeString, + // status: "Active", + // clientBranchId: session.empClientBranchId, + // mobileNo: session.mobileNo ?? '', + // ); + // + // print('activePoliciesDetails'); + // print(activePoliciesDetails); + + // if (kIsWeb) { + // openBotmanChat(); + // } + } } @override @@ -209,14 +243,15 @@ class _HomeState extends State { // Decode the JWT token received from the API response Map? decodedToken = Jwt.parseJwt(token); print(decodedToken); - mobileNo = session.mobileNo; - client_branch_id = session.empClientBranchId; - empCodeString = session.empCodeString; - empName = session.gpaEmpName; + mobileNo = session.mobileNo ?? ''; + client_branch_id = session.empClientBranchId ?? null; + empCodeString = session.empCodeString?? null; + empName = session.gpaEmpName?? null; print(empCodeString); // Check if emp_code is correct - empPrimaryId = session.empPrimaryId; - client_id = session.client_id; + empPrimaryId = session.empPrimaryId?? null; + client_id = session.client_id?? null; print(client_id); + emailId = session.empEmailCorporate?? null; // getAdvertisementSliderImage(); getActiveAndInactivePolicyDetails('Active'); } @@ -224,31 +259,49 @@ class _HomeState extends State { Future getActiveAndInactivePolicyDetails(String status) async { print(getActiveAndInactivePolicyDetails); - if (client_id == null || empCodeString == null) { - return; - } + // if (client_id == null || empCodeString == null) { + // return; + // } setState(() { isLoadingGif = true; }); print('check 1'); final response = await apiService.getActiveAndInactivePolicyDetails( - client_id!, empCodeString!, status, client_branch_id, mobileNo); + client_id, empCodeString, status, client_branch_id, mobileNo,emailId); print('check 1'); if (response['status'] == 'success') { setState(() { isLoadingGif = false; - policyList = response['data']; - print('policyList $policyList'); - emp_name = response['emp_name']; - pre_policy_count = response['pre_policy_count']; - setState(() { - isTokenAvailable = pre_policy_count != 0; - }); - // if (status == 'Active') { - // policyCount = policyList.length; - // print('policyCount : $policyCount'); - // } }); + emp_name = response['emp_name']; + policyList = List.from(response['data'] ?? []); + retailPolicyDetails = List.from(response['retail_policy_data'] ?? []); + print('policyList $policyList'); + pre_policy_count = response['pre_policy_count'] ?? ''; + final empNotEnrolledCount = response['emp_not_enrolled_count'] ?? ''; + final wellnessStatus = response['wellness_data']['status'] ?? 'failed'; + print('wellnessStatus $wellnessStatus'); + if (wellnessStatus == 'success') { + wellnessURL = response['wellness_data']['data']; + } + + setState(() { + isTokenAvailable = pre_policy_count != 0; + emp_not_enrolled_count = empNotEnrolledCount != 0; + }); + + if (emp_not_enrolled_count) { + final prefs = await SharedPreferences.getInstance(); + bool? cancelFlag = prefs.getBool('cancel_flag'); + + print('cancelFlag $cancelFlag'); + + // Show popup only when flag is NOT true + if (cancelFlag != true) { + showBlurPopup(context, emp_name); + } + } + print(policyList); } else { setState(() { @@ -260,7 +313,7 @@ class _HomeState extends State { } Future getAdvertisementSliderImage() async { - final response = await apiService.getAdvertisementImageToApi(); + final response = await apiService.getAdvertisementImageToApi(session.client_id); print('check 1'); if (response['status'] == 'success') { setState(() { @@ -314,7 +367,7 @@ class _HomeState extends State { 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; final random = Random.secure(); return List.generate( - length, (index) => characters[random.nextInt(characters.length)]) + length, (index) => characters[random.nextInt(characters.length)]) .join(); } @@ -352,11 +405,11 @@ class _HomeState extends State { actions: [ Row( mainAxisAlignment: - MainAxisAlignment.spaceEvenly, // Space buttons evenly + MainAxisAlignment.spaceEvenly, // Space buttons evenly children: [ SizedBox( width: - MediaQuery.of(context).size.width * 0.3, // Set button width + MediaQuery.of(context).size.width * 0.3, // Set button width child: TextButton( onPressed: () => Navigator.pop(dialogContext), style: TextButton.styleFrom( @@ -379,7 +432,7 @@ class _HomeState extends State { ), SizedBox( width: - MediaQuery.of(context).size.width * 0.3, // Set button width + MediaQuery.of(context).size.width * 0.3, // Set button width child: TextButton( onPressed: () => TokenService().logout(context), style: TextButton.styleFrom( @@ -390,8 +443,8 @@ class _HomeState extends State { ), ), child: FittedBox( - fit: BoxFit - .scaleDown, // Prevents wrapping while adjusting text size + fit: BoxFit.scaleDown, + // Prevents wrapping while adjusting text size child: Text( 'Log Out', style: GoogleFonts.poppins( @@ -413,7 +466,6 @@ class _HomeState extends State { @override Widget build(BuildContext context) { - if (!Responsive.isDesktop(context)) SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle( statusBarColor: Color(0xFFFFFBDE), // Status bar color @@ -431,447 +483,588 @@ class _HomeState extends State { body: Stack(children: [ SingleChildScrollView( child: Container( - padding: Responsive.isDesktop(context) - ? EdgeInsets.symmetric( - horizontal: MediaQuery.of(context).size.width * - 0.2, // 30% of screen width as horizontal padding - vertical: MediaQuery.of(context).size.height * - 0.03, // 5% of screen height as vertical padding - ) - : EdgeInsets.all(15), - color: Colors.white, - child: Column(children: [ - Container( - child: Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Expanded( - flex: 12, - child: Container( - alignment: Alignment.centerLeft, - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, + padding: Responsive.isDesktop(context) + ? EdgeInsets.symmetric( + horizontal: MediaQuery.of(context).size.width * + 0.2, // 30% of screen width as horizontal padding + vertical: MediaQuery.of(context).size.height * + 0.03, // 5% of screen height as vertical padding + ) + : EdgeInsets.all(15), + color: Colors.white, + child: Column(children: [ + // Card( + // elevation: 2, + // color: Color(0xFFDDF3FF), + // shape: RoundedRectangleBorder( + // borderRadius: + // BorderRadius.circular(10), // Set the border radius here + // ), + // child: ClipRRect( + // borderRadius: BorderRadius.circular( + // 10), // Apply border radius to the container + // child: Column( + // children: [ + // Container( + // decoration: BoxDecoration( + // color: Color( + // 0xFFDDF3FF), // Set background color for the container + // borderRadius: BorderRadius.circular( + // 10), // Set border radius for the container + // ), + // padding: Responsive.isDesktop(context) + // ? EdgeInsets.only( + // top: 10, bottom: 10, left: 10, right: 10) + // : EdgeInsets.only( + // top: 10, bottom: 10, left: 10, right: 10), + // child: Row( + // crossAxisAlignment: CrossAxisAlignment.center, + // children: [ + // Expanded( + // flex: 7, + // child: Row( + // mainAxisAlignment: MainAxisAlignment.start, + // children: [ + // Text( + // 'Update Mobile Number', + // textAlign: TextAlign.center, + // style: GoogleFonts.poppins( + // fontSize: + // Responsive.isDesktop(context) ? 18 : 12, + // fontWeight: FontWeight.w500, + // color: Color(0xFF404040), + // ), + // ).animate( + // delay: 1000.ms, // this delay only happens once at the very start + // onPlay: (controller) => controller.repeat(), // loop + // ).fadeIn(delay: 500.ms) // inherits the delay & duration from move + // ], + // ), + // ), + // Expanded( + // flex: 5, + // child: Row( + // mainAxisAlignment: MainAxisAlignment.end, + // children: [ + // ElevatedButton( + // onPressed: showProfileAlert + // ? () { + // + // } + // : null, + // child: Text('View Details'), + // ), + // ], + // ), + // ), + // ], + // ), + // ), + // ], + // ), + // ), + // ), + Container( + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + flex: 12, + child: Container( + alignment: Alignment.centerLeft, + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( children: [ - Row( - children: [ - Text( - 'Hello, $emp_name!', - textAlign: TextAlign.left, - style: GoogleFonts.poppins( - fontSize: Responsive.isDesktop(context) + Text( + 'Hello, $emp_name!', + textAlign: TextAlign.left, + style: GoogleFonts.poppins( + fontSize: + Responsive.isDesktop(context) ? 20 : 20, - fontWeight: FontWeight.w500, - color: Color(0xFF000000), - ), - ), - ], + fontWeight: FontWeight.w500, + color: Color(0xFF000000), + ), ), - // Text( - // 'Lorem ipsum dolor sit amet', - // textAlign: TextAlign.left, - // style: GoogleFonts.poppins( - // fontSize: - // Responsive.isDesktop(context) ? 12 : 14, - // fontWeight: FontWeight.w400, - // color: Color(0xFF7A7A7A), - // ), - // ), ], - ))), - ], - ), - ), - SizedBox(height: 15), - Card( - elevation: 2, - color: Color(0xFFDDF3FF), - shape: RoundedRectangleBorder( - borderRadius: - BorderRadius.circular(10), // Set the border radius here - ), - child: ClipRRect( - borderRadius: BorderRadius.circular( - 10), // Apply border radius to the container - child: Column( - children: [ - Container( - decoration: BoxDecoration( - color: Color( - 0xFFDDF3FF), // Set background color for the container - borderRadius: BorderRadius.circular( - 10), // Set border radius for the container - ), - padding: Responsive.isDesktop(context) - ? EdgeInsets.only( - top: 10, bottom: 10, left: 10, right: 10) - : EdgeInsets.only( - top: 10, bottom: 10, left: 10, right: 10), - child: Row( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Expanded( - flex: 7, - child: Row( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Text( - 'Policies for enrollment : $pre_policy_count', - textAlign: TextAlign.center, - style: GoogleFonts.poppins( - fontSize: - Responsive.isDesktop(context) ? 18 : 12, - fontWeight: FontWeight.w500, - color: Color(0xFF404040), - ), - ), - ], - ), - ), - Expanded( - flex: 5, - child: Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - ElevatedButton( - onPressed: isTokenAvailable - ? () { - context.go('/empDetails'); - } - : null, - child: Text('View Details'), - ), - ], - ), ), + // Text( + // 'Lorem ipsum dolor sit amet', + // textAlign: TextAlign.left, + // style: GoogleFonts.poppins( + // fontSize: + // Responsive.isDesktop(context) ? 12 : 14, + // fontWeight: FontWeight.w400, + // color: Color(0xFF7A7A7A), + // ), + // ), ], - ), - ), - ], - ), - ), - ), - SizedBox(height: 15), - if (advertisementImages != null) - Container( - child: Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Expanded( - flex: 12, - child: Container( - alignment: Alignment.centerLeft, - child: advertisementImages.isEmpty - ? Center(child: Text("No images available")) - : Container( - child: Row( - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - Expanded( - flex: 12, - child: Container( - alignment: Alignment.centerLeft, - child: Column( - mainAxisAlignment: - MainAxisAlignment.start, - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - Container( - child: ImageSlideshow( - /// Width of the [ImageSlideshow]. - width: double.infinity, + ))), + ], + ), + ), + if (showProfileAlert) + BlinkingCard( + onUpdated: () { + setState(() { + dataManager + .loadSelfEmployeeProfile( + clientId: session.client_id!, + empCode: session.empCodeString!, + clientBranchId: session.empClientBranchId!, + ) + .then((_) { + final selfDetails = dataManager.selfProfile; + final mobile = + selfDetails?["mobile"]?.toString().trim() ?? ''; - /// Height of the [ImageSlideshow]. - height: - Responsive.isDesktop( - context) - ? 200 - : 150, - - /// The page to show when first creating the [ImageSlideshow]. - initialPage: 0, - - /// The color to paint the indicator. - indicatorColor: - Color(0xFFE26728), - - /// The color to paint behind the indicator. - indicatorBackgroundColor: - Colors.grey, - - /// The widgets to display in the [ImageSlideshow]. - children: - advertisementImages - .map( - (imageUrl) => - ClipRRect( - borderRadius: - BorderRadius - .circular( - 8), - child: Image - .network( - imageUrl, - fit: BoxFit - .cover, - ), - ), - ) - .toList(), - - /// Called whenever the page in the center of the viewport changes. - onPageChanged: (value) { - print( - 'Page changed: $value'); - }, - - /// Auto scroll interval. - /// Do not auto scroll with null or 0. - autoPlayInterval: 3000, - - /// Loops back to the first slide. - isLoop: true, - ), - ), - ], - ), - ), + setState(() { + showProfileAlert = mobile.isEmpty; + }); + }); + }); + }, + ), + SizedBox(height: 15), + if(!isRetailLoggedIn) + Card( + elevation: 2, + color: Color(0xFFDDF3FF), + shape: RoundedRectangleBorder( + borderRadius: + BorderRadius.circular(10), // Set the border radius here + ), + child: ClipRRect( + borderRadius: BorderRadius.circular( + 10), // Apply border radius to the container + child: Column( + children: [ + Container( + decoration: BoxDecoration( + color: Color(0xFFDDF3FF), + // Set background color for the container + borderRadius: BorderRadius.circular( + 10), // Set border radius for the container + ), + padding: Responsive.isDesktop(context) + ? EdgeInsets.only( + top: 10, bottom: 10, left: 10, right: 10) + : EdgeInsets.only( + top: 10, bottom: 10, left: 10, right: 10), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Expanded( + flex: 7, + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Text( + 'Policies for enrollment : $pre_policy_count', + textAlign: TextAlign.center, + style: GoogleFonts.poppins( + fontSize: Responsive.isDesktop(context) + ? 18 + : 12, + fontWeight: FontWeight.w500, + color: Color(0xFF404040), ), - ], - ), + ), + ], ), ), - ), - ], + Expanded( + flex: 5, + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + ElevatedButton( + onPressed: isTokenAvailable + ? () { + context.go('/empDetails'); + } + : null, + child: Text('View Details'), + ), + ], + ), + ), + ], + ), ), - ), - SizedBox(height: 15), - Container( - child: Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Expanded( - flex: 12, - child: Container( - alignment: Alignment.centerLeft, - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - children: [ - Text( - 'Your Policies', - textAlign: TextAlign.left, - style: GoogleFonts.poppins( - fontSize: Responsive.isDesktop(context) - ? 18 - : 18, - fontWeight: FontWeight.w500, - color: Color(0xFF000000), + ], + ), + ), + ), + SizedBox(height: 15), + if (advertisementImages != null) + Container( + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + flex: 12, + child: Container( + alignment: Alignment.centerLeft, + child: advertisementImages.isEmpty + ? Center(child: Text("No images available")) + : Container( + child: Row( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Expanded( + flex: 12, + child: Container( + alignment: Alignment.centerLeft, + child: Column( + mainAxisAlignment: + MainAxisAlignment.start, + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Container( + child: ImageSlideshow( + /// Width of the [ImageSlideshow]. + width: double.infinity, + + /// Height of the [ImageSlideshow]. + height: + Responsive.isDesktop( + context) + ? 200 + : 150, + + /// The page to show when first creating the [ImageSlideshow]. + initialPage: 0, + + /// The color to paint the indicator. + indicatorColor: + Color(0xFFE26728), + + /// The color to paint behind the indicator. + indicatorBackgroundColor: + Colors.grey, + + /// The widgets to display in the [ImageSlideshow]. + children: + advertisementImages + .map( + (imageUrl) => + ClipRRect( + borderRadius: + BorderRadius + .circular( + 8), + child: Image + .network( + imageUrl, + fit: BoxFit + .cover, + ), + ), + ) + .toList(), + + /// Called whenever the page in the center of the viewport changes. + onPageChanged: (value) { + print( + 'Page changed: $value'); + }, + + /// Auto scroll interval. + /// Do not auto scroll with null or 0. + autoPlayInterval: 3000, + + /// Loops back to the first slide. + isLoop: true, + ), + ), + ], ), ), - ], - ), - // Text( - // 'Lorem ipsum dolor sit amet', - // textAlign: TextAlign.left, - // style: GoogleFonts.poppins( - // fontSize: - // Responsive.isDesktop(context) ? 12 : 14, - // fontWeight: FontWeight.w400, - // color: Color(0xFF7A7A7A), - // ), - // ), - ], - ))), - ], - ), + ), + ], + ), + ), + ), + ), + ], ), - SizedBox(height: 15), - Container( - decoration: BoxDecoration( - color: - Color(0xFFEEF5FF), // Set background color for the container - borderRadius: BorderRadius.circular( - 5), // Set border radius for the container - ), - padding: Responsive.isDesktop(context) - ? EdgeInsets.only(top: 10, bottom: 10, left: 25, right: 25) - : EdgeInsets.only(top: 10, bottom: 10, left: 10, right: 10), - child: Row( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Expanded( - flex: 12, - child: Container( - alignment: Alignment.center, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, + ), + SizedBox(height: 15), + Container( + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + flex: 12, + child: Container( + alignment: Alignment.centerLeft, + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( children: [ - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Expanded( - flex: 6, - child: Container( - alignment: Alignment.center, - child: GestureDetector( - onTap: () { - setState(() { - isActive = true; - policyList.clear(); - }); - getActiveAndInactivePolicyDetails( - 'Active'); - }, - child: isActive - ? Material( - elevation: 5, - borderRadius: - BorderRadius.circular(10), - color: Colors.white, - child: SizedBox( - width: Responsive.isDesktop( - context) - ? 400 - : 150, // Set the width here - child: TextButton( - onPressed: () {}, - style: - TextButton.styleFrom( - padding: EdgeInsets - .symmetric( - vertical: Responsive - .isDesktop( - context) - ? 12 - : 5, - ), - ), - child: Text( - 'Active', - style: - GoogleFonts.poppins( - fontSize: Responsive - .isDesktop( - context) - ? 18 - : 13, - fontWeight: - FontWeight.w500, - color: - Color(0xFF593AFF), - ), - ), - ), - ), - ) - : Text( - 'Active', - style: GoogleFonts.poppins( - fontSize: - Responsive.isDesktop( - context) - ? 18 - : 13, - fontWeight: FontWeight.w400, - color: Color(0xFF636363), - ), - ), - ), - )), - Expanded( - flex: 6, - child: Container( - alignment: Alignment.center, - child: GestureDetector( - onTap: () { - setState(() { - isActive = false; - policyList.clear(); - }); - getActiveAndInactivePolicyDetails( - 'Inactive'); - }, - child: !isActive - ? Material( - elevation: 5, - borderRadius: - BorderRadius.circular(10), - color: Colors.white, - child: SizedBox( - width: Responsive.isDesktop( - context) - ? 400 - : 150, // Set the width here - child: TextButton( - onPressed: () {}, - style: - TextButton.styleFrom( - padding: EdgeInsets - .symmetric( - vertical: Responsive - .isDesktop( - context) - ? 12 - : 5, - ), - ), - child: Text( - 'Inactive', - style: - GoogleFonts.poppins( - fontSize: Responsive - .isDesktop( - context) - ? 18 - : 13, - fontWeight: - FontWeight.w500, - color: - Color(0xFF593AFF), - ), - ), - ), - ), - ) - : Text( - 'Inactive', - style: GoogleFonts.poppins( - fontSize: - Responsive.isDesktop( - context) - ? 18 - : 13, - fontWeight: FontWeight.w400, - color: Color(0xFF636363), - ), - ), - ), - )) - ], - ) + Text( + 'Your Policies', + textAlign: TextAlign.left, + style: GoogleFonts.poppins( + fontSize: + Responsive.isDesktop(context) + ? 18 + : 18, + fontWeight: FontWeight.w500, + color: Color(0xFF000000), + ), + ), ], - ))), - ], - ), - ), - SizedBox(height: 15), - if (policyList != null && policyList.length > 0) - Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - ...generateCards(policyList), - ], - ), - SizedBox(height: Responsive.isDesktop(context) ? 40 : 70), - ]), - )), + ), + // Text( + // 'Lorem ipsum dolor sit amet', + // textAlign: TextAlign.left, + // style: GoogleFonts.poppins( + // fontSize: + // Responsive.isDesktop(context) ? 12 : 14, + // fontWeight: FontWeight.w400, + // color: Color(0xFF7A7A7A), + // ), + // ), + ], + ))), + ], + ), + ), + SizedBox(height: 15), + Container( + decoration: BoxDecoration( + color: Color(0xFFEEF5FF), + // Set background color for the container + borderRadius: BorderRadius.circular( + 5), // Set border radius for the container + ), + padding: Responsive.isDesktop(context) + ? EdgeInsets.only( + top: 10, bottom: 10, left: 25, right: 25) + : EdgeInsets.only( + top: 10, bottom: 10, left: 10, right: 10), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Expanded( + flex: 12, + child: Container( + alignment: Alignment.center, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Expanded( + flex: 6, + child: Container( + alignment: Alignment.center, + child: GestureDetector( + onTap: () { + setState(() { + isActive = true; + policyList.clear(); + }); + getActiveAndInactivePolicyDetails( + 'Active'); + }, + child: isActive + ? Material( + elevation: 5, + borderRadius: + BorderRadius.circular( + 10), + color: Colors.white, + child: SizedBox( + width: Responsive + .isDesktop( + context) + ? 400 + : 150, + // Set the width here + child: TextButton( + onPressed: () {}, + style: TextButton + .styleFrom( + padding: EdgeInsets + .symmetric( + vertical: Responsive + .isDesktop( + context) + ? 12 + : 5, + ), + ), + child: Text( + 'Active', + style: GoogleFonts + .poppins( + fontSize: Responsive + .isDesktop( + context) + ? 18 + : 13, + fontWeight: + FontWeight + .w500, + color: Color( + 0xFF593AFF), + ), + ), + ), + ), + ) + : Text( + 'Active', + style: + GoogleFonts.poppins( + fontSize: Responsive + .isDesktop( + context) + ? 18 + : 13, + fontWeight: + FontWeight.w400, + color: + Color(0xFF636363), + ), + ), + ), + )), + Expanded( + flex: 6, + child: Container( + alignment: Alignment.center, + child: GestureDetector( + onTap: () { + setState(() { + isActive = false; + policyList.clear(); + retailPolicyDetails.clear(); + }); + getActiveAndInactivePolicyDetails( + 'Inactive'); + }, + child: !isActive + ? Material( + elevation: 5, + borderRadius: + BorderRadius.circular( + 10), + color: Colors.white, + child: SizedBox( + width: Responsive + .isDesktop( + context) + ? 400 + : 150, + // Set the width here + child: TextButton( + onPressed: () {}, + style: TextButton + .styleFrom( + padding: EdgeInsets + .symmetric( + vertical: Responsive + .isDesktop( + context) + ? 12 + : 5, + ), + ), + child: Text( + 'Inactive', + style: GoogleFonts + .poppins( + fontSize: Responsive + .isDesktop( + context) + ? 18 + : 13, + fontWeight: + FontWeight + .w500, + color: Color( + 0xFF593AFF), + ), + ), + ), + ), + ) + : Text( + 'Inactive', + style: + GoogleFonts.poppins( + fontSize: Responsive + .isDesktop( + context) + ? 18 + : 13, + fontWeight: + FontWeight.w400, + color: + Color(0xFF636363), + ), + ), + ), + )) + ], + ) + ], + ))), + ], + ), + ), + SizedBox(height: 15), + // if (policyList != null && policyList.length > 0) + Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + buildCarousel() + // ...generateCards(policyList) + // CarouselSlider( + // options: CarouselOptions( + // aspectRatio: 2.0, + // enlargeCenterPage: true, + // enableInfiniteScroll: false, // 👈 NO LOOP + // initialPage: 2, // 👈 START FROM MIDDLE + // autoPlay: false, + // ), + // items: imgList.map((item) { + // return Container( + // width: double.infinity, + // decoration: BoxDecoration( + // image: DecorationImage( + // image: NetworkImage(item), + // fit: BoxFit.cover, + // ), + // ), + // ); + // }).toList(), + // ), + ], + ), + SizedBox(height: 15), + if (wellnessURL != null && wellnessURL.toString().isNotEmpty && isActive) + Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [_buildWellnessCard()], + ), + SizedBox(height: Responsive.isDesktop(context) ? 40 : 70), + ]), + )), // Positioned( // bottom: 25, // right: 15, @@ -938,10 +1131,10 @@ class _HomeState extends State { color: Color(0x98FFFCE5), // Semi-transparent background child: Center( child: // Your GIF loader widget - Image.asset( - height: 60, - width: 60, - 'assets/nhance-loader.gif'), // Adjust path to your GIF loader + Image.asset( + height: 60, + width: 60, + 'assets/nhance-loader.gif'), // Adjust path to your GIF loader ), ), if (Responsive.isDesktop(context)) @@ -997,303 +1190,1244 @@ class _HomeState extends State { bottomNavigationBar: Responsive.isDesktop(context) ? null : CustomBottomNavigationBar( - onTabChanged: (index) { - // Add your navigation logic here - // repositionBotman(); - if (index == 0) { - context.push('/home'); - } else if (index == 1) { - context.push('/claims'); - } else if (index == 2) { - context.push('/profile'); - } else if (index == 3) { - context.push('/help'); - } else if (index == 4) { - // context.push('/wellness'); - // Wellness tab clicked → show popup - PopupHelper.showRedirectPopup( - context: context, - apiService: apiService, - empPrimaryId: session.empPrimaryId, - ); - } - }, - icons: [ - Icons.home_outlined, - Icons.sticky_note_2_outlined, - Icons.person_outline_outlined, - Icons.headset_mic_outlined, - Icons.health_and_safety_outlined, - ], - labels: ["Home", "Claims", "Profile", "Help", "Wellness"], - initialIndex: 0, // Initial index of the bottom navigation bar + onTabChanged: (index) { + // Add your navigation logic here + // repositionBotman(); + if (index == 0) { + if(!isRetailLoggedIn) context.push('/home'); + } else if (index == 1) { + if(!isRetailLoggedIn) context.push('/claims'); + } else if (index == 2) { + if(!isRetailLoggedIn) context.push('/profile'); + } else if (index == 3) { + if(!isRetailLoggedIn) context.push('/help'); + } + // else if (index == 4) { + // // context.push('/wellness'); + // // Wellness tab clicked → show popup + // if(!isRetailLoggedIn) + // PopupHelper.showRedirectPopup( + // context: context, + // apiService: apiService, + // empPrimaryId: session.empPrimaryId, + // ); + // } + }, + icons: [ + Icons.home_outlined, + Icons.sticky_note_2_outlined, + Icons.person_outline_outlined, + Icons.headset_mic_outlined, + // Icons.health_and_safety_outlined, + ], + labels: ["Home", "Claims", "Profile", "Help"], + initialIndex: 0, // Initial index of the bottom navigation bar + ), + )); + } + + // String getMemberNames(List employeePolicy) { + // List names = []; + // for (var member in employeePolicy) { + // if (member['name'] != null) { + // names.add(member['name']); + // } + // } + // return names.join(' ~ '); + // } + String formatMemberNames(List memberList) { + if (memberList.isEmpty) return ""; + + // Extract only the names + List names = memberList + .map((item) => item["name"]?.toString() ?? "") + .where((name) => name.isNotEmpty) + .toList(); + + if (names.length <= 2) { + return names.join(" ~ "); + } + + final remaining = names.length - 2; + return "${names[0]} ~ ${names[1]} ~ + $remaining Members"; + } + +// List generateCards(List data) { +// print('generateCards'); +// print(data); +// return [ +// GridView.builder( +// shrinkWrap: true, +// physics: NeverScrollableScrollPhysics(), +// gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( +// crossAxisCount: Responsive.isDesktop(context) ? 2 : 1, +// crossAxisSpacing: 20.0, +// mainAxisSpacing: 20.0, +// childAspectRatio: Responsive.isDesktop(context) ? 2.2 : 2.6), +// itemCount: data.length, +// itemBuilder: (BuildContext context, int index) { +// var item = data[index]; +// String policyHeading = item['heading']; +// String policyName = item['policy_name']; +// // String si_value = item['si_value']; +// String siValueStr = item['si_value'] ?? '0'; +// String settledStr = item['total_settled_amount']?.toString() ?? '0'; +// +// double siValue = double.tryParse(siValueStr) ?? 0; +// double settled = double.tryParse(settledStr) ?? 0; +// +// // Main logic +// double finalValue = (settled == 0) ? siValue : (siValue - settled); +// +// print("Final Value: $finalValue"); +// String policyEndDate = item['policy_end_date']; +// List employeePolicy = item['EmployeePolicy']; +// String memberNames = getMemberNames(employeePolicy); +// +// DateTime parsedDate = DateFormat('dd-MMM-yyyy').parse(policyEndDate); +// String formattedDate = DateFormat('d MMM yyyy').format(parsedDate); +// +// return GestureDetector( +// onTap: () { +// context.go( +// '/policies', +// extra: item, // 👈 pass arguments here +// ); +// }, +// child: MouseRegion( +// cursor: SystemMouseCursors.click, +// child: Card( +// elevation: 5, +// shape: RoundedRectangleBorder( +// borderRadius: +// BorderRadius.circular(15.0), // Set border radius here +// ), +// child: Container( +// decoration: BoxDecoration( +// color: Colors.white, // Set background color to white +// borderRadius: BorderRadius.circular( +// 15.0), // Set border radius for Container +// ), +// padding: Responsive.isDesktop(context) +// ? EdgeInsets.only( +// top: 15, bottom: 15, left: 15, right: 15) +// : EdgeInsets.only( +// top: 10, +// bottom: 10, +// left: 10, +// right: 10), // Add padding to the container +// child: Row( +// children: [ +// Expanded( +// flex: 12, +// child: Container( +// alignment: Alignment.centerLeft, +// child: Column( +// mainAxisAlignment: MainAxisAlignment.start, +// crossAxisAlignment: +// CrossAxisAlignment.start, +// children: [ +// Row( +// children: [ +// Expanded( +// flex: 7, +// child: Column( +// crossAxisAlignment: +// CrossAxisAlignment.start, +// children: [ +// Text( +// policyHeading ?? '', +// textAlign: TextAlign.left, +// style: GoogleFonts.poppins( +// fontSize: +// Responsive.isDesktop( +// context) +// ? 10 +// : 10, +// fontWeight: FontWeight.w400, +// color: Color(0xFF785EFF), +// ), +// ), +// ], +// ), +// ), +// Expanded( +// flex: 5, +// child: Column( +// crossAxisAlignment: +// CrossAxisAlignment.end, +// children: [ +// Text( +// 'Expires on $formattedDate', +// textAlign: TextAlign.right, +// style: GoogleFonts.poppins( +// fontSize: +// Responsive.isDesktop( +// context) +// ? 10 +// : 10, +// fontWeight: FontWeight.w400, +// color: Color(0xFF747474), +// ), +// ), +// ], +// ), +// ), +// ], +// ), +// SizedBox(height: 7), +// Row( +// children: [ +// Expanded( +// flex: 6, +// child: Column( +// crossAxisAlignment: +// CrossAxisAlignment.start, +// children: [ +// Text( +// policyName ?? '', +// textAlign: TextAlign.left, +// style: GoogleFonts.poppins( +// fontSize: +// Responsive.isDesktop( +// context) +// ? 14 +// : 13, +// fontWeight: FontWeight.w500, +// color: Color(0xFF000000), +// ), +// ), +// ], +// ), +// ), +// Expanded( +// flex: 5, +// child: Column( +// crossAxisAlignment: +// CrossAxisAlignment.end, +// children: [ +// Text( +// formatToCroresLakhsAndThousands( +// siValueStr), +// textAlign: TextAlign.left, +// style: GoogleFonts.poppins( +// fontSize: +// Responsive.isDesktop( +// context) +// ? 14 +// : 13, +// fontWeight: FontWeight.w500, +// color: Color(0xFF000000), +// ), +// ), +// ], +// ), +// ), +// Expanded( +// flex: 1, +// child: Container( +// alignment: +// Alignment.centerRight, +// child: Icon( +// Icons.chevron_right, +// color: Color( +// 0xFFE26728), // Replace with your desired icon +// size: Responsive.isDesktop( +// context) +// ? 30 +// : 26, +// ), +// )) +// ], +// ), +// SizedBox(height: 7), +// Row( +// children: [ +// Expanded( +// flex: 12, +// child: Column( +// crossAxisAlignment: +// CrossAxisAlignment.start, +// children: [ +// Text( +// memberNames ?? '', +// textAlign: TextAlign.left, +// style: GoogleFonts.poppins( +// fontSize: +// Responsive.isDesktop( +// context) +// ? 14 +// : 13, +// fontWeight: FontWeight.w400, +// color: Color(0xFF000000), +// ), +// ), +// ], +// ), +// ), +// ], +// ), +// SizedBox(height: 20), +// Row( +// children: [ +// Expanded( +// flex: 12, +// child: Column( +// crossAxisAlignment: +// CrossAxisAlignment.end, +// children: [ +// Text( +// 'Remaining Amount: ${finalValue}', +// textAlign: TextAlign.right, +// style: GoogleFonts.poppins( +// fontSize: +// Responsive.isDesktop( +// context) +// ? 10 +// : 10, +// fontWeight: FontWeight.w400, +// color: Color(0xFF747474), +// ), +// ), +// ], +// ), +// ), +// ], +// ), +// // SizedBox(height: 7), +// ], +// ))), +// ], +// ), +// ), +// ))); +// }, +// ), +// ]; +// } + + + + // Widget _creditCardDesign({ + // required String policyHeading, + // required String policyName, + // required String siValue, + // required String memberNames, + // required String expiresOn, + // required double remainingAmount, + // }) { + // return Container( + // width: double.infinity, + // margin: const EdgeInsets.symmetric(vertical: 8), + // padding: const EdgeInsets.all(15), + // decoration: BoxDecoration( + // color: const Color(0xFFF4F8FF), // Light background like image + // borderRadius: BorderRadius.circular(16), + // ), + // child: Row( + // crossAxisAlignment: CrossAxisAlignment.start, + // children: [ + // // ======================= + // // LEFT LOGO + // // ======================= + // Container( + // height: 50, + // width: 50, + // decoration: BoxDecoration( + // color: Colors.white, + // borderRadius: BorderRadius.circular(12), + // boxShadow: [ + // BoxShadow( + // color: Colors.black12, + // blurRadius: 4, + // offset: Offset(0, 2), + // ) + // ], + // ), + // child: Center( + // child: Image.asset( + // "assets/company_logo.png", // <--- change logo here + // height: 35, + // fit: BoxFit.contain, + // ), + // ), + // ), + // + // const SizedBox(width: 15), + // + // // ======================= + // // RIGHT SIDE CONTENT + // // ======================= + // Expanded( + // child: Column( + // crossAxisAlignment: CrossAxisAlignment.start, + // children: [ + // // 🔹 Small category heading + // Text( + // "• Health Insurance", + // style: GoogleFonts.poppins( + // fontSize: 11, + // color: const Color(0xFF5370F0), + // fontWeight: FontWeight.w500, + // ), + // ), + // + // const SizedBox(height: 5), + // + // // 🔹 Main title (SI + policy name) + // Text( + // "${formatToCroresLakhsAndThousands(siValue)} health policy", + // style: GoogleFonts.poppins( + // fontSize: 15, + // fontWeight: FontWeight.w600, + // color: Colors.black, + // ), + // ), + // + // const SizedBox(height: 5), + // + // // 🔹 Members + // Text( + // memberNames, + // style: GoogleFonts.poppins( + // fontSize: 13, + // color: Colors.black87, + // ), + // ), + // + // const SizedBox(height: 10), + // + // // 🔹 Expires On + // Text( + // "Expires on $expiresOn", + // style: GoogleFonts.poppins( + // fontSize: 11, + // color: Colors.black54, + // ), + // ), + // ], + // ), + // ), + // + // const SizedBox(width: 10), + // + // // ======================= + // // ARROW ICON + // // ======================= + // Icon( + // Icons.chevron_right, + // color: const Color(0xFF5370F0), + // size: 24, + // ), + // ], + // ), + // ); + // } + + // List generateCardWidgets(List data, BuildContext context) { + // return data.map((item) { + // String policyHeading = item['heading']; + // String policyName = item['policy_name']; + // String siValueStr = item['si_value'] ?? '0'; + // String settledStr = item['total_settled_amount']?.toString() ?? '0'; + // + // double siValue = double.tryParse(siValueStr) ?? 0; + // double settled = double.tryParse(settledStr) ?? 0; + // + // double finalValue = (settled == 0) ? siValue : (siValue - settled); + // + // String policyEndDate = item['policy_end_date']; + // List employeePolicy = item['EmployeePolicy']; + // String memberNames = getMemberNames(employeePolicy); + // + // DateTime parsedDate = DateFormat('dd-MMM-yyyy').parse(policyEndDate); + // String formattedDate = DateFormat('d MMM yyyy').format(parsedDate); + // + // return GestureDetector( + // onTap: () { + // context.go( + // '/policies', + // extra: item, + // ); + // }, + // child: MouseRegion( + // cursor: SystemMouseCursors.click, + // child: Card( + // elevation: 5, + // shape: RoundedRectangleBorder( + // borderRadius: BorderRadius.circular(15.0), + // ), + // child: Container( + // padding: const EdgeInsets.all(15), + // decoration: BoxDecoration( + // color: Colors.white, + // borderRadius: BorderRadius.circular(15.0), + // ), + // child: _buildCardContent( + // context, + // policyHeading, + // formattedDate, + // policyName, + // siValueStr, + // memberNames, + // finalValue, + // ), + // ), + // ), + // ), + // ); + // }).toList(); + // } + // + // Widget _buildCardContent( + // BuildContext context, + // String policyHeading, + // String formattedDate, + // String policyName, + // String siValueStr, + // String memberNames, + // double finalValue, + // ) { + // return Column( + // crossAxisAlignment: CrossAxisAlignment.start, + // children: [ + // Row( + // mainAxisAlignment: MainAxisAlignment.spaceBetween, + // children: [ + // Text( + // policyHeading, + // style: GoogleFonts.poppins( + // fontSize: 10, + // fontWeight: FontWeight.w400, + // color: Color(0xFF785EFF), + // ), + // ), + // Text( + // 'Expires on $formattedDate', + // style: GoogleFonts.poppins( + // fontSize: 10, + // fontWeight: FontWeight.w400, + // color: Color(0xFF747474), + // ), + // ), + // ], + // ), + // + // const SizedBox(height: 7), + // + // Row( + // children: [ + // Expanded( + // flex: 6, + // child: Text( + // policyName, + // style: GoogleFonts.poppins( + // fontSize: 14, + // fontWeight: FontWeight.w500, + // color: Color(0xFF000000), + // ), + // ), + // ), + // Expanded( + // flex: 5, + // child: Text( + // formatToCroresLakhsAndThousands(siValueStr), + // textAlign: TextAlign.right, + // style: GoogleFonts.poppins( + // fontSize: 14, + // fontWeight: FontWeight.w500, + // color: Color(0xFF000000), + // ), + // ), + // ), + // const Icon(Icons.chevron_right, color: Color(0xFFE26728), size: 28), + // ], + // ), + // + // const SizedBox(height: 7), + // + // Text( + // memberNames, + // style: GoogleFonts.poppins( + // fontSize: 13, + // fontWeight: FontWeight.w400, + // color: Color(0xFF000000), + // ), + // ), + // + // const SizedBox(height: 20), + // + // Align( + // alignment: Alignment.centerRight, + // child: Text( + // "Remaining Amount: $finalValue", + // style: GoogleFonts.poppins( + // fontSize: 10, + // color: Color(0xFF747474), + // ), + // ), + // ), + // ], + // ); + // } + // + List generateCardWidgets(BuildContext context) { + return policyList.map((item) { + String policyHeading = item['heading']; + String policyName = item['policy_name']; + String policyType = item['policy_type']; + String sumInsuredLabel = item['sum_insured_label']; + String siValueStr = item['si_value'] ?? '0'; + String settledStr = item['total_settled_amount']?.toString() ?? '0'; + + double siValue = double.tryParse(siValueStr) ?? 0; + double settled = double.tryParse(settledStr) ?? 0; + + // double finalValue = (settled == 0) ? settled : (siValue - settled); + double finalValue = (settled == 0) ? siValue : (siValue - settled); + + + String policyEndDate = item['policy_end_date']; + List employeePolicy = item['EmployeePolicy']; + print('employeePolicy $employeePolicy'); + String memberNames = formatMemberNames(employeePolicy); + + DateTime parsedDate = DateFormat('dd-MMM-yyyy').parse(policyEndDate); + String formattedDate = DateFormat('d MMM yyyy').format(parsedDate); + + return GestureDetector( + onTap: () { + context.go('/policies', extra: item); + }, + child: MouseRegion( + cursor: SystemMouseCursors.click, + child: _creditCardDesign( + policyHeading: policyHeading, + policyName: policyName, + siValue: siValueStr, + memberNames: memberNames, + expiresOn: formattedDate, + remainingAmount: finalValue, + sumInsuredLabel: sumInsuredLabel, + policyType: policyType, ), - ) + ), + ); + }).toList(); + } + + Widget buildCarousel() { + final List cards = []; + + // ========================================== + // CASE 1: Retail logged in → Show ONLY retail cards + // ========================================== + if (isRetailLoggedIn == true) { + for (var retail in retailPolicyDetails) { + cards.add( + GestureDetector( + onTap: () { + print("Retail policy clicked: ${retail['policy_no']}"); + }, + child: _buildRetailPolicyCard(retail), + ), + ); + } + + return _buildCarouselSlider(cards); // return without add-card + } + + // ========================================== + // CASE 2: Normal flow → Add policy cards + // ========================================== + if (policyList.isNotEmpty) { + cards.addAll(generateCardWidgets(context)); + } + + // final cards = generateCardWidgets(data, context); + + // ====================== + // ADD RETAIL POLICIES + // ====================== + if (retailPolicyDetails.isNotEmpty) { + for (var retail in retailPolicyDetails) { + cards.add( + GestureDetector( + onTap: () { + // TODO: navigate to retail policy details page + print("Retail policy clicked: ${retail['policy_no']}"); + }, + child: _buildRetailPolicyCard(retail), + ), + ); + } + } + + // // ====================== + // // ADD WELLNESS CARD + // // ====================== + // if (wellnessURL != null && wellnessURL.toString().isNotEmpty) { + // cards.add(_buildWellnessCard()); // <-- add card + // } + + // ====================== + // ADD EMPTY "ADD CARD" + // ====================== + if(isActive){ + cards.add(_buildAddCard()); // <-- always add this card + } + + + return _buildCarouselSlider(cards); + } + + Widget _buildCarouselSlider(List cards) { + return Stack( + alignment: Alignment.center, + children: [ + CarouselSlider( + carouselController: _carouselController, + items: cards, + options: CarouselOptions( + height: Responsive.isDesktop(context) ? 200 : 170, + enlargeCenterPage: true, + enableInfiniteScroll: false, + autoPlay: false, + viewportFraction: Responsive.isDesktop(context) ? 0.45 : 0.85, + aspectRatio: 2.0, + initialPage: 0, + onPageChanged: (index, reason) { + setState(() => currentIndex = index); + }, + ), + ), + + // 🔵 CARD COUNTER (4/10) + if(isActive) + Positioned( + bottom: 10, + child: Container( + padding: EdgeInsets.symmetric(horizontal: 12, vertical: 5), + decoration: BoxDecoration( + color: Colors.black.withOpacity(0.45), + borderRadius: BorderRadius.circular(12), + ), + child: Text( + "${currentIndex + 1} / ${cards.length}", + style: TextStyle( + color: Colors.white, + fontSize: 12, + fontWeight: FontWeight.w600, + ), + ), + ), + ), + + // Arrows only on Desktop + if (Responsive.isDesktop(context)) ...[ + if(isActive) + Positioned( + left: 0, + child: IconButton( + icon: Icon(Icons.arrow_back_ios, color: Colors.black, size: 22), + onPressed: () { + if (currentIndex > 0) { + _carouselController.animateToPage( + currentIndex - 1, + duration: Duration(milliseconds: 300), + curve: Curves.easeInOut, + ); + } + }, + ), + ), + if(isActive) + Positioned( + right: 0, + child: IconButton( + icon: Icon(Icons.arrow_forward_ios, color: Colors.black54, size: 22), + onPressed: () { + if (currentIndex < cards.length - 1) { + _carouselController.animateToPage( + currentIndex + 1, + duration: Duration(milliseconds: 300), + curve: Curves.easeInOut, + ); + } + }, + ), + ), + ] + ], ); } - String getMemberNames(List employeePolicy) { - List names = []; - for (var member in employeePolicy) { - if (member['name'] != null) { - names.add(member['name']); - } - } - return names.join(' ~ '); + Widget _creditCardDesign({ + required String policyHeading, + required String policyName, + required String siValue, + required String memberNames, + required String expiresOn, + required double remainingAmount, + required String sumInsuredLabel, + required String policyType, + }) { + return Container( + margin: const EdgeInsets.symmetric(vertical: 8), + height: Responsive.isDesktop(context) + ? 170 + : Responsive.isTablet(context) + ? 150 + : 135, // You can adjust + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(20), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.15), + blurRadius: 10, + offset: const Offset(0, 4), + ) + ], + ), + + child: ClipRRect( + borderRadius: BorderRadius.circular(20), + child: Stack( + children: [ + /// 🔵 BACKGROUND IMAGE + Positioned.fill( + child: Image.asset( + "assets/nhance.png", // <-- REMOVE leading slash + fit: BoxFit.cover, + ), + ), + + // /// 🔵 SEMI-TRANSPARENT GRADIENT OVERLAY + // Positioned.fill( + // child: Container( + // decoration: BoxDecoration( + // gradient: LinearGradient( + // colors: [ + // const Color(0xFF00989e).withOpacity(0.50), // 70% visible + // const Color(0xFF7fcbce).withOpacity(0.70), + // ], + // begin: Alignment.topLeft, + // end: Alignment.bottomRight, + // ), + // ), + // ), + // ), + + /// 🔵 MAIN CARD CONTENT + Padding( + padding: const EdgeInsets.all(14), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + + /// 🔹 ROW 1: Policy Heading + Text( + policyHeading, + style: GoogleFonts.poppins( + fontSize: fontSize(context, 12, 14, 16), + fontWeight: FontWeight.w600, + color: Colors.black87, + ), + ), + + SizedBox(height: 6), + /// 🔹 ROW 3: Details + SVG + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + + /// LEFT TEXT BLOCK + Expanded( + flex: 8, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + memberNames, + style: GoogleFonts.poppins( + fontSize: fontSize(context, 11, 13, 14), + fontWeight: FontWeight.w500, + color: Colors.black87, + ), + ), + SizedBox(height: 6), + Text( + "${formatToCroresLakhsAndThousands(siValue)} ($sumInsuredLabel)", + style: GoogleFonts.poppins( + fontSize: fontSize(context, 11, 13, 14), + fontWeight: FontWeight.w600, + color: Colors.black87, + ), + ), + + SizedBox(height: 6), + + if (remainingAmount > 0) + Text( + "Available: ${remainingAmount.toStringAsFixed(0)}", + style: GoogleFonts.poppins( + fontSize: fontSize(context, 10, 12, 13), + color: Colors.black87, + ), + ), + + SizedBox(height: 6), + + Text( + "Expires: $expiresOn", + style: GoogleFonts.poppins( + fontSize: fontSize(context, 10, 12, 13), + color: Colors.black87, + ), + ), + ], + ), + ), + + /// RIGHT SVG BLOCK + Expanded( + flex: 2, + child: Align( + alignment: Alignment.bottomRight, + child: SizedBox( + height: Responsive.isDesktop(context) + ? 100 + : Responsive.isTablet(context) + ? 100 + : 90, + child: SvgPicture.string( + SvgService.getSvg(_mapPolicyTypeToSvg(policyType)), + fit: BoxFit.fitHeight, + ), + ), + ), + ), + ], + ) + + ], + ), + ) + + ], + ), + ), + ); } - List generateCards(List data) { - print('generateCards'); - print(data); - return [ - GridView.builder( - shrinkWrap: true, - physics: NeverScrollableScrollPhysics(), - gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: Responsive.isDesktop(context) ? 2 : 1, - crossAxisSpacing: 20.0, - mainAxisSpacing: 20.0, - childAspectRatio: Responsive.isDesktop(context) ? 2.2 : 2.6), - itemCount: data.length, - itemBuilder: (BuildContext context, int index) { - var item = data[index]; - String policyHeading = item['heading']; - String policyName = item['policy_name']; - // String si_value = item['si_value']; - String siValueStr = item['si_value'] ?? '0'; - String settledStr = item['total_settled_amount']?.toString() ?? '0'; - - double siValue = double.tryParse(siValueStr) ?? 0; - double settled = double.tryParse(settledStr) ?? 0; - -// Main logic - double finalValue = (settled == 0) ? siValue : (siValue - settled); - - print("Final Value: $finalValue"); - String policyEndDate = item['policy_end_date']; - List employeePolicy = item['EmployeePolicy']; - String memberNames = getMemberNames(employeePolicy); - - DateTime parsedDate = DateFormat('dd-MMM-yyyy').parse(policyEndDate); - String formattedDate = DateFormat('d MMM yyyy').format(parsedDate); - - return GestureDetector( - onTap: () { - context.go( - '/policies', - extra: item, // 👈 pass arguments here - ); - }, - child: MouseRegion( - cursor: SystemMouseCursors.click, - child: Card( - elevation: 5, - shape: RoundedRectangleBorder( - borderRadius: - BorderRadius.circular(15.0), // Set border radius here + Widget _buildWellnessCard() { + return GestureDetector( + onTap: () { + // OPEN WELLNESS URL + launchUrl(Uri.parse(wellnessURL)); + }, + child: Container( + width: double.infinity, + margin: const EdgeInsets.symmetric(vertical: 8), + padding: const EdgeInsets.all(18), + decoration: BoxDecoration( + gradient: const LinearGradient( + colors: [Color(0xFFFF7E5F), Color(0xFFFFB88C)], + begin: Alignment.topLeft, + end: Alignment.bottomRight, + ), + borderRadius: BorderRadius.circular(20), + // border: Border.all(color: Color(0xFF00999E), width: 2), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.05), + blurRadius: 6, + offset: const Offset(0, 3), + ) + ], + ), + child: Row( + children: [ + Icon(Icons.health_and_safety, color: Color(0xFF00999E), size: 40), + const SizedBox(width: 15), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + "OPT & Wellness", + style: GoogleFonts.poppins( + fontSize: 16, + fontWeight: FontWeight.w600, + color: Colors.white, ), - child: Container( - decoration: BoxDecoration( - color: Colors.white, // Set background color to white - borderRadius: BorderRadius.circular( - 15.0), // Set border radius for Container - ), - padding: Responsive.isDesktop(context) - ? EdgeInsets.only( - top: 15, bottom: 15, left: 15, right: 15) - : EdgeInsets.only( - top: 10, - bottom: 10, - left: 10, - right: 10), // Add padding to the container - child: Row( - children: [ - Expanded( - flex: 12, - child: Container( - alignment: Alignment.centerLeft, - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - Row( - children: [ - Expanded( - flex: 7, - child: Column( - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - Text( - policyHeading ?? '', - textAlign: TextAlign.left, - style: GoogleFonts.poppins( - fontSize: - Responsive.isDesktop( - context) - ? 10 - : 10, - fontWeight: FontWeight.w400, - color: Color(0xFF785EFF), - ), - ), - ], - ), - ), - Expanded( - flex: 5, - child: Column( - crossAxisAlignment: - CrossAxisAlignment.end, - children: [ - Text( - 'Expires on $formattedDate', - textAlign: TextAlign.right, - style: GoogleFonts.poppins( - fontSize: - Responsive.isDesktop( - context) - ? 10 - : 10, - fontWeight: FontWeight.w400, - color: Color(0xFF747474), - ), - ), - ], - ), - ), - ], - ), - SizedBox(height: 7), - Row( - children: [ - Expanded( - flex: 6, - child: Column( - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - Text( - policyName ?? '', - textAlign: TextAlign.left, - style: GoogleFonts.poppins( - fontSize: - Responsive.isDesktop( - context) - ? 14 - : 13, - fontWeight: FontWeight.w500, - color: Color(0xFF000000), - ), - ), - ], - ), - ), - Expanded( - flex: 5, - child: Column( - crossAxisAlignment: - CrossAxisAlignment.end, - children: [ - Text( - formatToCroresLakhsAndThousands( - siValueStr), - textAlign: TextAlign.left, - style: GoogleFonts.poppins( - fontSize: - Responsive.isDesktop( - context) - ? 14 - : 13, - fontWeight: FontWeight.w500, - color: Color(0xFF000000), - ), - ), - ], - ), - ), - Expanded( - flex: 1, - child: Container( - alignment: - Alignment.centerRight, - child: Icon( - Icons.chevron_right, - color: Color( - 0xFFE26728), // Replace with your desired icon - size: Responsive.isDesktop( - context) - ? 30 - : 26, - ), - )) - ], - ), - SizedBox(height: 7), - Row( - children: [ - Expanded( - flex: 12, - child: Column( - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - Text( - memberNames ?? '', - textAlign: TextAlign.left, - style: GoogleFonts.poppins( - fontSize: - Responsive.isDesktop( - context) - ? 14 - : 13, - fontWeight: FontWeight.w400, - color: Color(0xFF000000), - ), - ), - ], - ), - ), - ], - ), - SizedBox(height: 20), - Row( - children: [ - Expanded( - flex: 12, - child: Column( - crossAxisAlignment: - CrossAxisAlignment.end, - children: [ - Text( - 'Remaining Amount: ${finalValue}', - textAlign: TextAlign.right, - style: GoogleFonts.poppins( - fontSize: - Responsive.isDesktop( - context) - ? 10 - : 10, - fontWeight: FontWeight.w400, - color: Color(0xFF747474), - ), - ), - ], - ), - ), - ], - ), - // SizedBox(height: 7), - ], - ))), - ], - ), - ), - ))); - }, + ), + ], + ), + ), + Icon(Icons.chevron_right, color: Color(0xFF00999E)) + ], + ), ), - ]; + ); + } + + Widget _buildAddCard() { + return GestureDetector( + onTap: () { + context.go('/AddPolicyScreen'); + }, + child: Container( + width: double.infinity, + margin: const EdgeInsets.symmetric(vertical: 8), + padding: const EdgeInsets.all(20), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(20), + border: Border.all(color: Color(0xFFFF7E5F), width: 2), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.05), + blurRadius: 6, + offset: Offset(0, 3), + ) + ], + ), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Row( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Icon(Icons.add_circle_outline, + color: Color(0xFFFF7E5F), size: 32), + const SizedBox(width: 6), + Text( + "Add Policy", + style: GoogleFonts.poppins( + fontSize: 16, + fontWeight: FontWeight.w600, + color: Color(0xFFFF7E5F), + ), + ), + ], + ), + + const SizedBox(height: 4), + + // 🔶 SUBTITLE + Text( + "Add and manage all your insurance policies taken anywhere for value added services and renewal reminders", + maxLines: 3, + softWrap: true, + overflow: TextOverflow.ellipsis, + textAlign: TextAlign.center, + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w400, + color: Colors.black87, + ), + ), + ], + ), + ), + ], + ), + ), + ); + } + + Widget _buildRetailPolicyCard(Map item) { + return Container( + margin: const EdgeInsets.symmetric(vertical: 8), + height: Responsive.isDesktop(context) + ? 170 + : Responsive.isTablet(context) + ? 150 + : 135, // You can adjust + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(20), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.15), + blurRadius: 10, + offset: const Offset(0, 4), + ) + ], + ), + + child: ClipRRect( + borderRadius: BorderRadius.circular(20), + child: Stack( + children: [ + /// 🔵 BACKGROUND IMAGE + Positioned.fill( + child: Image.asset( + "assets/retail.png", // <-- REMOVE leading slash + fit: BoxFit.cover, + ), + ), + + // /// 🔵 SEMI-TRANSPARENT GRADIENT OVERLAY + // Positioned.fill( + // child: Container( + // decoration: BoxDecoration( + // gradient: LinearGradient( + // colors: [ + // const Color(0xFFFF7E5F).withOpacity(0.50), // 70% visible + // const Color(0xFFFFB88C).withOpacity(0.70), + // ], + // begin: Alignment.topLeft, + // end: Alignment.bottomRight, + // ), + // ), + // ), + // ), + + Padding( + padding: const EdgeInsets.all(14), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + + /// 🔹 ROW 1: Policy Heading + Text( + item['policy_type_long_name'] ?? item['policy_type'], + style: GoogleFonts.poppins( + fontSize: fontSize(context, 12, 14, 16), + fontWeight: FontWeight.w600, + color: Colors.black87, + ), + ), + + SizedBox(height: 6), + /// 🔹 ROW 3: Details + SVG + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + + /// LEFT TEXT BLOCK + Expanded( + flex: 8, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + item['insurer_name'] ?? "-", + style: GoogleFonts.poppins( + fontSize: fontSize(context, 11, 13, 14), + fontWeight: FontWeight.w500, + color: Colors.black87, + ), + ), + + SizedBox(height: 6), + + Text( + "Policy No: ${item['policy_no'] ?? '-'}", + style: GoogleFonts.poppins( + fontSize: fontSize(context, 10, 12, 13), + color: Colors.black87, + ), + ), + + SizedBox(height: 6), + + Text( + "Expires: ${item['policy_end_date'] ?? '-'}", + style: GoogleFonts.poppins( + fontSize: fontSize(context, 10, 12, 13), + color: Colors.black87, + ), + ), + ], + ), + ), + + /// RIGHT SVG BLOCK + Expanded( + flex: 2, + child: Align( + alignment: Alignment.bottomRight, + child: SizedBox( + height: Responsive.isDesktop(context) + ? 100 + : Responsive.isTablet(context) + ? 100 + : 90, + child: SvgPicture.string( + SvgService.getSvg(_mapPolicyTypeToSvg(item['policy_type'])), + fit: BoxFit.fitHeight, + ), + ), + ), + ), + ], + ) + + ], + ), + ) + ], + ), + ), + ); + } + + String _mapPolicyTypeToSvg(String? type) { + if (type == null) return 'InsuranceSvg'; // default icon + + switch (type.trim()) { + case 'Motor': + return 'motorPolicy'; + case 'Householders Policy': + return 'HouseholdersPolicy'; + case 'Individual Health': + return 'individualHealthPolicy'; + case 'Individual Term Life': + return 'individualTermLifePolicy'; + case 'Travel Policy': + return 'travelPolicy'; + case 'GPA': + return 'GPA'; + case 'GMC': + return 'GMC'; + case 'GMC - Parents': + return 'GMCPARENT'; + case 'GMC - TOPUP': + return 'InsuranceSvg'; + case 'GMC - Topup(Parents)': + return 'InsuranceSvg'; + case 'EDLI': + return 'InsuranceSvg'; + case 'GTLI': + return 'InsuranceSvg'; + + default: + return 'InsuranceSvg'; // fallback + } + } + + double fontSize(BuildContext context, double mobile, double tablet, double desktop) { + final width = MediaQuery.of(context).size.width; + + if (Responsive.isMobile(context)) return mobile; // Mobile + if (Responsive.isTablet(context)) return tablet; // Tablet + return desktop; // Desktop } void push(Widget page) { @@ -1305,4 +2439,4 @@ class _HomeState extends State { ), ); } -} \ No newline at end of file +} diff --git a/lib/pages/postEnrollment/planclaimsform.dart b/lib/pages/postEnrollment/planclaimsform.dart index 10f14e3..161a458 100755 --- a/lib/pages/postEnrollment/planclaimsform.dart +++ b/lib/pages/postEnrollment/planclaimsform.dart @@ -11,6 +11,7 @@ import 'package:nhance_app_pwa/pages/postEnrollment/service/api_service.dart'; import 'package:nhance_app_pwa/pages/postEnrollment/service/file_upload_service.dart'; import 'package:nhance_app_pwa/pages/postEnrollment/service/multi_file_upload_widget.dart'; import 'package:shared_preferences/shared_preferences.dart'; +import '../../config/environment.dart'; import '../../customAppBar/customFooter.dart'; import '../../customAppBar/responsive.dart'; import '../../customAppBar/tabs.dart'; @@ -18,7 +19,6 @@ import '../../customAppBar/toastHelper.dart'; import 'package:http/http.dart' as http; // import 'package:universal_html/html.dart' as html; -import '../../models/environment.dart'; import 'package:file_picker/file_picker.dart'; import '../service/SessionManager.dart'; @@ -70,6 +70,7 @@ class _planclaimsformState extends State { dynamic policyTypeCondition; dynamic client_policy_id; dynamic selfDetails; + dynamic emailId; List> employeePolicyList = []; String? selectedFileNames; // html.File? uploadedFile; @@ -83,6 +84,11 @@ class _planclaimsformState extends State { late TextEditingController messageController; late TextEditingController accidentDetailsController; late TextEditingController hospitalNameController; + late TextEditingController hospitalAddressController; + late TextEditingController hospitalCityController; + late TextEditingController hospitalStateController; + late TextEditingController hospitalPinCodeController; + late TextEditingController hospitalPhoneNoController; late TextEditingController claimAmountController; late TextEditingController sumInsuredController; late TextEditingController admitDateController; @@ -113,6 +119,11 @@ class _planclaimsformState extends State { bool isMemberValid = true; bool isSubjectValid = true; bool isHospitalNameValid = true; + bool isHospitalAddressValid = true; + bool isHospitalCityValid = true; + bool isHospitalStateValid = true; + bool isHospitalPincodeValid = true; + bool isHospitalPhoneNoValid = true; bool isAdmitDischargeValid = true; bool isClaimAmountValid = true; bool isAccidentDateValid = true; @@ -131,6 +142,11 @@ class _planclaimsformState extends State { messageController = TextEditingController(); accidentDetailsController = TextEditingController(); hospitalNameController = TextEditingController(); + hospitalAddressController = TextEditingController(); + hospitalCityController = TextEditingController(); + hospitalStateController = TextEditingController(); + hospitalPinCodeController = TextEditingController(); + hospitalPhoneNoController = TextEditingController(); claimAmountController = TextEditingController(); sumInsuredController = TextEditingController(); admitDateController = TextEditingController(); @@ -151,6 +167,11 @@ class _planclaimsformState extends State { messageController.dispose(); accidentDetailsController.dispose(); hospitalNameController.dispose(); + hospitalAddressController.dispose(); + hospitalCityController.dispose(); + hospitalStateController.dispose(); + hospitalPinCodeController.dispose(); + hospitalPhoneNoController.dispose(); claimAmountController.dispose(); sumInsuredController.dispose(); admitDateController.dispose(); @@ -243,6 +264,7 @@ class _planclaimsformState extends State { print(empCodeString); // Check if emp_code is correct empPrimaryId = session.empPrimaryId; client_id = session.client_id; + emailId = session.empEmailCorporate; print(client_id); getActiveAndInactivePolicyDetails(); fetchDepartmentList(); @@ -258,11 +280,11 @@ class _planclaimsformState extends State { try { // Call both APIs final responseActive = await apiService.getActiveAndInactivePolicyDetails( - client_id!, empCodeString!, 'Active', client_branch_id,mobileNo + client_id!, empCodeString!, 'Active', client_branch_id,mobileNo,emailId ); final responseInactive = await apiService.getActiveAndInactivePolicyDetails( - client_id!, empCodeString!, 'Inactive', client_branch_id,mobileNo + client_id!, empCodeString!, 'Inactive', client_branch_id,mobileNo,emailId ); final bool isActiveSuccess = responseActive['status'] == 'success' && responseActive['data'] != null; @@ -520,6 +542,11 @@ class _planclaimsformState extends State { isMemberValid = selectedMemberId != null; isSubjectValid = subjectController.text.trim().isNotEmpty; isHospitalNameValid = policyTypeCondition != 1 || hospitalNameController.text.trim().isNotEmpty; + isHospitalAddressValid = policyTypeCondition != 1 || hospitalAddressController.text.trim().isNotEmpty; + isHospitalStateValid = policyTypeCondition != 1 || hospitalStateController.text.trim().isNotEmpty; + isHospitalCityValid = policyTypeCondition != 1 || hospitalCityController.text.trim().isNotEmpty; + isHospitalPincodeValid = policyTypeCondition != 1 || hospitalPinCodeController.text.trim().isNotEmpty; + isHospitalPhoneNoValid = policyTypeCondition != 1 || hospitalPhoneNoController.text.trim().isNotEmpty; // isAdmitDischargeValid = policyTypeCondition != 1 || (admitDate != null && dischargeDate != null); isAdmitDateValid = policyTypeCondition != 1 || admitDate != null; isDischargeDateValid = policyTypeCondition != 1 || dischargeDate != null; @@ -533,6 +560,11 @@ class _planclaimsformState extends State { isMemberValid && isSubjectValid && isHospitalNameValid && + isHospitalAddressValid && + isHospitalStateValid && + isHospitalCityValid && + isHospitalPincodeValid && + isHospitalPhoneNoValid && isAdmitDateValid && isDischargeDateValid && isClaimAmountValid && @@ -590,6 +622,11 @@ class _planclaimsformState extends State { String formattedAdmitDate = DateFormat('yyyy-MM-dd').format(admitDate!); String formattedDischargeDate = DateFormat('yyyy-MM-dd').format(dischargeDate!); fields['hospital_name'] = hospitalNameController.text; + fields['hospital_address'] = hospitalAddressController.text; + fields['hospital_city'] = hospitalCityController.text; + fields['hospital_state'] = hospitalStateController.text; + fields['hospital_pin_code'] = hospitalPinCodeController.text; + fields['hospital_phone_no'] = hospitalPhoneNoController.text; fields['doa'] = formattedAdmitDate; fields['dod'] = formattedDischargeDate; fields['claim_amount'] = claimAmountController.text; @@ -617,6 +654,7 @@ class _planclaimsformState extends State { final request = http.MultipartRequest('POST', Uri.parse('${Environment.apiUrl}/initiateClaim')); request.headers['Authorization'] = 'Bearer $_token'; + request.headers['APP-SIGNATURE'] = 'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y'; final stringFields = fields.map((key, value) => MapEntry(key, value?.toString() ?? '')); request.fields.addAll(stringFields); @@ -1163,6 +1201,26 @@ SizedBox(height: 15), buildTextField('Hospital Name', hospitalNameController), if (!isHospitalNameValid) Text('Please enter the Hospital Name', style: TextStyle(color: Colors.red)), + SizedBox(height: 15), + buildTextAreaField('Hospital Address',hospitalAddressController), + if (!isHospitalAddressValid) + Text('Please enter the Hospital Address', style: TextStyle(color: Colors.red)), + SizedBox(height: 15), + buildTextField('Hospital City', hospitalCityController), + if (!isHospitalCityValid) + Text('Please enter the Hospital City', style: TextStyle(color: Colors.red)), + SizedBox(height: 15), + buildTextField('Hospital State', hospitalStateController), + if (!isHospitalStateValid) + Text('Please enter the Hospital State', style: TextStyle(color: Colors.red)), + SizedBox(height: 15), + buildTextField('Hospital Pincode', hospitalPinCodeController), + if (!isHospitalPincodeValid) + Text('Please enter the Hospital Pincode', style: TextStyle(color: Colors.red)), + SizedBox(height: 15), + buildTextField('Hospital Phone No', hospitalPhoneNoController), + if (!isHospitalPhoneNoValid) + Text('Please enter the Hospital Phone No', style: TextStyle(color: Colors.red)), ], SizedBox(height: 15), if (serviceId == 2 || @@ -1398,41 +1456,39 @@ SizedBox(height: 15), bottomNavigationBar: Responsive.isDesktop(context) ? null : CustomBottomNavigationBar( - onTabChanged: (index) { - // repositionBotman(); - if (index == 0) { - context.go('/home'); - } else if (index == 1) { - context.go('/claims'); - } else if (index == 2) { - context.go('/profile'); - } else if (index == 3) { - context.go('/help'); - } else if (index == 4) { - // Wellness tab clicked → show popup - PopupHelper.showRedirectPopup( - context: context, - apiService: apiService, - empPrimaryId: session.empPrimaryId, - ); - } - }, - icons: [ - Icons.home_outlined, - Icons.sticky_note_2_outlined, - Icons.person_outline_outlined, - Icons.headset_mic_outlined, - Icons.health_and_safety_outlined, - ], - labels: [ - "Home", - "Claims", - "Profile", - "Help", - "Wellness", - ], - initialIndex: 1,// Initial index of the bottom navigation bar - ), + onTabChanged: (index) { + // Add your navigation logic here + // repositionBotman(); + if (index == 0) { + context.push('/home'); + } else if (index == 1) { + context.push('/claims'); + } else if (index == 2) { + context.push('/profile'); + } else if (index == 3) { + context.push('/help'); + } + // else if (index == 4) { + // // context.push('/wellness'); + // // Wellness tab clicked → show popup + // if(!isRetailLoggedIn) + // PopupHelper.showRedirectPopup( + // context: context, + // apiService: apiService, + // empPrimaryId: session.empPrimaryId, + // ); + // } + }, + icons: [ + Icons.home_outlined, + Icons.sticky_note_2_outlined, + Icons.person_outline_outlined, + Icons.headset_mic_outlined, + // Icons.health_and_safety_outlined, + ], + labels: ["Home", "Claims", "Profile", "Help"], + initialIndex: 0, // Initial index of the bottom navigation bar + ), ) ); } diff --git a/lib/pages/postEnrollment/policies.dart b/lib/pages/postEnrollment/policies.dart index daa1c7b..fc9a16b 100755 --- a/lib/pages/postEnrollment/policies.dart +++ b/lib/pages/postEnrollment/policies.dart @@ -1106,39 +1106,39 @@ class _policiesState extends State { bottomNavigationBar: Responsive.isDesktop(context) ? null : CustomBottomNavigationBar( - onTabChanged: (index) { - if (index == 0) { - context.go('/home'); - } else if (index == 1) { - context.go('/claims'); - } else if (index == 2) { - context.go('/profile'); - } else if (index == 3) { - context.go('/help'); - } else if (index == 4) { - // Wellness tab clicked → show popup - PopupHelper.showRedirectPopup( - context: context, - apiService: apiService, - empPrimaryId: session.empPrimaryId, - ); - } - }, - icons: [ - Icons.home_outlined, - Icons.sticky_note_2_outlined, - Icons.person_outline_outlined, - Icons.headset_mic_outlined, - Icons.health_and_safety_outlined, - ], - labels: [ - "Home", - "Claims", - "Profile", - "Help", - "Wellness", - ], // Initial index of the bottom navigation bar - ), + onTabChanged: (index) { + // Add your navigation logic here + // repositionBotman(); + if (index == 0) { + context.push('/home'); + } else if (index == 1) { + context.push('/claims'); + } else if (index == 2) { + context.push('/profile'); + } else if (index == 3) { + context.push('/help'); + } + // else if (index == 4) { + // // context.push('/wellness'); + // // Wellness tab clicked → show popup + // if(!isRetailLoggedIn) + // PopupHelper.showRedirectPopup( + // context: context, + // apiService: apiService, + // empPrimaryId: session.empPrimaryId, + // ); + // } + }, + icons: [ + Icons.home_outlined, + Icons.sticky_note_2_outlined, + Icons.person_outline_outlined, + Icons.headset_mic_outlined, + // Icons.health_and_safety_outlined, + ], + labels: ["Home", "Claims", "Profile", "Help"], + initialIndex: 0, // Initial index of the bottom navigation bar + ), ) ); } diff --git a/lib/pages/postEnrollment/profile.dart b/lib/pages/postEnrollment/profile.dart index c7639dc..2ef3d43 100755 --- a/lib/pages/postEnrollment/profile.dart +++ b/lib/pages/postEnrollment/profile.dart @@ -11,12 +11,12 @@ import 'package:nhance_app_pwa/pages/postEnrollment/service/api_service.dart'; import 'package:nhance_app_pwa/pages/service/SessionManager.dart'; import 'package:package_info_plus/package_info_plus.dart'; import 'package:shared_preferences/shared_preferences.dart'; +import '../../config/environment.dart'; import '../../customAppBar/customFooter.dart'; import '../../customAppBar/responsive.dart'; import '../../customAppBar/tabs.dart'; import '../../customAppBar/toastHelper.dart'; import 'package:http/http.dart' as http; -import '../../models/environment.dart'; import '../../models/platform_helper_mobile.dart' if (dart.library.html) '../../models/platform_helper_other.dart'; @@ -64,6 +64,7 @@ class _profileState extends State { dynamic selfFamilyFloaterKey; dynamic selfEmpStatus; dynamic client_branch_id; + dynamic selfDetails; final dataManager = DataManager(); final session = SessionManager(); String installedVersion = ""; @@ -126,6 +127,15 @@ class _profileState extends State { empPrimaryId = session.empPrimaryId; client_id = session.client_id; print(client_id); + await dataManager.loadSelfEmployeeProfile( + clientId: session.client_id!, + empCode: session.empCodeString!, + clientBranchId: session.empClientBranchId!, + ) + .then((_) { + selfDetails = dataManager.selfProfile; + print('selfDetails $selfDetails'); + }); getSelfEmployeeProfile(); } } @@ -137,7 +147,7 @@ class _profileState extends State { } Future getSelfEmployeeProfile() async { - final selfDetails = dataManager.selfProfile; + // final selfDetails = dataManager.selfProfile; print('profile'); print(profile); // if (client_id == null || empCodeString == null) { @@ -879,40 +889,39 @@ class _profileState extends State { bottomNavigationBar: Responsive.isDesktop(context) ? null : CustomBottomNavigationBar( - onTabChanged: (index) { - if (index == 0) { - context.push('/home'); - } else if (index == 1) { - context.push('/claims'); - } else if (index == 2) { - context.push('/profile'); - } else if (index == 3) { - context.push('/help'); - } else if (index == 4) { - // Wellness tab clicked → show popup - PopupHelper.showRedirectPopup( - context: context, - apiService: apiService, - empPrimaryId: session.empPrimaryId, - ); - } - }, - icons: [ - Icons.home_outlined, - Icons.sticky_note_2_outlined, - Icons.person_outline_outlined, - Icons.headset_mic_outlined, - Icons.health_and_safety_outlined, - ], - labels: [ - "Home", - "Claims", - "Profile", - "Help", - "Wellness", - ], - initialIndex: 2, // Initial index of the bottom navigation bar - ), + onTabChanged: (index) { + // Add your navigation logic here + // repositionBotman(); + if (index == 0) { + context.push('/home'); + } else if (index == 1) { + context.push('/claims'); + } else if (index == 2) { + context.push('/profile'); + } else if (index == 3) { + context.push('/help'); + } + // else if (index == 4) { + // // context.push('/wellness'); + // // Wellness tab clicked → show popup + // if(!isRetailLoggedIn) + // PopupHelper.showRedirectPopup( + // context: context, + // apiService: apiService, + // empPrimaryId: session.empPrimaryId, + // ); + // } + }, + icons: [ + Icons.home_outlined, + Icons.sticky_note_2_outlined, + Icons.person_outline_outlined, + Icons.headset_mic_outlined, + // Icons.health_and_safety_outlined, + ], + labels: ["Home", "Claims", "Profile", "Help"], + initialIndex: 0, // Initial index of the bottom navigation bar + ), )); } diff --git a/lib/pages/postEnrollment/raisedTicketList.dart b/lib/pages/postEnrollment/raisedTicketList.dart index 7681aa2..5a25751 100644 --- a/lib/pages/postEnrollment/raisedTicketList.dart +++ b/lib/pages/postEnrollment/raisedTicketList.dart @@ -306,39 +306,36 @@ class _raisedTicketHistoryState extends State { : CustomBottomNavigationBar( onTabChanged: (index) { // Add your navigation logic here - // For example: + // repositionBotman(); if (index == 0) { - context.go('/home'); + context.push('/home'); } else if (index == 1) { - context.go('/claims'); + context.push('/claims'); } else if (index == 2) { - context.go('/profile'); + context.push('/profile'); } else if (index == 3) { - context.go('/help'); - } else if (index == 4) { - // Wellness tab clicked → show popup - PopupHelper.showRedirectPopup( - context: context, - apiService: apiService, - empPrimaryId: session.empPrimaryId, - ); + context.push('/help'); } + // else if (index == 4) { + // // context.push('/wellness'); + // // Wellness tab clicked → show popup + // if(!isRetailLoggedIn) + // PopupHelper.showRedirectPopup( + // context: context, + // apiService: apiService, + // empPrimaryId: session.empPrimaryId, + // ); + // } }, icons: [ Icons.home_outlined, Icons.sticky_note_2_outlined, Icons.person_outline_outlined, Icons.headset_mic_outlined, - Icons.health_and_safety_outlined, + // Icons.health_and_safety_outlined, ], - labels: [ - "Home", - "Claims", - "Profile", - "Help", - "Wellness", - ], - initialIndex: 3, // Initial index of the bottom navigation bar + labels: ["Home", "Claims", "Profile", "Help"], + initialIndex: 0, // Initial index of the bottom navigation bar ), ) ); @@ -398,7 +395,8 @@ class _raisedTicketHistoryState extends State { return GestureDetector( onTap: () { print("Navigating with thz_id: ${item['thz_id']}"); - context.go('/tickettracklist', extra: item['thz_id']); + context.go('/tickettracklist/${item['thz_id']}'); + // context.go('/tickettracklist', extra: item['thz_id']); }, child: MouseRegion( cursor: SystemMouseCursors.click, diff --git a/lib/pages/postEnrollment/retailClaimForm.dart b/lib/pages/postEnrollment/retailClaimForm.dart new file mode 100644 index 0000000..b6bcdb2 --- /dev/null +++ b/lib/pages/postEnrollment/retailClaimForm.dart @@ -0,0 +1,576 @@ +import 'dart:convert'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:intl/intl.dart'; +import 'package:jwt_decode/jwt_decode.dart'; +import 'package:nhance_app_pwa/customAppBar/customAppBar.dart'; +import 'package:nhance_app_pwa/pages/postEnrollment/service/api_service.dart'; +import 'package:nhance_app_pwa/pages/service/SessionManager.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import '../../config/environment.dart'; +import '../../customAppBar/customFooter.dart'; +import '../../customAppBar/responsive.dart'; +import '../../customAppBar/tabs.dart'; +import '../../customAppBar/toastHelper.dart'; +import 'package:http/http.dart' as http; + +import '../service/TokenService.dart'; +import '../service/popup_helper.dart'; + +class retailClaimForm extends StatefulWidget { + final Map? details; + const retailClaimForm({Key? key, this.details}) : super(key: key); + + @override + State createState() => _retailClaimFormsState(); +} + +class _retailClaimFormsState extends State { + late ApiService apiService; + bool isLoading = false; + dynamic _token; + dynamic empCodeString; + dynamic empPrimaryId; + dynamic client_id; + Map? decodedToken; + dynamic client_branch_id; + dynamic mobileNo; + dynamic policyTypeCondition; + dynamic client_policy_id; + dynamic emailId; + String? selectedClaimTypeId; // claim type id to submit + List claimTypeMasterList = []; // api list + TextEditingController policyNoController = TextEditingController(); + TextEditingController descriptionController = TextEditingController(); + + // Create a unique form key for each accordion section + final GlobalKey formKey = GlobalKey(); + + bool isServiceValid = true; + bool isPolicyValid = true; + bool isSubjectValid = true; + bool isMessageValid = true; + + final session = SessionManager(); + + @override + void initState() { + super.initState(); + apiService = ApiService(context); // Initialize ApiService here + descriptionController = TextEditingController(); + policyNoController = TextEditingController(); + // Retail policy details + final retailData = widget.details?['retailDetails'] ?? {}; + policyNoController.text = retailData['policy_no'] ?? ""; + _loadToken(); + } + + @override + void dispose() { + // Dispose the controllers to avoid memory leaks + policyNoController.dispose(); + descriptionController.dispose(); + super.dispose(); + } + + Future _loadToken() async { + print('_loadToken'); + final String? token = await TokenService.getPostToken(); + if (token != null && token.isNotEmpty) { + setState(() { + _token = token; + }); + // Decode the JWT token received from the API response + decodedToken = Jwt.parseJwt(token); + print(decodedToken); + mobileNo = session.mobileNo; + client_branch_id = session.empClientBranchId; + empCodeString = session.empCodeString; + print(empCodeString); // Check if emp_code is correct + empPrimaryId = session.empPrimaryId; + client_id = session.client_id; + emailId = session.empEmailCorporate; + print(client_id); + getClaimTypeMasterList(); + } + } + + Future getClaimTypeMasterList() async { + setState(() => isLoading = true); + + try { + final response = await apiService.getClaimTypeMasterApi(); + if (response["status"] == "success") { + setState(() { + claimTypeMasterList = response["data"]; + }); + } + } finally { + setState(() => isLoading = false); + } + } + + + Future submitRetailClaim() async { + if (!formKey.currentState!.validate()) { + ToastHelper.showErrorToast(context, "Please fill required fields"); + return; + } + + setState(() => isLoading = true); + + final retailData = widget.details?['retailDetails'] ?? {}; + + try { + // ------------------------------ + // Prepare data + // ------------------------------ + Map formData = { + "policy_transaction_id": widget.details?['retailDetails']['policy_transaction_id'] ?? "", + "policy_no": policyNoController.text, + "policy_type_id": widget.details?['retailDetails']['policy_type_id'] ?? "", + "claim_type":selectedClaimTypeId, + "claim_description": descriptionController.text, + }; + + print("Retail Claim FormData: $formData"); + + // ------------------------------ + // Prepare MultipartRequest + // ------------------------------ + final request = http.MultipartRequest( + 'POST', + Uri.parse("${Environment.apiUrl}/initiateClaim"), + ); + + request.headers['Authorization'] = 'Bearer $_token'; + request.headers['APP-SIGNATURE'] = 'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y'; + + // Convert all values to String and add to fields + final stringFields = formData.map( + (key, value) => MapEntry(key, value?.toString() ?? ''), + ); + + request.fields.addAll(stringFields); + + // ------------------------------ + // Send request + // ------------------------------ + final response = await request.send(); + final responseBody = await response.stream.bytesToString(); + + print("Retail Claim API Response: $responseBody"); + + final decoded = jsonDecode(responseBody); + + // ------------------------------ + // Handle Response + // ------------------------------ + if (decoded['status'] == true) { + ToastHelper.showSuccessToast( + context, + decoded['message'] ?? "Claim submitted successfully!", + ); + + // Clear form + selectedClaimTypeId = null; + descriptionController.clear(); + + setState(() => isLoading = false); + + // Redirect + context.go("/claims"); + } else { + ToastHelper.showErrorToast( + context, + decoded['message'] ?? "Failed to submit claim", + ); + } + } catch (e) { + print("Retail claim submit ERROR: $e"); + ToastHelper.showErrorToast(context, "Something went wrong"); + } + + setState(() => isLoading = false); + } + + + @override + Widget build(BuildContext context) { + return PopScope( + canPop: false, + onPopInvokedWithResult: (didPop, result) { + if (didPop) return; + context.go('/claims'); + }, + child: Scaffold( + backgroundColor: Colors.white, + appBar: CustomAppBar(), + body: Stack(children: [ + SingleChildScrollView( + child: Container( + padding: Responsive.isDesktop(context) + ? EdgeInsets.symmetric( + horizontal: MediaQuery.of(context).size.width * + 0.2, // 30% of screen width as horizontal padding + vertical: MediaQuery.of(context).size.height * + 0.05, // 5% of screen height as vertical padding + ) + : EdgeInsets.all(10), + color: Colors.white, + child: Column(children: [ + Card( + elevation: 5, + shape: RoundedRectangleBorder( + borderRadius: + BorderRadius.circular(15.0), // Set border radius here + ), + child: Container( + decoration: BoxDecoration( + color: Colors.white, // Set background color to white + borderRadius: BorderRadius.circular( + 15.0), // Set border radius for Container + ), + padding: Responsive.isDesktop(context) + ? EdgeInsets.only( + top: 15, bottom: 15, left: 15, right: 15) + : EdgeInsets.only( + top: 10, + bottom: 10, + left: 10, + right: 10), // Add padding to the container + child: Row( + children: [ + Expanded( + flex: 12, + child: Container( + alignment: Alignment.centerLeft, + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + decoration: BoxDecoration( + color: Color( + 0xFFFFFCE5), // Set background color for the container + borderRadius: BorderRadius.circular( + 10), // Set border radius for the container + ), + padding: Responsive.isDesktop(context) + ? EdgeInsets.only( + top: 20, + bottom: 20, + left: 0, + right: 0) + : EdgeInsets.only( + top: 10, + bottom: 10, + left: 10, + right: 10), + child: Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: + MainAxisAlignment.start, + children: [ + Expanded( + // flex: 1, + child: InkWell( + onTap: () { + context.go('/claims'); + }, + child: Icon( + Icons + .chevron_left, // Replace with your desired icon + color: Color(0xFF000000), + size: 30, + ), + ), + ), + Expanded( + flex: 11, + child: Row( + mainAxisAlignment: Responsive + .isDesktop(context) + ? MainAxisAlignment.start + : MainAxisAlignment.start, + children: [ + Text( + 'Claim Retail', + textAlign: + TextAlign.start, + style: + GoogleFonts.poppins( + fontSize: Responsive + .isDesktop( + context) + ? 20 + : 16, + fontWeight: + FontWeight.w600, + color: + Color(0xFF000000), + ), + ) + ], + ), + ) + ], + ), // Space between rows + // Add more rows as needed + ], + ), + ), + SizedBox(height: 15), + Container( + child: Column( + children: [ + Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Container( + width: double.infinity, + alignment: Alignment.centerLeft, + child: Column( + mainAxisAlignment: + MainAxisAlignment.start, + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Form( + key: formKey, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + + // ---------------- Claim Type Dropdown ---------------- + DropdownButtonFormField( + value: selectedClaimTypeId, + decoration: const InputDecoration(labelText: "Claim Type"), + items: claimTypeMasterList.map>((item) { + return DropdownMenuItem( + value: item['id']?.toString() ?? "", // FIXED + child: Text(item['claim_type']?.toString() ?? ""), // FIXED + ); + }).toList(), + onChanged: (value) { + setState(() => selectedClaimTypeId = value); + }, + validator: (value) => + value == null || value.isEmpty ? "Please select a Claim Type" : null, + ), + + + const SizedBox(height: 20), + + // ---------------- Policy Number (readonly) ---------------- + TextFormField( + controller: policyNoController, + readOnly: true, + decoration: const InputDecoration( + labelText: "Policy Number", + ), + validator: (value) { + if (value == null || value.isEmpty) { + return "Policy Number missing"; + } + return null; + }, + ), + + const SizedBox(height: 20), + + // ---------------- Claim Description ---------------- + TextFormField( + controller: descriptionController, + maxLines: 5, + decoration: const InputDecoration( + labelText: "Claim Description", + ), + validator: (value) { + if (value == null || value.trim().isEmpty) { + return "Please enter the claim description"; + } + return null; + }, + ), + + const SizedBox(height: 25), + + // ---------------- Submit Button ---------------- + Align( + alignment: Alignment.centerRight, + child: ElevatedButton( + onPressed: () => submitRetailClaim(), + style: ElevatedButton.styleFrom( + backgroundColor: const Color(0xFFE26728), + ), + child: Text("Submit", style: GoogleFonts.poppins(color: Colors.white)), + ), + ), + ], + ), + ) + + ], + ), + ), + ], + ), + ], + ), + ), + ], + ))), + ], + ), + ), + ), + SizedBox(height: Responsive.isDesktop(context) ? 40 : 60), + ]), + )), + if (isLoading) + Container( + color: Color(0x98FFFCE5), // Semi-transparent background + child: Center( + child: // Your GIF loader widget + Image.asset( + height: 60, + width: 60, + 'assets/nhance-loader.gif'), // Adjust path to your GIF loader + ), + ), + if (Responsive.isDesktop(context)) + Align( + alignment: Alignment.bottomCenter, + child: Container( + width: double.infinity, // Make the footer full width + child: CustomFooter(), + ), + ), + ]), + // floatingActionButton: Responsive.isDesktop(context) + // ? null + // : FloatingActionButton( + // onPressed: () { + // Navigator.pushNamed(context, 'chatbot'); + // }, + // child: Icon(Icons.chat), + // ), + floatingActionButtonLocation: Responsive.isDesktop(context) + ? null + : FloatingActionButtonLocation.miniEndFloat, + bottomNavigationBar: Responsive.isDesktop(context) + ? null + : CustomBottomNavigationBar( + onTabChanged: (index) { + // Add your navigation logic here + // repositionBotman(); + if (index == 0) { + context.push('/home'); + } else if (index == 1) { + context.push('/claims'); + } else if (index == 2) { + context.push('/profile'); + } else if (index == 3) { + context.push('/help'); + } + // else if (index == 4) { + // // context.push('/wellness'); + // // Wellness tab clicked → show popup + // if(!isRetailLoggedIn) + // PopupHelper.showRedirectPopup( + // context: context, + // apiService: apiService, + // empPrimaryId: session.empPrimaryId, + // ); + // } + }, + icons: [ + Icons.home_outlined, + Icons.sticky_note_2_outlined, + Icons.person_outline_outlined, + Icons.headset_mic_outlined, + // Icons.health_and_safety_outlined, + ], + labels: ["Home", "Claims", "Profile", "Help"], + initialIndex: 0, // Initial index of the bottom navigation bar + ), + )); + } + + Widget buildTextField(String label, TextEditingController controller, + [TextInputType keyboardType = TextInputType.text]) { + return TextFormField( + controller: controller, + decoration: InputDecoration(labelText: label), + keyboardType: keyboardType, + maxLength: 150, + validator: (value) { + if (value == null || value.isEmpty) { + return 'Please enter the $label'; + } + return null; + }, + ); + } + + Widget buildTextAreaField(String label, TextEditingController controller) { + return TextFormField( + controller: controller, + decoration: InputDecoration(labelText: label), + maxLines: 5, + maxLength: 1500, + validator: (value) { + if (value == null || value.isEmpty) { + return 'Please enter the $label'; + } + return null; + }, + ); + } + + Widget buildDropdownField( + String label, + void Function(T?) onChanged, + bool readOnly, + List> itemsList, + String displayField, + T? selectedValue, { + String valueKey = 'id', + String? combineField, + }) { + return DropdownButtonFormField( + value: selectedValue, + decoration: InputDecoration(labelText: label), + items: itemsList.map>((item) { + // ▼ If combineField is provided → show "policy_no - policy_type" + String textToShow; + if (combineField != null) { + textToShow = "${item[displayField]} - ${item[combineField]}"; + } else { + textToShow = item[displayField].toString(); + } + + return DropdownMenuItem( + value: item[valueKey] as T, // ✅ cast to generic type + child: + Text(textToShow), // always display as String + ); + }).toList(), + onChanged: readOnly ? null : onChanged, + validator: (value) { + if (value == null) { + return 'Please select a $label'; + } + return null; + }, + ); + } +} diff --git a/lib/pages/postEnrollment/service/api_service.dart b/lib/pages/postEnrollment/service/api_service.dart index 5a5714b..852edae 100755 --- a/lib/pages/postEnrollment/service/api_service.dart +++ b/lib/pages/postEnrollment/service/api_service.dart @@ -2,11 +2,12 @@ import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:http/http.dart' as http; import 'package:nhance_app_pwa/customAppBar/toastHelper.dart'; -import 'package:nhance_app_pwa/models/environment.dart'; import 'package:nhance_app_pwa/pages/service/TokenService.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'dart:convert'; +import '../../../config/environment.dart'; + class ApiService { final BuildContext context; String? _postToken; @@ -20,17 +21,17 @@ class ApiService { } Future> getActiveAndInactivePolicyDetails( - String clientId, - String empCode, - String status, - String branchID, - String mobileNo) async { + String? clientId, + String? empCode, + String? status, + String? branchID, + String? mobileNo,String? emailId) async { print(_postToken); if (_postToken == null) { await _initializeToken(); } final url = Uri.parse( - '${Environment.apiUrl}getEmployeeActiveOrInactivePolicy?client_id=$clientId&emp_code=$empCode&type=$status&client_branch_id=$branchID&mobile_no=$mobileNo'); + '${Environment.apiUrl}getEmployeeActiveOrInactivePolicy?client_id=${clientId ?? ''}&emp_code=${empCode ?? ''}&type=${status ?? ''}&client_branch_id=${branchID ?? ''}&mobile_no=${mobileNo ?? ''}&email_id=${emailId ?? ''}'); final headers = { 'Authorization': 'Bearer $_postToken' ?? '', }; @@ -51,12 +52,12 @@ class ApiService { return response; } - Future> getAdvertisementImageToApi() async { + Future> getAdvertisementImageToApi(clientID) async { print(_postToken); if (_postToken == null) { await _initializeToken(); } - final url = Uri.parse('${Environment.apiUrl}getAdvertisementImage'); + final url = Uri.parse('${Environment.apiUrl}getAdvertisementImage?client_id=$clientID'); final headers = { 'Authorization': 'Bearer $_postToken' ?? '', }; @@ -92,13 +93,13 @@ class ApiService { return response; } - Future> getTrackClaimsList(String empPrimaryId) async { + Future> getTrackClaimsList(String empPrimaryId,mobileNo,emailID) async { print(_postToken); if (_postToken == null) { await _initializeToken(); } final url = Uri.parse( - '${Environment.apiUrl}/get_ticket_data?emp_id=$empPrimaryId'); + '${Environment.apiUrl}/get_ticket_data?emp_id=$empPrimaryId&mobile_number=$mobileNo&email_id=$emailID'); final headers = { 'Authorization': 'Bearer $_postToken' ?? '', }; @@ -170,6 +171,22 @@ class ApiService { return response; } + Future> sendRetailPolicyDetails(formData) async { + print(_postToken); + if (_postToken == null) { + await _initializeToken(); + } + final url = Uri.parse('${Environment.apiUrl}addEmpRetailPolicy'); + final headers = { + 'Authorization': 'Bearer $_postToken' ?? '', + 'Content-Type': 'application/json', // Ensure content type is JSON + }; + final jsonBody = jsonEncode(formData); + // Send formDataJson as the body + final response = await _makePostRequestWithoutFormData(url,jsonBody, headers); + return response; + } + Future updateMobileNumber(Map updateParam) async { if (_postToken == null) { await _initializeToken(); @@ -194,6 +211,19 @@ class ApiService { } + Future> fetchPolicyTypeAndInsurer() async { + print(_postToken); + if (_postToken == null) { + await _initializeToken(); + } + final url = Uri.parse('${Environment.apiUrl}getPolicyTypeAndInsurer'); + final headers = { + 'Authorization': 'Bearer $_postToken' ?? '', + }; + final response = await _makeGetRequest(url, headers); + return response; + } + Future> fetchDepartmentList(String clientId, String empCode) async { print(_postToken); if (_postToken == null) { @@ -207,6 +237,19 @@ class ApiService { return response; } + Future> getClaimTypeMasterApi() async { + print(_postToken); + if (_postToken == null) { + await _initializeToken(); + } + final url = Uri.parse('${Environment.apiUrl}/getClaimTypeMaster'); + final headers = { + 'Authorization': 'Bearer $_postToken' ?? '', + }; + final response = await _makeGetRequest(url, headers); + return response; + } + Future> sendTicketFormDataToApi( Map formData) async { print(_postToken); @@ -313,19 +356,24 @@ class ApiService { Future> _makeGetRequest( Uri url, Map headers) async { + // Add global APP_SIGNATURE header + headers['APP-SIGNATURE'] = 'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y'; final response = await http.get(url, headers: headers); return _handleResponse(response); } Future> _makePostRequest( Uri url, Map body, Map headers) async { + // Add global APP_SIGNATURE header + headers['APP-SIGNATURE'] = 'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y'; final response = await http.post(url, headers: headers, body: body); return _handleResponse(response); } Future> _makePostRequestWithoutFormData( Uri url, String body, Map headers) async { - + // Add global APP_SIGNATURE header + headers['APP-SIGNATURE'] = 'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y'; final response = await http.post(url, headers: headers, body: body); return _handleResponse(response); } diff --git a/lib/pages/postEnrollment/service/multi_file_upload_widget.dart b/lib/pages/postEnrollment/service/multi_file_upload_widget.dart index 7ae2df4..018d1ed 100644 --- a/lib/pages/postEnrollment/service/multi_file_upload_widget.dart +++ b/lib/pages/postEnrollment/service/multi_file_upload_widget.dart @@ -3,12 +3,14 @@ import 'package:nhance_app_pwa/customAppBar/responsive.dart'; import 'file_upload_service.dart'; class MultiFileUploadWidget extends StatefulWidget { - const MultiFileUploadWidget({super.key}); + final bool forceMobile; + + const MultiFileUploadWidget({super.key, this.forceMobile = false}); @override State createState() => _MultiFileUploadWidgetState(); - static bool hasFiles = false; // ✅ Static variable to check from parent + static bool hasFiles = false; } class _MultiFileUploadWidgetState extends State { @@ -60,7 +62,7 @@ class _MultiFileUploadWidgetState extends State { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - if (Responsive.isMobile(context)) ...[ + if (widget.forceMobile || Responsive.isMobile(context)) ...[ OutlinedButton.icon( onPressed: _pickFiles, icon: const Icon(Icons.file_upload_outlined, diff --git a/lib/pages/postEnrollment/service/svg_service.dart b/lib/pages/postEnrollment/service/svg_service.dart index 33be40d..4b7abcc 100755 --- a/lib/pages/postEnrollment/service/svg_service.dart +++ b/lib/pages/postEnrollment/service/svg_service.dart @@ -140,11 +140,6 @@ class SvgService { '''; - static const String email = ''' - - - - '''; static const String claims = ''' @@ -181,6 +176,1425 @@ class SvgService { '''; + static const String wellnessopt = ''' + + + '''; + static const String female = ''' @@ -409,6 +1823,1744 @@ class SvgService { '''; + static const String initialPopup = ''' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + '''; + + static const String InsuranceSvg = ''' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + '''; + + static const String motorPolicy = ''' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + '''; + + static const String HouseholdersPolicy = ''' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + '''; + + static const String travelPolicy = ''' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + '''; + + static const String individualHealthPolicy = ''' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + '''; + + static const String individualTermLifePolicy = ''' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + '''; + + static const String GPA = ''' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + '''; + + static const String GMC = ''' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + '''; + + static const String GMCPARENT = ''' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + '''; + + static const String GMCTOPUP = ''' + + '''; + + static const String GMCTOPUPPARENT = ''' + + '''; + + static const String EDLI = ''' + + '''; + + static const String GTLI = ''' + + '''; + + static const String cardBG = ''' + + '''; + + static const String email = ''' + + + + '''; + static String getSvg(String svgName) { switch (svgName) { case 'ECard': @@ -445,6 +3597,36 @@ class SvgService { return male; case 'personFemale': return female; + case 'initialPopup': + return initialPopup; + case 'InsuranceSvg': + return InsuranceSvg; + case 'wellnessopt': + return wellnessopt; + case 'motorPolicy': + return motorPolicy; + case 'HouseholdersPolicy': + return HouseholdersPolicy; + case 'travelPolicy': + return travelPolicy; + case 'individualTermLifePolicy': + return individualTermLifePolicy; + case 'individualHealthPolicy': + return individualHealthPolicy; + case 'GPA': + return GPA; + case 'GMC': + return GMC; + case 'GMCPARENT': + return GMCPARENT; + case 'GMCTOPUP': + return GMCTOPUP; + case 'GMCTOPUPPARENT': + return GMCTOPUPPARENT; + case 'EDLI': + return EDLI; + case 'GTLI': + return GTLI; // Add more cases here for additional SVGs default: return ''; diff --git a/lib/pages/postEnrollment/tickets.dart b/lib/pages/postEnrollment/tickets.dart index 447ed35..2665262 100644 --- a/lib/pages/postEnrollment/tickets.dart +++ b/lib/pages/postEnrollment/tickets.dart @@ -47,6 +47,7 @@ class _ticketsState extends State { dynamic policyTypeCondition; dynamic client_policy_id; dynamic selfDetails; + dynamic emailId; List> employeePolicyList = []; // Declare subjectController and bodyController as instance variables @@ -103,6 +104,7 @@ class _ticketsState extends State { print(empCodeString); // Check if emp_code is correct empPrimaryId = session.empPrimaryId; client_id = session.client_id; + emailId = session.empEmailCorporate; print(client_id); getActiveAndInactivePolicyDetails(); getSelfEmployeeProfile(); @@ -117,11 +119,11 @@ class _ticketsState extends State { try { // Call both APIs final responseActive = await apiService.getActiveAndInactivePolicyDetails( - client_id!, empCodeString!, 'Active', client_branch_id, mobileNo); + client_id!, empCodeString!, 'Active', client_branch_id, mobileNo,emailId); final responseInactive = await apiService.getActiveAndInactivePolicyDetails(client_id!, - empCodeString!, 'Inactive', client_branch_id, mobileNo); + empCodeString!, 'Inactive', client_branch_id, mobileNo,emailId); final bool isActiveSuccess = responseActive['status'] == 'success' && responseActive['data'] != null; @@ -476,6 +478,7 @@ class _ticketsState extends State { policyNumberId, valueKey: 'policy_no', + combineField: 'policy_name', ), if (!isPolicyValid) Text( @@ -601,41 +604,39 @@ class _ticketsState extends State { bottomNavigationBar: Responsive.isDesktop(context) ? null : CustomBottomNavigationBar( - onTabChanged: (index) { - // repositionBotman(); - if (index == 0) { - context.go('/home'); - } else if (index == 1) { - context.go('/claims'); - } else if (index == 2) { - context.go('/profile'); - } else if (index == 3) { - context.go('/help'); - } else if (index == 4) { - // Wellness tab clicked → show popup - PopupHelper.showRedirectPopup( - context: context, - apiService: apiService, - empPrimaryId: session.empPrimaryId, - ); - } - }, - icons: [ - Icons.home_outlined, - Icons.sticky_note_2_outlined, - Icons.person_outline_outlined, - Icons.headset_mic_outlined, - Icons.health_and_safety_outlined, - ], - labels: [ - "Home", - "Claims", - "Profile", - "Help", - "Wellness", - ], - initialIndex: 3, // Initial index of the bottom navigation bar - ), + onTabChanged: (index) { + // Add your navigation logic here + // repositionBotman(); + if (index == 0) { + context.push('/home'); + } else if (index == 1) { + context.push('/claims'); + } else if (index == 2) { + context.push('/profile'); + } else if (index == 3) { + context.push('/help'); + } + // else if (index == 4) { + // // context.push('/wellness'); + // // Wellness tab clicked → show popup + // if(!isRetailLoggedIn) + // PopupHelper.showRedirectPopup( + // context: context, + // apiService: apiService, + // empPrimaryId: session.empPrimaryId, + // ); + // } + }, + icons: [ + Icons.home_outlined, + Icons.sticky_note_2_outlined, + Icons.person_outline_outlined, + Icons.headset_mic_outlined, + // Icons.health_and_safety_outlined, + ], + labels: ["Home", "Claims", "Profile", "Help"], + initialIndex: 0, // Initial index of the bottom navigation bar + ), )); } @@ -678,15 +679,24 @@ class _ticketsState extends State { String displayField, T? selectedValue, { String valueKey = 'id', - }) { + String? combineField, + }) { return DropdownButtonFormField( value: selectedValue, decoration: InputDecoration(labelText: label), items: itemsList.map>((item) { + // ▼ If combineField is provided → show "policy_no - policy_type" + String textToShow; + if (combineField != null) { + textToShow = "${item[displayField]} - ${item[combineField]}"; + } else { + textToShow = item[displayField].toString(); + } + return DropdownMenuItem( value: item[valueKey] as T, // ✅ cast to generic type child: - Text(item[displayField].toString()), // always display as String + Text(textToShow), // always display as String ); }).toList(), onChanged: readOnly ? null : onChanged, diff --git a/lib/pages/postEnrollment/tickettracklist.dart b/lib/pages/postEnrollment/tickettracklist.dart index 5a5f0c6..dbc6f89 100755 --- a/lib/pages/postEnrollment/tickettracklist.dart +++ b/lib/pages/postEnrollment/tickettracklist.dart @@ -675,40 +675,39 @@ class _tickettracklistState extends State { bottomNavigationBar: Responsive.isDesktop(context) ? null : CustomBottomNavigationBar( - onTabChanged: (index) { - if (index == 0) { - context.go('/home'); - } else if (index == 1) { - context.go('/claims'); - } else if (index == 2) { - context.go('/profile'); - } else if (index == 3) { - context.go('/help'); - } else if (index == 4) { - // Wellness tab clicked → show popup - PopupHelper.showRedirectPopup( - context: context, - apiService: apiService, - empPrimaryId: session.empPrimaryId, - ); - } - }, - icons: [ - Icons.home_outlined, - Icons.sticky_note_2_outlined, - Icons.person_outline_outlined, - Icons.headset_mic_outlined, - Icons.health_and_safety_outlined, - ], - labels: [ - "Home", - "Claims", - "Profile", - "Help", - "Wellness", - ], - initialIndex: 3, // Initial index of the bottom navigation bar - ), + onTabChanged: (index) { + // Add your navigation logic here + // repositionBotman(); + if (index == 0) { + context.push('/home'); + } else if (index == 1) { + context.push('/claims'); + } else if (index == 2) { + context.push('/profile'); + } else if (index == 3) { + context.push('/help'); + } + // else if (index == 4) { + // // context.push('/wellness'); + // // Wellness tab clicked → show popup + // if(!isRetailLoggedIn) + // PopupHelper.showRedirectPopup( + // context: context, + // apiService: apiService, + // empPrimaryId: session.empPrimaryId, + // ); + // } + }, + icons: [ + Icons.home_outlined, + Icons.sticky_note_2_outlined, + Icons.person_outline_outlined, + Icons.headset_mic_outlined, + // Icons.health_and_safety_outlined, + ], + labels: ["Home", "Claims", "Profile", "Help"], + initialIndex: 0, // Initial index of the bottom navigation bar + ), ) ); } diff --git a/lib/pages/postEnrollment/wellness.dart b/lib/pages/postEnrollment/wellness.dart index ab35263..dae8a31 100755 --- a/lib/pages/postEnrollment/wellness.dart +++ b/lib/pages/postEnrollment/wellness.dart @@ -38,6 +38,7 @@ class _wellnessState extends State { dynamic mobileNo; dynamic policyList; dynamic employeeDetailsList; + dynamic emailId; final session = SessionManager(); @override @@ -67,6 +68,7 @@ class _wellnessState extends State { print(empCodeString); // Check if emp_code is correct empPrimaryId = session.empPrimaryId; client_id = session.client_id; + emailId = session.empEmailCorporate; print(client_id); // getAdvertisementSliderImage(); getActiveAndInactivePolicyDetails('Active'); @@ -83,7 +85,7 @@ class _wellnessState extends State { }); print('check 1'); final response = await apiService.getActiveAndInactivePolicyDetails( - client_id!, empCodeString!, status, client_branch_id, mobileNo); + client_id!, empCodeString!, status, client_branch_id, mobileNo,emailId); print('check 1'); if (response['status'] == 'success') { policyList = response['data']; @@ -365,7 +367,7 @@ class _wellnessState extends State { : CustomBottomNavigationBar( onTabChanged: (index) { // Add your navigation logic here - // For example: + // repositionBotman(); if (index == 0) { context.push('/home'); } else if (index == 1) { @@ -374,25 +376,27 @@ class _wellnessState extends State { context.push('/profile'); } else if (index == 3) { context.push('/help'); - } else if (index == 4) { - context.push('/wellness'); } + // else if (index == 4) { + // // context.push('/wellness'); + // // Wellness tab clicked → show popup + // if(!isRetailLoggedIn) + // PopupHelper.showRedirectPopup( + // context: context, + // apiService: apiService, + // empPrimaryId: session.empPrimaryId, + // ); + // } }, icons: [ Icons.home_outlined, Icons.sticky_note_2_outlined, Icons.person_outline_outlined, Icons.headset_mic_outlined, - Icons.health_and_safety_outlined, + // Icons.health_and_safety_outlined, ], - labels: [ - "Home", - "Claims", - "Profile", - "Help", - "Wellness", - ], - initialIndex: 4, // Initial index of the bottom navigation bar + labels: ["Home", "Claims", "Profile", "Help"], + initialIndex: 0, // Initial index of the bottom navigation bar ), ) ); diff --git a/lib/pages/service/BlinkingCard.dart b/lib/pages/service/BlinkingCard.dart new file mode 100644 index 0000000..f67abaa --- /dev/null +++ b/lib/pages/service/BlinkingCard.dart @@ -0,0 +1,109 @@ +import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; + +import '../../customAppBar/responsive.dart'; +import '../postEnrollment/service/api_service.dart'; +import 'SessionManager.dart'; +import 'common_update_mobile_dialog.dart'; +import 'data_manager.dart'; + +class BlinkingCard extends StatefulWidget { + final VoidCallback onUpdated; // 👈 add this + + const BlinkingCard({super.key, required this.onUpdated}); + + @override + State createState() => _BlinkingCardState(); +} + +class _BlinkingCardState extends State + with SingleTickerProviderStateMixin { + late AnimationController _controller; + late Animation _colorTween; + late ApiService apiService; + final dataManager = DataManager(); + final session = SessionManager(); + + @override + void initState() { + super.initState(); + apiService = ApiService(context); + _controller = AnimationController( + duration: const Duration(seconds: 1), + vsync: this, + )..repeat(reverse: true); // 🔥 blinking loop + + _colorTween = ColorTween( + begin: const Color(0xFFFFE5E5), // Light red + end: const Color(0xFFFFC1C1), // slightly darker for blink effect + ).animate(_controller); + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return AnimatedBuilder( + animation: _colorTween, + builder: (context, child) { + return Card( + elevation: 3, + shadowColor: Colors.red.shade50, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + ), + child: Container( + decoration: BoxDecoration( + color: _colorTween.value, // 🔥 blinking background color + borderRadius: BorderRadius.circular(10), + ), + padding: const EdgeInsets.all(12), + child: Row( + children: [ + Expanded( + flex: 7, + child: Text( + "Update Mobile Number", + style: GoogleFonts.poppins( + fontSize: Responsive.isDesktop(context) ? 18 : 12, + fontWeight: FontWeight.w500, + color: const Color(0xFF404040), + ), + ), + ), + Expanded( + flex: 5, + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + ElevatedButton( + onPressed: true ? () async { + final selfDetails = dataManager.selfProfile; + final updatedNumber = await showUpdateMobileDialog(context, apiService, session.client_id!, selfDetails?['email_corporate'], selfDetails?['mobile']); + if (updatedNumber != null && updatedNumber.isNotEmpty) { + // setState(() => selfMobile = updatedNumber); + dataManager.loadSelfEmployeeProfile( + clientId: session.client_id!, + empCode: session.empCodeString!, + clientBranchId: session.empClientBranchId!, + ); + widget.onUpdated(); + } + } : null, + child: const Text("Update"), + ), + ], + ), + ) + ], + ), + ), + ); + }, + ); + } +} diff --git a/lib/pages/service/TokenService.dart b/lib/pages/service/TokenService.dart index 89f4e68..af01314 100644 --- a/lib/pages/service/TokenService.dart +++ b/lib/pages/service/TokenService.dart @@ -8,8 +8,8 @@ import 'package:go_router/go_router.dart'; import 'package:http/http.dart' as http; import 'package:shared_preferences/shared_preferences.dart'; +import '../../config/environment.dart'; import '../../customAppBar/toastHelper.dart'; -import '../../models/environment.dart'; import '../../models/platform_helper_mobile.dart'; import 'SessionManager.dart'; @@ -96,6 +96,7 @@ class TokenService { body: json.encode(params), headers: { HttpHeaders.contentTypeHeader: 'application/json', + 'APP-SIGNATURE': 'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y', }, ); diff --git a/lib/pages/service/common_update_mobile_dialog.dart b/lib/pages/service/common_update_mobile_dialog.dart index 1e7eb74..6d6b621 100644 --- a/lib/pages/service/common_update_mobile_dialog.dart +++ b/lib/pages/service/common_update_mobile_dialog.dart @@ -5,7 +5,6 @@ import 'package:google_fonts/google_fonts.dart'; import 'package:http/http.dart' as http; import '../../customAppBar/toastHelper.dart'; -import '../../models/environment.dart'; import '../postEnrollment/service/api_service.dart'; import 'package:flutter/material.dart'; diff --git a/lib/pages/service/data_manager.dart b/lib/pages/service/data_manager.dart index 8a96432..c3597a7 100644 --- a/lib/pages/service/data_manager.dart +++ b/lib/pages/service/data_manager.dart @@ -47,7 +47,7 @@ class DataManager extends ChangeNotifier { Future loadAdvertisementImages() async { if (_adsLoaded) return; - final response = await _apiService.getAdvertisementImageToApi(); + final response = await _apiService.getAdvertisementImageToApi(''); if (response['status'] == 'success') { _advertisementImages = await List.from(response['data']); _adsLoaded = true; @@ -72,7 +72,7 @@ class DataManager extends ChangeNotifier { empCode, status, clientBranchId!, - mobileNo!, + mobileNo ?? '','' ); if (response['status'] == 'success') { @@ -94,7 +94,7 @@ class DataManager extends ChangeNotifier { required String clientBranchId, bool forceRefresh = false, }) async { - if (_selfProfileLoaded && !forceRefresh) return; + // if (_selfProfileLoaded && !forceRefresh) return; final response = await _apiService.getSelfEmployeeProfileDetails( clientId, diff --git a/lib/pages/service/popup_widget.dart b/lib/pages/service/popup_widget.dart new file mode 100644 index 0000000..86aa245 --- /dev/null +++ b/lib/pages/service/popup_widget.dart @@ -0,0 +1,275 @@ +import 'dart:ui'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:go_router/go_router.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:nhance_app_pwa/customAppBar/responsive.dart'; +import 'package:nhance_app_pwa/pages/postEnrollment/service/svg_service.dart'; +import 'package:flutter/foundation.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +void showBlurPopup(BuildContext context, emp_name) { + showDialog( + context: context, + barrierColor: Colors.black.withOpacity(0.6), + barrierDismissible: true, + builder: (context) { + return BlurPopup( + empName: emp_name, + isWeb: kIsWeb, // WEB OR MOBILE + ); + }, + ); +} + +class BlurPopup extends StatelessWidget { + final String empName; + final bool isWeb; + + const BlurPopup({super.key, required this.empName,required this.isWeb,}); + + @override + Widget build(BuildContext context) { + return Stack( + children: [ + // Blurred background + BackdropFilter( + filter: ImageFilter.blur(sigmaX: 6, sigmaY: 6), + child: Container(color: Colors.black.withOpacity(0.3)), + ), + + Align( + alignment: isWeb ? Alignment.center : Alignment.bottomCenter, + child: Material( + // <-- FIX: Add Material widget + color: Colors.transparent, + child: Container( + width: isWeb ? 600 : double.infinity, + margin: isWeb ? const EdgeInsets.symmetric(horizontal: 20) : null, + padding: EdgeInsets.all(20), + decoration: BoxDecoration( + color: Color(0xFF55babe), + borderRadius: isWeb ? BorderRadius.all(Radius.circular(20)) : BorderRadius.vertical(top: Radius.circular(25)), + ), + child: SingleChildScrollView( + child: buildPopupContent(context,empName), + ), + ), + ), + ), + ], + ); + } +} + +Widget buildPopupContent(BuildContext context, String empName) { + return Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // if(Responsive.isDesktop(context)) + // Align( + // alignment: Alignment.topRight, + // child: InkWell( + // onTap: () => Navigator.pop(context), + // child: const Icon(Icons.close, + // size: 26, color: Colors.black87), + // ), + // ), + // if(Responsive.isDesktop(context)) + // const SizedBox(height: 10), + + // Center( + // child: Container( + // height: 90, + // width: 90, + // decoration: const BoxDecoration( + // shape: BoxShape.circle, + // color: Color(0xFFF8E6E8), + // ), + // padding: const EdgeInsets.all(18), // adjust svg size + // child: SvgPicture.string(SvgService.initialPopup) + // ), + // ), + + + + // Center( + // child: Container( + // height: 90, + // width: 90, + // decoration: const BoxDecoration( + // shape: BoxShape.circle, + // color: Color(0xFFF8E6E8), + // ), + // child: const Icon(Icons.rocket_launch_outlined, + // color: Colors.red, size: 45), + // ), + // ), + // + // const SizedBox(height: 15), + + Center( + child: Text( + "🚀 New Policy Alert!", + style: GoogleFonts.poppins( + fontSize: 20, + fontWeight: FontWeight.bold, + ), + ), + ), + + const SizedBox(height: 8), + + Center( + child: Text( + "Hello $empName,", + textAlign: TextAlign.center, + style: GoogleFonts.poppins( + fontSize: 18, + color: Colors.black87, + fontWeight: FontWeight.w600, + ), + ), + ), + + const SizedBox(height: 10), + + Text( + "We've confirmed your enrollment in your new health insurance policy.", + style: GoogleFonts.poppins( + fontSize: 15, color: Colors.black87), + ), + + const SizedBox(height: 8), + + Text( + "Your next crucial step is to ensure your coverage is accurate! " + "Please take a moment now to verify and update the following information:", + style: GoogleFonts.poppins( + fontSize: 15, color: Colors.black87), + ), + + const SizedBox(height: 10), + + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Icon(Icons.check_circle, + color: Color(0xFFE26728), size: 25), + SizedBox(width: 10), + Expanded( + child: Text( + "Your Personal Profile: Confirm your contact details and basic information are up-to-date.", + style: GoogleFonts.poppins(fontSize: 15), + ), + ), + ], + ), + + const SizedBox(height: 10), + + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Icon(Icons.groups, + color: Color(0xFFE26728), size: 25), + SizedBox(width: 10), + Expanded( + child: Text( + "Family Composition: Check and update the list of covered dependents on your policy.", + style: GoogleFonts.poppins(fontSize: 15), + ), + ), + ], + ), + + const SizedBox(height: 10), + + Text( + "Why update now? Accurate details ensure fast claim processing and correct coverage for your entire family.", + style: GoogleFonts.poppins( + fontSize: 15, color: Colors.black87), + ), + + const SizedBox(height: 15), + + // Primary Button — Update Details + SizedBox( + width: double.infinity, + height: 40, + child: DecoratedBox( + decoration: BoxDecoration( + color: Color(0xFFE26728), + borderRadius: BorderRadius.circular(10), + ), + child: ElevatedButton( + onPressed: () async { + final prefs = await SharedPreferences.getInstance(); + await prefs.setBool('cancel_flag', true); + context.go('/empDetails'); + }, + style: ElevatedButton.styleFrom( + backgroundColor: Colors.transparent, + shadowColor: Colors.transparent, + ), + child: Text( + "Update My Details Now", + style: GoogleFonts.poppins( + color: Colors.white, + fontSize: 15, + fontWeight: FontWeight.w500, + ), + ), + ), + ), + ), + + const SizedBox(height: 15), + + Center( + child: Text( + "You can complete this later in the Profile section, but we highly recommend doing it now.", + textAlign: TextAlign.center, + style: GoogleFonts.poppins( + fontSize: 14, + color: Colors.black87, + fontWeight: FontWeight.w400, + ), + ), + ), + + const SizedBox(height: 15), + + // Cancel Button + SizedBox( + width: double.infinity, + height: 40, + child: OutlinedButton( + onPressed: () async { + final prefs = await SharedPreferences.getInstance(); + await prefs.setBool('cancel_flag', true); // Save the key + + Navigator.pop(context); + }, + style: OutlinedButton.styleFrom( + side: const BorderSide(color: Colors.black54), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + ), + ), + child: Text( + "Cancel And Go Back", + style: GoogleFonts.poppins( + color: Colors.black87, + fontSize: 15, + fontWeight: FontWeight.w500, + ), + ), + ), + ), + + const SizedBox(height: 10), + ], + ); +} diff --git a/lib/pages/service/ribbon.dart b/lib/pages/service/ribbon.dart new file mode 100644 index 0000000..dbeebba --- /dev/null +++ b/lib/pages/service/ribbon.dart @@ -0,0 +1,318 @@ +library ribbon; + +import 'package:flutter/material.dart'; +import 'dart:math' as math; + +const TextStyle _kTextStyle = TextStyle( + color: Color(0xFFFFFFFF), + fontSize: 12, + fontWeight: FontWeight.w900, + height: 1.0); + +enum RibbonLocation { + topStart, + topEnd, + bottomStart, + bottomEnd, +} + +class Ribbon extends StatelessWidget { + final double nearLength; + final double farLength; + final String title; + final Color color; + final TextStyle titleStyle; + final RibbonLocation location; + final Widget child; + + const Ribbon( + {Key? key, + required this.nearLength, + required this.farLength, + required this.title, + this.titleStyle = _kTextStyle, + this.color = Colors.white, + this.location = RibbonLocation.topStart, + required this.child}) + : super(key: key); + + @override + Widget build(BuildContext context) { + return CustomPaint( + foregroundPainter: _RibbonPainter( + nearLength: nearLength, + farLength: farLength, + title: title, + titleStyle: titleStyle, + color: color, + location: location, + ), + child: child); + } +} + +class _RibbonPainter extends CustomPainter { + double nearLength; + double farLength; + final String title; + final Color color; + final TextStyle titleStyle; + final RibbonLocation location; + bool initialized = false; + late TextPainter textPainter; + late Paint paintRibbon; + late Path pathRibbon; + late double rotateRibbon; + late Offset offsetRibbon; + late Offset offsetTitle; + late Paint paintShadow; + + static const BoxShadow _shadow = BoxShadow( + color: Color(0x7F000000), + blurRadius: 6.0, + ); + + _RibbonPainter( + {required this.nearLength, + required this.farLength, + required this.title, + required this.titleStyle, + required this.color, + required this.location}); + + @override + void paint(Canvas canvas, Size size) { + if (!initialized) _initializ(size); + // canvas.drawPath(pathRibbon, paintShadow); + canvas + ..drawShadow(pathRibbon, const Color(0x7F000000), 2.0, true) + ..drawPath(pathRibbon, paintRibbon) + // canvas + ..translate(offsetRibbon.dx, offsetRibbon.dy) + ..rotate(rotateRibbon); + // ..clipPath(pathRibbon); + textPainter.paint(canvas, offsetTitle); + } + + @override + bool shouldRepaint(_RibbonPainter oldDelegate) { + return title != oldDelegate.title || + nearLength != oldDelegate.nearLength || + farLength != oldDelegate.farLength || + color != oldDelegate.color || + location != oldDelegate.location; + } + + void _initializ(Size size) { + initialized = true; + if (nearLength > farLength) { + double temp = farLength; + farLength = nearLength; + nearLength = temp; + } + if (farLength > size.width) farLength = size.width; + TextSpan span = TextSpan(style: titleStyle, text: title); + textPainter = TextPainter( + text: span, + textAlign: TextAlign.center, + textDirection: TextDirection.ltr); + textPainter.layout(); + paintRibbon = Paint() + ..color = color + ..style = PaintingStyle.fill; + offsetTitle = Offset(-textPainter.width / 2, -textPainter.height / 2); + rotateRibbon = _rotation; + pathRibbon = _ribbonPath(size); + paintShadow = _shadow.toPaint(); + } + + Path _ribbonPath(Size size) { + Path path = Path(); + List vec = []; + if (size.width <= size.height) { + switch (location) { + case RibbonLocation.topStart: + path.moveTo(nearLength, 0); + vec.add(Offset(nearLength, 0)); + path.lineTo(farLength, 0); + vec.add(Offset(farLength, 0)); + path.lineTo(0, farLength); + vec.add(Offset(0, farLength)); + path.lineTo(0, nearLength); + vec.add(Offset(0, nearLength)); + break; + case RibbonLocation.topEnd: + path.moveTo(size.width - nearLength, 0); + vec.add(Offset(size.width - nearLength, 0)); + path.lineTo(size.width - farLength, 0); + vec.add(Offset(size.width - farLength, 0)); + path.lineTo(size.width, farLength); + vec.add(Offset(size.width, farLength)); + path.lineTo(size.width, nearLength); + vec.add(Offset(size.width, nearLength)); + break; + case RibbonLocation.bottomStart: + path.moveTo(0, size.height - nearLength); + vec.add(Offset(0, size.height - nearLength)); + path.lineTo(0, size.height - farLength); + vec.add(Offset(0, size.height - farLength)); + path.lineTo(farLength, size.height); + vec.add(Offset(farLength, size.height)); + path.lineTo(nearLength, size.height); + vec.add(Offset(nearLength, size.height)); + break; + case RibbonLocation.bottomEnd: + path.moveTo(size.width - nearLength, size.height); + vec.add(Offset(size.width - nearLength, size.height)); + path.lineTo(size.width - farLength, size.height); + vec.add(Offset(size.width - farLength, size.height)); + path.lineTo(size.width, size.height - farLength); + vec.add(Offset(size.width, size.height - farLength)); + path.lineTo(size.width, size.height - nearLength); + vec.add(Offset(size.width, size.height - nearLength)); + break; + } + } else { + switch (location) { + case RibbonLocation.topStart: + path.moveTo(nearLength, 0); + vec.add(Offset(nearLength, 0)); + path.lineTo(farLength, 0); + vec.add(Offset(farLength, 0)); + if (farLength <= size.height) { + path.lineTo(0, farLength); + vec.add(Offset(0, farLength)); + path.lineTo(0, nearLength); + vec.add(Offset(0, nearLength)); + } else { + path.lineTo(farLength - size.height, size.height); + vec.add(Offset(farLength - size.height, size.height)); + if (nearLength <= size.height) { + path.lineTo(0, size.height); + vec.add(Offset(0, size.height)); + path.lineTo(0, nearLength); + vec.add(Offset(0, nearLength)); + } else { + path.lineTo(nearLength - size.height, size.height); + vec.add(Offset(nearLength - size.height, size.height)); + } + } + break; + case RibbonLocation.topEnd: + path.moveTo(size.width - nearLength, 0); + vec.add(Offset(size.width - nearLength, 0)); + path.lineTo(size.width - farLength, 0); + vec.add(Offset(size.width - farLength, 0)); + if (farLength <= size.height) { + path.lineTo(size.width, farLength); + vec.add(Offset(size.width, farLength)); + path.lineTo(size.width, nearLength); + vec.add(Offset(size.width, nearLength)); + } else { + path.lineTo(size.width - (farLength - size.height), size.height); + vec.add( + Offset(size.width - (farLength - size.height), size.height)); + if (nearLength <= size.height) { + path.lineTo(size.width, size.height); + vec.add(Offset(size.width, size.height)); + path.lineTo(size.width, nearLength); + vec.add(Offset(size.width, nearLength)); + } else { + path.lineTo(size.width - (nearLength - size.height), size.height); + vec.add( + Offset(size.width - (nearLength - size.height), size.height)); + } + } + break; + case RibbonLocation.bottomStart: + path.moveTo(nearLength, size.height); + vec.add(Offset(nearLength, size.height)); + path.lineTo(farLength, size.height); + vec.add(Offset(farLength, size.height)); + if (farLength <= size.height) { + path.lineTo(0, size.height - farLength); + vec.add(Offset(0, size.height - farLength)); + path.lineTo(0, size.height - nearLength); + vec.add(Offset(0, size.height - nearLength)); + } else { + path.lineTo(farLength - size.height, 0); + vec.add(Offset(farLength - size.height, 0)); + if (nearLength <= size.height) { + path.lineTo(0, 0); + vec.add(const Offset(0, 0)); + path.lineTo(0, size.height - nearLength); + vec.add(Offset(0, size.height - nearLength)); + } else { + path.lineTo(nearLength - size.height, 0); + vec.add(Offset(nearLength - size.height, 0)); + } + } + break; + case RibbonLocation.bottomEnd: + path.moveTo(size.width - nearLength, size.height); + vec.add(Offset(size.width - nearLength, size.height)); + path.lineTo(size.width - farLength, size.height); + vec.add(Offset(size.width - farLength, size.height)); + if (farLength <= size.height) { + path.lineTo(size.width, size.height - farLength); + vec.add(Offset(size.width, size.height - farLength)); + path.lineTo(size.width, size.height - nearLength); + vec.add(Offset(size.width, size.height - nearLength)); + } else { + path.lineTo(size.width - (farLength - size.height), 0); + vec.add(Offset(size.width - (farLength - size.height), 0)); + if (nearLength <= size.height) { + path.lineTo(size.width, 0); + vec.add(Offset(size.width, 0)); + path.lineTo(size.width, size.height - nearLength); + vec.add(Offset(size.width, size.height - nearLength)); + } else { + path.lineTo(size.width - (nearLength - size.height), 0); + vec.add(Offset(size.width - (nearLength - size.height), 0)); + } + } + break; + } + } + path.close(); + List vec2 = vec.toSet().toList(); + offsetRibbon = _center(vec2); + // print('cx = ${offsetRibbon.dx},cy = ${offsetRibbon.dy}'); + return path; + } + + double get _rotation { + switch (location) { + case RibbonLocation.topStart: + return -math.pi / 4; + case RibbonLocation.topEnd: + return math.pi / 4; + case RibbonLocation.bottomStart: + return math.pi / 4; + case RibbonLocation.bottomEnd: + return -math.pi / 4; + } + } + + Offset _center(List vecs) { + double sumX = 0, sumY = 0, sumS = 0; + double x1 = vecs[0].dx; + double y1 = vecs[0].dy; + double x2 = vecs[1].dx; + double y2 = vecs[1].dy; + double x3, y3; + for (int i = 2; i < vecs.length; i++) { + x3 = vecs[i].dx; + y3 = vecs[i].dy; + double s = ((x2 - x1) * (y3 - y1) - (x3 - x1) * (y2 - y1)) / 2.0; + sumX += (x1 + x2 + x3) * s; + sumY += (y1 + y2 + y3) * s; + sumS += s; + x2 = x3; + y2 = y3; + } + double cx = sumX / sumS / 3.0; + double cy = sumY / sumS / 3.0; + return Offset(cx, cy); + } +} \ No newline at end of file diff --git a/lib/pages/session/SetPinBiometric.dart b/lib/pages/session/SetPinBiometric.dart index 298ad80..f198e65 100755 --- a/lib/pages/session/SetPinBiometric.dart +++ b/lib/pages/session/SetPinBiometric.dart @@ -9,8 +9,8 @@ import 'package:jwt_decode/jwt_decode.dart'; import 'package:nhance_app_pwa/customAppBar/toastHelper.dart'; import 'package:pinput/pinput.dart'; import 'package:shared_preferences/shared_preferences.dart'; +import '../../config/environment.dart'; import '../../customAppBar/responsive.dart'; -import '../../models/environment.dart'; import '../postEnrollment/service/svg_service.dart'; import '../service/SessionManager.dart'; import '../service/TokenService.dart'; @@ -87,6 +87,7 @@ class _pinPageState extends State { body: json.encode(params), headers: { HttpHeaders.contentTypeHeader: 'application/json', + 'APP-SIGNATURE': 'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y', }, ); @@ -171,6 +172,7 @@ class _pinPageState extends State { body: json.encode(params), headers: { HttpHeaders.contentTypeHeader: 'application/json', + 'APP-SIGNATURE': 'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y', }, ); @@ -268,7 +270,7 @@ class _pinPageState extends State { print(_postToken); if (_postToken != null && _postToken.isNotEmpty) { - ToastHelper.showSuccessToast(context, 'Successfully Login'); + ToastHelper.showSuccessToast(context, 'Successfully Logged In'); // if (enrollmentEmp_status == 'enrolled' || // enrollmentEmp_status == 'active') { print('Token12345: ${await TokenService.getPostToken()}'); @@ -310,7 +312,7 @@ class _pinPageState extends State { // Redirect to another page final token = await TokenService.getPreToken(); if (token != null && token.isNotEmpty) { - ToastHelper.showSuccessToast(context, 'Successfully Login'); + ToastHelper.showSuccessToast(context, 'Successfully Logged In'); // if (enrollmentEmp_status == 'enrolled' || // enrollmentEmp_status == 'active') { // Navigator.pushReplacementNamed(context, 'home'); @@ -366,6 +368,7 @@ class _pinPageState extends State { body: json.encode(params), headers: { HttpHeaders.contentTypeHeader: 'application/json', + 'APP-SIGNATURE': 'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y', }, ); @@ -410,6 +413,7 @@ class _pinPageState extends State { headers: { 'Authorization': 'Bearer $_token', // Add token to the Authorization header + 'APP-SIGNATURE': 'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y', }, ); if (response.statusCode == 200) { @@ -789,43 +793,46 @@ class _pinPageState extends State { )), Align( alignment: Alignment.bottomCenter, - child: Container( - width: double.infinity, // Make the footer full width - child: RichText( - textAlign: TextAlign.center, - text: TextSpan( - text: 'By continuing, you agree with our ', - style: GoogleFonts.poppins( - color: Colors.black, - fontSize: 9, + child: SafeArea( + child: Container( + padding: EdgeInsets.only(bottom: 8), + width: double.infinity, + child: RichText( + textAlign: TextAlign.center, + text: TextSpan( + text: 'By continuing, you agree with our ', + style: GoogleFonts.poppins( + color: Colors.black, + fontSize: 9, + ), + children: [ + TextSpan( + text: 'privacy policy ', + style: GoogleFonts.poppins( + color: Color(0xFF00989E), + fontSize: 9, + ), + ), + TextSpan( + text: 'and ', + style: GoogleFonts.poppins( + color: Colors.black, + fontSize: 9, + ), + ), + TextSpan( + text: 'terms of use', + style: GoogleFonts.poppins( + color: Color(0xFF00989E), + fontSize: 9, + ), + ), + ], ), - children: [ - TextSpan( - text: 'privacy policy ', - style: GoogleFonts.poppins( - color: Color(0xFF00989E), - fontSize: 9, - ), - ), - TextSpan( - text: 'and ', - style: GoogleFonts.poppins( - color: Colors.black, - fontSize: 9, - ), - ), - TextSpan( - text: 'terms of use', - style: GoogleFonts.poppins( - color: Color(0xFF00989E), - fontSize: 9, - ), - ), - ], ), ), ), - ), + ) ]), )))); } diff --git a/lib/pages/session/changePin.dart b/lib/pages/session/changePin.dart index 5274847..bfeeabb 100755 --- a/lib/pages/session/changePin.dart +++ b/lib/pages/session/changePin.dart @@ -8,9 +8,9 @@ import 'package:flutter/material.dart'; import 'package:nhance_app_pwa/pages/service/TokenService.dart'; import 'package:pinput/pinput.dart'; import 'package:shared_preferences/shared_preferences.dart'; +import '../../config/environment.dart'; import '../../customAppBar/responsive.dart'; import '../../customAppBar/toastHelper.dart'; -import '../../models/environment.dart'; import '../postEnrollment/service/svg_service.dart'; import 'authenticationService.dart'; @@ -78,6 +78,7 @@ class _changePinState extends State { headers: { HttpHeaders.contentTypeHeader: 'application/json', 'Authorization': 'Bearer $_token', + 'APP-SIGNATURE': 'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y', }, ); print('response $response'); @@ -123,7 +124,7 @@ class _changePinState extends State { return 'PIN must be 4 digits'; } if (value == _oldPinController.text) { - return 'Pin not should be same'; + return 'Please enter a different PIN.'; } return null; } diff --git a/lib/pages/session/settingUpPinAndBiometric.dart b/lib/pages/session/settingUpPinAndBiometric.dart index 5876596..02dc825 100755 --- a/lib/pages/session/settingUpPinAndBiometric.dart +++ b/lib/pages/session/settingUpPinAndBiometric.dart @@ -8,9 +8,9 @@ import 'package:google_fonts/google_fonts.dart'; import 'package:nhance_app_pwa/pages/service/TokenService.dart'; import 'package:pinput/pinput.dart'; import 'package:shared_preferences/shared_preferences.dart'; +import '../../config/environment.dart'; import '../../customAppBar/responsive.dart'; import '../../customAppBar/toastHelper.dart'; -import '../../models/environment.dart'; import '../postEnrollment/service/svg_service.dart'; import '../service/SessionManager.dart'; import 'authenticationService.dart'; @@ -117,6 +117,7 @@ class _pinSettingPageState extends State { headers: { HttpHeaders.contentTypeHeader: 'application/json', 'Authorization': 'Bearer $token', + 'APP-SIGNATURE': 'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y', }, ); @@ -717,43 +718,46 @@ class _pinSettingPageState extends State { )), Align( alignment: Alignment.bottomCenter, - child: Container( - width: double.infinity, // Make the footer full width - child: RichText( - textAlign: TextAlign.center, - text: TextSpan( - text: 'By continuing, you agree with our ', - style: GoogleFonts.poppins( - color: Colors.black, - fontSize: 9, + child: SafeArea( + child: Container( + padding: EdgeInsets.only(bottom: 8), + width: double.infinity, + child: RichText( + textAlign: TextAlign.center, + text: TextSpan( + text: 'By continuing, you agree with our ', + style: GoogleFonts.poppins( + color: Colors.black, + fontSize: 9, + ), + children: [ + TextSpan( + text: 'privacy policy ', + style: GoogleFonts.poppins( + color: Color(0xFF00989E), + fontSize: 9, + ), + ), + TextSpan( + text: 'and ', + style: GoogleFonts.poppins( + color: Colors.black, + fontSize: 9, + ), + ), + TextSpan( + text: 'terms of use', + style: GoogleFonts.poppins( + color: Color(0xFF00989E), + fontSize: 9, + ), + ), + ], ), - children: [ - TextSpan( - text: 'privacy policy ', - style: GoogleFonts.poppins( - color: Color(0xFF00989E), - fontSize: 9, - ), - ), - TextSpan( - text: 'and ', - style: GoogleFonts.poppins( - color: Colors.black, - fontSize: 9, - ), - ), - TextSpan( - text: 'terms of use', - style: GoogleFonts.poppins( - color: Color(0xFF00989E), - fontSize: 9, - ), - ), - ], ), ), ), - ), + ) ]), )))); } diff --git a/lib/pages/setPassword.dart b/lib/pages/setPassword.dart index 9709cd5..0692b18 100644 --- a/lib/pages/setPassword.dart +++ b/lib/pages/setPassword.dart @@ -10,9 +10,9 @@ import 'package:nhance_app_pwa/pages/service/SessionManager.dart'; import 'dart:io'; import 'package:google_fonts/google_fonts.dart'; import 'package:shared_preferences/shared_preferences.dart'; +import '../config/environment.dart'; import '../customAppBar/responsive.dart'; import '../customAppBar/toastHelper.dart'; -import '../models/environment.dart'; class setPassword extends StatefulWidget { final String email; @@ -89,6 +89,7 @@ class _setPasswordState extends State { body: json.encode(payload), headers: { HttpHeaders.contentTypeHeader: 'application/json', + 'APP-SIGNATURE': 'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y', }, ); diff --git a/pubspec.yaml b/pubspec.yaml index b11c3fc..6b209c7 100755 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -17,8 +17,8 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # In Windows, build-name is used as the major, minor, and patch parts # of the product and file versions while build-number is used as the build suffix. #version: 1.0.32+32 -version: 1.0.22+25 -#version: 2.0.6+44 +version: 1.0.23+26 +#version: 2.0.7+45 environment: sdk: '>=3.3.3 <4.0.0' @@ -62,12 +62,16 @@ dependencies: webview_flutter: ^4.13.0 webview_flutter_android: ^4.7.0 flutter_inappwebview: ^6.1.5 - file_picker: ^10.3.2 + file_picker: ^10.3.7 file_selector: ^1.0.3 universal_html: ^2.2.4 go_router: ^16.2.2 flutter_secure_storage: ^9.2.4 printing: ^5.14.2 + flutter_animate: ^4.5.2 + carousel_slider: ^5.1.1 + dropdown_search: ^6.0.2 + ribbon_widget: ^1.0.5 dev_dependencies: flutter_test: @@ -94,11 +98,6 @@ flutter: # To add assets to your application, add an assets section, like this: assets: - assets/ - - .env - - .env.development - - .env.production - - .env.test - - .env.uat - assets/botman_chat.html # - images/a_dot_burr.jpeg # - images/a_dot_ham.jpeg diff --git a/web/index.html b/web/index.html index a5f832d..3efcb55 100755 --- a/web/index.html +++ b/web/index.html @@ -6,18 +6,18 @@ - + - + - Nhance + Nhance Benefits