From cb299c5bca380e5e598ad5778f374dce46145e66 Mon Sep 17 00:00:00 2001 From: faizatflutter Date: Sun, 26 Apr 2026 17:49:24 +0300 Subject: [PATCH] Design changes on fold --- ios/Podfile.lock | 4 +- lib/core/api/api_client.dart | 2 +- lib/core/utils/push_notification_handler.dart | 97 ++-- lib/extensions/string_extensions.dart | 2 + .../appointment_details_page.dart | 184 +++++--- .../widgets/appointment_card.dart | 201 +++++---- .../medical_file_appointment_card.dart | 420 +++++++++--------- .../symptoms_checker/user_info_selection.dart | 15 +- .../user_info_flow_manager.dart | 2 +- pubspec.lock | 8 +- pubspec.yaml | 2 - 11 files changed, 515 insertions(+), 422 deletions(-) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index b796a719..601f8fb0 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -601,7 +601,7 @@ SPEC CHECKSUMS: OrderedSet: e539b66b644ff081c73a262d24ad552a69be3a94 package_info_plus: c0502532a26c7662a62a356cebe2692ec5fe4ec4 path_provider_foundation: 0b743cbb62d8e47eab856f09262bb8c1ddcfe6ba - PayFortSDK: 233eabe9a45601fdbeac67fa6e5aae46ed8faf82 + PayFortSDK: e12f79051ae15b2e29536771a45354900180bfcb permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2 Polyline: 2a1f29f87f8d9b7de868940f4f76deb8c678a5b1 PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47 @@ -612,7 +612,7 @@ SPEC CHECKSUMS: shared_preferences_foundation: 5086985c1d43c5ba4d5e69a4e8083a389e2909e6 Solar-dev: 4612dc9878b9fed2667d23b327f1d4e54e16e8d0 sqflite_darwin: 5a7236e3b501866c1c9befc6771dfd73ffb8702d - SwiftProtobuf: e1b437c8e31a4c5577b643249a0bb62ed4f02153 + SwiftProtobuf: 9e106a71456f4d3f6a3b0c8fd87ef0be085efc38 SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4 Turf: aa2ede4298009639d10db36aba1a7ebaad072a5e url_launcher_ios: bb13df5870e8c4234ca12609d04010a21be43dfa diff --git a/lib/core/api/api_client.dart b/lib/core/api/api_client.dart index 269b204c..34f56efe 100644 --- a/lib/core/api/api_client.dart +++ b/lib/core/api/api_client.dart @@ -7,6 +7,7 @@ import 'package:flutter/material.dart'; import 'package:hmg_patient_app_new/core/api_consts.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/exceptions/api_failure.dart'; import 'package:hmg_patient_app_new/core/utils/utils.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/presentation/home/app_update_page.dart'; @@ -16,7 +17,6 @@ import 'package:hmg_patient_app_new/services/navigation_service.dart'; import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart'; import 'package:http/http.dart' as http; -import '../exceptions/api_failure.dart'; abstract class ApiClient { static final NavigationService _navigationService = getIt.get(); diff --git a/lib/core/utils/push_notification_handler.dart b/lib/core/utils/push_notification_handler.dart index 23cb4f51..c96591ab 100644 --- a/lib/core/utils/push_notification_handler.dart +++ b/lib/core/utils/push_notification_handler.dart @@ -9,18 +9,17 @@ import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_callkit_incoming/entities/android_params.dart'; -import 'package:flutter_callkit_incoming/entities/call_event.dart'; import 'package:flutter_callkit_incoming/entities/call_kit_params.dart'; import 'package:flutter_callkit_incoming/entities/ios_params.dart'; import 'package:flutter_callkit_incoming/entities/notification_params.dart'; import 'package:flutter_callkit_incoming/flutter_callkit_incoming.dart'; import 'package:flutter_ios_voip_kit_karmm/call_state_type.dart'; import 'package:flutter_ios_voip_kit_karmm/flutter_ios_voip_kit.dart'; +import 'package:hmg_patient_app_new/core/api/api_client.dart'; import 'package:hmg_patient_app_new/core/api_consts.dart'; import 'package:hmg_patient_app_new/core/cache_consts.dart'; -import 'package:hmg_patient_app_new/core/utils/utils.dart'; -import 'package:hmg_patient_app_new/core/api/api_client.dart'; import 'package:hmg_patient_app_new/core/dependencies.dart'; +import 'package:hmg_patient_app_new/core/utils/utils.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:uuid/uuid.dart'; @@ -33,7 +32,7 @@ String? _currentSessionId; // Store session ID for API call // |--> Push Notification Background @pragma('vm:entry-point') Future backgroundMessageHandler(dynamic message) async { - print("Firebase backgroundMessageHandler!!!"); + log("Firebase backgroundMessageHandler!!!"); await Firebase.initializeApp(); fir.RemoteMessage message_; @@ -47,20 +46,20 @@ Future backgroundMessageHandler(dynamic message) async { callPage(String sessionID, String token) async {} _incomingCall(Map data) async { - print('the value of the _incomingCall remote message is $data'); + log('the value of the _incomingCall remote message is $data'); // Check if there's already a call in progress to prevent duplicates if (_isCallInProgress && _currentCallId != null) { - print('โš ๏ธ Call already in progress (ID: $_currentCallId), ignoring duplicate notification'); + log('โš ๏ธ Call already in progress (ID: $_currentCallId), ignoring duplicate notification'); return; } String roomID = data['session_id'] ?? ''; String callTypeID = data['AppointmentNo'] ?? ''; - print('๐Ÿ” Extracted from notification data:'); - print(' - roomID (session_id): "$roomID"'); - print(' - callTypeID (AppointmentNo): "$callTypeID"'); + log('๐Ÿ” Extracted from notification data:'); + log(' - roomID (session_id): "$roomID"'); + log(' - callTypeID (AppointmentNo): "$callTypeID"'); // Generate unique call ID var _currentUuid = Uuid().v4(); @@ -68,16 +67,16 @@ _incomingCall(Map data) async { _isCallInProgress = true; _currentSessionId = roomID; // Store session ID for decline API call - print('๐Ÿ“ž Starting new call with ID: $_currentCallId, Session: $_currentSessionId'); + log('๐Ÿ“ž Starting new call with ID: $_currentCallId, Session: $_currentSessionId'); await Utils.saveStringFromPrefs(CacheConst.zoomRoomID, roomID); await Utils.saveStringFromPrefs(CacheConst.callTypeID, callTypeID); await Utils.saveBoolFromPrefs(CacheConst.isAppOpenedFromCall, true); - print('๐Ÿ’พ Saved to cache:'); - print(' - CacheConst.zoomRoomID: "$roomID"'); - print(' - CacheConst.callTypeID: "$callTypeID"'); - print(' - CacheConst.isAppOpenedFromCall: true'); + log('๐Ÿ’พ Saved to cache:'); + log(' - CacheConst.zoomRoomID: "$roomID"'); + log(' - CacheConst.callTypeID: "$callTypeID"'); + log(' - CacheConst.isAppOpenedFromCall: true'); WidgetsFlutterBinding.ensureInitialized(); @@ -148,7 +147,7 @@ _incomingCall(Map data) async { // Start 60-second timeout timer to auto-dismiss call _callTimeoutTimer?.cancel(); // Cancel any existing timer _callTimeoutTimer = Timer(Duration(seconds: 40), () async { - print('โฑ๏ธ Call timeout (60 seconds) - auto dismissing call notification'); + log('โฑ๏ธ Call timeout (60 seconds) - auto dismissing call notification'); await _endCallAndCleanup(_currentUuid); }); @@ -158,8 +157,6 @@ _incomingCall(Map data) async { // Helper method to end call and cleanup Future _endCallAndCleanup(String callId, {bool isDeclined = false}) async { try { - print('๐Ÿงน Cleaning up call: $callId'); - // If call was declined, update session status via API if (isDeclined && _currentSessionId != null && _currentSessionId!.isNotEmpty) { await _updateSessionStatus(_currentSessionId!, 3, 'Patient'); @@ -184,10 +181,7 @@ Future _endCallAndCleanup(String callId, {bool isDeclined = false}) async await Utils.saveBoolFromPrefs(CacheConst.isAppOpenedFromCall, false); await Utils.saveStringFromPrefs(CacheConst.zoomRoomID, ''); await Utils.saveStringFromPrefs(CacheConst.callTypeID, ''); - - print('โœ… Call and push notification cleanup completed successfully'); } catch (e) { - print('โŒ Error during call cleanup: $e'); // Force reset flags even if there's an error _isCallInProgress = false; _currentCallId = null; @@ -200,27 +194,20 @@ Future _endCallAndCleanup(String callId, {bool isDeclined = false}) async // Helper method to update session status via API Future _updateSessionStatus(String sessionId, int sessionStatus, String sessionEndedBy) async { try { - final apiClient = getIt.get(); - Map requestBody = { - "Open_SessionID": sessionId, - "SessionStatus": sessionStatus, - "SessionEndedBy": sessionEndedBy - }; + Map requestBody = {"Open_SessionID": sessionId, "SessionStatus": sessionStatus, "SessionEndedBy": sessionEndedBy}; await apiClient.post( CHANGE_PATIENT_ER_SESSION, body: requestBody, - onSuccess: (response, statusCode, {messageStatus, errorMessage}) { - print('โœ… Session status updated successfully: $response'); - }, + onSuccess: (response, statusCode, {messageStatus, errorMessage}) {}, onFailure: (error, statusCode, {messageStatus, failureType}) { - print('โŒ Failed to update session status: $error'); + log('โŒ Failed to update session status: $error'); }, ); } catch (e) { - print('โŒ Exception updating session status: $e'); + log('โŒ Exception updating session status: $e'); } } @@ -252,7 +239,7 @@ Future openCallPage(BuildContext context) async { // ); // } } catch (err) { - print(err); + log(err.toString()); // await PlatformExceptionAlertDialog( // exception: Exception(err), // ).show(context); @@ -351,7 +338,7 @@ class PushNotificationHandler { // int seconds = 30, // }) async { // timeOutTimer = Timer(Duration(seconds: seconds), () async { - // print('๐ŸŽˆ example: timeOut'); + // log('๐ŸŽˆ example: timeOut'); // final incomingCallerName = await voIPKit.getIncomingCallerName(); // voIPKit.unansweredIncomingCall( // skipLocalNotification: false, @@ -366,19 +353,19 @@ class PushNotificationHandler { if (Platform.isIOS) { voIPKit.getVoIPToken().then((value) { - print("๐ŸŽˆ APNS VOIP KIT TOKEN: $value"); + log("๐ŸŽˆ APNS VOIP KIT TOKEN: $value"); Utils.saveStringFromPrefs(CacheConst.voipToken, value ?? ""); // AppSharedPreferences().setString(APNS_TOKEN, value!); }); voIPKit.onDidUpdatePushToken = (String token) { - print('๐ŸŽˆ example: onDidUpdatePushToken: $token'); + log('๐ŸŽˆ example: onDidUpdatePushToken: $token'); }; voIPKit.onDidReceiveIncomingPush = ( Map payload, ) async { - print('๐ŸŽˆ example: onDidReceiveIncomingPush $payload'); + log('๐ŸŽˆ example: onDidReceiveIncomingPush $payload'); // _timeOut(); }; @@ -387,20 +374,22 @@ class PushNotificationHandler { String callerId, ) async { try { - print('๐ŸŽˆ example: onDidRejectIncomingCall $uuid - $callerId'); + log('๐ŸŽˆ example: onDidRejectIncomingCall $uuid - $callerId'); timeOutTimer.cancel(); // Cleanup on reject with API call if (_currentCallId != null) { await _endCallAndCleanup(_currentCallId!, isDeclined: true); } - } catch (err) {} + } catch (err) { + log(err.toString()); + } }; voIPKit.onDidAcceptIncomingCall = ( String uuid, String callerId, ) async { - print('๐ŸŽˆ example: onDidAcceptIncomingCall $uuid - $callerId'); + log('๐ŸŽˆ example: onDidAcceptIncomingCall $uuid - $callerId'); await voIPKit.acceptIncomingCall(callerState: CallStateType.calling); await voIPKit.callConnected(); @@ -430,12 +419,12 @@ class PushNotificationHandler { if (Platform.isAndroid) { try { final fcmToken = await FirebaseMessaging.instance.getToken().catchError((err) { - print(err); + log(err); }); if (fcmToken != null) onToken(fcmToken); // } } catch (ex) { - print("Notification Exception: $ex"); + log("Notification Exception: $ex"); } FirebaseMessaging.onBackgroundMessage(backgroundMessageHandler); } @@ -467,7 +456,7 @@ class PushNotificationHandler { } catch (ex) {} FirebaseMessaging.onMessage.listen((RemoteMessage message) async { - print("Firebase onMessage!!!"); + log("Firebase onMessage!!!"); // showCallkitIncoming(); if (Platform.isIOS) { await Future.delayed(Duration(milliseconds: 3000)).then((value) { @@ -479,7 +468,7 @@ class PushNotificationHandler { }); FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) async { - print("Firebase onMessageOpenedApp!!!"); + log("Firebase onMessageOpenedApp!!!"); if (Platform.isIOS) { await Future.delayed(Duration(milliseconds: 3000)).then((value) { newMessage(message); @@ -491,21 +480,21 @@ class PushNotificationHandler { if (Platform.isIOS) { FirebaseMessaging.instance.getAPNSToken().then((String? token) { - print("Push Notification getAPNSToken: ${token!}"); + log("Push Notification getAPNSToken: ${token!}"); }).catchError((err) { - print("Push Notification getAPNSToken ERR: ${err.toString()}"); + log("Push Notification getAPNSToken ERR: ${err.toString()}"); }); } FirebaseMessaging.instance.getToken().then((String? token) { - print("Push Notification getToken: ${token!}"); + log("Push Notification getToken: ${token!}"); onToken(token!); }).catchError((err) { - print(err); + log(err); }); FirebaseMessaging.instance.onTokenRefresh.listen((fcm_token) { - print("Push Notification onTokenRefresh: $fcm_token"); + log("Push Notification onTokenRefresh: $fcm_token"); onToken(fcm_token); }); @@ -521,11 +510,11 @@ class PushNotificationHandler { } newMessage(RemoteMessage remoteMessage) async { - print("Remote Message: ${remoteMessage.data}"); + log("Remote Message: ${remoteMessage.data}"); if (remoteMessage.data.isEmpty) { return; } - debugPrint('the value of the remote message is ${remoteMessage.data}'); + log('the value of the remote message is ${remoteMessage.data}'); if (remoteMessage.data['is_call'] == 'true' || remoteMessage.data['is_call'] == true) { _incomingCall(remoteMessage.data); // showCallkitIncoming(); @@ -547,7 +536,7 @@ class PushNotificationHandler { } onToken(String token) async { - print("Push Notification Token: $token"); + log("Push Notification Token: $token"); await Utils.saveStringFromPrefs(CacheConst.pushToken, token); } @@ -569,11 +558,11 @@ class PushNotificationHandler { // Permission.audio, // Permission.microphone, ].request(); - print("=-=-=-=-=-=-=-=-=-=-"); - print(statuses[Permission.notification]); + log("=-=-=-=-=-=-=-=-=-=-"); + log(statuses[Permission.notification].toString()); } } catch (_) { - debugPrint(_.toString()); + log(_.toString()); } } } diff --git a/lib/extensions/string_extensions.dart b/lib/extensions/string_extensions.dart index 7a71b345..469745cb 100644 --- a/lib/extensions/string_extensions.dart +++ b/lib/extensions/string_extensions.dart @@ -115,12 +115,14 @@ extension EmailValidator on String { bool isCenter = false, double? height, double? letterSpacing, + TextOverflow? textOverflow, int maxLine = 0}) => Text( this, textAlign: isCenter ? TextAlign.center : textAlignment, maxLines: (maxLine > 0) ? maxLine : null, style: TextStyle( + overflow: textOverflow, fontSize: 12.f, fontWeight: fontWeight ?? (isBold ? FontWeight.bold : FontWeight.normal), color: color ?? AppColors.blackColor, diff --git a/lib/presentation/appointments/appointment_details_page.dart b/lib/presentation/appointments/appointment_details_page.dart index 87ca5e93..83fd4342 100644 --- a/lib/presentation/appointments/appointment_details_page.dart +++ b/lib/presentation/appointments/appointment_details_page.dart @@ -262,7 +262,10 @@ class _AppointmentDetailsPageState extends State { Expanded( child: CollapsingListView( title: LocaleKeys.appointmentDetails.tr(context: context), - report: AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel) && widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment==false && widget.patientAppointmentHistoryResponseModel.isClinicReBookingAllowed! ==true && widget.patientAppointmentHistoryResponseModel.isActiveDoctor! == true + report: AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel) && + widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment == false && + widget.patientAppointmentHistoryResponseModel.isClinicReBookingAllowed! == true && + widget.patientAppointmentHistoryResponseModel.isActiveDoctor! == true ? () { contactUsViewModel.setSelectedFeedbackType(FeedbackType(id: 1, nameEN: "Complaint for appointment", nameAR: 'ุดูƒูˆู‰ ุนู„ู‰ ู…ูˆุนุฏ')); contactUsViewModel.setPatientFeedbackSelectedAppointment(widget.patientAppointmentHistoryResponseModel); @@ -280,8 +283,8 @@ class _AppointmentDetailsPageState extends State { children: [ AppointmentDoctorCard( // renderWidgetForERDisplay: ((widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment ?? false) || - renderWidgetForERDisplay: - ((widget.patientAppointmentHistoryResponseModel.isExecludeDoctor ?? false) || !Utils.isClinicAllowedForRebook(widget.patientAppointmentHistoryResponseModel.clinicID)), + renderWidgetForERDisplay: ((widget.patientAppointmentHistoryResponseModel.isExecludeDoctor ?? false) || + !Utils.isClinicAllowedForRebook(widget.patientAppointmentHistoryResponseModel.clinicID)), patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel, onAskDoctorTap: () async { LoaderBottomSheet.showLoader(loadingText: LocaleKeys.checkingDoctorAvailability.tr(context: context)); @@ -416,14 +419,17 @@ class _AppointmentDetailsPageState extends State { Row( mainAxisSize: MainAxisSize.max, children: [ - Utils.buildSvgWithAssets(icon: AppAssets.prescription_reminder_icon, width: 40.w, height: 40.h, applyThemeColor: false), + Utils.buildSvgWithAssets( + icon: AppAssets.prescription_reminder_icon, width: 40.w, height: 40.h, applyThemeColor: false), SizedBox(width: 8.w), Column( crossAxisAlignment: CrossAxisAlignment.start, spacing: 4.h, children: [ LocaleKeys.setReminder.tr(context: context).toText13(isBold: true), - LocaleKeys.notifyMeBeforeAppointment.tr(context: context).toText11(color: AppColors.textColorLight, isBold: true), + LocaleKeys.notifyMeBeforeAppointment + .tr(context: context) + .toText11(color: AppColors.textColorLight, isBold: true), ], ), const Spacer(), @@ -438,48 +444,53 @@ class _AppointmentDetailsPageState extends State { inactiveCircleColor: AppColors.greyTextColor, activeIconColor: AppColors.bgGreenColor, inactiveIconColor: AppColors.greyTextColor, - activeIcon: Utils.buildSvgWithAssets(icon: AppAssets.bell, iconColor: AppColors.whiteColor, width: 12.w, height: 12.h), - inactiveIcon: Utils.buildSvgWithAssets(icon: AppAssets.bell, iconColor: AppColors.whiteColor, width: 12.w, height: 12.h), + activeIcon: + Utils.buildSvgWithAssets(icon: AppAssets.bell, iconColor: AppColors.whiteColor, width: 12.w, height: 12.h), + inactiveIcon: + Utils.buildSvgWithAssets(icon: AppAssets.bell, iconColor: AppColors.whiteColor, width: 12.w, height: 12.h), onChanged: (newValue) async { CalenderUtilsNew calender = CalenderUtilsNew.instance; bool isEventAddedOrRemoved = false; if (newValue == true) { DateTime startDate = DateTime.now(); - DateTime endDate = DateUtil.convertStringToDate(widget.patientAppointmentHistoryResponseModel.appointmentDate); + DateTime endDate = + DateUtil.convertStringToDate(widget.patientAppointmentHistoryResponseModel.appointmentDate); // Show reminder bottom sheet and check if permission was granted bool permissionGranted = await BottomSheetUtils().showReminderBottomSheet( - context, - endDate, - widget.patientAppointmentHistoryResponseModel.doctorNameObj ?? "", - "${widget.patientAppointmentHistoryResponseModel.appointmentNo}" ?? "", - "", - "", - title: "Appointment with ${widget.patientAppointmentHistoryResponseModel.doctorNameObj}", - description: - "${widget.patientAppointmentHistoryResponseModel.doctorNameObj} will be having an appointment on ${widget.patientAppointmentHistoryResponseModel.appointmentDate}", - onSuccess: () { - setState(() { - myAppointmentsViewModel.setAppointmentReminder(newValue, widget.patientAppointmentHistoryResponseModel); - }); - }, - isMultiAllowed: true, - onMultiDateSuccess: (int selectedIndex) async { - isEventAddedOrRemoved = await calender.createOrUpdateEvent( - title: - "Appointment Reminder with ${widget.patientAppointmentHistoryResponseModel.doctorNameObj} on ${DateUtil.convertStringToDate(widget.patientAppointmentHistoryResponseModel.appointmentDate)}, Appointment #${widget.patientAppointmentHistoryResponseModel.appointmentNo}", - description: - "Appointment Reminder with ${widget.patientAppointmentHistoryResponseModel.doctorNameObj} in ${widget.patientAppointmentHistoryResponseModel.projectName}", - scheduleDateTime: DateUtil.convertStringToDate(widget.patientAppointmentHistoryResponseModel.appointmentDate), - eventId: "${widget.patientAppointmentHistoryResponseModel.appointmentNo}", - location: '', - reminderMinutes: selectedIndex); - setState(() { - myAppointmentsViewModel.setAppointmentReminder(isEventAddedOrRemoved, widget.patientAppointmentHistoryResponseModel); - }); - }, - isForAppointment: true - ); + context, + endDate, + widget.patientAppointmentHistoryResponseModel.doctorNameObj ?? "", + "${widget.patientAppointmentHistoryResponseModel.appointmentNo}" ?? "", + "", + "", + title: "Appointment with ${widget.patientAppointmentHistoryResponseModel.doctorNameObj}", + description: + "${widget.patientAppointmentHistoryResponseModel.doctorNameObj} will be having an appointment on ${widget.patientAppointmentHistoryResponseModel.appointmentDate}", + onSuccess: () { + setState(() { + myAppointmentsViewModel.setAppointmentReminder( + newValue, widget.patientAppointmentHistoryResponseModel); + }); + }, + isMultiAllowed: true, + onMultiDateSuccess: (int selectedIndex) async { + isEventAddedOrRemoved = await calender.createOrUpdateEvent( + title: + "Appointment Reminder with ${widget.patientAppointmentHistoryResponseModel.doctorNameObj} on ${DateUtil.convertStringToDate(widget.patientAppointmentHistoryResponseModel.appointmentDate)}, Appointment #${widget.patientAppointmentHistoryResponseModel.appointmentNo}", + description: + "Appointment Reminder with ${widget.patientAppointmentHistoryResponseModel.doctorNameObj} in ${widget.patientAppointmentHistoryResponseModel.projectName}", + scheduleDateTime: + DateUtil.convertStringToDate(widget.patientAppointmentHistoryResponseModel.appointmentDate), + eventId: "${widget.patientAppointmentHistoryResponseModel.appointmentNo}", + location: '', + reminderMinutes: selectedIndex); + setState(() { + myAppointmentsViewModel.setAppointmentReminder( + isEventAddedOrRemoved, widget.patientAppointmentHistoryResponseModel); + }); + }, + isForAppointment: true); // If permission was not granted, revert the switch back to OFF if (!permissionGranted) { @@ -490,7 +501,8 @@ class _AppointmentDetailsPageState extends State { id: "${widget.patientAppointmentHistoryResponseModel.appointmentNo}", ); setState(() { - myAppointmentsViewModel.setAppointmentReminder(!isEventAddedOrRemoved, widget.patientAppointmentHistoryResponseModel); + myAppointmentsViewModel.setAppointmentReminder( + !isEventAddedOrRemoved, widget.patientAppointmentHistoryResponseModel); }); } }, @@ -581,8 +593,12 @@ class _AppointmentDetailsPageState extends State { LocaleKeys.appointmentStatus.tr(context: context).toText16(isBold: true), SizedBox(height: 4.h), (!AppointmentType.isConfirmed(widget.patientAppointmentHistoryResponseModel) - ? LocaleKeys.notConfirmed.tr(context: context).toText12(color: AppColors.primaryRedColor, isBold: true) - : LocaleKeys.confirmed.tr(context: context).toText12(color: AppColors.successColor, isBold: true)), + ? LocaleKeys.notConfirmed + .tr(context: context) + .toText12(color: AppColors.primaryRedColor, isBold: true) + : LocaleKeys.confirmed + .tr(context: context) + .toText12(color: AppColors.successColor, isBold: true)), SizedBox(height: 16.h), ], ), @@ -640,7 +656,9 @@ class _AppointmentDetailsPageState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - LocaleKeys.doctorWillCallYou.tr(context: context).toText14(color: AppColors.greyTextColor, isBold: true), + LocaleKeys.doctorWillCallYou + .tr(context: context) + .toText14(color: AppColors.greyTextColor, isBold: true), ], ), ), @@ -648,13 +666,17 @@ class _AppointmentDetailsPageState extends State { ) : Stack( children: [ - ClipRRect( - clipBehavior: Clip.hardEdge, - borderRadius: BorderRadius.circular(24.r), - // Todo: what is this???? Api Key??? ๐Ÿ˜ฒ - child: Image.network( - "https://maps.googleapis.com/maps/api/staticmap?center=${widget.patientAppointmentHistoryResponseModel.latitude},${widget.patientAppointmentHistoryResponseModel.longitude}&zoom=14&size=${(MediaQuery.of(context).size.width * 1.5).toInt()}x${(MediaQuery.of(context).size.height * 0.35).toInt()}&maptype=roadmap&markers=color:red%7C${widget.patientAppointmentHistoryResponseModel.latitude},${widget.patientAppointmentHistoryResponseModel.longitude}&key=${ApiKeyConstants.googleMapsApiKey}", - fit: BoxFit.contain, + SizedBox( + width: double.infinity, + child: ClipRRect( + clipBehavior: Clip.hardEdge, + borderRadius: BorderRadius.circular(24.r), + // Todo: what is this???? Api Key??? ๐Ÿ˜ฒ + child: Image.network( + "https://maps.googleapis.com/maps/api/staticmap?center=${widget.patientAppointmentHistoryResponseModel.latitude},${widget.patientAppointmentHistoryResponseModel.longitude}&zoom=14&size=${(MediaQuery.of(context).size.width * 1.5).toInt()}x${(MediaQuery.of(context).size.height * 0.35).toInt()}&maptype=roadmap&markers=color:red%7C${widget.patientAppointmentHistoryResponseModel.latitude},${widget.patientAppointmentHistoryResponseModel.longitude}&key=${ApiKeyConstants.googleMapsApiKey}", + fit: BoxFit.cover, + width: double.infinity, + ), ), ), Positioned( @@ -663,7 +685,8 @@ class _AppointmentDetailsPageState extends State { width: MediaQuery.of(context).size.width - 85.w, child: CustomButton( onPressed: () async { - if (widget.patientAppointmentHistoryResponseModel.projectID == 130 || widget.patientAppointmentHistoryResponseModel.projectID == 120) { + if (widget.patientAppointmentHistoryResponseModel.projectID == 130 || + widget.patientAppointmentHistoryResponseModel.projectID == 120) { showDirectionsBottomSheet(); } else { await MapLauncher.showMarker( @@ -683,7 +706,9 @@ class _AppointmentDetailsPageState extends State { }, text: LocaleKeys.getDirections.tr(context: context), backgroundColor: AppColors.bookAppointment.withValues(alpha: 0.8), - borderColor: AppointmentType.getNextActionButtonColor(widget.patientAppointmentHistoryResponseModel.nextAction).withValues(alpha: 0.01), + borderColor: AppointmentType.getNextActionButtonColor( + widget.patientAppointmentHistoryResponseModel.nextAction) + .withValues(alpha: 0.01), textColor: Colors.white, fontSize: 14.f, fontWeight: FontWeight.w600, @@ -974,7 +999,8 @@ class _AppointmentDetailsPageState extends State { ); Navigator.of(context).push( CustomPageRoute( - page: PrescriptionDetailPage(isFromAppointments: true, prescriptionsResponseModel: patientPrescriptionsResponseModel), + page: PrescriptionDetailPage( + isFromAppointments: true, prescriptionsResponseModel: patientPrescriptionsResponseModel), ), ); } else { @@ -1289,7 +1315,8 @@ class _AppointmentDetailsPageState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - if (widget.patientAppointmentHistoryResponseModel.nextAction == 15 || widget.patientAppointmentHistoryResponseModel.nextAction == 20) + if (widget.patientAppointmentHistoryResponseModel.nextAction == 15 || + widget.patientAppointmentHistoryResponseModel.nextAction == 20) Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -1297,7 +1324,10 @@ class _AppointmentDetailsPageState extends State { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ LocaleKeys.amountBeforeTax.tr(context: context).toText18(isBold: true), - Utils.getPaymentAmountWithSymbol(widget.patientAppointmentHistoryResponseModel.patientShare!.toString().toText16(isBold: true), AppColors.blackColor, 13, + Utils.getPaymentAmountWithSymbol( + widget.patientAppointmentHistoryResponseModel.patientShare!.toString().toText16(isBold: true), + AppColors.blackColor, + 13, isSaudiCurrency: true), ], ), @@ -1305,8 +1335,10 @@ class _AppointmentDetailsPageState extends State { Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Expanded(child: LocaleKeys.upcomingPaymentNow.tr(context: context).toText12(isBold: true, color: AppColors.greyTextColor)), - "VAT 15%(${widget.patientAppointmentHistoryResponseModel.patientTaxAmount})".toText14(isBold: true, color: AppColors.greyTextColor, letterSpacing: -0.64), + Expanded( + child: LocaleKeys.upcomingPaymentNow.tr(context: context).toText12(isBold: true, color: AppColors.greyTextColor)), + "VAT 15%(${widget.patientAppointmentHistoryResponseModel.patientTaxAmount})" + .toText14(isBold: true, color: AppColors.greyTextColor, letterSpacing: -0.64), ], ), SizedBox(height: 18.h), @@ -1320,7 +1352,10 @@ class _AppointmentDetailsPageState extends State { Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Utils.getPaymentAmountWithSymbol(widget.patientAppointmentHistoryResponseModel.patientShareWithTax!.toString().toText24(isBold: true), AppColors.blackColor, 17, + Utils.getPaymentAmountWithSymbol( + widget.patientAppointmentHistoryResponseModel.patientShareWithTax!.toString().toText24(isBold: true), + AppColors.blackColor, + 17, isSaudiCurrency: true), ], ), @@ -1358,7 +1393,8 @@ class _AppointmentDetailsPageState extends State { handleAppointmentNextAction(widget.patientAppointmentHistoryResponseModel.nextAction); }, backgroundColor: AppointmentType.getNextActionButtonColor(widget.patientAppointmentHistoryResponseModel.nextAction), - borderColor: AppointmentType.getNextActionButtonColor(widget.patientAppointmentHistoryResponseModel.nextAction).withValues(alpha: 0.01), + borderColor: AppointmentType.getNextActionButtonColor(widget.patientAppointmentHistoryResponseModel.nextAction) + .withValues(alpha: 0.01), textColor: widget.patientAppointmentHistoryResponseModel.nextAction == 15 ? AppColors.textColor : Colors.white, fontSize: 16.f, fontWeight: FontWeight.w600, @@ -1398,7 +1434,10 @@ class _AppointmentDetailsPageState extends State { text: LocaleKeys.insideHospital.tr(context: context), onPressed: () { Navigator.pop(context); - initPenguinSDK(widget.patientAppointmentHistoryResponseModel.projectID == 130 ? 1 : (widget.patientAppointmentHistoryResponseModel.projectID == 120 ? 3 : -1), + initPenguinSDK( + widget.patientAppointmentHistoryResponseModel.projectID == 130 + ? 1 + : (widget.patientAppointmentHistoryResponseModel.projectID == 120 ? 3 : -1), clinicID: widget.patientAppointmentHistoryResponseModel.clinicID.toString()); }, backgroundColor: AppColors.primaryRedColor, @@ -1417,12 +1456,14 @@ class _AppointmentDetailsPageState extends State { Navigator.pop(context); await MapLauncher.showMarker( mapType: MapType.google, - coords: Coords(double.parse(widget.patientAppointmentHistoryResponseModel.latitude!), double.parse(widget.patientAppointmentHistoryResponseModel.longitude!)), + coords: Coords(double.parse(widget.patientAppointmentHistoryResponseModel.latitude!), + double.parse(widget.patientAppointmentHistoryResponseModel.longitude!)), title: widget.patientAppointmentHistoryResponseModel.projectName ?? "Habib Hospital", ).catchError((err) { MapLauncher.showMarker( mapType: Platform.isIOS ? MapType.apple : MapType.google, - coords: Coords(double.parse(widget.patientAppointmentHistoryResponseModel.latitude!), double.parse(widget.patientAppointmentHistoryResponseModel.longitude!)), + coords: Coords(double.parse(widget.patientAppointmentHistoryResponseModel.latitude!), + double.parse(widget.patientAppointmentHistoryResponseModel.longitude!)), title: widget.patientAppointmentHistoryResponseModel.projectName ?? "Habib Hospital", ); }); @@ -1456,7 +1497,9 @@ class _AppointmentDetailsPageState extends State { Permission.bluetoothScan, Permission.activityRecognition, ].request().whenComplete(() { - PenguinMethodChannel().launch("penguin", getIt.get().isArabic() ? "ar" : "en", getIt.get().getAuthenticatedUser()?.patientId?.toString() ?? "", true, details: data); + PenguinMethodChannel().launch("penguin", getIt.get().isArabic() ? "ar" : "en", + getIt.get().getAuthenticatedUser()?.patientId?.toString() ?? "", true, + details: data); }); } } @@ -1514,12 +1557,20 @@ class _AppointmentDetailsPageState extends State { LoaderBottomSheet.hideLoader(); myAppointmentsViewModel.setIsAppointmentDataToBeLoaded(true); myAppointmentsViewModel.getPatientAppointments(true, false); - showCommonBottomSheet(context, child: Utils.getSuccessWidget(loadingText: LocaleKeys.appointmentConfirmedSuccessfully.tr(context: context)), callBackFunc: (str) { + showCommonBottomSheet(context, + child: Utils.getSuccessWidget(loadingText: LocaleKeys.appointmentConfirmedSuccessfully.tr(context: context)), callBackFunc: (str) { myAppointmentsViewModel.setIsAppointmentDataToBeLoaded(true); myAppointmentsViewModel.initAppointmentsViewModel(); myAppointmentsViewModel.getPatientAppointments(true, false); Navigator.of(context).pop(); - }, title: "", height: ResponsiveExtension.screenHeight * 0.3, isAutoDismiss: true, isCloseButtonVisible: true, isDismissible: false, isFullScreen: false, isSuccessDialog: true); + }, + title: "", + height: ResponsiveExtension.screenHeight * 0.3, + isAutoDismiss: true, + isCloseButtonVisible: true, + isDismissible: false, + isFullScreen: false, + isSuccessDialog: true); }); // LoaderBottomSheet.hideLoader(); case 15: @@ -1539,7 +1590,8 @@ class _AppointmentDetailsPageState extends State { return Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ - Lottie.asset(AppAnimations.warningAnimation, repeat: false, reverse: false, frameRate: FrameRate(60), width: 100.h, height: 100.h, fit: BoxFit.fill), + Lottie.asset(AppAnimations.warningAnimation, + repeat: false, reverse: false, frameRate: FrameRate(60), width: 100.h, height: 100.h, fit: BoxFit.fill), SizedBox( height: 12, ), diff --git a/lib/presentation/appointments/widgets/appointment_card.dart b/lib/presentation/appointments/widgets/appointment_card.dart index 8b178c47..d53b541b 100644 --- a/lib/presentation/appointments/widgets/appointment_card.dart +++ b/lib/presentation/appointments/widgets/appointment_card.dart @@ -1,4 +1,6 @@ import 'dart:async'; +import 'dart:ui' as ui; + import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:hmg_patient_app_new/core/app_assets.dart'; @@ -18,6 +20,7 @@ import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_vie import 'package:hmg_patient_app_new/features/my_appointments/utils/appointment_type.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/presentation/appointments/appointment_details_page.dart'; +import 'package:hmg_patient_app_new/presentation/appointments/appointment_payment_page.dart'; import 'package:hmg_patient_app_new/presentation/book_appointment/widgets/appointment_calendar.dart'; import 'package:hmg_patient_app_new/presentation/medical_file/eye_measurement_details_page.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; @@ -26,8 +29,6 @@ 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/loader/bottomsheet_loader.dart'; import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart'; -import 'dart:ui' as ui; -import 'package:hmg_patient_app_new/presentation/appointments/appointment_payment_page.dart'; import 'package:lottie/lottie.dart'; class AppointmentCard extends StatefulWidget { @@ -42,6 +43,7 @@ class AppointmentCard extends StatefulWidget { final ContactUsViewModel? contactUsViewModel; final BookAppointmentsViewModel bookAppointmentsViewModel; final bool isForRate; + // bool isAppointmentWithin4Hours = false; const AppointmentCard( @@ -189,20 +191,28 @@ class _AppointmentCardState extends State { runSpacing: 6.h, children: [ AppCustomChipWidget( - icon: widget.isLoading ? AppAssets.walkin_appointment_icon : (isLiveCare ? AppAssets.small_livecare_icon : AppAssets.walkin_appointment_icon), + icon: + widget.isLoading ? AppAssets.walkin_appointment_icon : (isLiveCare ? AppAssets.small_livecare_icon : AppAssets.walkin_appointment_icon), iconColor: widget.isLoading ? AppColors.textColor : (isLiveCare ? Colors.white : AppColors.textColor), - labelText: widget.isLoading ? LocaleKeys.walkin.tr(context: context) : (isLiveCare ? LocaleKeys.livecare.tr(context: context) : LocaleKeys.walkin.tr(context: context)), + labelText: widget.isLoading + ? LocaleKeys.walkin.tr(context: context) + : (isLiveCare ? LocaleKeys.livecare.tr(context: context) : LocaleKeys.walkin.tr(context: context)), backgroundColor: widget.isLoading ? AppColors.greyColor : (isLiveCare ? AppColors.successColor : AppColors.greyColor), textColor: widget.isLoading ? AppColors.textColor : (isLiveCare ? Colors.white : AppColors.textColor), ).toShimmer2(isShow: widget.isLoading), AppCustomChipWidget( - labelText: - widget.isLoading ? 'OutPatient' : (appState.isArabic() ? widget.patientAppointmentHistoryResponseModel.isInOutPatientDescriptionN! : widget.patientAppointmentHistoryResponseModel.isInOutPatientDescription!), + labelText: widget.isLoading + ? 'OutPatient' + : (appState.isArabic() + ? widget.patientAppointmentHistoryResponseModel.isInOutPatientDescriptionN! + : widget.patientAppointmentHistoryResponseModel.isInOutPatientDescription!), backgroundColor: AppColors.warningColorYellow.withValues(alpha: 0.1), textColor: AppColors.warningColorYellow, ).toShimmer2(isShow: widget.isLoading), AppCustomChipWidget( - labelText: widget.isLoading ? 'Booked' : AppointmentType.getAppointmentStatusType(widget.patientAppointmentHistoryResponseModel.patientStatusType!), + labelText: widget.isLoading + ? 'Booked' + : AppointmentType.getAppointmentStatusType(widget.patientAppointmentHistoryResponseModel.patientStatusType!), backgroundColor: AppColors.successColor.withValues(alpha: 0.1), textColor: AppColors.successColor, ).toShimmer2(isShow: widget.isLoading), @@ -218,7 +228,9 @@ class _AppointmentCardState extends State { crossAxisAlignment: CrossAxisAlignment.center, children: [ Image.network( - widget.isLoading ? 'https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png' : widget.patientAppointmentHistoryResponseModel.doctorImageURL!, + widget.isLoading + ? 'https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png' + : widget.patientAppointmentHistoryResponseModel.doctorImageURL!, width: 63.h, height: 63.h, fit: BoxFit.cover, @@ -242,8 +254,10 @@ class _AppointmentCardState extends State { Utils.buildSvgWithAssets(icon: AppAssets.rating_icon, width: 15.w, height: 15.h, iconColor: AppColors.ratingColorYellow), SizedBox(height: 2.h), (isFoldable || isTablet) - ? "${widget.patientAppointmentHistoryResponseModel.decimalDoctorRate}".toText9(isBold: true, color: AppColors.textColor, isEnglishOnly: true) - : "${widget.patientAppointmentHistoryResponseModel.decimalDoctorRate ?? "0.0"}".toText11(isBold: true, color: AppColors.textColor, isEnglishOnly: true), + ? "${widget.patientAppointmentHistoryResponseModel.decimalDoctorRate}" + .toText9(isBold: true, color: AppColors.textColor, isEnglishOnly: true) + : "${widget.patientAppointmentHistoryResponseModel.decimalDoctorRate ?? "0.0"}" + .toText11(isBold: true, color: AppColors.textColor, isEnglishOnly: true), ], ), ).circle(100).toShimmer2(isShow: widget.isLoading), @@ -258,10 +272,16 @@ class _AppointmentCardState extends State { Row( children: [ (widget.isLoading ? 'Dr' : "${widget.patientAppointmentHistoryResponseModel.doctorTitle}").toText16(isBold: true, maxlines: 1), - (widget.isLoading ? 'John Doe' : " ${widget.patientAppointmentHistoryResponseModel.doctorNameObj!.truncate(20)}") - .toText16(isBold: true, maxlines: 1, isEnglishOnly: !Utils.isArabicText(widget.patientAppointmentHistoryResponseModel.doctorNameObj ?? "John Doe")), + Expanded( + child: (widget.isLoading ? 'John Doe' : " ${widget.patientAppointmentHistoryResponseModel.doctorNameObj!.truncate(20)}").toText16( + isBold: true, + maxlines: 1, + textOverflow: TextOverflow.ellipsis, + isEnglishOnly: !Utils.isArabicText(widget.patientAppointmentHistoryResponseModel.doctorNameObj ?? "John Doe")), + ), SizedBox(width: 12.w), - (widget.patientAppointmentHistoryResponseModel.doctorNationalityFlagURL != null && widget.patientAppointmentHistoryResponseModel.doctorNationalityFlagURL!.isNotEmpty) + (widget.patientAppointmentHistoryResponseModel.doctorNationalityFlagURL != null && + widget.patientAppointmentHistoryResponseModel.doctorNationalityFlagURL!.isNotEmpty) ? Image.network( widget.patientAppointmentHistoryResponseModel.doctorNationalityFlagURL ?? "https://hmgwebservices.com/Images/flag/SAU.png", width: 20.h, @@ -419,40 +439,40 @@ class _AppointmentCardState extends State { // } else { return CustomButton( text: widget.isFromMedicalReport ? LocaleKeys.selectAppointment.tr(context: context) : LocaleKeys.viewDetails.tr(context: context), - onPressed: () { - if (widget.isFromMedicalReport) { - if (widget.isForFeedback) { - widget.contactUsViewModel!.setPatientFeedbackSelectedAppointment(widget.patientAppointmentHistoryResponseModel); - } else { - widget.medicalFileViewModel!.setSelectedMedicalReportAppointment(widget.patientAppointmentHistoryResponseModel); - } - Navigator.pop(context, false); + onPressed: () { + if (widget.isFromMedicalReport) { + if (widget.isForFeedback) { + widget.contactUsViewModel!.setPatientFeedbackSelectedAppointment(widget.patientAppointmentHistoryResponseModel); } else { - Navigator.of(context) - .push( - CustomPageRoute( - page: AppointmentDetailsPage(patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel), - ), - ) - .then((_) { - widget.myAppointmentsViewModel.initAppointmentsViewModel(); - widget.myAppointmentsViewModel.getPatientAppointments(true, false); - }); + widget.medicalFileViewModel!.setSelectedMedicalReportAppointment(widget.patientAppointmentHistoryResponseModel); } - }, - backgroundColor: AppColors.secondaryLightRedColor, - borderColor: AppColors.secondaryLightRedColor, - textColor: AppColors.primaryRedColor, - fontSize: (isFoldable || isTablet) ? 12.f : 14.f, - fontWeight: FontWeight.w600, - borderRadius: 12.r, - padding: EdgeInsets.symmetric(horizontal: 10.w), - // height: isTablet || isFoldable ? 46.h : 40.h, - height: 40.h, - icon: widget.isFromMedicalReport ? AppAssets.checkmark_icon : null, - iconColor: AppColors.primaryRedColor, - iconSize: 16.h, - ); + Navigator.pop(context, false); + } else { + Navigator.of(context) + .push( + CustomPageRoute( + page: AppointmentDetailsPage(patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel), + ), + ) + .then((_) { + widget.myAppointmentsViewModel.initAppointmentsViewModel(); + widget.myAppointmentsViewModel.getPatientAppointments(true, false); + }); + } + }, + backgroundColor: AppColors.secondaryLightRedColor, + borderColor: AppColors.secondaryLightRedColor, + textColor: AppColors.primaryRedColor, + fontSize: (isFoldable || isTablet) ? 12.f : 14.f, + fontWeight: FontWeight.w600, + borderRadius: 12.r, + padding: EdgeInsets.symmetric(horizontal: 10.w), + // height: isTablet || isFoldable ? 46.h : 40.h, + height: 40.h, + icon: widget.isFromMedicalReport ? AppAssets.checkmark_icon : null, + iconColor: AppColors.primaryRedColor, + iconSize: 16.h, + ); // } } else { if (widget.isFromMedicalReport) { @@ -486,7 +506,7 @@ class _AppointmentCardState extends State { } return (widget.patientAppointmentHistoryResponseModel.isActiveDoctor ?? true) ? (AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel) && - widget.patientAppointmentHistoryResponseModel.isClinicReBookingAllowed == false) + widget.patientAppointmentHistoryResponseModel.isClinicReBookingAllowed == false) ? // Show only View Details button without arrow when rebooking not allowed _getArrivedButton(context) : Row( @@ -498,8 +518,10 @@ class _AppointmentCardState extends State { : CustomButton( text: AppointmentType.getNextActionText(widget.patientAppointmentHistoryResponseModel.nextAction), onPressed: () => handleAppointmentNextAction(widget.patientAppointmentHistoryResponseModel.nextAction, context), - backgroundColor: AppointmentType.getNextActionButtonColor(widget.patientAppointmentHistoryResponseModel.nextAction).withValues(alpha: 0.15), - borderColor: AppointmentType.getNextActionButtonColor(widget.patientAppointmentHistoryResponseModel.nextAction).withValues(alpha: 0.01), + backgroundColor: AppointmentType.getNextActionButtonColor(widget.patientAppointmentHistoryResponseModel.nextAction) + .withValues(alpha: 0.15), + borderColor: AppointmentType.getNextActionButtonColor(widget.patientAppointmentHistoryResponseModel.nextAction) + .withValues(alpha: 0.01), textColor: AppointmentType.getNextActionTextColor(widget.patientAppointmentHistoryResponseModel.nextAction), fontSize: (isFoldable || isTablet) ? 12.f : 14.f, fontWeight: FontWeight.w600, @@ -589,10 +611,10 @@ class _AppointmentCardState extends State { // Show Rebook button return CustomButton( - borderSide: BorderSide( - color: AppColors.textColor, - width: 1.2, - ), + borderSide: BorderSide( + color: AppColors.textColor, + width: 1.2, + ), text: LocaleKeys.rebookSameDoctor.tr(context: context), onPressed: () => openDoctorScheduleCalendar(context), backgroundColor: AppColors.transparent, @@ -620,7 +642,8 @@ class _AppointmentCardState extends State { ); } else { if (!AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel)) { - widget.bookAppointmentsViewModel.getAppointmentNearestGate(projectID: widget.patientAppointmentHistoryResponseModel.projectID, clinicID: widget.patientAppointmentHistoryResponseModel.clinicID); + widget.bookAppointmentsViewModel.getAppointmentNearestGate( + projectID: widget.patientAppointmentHistoryResponseModel.projectID, clinicID: widget.patientAppointmentHistoryResponseModel.clinicID); } Navigator.of(context) .push( @@ -708,46 +731,47 @@ class _AppointmentCardState extends State { children: [ Lottie.asset(AppAnimations.warningAnimation, repeat: false, reverse: false, frameRate: FrameRate(60), width: 100.h, height: 100.h, fit: BoxFit.fill), - SizedBox(height: 12,), - LocaleKeys.upcomingPaymentPending.tr(context: context).toText14( - color: AppColors.textColor, - isCenter: true, + SizedBox( + height: 12, ), + LocaleKeys.upcomingPaymentPending.tr(context: context).toText14( + color: AppColors.textColor, + isCenter: true, + ), SizedBox(height: 24.h), - // Countdown Timer - DD : HH : MM : SS format with labels Directionality( - textDirection: ui.TextDirection.ltr, - child:Row( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - // Days - _buildTimeUnit( - _timeRemaining != null ? _timeRemaining!.inDays.toString().padLeft(2, '0') : '00', - LocaleKeys.days.tr(context: context), - ), - _buildTimeSeparator(), - // Hours - _buildTimeUnit( - _timeRemaining != null ? _timeRemaining!.inHours.remainder(24).toString().padLeft(2, '0') : '00', - LocaleKeys.hours.tr(context: context), - ), - _buildTimeSeparator(), - // Minutes - _buildTimeUnit( - _timeRemaining != null ? _timeRemaining!.inMinutes.remainder(60).toString().padLeft(2, '0') : '00', - LocaleKeys.minutes.tr(context: context), - ), - _buildTimeSeparator(), - // Seconds - _buildTimeUnit( - _timeRemaining != null ? _timeRemaining!.inSeconds.remainder(60).toString().padLeft(2, '0') : '00', - LocaleKeys.seconds.tr(context: context), - ), - ], - )), + textDirection: ui.TextDirection.ltr, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Days + _buildTimeUnit( + _timeRemaining != null ? _timeRemaining!.inDays.toString().padLeft(2, '0') : '00', + LocaleKeys.days.tr(context: context), + ), + _buildTimeSeparator(), + // Hours + _buildTimeUnit( + _timeRemaining != null ? _timeRemaining!.inHours.remainder(24).toString().padLeft(2, '0') : '00', + LocaleKeys.hours.tr(context: context), + ), + _buildTimeSeparator(), + // Minutes + _buildTimeUnit( + _timeRemaining != null ? _timeRemaining!.inMinutes.remainder(60).toString().padLeft(2, '0') : '00', + LocaleKeys.minutes.tr(context: context), + ), + _buildTimeSeparator(), + // Seconds + _buildTimeUnit( + _timeRemaining != null ? _timeRemaining!.inSeconds.remainder(60).toString().padLeft(2, '0') : '00', + LocaleKeys.seconds.tr(context: context), + ), + ], + )), SizedBox(height: 24.h), // Green Acknowledge button with checkmark icon CustomButton( @@ -800,4 +824,3 @@ class _AppointmentCardState extends State { } } } - diff --git a/lib/presentation/medical_file/widgets/medical_file_appointment_card.dart b/lib/presentation/medical_file/widgets/medical_file_appointment_card.dart index 393dc3f6..9c64884a 100644 --- a/lib/presentation/medical_file/widgets/medical_file_appointment_card.dart +++ b/lib/presentation/medical_file/widgets/medical_file_appointment_card.dart @@ -1,4 +1,6 @@ import 'dart:async'; +import 'dart:ui' as ui; + import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:hmg_patient_app_new/core/app_assets.dart'; @@ -18,10 +20,8 @@ import 'package:hmg_patient_app_new/presentation/appointments/appointment_paymen 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/app_custom_chip_widget.dart'; -import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart'; import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart'; - -import 'dart:ui' as ui; +import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart'; class MedicalFileAppointmentCard extends StatefulWidget { final PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel; @@ -138,169 +138,193 @@ class _MedicalFileAppointmentCardState extends State richText: Directionality( textDirection: ui.TextDirection.ltr, child: DateUtil.formatDateToDate(DateUtil.convertStringToDate(widget.patientAppointmentHistoryResponseModel.appointmentDate), false) - .toText12(color: AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel) ? AppColors.textColor : AppColors.primaryRedColor, isBold: true, isEnglishOnly: true) + .toText12( + color: AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel) ? AppColors.textColor : AppColors.primaryRedColor, + isBold: true, + isEnglishOnly: true) .paddingSymmetrical(8.w, 0), ), - icon: AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel) ? AppAssets.appointment_calendar_icon : AppAssets.alarm_clock_icon, + icon: AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel) + ? AppAssets.appointment_calendar_icon + : AppAssets.alarm_clock_icon, iconColor: AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel) ? AppColors.textColor : AppColors.primaryRedColor, iconSize: 16.w, - backgroundColor: AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel) ? AppColors.greyColor : AppColors.secondaryLightRedColor, + backgroundColor: + AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel) ? AppColors.greyColor : AppColors.secondaryLightRedColor, textColor: AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel) ? AppColors.textColor : AppColors.primaryRedColor, padding: EdgeInsets.only(top: 12.h, bottom: 12.h, left: 8.w, right: 8.w), ).toShimmer2(isShow: widget.myAppointmentsViewModel.isMyAppointmentsLoading), SizedBox(height: 16.h), - Container( - decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.r, hasShadow: false), - width: 200.w, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - children: [ - Image.network( - widget.patientAppointmentHistoryResponseModel.doctorImageURL ?? "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png", - width: 25.w, - height: 27.h, - fit: BoxFit.fill, - ).circle(100).toShimmer2(isShow: widget.myAppointmentsViewModel.isMyAppointmentsLoading), - SizedBox(width: 8.w), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - (widget.patientAppointmentHistoryResponseModel.doctorNameObj ?? "").toText14(isBold: true, maxlines: 1, isEnglishOnly: !Utils.isArabicText(widget.patientAppointmentHistoryResponseModel.doctorNameObj ?? "")).toShimmer2(isShow: widget.myAppointmentsViewModel.isMyAppointmentsLoading), - (widget.patientAppointmentHistoryResponseModel.clinicName ?? "") - .toText12(maxLine: 1, isBold: true, color: AppColors.greyTextColor) - .toShimmer2(isShow: widget.myAppointmentsViewModel.isMyAppointmentsLoading), - ], + IntrinsicWidth( + child: Container( + decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.r, hasShadow: false), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Image.network( + widget.patientAppointmentHistoryResponseModel.doctorImageURL ?? + "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png", + width: 25.w, + height: 27.h, + fit: BoxFit.fill, + ).circle(100).toShimmer2(isShow: widget.myAppointmentsViewModel.isMyAppointmentsLoading), + SizedBox(width: 8.w), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + (widget.patientAppointmentHistoryResponseModel.doctorNameObj ?? "") + .toText14( + isBold: true, + maxlines: 1, + isEnglishOnly: !Utils.isArabicText(widget.patientAppointmentHistoryResponseModel.doctorNameObj ?? "")) + .toShimmer2(isShow: widget.myAppointmentsViewModel.isMyAppointmentsLoading), + (widget.patientAppointmentHistoryResponseModel.clinicName ?? "") + .toText12(maxLine: 1, textOverflow: TextOverflow.ellipsis, isBold: true, color: AppColors.greyTextColor) + .toShimmer2(isShow: widget.myAppointmentsViewModel.isMyAppointmentsLoading), + ], + ), ), - ), - ], - ), - SizedBox(height: 12.h), - // Check if doctor is active - if not, show only View Details button - (widget.patientAppointmentHistoryResponseModel.isActiveDoctor ?? true) - ? // Doctor is active - check rebooking logic - (AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel) && - widget.patientAppointmentHistoryResponseModel.isClinicReBookingAllowed == false) - ? // Show only the button without arrow when rebooking not allowed - widget.myAppointmentsViewModel.isMyAppointmentsLoading - ? Container().toShimmer2(isShow: true, height: 40.h, width: 100.w, radius: 12.r) - : AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel) - ? getArrivedAppointmentButton(context).toShimmer2(isShow: widget.myAppointmentsViewModel.isMyAppointmentsLoading) - : CustomButton( - text: AppointmentType.getNextActionText(widget.patientAppointmentHistoryResponseModel.nextAction), - onPressed: () { - handleAppointmentNextAction(widget.patientAppointmentHistoryResponseModel.nextAction, context); - }, - backgroundColor: AppointmentType.getNextActionButtonColor(widget.patientAppointmentHistoryResponseModel.nextAction).withValues(alpha: 0.15), - borderColor: AppointmentType.getNextActionButtonColor(widget.patientAppointmentHistoryResponseModel.nextAction).withValues(alpha: 0.01), - textColor: AppointmentType.getNextActionTextColor(widget.patientAppointmentHistoryResponseModel.nextAction), - fontSize: 14.f, - fontWeight: FontWeight.w600, - borderRadius: 12.r, - padding: EdgeInsets.symmetric(horizontal: 10.w), - height: 40.h, - icon: AppointmentType.getNextActionIcon(widget.patientAppointmentHistoryResponseModel.nextAction), - iconColor: AppointmentType.getNextActionTextColor(widget.patientAppointmentHistoryResponseModel.nextAction), - iconSize: 14.h, - ).toShimmer2(isShow: widget.myAppointmentsViewModel.isMyAppointmentsLoading) - : // Normal flow - show button with arrow - Row( - children: [ - widget.myAppointmentsViewModel.isMyAppointmentsLoading - ? Container().toShimmer2(isShow: true, height: 40.h, width: 100.w, radius: 12.r) - : Expanded( - flex: 7, - child: AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel) - ? getArrivedAppointmentButton(context).toShimmer2(isShow: widget.myAppointmentsViewModel.isMyAppointmentsLoading) - : CustomButton( - text: AppointmentType.getNextActionText(widget.patientAppointmentHistoryResponseModel.nextAction), - onPressed: () { - handleAppointmentNextAction(widget.patientAppointmentHistoryResponseModel.nextAction, context); - }, - backgroundColor: AppointmentType.getNextActionButtonColor(widget.patientAppointmentHistoryResponseModel.nextAction).withValues(alpha: 0.15), - borderColor: AppointmentType.getNextActionButtonColor(widget.patientAppointmentHistoryResponseModel.nextAction).withValues(alpha: 0.01), - textColor: AppointmentType.getNextActionTextColor(widget.patientAppointmentHistoryResponseModel.nextAction), - fontSize: 14.f, - fontWeight: FontWeight.w600, - borderRadius: 12.r, - padding: EdgeInsets.symmetric(horizontal: 10.w), - height: 40.h, - icon: AppointmentType.getNextActionIcon(widget.patientAppointmentHistoryResponseModel.nextAction), - iconColor: AppointmentType.getNextActionTextColor(widget.patientAppointmentHistoryResponseModel.nextAction), - iconSize: 14.h, - ).toShimmer2(isShow: widget.myAppointmentsViewModel.isMyAppointmentsLoading), - ), - SizedBox(width: 8.w), - ((((widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment ?? false) || - (widget.patientAppointmentHistoryResponseModel.isExecludeDoctor ?? false) || - !Utils.isClinicAllowedForRebook(widget.patientAppointmentHistoryResponseModel.clinicID ?? 0))) && - AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel)) - ? SizedBox.shrink() - : Expanded( - flex: 2, - child: Container( - height: 40.h, - width: 40.w, - decoration: RoundedRectangleBorder().toSmoothCornerDecoration( - color: AppColors.textColor, - borderRadius: 10.r, - ), - child: Padding( - padding: EdgeInsets.all(10.w), - child: Transform.flip( - flipX: appState.isArabic(), - child: Utils.buildSvgWithAssets( - iconColor: AppColors.whiteColor, - icon: AppAssets.forward_arrow_icon_small, - width: 40.w, - height: 40.h, - fit: BoxFit.contain, - ), + ], + ), + SizedBox(height: 8.h), + // Check if doctor is active - if not, show only View Details button + (widget.patientAppointmentHistoryResponseModel.isActiveDoctor ?? true) + ? // Doctor is active - check rebooking logic + (AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel) && + widget.patientAppointmentHistoryResponseModel.isClinicReBookingAllowed == false) + ? // Show only the button without arrow when rebooking not allowed + widget.myAppointmentsViewModel.isMyAppointmentsLoading + ? Container().toShimmer2(isShow: true, height: 40.h, width: 100.w, radius: 12.r) + : AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel) + ? getArrivedAppointmentButton(context).toShimmer2(isShow: widget.myAppointmentsViewModel.isMyAppointmentsLoading) + : CustomButton( + text: AppointmentType.getNextActionText(widget.patientAppointmentHistoryResponseModel.nextAction), + onPressed: () { + handleAppointmentNextAction(widget.patientAppointmentHistoryResponseModel.nextAction, context); + }, + backgroundColor: + AppointmentType.getNextActionButtonColor(widget.patientAppointmentHistoryResponseModel.nextAction) + .withValues(alpha: 0.15), + borderColor: AppointmentType.getNextActionButtonColor(widget.patientAppointmentHistoryResponseModel.nextAction) + .withValues(alpha: 0.01), + textColor: AppointmentType.getNextActionTextColor(widget.patientAppointmentHistoryResponseModel.nextAction), + fontSize: 14.f, + fontWeight: FontWeight.w600, + borderRadius: 12.r, + padding: EdgeInsets.symmetric(horizontal: 10.w), + height: 40.h, + icon: AppointmentType.getNextActionIcon(widget.patientAppointmentHistoryResponseModel.nextAction), + iconColor: AppointmentType.getNextActionTextColor(widget.patientAppointmentHistoryResponseModel.nextAction), + iconSize: 14.h, + ).toShimmer2(isShow: widget.myAppointmentsViewModel.isMyAppointmentsLoading) + : // Normal flow - show button with arrow + Row( + children: [ + widget.myAppointmentsViewModel.isMyAppointmentsLoading + ? Container().toShimmer2(isShow: true, height: 40.h, width: 100.w, radius: 12.r) + : Expanded( + flex: 7, + child: AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel) + ? getArrivedAppointmentButton(context) + .toShimmer2(isShow: widget.myAppointmentsViewModel.isMyAppointmentsLoading) + : CustomButton( + text: AppointmentType.getNextActionText(widget.patientAppointmentHistoryResponseModel.nextAction), + onPressed: () { + handleAppointmentNextAction(widget.patientAppointmentHistoryResponseModel.nextAction, context); + }, + backgroundColor: + AppointmentType.getNextActionButtonColor(widget.patientAppointmentHistoryResponseModel.nextAction) + .withValues(alpha: 0.15), + borderColor: + AppointmentType.getNextActionButtonColor(widget.patientAppointmentHistoryResponseModel.nextAction) + .withValues(alpha: 0.01), + textColor: + AppointmentType.getNextActionTextColor(widget.patientAppointmentHistoryResponseModel.nextAction), + fontSize: 14.f, + fontWeight: FontWeight.w600, + borderRadius: 12.r, + padding: EdgeInsets.symmetric(horizontal: 10.w), + height: 40.h, + icon: AppointmentType.getNextActionIcon(widget.patientAppointmentHistoryResponseModel.nextAction), + iconColor: + AppointmentType.getNextActionTextColor(widget.patientAppointmentHistoryResponseModel.nextAction), + iconSize: 14.h, + ).toShimmer2(isShow: widget.myAppointmentsViewModel.isMyAppointmentsLoading), + ), + SizedBox(width: 8.w), + ((((widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment ?? false) || + (widget.patientAppointmentHistoryResponseModel.isExecludeDoctor ?? false) || + !Utils.isClinicAllowedForRebook(widget.patientAppointmentHistoryResponseModel.clinicID ?? 0))) && + AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel)) + ? SizedBox.shrink() + : Expanded( + flex: 2, + child: Container( + height: 40.h, + width: 40.w, + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: AppColors.textColor, + borderRadius: 10.r, ), - ), - ).toShimmer2(isShow: widget.myAppointmentsViewModel.isMyAppointmentsLoading).onPress(() { - Navigator.of(context) - .push( - CustomPageRoute( - page: AppointmentDetailsPage(patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel), + child: Padding( + padding: EdgeInsets.all(10.w), + child: Transform.flip( + flipX: appState.isArabic(), + child: Utils.buildSvgWithAssets( + iconColor: AppColors.whiteColor, + icon: AppAssets.forward_arrow_icon_small, + width: 40.w, + height: 40.h, + fit: BoxFit.contain, + ), + ), ), - ) - .then((val) { - // widget.myAppointmentsViewModel.initAppointmentsViewModel(); - // widget.myAppointmentsViewModel.getPatientAppointments(true, false); - }); - }), - ), - ], - ) - : // Doctor is not active - show only View Details button - CustomButton( - text: LocaleKeys.viewDetails.tr(context: context), - onPressed: () { - Navigator.of(context) - .push( - CustomPageRoute( - page: AppointmentDetailsPage(patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel), - ), - ) - .then((_) { - widget.myAppointmentsViewModel.initAppointmentsViewModel(); - widget.myAppointmentsViewModel.getPatientAppointments(true, false); - }); - }, - backgroundColor: AppColors.secondaryLightRedColor, - borderColor: AppColors.secondaryLightRedColor, - textColor: AppColors.primaryRedColor, - fontSize: (isFoldable || isTablet) ? 12.f : 14.f, - fontWeight: FontWeight.w600, - borderRadius: 12.r, - padding: EdgeInsets.symmetric(horizontal: 10.w), - height: 40.h, - ).toShimmer2(isShow: widget.myAppointmentsViewModel.isMyAppointmentsLoading), - ], - ).paddingAll(16.w), + ).toShimmer2(isShow: widget.myAppointmentsViewModel.isMyAppointmentsLoading).onPress(() { + Navigator.of(context) + .push( + CustomPageRoute( + page: AppointmentDetailsPage( + patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel), + ), + ) + .then((val) { + // widget.myAppointmentsViewModel.initAppointmentsViewModel(); + // widget.myAppointmentsViewModel.getPatientAppointments(true, false); + }); + }), + ), + ], + ) + : // Doctor is not active - show only View Details button + CustomButton( + text: LocaleKeys.viewDetails.tr(context: context), + onPressed: () { + Navigator.of(context) + .push( + CustomPageRoute( + page: AppointmentDetailsPage(patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel), + ), + ) + .then((_) { + widget.myAppointmentsViewModel.initAppointmentsViewModel(); + widget.myAppointmentsViewModel.getPatientAppointments(true, false); + }); + }, + backgroundColor: AppColors.secondaryLightRedColor, + borderColor: AppColors.secondaryLightRedColor, + textColor: AppColors.primaryRedColor, + fontSize: (isFoldable || isTablet) ? 12.f : 14.f, + fontWeight: FontWeight.w600, + borderRadius: 12.r, + padding: EdgeInsets.symmetric(horizontal: 10.w), + height: 40.h, + ).toShimmer2(isShow: widget.myAppointmentsViewModel.isMyAppointmentsLoading), + ], + ).paddingAll(16.w), + ), ), ], ); @@ -393,16 +417,16 @@ class _MedicalFileAppointmentCardState extends State // No action needed - go to details Navigator.of(context) .push(CustomPageRoute( - page: AppointmentDetailsPage(patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel), - )) + page: AppointmentDetailsPage(patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel), + )) .then((val) {}); break; case 10: // Confirm appointment - go to details Navigator.of(context) .push(CustomPageRoute( - page: AppointmentDetailsPage(patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel), - )) + page: AppointmentDetailsPage(patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel), + )) .then((val) {}); break; case 15: @@ -425,41 +449,42 @@ class _MedicalFileAppointmentCardState extends State children: [ // Message text LocaleKeys.upcomingPaymentPending.tr(context: context).toText14( - color: AppColors.textColor, - isCenter: true, - ), + color: AppColors.textColor, + isCenter: true, + ), SizedBox(height: 24.h), // Countdown Timer - DD : HH : MM : SS format with labels Directionality( - textDirection: ui.TextDirection.ltr, child:Row( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - // Days - _buildTimeUnit( - _timeRemaining != null ? _timeRemaining!.inDays.toString().padLeft(2, '0') : '00', - LocaleKeys.days.tr(context: context), - ), - _buildTimeSeparator(), - // Hours - _buildTimeUnit( - _timeRemaining != null ? _timeRemaining!.inHours.remainder(24).toString().padLeft(2, '0') : '00', - LocaleKeys.hours.tr(context: context), - ), - _buildTimeSeparator(), - // Minutes - _buildTimeUnit( - _timeRemaining != null ? _timeRemaining!.inMinutes.remainder(60).toString().padLeft(2, '0') : '00', - LocaleKeys.minutes.tr(context: context), - ), - _buildTimeSeparator(), - // Seconds - _buildTimeUnit( - _timeRemaining != null ? _timeRemaining!.inSeconds.remainder(60).toString().padLeft(2, '0') : '00', - LocaleKeys.seconds.tr(context: context), - ), - ], - )), + textDirection: ui.TextDirection.ltr, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Days + _buildTimeUnit( + _timeRemaining != null ? _timeRemaining!.inDays.toString().padLeft(2, '0') : '00', + LocaleKeys.days.tr(context: context), + ), + _buildTimeSeparator(), + // Hours + _buildTimeUnit( + _timeRemaining != null ? _timeRemaining!.inHours.remainder(24).toString().padLeft(2, '0') : '00', + LocaleKeys.hours.tr(context: context), + ), + _buildTimeSeparator(), + // Minutes + _buildTimeUnit( + _timeRemaining != null ? _timeRemaining!.inMinutes.remainder(60).toString().padLeft(2, '0') : '00', + LocaleKeys.minutes.tr(context: context), + ), + _buildTimeSeparator(), + // Seconds + _buildTimeUnit( + _timeRemaining != null ? _timeRemaining!.inSeconds.remainder(60).toString().padLeft(2, '0') : '00', + LocaleKeys.seconds.tr(context: context), + ), + ], + )), SizedBox(height: 24.h), // Green Acknowledge button with checkmark icon CustomButton( @@ -502,26 +527,25 @@ class _MedicalFileAppointmentCardState extends State // Confirm livecare - go to details Navigator.of(context) .push(CustomPageRoute( - page: AppointmentDetailsPage(patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel), - )) + page: AppointmentDetailsPage(patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel), + )) .then((val) {}); break; case 90: // Check-in - go to details Navigator.of(context) .push(CustomPageRoute( - page: AppointmentDetailsPage(patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel), - )) + page: AppointmentDetailsPage(patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel), + )) .then((val) {}); break; default: // Default - go to details Navigator.of(context) .push(CustomPageRoute( - page: AppointmentDetailsPage(patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel), - )) + page: AppointmentDetailsPage(patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel), + )) .then((val) {}); } } } - diff --git a/lib/presentation/symptoms_checker/user_info_selection.dart b/lib/presentation/symptoms_checker/user_info_selection.dart index 9113487d..bef52a59 100644 --- a/lib/presentation/symptoms_checker/user_info_selection.dart +++ b/lib/presentation/symptoms_checker/user_info_selection.dart @@ -99,9 +99,8 @@ class _UserInfoSelectionPageState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ title.toText14(isBold: true), - subTitle - .toText12(color: AppColors.primaryRedColor, isBold: true, isEnglishOnly: true) - .toShimmer2(isShow: (leadingIcon == AppAssets.rulerIcon || leadingIcon == AppAssets.weightScale) && hmgServicesVM.isVitalSignLoading), + subTitle.toText12(color: AppColors.primaryRedColor, isBold: true, isEnglishOnly: true).toShimmer2( + isShow: (leadingIcon == AppAssets.rulerIcon || leadingIcon == AppAssets.weightScale) && hmgServicesVM.isVitalSignLoading), ], ), ], @@ -159,7 +158,9 @@ class _UserInfoSelectionPageState extends State { } } } else { - name = ""; /// as per the mahmooud instruction, if user is not authenticated, we will not show any name in the greeting message + name = ""; + + /// as per the mahmooud instruction, if user is not authenticated, we will not show any name in the greeting message } return Scaffold( @@ -167,7 +168,10 @@ class _UserInfoSelectionPageState extends State { body: Consumer2( builder: (context, viewModel, hmgServicesVM, child) { // Check if any field is empty - bool hasEmptyFields = viewModel.selectedGender == null || viewModel.selectedAge == null || viewModel.selectedHeight == null || viewModel.selectedWeight == null; + bool hasEmptyFields = viewModel.selectedGender == null || + viewModel.selectedAge == null || + viewModel.selectedHeight == null || + viewModel.selectedWeight == null; // Get display values String genderText = _getLocalizedGender(viewModel.selectedGender, context); @@ -313,6 +317,7 @@ class _UserInfoSelectionPageState extends State { ), ], ), + SizedBox(height: 24.h), ], ).paddingSymmetrical(24.w, 0), ), diff --git a/lib/presentation/symptoms_checker/user_info_selection/user_info_flow_manager.dart b/lib/presentation/symptoms_checker/user_info_selection/user_info_flow_manager.dart index fdfe2288..6672a968 100644 --- a/lib/presentation/symptoms_checker/user_info_selection/user_info_flow_manager.dart +++ b/lib/presentation/symptoms_checker/user_info_selection/user_info_flow_manager.dart @@ -181,7 +181,7 @@ class _UserInfoFlowManagerState extends State { color: AppColors.whiteColor, borderRadius: BorderRadius.vertical(top: Radius.circular(24.r)), ), - padding: EdgeInsets.only(left: 24.w, right: 24.w, top: 16.h), + padding: EdgeInsets.only(left: 24.w, right: 24.w, top: 16.h, bottom: 24.h), child: SafeArea( top: false, child: isSingleEdit diff --git a/pubspec.lock b/pubspec.lock index dd27bab8..3bd9576a 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1042,13 +1042,13 @@ packages: source: hosted version: "1.2.1" image_picker_android: - dependency: transitive + dependency: "direct main" description: name: image_picker_android - sha256: "5e9bf126c37c117cf8094215373c6d561117a3cfb50ebc5add1a61dc6e224677" + sha256: "66810af8e99b2657ee98e5c6f02064f69bb63f7a70e343937f70946c5f8c6622" url: "https://pub.dev" source: hosted - version: "0.8.13+10" + version: "0.8.13+16" image_picker_for_web: dependency: transitive description: @@ -1082,7 +1082,7 @@ packages: source: hosted version: "0.2.2+1" image_picker_platform_interface: - dependency: transitive + dependency: "direct main" description: name: image_picker_platform_interface sha256: "567e056716333a1647c64bb6bd873cff7622233a5c3f694be28a583d4715690c" diff --git a/pubspec.yaml b/pubspec.yaml index a27fb251..7cb61939 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -101,9 +101,7 @@ dependencies: flutter_widget_from_html: ^0.17.1 huawei_map: ^6.12.0+301 flutter_image_compress: ^2.3.0 - scrollable_positioned_list: ^0.3.8 - in_app_review: ^2.0.11 dev_dependencies: