livecare enhancements in progress

CR-19328-LiveCare-Enhancements
haroon amjad 4 weeks ago
parent 3030c13a17
commit f17142419c

@ -0,0 +1,5 @@
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="40" height="40" rx="8" fill="#EFEFF0"/>
<path d="M24.5303 16.5303C24.8232 16.2374 24.8232 15.7626 24.5303 15.4697C24.2374 15.1768 23.7626 15.1768 23.4697 15.4697L20.0001 18.9393L18.0303 16.9697C17.7374 16.6768 17.2625 16.6768 16.9697 16.9697C16.6768 17.2626 16.6768 17.7375 16.9697 18.0303L18.9394 19.9999L18.4697 20.4697C18.1768 20.7626 18.1768 21.2374 18.4697 21.5303C18.7626 21.8232 19.2374 21.8232 19.5303 21.5303L20.0001 21.0606L20.4696 21.53C20.7625 21.8229 21.2373 21.8228 21.5302 21.5299C21.8231 21.237 21.8231 20.7622 21.5302 20.4693L21.0608 19.9999L24.5303 16.5303Z" fill="#2E3039"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M20 9.25C14.0629 9.25 9.25 14.0629 9.25 20C9.25 25.9371 14.0629 30.75 20 30.75C25.9371 30.75 30.75 25.9371 30.75 20C30.75 14.0629 25.9371 9.25 20 9.25ZM10.75 20C10.75 14.8914 14.8914 10.75 20 10.75C25.1086 10.75 29.25 14.8914 29.25 20C29.25 25.1086 25.1086 29.25 20 29.25C14.8914 29.25 10.75 25.1086 10.75 20Z" fill="#2E3039"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

@ -1966,5 +1966,11 @@
"weeklyShift": "الفترات الأسبوعية",
"dayOff": "إجازة",
"notAvailable": "غير متاح",
"hide": "إخفاء"
"hide": "إخفاء",
"liveCareSupport": "دعم لايف كير",
"liveCareSupportAssistance": "للتواصل والمساعدة",
"liveCareSupportWhatsApp": "ارسال رسالة واتس اب",
"preferredConsultationLanguage": "اللغة المفضلة للاستشارة",
"preferredConsultationLanguageEn": "أرغب أن يتحدث معي الطبيب باللغة العربية",
"preferredConsultationLanguageAr": "أرغب أن يتحدث مع الطبيب باللغة اللانجليزية"
}

@ -1956,7 +1956,13 @@
"weeklyShift": "Weekly Shift",
"dayOff": "Off",
"notAvailable": "Not Available",
"hide": "Hide"
"hide": "Hide",
"liveCareSupport": "LiveCare Support",
"liveCareSupportAssistance": "For Support & Assistance",
"liveCareSupportWhatsApp": "Send WhatsApp Message",
"preferredConsultationLanguage": "Preferred Consultation Language",
"preferredConsultationLanguageEn": "I would like to speak with the doctor in Arabic",
"preferredConsultationLanguageAr": "I would like to speak with the doctor in English"
}

@ -210,8 +210,8 @@ class ApiClientImp implements ApiClient {
body['TokenID'] = "@dm!n";
}
// body['TokenID'] = "@dm!n";
// body['PatientID'] = 2122445;
body['TokenID'] = "@dm!n";
// body['PatientID'] = 4770714;
// body['PatientID'] = 53320;
// body['PatientTypeID'] = 1;
// body['PatientOutSA'] = 0;

@ -399,6 +399,7 @@ class AppAssets {
static const String habibLogoOnly = '$svgBasePath/habib logo_only.svg';
static const String descriptionIcon = '$svgBasePath/description_icon.svg';
static const String selectImageIcon = '$svgBasePath/select_image_icon.svg';
static const String liveCareRescheduleIcon = '$svgBasePath/livecare_reschedule_icon.svg';
// PNGS //
static const String hmgLogo = '$pngBasePath/hmg_logo.png';

@ -52,6 +52,8 @@ class ImmediateLiveCareViewModel extends ChangeNotifier {
late AppState _appState;
int selectedConsultationLanguage = 0;
Duration countdownDuration = Duration(minutes: 1, seconds: 0);
ValueNotifier<Duration> durationNotifier = ValueNotifier<Duration>(Duration(minutes: 1, seconds: 0));
Timer? timer;
@ -65,8 +67,10 @@ class ImmediateLiveCareViewModel extends ChangeNotifier {
mainLiveCareClinicList = [];
isImmediateLiveCareClinicsLoading = true;
liveCareSelectedCallType = 0; // 1- Video, 2- Audio, 3- Phone
selectedConsultationLanguage = 0;
immediateLiveCareSelectedClinic = GetLiveCareClinicListResponseModel();
liveCareImmediateAppointmentFeesList = LiveCareImmediateAppointmentFeesList();
notifyListeners();
}
setLiveCareSelectedCallType(int value) {
@ -79,6 +83,11 @@ class ImmediateLiveCareViewModel extends ChangeNotifier {
notifyListeners();
}
setSelectedConsultationLanguage(int selectedLanguageID) {
selectedConsultationLanguage = selectedLanguageID;
notifyListeners();
}
Future<void> getLiveCareImmediateClinicsList({Function(dynamic)? onSuccess, Function(String)? onError}) async {
immediateLiveCareClinicsList.clear();
isImmediateLiveCareClinicsLoading = true;

@ -172,9 +172,9 @@ class MyAppointmentsRepoImp implements MyAppointmentsRepo {
try {
final list = response['OnlineCheckInAppointments'];
patientShareObj = (list == null || list.isEmpty) ? PatientAppointmentShareResponseModel() : PatientAppointmentShareResponseModel.fromJson(list[0]);
patientShareObj.isCash = response["IsCash"];
patientShareObj.isEligible = response["IsEligible"];
patientShareObj.isInsured = response["IsInsured"];
patientShareObj.isCash = list[0]["IsCash"];
patientShareObj.isEligible = list[0]["IsEligible"];
patientShareObj.isInsured = list[0]["IsInsured"];
// if (list == null || list.isEmpty) {
// throw Exception("patient share list is empty");

@ -1948,5 +1948,11 @@ abstract class LocaleKeys {
static const weeklyShift = 'weeklyShift';
static const dayOff = 'dayOff';
static const hide = 'hide';
static const liveCareSupport = 'liveCareSupport';
static const liveCareSupportAssistance = 'liveCareSupportAssistance';
static const liveCareSupportWhatsApp = 'liveCareSupportWhatsApp';
static const preferredConsultationLanguage = 'preferredConsultationLanguage';
static const preferredConsultationLanguageEn = 'preferredConsultationLanguageEn';
static const preferredConsultationLanguageAr = 'preferredConsultationLanguageAr';
}

@ -551,7 +551,7 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment ?? false
? Row(
children: [
Utils.buildSvgWithAssets(icon: AppAssets.livecare_clinic_icon, width: 40.h, height: 40.h),
Utils.buildSvgWithAssets(icon: AppAssets.livecare_clinic_icon, width: 40.h, height: 40.h, applyThemeColor: false),
SizedBox(width: 12.w),
Expanded(
child: Column(

@ -27,6 +27,7 @@ import 'package:hmg_patient_app_new/presentation/appointments/widgets/region_bot
import 'package:hmg_patient_app_new/presentation/book_appointment/doctor_profile_page.dart';
import 'package:hmg_patient_app_new/presentation/book_appointment/livecare/immediate_livecare_pending_request_page.dart';
import 'package:hmg_patient_app_new/presentation/book_appointment/livecare/select_immediate_livecare_clinic_page.dart';
import 'package:hmg_patient_app_new/presentation/book_appointment/livecare/widgets/livecare_support_widget.dart';
import 'package:hmg_patient_app_new/presentation/book_appointment/search_doctor_by_name.dart';
import 'package:hmg_patient_app_new/presentation/book_appointment/select_clinic_page.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
@ -96,12 +97,21 @@ class _BookAppointmentPageState extends State<BookAppointmentPage> {
body: Column(
children: [
Expanded(
child: CollapsingListView(
title: LocaleKeys.bookAppo.tr(context: context),
isLeading: true,
child: SingleChildScrollView(
child: Consumer<BookAppointmentsViewModel>(builder: (context, bookAppointmentsVM, child) {
return Column(
child: Consumer<BookAppointmentsViewModel>(builder: (context, bookAppointmentsVM, child) {
return CollapsingListView(
liveCareSupport: bookAppointmentsViewModel.selectedTabIndex == 1 ? () {
showCommonBottomSheetWithoutHeight(
context,
title: LocaleKeys.liveCareSupport.tr(),
child: LivecareSupportWidget(),
callBackFunc: () {},
isFullScreen: false,
);
} : null,
title: LocaleKeys.bookAppo.tr(context: context),
isLeading: true,
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 16.h),
@ -442,10 +452,10 @@ class _BookAppointmentPageState extends State<BookAppointmentPage> {
}),
],
],
);
}),
),
),
),
),
);
}),
),
Consumer<BookAppointmentsViewModel>(builder: (context, bookAppointmentsVM, child) {
return _buildSymptomsBottomCard();

@ -15,6 +15,7 @@ import 'package:hmg_patient_app_new/features/immediate_livecare/immediate_liveca
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/book_appointment/livecare/immediate_livecare_payment_page.dart';
import 'package:hmg_patient_app_new/presentation/book_appointment/livecare/immediate_livecare_pending_request_page.dart';
import 'package:hmg_patient_app_new/presentation/book_appointment/livecare/widgets/preferred_consultation_language_select.dart';
import 'package:hmg_patient_app_new/presentation/book_appointment/livecare/widgets/select_livecare_call_type.dart';
import 'package:hmg_patient_app_new/presentation/home/navigation_screen.dart';
import 'package:hmg_patient_app_new/presentation/insurance/insurance_home_page.dart';
@ -29,12 +30,44 @@ import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:provider/provider.dart';
import 'package:smooth_corner/smooth_corner.dart';
import 'package:video_player/video_player.dart';
class ImmediateLiveCarePaymentDetails extends StatelessWidget {
class ImmediateLiveCarePaymentDetails extends StatefulWidget {
ImmediateLiveCarePaymentDetails({super.key});
@override
State<ImmediateLiveCarePaymentDetails> createState() => _ImmediateLiveCarePaymentDetailsState();
}
class _ImmediateLiveCarePaymentDetailsState extends State<ImmediateLiveCarePaymentDetails> {
// late ImmediateLiveCareViewModel immediateLiveCareViewModel;
late AppState appState;
late VideoPlayerController _videoController;
bool _isVideoInitialized = false;
@override
void initState() {
super.initState();
_initializeVideoPlayer();
}
void _initializeVideoPlayer() {
_videoController = VideoPlayerController.networkUrl(
Uri.parse('https://avtshare01.rz.tu-ilmenau.de/avt-vqdb-uhd-1/test_1/segments/bigbuck_bunny_8bit_15000kbps_1080p_60.0fps_hevc.mp4'),
)..initialize().then((_) {
setState(() {
_isVideoInitialized = true;
});
}).catchError((error) {
debugPrint('Error initializing video: $error');
});
}
@override
void dispose() {
_videoController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
@ -141,14 +174,28 @@ class ImmediateLiveCarePaymentDetails extends StatelessWidget {
Utils.buildSvgWithAssets(icon: AppAssets.edit_icon, width: 24.h, height: 24.h, fit: BoxFit.contain),
],
),
SizedBox(height: 16.h),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Utils.buildSvgWithAssets(icon: AppAssets.language, width: 32.h, height: 32.h, fit: BoxFit.contain, iconColor: AppColors.textColor, applyThemeColor: false),
SizedBox(width: 8.h),
"${LocaleKeys.preferredLanguage.tr()}: ${immediateLiveCareVM.selectedConsultationLanguage == 1 ? LocaleKeys.arabic.tr() : (immediateLiveCareVM.selectedConsultationLanguage == 2 ? LocaleKeys.english.tr() : "")}"
.toText16(isBold: true),
],
),
Utils.buildSvgWithAssets(icon: AppAssets.edit_icon, width: 24.h, height: 24.h, fit: BoxFit.contain),
],
),
],
),
),
).onPress(() {
showCommonBottomSheetWithoutHeight(context, child: SelectLiveCareCallType(immediateLiveCareViewModel: immediateLiveCareVM),
callBackFunc: () async {
debugPrint("Selected Call Type: ${immediateLiveCareVM.liveCareSelectedCallType}");
}, title: LocaleKeys.selectLiveCareCallType.tr(context: context), isCloseButtonVisible: true, isFullScreen: false);
showCommonBottomSheetWithoutHeight(context, child: PreferredConsultationLanguageSelect(immediateLiveCareViewModel: immediateLiveCareVM), callBackFunc: () async {
// debugPrint("Selected Call Type: ${immediateLiveCareVM.liveCareSelectedCallType}");
}, title: LocaleKeys.preferredConsultationLanguage.tr(context: context), isCloseButtonVisible: true, isFullScreen: false);
});
}),
SizedBox(height: 24.h)
@ -305,54 +352,108 @@ class ImmediateLiveCarePaymentDetails extends StatelessWidget {
? CustomButton(
text: LocaleKeys.confirmLiveCare.tr(context: context),
onPressed: () async {
await askVideoCallPermission(context).then((val) async {
if (val) {
LoaderBottomSheet.showLoader(loadingText: LocaleKeys.confirmingLiveCareRequest.tr(context: context));
if (immediateLiveCareVM.selectedConsultationLanguage != 0) {
await askVideoCallPermission(context).then((val) async {
if (val) {
LoaderBottomSheet.showLoader(loadingText: LocaleKeys.confirmingLiveCareRequest.tr(context: context));
await immediateLiveCareVM.addNewCallRequestForImmediateLiveCare(
"${appState.getAuthenticatedUser()!.patientId}${DateTime.now().millisecondsSinceEpoch}");
await immediateLiveCareVM.getPatientLiveCareHistory();
LoaderBottomSheet.hideLoader();
if (immediateLiveCareVM.patientHasPendingLiveCareRequest) {
Navigator.pushAndRemoveUntil(
context,
await immediateLiveCareVM.addNewCallRequestForImmediateLiveCare("${appState.getAuthenticatedUser()!.patientId}${DateTime.now().millisecondsSinceEpoch}");
await immediateLiveCareVM.getPatientLiveCareHistory();
LoaderBottomSheet.hideLoader();
if (immediateLiveCareVM.patientHasPendingLiveCareRequest) {
Navigator.pushAndRemoveUntil(
context,
CustomPageRoute(
page: LandingNavigation(),
),
(r) => false);
Navigator.of(context).push(
CustomPageRoute(
page: LandingNavigation(),
page: ImmediateLiveCarePendingRequestPage(),
),
(r) => false);
Navigator.of(context).push(
CustomPageRoute(
page: ImmediateLiveCarePendingRequestPage(),
),
);
);
} else {
showCommonBottomSheetWithoutHeight(
context,
child: Utils.getErrorWidget(loadingText: LocaleKeys.unknownErrorOccurred.tr(context: context)),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
}
} else {
showCommonBottomSheetWithoutHeight(
title: LocaleKeys.notice.tr(context: context),
context,
child: Utils.getErrorWidget(loadingText: LocaleKeys.unknownErrorOccurred.tr(context: context)),
child: Utils.getWarningWidget(
loadingText: LocaleKeys.liveCarePermissionsMessage.tr(context: context),
isShowActionButtons: true,
onCancelTap: () {
Navigator.pop(context);
},
onConfirmTap: () async {
openAppSettings();
}),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
}
} else {
showCommonBottomSheetWithoutHeight(
title: LocaleKeys.notice.tr(context: context),
context,
child: Utils.getWarningWidget(
loadingText: LocaleKeys.liveCarePermissionsMessage.tr(context: context),
isShowActionButtons: true,
onCancelTap: () {
Navigator.pop(context);
},
onConfirmTap: () async {
openAppSettings();
}),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
}
});
});
} else {
showCommonBottomSheetWithoutHeight(context, child: PreferredConsultationLanguageSelect(immediateLiveCareViewModel: immediateLiveCareVM), callBackFunc: () async {
debugPrint("Selected language: ${immediateLiveCareVM.selectedConsultationLanguage}");
if (immediateLiveCareVM.selectedConsultationLanguage != 0) {
await askVideoCallPermission(context).then((val) async {
if (val) {
LoaderBottomSheet.showLoader(loadingText: LocaleKeys.confirmingLiveCareRequest.tr(context: context));
await immediateLiveCareVM.addNewCallRequestForImmediateLiveCare("${appState.getAuthenticatedUser()!.patientId}${DateTime.now().millisecondsSinceEpoch}");
await immediateLiveCareVM.getPatientLiveCareHistory();
LoaderBottomSheet.hideLoader();
if (immediateLiveCareVM.patientHasPendingLiveCareRequest) {
Navigator.pushAndRemoveUntil(
context,
CustomPageRoute(
page: LandingNavigation(),
),
(r) => false);
Navigator.of(context).push(
CustomPageRoute(
page: ImmediateLiveCarePendingRequestPage(),
),
);
} else {
showCommonBottomSheetWithoutHeight(
context,
child: Utils.getErrorWidget(loadingText: LocaleKeys.unknownErrorOccurred.tr(context: context)),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
}
} else {
showCommonBottomSheetWithoutHeight(
title: LocaleKeys.notice.tr(context: context),
context,
child: Utils.getWarningWidget(
loadingText: LocaleKeys.liveCarePermissionsMessage.tr(context: context),
isShowActionButtons: true,
onCancelTap: () {
Navigator.pop(context);
},
onConfirmTap: () async {
openAppSettings();
}),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
}
});
}
}, title: LocaleKeys.preferredConsultationLanguage.tr(context: context), isCloseButtonVisible: true, isFullScreen: false);
}
},
backgroundColor: AppColors.successColor,
borderColor: AppColors.successColor,
@ -369,33 +470,68 @@ class ImmediateLiveCarePaymentDetails extends StatelessWidget {
: CustomButton(
text: LocaleKeys.payNow.tr(context: context),
onPressed: () async {
await askVideoCallPermission(context).then((val) {
if (val) {
Navigator.of(context).push(
CustomPageRoute(
page: ImmediateLiveCarePaymentPage(),
),
);
}
// else {
// showCommonBottomSheetWithoutHeight(
// title: LocaleKeys.notice.tr(context: context),
// context,
// child: Utils.getWarningWidget(
// loadingText: LocaleKeys.liveCarePermissionsMessage.tr(context: context),
// isShowActionButtons: true,
// onCancelTap: () {
// Navigator.pop(context);
// },
// onConfirmTap: () async {
// openAppSettings();
// }),
// callBackFunc: () {},
// isFullScreen: false,
// isCloseButtonVisible: true,
// );
// }
});
if (immediateLiveCareVM.selectedConsultationLanguage != 0) {
await askVideoCallPermission(context).then((val) {
if (val) {
Navigator.of(context).push(
CustomPageRoute(
page: ImmediateLiveCarePaymentPage(),
),
);
}
// else {
// showCommonBottomSheetWithoutHeight(
// title: LocaleKeys.notice.tr(context: context),
// context,
// child: Utils.getWarningWidget(
// loadingText: LocaleKeys.liveCarePermissionsMessage.tr(context: context),
// isShowActionButtons: true,
// onCancelTap: () {
// Navigator.pop(context);
// },
// onConfirmTap: () async {
// openAppSettings();
// }),
// callBackFunc: () {},
// isFullScreen: false,
// isCloseButtonVisible: true,
// );
// }
});
} else {
showCommonBottomSheetWithoutHeight(context, child: PreferredConsultationLanguageSelect(immediateLiveCareViewModel: immediateLiveCareVM), callBackFunc: () async {
debugPrint("Selected language: ${immediateLiveCareVM.selectedConsultationLanguage}");
if (immediateLiveCareVM.selectedConsultationLanguage != 0) {
await askVideoCallPermission(context).then((val) {
if (val) {
Navigator.of(context).push(
CustomPageRoute(
page: ImmediateLiveCarePaymentPage(),
),
);
}
// else {
// showCommonBottomSheetWithoutHeight(
// title: LocaleKeys.notice.tr(context: context),
// context,
// child: Utils.getWarningWidget(
// loadingText: LocaleKeys.liveCarePermissionsMessage.tr(context: context),
// isShowActionButtons: true,
// onCancelTap: () {
// Navigator.pop(context);
// },
// onConfirmTap: () async {
// openAppSettings();
// }),
// callBackFunc: () {},
// isFullScreen: false,
// isCloseButtonVisible: true,
// );
// }
});
}
}, title: LocaleKeys.preferredConsultationLanguage.tr(context: context), isCloseButtonVisible: true, isFullScreen: false);
}
},
backgroundColor: AppColors.infoColor,
borderColor: AppColors.infoColor,

@ -12,14 +12,17 @@ import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/contact_us/contact_us_view_model.dart';
import 'package:hmg_patient_app_new/features/immediate_livecare/immediate_livecare_view_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/book_appointment/livecare/widgets/livecare_reschedule_widget.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart';
import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart';
import 'package:hmg_patient_app_new/widgets/countdown_timer.dart';
import 'package:lottie/lottie.dart';
import 'package:provider/provider.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:video_player/video_player.dart';
class ImmediateLiveCarePendingRequestPage extends StatefulWidget {
ImmediateLiveCarePendingRequestPage({super.key});
@ -34,9 +37,35 @@ class _ImmediateLiveCarePendingRequestPageState extends State<ImmediateLiveCareP
late AppState appState;
String chatURL = "";
late VideoPlayerController _videoController;
bool _isVideoInitialized = false;
@override
void initState() {
super.initState();
_initializeVideoPlayer();
}
void _initializeVideoPlayer() {
debugPrint('🎥 Initializing video player...');
_videoController = VideoPlayerController.networkUrl(
Uri.parse('https://marathoon.com/service/hmgvideo/compressedvideo.mp4'),
)..initialize().then((_) {
setState(() {
_isVideoInitialized = true;
});
// Set video to loop
_videoController.setLooping(true);
// Start playing automatically
_videoController.play();
}).catchError((error) {});
// Add listener to update UI when video state changes
_videoController.addListener(() {
if (mounted) {
setState(() {});
}
});
}
@override
@ -47,6 +76,7 @@ class _ImmediateLiveCarePendingRequestPageState extends State<ImmediateLiveCareP
} catch (e) {
// Handle case where viewModel might not be initialized
}
_videoController.dispose();
super.dispose();
}
@ -128,7 +158,7 @@ class _ImmediateLiveCarePendingRequestPageState extends State<ImmediateLiveCareP
color: AppColors.whiteColor,
borderRadius: 20.h,
hasShadow: false,
// side: BorderSide(color: AppColors.ratingColorYellow, width: 3.h),
side: BorderSide(color: AppColors.ratingColorYellow, width: 3.h),
),
child: Padding(
padding: EdgeInsets.all(16.h),
@ -148,19 +178,229 @@ class _ImmediateLiveCarePendingRequestPageState extends State<ImmediateLiveCareP
);
},
),
SizedBox(height: 16.h),
CustomButton(
text: "I can't wait, Please call me later",
onPressed: () {
showCommonBottomSheetWithoutHeight(context,
child: LiveCareRescheduleWidget(), callBackFunc: () async {}, title: "When would you like to be called back?", isCloseButtonVisible: true, isFullScreen: false);
},
backgroundColor: AppColors.warningColorYellow.withValues(alpha: 0.20),
borderColor: AppColors.warningColorYellow.withValues(alpha: 0.01),
textColor: AppColors.alertColor,
fontSize: 14.f,
fontWeight: FontWeight.w600,
borderRadius: 12.r,
padding: EdgeInsets.symmetric(horizontal: 10.w),
height: 40.h,
iconColor: AppColors.alertColor,
iconSize: 18.h,
icon: AppAssets.calendar,
),
SizedBox(height: 8.h),
],
),
),
),
SizedBox(
height: 55.h,
height: 24.h,
),
Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 24.r,
hasShadow: false,
),
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 24.h, vertical: 24.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
"How LiveCare works".toText16(isBold: true).paddingOnly(bottom: 4.h),
"A quick guide to your video consultation".toText12(isBold: true, color: AppColors.greyTextColor).paddingOnly(bottom: 16.h),
// Video player
_isVideoInitialized
? SizedBox(
width: double.infinity,
child: ClipRRect(
borderRadius: BorderRadius.circular(12.h),
child: AspectRatio(
aspectRatio: _videoController.value.aspectRatio,
child: GestureDetector(
onTap: () {
setState(() {
if (_videoController.value.isPlaying) {
_videoController.pause();
} else {
_videoController.play();
}
});
},
child: Stack(
fit: StackFit.expand,
children: [
Container(
color: Colors.black,
child: VideoPlayer(_videoController),
),
// Play/Pause button overlay - only show when paused
if (!_videoController.value.isPlaying)
Positioned.fill(
child: Container(
color: Colors.black.withValues(alpha: 0.3),
child: Center(
child: Icon(
Icons.play_circle_filled,
color: Colors.white,
size: 64.h,
),
),
),
),
// Debug info overlay (top-left corner)
Positioned(
top: 8.h,
left: 8.h,
child: Container(
padding: EdgeInsets.symmetric(horizontal: 8.h, vertical: 4.h),
decoration: BoxDecoration(
color: Colors.black.withValues(alpha: 0.7),
borderRadius: BorderRadius.circular(4.h),
),
child: Text(
'Video ${_videoController.value.isPlaying ? "Playing" : "Paused"}',
style: TextStyle(
color: Colors.white,
fontSize: 10.f,
),
),
),
),
// Show error if video has error
if (_videoController.value.hasError)
Positioned.fill(
child: Container(
color: Colors.red.withValues(alpha: 0.8),
child: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
Icons.error_outline,
color: Colors.white,
size: 48.h,
),
SizedBox(height: 8.h),
Text(
'Video Error',
style: TextStyle(
color: Colors.white,
fontSize: 14.f,
fontWeight: FontWeight.bold,
),
),
SizedBox(height: 4.h),
Padding(
padding: EdgeInsets.symmetric(horizontal: 16.h),
child: Text(
'${_videoController.value.errorDescription}',
style: TextStyle(
color: Colors.white,
fontSize: 10.f,
),
textAlign: TextAlign.center,
),
),
],
),
),
),
),
],
),
),
),
),
)
: Container(
width: double.infinity,
height: 180.h,
decoration: BoxDecoration(
color: AppColors.greyColor,
borderRadius: BorderRadius.circular(12.h),
),
child: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
CircularProgressIndicator(
color: AppColors.primaryRedColor,
),
SizedBox(height: 8.h),
Text(
'Loading video...',
style: TextStyle(
color: AppColors.textColor,
fontSize: 12.f,
),
),
],
),
),
),
],
),
),
),
CustomButton(
text: LocaleKeys.startLiveChat.tr(),
onPressed: () async {
// launchUrl(Uri.parse("https://chat.hmg.com/geneysChat/Index.aspx?RequestedId=44A515DF-E668-46B5-BD76-D7747D9C7308"));
_getChatRequestID(
],
),
),
),
),
Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 24.r,
hasShadow: false,
),
child: SizedBox(
height: 56.h,
child: Row(
children: [
Expanded(
flex: 10,
child: CustomButton(
text: LocaleKeys.callLiveCareSupport.tr(),
onPressed: () async {
launchUrl(Uri.parse("tel://" + "966 11 525 9553"));
},
backgroundColor: AppColors.lightRedButtonColor,
borderColor: AppColors.lightRedButtonColor,
borderWidth: 0,
textColor: AppColors.primaryRedColor,
fontSize: 16,
isBold: true,
borderRadius: 12,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 50.h,
icon: AppAssets.call_fill,
iconColor: AppColors.primaryRedColor,
iconSize: 21.h,
),
),
Expanded(
flex: 1,
child: SizedBox(
width: 12.h,
)),
Expanded(
flex: 10,
child: CustomButton(
text: LocaleKeys.startLiveChat.tr(),
onPressed: () async {
// launchUrl(Uri.parse("https://chat.hmg.com/geneysChat/Index.aspx?RequestedId=44A515DF-E668-46B5-BD76-D7747D9C7308"));
_getChatRequestID(
context,
name: appState.getAuthenticatedUser()!.firstName ?? '',
mobileNo: appState.getAuthenticatedUser()!.mobileNumber ?? '',
@ -180,43 +420,10 @@ class _ImmediateLiveCarePendingRequestPageState extends State<ImmediateLiveCareP
iconColor: !Utils.havePrivilege(118) ? AppColors.textColor : AppColors.whiteColor,
iconSize: 21.h,
),
SizedBox(
height: 16.h,
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
LocaleKeys.oR.tr().toText19(
color: AppColors.textColor,
),
],
),
SizedBox(
height: 16.h,
),
CustomButton(
text: LocaleKeys.callLiveCareSupport.tr(),
onPressed: () async {
launchUrl(Uri.parse("tel://" + "966 11 525 9553"));
},
backgroundColor: AppColors.lightRedButtonColor,
borderColor: AppColors.lightRedButtonColor,
borderWidth: 0,
textColor: AppColors.primaryRedColor,
fontSize: 16,
isBold: true,
borderRadius: 12,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 50.h,
icon: AppAssets.call_fill,
iconColor: AppColors.primaryRedColor,
iconSize: 21.h,
)
],
),
),
),
),
).paddingSymmetrical(24.h, 24.h)),
],
);
}),

@ -0,0 +1,85 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
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/theme/colors.dart';
import '../../../../core/dependencies.dart';
import '../../../../generated/locale_keys.g.dart';
class LiveCareRescheduleWidget extends StatelessWidget {
LiveCareRescheduleWidget({super.key});
late AppState appState;
@override
Widget build(BuildContext context) {
appState = getIt.get<AppState>();
return Column(
children: [
checkInOptionCard(
AppAssets.liveCareRescheduleIcon,
"After 30 mins",
"You will be called by the doctor after 30 mins",
// "Call for immediate assistance",
).onPress(() {
Navigator.of(context).pop();
}),
SizedBox(height: 16.h),
checkInOptionCard(
AppAssets.liveCareRescheduleIcon,
"After 1 hour",
"You will be called by the doctor after 1 hour",
).onPress(() async {
Navigator.of(context).pop();
}),
SizedBox(height: 16.h),
],
);
}
Widget checkInOptionCard(String icon, String title, String subTitle) {
return Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 20.r,
hasShadow: false,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Utils.buildSvgWithAssets(icon: icon, width: 40.h, height: 40.h, fit: BoxFit.fill),
SizedBox(height: 16.h),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
title.toText16(isBold: true, color: AppColors.textColor),
subTitle.toText12(isBold: true, color: AppColors.greyTextColor),
],
),
),
Transform.flip(
flipX: appState.isArabic(),
child: Utils.buildSvgWithAssets(
icon: AppAssets.forward_arrow_icon_small,
iconColor: AppColors.blackColor,
width: 18.h,
height: 13.h,
fit: BoxFit.contain,
),
),
],
),
],
).paddingAll(16.h),
);
}
}

@ -0,0 +1,96 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
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/theme/colors.dart';
import 'package:url_launcher/url_launcher.dart';
import '../../../../generated/locale_keys.g.dart';
class LivecareSupportWidget extends StatelessWidget {
LivecareSupportWidget({super.key});
late AppState appState;
@override
Widget build(BuildContext context) {
appState = getIt.get<AppState>();
return Column(
children: [
checkInOptionCard(
AppAssets.call_fill,
LocaleKeys.liveCareSupportAssistance.tr(),
LocaleKeys.callForAssistance.tr(),
// "Call for immediate assistance",
).onPress(() {
launchUrl(Uri.parse("tel://" + "+966 11 525 9553"));
}),
SizedBox(height: 16.h),
checkInOptionCard(
AppAssets.whatsapp,
LocaleKeys.liveCareSupportWhatsApp.tr(),
LocaleKeys.viewNearestHMGLocations.tr(),
).onPress(() async {
final String url = "https://wa.me/+966553034949"; // Replace with your WhatsApp number
final Uri whatsappUri = Uri.parse(url);
if (await canLaunchUrl(whatsappUri)) {
await launchUrl(
whatsappUri,
mode: LaunchMode.externalApplication, // Forces the OS to open the WhatsApp app
);
} else {
// throw 'Could not launch WhatsApp. Ensure the app is installed.';
}
}),
SizedBox(height: 16.h),
],
);
}
Widget checkInOptionCard(String icon, String title, String subTitle) {
return Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 20.r,
hasShadow: false,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Utils.buildSvgWithAssets(icon: icon, width: 24.h, height: 24.h, fit: BoxFit.fill, applyThemeColor: false),
SizedBox(height: 16.h),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
title.toText16(isBold: true, color: AppColors.textColor),
// subTitle.toText12(isBold: true, color: AppColors.greyTextColor),
],
),
),
Transform.flip(
flipX: appState.isArabic(),
child: Utils.buildSvgWithAssets(
icon: AppAssets.forward_arrow_icon_small,
iconColor: AppColors.blackColor,
width: 18.h,
height: 13.h,
fit: BoxFit.contain,
),
),
],
),
],
).paddingAll(16.h),
);
}
}

@ -0,0 +1,89 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
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/immediate_livecare/immediate_livecare_view_model.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import '../../../../generated/locale_keys.g.dart';
class PreferredConsultationLanguageSelect extends StatelessWidget {
PreferredConsultationLanguageSelect({super.key, required this.immediateLiveCareViewModel});
late AppState appState;
ImmediateLiveCareViewModel immediateLiveCareViewModel;
@override
Widget build(BuildContext context) {
appState = getIt.get<AppState>();
return Column(
children: [
checkInOptionCard(
AppAssets.call_fill,
LocaleKeys.preferredConsultationLanguageAr.tr(),
LocaleKeys.callForAssistance.tr(),
// "Call for immediate assistance",
).onPress(() {
immediateLiveCareViewModel.setSelectedConsultationLanguage(1);
Navigator.of(context).pop();
}),
SizedBox(height: 16.h),
checkInOptionCard(
AppAssets.whatsapp,
LocaleKeys.preferredConsultationLanguageEn.tr(),
LocaleKeys.viewNearestHMGLocations.tr(),
).onPress(() async {
immediateLiveCareViewModel.setSelectedConsultationLanguage(2);
Navigator.of(context).pop();
}),
SizedBox(height: 16.h),
],
);
}
Widget checkInOptionCard(String icon, String title, String subTitle) {
return Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 20.r,
hasShadow: false,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Utils.buildSvgWithAssets(icon: icon, width: 24.h, height: 24.h, fit: BoxFit.fill, applyThemeColor: false),
SizedBox(height: 16.h),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
title.toText16(isBold: true, color: AppColors.textColor),
// subTitle.toText12(isBold: true, color: AppColors.greyTextColor),
],
),
),
Transform.flip(
flipX: appState.isArabic(),
child: Utils.buildSvgWithAssets(
icon: AppAssets.forward_arrow_icon_small,
iconColor: AppColors.blackColor,
width: 18.h,
height: 13.h,
fit: BoxFit.contain,
),
),
],
),
],
).paddingAll(16.h),
);
}
}

@ -29,6 +29,7 @@ class CollapsingListView extends StatelessWidget {
VoidCallback? viewImage;
VoidCallback? location;
VoidCallback? downloadInvoice;
VoidCallback? liveCareSupport;
Widget? bottomChild;
Widget? trailing;
bool isClose;
@ -55,6 +56,7 @@ class CollapsingListView extends StatelessWidget {
this.viewImage,
this.location,
this.downloadInvoice,
this.liveCareSupport,
this.isLeading = true,
this.trailing,
this.leadingCallback,
@ -101,6 +103,7 @@ class CollapsingListView extends StatelessWidget {
viewImage: viewImage,
location: location,
downloadInvoice: downloadInvoice,
liveCareSupport: liveCareSupport,
bottomChild: bottomChild,
trailing: trailing,
aiOverview: aiOverview,
@ -212,6 +215,7 @@ class ScrollAnimatedTitle extends StatefulWidget implements PreferredSizeWidget
VoidCallback? sendEmail;
VoidCallback? doctorResponse;
VoidCallback? downloadReport;
VoidCallback? liveCareSupport;
VoidCallback? viewImage;
VoidCallback? location;
VoidCallback? downloadInvoice;
@ -236,6 +240,7 @@ class ScrollAnimatedTitle extends StatefulWidget implements PreferredSizeWidget
this.viewImage,
this.location,
this.downloadInvoice,
this.liveCareSupport,
this.bottomChild,
this.trailing,
});
@ -338,6 +343,7 @@ class _ScrollAnimatedTitleState extends State<ScrollAnimatedTitle> {
if (widget.downloadInvoice != null)
actionButton(context, t, title: LocaleKeys.downloadInvoice.tr(context: context), icon: AppAssets.download)
.onPress(widget.downloadInvoice!),
if (widget.liveCareSupport != null) Utils.buildSvgWithAssets(icon: AppAssets.contact_icon).onPress(widget.liveCareSupport!),
if (widget.trailing != null) widget.trailing!,
]
],

@ -110,6 +110,7 @@ dependencies:
screen_brightness: ^1.0.1
flutter_screenshot_blocker: ^1.0.4
cloudflare_turnstile: ^3.7.2
video_player: 2.10.1
dev_dependencies:
flutter_test:

Loading…
Cancel
Save