diff --git a/assets/botman_chat.html b/assets/botman_chat.html deleted file mode 100755 index d9cce43..0000000 --- a/assets/botman_chat.html +++ /dev/null @@ -1,151 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Ask ILA - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/assets/faqs.jpg b/assets/faqs.jpg new file mode 100644 index 0000000..97dbdb3 Binary files /dev/null and b/assets/faqs.jpg differ diff --git a/lib/customAppBar/customAppBar.dart b/lib/customAppBar/customAppBar.dart index 9a0a308..f37029a 100755 --- a/lib/customAppBar/customAppBar.dart +++ b/lib/customAppBar/customAppBar.dart @@ -117,12 +117,25 @@ class _CustomAppBarState extends State { context.push('/claims'); }), ), + NavBarItem( + text: "FAQs", + onTap: () => handleMenuTap(() { + context.push('/faqs'); + }), + ), + NavBarItem( + text: "Profile", + onTap: () => handleMenuTap(() { + context.push('/profile'); + }), + ), NavBarItem( text: "Help", onTap: () => handleMenuTap(() { context.push('/help'); }), ), + // NavBarItem( // text: "Wellness", // onTap: () => handleMenuTap(() { @@ -133,12 +146,6 @@ class _CustomAppBarState extends State { // ); // }), // ), - NavBarItem( - text: "Profile", - onTap: () => handleMenuTap(() { - context.push('/profile'); - }), - ), NavBarItem( text: "Logout", onTap: () { diff --git a/lib/main.dart b/lib/main.dart index d1325bd..ca49a9f 100755 --- a/lib/main.dart +++ b/lib/main.dart @@ -14,6 +14,7 @@ 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'; +import 'package:nhance_app_pwa/pages/postEnrollment/faqs.dart'; import 'package:nhance_app_pwa/pages/postEnrollment/generalexclusionsdeductibles.dart'; import 'package:nhance_app_pwa/pages/postEnrollment/help.dart'; import 'package:nhance_app_pwa/pages/postEnrollment/home.dart'; @@ -230,6 +231,10 @@ Future startApp() async { path: '/empDetails', builder: (context, state) => empDetails(), ), + GoRoute( + path: '/faqs', + builder: (context, state) => faqs(), + ), GoRoute( path: '/addOnsDetails', builder: (context, state) => addOnsDetails()), GoRoute( diff --git a/lib/pages/login.dart b/lib/pages/login.dart index d3c3671..75435f0 100755 --- a/lib/pages/login.dart +++ b/lib/pages/login.dart @@ -1502,15 +1502,15 @@ class _loginState extends State { value.isEmpty) { return 'Please enter your password'; } - if (value.length < 6) { - return 'Password must be at least 6 characters'; - } - // Optional strong password rule - if (!RegExp( - r'^(?=.*[A-Z])(?=.*[0-9]).{6,}$') - .hasMatch(value)) { - return 'Include at least 1 uppercase letter and 1 number'; - } + // if (value.length < 6) { + // return 'Password must be at least 6 characters'; + // } + // // Optional strong password rule + // if (!RegExp( + // r'^(?=.*[A-Z])(?=.*[0-9]).{6,}$') + // .hasMatch(value)) { + // return 'Include at least 1 uppercase letter and 1 number'; + // } return null; }, ), diff --git a/lib/pages/postEnrollment/AddPolicyScreen.dart b/lib/pages/postEnrollment/AddPolicyScreen.dart index 2c5a4a4..e5569b1 100644 --- a/lib/pages/postEnrollment/AddPolicyScreen.dart +++ b/lib/pages/postEnrollment/AddPolicyScreen.dart @@ -367,17 +367,19 @@ class _AddPolicyScreenState extends State { icons: [ Icons.home_outlined, Icons.sticky_note_2_outlined, + Icons.question_answer_outlined, Icons.person_outline_outlined, Icons.headset_mic_outlined, - Icons.health_and_safety_outlined, ], - labels: ["Home", "Claims", "Profile", "Help", "Wellness"], - initialIndex: 3, + labels: ["Home", "Claims", "FAQs", "Profile", "Help"], + initialIndex: 0, 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'); + if (index == 2) context.go('/faqs'); + if (index == 3) context.go('/profile'); + if (index == 4) {context.push('/help'); + } }, ), ), diff --git a/lib/pages/postEnrollment/claimprocess.dart b/lib/pages/postEnrollment/claimprocess.dart index dbb5aba..8491ead 100755 --- a/lib/pages/postEnrollment/claimprocess.dart +++ b/lib/pages/postEnrollment/claimprocess.dart @@ -1,853 +1,660 @@ -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: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:shared_preferences/shared_preferences.dart'; -import 'package:http/http.dart' as http; + import 'dart:convert'; + import 'package:flutter/foundation.dart'; + import 'package:flutter/material.dart'; +import 'package:flutter_html/flutter_html.dart'; + import 'package:go_router/go_router.dart'; + import 'package:google_fonts/google_fonts.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:shared_preferences/shared_preferences.dart'; + import 'package:http/http.dart' as http; +import 'package:video_player/video_player.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'; + import '../../customAppBar/customFooter.dart'; + import '../../customAppBar/responsive.dart'; + import '../../customAppBar/tabs.dart'; + import '../../customAppBar/toastHelper.dart'; + import '../service/SessionManager.dart'; + import '../service/TokenService.dart'; + import '../service/multi_video_player.dart'; import '../service/popup_helper.dart'; -class claimprocess extends StatefulWidget { - const claimprocess({Key? key}) : super(key: key); + class claimprocess extends StatefulWidget { + const claimprocess({Key? key}) : super(key: key); - @override - State createState() => _claimprocessState(); -} - -class _claimprocessState extends State { - late ApiService apiService; - bool isActive = true; - dynamic _token; - dynamic empCodeString; - dynamic empPrimaryId; - dynamic client_id; - dynamic cashLessClaimsDetails; - dynamic reimbursementClaimsDetails; - dynamic cashLessSectionName; - dynamic cashLessHeading; - late Map cashLessContent; - late Map cashLessNotes; - - dynamic reimbursementSectionName; - dynamic reimbursementHeading; - late Map reimbursementContent; - late Map reimbursementNotes; - final session = SessionManager(); - - @override - void initState() { - super.initState(); - apiService = ApiService(context); // Initialize ApiService here - _loadToken(); + @override + State createState() => _claimprocessState(); } - @override - void dispose() { - super.dispose(); - } + class _claimprocessState extends State { + late ApiService apiService; + bool isLoading = false; + bool isActive = true; + dynamic _token; + dynamic empCodeString; + dynamic empPrimaryId; + dynamic client_id; + dynamic cashLessClaimsDetails; + dynamic reimbursementClaimsDetails; + dynamic cashLessSectionName; + dynamic cashLessHeading; + late Map cashLessContent; + late Map cashLessNotes; - Future _loadToken() async { - print('_loadToken'); - final String? token = await TokenService.getPostToken(); - if (token != null && token.isNotEmpty) { - // Decode the JWT token received from the API response - Map? decodedToken = Jwt.parseJwt(token); - print(decodedToken); - empCodeString = session.empCodeString; - print(empCodeString); // Check if emp_code is correct - empPrimaryId = session.empPrimaryId; - client_id = session.client_id; - print(client_id); - getCashlessAndReimbursement(); + dynamic reimbursementSectionName; + dynamic reimbursementHeading; + late Map reimbursementContent; + late Map reimbursementNotes; + final session = SessionManager(); + String? cashLessContentHtml; + String? cashLessNotesHtml; + + String? reimbursementContentHtml; + String? reimbursementNotesHtml; + + List multiVideoUrlsList = []; + + final PageController _videoPageController = PageController(); + int _currentVideoIndex = 0; + + final List _videoControllers = []; + + + @override + void initState() { + super.initState(); + apiService = ApiService(context); // Initialize ApiService here + _loadToken(); } - } - Future getCashlessAndReimbursement() async { - final response = await apiService.getCashlessAndReimbursementToApi(); - print('check 1 getCashlessAndReimbursement'); - if (response['status'] == 'success') { - print(response['data']); + @override + void dispose() { + for (final controller in _videoControllers) { + controller.dispose(); + } + _videoPageController.dispose(); + super.dispose(); + } + + + Future _loadToken() async { + print('_loadToken'); + final String? token = await TokenService.getPostToken(); + if (token != null && token.isNotEmpty) { + // Decode the JWT token received from the API response + Map? decodedToken = Jwt.parseJwt(token); + print(decodedToken); + empCodeString = session.empCodeString; + print(empCodeString); // Check if emp_code is correct + empPrimaryId = session.empPrimaryId; + client_id = session.client_id; + print(client_id); + getCashlessAndReimbursement(); + } + } + + Future getCashlessAndReimbursement() async { setState(() { - cashLessClaimsDetails = response['data'][0]; - print(cashLessClaimsDetails); - cashLessSectionName = cashLessClaimsDetails['content_section']; - cashLessHeading = cashLessClaimsDetails['heading']; - Map jsonData = - jsonDecode(cashLessClaimsDetails['content']); - cashLessContent = Map.from(jsonData); - print('cashLessContent'); - print(cashLessContent); - - Map notesJsonData = - jsonDecode(cashLessClaimsDetails['notes']); - cashLessNotes = Map.from(notesJsonData); - print('cashLessNotes'); - print(cashLessNotes); - - reimbursementClaimsDetails = response['data'][1]; - print(reimbursementClaimsDetails); - reimbursementSectionName = - reimbursementClaimsDetails['content_section']; - reimbursementHeading = reimbursementClaimsDetails['heading']; - Map reimbursementJsonData = - jsonDecode(reimbursementClaimsDetails['content']); - reimbursementContent = Map.from(reimbursementJsonData); - print('reimbursementContent'); - print(reimbursementContent); - - Map reimbursementNotesJsonData = - jsonDecode(reimbursementClaimsDetails['notes']); - reimbursementNotes = - Map.from(reimbursementNotesJsonData); - print('reimbursementNotes'); - print(reimbursementNotes); + isLoading = true; }); - } else { - print('API request failed with status: ${response['status']}'); - } - } + final response = await apiService.getCashlessAndReimbursementToApi(); + print('check 1 getCashlessAndReimbursement'); + if (response['status'] == 'success') { + print(response['data']); + setState(() { + cashLessClaimsDetails = response['data'][0]; + cashLessSectionName = cashLessClaimsDetails['content_section']; + cashLessHeading = cashLessClaimsDetails['heading']; - @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, - vertical: MediaQuery.of(context).size.height * 0.03, - ) - : EdgeInsets.all(10), - color: Colors.white, - child: Column( - children: [ - Container( - padding: Responsive.isDesktop(context) - ? EdgeInsets.symmetric(vertical: 15, horizontal: 25) - : EdgeInsets.all(0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Expanded( - flex: 12, - child: InkWell( - onTap: () { - context.go('/claims'); - }, - child: Row( - mainAxisAlignment: - MainAxisAlignment.start, - children: [ - // if (!Responsive.isDesktop(context)) - Icon( - Icons.chevron_left, - color: Color(0xFF000000), - size: 30, - ), - SizedBox( - width: Responsive.isDesktop(context) - ? 0 - : 5), - Column( - mainAxisAlignment: - MainAxisAlignment.center, + cashLessContentHtml = cashLessClaimsDetails['content']; + cashLessNotesHtml = cashLessClaimsDetails['notes']; + + reimbursementClaimsDetails = response['data'][1]; + reimbursementSectionName = reimbursementClaimsDetails['content_section']; + reimbursementHeading = reimbursementClaimsDetails['heading']; + + reimbursementContentHtml = reimbursementClaimsDetails['content']; + reimbursementNotesHtml = reimbursementClaimsDetails['notes']; + + final List dataList = response['data']; + multiVideoUrlsList.clear(); + for (final item in dataList) { + // ✅ FIND VIDEO SECTION + if (item['content_section'] == 'Video') { + final String? videoUrl = item['content']; + + // ✅ SAFETY CHECK + if (videoUrl != null && videoUrl.trim().isNotEmpty) { + multiVideoUrlsList.add(videoUrl.trim()); + } + } + } + + /// ✅ DIRECT VIDEO FILE PATHS (MP4) + // multiVideoUrlsList = [ + // "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4", + // "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ElephantsDream.mp4", + // "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/Sintel.mp4", + // ]; + + + isLoading = false; + }); + await _initVideoControllers(); + + + } else { + print('API request failed with status: ${response['status']}'); + } + } + + Future _initVideoControllers() async { + // ✅ Clear old controllers + for (final controller in _videoControllers) { + controller.dispose(); + } + _videoControllers.clear(); + + for (final url in multiVideoUrlsList) { + final controller = VideoPlayerController.networkUrl(Uri.parse(url)); + await controller.initialize(); + controller.setLooping(true); + controller.setVolume(1); + _videoControllers.add(controller); + } + + if (mounted) { + setState(() {}); + } + } + + + + @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, + vertical: MediaQuery.of(context).size.height * 0.03, + ) + : EdgeInsets.all(10), + color: Colors.white, + child: Column( + children: [ + Container( + padding: Responsive.isDesktop(context) + ? EdgeInsets.symmetric(vertical: 15, horizontal: 25) + : EdgeInsets.all(0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Expanded( + flex: 12, + child: InkWell( + onTap: () { + context.go('/claims'); + }, + child: Row( + mainAxisAlignment: + MainAxisAlignment.start, + children: [ + // if (!Responsive.isDesktop(context)) + Icon( + Icons.chevron_left, + color: Color(0xFF000000), + size: 30, + ), + SizedBox( + width: Responsive.isDesktop(context) + ? 0 + : 5), + Column( + mainAxisAlignment: + MainAxisAlignment.center, + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Text( + 'Claim Process', + textAlign: TextAlign.start, + style: GoogleFonts.poppins( + fontSize: 16, + fontWeight: FontWeight.w600, + color: Color(0xFF000000), + ), + ), + ], + ), + ], + ), + ), + ), + ], + ), + ], + ), + ), + SizedBox(height: 5), + if (multiVideoUrlsList.isNotEmpty) ...[ + NhanceMultiVideoWrapper( + videoUrls: multiVideoUrlsList, + ) + ], + SizedBox(height: 15), + Container( + decoration: BoxDecoration( + color: Color(0xFFF6FAFF), + borderRadius: BorderRadius.circular(5), + ), + padding: Responsive.isDesktop(context) + ? EdgeInsets.symmetric(vertical: 15, horizontal: 25) + : EdgeInsets.all(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: GestureDetector( + onTap: () { + setState(() { + isActive = true; + }); + }, + child: isActive + ? Material( + elevation: 5, + borderRadius: + BorderRadius.circular( + 10), + color: Colors.white, + child: TextButton( + onPressed: () {}, + style: + TextButton.styleFrom( + padding: EdgeInsets + .symmetric( + horizontal: Responsive + .isDesktop( + context) + ? 80 + : 40, + vertical: Responsive + .isDesktop( + context) + ? 12 + : 7, + ), + ), + child: Text( + cashLessSectionName ?? + '', + textAlign: + TextAlign.center, + style: + GoogleFonts.poppins( + fontSize: Responsive + .isDesktop( + context) + ? 18 + : 14, + fontWeight: + FontWeight.w500, + color: + Color(0xFF593AFF), + ), + ), + ), + ) + : Text( + cashLessSectionName ?? '', + textAlign: TextAlign.center, + style: GoogleFonts.poppins( + fontSize: + Responsive.isDesktop( + context) + ? 18 + : 14, + fontWeight: + FontWeight.w400, + color: Color(0xFF636363), + ), + ), + ), + ), + Expanded( + flex: 6, + child: GestureDetector( + onTap: () { + setState(() { + isActive = false; + }); + }, + child: !isActive + ? Material( + elevation: 5, + borderRadius: + BorderRadius.circular( + 10), + color: Colors.white, + child: TextButton( + onPressed: () {}, + style: + TextButton.styleFrom( + padding: EdgeInsets + .symmetric( + horizontal: Responsive + .isDesktop( + context) + ? 80 + : 20, + vertical: Responsive + .isDesktop( + context) + ? 12 + : 7, + ), + ), + child: Text( + reimbursementSectionName ?? + '', + textAlign: + TextAlign.center, + style: + GoogleFonts.poppins( + fontSize: Responsive + .isDesktop( + context) + ? 18 + : 14, + fontWeight: + FontWeight.w500, + color: + Color(0xFF593AFF), + ), + ), + ), + ) + : Text( + reimbursementSectionName ?? + '', + textAlign: TextAlign.center, + style: GoogleFonts.poppins( + fontSize: + Responsive.isDesktop( + context) + ? 18 + : 14, + fontWeight: + FontWeight.w400, + color: Color(0xFF636363), + ), + ), + ), + ), + ], + ), + ], + ), + ), + ), + ], + ), + ), + SizedBox(height: 15), + if (isActive) + Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(5), + ), + padding: Responsive.isDesktop(context) + ? EdgeInsets.symmetric( + vertical: 10, horizontal: 10) + : EdgeInsets.all(10), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (cashLessContentHtml != null) + Padding( + padding: + EdgeInsets.only(bottom: 10, left: 0), + child: Column( crossAxisAlignment: - CrossAxisAlignment.start, + CrossAxisAlignment.start, children: [ Text( - 'Health Insurance Claims Process', - textAlign: TextAlign.start, + cashLessHeading ?? '', style: GoogleFonts.poppins( - fontSize: 16, - fontWeight: FontWeight.w600, + fontSize: + Responsive.isDesktop(context) + ? 18 + : 16, + fontWeight: FontWeight.w500, color: Color(0xFF000000), ), ), - ], - ), - ], + // SizedBox(height: 5), + Html( + data: cashLessContentHtml!, + style: { + "li": Style( + fontFamily: GoogleFonts.poppins().fontFamily, + fontSize: FontSize(16), + color: const Color(0xFF000000), + margin: Margins.only(bottom: 5), + ), + }, + ), + ] + ) ), - ), - ), - ], - ), - ], - ), - ), - SizedBox(height: 15), - Container( - decoration: BoxDecoration( - color: Color(0xFFF6FAFF), - borderRadius: BorderRadius.circular(5), - ), - padding: Responsive.isDesktop(context) - ? EdgeInsets.symmetric(vertical: 15, horizontal: 25) - : EdgeInsets.all(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: GestureDetector( - onTap: () { - setState(() { - isActive = true; - }); - }, - child: isActive - ? Material( - elevation: 5, - borderRadius: - BorderRadius.circular( - 10), - color: Colors.white, - child: TextButton( - onPressed: () {}, - style: - TextButton.styleFrom( - padding: EdgeInsets - .symmetric( - horizontal: Responsive - .isDesktop( - context) - ? 80 - : 40, - vertical: Responsive - .isDesktop( - context) - ? 12 - : 7, - ), - ), - child: Text( - cashLessSectionName ?? - '', - textAlign: - TextAlign.center, - style: - GoogleFonts.poppins( - fontSize: Responsive - .isDesktop( - context) - ? 18 - : 14, - fontWeight: - FontWeight.w500, - color: - Color(0xFF593AFF), - ), - ), - ), - ) - : Text( - cashLessSectionName ?? '', - textAlign: TextAlign.center, - style: GoogleFonts.poppins( - fontSize: - Responsive.isDesktop( - context) - ? 18 - : 14, - fontWeight: - FontWeight.w400, - color: Color(0xFF636363), - ), - ), - ), - ), - Expanded( - flex: 6, - child: GestureDetector( - onTap: () { - setState(() { - isActive = false; - }); - }, - child: !isActive - ? Material( - elevation: 5, - borderRadius: - BorderRadius.circular( - 10), - color: Colors.white, - child: TextButton( - onPressed: () {}, - style: - TextButton.styleFrom( - padding: EdgeInsets - .symmetric( - horizontal: Responsive - .isDesktop( - context) - ? 80 - : 40, - vertical: Responsive - .isDesktop( - context) - ? 12 - : 7, - ), - ), - child: Text( - reimbursementSectionName ?? - '', - textAlign: - TextAlign.center, - style: - GoogleFonts.poppins( - fontSize: Responsive - .isDesktop( - context) - ? 18 - : 14, - fontWeight: - FontWeight.w500, - color: - Color(0xFF593AFF), - ), - ), - ), - ) - : Text( - reimbursementSectionName ?? - '', - textAlign: TextAlign.center, - style: GoogleFonts.poppins( - fontSize: - Responsive.isDesktop( - context) - ? 18 - : 14, - fontWeight: - FontWeight.w400, - color: Color(0xFF636363), - ), - ), - ), - ), - ], - ), - ], - ), - ), - ), - ], - ), - ), - SizedBox(height: 15), - if (isActive) - Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(5), - ), - padding: Responsive.isDesktop(context) - ? EdgeInsets.symmetric( - vertical: 15, horizontal: 25) - : EdgeInsets.all(10), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - if (cashLessClaimsDetails != null) + if (cashLessNotesHtml != null) Padding( padding: - EdgeInsets.only(bottom: 20, left: 15), + EdgeInsets.only(bottom: 10, left: 0), child: Column( crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - Text( - cashLessHeading ?? '', - style: GoogleFonts.poppins( - fontSize: - Responsive.isDesktop(context) - ? 18 - : 16, - fontWeight: FontWeight.w500, - color: Color(0xFF000000), - ), - ), - SizedBox(height: 10), - LayoutBuilder( - builder: (context, constraints) { - double itemHeight = - 40; // Adjust item height based on content - double listViewHeight = - cashLessContent.length * - itemHeight; - double maxHeight = - constraints.maxHeight; - - if (listViewHeight > maxHeight) { - listViewHeight = maxHeight; - } - - return Container( - height: listViewHeight, - child: ListView.builder( - itemCount: - cashLessContent.length, - itemBuilder: (context, index) { - String key = cashLessContent - .keys - .elementAt(index); - String value = - cashLessContent[key]!; - return Padding( - padding: const EdgeInsets - .symmetric( - vertical: 8.0), - child: Row( - crossAxisAlignment: - CrossAxisAlignment - .start, - children: [ - Padding( - padding: - const EdgeInsets - .only( - left: 20.0), - child: Text( - "• ", - style: GoogleFonts - .poppins( - fontSize: 16, - fontWeight: - FontWeight - .w400, - color: Color( - 0xFFE26728), - ), - ), - ), - Expanded( - child: Text( - value, - style: GoogleFonts - .poppins( - fontSize: 16, - fontWeight: - FontWeight - .w400, - color: Color( - 0xFF000000), - ), - ), - ), - ], - ), - ); - }, - ), - ); - }, - ), - ], - ), - ), - if (cashLessClaimsDetails != null) - Padding( - padding: - EdgeInsets.only(bottom: 20, left: 15), - child: Column( - crossAxisAlignment: - CrossAxisAlignment.start, + CrossAxisAlignment.start, children: [ Text( 'Notes', style: GoogleFonts.poppins( fontSize: - Responsive.isDesktop(context) - ? 18 - : 16, + Responsive.isDesktop(context) + ? 18 + : 16, fontWeight: FontWeight.w500, color: Color(0xFF000000), ), ), - SizedBox(height: 10), - LayoutBuilder( - builder: (context, constraints) { - double itemHeight = - 70; // Adjust item height based on content - double listViewHeight = - cashLessNotes.length * - itemHeight; - double maxHeight = - constraints.maxHeight; - - if (listViewHeight > maxHeight) { - listViewHeight = maxHeight; - } - - return Container( - height: listViewHeight, - child: ListView.builder( - itemCount: cashLessNotes.length, - itemBuilder: (context, index) { - String key = cashLessNotes - .keys - .elementAt(index); - String value = - cashLessNotes[key]!; - return Padding( - padding: const EdgeInsets - .symmetric( - vertical: 8.0), - child: Row( - crossAxisAlignment: - CrossAxisAlignment - .start, - children: [ - Padding( - padding: - const EdgeInsets - .only( - left: 20.0), - child: Text( - "• ", - style: GoogleFonts - .poppins( - fontSize: 16, - fontWeight: - FontWeight - .w400, - color: Color( - 0xFFE26728), - ), - ), - ), - Expanded( - child: Text( - value, - style: GoogleFonts - .poppins( - fontSize: 16, - fontWeight: - FontWeight - .w400, - color: Color( - 0xFF000000), - ), - ), - ), - ], - ), - ); - }, + // SizedBox(height: 5), + Html( + data: cashLessNotesHtml!, + style: { + "li": Style( + fontFamily: GoogleFonts.poppins().fontFamily, + fontSize: FontSize(15), + color: const Color(0xFF000000), ), - ); - }, - ), + }, + ), ], ), + ) + ]), + ) + else + Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(5), + ), + padding: Responsive.isDesktop(context) + ? EdgeInsets.symmetric( + vertical: 10, horizontal: 10) + : EdgeInsets.all(10), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (reimbursementContentHtml != null) + Padding( + padding: + EdgeInsets.only(bottom: 10, left: 0), + child: Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Text( + reimbursementHeading ?? '', + style: GoogleFonts.poppins( + fontSize: + Responsive.isDesktop(context) + ? 18 + : 16, + fontWeight: FontWeight.w500, + color: Color(0xFF000000), + ), + ), + // SizedBox(height: 5), + Html( + data: reimbursementContentHtml!, + ), + ] + ) ), - ]), - ) - else - Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(5), + + if (reimbursementNotesHtml != null) + Padding( + padding: + EdgeInsets.only(bottom: 10, left: 0), + child: Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Text( + 'Notes', + style: GoogleFonts.poppins( + fontSize: + Responsive.isDesktop(context) + ? 18 + : 16, + fontWeight: FontWeight.w500, + color: Color(0xFF000000), + ), + ), + // SizedBox(height: 5), + Html( + data: reimbursementNotesHtml!, + ), + ] + ) + ), + + ]), ), - padding: Responsive.isDesktop(context) - ? EdgeInsets.symmetric( - vertical: 15, horizontal: 25) - : EdgeInsets.all(10), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - if (reimbursementClaimsDetails != null) - Padding( - padding: - EdgeInsets.only(bottom: 20, left: 15), - child: Column( - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - Text( - reimbursementHeading ?? '', - style: GoogleFonts.poppins( - fontSize: - Responsive.isDesktop(context) - ? 18 - : 16, - fontWeight: FontWeight.w500, - color: Color(0xFF000000), - ), - ), - SizedBox(height: 10), - LayoutBuilder( - builder: (context, constraints) { - double itemHeight = - 40; // Adjust item height based on content - double listViewHeight = - reimbursementContent.length * - itemHeight; - double maxHeight = - constraints.maxHeight; - - if (listViewHeight > maxHeight) { - listViewHeight = maxHeight; - } - - return Container( - height: listViewHeight, - child: ListView.builder( - itemCount: - reimbursementContent.length, - itemBuilder: (context, index) { - String key = - reimbursementContent.keys - .elementAt(index); - String value = - reimbursementContent[ - key]!; - return Padding( - padding: const EdgeInsets - .symmetric( - vertical: 8.0), - child: Row( - crossAxisAlignment: - CrossAxisAlignment - .start, - children: [ - Padding( - padding: - const EdgeInsets - .only( - left: 20.0), - child: Text( - "• ", - style: GoogleFonts - .poppins( - fontSize: 16, - fontWeight: - FontWeight - .w400, - color: Color( - 0xFFE26728), - ), - ), - ), - Expanded( - child: Text( - value, - style: GoogleFonts - .poppins( - fontSize: 16, - fontWeight: - FontWeight - .w400, - color: Color( - 0xFF000000), - ), - ), - ), - ], - ), - ); - }, - ), - ); - }, - ), - ], - ), - ), - if (reimbursementClaimsDetails != null) - Padding( - padding: - EdgeInsets.only(bottom: 20, left: 15), - child: Column( - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - Text( - 'Notes', - style: GoogleFonts.poppins( - fontSize: - Responsive.isDesktop(context) - ? 18 - : 16, - fontWeight: FontWeight.w500, - color: Color(0xFF000000), - ), - ), - SizedBox(height: 10), - LayoutBuilder( - builder: (context, constraints) { - double itemHeight = - 70; // Adjust item height based on content - double listViewHeight = - reimbursementNotes.length * - itemHeight; - double maxHeight = - constraints.maxHeight; - - if (listViewHeight > maxHeight) { - listViewHeight = maxHeight; - } - - return Container( - height: listViewHeight, - child: ListView.builder( - itemCount: - reimbursementNotes.length, - itemBuilder: (context, index) { - String key = - reimbursementNotes.keys - .elementAt(index); - String value = - reimbursementNotes[key]!; - return Padding( - padding: const EdgeInsets - .symmetric( - vertical: 8.0), - child: Row( - crossAxisAlignment: - CrossAxisAlignment - .start, - children: [ - Padding( - padding: - const EdgeInsets - .only( - left: 20.0), - child: Text( - "• ", - style: GoogleFonts - .poppins( - fontSize: 16, - fontWeight: - FontWeight - .w400, - color: Color( - 0xFFE26728), - ), - ), - ), - Expanded( - child: Text( - value, - style: GoogleFonts - .poppins( - fontSize: 16, - fontWeight: - FontWeight - .w400, - color: Color( - 0xFF000000), - ), - ), - ), - ], - ), - ); - }, - ), - ); - }, - ), - ], - ), - ), - ]), - ), - SizedBox(height: Responsive.isDesktop(context) ? 40 : 10), - ], + SizedBox(height: Responsive.isDesktop(context) ? 40 : 10), + ], + ), ), ), - ), - if (Responsive.isDesktop(context)) - Align( - alignment: Alignment.bottomCenter, - child: Container( - width: double.infinity, - child: CustomFooter(), + 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 + ), ), - ), - ], - ), - // 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 - ), - )); + if (Responsive.isDesktop(context)) + Align( + alignment: Alignment.bottomCenter, + child: Container( + width: double.infinity, + 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('/faqs'); + } else if (index == 3) { + context.push('/profile'); + } else if (index == 4) { + 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.question_answer_outlined, + Icons.person_outline_outlined, + Icons.headset_mic_outlined, + ], + labels: ["Home", "Claims", "FAQs", "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 f556b15..b5df959 100755 --- a/lib/pages/postEnrollment/claims.dart +++ b/lib/pages/postEnrollment/claims.dart @@ -584,7 +584,7 @@ class _claimsState extends State { .isDesktop( context) ? 140 - : 50, + : 10 , vertical: Responsive .isDesktop( context) @@ -593,7 +593,7 @@ class _claimsState extends State { // primary: Color(0xFF000000), ), child: Text( - 'Your Plan', + 'Register a Claim', style: GoogleFonts.poppins( fontSize: Responsive .isDesktop( @@ -609,7 +609,7 @@ class _claimsState extends State { ), ) : Text( - 'Your Plan', + 'Register a Claim', style: GoogleFonts.poppins( fontSize: Responsive @@ -766,8 +766,10 @@ class _claimsState extends State { } else if (index == 1) { context.push('/claims'); } else if (index == 2) { - context.push('/profile'); + context.push('/faqs'); } else if (index == 3) { + context.push('/profile'); + } else if (index == 4) { context.push('/help'); } // else if (index == 4) { @@ -784,12 +786,12 @@ class _claimsState extends State { icons: [ Icons.home_outlined, Icons.sticky_note_2_outlined, + Icons.question_answer_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 + labels: ["Home", "Claims", "FAQs", "Profile","Help"], + initialIndex: 1, // Initial index of the bottom navigation bar ), )); } diff --git a/lib/pages/postEnrollment/faqs.dart b/lib/pages/postEnrollment/faqs.dart new file mode 100644 index 0000000..b0256b1 --- /dev/null +++ b/lib/pages/postEnrollment/faqs.dart @@ -0,0 +1,692 @@ +import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; +import 'package:google_fonts/google_fonts.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'; +import '../service/data_manager.dart'; +import 'package:flutter_html/flutter_html.dart'; + +import '../service/video_player_widget.dart'; + +class faqs extends StatefulWidget { + const faqs({super.key}); + + @override + State createState() => _faqsState(); +} + +class FaqItem { + final String id; + final String category; + final String question; + final String? answer; + + FaqItem({ + required this.id, + required this.category, + required this.question, + this.answer, + }); +} + +class FaqNode { + final String title; + final List children = []; + final List faqs = []; + + FaqNode({required this.title}); +} + +class _faqsState extends State { + bool isLoading = false; + late ApiService apiService; + dynamic empCodeString; + dynamic empPrimaryId; + dynamic empEmailID; + dynamic client_id; + dynamic client_branch_id; + dynamic empMobileNo; + final session = SessionManager(); + final dataManager = DataManager(); + + /// ---------------- STATE ---------------- + Map faqTree = {}; + String selectedTab = 'Others'; + + String? openSection; + String? openSubSection; + String? openQuestionId; + + /// ---------------- SAMPLE DATA (Replace with API) ---------------- + @override + void initState() { + super.initState(); + apiService = ApiService(context); + _loadToken(); + } + + @override + void dispose() { + super.dispose(); + } + + Future _loadToken() async { + print('_loadToken'); + final String? token = await TokenService.getPostToken(); + if (token != null && token.isNotEmpty) { + // 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 + empPrimaryId = session.empPrimaryId; + client_id = session.client_id; + print(client_id); + getFAQsDetails(); + } + } + + Future getFAQsDetails() async { + setState(() => isLoading = true); + + try { + final response = await apiService.getFAQsApiData(); + + if (response['status'] == 'success') { + final List list = response['data']['faq_list'] ?? []; + + // ✅ Convert API data → FaqItem list + final List apiFaqs = + list.where((e) => e['is_active'] == '1').map((e) { + return FaqItem( + id: e['id'].toString(), + category: e['category']?.toString().trim() ?? '', + question: e['question']?.toString() ?? '', + answer: e['answer']?.toString(), + ); + }).toList(); + + // ✅ Build tree + final tree = buildFaqTree(apiFaqs); + + setState(() { + faqTree = tree; + + // ✅ Set default tab safely + if (faqTree.isNotEmpty) { + selectedTab = faqTree.keys.first; + } + }); + } else { + ToastHelper.showErrorToast(context, 'No FAQs found'); + } + } catch (e) { + debugPrint('FAQ Error: $e'); + ToastHelper.showErrorToast(context, 'Failed to load FAQs'); + } finally { + if (mounted) { + setState(() => isLoading = false); + } + } + } + + /// ---------------- TREE BUILDER ---------------- + Map buildFaqTree(List faqs) { + final Map root = {}; + + for (var faq in faqs) { + final parts = faq.category.split('/').map((e) => e.trim()).toList(); + + final main = parts.first; + + root.putIfAbsent(main, () => FaqNode(title: main)); + FaqNode current = root[main]!; + + for (int i = 1; i < parts.length; i++) { + final part = parts[i]; + final existing = + current.children.where((e) => e.title == part).toList(); + + if (existing.isEmpty) { + final node = FaqNode(title: part); + current.children.add(node); + current = node; + } else { + current = existing.first; + } + } + + current.faqs.add(faq); + } + + return root; + } + + @override + Widget build(BuildContext context) { + return PopScope( + canPop: false, + onPopInvokedWithResult: (didPop, result) { + if (didPop) return; + context.pop(); + }, + 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.1, // 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: Colors.white, + child: Column(children: [ + Container( + padding: Responsive.isDesktop(context) + ? EdgeInsets.only( + top: 15, bottom: 15, left: 25, right: 25) + : EdgeInsets.only(top: 0, bottom: 0, left: 0, right: 0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Expanded( + flex: 12, + child: InkWell( + onTap: () { + context.pop(); + }, + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Icon( + Icons + .chevron_left, // Replace with your desired icon + color: Color(0xFF000000), + size: 30, + ), + SizedBox( + width: + 5), // Adjust space between icon and text + Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Text( + 'FAQs', + textAlign: TextAlign.start, + style: GoogleFonts.poppins( + fontSize: 16, + fontWeight: FontWeight.w600, + color: Color(0xFF000000), + ), + ), + Text( + 'Frequently Asked Questions', + textAlign: TextAlign.start, + style: GoogleFonts.poppins( + fontSize: + 12, // Adjust the font size as needed + fontWeight: FontWeight.w400, + color: Color(0xFF000000), + ), + ), + ], + ), + ], + ), + ), + ), + ], + ), + Padding( + padding: const EdgeInsets.all(16), + child: Column( + children: [ + Container( + width: double.infinity, + 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: Align( + alignment: Alignment.centerLeft, // ✅ LEFT ALIGN TABS + child: Responsive.isDesktop(context) + ? buildTabs() // no scroll on web + : SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: buildTabs(), + ), + ), + ), + const SizedBox(height: 20), + buildContent(), + ], + ), + ), + + // NhanceVideoWrapper(context) + + ], + ), + ), + ]), + )), + 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('/faqs'); + } else if (index == 3) { + context.push('/profile'); + } else if (index == 4) { + 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.question_answer_outlined, + Icons.person_outline_outlined, + Icons.headset_mic_outlined, + ], + labels: ["Home", "Claims", "FAQs", "Profile","Help"], + initialIndex: 2, // Initial index of the bottom navigation bar + ), + )); + } + + /// ---------------- MAIN TABS ---------------- + Widget buildTabs() { + final bool isDesktop = Responsive.isDesktop(context); + + return Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: faqTree.keys.map((tab) { + final active = selectedTab == tab; + + final tabWidget = InkWell( + onTap: () { + setState(() { + selectedTab = tab; + openSection = null; + openSubSection = null; + openQuestionId = null; + }); + }, + child: Container( + alignment: Alignment.center, + padding: const EdgeInsets.symmetric(vertical: 12), + decoration: BoxDecoration( + color: active ? Colors.deepOrange : Colors.white, + borderRadius: BorderRadius.circular(8), + border: Border.all( + color: active ? Colors.deepOrange : const Color(0xFFE0E0E0), + ), + ), + child: Text( + tab, + textAlign: TextAlign.center, + style: GoogleFonts.poppins( + color: active ? Colors.white : Colors.deepOrange, + fontWeight: FontWeight.w600, + ), + ), + ), + ); + + /// 🔥 DESKTOP → FULL WIDTH TAB + if (isDesktop) { + return Expanded( + child: Padding( + padding: const EdgeInsets.only(right: 10), + child: tabWidget, + ), + ); + } + + /// 📱 MOBILE → CONTENT WIDTH TAB + return Padding( + padding: const EdgeInsets.only(right: 8), + child: SizedBox( + width: 110, + child: tabWidget, + ), + ); + }).toList(), + ); + } + + /// ---------------- CONTENT ---------------- + Widget buildContent() { + final node = faqTree[selectedTab]; + + if (node == null) { + return const Center(child: Text('No FAQs')); + } + + // Health / Travel without hierarchy + if (node.children.isEmpty) { + return Column( + children: node.faqs.map(buildQuestion).toList(), + ); + } + + // Others → Fire Insurance → General + return Column( + children: node.children.map(buildSection).toList(), + ); + } + + /// ---------------- SECTION ---------------- + Widget buildSection(FaqNode node) { + final bool isOpen = openSection == node.title; + const Duration kExpandDuration = Duration(milliseconds: 450); + const Curve kExpandCurve = Curves.easeInOutCubic; + + return Column( + children: [ + InkWell( + onTap: () { + setState(() { + openSection = isOpen ? null : node.title; + openSubSection = null; + openQuestionId = null; + }); + }, + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14), + decoration: const BoxDecoration( + border: Border( + bottom: BorderSide(color: Color(0xFFE0E0E0)), + ), + ), + child: Row( + children: [ + Expanded( + child: Text( + node.title, + style: GoogleFonts.poppins( + fontSize: 15, + fontWeight: FontWeight.w600, + ), + ), + ), + AnimatedRotation( + turns: isOpen ? 0.5 : 0, + duration: kExpandDuration, + curve: kExpandCurve, + child: const Icon( + Icons.keyboard_arrow_down, + color: Colors.deepOrange, + ), + ), + ], + ), + ), + ), + + /// SECTION CONTENT + AnimatedSize( + duration: kExpandDuration, + curve: kExpandCurve, + child: isOpen + ? Column( + children: [ + if (node.children.isNotEmpty) + ...node.children.map(buildSubSection), + if (node.children.isEmpty) ...node.faqs.map(buildQuestion), + ], + ) + : const SizedBox.shrink(), + ), + ], + ); + } + + /// ---------------- SUB SECTION ---------------- + Widget buildSubSection(FaqNode node) { + final bool isOpen = openSubSection == node.title; + const Duration kExpandDuration = Duration(milliseconds: 450); + const Curve kExpandCurve = Curves.easeInOutCubic; + + return Column( + children: [ + InkWell( + onTap: () { + setState(() { + openSubSection = isOpen ? null : node.title; + openQuestionId = null; + }); + }, + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 14), + decoration: const BoxDecoration( + border: Border( + bottom: BorderSide(color: Color(0xFFEDEDED)), + ), + ), + child: Row( + children: [ + Expanded( + child: Text( + node.title, + style: GoogleFonts.poppins( + fontSize: 14, + fontWeight: FontWeight.w500, + ), + ), + ), + AnimatedRotation( + turns: isOpen ? 0.5 : 0, + duration: kExpandDuration, + curve: kExpandCurve, + child: const Icon( + Icons.keyboard_arrow_down, + size: 20, + color: Colors.deepOrange, + ), + ), + ], + ), + ), + ), + AnimatedSize( + duration: kExpandDuration, + curve: kExpandCurve, + child: isOpen + ? Column( + children: node.faqs.map(buildQuestion).toList(), + ) + : const SizedBox.shrink(), + ), + ], + ); + } + + /// ---------------- QUESTION ---------------- + Widget buildQuestion(FaqItem faq) { + final bool isOpen = openQuestionId == faq.id; + const Duration kExpandDuration = Duration(milliseconds: 450); + const Curve kExpandCurve = Curves.easeInOutCubic; + + return Column( + children: [ + InkWell( + onTap: () { + setState(() { + openQuestionId = isOpen ? null : faq.id; + }); + }, + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 14), + decoration: const BoxDecoration( + border: Border( + bottom: BorderSide(color: Color(0xFFF0F0F0)), + ), + ), + child: Row( + children: [ + Expanded( + child: Text( + faq.question, + style: GoogleFonts.poppins( + fontSize: 14, + fontWeight: FontWeight.w400, + ), + ), + ), + AnimatedRotation( + turns: isOpen ? 0.5 : 0, + duration: kExpandDuration, + curve: kExpandCurve, + child: const Icon( + Icons.keyboard_arrow_down, + size: 18, + color: Colors.deepOrange, + ), + ), + ], + ), + ), + ), + AnimatedSize( + duration: kExpandDuration, + curve: kExpandCurve, + child: isOpen + ? Container( + width: double.infinity, + padding: const EdgeInsets.fromLTRB(16, 12, 16, 16), + color: const Color(0xFFFAFAFA), + child: Html( + data: faq.answer ?? '', + style: { + "body": Style( + margin: Margins.zero, + padding: HtmlPaddings.zero, + fontSize: FontSize(13), + color: Colors.black87, + ), + "table": Style( + border: Border.all(color: Colors.grey.shade300), + ), + "td": Style( + padding: HtmlPaddings.all(8), + border: Border.all(color: Colors.grey.shade300), + ), + }, + ), + ) + : const SizedBox.shrink(), + ), + ], + ); + } + + /// ---------------- ARROW ---------------- + Widget buildArrow(bool open) { + return AnimatedRotation( + turns: open ? 0.5 : 0, + duration: const Duration(milliseconds: 300), + child: const Icon( + Icons.keyboard_arrow_down, + color: Colors.deepOrange, + ), + ); + } + + + Widget NhanceVideoWrapper(BuildContext context) { + final video = NetworkVideoPlayer( + videoUrl: "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4", + ); + + if (Responsive.isDesktop(context)) { + return Center( + child: SizedBox( + width: 500, + height: 250, + child: video, + ), + ); + } + + /// 📱 Mobile → default behavior + return video; + } + +} diff --git a/lib/pages/postEnrollment/generalexclusionsdeductibles.dart b/lib/pages/postEnrollment/generalexclusionsdeductibles.dart index 700ce2b..f1ba9df 100755 --- a/lib/pages/postEnrollment/generalexclusionsdeductibles.dart +++ b/lib/pages/postEnrollment/generalexclusionsdeductibles.dart @@ -806,7 +806,7 @@ class _generalExclusionsDeductiblesState : FloatingActionButtonLocation.miniEndFloat, bottomNavigationBar: Responsive.isDesktop(context) ? null - : CustomBottomNavigationBar( + : CustomBottomNavigationBar( onTabChanged: (index) { // Add your navigation logic here // repositionBotman(); @@ -815,8 +815,10 @@ class _generalExclusionsDeductiblesState } else if (index == 1) { context.push('/claims'); } else if (index == 2) { - context.push('/profile'); + context.push('/faqs'); } else if (index == 3) { + context.push('/profile'); + } else if (index == 4) { context.push('/help'); } // else if (index == 4) { @@ -833,11 +835,11 @@ class _generalExclusionsDeductiblesState icons: [ Icons.home_outlined, Icons.sticky_note_2_outlined, + Icons.question_answer_outlined, Icons.person_outline_outlined, Icons.headset_mic_outlined, - // Icons.health_and_safety_outlined, ], - labels: ["Home", "Claims", "Profile", "Help"], + labels: ["Home", "Claims", "FAQs", "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 bb2da32..d901fb7 100755 --- a/lib/pages/postEnrollment/help.dart +++ b/lib/pages/postEnrollment/help.dart @@ -583,6 +583,90 @@ class _helpState extends State { ), SizedBox(height: 20), + + InkWell( + onTap: () { + context.go('/faqs'); + }, + child: Container( + width: double.infinity, + height: Responsive.isDesktop(context) ? 140 : 120, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(6), + border: Border.all( + color: const Color(0xFF2F80ED), + width: 2, + ), + ), + child: Row( + children: [ + /// LEFT IMAGE BLOCK + Container( + width: Responsive.isDesktop(context) ? 180 : 120, + height: double.infinity, + color: Colors.white, + padding: const EdgeInsets.all(12), + child: Image.asset( + 'assets/faqs.jpg', + fit: BoxFit.contain, + ), + ), + + /// RIGHT CONTENT BLOCK + Expanded( + child: Container( + color: const Color(0xFF93C01F), + padding: EdgeInsets.symmetric( + horizontal: Responsive.isDesktop(context) ? 32 : 16, + vertical: Responsive.isDesktop(context) ? 24 : 16, + ), + child: Row( + children: [ + /// TEXT CONTENT + Expanded( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'FAQs', + style: GoogleFonts.poppins( + fontSize: + Responsive.isDesktop(context) ? 36 : 20, + fontWeight: FontWeight.w600, + color: Colors.white, + ), + ), + const SizedBox(height: 8), + Text( + 'Find answers to commonly asked questions', + style: GoogleFonts.poppins( + fontSize: + Responsive.isDesktop(context) ? 18 : 14, + fontWeight: FontWeight.w400, + color: Colors.white, + ), + ), + ], + ), + ), + + /// RIGHT ARROW + Icon( + Icons.chevron_right, + size: Responsive.isDesktop(context) ? 36 : 28, + color: Colors.white, + ), + ], + ), + ), + ), + ], + ), + ), + ), + + // SizedBox(height: 15), // Container( // decoration: BoxDecoration( @@ -1045,7 +1129,7 @@ class _helpState extends State { : FloatingActionButtonLocation.miniEndFloat, bottomNavigationBar: Responsive.isDesktop(context) ? null - : CustomBottomNavigationBar( + : CustomBottomNavigationBar( onTabChanged: (index) { // Add your navigation logic here // repositionBotman(); @@ -1054,8 +1138,10 @@ class _helpState extends State { } else if (index == 1) { context.push('/claims'); } else if (index == 2) { - context.push('/profile'); + context.push('/faqs'); } else if (index == 3) { + context.push('/profile'); + } else if (index == 4) { context.push('/help'); } // else if (index == 4) { @@ -1072,12 +1158,12 @@ class _helpState extends State { icons: [ Icons.home_outlined, Icons.sticky_note_2_outlined, + Icons.question_answer_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 + labels: ["Home", "Claims", "FAQs", "Profile","Help"], + initialIndex: 4, // Initial index of the bottom navigation bar ), )); } diff --git a/lib/pages/postEnrollment/home.dart b/lib/pages/postEnrollment/home.dart index a98265d..9c16e68 100755 --- a/lib/pages/postEnrollment/home.dart +++ b/lib/pages/postEnrollment/home.dart @@ -276,19 +276,19 @@ class _HomeState extends State { 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']; - } + 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; - }); + setState(() { + isTokenAvailable = pre_policy_count != 0; + emp_not_enrolled_count = empNotEnrolledCount != 0; + }); if (emp_not_enrolled_count) { final prefs = await SharedPreferences.getInstance(); @@ -630,7 +630,7 @@ class _HomeState extends State { }, ), SizedBox(height: 15), - if(!isRetailLoggedIn) + if(!isRetailLoggedIn && isTokenAvailable) Card( elevation: 2, color: Color(0xFFDDF3FF), @@ -1189,40 +1189,42 @@ class _HomeState extends State { : FloatingActionButtonLocation.miniEndFloat, bottomNavigationBar: Responsive.isDesktop(context) ? null - : CustomBottomNavigationBar( - 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 - ), + : 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('/faqs'); + } else if (index == 3) { + context.push('/profile'); + } else if (index == 4) { + 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.question_answer_outlined, + Icons.person_outline_outlined, + Icons.headset_mic_outlined, + ], + labels: ["Home", "Claims", "FAQs", "Profile","Help"], + initialIndex: 0, // Initial index of the bottom navigation bar + ), )); } @@ -1882,6 +1884,11 @@ class _HomeState extends State { } Widget _buildCarouselSlider(List cards) { + final int totalCards = cards.length; + final bool hasAddCard = isActive; // because you add AddCard only when isActive + final int displayCardCount = + hasAddCard ? totalCards - 1 : totalCards; + return Stack( alignment: Alignment.center, children: [ @@ -1897,13 +1904,17 @@ class _HomeState extends State { aspectRatio: 2.0, initialPage: 0, onPageChanged: (index, reason) { - setState(() => currentIndex = index); + setState(() { + currentIndex = index >= displayCardCount + ? displayCardCount - 1 + : index; + }); }, ), ), // 🔵 CARD COUNTER (4/10) - if(isActive) + if(isActive && displayCardCount > 0) Positioned( bottom: 10, child: Container( @@ -1913,7 +1924,7 @@ class _HomeState extends State { borderRadius: BorderRadius.circular(12), ), child: Text( - "${currentIndex + 1} / ${cards.length}", + "${(currentIndex + 1).clamp(1, displayCardCount)} / $displayCardCount", style: TextStyle( color: Colors.white, fontSize: 12, @@ -2158,7 +2169,7 @@ class _HomeState extends State { mainAxisAlignment: MainAxisAlignment.center, children: [ Text( - "OPT & Wellness", + "OPD & Wellness", style: GoogleFonts.poppins( fontSize: 16, fontWeight: FontWeight.w600, diff --git a/lib/pages/postEnrollment/planclaimsform.dart b/lib/pages/postEnrollment/planclaimsform.dart index 161a458..ac2043c 100755 --- a/lib/pages/postEnrollment/planclaimsform.dart +++ b/lib/pages/postEnrollment/planclaimsform.dart @@ -1455,7 +1455,7 @@ SizedBox(height: 15), : FloatingActionButtonLocation.miniEndFloat, bottomNavigationBar: Responsive.isDesktop(context) ? null - : CustomBottomNavigationBar( + : CustomBottomNavigationBar( onTabChanged: (index) { // Add your navigation logic here // repositionBotman(); @@ -1464,8 +1464,10 @@ SizedBox(height: 15), } else if (index == 1) { context.push('/claims'); } else if (index == 2) { - context.push('/profile'); + context.push('/faqs'); } else if (index == 3) { + context.push('/profile'); + } else if (index == 4) { context.push('/help'); } // else if (index == 4) { @@ -1482,11 +1484,11 @@ SizedBox(height: 15), icons: [ Icons.home_outlined, Icons.sticky_note_2_outlined, + Icons.question_answer_outlined, Icons.person_outline_outlined, Icons.headset_mic_outlined, - // Icons.health_and_safety_outlined, ], - labels: ["Home", "Claims", "Profile", "Help"], + labels: ["Home", "Claims", "FAQs", "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 fc9a16b..d0a69e8 100755 --- a/lib/pages/postEnrollment/policies.dart +++ b/lib/pages/postEnrollment/policies.dart @@ -1105,7 +1105,7 @@ class _policiesState extends State { : FloatingActionButtonLocation.miniEndFloat, bottomNavigationBar: Responsive.isDesktop(context) ? null - : CustomBottomNavigationBar( + : CustomBottomNavigationBar( onTabChanged: (index) { // Add your navigation logic here // repositionBotman(); @@ -1114,8 +1114,10 @@ class _policiesState extends State { } else if (index == 1) { context.push('/claims'); } else if (index == 2) { - context.push('/profile'); + context.push('/faqs'); } else if (index == 3) { + context.push('/profile'); + } else if (index == 4) { context.push('/help'); } // else if (index == 4) { @@ -1132,11 +1134,11 @@ class _policiesState extends State { icons: [ Icons.home_outlined, Icons.sticky_note_2_outlined, + Icons.question_answer_outlined, Icons.person_outline_outlined, Icons.headset_mic_outlined, - // Icons.health_and_safety_outlined, ], - labels: ["Home", "Claims", "Profile", "Help"], + labels: ["Home", "Claims", "FAQs", "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 2ef3d43..cf0111f 100755 --- a/lib/pages/postEnrollment/profile.dart +++ b/lib/pages/postEnrollment/profile.dart @@ -888,7 +888,7 @@ class _profileState extends State { : FloatingActionButtonLocation.miniEndFloat, bottomNavigationBar: Responsive.isDesktop(context) ? null - : CustomBottomNavigationBar( + : CustomBottomNavigationBar( onTabChanged: (index) { // Add your navigation logic here // repositionBotman(); @@ -897,8 +897,10 @@ class _profileState extends State { } else if (index == 1) { context.push('/claims'); } else if (index == 2) { - context.push('/profile'); + context.push('/faqs'); } else if (index == 3) { + context.push('/profile'); + } else if (index == 4) { context.push('/help'); } // else if (index == 4) { @@ -915,12 +917,12 @@ class _profileState extends State { icons: [ Icons.home_outlined, Icons.sticky_note_2_outlined, + Icons.question_answer_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 + labels: ["Home", "Claims", "FAQs", "Profile","Help"], + initialIndex: 3, // Initial index of the bottom navigation bar ), )); } diff --git a/lib/pages/postEnrollment/raisedTicketList.dart b/lib/pages/postEnrollment/raisedTicketList.dart index 5a25751..d72a75a 100644 --- a/lib/pages/postEnrollment/raisedTicketList.dart +++ b/lib/pages/postEnrollment/raisedTicketList.dart @@ -303,7 +303,7 @@ class _raisedTicketHistoryState extends State { : FloatingActionButtonLocation.miniEndFloat, bottomNavigationBar: Responsive.isDesktop(context) ? null - : CustomBottomNavigationBar( + : CustomBottomNavigationBar( onTabChanged: (index) { // Add your navigation logic here // repositionBotman(); @@ -312,8 +312,10 @@ class _raisedTicketHistoryState extends State { } else if (index == 1) { context.push('/claims'); } else if (index == 2) { - context.push('/profile'); + context.push('/faqs'); } else if (index == 3) { + context.push('/profile'); + } else if (index == 4) { context.push('/help'); } // else if (index == 4) { @@ -330,11 +332,11 @@ class _raisedTicketHistoryState extends State { icons: [ Icons.home_outlined, Icons.sticky_note_2_outlined, + Icons.question_answer_outlined, Icons.person_outline_outlined, Icons.headset_mic_outlined, - // Icons.health_and_safety_outlined, ], - labels: ["Home", "Claims", "Profile", "Help"], + labels: ["Home", "Claims", "FAQs", "Profile","Help"], initialIndex: 0, // Initial index of the bottom navigation bar ), ) diff --git a/lib/pages/postEnrollment/retailClaimForm.dart b/lib/pages/postEnrollment/retailClaimForm.dart index b6bcdb2..ccfe5c1 100644 --- a/lib/pages/postEnrollment/retailClaimForm.dart +++ b/lib/pages/postEnrollment/retailClaimForm.dart @@ -468,7 +468,7 @@ class _retailClaimFormsState extends State { : FloatingActionButtonLocation.miniEndFloat, bottomNavigationBar: Responsive.isDesktop(context) ? null - : CustomBottomNavigationBar( + : CustomBottomNavigationBar( onTabChanged: (index) { // Add your navigation logic here // repositionBotman(); @@ -477,8 +477,10 @@ class _retailClaimFormsState extends State { } else if (index == 1) { context.push('/claims'); } else if (index == 2) { - context.push('/profile'); + context.push('/faqs'); } else if (index == 3) { + context.push('/profile'); + } else if (index == 4) { context.push('/help'); } // else if (index == 4) { @@ -495,11 +497,11 @@ class _retailClaimFormsState extends State { icons: [ Icons.home_outlined, Icons.sticky_note_2_outlined, + Icons.question_answer_outlined, Icons.person_outline_outlined, Icons.headset_mic_outlined, - // Icons.health_and_safety_outlined, ], - labels: ["Home", "Claims", "Profile", "Help"], + labels: ["Home", "Claims", "FAQs", "Profile","Help"], initialIndex: 0, // Initial index of the bottom navigation bar ), )); diff --git a/lib/pages/postEnrollment/service/api_service.dart b/lib/pages/postEnrollment/service/api_service.dart index 852edae..9415962 100755 --- a/lib/pages/postEnrollment/service/api_service.dart +++ b/lib/pages/postEnrollment/service/api_service.dart @@ -93,6 +93,19 @@ class ApiService { return response; } + Future> getFAQsApiData() async { + print(_postToken); + if (_postToken == null) { + await _initializeToken(); + } + final url = Uri.parse('${Environment.apiUrl}FAQ'); + final headers = { + 'Authorization': 'Bearer $_postToken' ?? '', + }; + final response = await _makeGetRequest(url, headers); + return response; + } + Future> getTrackClaimsList(String empPrimaryId,mobileNo,emailID) async { print(_postToken); if (_postToken == null) { diff --git a/lib/pages/postEnrollment/tickets.dart b/lib/pages/postEnrollment/tickets.dart index 2665262..3a16d4b 100644 --- a/lib/pages/postEnrollment/tickets.dart +++ b/lib/pages/postEnrollment/tickets.dart @@ -603,7 +603,7 @@ class _ticketsState extends State { : FloatingActionButtonLocation.miniEndFloat, bottomNavigationBar: Responsive.isDesktop(context) ? null - : CustomBottomNavigationBar( + : CustomBottomNavigationBar( onTabChanged: (index) { // Add your navigation logic here // repositionBotman(); @@ -612,8 +612,10 @@ class _ticketsState extends State { } else if (index == 1) { context.push('/claims'); } else if (index == 2) { - context.push('/profile'); + context.push('/faqs'); } else if (index == 3) { + context.push('/profile'); + } else if (index == 4) { context.push('/help'); } // else if (index == 4) { @@ -630,11 +632,11 @@ class _ticketsState extends State { icons: [ Icons.home_outlined, Icons.sticky_note_2_outlined, + Icons.question_answer_outlined, Icons.person_outline_outlined, Icons.headset_mic_outlined, - // Icons.health_and_safety_outlined, ], - labels: ["Home", "Claims", "Profile", "Help"], + labels: ["Home", "Claims", "FAQs", "Profile","Help"], initialIndex: 0, // Initial index of the bottom navigation bar ), )); diff --git a/lib/pages/postEnrollment/tickettracklist.dart b/lib/pages/postEnrollment/tickettracklist.dart index dbc6f89..98de642 100755 --- a/lib/pages/postEnrollment/tickettracklist.dart +++ b/lib/pages/postEnrollment/tickettracklist.dart @@ -674,7 +674,7 @@ class _tickettracklistState extends State { : FloatingActionButtonLocation.miniEndFloat, bottomNavigationBar: Responsive.isDesktop(context) ? null - : CustomBottomNavigationBar( + : CustomBottomNavigationBar( onTabChanged: (index) { // Add your navigation logic here // repositionBotman(); @@ -683,8 +683,10 @@ class _tickettracklistState extends State { } else if (index == 1) { context.push('/claims'); } else if (index == 2) { - context.push('/profile'); + context.push('/faqs'); } else if (index == 3) { + context.push('/profile'); + } else if (index == 4) { context.push('/help'); } // else if (index == 4) { @@ -701,11 +703,11 @@ class _tickettracklistState extends State { icons: [ Icons.home_outlined, Icons.sticky_note_2_outlined, + Icons.question_answer_outlined, Icons.person_outline_outlined, Icons.headset_mic_outlined, - // Icons.health_and_safety_outlined, ], - labels: ["Home", "Claims", "Profile", "Help"], + labels: ["Home", "Claims", "FAQs", "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 dae8a31..17eff08 100755 --- a/lib/pages/postEnrollment/wellness.dart +++ b/lib/pages/postEnrollment/wellness.dart @@ -364,7 +364,7 @@ class _wellnessState extends State { : FloatingActionButtonLocation.miniEndFloat, bottomNavigationBar: Responsive.isDesktop(context) ? null - : CustomBottomNavigationBar( + : CustomBottomNavigationBar( onTabChanged: (index) { // Add your navigation logic here // repositionBotman(); @@ -373,8 +373,10 @@ class _wellnessState extends State { } else if (index == 1) { context.push('/claims'); } else if (index == 2) { - context.push('/profile'); + context.push('/faqs'); } else if (index == 3) { + context.push('/profile'); + } else if (index == 4) { context.push('/help'); } // else if (index == 4) { @@ -391,11 +393,11 @@ class _wellnessState extends State { icons: [ Icons.home_outlined, Icons.sticky_note_2_outlined, + Icons.question_answer_outlined, Icons.person_outline_outlined, Icons.headset_mic_outlined, - // Icons.health_and_safety_outlined, ], - labels: ["Home", "Claims", "Profile", "Help"], + labels: ["Home", "Claims", "FAQs", "Profile","Help"], initialIndex: 0, // Initial index of the bottom navigation bar ), ) diff --git a/lib/pages/service/multi_video_player.dart b/lib/pages/service/multi_video_player.dart new file mode 100644 index 0000000..4f0a4f6 --- /dev/null +++ b/lib/pages/service/multi_video_player.dart @@ -0,0 +1,560 @@ +import 'dart:async'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:video_player/video_player.dart'; + +class NhanceMultiVideoWrapper extends StatefulWidget { + final List videoUrls; + + const NhanceMultiVideoWrapper({ + super.key, + required this.videoUrls, + }); + + @override + State createState() => + _NhanceMultiVideoWrapperState(); +} + +class _NhanceMultiVideoWrapperState extends State { + final PageController _pageController = PageController(); + final List _controllers = []; + + int _currentIndex = 0; + bool _isMuted = false; + + /// Optional static cover image + final String coverImage = + "https://picsum.photos/900/500"; // replace with API thumbnail if available + + @override + void initState() { + super.initState(); + _initControllers(); + } + + Future _initControllers() async { + for (final url in widget.videoUrls) { + final controller = VideoPlayerController.networkUrl(Uri.parse(url)); + await controller.initialize(); + controller.setLooping(true); + controller.setVolume(1); + _controllers.add(controller); + } + if (mounted) setState(() {}); + } + + @override + void dispose() { + for (final c in _controllers) { + c.dispose(); + } + _pageController.dispose(); + super.dispose(); + } + + void _next() { + if (_currentIndex < _controllers.length - 1) { + _pageController.nextPage( + duration: const Duration(milliseconds: 300), + curve: Curves.easeInOut, + ); + } + } + + void _previous() { + if (_currentIndex > 0) { + _pageController.previousPage( + duration: const Duration(milliseconds: 300), + curve: Curves.easeInOut, + ); + } + } + + void _seek(VideoPlayerController c, int seconds) { + final pos = c.value.position + Duration(seconds: seconds); + c.seekTo( + pos < Duration.zero ? Duration.zero : pos, + ); + } + + void _toggleMute(VideoPlayerController c) { + setState(() { + _isMuted = !_isMuted; + c.setVolume(_isMuted ? 0 : 1); + }); + } + + void _openFullscreen(VideoPlayerController controller) { + Navigator.of(context).push( + MaterialPageRoute( + builder: (_) => Scaffold( + backgroundColor: Colors.black, + body: Center( + child: AspectRatio( + aspectRatio: controller.value.aspectRatio, + child: VideoPlayer(controller), + ), + ), + ), + ), + ); + } + + @override + Widget build(BuildContext context) { + if (_controllers.isEmpty) { + return const SizedBox( + height: 250, + child: Center(child: CircularProgressIndicator()), + ); + } + + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // const Text( + // "Videos", + // style: TextStyle(fontSize: 18, fontWeight: FontWeight.w600), + // ), + // const SizedBox(height: 10), + + SizedBox( + height: kIsWeb ? 340 : 240, + child: Stack( + alignment: Alignment.center, + children: [ + PageView.builder( + controller: _pageController, + itemCount: _controllers.length, + onPageChanged: (index) { + setState(() { + _controllers[_currentIndex].pause(); + _currentIndex = index; + }); + }, + itemBuilder: (context, index) { + final controller = _controllers[index]; + + return Center( + child: SizedBox( + width: kIsWeb ? 520 : double.infinity, + child: AspectRatio( + aspectRatio: controller.value.aspectRatio, + child: Stack( + alignment: Alignment.center, + children: [ + /// 🎥 VIDEO + VideoPlayer(controller), + + /// 🖼 COVER IMAGE + if (!controller.value.isPlaying) + // Positioned.fill( + // child: Image.network( + // coverImage, + // fit: BoxFit.cover, + // ), + // ), + + /// ▶ PLAY / PAUSE + IconButton( + iconSize: 64, + icon: Icon( + controller.value.isPlaying + ? Icons.pause_circle + : Icons.play_circle, + color: Colors.white, + ), + onPressed: () { + setState(() { + controller.value.isPlaying + ? controller.pause() + : controller.play(); + }); + }, + ), + + /// 🎛 BOTTOM CONTROLS + Positioned( + bottom: 0, + left: 0, + right: 0, + child: Container( + padding: const EdgeInsets.symmetric( + horizontal: 10, vertical: 6), + color: Colors.black.withOpacity(0.55), + child: Row( + children: [ + IconButton( + icon: const Icon(Icons.replay_10, + color: Colors.white), + onPressed: () => + _seek(controller, -10), + ), + IconButton( + icon: const Icon(Icons.forward_10, + color: Colors.white), + onPressed: () => + _seek(controller, 10), + ), + IconButton( + icon: Icon( + _isMuted + ? Icons.volume_off + : Icons.volume_up, + color: Colors.white, + ), + onPressed: () => + _toggleMute(controller), + ), + Expanded( + child: VideoProgressIndicator( + controller, + allowScrubbing: true, + colors: VideoProgressColors( + playedColor: + const Color(0xFF593AFF), + ), + ), + ), + IconButton( + icon: const Icon(Icons.fullscreen, + color: Colors.white), + onPressed: () => + _openFullscreen(controller), + ), + ], + ), + ), + ), + ], + ), + ), + ), + ); + }, + ), + + /// ⬅ PREVIOUS (WEB) + if (kIsWeb) + Positioned( + left: 12, + child: _ArrowButton( + icon: Icons.chevron_left, + enabled: _currentIndex > 0, + onTap: _previous, + ), + ), + + /// ➡ NEXT (WEB) + if (kIsWeb) + Positioned( + right: 12, + child: _ArrowButton( + icon: Icons.chevron_right, + enabled: _currentIndex < _controllers.length - 1, + onTap: _next, + ), + ), + ], + ), + ), + + const SizedBox(height: 8), + + /// 🔘 DOT INDICATOR + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: List.generate( + _controllers.length, + (i) => AnimatedContainer( + duration: const Duration(milliseconds: 250), + margin: const EdgeInsets.symmetric(horizontal: 4), + height: 8, + width: _currentIndex == i ? 20 : 8, + decoration: BoxDecoration( + color: _currentIndex == i + ? const Color(0xFF593AFF) + : Colors.grey.shade400, + borderRadius: BorderRadius.circular(4), + ), + ), + ), + ), + ], + ); + } +} + +/// 🔘 Arrow Button +class _ArrowButton extends StatelessWidget { + final IconData icon; + final bool enabled; + final VoidCallback onTap; + + const _ArrowButton({ + required this.icon, + required this.enabled, + required this.onTap, + }); + + @override + Widget build(BuildContext context) { + return IgnorePointer( + ignoring: !enabled, + child: AnimatedOpacity( + duration: const Duration(milliseconds: 200), + opacity: enabled ? 1 : 0.3, + child: Material( + color: Colors.black.withOpacity(0.6), + shape: const CircleBorder(), + elevation: 4, + child: InkWell( + customBorder: const CircleBorder(), + onTap: onTap, + child: Padding( + padding: const EdgeInsets.all(10), + child: Icon(icon, size: 36, color: Colors.white), + ), + ), + ), + ), + ); + } +} + + +// import 'package:flutter/foundation.dart'; +// import 'package:flutter/material.dart'; +// import 'package:video_player/video_player.dart'; +// +// class NhanceMultiVideoWrapper extends StatefulWidget { +// final List videoUrls; +// +// const NhanceMultiVideoWrapper({ +// super.key, +// required this.videoUrls, +// }); +// +// @override +// State createState() => +// _NhanceMultiVideoWrapperState(); +// } +// +// class _NhanceMultiVideoWrapperState extends State { +// final PageController _pageController = PageController(); +// int _currentIndex = 0; +// +// final List _controllers = []; +// +// @override +// void initState() { +// super.initState(); +// _initControllers(); +// } +// +// Future _initControllers() async { +// for (final url in widget.videoUrls) { +// final controller = VideoPlayerController.networkUrl(Uri.parse(url)); +// await controller.initialize(); +// controller.setLooping(true); +// controller.setVolume(1); +// _controllers.add(controller); +// } +// +// if (mounted) setState(() {}); +// } +// +// @override +// void dispose() { +// for (final c in _controllers) { +// c.dispose(); +// } +// _pageController.dispose(); +// super.dispose(); +// } +// +// void _next() { +// if (_currentIndex < widget.videoUrls.length - 1) { +// _pageController.nextPage( +// duration: const Duration(milliseconds: 300), +// curve: Curves.easeInOut, +// ); +// } +// } +// +// void _previous() { +// if (_currentIndex > 0) { +// _pageController.previousPage( +// duration: const Duration(milliseconds: 300), +// curve: Curves.easeInOut, +// ); +// } +// } +// +// @override +// Widget build(BuildContext context) { +// if (_controllers.isEmpty) { +// return const SizedBox( +// height: 250, +// child: Center(child: CircularProgressIndicator()), +// ); +// } +// +// return Column( +// crossAxisAlignment: CrossAxisAlignment.start, +// children: [ +// // Text( +// // "Videos", +// // style: Theme.of(context) +// // .textTheme +// // .titleMedium +// // ?.copyWith(fontWeight: FontWeight.w600), +// // ), +// // const SizedBox(height: 10), +// +// /// 🎥 VIDEO WITH WEB CONTROLS +// SizedBox( +// height: kIsWeb ? 320 : 220, +// child: Stack( +// alignment: Alignment.center, +// children: [ +// PageView.builder( +// controller: _pageController, +// itemCount: _controllers.length, +// onPageChanged: (index) { +// setState(() { +// _controllers[_currentIndex].pause(); +// _currentIndex = index; +// }); +// }, +// itemBuilder: (context, index) { +// final controller = _controllers[index]; +// +// return Center( +// child: SizedBox( +// width: kIsWeb ? 520 : double.infinity, +// child: AspectRatio( +// aspectRatio: controller.value.aspectRatio, +// child: Stack( +// alignment: Alignment.center, +// children: [ +// VideoPlayer(controller), +// +// IconButton( +// iconSize: 60, +// icon: Icon( +// controller.value.isPlaying +// ? Icons.pause_circle +// : Icons.play_circle, +// color: Colors.white, +// ), +// onPressed: () { +// setState(() { +// controller.value.isPlaying +// ? controller.pause() +// : controller.play(); +// }); +// }, +// ), +// ], +// ), +// ), +// ), +// ); +// }, +// ), +// +// /// ⬅ PREVIOUS (WEB ONLY) +// if (kIsWeb) +// Positioned( +// left: 16, +// child: _VideoArrowButton( +// icon: Icons.chevron_left, +// enabled: _currentIndex > 0, +// onTap: _previous, +// ), +// ), +// +// +// /// ➡ NEXT (WEB ONLY) +// if (kIsWeb) +// Positioned( +// right: 16, +// child: _VideoArrowButton( +// icon: Icons.chevron_right, +// enabled: _currentIndex < _controllers.length - 1, +// onTap: _next, +// ), +// ), +// +// ], +// ), +// ), +// +// const SizedBox(height: 8), +// +// /// 🔘 DOT INDICATOR +// Row( +// mainAxisAlignment: MainAxisAlignment.center, +// children: List.generate( +// _controllers.length, +// (index) => AnimatedContainer( +// duration: const Duration(milliseconds: 250), +// margin: const EdgeInsets.symmetric(horizontal: 4), +// height: 8, +// width: _currentIndex == index ? 20 : 8, +// decoration: BoxDecoration( +// color: _currentIndex == index +// ? const Color(0xFF593AFF) +// : Colors.grey.shade400, +// borderRadius: BorderRadius.circular(4), +// ), +// ), +// ), +// ), +// ], +// ); +// } +// +// +// } +// class _VideoArrowButton extends StatelessWidget { +// final IconData icon; +// final bool enabled; +// final VoidCallback onTap; +// +// const _VideoArrowButton({ +// required this.icon, +// required this.enabled, +// required this.onTap, +// }); +// +// @override +// Widget build(BuildContext context) { +// return IgnorePointer( +// ignoring: !enabled, +// child: AnimatedOpacity( +// duration: const Duration(milliseconds: 200), +// opacity: enabled ? 1 : 0.3, +// child: Material( +// color: Colors.black.withOpacity(0.55), +// shape: const CircleBorder(), +// elevation: 4, +// child: InkWell( +// customBorder: const CircleBorder(), +// onTap: onTap, +// child: Padding( +// padding: const EdgeInsets.all(10), +// child: Icon( +// icon, +// size: 36, +// color: Colors.white, +// ), +// ), +// ), +// ), +// ), +// ); +// } +// } diff --git a/lib/pages/service/network_video_player.dart b/lib/pages/service/network_video_player.dart new file mode 100644 index 0000000..a10a865 --- /dev/null +++ b/lib/pages/service/network_video_player.dart @@ -0,0 +1,81 @@ +import 'package:flutter/material.dart'; +import 'package:video_player/video_player.dart'; + +class NetworkVideoPlayer extends StatefulWidget { + final String videoUrl; + + const NetworkVideoPlayer({ + super.key, + required this.videoUrl, + }); + + @override + State createState() => _NetworkVideoPlayerState(); +} + +class _NetworkVideoPlayerState extends State { + late VideoPlayerController _controller; + bool isInitialized = false; + + @override + void initState() { + super.initState(); + + _controller = VideoPlayerController.networkUrl( + Uri.parse(widget.videoUrl), + )..initialize().then((_) { + if (!mounted) return; + setState(() => isInitialized = true); + }); + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return AspectRatio( + aspectRatio: isInitialized + ? _controller.value.aspectRatio + : 16 / 9, + child: isInitialized + ? Stack( + alignment: Alignment.center, + children: [ + VideoPlayer(_controller), + + IconButton( + iconSize: 60, + icon: Icon( + _controller.value.isPlaying + ? Icons.pause_circle + : Icons.play_circle, + color: Colors.white, + ), + onPressed: () { + setState(() { + _controller.value.isPlaying + ? _controller.pause() + : _controller.play(); + }); + }, + ), + + Positioned( + bottom: 0, + left: 0, + right: 0, + child: VideoProgressIndicator( + _controller, + allowScrubbing: true, + ), + ), + ], + ) + : const Center(child: CircularProgressIndicator()), + ); + } +} diff --git a/lib/pages/service/video_player_widget.dart b/lib/pages/service/video_player_widget.dart new file mode 100644 index 0000000..353ed65 --- /dev/null +++ b/lib/pages/service/video_player_widget.dart @@ -0,0 +1,74 @@ +import 'package:flutter/material.dart'; +import 'package:video_player/video_player.dart'; + +class NetworkVideoPlayer extends StatefulWidget { + final String videoUrl; + + const NetworkVideoPlayer({super.key, required this.videoUrl}); + + @override + State createState() => _NetworkVideoPlayerState(); +} + +class _NetworkVideoPlayerState extends State { + late VideoPlayerController _controller; + bool isInitialized = false; + + @override + void initState() { + super.initState(); + + _controller = VideoPlayerController.networkUrl( + Uri.parse(widget.videoUrl), + ) + ..initialize().then((_) { + setState(() { + isInitialized = true; + }); + }); + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return AspectRatio( + aspectRatio: + isInitialized ? _controller.value.aspectRatio : 16 / 9, + child: isInitialized + ? Stack( + alignment: Alignment.bottomCenter, + children: [ + VideoPlayer(_controller), + VideoProgressIndicator( + _controller, + allowScrubbing: true, + ), + Center( + child: IconButton( + iconSize: 60, + icon: Icon( + _controller.value.isPlaying + ? Icons.pause_circle + : Icons.play_circle, + color: Colors.white, + ), + onPressed: () { + setState(() { + _controller.value.isPlaying + ? _controller.pause() + : _controller.play(); + }); + }, + ), + ), + ], + ) + : const Center(child: CircularProgressIndicator()), + ); + } +} diff --git a/pubspec.yaml b/pubspec.yaml index 6b209c7..cb9dc5f 100755 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -72,6 +72,8 @@ dependencies: carousel_slider: ^5.1.1 dropdown_search: ^6.0.2 ribbon_widget: ^1.0.5 + flutter_html: ^3.0.0 + video_player: ^2.10.1 dev_dependencies: flutter_test: @@ -98,7 +100,6 @@ flutter: # To add assets to your application, add an assets section, like this: assets: - assets/ - - assets/botman_chat.html # - images/a_dot_burr.jpeg # - images/a_dot_ham.jpeg