# Conflicts:
#	lib/presentation/emergency_services/call_ambulance/tracking_screen.dart
#	lib/presentation/profile_settings/profile_settings.dart
#	lib/presentation/radiology/radiology_orders_page.dart
#	lib/widgets/chip/app_custom_chip_widget.dart
pull/98/head
Sultan khan 2 months ago
commit 8e132e964f

@ -1,6 +1,6 @@
# HMG Patient App
A comprehensive Flutter-based mobile application for HMG (Hospital Management Group) patients,
A comprehensive Flutter-based mobile application for HMG (Habib Medical Group) patients,
providing seamless healthcare services and patient management features.
## 📱 Features
@ -162,5 +162,4 @@ flutter build ios --release
This project is proprietary software developed for HMG Healthcare Group.
**Built with ❤️ for better healthcare accessibility**

@ -1,74 +0,0 @@
{
"agcgw":{
"backurl":"connect-drcn.hispace.hicloud.com",
"url":"connect-drcn.dbankcloud.cn",
"websocketbackurl":"connect-ws-drcn.hispace.dbankcloud.com",
"websocketurl":"connect-ws-drcn.hispace.dbankcloud.cn"
},
"agcgw_all":{
"CN":"connect-drcn.dbankcloud.cn",
"CN_back":"connect-drcn.hispace.hicloud.com",
"DE":"connect-dre.dbankcloud.cn",
"DE_back":"connect-dre.hispace.hicloud.com",
"RU":"connect-drru.dbankcloud.cn",
"RU_back":"connect-drru.hispace.hicloud.com",
"SG":"connect-dra.dbankcloud.cn",
"SG_back":"connect-dra.hispace.hicloud.com"
},
"client":{
"cp_id":"2640966000002322881",
"product_id":"736430079244816567",
"client_id":"563735388191982656",
"client_secret":"650C7C799812AFFD53A10C7756CF05FB9F215A7E49032ABA8EBF3E14B77535CF",
"project_id":"736430079244816567",
"app_id":"102857389",
"api_key":"CgB6e3x9DJzMgRCmnT6dyUEkp6UsIfddb6l3w0ZEXzeiRMHEFi3400Z5fJ5qaHneU0OrAI/JRpk+DMGVs3QpUxlI",
"package_name":"com.ejada.hmg"
},
"oauth_client":{
"client_id":"102857389",
"client_type":1
},
"app_info":{
"app_id":"102857389",
"package_name":"com.ejada.hmg"
},
"service":{
"analytics":{
"collector_url":"datacollector-drcn.dt.hicloud.com,datacollector-drcn.dt.dbankcloud.cn",
"collector_url_ru":"datacollector-drru.dt.hicloud.com,datacollector-drru.dt.dbankcloud.cn",
"collector_url_sg":"datacollector-dra.dt.hicloud.com,datacollector-dra.dt.dbankcloud.cn",
"collector_url_de":"datacollector-dre.dt.hicloud.com,datacollector-dre.dt.dbankcloud.cn",
"collector_url_cn":"datacollector-drcn.dt.hicloud.com,datacollector-drcn.dt.dbankcloud.cn",
"resource_id":"p1",
"channel_id":""
},
"search":{
"url":"https://search-drcn.cloud.huawei.com"
},
"cloudstorage":{
"storage_url":"https://agc-storage-drcn.platform.dbankcloud.cn"
},
"ml":{
"mlservice_url":"ml-api-drcn.ai.dbankcloud.com,ml-api-drcn.ai.dbankcloud.cn"
}
},
"region":"CN",
"configuration_version":"3.0",
"appInfos":[
{
"package_name":"com.ejada.hmg",
"client":{
"app_id":"102857389"
},
"app_info":{
"package_name":"com.ejada.hmg",
"app_id":"102857389"
},
"oauth_client":{
"client_type":1,
"client_id":"102857389"
}
}
]
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

@ -248,9 +248,6 @@
<meta-data
android:name="com.google.android.geo.API_KEY"
android:value="AIzaSyB6TERnxIr0yJ3qG4ULBZbu0sAD4tGqtng" />
<meta-data
android:name="com.huawei.hms.client.appid"
android:value="appid=102857389" />
<!--
Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java

@ -0,0 +1,27 @@
//package com.cloud.diplomaticquarterapp
package com.ejada.hmg
import io.flutter.app.FlutterApplication
class Application : FlutterApplication() {
override fun onCreate() {
super.onCreate()
}
}
//import io.flutter.app.FlutterApplication
//import io.flutter.plugin.common.PluginRegistry
//import io.flutter.plugin.common.PluginRegistry.PluginRegistrantCallback
//import io.flutter.plugins.firebasemessaging.FlutterFirebaseMessagingService
//
//class Application : FlutterApplication(), PluginRegistrantCallback {
// override fun onCreate() {
// super.onCreate()
// FlutterFirebaseMessagingService.setPluginRegistrant(this)
// }
//
// override fun registerWith(registry: PluginRegistry?) {
// FirebaseCloudMessagingPluginRegistrant.registerWith(registry)
// }
//}

@ -0,0 +1,6 @@
package com.ejada.hmg
import io.flutter.embedding.android.FlutterFragmentActivity
class MainActivity : FlutterFragmentActivity()

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1021 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 180 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.cloud.diplomaticquarterapp.whatsapp.WhatsAppCodeActivity">
</androidx.constraintlayout.widget.ConstraintLayout>

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:gravity="center_horizontal"
android:layout_gravity="center_horizontal">
<FrameLayout
android:id="@+id/publisher_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#FF9800" />
</LinearLayout>

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:gravity="center_horizontal"
android:layout_gravity="center_horizontal">
<FrameLayout
android:id="@+id/subscriber_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#3F51B5" />
<TextView
android:text="Remote"
android:textColor="#FFFFFF"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

@ -0,0 +1,3 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools"
tools:keep="@drawable/*,@raw/slow_spring_board" />

@ -0,0 +1,23 @@
<resources>
<string name="app_name">HMG Patient App</string>
<string name="geofence_unknown_error">
Unknown error: the Geofence service is not available now.
</string>
<string name="geofence_not_available">
Geofence service is not available now. Go to Settings>Location>Mode and choose High accuracy.
</string>
<string name="geofence_too_many_geofences">
Your app has registered too many geofences.
</string>
<string name="geofence_too_many_pending_intents">
You have provided too many PendingIntents to the addGeofences() call.
</string>
<string name="GEOFENCE_INSUFFICIENT_LOCATION_PERMISSION">
App do not have permission to access location service.
</string>
<string name="GEOFENCE_REQUEST_TOO_FREQUENT">
Geofence requests happened too frequently.
</string>
<string name="mapbox_access_token" translatable="false">sk.eyJ1IjoicndhaWQiLCJhIjoiY2x6NWo0bTMzMWZodzJrcGZpemYzc3Z4dSJ9.uSSZuwNSGCcCdPAiORECmg</string>
</resources>

File diff suppressed because one or more lines are too long

Binary file not shown.

@ -0,0 +1,5 @@
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M2.88966 2.26044H6.1556C6.54339 2.26042 6.87763 2.2604 7.1452 2.29638C7.43199 2.33494 7.7069 2.42189 7.92987 2.64486C8.15285 2.86784 8.2398 3.14275 8.27836 3.42953C8.30096 3.59766 8.30936 3.79206 8.31247 4.00954C8.31248 4.01004 8.31288 4.01044 8.31338 4.01044C8.31369 4.01044 8.31396 4.0106 8.31413 4.01083C8.31413 4.01063 8.31423 4.01098 8.31413 4.01083L8.31429 6.19794C8.31429 6.43957 8.51017 6.63544 8.75179 6.63544C8.99342 6.63544 9.18929 6.43957 9.18929 6.19794V4.20362C9.18929 4.10493 9.2722 4.02578 9.37041 4.03551C9.63863 4.06206 9.88036 4.11895 10.1131 4.2507C10.3443 4.38162 10.5211 4.56074 10.6893 4.77855C10.8614 5.00128 11.0127 5.23986 11.1635 5.47761L11.164 5.4784C11.1889 5.51758 11.2137 5.55673 11.2386 5.59579C11.5034 6.01096 11.8302 6.35342 12.2362 6.63158C12.6288 6.90049 12.9411 7.11434 13.1141 7.46714C13.2034 7.64923 13.24 7.83466 13.2568 8.03171C13.2597 8.06529 13.2329 8.09377 13.1992 8.09377H12.6893C12.4477 8.09377 12.2518 8.28965 12.2518 8.53127C12.2518 8.7729 12.4477 8.96877 12.6893 8.96877H12.9207C13.0883 8.96877 13.1722 8.96877 13.2238 9.02251C13.2755 9.07625 13.2722 9.15762 13.2658 9.32036C13.2616 9.42578 13.2549 9.52356 13.244 9.61307C13.2135 9.86447 13.1447 10.1138 12.9593 10.3235C12.7687 10.5391 12.5059 10.6479 12.2248 10.6872C12.1439 10.6985 12.0789 10.625 12.0886 10.5439C12.0989 10.4578 12.1042 10.3701 12.1042 10.2813C12.1042 9.07315 11.1249 8.09377 9.91675 8.09377C8.71731 8.09377 7.72602 9.09672 7.72928 10.2935C7.73046 10.5082 7.73105 10.6156 7.67973 10.6672C7.62842 10.7188 7.53389 10.7188 7.34482 10.7188H6.65534C6.46628 10.7188 6.37175 10.7188 6.32043 10.6672C6.26912 10.6156 6.26971 10.5082 6.27088 10.2935C6.27415 9.09672 5.28286 8.09377 4.08342 8.09377C2.87529 8.09377 1.89592 9.07315 1.89592 10.2813C1.89592 10.4083 1.90675 10.5328 1.92753 10.6539C1.93036 10.6704 1.9166 10.6851 1.90005 10.6828C1.61327 10.6443 1.33836 10.5573 1.11539 10.3344C0.892408 10.1114 0.805457 9.83647 0.766899 9.54969C0.730924 9.28211 0.73094 8.94798 0.730958 8.56018V4.41914C0.73094 4.03135 0.730924 3.69711 0.766899 3.42953C0.805457 3.14275 0.892408 2.86784 1.11539 2.64486C1.33836 2.42189 1.61327 2.33494 1.90005 2.29638C2.16763 2.2604 2.50187 2.26042 2.88966 2.26044ZM5.10596 4.44794C5.10596 4.20631 4.91008 4.01044 4.66846 4.01044C4.42683 4.01044 4.23096 4.20631 4.23096 4.44794V5.17711H3.50179C3.26017 5.17711 3.06429 5.37298 3.06429 5.61461C3.06429 5.85623 3.26017 6.05211 3.50179 6.05211H4.23096V6.78127C4.23096 7.0229 4.42683 7.21877 4.66846 7.21877C4.91008 7.21877 5.10596 7.0229 5.10596 6.78127V6.05211H5.83513C6.07675 6.05211 6.27263 5.85623 6.27263 5.61461C6.27263 5.37298 6.07675 5.17711 5.83513 5.17711H5.10596V4.44794Z" fill="#2E3039"/>
<path d="M11.3757 10.2813C11.3757 11.0867 10.7227 11.7396 9.91732 11.7396C9.1119 11.7396 8.45898 11.0867 8.45898 10.2813C8.45898 9.47586 9.1119 8.82294 9.91732 8.82294C10.7227 8.82294 11.3757 9.47586 11.3757 10.2813Z" fill="#2E3039"/>
<path d="M4.08399 11.7396C4.8894 11.7396 5.54232 11.0867 5.54232 10.2813C5.54232 9.47586 4.8894 8.82294 4.08399 8.82294C3.27857 8.82294 2.62565 9.47586 2.62565 10.2813C2.62565 11.0867 3.27857 11.7396 4.08399 11.7396Z" fill="#2E3039"/>
</svg>

After

Width:  |  Height:  |  Size: 3.2 KiB

@ -0,0 +1,4 @@
<svg width="10" height="13" viewBox="0 0 10 13" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M5 2.85449C3.79188 2.85449 2.8125 3.83387 2.8125 5.04199C2.8125 6.25011 3.79188 7.22949 5 7.22949C6.20812 7.22949 7.1875 6.25011 7.1875 5.04199C7.1875 3.83387 6.20812 2.85449 5 2.85449ZM3.6875 5.04199C3.6875 4.31712 4.27513 3.72949 5 3.72949C5.72487 3.72949 6.3125 4.31712 6.3125 5.04199C6.3125 5.76687 5.72487 6.35449 5 6.35449C4.27513 6.35449 3.6875 5.76687 3.6875 5.04199Z" fill="#ED1C2B"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M5 0.229492C2.38798 0.229492 0.1875 2.42783 0.1875 5.09268C0.1875 7.8228 2.43772 9.64709 4.31579 10.8329L4.32334 10.8377L4.33107 10.8422C4.5346 10.9593 4.76514 11.0212 5 11.0212C5.23486 11.0212 5.4654 10.9593 5.66893 10.8422L5.67567 10.8383L5.68227 10.8342C7.56769 9.65737 9.8125 7.81358 9.8125 5.09268C9.8125 2.42783 7.61202 0.229492 5 0.229492ZM1.0625 5.09268C1.0625 2.90553 2.87676 1.10449 5 1.10449C7.12324 1.10449 8.9375 2.90553 8.9375 5.09268C8.9375 7.29808 7.10717 8.91207 5.22662 10.0871C5.15694 10.1259 5.079 10.1462 5 10.1462C4.92132 10.1462 4.84369 10.1261 4.77422 10.0876C2.89372 8.89884 1.0625 7.30546 1.0625 5.09268Z" fill="#ED1C2B"/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

@ -455,6 +455,8 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2;
DEVELOPMENT_TEAM = 3A359E86ZF;
ENABLE_BITCODE = NO;
@ -467,6 +469,7 @@
MARKETING_VERSION = 1.0.0;
PRODUCT_BUNDLE_IDENTIFIER = "com.HMG.HMG-Smartphone";
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
@ -637,7 +640,9 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/RunnerDebug.entitlements;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2;
DEVELOPMENT_TEAM = 3A359E86ZF;
ENABLE_BITCODE = NO;
@ -650,6 +655,7 @@
MARKETING_VERSION = 1.0.0;
PRODUCT_BUNDLE_IDENTIFIER = "com.HMG.HMG-Smartphone";
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
@ -664,6 +670,8 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2;
DEVELOPMENT_TEAM = 3A359E86ZF;
ENABLE_BITCODE = NO;
@ -676,6 +684,7 @@
MARKETING_VERSION = 1.0.0;
PRODUCT_BUNDLE_IDENTIFIER = "com.HMG.HMG-Smartphone";
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
@ -693,7 +702,7 @@
331C808A294A63A400263BE5 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
defaultConfigurationName = Debug;
};
97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
isa = XCConfigurationList;
@ -703,7 +712,7 @@
249021D3217E4FDB00AE95B9 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
defaultConfigurationName = Debug;
};
97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
isa = XCConfigurationList;
@ -713,7 +722,7 @@
249021D4217E4FDB00AE95B9 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
defaultConfigurationName = Debug;
};
/* End XCConfigurationList section */
};

@ -2,13 +2,14 @@ import Flutter
import UIKit
import FirebaseCore
import FirebaseMessaging
import GoogleMaps
@main
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
GMSServices.provideAPIKey("AIzaSyB6TERnxIr0yJ3qG4ULBZbu0sAD4tGqtng")
FirebaseApp.configure()
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)

@ -88,16 +88,16 @@ class ApiClientImp implements ApiClient {
@override
post(
String endPoint, {
required Map<String, dynamic> body,
required Function(dynamic response, int statusCode, {int? messageStatus, String? errorMessage}) onSuccess,
required Function(String error, int statusCode, {int? messageStatus, Failure? failureType}) onFailure,
bool isAllowAny = false,
bool isExternal = false,
bool isRCService = false,
bool isPaymentServices = false,
bool bypassConnectionCheck = true,
}) async {
String endPoint, {
required Map<String, dynamic> body,
required Function(dynamic response, int statusCode, {int? messageStatus, String? errorMessage}) onSuccess,
required Function(String error, int statusCode, {int? messageStatus, Failure? failureType}) onFailure,
bool isAllowAny = false,
bool isExternal = false,
bool isRCService = false,
bool isPaymentServices = false,
bool bypassConnectionCheck = true,
}) async {
String url;
if (isExternal) {
url = endPoint;
@ -119,7 +119,8 @@ class ApiClientImp implements ApiClient {
} else {}
if (body.containsKey('isDentalAllowedBackend')) {
body['isDentalAllowedBackend'] = body.containsKey('isDentalAllowedBackend') ? body['isDentalAllowedBackend'] ?? IS_DENTAL_ALLOWED_BACKEND : IS_DENTAL_ALLOWED_BACKEND;
body['isDentalAllowedBackend'] =
body.containsKey('isDentalAllowedBackend') ? body['isDentalAllowedBackend'] ?? IS_DENTAL_ALLOWED_BACKEND : IS_DENTAL_ALLOWED_BACKEND;
}
if (!body.containsKey('IsPublicRequest')) {
@ -136,9 +137,9 @@ class ApiClientImp implements ApiClient {
body['PatientType'] = PATIENT_TYPE_ID.toString();
}
// TODO : These should be from the appState
if (user != null) {
body['TokenID'] = body['TokenID'] ?? token;
body['PatientID'] = body['PatientID'] ?? user.patientId;
body['PatientOutSA'] = body.containsKey('PatientOutSA') ? body['PatientOutSA'] ?? user.outSa : user.outSa;
@ -174,7 +175,8 @@ class ApiClientImp implements ApiClient {
}
// body['TokenID'] = "@dm!n";
// body['PatientID'] = 3966014;
// body['PatientID'] = 4772429;
// body['PatientID'] = 1231755;
// body['PatientTypeID'] = 1;
//
// body['PatientOutSA'] = 0;
@ -182,9 +184,10 @@ class ApiClientImp implements ApiClient {
}
body.removeWhere((key, value) => value == null);
log("body: ${json.encode(body)}");
log("uri: ${Uri.parse(url.trim())}");
log("body: ${json.encode(body)}");
final bool networkStatus = await Utils.checkConnection(bypassConnectionCheck: bypassConnectionCheck);
if (!networkStatus) {
@ -210,35 +213,43 @@ class ApiClientImp implements ApiClient {
onSuccess(parsed, statusCode, messageStatus: 1, errorMessage: "");
} else {
onSuccess(parsed, statusCode,
messageStatus: parsed.contains('MessageStatus') ? parsed['MessageStatus'] : 1, errorMessage: parsed.contains('ErrorEndUserMessage') ? parsed['ErrorEndUserMessage'] : "");
messageStatus: parsed.contains('MessageStatus') ? parsed['MessageStatus'] : 1,
errorMessage: parsed.contains('ErrorEndUserMessage') ? parsed['ErrorEndUserMessage'] : "");
}
} else {
if (parsed['Response_Message'] != null) {
onSuccess(parsed, statusCode, messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']);
onSuccess(parsed, statusCode,
messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']);
} else {
if (parsed['ErrorType'] == 4) {
//TODO : handle app update
onFailure(parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage'], statusCode, failureType: AppUpdateFailure("parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']"));
onFailure(parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage'], statusCode,
failureType: AppUpdateFailure("parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']"));
logApiEndpointError(endPoint, parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage'], statusCode);
}
if (parsed['ErrorType'] == 2) {
// todo: handle Logout
// todo_section: handle Logout
onFailure(
parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage'],
statusCode,
failureType: UnAuthenticatedUserFailure(parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage'] ?? "User is not Authenticated", url: url),
failureType:
UnAuthenticatedUserFailure(parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage'] ?? "User is not Authenticated", url: url),
);
// logApiEndpointError(endPoint, "session logged out", statusCode);
}
if (isAllowAny) {
onSuccess(parsed, statusCode, messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']);
onSuccess(parsed, statusCode,
messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']);
} else if (parsed['IsAuthenticated'] == null) {
if (parsed['isSMSSent'] == true) {
onSuccess(parsed, statusCode, messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']);
onSuccess(parsed, statusCode,
messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']);
} else if (parsed['MessageStatus'] == 1) {
onSuccess(parsed, statusCode, messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']);
onSuccess(parsed, statusCode,
messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']);
} else if (parsed['Result'] == 'OK') {
onSuccess(parsed, statusCode, messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']);
onSuccess(parsed, statusCode,
messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']);
} else {
onFailure(
parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage'],
@ -248,16 +259,19 @@ class ApiClientImp implements ApiClient {
logApiEndpointError(endPoint, parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage'], statusCode);
}
} else if (parsed['MessageStatus'] == 1 || parsed['SMSLoginRequired'] == true) {
onSuccess(parsed, statusCode, messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']);
onSuccess(parsed, statusCode,
messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']);
} else if (parsed['IsAuthenticated'] == false) {
onFailure(
"User is not Authenticated",
statusCode,
failureType: UnAuthenticatedUserFailure(parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage'] ?? "User is not Authenticated", url: url),
failureType:
UnAuthenticatedUserFailure(parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage'] ?? "User is not Authenticated", url: url),
);
} else if (parsed['MessageStatus'] == 2 && parsed['IsAuthenticated']) {
if (parsed['SameClinicApptList'] != null) {
onSuccess(parsed, statusCode, messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']);
onSuccess(parsed, statusCode,
messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']);
} else {
if (parsed['message'] == null && parsed['ErrorEndUserMessage'] == null) {
if (parsed['ErrorSearchMsg'] == null) {
@ -276,7 +290,6 @@ class ApiClientImp implements ApiClient {
logApiEndpointError(endPoint, parsed['ErrorSearchMsg'], statusCode);
}
} else {
onFailure(
parsed['message'] ?? parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage'],
statusCode,
@ -287,7 +300,8 @@ class ApiClientImp implements ApiClient {
}
} else {
if (parsed['SameClinicApptList'] != null) {
onSuccess(parsed, statusCode, messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']);
onSuccess(parsed, statusCode,
messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']);
} else {
if (parsed['message'] != null) {
onFailure(

@ -1,9 +1,5 @@
import 'package:hmg_patient_app_new/core/enums.dart';
import 'package:amazon_payfort/amazon_payfort.dart';
var MAX_SMALL_SCREEN = 660;
final OPENTOK_API_KEY = '46209962';
// final OPENTOK_API_KEY = '47464241';
import 'package:hmg_patient_app_new/core/enums.dart';
// PACKAGES and OFFERS
var EXA_CART_API_BASE_URL = 'https://mdlaboratories.com/offersdiscounts';
@ -48,9 +44,9 @@ var PHARMACY_REDIRECT_URL = 'https://bit.ly/AlhabibPharmacy';
// var PHARMACY_BASE_URL = 'https://mdlaboratories.com/exacartapitest/api/';
// RC API URL
var RC_BASE_URL = 'https://rc.hmg.com/';
// var RC_BASE_URL = 'https://rc.hmg.com/';
// var RC_BASE_URL = 'https://rc.hmg.com/test/';
var RC_BASE_URL = 'https://rc.hmg.com/uat/';
// var RC_BASE_URL = 'https://ms.hmg.com/rc/';
@ -265,7 +261,6 @@ var CANCEL_APPOINTMENT = "Services/Doctors.svc/REST/CancelAppointment";
var GENERATE_QR_APPOINTMENT = "Services/Doctors.svc/REST/GenerateQRAppointmentNo";
//URL send email appointment QR
var EMAIL_QR_APPOINTMENT = "Services/Notifications.svc/REST/sendEmailForOnLineCheckin";
//URL check payment status
var CHECK_PAYMENT_STATUS = "Services/PayFort_Serv.svc/REST/GetRequestStatusByRequestID";
@ -275,14 +270,8 @@ var CREATE_ADVANCE_PAYMENT = "Services/Doctors.svc/REST/CreateAdvancePayment";
var HIS_CREATE_ADVANCE_PAYMENT = "Services/Patients.svc/REST/HIS_CreateAdvancePayment";
var ER_CREATE_ADVANCE_PAYMENT = "services/Doctors.svc/REST/ER_CreateAdvancePaymentForClinic";
var ER_INSERT_ADVANCE_PAYMENT = "services/Doctors.svc/REST/ER_InsertEROnlinePaymentDetails";
var ADD_ADVANCE_NUMBER_REQUEST = 'Services/PayFort_Serv.svc/REST/AddAdvancedNumberRequest';
var GENERATE_ANCILLARY_ORDERS_INVOICE = 'Services/Doctors.svc/REST/AutoGenerateAncillaryOrderInvoice';
var IS_ALLOW_ASK_DOCTOR = 'Services/Doctors.svc/REST/GetPatientDoctorAppointmentResult';
var GET_CALL_REQUEST_TYPE = 'Services/Doctors.svc/REST/GetCallRequestType_LOV';
var ADD_VIDA_REQUEST = 'Services/ER_VirtualCall.svc/REST/PatientER_VidaRequestInseart';
@ -308,8 +297,6 @@ var GET_LIVECARE_CLINIC_TIMING = 'Services/ER_VirtualCall.svc/REST/PatientER_Get
var GET_ER_APPOINTMENT_FEES = 'Services/DoctorApplication.svc/REST/GetERAppointmentFees';
var GET_ER_APPOINTMENT_TIME = 'Services/ER_VirtualCall.svc/REST/GetRestTime';
var CHECK_PATIENT_DERMA_PACKAGE = 'Services/OUTPs.svc/REST/getPatientPackageComponentsForOnlineCheckIn';
var ADD_NEW_CALL_FOR_PATIENT_ER = 'Services/DoctorApplication.svc/REST/NewCallForPatientER';
var GET_LIVECARE_HISTORY = 'Services/ER_VirtualCall.svc/REST/GetPatientErVirtualHistory';
@ -570,7 +557,8 @@ var GET_CUSTOMER_INFO = "VerifyCustomer";
//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_PRODUCTS = 'offerproducts/';
var GET_CATEGORISE_PARENT = 'categories?fields=id,name,namen,description,image,localized_names,display_order,parent_category_id,is_leaf&parent_id=';
@ -720,16 +708,18 @@ const SAVE_SETTING = 'Services/Patients.svc/REST/UpdatePateintInfo';
const DEACTIVATE_ACCOUNT = 'Services/Patients.svc/REST/PatientAppleActivation_InsertUpdate';
var ER_CREATE_ADVANCE_PAYMENT = "services/Doctors.svc/REST/ER_CreateAdvancePaymentForClinic";
//family Files
const FAMILY_FILES= 'Services/Authentication.svc/REST/GetAllSharedRecordsByStatus';
const FAMILY_FILES = 'Services/Authentication.svc/REST/GetAllSharedRecordsByStatus';
var GET_PRESCRIPTION_INSTRUCTIONS_PDF = 'Services/ChatBot_Service.svc/REST/Chatbot_SendMedicationInstructionByWhatsApp';
class ApiConsts {
static const maxSmallScreen = 660;
static AppEnvironmentTypeEnum appEnvironmentType = AppEnvironmentTypeEnum.prod;
static AppEnvironmentTypeEnum appEnvironmentType = AppEnvironmentTypeEnum.uat;
// static String baseUrl = 'https://uat.hmgwebservices.com/'; // HIS API URL UAT
@ -737,10 +727,6 @@ class ApiConsts {
static String RCBaseUrl = 'https://rc.hmg.com/'; // RC API URL PROD
static String SELECT_DEVICE_IMEI = 'Services/Patients.svc/REST/Patient_SELECTDeviceIMEIbyIMEI';
static num VERSION_ID = 18.9;
static var payFortEnvironment = FortEnvironment.production;
static var applePayMerchantId = "merchant.com.hmgwebservices";
@ -789,7 +775,6 @@ class ApiConsts {
RCBaseUrl = 'https://rc.hmg.com/test/';
break;
case AppEnvironmentTypeEnum.preProd:
baseUrl = "https://webservices.hmg.com/";
payFortEnvironment = FortEnvironment.production;
applePayMerchantId = "merchant.com.hmgwebservices";
@ -833,7 +818,7 @@ class ApiConsts {
static final String getUserAgreementContent = 'Services/Patients.svc/REST/GetUsageAgreementText';
static final String checkPatientForRegistration = 'Services/Authentication.svc/REST/CheckPatientForRegisteration';
static final String checkUserStatus= 'Services/NHIC.svc/REST/GetPatientInfo';
static final String checkUserStatus = 'Services/NHIC.svc/REST/GetPatientInfo';
static final String insertPatientDeviceIMEIData = 'Services/Patients.svc/REST/Patient_INSERTDeviceIMEI';
static final String insertPatientMobileData = 'Services/MobileNotifications.svc/REST/Insert_PatientMobileDeviceInfo';
@ -849,7 +834,19 @@ class ApiConsts {
static final String removeFileFromFamilyMembers = 'Services/Authentication.svc/REST/ActiveDeactive_PatientFile';
static final String acceptAndRejectFamilyFile = 'Services/Authentication.svc/REST/Update_FileStatus';
// static values for Api
// Ancillary Order Apis
static final String getOnlineAncillaryOrderList = 'Services/Doctors.svc/REST/GetOnlineAncillaryOrderList';
static final String getOnlineAncillaryOrderProcList = 'Services/Doctors.svc/REST/GetOnlineAncillaryOrderProcList';
static final String generateAncillaryOrderInvoice = 'Services/Doctors.svc/REST/AutoGenerateAncillaryOrderInvoice';
static final String autoGenerateAncillaryOrdersInvoice = 'Services/Doctors.svc/REST/AutoGenerateAncillaryOrderInvoice';
static final String getRequestStatusByRequestID = 'Services/PayFort_Serv.svc/REST/GetRequestStatusByRequestID';
//Payment APIs
static final String applePayInsertRequest = "Services/PayFort_Serv.svc/REST/PayFort_ApplePayRequestData_Insert";
static final String createAdvancePayments = 'Services/Patients.svc/REST/HIS_CreateAdvancePayment';
static final String addAdvanceNumberRequest = 'Services/PayFort_Serv.svc/REST/AddAdvancedNumberRequest';
// ************ static values for Api ****************
static final double appVersionID = 18.7;
static final int appChannelId = 3;
static final String appIpAddress = "10.20.10.20";

@ -25,6 +25,7 @@ class AppAssets {
static const String download = '$svgBasePath/download.svg';
static const String language = '$svgBasePath/language.svg';
static const String location = '$svgBasePath/location.svg';
static const String pin_location = '$svgBasePath/pin_location.svg';
static const String whatsapp = '$svgBasePath/whatsapp.svg';
static const String card_user = '$svgBasePath/card_user.svg';
static const String habiblogo = '$svgBasePath/habiblogo.svg';
@ -156,6 +157,7 @@ class AppAssets {
static const String activeCheck = '$svgBasePath/active-check.svg';
static const String deleteIcon = '$svgBasePath/delete_icon.svg';
static const String call_ambulance_icon = '$svgBasePath/call_ambulance_icon.svg';
static const String ambulance = '$svgBasePath/ambulance.svg';
static const String nearest_er_icon = '$svgBasePath/nearest_er_icon.svg';
static const String rrt_icon = '$svgBasePath/rrt_icon.svg';
static const String waiting_time_clock = '$svgBasePath/waiting_time_clock.svg';

@ -56,12 +56,12 @@ class AppState {
}
FutureOr<void> checkIfGMSAvailableOrNot() async {
isGMSAvailable = (await GmsCheck().checkGmsAvailability()) ?? true;
if(Platform.isIOS) {
isGMSAvailable = false;
isGMSAvailable = true;
isAndroid = false;
return;
}
isGMSAvailable = (await GmsCheck().checkGmsAvailability()) ?? true;
}
AuthenticatedUser? getAuthenticatedUser({bool isFamily = false}) {

@ -8,6 +8,8 @@ import 'package:hmg_patient_app_new/features/authentication/authentication_view_
import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_repo.dart';
import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_view_model.dart';
import 'package:hmg_patient_app_new/features/common/common_repo.dart';
import 'package:hmg_patient_app_new/features/contact_us/contact_us_repo.dart';
import 'package:hmg_patient_app_new/features/contact_us/contact_us_view_model.dart';
import 'package:hmg_patient_app_new/features/doctor_filter/doctor_filter_view_model.dart';
import 'package:hmg_patient_app_new/features/emergency_services/emergency_services_repo.dart';
import 'package:hmg_patient_app_new/features/emergency_services/emergency_services_view_model.dart';
@ -19,6 +21,8 @@ import 'package:hmg_patient_app_new/features/insurance/insurance_repo.dart';
import 'package:hmg_patient_app_new/features/insurance/insurance_view_model.dart';
import 'package:hmg_patient_app_new/features/lab/lab_repo.dart';
import 'package:hmg_patient_app_new/features/lab/lab_view_model.dart';
import 'package:hmg_patient_app_new/features/location/location_repo.dart';
import 'package:hmg_patient_app_new/features/location/location_view_model.dart';
import 'package:hmg_patient_app_new/features/medical_file/medical_file_repo.dart';
import 'package:hmg_patient_app_new/features/medical_file/medical_file_view_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/appointment_via_region_viewmodel.dart';
@ -31,6 +35,8 @@ import 'package:hmg_patient_app_new/features/prescriptions/prescriptions_view_mo
import 'package:hmg_patient_app_new/features/profile_settings/profile_settings_view_model.dart';
import 'package:hmg_patient_app_new/features/radiology/radiology_repo.dart';
import 'package:hmg_patient_app_new/features/radiology/radiology_view_model.dart';
import 'package:hmg_patient_app_new/features/todo_section/todo_section_repo.dart';
import 'package:hmg_patient_app_new/features/todo_section/todo_section_view_model.dart';
import 'package:hmg_patient_app_new/services/analytics/analytics_service.dart';
import 'package:hmg_patient_app_new/services/cache_service.dart';
import 'package:hmg_patient_app_new/services/dialog_service.dart';
@ -40,7 +46,6 @@ import 'package:hmg_patient_app_new/services/localauth_service.dart';
import 'package:hmg_patient_app_new/services/logger_service.dart';
import 'package:hmg_patient_app_new/services/navigation_service.dart';
import 'package:hmg_patient_app_new/widgets/date_range_selector/viewmodel/date_range_view_model.dart';
import 'package:http/http.dart';
import 'package:local_auth/local_auth.dart';
import 'package:logger/web.dart';
import 'package:shared_preferences/shared_preferences.dart';
@ -98,11 +103,16 @@ class AppDependencies {
getIt.registerLazySingleton<PrescriptionsRepo>(() => PrescriptionsRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt()));
getIt.registerLazySingleton<InsuranceRepo>(() => InsuranceRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt()));
getIt.registerLazySingleton<PayfortRepo>(() => PayfortRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt()));
getIt.registerLazySingleton<LocalAuthService>(() => LocalAuthService(loggerService: getIt<LoggerService>(), localAuth: getIt<LocalAuthentication>()));
getIt.registerLazySingleton<LocalAuthService>(
() => LocalAuthService(loggerService: getIt<LoggerService>(), localAuth: getIt<LocalAuthentication>()));
getIt.registerLazySingleton<HabibWalletRepo>(() => HabibWalletRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt()));
getIt.registerLazySingleton<MedicalFileRepo>(() => MedicalFileRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt()));
getIt.registerLazySingleton<ImmediateLiveCareRepo>(() => ImmediateLiveCareRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt()));
getIt.registerLazySingleton<EmergencyServicesRepo>(() => EmergencyServicesRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt()));
getIt.registerLazySingleton<TodoSectionRepo>(() => TodoSectionRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt()));
getIt.registerLazySingleton<LocationRepo>(
() => LocationRepoImpl(apiClient: getIt()));
getIt.registerLazySingleton<ContactUsRepo>(() => ContactUsRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt()));
// ViewModels
// Global/shared VMs LazySingleton
@ -158,7 +168,13 @@ class AppDependencies {
);
getIt.registerLazySingleton<BookAppointmentsViewModel>(
() => BookAppointmentsViewModel(bookAppointmentsRepo: getIt(), errorHandlerService: getIt(), navigationService: getIt(), myAppointmentsViewModel: getIt(), locationUtils: getIt(), dialogService: getIt()),
() => BookAppointmentsViewModel(
bookAppointmentsRepo: getIt(),
errorHandlerService: getIt(),
navigationService: getIt(),
myAppointmentsViewModel: getIt(),
locationUtils: getIt(),
dialogService: getIt()),
);
getIt.registerLazySingleton<ImmediateLiveCareViewModel>(
@ -172,7 +188,13 @@ class AppDependencies {
getIt.registerLazySingleton<AuthenticationViewModel>(
() => AuthenticationViewModel(
authenticationRepo: getIt(), cacheService: getIt(), navigationService: getIt(), dialogService: getIt(), appState: getIt(), errorHandlerService: getIt(), localAuthService: getIt()),
authenticationRepo: getIt(),
cacheService: getIt(),
navigationService: getIt(),
dialogService: getIt(),
appState: getIt(),
errorHandlerService: getIt(),
localAuthService: getIt()),
);
getIt.registerLazySingleton<ProfileSettingsViewModel>(() => ProfileSettingsViewModel());
@ -185,8 +207,7 @@ class AppDependencies {
);
getIt.registerLazySingleton<AppointmentViaRegionViewmodel>(
() =>
AppointmentViaRegionViewmodel(
() => AppointmentViaRegionViewmodel(
navigationService: getIt(),
appState: getIt(),
),
@ -199,6 +220,30 @@ class AppDependencies {
emergencyServicesRepo: getIt(),
appState: getIt(),
errorHandlerService: getIt(),
appointmentRepo: getIt(),
dialogService: getIt()
),
);
getIt.registerLazySingleton<LocationViewModel>(
() => LocationViewModel(
locationRepo: getIt(),
errorHandlerService: getIt(),
),
);
getIt.registerLazySingleton<ContactUsViewModel>(
() => ContactUsViewModel(
contactUsRepo: getIt(),
appState: getIt(),
errorHandlerService: getIt(),
),
);
getIt.registerLazySingleton<TodoSectionViewModel>(
() => TodoSectionViewModel(
todoSectionRepo: getIt(),
errorHandlerService: getIt(),
),
);

@ -6,7 +6,11 @@ class DateUtil {
/// convert String To Date function
/// [date] String we want to convert
static DateTime convertStringToDate(String? date) {
if (date != null) {
print("the date is $date");
if (date == null) return DateTime.now();
if (date.isEmpty) return DateTime.now();
const start = "/Date(";
const end = "+0300)";
final startIndex = date.indexOf(start);
@ -14,9 +18,7 @@ class DateUtil {
return DateTime.fromMillisecondsSinceEpoch(int.parse(
date.substring(startIndex + start.length, endIndex),
));
} else {
return DateTime.now();
}
}
static DateTime convertStringToDateSaudiTimezone(String date, int projectId) {

@ -1,3 +1,5 @@
import 'dart:developer';
import 'package:easy_localization/easy_localization.dart';
import 'package:hijri_gregorian_calendar/hijri_gregorian_calendar.dart';
import 'package:hmg_patient_app_new/core/api_consts.dart';
@ -96,6 +98,7 @@ class RequestUtils {
request.patientIdentificationID = request.nationalID = (registeredData.patientIdentificationId ?? 0);
request.dob = registeredData.dob;
request.isRegister = registeredData.isRegister;
log("nationIdText: ${nationIdText}");
} else {
if (fileNo) {
request.patientID = patientId ?? int.parse(nationIdText);
@ -199,7 +202,8 @@ class RequestUtils {
return request;
}
static dynamic getUserSignupCompletionRequest({String? fullName, String? emailAddress, GenderTypeEnum? gender, MaritalStatusTypeEnum? maritalStatus}) {
static dynamic getUserSignupCompletionRequest(
{String? fullName, String? emailAddress, GenderTypeEnum? gender, MaritalStatusTypeEnum? maritalStatus}) {
AppState appState = getIt.get<AppState>();
bool isDubai = appState.getUserRegistrationPayload.patientOutSa == 1 ? true : false;
@ -215,11 +219,19 @@ class RequestUtils {
return {
"Patientobject": {
"TempValue": true,
"PatientIdentificationType":
(isDubai ? appState.getUserRegistrationPayload.patientIdentificationId?.toString().substring(0, 1) : appState.getNHICUserData.idNumber!.substring(0, 1)) == "1" ? 1 : 2,
"PatientIdentificationNo": isDubai ? appState.getUserRegistrationPayload.patientIdentificationId.toString() : appState.getNHICUserData.idNumber.toString(),
"PatientIdentificationType": (isDubai
? appState.getUserRegistrationPayload.patientIdentificationId?.toString().substring(0, 1)
: appState.getNHICUserData.idNumber!.substring(0, 1)) ==
"1"
? 1
: 2,
"PatientIdentificationNo":
isDubai ? appState.getUserRegistrationPayload.patientIdentificationId.toString() : appState.getNHICUserData.idNumber.toString(),
"MobileNumber": appState.getUserRegistrationPayload.patientMobileNumber ?? 0,
"PatientOutSA": (appState.getUserRegistrationPayload.zipCode == CountryEnum.saudiArabia.countryCode || appState.getUserRegistrationPayload.zipCode == '+966') ? 0 : 1,
"PatientOutSA": (appState.getUserRegistrationPayload.zipCode == CountryEnum.saudiArabia.countryCode ||
appState.getUserRegistrationPayload.zipCode == '+966')
? 0
: 1,
"FirstNameN": isDubai ? "..." : appState.getNHICUserData.firstNameAr,
"FirstName": isDubai ? (names.isNotEmpty ? names[0] : "...") : appState.getNHICUserData.firstNameEn,
"MiddleNameN": isDubai ? "..." : appState.getNHICUserData.secondNameAr,
@ -233,7 +245,10 @@ class RequestUtils {
"eHealthIDField": isDubai ? null : appState.getNHICUserData.healthId,
"DateofBirthN": date,
"EmailAddress": emailAddress,
"SourceType": (appState.getUserRegistrationPayload.zipCode == CountryEnum.saudiArabia.countryCode || appState.getUserRegistrationPayload.zipCode == '+966') ? "1" : "2",
"SourceType": (appState.getUserRegistrationPayload.zipCode == CountryEnum.saudiArabia.countryCode ||
appState.getUserRegistrationPayload.zipCode == '+966')
? "1"
: "2",
"PreferredLanguage": appState.getLanguageCode() == "ar" ? (isDubai ? "1" : 1) : (isDubai ? "2" : 2),
"Marital": isDubai
? (maritalStatus == MaritalStatusTypeEnum.single
@ -247,20 +262,25 @@ class RequestUtils {
? '1'
: '2'),
},
"PatientIdentificationID": isDubai ? appState.getUserRegistrationPayload.patientIdentificationId.toString() : appState.getNHICUserData.idNumber.toString(),
"PatientIdentificationID":
isDubai ? appState.getUserRegistrationPayload.patientIdentificationId.toString() : appState.getNHICUserData.idNumber.toString(),
"PatientMobileNumber": appState.getUserRegistrationPayload.patientMobileNumber.toString()[0] == '0'
? appState.getUserRegistrationPayload.patientMobileNumber
: '0${appState.getUserRegistrationPayload.patientMobileNumber}',
"DOB": dob,
"IsHijri": appState.getUserRegistrationPayload.isHijri,
"PatientOutSA": (appState.getUserRegistrationPayload.zipCode == CountryEnum.saudiArabia.countryCode || appState.getUserRegistrationPayload.zipCode == '+966') ? 0 : 1,
"PatientOutSA": (appState.getUserRegistrationPayload.zipCode == CountryEnum.saudiArabia.countryCode ||
appState.getUserRegistrationPayload.zipCode == '+966')
? 0
: 1,
"isDentalAllowedBackend": appState.getUserRegistrationPayload.isDentalAllowedBackend,
"ZipCode": appState.getUserRegistrationPayload.zipCode,
if (!isDubai) "HealthId": appState.getNHICUserData.healthId,
};
}
static Future<FamilyFileRequest> getAddFamilyRequest({required String nationalIDorFile, required String mobileNo, required String countryCode}) async {
static Future<FamilyFileRequest> getAddFamilyRequest(
{required String nationalIDorFile, required String mobileNo, required String countryCode}) async {
FamilyFileRequest request = FamilyFileRequest();
int? loginType = 0;

@ -216,6 +216,7 @@ class SizeUtils {
log("longerSide: $longerSide");
log("shorterSide: $shorterSide");
log("isTablet: $isTablet");
log("isFoldable: $isFoldable");
}
}

@ -18,6 +18,8 @@ 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/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/doctor_list_api_response.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/hospital_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/services/navigation_service.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
@ -100,6 +102,12 @@ class Utils {
? getMonthArabic(dateTime.month) + " " + dateTime.day.toString() + ", " + dateTime.year.toString()
: getMonth(dateTime.month) + " " + dateTime.day.toString() + ", " + dateTime.year.toString();
}
static String getDayMonthYearDateFormatted(DateTime? dateTime) {
if(dateTime == null ) return "";
return appState.isArabic()
? "${dateTime.day.toString()} ${getMonthArabic(dateTime.month)}, ${dateTime.year.toString()}"
: "${dateTime.day.toString()} ${getMonth(dateTime.month)}, ${dateTime.year.toString()}";
}
/// get month by
/// [month] convert month number in to month name
@ -315,7 +323,8 @@ class Utils {
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SizedBox(height: isSmallWidget ? 0.h : 48.h),
Lottie.asset(AppAnimations.noData, repeat: false, reverse: false, frameRate: FrameRate(60), width: width.h, height: height.h, fit: BoxFit.fill),
Lottie.asset(AppAnimations.noData,
repeat: false, reverse: false, frameRate: FrameRate(60), width: width.h, height: height.h, fit: BoxFit.fill),
SizedBox(height: 16.h),
(noDataText ?? LocaleKeys.noDataAvailable.tr())
.toText16(weight: FontWeight.w500, color: AppColors.greyTextColor, isCenter: true)
@ -331,7 +340,8 @@ class Utils {
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Lottie.asset(AppAnimations.loadingAnimation, repeat: true, reverse: false, frameRate: FrameRate(60), width: 100.h, height: 100.h, fit: BoxFit.fill),
Lottie.asset(AppAnimations.loadingAnimation,
repeat: true, reverse: false, frameRate: FrameRate(60), width: 100.h, height: 100.h, fit: BoxFit.fill),
SizedBox(height: 8.h),
(loadingText ?? LocaleKeys.loadingText.tr()).toText16(color: AppColors.blackColor, isCenter: true),
SizedBox(height: 8.h),
@ -357,7 +367,8 @@ class Utils {
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
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),
(loadingText ?? LocaleKeys.loadingText.tr()).toText16(color: AppColors.blackColor),
SizedBox(height: 8.h),
@ -365,12 +376,14 @@ class Utils {
).center;
}
static Widget getWarningWidget({String? loadingText, bool isShowActionButtons = false, Widget? bodyWidget, Function? onConfirmTap, Function? onCancelTap}) {
static Widget getWarningWidget(
{String? loadingText, bool isShowActionButtons = false, Widget? bodyWidget, Function? onConfirmTap, Function? onCancelTap}) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Lottie.asset(AppAnimations.warningAnimation, repeat: false, reverse: false, frameRate: FrameRate(60), width: 128.h, height: 128.h, fit: BoxFit.fill),
Lottie.asset(AppAnimations.warningAnimation,
repeat: false, reverse: false, frameRate: FrameRate(60), width: 128.h, height: 128.h, fit: BoxFit.fill),
SizedBox(height: 8.h),
(loadingText ?? LocaleKeys.loadingText.tr()).toText14(color: AppColors.blackColor, letterSpacing: 0),
SizedBox(height: 16.h),
@ -690,7 +703,7 @@ class Utils {
fit: fit,
errorBuilder: errorBuilder ??
(_, __, ___) {
//todo change the error builder icon that it is returning
//todo_section change the error builder icon that it is returning
return Utils.buildSvgWithAssets(width: iconW, height: iconH, icon: AppAssets.no_visit_icon);
},
);
@ -742,10 +755,14 @@ class Utils {
static Widget getPaymentAmountWithSymbol2(num habibWalletAmount,
{double iconSize = 14,
Color iconColor = AppColors.textColor,
double? fontSize,
double? letterSpacing,
FontWeight? fontWeight,
Color iconColor = AppColors.textColor,
Color textColor = AppColors.blackColor,
bool isSaudiCurrency = true,
bool isExpanded = true}) {
bool isExpanded = true,
}) {
return RichText(
maxLines: 1,
text: TextSpan(
@ -756,8 +773,13 @@ class Utils {
child: Utils.buildSvgWithAssets(icon: AppAssets.saudi_riyal_icon, width: iconSize.h, height: iconSize.h, iconColor: iconColor),
),
TextSpan(
text: " $habibWalletAmount",
style: TextStyle(color: textColor, fontSize: 32.f, letterSpacing: -4, fontWeight: FontWeight.w600, height: 1),
text: NumberFormat.currency(locale: 'en_US', symbol: " ", decimalDigits: 0).format(habibWalletAmount),
style: TextStyle(
color: textColor,
fontSize: fontSize ?? 32.f,
letterSpacing: letterSpacing??-4,
fontWeight: fontWeight ?? FontWeight.w600,
height: 1),
),
],
),
@ -799,11 +821,27 @@ class Utils {
static Future<String> createFileFromString(String encodedStr, String ext) async {
Uint8List bytes = base64.decode(encodedStr);
String dir = (await getApplicationDocumentsDirectory()).path;
File file = File("$dir/" + DateTime.now().millisecondsSinceEpoch.toString() + "." + ext);
File file = File("$dir/${DateTime.now().millisecondsSinceEpoch}.$ext");
await file.writeAsBytes(bytes);
return file.path;
}
///method to be used to get the text as per the langauge of the application
static String getTextWRTCurrentLanguage(String? englishText, String? arabicText) {
String? text = appState.isArabic() ? arabicText : englishText;
return text ?? '';
}
static String formatNumberToInternationalFormat(num number, {String? currencySymbol, int decimalDigit = 0}) {
return NumberFormat.currency(locale: 'en_US', symbol: currencySymbol ?? "", decimalDigits: decimalDigit).format(number);
}
static PatientDoctorAppointmentList? convertToPatientDoctorAppointmentList(HospitalsModel? hospital) {
if (hospital == null) return null;
return PatientDoctorAppointmentList(
filterName: hospital.name, distanceInKMs: hospital.distanceInKilometers?.toString(), projectTopName: hospital.name, projectBottomName: hospital.name, model: hospital, isHMC: hospital.isHMC);
}
static bool havePrivilege(int id) {
bool isHavePrivilege = false;
try {
@ -815,4 +853,7 @@ class Utils {
}
return isHavePrivilege;
}
}

@ -279,11 +279,11 @@ extension EmailValidator on String {
height: 1, color: color ?? AppColors.blackColor, fontSize: 22.f, letterSpacing: -1, fontWeight: isBold ? FontWeight.bold : FontWeight.normal),
);
Widget toText24({Color? color, bool isBold = false, bool isCenter = false, FontWeight? fontWeight}) => Text(
Widget toText24({Color? color, bool isBold = false, bool isCenter = false, FontWeight? fontWeight, double? letterSpacing}) => Text(
this,
textAlign: isCenter ? TextAlign.center : null,
style: TextStyle(
height: 23 / 24, color: color ?? AppColors.blackColor, fontSize: 24.f, letterSpacing: -1, fontWeight: isBold ? FontWeight.bold : FontWeight.normal),
height: 23 / 24, color: color ?? AppColors.blackColor, fontSize: 24.f, letterSpacing: letterSpacing??-1, fontWeight: isBold ? FontWeight.bold : fontWeight??FontWeight.normal),
);
Widget toText26({Color? color, bool isBold = false, double? height, bool isCenter = false, FontWeight? weight, double? letterSpacing}) => Text(

@ -245,6 +245,7 @@ class AuthenticationViewModel extends ChangeNotifier {
}
Future<void> _handleNewImeiRegistration() async {
await selectDeviceImei(onSuccess: (dynamic respData) async {
try {
if (respData != null) {
@ -338,7 +339,7 @@ class AuthenticationViewModel extends ChangeNotifier {
_navigationService.pop();
});
},
activationCode: null, //todo silent login case halded on the repo itself..
activationCode: null, //todo_section silent login case halded on the repo itself..
);
}
}

@ -0,0 +1,95 @@
import 'package:dartz/dartz.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/common_models/generic_api_model.dart';
import 'package:hmg_patient_app_new/core/exceptions/api_failure.dart';
import 'package:hmg_patient_app_new/features/contact_us/models/resp_models/get_hmg_locations.dart';
import 'package:hmg_patient_app_new/features/contact_us/models/resp_models/get_patient_ic_projects.dart';
import 'package:hmg_patient_app_new/services/logger_service.dart';
abstract class ContactUsRepo {
Future<Either<Failure, GenericApiModel<List<GetHMGLocationsModel>>>> getHMGLocations();
Future<Either<Failure, GenericApiModel<List<GetPatientICProjectsModel>>>> getLiveChatProjectsList();
}
class ContactUsRepoImp implements ContactUsRepo {
final ApiClient apiClient;
final LoggerService loggerService;
ContactUsRepoImp({required this.apiClient, required this.loggerService});
@override
Future<Either<Failure, GenericApiModel<List<GetHMGLocationsModel>>>> getHMGLocations() async {
Map<String, dynamic> mapDevice = {};
try {
GenericApiModel<List<GetHMGLocationsModel>>? apiResponse;
Failure? failure;
await apiClient.post(
GET_FINDUS_REQUEST,
body: mapDevice,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
final list = response['ListHMGLocation'];
final hmgLocations = list.map((item) => GetHMGLocationsModel.fromJson(item as Map<String, dynamic>)).toList().cast<GetHMGLocationsModel>();
apiResponse = GenericApiModel<List<GetHMGLocationsModel>>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: null,
data: hmgLocations,
);
} catch (e) {
failure = DataParsingFailure(e.toString());
}
},
);
if (failure != null) return Left(failure!);
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
return Right(apiResponse!);
} catch (e) {
return Left(UnknownFailure(e.toString()));
}
}
@override
Future<Either<Failure, GenericApiModel<List<GetPatientICProjectsModel>>>> getLiveChatProjectsList() async {
Map<String, dynamic> mapDevice = {};
try {
GenericApiModel<List<GetPatientICProjectsModel>>? apiResponse;
Failure? failure;
await apiClient.post(
GET_LIVECHAT_REQUEST,
body: mapDevice,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
final list = response['List_PatientICProjects'];
final hmgLocations = list.map((item) => GetPatientICProjectsModel.fromJson(item as Map<String, dynamic>)).toList().cast<GetPatientICProjectsModel>();
apiResponse = GenericApiModel<List<GetPatientICProjectsModel>>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: null,
data: hmgLocations,
);
} catch (e) {
failure = DataParsingFailure(e.toString());
}
},
);
if (failure != null) return Left(failure!);
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
return Right(apiResponse!);
} catch (e) {
return Left(UnknownFailure(e.toString()));
}
}
}

@ -0,0 +1,65 @@
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/features/contact_us/contact_us_repo.dart';
import 'package:hmg_patient_app_new/features/contact_us/models/resp_models/get_hmg_locations.dart';
import 'package:hmg_patient_app_new/services/error_handler_service.dart';
class ContactUsViewModel extends ChangeNotifier {
ContactUsRepo contactUsRepo;
ErrorHandlerService errorHandlerService;
AppState appState;
bool isHMGLocationsListLoading = false;
bool isHMGHospitalsListSelected = true;
List<GetHMGLocationsModel> hmgHospitalsLocationsList = [];
List<GetHMGLocationsModel> hmgPharmacyLocationsList = [];
ContactUsViewModel({required this.contactUsRepo, required this.errorHandlerService, required this.appState});
initContactUsViewModel() {
isHMGLocationsListLoading = true;
isHMGHospitalsListSelected = true;
hmgHospitalsLocationsList.clear();
hmgPharmacyLocationsList.clear();
getHMGLocations();
notifyListeners();
}
setHMGHospitalsListSelected(bool isSelected) {
isHMGHospitalsListSelected = isSelected;
notifyListeners();
}
Future<void> getHMGLocations({Function(dynamic)? onSuccess, Function(String)? onError}) async {
isHMGLocationsListLoading = true;
hmgHospitalsLocationsList.clear();
hmgPharmacyLocationsList.clear();
notifyListeners();
final result = await contactUsRepo.getHMGLocations();
result.fold(
(failure) async => await errorHandlerService.handleError(failure: failure),
(apiResponse) {
if (apiResponse.messageStatus == 2) {
// dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
} else if (apiResponse.messageStatus == 1) {
// hmgLocationsList = apiResponse.data!;
for (var location in apiResponse.data!) {
if (location.locationType == 1) {
hmgHospitalsLocationsList.add(location);
} else if (location.locationType == 2) {
hmgPharmacyLocationsList.add(location);
}
}
isHMGLocationsListLoading = false;
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse);
}
}
},
);
}
}

@ -0,0 +1,80 @@
class GetHMGLocationsModel {
dynamic cityID;
String? cityName;
dynamic cityNameN;
dynamic distanceInKilometers;
bool? isActive;
String? latitude;
int? locationID;
String? locationName;
dynamic locationNameN;
dynamic locationType;
String? longitude;
int? pharmacyLocationID;
String? phoneNumber;
int? projectID;
String? projectImageURL;
int? setupID;
dynamic sortOrder;
GetHMGLocationsModel(
{this.cityID,
this.cityName,
this.cityNameN,
this.distanceInKilometers,
this.isActive,
this.latitude,
this.locationID,
this.locationName,
this.locationNameN,
this.locationType,
this.longitude,
this.pharmacyLocationID,
this.phoneNumber,
this.projectID,
this.projectImageURL,
this.setupID,
this.sortOrder});
GetHMGLocationsModel.fromJson(Map<String, dynamic> json) {
cityID = json['CityID'];
cityName = json['CityName'];
cityNameN = json['CityNameN'];
distanceInKilometers = json['DistanceInKilometers'];
isActive = json['IsActive'];
latitude = json['Latitude'];
locationID = json['LocationID'];
locationName = json['LocationName'];
locationNameN = json['LocationNameN'];
locationType = json['LocationType'];
longitude = json['Longitude'];
pharmacyLocationID = json['PharmacyLocationID'];
phoneNumber = json['PhoneNumber'];
projectID = json['ProjectID'];
projectImageURL = json['ProjectImageURL'];
setupID = json['SetupID'];
sortOrder = json['SortOrder'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['CityID'] = this.cityID;
data['CityName'] = this.cityName;
data['CityNameN'] = this.cityNameN;
data['DistanceInKilometers'] = this.distanceInKilometers;
data['IsActive'] = this.isActive;
data['Latitude'] = this.latitude;
data['LocationID'] = this.locationID;
data['LocationName'] = this.locationName;
data['LocationNameN'] = this.locationNameN;
data['LocationType'] = this.locationType;
data['Longitude'] = this.longitude;
data['PharmacyLocationID'] = this.pharmacyLocationID;
data['PhoneNumber'] = this.phoneNumber;
data['ProjectID'] = this.projectID;
data['ProjectImageURL'] = this.projectImageURL;
data['SetupID'] = this.setupID;
data['SortOrder'] = this.sortOrder;
return data;
}
}

@ -0,0 +1,48 @@
import 'package:hmg_patient_app_new/core/utils/date_util.dart';
class GetPatientICProjectsModel {
int? id;
String? projectName;
String? projectNameN;
String? value;
dynamic languageId;
DateTime? createdOn;
String? createdBy;
dynamic editedOn;
dynamic editedBy;
bool? isActive;
dynamic distanceInKilometers;
GetPatientICProjectsModel(
{this.id, this.projectName, this.projectNameN, this.value, this.languageId, this.createdOn, this.createdBy, this.editedOn, this.editedBy, this.distanceInKilometers, this.isActive});
GetPatientICProjectsModel.fromJson(Map<String, dynamic> json) {
id = json['id'];
projectName = json['ProjectName'];
projectNameN = json['ProjectNameN'];
value = json['Value'];
languageId = json['LanguageId'];
createdOn = DateUtil.convertStringToDate(json['CreatedOn']);
createdBy = json['CreatedBy'];
editedOn = json['EditedOn'];
editedBy = json['EditedBy'];
isActive = json['IsActive'];
distanceInKilometers = json['DistanceInKilometers'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['id'] = this.id;
data['ProjectName'] = this.projectName;
data['ProjectNameN'] = this.projectNameN;
data['Value'] = this.value;
data['LanguageId'] = this.languageId;
data['CreatedOn'] = this.createdOn;
data['CreatedBy'] = this.createdBy;
data['EditedOn'] = this.editedOn;
data['EditedBy'] = this.editedBy;
data['IsActive'] = this.isActive;
data['DistanceInKilometers'] = this.distanceInKilometers;
return data;
}
}

@ -1,16 +1,56 @@
import 'dart:developer';
import 'package:dartz/dartz.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/common_models/generic_api_model.dart';
import 'package:hmg_patient_app_new/core/exceptions/api_failure.dart';
import 'package:hmg_patient_app_new/features/emergency_services/model/resp_model/ProjectAvgERWaitingTime.dart';
import 'package:hmg_patient_app_new/features/emergency_services/models/resp_models/rrt_procedures_response_model.dart';
import 'package:hmg_patient_app_new/core/utils/date_util.dart';
import 'package:hmg_patient_app_new/features/authentication/models/resp_models/authenticated_user_resp_model.dart';
import 'package:hmg_patient_app_new/features/emergency_services/models/request_model/PatientER_RC.dart';
import 'package:hmg_patient_app_new/features/emergency_services/models/resp_model/AmbulanceRequestOrdersModel.dart';
import 'package:hmg_patient_app_new/features/emergency_services/models/resp_model/EROnlineCheckInPaymentDetailsResponse.dart';
import 'package:hmg_patient_app_new/features/emergency_services/models/resp_model/ProjectAvgERWaitingTime.dart';
import 'package:hmg_patient_app_new/features/emergency_services/models/resp_model/rrt_procedures_response_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/hospital_model.dart';
import 'package:hmg_patient_app_new/services/logger_service.dart';
import 'models/resp_model/PatientERTransportationMethod.dart';
abstract class EmergencyServicesRepo {
Future<Either<Failure, GenericApiModel<List<RRTProceduresResponseModel>>>> getRRTProcedures();
Future<Either<Failure, GenericApiModel<List<ProjectAvgERWaitingTime>>>> getNearestEr({int? id, int? projectID});
Future<Either<Failure, GenericApiModel<dynamic>>> checkPatientERAdvanceBalance();
Future<Either<Failure, GenericApiModel<List<HospitalsModel>>>> getProjectList();
Future<Either<Failure, GenericApiModel<EROnlineCheckInPaymentDetailsResponse>>> checkPatientERPaymentInformation({int projectID});
Future<Either<Failure, GenericApiModel<dynamic>>> createAdvancePaymentForER(
{required int projectID,
required AuthenticatedUser authUser,
required num paymentAmount,
required String paymentMethodName,
required String paymentReference});
Future<Either<Failure, GenericApiModel<dynamic>>> addAdvanceNumberRequest(
{required String advanceNumber, required String paymentReference, required String appointmentNo});
Future<Either<Failure, GenericApiModel<dynamic>>> getProjectIDFromNFC({required String nfcCode});
Future<Either<Failure, GenericApiModel<dynamic>>> autoGenerateInvoiceERClinic({required int projectID});
Future<Either<Failure, GenericApiModel<List<PatientERTransportationMethod>>>> getTransportationMethods({int? id});
Future<Either<Failure, GenericApiModel<bool>>> submitAmbulanceRequest(PatientER_RC request);
Future<Either<Failure, GenericApiModel<List<AmbulanceRequestOrdersModel>>>> getTransportationOrders({int? id});
Future<Either<Failure, GenericApiModel<bool>>> cancelOrder(int? iD, int patientId);
}
class EmergencyServicesRepoImp implements EmergencyServicesRepo {
@ -35,7 +75,8 @@ class EmergencyServicesRepoImp implements EmergencyServicesRepo {
try {
final list = response['List_ProjectAvgERWaitingTime'];
final clinicsList = list.map((item) => ProjectAvgERWaitingTime.fromJson(item as Map<String, dynamic>)).toList().cast<ProjectAvgERWaitingTime>();
final clinicsList =
list.map((item) => ProjectAvgERWaitingTime.fromJson(item as Map<String, dynamic>)).toList().cast<ProjectAvgERWaitingTime>();
apiResponse = GenericApiModel<List<ProjectAvgERWaitingTime>>(
messageStatus: messageStatus,
statusCode: statusCode,
@ -57,7 +98,7 @@ class EmergencyServicesRepoImp implements EmergencyServicesRepo {
@override
Future<Either<Failure, GenericApiModel<List<RRTProceduresResponseModel>>>> getRRTProcedures() async {
Map<String, dynamic> mapDevice = {};
Map<String, dynamic> mapDevice = {"ProjectID": 15};
try {
GenericApiModel<List<RRTProceduresResponseModel>>? apiResponse;
@ -71,7 +112,8 @@ class EmergencyServicesRepoImp implements EmergencyServicesRepo {
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
final list = response['Vida_ProcedureList'];
final proceduresList = list.map((item) => RRTProceduresResponseModel.fromJson(item as Map<String, dynamic>)).toList().cast<RRTProceduresResponseModel>();
final proceduresList =
list.map((item) => RRTProceduresResponseModel.fromJson(item as Map<String, dynamic>)).toList().cast<RRTProceduresResponseModel>();
apiResponse = GenericApiModel<List<RRTProceduresResponseModel>>(
messageStatus: messageStatus,
@ -91,4 +133,430 @@ class EmergencyServicesRepoImp implements EmergencyServicesRepo {
return Left(UnknownFailure(e.toString()));
}
}
@override
Future<Either<Failure, GenericApiModel<List<PatientERTransportationMethod>>>> getTransportationMethods({int? id}) async {
Map<String, dynamic> mapDevice = {};
try {
GenericApiModel<List<PatientERTransportationMethod>>? apiResponse;
Failure? failure;
await apiClient.get(
"$GET_ALL_TRANSPORTATIONS_RC?patientID=$id",
isRCService: true,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
final list = response['response']['transportationservices'];
final proceduresList = list.map((item) => PatientERTransportationMethod.fromJson(item as Map<String, dynamic>)).toList().cast<PatientERTransportationMethod>();
apiResponse = GenericApiModel<List<PatientERTransportationMethod>>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: null,
data: proceduresList,
);
} catch (e) {
failure = DataParsingFailure(e.toString());
}
},
);
if (failure != null) return Left(failure!);
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
return Right(apiResponse!);
} catch (e) {
return Left(UnknownFailure(e.toString()));
}
}
@override
Future<Either<Failure, GenericApiModel<List<HospitalsModel>>>> getProjectList() async {
Map<String, dynamic> request = {};
try {
GenericApiModel<List<HospitalsModel>>? apiResponse;
Failure? failure;
await apiClient.post(
GET_PROJECT_LIST,
body: request,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
final list = response['ListProject'];
final appointmentsList = list.map((item) => HospitalsModel.fromJson(item as Map<String, dynamic>)).toList().cast<HospitalsModel>();
apiResponse = GenericApiModel<List<HospitalsModel>>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: null,
data: appointmentsList,
);
} catch (e) {
failure = DataParsingFailure(e.toString());
}
},
);
if (failure != null) return Left(failure!);
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
return Right(apiResponse!);
} catch (e) {
return Left(UnknownFailure(e.toString()));
}
}
@override
Future<Either<Failure, GenericApiModel<bool>>> submitAmbulanceRequest(PatientER_RC request) async {
try {
GenericApiModel<bool>? apiResponse;
Failure? failure;
await apiClient.post(
body: request.toJson(),
INSERT_TRANSPORTATION_ORDER_RC,
isRCService: true,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
apiResponse = GenericApiModel<bool>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: null,
data: true,
);
} catch (e) {
failure = DataParsingFailure(e.toString());
}
},
);
if (failure != null) return Left(failure!);
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
return Right(apiResponse!);
} catch (e) {
return Left(UnknownFailure(e.toString()));
}
}
@override
Future<Either<Failure, GenericApiModel<List<AmbulanceRequestOrdersModel>>>> getTransportationOrders({int? id}) async {
try {
GenericApiModel<List<AmbulanceRequestOrdersModel>>? apiResponse;
Failure? failure;
await apiClient.post(
body: {},
"$GET_ALL_TRANSPORTATIONS_ORDERS?patientID=$id",
isRCService: true,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
final list = response['response'];
final proceduresList = list.map((item) => AmbulanceRequestOrdersModel.fromJson(item as Map<String, dynamic>)).toList().cast<AmbulanceRequestOrdersModel>();
apiResponse = GenericApiModel<List<AmbulanceRequestOrdersModel>>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: null,
data: proceduresList,
);
} catch (e) {
failure = DataParsingFailure(e.toString());
}
},
);
if (failure != null) return Left(failure!);
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
return Right(apiResponse!);
} catch (e) {
return Left(UnknownFailure(e.toString()));
}
}
@override
Future<Either<Failure, GenericApiModel<bool>>> cancelOrder(int? iD, int patientID) async {
try {
GenericApiModel<bool>? apiResponse;
Map<String, dynamic> request = {};
request['Id'] = iD;
request['StatusId'] = 6;
request['ClickButton'] = 14;
request['PatientID'] = patientID;
Failure? failure;
await apiClient.post(
body: request,
CANCEL_AMBULANCE_REQUEST,
isRCService: true,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
apiResponse = GenericApiModel<bool>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: null,
data: true,
);
} catch (e) {
failure = DataParsingFailure(e.toString());
}
},
);
if (failure != null) return Left(failure!);
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
return Right(apiResponse!);
} catch (e) {
return Left(UnknownFailure(e.toString()));
}
}
@override
Future<Either<Failure, GenericApiModel>> checkPatientERAdvanceBalance() async {
Map<String, dynamic> mapDevice = {"ClinicID": 10};
try {
GenericApiModel<dynamic>? apiResponse;
Failure? failure;
await apiClient.post(
CHECK_PATIENT_ER_ADVANCE_BALANCE,
body: mapDevice,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
final bool patientHasERBalance = response['BalanceAmount'] > 0;
print(patientHasERBalance);
apiResponse = GenericApiModel<dynamic>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: null,
data: patientHasERBalance,
);
} catch (e) {
failure = DataParsingFailure(e.toString());
}
},
);
if (failure != null) return Left(failure!);
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
return Right(apiResponse!);
} catch (e) {
return Left(UnknownFailure(e.toString()));
}
}
@override
Future<Either<Failure, GenericApiModel<EROnlineCheckInPaymentDetailsResponse>>> checkPatientERPaymentInformation({int? projectID}) async {
Map<String, dynamic> mapDevice = {"ClinicID": 10, "ProjectID": projectID ?? 0};
try {
GenericApiModel<EROnlineCheckInPaymentDetailsResponse>? apiResponse;
Failure? failure;
await apiClient.post(
GET_ER_ONLINE_PAYMENT_DETAILS,
body: mapDevice,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
final erOnlineCheckInPaymentDetailsResponse = EROnlineCheckInPaymentDetailsResponse.fromJson(response["ResponsePatientShare"]);
apiResponse = GenericApiModel<EROnlineCheckInPaymentDetailsResponse>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: null,
data: erOnlineCheckInPaymentDetailsResponse,
);
} catch (e) {
failure = DataParsingFailure(e.toString());
}
},
);
if (failure != null) return Left(failure!);
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
return Right(apiResponse!);
} catch (e) {
return Left(UnknownFailure(e.toString()));
}
}
@override
Future<Either<Failure, GenericApiModel>> createAdvancePaymentForER(
{required int projectID,
required AuthenticatedUser authUser,
required num paymentAmount,
required String paymentMethodName,
required String paymentReference}) async {
Map<String, dynamic> mapDevice = {
"LanguageID": 1,
"ERAdvanceAmount": {
"ProjectId": projectID,
"PatientId": authUser.patientId,
"ClinicId": 10,
"DepositorName": "${authUser.firstName!} ${authUser.lastName!}",
"MemberId": authUser.patientId,
"NationalityID": authUser.nationalityId,
"PaymentAmount": paymentAmount,
"PaymentDate": DateUtil.convertDateToString(DateTime.now()),
"PaymentMethodName": paymentMethodName,
"PaymentReferenceNumber": paymentReference,
"SourceType": 2
}
};
try {
GenericApiModel<dynamic>? apiResponse;
Failure? failure;
await apiClient.post(
ER_CREATE_ADVANCE_PAYMENT,
body: mapDevice,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
final vidaAdvanceNumber = response['ER_AdvancePaymentResponse']['AdvanceNumber'].toString();
log(vidaAdvanceNumber);
apiResponse = GenericApiModel<dynamic>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: null,
data: vidaAdvanceNumber,
);
} catch (e) {
failure = DataParsingFailure(e.toString());
}
},
);
if (failure != null) return Left(failure!);
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
return Right(apiResponse!);
} catch (e) {
return Left(UnknownFailure(e.toString()));
}
}
@override
Future<Either<Failure, GenericApiModel>> addAdvanceNumberRequest(
{required String advanceNumber, required String paymentReference, required String appointmentNo}) async {
Map<String, dynamic> requestBody = {
"AdvanceNumber": advanceNumber,
"AdvanceNumber_VP": advanceNumber,
"PaymentReferenceNumber": paymentReference,
"AppointmentID": appointmentNo,
};
try {
GenericApiModel<dynamic>? apiResponse;
Failure? failure;
await apiClient.post(
ADD_ADVANCE_NUMBER_REQUEST,
body: requestBody,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
apiResponse = GenericApiModel<dynamic>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: null,
data: response,
);
} catch (e) {
failure = DataParsingFailure(e.toString());
}
},
);
if (failure != null) return Left(failure!);
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
return Right(apiResponse!);
} catch (e) {
return Left(UnknownFailure(e.toString()));
}
}
@override
Future<Either<Failure, GenericApiModel<dynamic>>> getProjectIDFromNFC({required String nfcCode}) async {
Map<String, dynamic> mapDevice = {"nFC_Code": nfcCode};
try {
GenericApiModel<dynamic>? apiResponse;
Failure? failure;
await apiClient.post(
GET_PROJECT_FROM_NFC,
body: mapDevice,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
final projectID = response['GetProjectByNFC'][0]["ProjectID"];
apiResponse = GenericApiModel<dynamic>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: null,
data: projectID,
);
} catch (e) {
failure = DataParsingFailure(e.toString());
}
},
);
if (failure != null) return Left(failure!);
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
return Right(apiResponse!);
} catch (e) {
return Left(UnknownFailure(e.toString()));
}
}
@override
Future<Either<Failure, GenericApiModel<dynamic>>> autoGenerateInvoiceERClinic({required int projectID}) async {
Map<String, dynamic> mapDevice = {
"ProjectID": projectID,
"ClinicID": "10",
"IsAdvanceAvailable": true,
"MemberID": 102,
"PaymentMethod": "VISA",
};
try {
GenericApiModel<dynamic>? apiResponse;
Failure? failure;
await apiClient.post(
AUTO_GENERATE_INVOICE_ER,
body: mapDevice,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
apiResponse = GenericApiModel<dynamic>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: null,
data: true,
);
} catch (e) {
failure = DataParsingFailure(e.toString());
}
},
);
if (failure != null) return Left(failure!);
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
return Right(apiResponse!);
} catch (e) {
return Left(UnknownFailure(e.toString()));
}
}
}

@ -4,34 +4,99 @@ import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart' as GMSMapServices;
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/location_util.dart';
import 'package:hmg_patient_app_new/core/utils/date_util.dart';
import 'package:hmg_patient_app_new/core/utils/doctor_response_mapper.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/features/authentication/authentication_view_model.dart';
import 'package:hmg_patient_app_new/features/authentication/models/resp_models/authenticated_user_resp_model.dart';
import 'package:hmg_patient_app_new/features/emergency_services/emergency_services_repo.dart';
import 'package:hmg_patient_app_new/features/emergency_services/model/resp_model/ProjectAvgERWaitingTime.dart';
import 'package:hmg_patient_app_new/features/emergency_services/models/resp_models/rrt_procedures_response_model.dart';
import 'package:hmg_patient_app_new/features/emergency_services/models/resp_model/EROnlineCheckInPaymentDetailsResponse.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/facility_selection.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/hospital_model.dart';
import 'package:hmg_patient_app_new/features/emergency_services/models/AmbulanceCallingPlace.dart';
import 'package:hmg_patient_app_new/features/emergency_services/models/request_model/PatientER_RC.dart';
import 'package:hmg_patient_app_new/features/emergency_services/models/resp_model/AmbulanceRequestOrdersModel.dart';
import 'package:hmg_patient_app_new/features/emergency_services/models/resp_model/PatientERTransportationMethod.dart'
show PatientERTransportationMethod;
import 'package:hmg_patient_app_new/features/emergency_services/models/resp_model/ProjectAvgERWaitingTime.dart';
import 'package:hmg_patient_app_new/features/emergency_services/models/resp_model/rrt_procedures_response_model.dart';
import 'package:hmg_patient_app_new/features/location/PlaceDetails.dart' show PlaceDetails;
import 'package:hmg_patient_app_new/features/location/PlacePrediction.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/facility_selection.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/doctor_list_api_response.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/hospital_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/patient_appointment_history_response_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_repo.dart';
import 'package:hmg_patient_app_new/presentation/authentication/login.dart';
import 'package:hmg_patient_app_new/presentation/emergency_services/call_ambulance/call_ambulance_page.dart';
import 'package:hmg_patient_app_new/presentation/emergency_services/er_online_checkin/er_online_checkin_home.dart';
import 'package:hmg_patient_app_new/presentation/emergency_services/er_online_checkin/er_online_checkin_payment_details_page.dart';
import 'package:hmg_patient_app_new/presentation/emergency_services/call_ambulance/requesting_services_page.dart';
import 'package:hmg_patient_app_new/presentation/emergency_services/call_ambulance/tracking_screen.dart';
import 'package:hmg_patient_app_new/presentation/emergency_services/nearest_er_page.dart';
import 'package:hmg_patient_app_new/routes/app_routes.dart' show AppRoutes;
import 'package:hmg_patient_app_new/services/dialog_service.dart';
import 'package:hmg_patient_app_new/services/error_handler_service.dart';
import 'package:hmg_patient_app_new/services/navigation_service.dart';
import 'package:hmg_patient_app_new/widgets/expandable_bottom_sheet/model/BottomSheetType.dart';
import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart';
import 'package:hmg_patient_app_new/widgets/order_tracking/order_tracking_state.dart';
import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
import 'package:huawei_map/huawei_map.dart' as HMSCameraServices;
import 'package:url_launcher/url_launcher.dart';
import '../location/GeocodeResponse.dart';
import 'models/ambulance_direction.dart';
class EmergencyServicesViewModel extends ChangeNotifier {
EmergencyServicesRepo emergencyServicesRepo;
MyAppointmentsRepo appointmentRepo;
ErrorHandlerService errorHandlerService;
DialogService dialogService;
Completer<GMSMapServices.GoogleMapController>? gmsController;
Completer<HMSCameraServices.HuaweiMapController>? hmsController;
final NavigationService navServices;
final LocationUtils? locationUtils;
final AppState appState;
TextEditingController searchController = TextEditingController();
//loading variables
bool isERListLoading = false;
bool isTransportationOptionsLoading = false;
List<ProjectAvgERWaitingTime> nearestERList = [];
List<ProjectAvgERWaitingTime> nearestERFilteredList = [];
List<RRTProceduresResponseModel> RRTProceduresList = [];
List<HospitalsModel>? hospitalList;
List<HospitalsModel>? hmgHospitalList;
List<HospitalsModel>? hmcHospitalList;
List<HospitalsModel>? displayList;
HospitalsModel? selectedHospital;
FacilitySelection selectedFacility = FacilitySelection.ALL;
int hmgCount = 0;
int hmcCount = 0;
bool pickupFromInsideTheLocation = true;
List<PatientAppointmentHistoryResponseModel>? appointments;
List<AmbulanceRequestOrdersModel>? orders = [];
//ambulance selection data section
List<PatientERTransportationMethod> transportationOptions = [];
PatientERTransportationMethod? selectedTransportOption;
AmbulanceCallingPlace callingPlace = AmbulanceCallingPlace.FROM_HOSPITAL;
AmbulanceDirection ambulanceDirection = AmbulanceDirection.ONE_WAY;
late RRTProceduresResponseModel selectedRRTProcedure;
bool patientHasAdvanceERBalance = false;
bool isERBookAppointment = false;
late EROnlineCheckInPaymentDetailsResponse erOnlineCheckInPaymentDetailsResponse;
BottomSheetType bottomSheetType = BottomSheetType.FIXED;
setSelectedRRTProcedure(RRTProceduresResponseModel procedure) {
@ -39,19 +104,33 @@ class EmergencyServicesViewModel extends ChangeNotifier {
notifyListeners();
}
get isGMSAvailable
{
return appState.isGMSAvailable;
}
EmergencyServicesViewModel({
required this.emergencyServicesRepo,
required this.errorHandlerService,
required this.navServices,
required this.locationUtils,
required this.appState,
required this.appointmentRepo,
required this.dialogService,
});
get isGMSAvailable
{
return appState.isGMSAvailable;
void setIsERBookAppointment(bool value) {
isERBookAppointment = value;
notifyListeners();
}
bool get isArabic => appState.isArabic();
bool haveAnAppointment = false;
PatientAppointmentHistoryResponseModel? appointment;
bool isMyAppointmentsLoading = false;
Future<void> getRRTProcedures({Function(dynamic)? onSuccess, Function(String)? onError}) async {
RRTProceduresList.clear();
@ -149,46 +228,619 @@ class EmergencyServicesViewModel extends ChangeNotifier {
}
GMSMapServices.CameraPosition getGMSLocation() {
return GMSMapServices.CameraPosition(
target: GMSMapServices.LatLng(appState.userLat, appState.userLong), zoom: 18);
return GMSMapServices.CameraPosition(target: GMSMapServices.LatLng(appState.userLat, appState.userLong), zoom: 18);
}
handleGMSMapCameraMoved(GMSMapServices.CameraPosition value) {
//todo handle the camera moved position for GMS devices
//todo_section handle the camera moved position for GMS devices
}
HMSCameraServices.CameraPosition getHMSLocation() {
return HMSCameraServices.CameraPosition(
target: HMSCameraServices.LatLng(appState.userLat, appState.userLong),zoom: 18);
return HMSCameraServices.CameraPosition(target: HMSCameraServices.LatLng(appState.userLat, appState.userLong), zoom: 18);
}
handleHMSMapCameraMoved(HMSCameraServices.CameraPosition value) {
//todo handle the camera moved position for HMS devices
//todo_section handle the camera moved position for HMS devices
}
void navigateTOAmbulancePage() {
locationUtils!.getLocation(
isShowConfirmDialog: true,
onSuccess: (position) {
updateBottomSheetState(BottomSheetType.FIXED);
navServices.push(
CustomPageRoute(
page: CallAmbulancePage(), direction: AxisDirection.down
),
);
placeValueInController();
getProjects();
flushData();
selectedFacility = FacilitySelection.ALL;
print("the app state is ${appState.isAuthenticated}");
if (appState.isAuthenticated) {
locationUtils!.getLocation(
isShowConfirmDialog: true,
onSuccess: (position) {
updateBottomSheetState(BottomSheetType.FIXED);
navServices.push(
CustomPageRoute(
page: CallAmbulancePage(), direction: AxisDirection.down),
);
});
} else {
dialogService.showErrorBottomSheet(
message: "You Need To Login First To Continue".needTranslation,
onOkPressed: () {
navServices.pop();
navServices.pushAndReplace(
AppRoutes.loginScreen
);
});
}
}
});
void navigateToEROnlineCheckIn() {
navServices.push(
CustomPageRoute(page: ErOnlineCheckinHome()),
);
}
void navigateToEROnlineCheckInPaymentPage() {
navServices.push(
CustomPageRoute(page: ErOnlineCheckinPaymentDetailsPage()),
);
}
void updateBottomSheetState(BottomSheetType sheetType) {
if (sheetType == BottomSheetType.EXPANDED && selectedHospital == null) {
dialogService.showErrorBottomSheet(message: "Kindly Select Hospital".needTranslation);
return;
}
bottomSheetType = sheetType;
if (sheetType == BottomSheetType.EXPANDED) {
clearAppointmentData();
}
notifyListeners();
}
void setIsGMSAvailable(bool value) {
notifyListeners();
}
Future<void> checkPatientERAdvanceBalance({Function(dynamic)? onSuccess, Function(String)? onError}) async {
final result = await emergencyServicesRepo.checkPatientERAdvanceBalance();
result.fold(
// (failure) async => await errorHandlerService.handleError(failure: failure),
(failure) {
patientHasAdvanceERBalance = false;
isERBookAppointment = true;
if (onSuccess != null) {
onSuccess(failure.message);
}
},
(apiResponse) {
if (apiResponse.messageStatus == 2) {
// dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
patientHasAdvanceERBalance = false;
isERBookAppointment = true;
} else if (apiResponse.messageStatus == 1) {
patientHasAdvanceERBalance = apiResponse.data;
isERBookAppointment = !patientHasAdvanceERBalance;
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse);
}
}
},
);
}
Future<void> getPatientERPaymentInformation({Function(dynamic)? onSuccess, Function(String)? onError}) async {
final result = await emergencyServicesRepo.checkPatientERPaymentInformation(projectID: selectedHospital!.iD);
result.fold(
(failure) {
if (onError != null) {
onError(failure.message);
}
},
(apiResponse) {
if (apiResponse.messageStatus == 2) {
} else if (apiResponse.messageStatus == 1) {
erOnlineCheckInPaymentDetailsResponse = apiResponse.data!;
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse);
}
}
},
);
}
Future<void> ER_CreateAdvancePayment({required String paymentMethodName, required String paymentReference, Function(dynamic)? onSuccess, Function(String)? onError}) async {
final result = await emergencyServicesRepo.createAdvancePaymentForER(
projectID: selectedHospital!.iD,
authUser: appState.getAuthenticatedUser()!,
paymentAmount: erOnlineCheckInPaymentDetailsResponse.patientShareWithTax!,
paymentMethodName: paymentMethodName,
paymentReference: paymentReference);
result.fold(
(failure) {
if (onError != null) {
onError(failure.message);
}
},
(apiResponse) {
if (apiResponse.messageStatus == 2) {
} else if (apiResponse.messageStatus == 1) {
// erOnlineCheckInPaymentDetailsResponse = apiResponse.data!;
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse.data);
}
}
},
);
}
Future<void> addAdvanceNumberRequest(
{required String advanceNumber, required String paymentReference, required String appointmentNo, Function(dynamic)? onSuccess, Function(String)? onError}) async {
final result = await emergencyServicesRepo.addAdvanceNumberRequest(advanceNumber: advanceNumber, paymentReference: paymentReference, appointmentNo: appointmentNo);
result.fold(
(failure) async => await errorHandlerService.handleError(failure: failure),
(apiResponse) {
if (apiResponse.messageStatus == 2) {
// dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
} else if (apiResponse.messageStatus == 1) {
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse);
}
}
},
);
}
Future<void> getProjectIDFromNFC({required String nfcCode, Function(dynamic)? onSuccess, Function(String)? onError}) async {
final result = await emergencyServicesRepo.getProjectIDFromNFC(nfcCode: nfcCode);
result.fold(
// (failure) async => await errorHandlerService.handleError(failure: failure),
(failure) {
if (onError != null) {
onError(failure.message);
}
},
(apiResponse) {
if (apiResponse.messageStatus == 2) {
if (onError != null) {
onError(apiResponse.errorMessage!);
}
} else if (apiResponse.messageStatus == 1) {
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse.data);
}
}
},
);
}
Future<void> autoGenerateInvoiceERClinic({required int projectID, Function(dynamic)? onSuccess, Function(String)? onError}) async {
final result = await emergencyServicesRepo.autoGenerateInvoiceERClinic(projectID: projectID);
result.fold(
// (failure) async => await errorHandlerService.handleError(failure: failure),
(failure) {
if (onError != null) {
onError(failure.message);
}
},
(apiResponse) {
if (apiResponse.messageStatus == 2) {
if (onError != null) {
onError(apiResponse.data["InvoiceResponse"]["Message"]);
}
} else if (apiResponse.messageStatus == 1) {
patientHasAdvanceERBalance = false;
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse.data);
}
}
},
);
}
Future<void> getTransportationOption() async {
//handle the cache if the data is present then dont fetch it in the authenticated lifecycle
print("the app state is ${appState.isAuthenticated}");
if (appState.isAuthenticated == false) {
dialogService.showErrorBottomSheet(
message: "You Need To Login First To Continue".needTranslation,
onOkPressed: () {
navServices.pop();
print("inside the ok button");
getIt<AuthenticationViewModel>().onLoginPressed();
});
return;
}
if (transportationOptions.isNotEmpty) return;
int? id = appState.getAuthenticatedUser()?.patientId;
LoaderBottomSheet.showLoader(
loadingText: "Getting Ambulance Transport Option".needTranslation);
notifyListeners();
var response = await emergencyServicesRepo.getTransportationMethods(id: id);
response.fold(
(failure) async {
LoaderBottomSheet.hideLoader();
},
(apiResponse) {
transportationOptions = apiResponse.data!;
LoaderBottomSheet.hideLoader();
notifyListeners();
},
);
}
Future<void> getTransportationMethods() async {
int? id = appState.getAuthenticatedUser()?.patientId;
LoaderBottomSheet.showLoader(
loadingText: "Getting Ambulance Transport Option".needTranslation);
notifyListeners();
var response = await emergencyServicesRepo.getTransportationMethods(id: id);
response.fold(
(failure) async {
LoaderBottomSheet.hideLoader();
},
(apiResponse) {
transportationOptions = apiResponse.data!;
LoaderBottomSheet.hideLoader();
notifyListeners();
},
);
}
void setTransportationOption(PatientERTransportationMethod item) {
selectedTransportOption = item;
}
void updateCallingPlace(AmbulanceCallingPlace? value) {
callingPlace = value!;
notifyListeners();
}
void updateDirection(AmbulanceDirection? value) {
ambulanceDirection = value!;
notifyListeners();
}
void placeValueInController() {
if (isGMSAvailable) {
gmsController = Completer<GMSMapServices.GoogleMapController>();
} else {
hmsController = Completer<HMSCameraServices.HuaweiMapController>();
}
}
void moveController(Location location) {
if (isGMSAvailable) {
gmsController?.future.then((controller) {
controller.animateCamera(
GMSMapServices.CameraUpdate.newCameraPosition(
GMSMapServices.CameraPosition(
target: GMSMapServices.LatLng(location.lat, location.lng),
zoom: 18,
),
),
);
});
} else {
hmsController?.future.then((controller) {
controller.animateCamera(
HMSCameraServices.CameraUpdate.newCameraPosition(
HMSCameraServices.CameraPosition(
target: HMSCameraServices.LatLng(location.lat, location.lng),
zoom: 18,
),
),
);
});
}
}
void moveToCurrentLocation() {
moveController(Location(lat: appState.userLat, lng: appState.userLong));
}
FutureOr<void> getProjects() async {
// if (hospitalList.isNotEmpty) return;
var response = await emergencyServicesRepo.getProjectList();
response.fold(
(failure) async {},
(apiResponse) async {
List<HospitalsModel>? data = apiResponse.data;
if (data == null) return;
hospitalList = data;
hmgHospitalList = data.where((e) => e.isHMC == false).toList();
hmcHospitalList = data.where((e) => e.isHMC == true).toList();
hmgCount = hmgHospitalList?.length ?? 0;
hmcCount = hmcHospitalList?.length ?? 0;
getDisplayList();
notifyListeners();
},
);
}
setSelectedFacility(FacilitySelection selection) {
selectedFacility = selection;
notifyListeners();
}
searchHospitals(String query) {
if (query.isEmpty) {
getDisplayList();
return;
}
List<HospitalsModel>? sourceList;
switch (selectedFacility) {
case FacilitySelection.ALL:
sourceList = hospitalList;
break;
case FacilitySelection.HMG:
sourceList = hmgHospitalList;
break;
case FacilitySelection.HMC:
sourceList = hmcHospitalList;
break;
}
displayList = sourceList
?.where((hospital) =>
hospital.name != null &&
hospital.name!.toLowerCase().contains(query.toLowerCase()))
.toList();
notifyListeners();
}
getDisplayList() {
switch (selectedFacility) {
case FacilitySelection.ALL:
displayList = hospitalList;
break;
case FacilitySelection.HMG:
displayList = hmgHospitalList;
break;
case FacilitySelection.HMC:
displayList = hmcHospitalList;
break;
}
notifyListeners();
}
void setSelectedHospital(HospitalsModel? hospital) {
selectedHospital = hospital;
notifyListeners();
}
String? getSelectedHospitalName() {
return selectedHospital?.getName(isArabic);
}
void updateThePickupPlaceFromLocation(bool value) {
pickupFromInsideTheLocation = value;
notifyListeners();
}
void flushData() {
selectedHospital = null;
displayList = hospitalList;
selectedFacility = FacilitySelection.ALL;
pickupFromInsideTheLocation = false;
haveAnAppointment = false;
appointment = null;
notifyListeners();
}
void clearAppointmentData() {
haveAnAppointment = false;
appointment = null;
notifyListeners();
}
void flushPickupInformation() {
ambulanceDirection = AmbulanceDirection.ONE_WAY;
callingPlace = AmbulanceCallingPlace.FROM_HOSPITAL;
notifyListeners();
}
num getTotalPrice() {
print("the ambulance direction is $ambulanceDirection");
switch (ambulanceDirection) {
case AmbulanceDirection.ONE_WAY:
return selectedTransportOption?.priceTotal ?? 0;
case AmbulanceDirection.TWO_WAY:
return (selectedTransportOption?.priceTotal ?? 0) * 2;
}
}
Future<void> updateAppointment(bool value) async {
if (value) {
await getAppointments();
} else {
clearAppointmentData();
}
if(appointments?.isNotEmpty == true) {
haveAnAppointment = value;
}
notifyListeners();
}
Future<void> getAppointments() async {
LoaderBottomSheet.showLoader(loadingText: "Fetching Appointment".needTranslation);
var result = await appointmentRepo.getPatientAppointments(isActiveAppointment: true, isArrivedAppointments: false);
LoaderBottomSheet.hideLoader();
result.fold((failure) {
haveAnAppointment = false;
notifyListeners();
errorHandlerService.handleError(failure: failure);
}, (response) {
appointments = response.data;
if (appointments?.isEmpty == true) {
haveAnAppointment = false;
dialogService.showErrorBottomSheet(message: "Unable to Fetch Appointment");
}
notifyListeners();
});
}
void setSelectedAppointment(PatientAppointmentHistoryResponseModel selectedAppointment) {
appointment = selectedAppointment;
notifyListeners();
}
void checkHasAppointment() {
if (appointment == null) {
haveAnAppointment = false;
}
notifyListeners();
}
FutureOr<void> submitAmbulanceRequest(GeocodeResult? result, PlaceDetails? place, PlacePrediction? placePrediction) async {
PatientER_RC request = createRequest(result, place, placePrediction);
navServices.push(CustomPageRoute(page: RequestingServicesPage()));
var response = await emergencyServicesRepo.submitAmbulanceRequest(request);
response.fold((failure) {
navServices.pushAndRemoveUntil(
CustomPageRoute(
page: TrackingScreen(
state: OrderTrackingState.failed,
)),
ModalRoute.withName("/EmergencyServicesPage"));
}, (success) {
getTransportationOrders(shouldNavigateToTrackingScreen: true);
});
}
Future<void> getTransportationOrders({bool shouldNavigateToTrackingScreen = false, bool showLoader = false}) async {
if(shouldNavigateToTrackingScreen == false && showLoader ) {
LoaderBottomSheet.showLoader(loadingText: "Fetching Orders");
}
int? id = appState.getAuthenticatedUser()?.patientId;
var response = await emergencyServicesRepo.getTransportationOrders(id: id);
if(shouldNavigateToTrackingScreen == false && showLoader ) {
LoaderBottomSheet.hideLoader();}
response.fold(
(failure) async {
if (shouldNavigateToTrackingScreen) {
navServices.pushAndRemoveUntil(CustomPageRoute(page: TrackingScreen(state: OrderTrackingState.waitingForCall)), ModalRoute.withName("/EmergencyServicesPage"));
}
},
(apiResponse) {
if (shouldNavigateToTrackingScreen) {
navServices.pushAndRemoveUntil(
CustomPageRoute(
page: TrackingScreen(
state: OrderTrackingState.waitingForCall,
order: apiResponse.data?.firstWhere((element) => element.statusId == 1),
)),
ModalRoute.withName("/EmergencyServicesPage"));
}
orders = apiResponse.data;
notifyListeners();
},
);
}
PatientER_RC createRequest(GeocodeResult? result, PlaceDetails? place, PlacePrediction? placePrediction) {
PatientER_RC request = PatientER_RC();
TransportationDetails transportationDetails = TransportationDetails();
transportationDetails.requesterIsOutSA = false;
transportationDetails.pickupSpot = pickupFromInsideTheLocation ? 1 : 0;
transportationDetails.tripType = ambulanceDirection == AmbulanceDirection.ONE_WAY ? 0 : 1;
transportationDetails.ambulate = (transportationOptions.indexOf(selectedTransportOption!) + 1);
transportationDetails.transportationType = selectedTransportOption?.iD.toString() ?? "";
request.patientERTransportationMethod = selectedTransportOption;
transportationDetails.pickupUrgency = 1;
switch (callingPlace) {
case AmbulanceCallingPlace.FROM_HOSPITAL:
transportationDetails.dropoffLatitude = selectedHospital!.latitude;
transportationDetails.dropoffLongitude = selectedHospital!.longitude;
transportationDetails.dropoffLocationName = selectedHospital!.name;
transportationDetails.pickupLatitude = ((result?.geometry.location.lat) ?? place?.lat)?.toStringAsFixed(6);
transportationDetails.pickupLongitude = ((result?.geometry.location.lng) ?? place?.lng)?.toStringAsFixed(6);
transportationDetails.pickupLocationName = result?.formattedAddress ?? placePrediction?.description;
transportationDetails.direction = 1;
break;
case AmbulanceCallingPlace.TO_HOSPITAL:
transportationDetails.pickupLatitude = selectedHospital!.latitude;
transportationDetails.pickupLongitude = selectedHospital!.longitude;
transportationDetails.pickupLocationName = selectedHospital!.name;
transportationDetails.dropoffLatitude = ((result?.geometry.location.lat) ?? place?.lat)?.toStringAsFixed(6);
transportationDetails.dropoffLongitude = ((result?.geometry.location.lng) ?? place?.lng)?.toStringAsFixed(6);
transportationDetails.dropoffLocationName = result?.formattedAddress ?? placePrediction?.description;
transportationDetails.direction = 0;
break;
}
AuthenticatedUser? user = appState.getAuthenticatedUser();
if (user == null) throw Exception("Authentication Required to Continue");
request.patientIdentificationID = user.patientIdentificationNo;
request.projectID = user.projectId;
request.patientID = user.patientId;
transportationDetails.requesterIsOutSA = false;
transportationDetails.requesterMobileNo = user.mobileNumber;
transportationDetails.requesterFileNo = user.patientId;
transportationDetails.pickupDateTime = DateUtil.convertDateToStringLocation(DateTime.now());
if (haveAnAppointment) {
transportationDetails.appointmentNo = appointment!.appointmentNo.toString();
transportationDetails.appointmentClinicName = appointment!.clinicName;
transportationDetails.appointmentDoctorName = appointment!.doctorNameObj;
transportationDetails.appointmentBranch = appointment!.projectName;
transportationDetails.appointmentTime = appointment!.appointmentDate;
transportationDetails.haveAppointment = 1;
transportationDetails.dropoffLatitude = appointment!.latitude;
transportationDetails.dropoffLongitude = appointment!.longitude;
transportationDetails.dropoffLocationName = appointment!.projectName;
}
transportationDetails.pickupSpot = pickupFromInsideTheLocation ? 1 : 0;
transportationDetails.ambulate = 3;
transportationDetails.ambulateTitle = "none";
transportationDetails.notes = "";
request.transportationDetails = transportationDetails;
return request;
}
void clearSearchText() {
searchController.clear();
}
Future<void> cancelOrder(AmbulanceRequestOrdersModel? order, {bool shouldPop = false}) async {
dialogService.showCommonBottomSheetWithoutH(
message: "Do you want to cancel the order".needTranslation,
onOkPressed: () async {
navServices.pop();
LoaderBottomSheet.showLoader(loadingText: "Cancelling Order".needTranslation);
var response = await emergencyServicesRepo.cancelOrder(order?.iD, appState.getAuthenticatedUser()?.patientId ?? 0);
LoaderBottomSheet.hideLoader();
response.fold((failure) => errorHandlerService.handleError(failure: failure), (success) {
getTransportationOrders();
if (shouldPop) navServices.pop();
});
},
onCancelPressed: () {
navServices.pop();
});
}
}

@ -0,0 +1,4 @@
enum AmbulanceCallingPlace{
FROM_HOSPITAL,
TO_HOSPITAL
}

@ -0,0 +1,3 @@
enum AmbulanceDirection{
ONE_WAY, TWO_WAY
}

@ -0,0 +1,205 @@
import 'package:hmg_patient_app_new/features/emergency_services/models/resp_model/PatientERTransportationMethod.dart' show PatientERTransportationMethod;
class PatientER_RC {
double? versionID;
int? channel;
int? languageID;
String? iPAdress;
String? generalid;
bool? patientOutSA;
String? sessionID;
bool? isDentalAllowedBackend;
int? deviceTypeID;
int? patientID;
String? tokenID;
int? patientTypeID;
int? patientType;
int? orderServiceID;
String? patientIdentificationID;
dynamic patientOutSa;
int? projectID;
int? lineItemNo;
TransportationDetails? transportationDetails;
PatientERTransportationMethod? patientERTransportationMethod;
PatientER_RC(
{this.versionID,
this.channel,
this.languageID,
this.iPAdress,
this.generalid,
this.patientOutSA,
this.sessionID,
this.isDentalAllowedBackend,
this.deviceTypeID,
this.patientID,
this.tokenID,
this.patientTypeID,
this.patientType,
this.orderServiceID,
this.patientIdentificationID,
this.patientOutSa,
this.projectID,
this.lineItemNo,
this.transportationDetails });
PatientER_RC.fromJson(Map<String, dynamic> json) {
versionID = json['VersionID'];
channel = json['Channel'];
languageID = json['LanguageID'];
iPAdress = json['IPAdress'];
generalid = json['generalid'];
patientOutSA = json['PatientOutSA'];
sessionID = json['SessionID'];
isDentalAllowedBackend = json['isDentalAllowedBackend'];
deviceTypeID = json['DeviceTypeID'];
patientID = json['PatientID'];
tokenID = json['TokenID'];
patientTypeID = json['PatientTypeID'];
patientType = json['PatientType'];
orderServiceID = json['OrderServiceID'];
patientIdentificationID = json['PatientIdentificationID'];
patientOutSa = json['patientOutSa'];
projectID = json['projectID'];
lineItemNo = json['lineItemNo'];
transportationDetails = json['transportationDetails'] != null
? new TransportationDetails.fromJson(json['transportationDetails'])
: null;
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['VersionID'] = this.versionID;
data['Channel'] = this.channel;
data['LanguageID'] = this.languageID;
data['IPAdress'] = this.iPAdress;
data['generalid'] = this.generalid;
data['PatientOutSA'] = this.patientOutSA;
data['SessionID'] = this.sessionID;
data['isDentalAllowedBackend'] = this.isDentalAllowedBackend;
data['DeviceTypeID'] = this.deviceTypeID;
data['PatientID'] = this.patientID;
data['TokenID'] = this.tokenID;
data['PatientTypeID'] = this.patientTypeID;
data['PatientType'] = this.patientType;
data['OrderServiceID'] = this.orderServiceID;
data['PatientIdentificationID'] = this.patientIdentificationID;
data['patientOutSa'] = this.patientOutSa;
data['projectID'] = this.projectID;
data['lineItemNo'] = this.lineItemNo;
if (this.transportationDetails != null) {
data['transportationDetails'] = this.transportationDetails!.toJson();
}
return data;
}
}
class TransportationDetails {
int? direction;
int? haveAppointment;
int? tripType;
int? pickupUrgency;
int? pickupSpot;
String? pickupDateTime;
String? transportationType;
int? ambulate;
String? ambulateTitle;
String? notes;
int? requesterFileNo;
String? requesterMobileNo;
bool? requesterIsOutSA;
String? pickupLocationName;
String? dropoffLocationName;
String? pickupLatitude;
String? pickupLongitude;
String? dropoffLatitude;
String? dropoffLongitude;
String? appointmentNo;
String? appointmentClinicName;
String? appointmentDoctorName;
String? appointmentBranch;
String? appointmentTime;
TransportationDetails(
{this.direction,
this.haveAppointment = 0,
this.tripType,
this.pickupUrgency,
this.pickupSpot,
this.pickupDateTime,
this.transportationType,
this.ambulate,
this.ambulateTitle,
this.notes,
this.requesterFileNo,
this.requesterMobileNo,
this.requesterIsOutSA,
this.pickupLocationName,
this.dropoffLocationName,
this.pickupLatitude,
this.pickupLongitude,
this.dropoffLatitude,
this.dropoffLongitude,
this.appointmentNo = "0",
this.appointmentClinicName = "",
this.appointmentDoctorName = "",
this.appointmentBranch = "",
this.appointmentTime = ""});
TransportationDetails.fromJson(Map<String, dynamic> json) {
direction = json['direction'];
haveAppointment = json['haveAppointment'];
tripType = json['tripType'];
pickupUrgency = json['pickupUrgency'];
pickupSpot = json['pickupSpot'];
pickupDateTime = json['pickupDateTime'];
transportationType = json['transportationType'];
ambulate = json['ambulate'];
ambulateTitle = "";
notes = json['notes'];
requesterFileNo = json['requesterFileNo'];
requesterMobileNo = json['requesterMobileNo'];
requesterIsOutSA = json['requesterIsOutSA'];
pickupLocationName = json['pickupLocationName'];
dropoffLocationName = json['dropoffLocationName'];
pickupLatitude = json['pickup_Latitude'];
pickupLongitude = json['pickup_Longitude'];
dropoffLatitude = json['dropoff_Latitude'];
dropoffLongitude = json['dropoff_Longitude'];
appointmentNo = json['appointmentNo'];
appointmentClinicName = json['appointmentClinicName'];
appointmentDoctorName = json['appointmentDoctorName'];
appointmentBranch = json['appointmentBranch'];
appointmentTime = json['appointmentTime'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['direction'] = this.direction;
data['haveAppointment'] = this.haveAppointment;
data['tripType'] = this.tripType;
data['pickupUrgency'] = this.pickupUrgency;
data['pickupSpot'] = this.pickupSpot;
data['pickupDateTime'] = this.pickupDateTime;
data['transportationType'] = this.transportationType;
data['ambulate'] = this.ambulate;
data['ambulateTitle'] = this.ambulateTitle;
data['notes'] = this.notes;
data['requesterFileNo'] = this.requesterFileNo;
data['requesterMobileNo'] = this.requesterMobileNo;
data['requesterIsOutSA'] = this.requesterIsOutSA;
data['pickupLocationName'] = this.pickupLocationName;
data['dropoffLocationName'] = this.dropoffLocationName;
data['pickup_Latitude'] = this.pickupLatitude;
data['pickup_Longitude'] = this.pickupLongitude;
data['dropoff_Latitude'] = this.dropoffLatitude;
data['dropoff_Longitude'] = this.dropoffLongitude;
data['appointmentNo'] = this.appointmentNo;
data['appointmentClinicName'] = this.appointmentClinicName;
data['appointmentDoctorName'] = this.appointmentDoctorName;
data['appointmentBranch'] = this.appointmentBranch;
data['appointmentTime'] = this.appointmentTime;
return data;
}
}

@ -0,0 +1,346 @@
class AmbulanceRequestOrdersModel {
String? statusText;
num? paymentStatus;
dynamic clientRequestid;
dynamic paymentStatusText;
dynamic projectName;
String? nearestProjectName;
num? paymentAmount;
WFOrder? wFOrder;
String? serviceText;
bool? isSentForApproval;
num? exaCartOrderId;
String? exaCartGUID;
bool? isTimer;
num? timeSeconds;
num? totalPendingSeconds;
num? timeMinute;
num? timeHour;
num? timeTotalSeconds;
num? timeTotalMinute;
num? timeTotalHour;
dynamic approvalStatus;
bool? isActive;
num? clickButton;
dynamic orderHistory;
String? pickupLocation;
String? dropOffLocation;
String? clinicName;
String? doctorName;
String? branch;
String? time;
String? notes;
int? iD;
num? patientId;
num? patientOutSa;
bool? isOutPatient;
num? projectId;
num? nearestProjectId;
dynamic longitude;
dynamic latitude;
dynamic appointmentNo;
dynamic dischargeId;
int? statusId;
num? serviceId;
num? channel;
Orderpayment? orderpayment;
dynamic wforder;
dynamic orderapprovalobj;
String? created;
dynamic createdBy;
dynamic modified;
dynamic modifiedBy;
bool? isDeleted;
AmbulanceRequestOrdersModel(
{this.statusText,
this.paymentStatus,
this.clientRequestid,
this.paymentStatusText,
this.projectName,
this.nearestProjectName,
this.paymentAmount,
this.wFOrder,
this.serviceText,
this.isSentForApproval,
this.exaCartOrderId,
this.exaCartGUID,
this.isTimer,
this.timeSeconds,
this.totalPendingSeconds,
this.timeMinute,
this.timeHour,
this.timeTotalSeconds,
this.timeTotalMinute,
this.timeTotalHour,
this.approvalStatus,
this.isActive,
this.clickButton,
this.orderHistory,
this.pickupLocation,
this.dropOffLocation,
this.clinicName,
this.doctorName,
this.branch,
this.time,
this.notes,
this.iD,
this.patientId,
this.patientOutSa,
this.isOutPatient,
this.projectId,
this.nearestProjectId,
this.longitude,
this.latitude,
this.appointmentNo,
this.dischargeId,
this.statusId,
this.serviceId,
this.channel,
this.orderpayment,
this.wforder,
this.orderapprovalobj,
this.created,
this.createdBy,
this.modified,
this.modifiedBy,
this.isDeleted});
AmbulanceRequestOrdersModel.fromJson(Map<String, dynamic> json) {
statusText = json['StatusText'];
paymentStatus = json['PaymentStatus'];
clientRequestid = json['ClientRequestid'];
paymentStatusText = json['PaymentStatusText'];
projectName = json['ProjectName'];
nearestProjectName = json['NearestProjectName'];
paymentAmount = json['PaymentAmount'];
wFOrder = json['WF_order'] != null
? new WFOrder.fromJson(json['WF_order'])
: null;
serviceText = json['ServiceText'];
isSentForApproval = json['isSentForApproval'];
exaCartOrderId = json['ExaCart_OrderId'];
exaCartGUID = json['ExaCart_GUID'];
isTimer = json['isTimer'];
timeSeconds = json['TimeSeconds'];
totalPendingSeconds = json['TotalPendingSeconds'];
timeMinute = json['TimeMinute'];
timeHour = json['TimeHour'];
timeTotalSeconds = json['TimeTotalSeconds'];
timeTotalMinute = json['TimeTotalMinute'];
timeTotalHour = json['TimeTotalHour'];
approvalStatus = json['ApprovalStatus'];
isActive = json['isActive'];
clickButton = json['ClickButton'];
orderHistory = json['OrderHistory'];
pickupLocation = json['PickupLocation'];
dropOffLocation = json['DropOffLocation'];
clinicName = json['clinicName'];
doctorName = json['DoctorName'];
branch = json['Branch'];
time = json['Time'];
notes = json['Notes'];
iD = json['ID'];
patientId = json['PatientId'];
patientOutSa = json['PatientOutSa'];
isOutPatient = json['IsOutPatient'];
projectId = json['ProjectId'];
nearestProjectId = json['NearestProjectId'];
longitude = json['Longitude'];
latitude = json['Latitude'];
appointmentNo = json['AppointmentNo'];
dischargeId = json['DischargeId'];
statusId = json['StatusId'];
serviceId = json['ServiceId'];
channel = json['Channel'];
orderpayment = json['orderpayment'] != null
? new Orderpayment.fromJson(json['orderpayment'])
: null;
wforder = json['wforder'];
orderapprovalobj = json['orderapprovalobj'];
created = json['Created'];
createdBy = json['CreatedBy'];
modified = json['Modified'];
modifiedBy = json['ModifiedBy'];
isDeleted = json['IsDeleted'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['StatusText'] = this.statusText;
data['PaymentStatus'] = this.paymentStatus;
data['ClientRequestid'] = this.clientRequestid;
data['PaymentStatusText'] = this.paymentStatusText;
data['ProjectName'] = this.projectName;
data['NearestProjectName'] = this.nearestProjectName;
data['PaymentAmount'] = this.paymentAmount;
if (this.wFOrder != null) {
data['WF_order'] = this.wFOrder!.toJson();
}
data['ServiceText'] = this.serviceText;
data['isSentForApproval'] = this.isSentForApproval;
data['ExaCart_OrderId'] = this.exaCartOrderId;
data['ExaCart_GUID'] = this.exaCartGUID;
data['isTimer'] = this.isTimer;
data['TimeSeconds'] = this.timeSeconds;
data['TotalPendingSeconds'] = this.totalPendingSeconds;
data['TimeMinute'] = this.timeMinute;
data['TimeHour'] = this.timeHour;
data['TimeTotalSeconds'] = this.timeTotalSeconds;
data['TimeTotalMinute'] = this.timeTotalMinute;
data['TimeTotalHour'] = this.timeTotalHour;
data['ApprovalStatus'] = this.approvalStatus;
data['isActive'] = this.isActive;
data['ClickButton'] = this.clickButton;
data['OrderHistory'] = this.orderHistory;
data['PickupLocation'] = this.pickupLocation;
data['DropOffLocation'] = this.dropOffLocation;
data['clinicName'] = this.clinicName;
data['DoctorName'] = this.doctorName;
data['Branch'] = this.branch;
data['Time'] = this.time;
data['Notes'] = this.notes;
data['ID'] = this.iD;
data['PatientId'] = this.patientId;
data['PatientOutSa'] = this.patientOutSa;
data['IsOutPatient'] = this.isOutPatient;
data['ProjectId'] = this.projectId;
data['NearestProjectId'] = this.nearestProjectId;
data['Longitude'] = this.longitude;
data['Latitude'] = this.latitude;
data['AppointmentNo'] = this.appointmentNo;
data['DischargeId'] = this.dischargeId;
data['StatusId'] = this.statusId;
data['ServiceId'] = this.serviceId;
data['Channel'] = this.channel;
if (this.orderpayment != null) {
data['orderpayment'] = this.orderpayment!.toJson();
}
data['wforder'] = this.wforder;
data['orderapprovalobj'] = this.orderapprovalobj;
data['Created'] = this.created;
data['CreatedBy'] = this.createdBy;
data['Modified'] = this.modified;
data['ModifiedBy'] = this.modifiedBy;
data['IsDeleted'] = this.isDeleted;
return data;
}
}
class WFOrder {
dynamic wfButtonsDTO;
num? iD;
num? orderId;
num? previousStep;
num? nextStep;
num? serviceId;
dynamic order;
String? created;
dynamic createdBy;
dynamic modified;
dynamic modifiedBy;
bool? isDeleted;
WFOrder(
{this.wfButtonsDTO,
this.iD,
this.orderId,
this.previousStep,
this.nextStep,
this.serviceId,
this.order,
this.created,
this.createdBy,
this.modified,
this.modifiedBy,
this.isDeleted});
WFOrder.fromJson(Map<String, dynamic> json) {
wfButtonsDTO = json['wf_ButtonsDTO'];
iD = json['ID'];
orderId = json['OrderId'];
previousStep = json['PreviousStep'];
nextStep = json['NextStep'];
serviceId = json['ServiceId'];
order = json['Order'];
created = json['Created'];
createdBy = json['CreatedBy'];
modified = json['Modified'];
modifiedBy = json['ModifiedBy'];
isDeleted = json['IsDeleted'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['wf_ButtonsDTO'] = this.wfButtonsDTO;
data['ID'] = this.iD;
data['OrderId'] = this.orderId;
data['PreviousStep'] = this.previousStep;
data['NextStep'] = this.nextStep;
data['ServiceId'] = this.serviceId;
data['Order'] = this.order;
data['Created'] = this.created;
data['CreatedBy'] = this.createdBy;
data['Modified'] = this.modified;
data['ModifiedBy'] = this.modifiedBy;
data['IsDeleted'] = this.isDeleted;
return data;
}
}
class Orderpayment {
num? iD;
num? orderId;
dynamic clientRequestId;
num? totalAmount;
num? paymentStatus;
dynamic order;
String? created;
dynamic createdBy;
dynamic modified;
dynamic modifiedBy;
bool? isDeleted;
Orderpayment(
{this.iD,
this.orderId,
this.clientRequestId,
this.totalAmount,
this.paymentStatus,
this.order,
this.created,
this.createdBy,
this.modified,
this.modifiedBy,
this.isDeleted});
Orderpayment.fromJson(Map<String, dynamic> json) {
iD = json['ID'];
orderId = json['OrderId'];
clientRequestId = json['ClientRequestId'];
totalAmount = json['TotalAmount'];
paymentStatus = json['PaymentStatus'];
order = json['Order'];
created = json['Created'];
createdBy = json['CreatedBy'];
modified = json['Modified'];
modifiedBy = json['ModifiedBy'];
isDeleted = json['IsDeleted'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['ID'] = this.iD;
data['OrderId'] = this.orderId;
data['ClientRequestId'] = this.clientRequestId;
data['TotalAmount'] = this.totalAmount;
data['PaymentStatus'] = this.paymentStatus;
data['Order'] = this.order;
data['Created'] = this.created;
data['CreatedBy'] = this.createdBy;
data['Modified'] = this.modified;
data['ModifiedBy'] = this.modifiedBy;
data['IsDeleted'] = this.isDeleted;
return data;
}
}

@ -0,0 +1,108 @@
class EROnlineCheckInPaymentDetailsResponse {
num? cashPrice;
num? cashPriceTax;
num? cashPriceWithTax;
int? companyId;
String? companyName;
num? companyShareWithTax;
dynamic errCode;
int? groupID;
String? insurancePolicyNo;
String? message;
String? patientCardID;
num? patientShare;
num? patientShareWithTax;
num? patientTaxAmount;
int? policyId;
String? policyName;
String? procedureId;
String? procedureName;
dynamic setupID;
int? statusCode;
String? subPolicyNo;
bool? isCash;
bool? isEligible;
bool? isInsured;
EROnlineCheckInPaymentDetailsResponse(
{this.cashPrice,
this.cashPriceTax,
this.cashPriceWithTax,
this.companyId,
this.companyName,
this.companyShareWithTax,
this.errCode,
this.groupID,
this.insurancePolicyNo,
this.message,
this.patientCardID,
this.patientShare,
this.patientShareWithTax,
this.patientTaxAmount,
this.policyId,
this.policyName,
this.procedureId,
this.procedureName,
this.setupID,
this.statusCode,
this.subPolicyNo,
this.isCash,
this.isEligible,
this.isInsured});
EROnlineCheckInPaymentDetailsResponse.fromJson(Map<String, dynamic> json) {
cashPrice = json['CashPrice'];
cashPriceTax = json['CashPriceTax'];
cashPriceWithTax = json['CashPriceWithTax'];
companyId = json['CompanyId'];
companyName = json['CompanyName'];
companyShareWithTax = json['CompanyShareWithTax'];
errCode = json['ErrCode'];
groupID = json['GroupID'];
insurancePolicyNo = json['InsurancePolicyNo'];
message = json['Message'];
patientCardID = json['PatientCardID'];
patientShare = json['PatientShare'];
patientShareWithTax = json['PatientShareWithTax'];
patientTaxAmount = json['PatientTaxAmount'];
policyId = json['PolicyId'];
policyName = json['PolicyName'];
procedureId = json['ProcedureId'];
procedureName = json['ProcedureName'];
setupID = json['SetupID'];
statusCode = json['StatusCode'];
subPolicyNo = json['SubPolicyNo'];
isCash = json['IsCash'];
isEligible = json['IsEligible'];
isInsured = json['IsInsured'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['CashPrice'] = this.cashPrice;
data['CashPriceTax'] = this.cashPriceTax;
data['CashPriceWithTax'] = this.cashPriceWithTax;
data['CompanyId'] = this.companyId;
data['CompanyName'] = this.companyName;
data['CompanyShareWithTax'] = this.companyShareWithTax;
data['ErrCode'] = this.errCode;
data['GroupID'] = this.groupID;
data['InsurancePolicyNo'] = this.insurancePolicyNo;
data['Message'] = this.message;
data['PatientCardID'] = this.patientCardID;
data['PatientShare'] = this.patientShare;
data['PatientShareWithTax'] = this.patientShareWithTax;
data['PatientTaxAmount'] = this.patientTaxAmount;
data['PolicyId'] = this.policyId;
data['PolicyName'] = this.policyName;
data['ProcedureId'] = this.procedureId;
data['ProcedureName'] = this.procedureName;
data['SetupID'] = this.setupID;
data['StatusCode'] = this.statusCode;
data['SubPolicyNo'] = this.subPolicyNo;
data['IsCash'] = this.isCash;
data['IsEligible'] = this.isEligible;
data['IsInsured'] = this.isInsured;
return data;
}
}

@ -0,0 +1,56 @@
class PatientERTransportationMethod {
int? iD;
String? serviceID;
int? orderServiceID;
String? text;
String? textN;
dynamic price;
dynamic priceVAT;
dynamic priceTotal;
bool? isEnabled;
int? orderId;
int? quantity;
PatientERTransportationMethod(
{this.iD,
this.serviceID,
this.orderServiceID,
this.text,
this.textN,
this.price,
this.priceVAT,
this.priceTotal,
this.isEnabled,
this.orderId,
this.quantity});
PatientERTransportationMethod.fromJson(Map<String, dynamic> json) {
iD = json['ID'];
serviceID = json['ServiceID'];
orderServiceID = json['OrderServiceID'];
text = json['Text'];
textN = json['TextN'];
price = json['Price'];
priceVAT = json['PriceVAT'];
priceTotal = json['PriceTotal'];
isEnabled = json['IsEnabled'];
orderId = json['OrderId'];
quantity = json['Quantity'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['ID'] = this.iD;
data['ServiceID'] = this.serviceID;
data['OrderServiceID'] = this.orderServiceID;
data['Text'] = this.text;
data['TextN'] = this.textN;
data['Price'] = this.price;
data['PriceVAT'] = this.priceVAT;
data['PriceTotal'] = this.priceTotal;
data['IsEnabled'] = this.isEnabled;
data['OrderId'] = this.orderId;
data['Quantity'] = this.quantity;
return data;
}
}

@ -0,0 +1,80 @@
class GeocodeResponse {
final List<GeocodeResult> results;
final String status;
GeocodeResponse({
required this.results,
required this.status,
});
factory GeocodeResponse.fromJson(Map<String, dynamic> json) {
final resultsList = (json['results'] as List<dynamic>? ?? [])
.map((e) => GeocodeResult.fromJson(e as Map<String, dynamic>))
.toList();
return GeocodeResponse(
results: resultsList,
status: json['status'] ?? '',
);
}
@override
String toString() {
return 'GeocodeResponse(status: $status, results: [${results.map((r) => r.toString()).join(', ')}])';
}
}
class GeocodeResult {
final String formattedAddress;
final Geometry geometry;
final String placeId;
GeocodeResult({
required this.formattedAddress,
required this.geometry,
required this.placeId,
});
factory GeocodeResult.fromJson(Map<String, dynamic> json) {
return GeocodeResult(
formattedAddress: json['formatted_address'] ?? '',
geometry: Geometry.fromJson(json['geometry']),
placeId: json['place_id'] ?? '',
);
}
@override
String toString() {
return 'GeocodeResult(formattedAddress: $formattedAddress, placeId: $placeId, geometry: ${geometry.toString()})';
}
}
class Geometry {
final Location location;
Geometry({required this.location});
factory Geometry.fromJson(Map<String, dynamic> json) {
return Geometry(
location: Location.fromJson(json['location']),
);
}
@override
String toString() => 'Geometry(location: ${location.toString()})';
}
class Location {
final double lat;
final double lng;
Location({required this.lat, required this.lng});
factory Location.fromJson(Map<String, dynamic> json) {
return Location(
lat: (json['lat'] as num).toDouble(),
lng: (json['lng'] as num).toDouble(),
);
}
@override
String toString() => 'Location(lat: $lat, lng: $lng)';
}

@ -0,0 +1,14 @@
class PlaceDetails {
final double lat;
final double lng;
PlaceDetails({required this.lat, required this.lng});
factory PlaceDetails.fromJson(Map<String, dynamic> json) {
final loc = json['result']['geometry']['location'];
return PlaceDetails(
lat: (loc['lat'] as num).toDouble(),
lng: (loc['lng'] as num).toDouble(),
);
}
}

@ -0,0 +1,11 @@
class PlacePrediction {
final String description;
final String placeID;
PlacePrediction({required this.description, required this.placeID});
factory PlacePrediction.fromJson(Map<String, dynamic> json) {
return PlacePrediction(
description: json['description'] ?? '',
placeID: json['place_id'] ?? '',
);
}
}

@ -0,0 +1,153 @@
import 'package:dartz/dartz.dart';
import 'package:hmg_patient_app_new/core/api/api_client.dart';
import 'package:hmg_patient_app_new/core/common_models/generic_api_model.dart';
import 'package:hmg_patient_app_new/core/exceptions/api_failure.dart';
import 'package:hmg_patient_app_new/features/location/GeocodeResponse.dart';
import 'package:hmg_patient_app_new/features/location/PlaceDetails.dart';
import '../../core/api_consts.dart';
import 'PlacePrediction.dart';
abstract class LocationRepo {
Future<Either<Failure, GenericApiModel<List<PlacePrediction>>>>
getPlacePredictionsAsInput(String input);
Future<Either<Failure, GenericApiModel<PlaceDetails>>>
getPlaceDetailsOfSelectedPrediction(String placeId);
Future<Either<Failure, GenericApiModel<GeocodeResponse>>>
getGeoCodeFromLatLng(double lat, double lng);
}
class LocationRepoImpl implements LocationRepo {
final ApiClient apiClient;
LocationRepoImpl({required this.apiClient});
@override
Future<Either<Failure, GenericApiModel<List<PlacePrediction>>>> getPlacePredictionsAsInput(
String input) async {
final url = Uri.parse(
'https://maps.googleapis.com/maps/api/place/autocomplete/json?input=$input&region=SA&key=$GOOGLE_API_KEY',
);
GenericApiModel<List<PlacePrediction>>? apiResponse;
Failure? failure;
try {
await apiClient.get(
url.toString(),
isExternal: true,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
final list = response['predictions'];
final predictionsList = list
.map((item) =>
PlacePrediction.fromJson(item as Map<String, dynamic>))
.toList()
.cast<PlacePrediction>();
apiResponse = GenericApiModel<List<PlacePrediction>>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: null,
data: predictionsList,
);
} catch (e) {
failure = DataParsingFailure(e.toString());
}
},
);
if (failure != null) return Left(failure!);
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
return Right(apiResponse!);
} catch (e) {
return Left(UnknownFailure(e.toString()));
}
}
@override
Future<Either<Failure, GenericApiModel<GeocodeResponse>>> getGeoCodeFromLatLng(double lat, double lng) async {
final url = Uri.parse(
'https://maps.googleapis.com/maps/api/geocode/json?latlng=$lat,$lng&key=$GOOGLE_API_KEY',
);
GenericApiModel<GeocodeResponse>? apiResponse;
Failure? failure;
try {
await apiClient.get(
url.toString(),
isExternal: true,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
final predictionsList = GeocodeResponse.fromJson(response);
apiResponse = GenericApiModel<GeocodeResponse>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: null,
data: predictionsList,
);
} catch (e) {
failure = DataParsingFailure(e.toString());
}
},
);
if (failure != null) return Left(failure!);
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
return Right(apiResponse!);
} catch (e) {
return Left(UnknownFailure(e.toString()));
}
}
@override
Future<Either<Failure, GenericApiModel<PlaceDetails>>> getPlaceDetailsOfSelectedPrediction(String placeId) async {
final url = Uri.parse(
'https://maps.googleapis.com/maps/api/place/details/json'
'?place_id=$placeId&fields=geometry&key=$GOOGLE_API_KEY',
);
GenericApiModel<PlaceDetails>? apiResponse;
Failure? failure;
try {
await apiClient.get(
url.toString(),
isExternal: true,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
final predictionsList = PlaceDetails.fromJson(response);
apiResponse = GenericApiModel<PlaceDetails>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: null,
data: predictionsList,
);
} catch (e) {
failure = DataParsingFailure(e.toString());
}
},
);
if (failure != null) return Left(failure!);
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
return Right(apiResponse!);
} catch (e) {
return Left(UnknownFailure(e.toString()));
}
}
}

@ -0,0 +1,116 @@
import 'dart:async';
import 'package:flutter/foundation.dart' show ChangeNotifier;
import 'package:flutter/material.dart';
import 'package:google_maps_flutter_platform_interface/src/types/camera.dart';
import 'package:hmg_patient_app_new/features/location/GeocodeResponse.dart';
import 'package:hmg_patient_app_new/features/location/PlaceDetails.dart';
import 'package:hmg_patient_app_new/features/location/location_repo.dart';
import 'package:hmg_patient_app_new/services/error_handler_service.dart';
import 'package:huawei_map/huawei_map.dart' as HMSCameraServices;
import 'package:google_maps_flutter/google_maps_flutter.dart' as GMSMapServices;
import 'PlacePrediction.dart';
class LocationViewModel extends ChangeNotifier {
final LocationRepo locationRepo;
final ErrorHandlerService errorHandlerService;
LocationViewModel({required this.locationRepo, required this.errorHandlerService});
List<PlacePrediction> predictions = [];
PlacePrediction? selectedPrediction;
bool isPredictionLoading = false;
GeocodeResponse? geocodeResponse;
PlaceDetails? placeDetails;
Location? mapCapturedLocation;
FutureOr<void> getPlacesPrediction(String input) async {
predictions = [];
isPredictionLoading= true;
final result = await locationRepo.getPlacePredictionsAsInput(input);
result.fold(
(failure) {
errorHandlerService.handleError(failure: failure);
},
(apiModel) {
predictions = apiModel.data??[];
},
);
isPredictionLoading = false;
notifyListeners();
}
FutureOr<void> getPlaceEncodedData(double? lat, double? lng) async {
geocodeResponse = null;
final result = await locationRepo.getGeoCodeFromLatLng(lat!, lng!);
result.fold(
(failure) {
errorHandlerService.handleError(failure: failure);
},
(apiModel) {
print("Geocode Response: ${apiModel.data}");
geocodeResponse = apiModel.data;
},
);
notifyListeners();
}
FutureOr<void> getPlaceDetails(String placeID) async {
placeDetails = null;
final result = await locationRepo.getPlaceDetailsOfSelectedPrediction(placeID);
result.fold(
(failure) {
errorHandlerService.handleError(failure: failure);
},
(apiModel) {
placeDetails = apiModel.data;
},
);
notifyListeners();
}
handleGMSMapCameraMoved(GMSMapServices.CameraPosition value) {
mapCapturedLocation = Location(lat: value.target.latitude, lng: value.target.longitude);
}
handleHMSMapCameraMoved(HMSCameraServices.CameraPosition value) {
mapCapturedLocation = Location(lat: value.target.lat, lng: value.target.lng);
}
handleOnCameraIdle(){
if(mapCapturedLocation != null) {
getPlaceEncodedData(mapCapturedLocation!.lat, mapCapturedLocation!.lng);
}
}
void updateSearchQuery(String? value) {
if(value == null || value.isEmpty){
predictions = [];
return;
}
getPlacesPrediction(value);
}
void flushSearchPredictions() {
predictions = [];
mapCapturedLocation= null;
placeDetails= null;
geocodeResponse= null;
selectedPrediction= null;
notifyListeners();
}
FutureOr<void> selectPlacePrediction(PlacePrediction placePrediction) async{
selectedPrediction= placePrediction;
await getPlaceDetails(placePrediction.placeID);
}
}

@ -122,7 +122,7 @@ class AppointmentViaRegionViewmodel extends ChangeNotifier {
}
void handleLastStepForDentalAndLaser() {
//todo handle the routing here
//todo_section handle the routing here
navigationService.pop();
navigationService.push(
CustomPageRoute(

@ -0,0 +1,92 @@
class AskDocRequestType {
String? setupID;
int? parameterGroup;
int? parameterType;
int? parameterCode;
String? description;
String? descriptionN;
String? alias;
String? aliasN;
String? prefix;
String? suffix;
String? isColorCodingRequired;
String? backColor;
String? foreColor;
bool? isBuiltIn;
bool? isActive;
int? createdBy;
String? createdOn;
String? editedBy;
String? editedOn;
String? rowVer;
AskDocRequestType(
{this.setupID,
this.parameterGroup,
this.parameterType,
this.parameterCode,
this.description,
this.descriptionN,
this.alias,
this.aliasN,
this.prefix,
this.suffix,
this.isColorCodingRequired,
this.backColor,
this.foreColor,
this.isBuiltIn,
this.isActive,
this.createdBy,
this.createdOn,
this.editedBy,
this.editedOn,
this.rowVer});
AskDocRequestType.fromJson(Map<String, dynamic> json) {
setupID = json['SetupID'];
parameterGroup = json['ParameterGroup'];
parameterType = json['ParameterType'];
parameterCode = json['ParameterCode'];
description = json['Description'];
descriptionN = json['DescriptionN'];
alias = json['Alias'];
aliasN = json['AliasN'];
prefix = json['Prefix'];
suffix = json['Suffix'];
isColorCodingRequired = json['IsColorCodingRequired'];
backColor = json['BackColor'];
foreColor = json['ForeColor'];
isBuiltIn = json['IsBuiltIn'];
isActive = json['IsActive'];
createdBy = json['CreatedBy'];
createdOn = json['CreatedOn'];
editedBy = json['EditedBy'];
editedOn = json['EditedOn'];
rowVer = json['RowVer'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['SetupID'] = this.setupID;
data['ParameterGroup'] = this.parameterGroup;
data['ParameterType'] = this.parameterType;
data['ParameterCode'] = this.parameterCode;
data['Description'] = this.description;
data['DescriptionN'] = this.descriptionN;
data['Alias'] = this.alias;
data['AliasN'] = this.aliasN;
data['Prefix'] = this.prefix;
data['Suffix'] = this.suffix;
data['IsColorCodingRequired'] = this.isColorCodingRequired;
data['BackColor'] = this.backColor;
data['ForeColor'] = this.foreColor;
data['IsBuiltIn'] = this.isBuiltIn;
data['IsActive'] = this.isActive;
data['CreatedBy'] = this.createdBy;
data['CreatedOn'] = this.createdOn;
data['EditedBy'] = this.editedBy;
data['EditedOn'] = this.editedOn;
data['RowVer'] = this.rowVer;
return data;
}
}

@ -101,4 +101,6 @@ class HospitalsModel {
data['UsingInDoctorApp'] = this.usingInDoctorApp;
return data;
}
}

@ -55,8 +55,8 @@ class PatientAppointmentHistoryResponseModel {
bool? isMedicalReportRequested;
bool? isOnlineCheckedIN;
String? latitude;
dynamic listHISGetContactLensPerscription;
dynamic listHISGetGlassPerscription;
List<ListHISGetContactLensPrescription>? listHISGetContactLensPrescription;
List<ListHISGetGlassPrescription>? listHISGetGlassPrescription;
String? longitude;
dynamic nextAction;
dynamic noOfPatientsRate;
@ -130,8 +130,8 @@ class PatientAppointmentHistoryResponseModel {
this.isMedicalReportRequested,
this.isOnlineCheckedIN,
this.latitude,
this.listHISGetContactLensPerscription,
this.listHISGetGlassPerscription,
this.listHISGetContactLensPrescription,
this.listHISGetGlassPrescription,
this.longitude,
this.nextAction,
this.noOfPatientsRate,
@ -207,8 +207,18 @@ class PatientAppointmentHistoryResponseModel {
isMedicalReportRequested = json['IsMedicalReportRequested'];
isOnlineCheckedIN = json['IsOnlineCheckedIN'];
latitude = json['Latitude'];
listHISGetContactLensPerscription = json['List_HIS_GetContactLensPerscription'];
listHISGetGlassPerscription = json['List_HIS_GetGlassPerscription'];
if (json['List_HIS_GetContactLensPerscription'] != null) {
listHISGetContactLensPrescription = <ListHISGetContactLensPrescription>[];
json['List_HIS_GetContactLensPerscription'].forEach((v) {
listHISGetContactLensPrescription!.add(ListHISGetContactLensPrescription.fromJson(v));
});
}
if (json['List_HIS_GetGlassPerscription'] != null) {
listHISGetGlassPrescription = <ListHISGetGlassPrescription>[];
json['List_HIS_GetGlassPerscription'].forEach((v) {
listHISGetGlassPrescription!.add(ListHISGetGlassPrescription.fromJson(v));
});
}
longitude = json['Longitude'];
nextAction = json['NextAction'];
noOfPatientsRate = json['NoOfPatientsRate'];
@ -285,8 +295,8 @@ class PatientAppointmentHistoryResponseModel {
data['IsMedicalReportRequested'] = this.isMedicalReportRequested;
data['IsOnlineCheckedIN'] = this.isOnlineCheckedIN;
data['Latitude'] = this.latitude;
data['List_HIS_GetContactLensPerscription'] = this.listHISGetContactLensPerscription;
data['List_HIS_GetGlassPerscription'] = this.listHISGetGlassPerscription;
// data['List_HIS_GetContactLensPerscription'] = this.listHISGetContactLensPerscription;
// data['List_HIS_GetGlassPerscription'] = this.listHISGetGlassPerscription;
data['Longitude'] = this.longitude;
data['NextAction'] = this.nextAction;
data['NoOfPatientsRate'] = this.noOfPatientsRate;
@ -301,6 +311,196 @@ class PatientAppointmentHistoryResponseModel {
}
}
class ListHISGetContactLensPrescription {
String? setupId;
int? projectId;
int? patientType;
int? patientId;
int? encounterType;
int? encounterNo;
int? oDOS;
dynamic brand;
dynamic baseCurve;
dynamic power;
dynamic diameter;
dynamic oZ;
dynamic cT;
dynamic blend;
String? remarks;
int? status;
bool? isActive;
String? createdOn;
ListHISGetContactLensPrescription(
{this.setupId,
this.projectId,
this.patientType,
this.patientId,
this.encounterType,
this.encounterNo,
this.oDOS,
this.brand,
this.baseCurve,
this.power,
this.diameter,
this.oZ,
this.cT,
this.blend,
this.remarks,
this.status,
this.isActive,
this.createdOn});
ListHISGetContactLensPrescription.fromJson(Map<String, dynamic> json) {
setupId = json['SetupId'];
projectId = json['ProjectId'];
patientType = json['PatientType'];
patientId = json['PatientId'];
encounterType = json['EncounterType'];
encounterNo = json['EncounterNo'];
oDOS = json['OD_OS'];
brand = json['Brand'];
baseCurve = json['BaseCurve'];
power = json['Power'];
diameter = json['Diameter'];
oZ = json['OZ'];
cT = json['CT'];
blend = json['Blend'];
remarks = json['Remarks'];
status = json['Status'];
isActive = json['IsActive'];
createdOn = json['CreatedOn'];
}
Map<String?, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['SetupId'] = this.setupId;
data['ProjectId'] = this.projectId;
data['PatientType'] = this.patientType;
data['PatientId'] = this.patientId;
data['EncounterType'] = this.encounterType;
data['EncounterNo'] = this.encounterNo;
data['OD_OS'] = this.oDOS;
data['Brand'] = this.brand;
data['BaseCurve'] = this.baseCurve;
data['Power'] = this.power;
data['Diameter'] = this.diameter;
data['OZ'] = this.oZ;
data['CT'] = this.cT;
data['Blend'] = this.blend;
data['Remarks'] = this.remarks;
data['Status'] = this.status;
data['IsActive'] = this.isActive;
data['CreatedOn'] = this.createdOn;
return data;
}
}
class ListHISGetGlassPrescription {
dynamic projectID;
String? setupID;
dynamic patientId;
dynamic encounterType;
dynamic encounterNo;
String? visionType;
double? rightEyeSpherical;
dynamic rightEyeCylinder;
dynamic rightEyeAxis;
dynamic rightEyePrism;
dynamic rightEyeVA;
String? rightEyeRemarks;
dynamic leftEyeSpherical;
dynamic leftEyeCylinder;
dynamic leftEyeAxis;
dynamic leftEyePrism;
dynamic leftEyeVA;
String? leftEyeRemarks;
dynamic pD;
dynamic bVD;
dynamic status;
bool? isActive;
String? createdOn;
ListHISGetGlassPrescription(
{this.projectID,
this.setupID,
this.patientId,
this.encounterType,
this.encounterNo,
this.visionType,
this.rightEyeSpherical,
this.rightEyeCylinder,
this.rightEyeAxis,
this.rightEyePrism,
this.rightEyeVA,
this.rightEyeRemarks,
this.leftEyeSpherical,
this.leftEyeCylinder,
this.leftEyeAxis,
this.leftEyePrism,
this.leftEyeVA,
this.leftEyeRemarks,
this.pD,
this.bVD,
this.status,
this.isActive,
this.createdOn});
ListHISGetGlassPrescription.fromJson(Map<String, dynamic> json) {
projectID = json['ProjectID'];
setupID = json['SetupID'];
patientId = json['PatientId'];
encounterType = json['EncounterType'];
encounterNo = json['EncounterNo'];
visionType = json['VisionType'];
rightEyeSpherical = json['RightEyeSpherical'];
rightEyeCylinder = json['RightEyeCylinder'];
rightEyeAxis = json['RightEyeAxis'];
rightEyePrism = json['RightEyePrism'];
rightEyeVA = json['RightEyeVA'];
rightEyeRemarks = json['RightEyeRemarks'];
leftEyeSpherical = json['LeftEyeSpherical'];
leftEyeCylinder = json['LeftEyeCylinder'];
leftEyeAxis = json['LeftEyeAxis'];
leftEyePrism = json['LeftEyePrism'];
leftEyeVA = json['LeftEyeVA'];
leftEyeRemarks = json['LeftEyeRemarks'];
pD = json['PD'];
bVD = json['BVD'];
status = json['Status'];
isActive = json['IsActive'];
createdOn = json['CreatedOn'];
}
Map<String?, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['ProjectID'] = this.projectID;
data['SetupID'] = this.setupID;
data['PatientId'] = this.patientId;
data['EncounterType'] = this.encounterType;
data['EncounterNo'] = this.encounterNo;
data['VisionType'] = this.visionType;
data['RightEyeSpherical'] = this.rightEyeSpherical;
data['RightEyeCylinder'] = this.rightEyeCylinder;
data['RightEyeAxis'] = this.rightEyeAxis;
data['RightEyePrism'] = this.rightEyePrism;
data['RightEyeVA'] = this.rightEyeVA;
data['RightEyeRemarks'] = this.rightEyeRemarks;
data['LeftEyeSpherical'] = this.leftEyeSpherical;
data['LeftEyeCylinder'] = this.leftEyeCylinder;
data['LeftEyeAxis'] = this.leftEyeAxis;
data['LeftEyePrism'] = this.leftEyePrism;
data['LeftEyeVA'] = this.leftEyeVA;
data['LeftEyeRemarks'] = this.leftEyeRemarks;
data['PD'] = this.pD;
data['BVD'] = this.bVD;
data['Status'] = this.status;
data['IsActive'] = this.isActive;
data['CreatedOn'] = this.createdOn;
return data;
}
}
class PatientAppointmentList {
String? filterName = "";
List<PatientAppointmentHistoryResponseModel>? patientDoctorAppointmentList = [];

@ -6,7 +6,9 @@ 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/common_models/generic_api_model.dart';
import 'package:hmg_patient_app_new/core/exceptions/api_failure.dart';
import 'package:hmg_patient_app_new/core/utils/date_util.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/ask_doctor_request_type_response_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/get_tamara_installments_details_response_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/hospital_model.dart' show HospitalsModel;
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/patient_appointment_history_response_model.dart';
@ -14,7 +16,7 @@ import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/
import 'package:hmg_patient_app_new/services/logger_service.dart';
abstract class MyAppointmentsRepo {
Future<Either<Failure, GenericApiModel<List<PatientAppointmentHistoryResponseModel>>>> getPatientAppointments({required bool isActiveAppointment, required bool isArrivedAppointments});
Future<Either<Failure, GenericApiModel<List<PatientAppointmentHistoryResponseModel>>>> getPatientAppointments({required bool isActiveAppointment, required bool isArrivedAppointments, bool isForEyeMeasurement = false});
Future<Either<Failure, GenericApiModel<PatientAppointmentShareResponseModel>>> getPatientShareAppointment(
{required int projectID, required int clinicID, required String appointmentNo, required bool isLiveCareAppointment});
@ -49,6 +51,13 @@ abstract class MyAppointmentsRepo {
Future<Either<Failure, GenericApiModel<GetTamaraInstallmentsDetailsResponseModel>>> getTamaraInstallmentsDetails();
Future<Either<Failure, GenericApiModel<dynamic>>> getActiveAppointmentsCount();
Future<Either<Failure, GenericApiModel<List<AskDocRequestType>>>> getAskDoctorRequestTypes();
Future<Either<Failure, GenericApiModel<dynamic>>> isDoctorAvailable({required int projectID, required int clinicID, required int doctorID});
Future<Either<Failure, GenericApiModel<dynamic>>> sendAskDocCallRequest(
{required PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel, required String requestType, required String remarks, required String userMobileNumber});
}
class MyAppointmentsRepoImp implements MyAppointmentsRepo {
@ -58,13 +67,14 @@ class MyAppointmentsRepoImp implements MyAppointmentsRepo {
MyAppointmentsRepoImp({required this.loggerService, required this.apiClient});
@override
Future<Either<Failure, GenericApiModel<List<PatientAppointmentHistoryResponseModel>>>> getPatientAppointments({required bool isActiveAppointment, required bool isArrivedAppointments}) async {
Future<Either<Failure, GenericApiModel<List<PatientAppointmentHistoryResponseModel>>>> getPatientAppointments({required bool isActiveAppointment, required bool isArrivedAppointments, bool isForEyeMeasurement = false}) async {
Map<String, dynamic> mapDevice = {
"IsActiveAppointment": isActiveAppointment,
"IsComingFromCOC": false,
"isForUpcomming": false,
"IsForMedicalReport": false,
"IsForArrived": isArrivedAppointments,
"IsIrisPrescription": isForEyeMeasurement
};
try {
@ -618,4 +628,122 @@ class MyAppointmentsRepoImp implements MyAppointmentsRepo {
return Left(UnknownFailure(e.toString()));
}
}
@override
Future<Either<Failure, GenericApiModel>> isDoctorAvailable({required int projectID, required int clinicID, required int doctorID}) async {
Map<String, dynamic> mapDevice = {"isDentalAllowedBackend": false, "DoctorID": doctorID, "ProjectID": projectID, "ClinicID": clinicID};
try {
GenericApiModel<dynamic>? apiResponse;
Failure? failure;
await apiClient.post(
IS_DOCTOR_AVAILABLE_BY_CALENDAR_SCHEDULE,
body: mapDevice,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
final isDoctorAvailable = response['IsDoctorAvailable'];
apiResponse = GenericApiModel<dynamic>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: null,
data: isDoctorAvailable,
);
} catch (e) {
failure = DataParsingFailure(e.toString());
}
},
);
if (failure != null) return Left(failure!);
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
return Right(apiResponse!);
} catch (e) {
return Left(UnknownFailure(e.toString()));
}
}
@override
Future<Either<Failure, GenericApiModel<List<AskDocRequestType>>>> getAskDoctorRequestTypes() async {
Map<String, dynamic> mapDevice = {};
try {
GenericApiModel<List<AskDocRequestType>>? apiResponse;
Failure? failure;
await apiClient.post(GET_CALL_REQUEST_TYPE, onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
}, onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
final list = response['ListReqTypes'];
final askDoctorRequestTypesList = list.map((item) => AskDocRequestType.fromJson(item as Map<String, dynamic>)).toList().cast<AskDocRequestType>();
apiResponse = GenericApiModel<List<AskDocRequestType>>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: null,
data: askDoctorRequestTypesList,
);
} catch (e) {
failure = DataParsingFailure(e.toString());
}
}, body: mapDevice);
if (failure != null) return Left(failure!);
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
return Right(apiResponse!);
} catch (e) {
return Left(UnknownFailure(e.toString()));
}
}
@override
Future<Either<Failure, GenericApiModel>> sendAskDocCallRequest(
{required PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel, required String requestType, required String remarks, required String userMobileNumber}) async {
Map<String, dynamic> body = {};
body['ProjectID'] = patientAppointmentHistoryResponseModel.projectID;
body['SetupID'] = patientAppointmentHistoryResponseModel.setupID;
body['DoctorID'] = patientAppointmentHistoryResponseModel.doctorID;
body['PatientMobileNumber'] = userMobileNumber;
body['IsMessageSent'] = false;
body['RequestDate'] = DateUtil.convertDateToString(DateTime.now());
body['RequestTime'] = DateUtil.convertDateToString(DateTime.now());
body['Remarks'] = remarks;
body['Status'] = 2; // 4 for testing only.."cancelled status insert" else should be changed to 1 in live version
body['CreatedBy'] = 102;
body['CreatedOn'] = DateUtil.convertDateToString(DateTime.now());
body['EditedBy'] = 102;
body['EditedOn'] = DateUtil.convertDateToString(DateTime.now());
body['isDentalAllowedBackend'] = false;
body['AppointmentNo'] = patientAppointmentHistoryResponseModel.appointmentNo;
body['ClinicID'] = patientAppointmentHistoryResponseModel.clinicID;
body['QuestionType'] = num.parse(requestType);
body['RequestType'] = num.parse(requestType);
body['RequestTypeID'] = num.parse(requestType);
try {
GenericApiModel<dynamic>? apiResponse;
Failure? failure;
await apiClient.post(INSERT_CALL_INFO, onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
}, onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
apiResponse = GenericApiModel<dynamic>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: null,
data: true,
);
} catch (e) {
failure = DataParsingFailure(e.toString());
}
}, body: body);
if (failure != null) return Left(failure!);
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
return Right(apiResponse!);
} catch (e) {
return Left(UnknownFailure(e.toString()));
}
}
}

@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/utils/date_util.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/appointemnet_filters.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/ask_doctor_request_type_response_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/get_tamara_installments_details_response_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/patient_appointment_history_response_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/patient_appointment_share_response_model.dart';
@ -13,6 +14,8 @@ class MyAppointmentsViewModel extends ChangeNotifier {
int selectedTabIndex = 0;
int previouslySelectedTab = -1;
int eyeMeasurementsTabSelectedIndex = 0;
MyAppointmentsRepo myAppointmentsRepo;
ErrorHandlerService errorHandlerService;
AppState appState;
@ -26,6 +29,8 @@ class MyAppointmentsViewModel extends ChangeNotifier {
bool isAppointmentDataToBeLoaded = true;
bool isEyeMeasurementsAppointmentsLoading = false;
List<AppointmentListingFilters> availableFilters = [];
List<AppointmentListingFilters>? selectedFilter = [];
bool isDateFilterSelected = false;
@ -42,6 +47,10 @@ class MyAppointmentsViewModel extends ChangeNotifier {
List<PatientAppointmentHistoryResponseModel> patientMyDoctorsList = [];
List<PatientAppointmentHistoryResponseModel> patientEyeMeasurementsAppointmentsHistoryList = [];
List<AskDocRequestType> askDoctorRequestTypeList = [];
PatientAppointmentShareResponseModel? patientAppointmentShareResponseModel;
GetTamaraInstallmentsDetailsResponseModel? getTamaraInstallmentsDetailsResponseModel;
@ -57,18 +66,25 @@ class MyAppointmentsViewModel extends ChangeNotifier {
notifyListeners();
}
void onEyeMeasurementsTabChanged(int index) {
eyeMeasurementsTabSelectedIndex = index;
notifyListeners();
}
initAppointmentsViewModel() {
if (isAppointmentDataToBeLoaded) {
patientAppointmentsHistoryList.clear();
patientUpcomingAppointmentsHistoryList.clear();
patientArrivedAppointmentsHistoryList.clear();
patientTimelineAppointmentsList.clear();
patientEyeMeasurementsAppointmentsHistoryList.clear();
isMyAppointmentsLoading = true;
isTimeLineAppointmentsLoading = true;
patientMyDoctorsList.clear();
}
isTamaraDetailsLoading = true;
isAppointmentPatientShareLoading = true;
isEyeMeasurementsAppointmentsLoading = true;
notifyListeners();
}
@ -105,6 +121,11 @@ class MyAppointmentsViewModel extends ChangeNotifier {
notifyListeners();
}
setIsEyeMeasurementsAppointmentsLoading(bool val) {
isEyeMeasurementsAppointmentsLoading = val;
notifyListeners();
}
setAppointmentReminder(bool value, PatientAppointmentHistoryResponseModel item) {
int index = patientAppointmentsHistoryList.indexOf(item);
if (index != -1) {
@ -113,6 +134,29 @@ class MyAppointmentsViewModel extends ChangeNotifier {
}
}
Future<void> getPatientEyeMeasurementAppointments({Function(dynamic)? onSuccess, Function(String)? onError}) async {
patientEyeMeasurementsAppointmentsHistoryList.clear();
notifyListeners();
final result = await myAppointmentsRepo.getPatientAppointments(isActiveAppointment: false, isArrivedAppointments: true, isForEyeMeasurement: true);
result.fold(
(failure) async => await errorHandlerService.handleError(failure: failure),
(apiResponse) {
if (apiResponse.messageStatus == 2) {
// dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
} else if (apiResponse.messageStatus == 1) {
patientEyeMeasurementsAppointmentsHistoryList = apiResponse.data!;
isEyeMeasurementsAppointmentsLoading = false;
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse);
}
}
},
);
}
Future<void> getPatientAppointments(bool isActiveAppointment, bool isArrivedAppointments, {Function(dynamic)? onSuccess, Function(String)? onError}) async {
if (!isAppointmentDataToBeLoaded) return;
@ -568,4 +612,71 @@ class MyAppointmentsViewModel extends ChangeNotifier {
},
);
}
Future<void> isDoctorAvailable({required int projectID, required int doctorId, required int clinicId, Function(dynamic)? onSuccess, Function(String)? onError}) async {
final result = await myAppointmentsRepo.isDoctorAvailable(projectID: projectID, doctorID: doctorId, clinicID: clinicId);
result.fold(
(failure) async => await errorHandlerService.handleError(failure: failure),
(apiResponse) {
if (apiResponse.messageStatus == 2) {
onError!(apiResponse.errorMessage!);
} else if (apiResponse.messageStatus == 1) {
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse.data);
}
}
},
);
}
Future<void> getAskDoctorRequestTypes({Function(dynamic)? onSuccess, Function(String)? onError}) async {
final result = await myAppointmentsRepo.getAskDoctorRequestTypes();
result.fold(
(failure) async => await errorHandlerService.handleError(failure: failure),
(apiResponse) {
if (apiResponse.messageStatus == 2) {
onError!(apiResponse.errorMessage!);
} else if (apiResponse.messageStatus == 1) {
askDoctorRequestTypeList = apiResponse.data!;
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse.data);
}
}
},
);
}
Future<void> sendAskDocCallRequest({
required PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel,
required String requestType,
required String remarks,
required String userMobileNumber,
Function(dynamic)? onSuccess,
Function(String)? onError,
}) async {
final result = await myAppointmentsRepo.sendAskDocCallRequest(
patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel, requestType: requestType, remarks: remarks, userMobileNumber: userMobileNumber);
result.fold(
// (failure) async => await errorHandlerService.handleError(failure: failure),
(failure) async {
await errorHandlerService.handleError(failure: failure);
if (onError != null) onError(failure.message);
},
(apiResponse) {
if (apiResponse.messageStatus == 2) {
onError!(apiResponse.errorMessage!);
} else if (apiResponse.messageStatus == 1) {
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse.data);
}
}
},
);
}
}

@ -4,6 +4,7 @@ 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/common_models/generic_api_model.dart';
import 'package:hmg_patient_app_new/core/exceptions/api_failure.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/get_tamara_installments_details_response_model.dart';
import 'package:hmg_patient_app_new/features/payfort/models/apple_pay_request_insert_model.dart';
import 'package:hmg_patient_app_new/features/payfort/models/payfort_check_payment_status_response_model.dart';
import 'package:hmg_patient_app_new/features/payfort/models/payfort_project_details_resp_model.dart';
@ -25,6 +26,8 @@ abstract class PayfortRepo {
Future<Either<Failure, GenericApiModel<dynamic>>> updateTamaraRequestStatus(
{required String responseMessage, required String status, required String clientRequestID, required String tamaraOrderID});
Future<Either<Failure, GenericApiModel<GetTamaraInstallmentsDetailsResponseModel>>> getTamaraInstallmentsDetails();
}
class PayfortRepoImp implements PayfortRepo {
@ -250,4 +253,42 @@ class PayfortRepoImp implements PayfortRepo {
return Left(UnknownFailure(e.toString()));
}
}
@override
Future<Either<Failure, GenericApiModel<GetTamaraInstallmentsDetailsResponseModel>>> getTamaraInstallmentsDetails() async {
try {
GenericApiModel<GetTamaraInstallmentsDetailsResponseModel>? apiResponse;
Failure? failure;
await apiClient.get(
ApiConsts.GET_TAMARA_INSTALLMENTS_URL,
isExternal: true,
isAllowAny: true,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
final list = response;
final tamaraInstallmentsList = GetTamaraInstallmentsDetailsResponseModel.fromJson(list.first);
apiResponse = GenericApiModel<GetTamaraInstallmentsDetailsResponseModel>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: null,
data: tamaraInstallmentsList,
);
} catch (e) {
failure = DataParsingFailure(e.toString());
}
},
);
if (failure != null) return Left(failure!);
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
return Right(apiResponse!);
} catch (e) {
return Left(UnknownFailure(e.toString()));
}
}
}

@ -1,6 +1,9 @@
import 'dart:developer';
import 'package:amazon_payfort/amazon_payfort.dart';
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/api_consts.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/get_tamara_installments_details_response_model.dart';
import 'package:hmg_patient_app_new/features/payfort/models/apple_pay_request_insert_model.dart';
import 'package:hmg_patient_app_new/features/payfort/models/payfort_check_payment_status_response_model.dart';
import 'package:hmg_patient_app_new/features/payfort/models/payfort_project_details_resp_model.dart';
@ -21,6 +24,9 @@ class PayfortViewModel extends ChangeNotifier {
late AmazonPayfort _payfort;
final NetworkInfo _info = NetworkInfo();
GetTamaraInstallmentsDetailsResponseModel? getTamaraInstallmentsDetailsResponseModel;
bool isTamaraDetailsLoading = false;
PayfortViewModel({required this.payfortRepo, required this.errorHandlerService});
setIsApplePayConfigurationLoading(bool value) {
@ -36,7 +42,8 @@ class PayfortViewModel extends ChangeNotifier {
notifyListeners();
}
Future<void> getPayfortConfigurations({int? serviceId, int? projectId, int integrationId = 2, Function(dynamic)? onSuccess, Function(String)? onError}) async {
Future<void> getPayfortConfigurations(
{int? serviceId, int? projectId, int integrationId = 2, Function(dynamic)? onSuccess, Function(String)? onError}) async {
final result = await payfortRepo.getPayfortConfigurations(serviceId: serviceId, projectId: projectId, integrationId: integrationId);
result.fold(
@ -56,7 +63,8 @@ class PayfortViewModel extends ChangeNotifier {
);
}
Future<void> applePayRequestInsert({required ApplePayInsertRequest applePayInsertRequest, Function(dynamic)? onSuccess, Function(String)? onError}) async {
Future<void> applePayRequestInsert(
{required ApplePayInsertRequest applePayInsertRequest, Function(dynamic)? onSuccess, Function(String)? onError}) async {
final result = await payfortRepo.applePayRequestInsert(applePayInsertRequest: applePayInsertRequest);
result.fold(
@ -102,7 +110,7 @@ class PayfortViewModel extends ChangeNotifier {
onError!(failure.message);
},
(apiResponse) {
print(apiResponse.data);
log(apiResponse.data);
if (onSuccess != null) {
onSuccess(apiResponse);
}
@ -112,15 +120,21 @@ class PayfortViewModel extends ChangeNotifier {
}
Future<void> updateTamaraRequestStatus(
{required String responseMessage, required String status, required String clientRequestID, required String tamaraOrderID, Function(dynamic)? onSuccess, Function(String)? onError}) async {
final result = await payfortRepo.updateTamaraRequestStatus(responseMessage: responseMessage, status: status, clientRequestID: clientRequestID, tamaraOrderID: tamaraOrderID);
{required String responseMessage,
required String status,
required String clientRequestID,
required String tamaraOrderID,
Function(dynamic)? onSuccess,
Function(String)? onError}) async {
final result = await payfortRepo.updateTamaraRequestStatus(
responseMessage: responseMessage, status: status, clientRequestID: clientRequestID, tamaraOrderID: tamaraOrderID);
result.fold(
(failure) async {
onError!(failure.message);
},
(apiResponse) {
print(apiResponse.data);
log(apiResponse.data);
if (onSuccess != null) {
onSuccess(apiResponse);
}
@ -134,7 +148,7 @@ class PayfortViewModel extends ChangeNotifier {
String? applePayShaType,
String? applePayShaRequestPhrase,
}) async {
var sdkTokenResponse;
SdkTokenResponse? sdkTokenResponse;
try {
String? deviceId = await _payfort.getDeviceId();
@ -168,7 +182,7 @@ class PayfortViewModel extends ChangeNotifier {
},
);
} catch (e) {
print("Error here: ${e.toString()}");
log("Error here: ${e.toString()}");
}
return sdkTokenResponse;
}
@ -234,7 +248,8 @@ class PayfortViewModel extends ChangeNotifier {
}
}
Future<void> markAppointmentAsTamaraPaid({required int projectID, required int appointmentNo, Function(dynamic)? onSuccess, Function(String)? onError}) async {
Future<void> markAppointmentAsTamaraPaid(
{required int projectID, required int appointmentNo, Function(dynamic)? onSuccess, Function(String)? onError}) async {
final result = await payfortRepo.markAppointmentAsTamaraPaid(projectID: projectID, appointmentNo: appointmentNo);
result.fold(
@ -242,11 +257,28 @@ class PayfortViewModel extends ChangeNotifier {
onError!(failure.message);
},
(apiResponse) {
print(apiResponse.data);
log(apiResponse.data);
if (onSuccess != null) {
onSuccess(apiResponse);
}
},
);
}
Future<void> getTamaraInstallmentsDetails({Function(dynamic)? onSuccess, Function(String)? onError}) async {
final result = await payfortRepo.getTamaraInstallmentsDetails();
result.fold(
(failure) async => await errorHandlerService.handleError(failure: failure),
(apiResponse) {
getTamaraInstallmentsDetailsResponseModel = apiResponse.data!;
isTamaraDetailsLoading = false;
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse);
}
},
);
}
}

@ -9,11 +9,12 @@ import 'package:hmg_patient_app_new/features/radiology/models/resp_models/patien
import 'package:hmg_patient_app_new/services/logger_service.dart';
abstract class RadiologyRepo {
Future<Either<Failure, GenericApiModel<List<PatientRadiologyResponseModel>>>> getPatientRadiologyOrders({required String patientId});
Future<Either<Failure, GenericApiModel<List<PatientRadiologyResponseModel>>>> getPatientRadiologyOrders();
Future<Either<Failure, GenericApiModel<String>>> getRadiologyImage({required PatientRadiologyResponseModel patientRadiologyResponseModel});
Future<Either<Failure, GenericApiModel<String>>> getRadiologyReportPDF({required PatientRadiologyResponseModel patientRadiologyResponseModel, required AuthenticatedUser authenticatedUser});
Future<Either<Failure, GenericApiModel<String>>> getRadiologyReportPDF(
{required PatientRadiologyResponseModel patientRadiologyResponseModel, required AuthenticatedUser authenticatedUser});
}
class RadiologyRepoImp implements RadiologyRepo {
@ -23,7 +24,7 @@ class RadiologyRepoImp implements RadiologyRepo {
RadiologyRepoImp({required this.loggerService, required this.apiClient});
@override
Future<Either<Failure, GenericApiModel<List<PatientRadiologyResponseModel>>>> getPatientRadiologyOrders({required String patientId}) async {
Future<Either<Failure, GenericApiModel<List<PatientRadiologyResponseModel>>>> getPatientRadiologyOrders() async {
Map<String, dynamic> mapDevice = {};
try {
@ -40,10 +41,16 @@ class RadiologyRepoImp implements RadiologyRepo {
try {
if (response['FinalRadiologyList'] != null && response['FinalRadiologyList'].length != 0) {
final list = response['FinalRadiologyList'];
radOrders = list.map((item) => PatientRadiologyResponseModel.fromJson(item as Map<String, dynamic>)).toList().cast<PatientRadiologyResponseModel>();
radOrders = list
.map((item) => PatientRadiologyResponseModel.fromJson(item as Map<String, dynamic>))
.toList()
.cast<PatientRadiologyResponseModel>();
} else {
final list = response['FinalRadiologyListAPI'];
radOrders = list.map((item) => PatientRadiologyResponseModel.fromJson(item as Map<String, dynamic>)).toList().cast<PatientRadiologyResponseModel>();
radOrders = list
.map((item) => PatientRadiologyResponseModel.fromJson(item as Map<String, dynamic>))
.toList()
.cast<PatientRadiologyResponseModel>();
}
apiResponse = GenericApiModel<List<PatientRadiologyResponseModel>>(
@ -107,7 +114,8 @@ class RadiologyRepoImp implements RadiologyRepo {
}
@override
Future<Either<Failure, GenericApiModel<String>>> getRadiologyReportPDF({required PatientRadiologyResponseModel patientRadiologyResponseModel, required AuthenticatedUser authenticatedUser}) async {
Future<Either<Failure, GenericApiModel<String>>> getRadiologyReportPDF(
{required PatientRadiologyResponseModel patientRadiologyResponseModel, required AuthenticatedUser authenticatedUser}) async {
Map<String, dynamic> mapDevice = {
"InvoiceNo": Utils.isVidaPlusProject(patientRadiologyResponseModel.projectID!) ? 0 : patientRadiologyResponseModel.invoiceNo,
"InvoiceNo_VP": Utils.isVidaPlusProject(patientRadiologyResponseModel.projectID!) ? patientRadiologyResponseModel.invoiceNo : 0,
@ -121,7 +129,8 @@ class RadiologyRepoImp implements RadiologyRepo {
'ClinicName': patientRadiologyResponseModel.clinicDescription,
'DateofBirth': authenticatedUser.dateofBirth,
'DoctorName': patientRadiologyResponseModel.doctorName,
'OrderDate': '${patientRadiologyResponseModel.orderDate!.year}-${patientRadiologyResponseModel.orderDate!.month}-${patientRadiologyResponseModel.orderDate!.day}',
'OrderDate':
'${patientRadiologyResponseModel.orderDate!.year}-${patientRadiologyResponseModel.orderDate!.month}-${patientRadiologyResponseModel.orderDate!.day}',
'PatientIditificationNum': authenticatedUser.patientIdentificationNo,
'PatientMobileNumber': authenticatedUser.mobileNumber,
'PatientName': "${authenticatedUser.firstName!} ${authenticatedUser.lastName!}",

@ -24,7 +24,7 @@ class RadiologyViewModel extends ChangeNotifier {
RadiologyViewModel({required this.radiologyRepo, required this.errorHandlerService});
initRadiologyProvider() {
initRadiologyViewModel() {
patientRadiologyOrders.clear();
filteredRadiologyOrders.clear();
isRadiologyOrdersLoading = true;
@ -35,7 +35,7 @@ class RadiologyViewModel extends ChangeNotifier {
}
Future<void> getPatientRadiologyOrders({Function(dynamic)? onSuccess, Function(String)? onError}) async {
final result = await radiologyRepo.getPatientRadiologyOrders(patientId: "1231755");
final result = await radiologyRepo.getPatientRadiologyOrders();
result.fold(
(failure) async => await errorHandlerService.handleError(failure: failure),
@ -57,7 +57,8 @@ class RadiologyViewModel extends ChangeNotifier {
);
}
Future<void> getRadiologyImage({required PatientRadiologyResponseModel patientRadiologyResponseModel, Function(dynamic)? onSuccess, Function(String)? onError}) async {
Future<void> getRadiologyImage(
{required PatientRadiologyResponseModel patientRadiologyResponseModel, Function(dynamic)? onSuccess, Function(String)? onError}) async {
final result = await radiologyRepo.getRadiologyImage(patientRadiologyResponseModel: patientRadiologyResponseModel);
result.fold(
@ -77,8 +78,12 @@ class RadiologyViewModel extends ChangeNotifier {
}
Future<void> getRadiologyPDF(
{required PatientRadiologyResponseModel patientRadiologyResponseModel, required AuthenticatedUser authenticatedUser, Function(dynamic)? onSuccess, Function(String)? onError}) async {
final result = await radiologyRepo.getRadiologyReportPDF(patientRadiologyResponseModel: patientRadiologyResponseModel, authenticatedUser: authenticatedUser);
{required PatientRadiologyResponseModel patientRadiologyResponseModel,
required AuthenticatedUser authenticatedUser,
Function(dynamic)? onSuccess,
Function(String)? onError}) async {
final result =
await radiologyRepo.getRadiologyReportPDF(patientRadiologyResponseModel: patientRadiologyResponseModel, authenticatedUser: authenticatedUser);
result.fold(
(failure) async => await errorHandlerService.handleError(

@ -0,0 +1,115 @@
// Dart model for the "AncillaryOrderList" structure
// Uses DateUtil.convertStringToDate and DateUtil.dateToDotNetString from your project to parse/serialize .NET-style dates.
import 'package:hmg_patient_app_new/core/utils/date_util.dart';
class AncillaryOrderListModel {
List<AncillaryOrderGroup>? ancillaryOrderList;
AncillaryOrderListModel({this.ancillaryOrderList});
factory AncillaryOrderListModel.fromJson(Map<String, dynamic> json) => AncillaryOrderListModel(
ancillaryOrderList: json['AncillaryOrderList'] != null
? List<AncillaryOrderGroup>.from(
(json['AncillaryOrderList'] as List).map(
(x) => AncillaryOrderGroup.fromJson(x as Map<String, dynamic>),
),
)
: null,
);
}
class AncillaryOrderGroup {
List<AncillaryOrderItem>? ancillaryOrderList;
dynamic errCode;
String? message;
int? patientID;
String? patientName;
int? patientType;
int? projectID;
String? projectName;
String? setupID;
int? statusCode;
AncillaryOrderGroup({
this.ancillaryOrderList,
this.errCode,
this.message,
this.patientID,
this.patientName,
this.patientType,
this.projectID,
this.projectName,
this.setupID,
this.statusCode,
});
factory AncillaryOrderGroup.fromJson(Map<String, dynamic> json) => AncillaryOrderGroup(
ancillaryOrderList: json['AncillaryOrderList'] != null
? List<AncillaryOrderItem>.from(
(json['AncillaryOrderList'] as List).map(
(x) => AncillaryOrderItem.fromJson(x as Map<String, dynamic>),
),
)
: null,
errCode: json['ErrCode'],
message: json['Message'] as String?,
patientID: json['PatientID'] as int?,
patientName: json['PatientName'] as String?,
patientType: json['PatientType'] as int?,
projectID: json['ProjectID'] as int?,
projectName: json['ProjectName'] as String?,
setupID: json['SetupID'] as String?,
statusCode: json['StatusCode'] as int?,
);
}
class AncillaryOrderItem {
dynamic ancillaryProcedureListModels;
DateTime? appointmentDate;
int? appointmentNo;
int? clinicID;
String? clinicName;
int? doctorID;
String? doctorName;
int? invoiceNo;
bool? isCheckInAllow;
bool? isQueued;
DateTime? orderDate;
int? orderNo;
String? projectName; // Added from parent AncillaryOrderGroup
int? projectID; // Added from parent AncillaryOrderGroup
AncillaryOrderItem({
this.ancillaryProcedureListModels,
this.appointmentDate,
this.appointmentNo,
this.clinicID,
this.clinicName,
this.doctorID,
this.doctorName,
this.invoiceNo,
this.isCheckInAllow,
this.isQueued,
this.orderDate,
this.orderNo,
this.projectName,
this.projectID,
});
factory AncillaryOrderItem.fromJson(Map<String, dynamic> json, {String? projectName, int? projectID}) => AncillaryOrderItem(
ancillaryProcedureListModels: json['AncillaryProcedureListModels'],
appointmentDate: DateUtil.convertStringToDate(json['AppointmentDate']),
appointmentNo: json['AppointmentNo'] as int?,
clinicID: json['ClinicID'] as int?,
clinicName: json['ClinicName'] as String?,
doctorID: json['DoctorID'] as int?,
doctorName: json['DoctorName'] as String?,
invoiceNo: json['Invoiceno'] as int?,
isCheckInAllow: json['IsCheckInAllow'] as bool?,
isQueued: json['IsQueued'] as bool?,
orderDate: DateUtil.convertStringToDate(json['OrderDate']),
orderNo: json['OrderNo'] as int?,
projectName: projectName,
projectID: projectID,
);
}

@ -0,0 +1,221 @@
// Dart model classes for "AncillaryOrderProcList"
// Generated for user: faizatflutter
// Uses DateUtil.convertStringToDate for parsing .NET-style dates (same approach as your PatientRadiologyResponseModel)
import 'package:hmg_patient_app_new/core/utils/date_util.dart';
class AncillaryOrderProcListModel {
List<AncillaryOrderProcedureItem>? ancillaryOrderProcList;
AncillaryOrderProcListModel({this.ancillaryOrderProcList});
factory AncillaryOrderProcListModel.fromJson(Map<String, dynamic> json) => AncillaryOrderProcListModel(
ancillaryOrderProcList: json['AncillaryOrderProcList'] != null
? List<AncillaryOrderProcedureItem>.from(
(json['AncillaryOrderProcList'] as List).map(
(x) => AncillaryOrderProcedureItem.fromJson(x as Map<String, dynamic>),
),
)
: null,
);
}
class AncillaryOrderProcedureItem {
List<AncillaryOrderProcDetail>? ancillaryOrderProcDetailsList;
DateTime? appointmentDate;
int? appointmentNo;
int? clinicID;
String? clinicName;
int? companyID;
String? companyName;
int? doctorID;
String? doctorName;
dynamic errCode;
int? groupID;
String? insurancePolicyNo;
String? message;
String? patientCardID;
int? patientID;
String? patientName;
int? patientType;
int? policyID;
String? policyName;
int? projectID;
String? setupID;
int? statusCode;
int? subCategoryID;
String? subPolicyNo;
AncillaryOrderProcedureItem({
this.ancillaryOrderProcDetailsList,
this.appointmentDate,
this.appointmentNo,
this.clinicID,
this.clinicName,
this.companyID,
this.companyName,
this.doctorID,
this.doctorName,
this.errCode,
this.groupID,
this.insurancePolicyNo,
this.message,
this.patientCardID,
this.patientID,
this.patientName,
this.patientType,
this.policyID,
this.policyName,
this.projectID,
this.setupID,
this.statusCode,
this.subCategoryID,
this.subPolicyNo,
});
factory AncillaryOrderProcedureItem.fromJson(Map<String, dynamic> json) => AncillaryOrderProcedureItem(
ancillaryOrderProcDetailsList: json['AncillaryOrderProcDetailsList'] != null
? List<AncillaryOrderProcDetail>.from(
(json['AncillaryOrderProcDetailsList'] as List).map(
(x) => AncillaryOrderProcDetail.fromJson(x as Map<String, dynamic>),
),
)
: null,
appointmentDate: DateUtil.convertStringToDate(json['AppointmentDate']),
appointmentNo: json['AppointmentNo'] as int?,
clinicID: json['ClinicID'] as int?,
clinicName: json['ClinicName'] as String?,
companyID: json['CompanyID'] as int?,
companyName: json['CompanyName'] as String?,
doctorID: json['DoctorID'] as int?,
doctorName: json['DoctorName'] as String?,
errCode: json['ErrCode'],
groupID: json['GroupID'] as int?,
insurancePolicyNo: json['InsurancePolicyNo'] as String?,
message: json['Message'] as String?,
patientCardID: json['PatientCardID'] as String?,
patientID: json['PatientID'] as int?,
patientName: json['PatientName'] as String?,
patientType: json['PatientType'] as int?,
policyID: json['PolicyID'] as int?,
policyName: json['PolicyName'] as String?,
projectID: json['ProjectID'] as int?,
setupID: json['SetupID'] as String?,
statusCode: json['StatusCode'] as int?,
subCategoryID: json['SubCategoryID'] as int?,
subPolicyNo: json['SubPolicyNo'] as String?,
);
}
class AncillaryOrderProcDetail {
int? approvalLineItemNo;
int? approvalNo;
String? approvalStatus;
int? approvalStatusID;
num? companyShare;
num? companyShareWithTax;
num? companyTaxAmount;
num? discountAmount;
int? discountCategory;
String? discountType;
num? discountTypeValue;
bool? isApprovalCreated;
bool? isApprovalRequired;
dynamic isCheckInAllow;
bool? isCovered;
bool? isLab;
DateTime? orderDate;
int? orderLineItemNo;
int? orderNo;
int? partnerID;
num? partnerShare;
String? partnerShareType;
num? patientShare;
num? patientShareWithTax;
num? patientTaxAmount;
num? procPrice;
int? procedureCategoryID;
String? procedureCategoryName;
String? procedureID;
String? procedureName;
num? taxAmount;
num? taxPct;
AncillaryOrderProcDetail({
this.approvalLineItemNo,
this.approvalNo,
this.approvalStatus,
this.approvalStatusID,
this.companyShare,
this.companyShareWithTax,
this.companyTaxAmount,
this.discountAmount,
this.discountCategory,
this.discountType,
this.discountTypeValue,
this.isApprovalCreated,
this.isApprovalRequired,
this.isCheckInAllow,
this.isCovered,
this.isLab,
this.orderDate,
this.orderLineItemNo,
this.orderNo,
this.partnerID,
this.partnerShare,
this.partnerShareType,
this.patientShare,
this.patientShareWithTax,
this.patientTaxAmount,
this.procPrice,
this.procedureCategoryID,
this.procedureCategoryName,
this.procedureID,
this.procedureName,
this.taxAmount,
this.taxPct,
});
factory AncillaryOrderProcDetail.fromJson(Map<String, dynamic> json) => AncillaryOrderProcDetail(
approvalLineItemNo: json['ApprovalLineItemNo'] as int?,
approvalNo: json['ApprovalNo'] as int?,
approvalStatus: json['ApprovalStatus'] as String?,
approvalStatusID: json['ApprovalStatusID'] as int?,
companyShare: _toNum(json['CompanyShare']),
companyShareWithTax: _toNum(json['CompanyShareWithTax']),
companyTaxAmount: _toNum(json['CompanyTaxAmount']),
discountAmount: _toNum(json['DiscountAmount']),
discountCategory: json['DiscountCategory'] as int?,
discountType: json['DiscountType'] as String?,
discountTypeValue: _toNum(json['DiscountTypeValue']),
isApprovalCreated: json['IsApprovalCreated'] as bool?,
isApprovalRequired: json['IsApprovalRequired'] as bool?,
isCheckInAllow: json['IsCheckInAllow'],
isCovered: json['IsCovered'] as bool?,
isLab: json['IsLab'] as bool?,
orderDate: DateUtil.convertStringToDate(json['OrderDate']),
orderLineItemNo: json['OrderLineItemNo'] as int?,
orderNo: json['OrderNo'] as int?,
partnerID: json['PartnerID'] as int?,
partnerShare: _toNum(json['PartnerShare']),
partnerShareType: json['PartnerShareType'] as String?,
patientShare: _toNum(json['PatientShare']),
patientShareWithTax: _toNum(json['PatientShareWithTax']),
patientTaxAmount: _toNum(json['PatientTaxAmount']),
procPrice: _toNum(json['ProcPrice']),
procedureCategoryID: json['ProcedureCategoryID'] as int?,
procedureCategoryName: json['ProcedureCategoryName'] as String?,
procedureID: json['ProcedureID'] as String?,
procedureName: json['ProcedureName'] as String?,
taxAmount: _toNum(json['TaxAmount']),
taxPct: _toNum(json['TaxPct']),
);
}
// Helper to safely parse numeric fields that may be int/double/string/null
num? _toNum(dynamic v) {
if (v == null) return null;
if (v is num) return v;
if (v is String) return num.tryParse(v);
return null;
}

@ -0,0 +1,379 @@
import 'package:dartz/dartz.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/common_models/generic_api_model.dart';
import 'package:hmg_patient_app_new/core/exceptions/api_failure.dart';
import 'package:hmg_patient_app_new/features/todo_section/models/resp_models/ancillary_order_list_response_model.dart';
import 'package:hmg_patient_app_new/features/todo_section/models/resp_models/ancillary_order_procedures_detail_response_model.dart';
import 'package:hmg_patient_app_new/services/logger_service.dart';
abstract class TodoSectionRepo {
Future<Either<Failure, GenericApiModel<List<AncillaryOrderItem>>>> getOnlineAncillaryOrderList();
Future<Either<Failure, GenericApiModel<List<AncillaryOrderProcedureItem>>>> getOnlineAncillaryOrderDetailsProceduresList({
required int appointmentNoVida,
required int orderNo,
required int projectID,
});
Future<Either<Failure, dynamic>> checkPaymentStatus({required String transID});
Future<Either<Failure, dynamic>> createAdvancePayment({
required int projectID,
required double paymentAmount,
required String paymentReference,
required String paymentMethodName,
required int patientTypeID,
required String patientName,
required int patientID,
required String setupID,
required bool isAncillaryOrder,
});
Future<Either<Failure, dynamic>> addAdvancedNumberRequest({
required String advanceNumber,
required String paymentReference,
required int appointmentID,
required int patientID,
required int patientTypeID,
required int patientOutSA,
});
Future<Either<Failure, dynamic>> autoGenerateAncillaryOrdersInvoice({
required int orderNo,
required int projectID,
required int appointmentNo,
required List<dynamic> selectedProcedures,
required int languageID,
});
Future<Either<Failure, dynamic>> applePayInsertRequest({required dynamic applePayInsertRequest});
}
class TodoSectionRepoImp implements TodoSectionRepo {
final ApiClient apiClient;
final LoggerService loggerService;
TodoSectionRepoImp({required this.loggerService, required this.apiClient});
@override
Future<Either<Failure, GenericApiModel<List<AncillaryOrderItem>>>> getOnlineAncillaryOrderList() async {
Map<String, dynamic> mapDevice = {};
try {
GenericApiModel<List<AncillaryOrderItem>>? apiResponse;
Failure? failure;
await apiClient.post(
ApiConsts.getOnlineAncillaryOrderList,
body: mapDevice,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
List<AncillaryOrderItem> ancillaryOrders = [];
// Parse the nested structure
if (response['AncillaryOrderList'] != null && response['AncillaryOrderList'] is List) {
final groupsList = response['AncillaryOrderList'] as List;
// Iterate through each group
for (var group in groupsList) {
if (group is Map<String, dynamic> && group['AncillaryOrderList'] != null) {
final ordersList = group['AncillaryOrderList'] as List;
final projectName = group['ProjectName'] as String?;
final projectID = group['ProjectID'] as int?;
// Parse each order item in the group
for (var orderJson in ordersList) {
if (orderJson is Map<String, dynamic>) {
ancillaryOrders.add(AncillaryOrderItem.fromJson(orderJson, projectName: projectName, projectID: projectID));
}
}
}
}
}
apiResponse = GenericApiModel<List<AncillaryOrderItem>>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: errorMessage,
data: ancillaryOrders,
);
} catch (e) {
loggerService.logInfo("Error parsing ancillary orders: ${e.toString()}");
failure = DataParsingFailure(e.toString());
}
},
);
if (failure != null) return Left(failure!);
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
return Right(apiResponse!);
} catch (e) {
loggerService.logError("Unknown error in getOnlineAncillaryOrderList: ${e.toString()}");
return Left(UnknownFailure(e.toString()));
}
}
@override
Future<Either<Failure, GenericApiModel<List<AncillaryOrderProcedureItem>>>> getOnlineAncillaryOrderDetailsProceduresList({
required int appointmentNoVida,
required int orderNo,
required int projectID,
}) async {
Map<String, dynamic> mapDevice = {
'AppointmentNo_Vida': appointmentNoVida,
'OrderNo': orderNo,
'ProjectID': projectID,
};
try {
GenericApiModel<List<AncillaryOrderProcedureItem>>? apiResponse;
Failure? failure;
await apiClient.post(
ApiConsts.getOnlineAncillaryOrderProcList,
body: mapDevice,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
List<AncillaryOrderProcedureItem> ancillaryOrdersProcedures = [];
// Parse the flat array structure (NOT nested like AncillaryOrderList)
if (response['AncillaryOrderProcList'] != null && response['AncillaryOrderProcList'] is List) {
final procList = response['AncillaryOrderProcList'] as List;
// Parse each procedure item directly
for (var procJson in procList) {
if (procJson is Map<String, dynamic>) {
ancillaryOrdersProcedures.add(AncillaryOrderProcedureItem.fromJson(procJson));
}
}
}
apiResponse = GenericApiModel<List<AncillaryOrderProcedureItem>>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: errorMessage,
data: ancillaryOrdersProcedures,
);
} catch (e) {
loggerService.logError("Error parsing ancillary Procedures: ${e.toString()}");
failure = DataParsingFailure(e.toString());
}
},
);
if (failure != null) return Left(failure!);
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
return Right(apiResponse!);
} catch (e) {
loggerService.logError("Unknown error in getOnlineAncillaryOrderDetailsProceduresList: ${e.toString()}");
return Left(UnknownFailure(e.toString()));
}
}
@override
Future<Either<Failure, dynamic>> checkPaymentStatus({required String transID}) async {
Map<String, dynamic> mapDevice = {'ClientRequestID': transID};
try {
dynamic apiResponse;
Failure? failure;
await apiClient.post(
ApiConsts.getRequestStatusByRequestID,
body: mapDevice,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
apiResponse = response;
},
);
if (failure != null) return Left(failure!);
return Right(apiResponse);
} catch (e) {
loggerService.logError("Unknown error in checkPaymentStatus: ${e.toString()}");
return Left(UnknownFailure(e.toString()));
}
}
@override
Future<Either<Failure, dynamic>> createAdvancePayment({
required int projectID,
required double paymentAmount,
required String paymentReference,
required String paymentMethodName,
required int patientTypeID,
required String patientName,
required int patientID,
required String setupID,
required bool isAncillaryOrder,
}) async {
// //VersionID (number)
// // Channel (number)
// // IPAdress (string)
// // generalid (string)
// // LanguageID (number)
// // Latitude (number)
// // Longitude (number)
// // DeviceTypeID (number)
// // PatientType (number)
// // PatientTypeID (number)
// // PatientID (number)
// // PatientOutSA (number)
// // TokenID (string)
// // SessionID (string)
Map<String, dynamic> mapDevice = {
'CustName': patientName,
'CustID': patientID,
'SetupID': setupID,
'ProjectID': projectID,
'AccountID': patientID,
'PaymentAmount': paymentAmount,
'NationalityID': null,
'DepositorName': patientName,
'CreatedBy': 3,
'PaymentMethodName': paymentMethodName,
'PaymentReference': paymentReference,
'PaymentMethod': paymentMethodName,
'IsAncillaryOrder': isAncillaryOrder,
};
try {
dynamic apiResponse;
Failure? failure;
await apiClient.post(
ApiConsts.createAdvancePayments,
body: mapDevice,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
apiResponse = response;
},
);
if (failure != null) return Left(failure!);
return Right(apiResponse);
} catch (e) {
loggerService.logError("Unknown error in createAdvancePayment: ${e.toString()}");
return Left(UnknownFailure(e.toString()));
}
}
@override
Future<Either<Failure, dynamic>> addAdvancedNumberRequest({
required String advanceNumber,
required String paymentReference,
required int appointmentID,
required int patientID,
required int patientTypeID,
required int patientOutSA,
}) async {
Map<String, dynamic> mapDevice = {
'AdvanceNumber': advanceNumber,
'PaymentReference': paymentReference,
'AppointmentID': appointmentID,
'PatientID': patientID,
'PatientTypeID': patientTypeID,
'PatientOutSA': patientOutSA,
};
try {
dynamic apiResponse;
Failure? failure;
await apiClient.post(
ApiConsts.addAdvanceNumberRequest,
body: mapDevice,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
apiResponse = response;
},
);
if (failure != null) return Left(failure!);
return Right(apiResponse);
} catch (e) {
loggerService.logError("Unknown error in addAdvancedNumberRequest: ${e.toString()}");
return Left(UnknownFailure(e.toString()));
}
}
@override
Future<Either<Failure, dynamic>> autoGenerateAncillaryOrdersInvoice({
required int orderNo,
required int projectID,
required int appointmentNo,
required List<dynamic> selectedProcedures,
required int languageID,
}) async {
// Extract procedure IDs from selectedProcedures
List<String> procedureOrderIDs = [];
selectedProcedures.forEach((element) {
procedureOrderIDs.add(element["ProcedureID"].toString());
});
Map<String, dynamic> mapDevice = {
'LanguageID': languageID,
'RequestAncillaryOrderInvoice': [
{
'MemberID': 102,
'ProjectID': projectID,
'AppointmentNo': appointmentNo,
'OrderNo': orderNo,
'AncillaryOrderInvoiceProcList': selectedProcedures,
}
],
'ProcedureOrderIds': procedureOrderIDs,
};
try {
dynamic apiResponse;
Failure? failure;
await apiClient.post(
ApiConsts.autoGenerateAncillaryOrdersInvoice,
body: mapDevice,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
apiResponse = response;
},
);
if (failure != null) return Left(failure!);
return Right(apiResponse);
} catch (e) {
loggerService.logError("Unknown error in autoGenerateAncillaryOrdersInvoice: ${e.toString()}");
return Left(UnknownFailure(e.toString()));
}
}
@override
Future<Either<Failure, dynamic>> applePayInsertRequest({required dynamic applePayInsertRequest}) async {
Map<String, dynamic> mapDevice = {
'ApplePayInsertRequest': applePayInsertRequest,
};
try {
dynamic apiResponse;
Failure? failure;
await apiClient.post(
ApiConsts.applePayInsertRequest,
body: mapDevice,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
apiResponse = response;
},
);
if (failure != null) return Left(failure!);
return Right(apiResponse);
} catch (e) {
loggerService.logError("Unknown error in applePayInsertRequest: ${e.toString()}");
return Left(UnknownFailure(e.toString()));
}
}
}

@ -0,0 +1,242 @@
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/features/todo_section/models/resp_models/ancillary_order_list_response_model.dart';
import 'package:hmg_patient_app_new/features/todo_section/models/resp_models/ancillary_order_procedures_detail_response_model.dart';
import 'package:hmg_patient_app_new/features/todo_section/todo_section_repo.dart';
import 'package:hmg_patient_app_new/services/error_handler_service.dart';
class TodoSectionViewModel extends ChangeNotifier {
TodoSectionRepo todoSectionRepo;
ErrorHandlerService errorHandlerService;
TodoSectionViewModel({required this.todoSectionRepo, required this.errorHandlerService});
initializeTodoSectionViewModel() async {
patientAncillaryOrdersList.clear();
isAncillaryOrdersLoading = true;
isAncillaryDetailsProceduresLoading = true;
await getPatientOnlineAncillaryOrderList();
}
bool isAncillaryOrdersLoading = false;
bool isAncillaryDetailsProceduresLoading = false;
bool isProcessingPayment = false;
List<AncillaryOrderItem> patientAncillaryOrdersList = [];
List<AncillaryOrderProcedureItem> patientAncillaryOrderProceduresList = [];
void setProcessingPayment(bool value) {
isProcessingPayment = value;
notifyListeners();
}
Future<void> getPatientOnlineAncillaryOrderList({Function(dynamic)? onSuccess, Function(String)? onError}) async {
patientAncillaryOrdersList.clear();
isAncillaryOrdersLoading = true;
notifyListeners();
final result = await todoSectionRepo.getOnlineAncillaryOrderList();
result.fold(
(failure) async {
isAncillaryOrdersLoading = false;
await errorHandlerService.handleError(failure: failure);
},
(apiResponse) {
if (apiResponse.messageStatus == 2) {
// dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
} else if (apiResponse.messageStatus == 1) {
patientAncillaryOrdersList = apiResponse.data!;
isAncillaryOrdersLoading = false;
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse);
}
}
},
);
}
Future<void> getPatientOnlineAncillaryOrderDetailsProceduresList({
Function(dynamic)? onSuccess,
Function(String)? onError,
required int appointmentNoVida,
required int orderNo,
required int projectID,
}) async {
isAncillaryDetailsProceduresLoading = true;
notifyListeners();
final result = await todoSectionRepo.getOnlineAncillaryOrderDetailsProceduresList(
appointmentNoVida: appointmentNoVida,
orderNo: orderNo,
projectID: projectID,
);
result.fold(
(failure) async {
isAncillaryDetailsProceduresLoading = false;
await errorHandlerService.handleError(failure: failure);
},
(apiResponse) {
if (apiResponse.messageStatus == 2) {
// dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
} else if (apiResponse.messageStatus == 1) {
patientAncillaryOrderProceduresList = apiResponse.data!;
isAncillaryDetailsProceduresLoading = false;
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse);
}
}
},
);
}
Future<void> checkPaymentStatus({
required String transID,
Function(dynamic)? onSuccess,
Function(String)? onError,
}) async {
final result = await todoSectionRepo.checkPaymentStatus(transID: transID);
result.fold(
(failure) async {
await errorHandlerService.handleError(failure: failure);
if (onError != null) {
onError(failure.toString());
}
},
(response) {
if (onSuccess != null) {
onSuccess(response);
}
},
);
}
Future<void> createAdvancePayment({
required int projectID,
required double paymentAmount,
required String paymentReference,
required String paymentMethodName,
required int patientTypeID,
required String patientName,
required int patientID,
required String setupID,
required bool isAncillaryOrder,
Function(dynamic)? onSuccess,
Function(String)? onError,
}) async {
final result = await todoSectionRepo.createAdvancePayment(
projectID: projectID,
paymentAmount: paymentAmount,
paymentReference: paymentReference,
paymentMethodName: paymentMethodName,
patientTypeID: patientTypeID,
patientName: patientName,
patientID: patientID,
setupID: setupID,
isAncillaryOrder: isAncillaryOrder,
);
result.fold(
(failure) async {
await errorHandlerService.handleError(failure: failure);
if (onError != null) {
onError(failure.toString());
}
},
(response) {
if (onSuccess != null) {
onSuccess(response);
}
},
);
}
Future<void> addAdvancedNumberRequest({
required String advanceNumber,
required String paymentReference,
required int appointmentID,
required int patientID,
required int patientTypeID,
required int patientOutSA,
Function(dynamic)? onSuccess,
Function(String)? onError,
}) async {
final result = await todoSectionRepo.addAdvancedNumberRequest(
advanceNumber: advanceNumber,
paymentReference: paymentReference,
appointmentID: appointmentID,
patientID: patientID,
patientTypeID: patientTypeID,
patientOutSA: patientOutSA,
);
result.fold(
(failure) async {
await errorHandlerService.handleError(failure: failure);
if (onError != null) {
onError(failure.toString());
}
},
(response) {
if (onSuccess != null) {
onSuccess(response);
}
},
);
}
Future<void> autoGenerateAncillaryOrdersInvoice({
required int orderNo,
required int projectID,
required int appointmentNo,
required List<dynamic> selectedProcedures,
required int languageID,
Function(dynamic)? onSuccess,
Function(String)? onError,
}) async {
final result = await todoSectionRepo.autoGenerateAncillaryOrdersInvoice(
orderNo: orderNo,
projectID: projectID,
appointmentNo: appointmentNo,
selectedProcedures: selectedProcedures,
languageID: languageID,
);
result.fold(
(failure) async {
await errorHandlerService.handleError(failure: failure);
if (onError != null) {
onError(failure.toString());
}
},
(response) {
if (onSuccess != null) {
onSuccess(response);
}
},
);
}
Future<void> applePayInsertRequest({
required dynamic applePayInsertRequest,
Function(dynamic)? onSuccess,
Function(String)? onError,
}) async {
final result = await todoSectionRepo.applePayInsertRequest(applePayInsertRequest: applePayInsertRequest);
result.fold(
(failure) async {
await errorHandlerService.handleError(failure: failure);
if (onError != null) {
onError(failure.toString());
}
},
(response) {
if (onSuccess != null) {
onSuccess(response);
}
},
);
}
}

@ -10,6 +10,7 @@ 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/features/authentication/authentication_view_model.dart';
import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_view_model.dart';
import 'package:hmg_patient_app_new/features/contact_us/contact_us_view_model.dart';
import 'package:hmg_patient_app_new/features/doctor_filter/doctor_filter_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';
@ -17,6 +18,7 @@ import 'package:hmg_patient_app_new/features/immediate_livecare/immediate_liveca
import 'package:hmg_patient_app_new/features/insurance/insurance_view_model.dart';
import 'package:hmg_patient_app_new/features/lab/history/lab_history_viewmodel.dart';
import 'package:hmg_patient_app_new/features/lab/lab_view_model.dart';
import 'package:hmg_patient_app_new/features/location/location_view_model.dart';
import 'package:hmg_patient_app_new/features/medical_file/medical_file_view_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/appointment_via_region_viewmodel.dart';
import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart';
@ -24,6 +26,7 @@ import 'package:hmg_patient_app_new/features/payfort/payfort_view_model.dart';
import 'package:hmg_patient_app_new/features/prescriptions/prescriptions_view_model.dart';
import 'package:hmg_patient_app_new/features/profile_settings/profile_settings_view_model.dart';
import 'package:hmg_patient_app_new/features/radiology/radiology_view_model.dart';
import 'package:hmg_patient_app_new/features/todo_section/todo_section_view_model.dart';
import 'package:hmg_patient_app_new/routes/app_routes.dart';
import 'package:hmg_patient_app_new/services/logger_service.dart';
import 'package:hmg_patient_app_new/services/navigation_service.dart';
@ -129,6 +132,14 @@ void main() async {
),
ChangeNotifierProvider<EmergencyServicesViewModel>(
create: (_) => getIt.get<EmergencyServicesViewModel>(),
),ChangeNotifierProvider<LocationViewModel>(
create: (_) => getIt.get<LocationViewModel>(),
),
ChangeNotifierProvider<TodoSectionViewModel>(
create: (_) => getIt.get<TodoSectionViewModel>(),
),
ChangeNotifierProvider<ContactUsViewModel>(
create: (_) => getIt.get<ContactUsViewModel>(),
)
], child: MyApp()),
),

@ -20,6 +20,7 @@ import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/appointments/appointment_payment_page.dart';
import 'package:hmg_patient_app_new/presentation/appointments/widgets/appointment_checkin_bottom_sheet.dart';
import 'package:hmg_patient_app_new/presentation/appointments/widgets/appointment_doctor_card.dart';
import 'package:hmg_patient_app_new/presentation/appointments/widgets/ask_doctor_request_type_select.dart';
import 'package:hmg_patient_app_new/presentation/book_appointment/widgets/appointment_calendar.dart';
import 'package:hmg_patient_app_new/presentation/prescriptions/prescription_detail_page.dart';
import 'package:hmg_patient_app_new/presentation/prescriptions/prescriptions_list_page.dart';
@ -37,9 +38,9 @@ import '../../core/dependencies.dart';
import '../medical_file/widgets/medical_file_card.dart';
class AppointmentDetailsPage extends StatefulWidget {
AppointmentDetailsPage({super.key, required this.patientAppointmentHistoryResponseModel});
const AppointmentDetailsPage({super.key, required this.patientAppointmentHistoryResponseModel});
PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel;
final PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel;
@override
State<AppointmentDetailsPage> createState() => _AppointmentDetailsPageState();
@ -81,7 +82,35 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
children: [
AppointmentDoctorCard(
patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel,
onAskDoctorTap: () {},
onAskDoctorTap: () async {
LoaderBottomSheet.showLoader(loadingText: "Checking doctor availability...".needTranslation);
await myAppointmentsViewModel.isDoctorAvailable(
projectID: widget.patientAppointmentHistoryResponseModel.projectID,
doctorId: widget.patientAppointmentHistoryResponseModel.doctorID,
clinicId: widget.patientAppointmentHistoryResponseModel.clinicID,
onSuccess: (value) async {
if (value) {
print("Doctor is available");
await myAppointmentsViewModel.getAskDoctorRequestTypes(onSuccess: (val) {
LoaderBottomSheet.hideLoader();
showCommonBottomSheetWithoutHeight(
context,
title: LocaleKeys.askDoctor.tr(context: context),
child: AskDoctorRequestTypeSelect(
askDoctorRequestTypeList: myAppointmentsViewModel.askDoctorRequestTypeList,
myAppointmentsViewModel: myAppointmentsViewModel,
patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel,
),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
});
} else {
print("Doctor is not available");
}
});
},
onCancelTap: () async {
myAppointmentsViewModel.setIsAppointmentDataToBeLoaded(true);
LoaderBottomSheet.showLoader(loadingText: "Cancelling Appointment, Please Wait...".needTranslation);
@ -115,7 +144,7 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 20.h,
borderRadius: 20.r,
hasShadow: false,
),
child: Padding(
@ -138,7 +167,7 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
? Row(
children: [
Utils.buildSvgWithAssets(icon: AppAssets.livecare_clinic_icon, width: 40.h, height: 40.h),
SizedBox(width: 12.h),
SizedBox(width: 12.w),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
@ -155,7 +184,8 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
children: [
ClipRRect(
clipBehavior: Clip.hardEdge,
borderRadius: BorderRadius.circular(24),
borderRadius: BorderRadius.circular(24.r),
// Todo: what is this???? Api Key??? 😲
child: Image.network(
"https://maps.googleapis.com/maps/api/staticmap?center=${widget.patientAppointmentHistoryResponseModel.latitude},${widget.patientAppointmentHistoryResponseModel.longitude}&zoom=14&size=350x165&maptype=roadmap&markers=color:red%7C${widget.patientAppointmentHistoryResponseModel.latitude},${widget.patientAppointmentHistoryResponseModel.longitude}&key=AIzaSyB6TERnxIr0yJ3qG4ULBZbu0sAD4tGqtng",
fit: BoxFit.contain,
@ -173,19 +203,19 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
double.parse(widget.patientAppointmentHistoryResponseModel.longitude!),
widget.patientAppointmentHistoryResponseModel.projectName);
},
backgroundColor: AppColors.textColor.withOpacity(0.8),
borderColor:
AppointmentType.getNextActionButtonColor(widget.patientAppointmentHistoryResponseModel.nextAction)
.withOpacity(0.01),
backgroundColor: AppColors.textColor.withValues(alpha: 0.8),
borderColor: AppointmentType.getNextActionButtonColor(
widget.patientAppointmentHistoryResponseModel.nextAction)
.withValues(alpha: 0.01),
textColor: AppColors.whiteColor,
fontSize: 14.f,
fontWeight: FontWeight.w500,
borderRadius: 12.r,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
padding: EdgeInsets.symmetric(horizontal: 10.w),
height: 40.h,
icon: AppAssets.directions_icon,
iconColor: AppColors.whiteColor,
iconSize: 13.h,
iconSize: 14.h,
).paddingAll(12.h),
),
),
@ -199,7 +229,7 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 20.h,
borderRadius: 20.r,
hasShadow: false,
),
child: Row(
@ -211,12 +241,14 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
LocaleKeys.setReminder.tr(context: context).toText13(isBold: true),
"Notify me before the appointment".needTranslation.toText11(color: AppColors.textColorLight, weight: FontWeight.w500),
"Notify me before the appointment"
.needTranslation
.toText11(color: AppColors.textColorLight, weight: FontWeight.w500),
],
),
const Spacer(),
Switch(
activeColor: AppColors.successColor,
// activeThumbColor: AppColors.successColor,
activeTrackColor: AppColors.successColor.withValues(alpha: .15),
value: widget.patientAppointmentHistoryResponseModel.hasReminder!,
onChanged: (newValue) {
@ -237,8 +269,12 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
"Lab & Radiology".needTranslation.toText18(isBold: true),
SizedBox(height: 16.h),
GridView(
padding: EdgeInsets.zero,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2, crossAxisSpacing: 13.h, mainAxisSpacing: 13.h, childAspectRatio: 7 / 6),
crossAxisCount: isTablet || isFoldable ? 3 : 2,
crossAxisSpacing: 13.w,
mainAxisSpacing: 13.w,
),
physics: NeverScrollableScrollPhysics(),
shrinkWrap: true,
children: [
@ -247,19 +283,36 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
textColor: AppColors.blackColor,
backgroundColor: AppColors.whiteColor,
svgIcon: AppAssets.lab_result_icon,
iconSize: 40,
iconSize: 40.w,
isLargeText: true,
),
MedicalFileCard(
label: LocaleKeys.radiology.tr(context: context),
label: "Radiology Results".needTranslation,
textColor: AppColors.blackColor,
backgroundColor: AppColors.whiteColor,
svgIcon: AppAssets.radiology_icon,
iconSize: 40,
iconSize: 40.w,
isLargeText: true,
),
MedicalFileCard(
label: LocaleKeys.labResults.tr(context: context),
textColor: AppColors.blackColor,
backgroundColor: AppColors.whiteColor,
svgIcon: AppAssets.lab_result_icon,
iconSize: 40.w,
isLargeText: true,
),
MedicalFileCard(
label: "Radiology Results".needTranslation,
textColor: AppColors.blackColor,
backgroundColor: AppColors.whiteColor,
svgIcon: AppAssets.radiology_icon,
iconSize: 40.w,
isLargeText: true,
),
],
),
SizedBox(height: 16.h),
LocaleKeys.prescriptions.tr().toText18(isBold: true),
SizedBox(height: 16.h),
Consumer<PrescriptionsViewModel>(builder: (context, prescriptionVM, child) {
@ -268,153 +321,178 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
: Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: Colors.white,
borderRadius: 20.h,
borderRadius: 20.r,
),
child: Padding(
padding: EdgeInsets.all(16.h),
child: Column(
children: [
ListView.separated(
itemCount: prescriptionVM.prescriptionDetailsList.length,
shrinkWrap: true,
padding: const EdgeInsets.only(left: 0, right: 8),
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: [
SizedBox(
width: 150.h,
child: prescriptionVM.prescriptionDetailsList[index].itemDescription!
.toText12(isBold: true, maxLine: 1)),
SizedBox(
width: 150.h,
child:
"Prescribed By: ${widget.patientAppointmentHistoryResponseModel.doctorTitle} ${widget.patientAppointmentHistoryResponseModel.doctorNameObj}"
.needTranslation
.toText10(
weight: FontWeight.w500, color: AppColors.greyTextColor, letterSpacing: -0.4),
),
],
),
SizedBox(width: 68.h),
Transform.flip(
flipX: appState.isArabic(),
child: Utils.buildSvgWithAssets(
icon: AppAssets.forward_arrow_icon,
iconColor: AppColors.blackColor,
width: 18.h,
height: 13.h,
fit: BoxFit.contain,
),
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.w500,
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),
Row(
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,
// fontWeight: FontWeight.w500,
// 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: "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,
fontWeight: FontWeight.w500,
borderRadius: 12.h,
height: 40.h,
icon: AppAssets.requests,
iconColor: AppColors.primaryRedColor,
iconSize: 16.h,
),
},
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,
// fontWeight: FontWeight.w500,
// 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,
fontWeight: FontWeight.w500,
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,
fontWeight: FontWeight.w500,
borderRadius: 12.r,
height: 40.h,
icon: AppAssets.requests,
iconColor: AppColors.primaryRedColor,
iconSize: 16.h,
),
],
),
],
),
),
],
),
],
),
);
}),
],
),
],
).paddingSymmetrical(24.h, 24.h),
).paddingAll(24.w),
),
),
),
Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 24.h,
borderRadius: 24.r,
hasShadow: true,
),
child: SizedBox(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (widget.patientAppointmentHistoryResponseModel.nextAction == 15 || widget.patientAppointmentHistoryResponseModel.nextAction == 20)
if (widget.patientAppointmentHistoryResponseModel.nextAction == 15 ||
widget.patientAppointmentHistoryResponseModel.nextAction == 20)
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
@ -423,7 +501,9 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
children: [
"Amount before tax".needTranslation.toText18(isBold: true),
Utils.getPaymentAmountWithSymbol(
widget.patientAppointmentHistoryResponseModel.patientShare!.toString().toText16(isBold: true), AppColors.blackColor, 13,
widget.patientAppointmentHistoryResponseModel.patientShare!.toString().toText16(isBold: true),
AppColors.blackColor,
13,
isSaudiCurrency: true),
],
),
@ -432,8 +512,9 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child:
LocaleKeys.upcomingPaymentNow.tr(context: context).toText12(fontWeight: FontWeight.w500, color: AppColors.greyTextColor)),
child: LocaleKeys.upcomingPaymentNow
.tr(context: context)
.toText12(fontWeight: FontWeight.w500, color: AppColors.greyTextColor)),
"VAT 15%(${widget.patientAppointmentHistoryResponseModel.patientTaxAmount})"
.needTranslation
.toText14(isBold: true, color: AppColors.greyTextColor, letterSpacing: -2),
@ -470,10 +551,10 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
backgroundColor: AppColors.primaryRedColor,
borderColor: AppColors.primaryRedColor,
textColor: AppColors.whiteColor,
fontSize: 16,
fontSize: 16.f,
fontWeight: FontWeight.w500,
borderRadius: 12,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
borderRadius: 12.r,
padding: EdgeInsets.symmetric(horizontal: 10.w),
height: 50.h,
icon: AppAssets.add_icon,
iconColor: AppColors.whiteColor,
@ -486,13 +567,14 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
handleAppointmentNextAction(widget.patientAppointmentHistoryResponseModel.nextAction);
},
backgroundColor: AppointmentType.getNextActionButtonColor(widget.patientAppointmentHistoryResponseModel.nextAction),
borderColor: AppointmentType.getNextActionButtonColor(widget.patientAppointmentHistoryResponseModel.nextAction).withOpacity(0.01),
borderColor:
AppointmentType.getNextActionButtonColor(widget.patientAppointmentHistoryResponseModel.nextAction).withOpacity(0.01),
textColor: widget.patientAppointmentHistoryResponseModel.nextAction == 15 ? AppColors.textColor : AppColors.whiteColor,
fontSize: 16,
fontSize: 16.f,
fontWeight: FontWeight.w500,
borderRadius: 12,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 50.h,
borderRadius: 12.r,
padding: EdgeInsets.symmetric(horizontal: 10.w),
height: 56.h,
icon: AppointmentType.getNextActionIcon(widget.patientAppointmentHistoryResponseModel.nextAction),
iconColor: AppColors.whiteColor,
iconSize: 18.h,

@ -9,21 +9,19 @@ import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/cache_consts.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/features/payfort/models/apple_pay_request_insert_model.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/patient_appointment_history_response_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart';
import 'package:hmg_patient_app_new/features/payfort/models/apple_pay_request_insert_model.dart';
import 'package:hmg_patient_app_new/features/payfort/payfort_view_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/appointments/my_appointments_page.dart';
import 'package:hmg_patient_app_new/presentation/home/navigation_screen.dart';
import 'package:hmg_patient_app_new/presentation/insurance/insurance_home_page.dart';
import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
import 'package:hmg_patient_app_new/services/cache_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:hmg_patient_app_new/widgets/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart';
import 'package:hmg_patient_app_new/widgets/in_app_browser/InAppBrowser.dart';
@ -59,19 +57,20 @@ class _AppointmentPaymentPageState extends State<AppointmentPaymentPage> {
void initState() {
scheduleMicrotask(() {
payfortViewModel.initPayfortViewModel();
myAppointmentsViewModel.getTamaraInstallmentsDetails().then((val) {
if (myAppointmentsViewModel.patientAppointmentShareResponseModel!.patientShareWithTax! >= myAppointmentsViewModel.getTamaraInstallmentsDetailsResponseModel!.minLimit!.amount! &&
myAppointmentsViewModel.patientAppointmentShareResponseModel!.patientShareWithTax! <= myAppointmentsViewModel.getTamaraInstallmentsDetailsResponseModel!.maxLimit!.amount!) {
setState(() {
isShowTamara = true;
});
}
});
payfortViewModel.setIsApplePayConfigurationLoading(false);
myAppointmentsViewModel.getPatientShareAppointment(
widget.patientAppointmentHistoryResponseModel.projectID,
widget.patientAppointmentHistoryResponseModel.clinicID,
widget.patientAppointmentHistoryResponseModel.appointmentNo.toString(), widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment ?? false, onError: (err) {
widget.patientAppointmentHistoryResponseModel.appointmentNo.toString(), widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment ?? false, onSuccess: (val) {
myAppointmentsViewModel.getTamaraInstallmentsDetails().then((val) {
if (myAppointmentsViewModel.patientAppointmentShareResponseModel!.patientShareWithTax! >= myAppointmentsViewModel.getTamaraInstallmentsDetailsResponseModel!.minLimit!.amount! &&
myAppointmentsViewModel.patientAppointmentShareResponseModel!.patientShareWithTax! <= myAppointmentsViewModel.getTamaraInstallmentsDetailsResponseModel!.maxLimit!.amount!) {
setState(() {
isShowTamara = true;
});
}
});
}, onError: (err) {
Navigator.of(context).pop();
Navigator.of(context).pop();
});
@ -109,7 +108,8 @@ class _AppointmentPaymentPageState extends State<AppointmentPaymentPage> {
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Image.asset(AppAssets.mada, width: 72.h, height: 25.h).toShimmer2(isShow: myAppointmentsVM.isAppointmentPatientShareLoading),
Image.asset(AppAssets.mada, width: 72.h, height: 25.h)
.toShimmer2(isShow: myAppointmentsVM.isAppointmentPatientShareLoading),
SizedBox(height: 16.h),
"Mada".needTranslation.toText16(isBold: true).toShimmer2(isShow: myAppointmentsVM.isAppointmentPatientShareLoading),
],
@ -153,7 +153,10 @@ class _AppointmentPaymentPageState extends State<AppointmentPaymentPage> {
],
).toShimmer2(isShow: myAppointmentsVM.isAppointmentPatientShareLoading),
SizedBox(height: 16.h),
"Visa or Mastercard".needTranslation.toText16(isBold: true).toShimmer2(isShow: myAppointmentsVM.isAppointmentPatientShareLoading),
"Visa or Mastercard"
.needTranslation
.toText16(isBold: true)
.toShimmer2(isShow: myAppointmentsVM.isAppointmentPatientShareLoading),
],
),
SizedBox(width: 8.h),
@ -181,23 +184,27 @@ class _AppointmentPaymentPageState extends State<AppointmentPaymentPage> {
color: AppColors.whiteColor,
borderRadius: 20.h,
hasShadow: false,
),
child: Row(
mainAxisSize: MainAxisSize.max,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Image.asset(AppAssets.tamara_en, width: 72.h, height: 25.h).toShimmer2(isShow: myAppointmentsVM.isAppointmentPatientShareLoading),
SizedBox(height: 16.h),
"Tamara".needTranslation.toText16(isBold: true).toShimmer2(isShow: myAppointmentsVM.isAppointmentPatientShareLoading),
],
),
SizedBox(width: 8.h),
const Spacer(),
Transform.flip(
flipX: appState.isArabic(),
child: Utils.buildSvgWithAssets(
),
child: Row(
mainAxisSize: MainAxisSize.max,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Image.asset(AppAssets.tamara_en, width: 72.h, height: 25.h)
.toShimmer2(isShow: myAppointmentsVM.isAppointmentPatientShareLoading),
SizedBox(height: 16.h),
"Tamara"
.needTranslation
.toText16(isBold: true)
.toShimmer2(isShow: myAppointmentsVM.isAppointmentPatientShareLoading),
],
),
SizedBox(width: 8.h),
const Spacer(),
Transform.flip(
flipX: appState.isArabic(),
child: Utils.buildSvgWithAssets(
icon: AppAssets.forward_arrow_icon_small,
iconColor: AppColors.blackColor,
width: 18.h,
@ -243,7 +250,10 @@ class _AppointmentPaymentPageState extends State<AppointmentPaymentPage> {
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
"Insurance expired or inactive".needTranslation.toText14(color: AppColors.primaryRedColor, weight: FontWeight.w500).paddingSymmetrical(24.h, 0.h),
"Insurance expired or inactive"
.needTranslation
.toText14(color: AppColors.primaryRedColor, weight: FontWeight.w500)
.paddingSymmetrical(24.h, 0.h),
CustomButton(
text: LocaleKeys.updateInsurance.tr(context: context),
onPressed: () {
@ -256,7 +266,7 @@ class _AppointmentPaymentPageState extends State<AppointmentPaymentPage> {
backgroundColor: AppColors.primaryRedColor,
borderColor: AppColors.secondaryLightRedBorderColor,
textColor: AppColors.whiteColor,
fontSize: 10,
fontSize: 10.f,
fontWeight: FontWeight.w500,
borderRadius: 8,
padding: EdgeInsets.fromLTRB(15, 0, 15, 0),
@ -273,7 +283,10 @@ class _AppointmentPaymentPageState extends State<AppointmentPaymentPage> {
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
"Amount before tax".needTranslation.toText14(isBold: true),
Utils.getPaymentAmountWithSymbol(myAppointmentsVM.patientAppointmentShareResponseModel!.patientShare!.toString().toText16(isBold: true), AppColors.blackColor, 13,
Utils.getPaymentAmountWithSymbol(
myAppointmentsVM.patientAppointmentShareResponseModel!.patientShare!.toString().toText16(isBold: true),
AppColors.blackColor,
13,
isSaudiCurrency: true),
],
).paddingSymmetrical(24.h, 0.h),
@ -282,7 +295,9 @@ class _AppointmentPaymentPageState extends State<AppointmentPaymentPage> {
children: [
"VAT 15%".needTranslation.toText14(isBold: true, color: AppColors.greyTextColor),
Utils.getPaymentAmountWithSymbol(
myAppointmentsVM.patientAppointmentShareResponseModel!.patientTaxAmount!.toString().toText14(isBold: true, color: AppColors.greyTextColor),
myAppointmentsVM.patientAppointmentShareResponseModel!.patientTaxAmount!
.toString()
.toText14(isBold: true, color: AppColors.greyTextColor),
AppColors.greyTextColor,
13,
isSaudiCurrency: true),
@ -293,7 +308,10 @@ class _AppointmentPaymentPageState extends State<AppointmentPaymentPage> {
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
"".needTranslation.toText14(isBold: true),
Utils.getPaymentAmountWithSymbol(myAppointmentsVM.patientAppointmentShareResponseModel!.patientShareWithTax!.toString().toText24(isBold: true), AppColors.blackColor, 17,
Utils.getPaymentAmountWithSymbol(
myAppointmentsVM.patientAppointmentShareResponseModel!.patientShareWithTax!.toString().toText24(isBold: true),
AppColors.blackColor,
17,
isSaudiCurrency: true),
],
).paddingSymmetrical(24.h, 0.h),
@ -372,9 +390,11 @@ class _AppointmentPaymentPageState extends State<AppointmentPaymentPage> {
onSuccess: (apiResponse) async {
if (apiResponse.data["status"].toString().toLowerCase() == "success") {
tamaraOrderID = apiResponse.data["tamara_order_id"].toString();
await payfortViewModel.updateTamaraRequestStatus(responseMessage: "success", status: "14", clientRequestID: transID, tamaraOrderID: tamaraOrderID);
await payfortViewModel.updateTamaraRequestStatus(
responseMessage: "success", status: "14", clientRequestID: transID, tamaraOrderID: tamaraOrderID);
await payfortViewModel.markAppointmentAsTamaraPaid(
projectID: widget.patientAppointmentHistoryResponseModel.projectID, appointmentNo: widget.patientAppointmentHistoryResponseModel.appointmentNo);
projectID: widget.patientAppointmentHistoryResponseModel.projectID,
appointmentNo: widget.patientAppointmentHistoryResponseModel.appointmentNo);
await myAppointmentsViewModel.addAdvanceNumberRequest(
advanceNumber: "Tamara-Advance-0000",
paymentReference: tamaraOrderID,
@ -417,7 +437,8 @@ class _AppointmentPaymentPageState extends State<AppointmentPaymentPage> {
}
});
} else {
await payfortViewModel.updateTamaraRequestStatus(responseMessage: "Failed", status: "00", clientRequestID: transID, tamaraOrderID: tamaraOrderID);
await payfortViewModel.updateTamaraRequestStatus(
responseMessage: "Failed", status: "00", clientRequestID: transID, tamaraOrderID: tamaraOrderID);
LoaderBottomSheet.hideLoader();
showCommonBottomSheetWithoutHeight(
context,
@ -535,7 +556,9 @@ class _AppointmentPaymentPageState extends State<AppointmentPaymentPage> {
browser!,
widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment ?? false,
"2",
widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment! ? myAppointmentsViewModel.patientAppointmentShareResponseModel!.clinicID.toString() : "",
widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment!
? myAppointmentsViewModel.patientAppointmentShareResponseModel!.clinicID.toString()
: "",
context,
myAppointmentsViewModel.patientAppointmentShareResponseModel!.appointmentDate,
myAppointmentsViewModel.patientAppointmentShareResponseModel!.appointmentNo,
@ -546,7 +569,13 @@ class _AppointmentPaymentPageState extends State<AppointmentPaymentPage> {
startApplePay() async {
showCommonBottomSheet(context,
child: Utils.getLoadingWidget(), callBackFunc: (str) {}, title: "", height: ResponsiveExtension.screenHeight * 0.3, isCloseButtonVisible: false, isDismissible: false, isFullScreen: false);
child: Utils.getLoadingWidget(),
callBackFunc: (str) {},
title: "",
height: ResponsiveExtension.screenHeight * 0.3,
isCloseButtonVisible: false,
isDismissible: false,
isFullScreen: false);
transID = Utils.getAppointmentTransID(
widget.patientAppointmentHistoryResponseModel.projectID,
widget.patientAppointmentHistoryResponseModel.clinicID,
@ -556,7 +585,9 @@ class _AppointmentPaymentPageState extends State<AppointmentPaymentPage> {
ApplePayInsertRequest applePayInsertRequest = ApplePayInsertRequest();
await payfortViewModel.getPayfortConfigurations(
serviceId: ServiceTypeEnum.appointmentPayment.getIdFromServiceEnum(), projectId: widget.patientAppointmentHistoryResponseModel.projectID, integrationId: 2);
serviceId: ServiceTypeEnum.appointmentPayment.getIdFromServiceEnum(),
projectId: widget.patientAppointmentHistoryResponseModel.projectID,
integrationId: 2);
applePayInsertRequest.clientRequestID = transID;
applePayInsertRequest.clinicID = widget.patientAppointmentHistoryResponseModel.clinicID;
@ -625,7 +656,6 @@ class _AppointmentPaymentPageState extends State<AppointmentPaymentPage> {
);
},
onSucceeded: (successResult) async {
Navigator.of(context).pop();
log("successResult: ${successResult.responseMessage.toString()}");
selectedPaymentMethod = successResult.paymentOption ?? "VISA";
checkPaymentStatus();

@ -93,7 +93,7 @@ class _MyAppointmentsPageState extends State<MyAppointmentsPage> {
children: [
Visibility(visible: myAppointmentsVM.availableFilters.isNotEmpty, child: getAppointmentFilters(myAppointmentsVM)),
ListView.separated(
padding: EdgeInsets.only(top: 24.h),
padding: EdgeInsets.only(top: 16.h),
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemCount: myAppointmentsVM.isMyAppointmentsLoading
@ -169,6 +169,7 @@ class _MyAppointmentsPageState extends State<MyAppointmentsPage> {
Widget getAppointmentFilters(MyAppointmentsViewModel myAppointmentsVM) {
return SizedBox(
height: 60.h,
child: Row(
children: [
Expanded(
@ -177,6 +178,7 @@ class _MyAppointmentsPageState extends State<MyAppointmentsPage> {
width: 8.h,
),
scrollDirection: Axis.horizontal,
shrinkWrap: true,
itemCount: myAppointmentsVM.availableFilters.length,
itemBuilder: (_, index) => AppointmentFilters(
selectedFilter: myAppointmentsVM.selectedFilter,
@ -205,6 +207,6 @@ class _MyAppointmentsPageState extends State<MyAppointmentsPage> {
)),
),
],
)).paddingOnly(top: 24.h, left: 24.h, right: 24.h);
)).paddingOnly(top: 16.h, left: 24.h, right: 24.h);
}
}

@ -16,346 +16,363 @@ import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_vie
import 'package:hmg_patient_app_new/features/my_appointments/utils/appointment_type.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/appointments/appointment_details_page.dart';
import 'package:hmg_patient_app_new/presentation/appointments/widgets/ask_doctor_request_type_select.dart';
import 'package:hmg_patient_app_new/presentation/book_appointment/widgets/appointment_calendar.dart';
import 'package:hmg_patient_app_new/presentation/medical_file/eye_measurement_details_page.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart';
import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart';
import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart';
import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart';
import 'package:smooth_corner/smooth_corner.dart';
class AppointmentCard extends StatefulWidget {
AppointmentCard(
{super.key,
required this.patientAppointmentHistoryResponseModel,
required this.myAppointmentsViewModel,
required this.bookAppointmentsViewModel,
this.isLoading = false,
this.isFromHomePage = false,
this.isFromMedicalReport = false,
this.medicalFileViewModel});
class AppointmentCard extends StatelessWidget {
final PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel;
final MyAppointmentsViewModel myAppointmentsViewModel;
final bool isLoading;
final bool isFromHomePage;
final bool isFromMedicalReport;
final bool isForEyeMeasurements;
final MedicalFileViewModel? medicalFileViewModel;
final BookAppointmentsViewModel bookAppointmentsViewModel;
PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel;
MyAppointmentsViewModel myAppointmentsViewModel;
bool isLoading;
bool isFromHomePage;
bool isFromMedicalReport;
MedicalFileViewModel? medicalFileViewModel;
BookAppointmentsViewModel bookAppointmentsViewModel;
const AppointmentCard({
super.key,
required this.patientAppointmentHistoryResponseModel,
required this.myAppointmentsViewModel,
required this.bookAppointmentsViewModel,
this.isLoading = false,
this.isFromHomePage = false,
this.isFromMedicalReport = false,
this.isForEyeMeasurements = false,
this.medicalFileViewModel,
});
@override
State<AppointmentCard> createState() => _AppointmentCardState();
}
class _AppointmentCardState extends State<AppointmentCard> {
@override
Widget build(BuildContext context) {
AppState appState = getIt.get<AppState>();
final appState = getIt.get<AppState>();
return InkWell(
onTap: () {
Navigator.of(context)
.push(
CustomPageRoute(
page: AppointmentDetailsPage(patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel),
),
)
.then((val) {
widget.myAppointmentsViewModel.initAppointmentsViewModel();
widget.myAppointmentsViewModel.getPatientAppointments(true, false);
});
},
onTap: () => _goToDetails(context),
child: Padding(
padding: EdgeInsets.all(14.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Wrap(
alignment: WrapAlignment.start,
direction: Axis.horizontal,
spacing: 6.h,
runSpacing: 6.h,
children: [
AppCustomChipWidget(
icon: widget.isLoading
? AppAssets.walkin_appointment_icon
: (!widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment! ? AppAssets.walkin_appointment_icon : AppAssets.small_livecare_icon),
iconColor: widget.isLoading
? AppColors.textColor
: !widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment!
? AppColors.textColor
: AppColors.whiteColor,
labelText: widget.isLoading
? "Walk In"
: widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment!
? LocaleKeys.livecare.tr(context: context)
: "Walk In".needTranslation,
backgroundColor:
widget.isLoading ? AppColors.greyColor : (!widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment! ? AppColors.greyColor : AppColors.successColor),
textColor: widget.isLoading ? AppColors.textColor : (!widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment! ? AppColors.textColor : AppColors.whiteColor),
).toShimmer2(isShow: widget.isLoading),
AppCustomChipWidget(
labelText: widget.isLoading
? "OutPatient"
: appState.isArabic()
? widget.patientAppointmentHistoryResponseModel.isInOutPatientDescriptionN!
: widget.patientAppointmentHistoryResponseModel.isInOutPatientDescription!,
backgroundColor: AppColors.primaryRedColor.withOpacity(0.1),
textColor: AppColors.primaryRedColor,
).toShimmer2(isShow: widget.isLoading),
AppCustomChipWidget(
labelText: widget.isLoading ? "Booked" : AppointmentType.getAppointmentStatusType(widget.patientAppointmentHistoryResponseModel.patientStatusType!),
backgroundColor: AppColors.successColor.withOpacity(0.1),
textColor: AppColors.successColor,
).toShimmer2(isShow: widget.isLoading),
],
).toShimmer2(isShow: widget.isLoading),
),
// TODO: Implement the logic to enable/disable the switch based on reminder status
// AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel)
// ? SizedBox().toShimmer2(isShow: widget.isLoading)
// : Switch(
// activeColor: AppColors.successColor,
// activeTrackColor: AppColors.successColor.withValues(alpha: .15),
// thumbIcon: WidgetStateProperty.resolveWith<Icon?>(
// (Set<WidgetState> states) {
// if (states.contains(WidgetState.selected)) {
// return const Icon(Icons.check); // Icon when switch is ON
// }
// return const Icon(Icons.close); // Icon when switch is OFF
// },
// ),
// value: widget.isLoading ? false : widget.patientAppointmentHistoryResponseModel.hasReminder!,
// onChanged: (newValue) {
// setState(() {
// widget.myAppointmentsViewModel.setAppointmentReminder(newValue, widget.patientAppointmentHistoryResponseModel);
// });
// },
// ).toShimmer2(isShow: widget.isLoading),
],
),
_buildHeader(context, appState),
SizedBox(height: 16.h),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Image.network(
widget.isLoading ? "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png" : widget.patientAppointmentHistoryResponseModel.doctorImageURL!,
width: 63.h,
height: 63.h,
fit: BoxFit.cover,
).circle(100).toShimmer2(isShow: widget.isLoading),
SizedBox(width: 16.h),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
(widget.isLoading ? "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png" : widget.patientAppointmentHistoryResponseModel.doctorNameObj!)
.toText16(isBold: true, maxlines: 1)
.toShimmer2(isShow: widget.isLoading),
SizedBox(height: 8.h),
Wrap(
direction: Axis.horizontal,
spacing: 3.h,
runSpacing: 4.h,
children: [
widget.isFromHomePage
? SizedBox.shrink()
: AppCustomChipWidget(labelText: widget.isLoading ? "Cardiology" : widget.patientAppointmentHistoryResponseModel.clinicName!).toShimmer2(isShow: widget.isLoading),
widget.isFromHomePage
? SizedBox.shrink()
: AppCustomChipWidget(labelText: widget.isLoading ? "Olaya" : widget.patientAppointmentHistoryResponseModel.projectName!).toShimmer2(isShow: widget.isLoading),
AppCustomChipWidget(
icon: AppAssets.appointment_calendar_icon,
labelText:
widget.isLoading ? "Cardiology" : DateUtil.formatDateToDate(DateUtil.convertStringToDate(widget.patientAppointmentHistoryResponseModel.appointmentDate), false))
.toShimmer2(isShow: widget.isLoading),
widget.isFromMedicalReport
? SizedBox.shrink()
: AppCustomChipWidget(
icon: AppAssets.appointment_time_icon,
labelText: widget.isLoading
? "Cardiology"
: DateUtil.formatDateToTimeLang(DateUtil.convertStringToDate(widget.patientAppointmentHistoryResponseModel.appointmentDate), false))
.toShimmer2(isShow: widget.isLoading),
],
),
],
),
),
],
),
_buildDoctorRow(context),
SizedBox(height: 16.h),
widget.isFromMedicalReport
? CustomButton(
text: "Select appointment".needTranslation,
onPressed: () {
widget.medicalFileViewModel!.setSelectedMedicalReportAppointment(widget.patientAppointmentHistoryResponseModel);
Navigator.pop(context, false);
},
backgroundColor: AppColors.secondaryLightRedColor,
borderColor: AppColors.secondaryLightRedColor,
textColor: AppColors.primaryRedColor,
fontSize: 14,
fontWeight: FontWeight.w500,
borderRadius: 12,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 40.h,
icon: AppAssets.checkmark_icon,
iconColor: AppColors.primaryRedColor,
iconSize: 16.h,
)
: Row(
children: [
Expanded(
flex: 6,
child: AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel)
? getArrivedAppointmentButton().toShimmer2(isShow: widget.isLoading)
: CustomButton(
text: AppointmentType.getNextActionText(widget.patientAppointmentHistoryResponseModel.nextAction),
onPressed: () {
Navigator.of(context)
.push(CustomPageRoute(
page: AppointmentDetailsPage(patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel),
))
.then((val) {
widget.myAppointmentsViewModel.initAppointmentsViewModel();
widget.myAppointmentsViewModel.getPatientAppointments(true, false);
});
},
backgroundColor: AppointmentType.getNextActionButtonColor(widget.patientAppointmentHistoryResponseModel.nextAction).withOpacity(0.15),
borderColor: AppointmentType.getNextActionButtonColor(widget.patientAppointmentHistoryResponseModel.nextAction).withOpacity(0.01),
textColor: AppointmentType.getNextActionTextColor(widget.patientAppointmentHistoryResponseModel.nextAction),
fontSize: 14,
fontWeight: FontWeight.w500,
borderRadius: 12,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 40.h,
icon: AppointmentType.getNextActionIcon(widget.patientAppointmentHistoryResponseModel.nextAction),
iconColor: AppointmentType.getNextActionTextColor(widget.patientAppointmentHistoryResponseModel.nextAction),
iconSize: 15.h,
).toShimmer2(isShow: widget.isLoading),
),
SizedBox(width: 8.h),
Expanded(
flex: 1,
child: Container(
height: 40.h,
width: 40.h,
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.textColor,
borderRadius: 10.h,
),
child: Transform.flip(
flipX: appState.isArabic(),
child: Utils.buildSvgWithAssets(
icon: AppAssets.forward_arrow_icon,
iconColor: AppColors.whiteColor,
width: 40.h,
height: 40.h,
fit: BoxFit.cover,
),
),
).toShimmer2(isShow: widget.isLoading).onPress(() {
Navigator.of(context)
.push(
CustomPageRoute(
page: AppointmentDetailsPage(patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel),
),
)
.then((val) {
widget.myAppointmentsViewModel.initAppointmentsViewModel();
widget.myAppointmentsViewModel.getPatientAppointments(true, false);
});
}),
),
],
),
_buildActionArea(context, appState),
],
),
),
);
}
Widget getArrivedAppointmentButton() {
return DateTime.now().difference(DateUtil.convertStringToDate(widget.patientAppointmentHistoryResponseModel.appointmentDate)).inDays <= 15
? CustomButton(
text: LocaleKeys.askDoctor.tr(context: context),
onPressed: () {},
backgroundColor: AppColors.secondaryLightRedColor,
borderColor: AppColors.secondaryLightRedColor,
textColor: AppColors.primaryRedColor,
fontSize: 14,
fontWeight: FontWeight.w500,
borderRadius: 12,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 40.h,
icon: AppAssets.ask_doctor_icon,
iconColor: AppColors.primaryRedColor,
iconSize: 16.h,
)
: CustomButton(
text: "Rebook with same doctor".needTranslation,
onPressed: () {
openDoctorScheduleCalendar();
},
backgroundColor: AppColors.greyColor,
borderColor: AppColors.greyColor,
textColor: AppColors.blackColor,
fontSize: 14,
fontWeight: FontWeight.w500,
borderRadius: 12,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 40.h,
icon: AppAssets.rebook_appointment_icon,
iconColor: AppColors.blackColor,
iconSize: 16.h,
);
Widget _buildHeader(BuildContext context, AppState appState) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(child: _buildChips(context, appState)),
],
);
}
Widget _buildChips(BuildContext context, AppState appState) {
final isLiveCare = !isLoading && patientAppointmentHistoryResponseModel.isLiveCareAppointment!;
return Wrap(
alignment: WrapAlignment.start,
direction: Axis.horizontal,
spacing: 6.w,
runSpacing: 6.h,
children: [
AppCustomChipWidget(
icon: isLoading ? AppAssets.walkin_appointment_icon : (isLiveCare ? AppAssets.small_livecare_icon : AppAssets.walkin_appointment_icon),
iconColor: isLoading ? AppColors.textColor : (isLiveCare ? AppColors.whiteColor : AppColors.textColor),
labelText: isLoading ? 'Walk In'.needTranslation : (isLiveCare ? LocaleKeys.livecare.tr(context: context) : 'Walk In'.needTranslation),
backgroundColor: isLoading ? AppColors.greyColor : (isLiveCare ? AppColors.successColor : AppColors.greyColor),
textColor: isLoading ? AppColors.textColor : (isLiveCare ? AppColors.whiteColor : AppColors.textColor),
).toShimmer2(isShow: isLoading),
AppCustomChipWidget(
labelText: isLoading
? 'OutPatient'.needTranslation
: (appState.isArabic()
? patientAppointmentHistoryResponseModel.isInOutPatientDescriptionN!
: patientAppointmentHistoryResponseModel.isInOutPatientDescription!),
backgroundColor: AppColors.primaryRedColor.withValues(alpha: 0.1),
textColor: AppColors.primaryRedColor,
).toShimmer2(isShow: isLoading),
AppCustomChipWidget(
labelText: isLoading
? 'Booked'.needTranslation
: AppointmentType.getAppointmentStatusType(patientAppointmentHistoryResponseModel.patientStatusType!),
backgroundColor: AppColors.successColor.withValues(alpha: 0.1),
textColor: AppColors.successColor,
).toShimmer2(isShow: isLoading),
],
).toShimmer2(isShow: isLoading);
}
openDoctorScheduleCalendar() async {
DoctorsListResponseModel doctor = DoctorsListResponseModel(
clinicID: widget.patientAppointmentHistoryResponseModel.clinicID,
projectID: widget.patientAppointmentHistoryResponseModel.projectID,
doctorID: widget.patientAppointmentHistoryResponseModel.doctorID,
doctorImageURL: widget.patientAppointmentHistoryResponseModel.doctorImageURL,
doctorTitle: widget.patientAppointmentHistoryResponseModel.doctorTitle,
name: widget.patientAppointmentHistoryResponseModel.doctorNameObj,
nationalityFlagURL: "https://hmgwebservices.com/Images/flag/SYR.png",
speciality: [],
clinicName: widget.patientAppointmentHistoryResponseModel.clinicName,
projectName: widget.patientAppointmentHistoryResponseModel.projectName,
Widget _buildDoctorRow(BuildContext context) {
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Image.network(
isLoading
? 'https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png'
: patientAppointmentHistoryResponseModel.doctorImageURL!,
width: 63.h,
height: 63.h,
fit: BoxFit.cover,
).circle(100).toShimmer2(isShow: isLoading),
SizedBox(width: 16.h),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
(isLoading
? 'https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png'
: patientAppointmentHistoryResponseModel.doctorNameObj!)
.toText16(isBold: true, maxlines: 1)
.toShimmer2(isShow: isLoading),
SizedBox(height: 8.h),
Wrap(
direction: Axis.horizontal,
spacing: 3.h,
runSpacing: 4.h,
children: [
if (!isFromHomePage)
AppCustomChipWidget(labelText: isLoading ? 'Cardiology' : patientAppointmentHistoryResponseModel.clinicName!)
.toShimmer2(isShow: isLoading),
if (!isFromHomePage)
AppCustomChipWidget(labelText: isLoading ? 'Olaya' : patientAppointmentHistoryResponseModel.projectName!)
.toShimmer2(isShow: isLoading),
AppCustomChipWidget(
icon: AppAssets.appointment_calendar_icon,
labelText: isLoading
? 'Cardiology'
: DateUtil.formatDateToDate(DateUtil.convertStringToDate(patientAppointmentHistoryResponseModel.appointmentDate), false),
).toShimmer2(isShow: isLoading),
if (!isFromMedicalReport)
AppCustomChipWidget(
icon: AppAssets.appointment_time_icon,
labelText: isLoading
? 'Cardiology'
: DateUtil.formatDateToTimeLang(
DateUtil.convertStringToDate(patientAppointmentHistoryResponseModel.appointmentDate), false),
).toShimmer2(isShow: isLoading),
],
),
],
),
),
],
);
widget.bookAppointmentsViewModel.setSelectedDoctor(doctor);
LoaderBottomSheet.showLoader();
await widget.bookAppointmentsViewModel.getDoctorFreeSlots(
isBookingForLiveCare: false,
onSuccess: (dynamic respData) async {
LoaderBottomSheet.hideLoader();
showCommonBottomSheetWithoutHeight(
title: "Pick a Date".needTranslation,
context,
child: AppointmentCalendar(),
isFullScreen: false,
isCloseButtonVisible: true,
callBackFunc: () {},
);
}
Widget _buildActionArea(BuildContext context, AppState appState) {
if (isFromMedicalReport) {
if (isForEyeMeasurements) {
return SizedBox.shrink();
} else {
return CustomButton(
text: 'Select appointment'.needTranslation,
onPressed: () {
medicalFileViewModel!.setSelectedMedicalReportAppointment(patientAppointmentHistoryResponseModel);
Navigator.pop(context, false);
},
backgroundColor: AppColors.secondaryLightRedColor,
borderColor: AppColors.secondaryLightRedColor,
textColor: AppColors.primaryRedColor,
fontSize: 14.f,
fontWeight: FontWeight.w500,
borderRadius: 12.r,
padding: EdgeInsets.symmetric(horizontal: 10.w),
height: isTablet || isFoldable ? 46.h : 40.h,
icon: AppAssets.checkmark_icon,
iconColor: AppColors.primaryRedColor,
iconSize: 16.h,
);
}
}
return Row(
children: [
Expanded(
flex: 6,
child: (AppointmentType.isArrived(patientAppointmentHistoryResponseModel)
? _getArrivedButton(context)
: CustomButton(
text: AppointmentType.getNextActionText(patientAppointmentHistoryResponseModel.nextAction),
onPressed: () => _goToDetails(context),
backgroundColor:
AppointmentType.getNextActionButtonColor(patientAppointmentHistoryResponseModel.nextAction).withValues(alpha: 0.15),
borderColor:
AppointmentType.getNextActionButtonColor(patientAppointmentHistoryResponseModel.nextAction).withValues(alpha: 0.01),
textColor: AppointmentType.getNextActionTextColor(patientAppointmentHistoryResponseModel.nextAction),
fontSize: 14.f,
fontWeight: FontWeight.w500,
borderRadius: 12.r,
padding: EdgeInsets.symmetric(horizontal: 10.w),
height: isTablet || isFoldable ? 46.h : 40.h,
icon: AppointmentType.getNextActionIcon(patientAppointmentHistoryResponseModel.nextAction),
iconColor: AppointmentType.getNextActionTextColor(patientAppointmentHistoryResponseModel.nextAction),
iconSize: 15.h,
))
.toShimmer2(isShow: isLoading),
),
SizedBox(width: 8.h),
Expanded(
flex: 1,
child: Container(
height: (isFoldable || isTablet) ? 50.h : 40.h,
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.textColor, borderRadius: 10.h),
child: Transform.flip(
flipX: appState.isArabic(),
child: Utils.buildSvgWithAssets(
icon: AppAssets.forward_arrow_icon,
iconColor: AppColors.whiteColor,
width: 24.w,
height: 24.h,
fit: BoxFit.contain,
),
),
).toShimmer2(isShow: isLoading).onPress(() {
_goToDetails(context);
}),
),
],
);
}
Widget _getArrivedButton(BuildContext context) {
final isRecent = DateTime.now().difference(DateUtil.convertStringToDate(patientAppointmentHistoryResponseModel.appointmentDate)).inDays <= 15;
if (isRecent) {
return CustomButton(
text: LocaleKeys.askDoctor.tr(context: context),
onPressed: () async {
LoaderBottomSheet.showLoader(loadingText: "Checking doctor availability...".needTranslation);
await myAppointmentsViewModel.isDoctorAvailable(
projectID: patientAppointmentHistoryResponseModel.projectID,
doctorId: patientAppointmentHistoryResponseModel.doctorID,
clinicId: patientAppointmentHistoryResponseModel.clinicID,
onSuccess: (value) async {
if (value) {
print("Doctor is available");
await myAppointmentsViewModel.getAskDoctorRequestTypes(onSuccess: (val) {
LoaderBottomSheet.hideLoader();
showCommonBottomSheetWithoutHeight(
context,
title: LocaleKeys.askDoctor.tr(context: context),
child: AskDoctorRequestTypeSelect(
askDoctorRequestTypeList: myAppointmentsViewModel.askDoctorRequestTypeList,
myAppointmentsViewModel: myAppointmentsViewModel,
patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel,
),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
});
} else {
print("Doctor is not available");
}
});
},
onError: (err) {
LoaderBottomSheet.hideLoader();
showCommonBottomSheetWithoutHeight(
context,
child: Utils.getErrorWidget(loadingText: err),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
});
backgroundColor: AppColors.secondaryLightRedColor,
borderColor: AppColors.secondaryLightRedColor,
textColor: AppColors.primaryRedColor,
fontSize: 14.f,
fontWeight: FontWeight.w500,
borderRadius: 12.r,
padding: EdgeInsets.symmetric(horizontal: 10.w),
height: isTablet || isFoldable ? 46.h : 40.h,
icon: AppAssets.ask_doctor_icon,
iconColor: AppColors.primaryRedColor,
iconSize: 16.h,
);
}
return CustomButton(
text: 'Rebook with same doctor'.needTranslation,
onPressed: () => openDoctorScheduleCalendar(context),
backgroundColor: AppColors.greyColor,
borderColor: AppColors.greyColor,
textColor: AppColors.blackColor,
fontSize: 14.f,
fontWeight: FontWeight.w500,
borderRadius: 12.r,
padding: EdgeInsets.symmetric(horizontal: 10.w),
height: isTablet || isFoldable ? 46.h : 40.h,
icon: AppAssets.rebook_appointment_icon,
iconColor: AppColors.blackColor,
iconSize: 16.h,
);
}
void _goToDetails(BuildContext context) {
if (isForEyeMeasurements) {
Navigator.of(context).push(
CustomPageRoute(
page: EyeMeasurementDetailsPage(patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel),
),
);
} else {
Navigator.of(context)
.push(
CustomPageRoute(
page: AppointmentDetailsPage(patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel),
),
)
.then((_) {
myAppointmentsViewModel.initAppointmentsViewModel();
myAppointmentsViewModel.getPatientAppointments(true, false);
});
}
}
void performAppointmentNextAction(nextAction) {}
void openDoctorScheduleCalendar(BuildContext context) async {
final doctor = DoctorsListResponseModel(
clinicID: patientAppointmentHistoryResponseModel.clinicID,
projectID: patientAppointmentHistoryResponseModel.projectID,
doctorID: patientAppointmentHistoryResponseModel.doctorID,
doctorImageURL: patientAppointmentHistoryResponseModel.doctorImageURL,
doctorTitle: patientAppointmentHistoryResponseModel.doctorTitle,
name: patientAppointmentHistoryResponseModel.doctorNameObj,
nationalityFlagURL: 'https://hmgwebservices.com/Images/flag/SYR.png',
speciality: [],
clinicName: patientAppointmentHistoryResponseModel.clinicName,
projectName: patientAppointmentHistoryResponseModel.projectName,
);
bookAppointmentsViewModel.setSelectedDoctor(doctor);
LoaderBottomSheet.showLoader();
await bookAppointmentsViewModel.getDoctorFreeSlots(
isBookingForLiveCare: false,
onSuccess: (respData) async {
LoaderBottomSheet.hideLoader();
showCommonBottomSheetWithoutHeight(
context,
child: AppointmentCalendar(),
callBackFunc: () {},
title: 'Pick a Date'.needTranslation,
isFullScreen: false,
isCloseButtonVisible: true,
);
},
onError: (err) {
LoaderBottomSheet.hideLoader();
showCommonBottomSheetWithoutHeight(
context,
child: Utils.getErrorWidget(loadingText: err),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
},
);
}
}

@ -18,6 +18,7 @@ import 'package:hmg_patient_app_new/presentation/home/navigation_screen.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:barcode_scan2/barcode_scan2.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/nfc/nfc_reader_sheet.dart';
import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart';
@ -139,14 +140,13 @@ class AppointmentCheckinBottomSheet extends StatelessWidget {
}
void sendCheckInRequest(String scannedCode, BuildContext context) async {
showCommonBottomSheet(context,
child: Utils.getLoadingWidget(), callBackFunc: (str) {}, title: "", height: ResponsiveExtension.screenHeight * 0.3, isCloseButtonVisible: false, isDismissible: false, isFullScreen: false);
LoaderBottomSheet.showLoader(loadingText: "Processing Check-In...".needTranslation);
await myAppointmentsViewModel.sendCheckInNfcRequest(
patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel,
scannedCode: scannedCode,
checkInType: 2,
onSuccess: (apiResponse) {
Navigator.of(context).pop();
LoaderBottomSheet.hideLoader();
showCommonBottomSheetWithoutHeight(context, title: "Success".needTranslation, child: Utils.getSuccessWidget(loadingText: LocaleKeys.success.tr()), callBackFunc: () {
Navigator.of(context).pop();
Navigator.pushAndRemoveUntil(
@ -161,7 +161,7 @@ class AppointmentCheckinBottomSheet extends StatelessWidget {
}, isFullScreen: false);
},
onError: (error) {
Navigator.of(context).pop();
LoaderBottomSheet.hideLoader();
showCommonBottomSheetWithoutHeight(context, title: "Error".needTranslation, child: Utils.getErrorWidget(loadingText: error), callBackFunc: () {
Navigator.of(context).pop();
}, isFullScreen: false);

@ -13,12 +13,18 @@ import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart';
class AppointmentDoctorCard extends StatelessWidget {
AppointmentDoctorCard({super.key, required this.patientAppointmentHistoryResponseModel, required this.onRescheduleTap, required this.onCancelTap, required this.onAskDoctorTap});
const AppointmentDoctorCard(
{super.key,
required this.patientAppointmentHistoryResponseModel,
required this.onRescheduleTap,
required this.onCancelTap,
required this.onAskDoctorTap, this.renderWidgetForERDisplay = false});
PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel;
late Function onRescheduleTap;
late Function onCancelTap;
late Function onAskDoctorTap;
final PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel;
final VoidCallback onRescheduleTap;
final VoidCallback onCancelTap;
final VoidCallback onAskDoctorTap;
final bool renderWidgetForERDisplay;
@override
Widget build(BuildContext context) {
@ -38,11 +44,11 @@ class AppointmentDoctorCard extends StatelessWidget {
children: [
Image.network(
patientAppointmentHistoryResponseModel.doctorImageURL!,
width: 63.h,
width: 63.w,
height: 63.h,
fit: BoxFit.cover,
).circle(100),
SizedBox(width: 16.h),
).circle(100.r),
SizedBox(width: 16.w),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
@ -50,23 +56,41 @@ class AppointmentDoctorCard extends StatelessWidget {
patientAppointmentHistoryResponseModel.doctorNameObj!.toText16(isBold: true),
Wrap(
direction: Axis.horizontal,
spacing: 3.h,
runSpacing: 4.h,
spacing: 6.w,
runSpacing: 6.h,
children: [
AppCustomChipWidget(labelText: patientAppointmentHistoryResponseModel.clinicName!),
AppCustomChipWidget(labelText: patientAppointmentHistoryResponseModel.projectName!),
AppCustomChipWidget(
icon: AppAssets.doctor_calendar_icon,
labelText:
"${DateUtil.formatDateToDate(DateUtil.convertStringToDate(patientAppointmentHistoryResponseModel.appointmentDate), false)}, ${DateUtil.formatDateToTimeLang(DateUtil.convertStringToDate(patientAppointmentHistoryResponseModel.appointmentDate), false)}"),
labelText: patientAppointmentHistoryResponseModel.clinicName!,
labelPadding: EdgeInsetsDirectional.only(start: 4.w, end: 4.w),
),
AppCustomChipWidget(
labelText: patientAppointmentHistoryResponseModel.projectName!,
labelPadding: EdgeInsetsDirectional.only(start: 6.w, end: 6.w),
),
AppCustomChipWidget(
icon: AppAssets.doctor_calendar_icon,
labelText:
"${DateUtil.formatDateToDate(DateUtil.convertStringToDate(patientAppointmentHistoryResponseModel.appointmentDate), false)}, ${DateUtil.formatDateToTimeLang(
DateUtil.convertStringToDate(patientAppointmentHistoryResponseModel.appointmentDate),
false,
)}",
),
AppCustomChipWidget(
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 : AppColors.whiteColor,
labelText: patientAppointmentHistoryResponseModel.isLiveCareAppointment! ? LocaleKeys.livecare.tr(context: context) : "Walk In".needTranslation,
backgroundColor: !patientAppointmentHistoryResponseModel.isLiveCareAppointment! ? AppColors.greyColor : AppColors.successColor,
labelText: patientAppointmentHistoryResponseModel.isLiveCareAppointment!
? LocaleKeys.livecare.tr(context: context)
: "Walk In".needTranslation,
backgroundColor:
!patientAppointmentHistoryResponseModel.isLiveCareAppointment! ? AppColors.greyColor : AppColors.successColor,
textColor: !patientAppointmentHistoryResponseModel.isLiveCareAppointment! ? AppColors.textColor : AppColors.whiteColor,
),
AppCustomChipWidget(icon: AppAssets.rating_icon, iconColor: AppColors.ratingColorYellow, labelText: "Rating: ${patientAppointmentHistoryResponseModel.decimalDoctorRate}"),
AppCustomChipWidget(
icon: AppAssets.rating_icon,
iconColor: AppColors.ratingColorYellow,
labelText: "Rating: ${patientAppointmentHistoryResponseModel.decimalDoctorRate}"),
],
),
],
@ -75,8 +99,12 @@ class AppointmentDoctorCard extends StatelessWidget {
],
),
SizedBox(height: 16.h),
getAppointmentActionButtons(
AppointmentType.isArrived(patientAppointmentHistoryResponseModel),
Visibility(
visible: renderWidgetForERDisplay == false,
child: getAppointmentActionButtons(
AppointmentType.isArrived(patientAppointmentHistoryResponseModel),
),
),
],
),
@ -89,14 +117,16 @@ class AppointmentDoctorCard extends StatelessWidget {
return DateTime.now().difference(DateUtil.convertStringToDate(patientAppointmentHistoryResponseModel.appointmentDate)).inDays <= 15
? CustomButton(
text: LocaleKeys.askDoctor.tr(),
onPressed: () {},
onPressed: () {
onAskDoctorTap();
},
backgroundColor: AppColors.secondaryLightRedColor,
borderColor: AppColors.secondaryLightRedColor,
textColor: AppColors.primaryRedColor,
fontSize: 14,
fontSize: 14.f,
fontWeight: FontWeight.w500,
borderRadius: 12,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
borderRadius: 12.r,
padding: EdgeInsets.fromLTRB(10.w, 0, 10.w, 0),
height: 40.h,
iconSize: 16.h,
icon: AppAssets.ask_doctor_icon,
@ -110,10 +140,10 @@ class AppointmentDoctorCard extends StatelessWidget {
backgroundColor: AppColors.greyColor,
borderColor: AppColors.greyColor,
textColor: AppColors.blackColor,
fontSize: 14,
fontSize: 14.f,
fontWeight: FontWeight.w500,
borderRadius: 12,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
borderRadius: 12.r,
padding: EdgeInsets.fromLTRB(10.w, 0, 10.w, 0),
height: 40.h,
icon: AppAssets.rebook_appointment_icon,
iconColor: AppColors.blackColor,
@ -129,9 +159,9 @@ class AppointmentDoctorCard extends StatelessWidget {
backgroundColor: AppColors.primaryRedColor,
borderColor: AppColors.primaryRedColor,
textColor: AppColors.whiteColor,
fontSize: 14,
fontSize: 14.f,
fontWeight: FontWeight.w500,
borderRadius: 12.h,
borderRadius: 12.r,
height: 40.h,
icon: AppAssets.cancel,
iconColor: AppColors.whiteColor,
@ -148,9 +178,9 @@ class AppointmentDoctorCard extends StatelessWidget {
backgroundColor: AppColors.secondaryLightRedColor,
borderColor: AppColors.secondaryLightRedColor,
textColor: AppColors.primaryRedColor,
fontSize: 14,
fontSize: 14.f,
fontWeight: FontWeight.w500,
borderRadius: 12.h,
borderRadius: 12.r,
height: 40.h,
icon: AppAssets.appointment_calendar_icon,
iconColor: AppColors.primaryRedColor,
@ -167,9 +197,9 @@ class AppointmentDoctorCard extends StatelessWidget {
backgroundColor: AppColors.primaryRedColor,
borderColor: AppColors.primaryRedColor,
textColor: AppColors.whiteColor,
fontSize: 14,
fontSize: 14.f,
fontWeight: FontWeight.w500,
borderRadius: 12.h,
borderRadius: 12.r,
height: 40.h,
icon: AppAssets.cancel,
iconColor: AppColors.whiteColor,

@ -0,0 +1,138 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/ask_doctor_request_type_response_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/patient_appointment_history_response_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.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/common_bottom_sheet.dart';
import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart';
class AskDoctorRequestTypeSelect extends StatelessWidget {
AskDoctorRequestTypeSelect({super.key, required this.askDoctorRequestTypeList, required this.myAppointmentsViewModel, required this.patientAppointmentHistoryResponseModel});
final MyAppointmentsViewModel myAppointmentsViewModel;
final PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel;
List<AskDocRequestType> askDoctorRequestTypeList = [];
int selectedParameterCodeValue = 2;
int selectedParameterCode = 0;
final ValueNotifier<int> requestTypeSelectNotifier = ValueNotifier<int>(0);
@override
Widget build(BuildContext context) {
return Column(
children: [
Container(
width: double.infinity,
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24),
child: ListView.builder(
itemCount: askDoctorRequestTypeList.length,
physics: NeverScrollableScrollPhysics(),
padding: EdgeInsets.only(top: 8, bottom: 8),
shrinkWrap: true,
itemBuilder: (context, index) {
return ValueListenableBuilder<int>(
valueListenable: requestTypeSelectNotifier,
builder: (context, duration, child) {
return Theme(
data: Theme.of(context).copyWith(
listTileTheme: ListTileThemeData(horizontalTitleGap: 4),
),
child: RadioListTile<int>(
title: (askDoctorRequestTypeList[index].description ?? '').toText14(weight: FontWeight.w500),
value: index,
fillColor: WidgetStateProperty.resolveWith((states) {
if (states.contains(WidgetState.selected)) {
return AppColors.primaryRedColor;
}
return Color(0xffEEEEEE);
}),
contentPadding: EdgeInsets.only(left: 12.h, right: 12.h),
groupValue: selectedParameterCode,
onChanged: (int? newValue) {
selectedParameterCode = newValue!;
selectedParameterCodeValue = askDoctorRequestTypeList[index].parameterCode!;
requestTypeSelectNotifier.value = selectedParameterCode;
debugPrint(selectedParameterCodeValue.toString());
},
),
);
},
);
},
),
),
SizedBox(height: 16.h),
Row(
children: [
Expanded(
child: CustomButton(
text: LocaleKeys.cancel.tr(),
onPressed: () {
Navigator.pop(context);
},
backgroundColor: AppColors.primaryRedColor,
borderColor: AppColors.primaryRedColor,
textColor: AppColors.whiteColor,
icon: AppAssets.cancel,
iconColor: AppColors.whiteColor,
borderRadius: 12.r,
iconSize: 14.h,
fontSize: 14.f,
height: 40.h,
),
),
SizedBox(width: 8.h),
Expanded(
child: CustomButton(
text: LocaleKeys.confirm.tr(),
onPressed: () async {
Navigator.pop(context);
LoaderBottomSheet.showLoader(loadingText: "Sending Request...");
await myAppointmentsViewModel.sendAskDocCallRequest(
patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel,
requestType: selectedParameterCodeValue.toString(),
remarks: "",
userMobileNumber: myAppointmentsViewModel.appState.getAuthenticatedUser()!.mobileNumber!,
onSuccess: (val) {
LoaderBottomSheet.hideLoader();
showCommonBottomSheetWithoutHeight(
context,
child: Utils.getSuccessWidget(loadingText: "Request has been sent successfully, you will be contacted soon.".needTranslation),
callBackFunc: () {
Navigator.of(context).pop();
},
title: "",
isCloseButtonVisible: true,
isDismissible: false,
isFullScreen: false,
);
},
onError: (errMessage) {
LoaderBottomSheet.hideLoader();
});
},
backgroundColor: AppColors.bgGreenColor,
borderColor: AppColors.bgGreenColor,
textColor: Colors.white,
icon: AppAssets.confirm,
iconSize: 14.h,
borderRadius: 12.r,
fontSize: 14.f,
height: 40.h,
),
),
],
)
],
);
}
}

@ -72,6 +72,7 @@ class HospitalListItem extends StatelessWidget {
);
Widget get distanceInfo => Row(
spacing: 4.w,
children: [
Visibility(
visible: (hospitalData?.distanceInKMs != "0"),
@ -91,9 +92,9 @@ class HospitalListItem extends StatelessWidget {
labelText: "Distance not available".needTranslation,
textColor: AppColors.blackColor,
),
SizedBox(
width: 8.h,
)
// SizedBox(
// width: 8.h,
// )
],
)),
Visibility(
@ -101,7 +102,7 @@ class HospitalListItem extends StatelessWidget {
child: AppCustomChipWidget(
labelText: "Location turned off".needTranslation,
deleteIcon: AppAssets.location_unavailable,
deleteIconSize: Size(9, 12),
deleteIconSize: Size(9.w, 12.h),
textColor: AppColors.blackColor,
)),
],

@ -61,7 +61,7 @@ class RegionListItem extends StatelessWidget {
Widget placesCountItem(String svgPath, String count, String title) {
return AppCustomChipWidget(
iconSize: 14,
iconSize: 14.h,
icon: svgPath,
iconHasColor: false,
richText: RichText(

@ -1,6 +1,5 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
@ -12,7 +11,6 @@ import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/features/authentication/authentication_view_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/authentication/login.dart';
import 'package:hmg_patient_app_new/presentation/home/landing_page.dart';
import 'package:hmg_patient_app_new/presentation/home/navigation_screen.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/appbar/app_bar_widget.dart';
@ -21,7 +19,7 @@ import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
import 'package:provider/provider.dart';
class SavedLogin extends StatefulWidget {
const SavedLogin({Key? key}) : super(key: key);
const SavedLogin({super.key});
@override
_SavedLogin createState() => _SavedLogin();
@ -65,248 +63,255 @@ class _SavedLogin extends State<SavedLogin> {
body: SafeArea(
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 24.h),
child: appState.getSelectDeviceByImeiRespModelElement != null ? Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const Spacer(flex: 2),
// Welcome back text
LocaleKeys.welcomeBack.tr().toText16(color: AppColors.inputLabelTextColor),
SizedBox(height: 16.h),
appState.getSelectDeviceByImeiRespModelElement != null
? appState.getSelectDeviceByImeiRespModelElement!.name!.toCamelCase.toText26(isBold: true, height: 26 / 36, color: AppColors.textColor)
: SizedBox(),
SizedBox(height: 24.h),
Container(
padding: EdgeInsets.all(16.h),
decoration: BoxDecoration(
color: AppColors.whiteColor,
border: Border.all(color: AppColors.whiteColor),
borderRadius: BorderRadius.circular(24.h),
boxShadow: [
BoxShadow(color: Color(0x0D000000), blurRadius: 16, offset: Offset(0, 0), spreadRadius: 5),
],
),
child: Column(
child: appState.getSelectDeviceByImeiRespModelElement != null
? Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
// Last login info
("${LocaleKeys.lastLoginBy.tr()} ${loginType.displayName}").toText14(isBold: true, color: AppColors.greyTextColor, letterSpacing: -1),
const Spacer(flex: 2),
// Welcome back text
LocaleKeys.welcomeBack.tr().toText16(color: AppColors.inputLabelTextColor),
SizedBox(height: 16.h),
appState.getSelectDeviceByImeiRespModelElement != null
? (appState.getSelectDeviceByImeiRespModelElement!.createdOn != null
? DateUtil.getFormattedDate(DateUtil.convertStringToDate(appState.getSelectDeviceByImeiRespModelElement!.createdOn!), "d MMMM, y 'at' HH:mm")
: '--')
.toText16(isBold: true, color: AppColors.textColor)
? appState.getSelectDeviceByImeiRespModelElement!.name!.toCamelCase
.toText26(isBold: true, height: 26 / 36, color: AppColors.textColor)
: SizedBox(),
SizedBox(height: 24.h),
Container(
padding: EdgeInsets.all(16.h),
decoration: BoxDecoration(
color: AppColors.whiteColor,
border: Border.all(color: AppColors.whiteColor),
borderRadius: BorderRadius.circular(24.h),
boxShadow: [
BoxShadow(color: Color(0x0D000000), blurRadius: 16.h, offset: Offset(0, 0), spreadRadius: 5.h),
],
),
child: Column(
children: [
// Last login info
appState.getSelectDeviceByImeiRespModelElement != null
? Container(
margin: EdgeInsets.all(16.h),
child: Utils.buildSvgWithAssets(
icon: getTypeIcons(appState.getSelectDeviceByImeiRespModelElement!.logInType!),
height: 54,
width: 54,
iconColor: loginType.toInt == 4 ? null : AppColors.primaryRedColor))
: SizedBox(),
// Face ID login button
SizedBox(
height: 45,
child: CustomButton(
text: "${LocaleKeys.loginBy.tr()} ${loginType.displayName}",
onPressed: () {
if (loginType == LoginTypeEnum.fingerprint || loginType == LoginTypeEnum.face) {
authVm.loginWithFingerPrintFace(() {});
} else {
authVm.checkUserAuthentication(otpTypeEnum: loginType == LoginTypeEnum.sms ? OTPTypeEnum.sms : OTPTypeEnum.whatsapp);
}
},
backgroundColor: AppColors.primaryRedColor,
borderColor: AppColors.primaryRedColor,
textColor: Colors.white,
fontSize: 12,
fontWeight: FontWeight.w500,
borderRadius: 12,
padding: EdgeInsets.fromLTRB(0, 10, 0, 10),
icon: getTypeIcons(loginType.toInt),
//loginType == LoginTypeEnum.sms ? AppAssets.sms :AppAssets.whatsapp,
iconColor: loginType != LoginTypeEnum.whatsapp ? Colors.white : null,
("${LocaleKeys.lastLoginBy.tr()} ${loginType.displayName}")
.toText14(isBold: true, color: AppColors.greyTextColor, letterSpacing: -1),
appState.getSelectDeviceByImeiRespModelElement != null
? (appState.getSelectDeviceByImeiRespModelElement!.createdOn != null
? DateUtil.getFormattedDate(
DateUtil.convertStringToDate(appState.getSelectDeviceByImeiRespModelElement!.createdOn!),
"d MMMM, y 'at' HH:mm")
: '--')
.toText16(isBold: true, color: AppColors.textColor)
: SizedBox(),
appState.getSelectDeviceByImeiRespModelElement != null
? Container(
margin: EdgeInsets.all(16.h),
child: Utils.buildSvgWithAssets(
icon: getTypeIcons(appState.getSelectDeviceByImeiRespModelElement!.logInType!),
height: 54.h,
width: 54.w,
iconColor: loginType.toInt == 4 ? null : AppColors.primaryRedColor))
: SizedBox(),
// Face ID login button
CustomButton(
text: "${LocaleKeys.loginBy.tr()} ${loginType.displayName}",
onPressed: () {
if (loginType == LoginTypeEnum.fingerprint || loginType == LoginTypeEnum.face) {
authVm.loginWithFingerPrintFace(() {});
} else {
authVm.checkUserAuthentication(otpTypeEnum: loginType == LoginTypeEnum.sms ? OTPTypeEnum.sms : OTPTypeEnum.whatsapp);
}
},
backgroundColor: AppColors.primaryRedColor,
borderColor: AppColors.primaryRedColor,
textColor: Colors.white,
fontSize: 12.f,
fontWeight: FontWeight.w500,
borderRadius: 12.r,
height: 40.h,
padding: EdgeInsets.symmetric(vertical: 10.h),
icon: getTypeIcons(loginType.toInt),
//loginType == LoginTypeEnum.sms ? AppAssets.sms :AppAssets.whatsapp,
iconColor: loginType != LoginTypeEnum.whatsapp ? Colors.white : null,
),
],
),
),
],
),
),
const SizedBox(height: 24),
Padding(
padding: EdgeInsets.symmetric(horizontal: 16.0),
child: Text(
LocaleKeys.oR.tr(),
style: context.dynamicTextStyle(fontSize: 16, fontWeight: FontWeight.w500),
),
),
const SizedBox(height: 24),
// OTP login button
loginType.toInt != 1
? Column(
children: [
loginType.toInt != 1
? CustomButton(
text: LocaleKeys.loginByOTP.tr(),
onPressed: () {
showModalBottomSheet(
context: context,
isScrollControlled: true,
isDismissible: false,
useSafeArea: true,
backgroundColor: Colors.transparent,
enableDrag: false,
// Prevent dragging to avoid focus conflicts
builder: (bottomSheetContext) => StatefulBuilder(builder: (BuildContext context, StateSetter setModalState) {
return Padding(
padding: EdgeInsets.only(bottom: MediaQuery.of(bottomSheetContext).viewInsets.bottom),
child: SingleChildScrollView(
child: GenericBottomSheet(
countryCode: "966",
initialPhoneNumber: "",
textController: TextEditingController(),
isFromSavedLogin: true,
isEnableCountryDropdown: true,
onCountryChange: (value) {},
onChange: (String? value) {},
buttons: [
Padding(
padding: EdgeInsets.only(bottom: 10.h),
child: CustomButton(
text: LocaleKeys.sendOTPSMS.tr(),
onPressed: () {
Navigator.of(context).pop();
loginType = LoginTypeEnum.sms;
authVm.checkUserAuthentication(otpTypeEnum: OTPTypeEnum.sms);
},
backgroundColor: AppColors.primaryRedColor,
borderColor: AppColors.primaryRedColor,
textColor: AppColors.whiteColor,
icon: AppAssets.sms),
),
Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Padding(padding: EdgeInsets.symmetric(horizontal: 8.h), child: (LocaleKeys.oR.tr()).toText16(color: AppColors.textColor)),
],
),
Padding(
padding: const EdgeInsets.only(bottom: 10, top: 10),
child: CustomButton(
text: LocaleKeys.sendOTPWHATSAPP.tr(),
onPressed: () {
Navigator.of(context).pop();
loginType = LoginTypeEnum.whatsapp;
authVm.checkUserAuthentication(otpTypeEnum: OTPTypeEnum.whatsapp);
},
backgroundColor: AppColors.transparent,
borderColor: AppColors.textColor,
textColor: AppColors.textColor,
icon: AppAssets.whatsapp,
iconColor: null,
SizedBox(height: 24.h),
Padding(
padding: EdgeInsets.symmetric(horizontal: 16.w),
child: Text(
LocaleKeys.oR.tr(),
style: context.dynamicTextStyle(fontSize: 16.f, fontWeight: FontWeight.w500),
),
),
SizedBox(height: 24.h),
// OTP login button
loginType.toInt != 1
? Column(
children: [
loginType.toInt != 1
? CustomButton(
text: LocaleKeys.loginByOTP.tr(),
onPressed: () {
showModalBottomSheet(
context: context,
isScrollControlled: true,
isDismissible: false,
useSafeArea: true,
backgroundColor: Colors.transparent,
enableDrag: false,
// Prevent dragging to avoid focus conflicts
builder: (bottomSheetContext) =>
StatefulBuilder(builder: (BuildContext context, StateSetter setModalState) {
return Padding(
padding: EdgeInsets.only(bottom: MediaQuery.of(bottomSheetContext).viewInsets.bottom),
child: SingleChildScrollView(
child: GenericBottomSheet(
countryCode: "966",
initialPhoneNumber: "",
textController: TextEditingController(),
isFromSavedLogin: true,
isEnableCountryDropdown: true,
onCountryChange: (value) {},
onChange: (String? value) {},
buttons: [
Padding(
padding: EdgeInsets.only(bottom: 10.h),
child: CustomButton(
text: LocaleKeys.sendOTPSMS.tr(),
onPressed: () {
Navigator.of(context).pop();
loginType = LoginTypeEnum.sms;
authVm.checkUserAuthentication(otpTypeEnum: OTPTypeEnum.sms);
},
backgroundColor: AppColors.primaryRedColor,
borderColor: AppColors.primaryRedColor,
textColor: AppColors.whiteColor,
icon: AppAssets.sms),
),
Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Padding(
padding: EdgeInsets.symmetric(horizontal: 8.h),
child: (LocaleKeys.oR.tr()).toText16(color: AppColors.textColor)),
],
),
Padding(
padding: EdgeInsets.only(bottom: 10.h, top: 10.h),
child: CustomButton(
text: LocaleKeys.sendOTPWHATSAPP.tr(),
onPressed: () {
Navigator.of(context).pop();
loginType = LoginTypeEnum.whatsapp;
authVm.checkUserAuthentication(otpTypeEnum: OTPTypeEnum.whatsapp);
},
backgroundColor: AppColors.transparent,
borderColor: AppColors.textColor,
textColor: AppColors.textColor,
icon: AppAssets.whatsapp,
iconColor: null,
),
),
],
),
),
],
),
),
);
}),
);
},
backgroundColor: AppColors.whiteColor,
borderColor: AppColors.borderOnlyColor,
textColor: AppColors.textColor,
borderWidth: 2,
padding: EdgeInsets.fromLTRB(0, 14, 0, 14),
icon: AppAssets.sms,
iconColor: AppColors.textColor,
)
: Container(),
);
}),
);
},
backgroundColor: AppColors.whiteColor,
borderColor: AppColors.borderOnlyColor,
textColor: AppColors.textColor,
borderWidth: 2,
padding: EdgeInsets.fromLTRB(0, 14.h, 0, 14.h),
icon: AppAssets.sms,
iconColor: AppColors.textColor,
)
: Container(),
SizedBox(
height: 20.h,
),
],
)
: CustomButton(
text: "${LocaleKeys.loginBy.tr()} ${LoginTypeEnum.whatsapp.displayName}",
icon: AppAssets.whatsapp,
iconColor: null,
onPressed: () {
if (loginType == LoginTypeEnum.fingerprint || loginType == LoginTypeEnum.face) {
authVm.loginWithFingerPrintFace(() {});
} else {
loginType = LoginTypeEnum.whatsapp;
authVm.checkUserAuthentication(otpTypeEnum: OTPTypeEnum.whatsapp);
}
},
backgroundColor: AppColors.whiteColor,
borderColor: Color(0xFF2E3039),
textColor: Color(0xFF2E3039),
borderWidth: 2.w,
padding: EdgeInsets.fromLTRB(0, 14.h, 0, 14.h),
),
const Spacer(flex: 2),
// OR divider
SizedBox(height: 24.h),
// Guest and Switch account
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: SizedBox(
height: 56.h,
child: CustomButton(
text: LocaleKeys.guest.tr(),
onPressed: () {
Navigator.of(context).pushReplacement(
MaterialPageRoute(builder: (BuildContext context) => LandingNavigation()),
);
},
backgroundColor: Color(0xffFEE9EA),
borderColor: Color(0xffFEE9EA),
textColor: Color(0xffED1C2B),
fontSize: 16.f,
fontWeight: FontWeight.w500,
borderRadius: 12.r,
padding: EdgeInsets.fromLTRB(0, 10.h, 0, 10.h),
// icon: "assets/images/svg/apple-finder.svg",
),
),
),
SizedBox(
height: 20,
width: MediaQuery.of(context).size.width * 0.05,
),
Expanded(
child: SizedBox(
height: 56.h,
child: CustomButton(
text: LocaleKeys.switchAccount.tr(),
onPressed: () async {
await authVm.clearDefaultInputValues();
Navigator.of(context).push(
MaterialPageRoute(builder: (BuildContext context) => LoginScreen()),
);
},
backgroundColor: Color(0xffFEE9EA),
borderColor: Color(0xffFEE9EA),
textColor: Color(0xffED1C2B),
fontSize: 15.f,
fontWeight: FontWeight.w500,
borderRadius: 12.r,
padding: EdgeInsets.fromLTRB(0, 10.h, 0, 10.h),
// icon: "assets/images/svg/apple-finder.svg",
),
),
),
],
)
: CustomButton(
text: "${LocaleKeys.loginBy.tr()} ${LoginTypeEnum.whatsapp.displayName}",
icon: AppAssets.whatsapp,
iconColor: null,
onPressed: () {
if (loginType == LoginTypeEnum.fingerprint || loginType == LoginTypeEnum.face) {
authVm.loginWithFingerPrintFace(() {});
} else {
loginType = LoginTypeEnum.whatsapp;
authVm.checkUserAuthentication(otpTypeEnum: OTPTypeEnum.whatsapp);
}
},
backgroundColor: AppColors.whiteColor,
borderColor: Color(0xFF2E3039),
textColor: Color(0xFF2E3039),
borderWidth: 2,
padding: EdgeInsets.fromLTRB(0, 14, 0, 14),
),
const Spacer(flex: 2),
// OR divider
const SizedBox(height: 24),
// Guest and Switch account
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: SizedBox(
height: 56,
child: CustomButton(
text: LocaleKeys.guest.tr(),
onPressed: () {
Navigator.of(context).pushReplacement(
MaterialPageRoute(builder: (BuildContext context) => LandingNavigation()),
);
},
backgroundColor: Color(0xffFEE9EA),
borderColor: Color(0xffFEE9EA),
textColor: Color(0xffED1C2B),
fontSize: 16,
fontWeight: FontWeight.w500,
borderRadius: 12,
padding: EdgeInsets.fromLTRB(0, 10, 0, 10),
// icon: "assets/images/svg/apple-finder.svg",
),
),
),
SizedBox(
width: MediaQuery.of(context).size.width * 0.05,
),
Expanded(
child: SizedBox(
height: 56,
child: CustomButton(
text: LocaleKeys.switchAccount.tr(),
onPressed: () async {
await authVm.clearDefaultInputValues();
Navigator.of(context).push(
MaterialPageRoute(builder: (BuildContext context) => LoginScreen()),
);
},
backgroundColor: Color(0xffFEE9EA),
borderColor: Color(0xffFEE9EA),
textColor: Color(0xffED1C2B),
fontSize: 15,
fontWeight: FontWeight.w500,
borderRadius: 12,
padding: EdgeInsets.fromLTRB(0, 10, 0, 10),
// icon: "assets/images/svg/apple-finder.svg",
),
),
),
],
),
const SizedBox(height: 20),
],
) : SizedBox.shrink(),
SizedBox(height: 20.h),
],
)
: SizedBox.shrink(),
),
),
);

@ -13,7 +13,6 @@ import 'package:hmg_patient_app_new/features/authentication/authentication_view_
import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_view_model.dart';
import 'package:hmg_patient_app_new/features/immediate_livecare/immediate_livecare_view_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/appointment_via_region_viewmodel.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/doctor_list_api_response.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/appointments/widgets/faculity_selection/facility_type_selection_widget.dart';
import 'package:hmg_patient_app_new/presentation/appointments/widgets/region_bottomsheet/region_list_widget.dart' show RegionBottomSheetBody;
@ -21,14 +20,13 @@ import 'package:hmg_patient_app_new/presentation/book_appointment/livecare/immed
import 'package:hmg_patient_app_new/presentation/book_appointment/livecare/select_immediate_livecare_clinic_page.dart';
import 'package:hmg_patient_app_new/presentation/book_appointment/search_doctor_by_name.dart';
import 'package:hmg_patient_app_new/presentation/book_appointment/select_clinic_page.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/widgets/appbar/collapsing_list_view.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart' show showCommonBottomSheetWithoutHeight;
import 'package:hmg_patient_app_new/widgets/custom_tab_bar.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/transitions/fade_page.dart';
import 'package:provider/provider.dart';
import '../appointments/widgets/hospital_bottom_sheet/hospital_bottom_sheet_body.dart';
@ -69,7 +67,6 @@ class _BookAppointmentPageState extends State<BookAppointmentPage> {
backgroundColor: AppColors.bgScaffoldColor,
body: CollapsingListView(
title: LocaleKeys.bookAppo.tr(context: context),
isLeading: Navigator.of(context).canPop(),
child: SingleChildScrollView(
child: Consumer<BookAppointmentsViewModel>(builder: (context, bookAppointmentsVM, child) {
return Column(
@ -130,7 +127,9 @@ class _BookAppointmentPageState extends State<BookAppointmentPage> {
],
),
Transform.flip(
flipX: appState.isArabic(), child: Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_icon, iconColor: AppColors.textColor, width: 40.h, height: 40.h)),
flipX: appState.isArabic(),
child: Utils.buildSvgWithAssets(
icon: AppAssets.forward_arrow_icon, iconColor: AppColors.textColor, width: 40.h, height: 40.h)),
],
).onPress(() {
bookAppointmentsViewModel.setIsClinicsListLoading(true);
@ -163,7 +162,9 @@ class _BookAppointmentPageState extends State<BookAppointmentPage> {
],
),
Transform.flip(
flipX: appState.isArabic(), child: Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_icon, iconColor: AppColors.textColor, width: 40.h, height: 40.h)),
flipX: appState.isArabic(),
child: Utils.buildSvgWithAssets(
icon: AppAssets.forward_arrow_icon, iconColor: AppColors.textColor, width: 40.h, height: 40.h)),
],
).onPress(() {
bookAppointmentsViewModel.setIsDoctorSearchByNameStarted(false);
@ -194,7 +195,9 @@ class _BookAppointmentPageState extends State<BookAppointmentPage> {
],
),
Transform.flip(
flipX: appState.isArabic(), child: Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_icon, iconColor: AppColors.textColor, width: 40.h, height: 40.h)),
flipX: appState.isArabic(),
child: Utils.buildSvgWithAssets(
icon: AppAssets.forward_arrow_icon, iconColor: AppColors.textColor, width: 40.h, height: 40.h)),
],
).onPress(() {
bookAppointmentsViewModel.setProjectID(null);
@ -214,115 +217,121 @@ class _BookAppointmentPageState extends State<BookAppointmentPage> {
Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 24.h,
hasShadow: false,
),
child: Padding(
padding: EdgeInsets.all(16.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Utils.buildSvgWithAssets(icon: AppAssets.search_by_clinic_icon, width: 40.h, height: 40.h),
SizedBox(width: 12.h),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
"Immediate Consultation".needTranslation.toText14(color: AppColors.textColor, weight: FontWeight.w500),
"Tap to select clinic".needTranslation.toText12(color: AppColors.primaryRedColor, fontWeight: FontWeight.w500),
],
),
],
),
Transform.flip(
flipX: appState.isArabic(), child: Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_icon, iconColor: AppColors.textColor, width: 40.h, height: 40.h)),
],
).onPress(() async {
//TODO Implement API to check for existing LiveCare Requests
borderRadius: 24.h,
hasShadow: false,
),
child: Padding(
padding: EdgeInsets.all(16.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Utils.buildSvgWithAssets(icon: AppAssets.search_by_clinic_icon, width: 40.h, height: 40.h),
SizedBox(width: 12.h),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
"Immediate Consultation".needTranslation.toText14(color: AppColors.textColor, weight: FontWeight.w500),
"Tap to select clinic".needTranslation.toText12(color: AppColors.primaryRedColor, fontWeight: FontWeight.w500),
],
),
],
),
Transform.flip(
flipX: appState.isArabic(),
child: Utils.buildSvgWithAssets(
icon: AppAssets.forward_arrow_icon, iconColor: AppColors.textColor, width: 40.h, height: 40.h)),
],
).onPress(() async {
//TODO Implement API to check for existing LiveCare Requests
LoaderBottomSheet.showLoader();
await immediateLiveCareViewModel.getPatientLiveCareHistory();
LoaderBottomSheet.hideLoader();
LoaderBottomSheet.showLoader();
await immediateLiveCareViewModel.getPatientLiveCareHistory();
LoaderBottomSheet.hideLoader();
if (immediateLiveCareViewModel.patientHasPendingLiveCareRequest) {
Navigator.of(context).push(
CustomPageRoute(
page: ImmediateLiveCarePendingRequestPage(),
),
);
} else {
Navigator.of(context).push(
CustomPageRoute(
page: SelectImmediateLiveCareClinicPage(),
),
);
}
}),
SizedBox(height: 16.h),
Divider(color: AppColors.borderOnlyColor.withValues(alpha: 0.1), height: 1.h),
SizedBox(height: 16.h),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Utils.buildSvgWithAssets(icon: AppAssets.search_by_doctor_icon, width: 40.h, height: 40.h),
SizedBox(width: 12.h),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
"Scheduled Consultation".needTranslation.toText14(color: AppColors.textColor, weight: FontWeight.w500),
"Tap to select clinic".needTranslation.toText12(color: AppColors.primaryRedColor, fontWeight: FontWeight.w500),
],
),
],
),
Transform.flip(
flipX: appState.isArabic(), child: Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_icon, iconColor: AppColors.textColor, width: 40.h, height: 40.h)),
],
).onPress(() {
bookAppointmentsViewModel.setIsClinicsListLoading(true);
bookAppointmentsViewModel.setIsLiveCareSchedule(true);
Navigator.of(context).push(
CustomPageRoute(
page: SelectClinicPage(),
),
);
}),
SizedBox(height: 16.h),
Divider(color: AppColors.borderOnlyColor.withValues(alpha: 0.1), height: 1.h),
SizedBox(height: 16.h),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Utils.buildSvgWithAssets(icon: AppAssets.search_by_region_icon, width: 40.h, height: 40.h),
SizedBox(width: 12.h),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
"Pharma LiveCare".needTranslation.toText14(color: AppColors.textColor, weight: FontWeight.w500),
"".needTranslation.toText12(color: AppColors.primaryRedColor, fontWeight: FontWeight.w500),
],
),
],
),
Transform.flip(
flipX: appState.isArabic(), child: Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_icon, iconColor: AppColors.textColor, width: 40.h, height: 40.h)),
],
).onPress(() {
openRegionListBottomSheet(context, RegionBottomSheetType.FOR_REGION);
}),
],
),
),
),
],
if (immediateLiveCareViewModel.patientHasPendingLiveCareRequest) {
Navigator.of(context).push(
CustomPageRoute(
page: ImmediateLiveCarePendingRequestPage(),
),
);
} else {
Navigator.of(context).push(
CustomPageRoute(
page: SelectImmediateLiveCareClinicPage(),
),
);
}
}),
SizedBox(height: 16.h),
Divider(color: AppColors.borderOnlyColor.withValues(alpha: 0.1), height: 1.h),
SizedBox(height: 16.h),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Utils.buildSvgWithAssets(icon: AppAssets.search_by_doctor_icon, width: 40.h, height: 40.h),
SizedBox(width: 12.h),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
"Scheduled Consultation".needTranslation.toText14(color: AppColors.textColor, weight: FontWeight.w500),
"Tap to select clinic".needTranslation.toText12(color: AppColors.primaryRedColor, fontWeight: FontWeight.w500),
],
),
],
),
Transform.flip(
flipX: appState.isArabic(),
child: Utils.buildSvgWithAssets(
icon: AppAssets.forward_arrow_icon, iconColor: AppColors.textColor, width: 40.h, height: 40.h)),
],
).onPress(() {
bookAppointmentsViewModel.setIsClinicsListLoading(true);
bookAppointmentsViewModel.setIsLiveCareSchedule(true);
Navigator.of(context).push(
CustomPageRoute(
page: SelectClinicPage(),
),
);
}),
SizedBox(height: 16.h),
Divider(color: AppColors.borderOnlyColor.withValues(alpha: 0.1), height: 1.h),
SizedBox(height: 16.h),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Utils.buildSvgWithAssets(icon: AppAssets.search_by_region_icon, width: 40.h, height: 40.h),
SizedBox(width: 12.h),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
"Pharma LiveCare".needTranslation.toText14(color: AppColors.textColor, weight: FontWeight.w500),
"".needTranslation.toText12(color: AppColors.primaryRedColor, fontWeight: FontWeight.w500),
],
),
],
),
Transform.flip(
flipX: appState.isArabic(),
child: Utils.buildSvgWithAssets(
icon: AppAssets.forward_arrow_icon, iconColor: AppColors.textColor, width: 40.h, height: 40.h)),
],
).onPress(() {
openRegionListBottomSheet(context, RegionBottomSheetType.FOR_REGION);
}),
],
),
),
),
],
).paddingSymmetrical(24.h, 0.h)
: getLiveCareNotLoggedInUI();
default:
@ -335,8 +344,10 @@ class _BookAppointmentPageState extends State<BookAppointmentPage> {
regionalViewModel.flush();
regionalViewModel.setBottomSheetType(type);
// AppointmentViaRegionViewmodel? viewmodel = null;
showCommonBottomSheetWithoutHeight(context, title: "", titleWidget: Consumer<AppointmentViaRegionViewmodel>(builder: (_, data, __) => getTitle(data)), isDismissible: false,
child: Consumer<AppointmentViaRegionViewmodel>(builder: (_, data, __) {
showCommonBottomSheetWithoutHeight(context,
title: "",
titleWidget: Consumer<AppointmentViaRegionViewmodel>(builder: (_, data, __) => getTitle(data)),
isDismissible: false, child: Consumer<AppointmentViaRegionViewmodel>(builder: (_, data, __) {
return getRegionalSelectionWidget(data);
}), callBackFunc: () {});
}
@ -395,7 +406,9 @@ class _BookAppointmentPageState extends State<BookAppointmentPage> {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
"Immediate service".needTranslation.toText18(color: AppColors.textColor, isBold: true),
"No need to wait, you will get medical consultation immediately via video call".needTranslation.toText14(color: AppColors.greyTextColor, weight: FontWeight.w500),
"No need to wait, you will get medical consultation immediately via video call"
.needTranslation
.toText14(color: AppColors.greyTextColor, weight: FontWeight.w500),
],
),
),
@ -427,7 +440,9 @@ class _BookAppointmentPageState extends State<BookAppointmentPage> {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
"Doctor will contact".needTranslation.toText18(color: AppColors.textColor, isBold: true),
"A specialised doctor will contact you and will be able to view your medical history".needTranslation.toText14(color: AppColors.greyTextColor, weight: FontWeight.w500),
"A specialised doctor will contact you and will be able to view your medical history"
.needTranslation
.toText14(color: AppColors.greyTextColor, weight: FontWeight.w500),
],
),
),
@ -443,7 +458,9 @@ class _BookAppointmentPageState extends State<BookAppointmentPage> {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
"Free medicine delivery".needTranslation.toText18(color: AppColors.textColor, isBold: true),
"Offers free medicine delivery for the LiveCare appointment".needTranslation.toText14(color: AppColors.greyTextColor, weight: FontWeight.w500),
"Offers free medicine delivery for the LiveCare appointment"
.needTranslation
.toText14(color: AppColors.greyTextColor, weight: FontWeight.w500),
],
),
),
@ -458,9 +475,9 @@ class _BookAppointmentPageState extends State<BookAppointmentPage> {
backgroundColor: AppColors.primaryRedColor,
borderColor: AppColors.primaryRedColor,
textColor: AppColors.whiteColor,
fontSize: 16,
fontSize: 16.f,
fontWeight: FontWeight.w500,
borderRadius: 12,
borderRadius: 12.r,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 50.h,
icon: AppAssets.login1,

@ -180,10 +180,10 @@ class DoctorsFilters extends StatelessWidget{
backgroundColor: AppColors.primaryRedColor,
borderColor: AppColors.primaryRedColor,
textColor: Colors.white,
fontSize: 16,
fontSize: 16.f,
padding: EdgeInsets.zero,
fontWeight: FontWeight.w500,
borderRadius: 12,
borderRadius: 10.r,
icon: AppAssets.add_icon,
iconColor: AppColors.primaryRedColor,
).paddingAll(24.h),

@ -46,7 +46,7 @@ class _SearchDoctorByNameState extends State<SearchDoctorByName> {
body: Column(
children: [
Expanded(
child: CollapsingListView(
child: CollapsingListView(
title: "Choose Doctor".needTranslation,
child: SingleChildScrollView(
child: Padding(
@ -250,9 +250,9 @@ class _SearchDoctorByNameState extends State<SearchDoctorByName> {
backgroundColor: AppColors.primaryRedColor,
borderColor: AppColors.primaryRedColor,
textColor: AppColors.whiteColor,
fontSize: 16,
fontSize: 16.f,
fontWeight: FontWeight.w500,
borderRadius: 12,
borderRadius: 10.r,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 50.h,
icon: AppAssets.search_icon,

@ -107,9 +107,9 @@ class SelectLivecareClinicPage extends StatelessWidget {
backgroundColor: AppColors.primaryRedColor,
borderColor: AppColors.primaryRedColor,
textColor: AppColors.whiteColor,
fontSize: 16,
fontSize: 16.f,
fontWeight: FontWeight.w500,
borderRadius: 12,
borderRadius: 10.r,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 50.h,
icon: AppAssets.livecare_book_icon,
@ -122,18 +122,13 @@ class SelectLivecareClinicPage extends StatelessWidget {
onPressed: () {
Navigator.of(context).pop();
onNegativeClicked?.call();
// Navigator.of(context).push(
// CustomPageRoute(
// page: SelectDoctorPage(),
// ),
// );
},
backgroundColor: AppColors.secondaryLightRedColor,
borderColor: AppColors.secondaryLightRedColor,
textColor: AppColors.primaryRedColor,
fontSize: 16,
fontSize: 16.f,
fontWeight: FontWeight.w500,
borderRadius: 12,
borderRadius: 10.r,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 50.h,
).paddingSymmetrical(24.h, 0.h),

@ -111,7 +111,7 @@ class _AppointmentCalendarState extends State<AppointmentCalendar> {
selectionDecoration: ShapeDecoration(
color: AppColors.transparent,
shape: SmoothRectangleBorder(
borderRadius: BorderRadius.circular(12 ?? 0),
borderRadius: BorderRadius.circular(10.r),
smoothness: 1,
side: BorderSide(color: AppColors.primaryRedColor, width: 1.5),
),
@ -246,10 +246,10 @@ class _AppointmentCalendarState extends State<AppointmentCalendar> {
backgroundColor: AppColors.primaryRedColor,
borderColor: AppColors.primaryRedColor,
textColor: AppColors.whiteColor,
fontSize: 16,
fontSize: 16.f,
fontWeight: FontWeight.w500,
borderRadius: 12,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
borderRadius: 10.r,
padding: EdgeInsets.symmetric(horizontal: 10.w),
height: 50.h,
),
],

@ -16,11 +16,17 @@ import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart';
import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart';
class DoctorCard extends StatelessWidget {
DoctorCard({super.key, required this.doctorsListResponseModel, required this.isLoading, required this.bookAppointmentsViewModel});
const DoctorCard({
super.key,
required this.doctorsListResponseModel,
required this.isLoading,
required this.bookAppointmentsViewModel,
});
DoctorsListResponseModel doctorsListResponseModel;
bool isLoading = false;
BookAppointmentsViewModel bookAppointmentsViewModel;
final DoctorsListResponseModel doctorsListResponseModel;
final bool isLoading;
final BookAppointmentsViewModel bookAppointmentsViewModel;
@override
Widget build(BuildContext context) {
@ -55,10 +61,14 @@ class DoctorCard extends StatelessWidget {
children: [
SizedBox(
width: MediaQuery.of(context).size.width * 0.49,
child: (isLoading ? "Dr John Smith" : "${doctorsListResponseModel.doctorTitle} ${doctorsListResponseModel.name}").toString().toText16(isBold: true, maxlines: 1),
child: (isLoading ? "Dr John Smith" : "${doctorsListResponseModel.doctorTitle} ${doctorsListResponseModel.name}")
.toString()
.toText16(isBold: true, maxlines: 1),
).toShimmer2(isShow: isLoading),
Image.network(
isLoading ? "https://hmgwebservices.com/Images/flag/SYR.png" : doctorsListResponseModel.nationalityFlagURL ?? "https://hmgwebservices.com/Images/flag/SYR.png",
isLoading
? "https://hmgwebservices.com/Images/flag/SYR.png"
: doctorsListResponseModel.nationalityFlagURL ?? "https://hmgwebservices.com/Images/flag/SYR.png",
width: 20.h,
height: 15.h,
fit: BoxFit.fill,
@ -79,7 +89,8 @@ class DoctorCard extends StatelessWidget {
),
Expanded(
flex: 1,
child: Utils.buildSvgWithAssets(icon: AppAssets.doctor_profile_icon, width: 20.h, height: 20.h, fit: BoxFit.scaleDown).toShimmer2(isShow: isLoading),
child: Utils.buildSvgWithAssets(icon: AppAssets.doctor_profile_icon, width: 20.h, height: 20.h, fit: BoxFit.scaleDown)
.toShimmer2(isShow: isLoading),
),
],
),
@ -159,10 +170,10 @@ class DoctorCard extends StatelessWidget {
backgroundColor: Color(0xffFEE9EA),
borderColor: Color(0xffFEE9EA),
textColor: Color(0xffED1C2B),
fontSize: 14,
fontSize: 14.f,
fontWeight: FontWeight.w500,
borderRadius: 12,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
borderRadius: 10.r,
padding: EdgeInsets.fromLTRB(10.h, 0, 10.h, 0),
height: 40.h,
icon: AppAssets.add_icon,
iconColor: AppColors.primaryRedColor,

@ -0,0 +1,114 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/core/location_util.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/contact_us/contact_us_view_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/contact_us/find_us_page.dart';
import 'package:hmg_patient_app_new/presentation/contact_us/live_chat_page.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
import 'package:provider/provider.dart';
class ContactUs extends StatelessWidget {
ContactUs({super.key});
late AppState appState;
late ContactUsViewModel contactUsViewModel;
late LocationUtils locationUtils;
@override
Widget build(BuildContext context) {
appState = getIt.get<AppState>();
locationUtils = getIt.get<LocationUtils>();
locationUtils.isShowConfirmDialog = true;
contactUsViewModel = Provider.of<ContactUsViewModel>(context);
return Column(
children: [
checkInOptionCard(
AppAssets.checkin_location_icon,
LocaleKeys.findUs.tr(),
"View your nearest HMG locations".needTranslation,
).onPress(() {
locationUtils.getCurrentLocation(onSuccess: (value) {
contactUsViewModel.initContactUsViewModel();
Navigator.pop(context);
Navigator.of(context).push(
CustomPageRoute(
page: FindUsPage(),
),
);
});
}),
SizedBox(height: 16.h),
checkInOptionCard(
AppAssets.checkin_location_icon,
LocaleKeys.feedback.tr(),
"Provide your feedback on our services".needTranslation,
),
SizedBox(height: 16.h),
checkInOptionCard(
AppAssets.checkin_location_icon,
LocaleKeys.liveChat.tr(),
"Live chat option with HMG".needTranslation,
).onPress(() {
locationUtils.getCurrentLocation(onSuccess: (value) {
Navigator.pop(context);
Navigator.of(context).push(
CustomPageRoute(
page: LiveChatPage(),
),
);
});
}),
],
);
}
Widget checkInOptionCard(String icon, String title, String subTitle) {
return Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 20.r,
hasShadow: false,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Utils.buildSvgWithAssets(icon: icon, width: 40.h, height: 40.h, fit: BoxFit.fill),
SizedBox(height: 16.h),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
title.toText16(isBold: true, color: AppColors.textColor),
subTitle.toText12(fontWeight: FontWeight.w500, color: AppColors.greyTextColor),
],
),
),
Transform.flip(
flipX: appState.isArabic(),
child: Utils.buildSvgWithAssets(
icon: AppAssets.forward_arrow_icon_small,
iconColor: AppColors.blackColor,
width: 18.h,
height: 13.h,
fit: BoxFit.contain,
),
),
],
),
],
).paddingAll(16.h),
);
}
}

@ -0,0 +1,165 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/contact_us/contact_us_view_model.dart';
import 'package:hmg_patient_app_new/features/medical_file/models/patient_sickleave_response_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/contact_us/widgets/find_us_item_card.dart';
import 'package:hmg_patient_app_new/presentation/medical_file/widgets/patient_sick_leave_card.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/chip/app_custom_chip_widget.dart';
import 'package:hmg_patient_app_new/widgets/custom_tab_bar.dart';
import 'package:provider/provider.dart';
class FindUsPage extends StatelessWidget {
FindUsPage({super.key});
late AppState appState;
late ContactUsViewModel contactUsViewModel;
@override
Widget build(BuildContext context) {
contactUsViewModel = Provider.of<ContactUsViewModel>(context);
appState = getIt.get<AppState>();
return Scaffold(
backgroundColor: AppColors.bgScaffoldColor,
body: CollapsingListView(
title: LocaleKeys.location.tr(),
child: Consumer<ContactUsViewModel>(builder: (context, contactUsVM, child) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 16.h),
contactUsVM.isHMGLocationsListLoading
? SizedBox.shrink()
: CustomTabBar(
activeTextColor: AppColors.primaryRedColor,
activeBackgroundColor: AppColors.primaryRedColor.withValues(alpha: .1),
tabs: [
CustomTabBarModel(null, LocaleKeys.hmgHospitals.tr()),
CustomTabBarModel(null, LocaleKeys.pharmaciesList.tr()),
],
onTabChange: (index) {
contactUsVM.setHMGHospitalsListSelected(index == 0);
},
).paddingSymmetrical(24.h, 0.h),
ListView.separated(
padding: EdgeInsets.only(top: 16.h),
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemCount: contactUsVM.isHMGLocationsListLoading
? 5
: contactUsVM.isHMGHospitalsListSelected
? contactUsVM.hmgHospitalsLocationsList.length
: contactUsVM.hmgPharmacyLocationsList.length,
itemBuilder: (context, index) {
return contactUsVM.isHMGLocationsListLoading
? Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: true),
child: Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 20.h,
hasShadow: true,
),
child: Padding(
padding: EdgeInsets.all(14.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Image.network(
"https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png",
width: 63.h,
height: 63.h,
fit: BoxFit.cover,
).circle(100).toShimmer2(isShow: true),
SizedBox(width: 16.h),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
"Dr John Smith".toText16(isBold: true).toShimmer2(isShow: true),
SizedBox(height: 8.h),
Wrap(
direction: Axis.horizontal,
spacing: 3.h,
runSpacing: 4.h,
children: [
AppCustomChipWidget(labelText: "").toShimmer2(isShow: true, width: 16.h),
AppCustomChipWidget(labelText: "").toShimmer2(isShow: true, width: 16.h),
],
),
],
),
),
],
),
],
),
),
),
).paddingSymmetrical(24.h, 0.h)
: contactUsVM.isHMGHospitalsListSelected
// ? contactUsVM.hmgHospitalsLocationsList.isNotEmpty
? AnimationConfiguration.staggeredList(
position: index,
duration: const Duration(milliseconds: 500),
child: SlideAnimation(
verticalOffset: 100.0,
child: FadeInAnimation(
child: AnimatedContainer(
duration: Duration(milliseconds: 300),
curve: Curves.easeInOut,
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: true),
child: FindUsItemCard(
getHMGLocationsModel: contactUsVM.hmgHospitalsLocationsList[index],
),
).paddingSymmetrical(24.h, 0.h),
),
),
)
: AnimationConfiguration.staggeredList(
position: index,
duration: const Duration(milliseconds: 500),
child: SlideAnimation(
verticalOffset: 100.0,
child: FadeInAnimation(
child: AnimatedContainer(
duration: Duration(milliseconds: 300),
curve: Curves.easeInOut,
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: true),
child: FindUsItemCard(
getHMGLocationsModel: contactUsVM.hmgPharmacyLocationsList[index],
),
).paddingSymmetrical(24.h, 0.h),
),
),
);
// : Utils.getNoDataWidget(
// context,
// noDataText: "No any locations yet.".needTranslation,
// );
},
separatorBuilder: (BuildContext cxt, int index) => SizedBox(height: 16.h),
),
SizedBox(height: 24.h),
// FindUsItemCard(),
// FindUsItemCard(),
// FindUsItemCard(),
],
);
}),
),
);
}
}

@ -0,0 +1,27 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
class LiveChatPage extends StatelessWidget {
const LiveChatPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: AppColors.bgScaffoldColor,
body: Column(
children: [
Expanded(
child: CollapsingListView(
title: LocaleKeys.liveChat.tr(),
child: SingleChildScrollView(),
),
),
Container()
],
),
);
}
}

@ -0,0 +1,106 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/contact_us/models/resp_models/get_hmg_locations.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart';
import 'package:maps_launcher/maps_launcher.dart';
import 'package:url_launcher/url_launcher.dart';
class FindUsItemCard extends StatelessWidget {
FindUsItemCard({super.key, required this.getHMGLocationsModel});
late AppState appState;
GetHMGLocationsModel getHMGLocationsModel;
@override
Widget build(BuildContext context) {
appState = getIt.get<AppState>();
return DecoratedBox(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 20.r,
hasShadow: false,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
spacing: 8.h,
children: [hospitalName, distanceInfo],
),
),
],
).paddingSymmetrical(16.h, 16.h),
);
}
Widget get hospitalName => Row(
children: [
Image.network(
getHMGLocationsModel.projectImageURL ?? "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png",
width: 40.h,
height: 40.h,
fit: BoxFit.cover,
).circle(100).toShimmer2(isShow: false).paddingOnly(right: 10),
Expanded(
child: Text(
getHMGLocationsModel.locationName!,
style: TextStyle(
fontWeight: FontWeight.w600,
fontSize: 16,
color: AppColors.blackColor,
),
),
)
],
);
Widget get distanceInfo => Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
AppCustomChipWidget(
labelText: "${getHMGLocationsModel.distanceInKilometers ?? ""} km".needTranslation,
icon: AppAssets.location_red,
iconColor: AppColors.primaryRedColor,
backgroundColor: AppColors.secondaryLightRedColor,
textColor: AppColors.errorColor,
),
Row(
children: [
AppCustomChipWidget(
labelText: "Get Directions".needTranslation,
icon: AppAssets.directions_icon,
iconColor: AppColors.whiteColor,
backgroundColor: AppColors.textColor.withValues(alpha: 0.8),
textColor: AppColors.whiteColor,
onChipTap: () {
MapsLauncher.launchCoordinates(double.parse(getHMGLocationsModel.latitude ?? "0.0"), double.parse(getHMGLocationsModel.longitude ?? "0.0"), getHMGLocationsModel.locationName!);
},
),
SizedBox(width: 4.w),
AppCustomChipWidget(
labelText: LocaleKeys.callNow.tr(),
icon: AppAssets.call_fill,
iconColor: AppColors.whiteColor,
backgroundColor: AppColors.primaryRedColor.withValues(alpha: 1.0),
textColor: AppColors.whiteColor,
onChipTap: () {
launchUrl(Uri.parse("tel://" + "${getHMGLocationsModel.phoneNumber}"));
},
),
],
),
],
);
}

@ -12,8 +12,8 @@ class RrtRequestTypeSelect extends StatelessWidget {
@override
Widget build(BuildContext context) {
// return Consumer<EmergencyServicesViewModel>(builder: (context, emergencyServicesVM, child) {
return Column(
return Consumer<EmergencyServicesViewModel>(builder: (context, emergencyServicesVM, child) {
return Column(
children: [
Container(
padding: EdgeInsets.all(16.h),
@ -41,6 +41,6 @@ class RrtRequestTypeSelect extends StatelessWidget {
SizedBox(height: 32.h),
],
);
// });
});
}
}

@ -8,10 +8,19 @@ 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/emergency_services/emergency_services_view_model.dart';
import 'package:hmg_patient_app_new/features/emergency_services/models/AmbulanceCallingPlace.dart';
import 'package:hmg_patient_app_new/features/location/GeocodeResponse.dart';
import 'package:hmg_patient_app_new/features/location/PlaceDetails.dart';
import 'package:hmg_patient_app_new/features/location/PlacePrediction.dart';
import 'package:hmg_patient_app_new/features/location/location_view_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/appointments/widgets/appointment_checkin_bottom_sheet.dart';
import 'package:hmg_patient_app_new/presentation/appointments/widgets/appointment_doctor_card.dart';
import 'package:hmg_patient_app_new/presentation/emergency_services/call_ambulance/requesting_services_page.dart' show RequestingServicesPage;
import 'package:hmg_patient_app_new/presentation/emergency_services/call_ambulance/tracking_screen.dart' show TrackingScreen;
import 'package:hmg_patient_app_new/presentation/emergency_services/call_ambulance/widgets/HospitalBottomSheetBody.dart';
import 'package:hmg_patient_app_new/presentation/emergency_services/call_ambulance/widgets/appointment_bottom_sheet.dart' show AppointmentBottomSheet;
import 'package:hmg_patient_app_new/presentation/emergency_services/widgets/location_input_bottom_sheet.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/CustomSwitch.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
@ -30,38 +39,79 @@ class CallAmbulancePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
bottomSheet: ExpandableBottomSheet(
bottomSheetType:
context.watch<EmergencyServicesViewModel>().bottomSheetType,
children: {
BottomSheetType.EXPANDED: ExpanedBottomSheet(context),
BottomSheetType.FIXED: FixedBottomSheet(context),
},
floatingActionButton: Visibility(
visible: context.watch<EmergencyServicesViewModel>().bottomSheetType ==
BottomSheetType.FIXED,
child: Padding(
padding: EdgeInsetsDirectional.only(end: 8.h, bottom: 68.h),
child: DecoratedBox(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor, borderRadius: 12.h),
child: Utils.buildSvgWithAssets(
icon: AppAssets.locate_me, width: 24.h, height: 24.h)
.paddingAll(12.h)
.onPress(() {
context
.read<EmergencyServicesViewModel>()
.moveToCurrentLocation();
}),
),
),
),
bottomSheet: Column(
mainAxisSize: MainAxisSize.min,
children: [
ExpandableBottomSheet(
bottomSheetType:
context.watch<EmergencyServicesViewModel>().bottomSheetType,
children: {
BottomSheetType.EXPANDED: ExpanedBottomSheet(context),
BottomSheetType.FIXED: FixedBottomSheet(context),
},
),
],
),
body: Stack(
children: [
if (context.read<EmergencyServicesViewModel>().isGMSAvailable ||
Platform.isIOS)
if (context.read<EmergencyServicesViewModel>().isGMSAvailable )
GMSMap(
currentLocation:
context.read<EmergencyServicesViewModel>().getGMSLocation(),
onCameraMoved: (value) => context
.read<EmergencyServicesViewModel>()
.handleGMSMapCameraMoved(value))
.read<LocationViewModel>()
.handleGMSMapCameraMoved(value),
onCameraIdle:
context.read<LocationViewModel>().handleOnCameraIdle,
myLocationEnabled: true,
inputController:
context.read<EmergencyServicesViewModel>().gmsController,
showCenterMarker: true,
)
else
HMSMap(
currentLocation:
context.read<EmergencyServicesViewModel>().getHMSLocation(),
onCameraMoved: (value) => context
.read<EmergencyServicesViewModel>()
.handleHMSMapCameraMoved(value)),
.read<LocationViewModel>()
.handleHMSMapCameraMoved(value),
onCameraIdle:
context.read<LocationViewModel>().handleOnCameraIdle,
myLocationEnabled: false,
inputController:
context.read<EmergencyServicesViewModel>().hmsController,
showCenterMarker: true,
),
Align(
alignment: AlignmentDirectional.topStart,
child: Utils.buildSvgWithAssets(
icon: AppAssets.closeBottomNav, width: 32.h, height: 32.h),
).paddingOnly(top: 51.h, left: 24.h).onPress((){
Navigator.pop(context);
})
icon: AppAssets.closeBottomNav, width: 32.h, height: 32.h)
.onPress(() {
context
.read<EmergencyServicesViewModel>()
.flushPickupInformation();
Navigator.pop(context);
}),
).paddingOnly(top: 51.h, left: 24.h),
],
),
);
@ -70,33 +120,34 @@ class CallAmbulancePage extends StatelessWidget {
Widget FixedBottomSheet(BuildContext context) {
return GestureDetector(
onVerticalDragUpdate: (details){
print("the delta is ${details.delta.dy}");
if(details.delta.dy<0){
context.read<EmergencyServicesViewModel>().updateBottomSheetState(BottomSheetType.EXPANDED);
}
// if(details.delta.dy<0){
//
// context.read<EmergencyServicesViewModel>().updateBottomSheetState(BottomSheetType.EXPANDED);
// }
},
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Padding(
padding: EdgeInsetsDirectional.only(end:24.h, bottom: 24.h),
child: DecoratedBox(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor, borderRadius: 12.h),
child: Utils.buildSvgWithAssets(
icon: AppAssets.locate_me, width: 24.h, height: 24.h)
.paddingAll(12.h),
),
),
],
),
// Row(
// mainAxisAlignment: MainAxisAlignment.end,
// children: [
// Padding(
//
// padding: EdgeInsetsDirectional.only(end:24.h, bottom: 24.h),
// child: DecoratedBox(
// decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
// color: AppColors.whiteColor, borderRadius: 12.h),
// child: Utils.buildSvgWithAssets(
// icon: AppAssets.locate_me, width: 24.h, height: 24.h)
// .paddingAll(12.h).onPress((){
// context.read<EmergencyServicesViewModel>().moveToCurrentLocation();
// }),
// ),
// ),
// ],
// ),
// PositionedDirectional(
// end: 1,
// child: Padding(
@ -116,25 +167,7 @@ class CallAmbulancePage extends StatelessWidget {
mainAxisSize: MainAxisSize.min,
spacing: 24.h,
children: [
TextInputWidget(
labelText: "Enter Pickup Location Manually".needTranslation,
hintText: "Enter Pickup Location".needTranslation,
leadingIcon: AppAssets.location_pickup,
isAllowLeadingIcon: true,
isEnable: false,
prefix: null,
autoFocus: false,
isBorderAllowed: false,
keyboardType: TextInputType.text,
padding: EdgeInsets.symmetric(
vertical: ResponsiveExtension(10).h,
horizontal: ResponsiveExtension(15).h,
),
).onPress(() {
context
.read<EmergencyServicesViewModel>()
.updateBottomSheetState(BottomSheetType.EXPANDED);
}).paddingOnly(right: 24.h, left: 24.h),
inputFields(context),
SizedBox(
height: 200.h,
child: DecoratedBox(
@ -152,11 +185,11 @@ class CallAmbulancePage extends StatelessWidget {
Column(
spacing: 4.h,
children: [
"Select Pickup Location".needTranslation.toText21(
"Select Pickup Details".needTranslation.toText21(
weight: FontWeight.w600,
color: AppColors.textColor,
),
" Please select the location of pickup"
" Please select the details of pickup"
.needTranslation
.toText12(
fontWeight: FontWeight.w500,
@ -165,7 +198,7 @@ class CallAmbulancePage extends StatelessWidget {
],
),
CustomButton(
text: "Select Location".needTranslation,
text: "Select Details".needTranslation,
onPressed: () {
context
.read<EmergencyServicesViewModel>()
@ -202,21 +235,25 @@ class CallAmbulancePage extends StatelessWidget {
),
child: Column(
children: [
SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
spacing: 16.h,
children: [
"Select Pickup Location".needTranslation.toText21(
weight: FontWeight.w600,
color: AppColors.textColor,
),
locationsSections(context),
hospitalAndPickUpSection(context),
],
).paddingOnly(top: 24.h, bottom: 32.h,left: 24.h, right: 24.h),
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
spacing: 16.h,
children: [
hospitalAndPickUpSection(context),
if(context.read<EmergencyServicesViewModel>().appointment != null)
AppointmentDoctorCard(
renderWidgetForERDisplay: true,
patientAppointmentHistoryResponseModel: context.read<EmergencyServicesViewModel>().appointment!,
onAskDoctorTap: () {},
onCancelTap: () async {},
onRescheduleTap: () async {},
).onPress((){
openAppointmentList(context);
})
],
).paddingOnly(top: 24.h, bottom: 32.h,left: 24.h, right: 24.h),
bottomPriceContent(context)
],
@ -281,44 +318,74 @@ class CallAmbulancePage extends StatelessWidget {
return DecoratedBox(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 24.h,
borderRadius: 24.r,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
spacing: 16.h,
children: [
// Row(
// children: [
// hospitalAndPickUpItemContent(
// title: "Select Hospital".needTranslation,
// subTitle: "hospital".needTranslation,
// leadingIcon: AppAssets.hospital,
// ),
// Utils.buildSvgWithAssets(icon: AppAssets.down_cheveron,
// width: 24.h, height: 24.h)
// .paddingAll(16.h)
// ],
// ).onPress((){
// showHospitalBottomSheet(context);
// }),
// SizedBox(height: 16.h),
// Divider(
// color: AppColors.bottomNAVBorder,
// height: 1,
// thickness: 1,
// ),
// SizedBox(height: 16.h),
Row(
children: [
hospitalAndPickUpItemContent(
title: "Select Hospital".needTranslation,
subTitle: "hospital".needTranslation,
leadingIcon: AppAssets.hospital,
title: "Pick".needTranslation,
subTitle: "Inside the home".needTranslation,
leadingIcon: AppAssets.pickup_bed,
),
Utils.buildSvgWithAssets(icon: AppAssets.down_cheveron,
width: 24.h, height: 24.h)
.paddingAll(16.h)
CustomSwitch(
value: context
.watch<EmergencyServicesViewModel>()
.pickupFromInsideTheLocation,
onChanged: (value){
context
.read<EmergencyServicesViewModel>()
.updateThePickupPlaceFromLocation(value);
},
)
],
).onPress((){
showHospitalBottomSheet(context);
}),
SizedBox(height: 16.h),
Divider(
color: AppColors.bottomNAVBorder,
height: 1,
thickness: 1,
),
SizedBox(height: 16.h),
Row(
children: [
hospitalAndPickUpItemContent(
title: "Pick".needTranslation,
subTitle: "Inside the home".needTranslation,
leadingIcon: AppAssets.pickup_bed,
title: '',
subTitle: "Have any appointment".needTranslation,
leadingIcon: AppAssets.appointment_checkin_icon,
),
CustomSwitch(
value: context.watch<EmergencyServicesViewModel>().isGMSAvailable,
onChanged: (value){
context.read<EmergencyServicesViewModel>().setIsGMSAvailable( value);
value: context
.watch<EmergencyServicesViewModel>()
.haveAnAppointment,
onChanged: (value) async {
// if (value) {
// openAppointmentList(context);
// }
await context.read<EmergencyServicesViewModel>()
.updateAppointment(value);
if (context.read<EmergencyServicesViewModel>().appointments?.isNotEmpty == true) {
openAppointmentList(context);
}
},
)
],
@ -354,13 +421,22 @@ class CallAmbulancePage extends StatelessWidget {
this.leadingIcon(leadingIcon),
Expanded(
child: Column(
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: [
title.toText12(
color: AppColors.greyTextColor,
fontWeight: FontWeight.w500,
Visibility(
visible: title.isNotEmpty,
child: Column(
children: [
title.toText12(
color: AppColors.greyTextColor,
fontWeight: FontWeight.w500,
),
SizedBox(height: 2.h),
],
),
),
SizedBox(height: 2.h),
subTitle.toText14(
color: AppColors.textColor,
weight: FontWeight.w500,
@ -418,17 +494,30 @@ class CallAmbulancePage extends StatelessWidget {
],
),
),
"\$250".toText24(
fontWeight: FontWeight.w600,
color: AppColors.textColor,
)
],
),
CustomButton(
text: "Submit Request".needTranslation,
onPressed: () {
Navigator.push(context, CustomPageRoute(page: RequestingServicesPage()));
})
Utils.getPaymentAmountWithSymbol(
(Utils.formatNumberToInternationalFormat(context
.read<EmergencyServicesViewModel>()
.getTotalPrice() ??
0))
.toText24(
fontWeight: FontWeight.w600,
color: AppColors.textColor,
letterSpacing: -2),
AppColors.blackColor,
17.h)
// Utils.getPaymentAmountWithSymbol2(context.read<EmergencyServicesViewModel>().selectedTransportOption?.priceTotal??"0", letterSpacing: -2)
],
),
CustomButton(
text: "Submit Request".needTranslation,
onPressed: () {
LocationViewModel locationViewModel = context.read<LocationViewModel>();
GeocodeResponse? response = locationViewModel.geocodeResponse;
PlaceDetails? placeDetails = locationViewModel.placeDetails;
PlacePrediction? placePrediction = locationViewModel.selectedPrediction;
context.read<EmergencyServicesViewModel>().submitAmbulanceRequest(response?.results.first, placeDetails, placePrediction);
})
],
).paddingOnly(top: 24.h, bottom: 12.h, left: 24.h, right: 24.h),
),
@ -441,7 +530,105 @@ class CallAmbulancePage extends StatelessWidget {
title:
LocaleKeys.selectHospital.tr(),
context,
child: HospitalBottomSheetBody(),
child: Consumer<EmergencyServicesViewModel>(
builder:(_,vm,__)=> HospitalBottomSheetBody(
searchText: vm.searchController,
displayList: vm.displayList,
onFacilityClicked: (value) {
vm.setSelectedFacility(value);
vm.getDisplayList();
},
onHospitalClicked: (hospital) {
Navigator.pop(context);
vm.setSelectedHospital(hospital);
},
onHospitalSearch: (value) {
vm.searchHospitals(value ?? "");
},
selectedFacility:
vm.selectedFacility,
hmcCount: vm.hmcCount,
hmgCount: vm.hmgCount,
),
),
isFullScreen: false,
isCloseButtonVisible: true,
hasBottomPadding: false,
backgroundColor: AppColors.bottomSheetBgColor,
callBackFunc: () {
context.read<EmergencyServicesViewModel>().clearSearchText();
},
);
}
///it will show the places field first and then hospital field
PlaceFirstThanHospitalField(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
spacing: 16.h,
children: [
textPlaceInput(context),
hospitalField(context),
],
).paddingOnly(right: 24.h, left: 24.h);
}
HospitalFieldFirstThanPlaces(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
spacing: 16.h,
children: [hospitalField(context), textPlaceInput(context)],
).paddingOnly(right: 24.h, left: 24.h);
}
textPlaceInput(context) {
return Consumer<LocationViewModel>(builder: (_, vm, __) {
print(
"the data is ${vm.geocodeResponse?.results.first.formattedAddress ?? vm.selectedPrediction?.description}");
return SizedBox(
width: MediaQuery.sizeOf(context).width,
child: TextInputWidget(
labelText: "Enter Pickup Location Manually".needTranslation,
hintText: "Enter Pickup Location".needTranslation,
controller: TextEditingController(
text: vm.geocodeResponse?.results.first.formattedAddress ??
vm.selectedPrediction?.description,
),
leadingIcon: AppAssets.location_pickup,
isAllowLeadingIcon: true,
isEnable: false,
prefix: null,
autoFocus: false,
isBorderAllowed: false,
keyboardType: TextInputType.text,
padding: EdgeInsets.symmetric(
vertical: ResponsiveExtension(10).h,
horizontal: ResponsiveExtension(15).h,
),
).onPress(() {
openLocationInputBottomSheet(context);
}),
);
});
}
///decide which field to show first based on the selected calling place
inputFields(BuildContext context) {
return context.read<EmergencyServicesViewModel>().callingPlace ==
AmbulanceCallingPlace.FROM_HOSPITAL
? HospitalFieldFirstThanPlaces(context)
: PlaceFirstThanHospitalField(context);
}
openLocationInputBottomSheet(BuildContext context) {
context.read<LocationViewModel>().flushSearchPredictions();
showCommonBottomSheetWithoutHeight(
title: "".needTranslation,
context,
child: SizedBox(
height: MediaQuery.sizeOf(context).height * .8,
child: LocationInputBottomSheet(),
),
isFullScreen: false,
isCloseButtonVisible: true,
hasBottomPadding: false,
@ -449,4 +636,56 @@ class CallAmbulancePage extends StatelessWidget {
callBackFunc: () {},
);
}
hospitalField(BuildContext context) {
return DecoratedBox(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor, borderRadius: 12.h),
child: Row(
children: [
hospitalAndPickUpItemContent(
title: "Select Hospital".needTranslation,
subTitle: context
.read<EmergencyServicesViewModel>()
.getSelectedHospitalName() ??
"Select Hospital".needTranslation,
leadingIcon: AppAssets.hospital,
),
Utils.buildSvgWithAssets(
icon: AppAssets.down_cheveron, width: 24.h, height: 24.h)
.paddingAll(16.h)
],
).onPress(() {
print("the item is clicked");
showHospitalBottomSheet(context);
}).paddingSymmetrical(
10.w,
12.h,
),
);
}
void openAppointmentList(BuildContext context) {
showCommonBottomSheetWithoutHeight(
title: "Select Appointment".needTranslation,
context,
child: SizedBox(
height: MediaQuery.sizeOf(context).height * .5,
child: AppointmentBottomSheet(
list: context.read<EmergencyServicesViewModel>().appointments!,
onAppointmentSelection: (appointment) {
Navigator.pop(context);
context.read<EmergencyServicesViewModel>().setSelectedAppointment(appointment);
}),
),
isFullScreen: false,
isCloseButtonVisible: true,
hasBottomPadding: false,
backgroundColor: AppColors.bottomSheetBgColor,
callBackFunc: () {
context.read<EmergencyServicesViewModel>().checkHasAppointment();
},
);
// );
}
}

@ -5,27 +5,17 @@ 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/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/emergency_services/models/request_model/PatientER_RC.dart';
import 'package:hmg_patient_app_new/presentation/emergency_services/call_ambulance/tracking_screen.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart'
show CustomPageRoute;
import 'package:lottie/lottie.dart';
class RequestingServicesPage extends StatefulWidget {
@override
State<RequestingServicesPage> createState() => _RequestingServicesPageState();
}
class RequestingServicesPage extends StatelessWidget {
class _RequestingServicesPageState extends State<RequestingServicesPage> {
@override
void initState() {
Timer(Duration(seconds: 3), () {
Navigator.pushReplacement(
context, CustomPageRoute(page: TrackingScreen()));
});
super.initState();
}
const RequestingServicesPage({super.key,});
@override
Widget build(BuildContext context) {
return Material(

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save