diff --git a/assets/images/cat_all.png b/assets/images/cat_all.png index aa6f895..3bc5554 100644 Binary files a/assets/images/cat_all.png and b/assets/images/cat_all.png differ diff --git a/lib/classes/utils.dart b/lib/classes/utils.dart index 9971317..99e7207 100644 --- a/lib/classes/utils.dart +++ b/lib/classes/utils.dart @@ -22,6 +22,7 @@ import 'package:mohem_flutter_app/widgets/loading_dialog.dart'; import 'package:nfc_manager/nfc_manager.dart'; import 'package:nfc_manager/platform_tags.dart'; import 'package:shared_preferences/shared_preferences.dart'; +import 'package:mohem_flutter_app/ui/profile/mazaya_access_result_screen.dart'; // ignore_for_file: avoid_annotating_with_dynamic @@ -94,7 +95,59 @@ class Utils { return await prefs.setString(key, value); } + static bool isMazayaAccessRevoked(dynamic error) { + if (error == null) return false; + String str = ""; + if (error is APIException && error.error != null) { + String errCode = error.error?.errorCode?.toString() ?? ""; + String errMsg = error.error?.errorMessage?.toString() ?? ""; + str = "$errCode $errMsg ${error.message}"; + if (errCode.trim().toUpperCase() == "E4") { + return true; + } + } else { + str = error.toString(); + } + String lower = str.toLowerCase(); + if (lower.contains("no active grant") || + lower.contains("grant request") || + lower.contains("grant was found") || + lower.contains("grant_request") || + lower.contains("e4:") || + lower.contains("e4 -") || + lower.contains("e4 :")) { + return true; + } + if (str.contains("طلب منح") || + str.contains("منح نشط") || + str.contains("لا يوجد طلب منح") || + str.contains("تم إلغاء وصول مزايا") || + str.contains("إلغاء مزايا")) { + return true; + } + return false; + } + static void handleException(dynamic exception, cxt, Function(String)? onErrorMessage) { + if (isMazayaAccessRevoked(exception)) { + String depName = AppState().dependentName ?? ""; + if (depName.isEmpty) { + depName = AppState().walaplusDepInfoModel?.pDependentName ?? ""; + } + if (cxt != null) { + Navigator.push( + cxt, + MaterialPageRoute( + builder: (context) => MazayaAccessResultScreen( + isGranted: false, + dependentName: depName, + ), + ), + ); + return; + } + } + String errorMessage; if (exception.error.errorType != null && exception.error.errorType == 4) { Navigator.pushNamedAndRemoveUntil(cxt, AppRoutes.appUpdateScreen, (_) => false, arguments: exception.error?.errorMessage); diff --git a/lib/models/get_employee_contacts.model.dart b/lib/models/get_employee_contacts.model.dart index be139fa..63964f6 100644 --- a/lib/models/get_employee_contacts.model.dart +++ b/lib/models/get_employee_contacts.model.dart @@ -10,6 +10,7 @@ class GetEmployeeContactsList { String? rELATIONSHIP; String? mAZAYAACCESSFLG; dynamic mazayaAccess; + String? nATIONALIDENTIFIER; GetEmployeeContactsList( {this.cONTACTNAME, @@ -21,7 +22,8 @@ class GetEmployeeContactsList { this.pRIMARYCONTACTFLAG, this.rELATIONSHIP, this.mAZAYAACCESSFLG, - this.mazayaAccess}); + this.mazayaAccess, + this.nATIONALIDENTIFIER}); GetEmployeeContactsList.fromJson(Map json) { cONTACTNAME = json['CONTACT_NAME']; @@ -34,6 +36,10 @@ class GetEmployeeContactsList { rELATIONSHIP = json['RELATIONSHIP']; mAZAYAACCESSFLG = json['MAZAYA_ACCESS_FLG']?.toString() ?? json['MAZAYA_ACCESS']?.toString(); mazayaAccess = mAZAYAACCESSFLG; + nATIONALIDENTIFIER = json['NATIONAL_IDENTIFIER']?.toString() ?? + json['NATIONAL_ID']?.toString() ?? + json['CIVIL_ID']?.toString() ?? + json['IQAMA_NUMBER']?.toString(); } Map toJson() { @@ -48,6 +54,7 @@ class GetEmployeeContactsList { data['RELATIONSHIP'] = rELATIONSHIP; data['MAZAYA_ACCESS_FLG'] = mAZAYAACCESSFLG; data['MAZAYA_ACCESS'] = mazayaAccess; + data['NATIONAL_IDENTIFIER'] = nATIONALIDENTIFIER; return data; } } \ No newline at end of file diff --git a/lib/ui/login/login_screen.dart b/lib/ui/login/login_screen.dart index 81e79c3..9957cc1 100644 --- a/lib/ui/login/login_screen.dart +++ b/lib/ui/login/login_screen.dart @@ -22,6 +22,7 @@ import 'package:mohem_flutter_app/extensions/int_extensions.dart'; import 'package:mohem_flutter_app/extensions/string_extensions.dart'; import 'package:mohem_flutter_app/extensions/widget_extensions.dart'; import 'package:mohem_flutter_app/generated/locale_keys.g.dart'; +import 'package:mohem_flutter_app/ui/profile/mazaya_access_result_screen.dart'; import 'package:mohem_flutter_app/models/basic_member_information_model.dart'; import 'package:mohem_flutter_app/models/check_mobile_app_version_model.dart'; import 'package:mohem_flutter_app/models/generic_response_model.dart'; @@ -125,11 +126,6 @@ class _LoginScreenState extends State { GetMobileLoginInfoListModel? loginInfo; GetMobileLoginInfoDependentModel? dependentLoginInfo; - bool get _hasDeviceDependentData { - if (dependentLoginInfo == null) return false; - String id = dependentLoginInfo?.nationalId ?? dependentLoginInfo?.dependentID ?? ""; - return id.trim().isNotEmpty; - } Future checkFirebaseToken() async { try { @@ -598,6 +594,23 @@ class _LoginScreenState extends State { return ""; } + void _navigateToMazayaAccessRevoked([String? depName]) async { + String name = depName ?? AppState().dependentName ?? ""; + if (name.isEmpty) { + name = await Utils.getStringFromPrefs("P_DEPENDENT_NAME"); + } + if (!mounted) return; + Navigator.push( + context, + MaterialPageRoute( + builder: (_) => MazayaAccessResultScreen( + isGranted: false, + dependentName: name, + ), + ), + ); + } + void performDependentLogin() async { if (nationalId.text.trim().isEmpty) { Utils.showToast(LocaleKeys.pleaseEnterNationalId.tr()); @@ -634,6 +647,10 @@ class _LoginScreenState extends State { if (depLoginResponse?.errorMessage != null && depLoginResponse!.errorMessage!.isNotEmpty) { Utils.hideLoading(context); + if (Utils.isMazayaAccessRevoked(depLoginResponse.errorMessage)) { + _navigateToMazayaAccessRevoked(); + return; + } Utils.showToast(depLoginResponse.errorMessage!); return; } @@ -694,6 +711,10 @@ class _LoginScreenState extends State { } catch (ex) { if (kDebugMode) print(ex); Utils.hideLoading(context); + if (Utils.isMazayaAccessRevoked(ex)) { + _navigateToMazayaAccessRevoked(); + return; + } Utils.handleException(ex, context, null); } } @@ -716,6 +737,11 @@ class _LoginScreenState extends State { Utils.hideLoading(context); if (checkOtpResponse?.errorMessage != null && checkOtpResponse!.errorMessage!.isNotEmpty) { + if (Utils.isMazayaAccessRevoked(checkOtpResponse.errorMessage)) { + Navigator.pop(context); + _navigateToMazayaAccessRevoked(); + return; + } Utils.showToast(checkOtpResponse.errorMessage!); _pinPutController.clear(); otpFieldClear.value = ""; @@ -736,6 +762,11 @@ class _LoginScreenState extends State { _pinPutController.clear(); otpFieldClear.value = ""; Utils.hideLoading(context); + if (Utils.isMazayaAccessRevoked(ex)) { + Navigator.pop(context); + _navigateToMazayaAccessRevoked(); + return; + } Utils.handleException(ex, context, null); } }, @@ -755,6 +786,11 @@ class _LoginScreenState extends State { Utils.showToast(LocaleKeys.verificationCodeResent.tr()); } catch (ex) { Utils.hideLoading(context); + if (Utils.isMazayaAccessRevoked(ex)) { + Navigator.pop(context); + _navigateToMazayaAccessRevoked(); + return; + } Utils.handleException(ex, context, null); } }, diff --git a/lib/ui/profile/add_dependent_verify_screen.dart b/lib/ui/profile/add_dependent_verify_screen.dart index aff9369..c3f66ae 100644 --- a/lib/ui/profile/add_dependent_verify_screen.dart +++ b/lib/ui/profile/add_dependent_verify_screen.dart @@ -55,34 +55,136 @@ class _AddDependentVerifyScreenState extends State { // Cache for dynamically generated text field controllers final Map _fieldControllers = {}; + // Track field keys/prompts that were prefilled from patientInfo + final Set _prefilledFieldKeys = {}; + + bool _isFieldPrefilledFromPatientInfo(String? colName, String? prompt, String? val) { + if (_patientInfo == null) return false; + if (val == null || val.trim().isEmpty) return false; + if (colName != null && _prefilledFieldKeys.contains(colName.toUpperCase().trim())) { + return true; + } + if (prompt != null && _prefilledFieldKeys.contains(prompt.toLowerCase().trim())) { + return true; + } + return false; + } + + IconData? _getSuffixIconForField(String colName, String prompt, String dataType) { + String col = colName.toUpperCase(); + String p = prompt.toLowerCase(); + if (col.contains('EMAIL') || p.contains('email') || p.contains('بريد')) { + return Icons.mail_outline_rounded; + } + if (col.contains('PHONE') || col.contains('MOBILE') || col.contains('TEL') || p.contains('phone') || p.contains('mobile') || p.contains('جوال') || p.contains('هاتف')) { + return Icons.phone_outlined; + } + if (dataType == 'DATE' || col.contains('DATE') || p.contains('date') || p.contains('تاريخ')) { + return Icons.calendar_today_outlined; + } + return null; + } + @override void initState() { super.initState(); + _nationalIdController.addListener(_onNationalIdChanged); _fetchEbsStructures(); } + void _onNationalIdChanged() { + if (mounted) setState(() {}); + } + + void _onFieldControllerChanged() { + if (mounted) setState(() {}); + } + @override void dispose() { + _nationalIdController.removeListener(_onNationalIdChanged); _nationalIdController.dispose(); for (var c in _fieldControllers.values) { + c.removeListener(_onFieldControllerChanged); c.dispose(); } _fieldControllers.clear(); super.dispose(); } + bool _isStep1Valid() { + String natId = _nationalIdController.text.trim(); + if (natId.isEmpty) return false; + if (_isLegislationSA && natId.length != 10) return false; + if (_selectedDob == null) return false; + return true; + } + + bool _isStep2Valid() { + for (int i = 0; i < _contactColsStructureList.length; i++) { + var col = _contactColsStructureList[i]; + if (col.dISPLAYFLAG != "N" && col.rEQUIREDFLAG == "Y") { + String colKey = col.aPPLICATIONCOLUMNNAME ?? "COL_$i"; + String textVal = _fieldControllers[colKey]?.text.trim() ?? ""; + String dspVal = col.getContactDetailsList?.sEGMENTVALUEDSP?.trim() ?? ""; + String codeVal = col.getContactDetailsList?.vARCHAR2VALUE?.trim() ?? ""; + String dateVal = col.getContactDetailsList?.dATEVALUE?.trim() ?? ""; + if (textVal.isEmpty && dspVal.isEmpty && codeVal.isEmpty && dateVal.isEmpty) { + return false; + } + } + } + for (int i = 0; i < _basicDetDffStructureList.length; i++) { + var basic = _basicDetDffStructureList[i]; + if (basic.dISPLAYFLAG != "N" && basic.rEQUIREDFLAG == "Y") { + String colKey = basic.aPPLICATIONCOLUMNNAME ?? "BASIC_DFF_$i"; + String textVal = _fieldControllers[colKey]?.text.trim() ?? ""; + String dspVal = basic.getContactDetailsList?.sEGMENTVALUEDSP?.trim() ?? ""; + String codeVal = basic.getContactDetailsList?.vARCHAR2VALUE?.trim() ?? ""; + String dateVal = basic.getContactDetailsList?.dATEVALUE?.trim() ?? ""; + if (textVal.isEmpty && dspVal.isEmpty && codeVal.isEmpty && dateVal.isEmpty) { + return false; + } + } + } + return true; + } + + bool _isStep3Valid() { + for (int i = 0; i < _contactDffStructureList.length; i++) { + var dff = _contactDffStructureList[i]; + if (dff.dISPLAYFLAG != "N" && dff.rEQUIREDFLAG == "Y") { + String colKey = dff.aPPLICATIONCOLUMNNAME ?? "CONTACT_DFF_$i"; + String textVal = _fieldControllers[colKey]?.text.trim() ?? ""; + String dspVal = dff.getContactDetailsList?.sEGMENTVALUEDSP?.trim() ?? ""; + String codeVal = dff.getContactDetailsList?.vARCHAR2VALUE?.trim() ?? ""; + String dateVal = dff.getContactDetailsList?.dATEVALUE?.trim() ?? ""; + if (textVal.isEmpty && dspVal.isEmpty && codeVal.isEmpty && dateVal.isEmpty) { + return false; + } + } + } + return true; + } + TextEditingController _getControllerForField(String key, String? initialValue) { if (!_fieldControllers.containsKey(key)) { - _fieldControllers[key] = TextEditingController(text: initialValue ?? ''); + TextEditingController ctrl = TextEditingController(text: initialValue ?? ''); + ctrl.addListener(_onFieldControllerChanged); + _fieldControllers[key] = ctrl; } return _fieldControllers[key]!; } void _syncController(String key, String? value) { if (_fieldControllers.containsKey(key)) { - _fieldControllers[key]!.text = value ?? ''; + if (_fieldControllers[key]!.text != (value ?? '')) { + _fieldControllers[key]!.text = value ?? ''; + } } else { - _fieldControllers[key] = TextEditingController(text: value ?? ''); + TextEditingController ctrl = TextEditingController(text: value ?? ''); + ctrl.addListener(_onFieldControllerChanged); + _fieldControllers[key] = ctrl; } } @@ -332,6 +434,14 @@ class _AddDependentVerifyScreenState extends State { details.sEGMENTVALUEDSP = nationalId; details.vARCHAR2VALUE = nationalId; _syncController(colKey, nationalId); + if (_patientInfo != null) { + if (colName.isNotEmpty) _prefilledFieldKeys.add(colName); + if (prompt.isNotEmpty) { + _prefilledFieldKeys.add(prompt); + _prefilledFieldKeys.add(prompt.replaceAll('*', '').trim()); + } + if (colKey.isNotEmpty) _prefilledFieldKeys.add(colKey); + } } // 2. Date of birth else if (colName.contains('DATE_OF_BIRTH') || @@ -345,6 +455,14 @@ class _AddDependentVerifyScreenState extends State { details.dATEVALUE = ymdDob; details.vARCHAR2VALUE = ymdDob; _syncController(colKey, ymdDob); + if (_patientInfo != null) { + if (colName.isNotEmpty) _prefilledFieldKeys.add(colName); + if (prompt.isNotEmpty) { + _prefilledFieldKeys.add(prompt); + _prefilledFieldKeys.add(prompt.replaceAll('*', '').trim()); + } + if (colKey.isNotEmpty) _prefilledFieldKeys.add(colKey); + } } } } @@ -356,6 +474,8 @@ class _AddDependentVerifyScreenState extends State { // --- Map GetPatientInfo to Dynamic Structures --- void _applyPrefilledPatientInfo(PatientInfoModel patient, bool isAr, String nationalId, String dob) { + _prefilledFieldKeys.clear(); + String fullNameAr = patient.fullNameAr; String fullNameEn = patient.fullNameEn; String effectiveName = isAr @@ -378,7 +498,7 @@ class _AddDependentVerifyScreenState extends State { } String ymdDob = parsedDob != null ? DateFormat('yyyy-MM-dd').format(parsedDob) : (patient.dateOfBirth ?? dob); - void matchAndFill( colItem) { + void matchAndFill(colItem) { if (colItem == null) return; colItem.getContactDetailsList ??= GetContactDetailsList(); GetContactDetailsList details = colItem.getContactDetailsList!; @@ -387,6 +507,15 @@ class _AddDependentVerifyScreenState extends State { String prompt = (colItem.sEGMENTPROMPT ?? '').toString().toLowerCase().trim(); String colKey = colItem.aPPLICATIONCOLUMNNAME ?? ''; + void markPrefilled() { + if (colName.isNotEmpty) _prefilledFieldKeys.add(colName); + if (prompt.isNotEmpty) { + _prefilledFieldKeys.add(prompt); + _prefilledFieldKeys.add(prompt.replaceAll('*', '').trim()); + } + if (colKey.isNotEmpty) _prefilledFieldKeys.add(colKey); + } + // 1. Civil ID / National ID / Iqama if (colName.contains('NATIONAL_IDENTIFIER') || colName.contains('CIVIL_ID') || @@ -407,6 +536,7 @@ class _AddDependentVerifyScreenState extends State { details.sEGMENTVALUEDSP = idVal; details.vARCHAR2VALUE = idVal; _syncController(colKey, idVal); + markPrefilled(); } // 2. Date of birth else if (colName.contains('DATE_OF_BIRTH') || @@ -421,6 +551,7 @@ class _AddDependentVerifyScreenState extends State { details.dATEVALUE = dobVal; details.vARCHAR2VALUE = dobVal; _syncController(colKey, dobVal); + markPrefilled(); } } // 3. First Name @@ -436,6 +567,7 @@ class _AddDependentVerifyScreenState extends State { details.sEGMENTVALUEDSP = val; details.vARCHAR2VALUE = val; _syncController(colKey, val); + markPrefilled(); } } // 4. Second / Father Name @@ -453,6 +585,7 @@ class _AddDependentVerifyScreenState extends State { details.sEGMENTVALUEDSP = val; details.vARCHAR2VALUE = val; _syncController(colKey, val); + markPrefilled(); } } // 5. Third / Grandfather Name @@ -470,6 +603,7 @@ class _AddDependentVerifyScreenState extends State { details.sEGMENTVALUEDSP = val; details.vARCHAR2VALUE = val; _syncController(colKey, val); + markPrefilled(); } } // 6. Last / Family Name @@ -488,6 +622,7 @@ class _AddDependentVerifyScreenState extends State { details.sEGMENTVALUEDSP = val; details.vARCHAR2VALUE = val; _syncController(colKey, val); + markPrefilled(); } } // 7. Full Name / Contact Name @@ -504,6 +639,7 @@ class _AddDependentVerifyScreenState extends State { details.sEGMENTVALUEDSP = val; details.vARCHAR2VALUE = val; _syncController(colKey, val); + markPrefilled(); } } List? objValues; @@ -591,6 +727,7 @@ class _AddDependentVerifyScreenState extends State { details.sEGMENTVALUEDSP = isFemale ? LocaleKeys.genderFemale.tr() : LocaleKeys.genderMale.tr(); details.vARCHAR2VALUE = isFemale ? 'F' : 'M'; } + markPrefilled(); } // 10. Title else if (colName.contains('TITLE') || prompt.contains('title') || prompt.contains('اللقب')) { @@ -604,10 +741,28 @@ class _AddDependentVerifyScreenState extends State { if (isFemale && (code == 'MS' || code == 'MRS' || code == 'MISS' || meaning.contains('ms') || meaning.contains('mrs') || meaning.contains('miss') || meaning.contains('سيدة') || meaning.contains('آنسة'))) { details.sEGMENTVALUEDSP = opt.mEANING; details.vARCHAR2VALUE = opt.cODE; + markPrefilled(); break; } else if (isMale && (code == 'MR' || (!meaning.contains('mrs') && !meaning.contains('سيدة') && (meaning.startsWith('mr') || meaning.contains('mr.') || meaning.contains('سيد'))))) { details.sEGMENTVALUEDSP = opt.mEANING; details.vARCHAR2VALUE = opt.cODE; + markPrefilled(); + break; + } + } + } else if (eServices != null && eServices.isNotEmpty) { + for (var opt in eServices) { + String code = (opt.iDCOLUMNNAME ?? '').trim().toUpperCase(); + String meaning = (opt.vALUECOLUMNNAME ?? '').trim().toLowerCase(); + if (isFemale && (code == 'MS' || code == 'MRS' || code == 'MISS' || meaning.contains('ms') || meaning.contains('mrs') || meaning.contains('miss') || meaning.contains('سيدة') || meaning.contains('آنسة'))) { + details.sEGMENTVALUEDSP = opt.vALUECOLUMNNAME; + details.vARCHAR2VALUE = opt.iDCOLUMNNAME; + markPrefilled(); + break; + } else if (isMale && (code == 'MR' || (!meaning.contains('mrs') && !meaning.contains('سيدة') && (meaning.startsWith('mr') || meaning.contains('mr.') || meaning.contains('سيد'))))) { + details.sEGMENTVALUEDSP = opt.vALUECOLUMNNAME; + details.vARCHAR2VALUE = opt.iDCOLUMNNAME; + markPrefilled(); break; } } @@ -628,10 +783,28 @@ class _AddDependentVerifyScreenState extends State { if (isFemale && (code == 'DAUGHTER' || meaning.contains('daughter') || meaning.contains('ابنة') || meaning.contains('بنت'))) { details.sEGMENTVALUEDSP = opt.mEANING; details.vARCHAR2VALUE = opt.cODE; + markPrefilled(); break; } else if (isMale && (code == 'SON' || (!meaning.contains('daughter') && (meaning.contains('son') || meaning.contains('ابن') || meaning.contains('ولد'))))) { details.sEGMENTVALUEDSP = opt.mEANING; details.vARCHAR2VALUE = opt.cODE; + markPrefilled(); + break; + } + } + } else if (eServices != null && eServices.isNotEmpty) { + for (var opt in eServices) { + String code = (opt.iDCOLUMNNAME ?? '').trim().toUpperCase(); + String meaning = (opt.vALUECOLUMNNAME ?? '').trim().toLowerCase(); + if (isFemale && (code == 'DAUGHTER' || meaning.contains('daughter') || meaning.contains('ابنة') || meaning.contains('بنت'))) { + details.sEGMENTVALUEDSP = opt.vALUECOLUMNNAME; + details.vARCHAR2VALUE = opt.iDCOLUMNNAME; + markPrefilled(); + break; + } else if (isMale && (code == 'SON' || (!meaning.contains('daughter') && (meaning.contains('son') || meaning.contains('ابن') || meaning.contains('ولد'))))) { + details.sEGMENTVALUEDSP = opt.vALUECOLUMNNAME; + details.vARCHAR2VALUE = opt.iDCOLUMNNAME; + markPrefilled(); break; } } @@ -788,6 +961,64 @@ class _AddDependentVerifyScreenState extends State { } } + // --- Helper to build text fields styled specifically for Add Dependent Screen --- + Widget _buildFigmaTextField( + String labelText, + String hintText, { + TextEditingController? controller, + VoidCallback? onTap, + IconData? suffixIconData, + bool isEnable = true, + bool isReadOnly = false, + bool isPopup = false, + int? lines = 1, + TextInputAction? inputAction, + void Function(String)? onChange, + bool isInputTypeNum = false, + int? maxLength, + List? inputFormatters, + Color? backgroundColor, + Color? borderColor, + double? borderWidth, + Color? labelColor, + Color? textColor, + Color? suffixIconColor, + }) { + return DynamicTextFieldWidget( + labelText, + hintText, + controller: controller, + onTap: onTap, + suffixIconData: suffixIconData, + isEnable: isEnable, + isReadOnly: isReadOnly, + isPopup: isPopup, + lines: lines, + inputAction: inputAction, + onChange: onChange, + isInputTypeNum: isInputTypeNum, + maxLength: maxLength, + inputFormatters: inputFormatters, + backgroundColor: backgroundColor ?? Colors.white, + borderColor: borderColor, + borderWidth: borderWidth, + labelColor: labelColor, + textColor: textColor, + suffixIconColor: suffixIconColor, + borderRadius: BorderRadius.circular(14), + constraints: const BoxConstraints(minHeight: 66), + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), + boxShadow: const [ + BoxShadow( + color: Color.fromRGBO(0, 0, 0, 0.0314), + offset: Offset(0, 2), + blurRadius: 5, + spreadRadius: 0, + ), + ], + ); + } + // --- Build Dynamic Fields for Contact Columns --- Widget _buildDynamicColField(GetContactColsStructureList model, int index) { if (model.dISPLAYFLAG == "N") { @@ -795,14 +1026,17 @@ class _AddDependentVerifyScreenState extends State { } bool isRequired = model.rEQUIREDFLAG == "Y"; - String label = (model.sEGMENTPROMPT ?? "") + (isRequired ? "*" : ""); + String rawPrompt = (model.sEGMENTPROMPT ?? "").trim(); + String label = rawPrompt.endsWith("*") + ? rawPrompt + : (isRequired ? "$rawPrompt*" : rawPrompt); model.getContactDetailsList ??= GetContactDetailsList(); + String colName = (model.aPPLICATIONCOLUMNNAME ?? "").toUpperCase().trim(); + String prompt = rawPrompt.toLowerCase(); String colKey = model.aPPLICATIONCOLUMNNAME ?? "COL_$index"; if (model.dATATYPE == "VARCHAR2") { if (model.objectValuesList != null && model.objectValuesList!.isNotEmpty) { - String colName = (model.aPPLICATIONCOLUMNNAME ?? "").toUpperCase(); - String prompt = (model.sEGMENTPROMPT ?? "").toLowerCase(); bool isSponsorRelated = colName.contains("SPONSOR") || prompt.contains("sponsor") || prompt.contains("كفيل"); bool valueInOptions = model.objectValuesList!.any((opt) => opt.mEANING == model.getContactDetailsList!.sEGMENTVALUEDSP); @@ -812,94 +1046,180 @@ class _AddDependentVerifyScreenState extends State { } String displayVal = model.getContactDetailsList!.sEGMENTVALUEDSP ?? ""; + bool isFieldDisabled = _isFieldPrefilledFromPatientInfo(colName, prompt, displayVal); + bool hasSelectedValue = displayVal.trim().isNotEmpty; + bool showRedBorder = isRequired && !isFieldDisabled && !hasSelectedValue; + Color effectiveBorderColor = showRedBorder + ? const Color(0xFFED1C2B) + : const Color(0xFFE6E9E8); + double effectiveBorderWidth = showRedBorder ? 1.4 : 1.0; + Color effectiveLabelColor = isFieldDisabled ? const Color(0xFF8E95A2) : const Color(0xff2B353E); + Color effectiveTextColor = isFieldDisabled + ? const Color(0xFF8E95A2) + : (displayVal.isNotEmpty ? const Color(0xff2B353E) : const Color(0xFF94A3B8)); + Color effectiveSuffixColor = isFieldDisabled ? const Color(0xFF8E95A2) : const Color(0xFF64748B); + return Padding( padding: const EdgeInsets.only(bottom: 12), child: PopupMenuButton( - child: DynamicTextFieldWidget( + enabled: !isFieldDisabled, + child: _buildFigmaTextField( label, displayVal.isNotEmpty ? displayVal : LocaleKeys.select.tr(), isEnable: false, isPopup: true, backgroundColor: Colors.white, + borderColor: effectiveBorderColor, + borderWidth: effectiveBorderWidth, + labelColor: effectiveLabelColor, + textColor: effectiveTextColor, + suffixIconColor: effectiveSuffixColor, ), - itemBuilder: (_) => >[ - for (int i = 0; i < model.objectValuesList!.length; i++) - PopupMenuItem( - value: i, - child: (model.objectValuesList![i].mEANING ?? "").toText14(), - ), - ], - onSelected: (int selectedIdx) { - setState(() { - model.getContactDetailsList!.sEGMENTVALUEDSP = model.objectValuesList![selectedIdx].mEANING; - model.getContactDetailsList!.vARCHAR2VALUE = model.objectValuesList![selectedIdx].cODE; - }); - }, + itemBuilder: (_) => isFieldDisabled + ? >[] + : >[ + for (int i = 0; i < model.objectValuesList!.length; i++) + PopupMenuItem( + value: i, + child: (model.objectValuesList![i].mEANING ?? "").toText14(), + ), + ], + onSelected: isFieldDisabled + ? null + : (int selectedIdx) { + setState(() { + model.getContactDetailsList!.sEGMENTVALUEDSP = model.objectValuesList![selectedIdx].mEANING; + model.getContactDetailsList!.vARCHAR2VALUE = model.objectValuesList![selectedIdx].cODE; + }); + }, ), ); } else { var controller = _getControllerForField(colKey, model.getContactDetailsList!.sEGMENTVALUEDSP); + bool isFieldDisabled = _isFieldPrefilledFromPatientInfo(colName, prompt, controller.text); + bool hasValue = controller.text.trim().isNotEmpty || (model.getContactDetailsList?.sEGMENTVALUEDSP?.trim().isNotEmpty ?? false); + bool showRedBorder = isRequired && !isFieldDisabled && !hasValue; + Color effectiveBorderColor = showRedBorder + ? const Color(0xFFED1C2B) + : const Color(0xFFE6E9E8); + double effectiveBorderWidth = showRedBorder ? 1.4 : 1.0; + Color effectiveLabelColor = isFieldDisabled ? const Color(0xFF8E95A2) : const Color(0xff2B353E); + Color effectiveTextColor = isFieldDisabled ? const Color(0xFF8E95A2) : const Color(0xff2B353E); + Color effectiveSuffixColor = isFieldDisabled ? const Color(0xFF8E95A2) : const Color(0xFF64748B); + return Padding( padding: const EdgeInsets.only(bottom: 12), - child: DynamicTextFieldWidget( + child: _buildFigmaTextField( label, LocaleKeys.enterFieldPlaceholder.tr(args: [label]), controller: controller, + isEnable: !isFieldDisabled, + isReadOnly: isFieldDisabled, backgroundColor: Colors.white, - onChange: (String text) { - model.getContactDetailsList!.sEGMENTVALUEDSP = text; - model.getContactDetailsList!.vARCHAR2VALUE = text; - }, + borderColor: effectiveBorderColor, + borderWidth: effectiveBorderWidth, + labelColor: effectiveLabelColor, + textColor: effectiveTextColor, + suffixIconData: _getSuffixIconForField(colName, prompt, "VARCHAR2"), + suffixIconColor: effectiveSuffixColor, + onChange: isFieldDisabled + ? null + : (String text) { + model.getContactDetailsList!.sEGMENTVALUEDSP = text; + model.getContactDetailsList!.vARCHAR2VALUE = text; + }, ), ); } } else if (model.dATATYPE == "NUMBER") { var controller = _getControllerForField(colKey, model.getContactDetailsList!.sEGMENTVALUEDSP); + bool isFieldDisabled = _isFieldPrefilledFromPatientInfo(colName, prompt, controller.text); + bool hasValue = controller.text.trim().isNotEmpty || (model.getContactDetailsList?.sEGMENTVALUEDSP?.trim().isNotEmpty ?? false); + bool showRedBorder = isRequired && !isFieldDisabled && !hasValue; + Color effectiveBorderColor = showRedBorder + ? const Color(0xFFED1C2B) + : const Color(0xFFE6E9E8); + double effectiveBorderWidth = showRedBorder ? 1.4 : 1.0; + Color effectiveLabelColor = isFieldDisabled ? const Color(0xFF8E95A2) : const Color(0xff2B353E); + Color effectiveTextColor = isFieldDisabled ? const Color(0xFF8E95A2) : const Color(0xff2B353E); + Color effectiveSuffixColor = isFieldDisabled ? const Color(0xFF8E95A2) : const Color(0xFF64748B); + return Padding( padding: const EdgeInsets.only(bottom: 12), - child: DynamicTextFieldWidget( + child: _buildFigmaTextField( label, LocaleKeys.enterFieldPlaceholder.tr(args: [label]), controller: controller, + isEnable: !isFieldDisabled, + isReadOnly: isFieldDisabled, isInputTypeNum: true, backgroundColor: Colors.white, - onChange: (String text) { - model.getContactDetailsList!.sEGMENTVALUEDSP = text; - model.getContactDetailsList!.vARCHAR2VALUE = text; - }, + borderColor: effectiveBorderColor, + borderWidth: effectiveBorderWidth, + labelColor: effectiveLabelColor, + textColor: effectiveTextColor, + suffixIconData: _getSuffixIconForField(colName, prompt, "NUMBER"), + suffixIconColor: effectiveSuffixColor, + onChange: isFieldDisabled + ? null + : (String text) { + model.getContactDetailsList!.sEGMENTVALUEDSP = text; + model.getContactDetailsList!.vARCHAR2VALUE = text; + }, ), ); } else if (model.dATATYPE == "DATE") { String displayVal = model.getContactDetailsList!.sEGMENTVALUEDSP ?? ""; + bool isFieldDisabled = _isFieldPrefilledFromPatientInfo(colName, prompt, displayVal); + bool hasSelectedValue = displayVal.trim().isNotEmpty; + bool showRedBorder = isRequired && !isFieldDisabled && !hasSelectedValue; + Color effectiveBorderColor = showRedBorder + ? const Color(0xFFED1C2B) + : const Color(0xFFE6E9E8); + double effectiveBorderWidth = showRedBorder ? 1.4 : 1.0; + Color effectiveLabelColor = isFieldDisabled ? const Color(0xFF8E95A2) : const Color(0xff2B353E); + Color effectiveTextColor = isFieldDisabled + ? const Color(0xFF8E95A2) + : (displayVal.isNotEmpty ? const Color(0xff2B353E) : const Color(0xFF94A3B8)); + Color effectiveSuffixColor = isFieldDisabled ? const Color(0xFF8E95A2) : const Color(0xFF64748B); + return Padding( padding: const EdgeInsets.only(bottom: 12), - child: DynamicTextFieldWidget( + child: _buildFigmaTextField( label, displayVal.isNotEmpty ? displayVal : LocaleKeys.dateFormatPlaceholder.tr(), suffixIconData: Icons.calendar_today_outlined, + isEnable: !isFieldDisabled, isReadOnly: true, backgroundColor: Colors.white, - onTap: () async { - DateTime initial = DateTime.now(); - if (displayVal.isNotEmpty) { - try { - initial = DateFormat('yyyy-MM-dd').parse(displayVal); - } catch (_) { - try { - initial = DateFormat('dd/MM/yyyy').parse(displayVal); - } catch (_) {} - } - } - DateTime? picked = await _selectDate(context, initial); - if (picked != null) { - String formatted = DateFormat('yyyy-MM-dd', "en_US").format(picked); - setState(() { - model.getContactDetailsList!.sEGMENTVALUEDSP = formatted; - model.getContactDetailsList!.dATEVALUE = formatted; - model.getContactDetailsList!.vARCHAR2VALUE = formatted; - }); - } - }, + borderColor: effectiveBorderColor, + borderWidth: effectiveBorderWidth, + labelColor: effectiveLabelColor, + textColor: effectiveTextColor, + suffixIconColor: effectiveSuffixColor, + onTap: isFieldDisabled + ? null + : () async { + DateTime initial = DateTime.now(); + if (displayVal.isNotEmpty) { + try { + initial = DateFormat('yyyy-MM-dd').parse(displayVal); + } catch (_) { + try { + initial = DateFormat('dd/MM/yyyy').parse(displayVal); + } catch (_) {} + } + } + DateTime? picked = await _selectDate(context, initial); + if (picked != null) { + String formatted = DateFormat('yyyy-MM-dd', "en_US").format(picked); + setState(() { + model.getContactDetailsList!.sEGMENTVALUEDSP = formatted; + model.getContactDetailsList!.dATEVALUE = formatted; + model.getContactDetailsList!.vARCHAR2VALUE = formatted; + }); + } + }, ), ); } @@ -913,14 +1233,17 @@ class _AddDependentVerifyScreenState extends State { } bool isRequired = model.rEQUIREDFLAG == "Y"; - String label = (model.sEGMENTPROMPT ?? "") + (isRequired ? "*" : ""); + String rawPrompt = (model.sEGMENTPROMPT ?? "").trim(); + String label = rawPrompt.endsWith("*") + ? rawPrompt + : (isRequired ? "$rawPrompt*" : rawPrompt); model.getContactDetailsList ??= GetContactDetailsList(); + String colName = (model.aPPLICATIONCOLUMNNAME ?? "").toUpperCase().trim(); + String prompt = rawPrompt.toLowerCase(); String colKey = model.aPPLICATIONCOLUMNNAME ?? "BASIC_DFF_$index"; if (model.fORMATTYPE == "C") { if (model.eSERVICESVS != null && model.eSERVICESVS!.isNotEmpty) { - String colName = (model.aPPLICATIONCOLUMNNAME ?? "").toUpperCase(); - String prompt = (model.sEGMENTPROMPT ?? "").toLowerCase(); bool isSponsorRelated = colName.contains("SPONSOR") || prompt.contains("sponsor") || prompt.contains("كفيل"); bool valueInOptions = model.eSERVICESVS!.any((opt) => opt.vALUECOLUMNNAME == model.getContactDetailsList!.sEGMENTVALUEDSP); @@ -930,94 +1253,180 @@ class _AddDependentVerifyScreenState extends State { } String displayVal = model.getContactDetailsList!.sEGMENTVALUEDSP ?? ""; + bool isFieldDisabled = _isFieldPrefilledFromPatientInfo(colName, prompt, displayVal); + bool hasSelectedValue = displayVal.trim().isNotEmpty; + bool showRedBorder = isRequired && !isFieldDisabled && !hasSelectedValue; + Color effectiveBorderColor = showRedBorder + ? const Color(0xFFED1C2B) + : const Color(0xFFE6E9E8); + double effectiveBorderWidth = showRedBorder ? 1.4 : 1.0; + Color effectiveLabelColor = isFieldDisabled ? const Color(0xFF8E95A2) : const Color(0xff2B353E); + Color effectiveTextColor = isFieldDisabled + ? const Color(0xFF8E95A2) + : (displayVal.isNotEmpty ? const Color(0xff2B353E) : const Color(0xFF94A3B8)); + Color effectiveSuffixColor = isFieldDisabled ? const Color(0xFF8E95A2) : const Color(0xFF64748B); + return Padding( padding: const EdgeInsets.only(bottom: 12), child: PopupMenuButton( - child: DynamicTextFieldWidget( + enabled: !isFieldDisabled, + child: _buildFigmaTextField( label, displayVal.isNotEmpty ? displayVal : LocaleKeys.select.tr(), isEnable: false, isPopup: true, backgroundColor: Colors.white, + borderColor: effectiveBorderColor, + borderWidth: effectiveBorderWidth, + labelColor: effectiveLabelColor, + textColor: effectiveTextColor, + suffixIconColor: effectiveSuffixColor, ), - itemBuilder: (_) => >[ - for (int i = 0; i < model.eSERVICESVS!.length; i++) - PopupMenuItem( - value: i, - child: (model.eSERVICESVS![i].vALUECOLUMNNAME ?? "").toText14(), - ), - ], - onSelected: (int selectedIdx) { - setState(() { - model.getContactDetailsList!.sEGMENTVALUEDSP = model.eSERVICESVS![selectedIdx].vALUECOLUMNNAME; - model.getContactDetailsList!.vARCHAR2VALUE = model.eSERVICESVS![selectedIdx].iDCOLUMNNAME; - }); - }, + itemBuilder: (_) => isFieldDisabled + ? >[] + : >[ + for (int i = 0; i < model.eSERVICESVS!.length; i++) + PopupMenuItem( + value: i, + child: (model.eSERVICESVS![i].vALUECOLUMNNAME ?? "").toText14(), + ), + ], + onSelected: isFieldDisabled + ? null + : (int selectedIdx) { + setState(() { + model.getContactDetailsList!.sEGMENTVALUEDSP = model.eSERVICESVS![selectedIdx].vALUECOLUMNNAME; + model.getContactDetailsList!.vARCHAR2VALUE = model.eSERVICESVS![selectedIdx].iDCOLUMNNAME; + }); + }, ), ); } else { var controller = _getControllerForField(colKey, model.getContactDetailsList!.sEGMENTVALUEDSP); + bool isFieldDisabled = _isFieldPrefilledFromPatientInfo(colName, prompt, controller.text); + bool hasValue = controller.text.trim().isNotEmpty || (model.getContactDetailsList?.sEGMENTVALUEDSP?.trim().isNotEmpty ?? false); + bool showRedBorder = isRequired && !isFieldDisabled && !hasValue; + Color effectiveBorderColor = showRedBorder + ? const Color(0xFFED1C2B) + : const Color(0xFFE6E9E8); + double effectiveBorderWidth = showRedBorder ? 1.4 : 1.0; + Color effectiveLabelColor = isFieldDisabled ? const Color(0xFF8E95A2) : const Color(0xff2B353E); + Color effectiveTextColor = isFieldDisabled ? const Color(0xFF8E95A2) : const Color(0xff2B353E); + Color effectiveSuffixColor = isFieldDisabled ? const Color(0xFF8E95A2) : const Color(0xFF64748B); + return Padding( padding: const EdgeInsets.only(bottom: 12), - child: DynamicTextFieldWidget( + child: _buildFigmaTextField( label, LocaleKeys.enterFieldPlaceholder.tr(args: [label]), controller: controller, + isEnable: !isFieldDisabled, + isReadOnly: isFieldDisabled, backgroundColor: Colors.white, - onChange: (String text) { - model.getContactDetailsList!.sEGMENTVALUEDSP = text; - model.getContactDetailsList!.vARCHAR2VALUE = text; - }, + borderColor: effectiveBorderColor, + borderWidth: effectiveBorderWidth, + labelColor: effectiveLabelColor, + textColor: effectiveTextColor, + suffixIconData: _getSuffixIconForField(colName, prompt, "VARCHAR2"), + suffixIconColor: effectiveSuffixColor, + onChange: isFieldDisabled + ? null + : (String text) { + model.getContactDetailsList!.sEGMENTVALUEDSP = text; + model.getContactDetailsList!.vARCHAR2VALUE = text; + }, ), ); } } else if (model.fORMATTYPE == "N") { var controller = _getControllerForField(colKey, model.getContactDetailsList!.sEGMENTVALUEDSP); + bool isFieldDisabled = _isFieldPrefilledFromPatientInfo(colName, prompt, controller.text); + bool hasValue = controller.text.trim().isNotEmpty || (model.getContactDetailsList?.sEGMENTVALUEDSP?.trim().isNotEmpty ?? false); + bool showRedBorder = isRequired && !isFieldDisabled && !hasValue; + Color effectiveBorderColor = showRedBorder + ? const Color(0xFFED1C2B) + : const Color(0xFFE6E9E8); + double effectiveBorderWidth = showRedBorder ? 1.4 : 1.0; + Color effectiveLabelColor = isFieldDisabled ? const Color(0xFF8E95A2) : const Color(0xff2B353E); + Color effectiveTextColor = isFieldDisabled ? const Color(0xFF8E95A2) : const Color(0xff2B353E); + Color effectiveSuffixColor = isFieldDisabled ? const Color(0xFF8E95A2) : const Color(0xFF64748B); + return Padding( padding: const EdgeInsets.only(bottom: 12), - child: DynamicTextFieldWidget( + child: _buildFigmaTextField( label, LocaleKeys.enterFieldPlaceholder.tr(args: [label]), controller: controller, + isEnable: !isFieldDisabled, + isReadOnly: isFieldDisabled, isInputTypeNum: true, backgroundColor: Colors.white, - onChange: (String text) { - model.getContactDetailsList!.sEGMENTVALUEDSP = text; - model.getContactDetailsList!.vARCHAR2VALUE = text; - }, + borderColor: effectiveBorderColor, + borderWidth: effectiveBorderWidth, + labelColor: effectiveLabelColor, + textColor: effectiveTextColor, + suffixIconData: _getSuffixIconForField(colName, prompt, "NUMBER"), + suffixIconColor: effectiveSuffixColor, + onChange: isFieldDisabled + ? null + : (String text) { + model.getContactDetailsList!.sEGMENTVALUEDSP = text; + model.getContactDetailsList!.vARCHAR2VALUE = text; + }, ), ); } else if (model.fORMATTYPE == "X") { String displayVal = model.getContactDetailsList!.sEGMENTVALUEDSP ?? ""; + bool isFieldDisabled = _isFieldPrefilledFromPatientInfo(colName, prompt, displayVal); + bool hasSelectedValue = displayVal.trim().isNotEmpty; + bool showRedBorder = isRequired && !isFieldDisabled && !hasSelectedValue; + Color effectiveBorderColor = showRedBorder + ? const Color(0xFFED1C2B) + : const Color(0xFFE6E9E8); + double effectiveBorderWidth = showRedBorder ? 1.4 : 1.0; + Color effectiveLabelColor = isFieldDisabled ? const Color(0xFF8E95A2) : const Color(0xff2B353E); + Color effectiveTextColor = isFieldDisabled + ? const Color(0xFF8E95A2) + : (displayVal.isNotEmpty ? const Color(0xff2B353E) : const Color(0xFF94A3B8)); + Color effectiveSuffixColor = isFieldDisabled ? const Color(0xFF8E95A2) : const Color(0xFF64748B); + return Padding( padding: const EdgeInsets.only(bottom: 12), - child: DynamicTextFieldWidget( + child: _buildFigmaTextField( label, displayVal.isNotEmpty ? displayVal : LocaleKeys.dateFormatPlaceholder.tr(), suffixIconData: Icons.calendar_today_outlined, + isEnable: !isFieldDisabled, isReadOnly: true, backgroundColor: Colors.white, - onTap: () async { - DateTime initial = DateTime.now(); - if (displayVal.isNotEmpty) { - try { - initial = DateFormat('yyyy-MM-dd').parse(displayVal); - } catch (_) { - try { - initial = DateFormat('dd/MM/yyyy').parse(displayVal); - } catch (_) {} - } - } - DateTime? picked = await _selectDate(context, initial); - if (picked != null) { - String formatted = DateFormat('yyyy-MM-dd', "en_US").format(picked); - setState(() { - model.getContactDetailsList!.sEGMENTVALUEDSP = formatted; - model.getContactDetailsList!.dATEVALUE = formatted; - model.getContactDetailsList!.vARCHAR2VALUE = formatted; - }); - } - }, + borderColor: effectiveBorderColor, + borderWidth: effectiveBorderWidth, + labelColor: effectiveLabelColor, + textColor: effectiveTextColor, + suffixIconColor: effectiveSuffixColor, + onTap: isFieldDisabled + ? null + : () async { + DateTime initial = DateTime.now(); + if (displayVal.isNotEmpty) { + try { + initial = DateFormat('yyyy-MM-dd').parse(displayVal); + } catch (_) { + try { + initial = DateFormat('dd/MM/yyyy').parse(displayVal); + } catch (_) {} + } + } + DateTime? picked = await _selectDate(context, initial); + if (picked != null) { + String formatted = DateFormat('yyyy-MM-dd', "en_US").format(picked); + setState(() { + model.getContactDetailsList!.sEGMENTVALUEDSP = formatted; + model.getContactDetailsList!.dATEVALUE = formatted; + model.getContactDetailsList!.vARCHAR2VALUE = formatted; + }); + } + }, ), ); } @@ -1031,14 +1440,17 @@ class _AddDependentVerifyScreenState extends State { } bool isRequired = model.rEQUIREDFLAG == "Y"; - String label = (model.sEGMENTPROMPT ?? "") + (isRequired ? "*" : ""); + String rawPrompt = (model.sEGMENTPROMPT ?? "").trim(); + String label = rawPrompt.endsWith("*") + ? rawPrompt + : (isRequired ? "$rawPrompt*" : rawPrompt); model.getContactDetailsList ??= GetContactDetailsList(); + String colName = (model.aPPLICATIONCOLUMNNAME ?? "").toUpperCase().trim(); + String prompt = rawPrompt.toLowerCase(); String colKey = model.aPPLICATIONCOLUMNNAME ?? "CONTACT_DFF_$index"; if (model.fORMATTYPE == "C") { if (model.eSERVICESVS != null && model.eSERVICESVS!.isNotEmpty) { - String colName = (model.aPPLICATIONCOLUMNNAME ?? "").toUpperCase(); - String prompt = (model.sEGMENTPROMPT ?? "").toLowerCase(); bool isSponsorRelated = colName.contains("SPONSOR") || prompt.contains("sponsor") || prompt.contains("كفيل"); bool valueInOptions = model.eSERVICESVS!.any((opt) => opt.vALUECOLUMNNAME == model.getContactDetailsList!.sEGMENTVALUEDSP); @@ -1048,94 +1460,180 @@ class _AddDependentVerifyScreenState extends State { } String displayVal = model.getContactDetailsList!.sEGMENTVALUEDSP ?? ""; + bool isFieldDisabled = _isFieldPrefilledFromPatientInfo(colName, prompt, displayVal); + bool hasSelectedValue = displayVal.trim().isNotEmpty; + bool showRedBorder = isRequired && !isFieldDisabled && !hasSelectedValue; + Color effectiveBorderColor = showRedBorder + ? const Color(0xFFED1C2B) + : const Color(0xFFE6E9E8); + double effectiveBorderWidth = showRedBorder ? 1.4 : 1.0; + Color effectiveLabelColor = isFieldDisabled ? const Color(0xFF8E95A2) : const Color(0xff2B353E); + Color effectiveTextColor = isFieldDisabled + ? const Color(0xFF8E95A2) + : (displayVal.isNotEmpty ? const Color(0xff2B353E) : const Color(0xFF94A3B8)); + Color effectiveSuffixColor = isFieldDisabled ? const Color(0xFF8E95A2) : const Color(0xFF64748B); + return Padding( padding: const EdgeInsets.only(bottom: 12), child: PopupMenuButton( - child: DynamicTextFieldWidget( + enabled: !isFieldDisabled, + child: _buildFigmaTextField( label, displayVal.isNotEmpty ? displayVal : LocaleKeys.select.tr(), isEnable: false, isPopup: true, backgroundColor: Colors.white, + borderColor: effectiveBorderColor, + borderWidth: effectiveBorderWidth, + labelColor: effectiveLabelColor, + textColor: effectiveTextColor, + suffixIconColor: effectiveSuffixColor, ), - itemBuilder: (_) => >[ - for (int i = 0; i < model.eSERVICESVS!.length; i++) - PopupMenuItem( - value: i, - child: (model.eSERVICESVS![i].vALUECOLUMNNAME ?? "").toText14(), - ), - ], - onSelected: (int selectedIdx) { - setState(() { - model.getContactDetailsList!.sEGMENTVALUEDSP = model.eSERVICESVS![selectedIdx].vALUECOLUMNNAME; - model.getContactDetailsList!.vARCHAR2VALUE = model.eSERVICESVS![selectedIdx].iDCOLUMNNAME; - }); - }, + itemBuilder: (_) => isFieldDisabled + ? >[] + : >[ + for (int i = 0; i < model.eSERVICESVS!.length; i++) + PopupMenuItem( + value: i, + child: (model.eSERVICESVS![i].vALUECOLUMNNAME ?? "").toText14(), + ), + ], + onSelected: isFieldDisabled + ? null + : (int selectedIdx) { + setState(() { + model.getContactDetailsList!.sEGMENTVALUEDSP = model.eSERVICESVS![selectedIdx].vALUECOLUMNNAME; + model.getContactDetailsList!.vARCHAR2VALUE = model.eSERVICESVS![selectedIdx].iDCOLUMNNAME; + }); + }, ), ); } else { var controller = _getControllerForField(colKey, model.getContactDetailsList!.sEGMENTVALUEDSP); + bool isFieldDisabled = _isFieldPrefilledFromPatientInfo(colName, prompt, controller.text); + bool hasValue = controller.text.trim().isNotEmpty || (model.getContactDetailsList?.sEGMENTVALUEDSP?.trim().isNotEmpty ?? false); + bool showRedBorder = isRequired && !isFieldDisabled && !hasValue; + Color effectiveBorderColor = showRedBorder + ? const Color(0xFFED1C2B) + : const Color(0xFFE6E9E8); + double effectiveBorderWidth = showRedBorder ? 1.4 : 1.0; + Color effectiveLabelColor = isFieldDisabled ? const Color(0xFF8E95A2) : const Color(0xff2B353E); + Color effectiveTextColor = isFieldDisabled ? const Color(0xFF8E95A2) : const Color(0xff2B353E); + Color effectiveSuffixColor = isFieldDisabled ? const Color(0xFF8E95A2) : const Color(0xFF64748B); + return Padding( padding: const EdgeInsets.only(bottom: 12), - child: DynamicTextFieldWidget( + child: _buildFigmaTextField( label, LocaleKeys.enterFieldPlaceholder.tr(args: [label]), controller: controller, + isEnable: !isFieldDisabled, + isReadOnly: isFieldDisabled, backgroundColor: Colors.white, - onChange: (String text) { - model.getContactDetailsList!.sEGMENTVALUEDSP = text; - model.getContactDetailsList!.vARCHAR2VALUE = text; - }, + borderColor: effectiveBorderColor, + borderWidth: effectiveBorderWidth, + labelColor: effectiveLabelColor, + textColor: effectiveTextColor, + suffixIconData: _getSuffixIconForField(colName, prompt, "VARCHAR2"), + suffixIconColor: effectiveSuffixColor, + onChange: isFieldDisabled + ? null + : (String text) { + model.getContactDetailsList!.sEGMENTVALUEDSP = text; + model.getContactDetailsList!.vARCHAR2VALUE = text; + }, ), ); } } else if (model.fORMATTYPE == "N") { var controller = _getControllerForField(colKey, model.getContactDetailsList!.sEGMENTVALUEDSP); + bool isFieldDisabled = _isFieldPrefilledFromPatientInfo(colName, prompt, controller.text); + bool hasValue = controller.text.trim().isNotEmpty || (model.getContactDetailsList?.sEGMENTVALUEDSP?.trim().isNotEmpty ?? false); + bool showRedBorder = isRequired && !isFieldDisabled && !hasValue; + Color effectiveBorderColor = showRedBorder + ? const Color(0xFFED1C2B) + : const Color(0xFFE6E9E8); + double effectiveBorderWidth = showRedBorder ? 1.4 : 1.0; + Color effectiveLabelColor = isFieldDisabled ? const Color(0xFF8E95A2) : const Color(0xff2B353E); + Color effectiveTextColor = isFieldDisabled ? const Color(0xFF8E95A2) : const Color(0xff2B353E); + Color effectiveSuffixColor = isFieldDisabled ? const Color(0xFF8E95A2) : const Color(0xFF64748B); + return Padding( padding: const EdgeInsets.only(bottom: 12), - child: DynamicTextFieldWidget( + child: _buildFigmaTextField( label, LocaleKeys.enterFieldPlaceholder.tr(args: [label]), controller: controller, + isEnable: !isFieldDisabled, + isReadOnly: isFieldDisabled, isInputTypeNum: true, backgroundColor: Colors.white, - onChange: (String text) { - model.getContactDetailsList!.sEGMENTVALUEDSP = text; - model.getContactDetailsList!.vARCHAR2VALUE = text; - }, + borderColor: effectiveBorderColor, + borderWidth: effectiveBorderWidth, + labelColor: effectiveLabelColor, + textColor: effectiveTextColor, + suffixIconData: _getSuffixIconForField(colName, prompt, "NUMBER"), + suffixIconColor: effectiveSuffixColor, + onChange: isFieldDisabled + ? null + : (String text) { + model.getContactDetailsList!.sEGMENTVALUEDSP = text; + model.getContactDetailsList!.vARCHAR2VALUE = text; + }, ), ); } else if (model.fORMATTYPE == "X") { String displayVal = model.getContactDetailsList?.dATEVALUE ?? model.getContactDetailsList?.sEGMENTVALUEDSP ?? ""; + bool isFieldDisabled = _isFieldPrefilledFromPatientInfo(colName, prompt, displayVal); + bool hasSelectedValue = displayVal.trim().isNotEmpty; + bool showRedBorder = isRequired && !isFieldDisabled && !hasSelectedValue; + Color effectiveBorderColor = showRedBorder + ? const Color(0xFFED1C2B) + : const Color(0xFFE6E9E8); + double effectiveBorderWidth = showRedBorder ? 1.4 : 1.0; + Color effectiveLabelColor = isFieldDisabled ? const Color(0xFF8E95A2) : const Color(0xff2B353E); + Color effectiveTextColor = isFieldDisabled + ? const Color(0xFF8E95A2) + : (displayVal.isNotEmpty ? const Color(0xff2B353E) : const Color(0xFF94A3B8)); + Color effectiveSuffixColor = isFieldDisabled ? const Color(0xFF8E95A2) : const Color(0xFF64748B); + return Padding( padding: const EdgeInsets.only(bottom: 12), - child: DynamicTextFieldWidget( + child: _buildFigmaTextField( label, displayVal.isNotEmpty ? displayVal : LocaleKeys.dateFormatPlaceholder.tr(), suffixIconData: Icons.calendar_today_outlined, + isEnable: !isFieldDisabled, isReadOnly: true, backgroundColor: Colors.white, - onTap: () async { - DateTime initial = DateTime.now(); - if (displayVal.isNotEmpty) { - try { - initial = DateFormat('yyyy-MM-dd').parse(displayVal); - } catch (_) { - try { - initial = DateFormat('dd/MM/yyyy').parse(displayVal); - } catch (_) {} - } - } - DateTime? picked = await _selectDate(context, initial); - if (picked != null) { - String formatted = DateFormat('yyyy-MM-dd', "en_US").format(picked); - setState(() { - model.getContactDetailsList!.sEGMENTVALUEDSP = formatted; - model.getContactDetailsList!.dATEVALUE = formatted; - model.getContactDetailsList!.vARCHAR2VALUE = formatted; - }); - } - }, + borderColor: effectiveBorderColor, + borderWidth: effectiveBorderWidth, + labelColor: effectiveLabelColor, + textColor: effectiveTextColor, + suffixIconColor: effectiveSuffixColor, + onTap: isFieldDisabled + ? null + : () async { + DateTime initial = DateTime.now(); + if (displayVal.isNotEmpty) { + try { + initial = DateFormat('yyyy-MM-dd').parse(displayVal); + } catch (_) { + try { + initial = DateFormat('dd/MM/yyyy').parse(displayVal); + } catch (_) {} + } + } + DateTime? picked = await _selectDate(context, initial); + if (picked != null) { + String formatted = DateFormat('yyyy-MM-dd', "en_US").format(picked); + setState(() { + model.getContactDetailsList!.sEGMENTVALUEDSP = formatted; + model.getContactDetailsList!.dATEVALUE = formatted; + model.getContactDetailsList!.vARCHAR2VALUE = formatted; + }); + } + }, ), ); } @@ -1570,7 +2068,7 @@ class _AddDependentVerifyScreenState extends State { const SizedBox(height: 24), // National ID - DynamicTextFieldWidget( + _buildFigmaTextField( "${LocaleKeys.nationalId.tr()}*", _isLegislationSA ? LocaleKeys.enter10DigitNationalId.tr() @@ -1580,6 +2078,10 @@ class _AddDependentVerifyScreenState extends State { isInputTypeNum: _isLegislationSA, maxLength: _isLegislationSA ? 10 : 25, backgroundColor: Colors.white, + borderColor: const Color(0xFFE6E9E8), + labelColor: const Color(0xff2B353E), + textColor: const Color(0xff2B353E), + suffixIconColor: const Color(0xFF64748B), inputFormatters: _isLegislationSA ? [ FilteringTextInputFormatter.digitsOnly, @@ -1592,11 +2094,15 @@ class _AddDependentVerifyScreenState extends State { const SizedBox(height: 16), // Date of Birth - DynamicTextFieldWidget( + _buildFigmaTextField( "${LocaleKeys.dateOfBirth.tr()}*", dobDisplay, isReadOnly: true, backgroundColor: Colors.white, + borderColor: const Color(0xFFE6E9E8), + labelColor: const Color(0xff2B353E), + textColor: _selectedDob != null ? const Color(0xff2B353E) : const Color(0xFF94A3B8), + suffixIconColor: const Color(0xFF64748B), suffixIconData: Icons.calendar_today_outlined, onTap: () => _pickDateOfBirth(context), ), @@ -1838,13 +2344,15 @@ class _AddDependentVerifyScreenState extends State { // --- Bottom Buttons (Always reusing DefaultButton with app gradient!) --- Widget _buildBottomButtons(bool isAr) { if (_currentStep == 1) { + bool canProceed = _isStep1Valid() && !_isLoading; return DefaultButton( _isLegislationSA ? LocaleKeys.searchUser.tr() : LocaleKeys.next.tr(), - _isLoading ? null : _onSearchUser, + canProceed ? _onSearchUser : null, iconData: _isLegislationSA ? Icons.search : null, isTextExpanded: false, height: 48, borderRadius: 12, + textColor: canProceed ? Colors.white : const Color(0xFF8E95A2), ).insideContainer; } @@ -1854,16 +2362,20 @@ class _AddDependentVerifyScreenState extends State { }); }; + bool canProceed = false; VoidCallback onNext; String nextLabel; if (_currentStep == 2) { + canProceed = _isStep2Valid(); onNext = _onNextFromBasicInfo; nextLabel = LocaleKeys.next.tr(); } else if (_currentStep == 3) { + canProceed = _isStep3Valid(); onNext = _onNextFromAdditionalInfo; nextLabel = LocaleKeys.next.tr(); } else { + canProceed = true; onNext = _onSubmitTransaction; nextLabel = LocaleKeys.submitRequest.tr(); } @@ -1890,9 +2402,10 @@ class _AddDependentVerifyScreenState extends State { Expanded( child: DefaultButton( nextLabel, - onNext, + canProceed ? onNext : null, height: 48, borderRadius: 12, + textColor: canProceed ? Colors.white : const Color(0xFF8E95A2), ), ), ], diff --git a/lib/ui/profile/family_members.dart b/lib/ui/profile/family_members.dart index eb9ddc0..0957db3 100644 --- a/lib/ui/profile/family_members.dart +++ b/lib/ui/profile/family_members.dart @@ -110,33 +110,17 @@ class _FamilyMembersState extends State { return "${parts.first[0]}${parts.last[0]}".toUpperCase(); } - String _getIdEnding(GetEmployeeContactsList contact) { - String raw = contact.pERSONID?.toString() ?? - contact.cONTACTPERSONID?.toString() ?? - contact.cONTACTRELATIONSHIPID?.toString() ?? - ""; - if (raw.length > 4) { - return raw.substring(raw.length - 4); - } - if (raw.isNotEmpty) { - return raw.padLeft(4, '0'); - } - return ""; - } - 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 id = _getIdEnding(item); String personId = (item.pERSONID?.toString() ?? "").toLowerCase(); String contactPersonId = (item.cONTACTPERSONID?.toString() ?? "").toLowerCase(); String contactType = (item.cONTACTTYPE ?? "").toLowerCase(); return name.contains(q) || rel.contains(q) || - id.contains(q) || personId.contains(q) || contactPersonId.contains(q) || contactType.contains(q); @@ -178,12 +162,13 @@ class _FamilyMembersState extends State { List displayList = _filteredList; return Scaffold( - backgroundColor: Colors.white, + 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: [ @@ -342,7 +327,7 @@ class _FamilyMembersState extends State { bool isAllowed = _isAccessAllowed(item); return Container( - padding: const EdgeInsets.all(16), + padding: const EdgeInsets.fromLTRB(16, 16, 16, 14), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(16), @@ -364,16 +349,20 @@ class _FamilyMembersState extends State { children: [ // Initials Circle Container( - width: 44, - height: 44, + width: 48, + height: 48, decoration: const BoxDecoration( - color: Color(0xFFE6F7F2), + color: Color(0xFFE6F5F3), shape: BoxShape.circle, ), child: Center( - child: _getInitials(item.cONTACTNAME).toText16( - isBold: true, - color: const Color(0xFF168A89), + child: Text( + _getInitials(item.cONTACTNAME), + style: const TextStyle( + color: Color(0xFF168A89), + fontWeight: FontWeight.w700, + fontSize: 15, + ), ), ), ), @@ -384,10 +373,15 @@ class _FamilyMembersState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - (item.cONTACTNAME ?? "").toText16( - isBold: true, - maxlines: 1, - color: const Color(0xFF1E293B), + Text( + item.cONTACTNAME ?? "", + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.w700, + color: Color(0xFF1E293B), + ), ), const SizedBox(height: 3), Row( @@ -398,7 +392,6 @@ class _FamilyMembersState extends State { color: const Color(0xFF168A89), ), ), - ], ), ], @@ -411,11 +404,16 @@ class _FamilyMembersState extends State { Container( padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4), decoration: BoxDecoration( - color: isAllowed ? const Color(0xFFE8F8F0) : const Color(0xFFFEECEB), + color: isAllowed ? const Color(0xFFE8F8F0) : const Color(0xFFFDE8E8), borderRadius: BorderRadius.circular(20), ), - child: (isAllowed ? LocaleKeys.active.tr() : LocaleKeys.noAccess.tr()).toText11( - color: isAllowed ? const Color(0xFF2DBD72) : const Color(0xFFED1C24), + child: Text( + isAllowed ? LocaleKeys.active.tr() : LocaleKeys.noAccess.tr(), + style: TextStyle( + fontSize: 11, + fontWeight: FontWeight.w500, + color: isAllowed ? const Color(0xFF168A89) : const Color(0xFFDC2626), + ), ), ), ], @@ -423,32 +421,41 @@ class _FamilyMembersState extends State { ), 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 - InkWell( - borderRadius: BorderRadius.circular(10), - onTap: () => _toggleMazayaAccess(item), - child: Container( - width: double.infinity, - height: 40, - decoration: BoxDecoration( - color: isAllowed ? const Color(0xFFFFF1F0) : const Color(0xFFE6F7F2), - borderRadius: BorderRadius.circular(10), - border: Border.all( - color: isAllowed ? const Color(0xFFFFD9D7) : const Color(0xFFCEEFE7), - width: 0.8, - ), - ), - child: Center( - child: (isAllowed - ? LocaleKeys.revokeMazayaAccess.tr() - : LocaleKeys.allowMazayaAccess.tr()) - .toText14( - isBold: true, - isCenter: true, - color: isAllowed ? const Color(0xFFED1C24) : const Color(0xFF168A89), + 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.w700, + color: isAllowed ? const Color(0xFFDC2626) : const Color(0xFF168A89), + decoration: TextDecoration.underline, + decorationColor: isAllowed ? const Color(0xFFDC2626) : const Color(0xFF168A89), + ), + ), + ], ), ), ), diff --git a/lib/ui/profile/mazaya_access_result_screen.dart b/lib/ui/profile/mazaya_access_result_screen.dart index 032d84e..2c20179 100644 --- a/lib/ui/profile/mazaya_access_result_screen.dart +++ b/lib/ui/profile/mazaya_access_result_screen.dart @@ -142,6 +142,15 @@ class MazayaAccessResultScreen extends StatelessWidget { effectiveUrl = routeArgs['downloadUrl'] ?? downloadUrl; } + if (effectiveName.trim().isEmpty) { + String fallback = AppState().dependentName ?? ""; + if (fallback.isNotEmpty) { + effectiveName = fallback; + } else { + effectiveName = isAr ? "التابع" : "Dependent"; + } + } + String linkToShare = effectiveUrl ?? getDownloadLink(); String initials = effectiveInitials ?? _getInitials(effectiveName); diff --git a/lib/widgets/dynamic_forms/dynamic_textfield_widget.dart b/lib/widgets/dynamic_forms/dynamic_textfield_widget.dart index 4fd5fd2..f03a833 100644 --- a/lib/widgets/dynamic_forms/dynamic_textfield_widget.dart +++ b/lib/widgets/dynamic_forms/dynamic_textfield_widget.dart @@ -21,6 +21,15 @@ class DynamicTextFieldWidget extends StatelessWidget { final int? maxLength; final List? inputFormatters; final Color? backgroundColor; + final Color? borderColor; + final double? borderWidth; + final Color? labelColor; + final Color? textColor; + final Color? suffixIconColor; + final BorderRadius? borderRadius; + final List? boxShadow; + final BoxConstraints? constraints; + final EdgeInsetsGeometry? padding; DynamicTextFieldWidget(this.labelText, this.hintText, {this.controller, @@ -38,20 +47,37 @@ class DynamicTextFieldWidget extends StatelessWidget { this.isBackgroundEnable = false, this.maxLength, this.inputFormatters, - this.backgroundColor}); + this.backgroundColor, + this.borderColor, + this.borderWidth, + this.labelColor, + this.textColor, + this.suffixIconColor, + this.borderRadius, + this.boxShadow, + this.constraints, + this.padding}); @override Widget build(BuildContext context) { + Color effectiveBorderColor = borderColor ?? MyColors.lightGreyEFColor; + double effectiveBorderWidth = borderWidth ?? 1.0; + Color effectiveLabelColor = labelColor ?? const Color(0xff2B353E); + Color effectiveTextColor = textColor ?? const Color(0xff2B353E); + Color effectiveSuffixColor = suffixIconColor ?? MyColors.darkIconColor; + return Container( - padding: const EdgeInsets.only(left: 16, right: 16, bottom: 15, top: 15), + constraints: constraints, + padding: padding ?? const EdgeInsets.only(left: 16, right: 16, bottom: 15, top: 15), alignment: Alignment.center, decoration: BoxDecoration( - borderRadius: BorderRadius.circular(15), + borderRadius: borderRadius ?? BorderRadius.circular(15), color: backgroundColor ?? (isReadOnly ? MyColors.lightGreyEFColor : Colors.white), border: Border.all( - color: MyColors.lightGreyEFColor, - width: 1, + color: effectiveBorderColor, + width: effectiveBorderWidth, ), + boxShadow: boxShadow, ), child: InkWell( onTap: onTap, @@ -65,10 +91,10 @@ class DynamicTextFieldWidget extends StatelessWidget { if (labelText.isNotEmpty) Text( labelText, - style: const TextStyle( + style: TextStyle( fontSize: 11, fontWeight: FontWeight.w600, - color: Color(0xff2B353E), + color: effectiveLabelColor, letterSpacing: -0.44, ), ), @@ -87,11 +113,11 @@ class DynamicTextFieldWidget extends StatelessWidget { obscuringCharacter: "*", obscureText: isObscureText, onChanged: onChange, - style: const TextStyle( + style: TextStyle( fontSize: 14, height: 21 / 14, fontWeight: FontWeight.w400, - color: Color(0xff2B353E), + color: effectiveTextColor, letterSpacing: -0.44, ), decoration: InputDecoration( @@ -106,9 +132,6 @@ class DynamicTextFieldWidget extends StatelessWidget { color: Color(0xff575757), letterSpacing: -0.56, ), - // suffixIconConstraints: const BoxConstraints(minWidth: 50), - // suffixIcon: suffixIconData == null ? null : Icon(suffixIconData, color: MyColors.darkTextColor), - // suffixIcon: suffixTap == null ? null : IconButton(icon: Icon(suffixIconData, color: MyColors.darkTextColor), onPressed: suffixTap), contentPadding: EdgeInsets.zero, border: InputBorder.none, focusedBorder: InputBorder.none, @@ -118,8 +141,12 @@ class DynamicTextFieldWidget extends StatelessWidget { ], ), ), - if (isPopup) const Icon(Icons.keyboard_arrow_down_outlined, color: MyColors.darkIconColor), - if (onTap != null) Icon(suffixIconData ?? Icons.keyboard_arrow_down_outlined, color: MyColors.darkIconColor), + if (isPopup) + Icon(Icons.keyboard_arrow_down_outlined, color: effectiveSuffixColor) + else if (onTap != null) + Icon(suffixIconData ?? Icons.keyboard_arrow_down_outlined, color: effectiveSuffixColor) + else if (suffixIconData != null) + Icon(suffixIconData, color: effectiveSuffixColor), ], ), ),