import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:mohem_flutter_app/api/profile_api_client.dart'; import 'package:mohem_flutter_app/app_state/app_state.dart'; import 'package:mohem_flutter_app/classes/utils.dart'; import 'package:mohem_flutter_app/config/routes.dart'; import 'package:mohem_flutter_app/extensions/string_extensions.dart'; import 'package:mohem_flutter_app/generated/locale_keys.g.dart'; import 'package:mohem_flutter_app/models/dashboard/menu_entries.dart'; import 'package:mohem_flutter_app/models/get_employee_contacts.model.dart'; import 'package:mohem_flutter_app/provider/dashboard_provider_model.dart'; import 'package:mohem_flutter_app/ui/profile/add_update_family_member.dart'; import 'package:mohem_flutter_app/ui/profile/mazaya_access_result_screen.dart'; import 'package:mohem_flutter_app/widgets/button/default_button.dart'; import 'package:provider/provider.dart'; class FamilyMembers extends StatefulWidget { const FamilyMembers({Key? key}) : super(key: key); @override _FamilyMembersState createState() => _FamilyMembersState(); } class _FamilyMembersState extends State { List getEmployeeContactsList = []; bool fromMazaya = false; bool _argsLoaded = false; bool _isLoading = false; final TextEditingController _searchController = TextEditingController(); String _searchQuery = ""; @override void initState() { super.initState(); getEmployeeContacts(); } @override void didChangeDependencies() { super.didChangeDependencies(); if (!_argsLoaded) { Object? args = ModalRoute.of(context)?.settings.arguments; if (args is Map && args['fromMazaya'] == true) { fromMazaya = true; } _argsLoaded = true; } } @override void dispose() { _searchController.dispose(); super.dispose(); } bool _isAccessAllowed(GetEmployeeContactsList contact) { String? flag = contact.mAZAYAACCESSFLG ?? contact.mazayaAccess?.toString(); return flag?.trim().toUpperCase() == 'Y'; } Future _toggleMazayaAccess(GetEmployeeContactsList contact) async { bool current = _isAccessAllowed(contact); String action = current ? "Revoke" : "Grant"; try { Utils.showLoading(context); await ProfileApiClient().processContactMazayaAccess( accessAction: action, contactRelationshipId: contact.cONTACTRELATIONSHIPID, contactPersonId: contact.cONTACTPERSONID, personId: contact.pERSONID, ); List rawList = await ProfileApiClient().getEmployeeContacts(); getEmployeeContactsList = rawList; Utils.hideLoading(context); if (mounted) { setState(() {}); if (action == "Grant") { Navigator.push( context, MaterialPageRoute( builder: (BuildContext cxt) => MazayaAccessResultScreen( isGranted: true, dependentName: contact.cONTACTNAME ?? "", dependentInitials: _getInitials(contact.cONTACTNAME), ), ), ); } else { Utils.showToast(LocaleKeys.revokeMazayaAccess.tr()); } } } catch (ex) { Utils.hideLoading(context); Utils.handleException(ex, context, null); } } String _getInitials(String? name) { if (name == null || name.trim().isEmpty) return "D"; List parts = name.trim().split(RegExp(r'\s+')).where((String p) => p.isNotEmpty).toList(); if (parts.isEmpty) return "D"; if (parts.length == 1) { return parts.first.length >= 2 ? parts.first.substring(0, 2).toUpperCase() : parts.first.toUpperCase(); } return "${parts.first[0]}${parts.last[0]}".toUpperCase(); } List get _filteredList { if (_searchQuery.trim().isEmpty) return getEmployeeContactsList; String q = _searchQuery.trim().toLowerCase(); return getEmployeeContactsList.where((GetEmployeeContactsList item) { String name = (item.cONTACTNAME ?? "").toLowerCase(); String rel = (item.rELATIONSHIP ?? "").toLowerCase(); String personId = (item.pERSONID?.toString() ?? "").toLowerCase(); String contactPersonId = (item.cONTACTPERSONID?.toString() ?? "").toLowerCase(); String contactType = (item.cONTACTTYPE ?? "").toLowerCase(); return name.contains(q) || rel.contains(q) || personId.contains(q) || contactPersonId.contains(q) || contactType.contains(q); }).toList(); } void getEmployeeContacts() async { setState(() { _isLoading = true; }); try { List rawList = await ProfileApiClient().getEmployeeContacts(); getEmployeeContactsList = rawList; } catch (ex) { Utils.handleException(ex, context, null); } finally { if (mounted) { setState(() { _isLoading = false; }); } } } GetMenuEntriesList _getContactMenuEntries(BuildContext context) { List menuData = context.watch().getMenuEntriesList ?? []; List arr = menuData.where((GetMenuEntriesList e) => e.requestType == 'CONTACT').toList(); return arr.isNotEmpty ? arr.first : GetMenuEntriesList(); } @override Widget build(BuildContext context) { bool isAr = AppState().isArabic(context); GetMenuEntriesList menuEntries = _getContactMenuEntries(context); bool canAddFamilyMember = menuEntries.addButton == 'Y' || menuEntries.updateButton == 'Y'; bool canUpdateFamilyMember = menuEntries.updateButton == 'Y'; bool canDeleteFamilyMember = menuEntries.deleteButton == 'Y'; List displayList = _filteredList; return Scaffold( backgroundColor: const Color(0xFFF8FAFC), appBar: PreferredSize( preferredSize: const Size.fromHeight(56), child: SafeArea( child: Container( height: 56, color: const Color(0xFFF8FAFC), padding: const EdgeInsets.symmetric(horizontal: 16), child: Row( children: [ InkWell( borderRadius: BorderRadius.circular(20), onTap: () => Navigator.pop(context), child: const Padding( padding: EdgeInsets.all(8.0), child: Icon( Icons.arrow_back_ios_new_rounded, size: 20, color: Color(0xFF1E293B), ), ), ), const SizedBox(width: 8), (fromMazaya ? LocaleKeys.myDependents.tr() : LocaleKeys.profile_familyDetails.tr()) .toText20( isBold: true, color: const Color(0xFF1E293B), ), const Spacer(), InkWell( borderRadius: BorderRadius.circular(20), onTap: () => Navigator.popUntil(context, ModalRoute.withName(AppRoutes.dashboard)), child: const Padding( padding: EdgeInsets.all(8.0), child: Icon( Icons.home_outlined, size: 24, color: Color(0xFF1E293B), ), ), ), ], ), ), ), ), body: SafeArea( child: Column( children: [ // Search Bar Padding( padding: const EdgeInsets.fromLTRB(20, 8, 20, 12), child: Container( height: 48, decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(12), border: Border.all(color: const Color(0xFFE2E8F0)), ), padding: const EdgeInsets.symmetric(horizontal: 14), child: Row( children: [ const Icon(Icons.search, color: Color(0xFF64748B), size: 20), const SizedBox(width: 10), Expanded( child: TextField( controller: _searchController, onChanged: (String val) { setState(() { _searchQuery = val; }); }, decoration: InputDecoration( border: InputBorder.none, isDense: true, contentPadding: EdgeInsets.zero, hintText: fromMazaya ? LocaleKeys.searchDependents.tr() : LocaleKeys.searchFamilyMembers.tr(), hintStyle: const TextStyle(color: Color(0xFF94A3B8), fontSize: 14), ), ), ), if (_searchQuery.isNotEmpty) InkWell( onTap: () { _searchController.clear(); setState(() { _searchQuery = ""; }); }, child: const Padding( padding: EdgeInsets.symmetric(horizontal: 4), child: Icon(Icons.close, color: Color(0xFF94A3B8), size: 18), ), ), ], ), ), ), // Dependents list Expanded( child: _isLoading ? Center( child: Image.asset( "assets/images/logos/loading_mohemm_logo.gif", height: 96.0, width: 96.0, ), ) : displayList.isEmpty ? Utils.getNoDataWidget(context) : ListView.separated( padding: const EdgeInsets.fromLTRB(20, 4, 20, 20), separatorBuilder: (BuildContext cxt, int index) => const SizedBox(height: 12), itemCount: displayList.length, itemBuilder: (BuildContext context, int index) { return _buildDependentCard( displayList[index], isAr, canUpdateFamilyMember, canDeleteFamilyMember, ); }, ), ), // Bottom action button: + Add another user DefaultButton( fromMazaya ? LocaleKeys.addAnotherUser.tr() : LocaleKeys.addNewFamilyMember.tr(), (canAddFamilyMember || fromMazaya) ? () async { await Navigator.pushNamed( context, AppRoutes.addDependentVerify, arguments: {'fromMazaya': fromMazaya}, ); getEmployeeContacts(); } : null, iconData: Icons.add, isTextExpanded: false, height: 48, borderRadius: 12, ).insideContainer, ], ), ), ); } Widget _buildDependentCard( GetEmployeeContactsList item, bool isAr, bool canUpdate, bool canDelete, ) { bool isAllowed = _isAccessAllowed(item); return Container( padding: const EdgeInsets.fromLTRB(16, 16, 16, 14), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(16), border: Border.all(color: const Color(0xFFF1F5F9)), boxShadow: const [ BoxShadow( color: Color(0x0A000000), blurRadius: 8, offset: Offset(0, 3), ), ], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Top row: Avatar, Info, and Status Badge Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ // Initials Circle Container( width: 48, height: 48, decoration: const BoxDecoration( color: Color(0xFFE6F5F3), shape: BoxShape.circle, ), child: Center( child: Text( _getInitials(item.cONTACTNAME), style: const TextStyle( color: Color(0xFF168A89), fontWeight: FontWeight.w700, fontSize: 15, ), ), ), ), const SizedBox(width: 12), // Name & Details Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( item.cONTACTNAME ?? "", maxLines: 1, overflow: TextOverflow.ellipsis, style: const TextStyle( fontSize: 16, fontWeight: FontWeight.w600, color: Color(0xFF1E293B), ), ), const SizedBox(height: 3), Row( children: [ Flexible( child: (item.rELATIONSHIP ?? "").toText12( maxLine: 1, color: const Color(0xFF168A89), ), ), ], ), ], ), ), // Top right pill (shown in Mazaya flow) if (fromMazaya) ...[ const SizedBox(width: 8), Container( padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4), decoration: BoxDecoration( color: isAllowed ? const Color(0xFFE8F8F0) : const Color(0xFFFDE8E8), borderRadius: BorderRadius.circular(20), ), child: Text( isAllowed ? LocaleKeys.active.tr() : LocaleKeys.noAccess.tr(), style: TextStyle( fontSize: 11, fontWeight: FontWeight.w400, color: isAllowed ? const Color(0xFF168A89) : const Color(0xFFDC2626), ), ), ), ], ], ), const SizedBox(height: 14), const Divider(height: 1, thickness: 1, color: Color(0xFFF1F5F9)), const SizedBox(height: 12), // Bottom Action Row if (fromMazaya) ...[ // Mazaya Access Buttons: Revoke or Allow Center( child: InkWell( borderRadius: BorderRadius.circular(8), onTap: () => _toggleMazayaAccess(item), child: Padding( padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 8), child: Row( mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( isAllowed ? Icons.close : Icons.check, size: isAllowed ? 16 : 18, color: isAllowed ? const Color(0xFFDC2626) : const Color(0xFF168A89), ), const SizedBox(width: 6), Text( isAllowed ? LocaleKeys.revokeMazayaAccess.tr() : LocaleKeys.allowMazayaAccess.tr(), style: TextStyle( fontSize: 14, fontWeight: FontWeight.w600, color: isAllowed ? const Color(0xFFDC2626) : const Color(0xFF168A89), decoration: TextDecoration.underline, decorationColor: isAllowed ? const Color(0xFFDC2626) : const Color(0xFF168A89), ), ), ], ), ), ), ), ] else ...[ // Profile Flow: Update and Remove action buttons Row( children: [ Expanded( child: InkWell( borderRadius: BorderRadius.circular(10), onTap: canUpdate ? () { int? relationId = item.cONTACTRELATIONSHIPID; if (relationId != null) { showUpdateAlertDialog(context, relationId, 2, "UPDATE"); } } : null, child: Container( height: 38, decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(10), border: Border.all( color: canUpdate ? const Color(0xFFCBD5E1) : const Color(0xFFE2E8F0), ), ), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( Icons.edit_outlined, size: 15, color: canUpdate ? const Color(0xFF475569) : const Color(0xFF94A3B8), ), const SizedBox(width: 6), LocaleKeys.update.tr().toText12( color: canUpdate ? const Color(0xFF475569) : const Color(0xFF94A3B8), ), ], ), ), ), ), const SizedBox(width: 10), Expanded( child: InkWell( borderRadius: BorderRadius.circular(10), onTap: canDelete ? () { int? relationId = item.cONTACTRELATIONSHIPID; if (relationId != null) { showRemoveAlertDialog(context, relationId); } } : null, child: Container( height: 38, decoration: BoxDecoration( color: canDelete ? const Color(0xFFFFF5F5) : Colors.white, borderRadius: BorderRadius.circular(10), border: Border.all( color: canDelete ? const Color(0xFFFFCDD2) : const Color(0xFFE2E8F0), ), ), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( Icons.delete_outline_rounded, size: 15, color: canDelete ? const Color(0xFFED1C24) : const Color(0xFF94A3B8), ), const SizedBox(width: 6), LocaleKeys.remove.tr().toText12( color: canDelete ? const Color(0xFFED1C24) : const Color(0xFF94A3B8), ), ], ), ), ), ), ], ), ], ], ), ); } void showUpdateAlertDialog(BuildContext context, int relationId, int flag, String actionType) { Widget cancelButton = TextButton( child: LocaleKeys.cancel.tr().toText14(), onPressed: () => Navigator.pop(context), ); Widget continueButton = TextButton( child: LocaleKeys.ok.tr().toText14(), onPressed: () { Navigator.pop(context); Navigator.pushNamed( context, AppRoutes.addUpdateFamilyMember, arguments: FamilyMemberScreenParams(relationId, flag: 2, actionType: "UPDATE"), ).then((_) => getEmployeeContacts()); }, ); AlertDialog alert = AlertDialog( title: LocaleKeys.confirm.tr().toText16(isBold: true), content: LocaleKeys.updateMember.tr().toText14(), actions: [cancelButton, continueButton], ); showDialog( context: context, builder: (BuildContext context) => alert, ); } void showRemoveAlertDialog(BuildContext context, int relationId) { Widget cancelButton = TextButton( child: LocaleKeys.cancel.tr().toText14(), onPressed: () => Navigator.pop(context), ); Widget continueButton = TextButton( child: LocaleKeys.ok.tr().toText14(), onPressed: () { Navigator.pop(context); Navigator.pushNamed(context, AppRoutes.deleteFamilyMember, arguments: relationId).then((_) => getEmployeeContacts()); }, ); AlertDialog alert = AlertDialog( title: LocaleKeys.confirm.tr().toText16(isBold: true), content: LocaleKeys.removeThisMember.tr().toText14(), actions: [cancelButton, continueButton], ); showDialog( context: context, builder: (BuildContext context) => alert, ); } }