import 'package:flutter/cupertino.dart'; 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/postEnrollment/service/svg_service.dart'; import 'package:url_launcher/url_launcher.dart'; import '../../customAppBar/customFooter.dart'; import '../helpers/custom_download_snackbar.dart'; import '../helpers/ecard_download_service.dart'; import '../helpers/ecard_download_notification_service.dart'; import '../../customAppBar/responsive.dart'; import '../../customAppBar/tabs.dart'; import '../../customAppBar/toastHelper.dart'; import 'package:http/http.dart' as http; import 'package:flutter_svg/flutter_svg.dart'; import '../service/SessionManager.dart'; import '../service/TokenService.dart'; import '../service/popup_helper.dart'; import 'package:nhance_app_pwa/logger.dart'; class policies extends StatefulWidget { final Map? arguments; const policies({super.key, this.arguments}); @override State createState() => _policiesState(); } class _policiesState extends State { late ApiService apiService; final EcardDownloadService _ecardDownloadService = const EcardDownloadService(); final session = SessionManager(); int _currentIndex = 0; bool isActive = true; dynamic _token; dynamic empCodeString; dynamic empPrimaryId; dynamic empName; dynamic mobileNo; dynamic client_branch_id; dynamic client_id; dynamic policyList; dynamic policyDataIsEmpty = 1; dynamic policyHeading; dynamic policyName; dynamic EmployeePolicy; List> employeeDetails = []; dynamic argumentsData; bool ECardHide = true; bool _isExpanded = false; bool _isDownloadingEcard = false; bool _isLoadingEmployeePolicy = false; bool _employeePolicyFetched = false; bool _addButtonShow = false; List _mappedFamilyFloaters = []; List _allowedRelationships = []; dynamic _basicCoverSi; Map? _employeePolicyData; bool _argsInitialized = false; final TextEditingController _relationShipController = TextEditingController(); final TextEditingController _memberNameController = TextEditingController(); final TextEditingController _dobController = TextEditingController(); final TextEditingController _effectiveDateController = TextEditingController(); void _onTabChanged(int index) { setState(() { _currentIndex = index; }); } void _toggleExpansion() { setState(() { _isExpanded = !_isExpanded; }); } @override void initState() { super.initState(); apiService = ApiService(context); // Initialize ApiService here _loadToken(); EcardDownloadNotificationService.ensureInitialized(); if (!kIsWeb) { WidgetsBinding.instance.addPostFrameCallback((_) async { await _ecardDownloadService.ensureStorageAccess(); }); } } @override void dispose() { _relationShipController.dispose(); _memberNameController.dispose(); _dobController.dispose(); _effectiveDateController.dispose(); super.dispose(); } Future _loadToken() async { logDebug('_loadToken'); final String? token = await TokenService.getPostToken(); _token = token; if (token != null && token.isNotEmpty) { mobileNo = session.mobileNo; client_branch_id = session.empClientBranchId; empCodeString = session.empCodeString; empName = session.gpaEmpName; logDebug(empCodeString); empPrimaryId = session.empPrimaryId; client_id = session.client_id; await _fetchEmployeePolicyIfNeeded(); } } bool _isGmcOrOpdPolicy(String? policyType) { final type = (policyType ?? '').trim().toUpperCase(); return type == 'GMC' || type == 'OPD' || type == 'GMC - OPD'; } void _applyEmployeePolicyList(List? policyList) { employeeDetails.clear(); if (policyList == null) return; for (final employee in policyList) { if (employee is! Map) continue; employeeDetails.add({ 'id': employee['id']?.toString() ?? employee['employee_id']?.toString() ?? '', 'name': employee['name']?.toString() ?? '', 'relationship': employee['relationship']?.toString() ?? '', 'dob': employee['dob']?.toString() ?? '', 'gender': employee['gender']?.toString() ?? '', if (employee['emp_status'] != null) 'emp_status': employee['emp_status'].toString(), if (employee['status'] != null) 'status': employee['status'].toString(), if (employee['basic_cover_si'] != null) 'basic_cover_si': employee['basic_cover_si'].toString(), if (employee['dependent_effective_date'] != null) 'dependent_effective_date': employee['dependent_effective_date'].toString(), if (employee['date_coverage'] != null) 'date_coverage': employee['date_coverage'].toString(), }); } sortEmployeeDetails(employeeDetails); } Future _fetchEmployeePolicyIfNeeded({bool force = false}) async { final args = widget.arguments; if (args == null || !_isGmcOrOpdPolicy(args['policy_type']?.toString())) { return; } if (!force && (_isLoadingEmployeePolicy || _employeePolicyFetched)) return; final clientPolicyId = args['client_policy_id']?.toString(); if (empPrimaryId == null || empCodeString == null || client_id == null || client_branch_id == null || clientPolicyId == null || clientPolicyId.isEmpty) { logDebug('getEmployeePolicy skipped: missing required params'); return; } if (!mounted) return; setState(() { _isLoadingEmployeePolicy = true; if (force) _employeePolicyFetched = false; }); try { final response = await apiService.getEmployeePolicy( id: empPrimaryId.toString(), empCode: empCodeString.toString(), clientId: client_id.toString(), clientBranchId: client_branch_id.toString(), clientPolicyId: clientPolicyId, ); if (!mounted) return; if (response['status'] == 'success') { final data = response['data']; setState(() { EmployeePolicy = response['EmployeePolicy'] ?? []; _addButtonShow = response['add_button_show'] == true; if (data is Map) { _employeePolicyData = Map.from(data); _mappedFamilyFloaters = data['mapped_family_floaters'] is List ? List.from(data['mapped_family_floaters']) : []; _allowedRelationships = data['allowed_relationships'] is List ? List.from(data['allowed_relationships']) : (data['relationship'] is List ? List.from(data['relationship']) : []); _basicCoverSi = _resolveBasicCoverSi(data); } _applyEmployeePolicyList( EmployeePolicy is List ? EmployeePolicy as List : null, ); _employeePolicyFetched = true; _isLoadingEmployeePolicy = false; }); logDebug('getEmployeePolicy success; add_button_show=$_addButtonShow'); } else { setState(() { _isLoadingEmployeePolicy = false; _employeePolicyFetched = true; }); final message = response['message']?.toString() ?? response['data']?.toString() ?? 'Failed to load employee policy'; ToastHelper.showErrorToast(context, message); logDebug('getEmployeePolicy failed: $message'); } } catch (e) { logDebug('getEmployeePolicy exception: $e'); if (mounted) { setState(() { _isLoadingEmployeePolicy = false; _employeePolicyFetched = true; }); ToastHelper.showErrorToast( context, 'Unable to load dependent details. Please try again.', ); } } } dynamic _resolveBasicCoverSi(Map data) { if (data['family_floaters_of_dependent_and_si_value'] != null) { return data['family_floaters_of_dependent_and_si_value']; } if (data['si_value'] != null) return data['si_value']; if (data['sum_insured'] != null) return data['sum_insured']; if (data['Policy_Terms'] is Map && data['Policy_Terms']['sum_insured'] != null) { return data['Policy_Terms']['sum_insured']; } final floaters = data['mapped_family_floaters']; if (floaters is List) { for (final floater in floaters) { if (floater is Map && floater['is_value_exist'] == true && floater['data'] is Map && floater['data']['basic_cover_si'] != null) { return floater['data']['basic_cover_si']; } } } return argumentsData != null ? argumentsData['si_value'] : null; } bool _isSpouseRelationship(String? relationship) => (relationship ?? '').trim().toLowerCase() == 'spouse'; bool _isChildRelationship(String? relationship) { final value = (relationship ?? '').trim().toLowerCase(); return value == 'son' || value == 'daughter' || value == 'child'; } bool _isWithinLast30Days(DateTime date) { final now = DateTime.now(); final today = DateTime(now.year, now.month, now.day); final selected = DateTime(date.year, date.month, date.day); final diff = today.difference(selected).inDays; return !selected.isAfter(today) && diff <= 30; } String _thirtyDayRuleMessage(String? relationship) { if (_isSpouseRelationship(relationship)) { return 'A dependent can only be added within 30 days of the Marriage Date'; } return 'A dependent can only be added within 30 days of the Birth Date'; } /// Relationship options from empty family slots only (`is_value_exist == false`). List> _availableSlotRelationships() { final result = >[]; final seen = {}; for (final floater in _mappedFamilyFloaters) { if (floater is! Map || floater['is_value_exist'] != false) continue; final data = floater['data']; if (data is! Map) continue; final List relationships = []; if (data['allowed_relationships'] is List) { for (final item in data['allowed_relationships'] as List) { final name = item?.toString().trim() ?? ''; if (name.isNotEmpty) relationships.add(name); } } else { final formType = (data['form_type'] ?? '').toString().toLowerCase(); if (formType == 'spouse') { relationships.add('Spouse'); } else if (formType == 'child') { relationships.addAll(['Son', 'Daughter']); } } for (final relationship in relationships) { final key = relationship.toLowerCase(); if (seen.contains(key)) continue; seen.add(key); result.add({ 'relationship': relationship, 'age_validation': data['age_validation'] ?? {'min': '0', 'max': '99'}, 'form_type': data['form_type'], 'family_floater_key': data['family_floater_key'], 'client_policy_id': data['client_policy_id'], 'basic_cover_si': data['basic_cover_si'] ?? _basicCoverSi, }); } } return result; } String _formatDateForApi(DateTime date) => DateFormat('dd/MM/yyyy').format(date); DateTime? _parseApiDate(String raw) { if (raw.trim().isEmpty) return null; try { if (raw.contains('/')) { return DateFormat('dd/MM/yyyy').parseStrict(raw); } if (raw.contains('-') && raw.length == 10 && raw[4] == '-') { return DateTime.parse(raw); } if (raw.contains('-')) { return DateFormat('dd-MM-yyyy').parseStrict(raw); } return DateFormat('d MMM yyyy').parseStrict(raw); } catch (_) { return null; } } String _normalizeDobForDisplay(String raw) { final parsed = _parseApiDate(raw); if (parsed == null) return raw; return _formatDateForApi(parsed); } Future _pickDate({ required TextEditingController controller, Map? relationshipData, bool isMarriageDate = false, bool restrictToLastThirtyDays = false, String? thirtyDayToastMessage, }) async { final now = DateTime.now(); final today = DateTime(now.year, now.month, now.day); DateTime initial = today; DateTime first = DateTime(1900); DateTime last = today; if (isMarriageDate || restrictToLastThirtyDays) { // Spouse marriage date / Child DOB: only allow last 30 days. first = today.subtract(const Duration(days: 30)); last = today; initial = today; } else if (relationshipData != null) { final age = relationshipData['age_validation']; if (age is Map) { final min = int.tryParse(age['min']?.toString() ?? '') ?? 0; final max = int.tryParse(age['max']?.toString() ?? '') ?? 100; last = DateTime(now.year - min, now.month, now.day); first = DateTime(now.year - max, now.month, now.day); initial = last; } } if (controller.text.isNotEmpty) { final existing = _parseApiDate(controller.text); if (existing != null) initial = existing; } final picked = await showDatePicker( context: context, initialDate: initial.isBefore(first) ? first : (initial.isAfter(last) ? last : initial), firstDate: first, lastDate: last.isBefore(first) ? first : last, ); if (picked == null) return; if ((isMarriageDate || restrictToLastThirtyDays) && !_isWithinLast30Days(picked)) { ToastHelper.showWarningToast( context, thirtyDayToastMessage ?? (isMarriageDate ? 'A dependent can only be added within 30 days of the Marriage Date' : 'A dependent can only be added within 30 days of the Birth Date'), ); return; } controller.text = _formatDateForApi(picked); } void _onAddFamilyMember() { _openDependentForm(action: 'Add'); } void _onEditFamilyMember(Map detail) { _openDependentForm(action: 'Edit', detail: detail); } String _resolveCoverageDateForDetail(Map detail) { final directCoverageDate = detail['date_coverage']?.toString().trim() ?? ''; if (directCoverageDate.isNotEmpty) return directCoverageDate; final directDependentDate = detail['dependent_effective_date']?.toString().trim() ?? ''; if (directDependentDate.isNotEmpty) return directDependentDate; final detailId = detail['id']?.toString().trim() ?? ''; final detailRelationship = (detail['relationship']?.toString().trim().toLowerCase()) ?? ''; for (final floater in _mappedFamilyFloaters) { if (floater is! Map || floater['is_value_exist'] != true) continue; final data = floater['data']; if (data is! Map) continue; final floaterId = data['employee_id']?.toString().trim() ?? ''; final floaterRelationship = (data['relationship']?.toString().trim().toLowerCase()) ?? ''; final idMatches = detailId.isNotEmpty && floaterId == detailId; final relationMatches = detailRelationship.isNotEmpty && floaterRelationship == detailRelationship; if (!idMatches && !relationMatches) continue; final coverage = data['date_coverage']?.toString().trim() ?? ''; if (coverage.isNotEmpty) return coverage; final dependentDate = data['dependent_effective_date']?.toString().trim() ?? ''; if (dependentDate.isNotEmpty) return dependentDate; } return ''; } Future _onDeleteFamilyMember(Map detail) async { final id = detail['id'] ?? ''; final relationship = (detail['relationship'] ?? '').toLowerCase(); if (id.isEmpty || relationship == 'self') { ToastHelper.showWarningToast(context, 'Cannot delete this member'); return; } final confirmed = await showDialog( context: context, builder: (ctx) => AlertDialog( title: Text( 'Delete Dependent', style: GoogleFonts.poppins(fontWeight: FontWeight.w600), ), content: Text( 'Are you sure you want to delete ${detail['name']}?', style: GoogleFonts.poppins(fontSize: 14), ), actions: [ TextButton( onPressed: () => Navigator.pop(ctx, false), child: const Text('Cancel'), ), ElevatedButton( style: ElevatedButton.styleFrom( backgroundColor: const Color(0xFFE26728), ), onPressed: () => Navigator.pop(ctx, true), child: const Text('Delete', style: TextStyle(color: Colors.white)), ), ], ), ); if (confirmed != true) return; try { final response = await apiService.deleteDependence(id); if (!mounted) return; if (response['status'] == 'success') { ToastHelper.showSuccessToast( context, response['message']?.toString() ?? 'Dependent deleted successfully', ); await _fetchEmployeePolicyIfNeeded(force: true); } else { ToastHelper.showErrorToast( context, response['message']?.toString() ?? response['data']?.toString() ?? 'Failed to delete dependent', ); } } catch (e) { logDebug('deleteDependence exception: $e'); if (mounted) { ToastHelper.showErrorToast( context, 'Unable to delete dependent. Please try again.', ); } } } void _openDependentForm({ required String action, Map? detail, }) { final relationshipObjects = action == 'Add' ? _availableSlotRelationships() : >[]; String? dropdownValue; Map? selectedRelationship; _relationShipController.clear(); _memberNameController.clear(); _dobController.clear(); _effectiveDateController.clear(); if (action == 'Edit' && detail != null) { dropdownValue = detail['relationship']; _relationShipController.text = detail['relationship'] ?? ''; _memberNameController.text = detail['name'] ?? ''; _dobController.text = _normalizeDobForDisplay(detail['dob'] ?? ''); if (_isSpouseRelationship(dropdownValue)) { final coverageDate = _resolveCoverageDateForDetail(detail); _effectiveDateController.text = _normalizeDobForDisplay( coverageDate, ); } selectedRelationship = { 'relationship': dropdownValue, 'age_validation': {'min': '0', 'max': '99'}, 'basic_cover_si': detail['basic_cover_si'] ?? _basicCoverSi, }; relationshipObjects.add(selectedRelationship); } if (relationshipObjects.isEmpty && action == 'Add') { ToastHelper.showWarningToast( context, 'No family slots available to add', ); return; } showDialog( context: context, barrierDismissible: false, builder: (dialogContext) { bool isSaving = false; return StatefulBuilder( builder: (context, setDialogState) { final isSpouse = _isSpouseRelationship(dropdownValue); final isChild = _isChildRelationship(dropdownValue); return AlertDialog( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(20), ), content: SizedBox( width: Responsive.isDesktop(context) ? 500 : 350, child: SingleChildScrollView( child: Column( mainAxisSize: MainAxisSize.min, children: [ Text( action == 'Edit' ? 'Edit Family Member' : 'Add Family Member', style: GoogleFonts.poppins( fontSize: 18, fontWeight: FontWeight.bold, color: const Color(0xFFE26728), ), ), const SizedBox(height: 20), DropdownButtonFormField( value: dropdownValue, decoration: const InputDecoration( border: OutlineInputBorder(), labelText: 'Relationship', ), items: relationshipObjects.map((item) { final value = item['relationship']?.toString() ?? ''; return DropdownMenuItem( value: value, child: Text(value), ); }).toList(), onChanged: action == 'Edit' ? null : (value) { setDialogState(() { dropdownValue = value; _relationShipController.text = value ?? ''; selectedRelationship = relationshipObjects.firstWhere( (item) => item['relationship'] == value, ); _dobController.clear(); _effectiveDateController.clear(); }); }, ), const SizedBox(height: 15), TextFormField( controller: _memberNameController, decoration: const InputDecoration( border: OutlineInputBorder(), labelText: 'Member Name As Per Govt Id Proof', ), ), const SizedBox(height: 15), TextFormField( controller: _dobController, readOnly: true, onTap: isSaving || dropdownValue == null ? null : () async { await _pickDate( controller: _dobController, relationshipData: selectedRelationship, restrictToLastThirtyDays: isChild, ); setDialogState(() {}); }, decoration: InputDecoration( border: const OutlineInputBorder(), labelText: 'Date of Birth', suffixIcon: IconButton( icon: const Icon(Icons.calendar_today), onPressed: isSaving || dropdownValue == null ? null : () async { await _pickDate( controller: _dobController, relationshipData: selectedRelationship, restrictToLastThirtyDays: isChild, ); setDialogState(() {}); }, ), ), ), if (isSpouse) ...[ const SizedBox(height: 15), TextFormField( controller: _effectiveDateController, readOnly: true, onTap: isSaving ? null : () async { await _pickDate( controller: _effectiveDateController, isMarriageDate: true, thirtyDayToastMessage: _thirtyDayRuleMessage(dropdownValue), ); setDialogState(() {}); }, decoration: InputDecoration( border: const OutlineInputBorder(), labelText: 'Date of Wedding', suffixIcon: IconButton( icon: const Icon(Icons.calendar_today), onPressed: isSaving ? null : () async { await _pickDate( controller: _effectiveDateController, isMarriageDate: true, thirtyDayToastMessage: _thirtyDayRuleMessage( dropdownValue), ); setDialogState(() {}); }, ), ), ), ], const SizedBox(height: 20), Row( children: [ Expanded( child: OutlinedButton( onPressed: isSaving ? null : () => Navigator.pop(dialogContext), child: const Text('Cancel'), ), ), const SizedBox(width: 10), Expanded( child: ElevatedButton( style: ElevatedButton.styleFrom( backgroundColor: const Color(0xFFE26728), ), onPressed: isSaving ? null : () async { if (dropdownValue == null || _memberNameController.text .trim() .isEmpty || _dobController.text.trim().isEmpty || (isSpouse && _effectiveDateController.text .trim() .isEmpty)) { ToastHelper.showWarningToast( context, 'All fields are required', ); return; } final dob = _parseApiDate( _dobController.text.trim()); if (isChild && (dob == null || !_isWithinLast30Days(dob))) { ToastHelper.showWarningToast( context, _thirtyDayRuleMessage(dropdownValue), ); return; } if (isSpouse) { final marriageDate = _parseApiDate( _effectiveDateController.text.trim(), ); if (marriageDate == null || !_isWithinLast30Days( marriageDate)) { ToastHelper.showWarningToast( context, _thirtyDayRuleMessage( dropdownValue), ); return; } } setDialogState(() => isSaving = true); final saved = await _saveDependent( action: action, relationship: dropdownValue!, detail: detail, selectedSlot: selectedRelationship, ); if (!dialogContext.mounted) return; setDialogState(() => isSaving = false); if (saved) { Navigator.pop(dialogContext); } }, child: isSaving ? const SizedBox( width: 18, height: 18, child: CircularProgressIndicator( strokeWidth: 2, color: Colors.white, ), ) : const Text( 'Save', style: TextStyle(color: Colors.white), ), ), ), ], ), ], ), ), ), ); }, ); }, ); } Future _saveDependent({ required String action, required String relationship, Map? detail, Map? selectedSlot, }) async { final clientPolicyId = selectedSlot?['client_policy_id']?.toString() ?? _employeePolicyData?['ClientPolicyId']?.toString() ?? widget.arguments?['client_policy_id']?.toString() ?? ''; if (empCodeString == null || client_id == null || client_branch_id == null || clientPolicyId.isEmpty) { ToastHelper.showErrorToast(context, 'Missing required policy details'); return false; } final si = selectedSlot?['basic_cover_si'] ?? detail?['basic_cover_si'] ?? _basicCoverSi ?? argumentsData?['si_value'] ?? 0; final dependentEffectiveDate = _isSpouseRelationship(relationship) ? _effectiveDateController.text.trim() : _dobController.text.trim(); final body = { 'emp_code': empCodeString, 'client_id': int.tryParse(client_id.toString()) ?? client_id, 'client_branch_id': int.tryParse(client_branch_id.toString()) ?? client_branch_id, 'client_policy_id': int.tryParse(clientPolicyId) ?? clientPolicyId, 'name': _memberNameController.text.trim(), 'relationship': relationship, 'dob': _dobController.text.trim(), 'basic_cover_si': int.tryParse(si.toString()) ?? si, 'dependent_effective_date': dependentEffectiveDate, if (action == 'Edit' && (detail?['id']?.isNotEmpty ?? false)) 'id': int.tryParse(detail!['id']!) ?? detail['id'], }; try { final response = await apiService.addEmployeeAndDependence([body]); if (!mounted) return false; if (response['status'] == 'success') { ToastHelper.showSuccessToast( context, response['message']?.toString() ?? 'Dependent submitted for approval', ); await _fetchEmployeePolicyIfNeeded(force: true); return true; } ToastHelper.showErrorToast( context, response['message']?.toString() ?? response['data']?.toString() ?? 'Failed to save dependent', ); return false; } catch (e) { logDebug('addEmployeeAndDependence exception: $e'); if (mounted) { ToastHelper.showErrorToast( context, 'Unable to save dependent. Please try again.', ); } return false; } } Future getEcardDownload(id) async { if (_isDownloadingEcard) return; if (!mounted) return; setState(() => _isDownloadingEcard = true); try { final clientPolicyID = widget.arguments?['client_policy_id']; final policyNo = widget.arguments?['policy_no']; final eCarDParams = { 'id': id, 'emp_code': empCodeString, 'client_policy_id': clientPolicyID, 'policy_no': policyNo, }; final response = await apiService.getEcardRequest(eCarDParams); if (!mounted) return; final data = response['data']; if (data is! Map) { ToastHelper.showErrorToast(context, 'Unable to download E-Card'); return; } final ecardDownloadUrl = data['eCardDownload']; final message = data['message']?.toString(); if (ecardDownloadUrl == null || ecardDownloadUrl.toString().isEmpty) { logDebug('❌ Error: $message'); ToastHelper.showErrorToast( context, message ?? 'E-Card is not available', ); return; } final url = ecardDownloadUrl.toString(); logDebug('✅ E-Card URL: $url'); final safePolicyNo = policyNo?.toString().replaceAll(RegExp(r'[^\w\-.]'), '_'); final fileName = safePolicyNo == null || safePolicyNo.isEmpty ? 'ecard.pdf' : 'ecard_$safePolicyNo.pdf'; if (!kIsWeb) { CustomDownloadSnackbar.show( context, message: 'Downloading eCard...', ); } final result = await _ecardDownloadService.downloadEcard( url: url, fileName: fileName, headers: { 'APP-SIGNATURE': 'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y', if (_token != null && _token.toString().isNotEmpty) 'Authorization': 'Bearer ${_token.toString()}', }, ); if (!mounted) return; if (result.success) { if (kIsWeb) { ToastHelper.showSuccessToast( context, result.message ?? 'E-Card downloaded', ); } else { CustomDownloadSnackbar.show( context, message: result.message ?? 'eCard downloaded successfully', ); if (result.savedPath != null && result.savedPath!.isNotEmpty) { await EcardDownloadNotificationService.showDownloadCompleted( filePath: result.savedPath!, fileName: fileName, ); } } } else { if (kIsWeb) { ToastHelper.showErrorToast( context, result.message ?? 'Could not download E-Card', ); } else { CustomDownloadSnackbar.show( context, message: result.message ?? 'Download failed', ); } } } catch (e) { logDebug('E-Card download error: $e'); if (mounted) { ToastHelper.showErrorToast( context, 'Unable to download E-Card. Please try again.', ); } } finally { if (mounted) setState(() => _isDownloadingEcard = false); } } String convertDateFormat(String date) { // Define input and output date formats DateFormat inputFormat = DateFormat('dd-MMM-yyyy'); DateFormat outputFormat = DateFormat('d MMM yyyy'); // Parse the input date string DateTime parsedDate = inputFormat.parse(date); // Format the parsed date to the desired output format String formattedDate = outputFormat.format(parsedDate); return formattedDate; } String formatDob(String dob) { try { if (dob.contains('/')) { final date = DateFormat('dd/MM/yyyy').parseStrict(dob); return DateFormat('d MMM yyyy').format(date); } final date = DateTime.parse(dob); // input format: yyyy-MM-dd return DateFormat("d MMM yyyy").format(date); // output: 18 Jul 1988 } catch (e) { return dob; // fallback in case of error } } Future _openUrl(String url) async { final uri = Uri.tryParse(url); if (uri == null) { logDebug('Could not launch URL'); return; } if (await canLaunchUrl(uri)) { await launchUrl(uri, mode: LaunchMode.externalApplication); } else { logDebug('Could not launch URL'); } } DateTime _parseDob(String dob) { try { // Expected format: dd-MM-yyyy final parts = dob.split('-'); if (parts.length == 3) { return DateTime( int.parse(parts[2]), int.parse(parts[1]), int.parse(parts[0]), ); } } catch (_) {} return DateTime(1900); // fallback } List> sortEmployeeDetails( List> list) { int priority(String r) { final rel = r.toLowerCase(); if (rel == 'self') return 1; if (rel == 'spouse') return 2; if (rel == 'son' || rel == 'daughter' || rel == 'child') return 3; if (rel == 'father') return 4; if (rel == 'mother') return 5; if (rel == 'father in law') return 6; if (rel == 'mother in law') return 7; return 99; } list.sort((a, b) { final p1 = priority(a['relationship'] ?? ''); final p2 = priority(b['relationship'] ?? ''); // main relationship order if (p1 != p2) return p1.compareTo(p2); // ONLY children → oldest first if (p1 == 3) { final d1 = parseDobSafe(a['dob'] ?? ''); final d2 = parseDobSafe(b['dob'] ?? ''); return d1.compareTo(d2); // older first } return 0; }); return list; } DateTime parseDobSafe(String dob) { try { // Case 1: yyyy-MM-dd → 2015-05-05 if (dob.contains('-') && dob.length == 10) { return DateTime.parse(dob); } // Case 2: d MMM yyyy → 1 Feb 1996 final parts = dob.split(' '); final day = int.parse(parts[0]); final monthMap = { 'Jan': 1, 'Feb': 2, 'Mar': 3, 'Apr': 4, 'May': 5, 'Jun': 6, 'Jul': 7, 'Aug': 8, 'Sep': 9, 'Oct': 10, 'Nov': 11, 'Dec': 12, }; final month = monthMap[parts[1]]!; final year = int.parse(parts[2]); return DateTime(year, month, day); } catch (e) { // fallback → push invalid dates to bottom return DateTime(3000); } } @override Widget build(BuildContext context) { final arguments = widget.arguments; logDebug('arguments'); logDebug(arguments); if (arguments != null && arguments is Map) { argumentsData = arguments; if (!_argsInitialized) { _argsInitialized = true; final policyType = argumentsData['policy_type']?.toString(); ECardHide = policyType == 'GMC' || policyType == 'GMC - Parents' || policyType == 'GMC - Topup' || policyType == 'GMC - Topup(Parents)'; // Seed from route args until getEmployeePolicy returns (GMC/OPD), // or keep args as source of truth for other policy types. if (!_employeePolicyFetched && argumentsData.containsKey('EmployeePolicy') && argumentsData['EmployeePolicy'] is List) { _applyEmployeePolicyList(argumentsData['EmployeePolicy'] as List); } if (_isGmcOrOpdPolicy(policyType) && !_employeePolicyFetched && !_isLoadingEmployeePolicy) { WidgetsBinding.instance.addPostFrameCallback((_) { _fetchEmployeePolicyIfNeeded(); }); } } logDebug('employeeDetails'); logDebug(employeeDetails); } return PopScope( canPop: false, onPopInvokedWithResult: (didPop, result) { if (didPop) return; // context.go('/home'); 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.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(0), color: Responsive.isDesktop(context) ? Colors.white : Color(0xFFFFFCE5), child: Column(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: 25, right: 25) : EdgeInsets.only(top: 10, bottom: 10, left: 10, right: 10), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Row( mainAxisAlignment: MainAxisAlignment.start, children: [ Expanded( flex: 1, child: InkWell( onTap: () { // context.go('/home'); context.pop(); }, 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( argumentsData['policy_name'] ?? '', textAlign: TextAlign.start, style: GoogleFonts.poppins( fontSize: Responsive.isDesktop(context) ? 20 : 16, fontWeight: FontWeight.w600, color: Color(0xFF000000), ), ) ], ), ) ], ), SizedBox( height: Responsive.isDesktop(context) ? 20 : 16), _buildPolicyDetailsSection(context), SizedBox( height: Responsive.isDesktop(context) ? 32 : 24), _buildPolicyActionButtons(context), SizedBox(height: Responsive.isDesktop(context) ? 30 : 20), // Add more rows as needed ], ), ), Container( decoration: BoxDecoration( color: Colors.white, // Set background color for the container borderRadius: BorderRadius.only( topLeft: Radius.circular(40.0), topRight: Radius.circular(40.0), ), // Set border radius for the container ), padding: Responsive.isDesktop(context) ? EdgeInsets.only(top: 15, bottom: 15, left: 0, right: 0) : EdgeInsets.only(top: 20, bottom: 10, left: 10, right: 10), child: Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ Card( elevation: 0, shape: RoundedRectangleBorder( side: BorderSide( color: Color(0xFFD9D9D9), // Set the border color here width: 1.0, // Set the border width here ), borderRadius: BorderRadius.circular( 8.0), // Set the border radius here ), child: Column(children: [ Container( decoration: BoxDecoration( color: Color( 0xFFECF2FF), // Set background color for the container borderRadius: BorderRadius.only( topLeft: Radius.circular(10), topRight: Radius.circular( 10)), // Set border radius for the container ), padding: Responsive.isDesktop(context) ? EdgeInsets.only( top: 15, bottom: 15, left: 15, right: 15) : EdgeInsets.only( top: 20, bottom: 20, left: 10, right: 10), child: Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ Expanded( flex: 12, child: Container( alignment: Alignment.centerLeft, child: Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Insured Members', textAlign: TextAlign.start, style: GoogleFonts.poppins( fontSize: Responsive.isDesktop(context) ? 18 : 12, color: Color(0xFF404040), fontWeight: FontWeight.w400, ), ), ], ))), ], ), ), Container( decoration: BoxDecoration( color: Colors.white, ), padding: Responsive.isDesktop(context) ? EdgeInsets.all(30) : EdgeInsets.only( top: 15, bottom: 15, left: 20, right: 20), child: Column( children: [ if (_isLoadingEmployeePolicy) const Padding( padding: EdgeInsets.symmetric(vertical: 16), child: Center( child: SizedBox( width: 24, height: 24, child: CircularProgressIndicator( strokeWidth: 2, color: Color(0xFFE26728), ), ), ), ), ...employeeDetails.map((detail) { int index = employeeDetails.indexOf(detail); final isPending = (detail['emp_status'] ?? '') .toLowerCase() == 'pending_approval'; final isSelf = (detail['relationship'] ?? '') .toLowerCase() == 'self'; final canManageDependent = _employeePolicyFetched && _isGmcOrOpdPolicy( argumentsData != null ? argumentsData['policy_type'] ?.toString() : null, ) && !isSelf && isPending; return Container( decoration: BoxDecoration( border: Border( bottom: index != employeeDetails.length - 1 ? BorderSide( color: Color(0xFFD9D9D9), width: 1.0, // Adjust the width as needed ) : BorderSide.none, ), ), child: Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ Expanded( flex: 6, child: Container( alignment: Alignment.centerLeft, padding: Responsive.isDesktop(context) ? EdgeInsets.only( top: 10, bottom: 10, left: 10, right: 10) : EdgeInsets.only( top: 15, bottom: 15, left: 7, right: 7), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ // 👇 SVG icon based on gender if (detail['gender'] == 'M') SvgPicture.string( SvgService.getSvg('personMale'), width: 25, height: 25, ) else if (detail['gender'] == 'F') SvgPicture.string( SvgService.getSvg('personFemale'), width: 25, height: 25, ), const SizedBox(width: 6), // Name text Flexible( child: Text( detail['name']!, textAlign: TextAlign.start, style: GoogleFonts.poppins( fontSize: Responsive.isDesktop(context) ? 16 : 14, color: const Color(0xFF000000), fontWeight: FontWeight.w400, ), ), ), if (isPending) ...[ const SizedBox(width: 8), Container( padding: const EdgeInsets.symmetric( horizontal: 8, vertical: 2, ), decoration: BoxDecoration( color: const Color(0xFFFFF1DD), borderRadius: BorderRadius.circular(4), ), child: Text( 'Pending', style: GoogleFonts.poppins( fontSize: 10, color: const Color(0xFFE26728), fontWeight: FontWeight.w500, ), ), ), ], ], ), const SizedBox(height: 4), // DOB text Text( "DOB: ${formatDob(detail['dob']!) ?? '-'}", style: GoogleFonts.poppins( fontSize: Responsive.isDesktop(context) ? 14 : 12, color: const Color(0xFF606060), fontWeight: FontWeight.w300, ), ), ], ) ), ), Expanded( flex: 6, child: Container( alignment: Alignment.centerRight, padding: Responsive.isDesktop(context) ? EdgeInsets.only( top: 10, bottom: 10, left: 10, right: 10) : EdgeInsets.only( top: 15, bottom: 15, left: 7, right: 7), child: Column( crossAxisAlignment: CrossAxisAlignment.end, children: [ Row( mainAxisAlignment: MainAxisAlignment.end, children: [ if (canManageDependent) ...[ MouseRegion( cursor: SystemMouseCursors .click, child: GestureDetector( onTap: () => _onEditFamilyMember( detail), child: const Tooltip( message: 'Edit', child: Icon( Icons.edit, size: 20, color: Color( 0xFFE26728), ), ), ), ), const SizedBox(width: 10), MouseRegion( cursor: SystemMouseCursors .click, child: GestureDetector( onTap: () => _onDeleteFamilyMember( detail), child: const Tooltip( message: 'Delete', child: Icon( Icons.delete, size: 20, color: Color( 0xFFE26728), ), ), ), ), ], if (ECardHide && !isPending) ...[ if (canManageDependent) const SizedBox(width: 10), MouseRegion( cursor: SystemMouseCursors .click, child: GestureDetector( onTap: _isDownloadingEcard ? null : () { getEcardDownload( detail[ 'id']); }, child: Tooltip( message: 'E-Card', waitDuration: const Duration( milliseconds: 300), showDuration: const Duration( seconds: 2), child: SvgPicture.string( SvgService.getSvg( 'ECard'), width: 25, height: 25, ), ), ), ), ], ], ), if (canManageDependent || (ECardHide && !isPending)) const SizedBox(height: 4), Text( detail['relationship']!, textAlign: TextAlign.end, style: GoogleFonts.poppins( fontSize: Responsive.isDesktop(context) ? 16 : 14, color: Color(0xFF404040), fontWeight: FontWeight.w400, ), ), ], ) ), ), ], )); }).toList(), if (_addButtonShow && _isGmcOrOpdPolicy( argumentsData != null ? argumentsData['policy_type'] ?.toString() : null)) ...[ if (employeeDetails.isNotEmpty) const SizedBox(height: 12), MouseRegion( cursor: SystemMouseCursors.click, child: GestureDetector( onTap: _onAddFamilyMember, child: Container( width: double.infinity, padding: EdgeInsets.symmetric( vertical: Responsive.isDesktop(context) ? 14 : 12, horizontal: 12, ), decoration: BoxDecoration( border: Border.all( color: const Color(0xFF000000), width: 1, ), borderRadius: BorderRadius.circular(8), ), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ const Icon( Icons.person_add, color: Color(0xFF000000), size: 22, ), const SizedBox(width: 10), Text( 'Add Family Member', style: GoogleFonts.poppins( fontSize: Responsive.isDesktop(context) ? 16 : 14, fontWeight: FontWeight.w500, color: const Color(0xFF000000), ), ), ], ), ), ), ), ], ], ), ) ])), SizedBox(height: 10), Card( elevation: 0, shape: RoundedRectangleBorder( side: BorderSide( color: Color(0xFFD9D9D9), // Set the border color here width: 1.0, // Set the border width here ), borderRadius: BorderRadius.circular( 8.0), // Set the border radius here ), child: Column(children: [ InkWell( onTap: _toggleExpansion, child: Container( decoration: BoxDecoration( color: Colors .white, // Set background color for the container ), padding: Responsive.isDesktop(context) ? EdgeInsets.all(15) : EdgeInsets.symmetric( vertical: 20, horizontal: 10), child: Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ Expanded( flex: 8, child: Row( mainAxisAlignment: MainAxisAlignment.start, children: [ SvgPicture.string( SvgService.getSvg('policyCoverage'), width: 25, height: 25, ), SizedBox( width: 10), // Adjust space between icon and text Text( 'Policy coverage', textAlign: TextAlign.center, style: GoogleFonts.poppins( fontSize: Responsive.isDesktop(context) ? 18 : 12, fontWeight: FontWeight.w400, color: Color(0xFF404040), ), ), ], ), ), Expanded( flex: 4, child: Row( mainAxisAlignment: MainAxisAlignment.end, children: [ Icon( _isExpanded ? Icons.remove : Icons.add, color: Color(0xFFE26728), size: 25, ), ], ), ), ], ), ), ), if (_isExpanded) Builder( builder: (context) { final terms = argumentsData['policy_terms']; if (terms == null || (terms is List && terms.isEmpty) || (terms is Map && terms.isEmpty)) { // ✅ Show toast once WidgetsBinding.instance.addPostFrameCallback((_) { ToastHelper.showInfoToast(context, 'No policy terms available'); }); return const SizedBox(); // Don't render container } return Container( decoration: const BoxDecoration( color: Colors.white, ), padding: Responsive.isDesktop(context) ? const EdgeInsets.all(30) : const EdgeInsets.symmetric(vertical: 15, horizontal: 20), child: SingleChildScrollView( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: (() { if (terms is Map) { // ✅ Case 1: Map return terms.entries .where((entry) => entry.value is String) .toList() .asMap() .entries .map((entry) { int index = entry.key; MapEntry value = entry.value; bool isLast = index == terms.length - 1; return Container( width: double.infinity, decoration: BoxDecoration( border: isLast ? null : const Border( bottom: BorderSide( color: Color(0xFFD9D9D9), width: 1.0, ), ), ), child: Padding( padding: const EdgeInsets.symmetric(vertical: 8.0), child: Responsive.isDesktop(context) ? Row( children: [ Expanded( flex: 6, child: Text( value.key, style: GoogleFonts.poppins( fontSize: 14, fontWeight: FontWeight.w600, color: const Color(0xFF777777), ), ), ), Expanded( flex: 6, child: Text( value.value, textAlign: TextAlign.end, style: GoogleFonts.poppins( fontSize: 16, fontWeight: FontWeight.w400, color: const Color(0xFF000000), ), ), ), ], ) : Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( value.key, style: GoogleFonts.poppins( fontSize: 14, fontWeight: FontWeight.w600, color: const Color(0xFF777777), ), ), const SizedBox(height: 4), Text( value.value, style: GoogleFonts.poppins( fontSize: 16, fontWeight: FontWeight.w400, color: const Color(0xFF000000), ), ), ], ), ), ); }).toList(); } else if (terms is List) { // ✅ Case 2: List return terms.map((item) { return Padding( padding: const EdgeInsets.symmetric(vertical: 6.0), child: Text( item.toString(), style: GoogleFonts.poppins( fontSize: 16, fontWeight: FontWeight.w400, color: const Color(0xFF000000), ), ), ); }).toList(); } else { return [const SizedBox()]; } })(), ), ), ); }, ), ])), SizedBox(height: 10), MouseRegion( cursor: SystemMouseCursors.click, child: GestureDetector( onTap: () => { context.push('/generalExclusionsDeductibles') }, child: Card( elevation: 0, shape: RoundedRectangleBorder( side: BorderSide( color: Color(0xFFD9D9D9), // Set the border color here width: 1.0, // Set the border width here ), borderRadius: BorderRadius.circular( 8.0), // Set the border radius here ), child: Column(children: [ Container( decoration: BoxDecoration( color: Colors .white, // Set background color for the container ), padding: Responsive.isDesktop(context) ? EdgeInsets.only( top: 15, bottom: 15, left: 15, right: 15) : EdgeInsets.only( top: 20, bottom: 20, left: 10, right: 10), child: Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ Expanded( flex: 11, child: Row( mainAxisAlignment: MainAxisAlignment.start, children: [ SvgPicture.string( SvgService.getSvg('general'), width: 25, height: 25, ), SizedBox( width: 10), // Adjust space between icon and text Text( 'General Exclusions & Deductibles', textAlign: TextAlign.center, style: GoogleFonts.poppins( fontSize: Responsive.isDesktop(context) ? 18 : 12, fontWeight: FontWeight.w400, color: Color(0xFF404040), ), ), ], ), ), Expanded( flex: 1, child: Row( mainAxisAlignment: MainAxisAlignment.end, children: [ Icon( Icons .chevron_right, // Replace with your desired icon color: Color(0xFFE26728), size: 20, ), ], ), ), ], ), ), ]), ), ), ), SizedBox(height: 10), Card( elevation: 0, shape: RoundedRectangleBorder( side: BorderSide( color: Color(0xFFD9D9D9), // Set the border color here width: 1.0, // Set the border width here ), borderRadius: BorderRadius.circular( 8.0), // Set the border radius here ), child: Column(children: [ Container( decoration: BoxDecoration( color: Colors.white, // 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: 6, child: MouseRegion( cursor: SystemMouseCursors.click, child: GestureDetector( onTap: () { context.push('/help'); }, child: Container( alignment: Alignment.center, child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ SvgPicture.string( SvgService.getSvg('needHelp'), width: 25, height: 25, ), SizedBox(height: 10), Text( 'Need Help', textAlign: TextAlign.center, style: GoogleFonts.poppins( fontSize: Responsive.isDesktop(context) ? 14 : 10, fontWeight: FontWeight.w400, color: Color(0xFF404040), ), ), ], ), ), ), ), ), Expanded( flex: 6, child: MouseRegion( cursor: SystemMouseCursors.click, child: GestureDetector( onTap: () { context.push('/claims', extra: 0); }, child: Container( alignment: Alignment.center, child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ SvgPicture.string( SvgService.getSvg( 'cliamHistory'), width: 25, height: 25, ), SizedBox(height: 10), Text( 'Claims history', textAlign: TextAlign.center, style: GoogleFonts.poppins( fontSize: Responsive.isDesktop( context) ? 14 : 10, fontWeight: FontWeight.w400, color: Color(0xFF404040), ), ), ], ))), ), ), ], ), ), ])), SizedBox(height: 10), if (argumentsData['network_hospitals_url'] != null) MouseRegion( cursor: SystemMouseCursors.click, child: GestureDetector( onTap: () { final String? url = argumentsData['network_hospitals_url']; if (url != null && url.isNotEmpty) { _openUrl(url); } else { logDebug('No URL provided'); } }, child: Card( elevation: 0, shape: RoundedRectangleBorder( side: BorderSide( color: Color(0xFFD9D9D9), width: 1.0, ), borderRadius: BorderRadius.circular(8.0), ), child: Column( children: [ Container( decoration: BoxDecoration( color: Colors.white, ), padding: Responsive.isDesktop(context) ? EdgeInsets.only( top: 15, bottom: 15, left: 15, right: 15) : EdgeInsets.only( top: 20, bottom: 20, left: 10, right: 10), child: Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ Expanded( flex: 11, child: Row( mainAxisAlignment: MainAxisAlignment.start, children: [ SvgPicture.string( SvgService.getSvg('cashLess'), width: 25, height: 25, ), SizedBox(width: 10), Text( 'Cashless Hospitals', textAlign: TextAlign.center, style: GoogleFonts.poppins( fontSize: Responsive.isDesktop(context) ? 18 : 12, fontWeight: FontWeight.w400, color: Color(0xFF404040), ), ), ], ), ), Expanded( flex: 1, child: Row( mainAxisAlignment: MainAxisAlignment.end, children: [ Icon( Icons.chevron_right, color: Color(0xFFE26728), size: 20, ), ], ), ), ], ), ), ], ), ), ), ), SizedBox(height: Responsive.isDesktop(context) ? 40 : 70), ], ), ), ]), )), 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: 0, // Initial index of the bottom navigation bar ), ) ); } TextStyle _policyLabelStyle(BuildContext context) { return GoogleFonts.poppins( fontSize: Responsive.isDesktop(context) ? 14 : 13, fontWeight: FontWeight.w400, color: const Color(0xFF777777), ); } TextStyle _policyValueStyle(BuildContext context) { return GoogleFonts.poppins( fontSize: Responsive.isDesktop(context) ? 18 : 15, fontWeight: FontWeight.w600, color: const Color(0xFF000000), ); } String _policyFieldValue(dynamic value) { final text = value?.toString().trim() ?? ''; return text.isEmpty ? '-' : text; } String _formattedPolicyExpiry() { final raw = argumentsData['policy_end_date']?.toString() ?? ''; if (raw.isEmpty) return '-'; try { return convertDateFormat(raw); } catch (_) { return raw; } } String _formattedSiValue() { final raw = _policyFieldValue(argumentsData['si_value']); if (raw == '-') return raw; return '₹ $raw'; } Widget _buildPolicyDetailField( BuildContext context, String label, String value, ) { return SizedBox( width: double.infinity, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( label, textAlign: TextAlign.start, style: _policyLabelStyle(context), ), SizedBox(height: Responsive.isDesktop(context) ? 8 : 6), Text( value, textAlign: TextAlign.start, softWrap: true, style: _policyValueStyle(context), ), ], ), ); } Widget _buildPolicyDetailDivider() { return const Padding( padding: EdgeInsets.symmetric(vertical: 14), child: Divider( height: 1, thickness: 1, color: Color(0xFFD9D9D9), ), ); } Widget _buildPolicyDetailsCard(BuildContext context, Widget child) { return Container( width: double.infinity, padding: EdgeInsets.all(Responsive.isDesktop(context) ? 24 : 16), decoration: BoxDecoration( color: const Color(0xFFFFFCE5), borderRadius: BorderRadius.circular(12), border: Border.all(color: const Color(0xFFD9D9D9)), ), child: child, ); } Widget _buildPolicyDetailsSection(BuildContext context) { final isDesktop = Responsive.isDesktop(context); final siLabel = _policyFieldValue(argumentsData['sum_insured_label'] ?? 'Sum Insured'); if (isDesktop) { return _buildPolicyDetailsCard( context, Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ IntrinsicHeight( child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Expanded( child: _buildPolicyDetailField( context, 'Policy No', _policyFieldValue(argumentsData['policy_no']), ), ), const SizedBox(width: 24), Expanded( child: _buildPolicyDetailField( context, siLabel, _formattedSiValue(), ), ), const SizedBox(width: 24), Expanded( child: _buildPolicyDetailField( context, 'Policy Expiry', _formattedPolicyExpiry(), ), ), ], ), ), _buildPolicyDetailDivider(), _buildPolicyDetailField( context, 'Insurer', _policyFieldValue(argumentsData['insurer_name']), ), ], ), ); } return _buildPolicyDetailsCard( context, Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ _buildPolicyDetailField( context, 'Policy No', _policyFieldValue(argumentsData['policy_no']), ), _buildPolicyDetailDivider(), _buildPolicyDetailField( context, siLabel, _formattedSiValue(), ), _buildPolicyDetailDivider(), _buildPolicyDetailField( context, 'Policy Expiry', _formattedPolicyExpiry(), ), _buildPolicyDetailDivider(), _buildPolicyDetailField( context, 'Insurer', _policyFieldValue(argumentsData['insurer_name']), ), ], ), ); } Widget _buildPolicyActionButton({ required BuildContext context, required String label, required String svgKey, required VoidCallback? onPressed, bool expanded = false, }) { final button = Material( elevation: 0, borderRadius: BorderRadius.circular(8), color: const Color(0xFFFFF8BF), child: SizedBox( height: 48, width: Responsive.isDesktop(context) ? 220 : double.infinity, child: TextButton( onPressed: onPressed, style: TextButton.styleFrom( padding: const EdgeInsets.symmetric(horizontal: 16), ), child: Row( mainAxisAlignment: MainAxisAlignment.center, mainAxisSize: MainAxisSize.min, children: [ SvgPicture.string( SvgService.getSvg(svgKey), width: 24, height: 24, ), const SizedBox(width: 8), Flexible( child: Text( label, textAlign: TextAlign.center, style: GoogleFonts.poppins( fontSize: Responsive.isDesktop(context) ? 16 : 14, fontWeight: FontWeight.w600, color: const Color(0xFF000000), ), ), ), ], ), ), ), ); if (expanded) { return Expanded(child: button); } return button; } Widget _buildPolicyActionButtons(BuildContext context) { final isDesktop = Responsive.isDesktop(context); if (isDesktop) { return Row( mainAxisAlignment: MainAxisAlignment.center, children: [ if (ECardHide) ...[ _buildPolicyActionButton( context: context, label: 'E-Card', svgKey: 'ECard', onPressed: _isDownloadingEcard ? null : () => getEcardDownload(empPrimaryId), ), const SizedBox(width: 16), ], _buildPolicyActionButton( context: context, label: 'Initiate a Claim', svgKey: 'fileaclaims', onPressed: () => context.push('/claims'), ), ], ); } return Column( children: [ _buildPolicyActionButton( context: context, label: 'Initiate a Claim', svgKey: 'fileaclaims', onPressed: () => context.push('/claims'), ), if (ECardHide) ...[ const SizedBox(height: 12), _buildPolicyActionButton( context: context, label: 'E-Card', svgKey: 'ECard', onPressed: _isDownloadingEcard ? null : () => getEcardDownload(empPrimaryId), ), ], ], ); } }