family screen & widgets

pull/76/head
aamir-csol 3 weeks ago
parent aaa8222b1a
commit 24362f67cb

@ -24,6 +24,7 @@ import 'package:hmg_patient_app_new/features/payfort/payfort_repo.dart';
import 'package:hmg_patient_app_new/features/payfort/payfort_view_model.dart';
import 'package:hmg_patient_app_new/features/prescriptions/prescriptions_repo.dart';
import 'package:hmg_patient_app_new/features/prescriptions/prescriptions_view_model.dart';
import 'package:hmg_patient_app_new/features/profile_settings/profile_settings_view_model.dart';
import 'package:hmg_patient_app_new/features/radiology/radiology_repo.dart';
import 'package:hmg_patient_app_new/features/radiology/radiology_view_model.dart';
import 'package:hmg_patient_app_new/services/analytics/analytics_service.dart';
@ -166,6 +167,7 @@ class AppDependencies {
() => AuthenticationViewModel(
authenticationRepo: getIt(), cacheService: getIt(), navigationService: getIt(), dialogService: getIt(), appState: getIt(), errorHandlerService: getIt(), localAuthService: getIt()),
);
getIt.registerLazySingleton<ProfileSettingsViewModel>(() => ProfileSettingsViewModel());
// Screen-specific VMs Factory
// getIt.registerFactory<BookAppointmentsViewModel>(

@ -275,7 +275,7 @@ class AuthenticationRepoImp implements AuthenticationRepo {
'PatientShareRequestID': patientShareRequestID,
'ResponseID': responseID,
'Status': 3,
'PatientID': appState.getAuthenticatedUser()?.patientId ?? 0,
// 'PatientID': appState.getAuthenticatedUser()?.patientId ?? 0,
'LogInTokenID': appState.getFamilyFileTokenID,
'activationCode': activationCode ?? "0000",
'PatientMobileNumber': newRequest.patientMobileNumber,

@ -550,7 +550,7 @@ class AuthenticationViewModel extends ChangeNotifier {
if (!_appState.getIsChildLoggedIn) {
await medicalVm.getFamilyFiles(status: 0);
await medicalVm.getAllPendingRecordsByResponseId();
_navigationService.popUntilNamed(AppRoutes.medicalFilePage);
_navigationService.popUntilNamed(AppRoutes.landingScreen);
}
} else {
if (activation.list != null && activation.list!.isNotEmpty) {
@ -567,12 +567,13 @@ class AuthenticationViewModel extends ChangeNotifier {
} else {
activation.list!.first.isParentUser = true;
}
activation.list!.first.bloodGroup = activation.patientBlodType;
_appState.setAuthenticatedUser(activation.list!.first);
_appState.setPrivilegeModelList(activation.list!.first.listPrivilege!);
}
_appState.setUserBloodGroup = (activation.patientBlodType ?? "");
// _appState.setUserBloodGroup = (activation.patientBlodType ?? "");
_appState.setAppAuthToken = activation.authenticationTokenId;
final request = RequestUtils.getAuthanticatedCommonRequest().toJson();
final request = await RequestUtils.getAuthanticatedCommonRequest().toJson();
bool isUserAgreedBefore = await checkIfUserAgreedBefore(request: request);
//updating the last login type in app state to show the fingerprint/face id option on home screen
@ -583,9 +584,9 @@ class AuthenticationViewModel extends ChangeNotifier {
//
if (!isSwitchUser && !_appState.getIsChildLoggedIn) {
MedicalFileViewModel medicalVm = getIt<MedicalFileViewModel>();
insertPatientIMEIData(loginTypeEnum.toInt);
await insertPatientIMEIData(loginTypeEnum.toInt);
await medicalVm.getFamilyFiles(status: 0); //TODO: Remove status: 1 by Aamir Need to Discuss With Sultan
// medicalVm.getAllPendingRecordsByResponseId();
await medicalVm.getAllPendingRecordsByResponseId();
}
await clearDefaultInputValues();

@ -29,7 +29,7 @@ abstract class MedicalFileRepo {
Future<Either<Failure, GenericApiModel<List<FamilyFileResponseModelLists>>>> getAllPendingRecordsByResponseId({required Map<String, dynamic> request});
Future<Either<Failure, GenericApiModel<List<dynamic>>>> addFamilyFile({required dynamic request});
Future<Either<Failure, GenericApiModel<dynamic>>> addFamilyFile({required dynamic request});
Future<Either<Failure, GenericApiModel<List<PatientAppointmentHistoryResponseModel>>>> getPatientAppointmentsForMedicalReport();

@ -89,9 +89,6 @@ class MedicalFileViewModel extends ChangeNotifier {
void onFamilyFileTabChange(int index) {
setSelectedFamilyFileTabIndex = index;
if (index == 1) {
// getAllPendingRecordsByResponseId();
}
notifyListeners();
}
@ -323,8 +320,6 @@ class MedicalFileViewModel extends ChangeNotifier {
final isPending = element.status == FamilyFileEnum.pending.toInt || element.status == FamilyFileEnum.rejected.toInt;
final isActive = element.status == FamilyFileEnum.active.toInt;
print("====== Element Status: ${element.status}, isPending: $isPending, isActive: $isActive ============");
if (!isPending && !isActive) {
continue;
}
@ -423,9 +418,6 @@ class MedicalFileViewModel extends ChangeNotifier {
}
// pendingFamilyFiles.addAll(tempPendingFamilyFiles.where((element) => !pendingFamilyFiles.any((e) => e.responseId == element.responseId)));
pendingFamilyFiles.addAll(tempPendingFamilyFiles.where((element) => !pendingFamilyFiles.any((e) => e.patientId == element.patientId)));
print("====== Pending Family Length: ${pendingFamilyFiles.length} ============");
print("====== Pending Family Files: ${jsonEncode(pendingFamilyFiles)} ============");
}
notifyListeners();
}
@ -447,7 +439,7 @@ class MedicalFileViewModel extends ChangeNotifier {
);
}
Future<void> addFamilyFile({required OTPTypeEnum otpTypeEnum, required bool isExcludedUser}) async {
Future<void> addFamilyFile({required OTPTypeEnum otpTypeEnum}) async {
LoaderBottomSheet.showLoader();
AuthenticationViewModel authVM = getIt.get<AuthenticationViewModel>();
NavigationService navigationService = getIt.get<NavigationService>();

@ -75,6 +75,11 @@ class _LandingPageState extends State<LandingPage> {
void initState() {
authVM = context.read<AuthenticationViewModel>();
habibWalletVM = context.read<HabibWalletViewModel>();
myAppointmentsViewModel = context.read<MyAppointmentsViewModel>();
prescriptionsViewModel = context.read<PrescriptionsViewModel>();
insuranceViewModel = context.read<InsuranceViewModel>();
immediateLiveCareViewModel = context.read<ImmediateLiveCareViewModel>();
authVM.savePushTokenToAppState();
if (mounted) {
authVM.checkLastLoginStatus(() {
@ -100,11 +105,6 @@ class _LandingPageState extends State<LandingPage> {
@override
Widget build(BuildContext context) {
appState = getIt.get<AppState>();
NavigationService navigationService = getIt.get<NavigationService>();
myAppointmentsViewModel = Provider.of<MyAppointmentsViewModel>(context, listen: false);
prescriptionsViewModel = Provider.of<PrescriptionsViewModel>(context, listen: false);
insuranceViewModel = Provider.of<InsuranceViewModel>(context, listen: false);
immediateLiveCareViewModel = Provider.of<ImmediateLiveCareViewModel>(context, listen: false);
return Scaffold(
backgroundColor: AppColors.bgScaffoldColor,
body: SingleChildScrollView(

@ -81,10 +81,10 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
insuranceViewModel.initInsuranceProvider();
medicalFileViewModel.setIsPatientSickLeaveListLoading(true);
medicalFileViewModel.getPatientSickLeaveList();
if (appState.getSuperUserID == null) {
medicalFileViewModel.getFamilyFiles(status: 0); //TODO: Remove status: 1 by Aamir Need to Discuss With Sultan
medicalFileViewModel.getAllPendingRecordsByResponseId(); //TODO: Added By Aamir
}
// if (appState.getSuperUserID == null) {
// medicalFileViewModel.getFamilyFiles(status: 0); //TODO: Remove status: 1 by Aamir Need to Discuss With Sultan
// medicalFileViewModel.getAllPendingRecordsByResponseId(); //TODO: Added By Aamir
// }
medicalFileViewModel.onTabChanged(0);
}

@ -40,7 +40,6 @@ class FamilyMedicalScreen extends StatefulWidget {
}
class _FamilyMedicalScreenState extends State<FamilyMedicalScreen> {
List<CustomTabBarModel> tabs = [CustomTabBarModel(null, LocaleKeys.medicalFile.tr()), CustomTabBarModel(null, LocaleKeys.request.tr())];
MedicalFileViewModel? medicalVM;
@override
@ -52,6 +51,7 @@ class _FamilyMedicalScreenState extends State<FamilyMedicalScreen> {
@override
Widget build(BuildContext context) {
AppState appState = getIt.get<AppState>();
return CollapsingListView(
title: "My Medical File".needTranslation,
bottomChild: appState.getAuthenticatedUser()!.isParentUser!
@ -69,7 +69,7 @@ class _FamilyMedicalScreenState extends State<FamilyMedicalScreen> {
label: "Add Family Member".needTranslation,
message: "Please fill the below field to add a new family member to your profile".needTranslation,
onVerificationPress: () {
medicalVM?.addFamilyFile(otpTypeEnum: OTPTypeEnum.sms, isExcludedUser: true);
medicalVM!.addFamilyFile(otpTypeEnum: OTPTypeEnum.sms);
});
},
icon: AppAssets.add_icon,
@ -81,13 +81,17 @@ class _FamilyMedicalScreenState extends State<FamilyMedicalScreen> {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
CustomTabBar(
tabs: tabs,
activeBackgroundColor: AppColors.secondaryLightRedColor,
activeTextColor: AppColors.primaryRedColor,
tabs: appState.isChildLoggedIn
? [CustomTabBarModel(null, LocaleKeys.medicalFile.tr())]
: [CustomTabBarModel(null, LocaleKeys.medicalFile.tr()), CustomTabBarModel(null, LocaleKeys.request.tr())],
onTabChange: (index) {
medicalVM!.onFamilyFileTabChange(index);
},
),
SizedBox(height: 25.h),
Consumer<MedicalFileViewModel>(builder: (context, medicalVM, child) => getFamilyTabs(index: medicalVM.getSelectedFamilyFileTabIndex)),
Selector<MedicalFileViewModel, int>(selector: (_, model) => model.getSelectedFamilyFileTabIndex, builder: (context, selectedIndex, child) => getFamilyTabs(index: selectedIndex)),
SizedBox(height: 20.h),
],
).paddingSymmetrical(20, 0),
@ -97,7 +101,6 @@ class _FamilyMedicalScreenState extends State<FamilyMedicalScreen> {
Widget getFamilyTabs({required int index}) {
switch (index) {
case 0:
print(jsonEncode(medicalVM!.patientFamilyFiles));
return FamilyCards(
profiles: medicalVM!.patientFamilyFiles,
onSelect: (FamilyFileResponseModelLists profile) {
@ -111,7 +114,6 @@ class _FamilyMedicalScreenState extends State<FamilyMedicalScreen> {
isShowRemoveButton: true,
);
case 1:
print(jsonEncode(medicalVM!.pendingFamilyFiles));
return FamilyCards(
profiles: medicalVM!.pendingFamilyFiles,
isRequestDesign: true,

@ -10,6 +10,8 @@ import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/medical_file/models/family_file_response_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/services/dialog_service.dart';
import 'package:hmg_patient_app_new/services/navigation_service.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/chip/custom_chip_widget.dart';
@ -42,10 +44,9 @@ class FamilyCards extends StatefulWidget {
class _FamilyCardsState extends State<FamilyCards> {
AppState appState = getIt<AppState>();
// bool isShowActions = true;
@override
Widget build(BuildContext context) {
DialogService dialogService = getIt.get<DialogService>();
if (widget.isRequestDesign) {
return Column(
children: [
@ -53,7 +54,14 @@ class _FamilyCardsState extends State<FamilyCards> {
children: [
Utils.buildSvgWithAssets(icon: AppAssets.alertSquare),
SizedBox(width: 8.h),
"Sent Requests".needTranslation.toText14(color: AppColors.textColor, isUnderLine: true, weight: FontWeight.w500),
"Who can view my medical file ?".needTranslation.toText14(color: AppColors.textColor, isUnderLine: true, weight: FontWeight.w500).onPress(() {
dialogService.showFamilyBottomSheetWithoutHWithChild(
label: "Manage Family".needTranslation,
message: "",
child: manageFamily(),
onOkPressed: () {},
);
}),
SizedBox(width: 4.h),
Utils.buildSvgWithAssets(icon: AppAssets.arrowRight),
],
@ -67,15 +75,9 @@ class _FamilyCardsState extends State<FamilyCards> {
itemBuilder: (context, index) {
final mySideProfiles = widget.profiles.where((profile) => profile.isRequestFromMySide ?? false).toList();
FamilyFileResponseModelLists profile = mySideProfiles[index];
return Container(
margin: EdgeInsets.only(
bottom: 12.h,
),
padding: EdgeInsets.symmetric(
vertical: 15.h,
horizontal: 15.h,
),
margin: EdgeInsets.only(bottom: 12.h),
padding: EdgeInsets.symmetric(vertical: 15.h, horizontal: 15.h),
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24),
child: Opacity(
opacity: 1.0,
@ -104,27 +106,14 @@ class _FamilyCardsState extends State<FamilyCards> {
? AppColors.primaryRedColor
: profile.status == FamilyFileEnum.active.toInt
? AppColors.textGreenColor
: AppColors.alertColor,
),
Wrap(
alignment: WrapAlignment.start,
children: [
(profile.patientName ?? "").toText16(
isBold: false,
isCenter: true,
maxlines: 1,
weight: FontWeight.w600,
),
("has ${(profile.statusDescription ?? "").toLowerCase()} your family member request").toText14(
isBold: false,
isCenter: true,
maxlines: 1,
weight: FontWeight.w500,
color: AppColors.greyTextColor,
),
],
),
SizedBox(height: 4.h),
: AppColors.alertColor),
SizedBox(height: 8.h),
Wrap(alignment: WrapAlignment.start, crossAxisAlignment: WrapCrossAlignment.start, runAlignment: WrapAlignment.start, spacing: 0.h, children: [
(profile.patientName ?? "").toText14(isBold: false, isCenter: false, maxlines: 1, weight: FontWeight.w600),
(getStatusTextByRequest(FamilyFileEnum.values.firstWhere((e) => e.toInt == profile.status), profile.isRequestFromMySide ?? false))
.toText14(isBold: false, isCenter: false, maxlines: 1, weight: FontWeight.w500, color: AppColors.greyTextColor),
]),
SizedBox(height: 8.h),
CustomChipWidget(
height: 30.h,
chipType: ChipTypeEnum.alert,
@ -133,8 +122,7 @@ class _FamilyCardsState extends State<FamilyCards> {
iconAsset: null,
isShowBorder: false,
borderRadius: 8.h,
textColor: AppColors.textColor,
),
textColor: AppColors.textColor),
],
),
),
@ -142,21 +130,111 @@ class _FamilyCardsState extends State<FamilyCards> {
},
),
SizedBox(height: 20.h),
if (widget.profiles.where((profile) => !(profile.isRequestFromMySide ?? false)).isNotEmpty)
Row(
],
);
} else {
return GridView.builder(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemCount: widget.profiles.length,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: 10.h,
mainAxisSpacing: 10.h,
childAspectRatio: widget.isShowDetails ? 0.56.h : 0.65.h,
),
padding: EdgeInsets.only(bottom: 20.h),
itemBuilder: (context, index) {
final profile = widget.profiles[index];
final isActive = (profile.responseId == appState.getAuthenticatedUser()?.patientId);
final isParentUser = appState.getAuthenticatedUser()?.isParentUser ?? false;
final canSwitch = isParentUser || (!isParentUser && profile.responseId == appState.getSuperUserID);
return Container(
padding: EdgeInsets.symmetric(vertical: 15.h, horizontal: 15.h),
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24),
child: Opacity(
opacity: isActive || profile.status == FamilyFileEnum.pending.toInt || !canSwitch ? 0.4 : 1.0, // Fade all content if active
child: Stack(
children: [
Utils.buildSvgWithAssets(icon: AppAssets.alertSquare),
SizedBox(width: 8.h),
"Users who want to view your profile".needTranslation.toText14(color: AppColors.textColor, isUnderLine: true, weight: FontWeight.w500),
SizedBox(width: 4.h),
Utils.buildSvgWithAssets(icon: AppAssets.arrowRight),
Column(
mainAxisSize: MainAxisSize.min,
children: [
Utils.buildImgWithAssets(
icon: profile.gender == null
? AppAssets.dummy_user
: profile.gender == 1
? ((profile.age ?? 0) < 7 ? AppAssets.babyBoyImg : AppAssets.male_img)
: (profile.age! < 7 ? AppAssets.babyGirlImg : AppAssets.femaleImg),
width: 80.h,
height: 78.h),
SizedBox(height: 8.h),
(profile.patientName ?? "Unknown").toText14(isBold: false, isCenter: true, maxlines: 1, weight: FontWeight.w600),
SizedBox(height: 8.h),
CustomChipWidget(
chipType: ChipTypeEnum.alert,
backgroundColor: AppColors.lightGrayBGColor,
chipText: "Relation:${profile.relationship ?? "N/A"}",
iconAsset: AppAssets.heart,
isShowBorder: false,
borderRadius: 8.h,
textColor: AppColors.textColor),
widget.isShowDetails ? SizedBox(height: 4.h) : SizedBox(),
widget.isShowDetails
? CustomChipWidget(
chipType: ChipTypeEnum.alert,
backgroundColor: AppColors.lightGrayBGColor,
chipText: "Age:${profile.age ?? "N/A"} Years",
isShowBorder: false,
borderRadius: 8.h,
textColor: AppColors.textColor,
)
: SizedBox(),
widget.isShowDetails
? SizedBox(height: 8.h)
: SizedBox(
height: 4.h,
),
Spacer(),
CustomButton(
height: 40.h,
onPressed: () {
if (canSwitch) widget.onSelect(profile);
},
text: isActive ? "Active".needTranslation : "Switch".needTranslation,
backgroundColor: isActive || !canSwitch ? Colors.grey.shade200 : AppColors.secondaryLightRedColor,
borderColor: isActive || !canSwitch ? Colors.grey.shade200 : AppColors.secondaryLightRedColor,
textColor: isActive || !canSwitch ? AppColors.greyTextColor : AppColors.primaryRedColor,
fontSize: 13.h,
icon: isActive ? AppAssets.activeCheck : AppAssets.switch_user,
iconColor: isActive || !canSwitch ? (isActive ? null : AppColors.greyTextColor) : AppColors.primaryRedColor,
padding: EdgeInsets.symmetric(vertical: 0, horizontal: 0),
).paddingOnly(top: 0, bottom: 0),
],
),
if (widget.isShowRemoveButton) ...[
Positioned(
top: 0,
right: 0,
child: Utils.buildSvgWithAssets(icon: AppAssets.deleteIcon).onPress(() {
if (!isActive) widget.onRemove(profile);
}),
),
],
],
),
),
);
},
);
}
}
// Items for second group (requests from others)
ListView.builder(
Widget manageFamily() {
NavigationService navigationService = getIt<NavigationService>();
return ListView.builder(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
padding: EdgeInsetsGeometry.zero,
itemCount: widget.profiles.where((profile) => !(profile.isRequestFromMySide ?? false)).length,
itemBuilder: (context, index) {
final otherProfiles = widget.profiles.where((profile) => !(profile.isRequestFromMySide ?? false)).toList();
@ -194,11 +272,12 @@ class _FamilyCardsState extends State<FamilyCards> {
? AppColors.textGreenColor
: AppColors.alertColor,
),
SizedBox(height: 8.h),
Wrap(
alignment: WrapAlignment.start,
children: [
(profile.patientName ?? "").toText16(isBold: false, isCenter: true, maxlines: 1, weight: FontWeight.w600),
(profile.status == FamilyFileEnum.active.toInt ? "can view your family".needTranslation : "wants to add you as their family member".needTranslation).toText14(
(profile.patientName ?? "").toText14(isBold: false, isCenter: true, maxlines: 1, weight: FontWeight.w600),
(getStatusTextByRequest(FamilyFileEnum.values.firstWhere((e) => e.toInt == profile.status), profile.isRequestFromMySide ?? false)).toText14(
isBold: false,
isCenter: true,
maxlines: 1,
@ -207,7 +286,7 @@ class _FamilyCardsState extends State<FamilyCards> {
),
],
),
SizedBox(height: 4.h),
SizedBox(height: 8.h),
CustomChipWidget(
height: 30.h,
chipType: ChipTypeEnum.alert,
@ -228,6 +307,7 @@ class _FamilyCardsState extends State<FamilyCards> {
height: 40.h,
text: LocaleKeys.confirm.tr(),
onPressed: () {
navigationService.pop();
widget.onSelect(profile);
},
backgroundColor: AppColors.lightGreenButtonColor,
@ -242,6 +322,7 @@ class _FamilyCardsState extends State<FamilyCards> {
height: 40.h,
text: profile.status == FamilyFileEnum.active.toInt ? LocaleKeys.removeMember.tr() : LocaleKeys.cancel.tr(),
onPressed: () {
navigationService.pop();
widget.onRemove(profile);
},
backgroundColor: AppColors.secondaryLightRedColor,
@ -258,99 +339,33 @@ class _FamilyCardsState extends State<FamilyCards> {
),
);
},
),
],
);
}
String getStatusTextByRequest(FamilyFileEnum status, bool isRequestFromMySide) {
switch (status) {
case FamilyFileEnum.active:
if (isRequestFromMySide) {
return "${status.displayName} your request to be your family member".needTranslation;
} else {
return GridView.builder(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemCount: widget.profiles.length,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: 10.h,
mainAxisSpacing: 10.h,
childAspectRatio: widget.isShowDetails ? 0.56.h : 0.66.h,
),
padding: EdgeInsets.only(bottom: 20.h),
itemBuilder: (context, index) {
final profile = widget.profiles[index];
final isActive = (profile.responseId == appState.getAuthenticatedUser()?.patientId);
final isParentUser = appState.getAuthenticatedUser()?.isParentUser ?? false;
final canSwitch = isParentUser || (!isParentUser && profile.responseId == appState.getSuperUserID);
return Container(
padding: EdgeInsets.symmetric(vertical: 15.h, horizontal: 15.h),
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24),
child: Opacity(
opacity: isActive || profile.status == FamilyFileEnum.pending.toInt || !canSwitch ? 0.4 : 1.0, // Fade all content if active
child: Stack(
children: [
Column(
mainAxisSize: MainAxisSize.min,
children: [
Utils.buildImgWithAssets(
icon: profile.gender == null
? AppAssets.dummy_user
: profile.gender == 1
? ((profile.age ?? 0) < 7 ? AppAssets.babyBoyImg : AppAssets.male_img)
: (profile.age! < 7 ? AppAssets.babyGirlImg : AppAssets.femaleImg),
width: 80.h,
height: 78.h),
SizedBox(height: 8.h),
(profile.patientName ?? "Unknown").toText16(isBold: false, isCenter: true, maxlines: 1, weight: FontWeight.w600),
SizedBox(height: 4.h),
CustomChipWidget(
chipType: ChipTypeEnum.alert,
backgroundColor: AppColors.lightGrayBGColor,
chipText: "Relation:${profile.relationship ?? "N/A"}",
iconAsset: AppAssets.heart,
isShowBorder: false,
borderRadius: 8.h,
textColor: AppColors.textColor),
widget.isShowDetails ? SizedBox(height: 4.h) : SizedBox(),
widget.isShowDetails
? CustomChipWidget(
chipType: ChipTypeEnum.alert,
backgroundColor: AppColors.lightGrayBGColor,
chipText: "Age:${profile.age ?? "N/A"} Years",
isShowBorder: false,
borderRadius: 8.h,
textColor: AppColors.textColor,
)
: SizedBox(),
widget.isShowDetails ? SizedBox(height: 8.h) : SizedBox(),
Spacer(),
CustomButton(
height: 40.h,
onPressed: () {
if (canSwitch) widget.onSelect(profile);
},
text: isActive ? "Active".needTranslation : "Switch".needTranslation,
backgroundColor: isActive || !canSwitch ? Colors.grey.shade200 : AppColors.secondaryLightRedColor,
borderColor: isActive || !canSwitch ? Colors.grey.shade200 : AppColors.secondaryLightRedColor,
textColor: isActive || !canSwitch ? AppColors.greyTextColor : AppColors.primaryRedColor,
fontSize: 13.h,
icon: isActive ? AppAssets.activeCheck : AppAssets.switch_user,
iconColor: isActive || !canSwitch ? (isActive ? null : AppColors.greyTextColor) : AppColors.primaryRedColor,
padding: EdgeInsets.symmetric(vertical: 0, horizontal: 0),
).paddingOnly(top: 0, bottom: 0),
],
),
if (widget.isShowRemoveButton) ...[
Positioned(
top: 0,
right: 0,
child: Utils.buildSvgWithAssets(icon: AppAssets.deleteIcon).onPress(() {
if (!isActive) widget.onRemove(profile);
}),
),
],
],
),
),
);
},
);
return "can view your file".needTranslation;
}
case FamilyFileEnum.pending:
if (isRequestFromMySide) {
return "has a request ${status.displayName} to be your family member".needTranslation;
} else {
return "wants to add you as their family member".needTranslation;
}
case FamilyFileEnum.rejected:
if (isRequestFromMySide) {
return "${status.displayName} your request to be your family member".needTranslation;
} else {
return "${status.displayName} your family member request".needTranslation;
}
case FamilyFileEnum.inactive:
return "Inactive".needTranslation;
default:
return "N/A".needTranslation;
}
}
}

@ -1,26 +1,29 @@
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/features/medical_file/models/family_file_response_model.dart';
import 'package:hmg_patient_app_new/presentation/my_family/widget/family_cards.dart';
import 'package:hmg_patient_app_new/services/navigation_service.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart';
class MyFamilySheet {
static Future<void> show(BuildContext context, List<FamilyFileResponseModelLists> familyLists, Function(FamilyFileResponseModelLists) onSelect) async {
NavigationService navigationService = getIt<NavigationService>();
return showCommonBottomSheetWithoutHeight(
context,
titleWidget: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
'Please select a profile'.toText21(isBold: true),
'switch from the below list of medical file'.toText16(weight: FontWeight.w100, color: AppColors.greyTextColor),
'Please select a profile'.needTranslation.toText21(isBold: true),
'switch from the below list of medical file'.needTranslation.toText16(weight: FontWeight.w100, color: AppColors.greyTextColor),
],
),
child: FamilyCards(
profiles: familyLists,
onSelect: (profile) {
Navigator.of(context).pop(); // Close the bottom sheet
onSelect(profile); // Call the onSelect callback
navigationService.pop();
onSelect(profile);
},
onRemove: (profile) {},
isBottomSheet: true),

@ -59,11 +59,8 @@ class _ProfileSettingsState extends State<ProfileSettings> {
int length = 3;
final SwiperController _controller = SwiperController();
int _index = 0;
@override
Widget build(BuildContext context) {
final MedicalFileViewModel medicalFileViewModel = getIt.get<MedicalFileViewModel>();
return CollapsingListView(
title: "Profile & Settings".needTranslation,
logout: () {},
@ -71,28 +68,41 @@ class _ProfileSettingsState extends State<ProfileSettings> {
child: SingleChildScrollView(
padding: EdgeInsets.only(top: 24, bottom: 24),
physics: NeverScrollableScrollPhysics(),
child: Consumer<ProfileSettingsViewModel>(
builder: (context, model, child) {
print(jsonEncode(medicalFileViewModel.patientFamilyFiles));
child: Consumer2<ProfileSettingsViewModel, MedicalFileViewModel>(
builder: (context, profileVm, medicalVm, child) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Swiper(
itemCount: medicalFileViewModel.patientFamilyFiles.length,
itemCount: medicalVm.patientFamilyFiles.length,
layout: SwiperLayout.STACK,
loop: true,
itemWidth: MediaQuery.of(context).size.width - 42,
indicatorLayout: PageIndicatorLayout.COLOR,
axisDirection: AxisDirection.right,
controller: _controller,
itemHeight: 210 + 16,
itemHeight: 220 + 16,
pagination: const SwiperPagination(
alignment: Alignment.bottomCenter,
margin: EdgeInsets.only(top: 210 + 8 + 24),
builder: DotSwiperPaginationBuilder(color: Color(0xffD9D9D9), activeColor: AppColors.blackBgColor),
),
itemBuilder: (BuildContext context, int index) {
return FamilyCardWidget(isRootUser: true).paddingOnly(right: 16);
return FamilyCardWidget(
profile: medicalVm.patientFamilyFiles[index],
onAddFamilyMemberPress: () {
DialogService dialogService = getIt.get<DialogService>();
dialogService.showAddFamilyFileSheet(
label: "Add Family Member".needTranslation,
message: "Please fill the below field to add a new family member to your profile".needTranslation,
onVerificationPress: () {
medicalVm.addFamilyFile(otpTypeEnum: OTPTypeEnum.sms);
});
},
onFamilySwitchPress: (FamilyFileResponseModelLists profile) {
medicalVm.switchFamilyFiles(responseID: profile.responseId, patientID: profile.patientId, phoneNumber: profile.mobileNumber);
},
).paddingOnly(right: 16);
},
),
GridView(
@ -241,15 +251,12 @@ class _ProfileSettingsState extends State<ProfileSettings> {
}
class FamilyCardWidget extends StatelessWidget {
FamilyCardWidget({this.isRootUser = true, Key? key}) : super(key: key);
bool isRootUser;
late AppState appState;
final Function() onAddFamilyMemberPress;
final Function(FamilyFileResponseModelLists member) onFamilySwitchPress;
final FamilyFileResponseModelLists profile;
const FamilyCardWidget({required this.onAddFamilyMemberPress, required this.profile, required this.onFamilySwitchPress(FamilyFileResponseModelLists member)});
FamilyCardWidget({super.key, required this.onAddFamilyMemberPress, required this.profile, required this.onFamilySwitchPress(FamilyFileResponseModelLists member)});
@override
Widget build(BuildContext context) {
@ -268,17 +275,16 @@ class FamilyCardWidget extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start,
spacing: 8.h,
children: [
Image.asset(appState.getAuthenticatedUser()?.gender == 1 ? AppAssets.male_img : AppAssets.femaleImg, width: 56.h, height: 56.h),
Image.asset(profile.gender == 1 ? AppAssets.male_img : AppAssets.femaleImg, width: 56.h, height: 56.h),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
spacing: 0.h,
mainAxisSize: MainAxisSize.min,
children: [
"${appState.getAuthenticatedUser()!.firstName} ${appState.getAuthenticatedUser()!.lastName}"
.toText18(isBold: true, weight: FontWeight.w600, textOverflow: TextOverflow.ellipsis, maxlines: 1),
"${profile.patientName}".toText18(isBold: true, weight: FontWeight.w600, textOverflow: TextOverflow.ellipsis, maxlines: 1),
AppCustomChipWidget(
icon: AppAssets.file_icon,
labelText: "${LocaleKeys.fileNo.tr(context: context)}: ${appState.getAuthenticatedUser()!.patientId}",
labelText: "${LocaleKeys.fileNo.tr(context: context)}: ${profile.patientId}",
iconSize: 12,
),
],
@ -291,46 +297,137 @@ class FamilyCardWidget extends StatelessWidget {
child: Wrap(
alignment: WrapAlignment.start,
spacing: 8.h,
runSpacing: 8.h,
children: [
AppCustomChipWidget(labelText: "${appState.getAuthenticatedUser()!.age} Years Old"),
AppCustomChipWidget(labelText: "${profile.age} Years Old"),
AppCustomChipWidget(
icon: AppAssets.blood_icon,
labelText: "${LocaleKeys.bloodType.tr(context: context)}: ${appState.getUserBloodGroup}",
iconColor: AppColors.primaryRedColor,
icon: AppAssets.blood_icon, labelText: "${LocaleKeys.bloodType.tr()}: ${appState.getAuthenticatedUser()!.bloodGroup ?? "N/A"}", iconColor: AppColors.primaryRedColor),
Selector<InsuranceViewModel, ({bool isEmpty, int? patientID, bool isLoading, String? cardValidTo})>(
selector: (context, insuranceVM) => (
isEmpty: insuranceVM.patientInsuranceList.isEmpty,
patientID: insuranceVM.patientInsuranceList.isNotEmpty ? insuranceVM.patientInsuranceList.first.patientID : null,
isLoading: insuranceVM.isInsuranceLoading,
cardValidTo: insuranceVM.patientInsuranceList.isNotEmpty ? insuranceVM.patientInsuranceList.first.cardValidTo : null
),
Consumer<InsuranceViewModel>(builder: (context, insuranceVM, child) {
builder: (context, data, child) {
if (data.isEmpty) {
return const SizedBox();
} else if (profile.responseId != data.patientID) {
return SizedBox();
}
final isLoading = data.isLoading;
final isExpired = !isLoading && DateTime.now().isAfter(DateUtil.convertStringToDate(data.cardValidTo));
final String icon;
final String labelText;
final Color iconColor;
final Color backgroundColor;
if (isLoading) {
icon = AppAssets.cancel_circle_icon;
labelText = "Insurance";
iconColor = AppColors.primaryRedColor;
backgroundColor = AppColors.primaryRedColor;
} else if (isExpired) {
icon = AppAssets.cancel_circle_icon;
labelText = "Insurance Expired".needTranslation;
iconColor = AppColors.primaryRedColor;
backgroundColor = AppColors.primaryRedColor.withValues(alpha: 0.15);
} else {
icon = AppAssets.insurance_active_icon;
labelText = "Insurance Active".needTranslation;
iconColor = AppColors.successColor;
backgroundColor = AppColors.successColor.withValues(alpha: 0.15);
}
return AppCustomChipWidget(
icon: insuranceVM.isInsuranceLoading
? AppAssets.cancel_circle_icon
: (DateTime.now().isAfter(
DateUtil.convertStringToDate(insuranceVM.patientInsuranceList.first.cardValidTo),
))
? AppAssets.cancel_circle_icon
: AppAssets.insurance_active_icon,
labelText: insuranceVM.isInsuranceLoading
? "Insurance"
: (DateTime.now().isAfter(
DateUtil.convertStringToDate(insuranceVM.patientInsuranceList.first.cardValidTo),
icon: icon,
labelText: labelText,
iconColor: iconColor,
iconSize: 12,
backgroundColor: backgroundColor,
// padding: EdgeInsets.zero,
).toShimmer2(isShow: isLoading);
},
)
? "Insurance Expired".needTranslation
: "Insurance Active".needTranslation),
iconColor: insuranceVM.isInsuranceLoading
? AppColors.primaryRedColor
: (DateTime.now().isAfter(
DateUtil.convertStringToDate(insuranceVM.patientInsuranceList.first.cardValidTo),
))
? AppColors.primaryRedColor
: AppColors.successColor,
iconSize: 14,
backgroundColor: insuranceVM.isInsuranceLoading
? AppColors.primaryRedColor
: (DateTime.now().isAfter(
DateUtil.convertStringToDate(insuranceVM.patientInsuranceList.first.cardValidTo),
))
? AppColors.primaryRedColor.withValues(alpha: 0.15)
: AppColors.successColor.withValues(alpha: 0.15),
).toShimmer2(isShow: insuranceVM.isInsuranceLoading);
}),
// Consumer<InsuranceViewModel>(builder: (context, insuranceVM, child) {
// if (insuranceVM.patientInsuranceList.isEmpty) {
// return const SizedBox();
// } else if (profile.responseId != insuranceVM.patientInsuranceList.first.patientID) {
// return SizedBox();
// }
//
// final isLoading = insuranceVM.isInsuranceLoading;
// final isExpired = !isLoading && DateTime.now().isAfter(DateUtil.convertStringToDate(insuranceVM.patientInsuranceList.first.cardValidTo));
//
// final String icon;
// final String labelText;
// final Color iconColor;
// final Color backgroundColor;
//
// if (isLoading) {
// icon = AppAssets.cancel_circle_icon;
// labelText = "Insurance";
// iconColor = AppColors.primaryRedColor;
// backgroundColor = AppColors.primaryRedColor;
// } else if (isExpired) {
// icon = AppAssets.cancel_circle_icon;
// labelText = "Insurance Expired".needTranslation;
// iconColor = AppColors.primaryRedColor;
// backgroundColor = AppColors.primaryRedColor.withValues(alpha: 0.15);
// } else {
// icon = AppAssets.insurance_active_icon;
// labelText = "Insurance Active".needTranslation;
// iconColor = AppColors.successColor;
// backgroundColor = AppColors.successColor.withValues(alpha: 0.15);
// }
//
// return AppCustomChipWidget(
// icon: icon,
// labelText: labelText,
// iconColor: iconColor,
// iconSize: 12,
// backgroundColor: backgroundColor,
// // padding: EdgeInsets.zero,
// ).toShimmer2(isShow: isLoading);
// })
// Consumer<InsuranceViewModel>(builder: (context, insuranceVM, child) {
// return insuranceVM.patientInsuranceList. isNotEmpty ? AppCustomChipWidget(
// icon: insuranceVM.isInsuranceLoading
// ? AppAssets.cancel_circle_icon
// : (DateTime.now().isAfter(
// DateUtil.convertStringToDate(insuranceVM.patientInsuranceList.first.cardValidTo),
// ))
// ? AppAssets.cancel_circle_icon
// : AppAssets.insurance_active_icon,
// labelText: insuranceVM.isInsuranceLoading
// ? "Insurance"
// : (DateTime.now().isAfter(
// DateUtil.convertStringToDate(insuranceVM.patientInsuranceList.first.cardValidTo),
// )
// ? "Insurance Expired".needTranslation
// : "Insurance Active".needTranslation),
// iconColor: insuranceVM.isInsuranceLoading
// ? AppColors.primaryRedColor
// : (DateTime.now().isAfter(
// DateUtil.convertStringToDate(insuranceVM.patientInsuranceList.first.cardValidTo),
// ))
// ? AppColors.primaryRedColor
// : AppColors.successColor,
// iconSize: 14,
// backgroundColor: insuranceVM.isInsuranceLoading
// ? AppColors.primaryRedColor
// : (DateTime.now().isAfter(
// DateUtil.convertStringToDate(insuranceVM.patientInsuranceList.first.cardValidTo),
// ))
// ? AppColors.primaryRedColor.withValues(alpha: 0.15)
// : AppColors.successColor.withValues(alpha: 0.15),
// ).toShimmer2(isShow: insuranceVM.isInsuranceLoading) : SizedBox();
// }),
],
),
),
@ -426,8 +523,7 @@ class FamilyCardWidget extends StatelessWidget {
).paddingOnly(top: 12, right: 16, left: 16, bottom: 16);
}
// //TODO: Add family file switch logic here
// //TODO: Add family file switch logic here
// isRootUser
// ? CustomButton(icon: AppAssets.add_family, text: "Add a new family member".needTranslation, height: 40.h, fontSize: 14, onPressed: () {})
// .paddingOnly(top: 12, right: 16, left: 16, bottom: 16)

@ -24,6 +24,8 @@ abstract class DialogService {
Future<void> showFamilyBottomSheetWithoutH(
{String? label, required String message, required Function(FamilyFileResponseModelLists response) onSwitchPress, required List<FamilyFileResponseModelLists> profiles});
Future<void> showFamilyBottomSheetWithoutHWithChild({String? label, required String message, Widget? child, required Function() onOkPressed, Function()? onCancelPressed});
Future<void> showPhoneNumberPickerSheet({String? label, String? message, required Function() onSMSPress, required Function() onWhatsappPress});
Future<void> showAddFamilyFileSheet({String? label, String? message, required Function() onVerificationPress});
@ -119,6 +121,18 @@ class DialogServiceImp implements DialogService {
callBackFunc: () {});
}
@override
Future<void> showFamilyBottomSheetWithoutHWithChild({String? label, required String message, Widget? child, required Function() onOkPressed, Function()? onCancelPressed}) async {
final context = navigationService.navigatorKey.currentContext;
if (context == null) return;
showCommonBottomSheetWithoutHeight(
context,
title: label ?? "",
child: child ?? SizedBox(),
callBackFunc: () {},
);
}
@override
Future<void> showPhoneNumberPickerSheet({String? label, String? message, required Function() onSMSPress, required Function() onWhatsappPress}) async {
final context = navigationService.navigatorKey.currentContext;

@ -24,8 +24,8 @@ class AppCustomChipWidget extends StatelessWidget {
this.deleteIconColor = AppColors.textColor,
this.deleteIconHasColor = false,
this.padding = EdgeInsets.zero,
this.onChipTap
this.labelPadding ,
this.onChipTap,
this.labelPadding,
});
final String? labelText;
@ -47,7 +47,9 @@ class AppCustomChipWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ChipTheme(
return GestureDetector(
onTap: onChipTap,
child: ChipTheme(
data: ChipThemeData(
padding: EdgeInsets.all(0.0),
shape: SmoothRectangleBorder(
@ -66,7 +68,7 @@ class AppCustomChipWidget extends StatelessWidget {
// padding: EdgeInsets.all(0.0),
padding: padding,
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
labelPadding: labelPadding??EdgeInsetsDirectional.only(start: 0.h, end: deleteIcon?.isNotEmpty == true ? 2.h : 8.h),
labelPadding: labelPadding ?? EdgeInsetsDirectional.only(start: 0.h, end: deleteIcon?.isNotEmpty == true ? 2.h : 8.h),
backgroundColor: backgroundColor,
shape: shape ??
SmoothRectangleBorder(
@ -84,7 +86,8 @@ class AppCustomChipWidget extends StatelessWidget {
label: richText ?? labelText!.toText10(weight: FontWeight.w500, letterSpacing: 0, color: textColor),
padding: EdgeInsets.all(0.0),
backgroundColor: backgroundColor,
shape: shape ?? SmoothRectangleBorder(
shape: shape ??
SmoothRectangleBorder(
borderRadius: BorderRadius.circular(8 ?? 0),
smoothness: 10,
side: BorderSide(color: AppColors.transparent, width: 1.5),
@ -95,6 +98,7 @@ class AppCustomChipWidget extends StatelessWidget {
: null,
onDeleted: deleteIcon?.isNotEmpty == true ? () {} : null,
),
),
);
}
}

Loading…
Cancel
Save