2459 lines
103 KiB
Dart
Executable File
2459 lines
103 KiB
Dart
Executable File
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<String, dynamic>? arguments;
|
|
const policies({super.key, this.arguments});
|
|
|
|
@override
|
|
State<policies> createState() => _policiesState();
|
|
}
|
|
|
|
class _policiesState extends State<policies> {
|
|
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<Map<String, String>> employeeDetails = [];
|
|
dynamic argumentsData;
|
|
bool ECardHide = true;
|
|
bool _isExpanded = false;
|
|
bool _isDownloadingEcard = false;
|
|
bool _isLoadingEmployeePolicy = false;
|
|
bool _employeePolicyFetched = false;
|
|
bool _addButtonShow = false;
|
|
List<dynamic> _mappedFamilyFloaters = [];
|
|
List<dynamic> _allowedRelationships = [];
|
|
dynamic _basicCoverSi;
|
|
Map<String, dynamic>? _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<void> _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<void> _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<String, dynamic>.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<Map<String, dynamic>> _availableSlotRelationships() {
|
|
final result = <Map<String, dynamic>>[];
|
|
final seen = <String>{};
|
|
|
|
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<String> 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<void> _pickDate({
|
|
required TextEditingController controller,
|
|
Map<String, dynamic>? 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<String, String> detail) {
|
|
_openDependentForm(action: 'Edit', detail: detail);
|
|
}
|
|
|
|
String _resolveCoverageDateForDetail(Map<String, String> 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<void> _onDeleteFamilyMember(Map<String, String> 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<bool>(
|
|
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<String, String>? detail,
|
|
}) {
|
|
final relationshipObjects = action == 'Add'
|
|
? _availableSlotRelationships()
|
|
: <Map<String, dynamic>>[];
|
|
|
|
String? dropdownValue;
|
|
Map<String, dynamic>? 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<String>(
|
|
value: dropdownValue,
|
|
decoration: const InputDecoration(
|
|
border: OutlineInputBorder(),
|
|
labelText: 'Relationship',
|
|
),
|
|
items: relationshipObjects.map((item) {
|
|
final value = item['relationship']?.toString() ?? '';
|
|
return DropdownMenuItem<String>(
|
|
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<bool> _saveDependent({
|
|
required String action,
|
|
required String relationship,
|
|
Map<String, String>? detail,
|
|
Map<String, dynamic>? 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 = <String, dynamic>{
|
|
'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<void> 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<void> _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<Map<String, String>> sortEmployeeDetails(
|
|
List<Map<String, String>> 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<String, dynamic>) {
|
|
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<String, dynamic>
|
|
return terms.entries
|
|
.where((entry) => entry.value is String)
|
|
.toList()
|
|
.asMap()
|
|
.entries
|
|
.map<Widget>((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<Widget>((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),
|
|
),
|
|
],
|
|
],
|
|
);
|
|
}
|
|
} |