Symptoms checker flow updates and Design Fixes on Fold (faiz_dev) #301

Merged
Haroon6138 merged 4 commits from faiz_dev into master 1 day ago

@ -601,7 +601,7 @@ SPEC CHECKSUMS:
OrderedSet: e539b66b644ff081c73a262d24ad552a69be3a94 OrderedSet: e539b66b644ff081c73a262d24ad552a69be3a94
package_info_plus: c0502532a26c7662a62a356cebe2692ec5fe4ec4 package_info_plus: c0502532a26c7662a62a356cebe2692ec5fe4ec4
path_provider_foundation: 0b743cbb62d8e47eab856f09262bb8c1ddcfe6ba path_provider_foundation: 0b743cbb62d8e47eab856f09262bb8c1ddcfe6ba
PayFortSDK: 233eabe9a45601fdbeac67fa6e5aae46ed8faf82 PayFortSDK: e12f79051ae15b2e29536771a45354900180bfcb
permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2 permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2
Polyline: 2a1f29f87f8d9b7de868940f4f76deb8c678a5b1 Polyline: 2a1f29f87f8d9b7de868940f4f76deb8c678a5b1
PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47 PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47
@ -612,7 +612,7 @@ SPEC CHECKSUMS:
shared_preferences_foundation: 5086985c1d43c5ba4d5e69a4e8083a389e2909e6 shared_preferences_foundation: 5086985c1d43c5ba4d5e69a4e8083a389e2909e6
Solar-dev: 4612dc9878b9fed2667d23b327f1d4e54e16e8d0 Solar-dev: 4612dc9878b9fed2667d23b327f1d4e54e16e8d0
sqflite_darwin: 5a7236e3b501866c1c9befc6771dfd73ffb8702d sqflite_darwin: 5a7236e3b501866c1c9befc6771dfd73ffb8702d
SwiftProtobuf: e1b437c8e31a4c5577b643249a0bb62ed4f02153 SwiftProtobuf: 9e106a71456f4d3f6a3b0c8fd87ef0be085efc38
SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4 SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4
Turf: aa2ede4298009639d10db36aba1a7ebaad072a5e Turf: aa2ede4298009639d10db36aba1a7ebaad072a5e
url_launcher_ios: bb13df5870e8c4234ca12609d04010a21be43dfa url_launcher_ios: bb13df5870e8c4234ca12609d04010a21be43dfa

@ -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/api_consts.dart';
import 'package:hmg_patient_app_new/core/app_state.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/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/core/utils/utils.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/home/app_update_page.dart'; import 'package:hmg_patient_app_new/presentation/home/app_update_page.dart';
@ -16,8 +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:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
import 'package:http/http.dart' as http; import 'package:http/http.dart' as http;
import '../exceptions/api_failure.dart';
abstract class ApiClient { abstract class ApiClient {
static final NavigationService _navigationService = getIt.get<NavigationService>(); static final NavigationService _navigationService = getIt.get<NavigationService>();
@ -209,7 +208,7 @@ class ApiClientImp implements ApiClient {
// body['PatientOutSA'] = 0; // body['PatientOutSA'] = 0;
// body['SessionID'] = "45786230487560q"; // body['SessionID'] = "45786230487560q";
//VIP Patient: 1181868 //VIP Patient: 1181868body:
// body['IdentificationNo'] = "2235558844"; // body['IdentificationNo'] = "2235558844";
// body['MobileNo'] = "966533147722"; // body['MobileNo'] = "966533147722";
@ -244,6 +243,7 @@ class ApiClientImp implements ApiClient {
http.Response response; http.Response response;
try { try {
response = await http.post(Uri.parse(url.trim()), body: requestBody, headers: headers); response = await http.post(Uri.parse(url.trim()), body: requestBody, headers: headers);
debugPrint("response: ${response.body}", wrapWidth: 2048);
} on SocketException catch (e) { } on SocketException catch (e) {
final message = e.message.contains('Connection reset by peer') ? LocaleKeys.networkConnectionReset.tr() : LocaleKeys.networkErrorMessage.tr(); final message = e.message.contains('Connection reset by peer') ? LocaleKeys.networkConnectionReset.tr() : LocaleKeys.networkErrorMessage.tr();
onFailure(message, -1, failureType: ConnectivityFailure(message)); onFailure(message, -1, failureType: ConnectivityFailure(message));

@ -11,8 +11,14 @@ class ApiConsts {
static String baseUrl = 'https://hmgwebservices.com/'; // HIS API URL PROD static String baseUrl = 'https://hmgwebservices.com/'; // HIS API URL PROD
static String rcBaseUrl = 'https://rc.hmg.com/'; // dRC API URL PROD static String rcBaseUrl = 'https://rc.hmg.com/'; // dRC API URL PROD
static String hmgPharmacyApiBaseUrl = 'https://hmgpharmacyapi.hmg.com/'; // dRC API URL PROD static String hmgPharmacyApiBaseUrl = 'https://hmgpharmacyapi.hmg.com/'; // symptoms API URL PROD
static String symptomsCheckerApi = '${hmgPharmacyApiBaseUrl}symptomsapi/api/SymptomChecker'; // dRC API URL PROD static String symptomsCheckerApiUAT = '${hmgPharmacyApiBaseUrl}symptomsapi/api/SymptomChecker'; // dRC API URL PROD
static String symptomsCheckerApiLive = '${hmgPharmacyApiBaseUrl}symptomsapi_live/api/SymptomChecker'; // dRC API URL PROD
static String symptomsCheckerApi = '${hmgPharmacyApiBaseUrl}symptomsapi_live/api/SymptomChecker'; // dRC API URL PROD
// Symptoms Checker Credentials
static String symptomsCheckerUsername = 'mobile_user';
static String symptomsCheckerPassword = '8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92';
static var payFortEnvironment = FortEnvironment.production; static var payFortEnvironment = FortEnvironment.production;
static var applePayMerchantId = "merchant.com.hmgwebservices"; static var applePayMerchantId = "merchant.com.hmgwebservices";
@ -25,6 +31,7 @@ class ApiConsts {
static String GET_TAMARA_PAYMENT_STATUS = 'https://mdlaboratories.com/tamaralive/api/OnlineTamara/order_status?orderid='; static String GET_TAMARA_PAYMENT_STATUS = 'https://mdlaboratories.com/tamaralive/api/OnlineTamara/order_status?orderid=';
static String QLINE_URL = "https://ms.hmg.com/nscapi/api/PatientCall/PatientInQueue_Detail"; static String QLINE_URL = "https://ms.hmg.com/nscapi/api/PatientCall/PatientInQueue_Detail";
// static String QLINE_URL = "https://qline.hmg.com/api/PatientCall/PatientInQueue_Detail"; // static String QLINE_URL = "https://qline.hmg.com/api/PatientCall/PatientInQueue_Detail";
static String CHAT_URL = "https://chat.hmg.com/geneysChat/Index.aspx?RequestedId="; static String CHAT_URL = "https://chat.hmg.com/geneysChat/Index.aspx?RequestedId=";
@ -46,6 +53,11 @@ class ApiConsts {
GET_TAMARA_PAYMENT_STATUS = 'https://mdlaboratories.com/tamaralive/api/OnlineTamara/order_status?orderid='; GET_TAMARA_PAYMENT_STATUS = 'https://mdlaboratories.com/tamaralive/api/OnlineTamara/order_status?orderid=';
rcBaseUrl = 'https://rc.hmg.com/'; rcBaseUrl = 'https://rc.hmg.com/';
QLINE_URL = "https://qline.hmg.com/api/PatientCall/PatientInQueue_Detail"; QLINE_URL = "https://qline.hmg.com/api/PatientCall/PatientInQueue_Detail";
CHAT_URL = "https://chat.hmg.com/Index.aspx?RequestedId=";
symptomsCheckerApi = symptomsCheckerApiLive;
symptomsCheckerUsername = 'mobile_user';
symptomsCheckerPassword = '8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92';
CHAT_URL = "https://chat.hmg.com/geneysChat/Index.aspx?RequestedId="; CHAT_URL = "https://chat.hmg.com/geneysChat/Index.aspx?RequestedId=";
break; break;
case AppEnvironmentTypeEnum.dev: case AppEnvironmentTypeEnum.dev:
@ -59,6 +71,10 @@ class ApiConsts {
rcBaseUrl = 'https://rc.hmg.com/uat/'; rcBaseUrl = 'https://rc.hmg.com/uat/';
QLINE_URL = "https://ms.hmg.com/nscapi/api/PatientCall/PatientInQueue_Detail"; QLINE_URL = "https://ms.hmg.com/nscapi/api/PatientCall/PatientInQueue_Detail";
CHAT_URL = "https://chat.hmg.com/geneysChat/Index.aspx?RequestedId="; CHAT_URL = "https://chat.hmg.com/geneysChat/Index.aspx?RequestedId=";
symptomsCheckerApi = symptomsCheckerApiUAT;
symptomsCheckerUsername = 'guest_user';
symptomsCheckerPassword = '123456';
break; break;
case AppEnvironmentTypeEnum.uat: case AppEnvironmentTypeEnum.uat:
baseUrl = "https://uat.hmgwebservices.com/"; baseUrl = "https://uat.hmgwebservices.com/";
@ -71,6 +87,10 @@ class ApiConsts {
rcBaseUrl = 'https://rc.hmg.com/uat/'; rcBaseUrl = 'https://rc.hmg.com/uat/';
QLINE_URL = "https://ms.hmg.com/nscapi/api/PatientCall/PatientInQueue_Detail"; QLINE_URL = "https://ms.hmg.com/nscapi/api/PatientCall/PatientInQueue_Detail";
CHAT_URL = "https://chat.hmg.com/geneysChat/Index.aspx?RequestedId="; CHAT_URL = "https://chat.hmg.com/geneysChat/Index.aspx?RequestedId=";
symptomsCheckerApi = symptomsCheckerApiUAT;
symptomsCheckerUsername = 'guest_user';
symptomsCheckerPassword = '123456';
break; break;
case AppEnvironmentTypeEnum.preProd: case AppEnvironmentTypeEnum.preProd:
baseUrl = "https://webservices.hmg.com/"; baseUrl = "https://webservices.hmg.com/";
@ -83,6 +103,10 @@ class ApiConsts {
rcBaseUrl = 'https://rc.hmg.com/'; rcBaseUrl = 'https://rc.hmg.com/';
QLINE_URL = "https://qline.hmg.com/api/PatientCall/PatientInQueue_Detail"; QLINE_URL = "https://qline.hmg.com/api/PatientCall/PatientInQueue_Detail";
CHAT_URL = "https://chat.hmg.com/geneysChat/Index.aspx?RequestedId="; CHAT_URL = "https://chat.hmg.com/geneysChat/Index.aspx?RequestedId=";
symptomsCheckerApi = symptomsCheckerApiUAT;
symptomsCheckerUsername = 'guest_user';
symptomsCheckerPassword = '123456';
break; break;
case AppEnvironmentTypeEnum.qa: case AppEnvironmentTypeEnum.qa:
baseUrl = "https://uat.hmgwebservices.com/"; baseUrl = "https://uat.hmgwebservices.com/";
@ -95,6 +119,10 @@ class ApiConsts {
rcBaseUrl = 'https://rc.hmg.com/uat/'; rcBaseUrl = 'https://rc.hmg.com/uat/';
QLINE_URL = "https://ms.hmg.com/nscapi/api/PatientCall/PatientInQueue_Detail"; QLINE_URL = "https://ms.hmg.com/nscapi/api/PatientCall/PatientInQueue_Detail";
CHAT_URL = "https://chat.hmg.com/geneysChat/Index.aspx?RequestedId="; CHAT_URL = "https://chat.hmg.com/geneysChat/Index.aspx?RequestedId=";
symptomsCheckerApi = symptomsCheckerApiUAT;
symptomsCheckerUsername = 'guest_user';
symptomsCheckerPassword = '123456';
break; break;
case AppEnvironmentTypeEnum.staging: case AppEnvironmentTypeEnum.staging:
baseUrl = "https://uat.hmgwebservices.com/"; baseUrl = "https://uat.hmgwebservices.com/";
@ -107,6 +135,10 @@ class ApiConsts {
rcBaseUrl = 'https://rc.hmg.com/uat/'; rcBaseUrl = 'https://rc.hmg.com/uat/';
QLINE_URL = "https://ms.hmg.com/nscapi/api/PatientCall/PatientInQueue_Detail"; QLINE_URL = "https://ms.hmg.com/nscapi/api/PatientCall/PatientInQueue_Detail";
CHAT_URL = "https://chat.hmg.com/geneysChat/Index.aspx?RequestedId="; CHAT_URL = "https://chat.hmg.com/geneysChat/Index.aspx?RequestedId=";
symptomsCheckerApi = symptomsCheckerApiUAT;
symptomsCheckerUsername = 'guest_user';
symptomsCheckerPassword = '123456';
break; break;
} }
} }
@ -189,7 +221,6 @@ class ApiConsts {
static final createEReferral = "Services/Patients.svc/REST/CreateEReferral"; static final createEReferral = "Services/Patients.svc/REST/CreateEReferral";
static final getEReferrals = "Services/Patients.svc/REST/GetEReferrals"; static final getEReferrals = "Services/Patients.svc/REST/GetEReferrals";
//WATER CONSUMPTION //WATER CONSUMPTION
static String h2oGetUserProgress = "Services/H2ORemainder.svc/REST/H2O_GetUserProgress"; static String h2oGetUserProgress = "Services/H2ORemainder.svc/REST/H2O_GetUserProgress";
static String h2oInsertUserActivity = "Services/H2ORemainder.svc/REST/H2O_InsertUserActivity"; static String h2oInsertUserActivity = "Services/H2ORemainder.svc/REST/H2O_InsertUserActivity";
@ -235,6 +266,7 @@ class ApiConsts {
static String getPatientBloodGroup = "services/PatientVarification.svc/REST/BloodDonation_GetBloodGroupDetails"; static String getPatientBloodGroup = "services/PatientVarification.svc/REST/BloodDonation_GetBloodGroupDetails";
static String getPatientBloodAgreement = "Services/PatientVarification.svc/REST/CheckUserAgreementForBloodDonation"; static String getPatientBloodAgreement = "Services/PatientVarification.svc/REST/CheckUserAgreementForBloodDonation";
static String getPatientBloodTypeNew = "Services/Patients.svc/REST/HIS_GetPatientBloodType_New"; static String getPatientBloodTypeNew = "Services/Patients.svc/REST/HIS_GetPatientBloodType_New";
// static String getAiOverViewLabOrders = "Services/Patients.svc/REST/HMGAI_Lab_Analyze_Orders_API"; // static String getAiOverViewLabOrders = "Services/Patients.svc/REST/HMGAI_Lab_Analyze_Orders_API";
// static String getAiOverViewLabOrder = "Services/Patients.svc/REST/HMGAI_Lab_Analyzer_API"; // static String getAiOverViewLabOrder = "Services/Patients.svc/REST/HMGAI_Lab_Analyzer_API";
@ -806,7 +838,8 @@ var GET_CUSTOMER_INFO = "VerifyCustomer";
//Pharmacy //Pharmacy
var GET_PHARMACY_CATEGORISE = 'categories?fields=id,name,namen,description,image,localized_names,display_order,parent_category_id,is_leaf&parent_id=0'; var GET_PHARMACY_CATEGORISE =
'categories?fields=id,name,namen,description,image,localized_names,display_order,parent_category_id,is_leaf&parent_id=0';
var GET_OFFERS_CATEGORISE = 'discountcategories'; var GET_OFFERS_CATEGORISE = 'discountcategories';
var GET_OFFERS_PRODUCTS = 'offerproducts/'; var GET_OFFERS_PRODUCTS = 'offerproducts/';
var GET_CATEGORISE_PARENT = 'categories?fields=id,name,namen,description,image,localized_names,display_order,parent_category_id,is_leaf&parent_id='; var GET_CATEGORISE_PARENT = 'categories?fields=id,name,namen,description,image,localized_names,display_order,parent_category_id,is_leaf&parent_id=';
@ -834,7 +867,7 @@ var FILTERED_PRODUCTS = 'products?categoryids=';
var GET_DOCTOR_LIST_CALCULATION = "Services/Doctors.svc/REST/GetCallculationDoctors"; var GET_DOCTOR_LIST_CALCULATION = "Services/Doctors.svc/REST/GetCallculationDoctors";
// var GET_ALL_APPOINTMENTS_FOR_DENTAL_CLINIC = "Services/Patients.svc/REST/GetDentalAppointments"; // var GET_ALL_APPOINTMENTS_FOR_DENTAL_CLINIC = "Services/Patients.svc/REST/GetDentalAppointments";
var GET_ALL_APPOINTMENTS_FOR_DENTAL_CLINIC ='Services/Patients.svc/REST/GetAllInvoices'; var GET_ALL_APPOINTMENTS_FOR_DENTAL_CLINIC = 'Services/Patients.svc/REST/GetAllInvoices';
var GET_DENTAL_APPOINTMENT_INVOICE = "Services/Patients.svc/REST/HIS_eInvoiceForDentalByAppointmentNo"; var GET_DENTAL_APPOINTMENT_INVOICE = "Services/Patients.svc/REST/HIS_eInvoiceForDentalByAppointmentNo";
var SEND_DENTAL_APPOINTMENT_INVOICE_EMAIL = "Services/Notifications.svc/REST/SendInvoiceForDental"; var SEND_DENTAL_APPOINTMENT_INVOICE_EMAIL = "Services/Notifications.svc/REST/SendInvoiceForDental";

@ -9,18 +9,17 @@ import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_callkit_incoming/entities/android_params.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/call_kit_params.dart';
import 'package:flutter_callkit_incoming/entities/ios_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/entities/notification_params.dart';
import 'package:flutter_callkit_incoming/flutter_callkit_incoming.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/call_state_type.dart';
import 'package:flutter_ios_voip_kit_karmm/flutter_ios_voip_kit.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/api_consts.dart';
import 'package:hmg_patient_app_new/core/cache_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/dependencies.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:permission_handler/permission_handler.dart'; import 'package:permission_handler/permission_handler.dart';
import 'package:uuid/uuid.dart'; import 'package:uuid/uuid.dart';
@ -33,7 +32,7 @@ String? _currentSessionId; // Store session ID for API call
// |--> Push Notification Background // |--> Push Notification Background
@pragma('vm:entry-point') @pragma('vm:entry-point')
Future<dynamic> backgroundMessageHandler(dynamic message) async { Future<dynamic> backgroundMessageHandler(dynamic message) async {
print("Firebase backgroundMessageHandler!!!"); log("Firebase backgroundMessageHandler!!!");
await Firebase.initializeApp(); await Firebase.initializeApp();
fir.RemoteMessage message_; fir.RemoteMessage message_;
@ -47,20 +46,20 @@ Future<dynamic> backgroundMessageHandler(dynamic message) async {
callPage(String sessionID, String token) async {} callPage(String sessionID, String token) async {}
_incomingCall(Map<String, dynamic> data) async { _incomingCall(Map<String, dynamic> 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 // Check if there's already a call in progress to prevent duplicates
if (_isCallInProgress && _currentCallId != null) { 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; return;
} }
String roomID = data['session_id'] ?? ''; String roomID = data['session_id'] ?? '';
String callTypeID = data['AppointmentNo'] ?? ''; String callTypeID = data['AppointmentNo'] ?? '';
print('🔍 Extracted from notification data:'); log('🔍 Extracted from notification data:');
print(' - roomID (session_id): "$roomID"'); log(' - roomID (session_id): "$roomID"');
print(' - callTypeID (AppointmentNo): "$callTypeID"'); log(' - callTypeID (AppointmentNo): "$callTypeID"');
// Generate unique call ID // Generate unique call ID
var _currentUuid = Uuid().v4(); var _currentUuid = Uuid().v4();
@ -68,16 +67,16 @@ _incomingCall(Map<String, dynamic> data) async {
_isCallInProgress = true; _isCallInProgress = true;
_currentSessionId = roomID; // Store session ID for decline API call _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.zoomRoomID, roomID);
await Utils.saveStringFromPrefs(CacheConst.callTypeID, callTypeID); await Utils.saveStringFromPrefs(CacheConst.callTypeID, callTypeID);
await Utils.saveBoolFromPrefs(CacheConst.isAppOpenedFromCall, true); await Utils.saveBoolFromPrefs(CacheConst.isAppOpenedFromCall, true);
print('💾 Saved to cache:'); log('💾 Saved to cache:');
print(' - CacheConst.zoomRoomID: "$roomID"'); log(' - CacheConst.zoomRoomID: "$roomID"');
print(' - CacheConst.callTypeID: "$callTypeID"'); log(' - CacheConst.callTypeID: "$callTypeID"');
print(' - CacheConst.isAppOpenedFromCall: true'); log(' - CacheConst.isAppOpenedFromCall: true');
WidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();
@ -148,7 +147,7 @@ _incomingCall(Map<String, dynamic> data) async {
// Start 60-second timeout timer to auto-dismiss call // Start 60-second timeout timer to auto-dismiss call
_callTimeoutTimer?.cancel(); // Cancel any existing timer _callTimeoutTimer?.cancel(); // Cancel any existing timer
_callTimeoutTimer = Timer(Duration(seconds: 40), () async { _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); await _endCallAndCleanup(_currentUuid);
}); });
@ -158,8 +157,6 @@ _incomingCall(Map<String, dynamic> data) async {
// Helper method to end call and cleanup // Helper method to end call and cleanup
Future<void> _endCallAndCleanup(String callId, {bool isDeclined = false}) async { Future<void> _endCallAndCleanup(String callId, {bool isDeclined = false}) async {
try { try {
print('🧹 Cleaning up call: $callId');
// If call was declined, update session status via API // If call was declined, update session status via API
if (isDeclined && _currentSessionId != null && _currentSessionId!.isNotEmpty) { if (isDeclined && _currentSessionId != null && _currentSessionId!.isNotEmpty) {
await _updateSessionStatus(_currentSessionId!, 3, 'Patient'); await _updateSessionStatus(_currentSessionId!, 3, 'Patient');
@ -184,10 +181,7 @@ Future<void> _endCallAndCleanup(String callId, {bool isDeclined = false}) async
await Utils.saveBoolFromPrefs(CacheConst.isAppOpenedFromCall, false); await Utils.saveBoolFromPrefs(CacheConst.isAppOpenedFromCall, false);
await Utils.saveStringFromPrefs(CacheConst.zoomRoomID, ''); await Utils.saveStringFromPrefs(CacheConst.zoomRoomID, '');
await Utils.saveStringFromPrefs(CacheConst.callTypeID, ''); await Utils.saveStringFromPrefs(CacheConst.callTypeID, '');
print('✅ Call and push notification cleanup completed successfully');
} catch (e) { } catch (e) {
print('❌ Error during call cleanup: $e');
// Force reset flags even if there's an error // Force reset flags even if there's an error
_isCallInProgress = false; _isCallInProgress = false;
_currentCallId = null; _currentCallId = null;
@ -200,27 +194,20 @@ Future<void> _endCallAndCleanup(String callId, {bool isDeclined = false}) async
// Helper method to update session status via API // Helper method to update session status via API
Future<void> _updateSessionStatus(String sessionId, int sessionStatus, String sessionEndedBy) async { Future<void> _updateSessionStatus(String sessionId, int sessionStatus, String sessionEndedBy) async {
try { try {
final apiClient = getIt.get<ApiClient>(); final apiClient = getIt.get<ApiClient>();
Map<String, dynamic> requestBody = { Map<String, dynamic> requestBody = {"Open_SessionID": sessionId, "SessionStatus": sessionStatus, "SessionEndedBy": sessionEndedBy};
"Open_SessionID": sessionId,
"SessionStatus": sessionStatus,
"SessionEndedBy": sessionEndedBy
};
await apiClient.post( await apiClient.post(
CHANGE_PATIENT_ER_SESSION, CHANGE_PATIENT_ER_SESSION,
body: requestBody, body: requestBody,
onSuccess: (response, statusCode, {messageStatus, errorMessage}) { onSuccess: (response, statusCode, {messageStatus, errorMessage}) {},
print('✅ Session status updated successfully: $response');
},
onFailure: (error, statusCode, {messageStatus, failureType}) { onFailure: (error, statusCode, {messageStatus, failureType}) {
print('❌ Failed to update session status: $error'); log('❌ Failed to update session status: $error');
}, },
); );
} catch (e) { } catch (e) {
print('❌ Exception updating session status: $e'); log('❌ Exception updating session status: $e');
} }
} }
@ -252,7 +239,7 @@ Future<void> openCallPage(BuildContext context) async {
// ); // );
// } // }
} catch (err) { } catch (err) {
print(err); log(err.toString());
// await PlatformExceptionAlertDialog( // await PlatformExceptionAlertDialog(
// exception: Exception(err), // exception: Exception(err),
// ).show(context); // ).show(context);
@ -351,7 +338,7 @@ class PushNotificationHandler {
// int seconds = 30, // int seconds = 30,
// }) async { // }) async {
// timeOutTimer = Timer(Duration(seconds: seconds), () async { // timeOutTimer = Timer(Duration(seconds: seconds), () async {
// print('🎈 example: timeOut'); // log('🎈 example: timeOut');
// final incomingCallerName = await voIPKit.getIncomingCallerName(); // final incomingCallerName = await voIPKit.getIncomingCallerName();
// voIPKit.unansweredIncomingCall( // voIPKit.unansweredIncomingCall(
// skipLocalNotification: false, // skipLocalNotification: false,
@ -368,19 +355,19 @@ class PushNotificationHandler {
if (Platform.isIOS) { if (Platform.isIOS) {
voIPKit.getVoIPToken().then((value) { voIPKit.getVoIPToken().then((value) {
print("🎈 APNS VOIP KIT TOKEN: $value"); log("🎈 APNS VOIP KIT TOKEN: $value");
Utils.saveStringFromPrefs(CacheConst.voipToken, value ?? ""); Utils.saveStringFromPrefs(CacheConst.voipToken, value ?? "");
// AppSharedPreferences().setString(APNS_TOKEN, value!); // AppSharedPreferences().setString(APNS_TOKEN, value!);
}); });
voIPKit.onDidUpdatePushToken = (String token) { voIPKit.onDidUpdatePushToken = (String token) {
print('🎈 example: onDidUpdatePushToken: $token'); log('🎈 example: onDidUpdatePushToken: $token');
}; };
voIPKit.onDidReceiveIncomingPush = ( voIPKit.onDidReceiveIncomingPush = (
Map<String, dynamic> payload, Map<String, dynamic> payload,
) async { ) async {
print('🎈 example: onDidReceiveIncomingPush $payload'); log('🎈 example: onDidReceiveIncomingPush $payload');
// _timeOut(); // _timeOut();
}; };
@ -389,20 +376,22 @@ class PushNotificationHandler {
String callerId, String callerId,
) async { ) async {
try { try {
print('🎈 example: onDidRejectIncomingCall $uuid - $callerId'); log('🎈 example: onDidRejectIncomingCall $uuid - $callerId');
timeOutTimer.cancel(); timeOutTimer.cancel();
// Cleanup on reject with API call // Cleanup on reject with API call
if (_currentCallId != null) { if (_currentCallId != null) {
await _endCallAndCleanup(_currentCallId!, isDeclined: true); await _endCallAndCleanup(_currentCallId!, isDeclined: true);
} }
} catch (err) {} } catch (err) {
log(err.toString());
}
}; };
voIPKit.onDidAcceptIncomingCall = ( voIPKit.onDidAcceptIncomingCall = (
String uuid, String uuid,
String callerId, String callerId,
) async { ) async {
print('🎈 example: onDidAcceptIncomingCall $uuid - $callerId'); log('🎈 example: onDidAcceptIncomingCall $uuid - $callerId');
await voIPKit.acceptIncomingCall(callerState: CallStateType.calling); await voIPKit.acceptIncomingCall(callerState: CallStateType.calling);
await voIPKit.callConnected(); await voIPKit.callConnected();
@ -432,12 +421,12 @@ class PushNotificationHandler {
if (Platform.isAndroid) { if (Platform.isAndroid) {
try { try {
final fcmToken = await FirebaseMessaging.instance.getToken().catchError((err) { final fcmToken = await FirebaseMessaging.instance.getToken().catchError((err) {
print(err); log(err);
}); });
if (fcmToken != null) onToken(fcmToken); if (fcmToken != null) onToken(fcmToken);
// } // }
} catch (ex) { } catch (ex) {
print("Notification Exception: $ex"); log("Notification Exception: $ex");
} }
FirebaseMessaging.onBackgroundMessage(backgroundMessageHandler); FirebaseMessaging.onBackgroundMessage(backgroundMessageHandler);
} }
@ -471,7 +460,7 @@ class PushNotificationHandler {
} catch (ex) {} } catch (ex) {}
FirebaseMessaging.onMessage.listen((RemoteMessage message) async { FirebaseMessaging.onMessage.listen((RemoteMessage message) async {
print("Firebase onMessage!!!"); log("Firebase onMessage!!!");
// showCallkitIncoming(); // showCallkitIncoming();
if (Platform.isIOS) { if (Platform.isIOS) {
await Future.delayed(Duration(milliseconds: 3000)).then((value) { await Future.delayed(Duration(milliseconds: 3000)).then((value) {
@ -483,7 +472,7 @@ class PushNotificationHandler {
}); });
FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) async { FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) async {
print("Firebase onMessageOpenedApp!!!"); log("Firebase onMessageOpenedApp!!!");
if (Platform.isIOS) { if (Platform.isIOS) {
await Future.delayed(Duration(milliseconds: 3000)).then((value) { await Future.delayed(Duration(milliseconds: 3000)).then((value) {
newMessage(message); newMessage(message);
@ -495,21 +484,21 @@ class PushNotificationHandler {
if (Platform.isIOS) { if (Platform.isIOS) {
FirebaseMessaging.instance.getAPNSToken().then((String? token) { FirebaseMessaging.instance.getAPNSToken().then((String? token) {
print("Push Notification getAPNSToken: ${token!}"); log("Push Notification getAPNSToken: ${token!}");
}).catchError((err) { }).catchError((err) {
print("Push Notification getAPNSToken ERR: ${err.toString()}"); log("Push Notification getAPNSToken ERR: ${err.toString()}");
}); });
} }
FirebaseMessaging.instance.getToken().then((String? token) { FirebaseMessaging.instance.getToken().then((String? token) {
print("Push Notification getToken: ${token!}"); log("Push Notification getToken: ${token!}");
onToken(token!); onToken(token!);
}).catchError((err) { }).catchError((err) {
print(err); log(err);
}); });
FirebaseMessaging.instance.onTokenRefresh.listen((fcm_token) { FirebaseMessaging.instance.onTokenRefresh.listen((fcm_token) {
print("Push Notification onTokenRefresh: $fcm_token"); log("Push Notification onTokenRefresh: $fcm_token");
onToken(fcm_token); onToken(fcm_token);
}); });
@ -525,11 +514,11 @@ class PushNotificationHandler {
} }
newMessage(RemoteMessage remoteMessage) async { newMessage(RemoteMessage remoteMessage) async {
print("Remote Message: ${remoteMessage.data}"); log("Remote Message: ${remoteMessage.data}");
if (remoteMessage.data.isEmpty) { if (remoteMessage.data.isEmpty) {
return; 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) { if (remoteMessage.data['is_call'] == 'true' || remoteMessage.data['is_call'] == true) {
_incomingCall(remoteMessage.data); _incomingCall(remoteMessage.data);
// showCallkitIncoming(); // showCallkitIncoming();
@ -551,7 +540,7 @@ class PushNotificationHandler {
} }
onToken(String token) async { onToken(String token) async {
print("Push Notification Token: $token"); log("Push Notification Token: $token");
await Utils.saveStringFromPrefs(CacheConst.pushToken, token); await Utils.saveStringFromPrefs(CacheConst.pushToken, token);
} }
@ -573,11 +562,11 @@ class PushNotificationHandler {
// Permission.audio, // Permission.audio,
// Permission.microphone, // Permission.microphone,
].request(); ].request();
print("=-=-=-=-=-=-=-=-=-=-"); log("=-=-=-=-=-=-=-=-=-=-");
print(statuses[Permission.notification]); log(statuses[Permission.notification].toString());
} }
} catch (_) { } catch (_) {
debugPrint(_.toString()); log(_.toString());
} }
} }
} }

@ -1,4 +1,3 @@
import 'dart:developer';
import 'dart:math' as math; import 'dart:math' as math;
import 'package:flutter/material.dart'; // These are the Viewport values of your Figma Design. import 'package:flutter/material.dart'; // These are the Viewport values of your Figma Design.
@ -26,8 +25,14 @@ extension ResponsiveExtension on num {
/// Check if device is likely a foldable /// Check if device is likely a foldable
bool get _isFoldable { bool get _isFoldable {
double aspectRatio = _screenWidth / _screenHeight; double aspectRatio = _screenWidth / _screenHeight;
// Foldable devices typically have aspect ratios close to 1:1 when unfolded double shorterSide = _screenWidth < _screenHeight ? _screenWidth : _screenHeight;
return (aspectRatio > 0.9 && aspectRatio < 1.1) && (_screenWidth > 700 || _screenHeight > 700);
// Foldable devices (unfolded) typically have:
// - Shorter side > 600 logical pixels (to exclude regular phones)
// - Aspect ratio between 0.80 and 0.92 (almost square, like Galaxy Z Fold)
// Galaxy Z Fold 5: 1812x2176 physical, ~690x796 logical = 0.866 aspect ratio
// Regular phones: typically 375-430 width, aspect ratio 0.45-0.55
return (shorterSide > 600) && (aspectRatio > 0.80 && aspectRatio < 0.92);
} }
/// Scale text size - enhanced for foldable devices /// Scale text size - enhanced for foldable devices
@ -53,7 +58,7 @@ extension ResponsiveExtension on num {
double get w { double get w {
double baseScale = (this * _screenWidth) / figmaDesignWidth; double baseScale = (this * _screenWidth) / figmaDesignWidth;
if (_isFoldable|| isTablet ) { if (_isFoldable || isTablet) {
// For foldables, use more conservative width scaling // For foldables, use more conservative width scaling
double scale = _screenWidth / figmaDesignWidthTF; double scale = _screenWidth / figmaDesignWidthTF;
scale = scale.clamp(0.8, 1.4); scale = scale.clamp(0.8, 1.4);
@ -67,7 +72,7 @@ extension ResponsiveExtension on num {
double get h { double get h {
double baseScale = (this * _screenHeight) / figmaDesignHeight; double baseScale = (this * _screenHeight) / figmaDesignHeight;
if (_isFoldable || isTablet ) { if (_isFoldable || isTablet) {
// For foldables, use height-based scaling but with constraints // For foldables, use height-based scaling but with constraints
double scale = (_screenHeight / figmaDesignHeightTF).clamp(0.8, 1.4); double scale = (_screenHeight / figmaDesignHeightTF).clamp(0.8, 1.4);
return this * scale; return this * scale;
@ -225,10 +230,16 @@ class SizeUtils {
deviceType = DeviceType.mobile; deviceType = DeviceType.mobile;
} }
log("longerSide: $longerSide"); debugPrint("============ Device Detection ============");
log("shorterSide: $shorterSide"); debugPrint("longerSide: $longerSide");
log("isTablet: $isTablet"); debugPrint("shorterSide: $shorterSide");
log("isFoldable: $isFoldable"); debugPrint("width: $width");
debugPrint("height: $height");
debugPrint("deviceType: $deviceType");
debugPrint("isTablet: $isTablet");
debugPrint("isFoldable: $isFoldable");
debugPrint("aspectRatio: ${width / height}");
debugPrint("==========================================");
} }
} }
@ -241,6 +252,12 @@ bool get isDesktop => SizeUtils.deviceType == DeviceType.desktop;
bool get isFoldable { bool get isFoldable {
double aspectRatio = SizeUtils.width / SizeUtils.height; double aspectRatio = SizeUtils.width / SizeUtils.height;
// Foldable devices typically have aspect ratios close to 1:1 when unfolded double shorterSide = SizeUtils.width < SizeUtils.height ? SizeUtils.width : SizeUtils.height;
return (aspectRatio > 0.9 && aspectRatio < 1.1) && (SizeUtils.width > 700 || SizeUtils.height > 700);
// Foldable devices (unfolded) typically have:
// - Shorter side > 600 logical pixels (to exclude regular phones)
// - Aspect ratio between 0.80 and 0.92 (almost square, like Galaxy Z Fold)
// Galaxy Z Fold 5: 1812x2176 physical, ~690x796 logical = 0.866 aspect ratio
// Regular phones: typically 375-430 width, aspect ratio 0.45-0.55
return (shorterSide > 600) && (aspectRatio > 0.80 && aspectRatio < 0.92);
} }

@ -115,12 +115,14 @@ extension EmailValidator on String {
bool isCenter = false, bool isCenter = false,
double? height, double? height,
double? letterSpacing, double? letterSpacing,
TextOverflow? textOverflow,
int maxLine = 0}) => int maxLine = 0}) =>
Text( Text(
this, this,
textAlign: isCenter ? TextAlign.center : textAlignment, textAlign: isCenter ? TextAlign.center : textAlignment,
maxLines: (maxLine > 0) ? maxLine : null, maxLines: (maxLine > 0) ? maxLine : null,
style: TextStyle( style: TextStyle(
overflow: textOverflow,
fontSize: 12.f, fontSize: 12.f,
fontWeight: fontWeight ?? (isBold ? FontWeight.bold : FontWeight.normal), fontWeight: fontWeight ?? (isBold ? FontWeight.bold : FontWeight.normal),
color: color ?? AppColors.blackColor, color: color ?? AppColors.blackColor,

@ -3,7 +3,7 @@ import 'dart:math';
import 'package:hmg_patient_app_new/core/common_models/data_points.dart'; import 'package:hmg_patient_app_new/core/common_models/data_points.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'model/Vitals.dart'; import 'model/vitals_data_model.dart';
enum Durations { enum Durations {
daily("daily"), daily("daily"),

@ -1,18 +1,17 @@
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:health/health.dart'; import 'package:health/health.dart';
import 'package:hmg_patient_app_new/core/common_models/data_points.dart';
import 'package:hmg_patient_app_new/core/common_models/smart_watch.dart'; import 'package:hmg_patient_app_new/core/common_models/smart_watch.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/core/utils/date_util.dart'; import 'package:hmg_patient_app_new/core/utils/date_util.dart';
import 'package:hmg_patient_app_new/core/utils/loading_utils.dart';
import 'package:hmg_patient_app_new/features/smartwatch_health_data/health_service.dart'; import 'package:hmg_patient_app_new/features/smartwatch_health_data/health_service.dart';
import 'package:hmg_patient_app_new/presentation/smartwatches/activity_detail.dart';
import 'package:hmg_patient_app_new/presentation/smartwatches/smart_watches_health_data_screen.dart';
import 'package:hmg_patient_app_new/services/navigation_service.dart';
import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart'; import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart';
import '../../core/common_models/data_points.dart'; import 'health_data_transformations.dart';
import '../../core/dependencies.dart'; import 'model/vitals_data_model.dart';
import '../../presentation/smartwatches/activity_detail.dart' show ActivityDetails;
import '../../presentation/smartwatches/smart_watch_activity.dart' show SmartWatchActivity;
import '../../services/navigation_service.dart' show NavigationService;
import 'HealthDataTransformation.dart';
import 'model/Vitals.dart';
class HealthProvider with ChangeNotifier { class HealthProvider with ChangeNotifier {
final HealthService _healthService = HealthService(); final HealthService _healthService = HealthService();
@ -22,7 +21,8 @@ class HealthProvider with ChangeNotifier {
String selectedTimeRange = '7D'; String selectedTimeRange = '7D';
int selectedTabIndex = 0; int selectedTabIndex = 0;
SmartWatchTypes? selectedWatchType ; SmartWatchTypes? selectedWatchType;
String selectedWatchURL = 'assets/images/png/smartwatches/apple-watch-5.jpg'; String selectedWatchURL = 'assets/images/png/smartwatches/apple-watch-5.jpg';
HealthDataTransformation healthDataTransformation = HealthDataTransformation(); HealthDataTransformation healthDataTransformation = HealthDataTransformation();
@ -90,7 +90,7 @@ class HealthProvider with ChangeNotifier {
healthData[type] = data; healthData[type] = data;
notifyListeners(); notifyListeners();
} catch (e) { } catch (e) {
print('Error refreshing metric $type: $e'); debugPrint('Error refreshing metric $type: $e');
} }
} }
@ -129,14 +129,12 @@ class HealthProvider with ChangeNotifier {
await getVitals(); await getVitals();
// LoaderBottomSheet.hideLoader(); // LoaderBottomSheet.hideLoader();
// await Future.delayed(Duration(seconds: 5)); // await Future.delayed(Duration(seconds: 5));
getIt.get<NavigationService>().pushPage(page: SmartWatchActivity()); getIt.get<NavigationService>().pushPage(page: SmartWatchesHealthDataScreen());
print('Device initialized successfully');
} }
notifyListeners(); notifyListeners();
} }
Future<void> getVitals() async { Future<void> getVitals() async {
final result = await _healthService.getVitals(); final result = await _healthService.getVitals();
vitals = result; vitals = result;
LoaderBottomSheet.hideLoader(); LoaderBottomSheet.hideLoader();
@ -186,15 +184,17 @@ class HealthProvider with ChangeNotifier {
} }
selectedData = yearly = healthDataTransformation.transformVitalsToDataPoints(vitals!, Durations.yearly.value, selectedSection); selectedData = yearly = healthDataTransformation.transformVitalsToDataPoints(vitals!, Durations.yearly.value, selectedSection);
break; break;
default:
{}
;
} }
notifyListeners(); notifyListeners();
} }
void navigateToDetails(String value, {required String sectionName, required String uom}) { void navigateToDetails(String value, {required String sectionName, required String uom}) {
getIt.get<NavigationService>().pushPage(page: ActivityDetails(selectedActivity: value, sectionName:sectionName, uom: uom,)); getIt.get<NavigationService>().pushPage(
page: ActivityDetails(
selectedActivity: value,
sectionName: sectionName,
uom: uom,
));
} }
void saveSelectedSection(String value) { void saveSelectedSection(String value) {
@ -243,7 +243,6 @@ class HealthProvider with ChangeNotifier {
count++; count++;
} }
}); });
print("total count is $count and total is $total");
averageValue = count > 0 ? total / count : null; averageValue = count > 0 ? total / count : null;
notifyListeners(); notifyListeners();
} }
@ -261,7 +260,7 @@ class HealthProvider with ChangeNotifier {
String firstNonEmptyValue(List<Vitals> dataPoints) { String firstNonEmptyValue(List<Vitals> dataPoints) {
try { try {
return dataPoints.firstWhere((dp) => dp.value != null && dp.value!.trim().isNotEmpty).value; return dataPoints.firstWhere((dp) => dp.value.trim().isNotEmpty).value;
} catch (e) { } catch (e) {
return "0"; // no non-empty value found return "0"; // no non-empty value found
} }

@ -5,7 +5,7 @@ import 'dart:io';
import 'package:health/health.dart'; import 'package:health/health.dart';
import 'package:hmg_patient_app_new/core/common_models/smart_watch.dart'; import 'package:hmg_patient_app_new/core/common_models/smart_watch.dart';
import 'package:hmg_patient_app_new/features/smartwatch_health_data/model/Vitals.dart'; import 'package:hmg_patient_app_new/features/smartwatch_health_data/model/vitals_data_model.dart';
import 'package:hmg_patient_app_new/features/smartwatch_health_data/watch_connectors/create_watch_helper.dart'; import 'package:hmg_patient_app_new/features/smartwatch_health_data/watch_connectors/create_watch_helper.dart';
import 'package:hmg_patient_app_new/features/smartwatch_health_data/watch_connectors/watch_helper.dart'; import 'package:hmg_patient_app_new/features/smartwatch_health_data/watch_connectors/watch_helper.dart';
import 'package:permission_handler/permission_handler.dart'; import 'package:permission_handler/permission_handler.dart';

@ -1,5 +1,5 @@
class Vitals { class Vitals {
String value; String value;
final String timestamp; final String timestamp;
final String unitOfMeasure; final String unitOfMeasure;
@ -16,11 +16,6 @@ class Vitals {
unitOfMeasure: map['uom'] ?? "", unitOfMeasure: map['uom'] ?? "",
); );
} }
toString(){
return "{\"value\": \"$value\", \"timeStamp\": \"$timestamp\", \"uom\": \"$unitOfMeasure\"}";
}
} }
class VitalsWRTType { class VitalsWRTType {
@ -31,15 +26,21 @@ class VitalsWRTType {
final List<Vitals> activity; final List<Vitals> activity;
final List<Vitals> bodyOxygen; final List<Vitals> bodyOxygen;
final List<Vitals> bodyTemperature; final List<Vitals> bodyTemperature;
double maxHeartRate = double.negativeInfinity; double maxHeartRate = double.negativeInfinity;
double maxSleep = double.negativeInfinity; double maxSleep = double.negativeInfinity;
double maxStep= double.negativeInfinity; double maxStep = double.negativeInfinity;
double maxActivity = double.negativeInfinity; double maxActivity = double.negativeInfinity;
double maxBloodOxygen = double.negativeInfinity; double maxBloodOxygen = double.negativeInfinity;
double maxBodyTemperature = double.negativeInfinity; double maxBodyTemperature = double.negativeInfinity;
VitalsWRTType({required this.distance, required this.bodyOxygen, required this.bodyTemperature, required this.heartRate, required this.sleep, required this.step, required this.activity}); VitalsWRTType(
{required this.distance,
required this.bodyOxygen,
required this.bodyTemperature,
required this.heartRate,
required this.sleep,
required this.step,
required this.activity});
factory VitalsWRTType.fromMap(Map<dynamic, dynamic> map) { factory VitalsWRTType.fromMap(Map<dynamic, dynamic> map) {
List<Vitals> activity = []; List<Vitals> activity = [];
@ -82,16 +83,23 @@ class VitalsWRTType {
map["distance"].forEach((element) { map["distance"].forEach((element) {
element["uom"] = "km"; element["uom"] = "km";
var data = Vitals.fromMap(element); var data = Vitals.fromMap(element);
data.value = (double.parse(data.value)/1000).toStringAsFixed(2); data.value = (double.parse(data.value) / 1000).toStringAsFixed(2);
distance.add(data); distance.add(data);
}); });
return VitalsWRTType(bodyTemperature: bodyTemperature, bodyOxygen: bodyOxygen, heartRate: heartRate, sleep: sleeps, step: steps, activity: activity, distance: distance); return VitalsWRTType(
bodyTemperature: bodyTemperature,
bodyOxygen: bodyOxygen,
heartRate: heartRate,
sleep: sleeps,
step: steps,
activity: activity,
distance: distance);
} }
Map<String, List<Vitals>> getVitals() { Map<String, List<Vitals>> getVitals() {
return { return {
"heartRate": heartRate , "heartRate": heartRate,
"sleep": sleep, "sleep": sleep,
"steps": step, "steps": step,
"activity": activity, "activity": activity,

@ -6,7 +6,7 @@ import 'package:health/health.dart';
import 'package:hmg_patient_app_new/features/smartwatch_health_data/watch_connectors/watch_helper.dart' show WatchHelper; import 'package:hmg_patient_app_new/features/smartwatch_health_data/watch_connectors/watch_helper.dart' show WatchHelper;
import 'package:permission_handler/permission_handler.dart'; import 'package:permission_handler/permission_handler.dart';
import '../model/Vitals.dart'; import '../model/vitals_data_model.dart';
class HealthConnectHelper extends WatchHelper { class HealthConnectHelper extends WatchHelper {
final Health health = Health(); final Health health = Health();

@ -17,6 +17,7 @@ abstract class SymptomsCheckerRepo {
Future<Either<Failure, GenericApiModel<SymptomsUserDetailsResponseModel>>> getUserDetails({ Future<Either<Failure, GenericApiModel<SymptomsUserDetailsResponseModel>>> getUserDetails({
required String userName, required String userName,
required String password, required String password,
String? fileNo,
}); });
Future<Either<Failure, GenericApiModel<BodySymptomResponseModel>>> getBodySymptomsByName({ Future<Either<Failure, GenericApiModel<BodySymptomResponseModel>>> getBodySymptomsByName({
@ -70,8 +71,17 @@ class SymptomsCheckerRepoImp implements SymptomsCheckerRepo {
Future<Either<Failure, GenericApiModel<SymptomsUserDetailsResponseModel>>> getUserDetails({ Future<Either<Failure, GenericApiModel<SymptomsUserDetailsResponseModel>>> getUserDetails({
required String userName, required String userName,
required String password, required String password,
String? fileNo,
}) async { }) async {
Map<String, dynamic> body = {"userName": userName, "password": password}; Map<String, dynamic> body = {
"userName": userName,
"password": password,
};
// Add fileNo only if provided (user is logged in)
if (fileNo != null && fileNo.isNotEmpty) {
body["fileNo"] = fileNo;
}
try { try {
GenericApiModel<SymptomsUserDetailsResponseModel>? apiResponse; GenericApiModel<SymptomsUserDetailsResponseModel>? apiResponse;

@ -76,6 +76,10 @@ class SymptomsCheckerViewModel extends ChangeNotifier {
final List<Map<String, String>> _triageEvidenceList = []; // Store triage evidence with proper format final List<Map<String, String>> _triageEvidenceList = []; // Store triage evidence with proper format
int _triageQuestionCount = 0; // Track number of triage questions answered int _triageQuestionCount = 0; // Track number of triage questions answered
// For type=1 questions: single selection across all items
String? _selectedSingleItemId; // Store which item was selected
int? _selectedSingleChoiceIndex; // Store which choice was selected
// Selected risk factors tracking // Selected risk factors tracking
final Set<String> _selectedRiskFactorIds = {}; final Set<String> _selectedRiskFactorIds = {};
@ -170,12 +174,35 @@ class SymptomsCheckerViewModel extends ChangeNotifier {
return _selectedTriageChoicesByItemId[itemId]; return _selectedTriageChoicesByItemId[itemId];
} }
/// Check if current question type is single selection (type=1)
bool get isTriageQuestionSingleSelection => currentTriageQuestion?.type == 1;
/// Check if current question type is multi-item selection (type=2)
bool get isTriageQuestionMultiItem => currentTriageQuestion?.type == 2;
/// For type=1 questions: check if a specific item-choice combination is selected
bool isTriageSingleOptionSelected(String itemId, int choiceIndex) {
return _selectedSingleItemId == itemId && _selectedSingleChoiceIndex == choiceIndex;
}
/// Get selected item ID for type=1 questions
String? get selectedSingleItemId => _selectedSingleItemId;
/// Get selected choice index for type=1 questions
int? get selectedSingleChoiceIndex => _selectedSingleChoiceIndex;
/// Check if all items in current question have been answered /// Check if all items in current question have been answered
bool get areAllTriageItemsAnswered { bool get areAllTriageItemsAnswered {
if (currentTriageQuestion?.items == null || currentTriageQuestion!.items!.isEmpty) { if (currentTriageQuestion?.items == null || currentTriageQuestion!.items!.isEmpty) {
return false; return false;
} }
// Type 1: Single selection mode - check if any option is selected
if (isTriageQuestionSingleSelection) {
return _selectedSingleItemId != null && _selectedSingleChoiceIndex != null;
}
// Type 2: Multi-item selection mode - check if all items have answers
// Check if we have an answer for each item // Check if we have an answer for each item
for (var item in currentTriageQuestion!.items!) { for (var item in currentTriageQuestion!.items!) {
if (item.id != null && !_selectedTriageChoicesByItemId.containsKey(item.id)) { if (item.id != null && !_selectedTriageChoicesByItemId.containsKey(item.id)) {
@ -277,7 +304,7 @@ class SymptomsCheckerViewModel extends ChangeNotifier {
} }
} }
toggleZoomOut(){ toggleZoomOut() {
if (_currentZoomScale > _minZoomScale) { if (_currentZoomScale > _minZoomScale) {
_currentZoomScale = (_currentZoomScale - _zoomStep).clamp(_minZoomScale, _maxZoomScale); _currentZoomScale = (_currentZoomScale - _zoomStep).clamp(_minZoomScale, _maxZoomScale);
notifyListeners(); notifyListeners();
@ -846,7 +873,21 @@ class SymptomsCheckerViewModel extends ChangeNotifier {
/// Select a choice for a specific item (for multi-item questions) /// Select a choice for a specific item (for multi-item questions)
void selectTriageChoiceForItem(String itemId, int choiceIndex) { void selectTriageChoiceForItem(String itemId, int choiceIndex) {
_selectedTriageChoicesByItemId[itemId] = choiceIndex; // Type 1: Single selection mode - only one option can be selected across all items
if (isTriageQuestionSingleSelection) {
// If same option clicked again, deselect it
if (_selectedSingleItemId == itemId && _selectedSingleChoiceIndex == choiceIndex) {
_selectedSingleItemId = null;
_selectedSingleChoiceIndex = null;
} else {
// Select new option, clear previous selection
_selectedSingleItemId = itemId;
_selectedSingleChoiceIndex = choiceIndex;
}
} else {
// Type 2: Multi-item selection mode - each item can have one selected option
_selectedTriageChoicesByItemId[itemId] = choiceIndex;
}
notifyListeners(); notifyListeners();
} }
@ -854,6 +895,8 @@ class SymptomsCheckerViewModel extends ChangeNotifier {
void resetTriageChoice() { void resetTriageChoice() {
_selectedTriageChoiceIndex = null; _selectedTriageChoiceIndex = null;
_selectedTriageChoicesByItemId.clear(); _selectedTriageChoicesByItemId.clear();
_selectedSingleItemId = null;
_selectedSingleChoiceIndex = null;
_triageQuestionCount++; // Increment question count _triageQuestionCount++; // Increment question count
notifyListeners(); notifyListeners();
} }
@ -1018,12 +1061,17 @@ class SymptomsCheckerViewModel extends ChangeNotifier {
Future<void> getSymptomsUserDetails({ Future<void> getSymptomsUserDetails({
required String userName, required String userName,
required String password, required String password,
String? fileNo,
Function()? onSuccess, Function()? onSuccess,
Function(String)? onError, Function(String)? onError,
}) async { }) async {
isBodySymptomsLoading = true; isBodySymptomsLoading = true;
notifyListeners(); notifyListeners();
final result = await symptomsCheckerRepo.getUserDetails(userName: userName, password: password); final result = await symptomsCheckerRepo.getUserDetails(
userName: userName,
password: password,
fileNo: fileNo,
);
result.fold( result.fold(
(failure) async { (failure) async {

@ -262,7 +262,10 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
Expanded( Expanded(
child: CollapsingListView( child: CollapsingListView(
title: LocaleKeys.appointmentDetails.tr(context: context), 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.setSelectedFeedbackType(FeedbackType(id: 1, nameEN: "Complaint for appointment", nameAR: 'شكوى على موعد'));
contactUsViewModel.setPatientFeedbackSelectedAppointment(widget.patientAppointmentHistoryResponseModel); contactUsViewModel.setPatientFeedbackSelectedAppointment(widget.patientAppointmentHistoryResponseModel);
@ -280,8 +283,8 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
children: [ children: [
AppointmentDoctorCard( AppointmentDoctorCard(
// renderWidgetForERDisplay: ((widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment ?? false) || // renderWidgetForERDisplay: ((widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment ?? false) ||
renderWidgetForERDisplay: renderWidgetForERDisplay: ((widget.patientAppointmentHistoryResponseModel.isExecludeDoctor ?? false) ||
((widget.patientAppointmentHistoryResponseModel.isExecludeDoctor ?? false) || !Utils.isClinicAllowedForRebook(widget.patientAppointmentHistoryResponseModel.clinicID)), !Utils.isClinicAllowedForRebook(widget.patientAppointmentHistoryResponseModel.clinicID)),
patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel, patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel,
onAskDoctorTap: () async { onAskDoctorTap: () async {
LoaderBottomSheet.showLoader(loadingText: LocaleKeys.checkingDoctorAvailability.tr(context: context)); LoaderBottomSheet.showLoader(loadingText: LocaleKeys.checkingDoctorAvailability.tr(context: context));
@ -361,10 +364,6 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
isFullScreen: false, isFullScreen: false,
isCloseButtonVisible: true, isCloseButtonVisible: true,
); );
// var isEventAddedOrRemoved = await CalenderUtilsNew.instance.checkAndRemove( id:"${widget.patientAppointmentHistoryResponseModel.appointmentNo}", );
// setState(() {
// myAppointmentsViewModel.setAppointmentReminder(isEventAddedOrRemoved, widget.patientAppointmentHistoryResponseModel);
// });
}, },
onRescheduleTap: () async { onRescheduleTap: () async {
openDoctorScheduleCalendar(); openDoctorScheduleCalendar();
@ -416,14 +415,17 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
Row( Row(
mainAxisSize: MainAxisSize.max, mainAxisSize: MainAxisSize.max,
children: [ 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), SizedBox(width: 8.w),
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
spacing: 4.h, spacing: 4.h,
children: [ children: [
LocaleKeys.setReminder.tr(context: context).toText13(isBold: true), 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(), const Spacer(),
@ -438,48 +440,53 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
inactiveCircleColor: AppColors.greyTextColor, inactiveCircleColor: AppColors.greyTextColor,
activeIconColor: AppColors.bgGreenColor, activeIconColor: AppColors.bgGreenColor,
inactiveIconColor: AppColors.greyTextColor, inactiveIconColor: AppColors.greyTextColor,
activeIcon: Utils.buildSvgWithAssets(icon: AppAssets.bell, iconColor: AppColors.whiteColor, width: 12.w, height: 12.h), activeIcon:
inactiveIcon: Utils.buildSvgWithAssets(icon: AppAssets.bell, iconColor: AppColors.whiteColor, width: 12.w, height: 12.h), 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 { onChanged: (newValue) async {
CalenderUtilsNew calender = CalenderUtilsNew.instance; CalenderUtilsNew calender = CalenderUtilsNew.instance;
bool isEventAddedOrRemoved = false; bool isEventAddedOrRemoved = false;
if (newValue == true) { if (newValue == true) {
DateTime startDate = DateTime.now(); 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 // Show reminder bottom sheet and check if permission was granted
bool permissionGranted = await BottomSheetUtils().showReminderBottomSheet( bool permissionGranted = await BottomSheetUtils().showReminderBottomSheet(
context, context,
endDate, endDate,
widget.patientAppointmentHistoryResponseModel.doctorNameObj ?? "", widget.patientAppointmentHistoryResponseModel.doctorNameObj ?? "",
"${widget.patientAppointmentHistoryResponseModel.appointmentNo}" ?? "", "${widget.patientAppointmentHistoryResponseModel.appointmentNo}" ?? "",
"", "",
"", "",
title: "Appointment with ${widget.patientAppointmentHistoryResponseModel.doctorNameObj}", title: "Appointment with ${widget.patientAppointmentHistoryResponseModel.doctorNameObj}",
description: description:
"${widget.patientAppointmentHistoryResponseModel.doctorNameObj} will be having an appointment on ${widget.patientAppointmentHistoryResponseModel.appointmentDate}", "${widget.patientAppointmentHistoryResponseModel.doctorNameObj} will be having an appointment on ${widget.patientAppointmentHistoryResponseModel.appointmentDate}",
onSuccess: () { onSuccess: () {
setState(() { setState(() {
myAppointmentsViewModel.setAppointmentReminder(newValue, widget.patientAppointmentHistoryResponseModel); myAppointmentsViewModel.setAppointmentReminder(
}); newValue, widget.patientAppointmentHistoryResponseModel);
}, });
isMultiAllowed: true, },
onMultiDateSuccess: (int selectedIndex) async { isMultiAllowed: true,
isEventAddedOrRemoved = await calender.createOrUpdateEvent( onMultiDateSuccess: (int selectedIndex) async {
title: isEventAddedOrRemoved = await calender.createOrUpdateEvent(
"Appointment Reminder with ${widget.patientAppointmentHistoryResponseModel.doctorNameObj} on ${DateUtil.convertStringToDate(widget.patientAppointmentHistoryResponseModel.appointmentDate)}, Appointment #${widget.patientAppointmentHistoryResponseModel.appointmentNo}", title:
description: "Appointment Reminder with ${widget.patientAppointmentHistoryResponseModel.doctorNameObj} on ${DateUtil.convertStringToDate(widget.patientAppointmentHistoryResponseModel.appointmentDate)}, Appointment #${widget.patientAppointmentHistoryResponseModel.appointmentNo}",
"Appointment Reminder with ${widget.patientAppointmentHistoryResponseModel.doctorNameObj} in ${widget.patientAppointmentHistoryResponseModel.projectName}", description:
scheduleDateTime: DateUtil.convertStringToDate(widget.patientAppointmentHistoryResponseModel.appointmentDate), "Appointment Reminder with ${widget.patientAppointmentHistoryResponseModel.doctorNameObj} in ${widget.patientAppointmentHistoryResponseModel.projectName}",
eventId: "${widget.patientAppointmentHistoryResponseModel.appointmentNo}", scheduleDateTime:
location: '', DateUtil.convertStringToDate(widget.patientAppointmentHistoryResponseModel.appointmentDate),
reminderMinutes: selectedIndex); eventId: "${widget.patientAppointmentHistoryResponseModel.appointmentNo}",
setState(() { location: '',
myAppointmentsViewModel.setAppointmentReminder(isEventAddedOrRemoved, widget.patientAppointmentHistoryResponseModel); reminderMinutes: selectedIndex);
}); setState(() {
}, myAppointmentsViewModel.setAppointmentReminder(
isForAppointment: true isEventAddedOrRemoved, widget.patientAppointmentHistoryResponseModel);
); });
},
isForAppointment: true);
// If permission was not granted, revert the switch back to OFF // If permission was not granted, revert the switch back to OFF
if (!permissionGranted) { if (!permissionGranted) {
@ -490,72 +497,17 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
id: "${widget.patientAppointmentHistoryResponseModel.appointmentNo}", id: "${widget.patientAppointmentHistoryResponseModel.appointmentNo}",
); );
setState(() { setState(() {
myAppointmentsViewModel.setAppointmentReminder(!isEventAddedOrRemoved, widget.patientAppointmentHistoryResponseModel); myAppointmentsViewModel.setAppointmentReminder(
!isEventAddedOrRemoved, widget.patientAppointmentHistoryResponseModel);
}); });
} }
}, },
) )
// Switch(
// activeThumbColor: AppColors.successColor,
// // activeTrackColor: AppColors.successColor.withValues(alpha: .15),
// value: widget.patientAppointmentHistoryResponseModel.hasReminder!,
// onChanged: (newValue) async {
// CalenderUtilsNew calender = CalenderUtilsNew.instance;
// bool isEventAddedOrRemoved = false;
// if(newValue == true){
// DateTime startDate = DateTime.now();
// DateTime endDate = DateUtil.convertStringToDate(widget
// .patientAppointmentHistoryResponseModel.appointmentDate);
// 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);
// });
// },
// );
// }else {
// isEventAddedOrRemoved = await calender.checkAndRemove( id:"${widget.patientAppointmentHistoryResponseModel.appointmentNo}", );
// setState(() {
// myAppointmentsViewModel.setAppointmentReminder(!isEventAddedOrRemoved, widget.patientAppointmentHistoryResponseModel);
// });
// }
//
//
// },
// ),
], ],
).paddingSymmetrical(16.w, 0) ).paddingSymmetrical(16.w, 0)
], ],
), ),
), ),
SizedBox(height: 16.h), SizedBox(height: 16.h),
!AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel) !AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel)
? Column( ? Column(
@ -581,53 +533,15 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
LocaleKeys.appointmentStatus.tr(context: context).toText16(isBold: true), LocaleKeys.appointmentStatus.tr(context: context).toText16(isBold: true),
SizedBox(height: 4.h), SizedBox(height: 4.h),
(!AppointmentType.isConfirmed(widget.patientAppointmentHistoryResponseModel) (!AppointmentType.isConfirmed(widget.patientAppointmentHistoryResponseModel)
? LocaleKeys.notConfirmed.tr(context: context).toText12(color: AppColors.primaryRedColor, isBold: true) ? LocaleKeys.notConfirmed
: LocaleKeys.confirmed.tr(context: context).toText12(color: AppColors.successColor, isBold: true)), .tr(context: context)
.toText12(color: AppColors.primaryRedColor, isBold: true)
: LocaleKeys.confirmed
.tr(context: context)
.toText12(color: AppColors.successColor, isBold: true)),
SizedBox(height: 16.h), SizedBox(height: 16.h),
], ],
), ),
// ((!AppointmentType.isConfirmed(widget.patientAppointmentHistoryResponseModel) && widget.patientAppointmentHistoryResponseModel.nextAction != 10)
// ? CustomButton(
// text: LocaleKeys.confirm.tr(),
// onPressed: () async {
// LoaderBottomSheet.showLoader(loadingText: LocaleKeys.confirmingAppointmentPleaseWait.tr(context: context));
// await myAppointmentsViewModel.confirmAppointment(
// patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel,
// onSuccess: (apiResponse) {
// LoaderBottomSheet.hideLoader();
// myAppointmentsViewModel.setIsAppointmentDataToBeLoaded(true);
// myAppointmentsViewModel.initAppointmentsViewModel();
// // myAppointmentsViewModel.getPatientAppointments(true, false);
// showCommonBottomSheetWithoutHeight(
// title: "",
// context,
// child: Utils.getSuccessWidget(loadingText: LocaleKeys.appointmentConfirmedSuccessfully.tr(context: context)),
// callBackFunc: () {
// Navigator.pushAndRemoveUntil(
// context,
// CustomPageRoute(
// page: LandingNavigation(),
// ),
// (r) => false);
// },
// isFullScreen: false,
// isCloseButtonVisible: false,
// isAutoDismiss: true
// );
// });
// },
// backgroundColor: AppColors.successColor,
// borderColor: AppColors.successColor,
// textColor: Colors.white,
// fontSize: 14.f,
// isBold: true,
// borderRadius: 12.r,
// height: 40.h,
// icon: AppAssets.confirm_appointment_icon,
// iconColor: Colors.white,
// iconSize: 16.h,
// )
// : SizedBox.shrink())
], ],
), ),
//TODO Add countdown timer in case of LiveCare Appointment //TODO Add countdown timer in case of LiveCare Appointment
@ -640,7 +554,9 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ 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 +564,17 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
) )
: Stack( : Stack(
children: [ children: [
ClipRRect( SizedBox(
clipBehavior: Clip.hardEdge, width: double.infinity,
borderRadius: BorderRadius.circular(24.r), child: ClipRRect(
// Todo: what is this???? Api Key??? 😲 clipBehavior: Clip.hardEdge,
child: Image.network( borderRadius: BorderRadius.circular(24.r),
"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}", // Todo: what is this???? Api Key??? 😲
fit: BoxFit.contain, 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( Positioned(
@ -663,7 +583,8 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
width: MediaQuery.of(context).size.width - 85.w, width: MediaQuery.of(context).size.width - 85.w,
child: CustomButton( child: CustomButton(
onPressed: () async { onPressed: () async {
if (widget.patientAppointmentHistoryResponseModel.projectID == 130 || widget.patientAppointmentHistoryResponseModel.projectID == 120) { if (widget.patientAppointmentHistoryResponseModel.projectID == 130 ||
widget.patientAppointmentHistoryResponseModel.projectID == 120) {
showDirectionsBottomSheet(); showDirectionsBottomSheet();
} else { } else {
await MapLauncher.showMarker( await MapLauncher.showMarker(
@ -683,7 +604,9 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
}, },
text: LocaleKeys.getDirections.tr(context: context), text: LocaleKeys.getDirections.tr(context: context),
backgroundColor: AppColors.bookAppointment.withValues(alpha: 0.8), 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, textColor: Colors.white,
fontSize: 14.f, fontSize: 14.f,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
@ -723,148 +646,6 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
); );
}), }),
SizedBox(height: 16.h), SizedBox(height: 16.h),
// Container(
// decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
// color: AppColors.whiteColor,
// borderRadius: 20.r,
// hasShadow: false,
// ),
// child: Row(
// mainAxisSize: MainAxisSize.max,
// children: [
// Utils.buildSvgWithAssets(icon: AppAssets.prescription_reminder_icon, width: 35.h, height: 35.h, applyThemeColor: false),
// SizedBox(width: 8.h),
// Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// LocaleKeys.setReminder.tr(context: context).toText13(isBold: true),
// LocaleKeys.notifyMeBeforeAppointment.tr(context: context).toText11(color: AppColors.textColorLight, isBold: true),
// ],
// ),
// const Spacer(),
// BellAnimatedSwitch(
// key: _bellSwitchKey,
// initialValue: widget.patientAppointmentHistoryResponseModel.hasReminder ?? false,
// activeColor: AppColors.successColor.withOpacity(0.2),
// inactiveColor: AppColors.lightGrayBGColor,
// activeCircleColor: AppColors.successColor,
// inactiveCircleColor: AppColors.greyTextColor,
// activeIconColor: AppColors.bgGreenColor,
// inactiveIconColor: AppColors.greyTextColor,
// activeIcon: Utils.buildSvgWithAssets(icon: AppAssets.bell, iconColor: AppColors.whiteColor, width: 15.w, height: 15.h),
// inactiveIcon: Utils.buildSvgWithAssets(icon: AppAssets.bell, iconColor: AppColors.whiteColor, width: 15.w, height: 15.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);
//
// // 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);
// });
// },
// );
//
// // If permission was not granted, revert the switch back to OFF
// if (!permissionGranted) {
// _bellSwitchKey.currentState?.setSwitchValue(false);
// }
// } else {
// isEventAddedOrRemoved = await calender.checkAndRemove(
// id: "${widget.patientAppointmentHistoryResponseModel.appointmentNo}",
// );
// setState(() {
// myAppointmentsViewModel.setAppointmentReminder(!isEventAddedOrRemoved, widget.patientAppointmentHistoryResponseModel);
// });
// }
// },
// )
//
// // Switch(
// // activeThumbColor: AppColors.successColor,
// // // activeTrackColor: AppColors.successColor.withValues(alpha: .15),
// // value: widget.patientAppointmentHistoryResponseModel.hasReminder!,
// // onChanged: (newValue) async {
// // CalenderUtilsNew calender = CalenderUtilsNew.instance;
// // bool isEventAddedOrRemoved = false;
// // if(newValue == true){
// // DateTime startDate = DateTime.now();
// // DateTime endDate = DateUtil.convertStringToDate(widget
// // .patientAppointmentHistoryResponseModel.appointmentDate);
// // 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);
// // });
// // },
// // );
// // }else {
// // isEventAddedOrRemoved = await calender.checkAndRemove( id:"${widget.patientAppointmentHistoryResponseModel.appointmentNo}", );
// // setState(() {
// // myAppointmentsViewModel.setAppointmentReminder(!isEventAddedOrRemoved, widget.patientAppointmentHistoryResponseModel);
// // });
// // }
// //
// //
// // },
// // ),
// ],
// ).paddingSymmetrical(16.h, 16.h),
// ),
SizedBox(height: 16.h), SizedBox(height: 16.h),
], ],
) )
@ -875,7 +656,7 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
crossAxisCount: 3, crossAxisCount: 3,
crossAxisSpacing: 16.h, crossAxisSpacing: 16.h,
mainAxisSpacing: 16.w, mainAxisSpacing: 16.w,
mainAxisExtent: 115.h, childAspectRatio: isFoldable ? 1.2 : (isTablet ? 1.1 : 0.78),
), ),
physics: NeverScrollableScrollPhysics(), physics: NeverScrollableScrollPhysics(),
padding: EdgeInsets.zero, padding: EdgeInsets.zero,
@ -974,7 +755,8 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
); );
Navigator.of(context).push( Navigator.of(context).push(
CustomPageRoute( CustomPageRoute(
page: PrescriptionDetailPage(isFromAppointments: true, prescriptionsResponseModel: patientPrescriptionsResponseModel), page: PrescriptionDetailPage(
isFromAppointments: true, prescriptionsResponseModel: patientPrescriptionsResponseModel),
), ),
); );
} else { } else {
@ -1093,213 +875,6 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
], ],
); );
}), }),
// Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// "Lab & Radiology".needTranslation.toText18(isBold: true),
// SizedBox(height: 16.h),
// Row(
// children: [
// Expanded(
// child: LabRadCard(
// icon: AppAssets.lab_result_icon,
// labelText: LocaleKeys.labResults.tr(context: context),
// // labOrderTests: ["Complete blood count", "Creatinine", "Blood Sugar"],
// // labOrderTests: labViewModel.isLabOrdersLoading ? [] : labViewModel.labOrderTests,
// labOrderTests: [],
// // isLoading: labViewModel.isLabOrdersLoading,
// isLoading: false,
// ).onPress(() {
// Navigator.of(context).push(
// CustomPageRoute(
// page: LabOrdersPage(),
// ),
// );
// }),
// ),
// SizedBox(width: 16.h),
// Expanded(
// child: LabRadCard(
// icon: AppAssets.radiology_icon,
// labelText: LocaleKeys.radiology.tr(context: context),
// // labOrderTests: ["Chest X-ray", "Abdominal Ultrasound", "Dental X-ray"],
// labOrderTests: [],
// isLoading: false,
// ).onPress(() {
// Navigator.of(context).push(
// CustomPageRoute(
// page: RadiologyOrdersPage(),
// ),
// );
// }),
// ),
// ],
// ),
// SizedBox(height: 16.h),
// LocaleKeys.prescriptions.tr(context: context).toText18(isBold: true),
// SizedBox(height: 16.h),
// Consumer<PrescriptionsViewModel>(builder: (context, prescriptionVM, child) {
// return prescriptionVM.isPrescriptionsDetailsLoading
// ? const MoviesShimmerWidget()
// : Container(
// decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
// color: Colors.white,
// borderRadius: 20.r,
// ),
// padding: EdgeInsets.all(16.w),
// child: Column(
// children: [
// // ListView.separated(
// // itemCount: prescriptionVM.prescriptionDetailsList.length,
// // shrinkWrap: true,
// // padding: EdgeInsets.only(right: 8.w),
// // physics: NeverScrollableScrollPhysics(),
// // itemBuilder: (context, index) {
// // return AnimationConfiguration.staggeredList(
// // position: index,
// // duration: const Duration(milliseconds: 500),
// // child: SlideAnimation(
// // verticalOffset: 100.0,
// // child: FadeInAnimation(
// // child: Row(
// // children: [
// // Utils.buildSvgWithAssets(
// // icon: AppAssets.prescription_item_icon,
// // width: 40.h,
// // height: 40.h,
// // ),
// // SizedBox(width: 8.h),
// // Row(
// // mainAxisSize: MainAxisSize.max,
// // children: [
// // Column(
// // children: [
// // prescriptionVM.prescriptionDetailsList[index].itemDescription!
// // .toText12(isBold: true, maxLine: 1),
// // "Prescribed By: ${widget.patientAppointmentHistoryResponseModel.doctorTitle} ${widget.patientAppointmentHistoryResponseModel.doctorNameObj}"
// // .needTranslation
// // .toText10(
// // weight: FontWeight.w600,
// // color: AppColors.greyTextColor,
// // letterSpacing: -0.4),
// // ],
// // ),
// // SizedBox(width: 68.w),
// // Transform.flip(
// // flipX: appState.isArabic(),
// // child: Utils.buildSvgWithAssets(
// // icon: AppAssets.forward_arrow_icon,
// // iconColor: AppColors.blackColor,
// // width: 18.w,
// // height: 13.h,
// // fit: BoxFit.contain,
// // ),
// // ),
// // ],
// // ),
// // ],
// // ),
// // ),
// // ),
// // );
// // },
// // separatorBuilder: (BuildContext cxt, int index) => SizedBox(height: 16.h),
// // ).onPress(() {
// // prescriptionVM.setPrescriptionsDetailsLoading();
// // Navigator.of(context).push(
// // CustomPageRoute(
// // page: PrescriptionDetailPage(prescriptionsResponseModel: getPrescriptionRequestModel()),
// // ),
// // );
// // }),
// SizedBox(height: 16.h),
// const Divider(color: AppColors.dividerColor),
// SizedBox(height: 16.h),
// // Wrap(
// // runSpacing: 6.w,
// // children: [
// // // Expanded(
// // // child: CustomButton(
// // // text: widget.prescriptionsResponseModel.isHomeMedicineDeliverySupported! ? LocaleKeys.resendOrder.tr(context: context) : LocaleKeys.prescriptionDeliveryError.tr(context: context),
// // // onPressed: () {},
// // // backgroundColor: AppColors.secondaryLightRedColor,
// // // borderColor: AppColors.secondaryLightRedColor,
// // // textColor: AppColors.primaryRedColor,
// // // fontSize: 14,
// // // isBold: true,
// // // borderRadius: 12.h,
// // // height: 40.h,
// // // icon: AppAssets.appointment_calendar_icon,
// // // iconColor: AppColors.primaryRedColor,
// // // iconSize: 16.h,
// // // ),
// // // ),
// // // SizedBox(width: 16.h),
// // Expanded(
// // child: CustomButton(
// // text: "Refill & Delivery".needTranslation,
// // onPressed: () {
// // Navigator.of(context)
// // .push(
// // CustomPageRoute(
// // page: PrescriptionsListPage(),
// // ),
// // )
// // .then((val) {
// // prescriptionsViewModel.setPrescriptionsDetailsLoading();
// // prescriptionsViewModel.getPrescriptionDetails(getPrescriptionRequestModel());
// // });
// // },
// // backgroundColor: AppColors.secondaryLightRedColor,
// // borderColor: AppColors.secondaryLightRedColor,
// // textColor: AppColors.primaryRedColor,
// // fontSize: 14.f,
// // isBold: true,
// // borderRadius: 12.r,
// // height: 40.h,
// // icon: AppAssets.requests,
// // iconColor: AppColors.primaryRedColor,
// // iconSize: 16.h,
// // ),
// // ),
// //
// // SizedBox(width: 16.w),
// // Expanded(
// // child: CustomButton(
// // text: "All Prescriptions".needTranslation,
// // onPressed: () {
// // Navigator.of(context)
// // .push(
// // CustomPageRoute(
// // page: PrescriptionsListPage(),
// // ),
// // )
// // .then((val) {
// // prescriptionsViewModel.setPrescriptionsDetailsLoading();
// // prescriptionsViewModel.getPrescriptionDetails(getPrescriptionRequestModel());
// // });
// // },
// // backgroundColor: AppColors.secondaryLightRedColor,
// // borderColor: AppColors.secondaryLightRedColor,
// // textColor: AppColors.primaryRedColor,
// // fontSize: 14.f,
// // isBold: true,
// // borderRadius: 12.r,
// // height: 40.h,
// // icon: AppAssets.requests,
// // iconColor: AppColors.primaryRedColor,
// // iconSize: 16.h,
// // ),
// // ),
// // ],
// // ),
// ],
// ),
// );
// }),
// ],
// ),
], ],
).paddingAll(24.w), ).paddingAll(24.w),
), ),
@ -1315,7 +890,8 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
if (widget.patientAppointmentHistoryResponseModel.nextAction == 15 || widget.patientAppointmentHistoryResponseModel.nextAction == 20) if (widget.patientAppointmentHistoryResponseModel.nextAction == 15 ||
widget.patientAppointmentHistoryResponseModel.nextAction == 20)
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
@ -1323,7 +899,10 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
LocaleKeys.amountBeforeTax.tr(context: context).toText18(isBold: true), 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), isSaudiCurrency: true),
], ],
), ),
@ -1331,8 +910,10 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Expanded(child: LocaleKeys.upcomingPaymentNow.tr(context: context).toText12(isBold: true, color: AppColors.greyTextColor)), Expanded(
"VAT 15%(${widget.patientAppointmentHistoryResponseModel.patientTaxAmount})".toText14(isBold: true, color: AppColors.greyTextColor, letterSpacing: -0.64), 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), SizedBox(height: 18.h),
@ -1346,7 +927,10 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ 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), isSaudiCurrency: true),
], ],
), ),
@ -1384,7 +968,8 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
handleAppointmentNextAction(widget.patientAppointmentHistoryResponseModel.nextAction); handleAppointmentNextAction(widget.patientAppointmentHistoryResponseModel.nextAction);
}, },
backgroundColor: AppointmentType.getNextActionButtonColor(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, textColor: widget.patientAppointmentHistoryResponseModel.nextAction == 15 ? AppColors.textColor : Colors.white,
fontSize: 16.f, fontSize: 16.f,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
@ -1424,7 +1009,10 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
text: LocaleKeys.insideHospital.tr(context: context), text: LocaleKeys.insideHospital.tr(context: context),
onPressed: () { onPressed: () {
Navigator.pop(context); 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()); clinicID: widget.patientAppointmentHistoryResponseModel.clinicID.toString());
}, },
backgroundColor: AppColors.primaryRedColor, backgroundColor: AppColors.primaryRedColor,
@ -1443,12 +1031,14 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
Navigator.pop(context); Navigator.pop(context);
await MapLauncher.showMarker( await MapLauncher.showMarker(
mapType: MapType.google, 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", title: widget.patientAppointmentHistoryResponseModel.projectName ?? "Habib Hospital",
).catchError((err) { ).catchError((err) {
MapLauncher.showMarker( MapLauncher.showMarker(
mapType: Platform.isIOS ? MapType.apple : MapType.google, 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", title: widget.patientAppointmentHistoryResponseModel.projectName ?? "Habib Hospital",
); );
}); });
@ -1482,7 +1072,9 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
Permission.bluetoothScan, Permission.bluetoothScan,
Permission.activityRecognition, Permission.activityRecognition,
].request().whenComplete(() { ].request().whenComplete(() {
PenguinMethodChannel().launch("penguin", getIt.get<AppState>().isArabic() ? "ar" : "en", getIt.get<AppState>().getAuthenticatedUser()?.patientId?.toString() ?? "", true, details: data); PenguinMethodChannel().launch("penguin", getIt.get<AppState>().isArabic() ? "ar" : "en",
getIt.get<AppState>().getAuthenticatedUser()?.patientId?.toString() ?? "", true,
details: data);
}); });
} }
} }
@ -1540,12 +1132,20 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
LoaderBottomSheet.hideLoader(); LoaderBottomSheet.hideLoader();
myAppointmentsViewModel.setIsAppointmentDataToBeLoaded(true); myAppointmentsViewModel.setIsAppointmentDataToBeLoaded(true);
myAppointmentsViewModel.getPatientAppointments(true, false); 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.setIsAppointmentDataToBeLoaded(true);
myAppointmentsViewModel.initAppointmentsViewModel(); myAppointmentsViewModel.initAppointmentsViewModel();
myAppointmentsViewModel.getPatientAppointments(true, false); myAppointmentsViewModel.getPatientAppointments(true, false);
Navigator.of(context).pop(); 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(); // LoaderBottomSheet.hideLoader();
case 15: case 15:
@ -1565,7 +1165,8 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: [ 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( SizedBox(
height: 12, height: 12,
), ),

@ -1,4 +1,6 @@
import 'dart:async'; import 'dart:async';
import 'dart:ui' as ui;
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_assets.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/features/my_appointments/utils/appointment_type.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.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_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/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/presentation/medical_file/eye_measurement_details_page.dart';
import 'package:hmg_patient_app_new/theme/colors.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/common_bottom_sheet.dart';
import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.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 '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'; import 'package:lottie/lottie.dart';
class AppointmentCard extends StatefulWidget { class AppointmentCard extends StatefulWidget {
@ -42,6 +43,7 @@ class AppointmentCard extends StatefulWidget {
final ContactUsViewModel? contactUsViewModel; final ContactUsViewModel? contactUsViewModel;
final BookAppointmentsViewModel bookAppointmentsViewModel; final BookAppointmentsViewModel bookAppointmentsViewModel;
final bool isForRate; final bool isForRate;
// bool isAppointmentWithin4Hours = false; // bool isAppointmentWithin4Hours = false;
const AppointmentCard( const AppointmentCard(
@ -189,20 +191,28 @@ class _AppointmentCardState extends State<AppointmentCard> {
runSpacing: 6.h, runSpacing: 6.h,
children: [ children: [
AppCustomChipWidget( 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), 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), backgroundColor: widget.isLoading ? AppColors.greyColor : (isLiveCare ? AppColors.successColor : AppColors.greyColor),
textColor: widget.isLoading ? AppColors.textColor : (isLiveCare ? Colors.white : AppColors.textColor), textColor: widget.isLoading ? AppColors.textColor : (isLiveCare ? Colors.white : AppColors.textColor),
).toShimmer2(isShow: widget.isLoading), ).toShimmer2(isShow: widget.isLoading),
AppCustomChipWidget( AppCustomChipWidget(
labelText: labelText: widget.isLoading
widget.isLoading ? 'OutPatient' : (appState.isArabic() ? widget.patientAppointmentHistoryResponseModel.isInOutPatientDescriptionN! : widget.patientAppointmentHistoryResponseModel.isInOutPatientDescription!), ? 'OutPatient'
: (appState.isArabic()
? widget.patientAppointmentHistoryResponseModel.isInOutPatientDescriptionN!
: widget.patientAppointmentHistoryResponseModel.isInOutPatientDescription!),
backgroundColor: AppColors.warningColorYellow.withValues(alpha: 0.1), backgroundColor: AppColors.warningColorYellow.withValues(alpha: 0.1),
textColor: AppColors.warningColorYellow, textColor: AppColors.warningColorYellow,
).toShimmer2(isShow: widget.isLoading), ).toShimmer2(isShow: widget.isLoading),
AppCustomChipWidget( 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), backgroundColor: AppColors.successColor.withValues(alpha: 0.1),
textColor: AppColors.successColor, textColor: AppColors.successColor,
).toShimmer2(isShow: widget.isLoading), ).toShimmer2(isShow: widget.isLoading),
@ -218,7 +228,9 @@ class _AppointmentCardState extends State<AppointmentCard> {
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
Image.network( 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, width: 63.h,
height: 63.h, height: 63.h,
fit: BoxFit.cover, fit: BoxFit.cover,
@ -242,8 +254,10 @@ class _AppointmentCardState extends State<AppointmentCard> {
Utils.buildSvgWithAssets(icon: AppAssets.rating_icon, width: 15.w, height: 15.h, iconColor: AppColors.ratingColorYellow), Utils.buildSvgWithAssets(icon: AppAssets.rating_icon, width: 15.w, height: 15.h, iconColor: AppColors.ratingColorYellow),
SizedBox(height: 2.h), SizedBox(height: 2.h),
(isFoldable || isTablet) (isFoldable || isTablet)
? "${widget.patientAppointmentHistoryResponseModel.decimalDoctorRate}".toText9(isBold: true, color: AppColors.textColor, isEnglishOnly: true) ? "${widget.patientAppointmentHistoryResponseModel.decimalDoctorRate}"
: "${widget.patientAppointmentHistoryResponseModel.decimalDoctorRate ?? "0.0"}".toText11(isBold: true, color: AppColors.textColor, isEnglishOnly: true), .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), ).circle(100).toShimmer2(isShow: widget.isLoading),
@ -258,10 +272,16 @@ class _AppointmentCardState extends State<AppointmentCard> {
Row( Row(
children: [ children: [
(widget.isLoading ? 'Dr' : "${widget.patientAppointmentHistoryResponseModel.doctorTitle}").toText16(isBold: true, maxlines: 1), (widget.isLoading ? 'Dr' : "${widget.patientAppointmentHistoryResponseModel.doctorTitle}").toText16(isBold: true, maxlines: 1),
(widget.isLoading ? 'John Doe' : " ${widget.patientAppointmentHistoryResponseModel.doctorNameObj!.truncate(20)}") Expanded(
.toText16(isBold: true, maxlines: 1, isEnglishOnly: !Utils.isArabicText(widget.patientAppointmentHistoryResponseModel.doctorNameObj ?? "John Doe")), 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), SizedBox(width: 12.w),
(widget.patientAppointmentHistoryResponseModel.doctorNationalityFlagURL != null && widget.patientAppointmentHistoryResponseModel.doctorNationalityFlagURL!.isNotEmpty) (widget.patientAppointmentHistoryResponseModel.doctorNationalityFlagURL != null &&
widget.patientAppointmentHistoryResponseModel.doctorNationalityFlagURL!.isNotEmpty)
? Image.network( ? Image.network(
widget.patientAppointmentHistoryResponseModel.doctorNationalityFlagURL ?? "https://hmgwebservices.com/Images/flag/SAU.png", widget.patientAppointmentHistoryResponseModel.doctorNationalityFlagURL ?? "https://hmgwebservices.com/Images/flag/SAU.png",
width: 20.h, width: 20.h,
@ -419,40 +439,40 @@ class _AppointmentCardState extends State<AppointmentCard> {
// } else { // } else {
return CustomButton( return CustomButton(
text: widget.isFromMedicalReport ? LocaleKeys.selectAppointment.tr(context: context) : LocaleKeys.viewDetails.tr(context: context), text: widget.isFromMedicalReport ? LocaleKeys.selectAppointment.tr(context: context) : LocaleKeys.viewDetails.tr(context: context),
onPressed: () { onPressed: () {
if (widget.isFromMedicalReport) { if (widget.isFromMedicalReport) {
if (widget.isForFeedback) { if (widget.isForFeedback) {
widget.contactUsViewModel!.setPatientFeedbackSelectedAppointment(widget.patientAppointmentHistoryResponseModel); widget.contactUsViewModel!.setPatientFeedbackSelectedAppointment(widget.patientAppointmentHistoryResponseModel);
} else {
widget.medicalFileViewModel!.setSelectedMedicalReportAppointment(widget.patientAppointmentHistoryResponseModel);
}
Navigator.pop(context, false);
} else { } else {
Navigator.of(context) widget.medicalFileViewModel!.setSelectedMedicalReportAppointment(widget.patientAppointmentHistoryResponseModel);
.push(
CustomPageRoute(
page: AppointmentDetailsPage(patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel),
),
)
.then((_) {
widget.myAppointmentsViewModel.initAppointmentsViewModel();
widget.myAppointmentsViewModel.getPatientAppointments(true, false);
});
} }
}, Navigator.pop(context, false);
backgroundColor: AppColors.secondaryLightRedColor, } else {
borderColor: AppColors.secondaryLightRedColor, Navigator.of(context)
textColor: AppColors.primaryRedColor, .push(
fontSize: (isFoldable || isTablet) ? 12.f : 14.f, CustomPageRoute(
fontWeight: FontWeight.w600, page: AppointmentDetailsPage(patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel),
borderRadius: 12.r, ),
padding: EdgeInsets.symmetric(horizontal: 10.w), )
// height: isTablet || isFoldable ? 46.h : 40.h, .then((_) {
height: 40.h, widget.myAppointmentsViewModel.initAppointmentsViewModel();
icon: widget.isFromMedicalReport ? AppAssets.checkmark_icon : null, widget.myAppointmentsViewModel.getPatientAppointments(true, false);
iconColor: AppColors.primaryRedColor, });
iconSize: 16.h, }
); },
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 { } else {
if (widget.isFromMedicalReport) { if (widget.isFromMedicalReport) {
@ -486,7 +506,7 @@ class _AppointmentCardState extends State<AppointmentCard> {
} }
return (widget.patientAppointmentHistoryResponseModel.isActiveDoctor ?? true) return (widget.patientAppointmentHistoryResponseModel.isActiveDoctor ?? true)
? (AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel) && ? (AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel) &&
widget.patientAppointmentHistoryResponseModel.isClinicReBookingAllowed == false) widget.patientAppointmentHistoryResponseModel.isClinicReBookingAllowed == false)
? // Show only View Details button without arrow when rebooking not allowed ? // Show only View Details button without arrow when rebooking not allowed
_getArrivedButton(context) _getArrivedButton(context)
: Row( : Row(
@ -498,8 +518,10 @@ class _AppointmentCardState extends State<AppointmentCard> {
: CustomButton( : CustomButton(
text: AppointmentType.getNextActionText(widget.patientAppointmentHistoryResponseModel.nextAction), text: AppointmentType.getNextActionText(widget.patientAppointmentHistoryResponseModel.nextAction),
onPressed: () => handleAppointmentNextAction(widget.patientAppointmentHistoryResponseModel.nextAction, context), onPressed: () => handleAppointmentNextAction(widget.patientAppointmentHistoryResponseModel.nextAction, context),
backgroundColor: AppointmentType.getNextActionButtonColor(widget.patientAppointmentHistoryResponseModel.nextAction).withValues(alpha: 0.15), backgroundColor: AppointmentType.getNextActionButtonColor(widget.patientAppointmentHistoryResponseModel.nextAction)
borderColor: AppointmentType.getNextActionButtonColor(widget.patientAppointmentHistoryResponseModel.nextAction).withValues(alpha: 0.01), .withValues(alpha: 0.15),
borderColor: AppointmentType.getNextActionButtonColor(widget.patientAppointmentHistoryResponseModel.nextAction)
.withValues(alpha: 0.01),
textColor: AppointmentType.getNextActionTextColor(widget.patientAppointmentHistoryResponseModel.nextAction), textColor: AppointmentType.getNextActionTextColor(widget.patientAppointmentHistoryResponseModel.nextAction),
fontSize: (isFoldable || isTablet) ? 12.f : 14.f, fontSize: (isFoldable || isTablet) ? 12.f : 14.f,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
@ -589,10 +611,10 @@ class _AppointmentCardState extends State<AppointmentCard> {
// Show Rebook button // Show Rebook button
return CustomButton( return CustomButton(
borderSide: BorderSide( borderSide: BorderSide(
color: AppColors.textColor, color: AppColors.textColor,
width: 1.2, width: 1.2,
), ),
text: LocaleKeys.rebookSameDoctor.tr(context: context), text: LocaleKeys.rebookSameDoctor.tr(context: context),
onPressed: () => openDoctorScheduleCalendar(context), onPressed: () => openDoctorScheduleCalendar(context),
backgroundColor: AppColors.transparent, backgroundColor: AppColors.transparent,
@ -620,7 +642,8 @@ class _AppointmentCardState extends State<AppointmentCard> {
); );
} else { } else {
if (!AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel)) { 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) Navigator.of(context)
.push( .push(
@ -708,46 +731,47 @@ class _AppointmentCardState extends State<AppointmentCard> {
children: [ children: [
Lottie.asset(AppAnimations.warningAnimation, Lottie.asset(AppAnimations.warningAnimation,
repeat: false, reverse: false, frameRate: FrameRate(60), width: 100.h, height: 100.h, fit: BoxFit.fill), repeat: false, reverse: false, frameRate: FrameRate(60), width: 100.h, height: 100.h, fit: BoxFit.fill),
SizedBox(height: 12,), SizedBox(
LocaleKeys.upcomingPaymentPending.tr(context: context).toText14( height: 12,
color: AppColors.textColor,
isCenter: true,
), ),
LocaleKeys.upcomingPaymentPending.tr(context: context).toText14(
color: AppColors.textColor,
isCenter: true,
),
SizedBox(height: 24.h), SizedBox(height: 24.h),
// Countdown Timer - DD : HH : MM : SS format with labels // Countdown Timer - DD : HH : MM : SS format with labels
Directionality( Directionality(
textDirection: ui.TextDirection.ltr, textDirection: ui.TextDirection.ltr,
child:Row( child: Row(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
// Days // Days
_buildTimeUnit( _buildTimeUnit(
_timeRemaining != null ? _timeRemaining!.inDays.toString().padLeft(2, '0') : '00', _timeRemaining != null ? _timeRemaining!.inDays.toString().padLeft(2, '0') : '00',
LocaleKeys.days.tr(context: context), LocaleKeys.days.tr(context: context),
), ),
_buildTimeSeparator(), _buildTimeSeparator(),
// Hours // Hours
_buildTimeUnit( _buildTimeUnit(
_timeRemaining != null ? _timeRemaining!.inHours.remainder(24).toString().padLeft(2, '0') : '00', _timeRemaining != null ? _timeRemaining!.inHours.remainder(24).toString().padLeft(2, '0') : '00',
LocaleKeys.hours.tr(context: context), LocaleKeys.hours.tr(context: context),
), ),
_buildTimeSeparator(), _buildTimeSeparator(),
// Minutes // Minutes
_buildTimeUnit( _buildTimeUnit(
_timeRemaining != null ? _timeRemaining!.inMinutes.remainder(60).toString().padLeft(2, '0') : '00', _timeRemaining != null ? _timeRemaining!.inMinutes.remainder(60).toString().padLeft(2, '0') : '00',
LocaleKeys.minutes.tr(context: context), LocaleKeys.minutes.tr(context: context),
), ),
_buildTimeSeparator(), _buildTimeSeparator(),
// Seconds // Seconds
_buildTimeUnit( _buildTimeUnit(
_timeRemaining != null ? _timeRemaining!.inSeconds.remainder(60).toString().padLeft(2, '0') : '00', _timeRemaining != null ? _timeRemaining!.inSeconds.remainder(60).toString().padLeft(2, '0') : '00',
LocaleKeys.seconds.tr(context: context), LocaleKeys.seconds.tr(context: context),
), ),
], ],
)), )),
SizedBox(height: 24.h), SizedBox(height: 24.h),
// Green Acknowledge button with checkmark icon // Green Acknowledge button with checkmark icon
CustomButton( CustomButton(
@ -800,4 +824,3 @@ class _AppointmentCardState extends State<AppointmentCard> {
} }
} }
} }

@ -1,3 +1,5 @@
import 'dart:ui' as ui;
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart'; import 'package:hmg_patient_app_new/core/app_assets.dart';
@ -17,8 +19,6 @@ 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/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.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/common_bottom_sheet.dart';
import 'dart:ui' as ui;
import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.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 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
@ -67,8 +67,8 @@ class AppointmentDoctorCard extends StatelessWidget {
Transform.translate( Transform.translate(
offset: Offset(0.0, -20.h), offset: Offset(0.0, -20.h),
child: Container( child: Container(
width: 40.w, width: 50.h,
height: 40.h, height: 50.h,
decoration: BoxDecoration( decoration: BoxDecoration(
color: AppColors.whiteColor, color: AppColors.whiteColor,
shape: BoxShape.circle, // Makes the container circular shape: BoxShape.circle, // Makes the container circular
@ -80,9 +80,10 @@ class AppointmentDoctorCard extends StatelessWidget {
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
Utils.buildSvgWithAssets(icon: AppAssets.rating_icon, width: 15.w, height: 15.h, iconColor: AppColors.ratingColorYellow), Utils.buildSvgWithAssets(icon: AppAssets.rating_icon, width: 15.h, height: 15.h, iconColor: AppColors.ratingColorYellow),
SizedBox(height: 2.h), SizedBox(height: 2.h),
"${patientAppointmentHistoryResponseModel.decimalDoctorRate ?? 0.0}".toText11(isBold: true, color: AppColors.textColor, isEnglishOnly: true), "${patientAppointmentHistoryResponseModel.decimalDoctorRate ?? 0.0}"
.toText11(isBold: true, color: AppColors.textColor, isEnglishOnly: true),
], ],
), ),
).circle(100), ).circle(100),
@ -97,9 +98,14 @@ class AppointmentDoctorCard extends StatelessWidget {
children: [ children: [
Row( Row(
children: [ children: [
patientAppointmentHistoryResponseModel.doctorNameObj!.toText16(isBold: true, isEnglishOnly: !Utils.isArabicText(patientAppointmentHistoryResponseModel.doctorNameObj ?? "")), patientAppointmentHistoryResponseModel.doctorNameObj!.toText16(
isBold: true,
isEnglishOnly: !Utils.isArabicText(patientAppointmentHistoryResponseModel.doctorNameObj ?? ""),
textOverflow: TextOverflow.ellipsis,
),
SizedBox(width: 12.w), SizedBox(width: 12.w),
(patientAppointmentHistoryResponseModel.doctorNationalityFlagURL != null && patientAppointmentHistoryResponseModel.doctorNationalityFlagURL!.isNotEmpty) (patientAppointmentHistoryResponseModel.doctorNationalityFlagURL != null &&
patientAppointmentHistoryResponseModel.doctorNationalityFlagURL!.isNotEmpty)
? Image.network( ? Image.network(
patientAppointmentHistoryResponseModel.doctorNationalityFlagURL ?? "https://hmgwebservices.com/Images/flag/SAU.png", patientAppointmentHistoryResponseModel.doctorNationalityFlagURL ?? "https://hmgwebservices.com/Images/flag/SAU.png",
width: 20.h, width: 20.h,
@ -130,19 +136,25 @@ class AppointmentDoctorCard extends StatelessWidget {
child: AppCustomChipWidget( child: AppCustomChipWidget(
labelPadding: EdgeInsetsDirectional.only(start: -6.w, end: 6.w), labelPadding: EdgeInsetsDirectional.only(start: -6.w, end: 6.w),
icon: AppAssets.doctor_calendar_icon, icon: AppAssets.doctor_calendar_icon,
richText: "${DateUtil.formatDateToDate(DateUtil.convertStringToDate(patientAppointmentHistoryResponseModel.appointmentDate), false)} ${DateUtil.formatDateToTimeLang( richText:
"${DateUtil.formatDateToDate(DateUtil.convertStringToDate(patientAppointmentHistoryResponseModel.appointmentDate), false)} ${DateUtil.formatDateToTimeLang(
DateUtil.convertStringToDate(patientAppointmentHistoryResponseModel.appointmentDate), DateUtil.convertStringToDate(patientAppointmentHistoryResponseModel.appointmentDate),
false, false,
)}" )}"
.toText10(isBold: true), .toText10(isBold: true),
), ),
), ),
AppCustomChipWidget( AppCustomChipWidget(
labelPadding: EdgeInsetsDirectional.only(start: -6.w, end: 6.w), labelPadding: EdgeInsetsDirectional.only(start: -6.w, end: 6.w),
icon: !patientAppointmentHistoryResponseModel.isLiveCareAppointment! ? AppAssets.walkin_appointment_icon : AppAssets.small_livecare_icon, icon: !patientAppointmentHistoryResponseModel.isLiveCareAppointment!
? AppAssets.walkin_appointment_icon
: AppAssets.small_livecare_icon,
iconColor: !patientAppointmentHistoryResponseModel.isLiveCareAppointment! ? AppColors.textColor : Colors.white, iconColor: !patientAppointmentHistoryResponseModel.isLiveCareAppointment! ? AppColors.textColor : Colors.white,
labelText: patientAppointmentHistoryResponseModel.isLiveCareAppointment! ? LocaleKeys.livecare.tr(context: context) : LocaleKeys.walkin.tr(context: context), labelText: patientAppointmentHistoryResponseModel.isLiveCareAppointment!
backgroundColor: !patientAppointmentHistoryResponseModel.isLiveCareAppointment! ? AppColors.greyColor : AppColors.successColor, ? LocaleKeys.livecare.tr(context: context)
: LocaleKeys.walkin.tr(context: context),
backgroundColor:
!patientAppointmentHistoryResponseModel.isLiveCareAppointment! ? AppColors.greyColor : AppColors.successColor,
textColor: !patientAppointmentHistoryResponseModel.isLiveCareAppointment! ? AppColors.textColor : Colors.white, textColor: !patientAppointmentHistoryResponseModel.isLiveCareAppointment! ? AppColors.textColor : Colors.white,
), ),
], ],
@ -150,10 +162,12 @@ class AppointmentDoctorCard extends StatelessWidget {
], ],
), ),
), ),
patientAppointmentHistoryResponseModel.isLiveCareAppointment! ||
patientAppointmentHistoryResponseModel.isLiveCareAppointment! || patientAppointmentHistoryResponseModel.isClinicReBookingAllowed! ==false || patientAppointmentHistoryResponseModel.isActiveDoctor! == false patientAppointmentHistoryResponseModel.isClinicReBookingAllowed! == false ||
patientAppointmentHistoryResponseModel.isActiveDoctor! == false
? SizedBox.shrink() ? SizedBox.shrink()
: Utils.buildSvgWithAssets(icon: AppAssets.doctor_profile_icon, width: 20.h, height: 20.h, fit: BoxFit.scaleDown).onPress(() async { : Utils.buildSvgWithAssets(icon: AppAssets.doctor_profile_icon, width: 20.h, height: 20.h, fit: BoxFit.scaleDown)
.onPress(() async {
DoctorsListResponseModel selectedDoctor = DoctorsListResponseModel(); DoctorsListResponseModel selectedDoctor = DoctorsListResponseModel();
selectedDoctor.doctorID = patientAppointmentHistoryResponseModel.doctorID; selectedDoctor.doctorID = patientAppointmentHistoryResponseModel.doctorID;
selectedDoctor.doctorImageURL = patientAppointmentHistoryResponseModel.doctorImageURL; selectedDoctor.doctorImageURL = patientAppointmentHistoryResponseModel.doctorImageURL;
@ -197,8 +211,7 @@ class AppointmentDoctorCard extends StatelessWidget {
AppointmentType.isArrived(patientAppointmentHistoryResponseModel), AppointmentType.isArrived(patientAppointmentHistoryResponseModel),
), ),
), ),
if (timerWidget != null) if (timerWidget != null) timerWidget ?? SizedBox()
timerWidget ?? SizedBox()
], ],
), ),
), ),

@ -1,6 +1,5 @@
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:intl/intl.dart' show NumberFormat;
import 'package:hmg_patient_app_new/core/app_assets.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/app_state.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart'; import 'package:hmg_patient_app_new/core/dependencies.dart';
@ -13,8 +12,8 @@ import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_vie
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/book_appointment/widgets/appointment_calendar.dart'; import 'package:hmg_patient_app_new/presentation/book_appointment/widgets/appointment_calendar.dart';
import 'package:hmg_patient_app_new/presentation/book_appointment/widgets/doctor_rating_details.dart'; import 'package:hmg_patient_app_new/presentation/book_appointment/widgets/doctor_rating_details.dart';
import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
import 'package:hmg_patient_app_new/theme/colors.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/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.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/common_bottom_sheet.dart';
@ -56,7 +55,9 @@ class DoctorProfilePage extends StatelessWidget {
doctorID: viewModel.doctorsProfileResponseModel.doctorID ?? 0, doctorID: viewModel.doctorsProfileResponseModel.doctorID ?? 0,
isActive: viewModel.isFavouriteDoctor, isActive: viewModel.isFavouriteDoctor,
onSuccess: (response) { onSuccess: (response) {
Utils.showToast(viewModel.isFavouriteDoctor ? LocaleKeys.doctorAddedToFavourite.tr(context: context) : LocaleKeys.doctorRemovedFromFavourite.tr(context: context)); Utils.showToast(viewModel.isFavouriteDoctor
? LocaleKeys.doctorAddedToFavourite.tr(context: context)
: LocaleKeys.doctorRemovedFromFavourite.tr(context: context));
// Successfully added/removed favorite - refresh the favorites list // Successfully added/removed favorite - refresh the favorites list
getIt.get<MyAppointmentsViewModel>().refreshFavouriteDoctors(); getIt.get<MyAppointmentsViewModel>().refreshFavouriteDoctors();
}, },
@ -75,7 +76,7 @@ class DoctorProfilePage extends StatelessWidget {
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
// SizedBox(height: 24.h), isFoldable ? SizedBox(height: 24.h) : SizedBox.shrink(),
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
@ -87,18 +88,21 @@ class DoctorProfilePage extends StatelessWidget {
width: 63.h, width: 63.h,
height: 63.h, height: 63.h,
fit: BoxFit.cover, fit: BoxFit.cover,
).circle(100), ).circle(100.r),
SizedBox(width: 8.h), SizedBox(width: 8.h),
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
SizedBox( SizedBox(
width: 220.h, width: isFoldable ? 250.w : 220.w,
child: ("${bookAppointmentsViewModel.doctorsProfileResponseModel.doctorTitleForProfile} ${bookAppointmentsViewModel.doctorsProfileResponseModel.doctorName}") child:
.toString() ("${bookAppointmentsViewModel.doctorsProfileResponseModel.doctorTitleForProfile} ${bookAppointmentsViewModel.doctorsProfileResponseModel.doctorName}")
.toText24(isBold: true), .toString()
.toText24(isBold: true),
), ),
(bookAppointmentsViewModel.doctorsProfileResponseModel.specialty!.isNotEmpty ? bookAppointmentsViewModel.doctorsProfileResponseModel.specialty!.first : "") (bookAppointmentsViewModel.doctorsProfileResponseModel.specialty!.isNotEmpty
? bookAppointmentsViewModel.doctorsProfileResponseModel.specialty!.first
: "")
.toString() .toString()
.toText18(isBold: true, color: AppColors.primaryRedColor), .toText18(isBold: true, color: AppColors.primaryRedColor),
], ],
@ -137,12 +141,16 @@ class DoctorProfilePage extends StatelessWidget {
children: [ children: [
Column( Column(
children: [ children: [
Utils.buildSvgWithAssets(icon: AppAssets.doctor_profile_rating_icon, width: 48.w, height: 48.h, fit: BoxFit.contain, applyThemeColor: false), Utils.buildSvgWithAssets(
icon: AppAssets.doctor_profile_rating_icon, width: 48.w, height: 48.h, fit: BoxFit.contain, applyThemeColor: false),
SizedBox(height: 16.h), SizedBox(height: 16.h),
LocaleKeys.ratings.tr(context: context).toText12(isBold: true, color: AppColors.greyTextColor), LocaleKeys.ratings.tr(context: context).toText12(isBold: true, color: AppColors.greyTextColor),
bookAppointmentsViewModel.doctorsProfileResponseModel.decimalDoctorRate bookAppointmentsViewModel.doctorsProfileResponseModel.decimalDoctorRate.toString().toText16(
.toString() isBold: true,
.toText16(isBold: true, color: AppColors.textColor, isUnderLine: true, decorationColor: AppColors.textColor, fontFamily: "Poppins"), color: AppColors.textColor,
isUnderLine: true,
decorationColor: AppColors.textColor,
fontFamily: "Poppins"),
], ],
).onPress(() { ).onPress(() {
bookAppointmentsViewModel.getDoctorRatingDetails(); bookAppointmentsViewModel.getDoctorRatingDetails();
@ -158,11 +166,18 @@ class DoctorProfilePage extends StatelessWidget {
SizedBox(width: 36.w), SizedBox(width: 36.w),
Column( Column(
children: [ children: [
Utils.buildSvgWithAssets(icon: AppAssets.doctor_profile_reviews_icon, width: 48.w, height: 48.h, fit: BoxFit.contain, applyThemeColor: false), Utils.buildSvgWithAssets(
icon: AppAssets.doctor_profile_reviews_icon, width: 48.w, height: 48.h, fit: BoxFit.contain, applyThemeColor: false),
SizedBox(height: 16.h), SizedBox(height: 16.h),
LocaleKeys.reviews.tr(context: context).toText12(isBold: true, color: AppColors.greyTextColor), LocaleKeys.reviews.tr(context: context).toText12(isBold: true, color: AppColors.greyTextColor),
NumberFormat.decimalPattern().format(bookAppointmentsViewModel.doctorsProfileResponseModel.noOfPatientsRate ?? 0) NumberFormat.decimalPattern()
.toText16(isBold: true, color: AppColors.textColor, isUnderLine: true, decorationColor: AppColors.textColor, fontFamily: "Poppins"), .format(bookAppointmentsViewModel.doctorsProfileResponseModel.noOfPatientsRate ?? 0)
.toText16(
isBold: true,
color: AppColors.textColor,
isUnderLine: true,
decorationColor: AppColors.textColor,
fontFamily: "Poppins"),
], ],
).onPress(() { ).onPress(() {
bookAppointmentsViewModel.getDoctorRatingDetails(); bookAppointmentsViewModel.getDoctorRatingDetails();
@ -182,92 +197,97 @@ class DoctorProfilePage extends StatelessWidget {
SizedBox(height: 16.h), SizedBox(height: 16.h),
LocaleKeys.information.tr(context: context).toText14(isBold: true, color: AppColors.textColor), LocaleKeys.information.tr(context: context).toText14(isBold: true, color: AppColors.textColor),
SizedBox(height: 6.h), SizedBox(height: 6.h),
(bookAppointmentsViewModel.doctorsProfileResponseModel.doctorProfileInfo ?? "").trim().toText12(isBold: true, color: AppColors.greyTextColor), (bookAppointmentsViewModel.doctorsProfileResponseModel.doctorProfileInfo ?? "")
.trim()
.toText12(isBold: true, color: AppColors.greyTextColor),
SizedBox(height: 24.h), SizedBox(height: 24.h),
], ],
).paddingSymmetrical(24.h, 0.h), ).paddingSymmetrical(24.h, 0.h),
), ),
), ),
), ),
isDoctorAllowedToBook ? Container( isDoctorAllowedToBook
decoration: RoundedRectangleBorder().toSmoothCornerDecoration( ? Container(
color: AppColors.whiteColor, decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
borderRadius: 24.h, color: AppColors.whiteColor,
hasShadow: true, borderRadius: 24.h,
), hasShadow: true,
child: CustomButton( ),
text: LocaleKeys.viewAvailableAppointments.tr(), child: CustomButton(
onPressed: () async { text: LocaleKeys.viewAvailableAppointments.tr(),
bookAppointmentsViewModel.selectedDoctor.speciality = bookAppointmentsViewModel.doctorsProfileResponseModel.specialty; onPressed: () async {
bookAppointmentsViewModel.selectedDoctor.specialityN = bookAppointmentsViewModel.doctorsProfileResponseModel.specialty; bookAppointmentsViewModel.selectedDoctor.speciality = bookAppointmentsViewModel.doctorsProfileResponseModel.specialty;
bookAppointmentsViewModel.selectedDoctor.name = bookAppointmentsViewModel.doctorsProfileResponseModel.doctorName; bookAppointmentsViewModel.selectedDoctor.specialityN = bookAppointmentsViewModel.doctorsProfileResponseModel.specialty;
bookAppointmentsViewModel.selectedDoctor.doctorImageURL = bookAppointmentsViewModel.doctorsProfileResponseModel.doctorImageURL; bookAppointmentsViewModel.selectedDoctor.name = bookAppointmentsViewModel.doctorsProfileResponseModel.doctorName;
bookAppointmentsViewModel.selectedDoctor.nationalityFlagURL = bookAppointmentsViewModel.doctorsProfileResponseModel.nationalityFlagURL; bookAppointmentsViewModel.selectedDoctor.doctorImageURL = bookAppointmentsViewModel.doctorsProfileResponseModel.doctorImageURL;
bookAppointmentsViewModel.selectedDoctor.clinicName = bookAppointmentsViewModel.doctorsProfileResponseModel.clinicDescription; bookAppointmentsViewModel.selectedDoctor.nationalityFlagURL =
bookAppointmentsViewModel.selectedDoctor.projectName = bookAppointmentsViewModel.doctorsProfileResponseModel.projectName; bookAppointmentsViewModel.doctorsProfileResponseModel.nationalityFlagURL;
bookAppointmentsViewModel.selectedDoctor.clinicName = bookAppointmentsViewModel.doctorsProfileResponseModel.clinicDescription;
bookAppointmentsViewModel.selectedDoctor.projectName = bookAppointmentsViewModel.doctorsProfileResponseModel.projectName;
LoaderBottomSheet.showLoader(); LoaderBottomSheet.showLoader();
bookAppointmentsViewModel.isLiveCareSchedule bookAppointmentsViewModel.isLiveCareSchedule
? await bookAppointmentsViewModel.getLiveCareDoctorFreeSlots( ? await bookAppointmentsViewModel.getLiveCareDoctorFreeSlots(
isBookingForLiveCare: true, isBookingForLiveCare: true,
onSuccess: (dynamic respData) async { onSuccess: (dynamic respData) async {
LoaderBottomSheet.hideLoader(); LoaderBottomSheet.hideLoader();
showCommonBottomSheetWithoutHeight( showCommonBottomSheetWithoutHeight(
title: LocaleKeys.pickADate.tr(), title: LocaleKeys.pickADate.tr(),
context, context,
child: AppointmentCalendar(), child: AppointmentCalendar(),
isFullScreen: false, isFullScreen: false,
isCloseButtonVisible: true, isCloseButtonVisible: true,
callBackFunc: () {}, callBackFunc: () {},
); );
}, },
onError: (err) { onError: (err) {
LoaderBottomSheet.hideLoader(); LoaderBottomSheet.hideLoader();
showCommonBottomSheetWithoutHeight( showCommonBottomSheetWithoutHeight(
context, context,
child: Utils.getErrorWidget(loadingText: err), child: Utils.getErrorWidget(loadingText: err),
callBackFunc: () {}, callBackFunc: () {},
isFullScreen: false, isFullScreen: false,
isCloseButtonVisible: true, isCloseButtonVisible: true,
); );
}) })
: await bookAppointmentsViewModel.getDoctorFreeSlots( : await bookAppointmentsViewModel.getDoctorFreeSlots(
isBookingForLiveCare: false, isBookingForLiveCare: false,
onSuccess: (dynamic respData) async { onSuccess: (dynamic respData) async {
LoaderBottomSheet.hideLoader(); LoaderBottomSheet.hideLoader();
showCommonBottomSheetWithoutHeight( showCommonBottomSheetWithoutHeight(
title: LocaleKeys.pickADate.tr() , title: LocaleKeys.pickADate.tr(),
context, context,
child: AppointmentCalendar(), child: AppointmentCalendar(),
isFullScreen: false, isFullScreen: false,
isCloseButtonVisible: true, isCloseButtonVisible: true,
callBackFunc: () {}, callBackFunc: () {},
); );
}, },
onError: (err) { onError: (err) {
LoaderBottomSheet.hideLoader(); LoaderBottomSheet.hideLoader();
showCommonBottomSheetWithoutHeight( showCommonBottomSheetWithoutHeight(
context, context,
child: Utils.getErrorWidget(loadingText: err), child: Utils.getErrorWidget(loadingText: err),
callBackFunc: () {}, callBackFunc: () {},
isFullScreen: false, isFullScreen: false,
isCloseButtonVisible: true, isCloseButtonVisible: true,
); );
}); });
}, },
backgroundColor: AppColors.primaryRedColor, backgroundColor: AppColors.primaryRedColor,
borderColor: AppColors.primaryRedColor, borderColor: AppColors.primaryRedColor,
textColor: Colors.white, textColor: Colors.white,
fontSize: 16, fontSize: 16,
isBold: true, isBold: true,
borderRadius: 12, borderRadius: 12,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0), padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 50.h, height: 50.h,
icon: AppAssets.calendar, icon: AppAssets.calendar,
iconColor: Colors.white, iconColor: Colors.white,
iconSize: 20.h, iconSize: 20.h,
).paddingSymmetrical(24.h, 24.h), ).paddingSymmetrical(24.h, 24.h),
) : SizedBox.shrink(), )
: SizedBox.shrink(),
], ],
), ),
); );

@ -92,7 +92,7 @@ class _AppointmentCalendarState extends State<AppointmentCalendar> {
// ], // ],
// ), // ),
SizedBox( SizedBox(
height: 350.h, height: MediaQuery.of(context).size.height * 0.45,
child: Directionality( child: Directionality(
textDirection: isArabic ? ui.TextDirection.rtl : ui.TextDirection.ltr, textDirection: isArabic ? ui.TextDirection.rtl : ui.TextDirection.ltr,
child: Localizations.override( child: Localizations.override(
@ -102,7 +102,7 @@ class _AppointmentCalendarState extends State<AppointmentCalendar> {
controller: _calendarController, controller: _calendarController,
minDate: DateTime.now(), minDate: DateTime.now(),
showNavigationArrow: true, showNavigationArrow: true,
headerHeight: 60.h, // headerHeight: 60.h,
headerStyle: CalendarHeaderStyle( headerStyle: CalendarHeaderStyle(
backgroundColor: AppColors.transparent, backgroundColor: AppColors.transparent,
textAlign: isArabic ? TextAlign.end : TextAlign.start, textAlign: isArabic ? TextAlign.end : TextAlign.start,
@ -157,8 +157,11 @@ class _AppointmentCalendarState extends State<AppointmentCalendar> {
), ),
//TODO: Add Next Day Span here //TODO: Add Next Day Span here
dayEvents.isNotEmpty dayEvents.isNotEmpty
? SizedBox( ? ConstrainedBox(
height: 100.h, constraints: BoxConstraints(
maxHeight: MediaQuery.of(context).size.height * 0.15,
minHeight: 0,
),
child: Directionality( child: Directionality(
textDirection: isArabic ? ui.TextDirection.rtl : ui.TextDirection.ltr, textDirection: isArabic ? ui.TextDirection.rtl : ui.TextDirection.ltr,
child: SingleChildScrollView( child: SingleChildScrollView(
@ -169,7 +172,7 @@ class _AppointmentCalendarState extends State<AppointmentCalendar> {
spacing: 6.h, spacing: 6.h,
runSpacing: 6.h, runSpacing: 6.h,
children: List.generate( children: List.generate(
dayEvents.length, // Generate a large number of items to ensure scrolling dayEvents.length,
(index) => TimeSlotChip( (index) => TimeSlotChip(
label: dayEvents[index].isoTime!, label: dayEvents[index].isoTime!,
isSelected: index == selectedButtonIndex, isSelected: index == selectedButtonIndex,
@ -204,7 +207,8 @@ class _AppointmentCalendarState extends State<AppointmentCalendar> {
), ),
); );
} else { } else {
bookAppointmentsViewModel.getAppointmentNearestGate(projectID: bookAppointmentsViewModel.selectedDoctor.projectID!, clinicID: bookAppointmentsViewModel.selectedDoctor.clinicID!); bookAppointmentsViewModel.getAppointmentNearestGate(
projectID: bookAppointmentsViewModel.selectedDoctor.projectID!, clinicID: bookAppointmentsViewModel.selectedDoctor.clinicID!);
bookAppointmentsViewModel.setSelectedAppointmentDateTime(selectedDate, selectedTime, selectedDateDisplay); bookAppointmentsViewModel.setSelectedAppointmentDateTime(selectedDate, selectedTime, selectedDateDisplay);
Navigator.of(context).pop(); Navigator.of(context).pop();
Navigator.of(context).push( Navigator.of(context).push(
@ -221,7 +225,8 @@ class _AppointmentCalendarState extends State<AppointmentCalendar> {
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
Lottie.asset(AppAnimations.errorAnimation, repeat: true, reverse: false, frameRate: FrameRate(60), width: 100.h, height: 100.h, fit: BoxFit.fill), Lottie.asset(AppAnimations.errorAnimation,
repeat: true, reverse: false, frameRate: FrameRate(60), width: 100.h, height: 100.h, fit: BoxFit.fill),
SizedBox(height: 8.h), SizedBox(height: 8.h),
(LocaleKeys.loginToUseService.tr(context: context)).toText16(color: AppColors.blackColor), (LocaleKeys.loginToUseService.tr(context: context)).toText16(color: AppColors.blackColor),
SizedBox(height: 16.h), SizedBox(height: 16.h),
@ -314,13 +319,18 @@ class _AppointmentCalendarState extends State<AppointmentCalendar> {
if (bookAppointmentsViewModel.isWaitingAppointmentAvailable && DateUtils.isSameDay(dateStart, DateTime.now())) { if (bookAppointmentsViewModel.isWaitingAppointmentAvailable && DateUtils.isSameDay(dateStart, DateTime.now())) {
dayEvents.add(TimeSlot(isoTime: LocaleKeys.waitingAppointment.tr(context: context), start: DateTime.now(), end: DateTime.now(), vidaDate: "")); dayEvents.add(TimeSlot(isoTime: LocaleKeys.waitingAppointment.tr(context: context), start: DateTime.now(), end: DateTime.now(), vidaDate: ""));
} }
freeSlots.forEach((v) { for (var v in freeSlots) {
if (v.start == dateStartObj) dayEvents.add(v); if (v.start == dateStartObj) dayEvents.add(v);
}); }
selectedButtonIndex = 0; selectedButtonIndex = 0;
List<Map<String, dynamic>> timeList = []; List<Map<String, dynamic>> timeList = [];
for (var i = 0; i < dayEvents.length; i++) { for (var i = 0; i < dayEvents.length; i++) {
Map<String, dynamic> timeSlot = {"isoTime": dayEvents[i].isoTime, "start": dayEvents[i].start.toString(), "end": dayEvents[i].end.toString(), "vidaDate": dayEvents[i].vidaDate}; Map<String, dynamic> timeSlot = {
"isoTime": dayEvents[i].isoTime,
"start": dayEvents[i].start.toString(),
"end": dayEvents[i].end.toString(),
"vidaDate": dayEvents[i].vidaDate
};
timeList.add(timeSlot); timeList.add(timeSlot);
} }
if (dayEvents.isNotEmpty) { if (dayEvents.isNotEmpty) {
@ -333,9 +343,7 @@ class _AppointmentCalendarState extends State<AppointmentCalendar> {
final DateFormat formatter = DateFormat('yyyy-MM-dd', "en-US"); final DateFormat formatter = DateFormat('yyyy-MM-dd', "en-US");
final isArabic = appState.isArabic(); final isArabic = appState.isArabic();
setState(() { setState(() {
selectedDateDisplay = isArabic selectedDateDisplay = isArabic ? DateUtil.getMonthDayYearDateFormattedAr(day) : DateUtil.getMonthDayYearDateFormatted(day);
? DateUtil.getMonthDayYearDateFormattedAr(day)
: DateUtil.getMonthDayYearDateFormatted(day);
selectedNextDate = DateUtil.getWeekDayMonthDayYearDateFormatted(day.add(Duration(days: 1)), isArabic ? "ar" : "en"); selectedNextDate = DateUtil.getWeekDayMonthDayYearDateFormatted(day.add(Duration(days: 1)), isArabic ? "ar" : "en");
_calendarController.selectedDate = day; _calendarController.selectedDate = day;
openTimeSlotsPickerForDate(day, bookAppointmentsViewModel.docFreeSlots); openTimeSlotsPickerForDate(day, bookAppointmentsViewModel.docFreeSlots);

@ -1,9 +1,7 @@
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/core/enums.dart'; import 'package:hmg_patient_app_new/core/enums.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; import 'package:hmg_patient_app_new/core/utils/size_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/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/book_appointment/select_clinic_page.dart'; import 'package:hmg_patient_app_new/presentation/book_appointment/select_clinic_page.dart';
@ -19,7 +17,6 @@ import 'package:hmg_patient_app_new/presentation/health_calculators_and_converts
import 'package:hmg_patient_app_new/presentation/health_calculators_and_converts/widgets/ibw.dart'; import 'package:hmg_patient_app_new/presentation/health_calculators_and_converts/widgets/ibw.dart';
import 'package:hmg_patient_app_new/presentation/health_calculators_and_converts/widgets/ovulation.dart'; import 'package:hmg_patient_app_new/presentation/health_calculators_and_converts/widgets/ovulation.dart';
import 'package:hmg_patient_app_new/presentation/health_calculators_and_converts/widgets/triglycerides.dart'; import 'package:hmg_patient_app_new/presentation/health_calculators_and_converts/widgets/triglycerides.dart';
import 'package:hmg_patient_app_new/services/dialog_service.dart';
import 'package:hmg_patient_app_new/theme/colors.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/appbar/collapsing_list_view.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
@ -27,11 +24,11 @@ import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
class HealthCalculatorDetailedPage extends StatefulWidget { class HealthCalculatorDetailedPage extends StatefulWidget {
HealthCalculatorsTypeEnum calculatorType; final HealthCalculatorsTypeEnum calculatorType;
int? clinicID; final int? clinicID;
int? calculationID; final int? calculationID;
HealthCalculatorDetailedPage({super.key, required this.calculatorType, this.clinicID, this.calculationID}); const HealthCalculatorDetailedPage({super.key, required this.calculatorType, this.clinicID, this.calculationID});
@override @override
State<HealthCalculatorDetailedPage> createState() => _HealthCalculatorDetailedPageState(); State<HealthCalculatorDetailedPage> createState() => _HealthCalculatorDetailedPageState();
@ -52,8 +49,8 @@ class _HealthCalculatorDetailedPageState extends State<HealthCalculatorDetailedP
widget.calculatorType == HealthCalculatorsTypeEnum.triglycerides widget.calculatorType == HealthCalculatorsTypeEnum.triglycerides
? SizedBox() ? SizedBox()
: Container( : Container(
decoration: decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, customBorder: BorderRadius.only(topLeft: Radius.circular(24.r), topRight: Radius.circular(24.r))), color: AppColors.whiteColor, customBorder: BorderRadius.only(topLeft: Radius.circular(24.r), topRight: Radius.circular(24.r))),
padding: EdgeInsets.symmetric(vertical: 20.h, horizontal: 20.h), padding: EdgeInsets.symmetric(vertical: 20.h, horizontal: 20.h),
child: CustomButton( child: CustomButton(
text: widget.calculatorType == HealthCalculatorsTypeEnum.bloodSugar || text: widget.calculatorType == HealthCalculatorsTypeEnum.bloodSugar ||
@ -131,36 +128,6 @@ class _HealthCalculatorDetailedPageState extends State<HealthCalculatorDetailedP
: {'result': provider.triglyceridesResult ?? result, 'clinicId': widget.clinicID, 'calculationID': widget.calculationID}; : {'result': provider.triglyceridesResult ?? result, 'clinicId': widget.clinicID, 'calculationID': widget.calculationID};
break; break;
} }
// switch (widget.calculatorType) {
// case HealthCalculatorsTypeEnum.bmi:
// calculatedResult = provider.bmiResultMap ?? result;
// break;
// case HealthCalculatorsTypeEnum.calories:
// case HealthCalculatorsTypeEnum.bmr:
// calculatedResult = provider.caloriesResultMap ?? result;
// break;
// case HealthCalculatorsTypeEnum.idealBodyWeight:
// calculatedResult = provider.ibwResultMap ?? result;
// break;
// case HealthCalculatorsTypeEnum.bodyFat:
// calculatedResult = provider.bodyFatResultMap ?? result;
// break;
// case HealthCalculatorsTypeEnum.crabsProteinFat:
// calculatedResult = provider.macrosResultMap ?? result;
// break;
// case HealthCalculatorsTypeEnum.ovulation:
// calculatedResult = provider.ovulationResult ?? result;
// break;
// case HealthCalculatorsTypeEnum.deliveryDueDate:
// calculatedResult = provider.deliveryResult ?? result;
// break;
// case HealthCalculatorsTypeEnum.bloodSugar:
// calculatedResult = provider.bloodSugarResult ?? result;
// case HealthCalculatorsTypeEnum.bloodCholesterol:
// calculatedResult = provider.bloodCholesterolResult ?? result;
// case HealthCalculatorsTypeEnum.triglycerides:
// calculatedResult = provider.triglyceridesResult ?? result;
// }
}, },
).paddingSymmetrical(20.w, 24.h), ).paddingSymmetrical(20.w, 24.h),
); );
@ -172,58 +139,69 @@ class _HealthCalculatorDetailedPageState extends State<HealthCalculatorDetailedP
switch (widget.calculatorType) { switch (widget.calculatorType) {
case HealthCalculatorsTypeEnum.bmi: case HealthCalculatorsTypeEnum.bmi:
return Container( return Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, customBorder: BorderRadius.all(Radius.circular(24.r))), decoration:
RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, customBorder: BorderRadius.all(Radius.circular(24.r))),
child: BMIWidget(onChange: onCalculate), child: BMIWidget(onChange: onCalculate),
); );
case HealthCalculatorsTypeEnum.calories: case HealthCalculatorsTypeEnum.calories:
return Container( return Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, customBorder: BorderRadius.all(Radius.circular(24.r))), decoration:
RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, customBorder: BorderRadius.all(Radius.circular(24.r))),
child: CaloriesWidget(onChange: onCalculate), child: CaloriesWidget(onChange: onCalculate),
); );
case HealthCalculatorsTypeEnum.bmr: case HealthCalculatorsTypeEnum.bmr:
return Container( return Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, customBorder: BorderRadius.all(Radius.circular(24.r))), decoration:
RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, customBorder: BorderRadius.all(Radius.circular(24.r))),
child: BMRWidget(onChange: onCalculate), child: BMRWidget(onChange: onCalculate),
); );
case HealthCalculatorsTypeEnum.idealBodyWeight: case HealthCalculatorsTypeEnum.idealBodyWeight:
return Container( return Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, customBorder: BorderRadius.all(Radius.circular(24.r))), decoration:
RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, customBorder: BorderRadius.all(Radius.circular(24.r))),
child: IdealBodyWeightWidget(onChange: onCalculate), child: IdealBodyWeightWidget(onChange: onCalculate),
); );
case HealthCalculatorsTypeEnum.bodyFat: case HealthCalculatorsTypeEnum.bodyFat:
return Container( return Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, customBorder: BorderRadius.all(Radius.circular(24.r))), decoration:
RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, customBorder: BorderRadius.all(Radius.circular(24.r))),
child: BodyFatWidget(onChange: onCalculate), child: BodyFatWidget(onChange: onCalculate),
); );
case HealthCalculatorsTypeEnum.crabsProteinFat: case HealthCalculatorsTypeEnum.crabsProteinFat:
return Container( return Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, customBorder: BorderRadius.all(Radius.circular(24.r))), decoration:
RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, customBorder: BorderRadius.all(Radius.circular(24.r))),
child: CrabsWidget(onChange: onCalculate), child: CrabsWidget(onChange: onCalculate),
); );
case HealthCalculatorsTypeEnum.ovulation: case HealthCalculatorsTypeEnum.ovulation:
return Container( return Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, customBorder: BorderRadius.all(Radius.circular(24.r))), decoration:
RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, customBorder: BorderRadius.all(Radius.circular(24.r))),
child: OvulationWidget(onChange: onCalculate), child: OvulationWidget(onChange: onCalculate),
); );
case HealthCalculatorsTypeEnum.deliveryDueDate: case HealthCalculatorsTypeEnum.deliveryDueDate:
return Container( return Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, customBorder: BorderRadius.all(Radius.circular(24.r))), decoration:
RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, customBorder: BorderRadius.all(Radius.circular(24.r))),
child: DeliveryDueDWidget(onChange: onCalculate), child: DeliveryDueDWidget(onChange: onCalculate),
); );
case HealthCalculatorsTypeEnum.bloodSugar: case HealthCalculatorsTypeEnum.bloodSugar:
return Container( return Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, customBorder: BorderRadius.all(Radius.circular(24.r))), decoration:
RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, customBorder: BorderRadius.all(Radius.circular(24.r))),
child: BloodSugarWidget(onChange: onCalculate), child: BloodSugarWidget(onChange: onCalculate),
); );
case HealthCalculatorsTypeEnum.bloodCholesterol: case HealthCalculatorsTypeEnum.bloodCholesterol:
return Container( return Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, customBorder: BorderRadius.all(Radius.circular(24.r))), decoration:
RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, customBorder: BorderRadius.all(Radius.circular(24.r))),
child: BloodCholesterolWidget(onChange: onCalculate), child: BloodCholesterolWidget(onChange: onCalculate),
); );
case HealthCalculatorsTypeEnum.triglycerides: case HealthCalculatorsTypeEnum.triglycerides:
return Container( return Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, customBorder: BorderRadius.all(Radius.circular(24.r))), decoration:
RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, customBorder: BorderRadius.all(Radius.circular(24.r))),
child: TriglyceridesWidget(onChange: onCalculate), child: TriglyceridesWidget(onChange: onCalculate),
); );
} }

@ -11,16 +11,15 @@ import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/health_calculators_and_converts/health_calculator_detailed_page.dart'; import 'package:hmg_patient_app_new/presentation/health_calculators_and_converts/health_calculator_detailed_page.dart';
import 'package:hmg_patient_app_new/presentation/health_calculators_and_converts/widgets/health_card.dart'; import 'package:hmg_patient_app_new/presentation/health_calculators_and_converts/widgets/health_card.dart';
import 'package:hmg_patient_app_new/services/dialog_service.dart';
import 'package:hmg_patient_app_new/theme/colors.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/appbar/collapsing_list_view.dart';
import 'package:hmg_patient_app_new/widgets/expandable_list_widget.dart'; import 'package:hmg_patient_app_new/widgets/expandable_list_widget.dart';
import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart'; import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
class HealthCalculatorsPage extends StatefulWidget { class HealthCalculatorsPage extends StatefulWidget {
HealthCalConEnum type; final HealthCalConEnum type;
HealthCalculatorsPage({super.key, required this.type}); const HealthCalculatorsPage({super.key, required this.type});
@override @override
State<HealthCalculatorsPage> createState() => _HealthCalculatorsPageState(); State<HealthCalculatorsPage> createState() => _HealthCalculatorsPageState();
@ -34,9 +33,8 @@ class _HealthCalculatorsPageState extends State<HealthCalculatorsPage> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
DialogService dialogService = getIt.get<DialogService>();
return CollapsingListView( return CollapsingListView(
isLeading: Navigator.canPop(context), isLeading: Navigator.canPop(context),
title: widget.type == HealthCalConEnum.calculator ? LocaleKeys.healthCalculators.tr(context: context) : LocaleKeys.healthConverters.tr(), title: widget.type == HealthCalConEnum.calculator ? LocaleKeys.healthCalculators.tr(context: context) : LocaleKeys.healthConverters.tr(),
child: widget.type == HealthCalConEnum.calculator child: widget.type == HealthCalConEnum.calculator
? CustomExpandableList( ? CustomExpandableList(
@ -58,7 +56,8 @@ class _HealthCalculatorsPageState extends State<HealthCalculatorsPage> {
), ),
], ],
theme: ExpandableListTheme.custom( theme: ExpandableListTheme.custom(
defaultTrailingIcon: Utils.buildSvgWithAssets(icon: AppAssets.arrow_down, height: 22.h, width: 22.w, iconColor: AppColors.textColor), defaultTrailingIcon:
Utils.buildSvgWithAssets(icon: AppAssets.arrow_down, height: 22.h, width: 22.w, iconColor: AppColors.textColor),
), ),
).paddingSymmetrical(16.w, 0.0) ).paddingSymmetrical(16.w, 0.0)
// ? Column( // ? Column(
@ -129,7 +128,8 @@ class _HealthCalculatorsPageState extends State<HealthCalculatorsPage> {
child: Row( child: Row(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Utils.buildSvgWithAssets(icon: AppAssets.bloodSugar, height: 40.h, width: 40.w, fit: BoxFit.none, applyThemeColor: false), Utils.buildSvgWithAssets(
icon: AppAssets.bloodSugar, height: 40.h, width: 40.w, fit: BoxFit.none, applyThemeColor: false),
SizedBox(width: 12.w), SizedBox(width: 12.w),
Flexible( Flexible(
child: Column( child: Column(
@ -149,7 +149,8 @@ class _HealthCalculatorsPageState extends State<HealthCalculatorsPage> {
), ),
Transform.flip( Transform.flip(
flipX: getIt.get<AppState>().isArabic(), flipX: getIt.get<AppState>().isArabic(),
child: Utils.buildSvgWithAssets(icon: AppAssets.arrowRight, width: 24.w, height: 24.h, fit: BoxFit.contain, iconColor: AppColors.textColor), child: Utils.buildSvgWithAssets(
icon: AppAssets.arrowRight, width: 24.w, height: 24.h, fit: BoxFit.contain, iconColor: AppColors.textColor),
), ),
], ],
).paddingAll(16.w)) ).paddingAll(16.w))
@ -166,7 +167,8 @@ class _HealthCalculatorsPageState extends State<HealthCalculatorsPage> {
child: Row( child: Row(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Utils.buildSvgWithAssets(icon: AppAssets.bloodCholestrol, height: 40.h, width: 40.w, fit: BoxFit.none, applyThemeColor: false), Utils.buildSvgWithAssets(
icon: AppAssets.bloodCholestrol, height: 40.h, width: 40.w, fit: BoxFit.none, applyThemeColor: false),
SizedBox(width: 12.w), SizedBox(width: 12.w),
Flexible( Flexible(
child: Column( child: Column(
@ -181,7 +183,8 @@ class _HealthCalculatorsPageState extends State<HealthCalculatorsPage> {
SizedBox(width: 12.w), SizedBox(width: 12.w),
Transform.flip( Transform.flip(
flipX: getIt.get<AppState>().isArabic(), flipX: getIt.get<AppState>().isArabic(),
child: Utils.buildSvgWithAssets(icon: AppAssets.arrowRight, width: 24.w, height: 24.h, fit: BoxFit.contain, iconColor: AppColors.textColor), child: Utils.buildSvgWithAssets(
icon: AppAssets.arrowRight, width: 24.w, height: 24.h, fit: BoxFit.contain, iconColor: AppColors.textColor),
), ),
], ],
).paddingAll(16.w)) ).paddingAll(16.w))
@ -198,7 +201,8 @@ class _HealthCalculatorsPageState extends State<HealthCalculatorsPage> {
child: Row( child: Row(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Utils.buildSvgWithAssets(icon: AppAssets.triglycerides, height: 40.h, width: 40.w, fit: BoxFit.none, applyThemeColor: false), Utils.buildSvgWithAssets(
icon: AppAssets.triglycerides, height: 40.h, width: 40.w, fit: BoxFit.none, applyThemeColor: false),
SizedBox(width: 12.w), SizedBox(width: 12.w),
Flexible( Flexible(
child: Column( child: Column(
@ -213,7 +217,8 @@ class _HealthCalculatorsPageState extends State<HealthCalculatorsPage> {
SizedBox(width: 12.w), SizedBox(width: 12.w),
Transform.flip( Transform.flip(
flipX: getIt.get<AppState>().isArabic(), flipX: getIt.get<AppState>().isArabic(),
child: Utils.buildSvgWithAssets(icon: AppAssets.arrowRight, width: 24.w, height: 24.h, fit: BoxFit.contain, iconColor: AppColors.textColor), child: Utils.buildSvgWithAssets(
icon: AppAssets.arrowRight, width: 24.w, height: 24.h, fit: BoxFit.contain, iconColor: AppColors.textColor),
), ),
], ],
).paddingAll(16.w)) ).paddingAll(16.w))
@ -247,7 +252,8 @@ class _HealthCalculatorsPageState extends State<HealthCalculatorsPage> {
page: HealthCalculatorDetailedPage( page: HealthCalculatorDetailedPage(
calculatorType: type == HealthCalculatorEnum.general ? generalHealthServices[index].type : womenHealthServices[index].type, calculatorType: type == HealthCalculatorEnum.general ? generalHealthServices[index].type : womenHealthServices[index].type,
clinicID: type == HealthCalculatorEnum.general ? generalHealthServices[index].clinicID : womenHealthServices[index].clinicID, clinicID: type == HealthCalculatorEnum.general ? generalHealthServices[index].clinicID : womenHealthServices[index].clinicID,
calculationID: type == HealthCalculatorEnum.general ? generalHealthServices[index].calculationID : womenHealthServices[index].calculationID, calculationID:
type == HealthCalculatorEnum.general ? generalHealthServices[index].calculationID : womenHealthServices[index].calculationID,
), ),
), ),
); );
@ -341,5 +347,14 @@ class HealthComponentModel {
int? clinicID; int? clinicID;
int? calculationID; int? calculationID;
HealthComponentModel({required this.title, this.subTitle, required this.icon, this.iconColor, this.bgColor, this.textColor, required this.type, this.clinicID, this.calculationID}); HealthComponentModel(
{required this.title,
this.subTitle,
required this.icon,
this.iconColor,
this.bgColor,
this.textColor,
required this.type,
this.clinicID,
this.calculationID});
} }

@ -1,14 +1,14 @@
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:provider/provider.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart'; import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.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/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.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/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/health_calculators_and_converts/health_calculator_view_model.dart'; import 'package:hmg_patient_app_new/presentation/health_calculators_and_converts/health_calculator_view_model.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:provider/provider.dart';
class BloodCholesterolWidget extends StatefulWidget { class BloodCholesterolWidget extends StatefulWidget {
final Function(dynamic result)? onChange; final Function(dynamic result)? onChange;
@ -107,7 +107,7 @@ class _BloodCholesterolWidgetState extends State<BloodCholesterolWidget> {
], ],
), ),
_buildInputField( _buildInputField(
label:LocaleKeys.mmol.tr(), label: LocaleKeys.mmol.tr(),
hint: "3.1", hint: "3.1",
controller: _mmolController, controller: _mmolController,
focusNode: _mmolFocus, focusNode: _mmolFocus,
@ -123,7 +123,7 @@ class _BloodCholesterolWidgetState extends State<BloodCholesterolWidget> {
Utils.buildSvgWithAssets(icon: AppAssets.globe, width: 18.w, height: 18.w), Utils.buildSvgWithAssets(icon: AppAssets.globe, width: 18.w, height: 18.w),
SizedBox(width: 12.w), SizedBox(width: 12.w),
Expanded( Expanded(
child:LocaleKeys.convertBloodcholesterolInfo.tr(context: context).toText12(isBold: true, color: AppColors.inputLabelTextColor), child: LocaleKeys.convertBloodcholesterolInfo.tr(context: context).toText12(isBold: true, color: AppColors.inputLabelTextColor),
), ),
], ],
).paddingSymmetrical(0.w, 16.w), ).paddingSymmetrical(0.w, 16.w),
@ -149,29 +149,26 @@ class _BloodCholesterolWidgetState extends State<BloodCholesterolWidget> {
isBold: true, isBold: true,
color: AppColors.inputLabelTextColor, color: AppColors.inputLabelTextColor,
), ),
SizedBox( TextField(
height: 40.h, controller: controller,
child: TextField( focusNode: focusNode,
controller: controller, keyboardType: const TextInputType.numberWithOptions(decimal: true),
focusNode: focusNode, maxLines: 1,
keyboardType: const TextInputType.numberWithOptions(decimal: true), onChanged: onChanged,
maxLines: 1, cursorHeight: 35.h,
onChanged: onChanged, textAlignVertical: TextAlignVertical.center,
cursorHeight: 35.h, decoration: InputDecoration(
textAlignVertical: TextAlignVertical.center, border: InputBorder.none,
decoration: InputDecoration( contentPadding: EdgeInsets.zero,
border: InputBorder.none, isCollapsed: true,
contentPadding: EdgeInsets.zero, hintText: hint,
isCollapsed: true, hintStyle: const TextStyle(color: Colors.grey),
hintText: hint, ),
hintStyle: const TextStyle(color: Colors.grey), style: TextStyle(
), fontSize: 32.f,
style: TextStyle( fontWeight: FontWeight.bold,
fontSize: 32.f, color: Colors.black87,
fontWeight: FontWeight.bold, height: 1.h,
color: Colors.black87,
height: 1.h,
),
), ),
), ),
], ],

@ -1,14 +1,14 @@
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:provider/provider.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart'; import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.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/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.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/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/health_calculators_and_converts/health_calculator_view_model.dart'; import 'package:hmg_patient_app_new/presentation/health_calculators_and_converts/health_calculator_view_model.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:provider/provider.dart';
class TriglyceridesWidget extends StatefulWidget { class TriglyceridesWidget extends StatefulWidget {
final Function(dynamic result)? onChange; final Function(dynamic result)? onChange;
@ -91,7 +91,6 @@ class _TriglyceridesWidgetState extends State<TriglyceridesWidget> {
provider.onTriglyceridesMgdlChanged(value); provider.onTriglyceridesMgdlChanged(value);
}, },
).paddingOnly(top: 16.h), ).paddingOnly(top: 16.h),
Row( Row(
children: [ children: [
const Expanded( const Expanded(
@ -111,7 +110,6 @@ class _TriglyceridesWidgetState extends State<TriglyceridesWidget> {
}), }),
], ],
), ),
_buildInputField( _buildInputField(
label: LocaleKeys.mmol, label: LocaleKeys.mmol,
hint: "1.7", hint: "1.7",
@ -122,9 +120,7 @@ class _TriglyceridesWidgetState extends State<TriglyceridesWidget> {
provider.onTriglyceridesMmolChanged(value); provider.onTriglyceridesMmolChanged(value);
}, },
).paddingOnly(bottom: 16.h), ).paddingOnly(bottom: 16.h),
const Divider(height: 1, color: Color(0xFFEEEEEE)), const Divider(height: 1, color: Color(0xFFEEEEEE)),
Row( Row(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
@ -135,12 +131,10 @@ class _TriglyceridesWidgetState extends State<TriglyceridesWidget> {
), ),
SizedBox(width: 12.w), SizedBox(width: 12.w),
Expanded( Expanded(
child: child: LocaleKeys.triglycerideInfo.tr().toText12(
LocaleKeys.triglycerideInfo.tr() isBold: true,
.toText12( color: AppColors.inputLabelTextColor,
isBold: true, ),
color: AppColors.inputLabelTextColor,
),
), ),
], ],
).paddingSymmetrical(0.w, 16.w), ).paddingSymmetrical(0.w, 16.w),
@ -165,27 +159,23 @@ class _TriglyceridesWidgetState extends State<TriglyceridesWidget> {
isBold: true, isBold: true,
color: AppColors.inputLabelTextColor, color: AppColors.inputLabelTextColor,
), ),
SizedBox( TextField(
height: 40.h, controller: controller,
child: TextField( focusNode: focusNode,
controller: controller, keyboardType: const TextInputType.numberWithOptions(decimal: true),
focusNode: focusNode, onChanged: onChanged,
keyboardType: cursorHeight: 35.h,
const TextInputType.numberWithOptions(decimal: true), decoration: InputDecoration(
onChanged: onChanged, border: InputBorder.none,
cursorHeight: 35.h, contentPadding: EdgeInsets.zero,
decoration: InputDecoration( isCollapsed: true,
border: InputBorder.none, hintText: hint,
contentPadding: EdgeInsets.zero, hintStyle: const TextStyle(color: Colors.grey),
isCollapsed: true, ),
hintText: hint, style: TextStyle(
hintStyle: const TextStyle(color: Colors.grey), fontSize: 32.f,
), fontWeight: FontWeight.bold,
style: TextStyle( color: Colors.black87,
fontSize: 32.f,
fontWeight: FontWeight.bold,
color: Colors.black87,
),
), ),
), ),
], ],

@ -1,12 +1,10 @@
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_staggered_animations/flutter_staggered_animations.dart'; import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
import 'package:get_it/get_it.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart'; import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/app_export.dart'; import 'package:hmg_patient_app_new/core/app_export.dart';
import 'package:hmg_patient_app_new/core/app_state.dart'; import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/enums.dart'; import 'package:hmg_patient_app_new/core/enums.dart';
import 'package:hmg_patient_app_new/core/location_util.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart'; import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/route_extensions.dart'; import 'package:hmg_patient_app_new/extensions/route_extensions.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
@ -14,10 +12,8 @@ import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/authentication/authentication_view_model.dart'; import 'package:hmg_patient_app_new/features/authentication/authentication_view_model.dart';
import 'package:hmg_patient_app_new/features/blood_donation/blood_donation_view_model.dart'; import 'package:hmg_patient_app_new/features/blood_donation/blood_donation_view_model.dart';
import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_view_model.dart'; import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_view_model.dart';
import 'package:hmg_patient_app_new/features/emergency_services/emergency_services_view_model.dart';
import 'package:hmg_patient_app_new/features/habib_wallet/habib_wallet_view_model.dart'; import 'package:hmg_patient_app_new/features/habib_wallet/habib_wallet_view_model.dart';
import 'package:hmg_patient_app_new/features/hmg_services/models/ui_models/hmg_services_component_model.dart'; import 'package:hmg_patient_app_new/features/hmg_services/models/ui_models/hmg_services_component_model.dart';
import 'package:hmg_patient_app_new/features/hospital/hospital_selection_view_model.dart';
import 'package:hmg_patient_app_new/features/medical_file/medical_file_view_model.dart'; import 'package:hmg_patient_app_new/features/medical_file/medical_file_view_model.dart';
import 'package:hmg_patient_app_new/features/water_monitor/water_monitor_view_model.dart'; import 'package:hmg_patient_app_new/features/water_monitor/water_monitor_view_model.dart';
import 'package:hmg_patient_app_new/features/weather/weather_view_model.dart'; import 'package:hmg_patient_app_new/features/weather/weather_view_model.dart';
@ -29,13 +25,11 @@ import 'package:hmg_patient_app_new/presentation/emergency_services/emergency_se
import 'package:hmg_patient_app_new/presentation/habib_wallet/habib_wallet_page.dart'; import 'package:hmg_patient_app_new/presentation/habib_wallet/habib_wallet_page.dart';
import 'package:hmg_patient_app_new/presentation/habib_wallet/recharge_wallet_page.dart'; import 'package:hmg_patient_app_new/presentation/habib_wallet/recharge_wallet_page.dart';
import 'package:hmg_patient_app_new/presentation/hmg_services/services_view.dart'; import 'package:hmg_patient_app_new/presentation/hmg_services/services_view.dart';
import 'package:hmg_patient_app_new/presentation/hmg_services/widgets/weather_widget.dart';
import 'package:hmg_patient_app_new/presentation/home/data/landing_page_data.dart'; import 'package:hmg_patient_app_new/presentation/home/data/landing_page_data.dart';
import 'package:hmg_patient_app_new/presentation/home/service_info_page.dart'; import 'package:hmg_patient_app_new/presentation/home/service_info_page.dart';
import 'package:hmg_patient_app_new/presentation/home/widgets/large_service_card.dart'; import 'package:hmg_patient_app_new/presentation/home/widgets/large_service_card.dart';
import 'package:hmg_patient_app_new/presentation/hmg_services/widgets/weather_widget.dart';
import 'package:hmg_patient_app_new/presentation/medical_file/medical_file_page.dart';
import 'package:hmg_patient_app_new/presentation/my_family/my_family.dart'; import 'package:hmg_patient_app_new/presentation/my_family/my_family.dart';
import 'package:hmg_patient_app_new/presentation/parking/paking_page.dart';
import 'package:hmg_patient_app_new/presentation/servicesPriceList/services_price_list_page.dart'; import 'package:hmg_patient_app_new/presentation/servicesPriceList/services_price_list_page.dart';
import 'package:hmg_patient_app_new/services/dialog_service.dart'; import 'package:hmg_patient_app_new/services/dialog_service.dart';
import 'package:hmg_patient_app_new/services/navigation_service.dart'; import 'package:hmg_patient_app_new/services/navigation_service.dart';
@ -49,8 +43,6 @@ import 'package:provider/provider.dart';
import 'package:url_launcher/url_launcher.dart'; import 'package:url_launcher/url_launcher.dart';
import '../../core/dependencies.dart' show getIt; import '../../core/dependencies.dart' show getIt;
import '../../features/qr_parking/qr_parking_view_model.dart';
import '../emergency_services/call_ambulance/widgets/HospitalBottomSheetBody.dart';
class ServicesPage extends StatefulWidget { class ServicesPage extends StatefulWidget {
bool showBackIcon; bool showBackIcon;
@ -69,7 +61,14 @@ class _ServicesPageState extends State<ServicesPage> {
late WeatherMonitorViewModel weatherVM; late WeatherMonitorViewModel weatherVM;
late final List<HmgServicesComponentModel> hmgServices = [ late final List<HmgServicesComponentModel> hmgServices = [
HmgServicesComponentModel(11, LocaleKeys.emergencyServices.tr(), "", AppAssets.emergency_services_icon, bgColor: AppColors.primaryRedColor, true, route: null, onTap: () async { HmgServicesComponentModel(
11,
LocaleKeys.emergencyServices.tr(),
"",
AppAssets.emergency_services_icon,
bgColor: AppColors.primaryRedColor,
true,
route: null, onTap: () async {
// if (getIt.get<AppState>().isAuthenticated) { // if (getIt.get<AppState>().isAuthenticated) {
// getIt.get<EmergencyServicesViewModel>().flushData(); // getIt.get<EmergencyServicesViewModel>().flushData();
// getIt.get<EmergencyServicesViewModel>().getTransportationOrders( // getIt.get<EmergencyServicesViewModel>().getTransportationOrders(
@ -88,11 +87,19 @@ class _ServicesPageState extends State<ServicesPage> {
// await getIt.get<AuthenticationViewModel>().onLoginPressed(); // await getIt.get<AuthenticationViewModel>().onLoginPressed();
// } // }
}), }),
HmgServicesComponentModel(11, LocaleKeys.bookAppointmentService.tr(), "", AppAssets.appointment_calendar_icon, bgColor: AppColors.bookAppointment, true, route: null, onTap: () { HmgServicesComponentModel(
11,
LocaleKeys.bookAppointmentService.tr(),
"",
AppAssets.appointment_calendar_icon,
bgColor: AppColors.bookAppointment,
true,
route: null, onTap: () {
getIt.get<BookAppointmentsViewModel>().onTabChanged(0); getIt.get<BookAppointmentsViewModel>().onTabChanged(0);
Navigator.of(getIt<NavigationService>().navigatorKey.currentContext!).push(CustomPageRoute(page: BookAppointmentPage())); Navigator.of(getIt<NavigationService>().navigatorKey.currentContext!).push(CustomPageRoute(page: BookAppointmentPage()));
}), }),
HmgServicesComponentModel(5, LocaleKeys.completeCheckup.tr(), "", AppAssets.comprehensiveCheckup, bgColor: AppColors.bgGreenColor, true, route: null, onTap: () async { HmgServicesComponentModel(
5, LocaleKeys.completeCheckup.tr(), "", AppAssets.comprehensiveCheckup, bgColor: AppColors.bgGreenColor, true, route: null, onTap: () async {
if (getIt.get<AppState>().isAuthenticated) { if (getIt.get<AppState>().isAuthenticated) {
getIt.get<NavigationService>().pushPageRoute(AppRoutes.comprehensiveCheckupPage); getIt.get<NavigationService>().pushPageRoute(AppRoutes.comprehensiveCheckupPage);
} else { } else {
@ -152,7 +159,8 @@ class _ServicesPageState extends State<ServicesPage> {
// ); // );
// }, // },
// ), // ),
HmgServicesComponentModel(11, LocaleKeys.eReferralServices.tr(), "", AppAssets.eReferral, bgColor: AppColors.eReferralCardColor, true, route: null, onTap: () async { HmgServicesComponentModel(
11, LocaleKeys.eReferralServices.tr(), "", AppAssets.eReferral, bgColor: AppColors.eReferralCardColor, true, route: null, onTap: () async {
if (getIt.get<AppState>().isAuthenticated) { if (getIt.get<AppState>().isAuthenticated) {
getIt.get<NavigationService>().pushPageRoute(AppRoutes.eReferralPage); getIt.get<NavigationService>().pushPageRoute(AppRoutes.eReferralPage);
} else { } else {
@ -403,14 +411,13 @@ class _ServicesPageState extends State<ServicesPage> {
}, },
), ),
HmgServicesComponentModel( HmgServicesComponentModel(
103, 103,
LocaleKeys.watchUsOnYoutube.tr(), LocaleKeys.watchUsOnYoutube.tr(),
"", "",
AppAssets.youtube, AppAssets.youtube,
bgColor: AppColors.whiteColor, bgColor: AppColors.whiteColor,
true, true,
onTap:()=> launchUrl(Uri.parse("https://www.youtube.com/c/DrsulaimanAlhabibHospitals")) onTap: () => launchUrl(Uri.parse("https://www.youtube.com/c/DrsulaimanAlhabibHospitals"))),
),
HmgServicesComponentModel( HmgServicesComponentModel(
104, 104,
LocaleKeys.connectOnLinkedin.tr(), LocaleKeys.connectOnLinkedin.tr(),
@ -418,9 +425,8 @@ class _ServicesPageState extends State<ServicesPage> {
AppAssets.linkedin, AppAssets.linkedin,
bgColor: AppColors.whiteColor, bgColor: AppColors.whiteColor,
true, true,
onTap:()=> launchUrl(Uri.parse("https://www.linkedin.com/company/drsulaiman-alhabib-medical-group")), onTap: () => launchUrl(Uri.parse("https://www.linkedin.com/company/drsulaiman-alhabib-medical-group")),
), ),
]; ];
@override @override
@ -431,8 +437,6 @@ class _ServicesPageState extends State<ServicesPage> {
weatherVM.initiateFetchWeather(); weatherVM.initiateFetchWeather();
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
bloodDonationViewModel = Provider.of<BloodDonationViewModel>(context); bloodDonationViewModel = Provider.of<BloodDonationViewModel>(context);
@ -451,456 +455,393 @@ class _ServicesPageState extends State<ServicesPage> {
children: [ children: [
const WeatherWidget(), const WeatherWidget(),
SizedBox(height: 16.h), SizedBox(height: 16.h),
LocaleKeys.medicalAndCareServices.tr().toText18(isBold: true).paddingSymmetrical(24.w, 0), LocaleKeys.medicalAndCareServices.tr().toText18(isBold: true).paddingSymmetrical(24.w, 0),
SizedBox(height: 16.h), SizedBox(height: 16.h),
GridView.builder( GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: (isFoldable || isTablet) ? 6 : 4, // 4 icons per row crossAxisCount: (isFoldable || isTablet) ? 5 : 4, // 4 icons per row
crossAxisSpacing: 21.w, ),
mainAxisSpacing: 18.h, physics: NeverScrollableScrollPhysics(),
childAspectRatio: 80 / 94),
physics: NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemCount: hmgServices.length,
padding: EdgeInsets.zero,
itemBuilder: (BuildContext context, int index) {
return ServiceGridViewItem(hmgServices[index], index, false, isHealthToolIcon: false);
},
).paddingSymmetrical(24.w, 0),
SizedBox(height: 24.h),
LocaleKeys.hmgServices.tr().toText18(isBold: true).paddingSymmetrical(24.w, 0),
SizedBox(height: 16.h),
SizedBox(
height: 350.h,
child: ListView.separated(
scrollDirection: Axis.horizontal,
itemCount: LandingPageData.getServiceCardsList.length,
shrinkWrap: true, shrinkWrap: true,
padding: EdgeInsets.symmetric(horizontal: 24.w), itemCount: hmgServices.length,
itemBuilder: (context, index) { padding: EdgeInsets.zero,
return AnimationConfiguration.staggeredList( itemBuilder: (BuildContext context, int index) {
position: index, return ServiceGridViewItem(hmgServices[index], index, false, isHealthToolIcon: false);
duration: const Duration(milliseconds: 1000), },
child: SlideAnimation( ).paddingSymmetrical(24.w, 0),
horizontalOffset: 100.0, SizedBox(height: 24.h),
child: FadeInAnimation( LocaleKeys.hmgServices.tr().toText18(isBold: true).paddingSymmetrical(24.w, 0),
child: LargeServiceCard( SizedBox(height: 16.h),
serviceCardData: LandingPageData.getServiceCardsList[index], ConstrainedBox(
image: LandingPageData.getServiceCardsList[index].icon, constraints: BoxConstraints(
title: LandingPageData.getServiceCardsList[index].title, minHeight: 320.h,
subtitle: LandingPageData.getServiceCardsList[index].subtitle, maxHeight: isFoldable ? 400.h : (isTablet ? 360.h : 340.h),
icon: LandingPageData.getServiceCardsList[index].largeCardIcon, ),
isPNG: LandingPageData.getServiceCardsList[index].isPNG, child: ListView.separated(
scrollDirection: Axis.horizontal,
itemCount: LandingPageData.getServiceCardsList.length,
shrinkWrap: true,
padding: EdgeInsets.symmetric(horizontal: 24.w),
itemBuilder: (context, index) {
return AnimationConfiguration.staggeredList(
position: index,
duration: const Duration(milliseconds: 1000),
child: SlideAnimation(
horizontalOffset: 100.0,
child: FadeInAnimation(
child: LargeServiceCard(
serviceCardData: LandingPageData.getServiceCardsList[index],
image: LandingPageData.getServiceCardsList[index].icon,
title: LandingPageData.getServiceCardsList[index].title,
subtitle: LandingPageData.getServiceCardsList[index].subtitle,
icon: LandingPageData.getServiceCardsList[index].largeCardIcon,
isPNG: LandingPageData.getServiceCardsList[index].isPNG,
),
), ),
), ),
), );
},
separatorBuilder: (BuildContext cxt, int index) => SizedBox(width: 16.w),
),
),
SizedBox(height: 24.h),
getIt.get<AppState>().isAuthenticated
? Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
LocaleKeys.personalServices.tr().toText18(isBold: true).paddingSymmetrical(24.w, 0),
SizedBox(height: 16.h),
Row(
children: [
Expanded(
child: Container(
height: 183.h,
width: 183.h,
padding: EdgeInsets.all(16.w),
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 20.r,
hasShadow: false,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
spacing: 8.w,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Utils.buildSvgWithAssets(icon: AppAssets.wallet, width: 40.w, height: 40.h, applyThemeColor: false),
LocaleKeys.habibWallet.tr().toText14(isBold: true, maxlines: 2).expanded,
Utils.buildSvgWithAssets(
icon: getIt.get<AppState>().isArabic() ? AppAssets.arrow_back : AppAssets.arrow_forward),
],
),
Spacer(),
getIt.get<AppState>().isAuthenticated
? Consumer<HabibWalletViewModel>(builder: (context, habibWalletVM, child) {
return Utils.getPaymentAmountWithSymbol2(
num.parse(NumberFormat.decimalPattern().format(habibWalletVM.habibWalletAmount)),
isExpanded: false,
letterSpacing: -1)
.toShimmer2(isShow: habibWalletVM.isWalletAmountLoading, radius: 12.r, width: 80.w, height: 24.h);
})
: LocaleKeys.loginToViewWalletBalance.tr().toText12(isBold: true, maxLine: 2),
Spacer(),
getIt.get<AppState>().isAuthenticated
? CustomButton(
height: 40.h,
icon: AppAssets.recharge_icon,
iconSize: 24.w,
iconColor: AppColors.infoColor,
textColor: AppColors.infoColor,
text: LocaleKeys.recharge.tr(),
borderWidth: 0.w,
isBold: true,
borderColor: Colors.transparent,
backgroundColor: Color(0xff45A2F8).withValues(alpha: 0.08),
padding: EdgeInsets.all(8.w),
fontSize: 14.f,
onPressed: () {
Navigator.of(context).push(CustomPageRoute(page: RechargeWalletPage()));
},
)
: SizedBox.shrink(),
],
).onPress(() async {
if (getIt.get<AppState>().isAuthenticated) {
Navigator.of(context).push(CustomPageRoute(page: HabibWalletPage()));
} else {
await getIt.get<AuthenticationViewModel>().onLoginPressed();
}
}),
),
),
SizedBox(width: 16.w),
Expanded(
child: Container(
height: 183.h,
width: 183.h,
padding: EdgeInsets.all(16.w),
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 20.r,
hasShadow: false,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
spacing: 8.w,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Utils.buildSvgWithAssets(
icon: AppAssets.services_medical_file_icon, width: 40.w, height: 40.h, applyThemeColor: false),
LocaleKeys.familyTitle.tr().toText16(isBold: true, maxlines: 2).expanded,
Utils.buildSvgWithAssets(
icon: getIt.get<AppState>().isArabic() ? AppAssets.arrow_back : AppAssets.arrow_forward),
],
),
Spacer(),
getIt.get<AppState>().isAuthenticated
? Wrap(
spacing: -12.h,
// runSpacing: 0.h,
children: [
Utils.buildImgWithAssets(
icon: AppAssets.babyGirlImg,
height: 32.h,
width: 32.w,
border: 1,
fit: BoxFit.contain,
borderRadius: 50.r,
),
Utils.buildImgWithAssets(
icon: AppAssets.femaleImg,
height: 32.h,
width: 32.w,
border: 1,
borderRadius: 50.r,
fit: BoxFit.contain,
),
Utils.buildImgWithAssets(
icon: AppAssets.maleImg,
height: 32.h,
width: 32.w,
border: 1,
borderRadius: 50.r,
fit: BoxFit.contain,
),
],
)
: LocaleKeys.loginToViewMedicalFile.tr().toText12(isBold: true, maxLine: 2),
Spacer(),
getIt.get<AppState>().isAuthenticated
? CustomButton(
height: 40.h,
icon: AppAssets.add_icon,
iconSize: 24.h,
iconColor: AppColors.primaryRedColor,
textColor: AppColors.primaryRedColor,
text: getIt.get<AppState>().isArabic() ? LocaleKeys.add.tr() : LocaleKeys.addMember.tr(),
borderWidth: 0.w,
isBold: true,
borderColor: Colors.transparent,
backgroundColor: AppColors.primaryRedColor.withValues(alpha: 0.08),
padding: EdgeInsets.all(8.w),
fontSize: 14.f,
onPressed: () {
DialogService dialogService = getIt.get<DialogService>();
medicalFileViewModel.clearAuthValues();
dialogService.showAddFamilyFileSheet(
label: LocaleKeys.addFamilyMember.tr(),
message: LocaleKeys.pleaseFillBelowFieldToAddNewFamilyMember.tr(),
onVerificationPress: () {
medicalFileViewModel.addFamilyFile(otpTypeEnum: OTPTypeEnum.sms);
});
},
)
: SizedBox.shrink(),
],
).onPress(() async {
if (getIt.get<AppState>().isAuthenticated) {
// Navigator.of(context).push(
// CustomPageRoute(
// page: MedicalFilePage(),
// ),
// );
Navigator.of(context).push(
CustomPageRoute(
direction: AxisDirection.down,
page: FamilyMedicalScreen(),
),
);
} else {
await getIt.get<AuthenticationViewModel>().onLoginPressed();
}
}),
),
),
],
).paddingSymmetrical(24.w, 0),
],
)
: SizedBox(),
SizedBox(height: 24.h),
LocaleKeys.healthTools.tr().toText18(isBold: true).paddingSymmetrical(24.w, 0),
SizedBox(height: 16.h),
GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: (isFoldable || isTablet) ? 5 : 4, // 4 icons per row
mainAxisSpacing: 18.h,
),
physics: NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemCount: hmgHealthToolServices.length,
padding: EdgeInsets.zero,
itemBuilder: (BuildContext context, int index) {
return ServiceGridViewItem(
hmgHealthToolServices[index],
index,
false,
isHealthToolIcon: true,
); );
}, },
separatorBuilder: (BuildContext cxt, int index) => SizedBox(width: 16.w), ).paddingSymmetrical(24.w, 0),
), SizedBox(height: 24.h),
), LocaleKeys.supportServices.tr().toText18(isBold: true).paddingSymmetrical(24.w, 0),
SizedBox(height: 24.h), SizedBox(height: 16.h),
getIt.get<AppState>().isAuthenticated Column(
? Column( crossAxisAlignment: CrossAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start, children: [
SizedBox(height: 16.h),
Row(
children: [ children: [
LocaleKeys.personalServices.tr().toText18(isBold: true).paddingSymmetrical(24.w, 0), Expanded(
SizedBox(height: 16.h), child: Container(
Row( decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
children: [ color: AppColors.whiteColor,
Expanded( borderRadius: 12.h,
child: Container( hasShadow: false,
height: 183.h, ),
width: 183.h, child: Padding(
padding: EdgeInsets.all(16.w), padding: EdgeInsets.all(16.h),
decoration: RoundedRectangleBorder().toSmoothCornerDecoration( child: Row(
color: AppColors.whiteColor, children: [
borderRadius: 20.r, Utils.buildSvgWithAssets(
hasShadow: false, icon: AppAssets.latest_news_icon,
), width: 32.h,
child: Column( height: 32.h,
crossAxisAlignment: CrossAxisAlignment.start, fit: BoxFit.contain,
children: [ ),
Row( SizedBox(width: 8.w),
spacing: 8.w, LocaleKeys.latestNews.tr().toText14(isBold: true)
crossAxisAlignment: CrossAxisAlignment.center, ],
children: [
Utils.buildSvgWithAssets(icon: AppAssets.wallet, width: 40.w, height: 40.h, applyThemeColor: false),
LocaleKeys.habibWallet.tr().toText14(isBold: true, maxlines: 2).expanded,
Utils.buildSvgWithAssets(icon: getIt.get<AppState>().isArabic() ? AppAssets.arrow_back : AppAssets.arrow_forward),
],
),
Spacer(),
getIt.get<AppState>().isAuthenticated
? Consumer<HabibWalletViewModel>(builder: (context, habibWalletVM, child) {
return Utils.getPaymentAmountWithSymbol2(num.parse(NumberFormat.decimalPattern().format(habibWalletVM.habibWalletAmount)),
isExpanded: false, letterSpacing: -1)
.toShimmer2(isShow: habibWalletVM.isWalletAmountLoading, radius: 12.r, width: 80.w, height: 24.h);
})
: LocaleKeys.loginToViewWalletBalance.tr().toText12(isBold: true, maxLine: 2),
Spacer(),
getIt.get<AppState>().isAuthenticated
? CustomButton(
height: 40.h,
icon: AppAssets.recharge_icon,
iconSize: 24.w,
iconColor: AppColors.infoColor,
textColor: AppColors.infoColor,
text: LocaleKeys.recharge.tr(),
borderWidth: 0.w,
isBold: true,
borderColor: Colors.transparent,
backgroundColor: Color(0xff45A2F8).withValues(alpha: 0.08),
padding: EdgeInsets.all(8.w),
fontSize: 14.f,
onPressed: () {
Navigator.of(context).push(CustomPageRoute(page: RechargeWalletPage()));
},
)
: SizedBox.shrink(),
],
).onPress(() async {
if (getIt.get<AppState>().isAuthenticated) {
Navigator.of(context).push(CustomPageRoute(page: HabibWalletPage()));
} else {
await getIt.get<AuthenticationViewModel>().onLoginPressed();
}
}),
), ),
), ),
SizedBox(width: 16.w), ).onPress(() {
Expanded( Utils.openWebView(
child: Container( url: 'https://x.com/HMG',
height: 183.h, );
width: 183.h, }),
padding: EdgeInsets.all(16.w), ),
decoration: RoundedRectangleBorder().toSmoothCornerDecoration( SizedBox(width: 16.w),
color: AppColors.whiteColor, Expanded(
borderRadius: 20.r, child: Container(
hasShadow: false, decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
), color: AppColors.whiteColor,
child: Column( borderRadius: 12.h,
crossAxisAlignment: CrossAxisAlignment.start, hasShadow: false,
children: [ ),
Row( child: Padding(
spacing: 8.w, padding: EdgeInsets.all(16.h),
crossAxisAlignment: CrossAxisAlignment.center, child: Row(
children: [ children: [
Utils.buildSvgWithAssets(icon: AppAssets.services_medical_file_icon, width: 40.w, height: 40.h, applyThemeColor: false), Utils.buildSvgWithAssets(
LocaleKeys.familyTitle.tr().toText16(isBold: true, maxlines: 2).expanded, icon: AppAssets.hmg_contact_icon,
Utils.buildSvgWithAssets(icon: getIt.get<AppState>().isArabic() ? AppAssets.arrow_back : AppAssets.arrow_forward), width: 32.h,
], height: 32.h,
), fit: BoxFit.contain,
Spacer(), ),
getIt.get<AppState>().isAuthenticated SizedBox(width: 8.w),
? Wrap( Expanded(child: LocaleKeys.hmgContact.tr().toText14(isBold: true))
spacing: -12.h, ],
// runSpacing: 0.h,
children: [
Utils.buildImgWithAssets(
icon: AppAssets.babyGirlImg,
height: 32.h,
width: 32.w,
border: 1,
fit: BoxFit.contain,
borderRadius: 50.r,
),
Utils.buildImgWithAssets(
icon: AppAssets.femaleImg,
height: 32.h,
width: 32.w,
border: 1,
borderRadius: 50.r,
fit: BoxFit.contain,
),
Utils.buildImgWithAssets(
icon: AppAssets.maleImg,
height: 32.h,
width: 32.w,
border: 1,
borderRadius: 50.r,
fit: BoxFit.contain,
),
],
)
: LocaleKeys.loginToViewMedicalFile.tr().toText12(isBold: true, maxLine: 2),
Spacer(),
getIt.get<AppState>().isAuthenticated
? CustomButton(
height: 40.h,
icon: AppAssets.add_icon,
iconSize: 24.h,
iconColor: AppColors.primaryRedColor,
textColor: AppColors.primaryRedColor,
text: getIt.get<AppState>().isArabic() ? LocaleKeys.add.tr() :LocaleKeys.addMember.tr(),
borderWidth: 0.w,
isBold: true,
borderColor: Colors.transparent,
backgroundColor: AppColors.primaryRedColor.withValues(alpha: 0.08),
padding: EdgeInsets.all(8.w),
fontSize: 14.f,
onPressed: () {
DialogService dialogService = getIt.get<DialogService>();
medicalFileViewModel.clearAuthValues();
dialogService.showAddFamilyFileSheet(
label: LocaleKeys.addFamilyMember.tr(),
message: LocaleKeys.pleaseFillBelowFieldToAddNewFamilyMember.tr(),
onVerificationPress: () {
medicalFileViewModel.addFamilyFile(otpTypeEnum: OTPTypeEnum.sms);
});
},
)
: SizedBox.shrink(),
],
).onPress(() async {
if (getIt.get<AppState>().isAuthenticated) {
// Navigator.of(context).push(
// CustomPageRoute(
// page: MedicalFilePage(),
// ),
// );
Navigator.of(context).push(
CustomPageRoute(
direction: AxisDirection.down,
page: FamilyMedicalScreen(),
),
);
} else {
await getIt.get<AuthenticationViewModel>().onLoginPressed();
}
}),
), ),
), ),
], ).onPress(() {
).paddingSymmetrical(24.w, 0), showCommonBottomSheetWithoutHeight(
context,
title: LocaleKeys.contactUs.tr(),
child: ContactUs(),
callBackFunc: () {},
isFullScreen: false,
);
}),
)
], ],
) ),
: SizedBox(), SizedBox(height: 24.h),
SizedBox(height: 24.h), LocaleKeys.hmgPolicies.tr().toText18(weight: FontWeight.bold),
LocaleKeys.healthTools.tr().toText18(isBold: true).paddingSymmetrical(24.w, 0), SizedBox(height: 16.h),
SizedBox(height: 16.h), Row(
GridView.builder( children: [
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( Expanded(
crossAxisCount: (isFoldable || isTablet) ? 6 : 4, // 4 icons per row child: Container(
crossAxisSpacing: 21.w, decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
mainAxisSpacing: 18.h, color: AppColors.whiteColor,
childAspectRatio: 80.w / 94.h, borderRadius: 12.h,
), hasShadow: false,
physics: NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemCount: hmgHealthToolServices.length,
padding: EdgeInsets.zero,
itemBuilder: (BuildContext context, int index) {
return ServiceGridViewItem(
hmgHealthToolServices[index],
index,
false,
isHealthToolIcon: true,
);
},
).paddingSymmetrical(24.w, 0),
SizedBox(height: 24.h),
LocaleKeys.supportServices.tr().toText18(isBold: true).paddingSymmetrical(24.w, 0),
SizedBox(height: 16.h),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Row(
// children: [
// // Expanded(
// // child: Container(
// // decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
// // color: AppColors.whiteColor,
// // borderRadius: 12.h,
// // hasShadow: false,
// // ),
// // child: Padding(
// // padding: EdgeInsets.all(16.h),
// // child: Row(
// // children: [
// // Utils.buildSvgWithAssets(
// // icon: AppAssets.virtual_tour_icon,
// // width: 32.w,
// // height: 32.h,
// // fit: BoxFit.contain,
// // ),
// // SizedBox(width: 8.w),
// // LocaleKeys.virtualTour.tr().toText14(isBold: true)
// // ],
// // ),
// // ),
// // ).onPress(() {
// // Utils.openWebView(
// // url: 'https://hmgwebservices.com/vt_mobile/html/index.html',
// // );
// // }),
// // ),
// SizedBox(width: 16.w),
// Expanded(
// child: Container(
// decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
// color: AppColors.whiteColor,
// borderRadius: 12.h,
// hasShadow: false,
// ),
// child: Padding(
// padding: EdgeInsets.all(16.h),
// child: Row(
// children: [
// Utils.buildSvgWithAssets(
// icon: AppAssets.car_parking_icon,
// width: 32.w,
// height: 32.h,
// fit: BoxFit.contain,
// ),
// SizedBox(width: 8.w),
// LocaleKeys.carParking.tr().toText14(isBold: true)
// ],
// ).onPress(() {
// Navigator.push(
// context,
// MaterialPageRoute(
// builder: (_) => ChangeNotifierProvider(
// create: (_) => getIt<QrParkingViewModel>(),
// child: const ParkingPage(),
// ),
// ),
// );
// }),
// ),
// ),
// ),
// ],
// ),
SizedBox(height: 16.h),
Row(
children: [
Expanded(
child: Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 12.h,
hasShadow: false,
),
child: Padding(
padding: EdgeInsets.all(16.h),
child: Row(
children: [
Utils.buildSvgWithAssets(
icon: AppAssets.latest_news_icon,
width: 32.w,
height: 32.h,
fit: BoxFit.contain,
),
SizedBox(width: 8.w),
LocaleKeys.latestNews.tr().toText14(isBold: true)
],
), ),
), child: Padding(
).onPress(() { padding: EdgeInsets.all(16.h),
Utils.openWebView( child: Row(
url: 'https://x.com/HMG', children: [
); Utils.buildSvgWithAssets(
}), icon: AppAssets.privacy_terms, width: 32.w, height: 32.h, fit: BoxFit.contain, iconColor: AppColors.blackColor),
), SizedBox(width: 8.w),
SizedBox(width: 16.w), Expanded(child: LocaleKeys.termsConditoins.tr().toText14(isBold: true))
Expanded( ],
child: Container( ),
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 12.h,
hasShadow: false,
),
child: Padding(
padding: EdgeInsets.all(16.h),
child: Row(
children: [
Utils.buildSvgWithAssets(
icon: AppAssets.hmg_contact_icon,
width: 32.w,
height: 32.h,
fit: BoxFit.contain,
),
SizedBox(width: 8.w),
Expanded(child: LocaleKeys.hmgContact.tr().toText14(isBold: true))
],
), ),
), ).onPress(() {
).onPress(() { Utils.openWebView(
showCommonBottomSheetWithoutHeight( url: 'https://hmg.com/en/Pages/Terms.aspx',
context, );
title: LocaleKeys.contactUs.tr(), }),
child: ContactUs(), ),
callBackFunc: () {}, SizedBox(width: 16.w),
isFullScreen: false, Expanded(
); child: Container(
}), decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
) color: AppColors.whiteColor,
], borderRadius: 12.h,
), hasShadow: false,
SizedBox(height: 24.h),
LocaleKeys.hmgPolicies.tr().toText18(weight: FontWeight.bold),
SizedBox(height: 16.h),
Row(
children: [
Expanded(
child: Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 12.h,
hasShadow: false,
),
child: Padding(
padding: EdgeInsets.all(16.h),
child: Row(
children: [
Utils.buildSvgWithAssets(icon: AppAssets.privacy_terms, width: 32.w, height: 32.h, fit: BoxFit.contain, iconColor: AppColors.blackColor),
SizedBox(width: 8.w),
Expanded(child: LocaleKeys.termsConditoins.tr().toText14(isBold: true))
],
), ),
), child: Padding(
).onPress(() { padding: EdgeInsets.all(16.h),
Utils.openWebView( child: Row(
url: 'https://hmg.com/en/Pages/Terms.aspx', children: [
); Utils.buildSvgWithAssets(
}), icon: AppAssets.privacy_terms, width: 32.w, height: 32.h, fit: BoxFit.contain, iconColor: AppColors.blackColor),
), SizedBox(width: 8.w),
SizedBox(width: 16.w), Expanded(child: LocaleKeys.privacyPolicy.tr().toText14(isBold: true))
Expanded( ],
child: Container( ),
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 12.h,
hasShadow: false,
),
child: Padding(
padding: EdgeInsets.all(16.h),
child: Row(
children: [
Utils.buildSvgWithAssets(icon: AppAssets.privacy_terms, width: 32.w, height: 32.h, fit: BoxFit.contain, iconColor: AppColors.blackColor),
SizedBox(width: 8.w),
Expanded(child: LocaleKeys.privacyPolicy.tr().toText14(isBold: true))
],
), ),
), ).onPress(() {
).onPress(() { Utils.openWebView(
Utils.openWebView( url: 'https://hmg.com/en/Pages/Privacy.aspx',
url: 'https://hmg.com/en/Pages/Privacy.aspx', );
); }),
}), )
) ],
], )
) ],
], ).paddingSymmetrical(24.w, 0),
).paddingSymmetrical(24.w, 0),
SizedBox(height: 16.h), SizedBox(height: 16.h),
GridView.builder( GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: (isFoldable || isTablet) ? 6 : 4, // 4 icons per row crossAxisCount: (isFoldable || isTablet) ? 5 : 4, // 4 icons per row
crossAxisSpacing: 21.w,
mainAxisSpacing: 18.h, mainAxisSpacing: 18.h,
childAspectRatio: 80.w / 94.h,
), ),
physics: NeverScrollableScrollPhysics(), physics: NeverScrollableScrollPhysics(),
shrinkWrap: true, shrinkWrap: true,
@ -915,11 +856,11 @@ class _ServicesPageState extends State<ServicesPage> {
); );
}, },
).paddingSymmetrical(24.w, 0), ).paddingSymmetrical(24.w, 0),
SizedBox(height: 24.h), SizedBox(height: 24.h),
], ],
),
), ),
), ),
),
); );
} }
} }

File diff suppressed because it is too large Load Diff

@ -43,83 +43,87 @@ class LargeServiceCard extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Container( return Container(
height: 350.h,
width: 230.w, width: 230.w,
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.transparent, borderRadius: 24.r), decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
child: Stack( color: AppColors.whiteColor,
borderRadius: 24.r,
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [ children: [
ClipRRect( ClipRRect(
borderRadius: BorderRadius.circular(24.r), borderRadius: BorderRadius.only(
topLeft: Radius.circular(24.r),
topRight: Radius.circular(24.r),
),
child: Image.asset( child: Image.asset(
serviceCardData.largeCardIcon, serviceCardData.largeCardIcon,
fit: BoxFit.cover, fit: BoxFit.cover,
width: double.infinity,
height: isFoldable ? 190.h : (isTablet ? 200.h : 180.h),
), ),
), ),
Positioned( Container(
bottom: 0.0, // Positions the child 0 logical pixels from the bottom padding: EdgeInsets.all(16.w),
left: 0.0, child: Column(
right: 0.0, mainAxisSize: MainAxisSize.min,
child: Container( children: [
height: 180.h, Row(
padding: EdgeInsets.only(bottom: 16.h, top: 16.h), crossAxisAlignment: CrossAxisAlignment.start,
decoration: RoundedRectangleBorder().toSmoothCornerDecoration( children: [
color: AppColors.whiteColor, isPNG
customBorder: BorderRadius.only( ? Image.asset(serviceCardData.icon, width: 35.h, height: 35.h)
bottomLeft: Radius.circular(24.r), : Container(
bottomRight: Radius.circular(24.r), height: 48.h,
), width: 48.h,
), decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
child: Column( color: serviceCardData.backgroundColor!,
children: [ borderRadius: 12.r,
Row( hasShadow: false,
crossAxisAlignment: CrossAxisAlignment.start, ),
children: [ child: Padding(
isPNG ? Image.asset(serviceCardData.icon, width: 35.h, height: 35.h) : Container( padding: EdgeInsets.all(12.h),
height: 48.h, child: Utils.buildSvgWithAssets(
width: 48.h, icon: serviceCardData.icon,
decoration: RoundedRectangleBorder().toSmoothCornerDecoration( iconColor: serviceCardData.iconColor,
color: serviceCardData.backgroundColor!, fit: BoxFit.contain,
borderRadius: 12.r, applyThemeColor: false,
hasShadow: false, ),
), ),
child: Padding(
padding: EdgeInsets.all(12.h),
child: Utils.buildSvgWithAssets(
icon: serviceCardData.icon,
iconColor: serviceCardData.iconColor,
fit: BoxFit.contain,
applyThemeColor: false,
), ),
), SizedBox(width: 12.w),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
serviceCardData.title.tr(context: context).toText14(
isBold: true,
color: AppColors.textColor,
maxlines: 1,
textOverflow: TextOverflow.ellipsis,
),
serviceCardData.subtitle.tr(context: context).toText12(isBold: true, color: AppColors.textColorLight, maxLine: 2),
],
), ),
SizedBox(width: 12.w), ),
Expanded( ],
child: Column( ),
crossAxisAlignment: CrossAxisAlignment.start, SizedBox(height: 24.h),
children: [ CustomButton(
serviceCardData.title.tr(context: context).toText14(isBold: true, color: AppColors.textColor), text: serviceCardData.isBold ? LocaleKeys.visitPharmacyOnline.tr(context: context) : LocaleKeys.bookNow.tr(context: context),
serviceCardData.subtitle.tr(context: context).toText12(isBold: true, color: AppColors.textColorLight, maxLine: 2), onPressed: () {
], handleOnTap();
), },
), padding: EdgeInsets.zero,
], backgroundColor: serviceCardData.isBold ? AppColors.successLightColor.withValues(alpha: 0.2) : AppColors.bgRedLightColor,
).paddingSymmetrical(8.w, 0.h).expanded, borderColor: serviceCardData.isBold ? AppColors.successLightColor.withValues(alpha: 0.01) : AppColors.bgRedLightColor,
CustomButton( textColor: serviceCardData.isBold ? AppColors.successColor : AppColors.primaryRedColor,
text: serviceCardData.isBold ? LocaleKeys.visitPharmacyOnline.tr(context: context) : LocaleKeys.bookNow.tr(context: context), fontSize: 14.f,
onPressed: () { fontWeight: FontWeight.w600,
handleOnTap(); borderRadius: 10.r,
}, height: 40.h,
padding: EdgeInsets.zero, ),
backgroundColor: serviceCardData.isBold ? AppColors.successLightColor.withValues(alpha: 0.2) : AppColors.bgRedLightColor, ],
borderColor: serviceCardData.isBold ? AppColors.successLightColor.withValues(alpha: 0.01) : AppColors.bgRedLightColor,
textColor: serviceCardData.isBold ? AppColors.successColor : AppColors.primaryRedColor,
fontSize: 14.f,
fontWeight: FontWeight.w600,
borderRadius: 10.r,
height: 40.h,
).paddingSymmetrical(16.w, 0.h),
],
),
), ),
), ),
], ],
@ -229,27 +233,25 @@ class FadedLargeServiceCard extends StatelessWidget {
Row( Row(
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
isPNG ? Image.asset(serviceCardData.icon, width: 32.h, height: 32.h).circle(100.h) : Container( isPNG
height: 32.h, ? Image.asset(serviceCardData.icon, width: 32.h, height: 32.h).circle(100.h)
width: 32.h, : Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration( height: 32.h,
color: serviceCardData.backgroundColor!, width: 32.h,
borderRadius: 30.r, decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
hasShadow: false, color: serviceCardData.backgroundColor!,
), borderRadius: 30.r,
child: Padding( hasShadow: false,
padding: EdgeInsets.all(8.h), ),
child: Transform.flip( child: Padding(
flipX: getIt.get<AppState>().isArabic(), padding: EdgeInsets.all(8.h),
child: Utils.buildSvgWithAssets( child: Transform.flip(
icon: serviceCardData.icon, flipX: getIt.get<AppState>().isArabic(),
iconColor: serviceCardData.iconColor, child: Utils.buildSvgWithAssets(
fit: BoxFit.contain, icon: serviceCardData.icon, iconColor: serviceCardData.iconColor, fit: BoxFit.contain, applyThemeColor: false),
applyThemeColor: false ),
),
), ),
),
),
),
SizedBox(width: 12.w), SizedBox(width: 12.w),
serviceCardData.title.tr(context: context).toText18(isBold: true, color: AppColors.textColor).expanded, serviceCardData.title.tr(context: context).toText18(isBold: true, color: AppColors.textColor).expanded,
], ],

File diff suppressed because it is too large Load Diff

@ -1,4 +1,6 @@
import 'dart:async'; import 'dart:async';
import 'dart:ui' as ui;
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_assets.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/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.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/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 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart';
import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
import 'dart:ui' as ui;
class MedicalFileAppointmentCard extends StatefulWidget { class MedicalFileAppointmentCard extends StatefulWidget {
final PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel; final PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel;
@ -138,174 +138,231 @@ class _MedicalFileAppointmentCardState extends State<MedicalFileAppointmentCard>
richText: Directionality( richText: Directionality(
textDirection: ui.TextDirection.ltr, textDirection: ui.TextDirection.ltr,
child: DateUtil.formatDateToDate(DateUtil.convertStringToDate(widget.patientAppointmentHistoryResponseModel.appointmentDate), false) 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), .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, iconColor: AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel) ? AppColors.textColor : AppColors.primaryRedColor,
iconSize: 16.w, 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, textColor: AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel) ? AppColors.textColor : AppColors.primaryRedColor,
padding: EdgeInsets.only(top: 12.h, bottom: 12.h, left: 8.w, right: 8.w), padding: EdgeInsets.only(top: 12.h, left: 8.w, right: 8.w, bottom: 8.h),
).toShimmer2(isShow: widget.myAppointmentsViewModel.isMyAppointmentsLoading), ).toShimmer2(isShow: widget.myAppointmentsViewModel.isMyAppointmentsLoading),
SizedBox(height: 16.h), SizedBox(height: 16.h),
Container( IntrinsicWidth(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.r, hasShadow: false), child: Container(
width: 200.w, decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.r, hasShadow: false),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Row( Row(
children: [ children: [
Image.network( Image.network(
widget.patientAppointmentHistoryResponseModel.doctorImageURL ?? "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png", widget.patientAppointmentHistoryResponseModel.doctorImageURL ??
width: 25.w, "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png",
height: 27.h, width: 30.h,
fit: BoxFit.fill, height: 30.h,
).circle(100).toShimmer2(isShow: widget.myAppointmentsViewModel.isMyAppointmentsLoading), fit: BoxFit.fill,
SizedBox(width: 8.w), ).circle(100.r).toShimmer2(isShow: widget.myAppointmentsViewModel.isMyAppointmentsLoading),
Expanded( SizedBox(width: 8.w),
child: Column( Expanded(
crossAxisAlignment: CrossAxisAlignment.start, child: Column(
children: [ crossAxisAlignment: CrossAxisAlignment.start,
(widget.patientAppointmentHistoryResponseModel.doctorNameObj ?? "").toText14(isBold: true, maxlines: 1, isEnglishOnly: !Utils.isArabicText(widget.patientAppointmentHistoryResponseModel.doctorNameObj ?? "")).toShimmer2(isShow: widget.myAppointmentsViewModel.isMyAppointmentsLoading), children: [
(widget.patientAppointmentHistoryResponseModel.clinicName ?? "") (widget.patientAppointmentHistoryResponseModel.doctorNameObj ?? "")
.toText12(maxLine: 1, isBold: true, color: AppColors.greyTextColor) .toText14(
.toShimmer2(isShow: widget.myAppointmentsViewModel.isMyAppointmentsLoading), 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: 8.h),
SizedBox(height: 12.h), _buildAppointmentActionButton(context, appState),
// Check if doctor is active - if not, show only View Details button ],
(widget.patientAppointmentHistoryResponseModel.isActiveDoctor ?? true) ).paddingAll(16.w),
? // 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,
),
),
),
).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),
), ),
], ],
); );
} }
/// Builds the appropriate action button based on appointment state and doctor status
Widget _buildAppointmentActionButton(BuildContext context, AppState appState) {
final isLoading = widget.myAppointmentsViewModel.isMyAppointmentsLoading;
final appointment = widget.patientAppointmentHistoryResponseModel;
final isDoctorActive = appointment.isActiveDoctor ?? true;
// If doctor is not active, show only View Details button
if (!isDoctorActive) {
return _buildViewDetailsButton(context);
}
// Doctor is active - check rebooking logic
final isArrived = AppointmentType.isArrived(appointment);
final isRebookingNotAllowed = appointment.isClinicReBookingAllowed == false;
// If arrived and rebooking not allowed, show button without arrow
if (isArrived && isRebookingNotAllowed) {
return _buildSingleButton(context, isLoading);
}
// Normal flow - show button with arrow
return _buildButtonWithArrow(context, appState, isLoading);
}
/// Builds a single button without arrow (for no rebooking scenarios)
Widget _buildSingleButton(BuildContext context, bool isLoading) {
if (isLoading) {
return Container().toShimmer2(isShow: true, height: 40.h, width: 100.w, radius: 12.r);
}
final isArrived = AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel);
if (isArrived) {
return getArrivedAppointmentButton(context).toShimmer2(isShow: isLoading);
}
return _buildNextActionButton(context, isLoading);
}
/// Builds the next action button (Pay Now, Confirm, etc.)
Widget _buildNextActionButton(BuildContext context, bool isLoading) {
final appointment = widget.patientAppointmentHistoryResponseModel;
return CustomButton(
text: AppointmentType.getNextActionText(appointment.nextAction),
onPressed: () {
handleAppointmentNextAction(appointment.nextAction, context);
},
backgroundColor: AppointmentType.getNextActionButtonColor(appointment.nextAction).withValues(alpha: 0.15),
borderColor: AppointmentType.getNextActionButtonColor(appointment.nextAction).withValues(alpha: 0.01),
textColor: AppointmentType.getNextActionTextColor(appointment.nextAction),
fontSize: 14.f,
fontWeight: FontWeight.w600,
borderRadius: 12.r,
padding: EdgeInsets.symmetric(horizontal: 10.w),
height: 40.h,
icon: AppointmentType.getNextActionIcon(appointment.nextAction),
iconColor: AppointmentType.getNextActionTextColor(appointment.nextAction),
iconSize: 14.h,
).toShimmer2(isShow: isLoading);
}
/// Builds button with arrow (normal flow with navigation arrow)
Widget _buildButtonWithArrow(BuildContext context, AppState appState, bool isLoading) {
return Row(
children: [
_buildMainActionButton(context, isLoading),
SizedBox(width: 8.w),
_buildNavigationArrow(context, appState, isLoading),
],
);
}
/// Builds the main action button in the row (left side)
Widget _buildMainActionButton(BuildContext context, bool isLoading) {
if (isLoading) {
return Container().toShimmer2(isShow: true, height: 40.h, width: 100.w, radius: 12.r);
}
final isArrived = AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel);
return Expanded(
flex: 7,
child: isArrived ? getArrivedAppointmentButton(context).toShimmer2(isShow: isLoading) : _buildNextActionButton(context, isLoading),
);
}
/// Builds the navigation arrow button (right side)
Widget _buildNavigationArrow(BuildContext context, AppState appState, bool isLoading) {
final appointment = widget.patientAppointmentHistoryResponseModel;
final isArrived = AppointmentType.isArrived(appointment);
// Check if arrow should be hidden
final shouldHideArrow = (appointment.isLiveCareAppointment ?? false) ||
(appointment.isExecludeDoctor ?? false) ||
!Utils.isClinicAllowedForRebook(appointment.clinicID ?? 0);
if (shouldHideArrow && isArrived) {
return SizedBox.shrink();
}
return Expanded(
flex: 2,
child: Container(
height: 40.h,
width: 40.h,
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.h,
height: 40.h,
fit: BoxFit.contain,
),
),
),
).toShimmer2(isShow: isLoading).onPress(() {
Navigator.of(context)
.push(
CustomPageRoute(
page: AppointmentDetailsPage(patientAppointmentHistoryResponseModel: appointment),
),
)
.then((val) {
// Can refresh appointments here if needed
});
}),
);
}
/// Builds the View Details button (for inactive doctors)
Widget _buildViewDetailsButton(BuildContext context) {
return 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: isFoldable ? 36.h : 40.h,
).toShimmer2(isShow: widget.myAppointmentsViewModel.isMyAppointmentsLoading);
}
Widget getArrivedAppointmentButton(BuildContext context) { Widget getArrivedAppointmentButton(BuildContext context) {
// Check if rebooking is not allowed - show View Details button // Check if rebooking is not allowed - show View Details button
if (widget.patientAppointmentHistoryResponseModel.isClinicReBookingAllowed == false) { if (widget.patientAppointmentHistoryResponseModel.isClinicReBookingAllowed == false) {
@ -393,16 +450,16 @@ class _MedicalFileAppointmentCardState extends State<MedicalFileAppointmentCard>
// No action needed - go to details // No action needed - go to details
Navigator.of(context) Navigator.of(context)
.push(CustomPageRoute( .push(CustomPageRoute(
page: AppointmentDetailsPage(patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel), page: AppointmentDetailsPage(patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel),
)) ))
.then((val) {}); .then((val) {});
break; break;
case 10: case 10:
// Confirm appointment - go to details // Confirm appointment - go to details
Navigator.of(context) Navigator.of(context)
.push(CustomPageRoute( .push(CustomPageRoute(
page: AppointmentDetailsPage(patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel), page: AppointmentDetailsPage(patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel),
)) ))
.then((val) {}); .then((val) {});
break; break;
case 15: case 15:
@ -425,41 +482,42 @@ class _MedicalFileAppointmentCardState extends State<MedicalFileAppointmentCard>
children: [ children: [
// Message text // Message text
LocaleKeys.upcomingPaymentPending.tr(context: context).toText14( LocaleKeys.upcomingPaymentPending.tr(context: context).toText14(
color: AppColors.textColor, color: AppColors.textColor,
isCenter: true, isCenter: true,
), ),
SizedBox(height: 24.h), SizedBox(height: 24.h),
// Countdown Timer - DD : HH : MM : SS format with labels // Countdown Timer - DD : HH : MM : SS format with labels
Directionality( Directionality(
textDirection: ui.TextDirection.ltr, child:Row( textDirection: ui.TextDirection.ltr,
mainAxisAlignment: MainAxisAlignment.center, child: Row(
crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.center,
children: [ crossAxisAlignment: CrossAxisAlignment.start,
// Days children: [
_buildTimeUnit( // Days
_timeRemaining != null ? _timeRemaining!.inDays.toString().padLeft(2, '0') : '00', _buildTimeUnit(
LocaleKeys.days.tr(context: context), _timeRemaining != null ? _timeRemaining!.inDays.toString().padLeft(2, '0') : '00',
), LocaleKeys.days.tr(context: context),
_buildTimeSeparator(), ),
// Hours _buildTimeSeparator(),
_buildTimeUnit( // Hours
_timeRemaining != null ? _timeRemaining!.inHours.remainder(24).toString().padLeft(2, '0') : '00', _buildTimeUnit(
LocaleKeys.hours.tr(context: context), _timeRemaining != null ? _timeRemaining!.inHours.remainder(24).toString().padLeft(2, '0') : '00',
), LocaleKeys.hours.tr(context: context),
_buildTimeSeparator(), ),
// Minutes _buildTimeSeparator(),
_buildTimeUnit( // Minutes
_timeRemaining != null ? _timeRemaining!.inMinutes.remainder(60).toString().padLeft(2, '0') : '00', _buildTimeUnit(
LocaleKeys.minutes.tr(context: context), _timeRemaining != null ? _timeRemaining!.inMinutes.remainder(60).toString().padLeft(2, '0') : '00',
), LocaleKeys.minutes.tr(context: context),
_buildTimeSeparator(), ),
// Seconds _buildTimeSeparator(),
_buildTimeUnit( // Seconds
_timeRemaining != null ? _timeRemaining!.inSeconds.remainder(60).toString().padLeft(2, '0') : '00', _buildTimeUnit(
LocaleKeys.seconds.tr(context: context), _timeRemaining != null ? _timeRemaining!.inSeconds.remainder(60).toString().padLeft(2, '0') : '00',
), LocaleKeys.seconds.tr(context: context),
], ),
)), ],
)),
SizedBox(height: 24.h), SizedBox(height: 24.h),
// Green Acknowledge button with checkmark icon // Green Acknowledge button with checkmark icon
CustomButton( CustomButton(
@ -502,26 +560,25 @@ class _MedicalFileAppointmentCardState extends State<MedicalFileAppointmentCard>
// Confirm livecare - go to details // Confirm livecare - go to details
Navigator.of(context) Navigator.of(context)
.push(CustomPageRoute( .push(CustomPageRoute(
page: AppointmentDetailsPage(patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel), page: AppointmentDetailsPage(patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel),
)) ))
.then((val) {}); .then((val) {});
break; break;
case 90: case 90:
// Check-in - go to details // Check-in - go to details
Navigator.of(context) Navigator.of(context)
.push(CustomPageRoute( .push(CustomPageRoute(
page: AppointmentDetailsPage(patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel), page: AppointmentDetailsPage(patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel),
)) ))
.then((val) {}); .then((val) {});
break; break;
default: default:
// Default - go to details // Default - go to details
Navigator.of(context) Navigator.of(context)
.push(CustomPageRoute( .push(CustomPageRoute(
page: AppointmentDetailsPage(patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel), page: AppointmentDetailsPage(patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel),
)) ))
.then((val) {}); .then((val) {});
} }
} }
} }

@ -30,11 +30,7 @@ class MedicalFileCard extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
final iconS = iconSize ?? 30.w; final iconS = iconSize ?? 30.w;
return Container( return Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration( decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: backgroundColor, borderRadius: 20.r, hasShadow: false),
color: backgroundColor,
borderRadius: 20.r,
hasShadow: false
),
padding: EdgeInsets.all(12.w), padding: EdgeInsets.all(12.w),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
@ -64,5 +60,3 @@ class MedicalFileCard extends StatelessWidget {
); );
} }
} }

@ -14,7 +14,7 @@ import 'package:hmg_patient_app_new/widgets/custom_tab_bar.dart';
import 'package:hmg_patient_app_new/widgets/graph/CustomBarGraph.dart'; import 'package:hmg_patient_app_new/widgets/graph/CustomBarGraph.dart';
import 'package:intl/intl.dart' show DateFormat; import 'package:intl/intl.dart' show DateFormat;
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:hmg_patient_app_new/features/smartwatch_health_data/HealthDataTransformation.dart' as durations; import 'package:hmg_patient_app_new/features/smartwatch_health_data/health_data_transformations.dart' as durations;
import 'package:dartz/dartz.dart' show Tuple2; import 'package:dartz/dartz.dart' show Tuple2;
import '../../core/utils/date_util.dart'; import '../../core/utils/date_util.dart';

File diff suppressed because it is too large Load Diff

@ -1,252 +0,0 @@
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/app_export.dart';
import 'package:hmg_patient_app_new/core/dependencies.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/smartwatch_health_data/health_provider.dart';
import 'package:hmg_patient_app_new/presentation/smartwatches/activity_detail.dart';
import 'package:hmg_patient_app_new/services/navigation_service.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
import 'package:provider/provider.dart';
import 'package:hmg_patient_app_new/features/smartwatch_health_data/HealthDataTransformation.dart' as durations;
import '../../core/utils/date_util.dart' show DateUtil;
class SmartWatchActivity extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: AppColors.bgScaffoldColor,
body: CollapsingListView(
title: "All Health Data".needTranslation,
child: Column(
spacing: 16.h,
children: [
resultItem(
leadingIcon: AppAssets.watchActivity,
title: "Activity Calories".needTranslation,
description: "Activity rings give you a quick visual reference of how active you are each day. ".needTranslation,
trailingIcon: AppAssets.watchActivityTrailing,
result: context.read<HealthProvider>().sumOfNonEmptyData(context.read<HealthProvider>().vitals?.activity??[]),
unitsOfMeasure: "Kcal"
).onPress((){
// Map<String, List<Vitals>> getVitals() {
// return {
// "heartRate": heartRate ,
// "sleep": sleep,
// "steps": step,
// "activity": activity,
// "bodyOxygen": bodyOxygen,
// "bodyTemperature": bodyTemperature,
// };
// }
context.read<HealthProvider>().setDurations(durations.Durations.daily);
context.read<HealthProvider>().deleteDataIfSectionIsDifferent("activity");
context.read<HealthProvider>().saveSelectedSection("activity");
context.read<HealthProvider>().fetchData();
context.read<HealthProvider>().navigateToDetails("activity", sectionName:"Activity Calories", uom: "Kcal");
}),
resultItem(
leadingIcon: AppAssets.watchSteps,
title: "Steps".needTranslation,
description: "Step count is the number of steps you take throughout the day.".needTranslation,
trailingIcon: AppAssets.watchStepsTrailing,
result: context.read<HealthProvider>().sumOfNonEmptyData(context.read<HealthProvider>().vitals?.step??[]),
unitsOfMeasure: "Steps"
).onPress((){
// Map<String, List<Vitals>> getVitals() {
// return {
// "heartRate": heartRate ,
// "sleep": sleep,
// "steps": step,
// "activity": activity,
// "bodyOxygen": bodyOxygen,
// "bodyTemperature": bodyTemperature,
// };
// }
context.read<HealthProvider>().setDurations(durations.Durations.daily);
context.read<HealthProvider>().deleteDataIfSectionIsDifferent("steps");
context.read<HealthProvider>().saveSelectedSection("steps");
context.read<HealthProvider>().fetchData();
context.read<HealthProvider>().navigateToDetails("steps", sectionName: "Steps", uom: "Steps");
}),
resultItem(
leadingIcon: AppAssets.watchSteps,
title: "Distance Covered".needTranslation,
description: "Step count is the distance you take throughout the day.".needTranslation,
trailingIcon: AppAssets.watchStepsTrailing,
result: context.read<HealthProvider>().sumOfNonEmptyData(context.read<HealthProvider>().vitals?.distance??[]),
unitsOfMeasure: "Km"
).onPress((){
// Map<String, List<Vitals>> getVitals() {
// return {
// "heartRate": heartRate ,
// "sleep": sleep,
// "steps": step,
// "activity": activity,
// "bodyOxygen": bodyOxygen,
// "bodyTemperature": bodyTemperature,
// };
// }
context.read<HealthProvider>().setDurations(durations.Durations.daily);
context.read<HealthProvider>().deleteDataIfSectionIsDifferent("distance");
context.read<HealthProvider>().saveSelectedSection("distance");
context.read<HealthProvider>().fetchData();
context.read<HealthProvider>().navigateToDetails("distance", sectionName: "Distance Covered", uom: "km");
}),
resultItem(
leadingIcon: AppAssets.watchSleep,
title: "Sleep Score".needTranslation,
description: "This will keep track of how much hours you sleep in a day".needTranslation,
trailingIcon: AppAssets.watchSleepTrailing,
result: DateUtil.millisToHourMin(int.parse(context.read<HealthProvider>().firstNonEmptyValue(context.read<HealthProvider>().vitals?.sleep??[]))).split(" ")[0],
unitsOfMeasure: "hr",
resultSecondValue: DateUtil.millisToHourMin(int.parse(context.read<HealthProvider>().firstNonEmptyValue(context.read<HealthProvider>().vitals?.sleep??[]))).split(" ")[2],
unitOfSecondMeasure: "min"
).onPress((){
// Map<String, List<Vitals>> getVitals() {
// return {
// "heartRate": heartRate ,
// "sleep": sleep,
// "steps": step,
// "activity": activity,
// "bodyOxygen": bodyOxygen,
// "bodyTemperature": bodyTemperature,
// };
// }
context.read<HealthProvider>().setDurations(durations.Durations.daily);
context.read<HealthProvider>().deleteDataIfSectionIsDifferent("sleep");
context.read<HealthProvider>().saveSelectedSection("sleep");
context.read<HealthProvider>().fetchData();
context.read<HealthProvider>().navigateToDetails("sleep", sectionName:"Sleep Score",uom:"");
}),
resultItem(
leadingIcon: AppAssets.watchWeight,
title: "Blood Oxygen".needTranslation,
description: "This will calculate your Blood Oxygen to keep track and update history".needTranslation,
trailingIcon: AppAssets.watchWeightTrailing,
result: context.read<HealthProvider>().firstNonEmptyValue(context.read<HealthProvider>().vitals?.bodyOxygen??[], ),
unitsOfMeasure: "%"
).onPress((){
// Map<String, List<Vitals>> getVitals() {
// return {
// "heartRate": heartRate ,
// "sleep": sleep,
// "steps": step,
// "activity": activity,
// "bodyOxygen": bodyOxygen,
// "bodyTemperature": bodyTemperature,
// };
// }
context.read<HealthProvider>().setDurations(durations.Durations.daily);
context.read<HealthProvider>().deleteDataIfSectionIsDifferent("bodyOxygen");
context.read<HealthProvider>().saveSelectedSection("bodyOxygen");
context.read<HealthProvider>().fetchData();
context.read<HealthProvider>().navigateToDetails("bodyOxygen", uom: "%", sectionName:"Blood Oxygen" );
}),
resultItem(
leadingIcon: AppAssets.watchWeight,
title: "Body temperature".needTranslation,
description: "This will calculate your Body temprerature to keep track and update history".needTranslation,
trailingIcon: AppAssets.watchWeightTrailing,
result: context.read<HealthProvider>().firstNonEmptyValue(context.read<HealthProvider>().vitals?.bodyTemperature??[]),
unitsOfMeasure: "C"
).onPress((){
// Map<String, List<Vitals>> getVitals() {
// return {
// "heartRate": heartRate ,
// "sleep": sleep,
// "steps": step,
// "activity": activity,
// "bodyOxygen": bodyOxygen,
// "bodyTemperature": bodyTemperature,
// };
// }
context.read<HealthProvider>().setDurations(durations.Durations.daily);
context.read<HealthProvider>().deleteDataIfSectionIsDifferent("bodyTemperature");
context.read<HealthProvider>().saveSelectedSection("bodyTemperature");
context.read<HealthProvider>().fetchData();
context.read<HealthProvider>().navigateToDetails("bodyTemperature" , sectionName: "Body temperature".capitalizeFirstofEach, uom: "C");
}),
],
).paddingSymmetrical(24.w, 24.h),
));
}
Widget resultItem({
required String leadingIcon,
required String title,
required String description,
required String trailingIcon,
required String result,
required String unitsOfMeasure,
String? resultSecondValue,
String? unitOfSecondMeasure
}) {
return DecoratedBox(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 12.h),
child: Row(
spacing: 16.w,
children: [
Expanded(
child:Column(
spacing: 8.h,
children: [
Row(
spacing: 8.w,
children: [
Utils.buildSvgWithAssets(icon: leadingIcon, height: 16.h, width: 14.w),
title.toText16( weight: FontWeight.w600, color: AppColors.textColor),
],
),
description.toText12(isBold: true, color: AppColors.greyTextColor),
Row(
crossAxisAlignment: CrossAxisAlignment.baseline,
textBaseline: TextBaseline.alphabetic,
spacing: 2.h,
children: [
result.toText21(isBold: true, color: AppColors.textColor),
unitsOfMeasure.toText10(isBold: true, color:AppColors.greyTextColor ),
if(resultSecondValue != null)
Visibility(
visible: resultSecondValue != null ,
child: Row(
crossAxisAlignment: CrossAxisAlignment.baseline,
textBaseline: TextBaseline.alphabetic,
spacing: 2.h,
children: [
SizedBox(width: 2.w,),
resultSecondValue.toText21(isBold: true, color: AppColors.textColor),
unitOfSecondMeasure!.toText10(isBold: true, color:AppColors.greyTextColor )
],
),
)
],
),
],
) ,
),
Utils.buildSvgWithAssets(icon: trailingIcon, width: 72.w, height: 72.h),
],
).paddingSymmetrical(16.w, 16.h)
);
}
}

@ -0,0 +1,188 @@
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/app_export.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/smartwatch_health_data/health_data_transformations.dart' as durations;
import 'package:hmg_patient_app_new/features/smartwatch_health_data/health_provider.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
import 'package:provider/provider.dart';
import '../../core/utils/date_util.dart' show DateUtil;
class SmartWatchesHealthDataScreen extends StatelessWidget {
const SmartWatchesHealthDataScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: AppColors.bgScaffoldColor,
body: CollapsingListView(
title: "All Health Data".needTranslation,
child: Column(
spacing: 16.h,
children: [
resultItem(
leadingIcon: AppAssets.watchActivity,
title: "Activity Calories".needTranslation,
description: "Activity rings give you a quick visual reference of how active you are each day. ".needTranslation,
trailingIcon: AppAssets.watchActivityTrailing,
result: context.read<HealthProvider>().sumOfNonEmptyData(context.read<HealthProvider>().vitals?.activity ?? []),
unitsOfMeasure: "Kcal")
.onPress(() {
context.read<HealthProvider>().setDurations(durations.Durations.daily);
context.read<HealthProvider>().deleteDataIfSectionIsDifferent("activity");
context.read<HealthProvider>().saveSelectedSection("activity");
context.read<HealthProvider>().fetchData();
context.read<HealthProvider>().navigateToDetails("activity", sectionName: "Activity Calories", uom: "Kcal");
}),
resultItem(
leadingIcon: AppAssets.watchSteps,
title: "Steps".needTranslation,
description: "Step count is the number of steps you take throughout the day.".needTranslation,
trailingIcon: AppAssets.watchStepsTrailing,
result: context.read<HealthProvider>().sumOfNonEmptyData(context.read<HealthProvider>().vitals?.step ?? []),
unitsOfMeasure: "Steps")
.onPress(() {
// Map<String, List<Vitals>> getVitals() {
// return {
// "heartRate": heartRate ,
// "sleep": sleep,
// "steps": step,
// "activity": activity,
// "bodyOxygen": bodyOxygen,
// "bodyTemperature": bodyTemperature,
// };
// }
context.read<HealthProvider>().setDurations(durations.Durations.daily);
context.read<HealthProvider>().deleteDataIfSectionIsDifferent("steps");
context.read<HealthProvider>().saveSelectedSection("steps");
context.read<HealthProvider>().fetchData();
context.read<HealthProvider>().navigateToDetails("steps", sectionName: "Steps", uom: "Steps");
}),
resultItem(
leadingIcon: AppAssets.watchSteps,
title: "Distance Covered".needTranslation,
description: "Step count is the distance you take throughout the day.".needTranslation,
trailingIcon: AppAssets.watchStepsTrailing,
result: context.read<HealthProvider>().sumOfNonEmptyData(context.read<HealthProvider>().vitals?.distance ?? []),
unitsOfMeasure: "Km")
.onPress(() {
context.read<HealthProvider>().setDurations(durations.Durations.daily);
context.read<HealthProvider>().deleteDataIfSectionIsDifferent("distance");
context.read<HealthProvider>().saveSelectedSection("distance");
context.read<HealthProvider>().fetchData();
context.read<HealthProvider>().navigateToDetails("distance", sectionName: "Distance Covered", uom: "km");
}),
resultItem(
leadingIcon: AppAssets.watchSleep,
title: "Sleep Score".needTranslation,
description: "This will keep track of how much hours you sleep in a day".needTranslation,
trailingIcon: AppAssets.watchSleepTrailing,
result: DateUtil.millisToHourMin(
int.parse(context.read<HealthProvider>().firstNonEmptyValue(context.read<HealthProvider>().vitals?.sleep ?? [])))
.split(" ")[0],
unitsOfMeasure: "hr",
resultSecondValue: DateUtil.millisToHourMin(
int.parse(context.read<HealthProvider>().firstNonEmptyValue(context.read<HealthProvider>().vitals?.sleep ?? [])))
.split(" ")[2],
unitOfSecondMeasure: "min")
.onPress(() {
context.read<HealthProvider>().setDurations(durations.Durations.daily);
context.read<HealthProvider>().deleteDataIfSectionIsDifferent("sleep");
context.read<HealthProvider>().saveSelectedSection("sleep");
context.read<HealthProvider>().fetchData();
context.read<HealthProvider>().navigateToDetails("sleep", sectionName: "Sleep Score", uom: "");
}),
resultItem(
leadingIcon: AppAssets.watchWeight,
title: "Blood Oxygen".needTranslation,
description: "This will calculate your Blood Oxygen to keep track and update history".needTranslation,
trailingIcon: AppAssets.watchWeightTrailing,
result: context.read<HealthProvider>().firstNonEmptyValue(
context.read<HealthProvider>().vitals?.bodyOxygen ?? [],
),
unitsOfMeasure: "%")
.onPress(() {
context.read<HealthProvider>().setDurations(durations.Durations.daily);
context.read<HealthProvider>().deleteDataIfSectionIsDifferent("bodyOxygen");
context.read<HealthProvider>().saveSelectedSection("bodyOxygen");
context.read<HealthProvider>().fetchData();
context.read<HealthProvider>().navigateToDetails("bodyOxygen", uom: "%", sectionName: "Blood Oxygen");
}),
resultItem(
leadingIcon: AppAssets.watchWeight,
title: "Body temperature".needTranslation,
description: "This will calculate your Body temprerature to keep track and update history".needTranslation,
trailingIcon: AppAssets.watchWeightTrailing,
result: context.read<HealthProvider>().firstNonEmptyValue(context.read<HealthProvider>().vitals?.bodyTemperature ?? []),
unitsOfMeasure: "C")
.onPress(() {
context.read<HealthProvider>().setDurations(durations.Durations.daily);
context.read<HealthProvider>().deleteDataIfSectionIsDifferent("bodyTemperature");
context.read<HealthProvider>().saveSelectedSection("bodyTemperature");
context.read<HealthProvider>().fetchData();
context.read<HealthProvider>().navigateToDetails("bodyTemperature", sectionName: "Body temperature".capitalizeFirstofEach, uom: "C");
}),
],
).paddingSymmetrical(24.w, 24.h),
));
}
Widget resultItem({
required String leadingIcon,
required String title,
required String description,
required String trailingIcon,
required String result,
required String unitsOfMeasure,
String? resultSecondValue,
String? unitOfSecondMeasure,
}) {
return DecoratedBox(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 12.h),
child: Row(
spacing: 16.w,
children: [
Expanded(
child: Column(
spacing: 8.h,
children: [
Row(
spacing: 8.w,
children: [
Utils.buildSvgWithAssets(icon: leadingIcon, height: 16.h, width: 14.w),
title.toText16(weight: FontWeight.w600, color: AppColors.textColor),
],
),
description.toText12(isBold: true, color: AppColors.greyTextColor),
Row(
crossAxisAlignment: CrossAxisAlignment.baseline,
textBaseline: TextBaseline.alphabetic,
spacing: 2.h,
children: [
result.toText21(isBold: true, color: AppColors.textColor),
unitsOfMeasure.toText10(isBold: true, color: AppColors.greyTextColor),
if (resultSecondValue != null)
Row(
crossAxisAlignment: CrossAxisAlignment.baseline,
textBaseline: TextBaseline.alphabetic,
spacing: 2.h,
children: [
SizedBox(width: 2.w),
resultSecondValue.toText21(isBold: true, color: AppColors.textColor),
unitOfSecondMeasure!.toText10(isBold: true, color: AppColors.greyTextColor)
],
),
],
),
],
),
),
Utils.buildSvgWithAssets(icon: trailingIcon, width: 72.w, height: 72.h),
],
).paddingSymmetrical(16.w, 16.h));
}
}

@ -1,5 +1,3 @@
import 'dart:io';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart'; import 'package:hmg_patient_app_new/core/app_assets.dart';
@ -54,7 +52,6 @@ class SmartwatchHomePage extends StatelessWidget {
fontSize: 16.f, fontSize: 16.f,
isBold: true, isBold: true,
borderRadius: 12.r, borderRadius: 12.r,
height: 50.h, height: 50.h,
icon: AppAssets.ask_doctor_icon, icon: AppAssets.ask_doctor_icon,
iconColor: AppColors.infoColor, iconColor: AppColors.infoColor,
@ -69,11 +66,12 @@ class SmartwatchHomePage extends StatelessWidget {
child: GridView( child: GridView(
padding: EdgeInsets.zero, padding: EdgeInsets.zero,
shrinkWrap: true, shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2, crossAxisCount: 2,
crossAxisSpacing: 16.h, crossAxisSpacing: 16.h,
mainAxisSpacing: 16.w, mainAxisSpacing: 16.w,
mainAxisExtent: 240.h, childAspectRatio: isFoldable ? 1.1 : (isTablet ? 1.3 : 0.7),
), ),
children: [ children: [
Container( Container(
@ -83,20 +81,24 @@ class SmartwatchHomePage extends StatelessWidget {
), ),
child: Column( child: Column(
children: [ children: [
Image.asset("assets/images/png/smartwatches/apple-watch-5.jpg", width: 136.w, height: 136.h).paddingSymmetrical(24.w, 8.h), Image.asset("assets/images/png/smartwatches/apple-watch-5.jpg", width: 136.h, height: 136.h).paddingSymmetrical(24.w, 8.h),
"Apple Watch".needTranslation.toText16(isBold: true), "Apple Watch".needTranslation.toText16(isBold: true),
CustomButton( CustomButton(
text: LocaleKeys.selectSmartWatch.tr(context: context), text: LocaleKeys.selectSmartWatch.tr(context: context),
onPressed: () { onPressed: () {
context.read<HealthProvider>().setSelectedWatchType(SmartWatchTypes.apple, "assets/images/png/smartwatches/apple-watch-5.jpg"); context
getIt.get<NavigationService>().pushPage(page: SmartwatchInstructionsPage( .read<HealthProvider>()
smartwatchDetails: SmartwatchDetails(SmartWatchTypes.apple, .setSelectedWatchType(SmartWatchTypes.apple, "assets/images/png/smartwatches/apple-watch-5.jpg");
"assets/images/png/smartwatches/apple-watch-5.jpg", getIt.get<NavigationService>().pushPage(
AppAssets.bluetooth, page: SmartwatchInstructionsPage(
LocaleKeys.applehealthapplicationshouldbeinstalledinyourphone.tr(context: context), smartwatchDetails: SmartwatchDetails(
LocaleKeys.unabletodetectapplicationinstalledpleasecomebackonceinstalled.tr(context: context), SmartWatchTypes.apple,
LocaleKeys.applewatchshouldbeconnected.tr(context: context)), "assets/images/png/smartwatches/apple-watch-5.jpg",
)); AppAssets.bluetooth,
LocaleKeys.applehealthapplicationshouldbeinstalledinyourphone.tr(context: context),
LocaleKeys.unabletodetectapplicationinstalledpleasecomebackonceinstalled.tr(context: context),
LocaleKeys.applewatchshouldbeconnected.tr(context: context)),
));
}, },
backgroundColor: AppColors.primaryRedColor.withAlpha(40), backgroundColor: AppColors.primaryRedColor.withAlpha(40),
borderColor: AppColors.primaryRedColor.withAlpha(0), borderColor: AppColors.primaryRedColor.withAlpha(0),
@ -110,26 +112,29 @@ class SmartwatchHomePage extends StatelessWidget {
), ),
), ),
Container( Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration( decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.r),
color: AppColors.whiteColor,
borderRadius: 24.r,
),
child: Column( child: Column(
children: [ children: [
Image.asset("assets/images/png/smartwatches/galaxy_watch_8_classic.jpeg", fit: BoxFit.contain, width: 136.w, height: 136.h).paddingSymmetrical(24.w, 8.h), Image.asset("assets/images/png/smartwatches/galaxy_watch_8_classic.jpeg", fit: BoxFit.contain, width: 136.w, height: 136.h)
.paddingSymmetrical(24.w, 8.h),
"Samsung Watch".needTranslation.toText16(isBold: true), "Samsung Watch".needTranslation.toText16(isBold: true),
CustomButton( CustomButton(
text: LocaleKeys.selectSmartWatch.tr(context: context), text: LocaleKeys.selectSmartWatch.tr(context: context),
onPressed: () { onPressed: () {
context.read<HealthProvider>().setSelectedWatchType(SmartWatchTypes.samsung, "assets/images/png/smartwatches/galaxy_watch_8_classic.jpeg"); context
getIt.get<NavigationService>().pushPage(page: SmartwatchInstructionsPage( .read<HealthProvider>()
smartwatchDetails: SmartwatchDetails(SmartWatchTypes.samsung, .setSelectedWatchType(SmartWatchTypes.samsung, "assets/images/png/smartwatches/galaxy_watch_8_classic.jpeg");
"assets/images/png/smartwatches/galaxy_watch_8_classic.jpeg", getIt.get<NavigationService>().pushPage(
AppAssets.bluetooth, page: SmartwatchInstructionsPage(
LocaleKeys.samsunghealthapplicationshouldbeinstalledinyourphone.tr(context: context), smartwatchDetails: SmartwatchDetails(
LocaleKeys.unabletodetectapplicationinstalledpleasecomebackonceinstalled.tr(context: context), SmartWatchTypes.samsung,
LocaleKeys.samsungwatchshouldbeconnected.tr(context: context)), "assets/images/png/smartwatches/galaxy_watch_8_classic.jpeg",
)); }, AppAssets.bluetooth,
LocaleKeys.samsunghealthapplicationshouldbeinstalledinyourphone.tr(context: context),
LocaleKeys.unabletodetectapplicationinstalledpleasecomebackonceinstalled.tr(context: context),
LocaleKeys.samsungwatchshouldbeconnected.tr(context: context)),
));
},
backgroundColor: AppColors.primaryRedColor.withAlpha(40), backgroundColor: AppColors.primaryRedColor.withAlpha(40),
borderColor: AppColors.primaryRedColor.withAlpha(0), borderColor: AppColors.primaryRedColor.withAlpha(0),
textColor: AppColors.primaryRedColor, textColor: AppColors.primaryRedColor,
@ -187,7 +192,6 @@ class SmartwatchHomePage extends StatelessWidget {
CustomButton( CustomButton(
text: LocaleKeys.selectSmartWatch.tr(context: context), text: LocaleKeys.selectSmartWatch.tr(context: context),
onPressed: () { onPressed: () {
showUnavailableDialog(context); showUnavailableDialog(context);
// context.read<HealthProvider>().setSelectedWatchType(SmartWatchTypes.whoop, "assets/images/png/smartwatches/Whoop_Watch.png"); // context.read<HealthProvider>().setSelectedWatchType(SmartWatchTypes.whoop, "assets/images/png/smartwatches/Whoop_Watch.png");
// getIt.get<NavigationService>().pushPage(page: SmartwatchInstructionsPage( // getIt.get<NavigationService>().pushPage(page: SmartwatchInstructionsPage(
@ -221,18 +225,16 @@ class SmartwatchHomePage extends StatelessWidget {
} }
void showUnavailableDialog(BuildContext context) { void showUnavailableDialog(BuildContext context) {
showCommonBottomSheetWithoutHeight( showCommonBottomSheetWithoutHeight(
title: LocaleKeys.notice.tr(context: context), title: LocaleKeys.notice.tr(context: context),
context, context,
child: Utils.getWarningWidget( child: Utils.getWarningWidget(
loadingText: LocaleKeys.featureComingSoonDescription.tr(context: context), loadingText: LocaleKeys.featureComingSoonDescription.tr(context: context),
isShowActionButtons: false, isShowActionButtons: false,
showOkButton: true, showOkButton: true,
onConfirmTap: () async { onConfirmTap: () async {
context.pop(); context.pop();
} }),
),
callBackFunc: () {}, callBackFunc: () {},
isFullScreen: false, isFullScreen: false,
isCloseButtonVisible: true, isCloseButtonVisible: true,

@ -2,13 +2,10 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart'; import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/common_models/smart_watch.dart'; import 'package:hmg_patient_app_new/core/common_models/smart_watch.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/size_utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.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/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/smartwatches/smart_watch_activity.dart' show SmartWatchActivity;
import 'package:hmg_patient_app_new/services/navigation_service.dart';
import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.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/buttons/custom_button.dart';
@ -26,62 +23,66 @@ class SmartwatchInstructionsPage extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
backgroundColor: AppColors.bgScaffoldColor, backgroundColor: AppColors.bgScaffoldColor,
body: CollapsingListView( body: CollapsingListView(
title: "How does it work".needTranslation, title: "How does it work".needTranslation,
bottomChild: Container( bottomChild: Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration( decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor, color: AppColors.whiteColor,
borderRadius: 24.r, borderRadius: 24.r,
),
child: CustomButton(
text: LocaleKeys.getStarted.tr(context: context),
onPressed: () {
context.read<HealthProvider>().initDevice();
},
backgroundColor: AppColors.primaryRedColor,
borderColor: AppColors.primaryRedColor,
textColor: AppColors.whiteColor,
fontSize: 16.f,
isBold: true,
borderRadius: 12.r,
height: 50.h,
).paddingSymmetrical(24.w, 30.h),
), ),
child: Column( child: CustomButton(
mainAxisSize: MainAxisSize.max, text: LocaleKeys.getStarted.tr(context: context),
spacing: 18.h, onPressed: () {
children: [ context.read<HealthProvider>().initDevice();
Image.asset(smartwatchDetails.watchIcon, fit: BoxFit.contain, height: 280.h,width: 280.w,), },
DecoratedBox( backgroundColor: AppColors.primaryRedColor,
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 12.h), borderColor: AppColors.primaryRedColor,
child: Column( textColor: AppColors.whiteColor,
children: [ fontSize: 16.f,
watchContentDetails( isBold: true,
title: smartwatchDetails.detailsTitle, borderRadius: 12.r,
description: smartwatchDetails.details, height: 50.h,
icon: smartwatchDetails.smallIcon, ).paddingSymmetrical(24.w, 30.h),
descriptionTextColor: AppColors.primaryRedColor
),
Divider(
color: AppColors.dividerColor,
thickness: 1.h,
).paddingOnly(top: 16.h, bottom: 16.h),
watchContentDetails(
title: smartwatchDetails.secondTitle,
description: LocaleKeys.updatetheinformation.tr(),
icon: AppAssets.bluetooth,
descriptionTextColor: AppColors.greyTextColor
),
],
).paddingSymmetrical(16.w, 16.h),
)
],
).paddingSymmetrical(24.w, 16.h),
), ),
child: Column(
mainAxisSize: MainAxisSize.max,
spacing: 18.h,
children: [
Image.asset(
smartwatchDetails.watchIcon,
fit: BoxFit.contain,
height: 280.h,
width: 280.w,
),
DecoratedBox(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 12.h),
child: Column(
children: [
watchContentDetails(
title: smartwatchDetails.detailsTitle,
description: smartwatchDetails.details,
icon: smartwatchDetails.smallIcon,
descriptionTextColor: AppColors.primaryRedColor,
),
Divider(
color: AppColors.dividerColor,
thickness: 1.h,
).paddingOnly(top: 16.h, bottom: 16.h),
watchContentDetails(
title: smartwatchDetails.secondTitle,
description: LocaleKeys.updatetheinformation.tr(),
icon: AppAssets.bluetooth,
descriptionTextColor: AppColors.greyTextColor,
),
],
).paddingSymmetrical(16.w, 16.h),
)
],
).paddingSymmetrical(24.w, 16.h),
),
); );
} }
Widget watchContentDetails({required String title, required String description, required String icon, required Color descriptionTextColor}) { Widget watchContentDetails({required String title, required String description, required String icon, required Color descriptionTextColor}) {
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
@ -90,9 +91,7 @@ class SmartwatchInstructionsPage extends StatelessWidget {
DecoratedBox( DecoratedBox(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 12.h), decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 12.h),
child: Utils.buildSvgWithAssets(icon: icon, width: 40.w, height: 40.h), child: Utils.buildSvgWithAssets(icon: icon, width: 40.w, height: 40.h),
), ),
title.toText16(isBold: true, color: AppColors.textColor), title.toText16(isBold: true, color: AppColors.textColor),
description.toText12(isBold: true, color: descriptionTextColor) description.toText12(isBold: true, color: descriptionTextColor)
], ],

@ -1,5 +1,6 @@
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/api_consts.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart'; import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/app_export.dart'; import 'package:hmg_patient_app_new/core/app_export.dart';
import 'package:hmg_patient_app_new/core/app_state.dart'; import 'package:hmg_patient_app_new/core/app_state.dart';
@ -46,13 +47,13 @@ class _OrganSelectorPageState extends State<OrganSelectorPage> {
Future<void> _checkAndShowTutorial() async { Future<void> _checkAndShowTutorial() async {
// final hasSeenTutorial = cacheService.getBool(key: CacheConst.organSelectorTutorialShown) ?? false; // final hasSeenTutorial = cacheService.getBool(key: CacheConst.organSelectorTutorialShown) ?? false;
// if (!hasSeenTutorial) { // if (!hasSeenTutorial) {
// Show tutorial after a short delay to ensure the screen is fully built // Show tutorial after a short delay to ensure the screen is fully built
await Future.delayed(const Duration(milliseconds: 500)); await Future.delayed(const Duration(milliseconds: 500));
if (mounted) { if (mounted) {
setState(() { setState(() {
_showTutorial = true; _showTutorial = true;
}); });
} }
// } // }
} }
@ -74,12 +75,17 @@ class _OrganSelectorPageState extends State<OrganSelectorPage> {
loadingText: LocaleKeys.pleaseWait.tr(context: context), loadingText: LocaleKeys.pleaseWait.tr(context: context),
); );
final String userName = 'guest_user'; // Get fileNo if user is logged in
final String password = '123456'; String? fileNo;
if (_appState.isAuthenticated) {
final user = _appState.getAuthenticatedUser();
fileNo = user?.patientId.toString();
}
await viewModel.getSymptomsUserDetails( await viewModel.getSymptomsUserDetails(
userName: userName, userName: ApiConsts.symptomsCheckerUsername,
password: password, password: ApiConsts.symptomsCheckerPassword,
fileNo: fileNo,
onSuccess: () { onSuccess: () {
LoaderBottomSheet.hideLoader(); LoaderBottomSheet.hideLoader();
context.navigateWithName(AppRoutes.symptomsSelectorPage); context.navigateWithName(AppRoutes.symptomsSelectorPage);

@ -175,20 +175,44 @@ class _TriagePageState extends State<TriagePage> {
return; return;
} }
// Collect all evidence from all items // Collect evidence based on question type
for (var item in currentQuestion.items!) { if (viewModel.isTriageQuestionSingleSelection) {
final itemId = item.id ?? ""; // Type 1: Single selection - only one evidence entry
if (itemId.isEmpty) continue; final selectedItemId = viewModel.selectedSingleItemId;
final selectedChoiceIndex = viewModel.selectedSingleChoiceIndex;
final selectedChoiceIndex = viewModel.getTriageChoiceForItem(itemId);
if (selectedChoiceIndex == null) continue; if (selectedItemId != null && selectedChoiceIndex != null) {
// Find the item and choice
if (item.choices != null && selectedChoiceIndex < item.choices!.length) { for (var item in currentQuestion.items!) {
final selectedChoice = item.choices![selectedChoiceIndex]; if (item.id == selectedItemId) {
final choiceId = selectedChoice.id ?? ""; if (item.choices != null && selectedChoiceIndex < item.choices!.length) {
final selectedChoice = item.choices![selectedChoiceIndex];
if (choiceId.isNotEmpty) { final choiceId = selectedChoice.id ?? "";
viewModel.addTriageEvidence(itemId, choiceId);
if (choiceId.isNotEmpty) {
viewModel.addTriageEvidence(selectedItemId, choiceId);
}
}
break;
}
}
}
} else {
// Type 2: Multi-item selection - collect evidence from all items
for (var item in currentQuestion.items!) {
final itemId = item.id ?? "";
if (itemId.isEmpty) continue;
final selectedChoiceIndex = viewModel.getTriageChoiceForItem(itemId);
if (selectedChoiceIndex == null) continue;
if (item.choices != null && selectedChoiceIndex < item.choices!.length) {
final selectedChoice = item.choices![selectedChoiceIndex];
final choiceId = selectedChoice.id ?? "";
if (choiceId.isNotEmpty) {
viewModel.addTriageEvidence(itemId, choiceId);
}
} }
} }
} }
@ -390,7 +414,10 @@ class _TriagePageState extends State<TriagePage> {
SizedBox(height: 8.h), SizedBox(height: 8.h),
// Choices for this item // Choices for this item
...List.generate(choices.length, (choiceIndex) { ...List.generate(choices.length, (choiceIndex) {
bool selected = viewModel.getTriageChoiceForItem(itemId) == choiceIndex; // Check selection based on question type
bool selected = viewModel.isTriageQuestionSingleSelection
? viewModel.isTriageSingleOptionSelected(itemId, choiceIndex)
: viewModel.getTriageChoiceForItem(itemId) == choiceIndex;
return _buildOptionItem(itemId, choiceIndex, selected, choices[choiceIndex].label ?? ""); return _buildOptionItem(itemId, choiceIndex, selected, choices[choiceIndex].label ?? "");
}), }),
@ -468,18 +495,12 @@ class _TriagePageState extends State<TriagePage> {
text: TextSpan( text: TextSpan(
text: "${LocaleKeys.possibleSymptom.tr(context: context)} ", text: "${LocaleKeys.possibleSymptom.tr(context: context)} ",
style: TextStyle( style: TextStyle(
color: AppColors.greyTextColor, fontWeight: FontWeight.w600, fontSize: 14.f, fontFamily: isArabic ? 'CairoArabic' : 'Poppins'), color: AppColors.greyTextColor, fontWeight: FontWeight.w600, fontSize: 14.f, fontFamily: isArabic ? 'CairoArabic' : 'Poppins'),
children: [ children: [
TextSpan( TextSpan(
text: suggestedCondition, text: suggestedCondition,
style: TextStyle( style: TextStyle(
color: AppColors.textColor, color: AppColors.textColor, fontWeight: FontWeight.w600, fontSize: 14.f, fontFamily: isArabic ? 'CairoArabic' : 'Poppins'),
fontWeight: FontWeight.w600,
fontSize: 14.f, fontFamily: isArabic ? 'CairoArabic' : 'Poppins'),
), ),
], ],
), ),

@ -99,9 +99,8 @@ class _UserInfoSelectionPageState extends State<UserInfoSelectionPage> {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
title.toText14(isBold: true), title.toText14(isBold: true),
subTitle subTitle.toText12(color: AppColors.primaryRedColor, isBold: true, isEnglishOnly: true).toShimmer2(
.toText12(color: AppColors.primaryRedColor, isBold: true, isEnglishOnly: true) isShow: (leadingIcon == AppAssets.rulerIcon || leadingIcon == AppAssets.weightScale) && hmgServicesVM.isVitalSignLoading),
.toShimmer2(isShow: (leadingIcon == AppAssets.rulerIcon || leadingIcon == AppAssets.weightScale) && hmgServicesVM.isVitalSignLoading),
], ],
), ),
], ],
@ -159,7 +158,9 @@ class _UserInfoSelectionPageState extends State<UserInfoSelectionPage> {
} }
} }
} else { } 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( return Scaffold(
@ -167,7 +168,10 @@ class _UserInfoSelectionPageState extends State<UserInfoSelectionPage> {
body: Consumer2<SymptomsCheckerViewModel, HmgServicesViewModel>( body: Consumer2<SymptomsCheckerViewModel, HmgServicesViewModel>(
builder: (context, viewModel, hmgServicesVM, child) { builder: (context, viewModel, hmgServicesVM, child) {
// Check if any field is empty // 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 // Get display values
String genderText = _getLocalizedGender(viewModel.selectedGender, context); String genderText = _getLocalizedGender(viewModel.selectedGender, context);
@ -313,6 +317,7 @@ class _UserInfoSelectionPageState extends State<UserInfoSelectionPage> {
), ),
], ],
), ),
SizedBox(height: 24.h),
], ],
).paddingSymmetrical(24.w, 0), ).paddingSymmetrical(24.w, 0),
), ),

@ -53,9 +53,10 @@ class HeightSelectionPage extends StatelessWidget {
child: Text( child: Text(
'CM', 'CM',
style: TextStyle( style: TextStyle(
fontWeight: FontWeight.w700, fontWeight: FontWeight.w700,
fontSize: 14.f, fontSize: 14.f,
color: viewModel.isHeightCm ? AppColors.primaryRedColor : AppColors.textColor.withValues(alpha: 0.6), fontFamily: "Poppins"), color: viewModel.isHeightCm ? AppColors.primaryRedColor : AppColors.textColor.withValues(alpha: 0.6),
fontFamily: "Poppins"),
), ),
), ),
), ),
@ -74,9 +75,10 @@ class HeightSelectionPage extends StatelessWidget {
child: Text( child: Text(
'FT', 'FT',
style: TextStyle( style: TextStyle(
fontWeight: FontWeight.w700, fontWeight: FontWeight.w700,
fontSize: 14.f, fontSize: 14.f,
color: !viewModel.isHeightCm ? AppColors.primaryRedColor : AppColors.textColor.withValues(alpha: 0.6), fontFamily: "Poppins"), color: !viewModel.isHeightCm ? AppColors.primaryRedColor : AppColors.textColor.withValues(alpha: 0.6),
fontFamily: "Poppins"),
), ),
), ),
), ),
@ -128,10 +130,7 @@ class HeightSelectionPage extends StatelessWidget {
TextSpan( TextSpan(
text: text:
viewModel.isHeightCm ? viewModel.selectedHeight?.round().toString() : viewModel.selectedHeight?.toStringAsFixed(1), viewModel.isHeightCm ? viewModel.selectedHeight?.round().toString() : viewModel.selectedHeight?.toStringAsFixed(1),
style: TextStyle( style: TextStyle(fontSize: 90.f, color: AppColors.textColor, height: 1, fontFamily: "Poppins"),
fontSize: 90.f,
color: AppColors.textColor,
height: 1, fontFamily: "Poppins"),
), ),
TextSpan( TextSpan(
text: viewModel.isHeightCm ? 'cm' : 'ft', text: viewModel.isHeightCm ? 'cm' : 'ft',
@ -142,7 +141,7 @@ class HeightSelectionPage extends StatelessWidget {
), ),
], ],
), ),
).paddingOnly(bottom: 100.h, left: 20.w); ).paddingOnly(bottom: 100.h, left: isFoldable ? 40.w : 20.w);
}, },
), ),
), ),

@ -181,7 +181,7 @@ class _UserInfoFlowManagerState extends State<UserInfoFlowManager> {
color: AppColors.whiteColor, color: AppColors.whiteColor,
borderRadius: BorderRadius.vertical(top: Radius.circular(24.r)), 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( child: SafeArea(
top: false, top: false,
child: isSingleEdit child: isSingleEdit

@ -131,12 +131,11 @@ class _HeightScaleState extends State<HeightScale> {
child: Text( child: Text(
widget.isCm ? height.round().toString() : height.toStringAsFixed(1), widget.isCm ? height.round().toString() : height.toStringAsFixed(1),
style: TextStyle( style: TextStyle(
fontSize: 11.f, fontSize: 11.f,
color: AppColors.greyTextColor, color: AppColors.greyTextColor,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
height: 1, height: isFoldable ? 0.5 : 1,
fontFamily: "Poppins" fontFamily: "Poppins"),
),
textAlign: TextAlign.right, textAlign: TextAlign.right,
), ),
) )

@ -1,4 +1,5 @@
import 'dart:async'; import 'dart:async';
import 'dart:ui' as ui;
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
@ -24,8 +25,6 @@ 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/routes/custom_page_route.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'dart:ui' as ui;
class AncillaryOrderDetailsList extends StatefulWidget { class AncillaryOrderDetailsList extends StatefulWidget {
final int appointmentNoVida; final int appointmentNoVida;
final int orderNo; final int orderNo;
@ -653,13 +652,15 @@ class _AncillaryOrderDetailsListState extends State<AncillaryOrderDetailsList> {
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
SizedBox( SizedBox(
width: 200.h, width: isFoldable ? 220.w : 200.w,
child: Utils.getPaymentMethods(), child: Utils.getPaymentMethods(),
), ),
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Utils.getPaymentAmountWithSymbol(NumberFormat.decimalPattern().format(_getTotalAmount()).toText24(isBold: true, isEnglishOnly: true), AppColors.blackColor, 17, isSaudiCurrency: true), Utils.getPaymentAmountWithSymbol(
NumberFormat.decimalPattern().format(_getTotalAmount()).toText24(isBold: true, isEnglishOnly: true), AppColors.blackColor, 17,
isSaudiCurrency: true),
], ],
), ),
], ],
@ -695,40 +696,5 @@ class _AncillaryOrderDetailsListState extends State<AncillaryOrderDetailsList> {
], ],
).paddingOnly(left: 16.h, top: 24.h, right: 16.h, bottom: 0.h), ).paddingOnly(left: 16.h, top: 24.h, right: 16.h, bottom: 0.h),
); );
// Column(
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
// children: [
// SizedBox(height: 16.h),
// _buildSummarySection(orderData),
// SizedBox(height: 16.h),
// CustomButton(
// borderWidth: 0,
// backgroundColor: AppColors.infoLightColor,
// text: "Proceed to Payment".needTranslation,
// onPressed: () {
// // Navigate to payment page with selected procedures
// Navigator.of(context).push(
// CustomPageRoute(
// page: AncillaryOrderPaymentPage(
// appointmentNoVida: widget.appointmentNoVida,
// orderNo: widget.orderNo,
// projectID: widget.projectID,
// selectedProcedures: selectedProcedures,
// totalAmount: _getTotalAmount(),
// appointmentDate: orderData.appointmentDate,
// ),
// ),
// );
// },
// isDisabled: !isButtonEnabled,
// textColor: AppColors.whiteColor,
// borderRadius: 12.r,
// borderColor: Colors.transparent,
// padding: EdgeInsets.symmetric(vertical: 16.h),
// ),
// SizedBox(height: 22.h),
// ],
// ).paddingSymmetrical(24.w, 0);
} }
} }

@ -20,7 +20,6 @@ import 'package:hmg_patient_app_new/presentation/home_health_care/hhc_procedures
import 'package:hmg_patient_app_new/presentation/medical_file/medical_file_page.dart'; import 'package:hmg_patient_app_new/presentation/medical_file/medical_file_page.dart';
import 'package:hmg_patient_app_new/presentation/my_family/my_family.dart'; import 'package:hmg_patient_app_new/presentation/my_family/my_family.dart';
import 'package:hmg_patient_app_new/presentation/servicesPriceList/services_price_list_page.dart'; import 'package:hmg_patient_app_new/presentation/servicesPriceList/services_price_list_page.dart';
import 'package:hmg_patient_app_new/presentation/smartwatches/huawei_health_example.dart';
import 'package:hmg_patient_app_new/presentation/smartwatches/smartwatch_home_page.dart'; import 'package:hmg_patient_app_new/presentation/smartwatches/smartwatch_home_page.dart';
import 'package:hmg_patient_app_new/presentation/symptoms_checker/organ_selector_screen.dart'; import 'package:hmg_patient_app_new/presentation/symptoms_checker/organ_selector_screen.dart';
import 'package:hmg_patient_app_new/presentation/symptoms_checker/possible_conditions_screen.dart'; import 'package:hmg_patient_app_new/presentation/symptoms_checker/possible_conditions_screen.dart';
@ -43,7 +42,6 @@ import '../features/monthly_reports/monthly_reports_repo.dart';
import '../features/monthly_reports/monthly_reports_view_model.dart'; import '../features/monthly_reports/monthly_reports_view_model.dart';
import '../features/qr_parking/qr_parking_view_model.dart'; import '../features/qr_parking/qr_parking_view_model.dart';
import '../presentation/parking/paking_page.dart'; import '../presentation/parking/paking_page.dart';
import '../presentation/smartwatches/smartwatch_instructions_page.dart';
import '../services/error_handler_service.dart'; import '../services/error_handler_service.dart';
class AppRoutes { class AppRoutes {

@ -115,7 +115,8 @@ class CollapsingListView extends StatelessWidget {
margin: EdgeInsets.fromLTRB(24.w, 0, 24.w, 0), margin: EdgeInsets.fromLTRB(24.w, 0, 24.w, 0),
child: Transform.flip( child: Transform.flip(
flipX: appState.isArabic(), flipX: appState.isArabic(),
child: Utils.buildSvgWithAssets(icon: isClose ? AppAssets.close_bottom_nav_trans : AppAssets.arrow_back_new, width: 24.h, height: 24.h), child: Utils.buildSvgWithAssets(
icon: isClose ? AppAssets.close_bottom_nav_trans : AppAssets.arrow_back_new, width: 24.h, height: 24.h),
), ),
).onPress(() { ).onPress(() {
if (leadingCallback != null) { if (leadingCallback != null) {
@ -288,7 +289,8 @@ class _ScrollAnimatedTitleState extends State<ScrollAnimatedTitle> {
return Container( return Container(
// height: (widget.preferredSize.height - _fontSize / 2).h, // height: (widget.preferredSize.height - _fontSize / 2).h,
height: 56.h, height: 56.h,
alignment: isRtl ? (widget.showBack ? Alignment.topRight : Alignment.centerRight) : (widget.showBack ? Alignment.topLeft : Alignment.centerLeft), alignment:
isRtl ? (widget.showBack ? Alignment.topRight : Alignment.centerRight) : (widget.showBack ? Alignment.topLeft : Alignment.centerLeft),
padding: EdgeInsets.fromLTRB(24.w, 0, 24.w, 0), padding: EdgeInsets.fromLTRB(24.w, 0, 24.w, 0),
child: Row( child: Row(
spacing: 4.h, spacing: 4.h,
@ -304,19 +306,38 @@ class _ScrollAnimatedTitleState extends State<ScrollAnimatedTitle> {
), ),
).expanded, ).expanded,
...[ ...[
if (widget.logout != null) actionButton(context, t, title: LocaleKeys.logout.tr(context: context), icon: AppAssets.logout).onPress(widget.logout!), if (widget.logout != null)
if (widget.report != null) actionButton(context, t, title: LocaleKeys.feedback.tr(context: context), icon: AppAssets.report_icon).onPress(widget.report!), actionButton(context, t, title: LocaleKeys.logout.tr(context: context), icon: AppAssets.logout).onPress(widget.logout!),
if (widget.history != null) actionButton(context, t, title: LocaleKeys.history.tr(context: context), icon: AppAssets.insurance_history_icon).onPress(widget.history!), if (widget.report != null)
if (widget.instructions != null) actionButton(context, t, title: LocaleKeys.instructions.tr(context: context), icon: AppAssets.requests).onPress(widget.instructions!), actionButton(context, t, title: LocaleKeys.feedback.tr(context: context), icon: AppAssets.report_icon).onPress(widget.report!),
if (widget.requests != null) actionButton(context, t, title: LocaleKeys.requests.tr(context: context), icon: AppAssets.insurance_history_icon).onPress(widget.requests!), if (widget.history != null)
if (widget.sendEmail != null) actionButton(context, t, title: LocaleKeys.sendEmail.tr(context: context), icon: AppAssets.email).onPress(widget.sendEmail!), actionButton(context, t, title: LocaleKeys.history.tr(context: context), icon: AppAssets.insurance_history_icon)
if (widget.doctorResponse != null) actionButton(context, t, title: LocaleKeys.doctorResponses.tr(context: context), icon: AppAssets.doctorResponseIcon).onPress(widget.doctorResponse!), .onPress(widget.history!),
if (widget.instructions != null)
actionButton(context, t, title: LocaleKeys.instructions.tr(context: context), icon: AppAssets.requests).onPress(widget.instructions!),
if (widget.requests != null)
actionButton(context, t, title: LocaleKeys.requests.tr(context: context), icon: AppAssets.insurance_history_icon)
.onPress(widget.requests!),
if (widget.sendEmail != null)
actionButton(context, t, title: LocaleKeys.sendEmail.tr(context: context), icon: AppAssets.email).onPress(widget.sendEmail!),
if (widget.doctorResponse != null)
actionButton(context, t, title: LocaleKeys.doctorResponses.tr(context: context), icon: AppAssets.doctorResponseIcon)
.onPress(widget.doctorResponse!),
if (widget.search != null) Utils.buildSvgWithAssets(icon: AppAssets.search_icon).onPress(widget.search!), if (widget.search != null) Utils.buildSvgWithAssets(icon: AppAssets.search_icon).onPress(widget.search!),
if (widget.aiOverview != null) actionButton(context, t, title: LocaleKeys.aiOverView.tr(context: context), icon: AppAssets.aiOverView, isAiButton: true).onPress(widget.aiOverview!), if (widget.aiOverview != null)
if (widget.downloadReport != null) actionButton(context, t, title: LocaleKeys.downloadReport.tr(context: context), icon: AppAssets.download).onPress(widget.downloadReport!), actionButton(context, t, title: LocaleKeys.aiOverView.tr(context: context), icon: AppAssets.aiOverView, isAiButton: true)
if (widget.viewImage != null) actionButton(context, t, title: LocaleKeys.viewRadiologyImage.tr(context: context), icon: AppAssets.download).onPress(widget.viewImage!), .onPress(widget.aiOverview!),
if (widget.location != null) actionButton(context, t, title: LocaleKeys.sortByLocation.tr(context: context), icon: AppAssets.location).onPress(widget.location!), if (widget.downloadReport != null)
if (widget.downloadInvoice != null) actionButton(context, t, title: LocaleKeys.downloadInvoice.tr(context: context), icon: AppAssets.download).onPress(widget.downloadInvoice!), actionButton(context, t, title: LocaleKeys.downloadReport.tr(context: context), icon: AppAssets.download)
.onPress(widget.downloadReport!),
if (widget.viewImage != null)
actionButton(context, t, title: LocaleKeys.viewRadiologyImage.tr(context: context), icon: AppAssets.download)
.onPress(widget.viewImage!),
if (widget.location != null)
actionButton(context, t, title: LocaleKeys.sortByLocation.tr(context: context), icon: AppAssets.location).onPress(widget.location!),
if (widget.downloadInvoice != null)
actionButton(context, t, title: LocaleKeys.downloadInvoice.tr(context: context), icon: AppAssets.download)
.onPress(widget.downloadInvoice!),
if (widget.trailing != null) widget.trailing!, if (widget.trailing != null) widget.trailing!,
] ]
], ],
@ -330,9 +351,10 @@ class _ScrollAnimatedTitleState extends State<ScrollAnimatedTitle> {
duration: Duration(milliseconds: 150), duration: Duration(milliseconds: 150),
child: Center( child: Center(
child: Container( child: Container(
height: 40.h, height: isFoldable ? 50.h : 40.h,
padding: EdgeInsets.all(8.w), padding: EdgeInsets.all(8.w),
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 8.r, side: BorderSide(width: 1, color: AppColors.borderGrayColor)), decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor, borderRadius: 8.r, side: BorderSide(width: 1, color: AppColors.borderGrayColor)),
child: ShaderMask( child: ShaderMask(
blendMode: BlendMode.srcIn, blendMode: BlendMode.srcIn,
shaderCallback: (bounds) => AppColors.aiLinearGradient.createShader(bounds), shaderCallback: (bounds) => AppColors.aiLinearGradient.createShader(bounds),
@ -365,7 +387,7 @@ class _ScrollAnimatedTitleState extends State<ScrollAnimatedTitle> {
: AnimatedSize( : AnimatedSize(
duration: Duration(milliseconds: 150), duration: Duration(milliseconds: 150),
child: Container( child: Container(
height: 40.h, height: isFoldable ? 50.h : 40.h,
padding: EdgeInsets.all(8.w), padding: EdgeInsets.all(8.w),
decoration: RoundedRectangleBorder().toSmoothCornerDecoration( decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.secondaryLightRedColor, color: AppColors.secondaryLightRedColor,

@ -1042,13 +1042,13 @@ packages:
source: hosted source: hosted
version: "1.2.1" version: "1.2.1"
image_picker_android: image_picker_android:
dependency: transitive dependency: "direct main"
description: description:
name: image_picker_android name: image_picker_android
sha256: "5e9bf126c37c117cf8094215373c6d561117a3cfb50ebc5add1a61dc6e224677" sha256: "66810af8e99b2657ee98e5c6f02064f69bb63f7a70e343937f70946c5f8c6622"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.8.13+10" version: "0.8.13+16"
image_picker_for_web: image_picker_for_web:
dependency: transitive dependency: transitive
description: description:
@ -1082,7 +1082,7 @@ packages:
source: hosted source: hosted
version: "0.2.2+1" version: "0.2.2+1"
image_picker_platform_interface: image_picker_platform_interface:
dependency: transitive dependency: "direct main"
description: description:
name: image_picker_platform_interface name: image_picker_platform_interface
sha256: "567e056716333a1647c64bb6bd873cff7622233a5c3f694be28a583d4715690c" sha256: "567e056716333a1647c64bb6bd873cff7622233a5c3f694be28a583d4715690c"

@ -101,9 +101,7 @@ dependencies:
flutter_widget_from_html: ^0.17.1 flutter_widget_from_html: ^0.17.1
huawei_map: ^6.12.0+301 huawei_map: ^6.12.0+301
flutter_image_compress: ^2.3.0 flutter_image_compress: ^2.3.0
scrollable_positioned_list: ^0.3.8 scrollable_positioned_list: ^0.3.8
in_app_review: ^2.0.11 in_app_review: ^2.0.11
dev_dependencies: dev_dependencies:

Loading…
Cancel
Save