no message

wala_plus
Sultan khan 1 day ago
parent 6295fcc55c
commit 84ff0798fa

@ -785,5 +785,21 @@
"noBranchesFound": "لم يتم العثور على فروع مطابقة",
"browseAllBranches": "تصفح جميع الفروع ({}) في مختلف المدن",
"branchesCount": "{} فرع",
"kmAway": "على بعد {} كم"
"kmAway": "على بعد {} كم",
"dependentDetails": "تفاصيل التابع",
"verifiedWithNhic": "موثق مع NHIC",
"yearsOld": "{} سنة",
"keyDetails": "البيانات الأساسية",
"verified": "موثق",
"idEnding": "الهوية تنتهي بـ {}",
"servicesAndActions": "الخدمات والإجراءات",
"startDependentFlowsDesc": "بدء إجراءات التابع وإدارة الوصول من مكان واحد.",
"mazayaAccess": "الوصول إلى مزايا",
"accessOff": "معطل",
"allowDependentMazayaOffersDesc": "السماح لـ {} باستخدام عروض مزايا بهويتها الموثقة.",
"loginMethod": "طريقة تسجيل الدخول",
"nationalIdAndDob": "الهوية الوطنية وتاريخ الميلاد",
"sponsorOtp": "رمز التحقق للكفيل",
"revokingPreventsAccessDesc": "إلغاء الوصول يمنع التابع من استخدام المزايا حتى تعيد السماح به مرة أخرى."
}

@ -785,5 +785,21 @@
"noBranchesFound": "No branches found",
"browseAllBranches": "Browse all {} branches across cities",
"branchesCount": "{} Branches",
"kmAway": "{} KM Away"
"kmAway": "{} KM Away",
"dependentDetails": "Dependent Details",
"verifiedWithNhic": "Verified with NHIC",
"yearsOld": "{} years",
"keyDetails": "Key details",
"verified": "Verified",
"idEnding": "ID ending {}",
"servicesAndActions": "Services & actions",
"startDependentFlowsDesc": "Start dependent flows and manage access from one place.",
"mazayaAccess": "Mazaya access",
"accessOff": "Off",
"allowDependentMazayaOffersDesc": "Allow {} to use Mazaya offers with her verified identity.",
"loginMethod": "Login method",
"nationalIdAndDob": "National ID & Date of Birth",
"sponsorOtp": "Sponsor OTP",
"revokingPreventsAccessDesc": "Revoking prevents all future dependent access until you allow it again."
}

@ -49,6 +49,7 @@ import 'package:mohem_flutter_app/ui/parking_qr/parking_qr.dart';
import 'package:mohem_flutter_app/ui/payslip/monthly_pay_slip_screen.dart';
import 'package:mohem_flutter_app/ui/performance/performance_appraisal_screen.dart';
import 'package:mohem_flutter_app/ui/profile/add_dependent_verify_screen.dart';
import 'package:mohem_flutter_app/ui/profile/dependent_details_screen.dart';
import 'package:mohem_flutter_app/ui/profile/mazaya_access_result_screen.dart';
import 'package:mohem_flutter_app/ui/profile/add_update_family_member.dart';
import 'package:mohem_flutter_app/ui/profile/basic_details.dart';
@ -150,6 +151,7 @@ class AppRoutes {
static const String deleteFamilyMember = "/deleteFamilyMember";
static const String addUpdateFamilyMember = "/addUpdateFamilyMember";
static const String addDependentVerify = "/addDependentVerify";
static const String dependentDetails = "/dependentDetails";
static const String mazayaAccessResult = "/mazayaAccessResult";
// Mowadhafhi
@ -304,6 +306,7 @@ class AppRoutes {
requestSubmitScreen: (BuildContext context) => RequestSubmitScreen(),
addUpdateFamilyMember: (BuildContext context) => AddUpdateFamilyMember(),
addDependentVerify: (BuildContext context) => const AddDependentVerifyScreen(),
dependentDetails: (BuildContext context) => const DependentDetailsScreen(),
mazayaAccessResult: (BuildContext context) => const MazayaAccessResultScreen(),
//mowadhafhi

@ -82,12 +82,12 @@ extension EmailValidator on String {
),
);
Widget toText12({Color? color, bool isUnderLine = false, Color? underlineColor, double underlineGap = 2, bool isBold = false, bool isCenter = false, int? maxLine, TextOverflow? overflow}) {
Widget toText12({Color? color, FontWeight? weight, bool isUnderLine = false, Color? underlineColor, double underlineGap = 2, bool isBold = false, bool isCenter = false, int? maxLine, TextOverflow? overflow}) {
Widget text = Text(
this,
textAlign: isCenter ? TextAlign.center : null,
maxLines: maxLine == 0 ? null : maxLine,
style: TextStyle(fontSize: 12, fontWeight: isBold ? FontWeight.bold : FontWeight.w600, color: color ?? MyColors.darkTextColor, letterSpacing: -0.72, overflow: TextOverflow.visible),
style: TextStyle(fontSize: 12, fontWeight: weight ?? (isBold ? FontWeight.bold : FontWeight.w600), color: color ?? MyColors.darkTextColor, letterSpacing: -0.72, overflow: TextOverflow.visible),
);
if (!isUnderLine) return text;
@ -129,13 +129,13 @@ extension EmailValidator on String {
),
);
Widget toText13({Color? color, bool isUnderLine = false, bool isBold = false, int? maxlines, TextOverflow? overflow}) => Text(
Widget toText13({Color? color, FontWeight? weight, bool isUnderLine = false, bool isBold = false, int? maxlines, TextOverflow? overflow}) => Text(
this,
maxLines: maxlines,
overflow: overflow,
style: TextStyle(
fontSize: 13,
fontWeight: isBold ? FontWeight.bold : FontWeight.w600,
fontWeight: weight ?? (isBold ? FontWeight.bold : FontWeight.w600),
color: color ?? MyColors.darkTextColor,
letterSpacing: -0.52,
decoration: isUnderLine ? TextDecoration.underline : null,
@ -156,7 +156,7 @@ extension EmailValidator on String {
),
);
Widget toText16({Color? color, bool isUnderLine = false, bool isBold = false, int? maxlines, double? height, bool isCentered = false}) => Text(
Widget toText16({Color? color, FontWeight? weight, bool isUnderLine = false, bool isBold = false, int? maxlines, double? height, bool isCentered = false}) => Text(
this,
maxLines: maxlines,
textAlign: isCentered ? TextAlign.center : null,
@ -165,30 +165,30 @@ extension EmailValidator on String {
fontSize: 16,
letterSpacing: -0.64,
height: height,
fontWeight: isBold ? FontWeight.bold : FontWeight.w600,
fontWeight: weight ?? (isBold ? FontWeight.bold : FontWeight.w600),
decoration: isUnderLine ? TextDecoration.underline : null,
),
);
Widget toText17({Color? color, bool isBold = false, bool isCentered = false}) => Text(
Widget toText17({Color? color, FontWeight? weight, bool isBold = false, bool isCentered = false}) => Text(
this,
textAlign: isCentered ? TextAlign.center : null,
style: TextStyle(color: color ?? MyColors.darkTextColor, fontSize: 17, letterSpacing: -0.68, fontWeight: isBold ? FontWeight.bold : FontWeight.w600),
style: TextStyle(color: color ?? MyColors.darkTextColor, fontSize: 17, letterSpacing: -0.68, fontWeight: weight ?? (isBold ? FontWeight.bold : FontWeight.w600)),
);
Widget toText18({Color? color, bool isBold = false, bool isCentered = false}) => Text(
Widget toText18({Color? color, FontWeight? weight, bool isBold = false, bool isCentered = false}) => Text(
this,
textAlign: isCentered ? TextAlign.center : null,
style: TextStyle(fontSize: 18, fontWeight: isBold ? FontWeight.bold : FontWeight.w600, color: color ?? MyColors.darkTextColor, letterSpacing: -1.08),
style: TextStyle(fontSize: 18, fontWeight: weight ?? (isBold ? FontWeight.bold : FontWeight.w600), color: color ?? MyColors.darkTextColor, letterSpacing: -1.08),
);
Widget toText19({Color? color, bool isBold = false}) =>
Text(this, style: TextStyle(fontSize: 19, fontWeight: isBold ? FontWeight.bold : FontWeight.w600, color: color ?? MyColors.darkTextColor, letterSpacing: -1.14));
Widget toText20({Color? color, bool isBold = false, bool isCentered = false}) => Text(
Widget toText20({Color? color, FontWeight? weight, bool isBold = false, bool isCentered = false}) => Text(
this,
textAlign: isCentered ? TextAlign.center : null,
style: TextStyle(fontSize: 20, fontWeight: isBold ? FontWeight.bold : FontWeight.w600, color: color ?? MyColors.darkTextColor, letterSpacing: -0.4),
style: TextStyle(fontSize: 20, fontWeight: weight ?? (isBold ? FontWeight.bold : FontWeight.w600), color: color ?? MyColors.darkTextColor, letterSpacing: -0.4),
);
Widget toText21({Color? color, bool isBold = false, FontWeight? weight, int? maxlines}) =>

@ -801,7 +801,22 @@ class CodegenLoader extends AssetLoader{
"noBranchesFound": "لم يتم العثور على فروع مطابقة",
"browseAllBranches": "تصفح جميع الفروع ({}) في مختلف المدن",
"branchesCount": "{} فرع",
"kmAway": "على بعد {} كم"
"kmAway": "على بعد {} كم",
"dependentDetails": "تفاصيل التابع",
"verifiedWithNhic": "موثق مع NHIC",
"yearsOld": "{} سنة",
"keyDetails": "البيانات الأساسية",
"verified": "موثق",
"idEnding": "الهوية تنتهي بـ {}",
"servicesAndActions": "الخدمات والإجراءات",
"startDependentFlowsDesc": "بدء إجراءات التابع وإدارة الوصول من مكان واحد.",
"mazayaAccess": "الوصول إلى مزايا",
"accessOff": "معطل",
"allowDependentMazayaOffersDesc": "السماح لـ {} باستخدام عروض مزايا بهويتها الموثقة.",
"loginMethod": "طريقة تسجيل الدخول",
"nationalIdAndDob": "الهوية الوطنية وتاريخ الميلاد",
"sponsorOtp": "رمز التحقق للكفيل",
"revokingPreventsAccessDesc": "إلغاء الوصول يمنع التابع من استخدام المزايا حتى تعيد السماح به مرة أخرى."
};
static const Map<String,dynamic> _en_US = {
"mohemm": "Mohemm",
@ -1590,7 +1605,22 @@ static const Map<String,dynamic> _en_US = {
"noBranchesFound": "No branches found",
"browseAllBranches": "Browse all {} branches across cities",
"branchesCount": "{} Branches",
"kmAway": "{} KM Away"
"kmAway": "{} KM Away",
"dependentDetails": "Dependent Details",
"verifiedWithNhic": "Verified with NHIC",
"yearsOld": "{} years",
"keyDetails": "Key details",
"verified": "Verified",
"idEnding": "ID ending {}",
"servicesAndActions": "Services & actions",
"startDependentFlowsDesc": "Start dependent flows and manage access from one place.",
"mazayaAccess": "Mazaya access",
"accessOff": "Off",
"allowDependentMazayaOffersDesc": "Allow {} to use Mazaya offers with her verified identity.",
"loginMethod": "Login method",
"nationalIdAndDob": "National ID & Date of Birth",
"sponsorOtp": "Sponsor OTP",
"revokingPreventsAccessDesc": "Revoking prevents all future dependent access until you allow it again."
};
static const Map<String, Map<String,dynamic>> mapLocales = {"ar_SA": _ar_SA, "en_US": _en_US};
}

@ -774,6 +774,20 @@ abstract class LocaleKeys {
static const browseAllBranches = 'browseAllBranches';
static const branchesCount = 'branchesCount';
static const kmAway = 'kmAway';
static const dependentDetails = 'dependentDetails';
static const verifiedWithNhic = 'verifiedWithNhic';
static const yearsOld = 'yearsOld';
static const keyDetails = 'keyDetails';
static const verified = 'verified';
static const idEnding = 'idEnding';
static const servicesAndActions = 'servicesAndActions';
static const startDependentFlowsDesc = 'startDependentFlowsDesc';
static const mazayaAccess = 'mazayaAccess';
static const accessOff = 'accessOff';
static const allowDependentMazayaOffersDesc = 'allowDependentMazayaOffersDesc';
static const loginMethod = 'loginMethod';
static const nationalIdAndDob = 'nationalIdAndDob';
static const sponsorOtp = 'sponsorOtp';
static const revokingPreventsAccessDesc = 'revokingPreventsAccessDesc';
}

@ -254,7 +254,7 @@ class _LoginScreenState extends State<LoginScreen> {
DateTime? parsed = _parseBirthDate(rawDob);
if (parsed != null) {
selectedDob = parsed;
dobController.text = DateFormat('yyyy-MM-dd').format(parsed);
dobController.text = DateFormat('dd-MM-yyyy').format(parsed);
} else {
dobController.text = rawDob;
}
@ -295,7 +295,7 @@ class _LoginScreenState extends State<LoginScreen> {
if (dt != null) return DateTime(dt.year, dt.month, dt.day);
// 4. Try standard custom date patterns
List<String> formats = ['yyyy-MM-dd', 'dd/MM/yyyy', 'MM/dd/yyyy', 'dd-MM-yyyy', 'yyyy/MM/dd'];
List<String> formats = ['dd-MM-yyyy', 'yyyy-MM-dd', 'dd/MM/yyyy', 'MM/dd/yyyy', 'yyyy/MM/dd'];
for (String fmt in formats) {
try {
DateTime parsed = DateFormat(fmt).parseStrict(s);
@ -317,7 +317,7 @@ class _LoginScreenState extends State<LoginScreen> {
DateTime? parsed = _parseBirthDate(savedDob);
if (parsed != null) {
selectedDob = parsed;
dobController.text = DateFormat('yyyy-MM-dd').format(parsed);
dobController.text = DateFormat('dd-MM-yyyy').format(parsed);
} else {
dobController.text = savedDob;
}
@ -327,7 +327,7 @@ class _LoginScreenState extends State<LoginScreen> {
DateTime? parsed = _parseBirthDate(dobController.text);
if (parsed != null) {
selectedDob = parsed;
dobController.text = DateFormat('yyyy-MM-dd').format(parsed);
dobController.text = DateFormat('dd-MM-yyyy').format(parsed);
}
}
}
@ -530,7 +530,7 @@ class _LoginScreenState extends State<LoginScreen> {
if (picked != null) {
setState(() {
selectedDob = picked;
dobController.text = DateFormat('yyyy-MM-dd', 'en_US').format(picked);
dobController.text = DateFormat('dd-MM-yyyy', 'en_US').format(picked);
});
}
}

@ -0,0 +1,596 @@
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/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/get_employee_contacts.model.dart';
import 'package:mohem_flutter_app/ui/profile/mazaya_access_result_screen.dart';
import 'package:mohem_flutter_app/widgets/bottom_sheets/confirm_bottom_sheet.dart';
class _ShieldIconPainter extends CustomPainter {
final Color color;
final bool isGranted;
_ShieldIconPainter({required this.color, required this.isGranted});
@override
void paint(Canvas canvas, Size size) {
Paint paint = Paint()
..color = color
..style = PaintingStyle.stroke
..strokeWidth = 2.0
..strokeCap = StrokeCap.round
..strokeJoin = StrokeJoin.round;
double w = size.width;
double h = size.height;
Path path = Path();
path.moveTo(w * 0.5, h * 0.1);
path.cubicTo(w * 0.72, h * 0.1, w * 0.88, h * 0.18, w * 0.88, h * 0.38);
path.cubicTo(w * 0.88, h * 0.65, w * 0.68, h * 0.84, w * 0.5, h * 0.92);
path.cubicTo(w * 0.32, h * 0.84, w * 0.12, h * 0.65, w * 0.12, h * 0.38);
path.cubicTo(w * 0.12, h * 0.18, w * 0.28, h * 0.1, w * 0.5, h * 0.1);
path.close();
canvas.drawPath(path, paint);
if (isGranted) {
Path checkPath = Path();
checkPath.moveTo(w * 0.34, h * 0.49);
checkPath.lineTo(w * 0.46, h * 0.61);
checkPath.lineTo(w * 0.66, h * 0.39);
canvas.drawPath(checkPath, paint);
} else {
canvas.drawLine(
Offset(w * 0.38, h * 0.38),
Offset(w * 0.62, h * 0.62),
paint,
);
canvas.drawLine(
Offset(w * 0.62, h * 0.38),
Offset(w * 0.38, h * 0.62),
paint,
);
}
}
@override
bool shouldRepaint(covariant _ShieldIconPainter oldDelegate) =>
color != oldDelegate.color || isGranted != oldDelegate.isGranted;
}
class DependentDetailsScreen extends StatefulWidget {
final GetEmployeeContactsList? contact;
const DependentDetailsScreen({Key? key, this.contact}) : super(key: key);
@override
_DependentDetailsScreenState createState() => _DependentDetailsScreenState();
}
class _DependentDetailsScreenState extends State<DependentDetailsScreen> {
GetEmployeeContactsList? _contact;
bool _argsLoaded = false;
@override
void didChangeDependencies() {
super.didChangeDependencies();
if (!_argsLoaded) {
if (widget.contact != null) {
_contact = widget.contact;
} else {
Object? args = ModalRoute.of(context)?.settings.arguments;
if (args is GetEmployeeContactsList) {
_contact = args;
} else if (args is Map && args['contact'] is GetEmployeeContactsList) {
_contact = args['contact'] as GetEmployeeContactsList;
}
}
_argsLoaded = true;
}
}
bool get _isAccessAllowed {
if (_contact == null) return false;
String? flag = _contact!.mAZAYAACCESSFLG ?? _contact!.mazayaAccess?.toString();
return flag?.trim().toUpperCase() == 'Y';
}
String _getInitials(String? name) {
if (name == null || name.trim().isEmpty) return "D";
List<String> 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();
}
String _getFirstName(String? name) {
if (name == null || name.trim().isEmpty) return "";
List<String> parts = name.trim().split(RegExp(r'\s+')).where((String p) => p.isNotEmpty).toList();
if (parts.isEmpty) return "";
return parts.first;
}
DateTime? _parseBirthDate(String? dateStr) {
if (dateStr == null || dateStr.trim().isEmpty) return null;
String s = dateStr.trim();
RegExp dateRegex = RegExp(r'/?Date\((-?\d+)(?:[+-]\d+)?\)/?');
Match? match = dateRegex.firstMatch(s);
if (match != null) {
try {
int millis = int.parse(match.group(1)!);
DateTime dtUtc = DateTime.fromMillisecondsSinceEpoch(millis, isUtc: true);
return DateTime(dtUtc.year, dtUtc.month, dtUtc.day);
} catch (_) {}
}
if (RegExp(r'^-?\d{10,14}$').hasMatch(s)) {
try {
int millis = int.parse(s);
DateTime dtUtc = DateTime.fromMillisecondsSinceEpoch(millis, isUtc: true);
return DateTime(dtUtc.year, dtUtc.month, dtUtc.day);
} catch (_) {}
}
DateTime? dt = DateTime.tryParse(s);
if (dt != null) return DateTime(dt.year, dt.month, dt.day);
List<String> formats = <String>[
'yyyy-MM-dd',
'dd/MM/yyyy',
'MM/dd/yyyy',
'dd-MM-yyyy',
'yyyy/MM/dd',
'dd-MMM-yyyy',
'dd MMM yyyy',
];
for (String fmt in formats) {
try {
DateTime parsed = DateFormat(fmt, 'en_US').parseStrict(s);
return DateTime(parsed.year, parsed.month, parsed.day);
} catch (_) {}
}
return null;
}
String _formatDob(DateTime? dob, String? raw) {
if (dob != null) {
return DateFormat('dd MMM yyyy', 'en_US').format(dob);
}
if (raw != null && raw.trim().isNotEmpty) {
return raw.split(' ').first;
}
return "-";
}
String _formatNationalId(String? nationalId) {
if (nationalId == null || nationalId.trim().isEmpty) return "-";
String clean = nationalId.trim();
String last4 = clean.length >= 4 ? clean.substring(clean.length - 4) : clean;
return LocaleKeys.idEnding.tr(args: <String>[last4]);
}
void _onToggleAccess() {
if (_contact == null) return;
bool isAllowed = _isAccessAllowed;
String dependentName = _contact!.cONTACTNAME ?? "";
ConfirmBottomSheet.show(
context,
title: isAllowed ? LocaleKeys.revokeMazayaAccess.tr() : LocaleKeys.allowMazayaAccess.tr(),
message: isAllowed
? LocaleKeys.confirmRevokeMazayaAccess.tr(args: <String>[dependentName])
: LocaleKeys.confirmGrantMazayaAccess.tr(args: <String>[dependentName]),
icon: isAllowed ? Icons.remove_circle_outline : Icons.check_circle_outline,
iconColor: isAllowed ? const Color(0xFFDC2626) : const Color(0xFF168A89),
confirmButtonColors: isAllowed
? const <Color>[Color(0xFFEF4444), Color(0xFFDC2626)]
: const <Color>[Color(0xFF22B0A6), Color(0xFF168A89)],
confirmText: LocaleKeys.yes.tr(),
cancelText: LocaleKeys.no.tr(),
onConfirm: _performToggleAccess,
);
}
Future<void> _performToggleAccess() async {
if (_contact == null) return;
bool isAllowed = _isAccessAllowed;
String action = isAllowed ? "Revoke" : "Grant";
try {
Utils.showLoading(context);
await ProfileApiClient().processContactMazayaAccess(
accessAction: action,
contactRelationshipId: _contact!.cONTACTRELATIONSHIPID,
contactPersonId: _contact!.cONTACTPERSONID,
personId: _contact!.pERSONID,
);
setState(() {
_contact!.mAZAYAACCESSFLG = action == "Grant" ? "Y" : "N";
_contact!.mazayaAccess = _contact!.mAZAYAACCESSFLG;
});
Utils.hideLoading(context);
if (mounted) {
if (action == "Grant") {
Navigator.push(
context,
MaterialPageRoute<dynamic>(
builder: (BuildContext cxt) => MazayaAccessResultScreen(
isGranted: true,
dependentName: _contact!.cONTACTNAME ?? "",
dependentInitials: _getInitials(_contact!.cONTACTNAME),
nationalId: _contact!.nATIONALIDENTIFIER,
dateOfBirth: _contact!.dATEOFBIRTH,
),
),
);
} else {
Utils.showToast(LocaleKeys.revokeMazayaAccess.tr());
}
}
} catch (ex) {
Utils.hideLoading(context);
Utils.handleException(ex, context, null);
}
}
@override
Widget build(BuildContext context) {
DateTime? dob = _parseBirthDate(_contact?.dATEOFBIRTH);
String initials = _getInitials(_contact?.cONTACTNAME);
String fullName = _contact?.cONTACTNAME ?? "";
String firstName = _getFirstName(_contact?.cONTACTNAME);
String relation = (_contact?.rELATIONSHIP != null && _contact!.rELATIONSHIP!.trim().isNotEmpty)
? _contact!.rELATIONSHIP!.trim()
: (_contact?.cONTACTTYPE?.trim() ?? "");
String formattedId = _formatNationalId(_contact?.nATIONALIDENTIFIER);
String formattedDob = _formatDob(dob, _contact?.dATEOFBIRTH);
bool isAllowed = _isAccessAllowed;
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: <Widget>[
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),
LocaleKeys.dependentDetails.tr().toText20(
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: SingleChildScrollView(
padding: const EdgeInsets.fromLTRB(20, 12, 20, 30),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
// Profile Card (Mint Green)
Container(
width: double.infinity,
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: const Color(0xFFE8F7F2),
borderRadius: BorderRadius.circular(16),
border: Border.all(color: const Color(0xFFBCE6D7), width: 1.2),
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
// Avatar circle with initials
Container(
width: 52,
height: 52,
decoration: const BoxDecoration(
color: Colors.white,
shape: BoxShape.circle,
),
child: Center(
child: initials.toText17(
color: const Color(0xFF168A89),
),
),
),
const SizedBox(width: 14),
// Dependent Name & Relation
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
fullName.toText16(
maxlines: 2,
weight: FontWeight.w500,
color: const Color(0xFF1E293B),
),
if (relation.isNotEmpty) ...<Widget>[
const SizedBox(height: 3),
relation.toText12(
maxLine: 1,
weight: FontWeight.w500,
color: const Color(0xFF168A89),
),
],
],
),
),
// Verified badge on right
const SizedBox(width: 8),
const Icon(
Icons.verified_outlined,
color: Color(0xFF059669),
size: 24,
),
],
),
),
const SizedBox(height: 16),
// Key details Card
Container(
width: double.infinity,
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
border: Border.all(color: const Color(0xFFF1F5F9)),
boxShadow: const <BoxShadow>[
BoxShadow(
color: Color(0x08000000),
blurRadius: 8,
offset: Offset(0, 2),
),
],
),
child: Column(
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
LocaleKeys.keyDetails.tr().toText16(
weight: FontWeight.w500,
color: const Color(0xFF1E293B),
),
LocaleKeys.verified.tr().toText13(
weight: FontWeight.w500,
color: const Color(0xFF059669),
),
],
),
const SizedBox(height: 14),
const Divider(height: 1, thickness: 1, color: Color(0xFFF1F5F9)),
const SizedBox(height: 14),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
LocaleKeys.nationalId.tr().toText13(
weight: FontWeight.w400,
color: const Color(0xFF64748B),
),
formattedId.toText13(
weight: FontWeight.w500,
color: const Color(0xFF1E293B),
),
],
),
const SizedBox(height: 12),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
LocaleKeys.dateOfBirth.tr().toText13(
weight: FontWeight.w400,
color: const Color(0xFF64748B),
),
formattedDob.toText13(
weight: FontWeight.w500,
color: const Color(0xFF1E293B),
),
],
),
],
),
),
const SizedBox(height: 24),
// Services & actions section header
LocaleKeys.servicesAndActions.tr().toText17(
weight: FontWeight.w500,
color: const Color(0xFF1E293B),
),
const SizedBox(height: 4),
LocaleKeys.startDependentFlowsDesc.tr().toText13(
weight: FontWeight.w400,
color: const Color(0xFF64748B),
),
const SizedBox(height: 12),
// Mazaya access Card
Container(
width: double.infinity,
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
border: Border.all(color: const Color(0xFFF1F5F9)),
boxShadow: const <BoxShadow>[
BoxShadow(
color: Color(0x08000000),
blurRadius: 8,
offset: Offset(0, 2),
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
// Header Row: Mazaya access + Badge
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
LocaleKeys.mazayaAccess.tr().toText16(
weight: FontWeight.w500,
color: const Color(0xFF1E293B),
),
Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 3),
decoration: BoxDecoration(
color: isAllowed ? const Color(0xFFECFDF5) : const Color(0xFFFEE2E2),
borderRadius: BorderRadius.circular(12),
),
child: (isAllowed ? LocaleKeys.active.tr() : LocaleKeys.accessOff.tr()).toText12(
weight: FontWeight.w500,
color: isAllowed ? const Color(0xFF059669) : const Color(0xFFEF4444),
),
),
],
),
const SizedBox(height: 6),
LocaleKeys.allowDependentMazayaOffersDesc.tr(
args: <String>[firstName.isNotEmpty ? firstName : fullName],
).toText13(
weight: FontWeight.w400,
color: const Color(0xFF64748B),
),
const SizedBox(height: 16),
const Divider(height: 1, thickness: 1, color: Color(0xFFF1F5F9)),
const SizedBox(height: 14),
// Login method row
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
LocaleKeys.loginMethod.tr().toText13(
weight: FontWeight.w400,
color: const Color(0xFF64748B),
),
LocaleKeys.nationalIdAndDob.tr().toText13(
weight: FontWeight.w500,
color: const Color(0xFF1E293B),
),
],
),
const SizedBox(height: 12),
// Verification row
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
LocaleKeys.verification.tr().toText13(
weight: FontWeight.w400,
color: const Color(0xFF64748B),
),
LocaleKeys.sponsorOtp.tr().toText13(
weight: FontWeight.w500,
color: const Color(0xFF1E293B),
),
],
),
const SizedBox(height: 20),
// Action Button: Allow Mazaya Access (Teal) or Revoke Mazaya Access (Red Outline)
InkWell(
borderRadius: BorderRadius.circular(12),
onTap: _onToggleAccess,
child: Container(
height: 48,
width: double.infinity,
decoration: BoxDecoration(
color: isAllowed ? const Color(0xFFFEF2F2) : const Color(0xFF22B0A6),
borderRadius: BorderRadius.circular(12),
border: isAllowed ? Border.all(color: const Color(0xFFFECACA), width: 1.2) : null,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
SizedBox(
width: 18,
height: 18,
child: CustomPaint(
painter: _ShieldIconPainter(
color: isAllowed ? const Color(0xFFDC2626) : Colors.white,
isGranted: !isAllowed,
),
),
),
const SizedBox(width: 8),
(isAllowed
? LocaleKeys.revokeMazayaAccess.tr()
: LocaleKeys.allowMazayaAccess.tr())
.toText16(
weight: FontWeight.w500,
color: isAllowed ? const Color(0xFFDC2626) : Colors.white,
),
],
),
),
),
if (isAllowed) ...<Widget>[
const SizedBox(height: 12),
Center(
child: LocaleKeys.revokingPreventsAccessDesc.tr().toText11(
weight: FontWeight.w400,
color: const Color(0xFF64748B),
isCenter: true,
),
),
],
],
),
),
],
),
),
),
);
}
}

@ -11,6 +11,7 @@ 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/dependent_details_screen.dart';
import 'package:mohem_flutter_app/ui/profile/mazaya_access_result_screen.dart';
import 'package:mohem_flutter_app/widgets/bottom_sheets/confirm_bottom_sheet.dart';
import 'package:mohem_flutter_app/widgets/button/default_button.dart';
@ -79,21 +80,6 @@ class _FamilyMembersState extends State<FamilyMembers> {
);
}
void _openMazayaGrantedScreen(GetEmployeeContactsList item) {
Navigator.push(
context,
MaterialPageRoute<dynamic>(
builder: (BuildContext cxt) => MazayaAccessResultScreen(
isGranted: true,
dependentName: item.cONTACTNAME ?? "",
dependentInitials: _getInitials(item.cONTACTNAME),
nationalId: item.nATIONALIDENTIFIER,
dateOfBirth: item.dATEOFBIRTH,
),
),
);
}
Future<void> _toggleMazayaAccess(GetEmployeeContactsList contact) async {
bool current = _isAccessAllowed(contact);
String action = current ? "Revoke" : "Grant";
@ -362,13 +348,18 @@ class _FamilyMembersState extends State<FamilyMembers> {
bool canDelete,
) {
bool isAllowed = _isAccessAllowed(item);
// Access already granted: tapping the card (outside the Revoke link) reopens the same
// "granted" result screen shown right after granting - no need to call the API again.
bool canOpenGrantedScreen = fromMazaya && isAllowed;
return InkWell(
borderRadius: BorderRadius.circular(16),
onTap: canOpenGrantedScreen ? () => _openMazayaGrantedScreen(item) : null,
onTap: () async {
await Navigator.push(
context,
MaterialPageRoute<dynamic>(
builder: (BuildContext cxt) => DependentDetailsScreen(contact: item),
),
);
getEmployeeContacts();
},
child: Container(
padding: const EdgeInsets.fromLTRB(16, 16, 16, 14),
decoration: BoxDecoration(

Loading…
Cancel
Save