Comprehensive Checkup Complete Flow (faiz_dev) #98

Merged
Haroon6138 merged 11 commits from faiz_dev into master 4 months ago

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 193 KiB

@ -0,0 +1,6 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M9 3.75C9 2.09315 10.3431 0.75 12 0.75C13.6569 0.75 15 2.09315 15 3.75C15 4.82282 14.4369 5.76411 13.5901 6.29442C16.5251 6.71056 18.309 9.97478 17.1349 12.8582C16.8794 13.4857 16.2803 13.9167 15.5919 13.9167H15.0998L14.272 17.4022C14.019 18.4673 13.0967 19.25 12 19.25C10.9033 19.25 9.98103 18.4673 9.72805 17.4022L8.9002 13.9167L8.40809 13.9167C7.71969 13.9167 7.12056 13.4857 6.86508 12.8582C5.69103 9.97478 7.47495 6.71056 10.4099 6.29442C9.56313 5.76411 9 4.82282 9 3.75Z" fill="white"/>
<path d="M7 15.25H6C5.44772 15.25 5 15.6977 5 16.25C5 16.8023 5.44772 17.25 6 17.25H7C7.55228 17.25 8 16.8023 8 16.25C8 15.6977 7.55228 15.25 7 15.25Z" fill="white"/>
<path d="M17 15.25C16.4477 15.25 16 15.6977 16 16.25C16 16.8023 16.4477 17.25 17 17.25H18C18.5523 17.25 19 16.8023 19 16.25C19 15.6977 18.5523 15.25 18 15.25H17Z" fill="white"/>
<path d="M13 21.25C13 20.6977 12.5523 20.25 12 20.25C11.4477 20.25 11 20.6977 11 21.25V22.25C11 22.8023 11.4477 23.25 12 23.25C12.5523 23.25 13 22.8023 13 22.25V21.25Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

@ -0,0 +1,7 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M14.0493 1.25C14.7141 1.24997 15.287 1.24994 15.7457 1.31161C16.2373 1.37771 16.7086 1.52677 17.0908 1.90902C17.4731 2.29126 17.6222 2.76253 17.6883 3.25416C17.7499 3.7129 17.7499 4.28574 17.7499 4.95059V6.04937C17.7499 6.71423 17.7499 7.28712 17.6883 7.74585C17.6222 8.23749 17.4731 8.70875 17.0908 9.091C16.7086 9.47324 16.2373 9.6223 15.7457 9.6884C15.287 9.75008 14.7141 9.75004 14.0492 9.75001L13.2269 9.75001L10.4159 11.624C10.1857 11.7775 9.88983 11.7918 9.64597 11.6613C9.4021 11.5307 9.24986 11.2766 9.24986 11V9.7469C8.87641 9.74158 8.54263 9.7272 8.25402 9.6884C7.76238 9.6223 7.29112 9.47324 6.90887 9.091C6.52662 8.70875 6.37756 8.23749 6.31147 7.74585C6.24979 7.28712 6.24982 6.71427 6.24986 6.04943V4.95063C6.24982 4.28579 6.24979 3.7129 6.31147 3.25416C6.37756 2.76253 6.52662 2.29126 6.90887 1.90902C7.29112 1.52677 7.76238 1.37771 8.25402 1.31161C8.71274 1.24994 9.28556 1.24997 9.95039 1.25L14.0493 1.25Z" fill="white"/>
<path d="M3.17258 18.1177C4.89963 16.9608 7.10028 16.9608 8.82733 18.1177C9.13739 18.3137 9.84338 18.7614 10.2141 19.1697C10.4495 19.4288 10.6975 19.7967 10.7428 20.2626C10.7914 20.7627 10.5934 21.2221 10.2316 21.6099C9.68447 22.1963 8.95287 22.75 7.95957 22.75H4.04034C3.04704 22.75 2.31543 22.1963 1.76831 21.6099C1.40651 21.2221 1.20853 20.7627 1.25712 20.2626C1.30239 19.7967 1.55044 19.4288 1.78577 19.1697C2.15652 18.7614 2.86252 18.3137 3.17258 18.1177Z" fill="white"/>
<path d="M20.8273 18.1177C19.1003 16.9608 16.8996 16.9608 15.1726 18.1177C14.8625 18.3137 14.1565 18.7614 13.7858 19.1697C13.5504 19.4288 13.3024 19.7967 13.2571 20.2626C13.2085 20.7627 13.4065 21.2221 13.7683 21.6099C14.3154 22.1963 15.047 22.75 16.0403 22.75H19.9596C20.9529 22.75 21.6845 22.1963 22.2316 21.6099C22.5934 21.2221 22.7914 20.7627 22.7428 20.2626C22.6975 19.7967 22.4495 19.4288 22.2141 19.1697C21.8434 18.7614 21.1374 18.3137 20.8273 18.1177Z" fill="white"/>
<path d="M3.24986 13.5C3.24986 11.9812 4.48107 10.75 5.99986 10.75C7.51864 10.75 8.74986 11.9812 8.74986 13.5C8.74986 15.0188 7.51864 16.25 5.99986 16.25C4.48107 16.25 3.24986 15.0188 3.24986 13.5Z" fill="white"/>
<path d="M17.9999 10.75C16.4811 10.75 15.2499 11.9812 15.2499 13.5C15.2499 15.0188 16.4811 16.25 17.9999 16.25C19.5186 16.25 20.7499 15.0188 20.7499 13.5C20.7499 11.9812 19.5186 10.75 17.9999 10.75Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

@ -103,7 +103,7 @@ class ApiClientImp implements ApiClient {
url = endPoint; url = endPoint;
} else { } else {
if (isRCService) { if (isRCService) {
url = RC_BASE_URL + endPoint; url = ApiConsts.rcBaseUrl + endPoint;
} else { } else {
url = ApiConsts.baseUrl + endPoint; url = ApiConsts.baseUrl + endPoint;
} }
@ -161,11 +161,10 @@ class ApiClientImp implements ApiClient {
// body['VersionID'] = ApiConsts.appVersionID.toString(); // body['VersionID'] = ApiConsts.appVersionID.toString();
if (!isExternal) { if (!isExternal) {
body['VersionID'] = "50.0"; body['VersionID'] = ApiConsts.appVersionID;
body['Channel'] = ApiConsts.appChannelId.toString(); body['Channel'] = ApiConsts.appChannelId;
body['IPAdress'] = ApiConsts.appIpAddress; body['IPAdress'] = ApiConsts.appIpAddress;
body['generalid'] = ApiConsts.appGeneralId; body['generalid'] = ApiConsts.appGeneralId;
body['LanguageID'] = _appState.getLanguageID().toString(); body['LanguageID'] = _appState.getLanguageID().toString();
body['Latitude'] = _appState.userLat.toString(); body['Latitude'] = _appState.userLat.toString();
body['Longitude'] = _appState.userLong.toString(); body['Longitude'] = _appState.userLong.toString();
@ -184,9 +183,6 @@ class ApiClientImp implements ApiClient {
} }
body.removeWhere((key, value) => value == null); body.removeWhere((key, value) => value == null);
log("uri: ${Uri.parse(url.trim())}");
log("body: ${json.encode(body)}");
final bool networkStatus = await Utils.checkConnection(bypassConnectionCheck: bypassConnectionCheck); final bool networkStatus = await Utils.checkConnection(bypassConnectionCheck: bypassConnectionCheck);
@ -202,6 +198,9 @@ class ApiClientImp implements ApiClient {
final response = await http.post(Uri.parse(url.trim()), body: json.encode(body), headers: headers); final response = await http.post(Uri.parse(url.trim()), body: json.encode(body), headers: headers);
final int statusCode = response.statusCode; final int statusCode = response.statusCode;
log("uri: ${Uri.parse(url.trim())}");
log("body: ${json.encode(body)}");
log("response.body: ${response.body}");
// log("response.body: ${response.body}"); // log("response.body: ${response.body}");
if (statusCode < 200 || statusCode >= 400) { if (statusCode < 200 || statusCode >= 400) {
onFailure('Error While Fetching data', statusCode, failureType: StatusCodeFailure("Error While Fetching data")); onFailure('Error While Fetching data', statusCode, failureType: StatusCodeFailure("Error While Fetching data"));
@ -213,13 +212,14 @@ class ApiClientImp implements ApiClient {
onSuccess(parsed, statusCode, messageStatus: 1, errorMessage: ""); onSuccess(parsed, statusCode, messageStatus: 1, errorMessage: "");
} else { } else {
onSuccess(parsed, statusCode, onSuccess(parsed, statusCode,
messageStatus: parsed.contains('MessageStatus') ? parsed['MessageStatus'] : 1, messageStatus: (parsed is Map && parsed.containsKey('MessageStatus')) ? parsed['MessageStatus'] : 1,
errorMessage: parsed.contains('ErrorEndUserMessage') ? parsed['ErrorEndUserMessage'] : ""); errorMessage: (parsed is Map && parsed.containsKey('ErrorEndUserMessage')) ? parsed['ErrorEndUserMessage'] : "");
} }
} else { } else {
if (parsed['Response_Message'] != null) { if (parsed['Response_Message'] != null) {
onSuccess(parsed, statusCode, onSuccess(parsed, statusCode,
messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']); messageStatus: (parsed is Map && parsed.containsKey('MessageStatus')) ? parsed['MessageStatus'] : 1,
errorMessage: (parsed is Map && parsed.containsKey('ErrorEndUserMessage')) ? parsed['ErrorEndUserMessage'] : "");
} else { } else {
if (parsed['ErrorType'] == 4) { if (parsed['ErrorType'] == 4) {
//TODO : handle app update //TODO : handle app update
@ -352,9 +352,9 @@ class ApiClientImp implements ApiClient {
url = endPoint; url = endPoint;
} else { } else {
if (isRCService) { if (isRCService) {
url = RC_BASE_URL + endPoint; url = ApiConsts.rcBaseUrl + endPoint;
} else { } else {
url = BASE_URL + endPoint; url = ApiConsts.baseUrl + endPoint;
} }
} }
if (queryParams != null) { if (queryParams != null) {

@ -14,8 +14,8 @@ var PACKAGES_ORDERS = '/api/orders';
var PACKAGES_ORDER_HISTORY = '/api/orders/items'; var PACKAGES_ORDER_HISTORY = '/api/orders/items';
var PACKAGES_TAMARA_OPT = '/api/orders/paymentoptions/tamara'; var PACKAGES_TAMARA_OPT = '/api/orders/paymentoptions/tamara';
// var BASE_URL = 'http://10.50.100.198:2018/'; // var BASE_URL = 'http://10.50.100.198:2018/';
// var BASE_URL = 'https://uat.hmgwebservices.com/'; var BASE_URL = 'https://uat.hmgwebservices.com/';
var BASE_URL = 'https://hmgwebservices.com/'; // var BASE_URL = 'https://hmgwebservices.com/';
// var BASE_URL = 'http://10.201.204.103/'; // var BASE_URL = 'http://10.201.204.103/';
// var BASE_URL = 'https://orash.cloudsolutions.com.sa/'; // var BASE_URL = 'https://orash.cloudsolutions.com.sa/';
// var BASE_URL = 'https://vidauat.cloudsolutions.com.sa/'; // var BASE_URL = 'https://vidauat.cloudsolutions.com.sa/';
@ -46,8 +46,6 @@ var PHARMACY_REDIRECT_URL = 'https://bit.ly/AlhabibPharmacy';
// RC API URL // 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/uat/';
// var RC_BASE_URL = 'https://ms.hmg.com/rc/'; // var RC_BASE_URL = 'https://ms.hmg.com/rc/';
var PING_SERVICE = 'Services/Weather.svc/REST/CheckConnectivity'; var PING_SERVICE = 'Services/Weather.svc/REST/CheckConnectivity';
@ -521,12 +519,6 @@ var ADD_HHC_ORDER_RC = "api/HHC/add";
var GET_ALL_HHC_ORDERS_RC = 'api/hhc/list'; var GET_ALL_HHC_ORDERS_RC = 'api/hhc/list';
var UPDATE_HHC_ORDER_RC = 'api/hhc/update'; var UPDATE_HHC_ORDER_RC = 'api/hhc/update';
// CMC RC SERVICES
var GET_ALL_CMC_SERVICES_RC = 'api/cmc/getallcmc';
var ADD_CMC_ORDER_RC = 'api/cmc/add';
var GET_ALL_CMC_ORDERS_RC = 'api/cmc/list';
var UPDATE_CMC_ORDER_RC = 'api/cmc/update';
// RRT RC SERVICES // RRT RC SERVICES
var ADD_RRT_ORDER_RC = "api/rrt/add"; var ADD_RRT_ORDER_RC = "api/rrt/add";
var GET_ALL_RRT_ORDERS_RC = "api/rrt/list"; var GET_ALL_RRT_ORDERS_RC = "api/rrt/list";
@ -725,7 +717,7 @@ class ApiConsts {
static String baseUrl = 'https://hmgwebservices.com/'; // HIS API URL PROD static String baseUrl = 'https://hmgwebservices.com/'; // HIS API URL PROD
static String RCBaseUrl = 'https://rc.hmg.com/'; // RC API URL PROD static String rcBaseUrl = 'https://rc.hmg.com/'; // RC API URL PROD
static var payFortEnvironment = FortEnvironment.production; static var payFortEnvironment = FortEnvironment.production;
static var applePayMerchantId = "merchant.com.hmgwebservices"; static var applePayMerchantId = "merchant.com.hmgwebservices";
@ -752,7 +744,7 @@ class ApiConsts {
TAMARA_URL = "https://mdlaboratories.com/tamaralive/Home/Checkout"; TAMARA_URL = "https://mdlaboratories.com/tamaralive/Home/Checkout";
GET_TAMARA_INSTALLMENTS_URL = "https://mdlaboratories.com/tamaralive/Home/GetInstallments"; GET_TAMARA_INSTALLMENTS_URL = "https://mdlaboratories.com/tamaralive/Home/GetInstallments";
GET_TAMARA_PAYMENT_STATUS = 'https://mdlaboratories.com/tamaralive/api/OnlineTamara/order_status?orderid='; GET_TAMARA_PAYMENT_STATUS = 'https://mdlaboratories.com/tamaralive/api/OnlineTamara/order_status?orderid=';
RCBaseUrl = 'https://rc.hmg.com/'; rcBaseUrl = 'https://rc.hmg.com/';
break; break;
case AppEnvironmentTypeEnum.dev: case AppEnvironmentTypeEnum.dev:
baseUrl = "https://uat.hmgwebservices.com/"; baseUrl = "https://uat.hmgwebservices.com/";
@ -762,7 +754,7 @@ class ApiConsts {
TAMARA_URL = "https://epharmacy.hmg.com/tamara/Home/Checkout"; TAMARA_URL = "https://epharmacy.hmg.com/tamara/Home/Checkout";
GET_TAMARA_INSTALLMENTS_URL = "https://epharmacy.hmg.com/tamara/Home/getinstallments"; GET_TAMARA_INSTALLMENTS_URL = "https://epharmacy.hmg.com/tamara/Home/getinstallments";
GET_TAMARA_PAYMENT_STATUS = 'https://epharmacy.hmg.com/tamara/api/OnlineTamara/order_status?orderid='; GET_TAMARA_PAYMENT_STATUS = 'https://epharmacy.hmg.com/tamara/api/OnlineTamara/order_status?orderid=';
RCBaseUrl = 'https://rc.hmg.com/test/'; rcBaseUrl = 'https://rc.hmg.com/';
break; break;
case AppEnvironmentTypeEnum.uat: case AppEnvironmentTypeEnum.uat:
baseUrl = "https://uat.hmgwebservices.com/"; baseUrl = "https://uat.hmgwebservices.com/";
@ -772,7 +764,7 @@ class ApiConsts {
TAMARA_URL = "https://epharmacy.hmg.com/tamara/Home/Checkout"; TAMARA_URL = "https://epharmacy.hmg.com/tamara/Home/Checkout";
GET_TAMARA_INSTALLMENTS_URL = "https://epharmacy.hmg.com/tamara/Home/getinstallments"; GET_TAMARA_INSTALLMENTS_URL = "https://epharmacy.hmg.com/tamara/Home/getinstallments";
GET_TAMARA_PAYMENT_STATUS = 'https://epharmacy.hmg.com/tamara/api/OnlineTamara/order_status?orderid='; GET_TAMARA_PAYMENT_STATUS = 'https://epharmacy.hmg.com/tamara/api/OnlineTamara/order_status?orderid=';
RCBaseUrl = 'https://rc.hmg.com/test/'; rcBaseUrl = 'https://rc.hmg.com/';
break; break;
case AppEnvironmentTypeEnum.preProd: case AppEnvironmentTypeEnum.preProd:
baseUrl = "https://webservices.hmg.com/"; baseUrl = "https://webservices.hmg.com/";
@ -782,7 +774,7 @@ class ApiConsts {
TAMARA_URL = "https://epharmacy.hmg.com/tamara/Home/Checkout"; TAMARA_URL = "https://epharmacy.hmg.com/tamara/Home/Checkout";
GET_TAMARA_INSTALLMENTS_URL = "https://epharmacy.hmg.com/tamara/Home/getinstallments"; GET_TAMARA_INSTALLMENTS_URL = "https://epharmacy.hmg.com/tamara/Home/getinstallments";
GET_TAMARA_PAYMENT_STATUS = 'https://epharmacy.hmg.com/tamara/api/OnlineTamara/order_status?orderid='; GET_TAMARA_PAYMENT_STATUS = 'https://epharmacy.hmg.com/tamara/api/OnlineTamara/order_status?orderid=';
RCBaseUrl = 'https://rc.hmg.com/'; rcBaseUrl = 'https://rc.hmg.com/';
break; break;
case AppEnvironmentTypeEnum.qa: case AppEnvironmentTypeEnum.qa:
baseUrl = "https://uat.hmgwebservices.com/"; baseUrl = "https://uat.hmgwebservices.com/";
@ -792,7 +784,7 @@ class ApiConsts {
TAMARA_URL = "https://epharmacy.hmg.com/tamara/Home/Checkout"; TAMARA_URL = "https://epharmacy.hmg.com/tamara/Home/Checkout";
GET_TAMARA_INSTALLMENTS_URL = "https://epharmacy.hmg.com/tamara/Home/getinstallments"; GET_TAMARA_INSTALLMENTS_URL = "https://epharmacy.hmg.com/tamara/Home/getinstallments";
GET_TAMARA_PAYMENT_STATUS = 'https://epharmacy.hmg.com/tamara/api/OnlineTamara/order_status?orderid='; GET_TAMARA_PAYMENT_STATUS = 'https://epharmacy.hmg.com/tamara/api/OnlineTamara/order_status?orderid=';
RCBaseUrl = 'https://rc.hmg.com/test/'; rcBaseUrl = 'https://rc.hmg.com/';
break; break;
case AppEnvironmentTypeEnum.staging: case AppEnvironmentTypeEnum.staging:
baseUrl = "https://uat.hmgwebservices.com/"; baseUrl = "https://uat.hmgwebservices.com/";
@ -802,7 +794,7 @@ class ApiConsts {
TAMARA_URL = "https://epharmacy.hmg.com/tamara/Home/Checkout"; TAMARA_URL = "https://epharmacy.hmg.com/tamara/Home/Checkout";
GET_TAMARA_INSTALLMENTS_URL = "https://epharmacy.hmg.com/tamara/Home/getinstallments"; GET_TAMARA_INSTALLMENTS_URL = "https://epharmacy.hmg.com/tamara/Home/getinstallments";
GET_TAMARA_PAYMENT_STATUS = 'https://epharmacy.hmg.com/tamara/api/OnlineTamara/order_status?orderid='; GET_TAMARA_PAYMENT_STATUS = 'https://epharmacy.hmg.com/tamara/api/OnlineTamara/order_status?orderid=';
RCBaseUrl = 'https://rc.hmg.com/test/'; rcBaseUrl = 'https://rc.hmg.com/';
break; break;
} }
} }
@ -846,8 +838,21 @@ class ApiConsts {
static final String createAdvancePayments = 'Services/Patients.svc/REST/HIS_CreateAdvancePayment'; static final String createAdvancePayments = 'Services/Patients.svc/REST/HIS_CreateAdvancePayment';
static final String addAdvanceNumberRequest = 'Services/PayFort_Serv.svc/REST/AddAdvancedNumberRequest'; static final String addAdvanceNumberRequest = 'Services/PayFort_Serv.svc/REST/AddAdvancedNumberRequest';
// RC COMPREHENSIVE MEDICAL CHECKUP ServIces
static final String allCMCOrdersRc = 'api/cmc/list';
static final String allCMCServicesRc = 'api/cmc/getallcmc';
static final String updateCMCOrder = 'api/cmc/update';
static final String addCMCOrder = 'api/cmc/add';
static final String getHospitalsList = 'Services/Lists.svc/REST/GetProject';
// RC HOME HEALTHCARE ServIces
static final String allHHCOrdersRc = 'api/hhc/list';
static final String allHHCServicesRc = 'api/HHC/getallhhc';
static final String updateHHCOrder = 'api/hhc/update';
static final String addHHCOrder = 'api/HHC/add';
// ************ static values for Api **************** // ************ static values for Api ****************
static final double appVersionID = 18.7; static final double appVersionID = 20.0;
static final int appChannelId = 3; static final int appChannelId = 3;
static final String appIpAddress = "10.20.10.20"; static final String appIpAddress = "10.20.10.20";
static final String appGeneralId = "Cs2020@2016\$2958"; static final String appGeneralId = "Cs2020@2016\$2958";

@ -171,6 +171,8 @@ class AppAssets {
static const String to_arrow = '$svgBasePath/to_arrow.svg'; static const String to_arrow = '$svgBasePath/to_arrow.svg';
static const String dual_arrow = '$svgBasePath/to_arrow.svg'; static const String dual_arrow = '$svgBasePath/to_arrow.svg';
static const String forward_arrow_medium = '$svgBasePath/forward_arrow_medium.svg'; static const String forward_arrow_medium = '$svgBasePath/forward_arrow_medium.svg';
static const String eReferral = '$svgBasePath/e-referral.svg';
static const String comprehensiveCheckup = '$svgBasePath/comprehensive_checkup.svg';
static const String all_payment_method = '$svgBasePath/all_payment_method.svg'; static const String all_payment_method = '$svgBasePath/all_payment_method.svg';
static const String ic_rrt_vehicle = '$svgBasePath/ic_rrt_vehicle.svg'; static const String ic_rrt_vehicle = '$svgBasePath/ic_rrt_vehicle.svg';
@ -202,6 +204,8 @@ class AppAssets {
static const String visa = '$pngBasePath/visa.png'; static const String visa = '$pngBasePath/visa.png';
static const String lockIcon = '$pngBasePath/lock-icon.png'; static const String lockIcon = '$pngBasePath/lock-icon.png';
static const String dummy_user = '$pngBasePath/dummy_user.png'; static const String dummy_user = '$pngBasePath/dummy_user.png';
static const String comprehensiveCheckupEn = '$pngBasePath/cc_en.png';
static const String comprehensiveCheckupAr = '$pngBasePath/cc_er.png';
} }
class AppAnimations { class AppAnimations {

@ -15,6 +15,8 @@ import 'package:hmg_patient_app_new/features/emergency_services/emergency_servic
import 'package:hmg_patient_app_new/features/emergency_services/emergency_services_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_repo.dart'; import 'package:hmg_patient_app_new/features/habib_wallet/habib_wallet_repo.dart';
import 'package:hmg_patient_app_new/features/habib_wallet/habib_wallet_view_model.dart'; import 'package:hmg_patient_app_new/features/habib_wallet/habib_wallet_view_model.dart';
import 'package:hmg_patient_app_new/features/hmg_services/hmg_services_repo.dart';
import 'package:hmg_patient_app_new/features/hmg_services/hmg_services_view_model.dart';
import 'package:hmg_patient_app_new/features/immediate_livecare/immediate_livecare_repo.dart'; import 'package:hmg_patient_app_new/features/immediate_livecare/immediate_livecare_repo.dart';
import 'package:hmg_patient_app_new/features/immediate_livecare/immediate_livecare_view_model.dart'; import 'package:hmg_patient_app_new/features/immediate_livecare/immediate_livecare_view_model.dart';
import 'package:hmg_patient_app_new/features/insurance/insurance_repo.dart'; import 'package:hmg_patient_app_new/features/insurance/insurance_repo.dart';
@ -104,47 +106,30 @@ class AppDependencies {
getIt.registerLazySingleton<InsuranceRepo>(() => InsuranceRepoImp(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<PayfortRepo>(() => PayfortRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt()));
getIt.registerLazySingleton<LocalAuthService>( getIt.registerLazySingleton<LocalAuthService>(
() => LocalAuthService(loggerService: getIt<LoggerService>(), localAuth: getIt<LocalAuthentication>())); () => LocalAuthService(loggerService: getIt<LoggerService>(), localAuth: getIt<LocalAuthentication>()),
);
getIt.registerLazySingleton<HabibWalletRepo>(() => HabibWalletRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt())); getIt.registerLazySingleton<HabibWalletRepo>(() => HabibWalletRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt()));
getIt.registerLazySingleton<MedicalFileRepo>(() => MedicalFileRepoImp(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<ImmediateLiveCareRepo>(() => ImmediateLiveCareRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt()));
getIt.registerLazySingleton<EmergencyServicesRepo>(() => EmergencyServicesRepoImp(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<TodoSectionRepo>(() => TodoSectionRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt()));
getIt.registerLazySingleton<LocationRepo>( getIt.registerLazySingleton<LocationRepo>(() => LocationRepoImpl(apiClient: getIt()));
() => LocationRepoImpl(apiClient: getIt()));
getIt.registerLazySingleton<ContactUsRepo>(() => ContactUsRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt())); getIt.registerLazySingleton<ContactUsRepo>(() => ContactUsRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt()));
getIt.registerLazySingleton<HmgServicesRepo>(() => HmgServicesRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt()));
// ViewModels // ViewModels
// Global/shared VMs LazySingleton // Global/shared VMs LazySingleton
getIt.registerLazySingleton<LabViewModel>( getIt.registerLazySingleton<LabViewModel>(() => LabViewModel(labRepo: getIt(), errorHandlerService: getIt(), navigationService: getIt()));
() => LabViewModel(labRepo: getIt(), errorHandlerService: getIt(), navigationService: getIt()),
);
getIt.registerLazySingleton<RadiologyViewModel>( getIt.registerLazySingleton<RadiologyViewModel>(() => RadiologyViewModel(radiologyRepo: getIt(), errorHandlerService: getIt()));
() => RadiologyViewModel(
radiologyRepo: getIt(),
errorHandlerService: getIt(),
),
);
getIt.registerLazySingleton<PrescriptionsViewModel>( getIt.registerLazySingleton<PrescriptionsViewModel>(() => PrescriptionsViewModel(prescriptionsRepo: getIt(), errorHandlerService: getIt()));
() => PrescriptionsViewModel(
prescriptionsRepo: getIt(),
errorHandlerService: getIt(),
),
);
getIt.registerLazySingleton<InsuranceViewModel>( getIt.registerLazySingleton<InsuranceViewModel>(() => InsuranceViewModel(insuranceRepo: getIt(), errorHandlerService: getIt()));
() => InsuranceViewModel(
insuranceRepo: getIt(),
errorHandlerService: getIt(),
),
);
getIt.registerLazySingleton<MyAppointmentsViewModel>( getIt.registerLazySingleton<MyAppointmentsViewModel>(
() => MyAppointmentsViewModel(myAppointmentsRepo: getIt(), errorHandlerService: getIt(), appState: getIt()), () => MyAppointmentsViewModel(myAppointmentsRepo: getIt(), errorHandlerService: getIt(), appState: getIt()));
);
getIt.registerLazySingleton<PayfortViewModel>( getIt.registerLazySingleton<PayfortViewModel>(
() => PayfortViewModel( () => PayfortViewModel(
@ -198,53 +183,39 @@ class AppDependencies {
); );
getIt.registerLazySingleton<ProfileSettingsViewModel>(() => ProfileSettingsViewModel()); getIt.registerLazySingleton<ProfileSettingsViewModel>(() => ProfileSettingsViewModel());
getIt.registerLazySingleton<DateRangeSelectorRangeViewModel>( getIt.registerLazySingleton<DateRangeSelectorRangeViewModel>(() => DateRangeSelectorRangeViewModel());
() => DateRangeSelectorRangeViewModel(),
);
getIt.registerLazySingleton<DoctorFilterViewModel>( getIt.registerLazySingleton<DoctorFilterViewModel>(() => DoctorFilterViewModel());
() => DoctorFilterViewModel(),
);
getIt.registerLazySingleton<AppointmentViaRegionViewmodel>( getIt.registerLazySingleton<AppointmentViaRegionViewmodel>(
() => AppointmentViaRegionViewmodel( () => AppointmentViaRegionViewmodel(navigationService: getIt(), appState: getIt()),
navigationService: getIt(),
appState: getIt(),
),
); );
getIt.registerLazySingleton<EmergencyServicesViewModel>( getIt.registerLazySingleton<EmergencyServicesViewModel>(
() => EmergencyServicesViewModel( () => EmergencyServicesViewModel(
locationUtils: getIt(), locationUtils: getIt(),
navServices: getIt(), navServices: getIt(),
emergencyServicesRepo: getIt(), emergencyServicesRepo: getIt(),
appState: getIt(), appState: getIt(),
errorHandlerService: getIt(), errorHandlerService: getIt(),
appointmentRepo: getIt(), appointmentRepo: getIt(),
dialogService: getIt() dialogService: getIt()),
),
); );
getIt.registerLazySingleton<LocationViewModel>( getIt.registerLazySingleton<LocationViewModel>(
() => LocationViewModel( () => LocationViewModel(locationRepo: getIt(), errorHandlerService: getIt()),
locationRepo: getIt(),
errorHandlerService: getIt(),
),
); );
getIt.registerLazySingleton<ContactUsViewModel>( getIt.registerLazySingleton<ContactUsViewModel>(
() => ContactUsViewModel( () => ContactUsViewModel(contactUsRepo: getIt(), appState: getIt(), errorHandlerService: getIt()),
contactUsRepo: getIt(),
appState: getIt(),
errorHandlerService: getIt(),
),
); );
getIt.registerLazySingleton<TodoSectionViewModel>( getIt.registerLazySingleton<TodoSectionViewModel>(
() => TodoSectionViewModel( () => TodoSectionViewModel(todoSectionRepo: getIt(), errorHandlerService: getIt()),
todoSectionRepo: getIt(), );
errorHandlerService: getIt(),
), getIt.registerLazySingleton<HmgServicesViewModel>(
() => HmgServicesViewModel(bookAppointmentsRepo: getIt(), hmgServicesRepo: getIt(), errorHandlerService: getIt()),
); );
// Screen-specific VMs Factory // Screen-specific VMs Factory

@ -11,14 +11,14 @@ class DateUtil {
if (date == null) return DateTime.now(); if (date == null) return DateTime.now();
if (date.isEmpty) return DateTime.now(); if (date.isEmpty) return DateTime.now();
const start = "/Date("; const start = "/Date(";
const end = "+0300)"; const end = "+0300)";
final startIndex = date.indexOf(start); final startIndex = date.indexOf(start);
final endIndex = date.indexOf(end, startIndex + start.length); final endIndex = date.indexOf(end, startIndex + start.length);
return DateTime.fromMillisecondsSinceEpoch(int.parse( return DateTime.fromMillisecondsSinceEpoch(int.parse(
date.substring(startIndex + start.length, endIndex), date.substring(startIndex + start.length, endIndex),
)); ))
;
} }
static DateTime convertStringToDateSaudiTimezone(String date, int projectId) { static DateTime convertStringToDateSaudiTimezone(String date, int projectId) {
@ -36,10 +36,10 @@ class DateUtil {
// .add(Duration(hours: 4)); // .add(Duration(hours: 4));
// } else { // } else {
return DateTime.fromMillisecondsSinceEpoch( return DateTime.fromMillisecondsSinceEpoch(
int.parse( int.parse(
date.substring(startIndex + start.length, endIndex), date.substring(startIndex + start.length, endIndex),
), ),
isUtc: true) isUtc: true)
.add(Duration(hours: 3)); .add(Duration(hours: 3));
// } // }
} else { } else {
@ -156,7 +156,13 @@ class DateUtil {
static String getDateFormatted(String date) { static String getDateFormatted(String date) {
DateTime dateObj = DateUtil.convertStringToDate(date); DateTime dateObj = DateUtil.convertStringToDate(date);
return DateUtil.getWeekDay(dateObj.weekday) + ", " + dateObj.day.toString() + " " + DateUtil.getMonth(dateObj.month) + " " + dateObj.year.toString(); return DateUtil.getWeekDay(dateObj.weekday) +
", " +
dateObj.day.toString() +
" " +
DateUtil.getMonth(dateObj.month) +
" " +
dateObj.year.toString();
} }
static String getISODateFormat(DateTime dateTime) { static String getISODateFormat(DateTime dateTime) {
@ -352,7 +358,13 @@ class DateUtil {
if (dateTime != null) { if (dateTime != null) {
return lang == 'en' return lang == 'en'
? getWeekDayEnglish(dateTime.weekday) + ", " + getMonth(dateTime.month) + " " + dateTime.day.toString() + " " + dateTime.year.toString() ? getWeekDayEnglish(dateTime.weekday) + ", " + getMonth(dateTime.month) + " " + dateTime.day.toString() + " " + dateTime.year.toString()
: getWeekDayArabic(dateTime.weekday) + ", " + dateTime.day.toString() + " " + getMonthArabic(dateTime.month) + " " + dateTime.year.toString(); : getWeekDayArabic(dateTime.weekday) +
", " +
dateTime.day.toString() +
" " +
getMonthArabic(dateTime.month) +
" " +
dateTime.year.toString();
} else { } else {
return ""; return "";
} }
@ -381,7 +393,9 @@ class DateUtil {
static String getMonthYearLangDateFormatted(DateTime dateTime, String lang) { static String getMonthYearLangDateFormatted(DateTime dateTime, String lang) {
if (dateTime != null) { if (dateTime != null) {
return lang == 'en' ? getMonth(dateTime.month) + " " + dateTime.year.toString() : getMonthArabic(dateTime.month) + " " + dateTime.year.toString(); return lang == 'en'
? getMonth(dateTime.month) + " " + dateTime.year.toString()
: getMonthArabic(dateTime.month) + " " + dateTime.year.toString();
} else { } else {
return ""; return "";
} }
@ -488,10 +502,8 @@ class DateUtil {
} }
} }
extension OnlyDate on DateTime {
extension OnlyDate on DateTime{ DateTime provideDateOnly() {
DateTime provideDateOnly(){
return DateTime(this.year, month, day); return DateTime(this.year, month, day);
} }
} }

@ -102,8 +102,9 @@ class Utils {
? getMonthArabic(dateTime.month) + " " + dateTime.day.toString() + ", " + dateTime.year.toString() ? getMonthArabic(dateTime.month) + " " + dateTime.day.toString() + ", " + dateTime.year.toString()
: getMonth(dateTime.month) + " " + dateTime.day.toString() + ", " + dateTime.year.toString(); : getMonth(dateTime.month) + " " + dateTime.day.toString() + ", " + dateTime.year.toString();
} }
static String getDayMonthYearDateFormatted(DateTime? dateTime) { static String getDayMonthYearDateFormatted(DateTime? dateTime) {
if(dateTime == null ) return ""; if (dateTime == null) return "";
return appState.isArabic() return appState.isArabic()
? "${dateTime.day.toString()} ${getMonthArabic(dateTime.month)}, ${dateTime.year.toString()}" ? "${dateTime.day.toString()} ${getMonthArabic(dateTime.month)}, ${dateTime.year.toString()}"
: "${dateTime.day.toString()} ${getMonth(dateTime.month)}, ${dateTime.year.toString()}"; : "${dateTime.day.toString()} ${getMonth(dateTime.month)}, ${dateTime.year.toString()}";
@ -376,16 +377,21 @@ class Utils {
).center; ).center;
} }
static Widget getWarningWidget( static Widget getWarningWidget({
{String? loadingText, bool isShowActionButtons = false, Widget? bodyWidget, Function? onConfirmTap, Function? onCancelTap}) { String? loadingText,
bool isShowActionButtons = false,
Widget? bodyWidget,
Function? onConfirmTap,
Function? onCancelTap,
}) {
return Column( return Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
Lottie.asset(AppAnimations.warningAnimation, Lottie.asset(AppAnimations.warningAnimation,
repeat: false, reverse: false, frameRate: FrameRate(60), width: 128.h, height: 128.h, fit: BoxFit.fill), repeat: false, reverse: false, frameRate: FrameRate(60), width: 100.h, height: 100.h, fit: BoxFit.fill),
SizedBox(height: 8.h), SizedBox(height: 8.h),
(loadingText ?? LocaleKeys.loadingText.tr()).toText14(color: AppColors.blackColor, letterSpacing: 0), (loadingText ?? LocaleKeys.loadingText.tr()).toText15(color: AppColors.blackColor, letterSpacing: 0),
SizedBox(height: 16.h), SizedBox(height: 16.h),
bodyWidget ?? SizedBox.shrink(), bodyWidget ?? SizedBox.shrink(),
SizedBox(height: 16.h), SizedBox(height: 16.h),
@ -753,14 +759,15 @@ class Utils {
); );
} }
static Widget getPaymentAmountWithSymbol2(num habibWalletAmount, static Widget getPaymentAmountWithSymbol2(
{double iconSize = 14, num habibWalletAmount, {
double iconSize = 14,
double? fontSize, double? fontSize,
double? letterSpacing, double? letterSpacing,
FontWeight? fontWeight, FontWeight? fontWeight,
Color iconColor = AppColors.textColor, Color iconColor = AppColors.textColor,
Color textColor = AppColors.blackColor, Color textColor = AppColors.blackColor,
bool isSaudiCurrency = true, bool isSaudiCurrency = true,
bool isExpanded = true, bool isExpanded = true,
}) { }) {
return RichText( return RichText(
@ -777,7 +784,7 @@ class Utils {
style: TextStyle( style: TextStyle(
color: textColor, color: textColor,
fontSize: fontSize ?? 32.f, fontSize: fontSize ?? 32.f,
letterSpacing: letterSpacing??-4, letterSpacing: letterSpacing ?? -4,
fontWeight: fontWeight ?? FontWeight.w600, fontWeight: fontWeight ?? FontWeight.w600,
height: 1), height: 1),
), ),
@ -839,7 +846,12 @@ class Utils {
static PatientDoctorAppointmentList? convertToPatientDoctorAppointmentList(HospitalsModel? hospital) { static PatientDoctorAppointmentList? convertToPatientDoctorAppointmentList(HospitalsModel? hospital) {
if (hospital == null) return null; if (hospital == null) return null;
return PatientDoctorAppointmentList( return PatientDoctorAppointmentList(
filterName: hospital.name, distanceInKMs: hospital.distanceInKilometers?.toString(), projectTopName: hospital.name, projectBottomName: hospital.name, model: hospital, isHMC: hospital.isHMC); filterName: hospital.name,
distanceInKMs: hospital.distanceInKilometers?.toString(),
projectTopName: hospital.name,
projectBottomName: hospital.name,
model: hospital,
isHMC: hospital.isHMC);
} }
static bool havePrivilege(int id) { static bool havePrivilege(int id) {
@ -853,7 +865,4 @@ class Utils {
} }
return isHavePrivilege; return isHavePrivilege;
} }
} }

@ -1,9 +1,8 @@
import 'package:hmg_patient_app_new/core/enums.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart'; import 'package:hmg_patient_app_new/core/enums.dart';
import 'package:hmg_patient_app_new/extensions/int_extensions.dart'; import 'package:hmg_patient_app_new/extensions/int_extensions.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:shimmer/shimmer.dart'; import 'package:shimmer/shimmer.dart';
import 'package:sizer/sizer.dart'; import 'package:sizer/sizer.dart';
import 'package:smooth_corner/smooth_corner.dart'; import 'package:smooth_corner/smooth_corner.dart';
@ -19,7 +18,8 @@ extension WidgetExtensions on Widget {
Widget paddingAll(double _value) => Padding(padding: EdgeInsets.all(_value), child: this); Widget paddingAll(double _value) => Padding(padding: EdgeInsets.all(_value), child: this);
Widget paddingSymmetrical(double horizontal, double vertical) => Padding(padding: EdgeInsets.symmetric(horizontal: horizontal, vertical: vertical), child: this); Widget paddingSymmetrical(double horizontal, double vertical) =>
Padding(padding: EdgeInsets.symmetric(horizontal: horizontal, vertical: vertical), child: this);
Widget paddingOnly({double left = 0.0, double right = 0.0, double top = 0.0, double bottom = 0.0}) => Widget paddingOnly({double left = 0.0, double right = 0.0, double top = 0.0, double bottom = 0.0}) =>
Padding(padding: EdgeInsetsDirectional.only(start: left, end: right, top: top, bottom: bottom), child: this); Padding(padding: EdgeInsetsDirectional.only(start: left, end: right, top: top, bottom: bottom), child: this);
@ -99,7 +99,7 @@ extension WidgetExtensions on Widget {
bool disablePadding = false, bool disablePadding = false,
double radius = 20, double radius = 20,
Color? color, Color? color,
Color borderColor = AppColors.buttonColor, Color? borderColor,
bool disableWidth = false, bool disableWidth = false,
bool isAlignment = false}) { bool isAlignment = false}) {
return Container( return Container(
@ -110,7 +110,7 @@ extension WidgetExtensions on Widget {
), ),
color: color, color: color,
border: Border.all( border: Border.all(
color: borderColor, color: borderColor ?? Colors.transparent,
width: disableWidth ? 2 : 1, width: disableWidth ? 2 : 1,
), ),
), ),

@ -3,7 +3,6 @@ import 'dart:async';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_state.dart'; 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/dependencies.dart';
import 'package:hmg_patient_app_new/core/location_util.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/date_util.dart';
@ -105,8 +104,7 @@ class BookAppointmentsViewModel extends ChangeNotifier {
List<String> searchedRegionList = []; List<String> searchedRegionList = [];
List<String> facilityList = ["hmgHospitals", "hmcMedicalClinic"]; List<String> facilityList = ["hmgHospitals", "hmcMedicalClinic"];
List<String> searchedHospitalList = []; List<String> searchedHospitalList = [];
List<PatientDoctorAppointmentList> List<PatientDoctorAppointmentList> searchedPatientDoctorAppointmentHospitalsList = [];
searchedPatientDoctorAppointmentHospitalsList = [];
List<String> searchedClinicList = []; List<String> searchedClinicList = [];
PatientDoctorAppointmentList? selectedHospitalForFilters; PatientDoctorAppointmentList? selectedHospitalForFilters;
@ -114,15 +112,14 @@ class BookAppointmentsViewModel extends ChangeNotifier {
String? selectedClinicForFilters; String? selectedClinicForFilters;
bool applyFilters = false; bool applyFilters = false;
///variables for laser clinic ///variables for laser clinic
List<LaserCategoryType> femaleLaserCategory = [ List<LaserCategoryType> femaleLaserCategory = [
LaserCategoryType(1, 'bodyString'), LaserCategoryType(1, 'bodyString'),
LaserCategoryType(2, 'face'), LaserCategoryType(2, 'face'),
LaserCategoryType(10,'bikini'), LaserCategoryType(10, 'bikini'),
LaserCategoryType(11, 'retouch'), LaserCategoryType(11, 'retouch'),
]; ];
List<LaserCategoryType> maleLaserCategory =[ List<LaserCategoryType> maleLaserCategory = [
LaserCategoryType(1, 'body'), LaserCategoryType(1, 'body'),
LaserCategoryType(2, 'face'), LaserCategoryType(2, 'face'),
LaserCategoryType(11, 'retouch'), LaserCategoryType(11, 'retouch'),
@ -136,9 +133,13 @@ class BookAppointmentsViewModel extends ChangeNotifier {
bool isBodyPartsLoading = false; bool isBodyPartsLoading = false;
int duration = 0; int duration = 0;
BookAppointmentsViewModel( BookAppointmentsViewModel(
{required this.bookAppointmentsRepo, required this.errorHandlerService, required this.navigationService, required this.myAppointmentsViewModel, required this.locationUtils, required this.dialogService }) { {required this.bookAppointmentsRepo,
required this.errorHandlerService,
required this.navigationService,
required this.myAppointmentsViewModel,
required this.locationUtils,
required this.dialogService}) {
initBookAppointmentViewModel(); initBookAppointmentViewModel();
} }
@ -287,7 +288,8 @@ class BookAppointmentsViewModel extends ChangeNotifier {
Future<void> getLiveCareScheduleClinics({Function(dynamic)? onSuccess, Function(String)? onError}) async { Future<void> getLiveCareScheduleClinics({Function(dynamic)? onSuccess, Function(String)? onError}) async {
liveCareClinicsList.clear(); liveCareClinicsList.clear();
final result = await bookAppointmentsRepo.getLiveCareScheduleClinics(_appState.getAuthenticatedUser()!.age!, _appState.getAuthenticatedUser()!.gender!); final result =
await bookAppointmentsRepo.getLiveCareScheduleClinics(_appState.getAuthenticatedUser()!.age!, _appState.getAuthenticatedUser()!.gender!);
result.fold( result.fold(
(failure) async => await errorHandlerService.handleError(failure: failure), (failure) async => await errorHandlerService.handleError(failure: failure),
@ -309,8 +311,9 @@ class BookAppointmentsViewModel extends ChangeNotifier {
Future<void> getLiveCareDoctorsList({Function(dynamic)? onSuccess, Function(String)? onError}) async { Future<void> getLiveCareDoctorsList({Function(dynamic)? onSuccess, Function(String)? onError}) async {
doctorsList.clear(); doctorsList.clear();
final result = final result = await bookAppointmentsRepo.getLiveCareDoctorsList(
await bookAppointmentsRepo.getLiveCareDoctorsList(selectedLiveCareClinic.serviceID!, _appState.getAuthenticatedUser()!.age!, _appState.getAuthenticatedUser()!.gender!, onError: onError); selectedLiveCareClinic.serviceID!, _appState.getAuthenticatedUser()!.age!, _appState.getAuthenticatedUser()!.gender!,
onError: onError);
result.fold( result.fold(
(failure) async { (failure) async {
@ -333,10 +336,17 @@ class BookAppointmentsViewModel extends ChangeNotifier {
} }
//TODO: Make the API dynamic with parameters for ProjectID, isNearest, languageID, doctorId, doctorName //TODO: Make the API dynamic with parameters for ProjectID, isNearest, languageID, doctorId, doctorName
Future<void> getDoctorsList({int projectID = 0, bool isNearest = false, int doctorId = 0, String doctorName = "", Function(dynamic)? onSuccess, Function(String)? onError}) async { Future<void> getDoctorsList(
{int projectID = 0,
bool isNearest = false,
int doctorId = 0,
String doctorName = "",
Function(dynamic)? onSuccess,
Function(String)? onError}) async {
doctorsList.clear(); doctorsList.clear();
projectID = currentlySelectedHospitalFromRegionFlow != null ? int.parse(currentlySelectedHospitalFromRegionFlow!) : projectID; projectID = currentlySelectedHospitalFromRegionFlow != null ? int.parse(currentlySelectedHospitalFromRegionFlow!) : projectID;
final result = await bookAppointmentsRepo.getDoctorsList(selectedClinic.clinicID ?? 0, projectID, isNearest, doctorId, doctorName, isContinueDentalPlan: isContinueDentalPlan); final result = await bookAppointmentsRepo.getDoctorsList(selectedClinic.clinicID ?? 0, projectID, isNearest, doctorId, doctorName,
isContinueDentalPlan: isContinueDentalPlan);
result.fold( result.fold(
(failure) async { (failure) async {
@ -365,7 +375,13 @@ class BookAppointmentsViewModel extends ChangeNotifier {
} }
Future<void> getMappedDoctors( Future<void> getMappedDoctors(
{int projectID = 0, bool isNearest = false, int doctorId = 0, String doctorName = "", isContinueDentalPlan = false, Function(dynamic)? onSuccess, Function(String)? onError}) async { {int projectID = 0,
bool isNearest = false,
int doctorId = 0,
String doctorName = "",
isContinueDentalPlan = false,
Function(dynamic)? onSuccess,
Function(String)? onError}) async {
filteredHospitalList = null; filteredHospitalList = null;
hospitalList = null; hospitalList = null;
isRegionListLoading = true; isRegionListLoading = true;
@ -374,10 +390,10 @@ class BookAppointmentsViewModel extends ChangeNotifier {
final result = await bookAppointmentsRepo.getDoctorsList(selectedClinic.clinicID ?? 0, projectID, isNearest, doctorId, doctorName); final result = await bookAppointmentsRepo.getDoctorsList(selectedClinic.clinicID ?? 0, projectID, isNearest, doctorId, doctorName);
result.fold( result.fold(
(failure) async { (failure) async {
onError?.call("No doctors found for the search criteria".needTranslation); onError?.call("No doctors found for the search criteria".needTranslation);
}, },
(apiResponse) async { (apiResponse) async {
if (apiResponse.messageStatus == 2) { if (apiResponse.messageStatus == 2) {
// dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {}); // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
} else if (apiResponse.messageStatus == 1) { } else if (apiResponse.messageStatus == 1) {
@ -401,7 +417,8 @@ class BookAppointmentsViewModel extends ChangeNotifier {
} }
Future<void> getDoctorProfile({Function(dynamic)? onSuccess, Function(String)? onError}) async { Future<void> getDoctorProfile({Function(dynamic)? onSuccess, Function(String)? onError}) async {
final result = await bookAppointmentsRepo.getDoctorProfile(selectedDoctor.clinicID ?? 0, selectedDoctor.projectID ?? 0, selectedDoctor.doctorID ?? 0, onError: onError); final result = await bookAppointmentsRepo
.getDoctorProfile(selectedDoctor.clinicID ?? 0, selectedDoctor.projectID ?? 0, selectedDoctor.doctorID ?? 0, onError: onError);
result.fold( result.fold(
(failure) async {}, (failure) async {},
@ -457,7 +474,8 @@ class BookAppointmentsViewModel extends ChangeNotifier {
// : // :
date = DateUtil.convertStringToDateSaudiTimezone(element, int.parse(selectedDoctor.projectID.toString())); date = DateUtil.convertStringToDateSaudiTimezone(element, int.parse(selectedDoctor.projectID.toString()));
slotsList.add(FreeSlot(date, ['slot'])); slotsList.add(FreeSlot(date, ['slot']));
docFreeSlots.add(TimeSlot(isoTime: formatter.format(date), start: new DateTime(date.year, date.month, date.day, 0, 0, 0, 0), end: date, vidaDate: element)); docFreeSlots.add(TimeSlot(
isoTime: formatter.format(date), start: new DateTime(date.year, date.month, date.day, 0, 0, 0, 0), end: date, vidaDate: element));
}); });
notifyListeners(); notifyListeners();
@ -476,8 +494,8 @@ class BookAppointmentsViewModel extends ChangeNotifier {
final DateFormat dateFormatter = DateFormat('yyyy-MM-dd'); final DateFormat dateFormatter = DateFormat('yyyy-MM-dd');
Map<DateTime, List> _eventsParsed; Map<DateTime, List> _eventsParsed;
final result = await bookAppointmentsRepo.getLiveCareDoctorFreeSlots( final result = await bookAppointmentsRepo.getLiveCareDoctorFreeSlots(selectedDoctor.clinicID ?? 0, selectedLiveCareClinic.serviceID ?? 0,
selectedDoctor.clinicID ?? 0, selectedLiveCareClinic.serviceID ?? 0, selectedDoctor.projectID ?? 0, selectedDoctor.doctorID ?? 0, isBookingForLiveCare, selectedDoctor.projectID ?? 0, selectedDoctor.doctorID ?? 0, isBookingForLiveCare,
onError: onError); onError: onError);
result.fold( result.fold(
@ -501,7 +519,8 @@ class BookAppointmentsViewModel extends ChangeNotifier {
// : // :
date = DateUtil.convertStringToDateSaudiTimezone(element, int.parse(selectedDoctor.projectID.toString())); date = DateUtil.convertStringToDateSaudiTimezone(element, int.parse(selectedDoctor.projectID.toString()));
slotsList.add(FreeSlot(date, ['slot'])); slotsList.add(FreeSlot(date, ['slot']));
docFreeSlots.add(TimeSlot(isoTime: formatter.format(date), start: new DateTime(date.year, date.month, date.day, 0, 0, 0, 0), end: date, vidaDate: element)); docFreeSlots.add(TimeSlot(
isoTime: formatter.format(date), start: new DateTime(date.year, date.month, date.day, 0, 0, 0, 0), end: date, vidaDate: element));
}); });
notifyListeners(); notifyListeners();
@ -513,7 +532,10 @@ class BookAppointmentsViewModel extends ChangeNotifier {
); );
} }
Future<void> cancelAppointment({required PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel, Function(dynamic)? onSuccess, Function(String)? onError}) async { Future<void> cancelAppointment(
{required PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel,
Function(dynamic)? onSuccess,
Function(String)? onError}) async {
final result = await bookAppointmentsRepo.cancelAppointment(patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel); final result = await bookAppointmentsRepo.cancelAppointment(patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel);
result.fold( result.fold(
@ -597,13 +619,15 @@ class BookAppointmentsViewModel extends ChangeNotifier {
await cancelAppointment(patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel).then((val) async { await cancelAppointment(patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel).then((val) async {
navigationService.pop(); navigationService.pop();
Future.delayed(Duration(milliseconds: 50)).then((value) async {}); Future.delayed(Duration(milliseconds: 50)).then((value) async {});
LoadingUtils.showFullScreenLoader(barrierDismissible: true, isSuccessDialog: false, loadingText: "Booking your appointment...".needTranslation); LoadingUtils.showFullScreenLoader(
barrierDismissible: true, isSuccessDialog: false, loadingText: "Booking your appointment...".needTranslation);
await insertSpecificAppointment( await insertSpecificAppointment(
onError: (err) {}, onError: (err) {},
onSuccess: (apiResp) async { onSuccess: (apiResp) async {
LoadingUtils.hideFullScreenLoader(); LoadingUtils.hideFullScreenLoader();
await Future.delayed(Duration(milliseconds: 50)).then((value) async { await Future.delayed(Duration(milliseconds: 50)).then((value) async {
LoadingUtils.showFullScreenLoader(barrierDismissible: true, isSuccessDialog: true, loadingText: LocaleKeys.appointmentSuccess.tr()); LoadingUtils.showFullScreenLoader(
barrierDismissible: true, isSuccessDialog: true, loadingText: LocaleKeys.appointmentSuccess.tr());
await Future.delayed(Duration(milliseconds: 4000)).then((value) { await Future.delayed(Duration(milliseconds: 4000)).then((value) {
LoadingUtils.hideFullScreenLoader(); LoadingUtils.hideFullScreenLoader();
Navigator.pushAndRemoveUntil( Navigator.pushAndRemoveUntil(
@ -693,13 +717,15 @@ class BookAppointmentsViewModel extends ChangeNotifier {
await cancelAppointment(patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel).then((val) async { await cancelAppointment(patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel).then((val) async {
navigationService.pop(); navigationService.pop();
Future.delayed(Duration(milliseconds: 50)).then((value) async {}); Future.delayed(Duration(milliseconds: 50)).then((value) async {});
LoadingUtils.showFullScreenLoader(barrierDismissible: true, isSuccessDialog: false, loadingText: "Booking your appointment...".needTranslation); LoadingUtils.showFullScreenLoader(
barrierDismissible: true, isSuccessDialog: false, loadingText: "Booking your appointment...".needTranslation);
await insertSpecificAppointment( await insertSpecificAppointment(
onError: (err) {}, onError: (err) {},
onSuccess: (apiResp) async { onSuccess: (apiResp) async {
LoadingUtils.hideFullScreenLoader(); LoadingUtils.hideFullScreenLoader();
await Future.delayed(Duration(milliseconds: 50)).then((value) async { await Future.delayed(Duration(milliseconds: 50)).then((value) async {
LoadingUtils.showFullScreenLoader(barrierDismissible: true, isSuccessDialog: true, loadingText: LocaleKeys.appointmentSuccess.tr()); LoadingUtils.showFullScreenLoader(
barrierDismissible: true, isSuccessDialog: true, loadingText: LocaleKeys.appointmentSuccess.tr());
await Future.delayed(Duration(milliseconds: 4000)).then((value) { await Future.delayed(Duration(milliseconds: 4000)).then((value) {
LoadingUtils.hideFullScreenLoader(); LoadingUtils.hideFullScreenLoader();
Navigator.pushAndRemoveUntil( Navigator.pushAndRemoveUntil(
@ -773,7 +799,9 @@ class BookAppointmentsViewModel extends ChangeNotifier {
} else { } else {
filteredHospitalList = RegionList(); filteredHospitalList = RegionList();
var list = isHMG ? hospitalList?.registeredDoctorMap![selectedRegionId]!.hmgDoctorList : hospitalList?.registeredDoctorMap![selectedRegionId]!.hmcDoctorList; var list = isHMG
? hospitalList?.registeredDoctorMap![selectedRegionId]!.hmgDoctorList
: hospitalList?.registeredDoctorMap![selectedRegionId]!.hmcDoctorList;
if (list != null && list.isEmpty) { if (list != null && list.isEmpty) {
notifyListeners(); notifyListeners();
@ -856,12 +884,8 @@ class BookAppointmentsViewModel extends ChangeNotifier {
notifyListeners(); notifyListeners();
} }
void setSelections( void setSelections(List<String>? selectedFacilityForFilters, List<String>? selectedRegionForFilters, String? selectedClinicForFilters,
List<String>? selectedFacilityForFilters, PatientDoctorAppointmentList? selectedHospitalForFilters, bool applyFilters) {
List<String>? selectedRegionForFilters,
String? selectedClinicForFilters,
PatientDoctorAppointmentList? selectedHospitalForFilters,
bool applyFilters) {
this.selectedFacilityForFilters = selectedFacilityForFilters; this.selectedFacilityForFilters = selectedFacilityForFilters;
this.selectedClinicForFilters = selectedClinicForFilters; this.selectedClinicForFilters = selectedClinicForFilters;
this.selectedHospitalForFilters = selectedHospitalForFilters; this.selectedHospitalForFilters = selectedHospitalForFilters;
@ -872,17 +896,14 @@ class BookAppointmentsViewModel extends ChangeNotifier {
void getFiltersFromDoctorList() { void getFiltersFromDoctorList() {
doctorsList.forEach((element) { doctorsList.forEach((element) {
if (!searchedRegionList if (!searchedRegionList.contains(element.getRegionName(_appState.isArabic()))) {
.contains(element.getRegionName(_appState.isArabic()))) { searchedRegionList.add(element.getRegionName(_appState.isArabic()) ?? "");
searchedRegionList
.add(element.getRegionName(_appState.isArabic()) ?? "");
} }
if (!searchedHospitalList.contains(element.projectName)) { if (!searchedHospitalList.contains(element.projectName)) {
searchedPatientDoctorAppointmentHospitalsList searchedPatientDoctorAppointmentHospitalsList.add(PatientDoctorAppointmentList()
.add(PatientDoctorAppointmentList() ..filterName = element.projectName
..filterName = element.projectName ..isHMC = element.isHMC
..isHMC = element.isHMC ..distanceInKMs = "0");
..distanceInKMs = "0");
searchedHospitalList.add(element.projectName ?? ""); searchedHospitalList.add(element.projectName ?? "");
} }
if (!searchedClinicList.contains(element.clinicName)) { if (!searchedClinicList.contains(element.clinicName)) {
@ -939,27 +960,15 @@ class BookAppointmentsViewModel extends ChangeNotifier {
return doctorsList; return doctorsList;
} }
var list = doctorsList.where((element) { var list = doctorsList.where((element) {
var isInSelectedRegion = (selectedRegionForFilters?.isEmpty == true) var isInSelectedRegion =
? true (selectedRegionForFilters?.isEmpty == true) ? true : selectedRegionForFilters?.any((region) => region == element.getRegionName(isArabic()));
: selectedRegionForFilters var shouldApplyFacilityFilter = (selectedFacilityForFilters?.isEmpty == true) ? false : true;
?.any((region) => region == element.getRegionName(isArabic())); var isHMC = (selectedFacilityForFilters?.isEmpty == true) ? true : selectedFacilityForFilters?.any((item) => item.contains("hmc"));
var shouldApplyFacilityFilter = var isInSelectedClinic = (selectedClinicForFilters == null) ? true : selectedClinicForFilters == element.clinicName;
(selectedFacilityForFilters?.isEmpty == true) ? false : true; var isInSelectedHospital = (selectedHospitalForFilters == null) ? true : element.projectName == selectedHospitalForFilters?.filterName;
var isHMC = (selectedFacilityForFilters?.isEmpty == true)
? true
: selectedFacilityForFilters?.any((item) => item.contains("hmc"));
var isInSelectedClinic = (selectedClinicForFilters == null)
? true
: selectedClinicForFilters == element.clinicName;
var isInSelectedHospital = (selectedHospitalForFilters == null)
? true
: element.projectName == selectedHospitalForFilters?.filterName;
var facilityFilter = ((shouldApplyFacilityFilter == true) ? isHMC : true); var facilityFilter = ((shouldApplyFacilityFilter == true) ? isHMC : true);
return (isInSelectedRegion ?? true) && return (isInSelectedRegion ?? true) && (facilityFilter ?? true) && isInSelectedClinic && isInSelectedHospital;
(facilityFilter ?? true) &&
isInSelectedClinic &&
isInSelectedHospital;
}).toList(); }).toList();
return list; return list;
} }
@ -1003,7 +1012,8 @@ class BookAppointmentsViewModel extends ChangeNotifier {
dentalChiefComplaintsList.clear(); dentalChiefComplaintsList.clear();
notifyListeners(); notifyListeners();
int patientID = _appState.isAuthenticated ? _appState.getAuthenticatedUser()!.patientId ?? -1 : -1; int patientID = _appState.isAuthenticated ? _appState.getAuthenticatedUser()!.patientId ?? -1 : -1;
final result = await bookAppointmentsRepo.getDentalChiefComplaintsList(patientID: patientID, projectID: int.parse(currentlySelectedHospitalFromRegionFlow ?? "0"), clinicID: 17); final result = await bookAppointmentsRepo.getDentalChiefComplaintsList(
patientID: patientID, projectID: int.parse(currentlySelectedHospitalFromRegionFlow ?? "0"), clinicID: 17);
result.fold( result.fold(
(failure) async => await errorHandlerService.handleError(failure: failure), (failure) async => await errorHandlerService.handleError(failure: failure),
@ -1051,7 +1061,7 @@ class BookAppointmentsViewModel extends ChangeNotifier {
); );
} }
setBodyType(int bodyType){ setBodyType(int bodyType) {
selectedBodyTypeIndex = bodyType; selectedBodyTypeIndex = bodyType;
selectedCategory = 0; selectedCategory = 0;
selectedBodyPartList = []; selectedBodyPartList = [];
@ -1059,33 +1069,33 @@ class BookAppointmentsViewModel extends ChangeNotifier {
notifyListeners(); notifyListeners();
} }
FutureOr<void> getLaserClinic() async{ FutureOr<void> getLaserClinic() async {
isBodyPartsLoading = true; isBodyPartsLoading = true;
int id = bodyTypes[selectedBodyTypeIndex][selectedCategory].laserCategoryID; int id = bodyTypes[selectedBodyTypeIndex][selectedCategory].laserCategoryID;
int projectID = currentlySelectedHospitalFromRegionFlow != null ? int.parse(currentlySelectedHospitalFromRegionFlow!) : 0; int projectID = currentlySelectedHospitalFromRegionFlow != null ? int.parse(currentlySelectedHospitalFromRegionFlow!) : 0;
int languageID = _appState.isArabic() ? 1 : 0; int languageID = _appState.isArabic() ? 1 : 0;
final result = await bookAppointmentsRepo.getLaserClinics(id, projectID, languageID); final result = await bookAppointmentsRepo.getLaserClinics(id, projectID, languageID);
result.fold( result.fold(
(failure) { (failure) {
isBodyPartsLoading = false; isBodyPartsLoading = false;
notifyListeners(); notifyListeners();
}, },
(apiResponse) {isBodyPartsLoading = false; (apiResponse) {
isBodyPartsLoading = false;
if (apiResponse.messageStatus == 2) { if (apiResponse.messageStatus == 2) {
// dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {}); // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
} else if (apiResponse.messageStatus == 1) { } else if (apiResponse.messageStatus == 1) {
List<LaserBodyPart> response =apiResponse.data!; List<LaserBodyPart> response = apiResponse.data!;
if(response.first.category == 2 || response.first.category == 10 ) response.remove(response.first); if (response.first.category == 2 || response.first.category == 10) response.remove(response.first);
laserBodyPartsList = response; laserBodyPartsList = response;
} }
notifyListeners(); notifyListeners();
}, },
); );
} }
int getDuration() { int getDuration() {
var duration = 0; var duration = 0;
var lowerUpperLegsList = selectedBodyPartList.where((element) => element.mappingCode == "47" || element.mappingCode == "48")?.toList() ?? []; var lowerUpperLegsList = selectedBodyPartList.where((element) => element.mappingCode == "47" || element.mappingCode == "48")?.toList() ?? [];
var upperLowerArmsList = selectedBodyPartList.where((element) => element.mappingCode == "40" || element.mappingCode == "41")?.toList() ?? []; var upperLowerArmsList = selectedBodyPartList.where((element) => element.mappingCode == "40" || element.mappingCode == "41")?.toList() ?? [];
@ -1110,21 +1120,25 @@ class BookAppointmentsViewModel extends ChangeNotifier {
} }
void setSelectedBodyPart(LaserBodyPart part) { void setSelectedBodyPart(LaserBodyPart part) {
if(selectedBodyPartList.contains(part)){ if (selectedBodyPartList.contains(part)) {
selectedBodyPartList.remove(part); selectedBodyPartList.remove(part);
this.duration = getDuration(); this.duration = getDuration();
notifyListeners(); notifyListeners();
} else { } else {
if(this.duration == 90){ if (this.duration == 90) {
dialogService.showErrorBottomSheet(message: "Duration can not exceed 90 min".needTranslation,); dialogService.showErrorBottomSheet(
message: "Duration can not exceed 90 min".needTranslation,
);
return; return;
} }
selectedBodyPartList.add(part); selectedBodyPartList.add(part);
var duration = getDuration(); var duration = getDuration();
if(duration > 90){ if (duration > 90) {
selectedBodyPartList.remove(part); selectedBodyPartList.remove(part);
dialogService.showErrorBottomSheet(message: "Duration Exceeds 90 min".needTranslation,); dialogService.showErrorBottomSheet(
message: "Duration Exceeds 90 min".needTranslation,
);
return; return;
} }
this.duration = duration; this.duration = duration;
@ -1137,10 +1151,10 @@ class BookAppointmentsViewModel extends ChangeNotifier {
} }
String getLaserProcedureNameWRTLanguage(LaserBodyPart part) { String getLaserProcedureNameWRTLanguage(LaserBodyPart part) {
if(_appState.isArabic()){ if (_appState.isArabic()) {
return part.bodyPartN??""; return part.bodyPartN ?? "";
}else { } else {
return part.bodyPart??""; return part.bodyPart ?? "";
} }
} }

@ -46,7 +46,6 @@ abstract class EmergencyServicesRepo {
Future<Either<Failure, GenericApiModel<List<PatientERTransportationMethod>>>> getTransportationMethods({int? id}); Future<Either<Failure, GenericApiModel<List<PatientERTransportationMethod>>>> getTransportationMethods({int? id});
Future<Either<Failure, GenericApiModel<bool>>> submitAmbulanceRequest(PatientER_RC request); Future<Either<Failure, GenericApiModel<bool>>> submitAmbulanceRequest(PatientER_RC request);
Future<Either<Failure, GenericApiModel<List<AmbulanceRequestOrdersModel>>>> getTransportationOrders({int? id}); Future<Either<Failure, GenericApiModel<List<AmbulanceRequestOrdersModel>>>> getTransportationOrders({int? id});
@ -158,7 +157,10 @@ class EmergencyServicesRepoImp implements EmergencyServicesRepo {
onSuccess: (response, statusCode, {messageStatus, errorMessage}) { onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try { try {
final list = response['response']['transportationservices']; final list = response['response']['transportationservices'];
final proceduresList = list.map((item) => PatientERTransportationMethod.fromJson(item as Map<String, dynamic>)).toList().cast<PatientERTransportationMethod>(); final proceduresList = list
.map((item) => PatientERTransportationMethod.fromJson(item as Map<String, dynamic>))
.toList()
.cast<PatientERTransportationMethod>();
apiResponse = GenericApiModel<List<PatientERTransportationMethod>>( apiResponse = GenericApiModel<List<PatientERTransportationMethod>>(
messageStatus: messageStatus, messageStatus: messageStatus,
@ -257,7 +259,7 @@ class EmergencyServicesRepoImp implements EmergencyServicesRepo {
Failure? failure; Failure? failure;
await apiClient.post( await apiClient.post(
body: {}, body: {},
"$GET_ALL_TRANSPORTATIONS_ORDERS?patientID=$id", "$GET_ALL_TRANSPORTATIONS_ORDERS?patientID=$id",
isRCService: true, isRCService: true,
onFailure: (error, statusCode, {messageStatus, failureType}) { onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType; failure = failureType;
@ -265,7 +267,8 @@ class EmergencyServicesRepoImp implements EmergencyServicesRepo {
onSuccess: (response, statusCode, {messageStatus, errorMessage}) { onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try { try {
final list = response['response']; final list = response['response'];
final proceduresList = list.map((item) => AmbulanceRequestOrdersModel.fromJson(item as Map<String, dynamic>)).toList().cast<AmbulanceRequestOrdersModel>(); final proceduresList =
list.map((item) => AmbulanceRequestOrdersModel.fromJson(item as Map<String, dynamic>)).toList().cast<AmbulanceRequestOrdersModel>();
apiResponse = GenericApiModel<List<AmbulanceRequestOrdersModel>>( apiResponse = GenericApiModel<List<AmbulanceRequestOrdersModel>>(
messageStatus: messageStatus, messageStatus: messageStatus,
@ -335,13 +338,11 @@ class EmergencyServicesRepoImp implements EmergencyServicesRepo {
await apiClient.post( await apiClient.post(
CHECK_PATIENT_ER_ADVANCE_BALANCE, CHECK_PATIENT_ER_ADVANCE_BALANCE,
body: mapDevice, body: mapDevice,
onFailure: (error, statusCode, {messageStatus, failureType}) { onFailure: (error, statusCode, {messageStatus, failureType}) => failure = failureType,
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) { onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try { try {
final bool patientHasERBalance = response['BalanceAmount'] > 0; final bool patientHasERBalance = response['BalanceAmount'] > 0;
print(patientHasERBalance); log(patientHasERBalance.toString());
apiResponse = GenericApiModel<dynamic>( apiResponse = GenericApiModel<dynamic>(
messageStatus: messageStatus, messageStatus: messageStatus,
statusCode: statusCode, statusCode: statusCode,
@ -361,7 +362,6 @@ class EmergencyServicesRepoImp implements EmergencyServicesRepo {
} }
} }
@override @override
Future<Either<Failure, GenericApiModel<EROnlineCheckInPaymentDetailsResponse>>> checkPatientERPaymentInformation({int? projectID}) async { Future<Either<Failure, GenericApiModel<EROnlineCheckInPaymentDetailsResponse>>> checkPatientERPaymentInformation({int? projectID}) async {
Map<String, dynamic> mapDevice = {"ClinicID": 10, "ProjectID": projectID ?? 0}; Map<String, dynamic> mapDevice = {"ClinicID": 10, "ProjectID": projectID ?? 0};

@ -25,6 +25,7 @@ import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/
import 'package:hmg_patient_app_new/features/emergency_services/models/AmbulanceCallingPlace.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/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/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/PatientERTransportationMethod.dart' import 'package:hmg_patient_app_new/features/emergency_services/models/resp_model/PatientERTransportationMethod.dart'
show PatientERTransportationMethod; 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/ProjectAvgERWaitingTime.dart';
@ -32,7 +33,6 @@ import 'package:hmg_patient_app_new/features/emergency_services/models/resp_mode
import 'package:hmg_patient_app_new/features/location/PlaceDetails.dart' show PlaceDetails; 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/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/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/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/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/features/my_appointments/my_appointments_repo.dart';
@ -41,10 +41,10 @@ import 'package:hmg_patient_app_new/presentation/emergency_services/RRT/rrt_map_
import 'package:hmg_patient_app_new/presentation/emergency_services/RRT/rrt_request_type_select.dart'; import 'package:hmg_patient_app_new/presentation/emergency_services/RRT/rrt_request_type_select.dart';
import 'package:hmg_patient_app_new/presentation/emergency_services/RRT/terms_and_condition.dart'; import 'package:hmg_patient_app_new/presentation/emergency_services/RRT/terms_and_condition.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/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/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/call_ambulance/tracking_screen.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/nearest_er_page.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/routes/app_routes.dart' show AppRoutes;
import 'package:hmg_patient_app_new/services/dialog_service.dart'; import 'package:hmg_patient_app_new/services/dialog_service.dart';
@ -129,8 +129,7 @@ class EmergencyServicesViewModel extends ChangeNotifier {
notifyListeners(); notifyListeners();
} }
get isGMSAvailable get isGMSAvailable {
{
return appState.isGMSAvailable; return appState.isGMSAvailable;
} }
@ -212,7 +211,8 @@ class EmergencyServicesViewModel extends ChangeNotifier {
if (query.isEmpty) { if (query.isEmpty) {
nearestERFilteredList = nearestERList; nearestERFilteredList = nearestERList;
} else { } else {
nearestERFilteredList = nearestERList.where((er) => er.projectName != null && er.projectName!.toLowerCase().contains(query.toLowerCase())).toList(); nearestERFilteredList =
nearestERList.where((er) => er.projectName != null && er.projectName!.toLowerCase().contains(query.toLowerCase())).toList();
} }
notifyListeners(); notifyListeners();
} }
@ -295,8 +295,7 @@ class EmergencyServicesViewModel extends ChangeNotifier {
onSuccess: (position) { onSuccess: (position) {
updateBottomSheetState(BottomSheetType.FIXED); updateBottomSheetState(BottomSheetType.FIXED);
navServices.push( navServices.push(
CustomPageRoute( CustomPageRoute(page: CallAmbulancePage(), direction: AxisDirection.down),
page: CallAmbulancePage(), direction: AxisDirection.down),
); );
}); });
} else { } else {
@ -304,9 +303,7 @@ class EmergencyServicesViewModel extends ChangeNotifier {
message: "You Need To Login First To Continue".needTranslation, message: "You Need To Login First To Continue".needTranslation,
onOkPressed: () { onOkPressed: () {
navServices.pop(); navServices.pop();
navServices.pushAndReplace( navServices.pushAndReplace(AppRoutes.loginScreen);
AppRoutes.loginScreen
);
}); });
} }
} }
@ -338,19 +335,20 @@ class EmergencyServicesViewModel extends ChangeNotifier {
void setIsGMSAvailable(bool value) { void setIsGMSAvailable(bool value) {
notifyListeners(); notifyListeners();
} }
Future<void> checkPatientERAdvanceBalance({Function(dynamic)? onSuccess, Function(String)? onError}) async { Future<void> checkPatientERAdvanceBalance({Function(dynamic)? onSuccess, Function(String)? onError}) async {
final result = await emergencyServicesRepo.checkPatientERAdvanceBalance(); final result = await emergencyServicesRepo.checkPatientERAdvanceBalance();
result.fold( result.fold(
// (failure) async => await errorHandlerService.handleError(failure: failure), // (failure) async => await errorHandlerService.handleError(failure: failure),
(failure) { (failure) {
patientHasAdvanceERBalance = false; patientHasAdvanceERBalance = false;
isERBookAppointment = true; isERBookAppointment = true;
if (onSuccess != null) { if (onSuccess != null) {
onSuccess(failure.message); onSuccess(failure.message);
} }
}, },
(apiResponse) { (apiResponse) {
if (apiResponse.messageStatus == 2) { if (apiResponse.messageStatus == 2) {
// dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {}); // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
patientHasAdvanceERBalance = false; patientHasAdvanceERBalance = false;
@ -371,12 +369,12 @@ class EmergencyServicesViewModel extends ChangeNotifier {
final result = await emergencyServicesRepo.checkPatientERPaymentInformation(projectID: selectedHospital!.iD); final result = await emergencyServicesRepo.checkPatientERPaymentInformation(projectID: selectedHospital!.iD);
result.fold( result.fold(
(failure) { (failure) {
if (onError != null) { if (onError != null) {
onError(failure.message); onError(failure.message);
} }
}, },
(apiResponse) { (apiResponse) {
if (apiResponse.messageStatus == 2) { if (apiResponse.messageStatus == 2) {
} else if (apiResponse.messageStatus == 1) { } else if (apiResponse.messageStatus == 1) {
erOnlineCheckInPaymentDetailsResponse = apiResponse.data!; erOnlineCheckInPaymentDetailsResponse = apiResponse.data!;
@ -389,7 +387,8 @@ class EmergencyServicesViewModel extends ChangeNotifier {
); );
} }
Future<void> ER_CreateAdvancePayment({required String paymentMethodName, required String paymentReference, Function(dynamic)? onSuccess, Function(String)? onError}) async { Future<void> ER_CreateAdvancePayment(
{required String paymentMethodName, required String paymentReference, Function(dynamic)? onSuccess, Function(String)? onError}) async {
final result = await emergencyServicesRepo.createAdvancePaymentForER( final result = await emergencyServicesRepo.createAdvancePaymentForER(
projectID: selectedHospital!.iD, projectID: selectedHospital!.iD,
authUser: appState.getAuthenticatedUser()!, authUser: appState.getAuthenticatedUser()!,
@ -398,12 +397,12 @@ class EmergencyServicesViewModel extends ChangeNotifier {
paymentReference: paymentReference); paymentReference: paymentReference);
result.fold( result.fold(
(failure) { (failure) {
if (onError != null) { if (onError != null) {
onError(failure.message); onError(failure.message);
} }
}, },
(apiResponse) { (apiResponse) {
if (apiResponse.messageStatus == 2) { if (apiResponse.messageStatus == 2) {
} else if (apiResponse.messageStatus == 1) { } else if (apiResponse.messageStatus == 1) {
// erOnlineCheckInPaymentDetailsResponse = apiResponse.data!; // erOnlineCheckInPaymentDetailsResponse = apiResponse.data!;
@ -417,12 +416,17 @@ class EmergencyServicesViewModel extends ChangeNotifier {
} }
Future<void> addAdvanceNumberRequest( Future<void> addAdvanceNumberRequest(
{required String advanceNumber, required String paymentReference, required String appointmentNo, Function(dynamic)? onSuccess, Function(String)? onError}) async { {required String advanceNumber,
final result = await emergencyServicesRepo.addAdvanceNumberRequest(advanceNumber: advanceNumber, paymentReference: paymentReference, appointmentNo: appointmentNo); 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( result.fold(
(failure) async => await errorHandlerService.handleError(failure: failure), (failure) async => await errorHandlerService.handleError(failure: failure),
(apiResponse) { (apiResponse) {
if (apiResponse.messageStatus == 2) { if (apiResponse.messageStatus == 2) {
// dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {}); // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
} else if (apiResponse.messageStatus == 1) { } else if (apiResponse.messageStatus == 1) {
@ -440,12 +444,12 @@ class EmergencyServicesViewModel extends ChangeNotifier {
result.fold( result.fold(
// (failure) async => await errorHandlerService.handleError(failure: failure), // (failure) async => await errorHandlerService.handleError(failure: failure),
(failure) { (failure) {
if (onError != null) { if (onError != null) {
onError(failure.message); onError(failure.message);
} }
}, },
(apiResponse) { (apiResponse) {
if (apiResponse.messageStatus == 2) { if (apiResponse.messageStatus == 2) {
if (onError != null) { if (onError != null) {
onError(apiResponse.errorMessage!); onError(apiResponse.errorMessage!);
@ -465,12 +469,12 @@ class EmergencyServicesViewModel extends ChangeNotifier {
result.fold( result.fold(
// (failure) async => await errorHandlerService.handleError(failure: failure), // (failure) async => await errorHandlerService.handleError(failure: failure),
(failure) { (failure) {
if (onError != null) { if (onError != null) {
onError(failure.message); onError(failure.message);
} }
}, },
(apiResponse) { (apiResponse) {
if (apiResponse.messageStatus == 2) { if (apiResponse.messageStatus == 2) {
if (onError != null) { if (onError != null) {
onError(apiResponse.data["InvoiceResponse"]["Message"]); onError(apiResponse.data["InvoiceResponse"]["Message"]);
@ -496,14 +500,13 @@ class EmergencyServicesViewModel extends ChangeNotifier {
onOkPressed: () { onOkPressed: () {
navServices.pop(); navServices.pop();
print("inside the ok button"); print("inside the ok button");
getIt<AuthenticationViewModel>().onLoginPressed(); getIt<AuthenticationViewModel>().onLoginPressed();
}); });
return; return;
} }
int? id = appState.getAuthenticatedUser()?.patientId; int? id = appState.getAuthenticatedUser()?.patientId;
LoaderBottomSheet.showLoader( LoaderBottomSheet.showLoader(loadingText: "Getting Ambulance Transport Option".needTranslation);
loadingText: "Getting Ambulance Transport Option".needTranslation);
notifyListeners(); notifyListeners();
var response = await emergencyServicesRepo.getTransportationMethods(id: id); var response = await emergencyServicesRepo.getTransportationMethods(id: id);
@ -522,8 +525,7 @@ class EmergencyServicesViewModel extends ChangeNotifier {
Future<void> getTransportationMethods() async { Future<void> getTransportationMethods() async {
int? id = appState.getAuthenticatedUser()?.patientId; int? id = appState.getAuthenticatedUser()?.patientId;
LoaderBottomSheet.showLoader( LoaderBottomSheet.showLoader(loadingText: "Getting Ambulance Transport Option".needTranslation);
loadingText: "Getting Ambulance Transport Option".needTranslation);
notifyListeners(); notifyListeners();
var response = await emergencyServicesRepo.getTransportationMethods(id: id); var response = await emergencyServicesRepo.getTransportationMethods(id: id);
@ -634,11 +636,7 @@ class EmergencyServicesViewModel extends ChangeNotifier {
sourceList = hmcHospitalList; sourceList = hmcHospitalList;
break; break;
} }
displayList = sourceList displayList = sourceList?.where((hospital) => hospital.name != null && hospital.name!.toLowerCase().contains(query.toLowerCase())).toList();
?.where((hospital) =>
hospital.name != null &&
hospital.name!.toLowerCase().contains(query.toLowerCase()))
.toList();
notifyListeners(); notifyListeners();
} }
@ -657,7 +655,6 @@ class EmergencyServicesViewModel extends ChangeNotifier {
notifyListeners(); notifyListeners();
} }
void setSelectedHospital(HospitalsModel? hospital) { void setSelectedHospital(HospitalsModel? hospital) {
selectedHospital = hospital; selectedHospital = hospital;
notifyListeners(); notifyListeners();
@ -705,13 +702,12 @@ class EmergencyServicesViewModel extends ChangeNotifier {
} }
Future<void> updateAppointment(bool value) async { Future<void> updateAppointment(bool value) async {
if (value) { if (value) {
await getAppointments(); await getAppointments();
} else { } else {
clearAppointmentData(); clearAppointmentData();
} }
if(appointments?.isNotEmpty == true) { if (appointments?.isNotEmpty == true) {
haveAnAppointment = value; haveAnAppointment = value;
} }
notifyListeners(); notifyListeners();
@ -766,7 +762,7 @@ class EmergencyServicesViewModel extends ChangeNotifier {
} }
Future<void> getTransportationOrders({bool shouldNavigateToTrackingScreen = false, bool showLoader = false}) async { Future<void> getTransportationOrders({bool shouldNavigateToTrackingScreen = false, bool showLoader = false}) async {
if(shouldNavigateToTrackingScreen == false && showLoader ) { if (shouldNavigateToTrackingScreen == false && showLoader) {
LoaderBottomSheet.showLoader(loadingText: "Fetching Orders"); LoaderBottomSheet.showLoader(loadingText: "Fetching Orders");
} }
int? id = appState.getAuthenticatedUser()?.patientId; int? id = appState.getAuthenticatedUser()?.patientId;
@ -774,14 +770,16 @@ class EmergencyServicesViewModel extends ChangeNotifier {
notifyListeners(); notifyListeners();
var response = await emergencyServicesRepo.getTransportationOrders(id: id); var response = await emergencyServicesRepo.getTransportationOrders(id: id);
if(shouldNavigateToTrackingScreen == false && showLoader ) { if (shouldNavigateToTrackingScreen == false && showLoader) {
LoaderBottomSheet.hideLoader();} LoaderBottomSheet.hideLoader();
}
response.fold( response.fold(
(failure) async { (failure) async {
historyLoading = false; historyLoading = false;
notifyListeners(); notifyListeners();
if (shouldNavigateToTrackingScreen) { if (shouldNavigateToTrackingScreen) {
navServices.pushAndRemoveUntil(CustomPageRoute(page: TrackingScreen(state: OrderTrackingState.waitingForCall)), ModalRoute.withName("/EmergencyServicesPage")); navServices.pushAndRemoveUntil(
CustomPageRoute(page: TrackingScreen(state: OrderTrackingState.waitingForCall)), ModalRoute.withName("/EmergencyServicesPage"));
} }
}, },
(apiResponse) { (apiResponse) {

@ -0,0 +1,522 @@
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/hmg_services/models/req_models/cmc_create_new_order_req_model.dart';
import 'package:hmg_patient_app_new/features/hmg_services/models/req_models/order_update_req_model.dart';
import 'package:hmg_patient_app_new/features/hmg_services/models/resq_models/get_cmc_all_orders_resp_model.dart';
import 'package:hmg_patient_app_new/features/hmg_services/models/resq_models/get_cmc_services_resp_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';
abstract class HmgServicesRepo {
Future<Either<Failure, GenericApiModel<List<GetCMCAllOrdersResponseModel>>>> getAllComprehensiveCheckupOrders();
Future<Either<Failure, GenericApiModel<List<GetCMCAllOrdersResponseModel>>>> getAllHomeHealthCareCheckupOrders();
Future<Either<Failure, GenericApiModel<bool>>> updateCmcPresOrder(OrderUpdateRequestModel requestModel);
Future<Either<Failure, GenericApiModel<bool>>> updateHhcPresOrder(OrderUpdateRequestModel requestModel);
Future<Either<Failure, GenericApiModel<List<GetCMCServicesResponseModel>>>> getAllCmcServices({required int patientID});
Future<Either<Failure, GenericApiModel<List<GetCMCServicesResponseModel>>>> getAllHhcServices({required int patientID});
Future<Either<Failure, GenericApiModel<List<HospitalsModel>>>> getHospitalsList();
Future<Either<Failure, GenericApiModel<int>>> addCmcOrder({
required int projectID,
required int orderServiceID,
required List<PatientERCMCInsertServicesList> services,
});
Future<Either<Failure, GenericApiModel<int>>> addHhcOrder({
required int projectID,
required int orderServiceID,
required List<PatientERCMCInsertServicesList> services,
});
}
class HmgServicesRepoImp implements HmgServicesRepo {
final ApiClient apiClient;
final LoggerService loggerService;
HmgServicesRepoImp({required this.apiClient, required this.loggerService});
@override
Future<Either<Failure, GenericApiModel<List<GetCMCAllOrdersResponseModel>>>> getAllComprehensiveCheckupOrders() async {
Map<String, dynamic> requestBody = {};
try {
GenericApiModel<List<GetCMCAllOrdersResponseModel>>? apiResponse;
Failure? failure;
await apiClient.post(
ApiConsts.allCMCOrdersRc,
isRCService: true,
body: requestBody,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
loggerService.logError("CMC Orders API Failed: $error, Status: $statusCode");
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
List<GetCMCAllOrdersResponseModel> cmcOrdersList = [];
// Log the full response for debugging
// Extract MessageStatus and ErrorEndUserMessage from root level
final apiErrorMessage = response['ErrorEndUserMessage'] as String?;
// Parse the response array
if (response['response'] != null && response['response'] is List) {
final ordersList = response['response'] as List;
for (var orderJson in ordersList) {
if (orderJson is Map<String, dynamic>) {
try {
cmcOrdersList.add(GetCMCAllOrdersResponseModel.fromJson(orderJson));
} catch (e) {
loggerService.logError("Error parsing individual order: ${e.toString()}");
}
}
}
}
apiResponse = GenericApiModel<List<GetCMCAllOrdersResponseModel>>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: apiErrorMessage ?? errorMessage,
data: cmcOrdersList,
);
} catch (e) {
loggerService.logError("Error parsing CMC 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 getAllCmcOrders: ${e.toString()}");
return Left(UnknownFailure(e.toString()));
}
}
@override
Future<Either<Failure, GenericApiModel<List<GetCMCAllOrdersResponseModel>>>> getAllHomeHealthCareCheckupOrders() async {
Map<String, dynamic> requestBody = {};
try {
GenericApiModel<List<GetCMCAllOrdersResponseModel>>? apiResponse;
Failure? failure;
await apiClient.post(
ApiConsts.allHHCOrdersRc,
isRCService: true,
body: requestBody,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
loggerService.logError("HHC Orders API Failed: $error, Status: $statusCode");
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
List<GetCMCAllOrdersResponseModel> cmcOrdersList = [];
// Log the full response for debugging
// Extract MessageStatus and ErrorEndUserMessage from root level
final apiErrorMessage = response['ErrorEndUserMessage'] as String?;
// Parse the response array
if (response['response'] != null && response['response'] is List) {
final ordersList = response['response'] as List;
for (var orderJson in ordersList) {
if (orderJson is Map<String, dynamic>) {
try {
cmcOrdersList.add(GetCMCAllOrdersResponseModel.fromJson(orderJson));
} catch (e) {
loggerService.logError("Error parsing individual order: ${e.toString()}");
}
}
}
}
apiResponse = GenericApiModel<List<GetCMCAllOrdersResponseModel>>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: apiErrorMessage ?? errorMessage,
data: cmcOrdersList,
);
} catch (e) {
loggerService.logError("Error parsing HHC 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 getAllHHCOrders: ${e.toString()}");
return Left(UnknownFailure(e.toString()));
}
}
@override
Future<Either<Failure, GenericApiModel<List<GetCMCServicesResponseModel>>>> getAllCmcServices({required int patientID}) async {
Map<String, dynamic> requestBody = {};
try {
GenericApiModel<List<GetCMCServicesResponseModel>>? apiResponse;
Failure? failure;
await apiClient.post(
'${ApiConsts.allCMCServicesRc}?patientID=$patientID',
isRCService: true,
isAllowAny: true,
body: requestBody,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
loggerService.logError("CMC Services API Failed: $error, Status: $statusCode");
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
List<GetCMCServicesResponseModel> cmcServicesList = [];
if (response['response'] != null && response['response'] is List) {
final servicesList = response['response'] as List;
for (var serviceJson in servicesList) {
if (serviceJson is Map<String, dynamic>) {
cmcServicesList.add(GetCMCServicesResponseModel.fromJson(serviceJson));
}
}
}
apiResponse = GenericApiModel<List<GetCMCServicesResponseModel>>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: errorMessage,
data: cmcServicesList,
);
} catch (e) {
loggerService.logError("Error parsing CMC services: ${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) {
log("Unknown error in getAllCmcServices: ${e.toString()}");
return Left(UnknownFailure(e.toString()));
}
}
@override
Future<Either<Failure, GenericApiModel<List<GetCMCServicesResponseModel>>>> getAllHhcServices({required int patientID}) async {
Map<String, dynamic> requestBody = {};
try {
GenericApiModel<List<GetCMCServicesResponseModel>>? apiResponse;
Failure? failure;
await apiClient.post(
'${ApiConsts.allHHCServicesRc}?patientID=$patientID',
isRCService: true,
isAllowAny: true,
body: requestBody,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
loggerService.logError("HHC Services API Failed: $error, Status: $statusCode");
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
List<GetCMCServicesResponseModel> hhcServicesList = [];
if (response['response'] != null && response['response'] is List) {
final servicesList = response['response'] as List;
for (var serviceJson in servicesList) {
if (serviceJson is Map<String, dynamic>) {
hhcServicesList.add(GetCMCServicesResponseModel.fromJson(serviceJson));
}
}
}
apiResponse = GenericApiModel<List<GetCMCServicesResponseModel>>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: errorMessage,
data: hhcServicesList,
);
} catch (e) {
loggerService.logError("Error parsing HHC services: ${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) {
log("Unknown error in getAllHhcServices: ${e.toString()}");
return Left(UnknownFailure(e.toString()));
}
}
@override
Future<Either<Failure, GenericApiModel<bool>>> updateCmcPresOrder(OrderUpdateRequestModel requestModel) async {
try {
GenericApiModel<bool>? apiResponse;
Failure? failure;
await apiClient.post(
ApiConsts.updateCMCOrder,
isRCService: true,
body: requestModel.toJson(),
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
loggerService.logError("Update CMC Order API Failed: $error, Status: $statusCode");
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
apiResponse = GenericApiModel<bool>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: errorMessage,
data: true,
);
loggerService.logInfo("CMC Order updated successfully: PresOrderID=${requestModel.presOrderID}");
} catch (e) {
loggerService.logError("Error processing update CMC order response: ${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 updateCmcPresOrder: ${e.toString()}");
return Left(UnknownFailure(e.toString()));
}
}
@override
Future<Either<Failure, GenericApiModel<bool>>> updateHhcPresOrder(OrderUpdateRequestModel requestModel) async {
try {
GenericApiModel<bool>? apiResponse;
Failure? failure;
await apiClient.post(
ApiConsts.updateHHCOrder,
isRCService: true,
body: requestModel.toJson(),
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
loggerService.logError("Update HHC Order API Failed: $error, Status: $statusCode");
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
apiResponse = GenericApiModel<bool>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: errorMessage,
data: true,
);
loggerService.logInfo("HHC Order updated successfully: PresOrderID=${requestModel.presOrderID}");
} catch (e) {
loggerService.logError("Error processing update HHC order response: ${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 updateHhcPresOrder: ${e.toString()}");
return Left(UnknownFailure(e.toString()));
}
}
@override
Future<Either<Failure, GenericApiModel<List<HospitalsModel>>>> getHospitalsList() async {
Map<String, dynamic> requestBody = {};
try {
GenericApiModel<List<HospitalsModel>>? apiResponse;
Failure? failure;
await apiClient.post(
ApiConsts.getHospitalsList,
isRCService: false, // This uses the base HIS API URL, not RC
body: requestBody,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
loggerService.logError("Get Hospitals List API Failed: $error, Status: $statusCode");
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
List<HospitalsModel> hospitalsList = [];
loggerService.logInfo("Hospitals List Raw Response: $response");
if (response['ListProject'] != null && response['ListProject'] is List) {
final projectsList = response['ListProject'] as List;
for (var projectJson in projectsList) {
try {
if (projectJson is Map<String, dynamic>) {
hospitalsList.add(HospitalsModel.fromJson(projectJson));
}
} catch (e) {
loggerService.logError('Error parsing hospital item: ${e.toString()}');
}
}
} else {
loggerService.logInfo('Hospitals list response array is empty or missing');
}
apiResponse = GenericApiModel<List<HospitalsModel>>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: errorMessage,
data: hospitalsList,
);
loggerService.logInfo("Hospitals fetched successfully: ${hospitalsList.length} hospitals");
} catch (e) {
loggerService.logError("Error parsing hospitals list: ${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 getHospitalsList: ${e.toString()}");
return Left(UnknownFailure(e.toString()));
}
}
@override
Future<Either<Failure, GenericApiModel<int>>> addCmcOrder({
required int projectID,
required int orderServiceID,
required List<PatientERCMCInsertServicesList> services,
}) async {
try {
GenericApiModel<int>? apiResponse;
Failure? failure;
final requestBody = {
'ProjectID': projectID,
'OrderServiceID': orderServiceID,
'procedures': services.map((service) => service.toJson()).toList(),
};
await apiClient.post(
ApiConsts.addCMCOrder,
isRCService: true,
body: requestBody,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
loggerService.logError("Add CMC Order API Failed: $error, Status: $statusCode");
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
int requestId = 0;
if (response is Map) {
requestId = response['response'];
}
try {
apiResponse = GenericApiModel<int>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: errorMessage,
data: requestId,
);
loggerService.logInfo("CMC Order added successfully: ProjectID=$projectID, OrderServiceID=$orderServiceID");
} catch (e) {
loggerService.logError("Error processing add CMC order response: ${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 addCmcOrder: ${e.toString()}");
return Left(UnknownFailure(e.toString()));
}
}
@override
Future<Either<Failure, GenericApiModel<int>>> addHhcOrder({
required int projectID,
required int orderServiceID,
required List<PatientERCMCInsertServicesList> services,
}) async {
try {
GenericApiModel<int>? apiResponse;
Failure? failure;
final requestBody = {
'ProjectID': projectID,
'OrderServiceID': orderServiceID,
'procedures': services.map((service) => service.toJson()).toList(),
};
await apiClient.post(
ApiConsts.addHHCOrder,
isRCService: true,
body: requestBody,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
loggerService.logError("Add HHC Order API Failed: $error, Status: $statusCode");
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
int requestId = 0;
if (response is Map) {
requestId = response['response'];
}
try {
apiResponse = GenericApiModel<int>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: errorMessage,
data: requestId,
);
loggerService.logInfo("HHC Order added successfully: ProjectID=$projectID, OrderServiceID=$orderServiceID");
} catch (e) {
loggerService.logError("Error processing add HHC order response: ${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 addHhcOrder: ${e.toString()}");
return Left(UnknownFailure(e.toString()));
}
}
}

@ -0,0 +1,515 @@
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_repo.dart';
import 'package:hmg_patient_app_new/features/hmg_services/hmg_services_repo.dart';
import 'package:hmg_patient_app_new/features/hmg_services/models/req_models/cmc_create_new_order_req_model.dart';
import 'package:hmg_patient_app_new/features/hmg_services/models/req_models/order_update_req_model.dart';
import 'package:hmg_patient_app_new/features/hmg_services/models/resq_models/get_cmc_all_orders_resp_model.dart';
import 'package:hmg_patient_app_new/features/hmg_services/models/resq_models/get_cmc_services_resp_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/hospital_model.dart';
import 'package:hmg_patient_app_new/services/error_handler_service.dart';
class HmgServicesViewModel extends ChangeNotifier {
final HmgServicesRepo hmgServicesRepo;
final BookAppointmentsRepo bookAppointmentsRepo;
final ErrorHandlerService errorHandlerService;
HmgServicesViewModel({required this.bookAppointmentsRepo, required this.hmgServicesRepo, required this.errorHandlerService});
bool isCmcOrdersLoading = false;
bool isCmcServicesLoading = false;
bool isUpdatingOrder = false;
bool isHospitalListLoading = false;
// HHC specific loading states
bool isHhcOrdersLoading = false;
bool isHhcServicesLoading = false;
List<GetCMCAllOrdersResponseModel> cmcOrdersList = [];
List<GetCMCServicesResponseModel> cmcServicesList = [];
List<HospitalsModel> hospitalsList = [];
List<HospitalsModel> filteredHospitalsList = [];
HospitalsModel? selectedHospital;
// HHC specific lists
List<GetCMCAllOrdersResponseModel> hhcOrdersList = [];
List<GetCMCServicesResponseModel> hhcServicesList = [];
// CMC order creation state
HospitalsModel? selectedHospitalForOrder;
GetCMCServicesResponseModel? selectedServiceForOrder;
// HHC order creation state (no hospital selection needed for home healthcare)
GetCMCServicesResponseModel? selectedServiceForHhcOrder;
// HHC multiple services selection
List<GetCMCServicesResponseModel> selectedHhcServices = [];
Future<void> getCmcOrdersList() async {
cmcOrdersList.clear();
isCmcOrdersLoading = true;
notifyListeners();
await getAllCmcOrders();
}
// Helper to sort hospitals by distance (ascending). Safely converts distanceinkMS to double.
void _sortHospitalsByDistance(List<HospitalsModel> list) {
double toDouble(dynamic v) {
if (v == null) return double.infinity;
if (v is num) return v.toDouble();
if (v is String) return double.tryParse(v) ?? double.infinity;
return double.infinity;
}
list.sort((a, b) {
final da = toDouble(a.distanceInKilometers);
final db = toDouble(b.distanceInKilometers);
return da.compareTo(db);
});
}
Future<void> getAllCmcOrders({
Function(dynamic)? onSuccess,
Function(String)? onError,
}) async {
isCmcOrdersLoading = true;
notifyListeners();
final result = await hmgServicesRepo.getAllComprehensiveCheckupOrders();
result.fold(
(failure) async {
isCmcOrdersLoading = false;
notifyListeners();
await errorHandlerService.handleError(failure: failure);
if (onError != null) {
onError(failure.toString());
}
},
(apiResponse) {
isCmcOrdersLoading = false;
if (apiResponse.messageStatus == 1) {
cmcOrdersList = apiResponse.data ?? [];
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse);
}
} else {
notifyListeners();
if (onError != null) {
onError(apiResponse.errorMessage ?? 'Unknown error');
}
}
},
);
}
Future<void> getAllCmcServices({required int patientID, Function(dynamic)? onSuccess, Function(String)? onError}) async {
isCmcServicesLoading = true;
notifyListeners();
final result = await hmgServicesRepo.getAllCmcServices(patientID: patientID);
result.fold(
(failure) async {
isCmcServicesLoading = false;
notifyListeners();
await errorHandlerService.handleError(failure: failure);
if (onError != null) {
onError(failure.toString());
}
},
(apiResponse) {
isCmcServicesLoading = false;
if (apiResponse.messageStatus == 1) {
cmcServicesList = apiResponse.data ?? [];
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse);
}
} else {
notifyListeners();
if (onError != null) {
onError(apiResponse.errorMessage ?? 'Unknown error');
}
}
},
);
}
Future<bool> updateCmcPresOrder({
required OrderUpdateRequestModel requestModel,
Function(dynamic)? onSuccess,
Function(String)? onError,
}) async {
isUpdatingOrder = true;
notifyListeners();
final result = await hmgServicesRepo.updateCmcPresOrder(requestModel);
bool success = false;
result.fold(
(failure) async {
isUpdatingOrder = false;
notifyListeners();
await errorHandlerService.handleError(failure: failure);
if (onError != null) {
onError(failure.toString());
}
},
(apiResponse) {
isUpdatingOrder = false;
if (apiResponse.messageStatus == 1) {
success = true;
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse);
}
} else {
notifyListeners();
if (onError != null) {
onError(apiResponse.errorMessage ?? 'Unknown error');
}
}
},
);
return success;
}
Future<void> getHospitalsList({Function(dynamic)? onSuccess, Function(String)? onError}) async {
isHospitalListLoading = true;
notifyListeners();
final result = await hmgServicesRepo.getHospitalsList();
result.fold(
(failure) async {
isHospitalListLoading = false;
notifyListeners();
await errorHandlerService.handleError(failure: failure);
if (onError != null) {
onError(failure.toString());
}
},
(apiResponse) {
isHospitalListLoading = false;
if (apiResponse.messageStatus == 1) {
hospitalsList = apiResponse.data ?? [];
filteredHospitalsList = List.from(hospitalsList);
// ensure hospitals are sorted by distance before showing
_sortHospitalsByDistance(filteredHospitalsList);
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse);
}
} else {
notifyListeners();
if (onError != null) {
onError(apiResponse.errorMessage ?? 'Unknown error');
}
}
},
);
}
void filterHospitalsByString(String searchText, bool isArabic) {
if (searchText.isEmpty) {
filteredHospitalsList = List.from(hospitalsList);
_sortHospitalsByDistance(filteredHospitalsList);
} else {
filteredHospitalsList = hospitalsList.where((HospitalsModel hospital) {
final name = isArabic ? (hospital.nameN ?? '') : (hospital.name ?? '');
return name.toLowerCase().contains(searchText.toLowerCase());
}).toList();
_sortHospitalsByDistance(filteredHospitalsList);
}
notifyListeners();
}
void setSelectedHospital(HospitalsModel? hospital) {
selectedHospital = hospital;
notifyListeners();
}
void clearHospitalSelection() {
selectedHospital = null;
filteredHospitalsList = List.from(hospitalsList);
_sortHospitalsByDistance(filteredHospitalsList);
notifyListeners();
}
// CMC Order management methods
void setSelectedHospitalForOrder(HospitalsModel? hospital) {
selectedHospitalForOrder = hospital;
notifyListeners();
}
void setSelectedServiceForOrder(GetCMCServicesResponseModel? service) {
selectedServiceForOrder = service;
notifyListeners();
}
void clearOrderSelection() {
selectedHospitalForOrder = null;
selectedServiceForOrder = null;
notifyListeners();
}
bool get isOrderReadyToConfirm => selectedHospitalForOrder != null && selectedServiceForOrder != null;
Future<int?> addCmcOrder({
required int projectID,
required int orderServiceID,
required List<PatientERCMCInsertServicesList> services,
Function(int)? onSuccess,
Function(String)? onError,
}) async {
isUpdatingOrder = true;
notifyListeners();
final result = await hmgServicesRepo.addCmcOrder(
projectID: projectID,
orderServiceID: orderServiceID,
services: services,
);
int requestId = 0;
result.fold(
(failure) async {
isUpdatingOrder = false;
notifyListeners();
await errorHandlerService.handleError(failure: failure);
if (onError != null) {
onError(failure.toString());
}
},
(apiResponse) {
isUpdatingOrder = false;
if (apiResponse.messageStatus == 1) {
requestId = apiResponse.data ?? 0;
notifyListeners();
if (onSuccess != null) {
onSuccess(requestId);
}
} else {
notifyListeners();
if (onError != null) {
onError(apiResponse.errorMessage ?? 'Unknown error');
}
}
},
);
return requestId;
}
// ******************* HOME HEALTHCARE APIs ********************
Future<void> getHhcOrdersList() async {
hhcOrdersList.clear();
isHhcOrdersLoading = true;
notifyListeners();
await getAllHhcOrders();
}
Future<void> getAllHhcOrders({
Function(dynamic)? onSuccess,
Function(String)? onError,
}) async {
isHhcOrdersLoading = true;
notifyListeners();
final result = await hmgServicesRepo.getAllHomeHealthCareCheckupOrders();
result.fold(
(failure) async {
isHhcOrdersLoading = false;
notifyListeners();
await errorHandlerService.handleError(failure: failure);
if (onError != null) {
onError(failure.toString());
}
},
(apiResponse) {
isHhcOrdersLoading = false;
if (apiResponse.messageStatus == 1) {
hhcOrdersList = apiResponse.data ?? [];
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse);
}
} else {
notifyListeners();
if (onError != null) {
onError(apiResponse.errorMessage ?? 'Unknown error');
}
}
},
);
}
Future<void> getAllHhcServices({required int patientID, Function(dynamic)? onSuccess, Function(String)? onError}) async {
isHhcServicesLoading = true;
notifyListeners();
final result = await hmgServicesRepo.getAllHhcServices(patientID: patientID);
result.fold(
(failure) async {
isHhcServicesLoading = false;
notifyListeners();
await errorHandlerService.handleError(failure: failure);
if (onError != null) {
onError(failure.toString());
}
},
(apiResponse) {
isHhcServicesLoading = false;
if (apiResponse.messageStatus == 1) {
hhcServicesList = apiResponse.data ?? [];
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse);
}
} else {
notifyListeners();
if (onError != null) {
onError(apiResponse.errorMessage ?? 'Unknown error');
}
}
},
);
}
Future<bool> updateHhcPresOrder({
required OrderUpdateRequestModel requestModel,
Function(dynamic)? onSuccess,
Function(String)? onError,
}) async {
isUpdatingOrder = true;
notifyListeners();
final result = await hmgServicesRepo.updateHhcPresOrder(requestModel);
bool success = false;
result.fold(
(failure) async {
isUpdatingOrder = false;
notifyListeners();
await errorHandlerService.handleError(failure: failure);
if (onError != null) {
onError(failure.toString());
}
},
(apiResponse) {
isUpdatingOrder = false;
if (apiResponse.messageStatus == 1) {
success = true;
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse);
}
} else {
notifyListeners();
if (onError != null) {
onError(apiResponse.errorMessage ?? 'Unknown error');
}
}
},
);
return success;
}
Future<int?> addHhcOrder({
required int projectID,
required int orderServiceID,
required List<PatientERCMCInsertServicesList> services,
Function(int)? onSuccess,
Function(String)? onError,
}) async {
isUpdatingOrder = true;
notifyListeners();
final result = await hmgServicesRepo.addHhcOrder(
projectID: projectID,
orderServiceID: orderServiceID,
services: services,
);
int requestId = 0;
result.fold(
(failure) async {
isUpdatingOrder = false;
notifyListeners();
await errorHandlerService.handleError(failure: failure);
if (onError != null) {
onError(failure.toString());
}
},
(apiResponse) {
isUpdatingOrder = false;
if (apiResponse.messageStatus == 1) {
requestId = apiResponse.data ?? 0;
notifyListeners();
if (onSuccess != null) {
onSuccess(requestId);
}
} else {
notifyListeners();
if (onError != null) {
onError(apiResponse.errorMessage ?? 'Unknown error');
}
}
},
);
return requestId;
}
// HHC Order management methods (no hospital selection for home healthcare)
void setSelectedServiceForHhcOrder(GetCMCServicesResponseModel? service) {
selectedServiceForHhcOrder = service;
notifyListeners();
}
void clearHhcOrderSelection() {
selectedServiceForHhcOrder = null;
selectedHhcServices.clear();
notifyListeners();
}
bool get isHhcOrderReadyToConfirm => selectedServiceForHhcOrder != null;
// Multiple HHC services selection methods
void toggleHhcServiceSelection(GetCMCServicesResponseModel service) {
final index = selectedHhcServices.indexWhere((s) => s.iD == service.iD);
if (index != -1) {
selectedHhcServices.removeAt(index);
} else {
selectedHhcServices.add(service);
}
notifyListeners();
}
bool isHhcServiceSelected(GetCMCServicesResponseModel service) {
return selectedHhcServices.any((s) => s.iD == service.iD);
}
double getHhcSelectedServicesTotal() {
double total = 0.0;
for (var service in selectedHhcServices) {
total += (service.priceTotal ?? 0);
}
return total;
}
void clearHhcServicesSelection() {
selectedHhcServices.clear();
notifyListeners();
}
}

@ -0,0 +1,131 @@
class CMCInsertPresOrderRequestModel {
double? versionID;
int? channel;
int? languageID;
String? iPAdress;
String? generalid;
int? patientOutSA;
String? sessionID;
bool? isDentalAllowedBackend;
int? deviceTypeID;
int? patientID;
String? tokenID;
int? patientTypeID;
int? patientType;
double? latitude;
double? longitude;
int? createdBy;
int? orderServiceID;
int? projectID;
List<PatientERCMCInsertServicesList>? patientERCMCInsertServicesList;
CMCInsertPresOrderRequestModel(
{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.latitude,
this.longitude,
this.createdBy,
this.orderServiceID,
this.projectID,
this.patientERCMCInsertServicesList});
CMCInsertPresOrderRequestModel.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'];
latitude = json['Latitude'];
longitude = json['Longitude'];
createdBy = json['CreatedBy'];
orderServiceID = json['OrderServiceID'];
projectID = json['ProjectId'];
if (json['PatientER_CMC_InsertServicesList'] != null) {
patientERCMCInsertServicesList = <PatientERCMCInsertServicesList>[];
json['PatientER_CMC_InsertServicesList'].forEach((v) {
patientERCMCInsertServicesList!.add(
new PatientERCMCInsertServicesList.fromJson(v),
);
});
}
}
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['isOutPatient'] = this.patientOutSA == 0 ? false : true;
data['SessionID'] = this.sessionID;
data['isDentalAllowedBackend'] = this.isDentalAllowedBackend;
data['DeviceTypeID'] = this.deviceTypeID;
data['TokenID'] = this.tokenID;
data['PatientTypeID'] = this.patientTypeID;
data['PatientType'] = this.patientType;
data['latitude'] = this.latitude;
data['longitude'] = this.longitude;
// data['CreatedBy'] = this.createdBy;
data['OrderServiceID'] = this.orderServiceID;
data['ProjectID'] = this.projectID;
if (this.patientERCMCInsertServicesList != null) {
data['procedures'] = this.patientERCMCInsertServicesList!.map((v) => v.toJson()).toList();
}
return data;
}
}
class PatientERCMCInsertServicesList {
int? recordID;
String? serviceID;
String? selectedServiceName;
String? selectedServiceNameAR;
dynamic price;
dynamic vAT;
dynamic totalPrice;
PatientERCMCInsertServicesList(
{this.recordID, this.serviceID, this.selectedServiceName, this.selectedServiceNameAR, this.price, this.vAT, this.totalPrice});
PatientERCMCInsertServicesList.fromJson(Map<String, dynamic> json) {
recordID = json['RecordID'];
serviceID = json['ServiceID'];
selectedServiceName = json['selectedServiceName'];
selectedServiceNameAR = json['selectedServiceNameAR'];
price = json['Price'];
vAT = json['VAT'];
totalPrice = json['TotalPrice'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['RecordID'] = this.recordID;
data['ServiceID'] = this.serviceID;
data['selectedServiceName'] = this.selectedServiceName;
data['selectedServiceNameAR'] = this.selectedServiceNameAR;
data['Price'] = this.price;
data['VAT'] = this.vAT;
data['TotalPrice'] = this.totalPrice;
return data;
}
}

@ -0,0 +1,41 @@
class CmcCreateServiceOrderReqModel {
int? recordID;
String? serviceID;
String? selectedServiceName;
String? selectedServiceNameAR;
dynamic price;
dynamic vAT;
dynamic totalPrice;
CmcCreateServiceOrderReqModel({
this.recordID,
this.serviceID,
this.selectedServiceName,
this.selectedServiceNameAR,
this.price,
this.vAT,
this.totalPrice,
});
CmcCreateServiceOrderReqModel.fromJson(Map<String, dynamic> json) {
recordID = json['RecordID'];
serviceID = json['ServiceID'];
selectedServiceName = json['selectedServiceName'];
selectedServiceNameAR = json['selectedServiceNameAR'];
price = json['Price'];
vAT = json['VAT'];
totalPrice = json['TotalPrice'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['RecordID'] = recordID;
data['ServiceID'] = serviceID;
data['selectedServiceName'] = selectedServiceName;
data['selectedServiceNameAR'] = selectedServiceNameAR;
data['Price'] = price;
data['VAT'] = vAT;
data['TotalPrice'] = totalPrice;
return data;
}
}

@ -0,0 +1,82 @@
class OrderUpdateRequestModel {
double? versionID;
int? channel;
int? languageID;
String? iPAdress;
String? generalid;
int? patientOutSA;
String? sessionID;
bool? isDentalAllowedBackend;
int? deviceTypeID;
int? patientID;
String? tokenID;
int? patientTypeID;
int? patientType;
int? presOrderID;
int? presOrderStatus;
int? editedBy;
String? rejectionReason;
OrderUpdateRequestModel({
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.presOrderID,
this.presOrderStatus,
this.editedBy,
this.rejectionReason,
});
OrderUpdateRequestModel.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'];
presOrderID = json['PresOrderID'];
presOrderStatus = json['PresOrderStatus'];
editedBy = json['EditedBy'];
rejectionReason = json['RejectionReason'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['VersionID'] = versionID;
data['Channel'] = channel;
data['LanguageID'] = languageID;
data['IPAdress'] = iPAdress;
data['generalid'] = generalid;
data['PatientOutSA'] = patientOutSA;
data['SessionID'] = sessionID;
data['isDentalAllowedBackend'] = isDentalAllowedBackend;
data['DeviceTypeID'] = deviceTypeID;
data['PatientID'] = patientID;
data['TokenID'] = tokenID;
data['PatientTypeID'] = patientTypeID;
data['PatientType'] = patientType;
data['Id'] = presOrderID;
data['ClickButton'] = 14;
data['PresOrderStatus'] = presOrderStatus;
data['EditedBy'] = editedBy;
data['RejectionReason'] = rejectionReason;
return data;
}
}

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

@ -0,0 +1,57 @@
class GetCMCServicesResponseModel {
int? iD;
String? serviceID;
int? orderServiceID;
String? text;
String? textN;
dynamic price;
dynamic priceVAT;
dynamic priceTotal;
bool? isEnabled;
int? orderId;
int? quantity;
GetCMCServicesResponseModel({
this.iD,
this.serviceID,
this.orderServiceID,
this.text,
this.textN,
this.price,
this.priceVAT,
this.priceTotal,
this.isEnabled,
this.orderId,
this.quantity,
});
GetCMCServicesResponseModel.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 = {};
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,25 @@
import 'package:flutter/material.dart';
class HmgServicesComponentModel {
int action;
String title;
String subTitle;
String icon;
bool isLogin;
bool isLocked;
Color bgColor;
Color textColor;
String route;
HmgServicesComponentModel(
this.action,
this.title,
this.subTitle,
this.icon,
this.isLogin, {
this.isLocked = false,
this.bgColor = Colors.white,
this.textColor = Colors.black,
this.route = '',
});
}

@ -62,9 +62,9 @@ class HospitalsModel {
mainProjectID = json['MainProjectID']; mainProjectID = json['MainProjectID'];
projectOutSA = json['ProjectOutSA']; projectOutSA = json['ProjectOutSA'];
usingInDoctorApp = json['UsingInDoctorApp']; usingInDoctorApp = json['UsingInDoctorApp'];
this.isHMC = json["IsHMC"]; isHMC = json["IsHMC"];
this.regionArabic = json['RegionNameN']; regionArabic = json['RegionNameN'];
this.regionEnglish = json['RegionName']; regionEnglish = json['RegionName'];
} }
String? getRegionName(bool isArabic) { String? getRegionName(bool isArabic) {
@ -83,24 +83,22 @@ class HospitalsModel {
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>(); final Map<String, dynamic> data = new Map<String, dynamic>();
data['Desciption'] = this.desciption; data['Desciption'] = desciption;
data['DesciptionN'] = this.desciptionN; data['DesciptionN'] = desciptionN;
data['ID'] = this.iD; data['ID'] = iD;
data['LegalName'] = this.legalName; data['LegalName'] = legalName;
data['LegalNameN'] = this.legalNameN; data['LegalNameN'] = legalNameN;
data['Name'] = this.name; data['Name'] = name;
data['NameN'] = this.nameN; data['NameN'] = nameN;
data['PhoneNumber'] = this.phoneNumber; data['PhoneNumber'] = phoneNumber;
data['SetupID'] = this.setupID; data['SetupID'] = setupID;
data['DistanceInKilometers'] = this.distanceInKilometers; data['DistanceInKilometers'] = distanceInKilometers;
data['IsActive'] = this.isActive; data['IsActive'] = isActive;
data['Latitude'] = this.latitude; data['Latitude'] = latitude;
data['Longitude'] = this.longitude; data['Longitude'] = longitude;
data['MainProjectID'] = this.mainProjectID; data['MainProjectID'] = mainProjectID;
data['ProjectOutSA'] = this.projectOutSA; data['ProjectOutSA'] = projectOutSA;
data['UsingInDoctorApp'] = this.usingInDoctorApp; data['UsingInDoctorApp'] = usingInDoctorApp;
return data; return data;
} }
} }

@ -13,14 +13,20 @@ class RadiologyViewModel extends ChangeNotifier {
ErrorHandlerService errorHandlerService; ErrorHandlerService errorHandlerService;
List<PatientRadiologyResponseModel> patientRadiologyOrders = []; List<PatientRadiologyResponseModel> patientRadiologyOrders = [];
List<PatientRadiologyResponseModel> filteredRadiologyOrders = [];
List<PatientRadiologyResponseModel> tempRadiologyOrders = [];
String radiologyImageURL = ""; String radiologyImageURL = "";
String patientRadiologyReportPDFBase64 = ""; String patientRadiologyReportPDFBase64 = "";
late List<String> _radiologySuggestionsList = [];
List<String> get radiologySuggestions => _radiologySuggestionsList;
RadiologyViewModel({required this.radiologyRepo, required this.errorHandlerService}); RadiologyViewModel({required this.radiologyRepo, required this.errorHandlerService});
initRadiologyViewModel() { initRadiologyViewModel() {
patientRadiologyOrders.clear(); patientRadiologyOrders.clear();
filteredRadiologyOrders.clear();
isRadiologyOrdersLoading = true; isRadiologyOrdersLoading = true;
isRadiologyPDFReportLoading = true; isRadiologyPDFReportLoading = true;
radiologyImageURL = ""; radiologyImageURL = "";
@ -38,7 +44,10 @@ class RadiologyViewModel extends ChangeNotifier {
// dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {}); // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
} else if (apiResponse.messageStatus == 1) { } else if (apiResponse.messageStatus == 1) {
patientRadiologyOrders = apiResponse.data!; patientRadiologyOrders = apiResponse.data!;
filteredRadiologyOrders = List.from(patientRadiologyOrders);
tempRadiologyOrders = [...patientRadiologyOrders];
isRadiologyOrdersLoading = false; isRadiologyOrdersLoading = false;
filterSuggestions();
notifyListeners(); notifyListeners();
if (onSuccess != null) { if (onSuccess != null) {
onSuccess(apiResponse); onSuccess(apiResponse);
@ -98,4 +107,23 @@ class RadiologyViewModel extends ChangeNotifier {
}, },
); );
} }
filterSuggestions() {
final List<String> labels = patientRadiologyOrders
.map((detail) => detail.description)
.whereType<String>()
.toList();
_radiologySuggestionsList = labels.toSet().toList();
notifyListeners();
}
filterRadiologyReports(String query) {
if (query.isEmpty) {
patientRadiologyOrders =tempRadiologyOrders;// reset
} else {
filteredRadiologyOrders =
filteredRadiologyOrders.where((desc) => desc.description!.toLowerCase().contains(query.toLowerCase())).toList();
patientRadiologyOrders = filteredRadiologyOrders;
}
notifyListeners();
}
} }

@ -14,6 +14,7 @@ import 'package:hmg_patient_app_new/features/contact_us/contact_us_view_model.da
import 'package:hmg_patient_app_new/features/doctor_filter/doctor_filter_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/emergency_services/emergency_services_view_model.dart';
import 'package:hmg_patient_app_new/features/habib_wallet/habib_wallet_view_model.dart'; import 'package:hmg_patient_app_new/features/habib_wallet/habib_wallet_view_model.dart';
import 'package:hmg_patient_app_new/features/hmg_services/hmg_services_view_model.dart';
import 'package:hmg_patient_app_new/features/immediate_livecare/immediate_livecare_view_model.dart'; import 'package:hmg_patient_app_new/features/immediate_livecare/immediate_livecare_view_model.dart';
import 'package:hmg_patient_app_new/features/insurance/insurance_view_model.dart'; 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/history/lab_history_viewmodel.dart';
@ -132,7 +133,8 @@ void main() async {
), ),
ChangeNotifierProvider<EmergencyServicesViewModel>( ChangeNotifierProvider<EmergencyServicesViewModel>(
create: (_) => getIt.get<EmergencyServicesViewModel>(), create: (_) => getIt.get<EmergencyServicesViewModel>(),
),ChangeNotifierProvider<LocationViewModel>( ),
ChangeNotifierProvider<LocationViewModel>(
create: (_) => getIt.get<LocationViewModel>(), create: (_) => getIt.get<LocationViewModel>(),
), ),
ChangeNotifierProvider<TodoSectionViewModel>( ChangeNotifierProvider<TodoSectionViewModel>(
@ -140,6 +142,9 @@ void main() async {
), ),
ChangeNotifierProvider<ContactUsViewModel>( ChangeNotifierProvider<ContactUsViewModel>(
create: (_) => getIt.get<ContactUsViewModel>(), create: (_) => getIt.get<ContactUsViewModel>(),
),
ChangeNotifierProvider<HmgServicesViewModel>(
create: (_) => getIt.get<HmgServicesViewModel>(),
) )
], child: MyApp()), ], child: MyApp()),
), ),

@ -30,7 +30,7 @@ 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/common_bottom_sheet.dart';
import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart'; import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart';
import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart'; import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
import 'package:hmg_patient_app_new/widgets/shimmer/movies_shimmer_widget.dart'; import 'package:hmg_patient_app_new/widgets/shimmer/common_shimmer_widget.dart';
import 'package:maps_launcher/maps_launcher.dart'; import 'package:maps_launcher/maps_launcher.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
@ -317,7 +317,7 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
SizedBox(height: 16.h), SizedBox(height: 16.h),
Consumer<PrescriptionsViewModel>(builder: (context, prescriptionVM, child) { Consumer<PrescriptionsViewModel>(builder: (context, prescriptionVM, child) {
return prescriptionVM.isPrescriptionsDetailsLoading return prescriptionVM.isPrescriptionsDetailsLoading
? const MoviesShimmerWidget() ? const CommonShimmerWidget()
: Container( : Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration( decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: Colors.white, color: Colors.white,

@ -1,18 +1,13 @@
import 'package:easy_localization/easy_localization.dart' import 'package:easy_localization/easy_localization.dart' show StringTranslateExtension;
show tr, StringTranslateExtension;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/enums.dart'; import 'package:hmg_patient_app_new/core/enums.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_view_model.dart'; import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_view_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/appointment_via_region_viewmodel.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/facility_selection.dart'; import 'package:hmg_patient_app_new/features/my_appointments/models/facility_selection.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/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/appointments/widgets/hospital_bottom_sheet/hospital_list_items.dart'; import 'package:hmg_patient_app_new/presentation/appointments/widgets/hospital_bottom_sheet/hospital_list_items.dart';
import 'package:hmg_patient_app_new/presentation/appointments/widgets/hospital_bottom_sheet/type_selection_widget.dart';
import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
import 'package:hmg_patient_app_new/theme/colors.dart' show AppColors; import 'package:hmg_patient_app_new/theme/colors.dart' show AppColors;
import 'package:hmg_patient_app_new/widgets/input_widget.dart'; import 'package:hmg_patient_app_new/widgets/input_widget.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
@ -53,8 +48,8 @@ class HospitalBottomSheetBody extends StatelessWidget {
hintText: LocaleKeys.searchHospital.tr(), hintText: LocaleKeys.searchHospital.tr(),
controller: searchText, controller: searchText,
onChange: (value) { onChange: (value) {
appointmentsViewModel.filterHospitalListByString(value, regionalViewModel.selectedRegionId , regionalViewModel.selectedFacilityType == appointmentsViewModel.filterHospitalListByString(
FacilitySelection.HMG.name); value, regionalViewModel.selectedRegionId, regionalViewModel.selectedFacilityType == FacilitySelection.HMG.name);
}, },
isEnable: true, isEnable: true,
prefix: null, prefix: null,
@ -77,25 +72,15 @@ class HospitalBottomSheetBody extends StatelessWidget {
SizedBox( SizedBox(
height: MediaQuery.sizeOf(context).height * .4, height: MediaQuery.sizeOf(context).height * .4,
child: ListView.separated( child: ListView.separated(
itemBuilder: (_, index) itemBuilder: (_, index) {
{ var hospital = regionalViewModel.selectedFacilityType == FacilitySelection.HMG.name
var hospital = regionalViewModel.selectedFacilityType == ? appointmentsViewModel.filteredHospitalList!.registeredDoctorMap![regionalViewModel.selectedRegionId!]!.hmgDoctorList![index]
FacilitySelection.HMG.name : appointmentsViewModel.filteredHospitalList?.registeredDoctorMap?[regionalViewModel.selectedRegionId!]?.hmcDoctorList?[index];
? appointmentsViewModel
.filteredHospitalList!
.registeredDoctorMap![
regionalViewModel.selectedRegionId!]!
.hmgDoctorList![index]
: appointmentsViewModel
.filteredHospitalList
?.registeredDoctorMap?[
regionalViewModel.selectedRegionId!]
?.hmcDoctorList?[index];
return HospitalListItem( return HospitalListItem(
hospitalData: hospital, hospitalData: hospital,
isLocationEnabled: appointmentsViewModel.isLocationEnabled(), isLocationEnabled: appointmentsViewModel.isLocationEnabled(),
).onPress(() { ).onPress(() {
regionalViewModel.setHospitalModel(hospital); regionalViewModel.setHospitalModel(hospital);
if (regionalViewModel.regionBottomSheetType == RegionBottomSheetType.FOR_REGION) { if (regionalViewModel.regionBottomSheetType == RegionBottomSheetType.FOR_REGION) {
regionalViewModel.setBottomSheetState(AppointmentViaRegionState.CLINIC_SELECTION); regionalViewModel.setBottomSheetState(AppointmentViaRegionState.CLINIC_SELECTION);
regionalViewModel.handleLastStepForRegion(); regionalViewModel.handleLastStepForRegion();
@ -104,21 +89,18 @@ class HospitalBottomSheetBody extends StatelessWidget {
regionalViewModel.handleLastStepForClinic(); regionalViewModel.handleLastStepForClinic();
} else if (regionalViewModel.regionBottomSheetType == RegionBottomSheetType.REGION_FOR_DENTAL_AND_LASER) { } else if (regionalViewModel.regionBottomSheetType == RegionBottomSheetType.REGION_FOR_DENTAL_AND_LASER) {
regionalViewModel.setBottomSheetState(AppointmentViaRegionState.DOCTOR_SELECTION); regionalViewModel.setBottomSheetState(AppointmentViaRegionState.DOCTOR_SELECTION);
regionalViewModel.handleLastStepForClinicForDentalAndLaser(appointmentsViewModel.selectedClinic.clinicID??-1); regionalViewModel.handleLastStepForClinicForDentalAndLaser(appointmentsViewModel.selectedClinic.clinicID ?? -1);
// regionalViewModel.handleLastStepForClinic(); // regionalViewModel.handleLastStepForClinic();
} }
});}, });
},
separatorBuilder: (_, __) => SizedBox( separatorBuilder: (_, __) => SizedBox(
height: 16.h, height: 16.h,
), ),
itemCount: (regionalViewModel.selectedFacilityType == itemCount: (regionalViewModel.selectedFacilityType == FacilitySelection.HMG.name
FacilitySelection.HMG.name ? (appointmentsViewModel.filteredHospitalList?.registeredDoctorMap?[regionalViewModel.selectedRegionId]?.hmgDoctorList)
? (appointmentsViewModel.filteredHospitalList?.registeredDoctorMap?[ : (appointmentsViewModel.filteredHospitalList?.registeredDoctorMap?[regionalViewModel.selectedRegionId]?.hmcDoctorList))
regionalViewModel.selectedRegionId]?.hmgDoctorList) ?.length ??
: (appointmentsViewModel
.filteredHospitalList
?.registeredDoctorMap?[
regionalViewModel.selectedRegionId]?.hmcDoctorList))?.length ??
0), 0),
) )
], ],

@ -0,0 +1,255 @@
import 'dart:async';
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_assets.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/hmg_services/hmg_services_view_model.dart';
import 'package:hmg_patient_app_new/features/hmg_services/models/resq_models/get_cmc_all_orders_resp_model.dart';
import 'package:hmg_patient_app_new/presentation/comprehensive_checkup/widgets/cmc_ui_selection_helper.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart';
import 'package:provider/provider.dart';
class CmcOrderDetailPage extends StatefulWidget {
const CmcOrderDetailPage({super.key});
@override
State<CmcOrderDetailPage> createState() => _CmcOrderDetailPageState();
}
class _CmcOrderDetailPageState extends State<CmcOrderDetailPage> {
@override
void initState() {
super.initState();
final hmgServicesViewModel = context.read<HmgServicesViewModel>();
scheduleMicrotask(() async {
await hmgServicesViewModel.getCmcOrdersList();
});
}
Color _getStatusColor(int? statusId) {
switch (statusId) {
case 1: // Pending
return const Color(0xffCC9B14);
case 2: // Processing
return const Color(0xff2E303A);
case 3: // Completed
return const Color(0xff359846);
case 4: // Cancelled
case 6: // Rejected
case 7: // Rejected
return const Color(0xffD02127);
default:
return AppColors.greyColor;
}
}
String _formatDate(String? dateString) {
if (dateString == null) return '';
try {
final date = DateTime.parse(dateString);
return DateFormat('MMM dd, yyyy').format(date);
} catch (e) {
return dateString;
}
}
Widget _buildLoadingShimmer() {
return ListView.separated(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: 3,
separatorBuilder: (_, __) => SizedBox(height: 12.h),
itemBuilder: (context, index) {
return _buildOrderCard(GetCMCAllOrdersResponseModel(), isLoading: true);
},
);
}
Widget _buildOrderCard(GetCMCAllOrdersResponseModel order, {bool isLoading = false}) {
final statusColor = _getStatusColor(order.statusId);
final canCancel = order.statusId == 1 || order.statusId == 2;
return AnimatedContainer(
duration: Duration(milliseconds: 300),
curve: Curves.easeInOut,
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 24.h,
hasShadow: true,
),
child: Padding(
padding: EdgeInsets.all(16.w),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Status and Date Row
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 6.h),
decoration: BoxDecoration(
color: statusColor.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(8.r),
),
child: (isLoading ? "Processing" : order.statusText ?? '')
.toText12(
color: statusColor,
fontWeight: FontWeight.w600,
)
.toShimmer2(isShow: isLoading),
),
SizedBox(width: 8.w),
(isLoading ? "Jan 15, 2024" : _formatDate(order.created))
.toText12(
color: AppColors.textColorLight,
fontWeight: FontWeight.w500,
)
.toShimmer2(isShow: isLoading),
],
),
SizedBox(height: 16.h),
// Request ID
Row(
children: [
if (!isLoading) ...[
"Request ID:".needTranslation.toText14(
color: AppColors.textColorLight,
weight: FontWeight.w500,
),
SizedBox(width: 4.w),
],
(isLoading ? "12345" : "${order.iD ?? '-'}").toText16(isBold: true).toShimmer2(isShow: isLoading),
],
),
SizedBox(height: 12.h),
// Chips for Hospital, Service, and Amount
Wrap(
spacing: 6.w,
runSpacing: 6.h,
children: [
// Hospital
if (order.projectName != null || isLoading)
AppCustomChipWidget(
icon: AppAssets.hospital,
labelText: isLoading ? "Hospital Name" : order.projectName ?? '-',
).toShimmer2(isShow: isLoading),
// Service
if (order.serviceText != null || isLoading)
AppCustomChipWidget(
icon: AppAssets.servicesBottom,
labelText: isLoading ? "Service Name" : order.serviceText ?? '-',
).toShimmer2(isShow: isLoading),
],
),
// Cancel Button
if (canCancel || isLoading) ...[
SizedBox(height: 16.h),
Row(
children: [
Expanded(
child: CustomButton(
text: "Cancel Order".needTranslation,
onPressed: isLoading ? () {} : () => CmcUiSelectionHelper.showCancelConfirmationDialog(context: context, order: order),
backgroundColor: AppColors.primaryRedColor,
borderColor: AppColors.primaryRedColor,
textColor: AppColors.whiteColor,
fontSize: 14.f,
fontWeight: FontWeight.w600,
borderRadius: 10.r,
height: 44.h,
).toShimmer2(isShow: isLoading),
),
],
),
]
],
),
),
);
}
Widget _buildEmptyState() {
return Center(
child: Padding(
padding: EdgeInsets.symmetric(vertical: 40.h),
child: Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 12.r,
hasShadow: false,
),
child: Utils.getNoDataWidget(
context,
noDataText: "You don't have any CMC orders yet.".needTranslation,
isSmallWidget: true,
width: 62.w,
height: 62.h,
),
),
),
);
}
@override
Widget build(BuildContext context) {
return CollapsingListView(
title: "CMC Orders".needTranslation,
isLeading: true,
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Consumer<HmgServicesViewModel>(
builder: (context, viewModel, child) {
if (viewModel.isCmcOrdersLoading) {
return _buildLoadingShimmer();
}
if (viewModel.cmcOrdersList.isEmpty) {
return _buildEmptyState();
}
return ListView.separated(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: viewModel.cmcOrdersList.length,
separatorBuilder: (_, __) => SizedBox(height: 12.h),
itemBuilder: (context, index) {
final order = viewModel.cmcOrdersList.reversed.toList()[index];
return AnimationConfiguration.staggeredList(
position: index,
duration: const Duration(milliseconds: 500),
child: SlideAnimation(
verticalOffset: 100.0,
child: FadeInAnimation(
child: _buildOrderCard(order),
),
),
);
},
);
},
),
],
).paddingSymmetrical(24.w, 0),
),
);
}
}

@ -0,0 +1,515 @@
import 'dart:developer';
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/route_extensions.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/hmg_services/hmg_services_view_model.dart';
import 'package:hmg_patient_app_new/features/hmg_services/models/req_models/cmc_create_new_order_req_model.dart';
import 'package:hmg_patient_app_new/features/hmg_services/models/resq_models/get_cmc_services_resp_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/hospital_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/comprehensive_checkup/widgets/cmc_ui_selection_helper.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/loader/bottomsheet_loader.dart';
import 'package:maps_launcher/maps_launcher.dart';
import 'package:provider/provider.dart';
class CmcSelectionReviewPage extends StatefulWidget {
final GetCMCServicesResponseModel selectedService;
final HospitalsModel? preSelectedHospital;
const CmcSelectionReviewPage({super.key, required this.selectedService, this.preSelectedHospital});
@override
State<CmcSelectionReviewPage> createState() => _CmcSelectionReviewPageState();
}
class _CmcSelectionReviewPageState extends State<CmcSelectionReviewPage> {
@override
void initState() {
super.initState();
// Initialize ViewModel state with preselected hospital if provided
if (widget.preSelectedHospital != null) {
WidgetsBinding.instance.addPostFrameCallback((_) {
final hmgServicesViewModel = context.read<HmgServicesViewModel>();
hmgServicesViewModel.setSelectedHospitalForOrder(widget.preSelectedHospital);
hmgServicesViewModel.setSelectedServiceForOrder(widget.selectedService);
});
}
}
@override
Widget build(BuildContext context) {
final appState = getIt.get<AppState>();
final isArabic = appState.isArabic();
return CollapsingListView(
title: "Summary".needTranslation,
bottomChild: _buildBottomButton(),
child: SingleChildScrollView(
padding: EdgeInsets.all(16.w),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildOrderSummaryCard(isArabic),
SizedBox(height: 16.h),
_buildSelectedServiceCard(isArabic),
SizedBox(height: 16.h),
_buildPaymentSummary(),
],
),
),
);
}
Widget _buildOrderSummaryCard(bool isArabic) {
return Consumer<HmgServicesViewModel>(
builder: (context, hmgServicesViewModel, child) {
final selectedHospital = hmgServicesViewModel.selectedHospitalForOrder;
final isLocationSelected = selectedHospital != null;
return Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 16.r,
),
padding: EdgeInsets.all(16.w),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Select Hospital".needTranslation,
style: TextStyle(
fontSize: 16.f,
fontWeight: FontWeight.w700,
color: AppColors.blackColor,
letterSpacing: -0.5,
),
),
SizedBox(height: 12.h),
_buildHospitalSelector(isArabic, selectedHospital, isLocationSelected),
if (isLocationSelected) ...[
SizedBox(height: 16.h),
_buildHospitalMap(selectedHospital),
],
],
),
);
},
);
}
Widget _buildHospitalSelector(bool isArabic, HospitalsModel? selectedHospital, bool isLocationSelected) {
return InkWell(
onTap: _showHospitalSelectionBottomSheet,
child: Container(
padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 14.h),
decoration: BoxDecoration(
color: AppColors.bgScaffoldColor,
borderRadius: BorderRadius.circular(12.r),
border: Border.all(
color: AppColors.greyColor.withAlpha(51),
width: 1,
),
),
child: Row(
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
isLocationSelected && selectedHospital != null
? (isArabic ? (selectedHospital.nameN ?? selectedHospital.name ?? '') : (selectedHospital.name ?? ''))
: "Select Hospital".needTranslation,
style: TextStyle(
fontSize: 14.f,
fontWeight: isLocationSelected ? FontWeight.w600 : FontWeight.w400,
color: isLocationSelected ? AppColors.blackColor : AppColors.greyTextColor,
letterSpacing: -0.4,
),
),
],
),
),
Icon(
Icons.keyboard_arrow_down,
color: AppColors.greyTextColor,
size: 24.h,
),
],
),
),
);
}
Widget _buildHospitalMap(HospitalsModel selectedHospital) {
final String lat = selectedHospital.latitude ?? "0.0";
final String lng = selectedHospital.longitude ?? "0.0";
log("selectedHospital: $lng and $lat");
if (lat == "0.0" || lng == "0.0") return SizedBox.shrink();
final String staticMapUrl =
"https://maps.googleapis.com/maps/api/staticmap?center=$lat,$lng&zoom=16&size=600x300&maptype=roadmap&markers=color:red%7C$lat,$lng&key=AIzaSyCyDbWUM9d_sBUGIE8PcuShzPaqO08NSC8";
return Stack(
children: [
ClipRRect(
borderRadius: BorderRadius.circular(12.r),
child: Image.network(
staticMapUrl,
height: 200.h,
width: double.infinity,
fit: BoxFit.cover,
loadingBuilder: (context, child, loadingProgress) {
if (loadingProgress == null) return child;
return Container(
height: 200.h,
decoration: BoxDecoration(
color: AppColors.bgScaffoldColor,
borderRadius: BorderRadius.circular(12.r),
),
child: Center(
child: CircularProgressIndicator(
color: AppColors.primaryRedColor,
),
),
);
},
errorBuilder: (context, error, stackTrace) {
return Container(
height: 200.h,
decoration: BoxDecoration(
color: AppColors.bgScaffoldColor,
borderRadius: BorderRadius.circular(12.r),
),
child: Center(
child: Icon(
Icons.error_outline,
size: 48.h,
color: AppColors.greyTextColor,
),
),
);
},
),
),
Positioned(
bottom: 12.h,
right: 12.w,
child: InkWell(
onTap: () => _launchDirections(selectedHospital),
child: Container(
padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 8.h),
decoration: BoxDecoration(
color: AppColors.whiteColor,
borderRadius: BorderRadius.circular(1000.r),
boxShadow: [
BoxShadow(
color: Color.fromARGB(26, 0, 0, 0),
blurRadius: 8,
offset: Offset(0, 2),
),
],
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Utils.buildSvgWithAssets(
icon: AppAssets.directions_icon,
width: 16.w,
height: 16.h,
),
SizedBox(width: 6.w),
Text(
"Get Directions".needTranslation,
style: TextStyle(
fontSize: 12.f,
fontWeight: FontWeight.w600,
color: AppColors.blackColor,
letterSpacing: -0.4,
),
),
],
),
),
),
),
],
);
}
Widget _buildSelectedServiceCard(bool isArabic) {
final serviceName = isArabic ? (widget.selectedService.textN ?? widget.selectedService.text ?? '') : (widget.selectedService.text ?? '');
final price = widget.selectedService.priceTotal ?? 0.0;
return Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 16.r,
),
padding: EdgeInsets.all(16.w),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
"Selected Service".needTranslation.toText14(
weight: FontWeight.w600,
color: AppColors.greyTextColor,
letterSpacing: -0.4,
),
SizedBox(height: 6.h),
Row(
children: [
Expanded(child: serviceName.toText16(weight: FontWeight.w700, color: AppColors.blackColor, letterSpacing: -0.5)),
],
),
],
),
);
}
Widget _buildPaymentSummary() {
// Use selected service from widget
final service = widget.selectedService;
log("service: ${service.toJson()}");
final double amountBeforeTax = service.price ?? 0.0;
final double taxAmount = service.priceVAT ?? 0.0;
final double totalAmount = service.priceTotal ?? (amountBeforeTax + taxAmount);
return Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 24.h,
hasShadow: false,
),
child: Consumer<PayfortViewModel>(builder: (context, payfortVM, child) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 24.h),
"Total amount to pay".needTranslation.toText18(isBold: true).paddingSymmetrical(24.h, 0.h),
SizedBox(height: 17.h),
// Amount before tax
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
"Amount before tax".needTranslation.toText14(isBold: true),
Utils.getPaymentAmountWithSymbol(
amountBeforeTax.toString().toText16(isBold: true),
AppColors.blackColor,
13,
isSaudiCurrency: true,
),
],
).paddingSymmetrical(24.h, 0.h),
// VAT (use label VAT 15% if desired)
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
"VAT 15%".needTranslation.toText14(isBold: true, color: AppColors.greyTextColor),
Utils.getPaymentAmountWithSymbol(
taxAmount.toString().toText14(isBold: true, color: AppColors.greyTextColor),
AppColors.greyTextColor,
13,
isSaudiCurrency: true,
),
],
).paddingSymmetrical(24.h, 0.h),
SizedBox(height: 17.h),
// Total Amount
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
"".needTranslation.toText14(isBold: true),
Utils.getPaymentAmountWithSymbol(
totalAmount.toString().toText24(isBold: true),
AppColors.blackColor,
17,
isSaudiCurrency: true,
),
],
).paddingSymmetrical(24.h, 0.h),
SizedBox(height: 16.h),
],
);
}),
);
}
Widget _buildBottomButton() {
return Consumer<HmgServicesViewModel>(
builder: (context, hmgServicesViewModel, child) {
final isLocationSelected = hmgServicesViewModel.selectedHospitalForOrder != null;
return SafeArea(
top: false,
child: Container(
padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 12.h),
decoration: BoxDecoration(
color: AppColors.whiteColor,
boxShadow: [
BoxShadow(
color: Color.fromARGB(13, 0, 0, 0),
blurRadius: 8,
offset: Offset(0, -2),
),
],
),
child: CustomButton(
text: "Confirm".needTranslation,
onPressed: () {
isLocationSelected ? _handleConfirm() : null;
},
textColor: AppColors.whiteColor,
backgroundColor: isLocationSelected ? AppColors.successColor : AppColors.greyColor,
borderRadius: 12.r,
borderColor: Colors.transparent,
borderWidth: 0,
padding: EdgeInsets.symmetric(vertical: 14.h),
),
),
);
},
);
}
void _showHospitalSelectionBottomSheet() {
CmcUiSelectionHelper.showHospitalSelectionBottomSheet(context: context, onHospitalSelected: (hospital) => context.pop());
}
void _launchDirections(HospitalsModel selectedHospital) {
final double lat = double.parse(selectedHospital.latitude ?? "0.0");
final double lng = double.parse(selectedHospital.longitude ?? "0.0");
if (lat != 0.0 && lng != 0.0) {
MapsLauncher.launchCoordinates(
lat,
lng,
selectedHospital.name ?? "Hospital",
);
}
}
showSuccessBottomSheet(int requestId, HmgServicesViewModel hmgServicesViewModel) {
return showCommonBottomSheetWithoutHeight(
context,
child: Padding(
padding: EdgeInsets.all(16.w),
child: Column(
children: [
Utils.getSuccessWidget(loadingText: "Your request has been successfully submitted.".needTranslation),
Row(
children: [
"Here is your request #: ".needTranslation.toText14(
color: AppColors.textColorLight,
weight: FontWeight.w500,
),
SizedBox(width: 4.w),
("$requestId").toText16(isBold: true),
],
),
SizedBox(height: 24.h),
Row(
children: [
Expanded(
child: CustomButton(
height: 56.h,
text: LocaleKeys.ok.tr(),
onPressed: () {
context.pop();
context.pop();
hmgServicesViewModel.getAllCmcOrders();
},
textColor: AppColors.whiteColor,
),
),
],
),
],
),
),
isCloseButtonVisible: false,
isDismissible: false,
isFullScreen: false,
);
}
void _handleConfirm() {
final hmgServicesViewModel = context.read<HmgServicesViewModel>();
final selectedHospital = hmgServicesViewModel.selectedHospitalForOrder;
if (selectedHospital == null) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text("Please select a hospital to continue".needTranslation),
backgroundColor: AppColors.errorColor,
),
);
return;
}
final selectedService = widget.selectedService;
return showCommonBottomSheetWithoutHeight(
title: LocaleKeys.notice.tr(context: context),
context,
child: Utils.getWarningWidget(
loadingText: "Are you sure you want to submit this request?".needTranslation,
isShowActionButtons: true,
onCancelTap: () {
Navigator.pop(context);
},
onConfirmTap: () async {
Navigator.pop(context);
LoaderBottomSheet.showLoader();
// Create the services list
final servicesList = [
PatientERCMCInsertServicesList(
recordID: selectedService.iD,
serviceID: selectedService.serviceID,
selectedServiceName: selectedService.text,
selectedServiceNameAR: selectedService.textN,
price: selectedService.price,
vAT: selectedService.priceVAT,
totalPrice: selectedService.priceTotal,
),
];
await hmgServicesViewModel.addCmcOrder(
projectID: selectedHospital.mainProjectID ?? 0,
orderServiceID: selectedService.orderServiceID ?? 3,
services: servicesList,
onSuccess: (requestId) {
LoaderBottomSheet.hideLoader();
showSuccessBottomSheet(requestId, hmgServicesViewModel);
},
onError: (err) {
LoaderBottomSheet.hideLoader();
// showCommonBottomSheetWithoutHeight(context, child: Utils.getErrorWidget(loadingText: err), callBackFunc: () {});
},
);
}),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
}
}

@ -0,0 +1,405 @@
import 'dart:async';
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/hmg_services/hmg_services_view_model.dart';
import 'package:hmg_patient_app_new/features/hmg_services/models/resq_models/get_cmc_all_orders_resp_model.dart';
import 'package:hmg_patient_app_new/features/hmg_services/models/resq_models/get_cmc_services_resp_model.dart';
import 'package:hmg_patient_app_new/presentation/comprehensive_checkup/cmc_order_detail_page.dart';
import 'package:hmg_patient_app_new/presentation/comprehensive_checkup/cmc_selection_review_page.dart';
import 'package:hmg_patient_app_new/presentation/comprehensive_checkup/widgets/cmc_ui_selection_helper.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart';
import 'package:hmg_patient_app_new/widgets/media_viewer/full_screen_image_viewer.dart';
import 'package:hmg_patient_app_new/widgets/radio_list_tile_widget.dart';
import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
import 'package:provider/provider.dart';
import 'package:shimmer/shimmer.dart';
class ComprehensiveCheckupPage extends StatefulWidget {
const ComprehensiveCheckupPage({super.key});
@override
State<ComprehensiveCheckupPage> createState() => _ComprehensiveCheckupPageState();
}
class _ComprehensiveCheckupPageState extends State<ComprehensiveCheckupPage> {
int? _selectedServiceId;
GetCMCServicesResponseModel? _selectedService;
@override
void initState() {
super.initState();
final HmgServicesViewModel hmgServicesViewModel = context.read<HmgServicesViewModel>();
final AppState appState = getIt.get<AppState>();
scheduleMicrotask(() async {
final user = appState.getAuthenticatedUser();
if (user != null) {
await hmgServicesViewModel.getAllCmcOrders();
await hmgServicesViewModel.getAllCmcServices(patientID: user.patientId ?? 0);
}
});
}
GetCMCAllOrdersResponseModel? _getPendingOrder(List<GetCMCAllOrdersResponseModel> orders) {
if (orders.isEmpty) return null;
// Find pending or processing orders (status 1 or 2)
for (var order in orders) {
if (order.statusId == 1 || order.statusId == 2) {
return order;
}
}
return null;
}
Widget _buildPendingOrderCard(GetCMCAllOrdersResponseModel order) {
int status = order.statusId ?? 0;
String statusDisp = order.statusText ?? "";
Color statusColor;
if (status == 1) {
// pending
statusColor = AppColors.statusPendingColor;
} else if (status == 2) {
// processing
statusColor = AppColors.statusProcessingColor;
} else if (status == 3) {
// completed
statusColor = AppColors.statusCompletedColor;
} else {
// cancel / rejected
statusColor = AppColors.statusRejectedColor;
}
final canCancel = order.statusId == 1 || order.statusId == 2;
return Container(
width: double.infinity,
margin: EdgeInsets.all(16.w),
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 24.h,
hasShadow: true,
),
child: Padding(
padding: EdgeInsets.all(16.w),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Status and Date Row
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 6.h),
decoration: BoxDecoration(
color: statusColor.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(8.r),
),
child: statusDisp.toText12(
color: statusColor,
fontWeight: FontWeight.w600,
),
),
SizedBox(width: 8.w),
if (order.created != null)
DateFormat('MMM dd, yyyy').format(DateTime.parse(order.created!)).toText12(
color: AppColors.textColorLight,
fontWeight: FontWeight.w500,
),
],
),
SizedBox(height: 16.h),
// Request ID
Row(
children: [
"Request ID:".needTranslation.toText14(color: AppColors.textColorLight, weight: FontWeight.w500),
SizedBox(width: 4.w),
"${order.iD ?? '-'}".toText16(isBold: true),
],
),
SizedBox(height: 12.h),
// Chips for Hospital, Service, and Amount
Wrap(
spacing: 6.w,
runSpacing: 6.h,
children: [
// Hospital
if (order.projectName != null)
AppCustomChipWidget(
icon: AppAssets.hospital,
labelText: order.projectName ?? '-',
),
// Service
if (order.serviceText != null)
AppCustomChipWidget(
icon: AppAssets.file_icon,
labelText: order.serviceText ?? '-',
),
],
),
SizedBox(height: 16.h),
// Info message
Container(
padding: EdgeInsets.all(12.w),
decoration: BoxDecoration(
color: AppColors.infoBannerBgColor,
borderRadius: BorderRadius.circular(10.r),
border: Border.all(
color: AppColors.infoBannerBorderColor,
width: 1,
),
),
child: Row(
children: [
Icon(
Icons.info_outline,
size: 20.w,
color: AppColors.infoBannerIconColor,
),
SizedBox(width: 8.w),
Expanded(
child: "You have a pending order. Please wait for it to be processed.".needTranslation.toText12(
color: AppColors.infoBannerTextColor,
fontWeight: FontWeight.w500,
),
),
],
),
),
if (canCancel) ...[
SizedBox(height: 16.h),
Row(
children: [
Expanded(
child: CustomButton(
text: "Cancel Order".needTranslation,
onPressed: () => CmcUiSelectionHelper.showCancelConfirmationDialog(context: context, order: order),
backgroundColor: AppColors.primaryRedColor,
borderColor: AppColors.primaryRedColor,
textColor: AppColors.whiteColor,
fontSize: 14.f,
fontWeight: FontWeight.w600,
borderRadius: 10.r,
height: 44.h,
),
),
],
),
]
],
),
),
);
}
Widget _buildServiceSelectionList(List<GetCMCServicesResponseModel> services) {
if (services.isEmpty) {
return Center(
child: Padding(
padding: EdgeInsets.all(24.h),
child: Text(
'No services available'.needTranslation,
style: TextStyle(
fontSize: 16.h,
color: AppColors.greyTextColor,
),
),
),
);
}
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 16.h),
Text(
'Select a Service'.needTranslation,
style: TextStyle(
fontSize: 20.h,
fontWeight: FontWeight.w700,
color: AppColors.blackColor,
letterSpacing: -0.8,
),
).paddingOnly(left: 16.w, right: 16.w),
ListView.builder(
padding: EdgeInsets.zero,
itemCount: services.length,
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemBuilder: (context, index) {
final service = services[index];
final serviceName = service.text ?? service.textN ?? '';
final price = service.priceTotal ?? 0.0;
return RadioListTileWidget<int>(
value: service.iD ?? 0,
groupValue: _selectedServiceId,
title: serviceName,
subtitleWidget: Utils.getPaymentAmountWithSymbol(
isExpanded: false,
price.toString().toText14(),
AppColors.blackColor,
14,
isSaudiCurrency: true,
),
onChanged: (value) {
setState(() {
_selectedServiceId = value;
_selectedService = service;
});
},
);
},
),
// Illustration image below the services list similar to the old implementation
SizedBox(height: 12.h),
Builder(builder: (context) {
final appStateLocal = getIt.get<AppState>();
final String imagePath = appStateLocal.isArabic() ? AppAssets.comprehensiveCheckupAr : AppAssets.comprehensiveCheckupEn;
return Stack(
children: [
Image.asset(
imagePath,
width: double.infinity,
fit: BoxFit.cover,
).paddingAll(16.w),
Align(
alignment: Alignment.topRight,
child: Container(
decoration: BoxDecoration(
color: Color.fromARGB(51, 0, 0, 0),
borderRadius: BorderRadius.circular(1000.r),
),
margin: EdgeInsets.all(16.h),
child: IconButton(
icon: Icon(
Icons.zoom_in,
color: Colors.white,
size: 26.w,
),
padding: EdgeInsets.all(10.h),
onPressed: () => _showFullScreenImage(context, imagePath, isSvg: false),
),
),
),
],
);
}),
],
);
}
void _proceedWithSelectedService() {
if (_selectedService != null) {
final hmgServicesViewModel = context.read<HmgServicesViewModel>();
// Store selected service in ViewModel
hmgServicesViewModel.setSelectedServiceForOrder(_selectedService);
hmgServicesViewModel.getHospitalsList();
// Show hospital selection bottom sheet using common helper
CmcUiSelectionHelper.showHospitalSelectionBottomSheet(
context: context,
onHospitalSelected: (hospital) {
Navigator.of(context).pushReplacement(
CustomPageRoute(
page: CmcSelectionReviewPage(selectedService: _selectedService!, preSelectedHospital: hospital),
direction: AxisDirection.left,
),
);
},
);
}
}
Widget _buildLoadingShimmer() {
return ListView.separated(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
padding: EdgeInsets.all(16.w),
itemCount: 10,
separatorBuilder: (_, __) => SizedBox(height: 12.h),
itemBuilder: (context, index) {
return Shimmer.fromColors(
baseColor: Colors.grey[300]!,
highlightColor: Colors.grey[100]!,
child: Container(
height: 80.h,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(10.r),
),
),
);
},
);
}
@override
Widget build(BuildContext context) {
return CollapsingListView(
title: "Comprehensive Checkup".needTranslation,
history: () => Navigator.of(context).push(CustomPageRoute(page: CmcOrderDetailPage(), direction: AxisDirection.up)),
bottomChild: Consumer<HmgServicesViewModel>(
builder: (context, hmgServicesViewModel, child) {
if (hmgServicesViewModel.isCmcOrdersLoading || hmgServicesViewModel.isCmcServicesLoading) return SizedBox.shrink();
final pendingOrder = _getPendingOrder(hmgServicesViewModel.cmcOrdersList);
if (pendingOrder == null && _selectedServiceId != null) {
return SafeArea(
top: false,
child: Padding(
padding: EdgeInsets.only(left: 16.w, right: 16.w, top: 12.h),
child: CustomButton(
borderWidth: 0,
text: "Next".needTranslation,
onPressed: _proceedWithSelectedService,
textColor: AppColors.whiteColor,
borderRadius: 12.r,
borderColor: Colors.transparent,
padding: EdgeInsets.symmetric(vertical: 14.h),
),
),
);
}
return SizedBox.shrink();
},
),
child: Consumer<HmgServicesViewModel>(
builder: (context, hmgServicesViewModel, child) {
if (hmgServicesViewModel.isCmcOrdersLoading || hmgServicesViewModel.isCmcServicesLoading) {
return _buildLoadingShimmer();
}
final pendingOrder = _getPendingOrder(hmgServicesViewModel.cmcOrdersList);
if (pendingOrder != null) {
return _buildPendingOrderCard(pendingOrder);
} else {
return _buildServiceSelectionList(hmgServicesViewModel.cmcServicesList);
}
},
),
);
}
void _showFullScreenImage(BuildContext context, String path, {bool isSvg = false}) {
Navigator.of(context).push(MaterialPageRoute(builder: (_) => FullScreenImageViewer(isSvg: isSvg, path: path)));
}
}

@ -0,0 +1,111 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.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/enums.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/features/hmg_services/hmg_services_view_model.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/presentation/comprehensive_checkup/widgets/cmc_hospital_list_item.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/input_widget.dart';
import 'package:provider/provider.dart';
import 'package:shimmer/shimmer.dart';
class CmcHospitalBottomSheetBody extends StatelessWidget {
final Function(HospitalsModel) onHospitalSelected;
const CmcHospitalBottomSheetBody({super.key, required this.onHospitalSelected});
Widget _buildLoadingShimmer() {
return ListView.separated(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: 4,
separatorBuilder: (_, __) => SizedBox(height: 12.h),
itemBuilder: (context, index) {
return Shimmer.fromColors(
baseColor: Colors.grey[300]!,
highlightColor: Colors.grey[100]!,
child: Container(
height: 80.h,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(10.r),
),
),
);
},
);
}
@override
Widget build(BuildContext context) {
final appState = getIt.get<AppState>();
final bool isArabic = appState.isArabic();
final bool isLocationEnabled = (appState.userLat != 0) && (appState.userLong != 0);
return Consumer(
builder: (BuildContext context, HmgServicesViewModel hmgServicesViewModel, Widget? child) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
"Choose your preferred hospital for the service".needTranslation.toText14(
weight: FontWeight.w400,
color: AppColors.greyTextColor,
letterSpacing: -0.4,
),
SizedBox(height: 16.h),
TextInputWidget(
labelText: LocaleKeys.search.tr(),
hintText: LocaleKeys.searchHospital.tr(),
onChange: (value) {
hmgServicesViewModel.filterHospitalsByString(value ?? '', isArabic);
},
isEnable: true,
prefix: null,
autoFocus: false,
isBorderAllowed: false,
keyboardType: TextInputType.text,
isAllowLeadingIcon: true,
selectionType: SelectionTypeEnum.search,
padding: EdgeInsets.symmetric(vertical: ResponsiveExtension(10).h, horizontal: ResponsiveExtension(15).h),
),
],
),
SizedBox(height: 8.h),
SizedBox(
height: MediaQuery.of(context).size.height * 0.4,
child: hmgServicesViewModel.isHospitalListLoading
? _buildLoadingShimmer()
: hmgServicesViewModel.filteredHospitalsList.isEmpty
? Center(
child: "No hospitals Found".needTranslation.toText16(weight: FontWeight.w500, color: AppColors.greyTextColor),
)
: ListView.separated(
itemCount: hmgServicesViewModel.filteredHospitalsList.length,
separatorBuilder: (context, index) => SizedBox(height: 12.h),
itemBuilder: (context, index) {
final hospital = hmgServicesViewModel.filteredHospitalsList[index];
return CmcHospitalListItem(
hospital: hospital,
isLocationEnabled: isLocationEnabled,
onPress: () {
hmgServicesViewModel.setSelectedHospital(hospital);
onHospitalSelected(hospital);
},
);
},
),
),
],
);
},
);
}
}

@ -0,0 +1,126 @@
import 'dart:developer';
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/my_appointments/models/resp_models/hospital_model.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart';
class CmcHospitalListItem extends StatelessWidget {
final HospitalsModel hospital;
final VoidCallback onPress;
final bool isLocationEnabled;
const CmcHospitalListItem({
super.key,
required this.hospital,
required this.onPress,
this.isLocationEnabled = false,
});
@override
Widget build(BuildContext context) {
final appState = getIt.get<AppState>();
final bool isArabic = appState.isArabic();
final String hospitalName = isArabic ? (hospital.nameN ?? hospital.name ?? '') : (hospital.name ?? '');
return InkWell(
onTap: onPress,
child: DecoratedBox(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 20.h,
hasShadow: false,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
spacing: 8.h,
children: [
_buildHospitalName(hospitalName),
_buildDistanceInfo(),
],
),
),
Transform.flip(
flipX: isArabic,
child: Utils.buildSvgWithAssets(
icon: AppAssets.forward_arrow_icon_small,
iconColor: AppColors.blackColor,
width: 18.h,
height: 13.h,
fit: BoxFit.contain,
),
),
],
).paddingSymmetrical(16.h, 16.h),
),
);
}
Widget _buildHospitalName(String hospitalName) {
return Row(
children: [
Utils.buildSvgWithAssets(
icon: (hospital.isHMC == true) ? AppAssets.hmc : AppAssets.hmg,
).paddingOnly(right: 10),
Expanded(
child: Text(
hospitalName,
style: TextStyle(
fontWeight: FontWeight.w600,
fontSize: 16,
color: AppColors.blackColor,
),
),
)
],
);
}
Widget _buildDistanceInfo() {
log("hospital: ${hospital.distanceInKilometers}");
final distanceText = hospital.distanceInKilometers != null ? hospital.distanceInKilometers!.toStringAsFixed(1) : "0";
return Row(
spacing: 4.w,
children: [
Visibility(
visible: (hospital.distanceInKilometers != null && hospital.distanceInKilometers! > 0),
child: AppCustomChipWidget(
labelText: "$distanceText km".needTranslation,
icon: AppAssets.location_red,
iconColor: AppColors.errorColor,
backgroundColor: AppColors.secondaryLightRedColor,
textColor: AppColors.errorColor,
),
),
Visibility(
visible: (hospital.distanceInKilometers == null || hospital.distanceInKilometers == 0),
child: AppCustomChipWidget(
labelText: " Distance not available".needTranslation,
textColor: AppColors.blackColor,
),
),
// Visibility(
// visible: !isLocationEnabled,
// child: AppCustomChipWidget(
// labelText: "Location turned off".needTranslation,
// deleteIcon: AppAssets.location_unavailable,
// deleteIconSize: Size(9.w, 12.h),
// textColor: AppColors.blackColor,
// ),
// ),
],
);
}
}

@ -0,0 +1,110 @@
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/features/hmg_services/hmg_services_view_model.dart';
import 'package:hmg_patient_app_new/presentation/comprehensive_checkup/widgets/cmc_hospital_bottom_sheet_body.dart';
import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart';
import 'package:provider/provider.dart';
import 'package:easy_localization/easy_localization.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/route_extensions.dart';
import 'package:hmg_patient_app_new/features/hmg_services/models/req_models/order_update_req_model.dart';
import 'package:hmg_patient_app_new/features/hmg_services/models/resq_models/get_cmc_all_orders_resp_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/loader/bottomsheet_loader.dart';
class CmcUiSelectionHelper {
static void showHospitalSelectionBottomSheet({
required BuildContext context,
required Function(dynamic) onHospitalSelected,
}) {
final hmgServicesViewModel = context.read<HmgServicesViewModel>();
showCommonBottomSheetWithoutHeight(
context,
title: "Select Hospital".needTranslation,
child: CmcHospitalBottomSheetBody(
onHospitalSelected: (hospital) {
hmgServicesViewModel.setSelectedHospitalForOrder(hospital);
onHospitalSelected(hospital);
},
),
);
}
static void showCancelConfirmationDialog({
required BuildContext context,
required GetCMCAllOrdersResponseModel order,
}) {
final HmgServicesViewModel hmgServicesViewModel = context.read<HmgServicesViewModel>();
return showCommonBottomSheetWithoutHeight(
title: LocaleKeys.notice.tr(context: context),
context,
child: Utils.getWarningWidget(
loadingText: "Are you sure you want to cancel this order?".needTranslation,
isShowActionButtons: true,
onCancelTap: () {
Navigator.pop(context);
},
onConfirmTap: () async {
Navigator.pop(context);
LoaderBottomSheet.showLoader();
final requestModel = OrderUpdateRequestModel(
presOrderID: order.iD,
rejectionReason: "Cancelled by user",
presOrderStatus: 4, // Cancelled status
editedBy: 3,
);
await hmgServicesViewModel.updateCmcPresOrder(
requestModel: requestModel,
onSuccess: (_) async {
LoaderBottomSheet.hideLoader();
showCommonBottomSheetWithoutHeight(
context,
child: Padding(
padding: EdgeInsets.all(16.w),
child: Column(
children: [
Utils.getSuccessWidget(loadingText: "Order has been cancelled successfully".needTranslation),
SizedBox(height: 24.h),
Row(
children: [
Expanded(
child: CustomButton(
height: 56.h,
text: LocaleKeys.ok.tr(),
onPressed: () {
context.pop();
context.pop();
hmgServicesViewModel.getAllCmcOrders();
},
textColor: AppColors.whiteColor,
),
),
],
),
],
),
),
isCloseButtonVisible: false,
isDismissible: false,
isFullScreen: false,
);
},
onError: (error) {
LoaderBottomSheet.hideLoader();
},
);
},
),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
}
}

@ -0,0 +1,97 @@
import 'dart:ui';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.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/prescriptions/prescriptions_view_model.dart';
import 'package:hmg_patient_app_new/presentation/e_referral/new_referral.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:provider/provider.dart';
class EReferralPage extends StatefulWidget {
const EReferralPage({super.key});
@override
_EReferralPageState createState() => _EReferralPageState();
}
class _EReferralPageState extends State<EReferralPage>
{
@override
void initState() {
super.initState();
}
@override
void dispose() {
super.dispose();
}
bool isNewReferral = true;
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: AppColors.bgScaffoldColor,
body: CollapsingListView(
title:"E Referral".needTranslation,
child: SingleChildScrollView(
child: Consumer<PrescriptionsViewModel>(builder: (context, model, child) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 16.h),
Row(
children: [
CustomButton(
text: "New Referral".needTranslation,
onPressed: () {
isNewReferral =true;
setState(() {
});
},
backgroundColor: model.isSortByClinic ? AppColors.bgRedLightColor : AppColors.whiteColor,
borderColor: model.isSortByClinic ? AppColors.primaryRedColor : AppColors.textColor.withOpacity(0.2),
textColor: model.isSortByClinic ? AppColors.primaryRedColor : AppColors.blackColor,
fontSize: 12,
fontWeight: FontWeight.w500,
borderRadius: 10,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 40.h,
),
SizedBox(width: 8.h),
CustomButton(
text: "Search Referral".needTranslation,
onPressed: () {
isNewReferral =false;
},
backgroundColor: model.isSortByClinic ? AppColors.whiteColor : AppColors.bgRedLightColor,
borderColor: model.isSortByClinic ? AppColors.textColor.withOpacity(0.2) : AppColors.primaryRedColor,
textColor: model.isSortByClinic ? AppColors.blackColor : AppColors.primaryRedColor,
fontSize: 12,
fontWeight: FontWeight.w500,
borderRadius: 10,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 40.h,
),
],
).paddingSymmetrical(24.h, 0.h),
SizedBox(height: 20.h),
isNewReferral ? NewEReferral() : SizedBox(),
],
);
}),
),
),
);
}
}

@ -0,0 +1,172 @@
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
class NewEReferral extends StatefulWidget {
NewEReferral();
@override
_NewEReferralState createState() => _NewEReferralState();
}
class _NewEReferralState extends State<NewEReferral> with TickerProviderStateMixin {
late PageController _controller;
int _currentIndex = 0;
int pageSelected = 2;
// CreateEReferralRequestModel createEReferralRequestModel = new CreateEReferralRequestModel();
@override
void initState() {
super.initState();
_controller = new PageController();
}
@override
void dispose() {
super.dispose();
}
changePageViewIndex(pageIndex) {
_controller.jumpToPage(pageIndex);
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
height: double.infinity,
child: Column(
children: [
Container(
width: double.infinity,
padding: EdgeInsets.only(left: 12,right: 12,top: 12),
child: Row(
children: [
Expanded(
child: showProgress(
title: "Requester Info".needTranslation,
status: _currentIndex == 0
? "InProgress".needTranslation
: _currentIndex > 0
? "Completed".needTranslation
: "Locked".needTranslation,
color: _currentIndex == 0 ? AppColors.infoColor : AppColors.successColor,
),
),
Expanded(
child: showProgress(
title:"Patient Info".needTranslation,
status: _currentIndex == 1
? "InProgress".needTranslation
: _currentIndex > 1
? "Completed".needTranslation
: "Locked".needTranslation,
color: _currentIndex == 1
? AppColors.infoColor
: _currentIndex > 1
? AppColors.successColor
: AppColors.greyColor,
),
),
showProgress(
title: "Other Info".needTranslation,
status: _currentIndex == 2 ? "InProgress".needTranslation :"Locked".needTranslation,
color: _currentIndex == 2
? AppColors.infoColor
: _currentIndex > 3
? AppColors.successColor
: AppColors.greyColor,
isNeedBorder: false,
),
],
),
),
Expanded(
child: PageView(
physics: NeverScrollableScrollPhysics(),
controller: _controller,
onPageChanged: (index) {
setState(() {
_currentIndex = index;
});
},
scrollDirection: Axis.horizontal,
children: <Widget>[
// NewEReferralStepOnePage(
// changePageViewIndex: changePageViewIndex,
// createEReferralRequestModel: createEReferralRequestModel,
// ),
// NewEReferralStepTowPage(
// changePageViewIndex: changePageViewIndex,
// createEReferralRequestModel: createEReferralRequestModel,
// ),
// NewEReferralStepThreePage(
// changePageViewIndex: changePageViewIndex,
// createEReferralRequestModel: createEReferralRequestModel,
// ),
],
),
),
],
),
),
);
}
Widget showProgress({required String title, required String status, required Color color, bool isNeedBorder = true}) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Container(
width: 26,
height: 26,
// decoration: containerRadius(color, 200),
child: Icon(
Icons.done,
color: Colors.white,
size: 16,
),
),
if (isNeedBorder)
Expanded(
child: Padding(
padding: const EdgeInsets.all(8.0),
child:Divider(),
)),
],
),
// mHeight(8),
Text(
title,
style: TextStyle(
fontSize: 11,
fontWeight: FontWeight.w600,
letterSpacing: -0.44,
),
),
// mHeight(2),
Container(
padding: EdgeInsets.all(5),
// decoration: containerRadius(color.withOpacity(0.2), 4),
child: Text(
status,
style: TextStyle(
fontSize: 8,
fontWeight: FontWeight.w600,
letterSpacing: -0.32,
color: color,
),
),
),
],
)
],
);
}
}

@ -1,5 +1,3 @@
import 'dart:io';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart'; import 'package:hmg_patient_app_new/core/app_assets.dart';
@ -14,10 +12,7 @@ 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/PlacePrediction.dart';
import 'package:hmg_patient_app_new/features/location/location_view_model.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/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/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/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/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/presentation/emergency_services/widgets/location_input_bottom_sheet.dart';
@ -29,7 +24,6 @@ import 'package:hmg_patient_app_new/widgets/expandable_bottom_sheet/model/Bottom
import 'package:hmg_patient_app_new/widgets/input_widget.dart'; import 'package:hmg_patient_app_new/widgets/input_widget.dart';
import 'package:hmg_patient_app_new/widgets/map/HMSMap.dart'; import 'package:hmg_patient_app_new/widgets/map/HMSMap.dart';
import 'package:hmg_patient_app_new/widgets/map/map.dart'; import 'package:hmg_patient_app_new/widgets/map/map.dart';
import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import '../../../widgets/common_bottom_sheet.dart'; import '../../../widgets/common_bottom_sheet.dart';
@ -40,20 +34,13 @@ class CallAmbulancePage extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
floatingActionButton: Visibility( floatingActionButton: Visibility(
visible: context.watch<EmergencyServicesViewModel>().bottomSheetType == visible: context.watch<EmergencyServicesViewModel>().bottomSheetType == BottomSheetType.FIXED,
BottomSheetType.FIXED,
child: Padding( child: Padding(
padding: EdgeInsetsDirectional.only(end: 8.h, bottom: 68.h), padding: EdgeInsetsDirectional.only(end: 8.h, bottom: 68.h),
child: DecoratedBox( child: DecoratedBox(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration( decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 12.h),
color: AppColors.whiteColor, borderRadius: 12.h), child: Utils.buildSvgWithAssets(icon: AppAssets.locate_me, width: 24.h, height: 24.h).paddingAll(12.h).onPress(() {
child: Utils.buildSvgWithAssets( context.read<EmergencyServicesViewModel>().moveToCurrentLocation();
icon: AppAssets.locate_me, width: 24.h, height: 24.h)
.paddingAll(12.h)
.onPress(() {
context
.read<EmergencyServicesViewModel>()
.moveToCurrentLocation();
}), }),
), ),
), ),
@ -62,8 +49,7 @@ class CallAmbulancePage extends StatelessWidget {
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
ExpandableBottomSheet( ExpandableBottomSheet(
bottomSheetType: bottomSheetType: context.watch<EmergencyServicesViewModel>().bottomSheetType,
context.watch<EmergencyServicesViewModel>().bottomSheetType,
children: { children: {
BottomSheetType.EXPANDED: ExpanedBottomSheet(context), BottomSheetType.EXPANDED: ExpanedBottomSheet(context),
BottomSheetType.FIXED: FixedBottomSheet(context), BottomSheetType.FIXED: FixedBottomSheet(context),
@ -73,42 +59,28 @@ class CallAmbulancePage extends StatelessWidget {
), ),
body: Stack( body: Stack(
children: [ children: [
if (context.read<EmergencyServicesViewModel>().isGMSAvailable ) if (context.read<EmergencyServicesViewModel>().isGMSAvailable)
GMSMap( GMSMap(
currentLocation: currentLocation: context.read<EmergencyServicesViewModel>().getGMSLocation(),
context.read<EmergencyServicesViewModel>().getGMSLocation(), onCameraMoved: (value) => context.read<LocationViewModel>().handleGMSMapCameraMoved(value),
onCameraMoved: (value) => context onCameraIdle: context.read<LocationViewModel>().handleOnCameraIdle,
.read<LocationViewModel>()
.handleGMSMapCameraMoved(value),
onCameraIdle:
context.read<LocationViewModel>().handleOnCameraIdle,
myLocationEnabled: true, myLocationEnabled: true,
inputController: inputController: context.read<EmergencyServicesViewModel>().gmsController,
context.read<EmergencyServicesViewModel>().gmsController,
showCenterMarker: true, showCenterMarker: true,
) )
else else
HMSMap( HMSMap(
currentLocation: currentLocation: context.read<EmergencyServicesViewModel>().getHMSLocation(),
context.read<EmergencyServicesViewModel>().getHMSLocation(), onCameraMoved: (value) => context.read<LocationViewModel>().handleHMSMapCameraMoved(value),
onCameraMoved: (value) => context onCameraIdle: context.read<LocationViewModel>().handleOnCameraIdle,
.read<LocationViewModel>()
.handleHMSMapCameraMoved(value),
onCameraIdle:
context.read<LocationViewModel>().handleOnCameraIdle,
myLocationEnabled: false, myLocationEnabled: false,
inputController: inputController: context.read<EmergencyServicesViewModel>().hmsController,
context.read<EmergencyServicesViewModel>().hmsController,
showCenterMarker: true, showCenterMarker: true,
), ),
Align( Align(
alignment: AlignmentDirectional.topStart, alignment: AlignmentDirectional.topStart,
child: Utils.buildSvgWithAssets( child: Utils.buildSvgWithAssets(icon: AppAssets.closeBottomNav, width: 32.h, height: 32.h).onPress(() {
icon: AppAssets.closeBottomNav, width: 32.h, height: 32.h) context.read<EmergencyServicesViewModel>().flushPickupInformation();
.onPress(() {
context
.read<EmergencyServicesViewModel>()
.flushPickupInformation();
Navigator.pop(context); Navigator.pop(context);
}), }),
).paddingOnly(top: 51.h, left: 24.h), ).paddingOnly(top: 51.h, left: 24.h),
@ -119,8 +91,7 @@ class CallAmbulancePage extends StatelessWidget {
Widget FixedBottomSheet(BuildContext context) { Widget FixedBottomSheet(BuildContext context) {
return GestureDetector( return GestureDetector(
onVerticalDragUpdate: (details){ onVerticalDragUpdate: (details) {
// if(details.delta.dy<0){ // if(details.delta.dy<0){
// //
// context.read<EmergencyServicesViewModel>().updateBottomSheetState(BottomSheetType.EXPANDED); // context.read<EmergencyServicesViewModel>().updateBottomSheetState(BottomSheetType.EXPANDED);
@ -161,7 +132,7 @@ class CallAmbulancePage extends StatelessWidget {
// ), // ),
// ), // ),
// ), // ),
// .paddingOnly(right: 24.h, bottom: 24.h), // .paddingOnly(right: 24.h, bottom: 24.h),
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
@ -189,9 +160,7 @@ class CallAmbulancePage extends StatelessWidget {
weight: FontWeight.w600, weight: FontWeight.w600,
color: AppColors.textColor, color: AppColors.textColor,
), ),
" Please select the details of pickup" " Please select the details of pickup".needTranslation.toText12(
.needTranslation
.toText12(
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
color: AppColors.greyTextColor, color: AppColors.greyTextColor,
) )
@ -200,9 +169,7 @@ class CallAmbulancePage extends StatelessWidget {
CustomButton( CustomButton(
text: "Select Details".needTranslation, text: "Select Details".needTranslation,
onPressed: () { onPressed: () {
context context.read<EmergencyServicesViewModel>().updateBottomSheetState(BottomSheetType.EXPANDED);
.read<EmergencyServicesViewModel>()
.updateBottomSheetState(BottomSheetType.EXPANDED);
}) })
], ],
).paddingOnly(top: 24.h, bottom: 32.h, left: 24.h, right: 24.h), ).paddingOnly(top: 24.h, bottom: 32.h, left: 24.h, right: 24.h),
@ -217,11 +184,11 @@ class CallAmbulancePage extends StatelessWidget {
Widget ExpanedBottomSheet(BuildContext context) { Widget ExpanedBottomSheet(BuildContext context) {
return GestureDetector( return GestureDetector(
onVerticalDragUpdate: (details){ onVerticalDragUpdate: (details) {
if(details.delta.dy>0){ if (details.delta.dy > 0) {
context.read<EmergencyServicesViewModel>().updateBottomSheetState(BottomSheetType.FIXED); context.read<EmergencyServicesViewModel>().updateBottomSheetState(BottomSheetType.FIXED);
} }
}, },
child: Column( child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
@ -239,22 +206,19 @@ class CallAmbulancePage extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
spacing: 16.h, spacing: 16.h,
children: [ children: [
hospitalAndPickUpSection(context), hospitalAndPickUpSection(context),
if(context.read<EmergencyServicesViewModel>().appointment != null) if (context.read<EmergencyServicesViewModel>().appointment != null)
AppointmentDoctorCard( AppointmentDoctorCard(
renderWidgetForERDisplay: true, renderWidgetForERDisplay: true,
patientAppointmentHistoryResponseModel: context.read<EmergencyServicesViewModel>().appointment!, patientAppointmentHistoryResponseModel: context.read<EmergencyServicesViewModel>().appointment!,
onAskDoctorTap: () {}, onAskDoctorTap: () {},
onCancelTap: () async {}, onCancelTap: () async {},
onRescheduleTap: () async {}, onRescheduleTap: () async {},
).onPress((){ ).onPress(() {
openAppointmentList(context); openAppointmentList(context);
}) })
], ],
).paddingOnly(top: 24.h, bottom: 32.h,left: 24.h, right: 24.h), ).paddingOnly(top: 24.h, bottom: 32.h, left: 24.h, right: 24.h),
bottomPriceContent(context) bottomPriceContent(context)
], ],
), ),
@ -286,8 +250,7 @@ class CallAmbulancePage extends StatelessWidget {
shrinkWrap: true, shrinkWrap: true,
itemCount: 3, itemCount: 3,
itemBuilder: (__, index) { itemBuilder: (__, index) {
if (index == if (index == 2) // todo means the end of the list so handle as per the viewmodel
2) // todo means the end of the list so handle as per the viewmodel
{ {
return CustomButton( return CustomButton(
height: 40.h, height: 40.h,
@ -301,11 +264,8 @@ class CallAmbulancePage extends StatelessWidget {
} else { } else {
return AddressItem( return AddressItem(
isSelected: index == 0, isSelected: index == 0,
address: address: "Flat No 301, Building No 12, Palm Spring Apartment, Sector 45, Gurugram, Haryana 122003",
"Flat No 301, Building No 12, Palm Spring Apartment, Sector 45, Gurugram, Haryana 122003", title: index == 0 ? "Home".needTranslation : "Work".needTranslation,
title: index == 0
? "Home".needTranslation
: "Work".needTranslation,
onTap: () {}, onTap: () {},
); );
} }
@ -354,13 +314,9 @@ class CallAmbulancePage extends StatelessWidget {
leadingIcon: AppAssets.pickup_bed, leadingIcon: AppAssets.pickup_bed,
), ),
CustomSwitch( CustomSwitch(
value: context value: context.watch<EmergencyServicesViewModel>().pickupFromInsideTheLocation,
.watch<EmergencyServicesViewModel>() onChanged: (value) {
.pickupFromInsideTheLocation, context.read<EmergencyServicesViewModel>().updateThePickupPlaceFromLocation(value);
onChanged: (value){
context
.read<EmergencyServicesViewModel>()
.updateThePickupPlaceFromLocation(value);
}, },
) )
], ],
@ -374,15 +330,12 @@ class CallAmbulancePage extends StatelessWidget {
leadingIcon: AppAssets.appointment_calendar_icon, leadingIcon: AppAssets.appointment_calendar_icon,
), ),
CustomSwitch( CustomSwitch(
value: context value: context.watch<EmergencyServicesViewModel>().haveAnAppointment,
.watch<EmergencyServicesViewModel>()
.haveAnAppointment,
onChanged: (value) async { onChanged: (value) async {
// if (value) { // if (value) {
// openAppointmentList(context); // openAppointmentList(context);
// } // }
await context.read<EmergencyServicesViewModel>() await context.read<EmergencyServicesViewModel>().updateAppointment(value);
.updateAppointment(value);
if (context.read<EmergencyServicesViewModel>().appointments?.isNotEmpty == true) { if (context.read<EmergencyServicesViewModel>().appointments?.isNotEmpty == true) {
openAppointmentList(context); openAppointmentList(context);
} }
@ -454,55 +407,47 @@ class CallAmbulancePage extends StatelessWidget {
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.end, crossAxisAlignment: CrossAxisAlignment.end,
children: [ children: [
DecoratedBox( DecoratedBox(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration( decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.scaffoldBgColor, color: AppColors.scaffoldBgColor,
customBorder: BorderRadius.only( customBorder: BorderRadius.only(
topLeft: Radius.circular(24.h), topLeft: Radius.circular(24.h),
topRight: Radius.circular(24.h), topRight: Radius.circular(24.h),
),
hasShadow: true
), ),
child: Column( hasShadow: true),
crossAxisAlignment: CrossAxisAlignment.start, child: Column(
spacing: 12.h, crossAxisAlignment: CrossAxisAlignment.start,
spacing: 12.h,
children: [
Row(
children: [ children: [
Row( Expanded(
children: [ child: Column(
Expanded( crossAxisAlignment: CrossAxisAlignment.start,
child: Column( spacing: 4.h,
crossAxisAlignment: CrossAxisAlignment.start, children: [
spacing: 4.h, "Total amount to pay".needTranslation.toText18(
children: [
"Total amount to pay".needTranslation.toText18(
weight: FontWeight.w600, weight: FontWeight.w600,
color: AppColors.textColor, color: AppColors.textColor,
), ),
Row( Row(
children: [ children: [
Utils.buildSvgWithAssets(icon: AppAssets.warning, Utils.buildSvgWithAssets(icon: AppAssets.warning, height: 18.h, width: 18.h),
height: 18.h, width: 18.h), SizedBox(
SizedBox(width: 4.h,), width: 4.h,
"Amount will be paid at the hospital" ),
.needTranslation "Amount will be paid at the hospital".needTranslation.toText12(
.toText12(
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
color: AppColors.greyTextColor, color: AppColors.greyTextColor,
), ),
],
)
], ],
), )
), ],
),
),
Utils.getPaymentAmountWithSymbol( Utils.getPaymentAmountWithSymbol(
(Utils.formatNumberToInternationalFormat(context (Utils.formatNumberToInternationalFormat(context.read<EmergencyServicesViewModel>().getTotalPrice() ?? 0))
.read<EmergencyServicesViewModel>() .toText24(fontWeight: FontWeight.w600, color: AppColors.textColor, letterSpacing: -2),
.getTotalPrice() ??
0))
.toText24(
fontWeight: FontWeight.w600,
color: AppColors.textColor,
letterSpacing: -2),
AppColors.blackColor, AppColors.blackColor,
17.h) 17.h)
@ -518,20 +463,19 @@ class CallAmbulancePage extends StatelessWidget {
PlacePrediction? placePrediction = locationViewModel.selectedPrediction; PlacePrediction? placePrediction = locationViewModel.selectedPrediction;
context.read<EmergencyServicesViewModel>().submitAmbulanceRequest(response?.results.first, placeDetails, placePrediction); context.read<EmergencyServicesViewModel>().submitAmbulanceRequest(response?.results.first, placeDetails, placePrediction);
}) })
], ],
).paddingOnly(top: 24.h, bottom: 12.h, left: 24.h, right: 24.h), ).paddingOnly(top: 24.h, bottom: 12.h, left: 24.h, right: 24.h),
), ),
], ],
); );
} }
showHospitalBottomSheet(BuildContext context){ showHospitalBottomSheet(BuildContext context) {
showCommonBottomSheetWithoutHeight( showCommonBottomSheetWithoutHeight(
title: title: LocaleKeys.selectHospital.tr(),
LocaleKeys.selectHospital.tr(),
context, context,
child: Consumer<EmergencyServicesViewModel>( child: Consumer<EmergencyServicesViewModel>(
builder:(_,vm,__)=> HospitalBottomSheetBody( builder: (_, vm, __) => HospitalBottomSheetBody(
searchText: vm.searchController, searchText: vm.searchController,
displayList: vm.displayList, displayList: vm.displayList,
onFacilityClicked: (value) { onFacilityClicked: (value) {
@ -545,8 +489,7 @@ class CallAmbulancePage extends StatelessWidget {
onHospitalSearch: (value) { onHospitalSearch: (value) {
vm.searchHospitals(value ?? ""); vm.searchHospitals(value ?? "");
}, },
selectedFacility: selectedFacility: vm.selectedFacility,
vm.selectedFacility,
hmcCount: vm.hmcCount, hmcCount: vm.hmcCount,
hmgCount: vm.hmgCount, hmgCount: vm.hmgCount,
), ),
@ -583,16 +526,14 @@ class CallAmbulancePage extends StatelessWidget {
textPlaceInput(context) { textPlaceInput(context) {
return Consumer<LocationViewModel>(builder: (_, vm, __) { return Consumer<LocationViewModel>(builder: (_, vm, __) {
print( print("the data is ${vm.geocodeResponse?.results.first.formattedAddress ?? vm.selectedPrediction?.description}");
"the data is ${vm.geocodeResponse?.results.first.formattedAddress ?? vm.selectedPrediction?.description}");
return SizedBox( return SizedBox(
width: MediaQuery.sizeOf(context).width, width: MediaQuery.sizeOf(context).width,
child: TextInputWidget( child: TextInputWidget(
labelText: "Enter Pickup Location Manually".needTranslation, labelText: "Enter Pickup Location Manually".needTranslation,
hintText: "Enter Pickup Location".needTranslation, hintText: "Enter Pickup Location".needTranslation,
controller: TextEditingController( controller: TextEditingController(
text: vm.geocodeResponse?.results.first.formattedAddress ?? text: vm.geocodeResponse?.results.first.formattedAddress ?? vm.selectedPrediction?.description,
vm.selectedPrediction?.description,
), ),
leadingIcon: AppAssets.location_pickup, leadingIcon: AppAssets.location_pickup,
isAllowLeadingIcon: true, isAllowLeadingIcon: true,
@ -614,8 +555,7 @@ class CallAmbulancePage extends StatelessWidget {
///decide which field to show first based on the selected calling place ///decide which field to show first based on the selected calling place
inputFields(BuildContext context) { inputFields(BuildContext context) {
return context.read<EmergencyServicesViewModel>().callingPlace == return context.read<EmergencyServicesViewModel>().callingPlace == AmbulanceCallingPlace.FROM_HOSPITAL
AmbulanceCallingPlace.FROM_HOSPITAL
? HospitalFieldFirstThanPlaces(context) ? HospitalFieldFirstThanPlaces(context)
: PlaceFirstThanHospitalField(context); : PlaceFirstThanHospitalField(context);
} }
@ -639,21 +579,15 @@ class CallAmbulancePage extends StatelessWidget {
hospitalField(BuildContext context) { hospitalField(BuildContext context) {
return DecoratedBox( return DecoratedBox(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration( decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 12.h),
color: AppColors.whiteColor, borderRadius: 12.h),
child: Row( child: Row(
children: [ children: [
hospitalAndPickUpItemContent( hospitalAndPickUpItemContent(
title: "Select Hospital".needTranslation, title: "Select Hospital".needTranslation,
subTitle: context subTitle: context.read<EmergencyServicesViewModel>().getSelectedHospitalName() ?? "Select Hospital".needTranslation,
.read<EmergencyServicesViewModel>()
.getSelectedHospitalName() ??
"Select Hospital".needTranslation,
leadingIcon: AppAssets.hospital, leadingIcon: AppAssets.hospital,
), ),
Utils.buildSvgWithAssets( Utils.buildSvgWithAssets(icon: AppAssets.down_cheveron, width: 24.h, height: 24.h).paddingAll(16.h)
icon: AppAssets.down_cheveron, width: 24.h, height: 24.h)
.paddingAll(16.h)
], ],
).onPress(() { ).onPress(() {
print("the item is clicked"); print("the item is clicked");

@ -1,8 +1,6 @@
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart'; import 'package:hmg_patient_app_new/core/app_assets.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/size_utils.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart'; import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
@ -12,11 +10,9 @@ import 'package:hmg_patient_app_new/features/emergency_services/models/OrderDisp
import 'package:hmg_patient_app_new/features/location/location_view_model.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/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/emergency_services/RRT/rrt_request_type_select.dart'; import 'package:hmg_patient_app_new/presentation/emergency_services/RRT/rrt_request_type_select.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/call_ambulance/widgets/ambulance_option_selection_bottomsheet.dart'; import 'package:hmg_patient_app_new/presentation/emergency_services/call_ambulance/widgets/ambulance_option_selection_bottomsheet.dart';
import 'package:hmg_patient_app_new/presentation/emergency_services/call_ambulance/widgets/pickup_location.dart'; import 'package:hmg_patient_app_new/presentation/emergency_services/call_ambulance/widgets/pickup_location.dart';
import 'package:hmg_patient_app_new/presentation/emergency_services/history/er_history_listing.dart'; import 'package:hmg_patient_app_new/presentation/emergency_services/history/er_history_listing.dart';
import 'package:hmg_patient_app_new/presentation/emergency_services/nearest_er_page.dart';
import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart'; import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
@ -30,13 +26,13 @@ class EmergencyServicesPage extends StatelessWidget {
EmergencyServicesPage({super.key}); EmergencyServicesPage({super.key});
late EmergencyServicesViewModel emergencyServicesViewModel; late EmergencyServicesViewModel emergencyServicesViewModel;
LocationUtils? locationUtils;
_handleConfirmationBottomSheet() {}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
emergencyServicesViewModel = Provider.of<EmergencyServicesViewModel>(context, listen: false); emergencyServicesViewModel = Provider.of<EmergencyServicesViewModel>(context, listen: false);
locationUtils = getIt.get<LocationUtils>();
locationUtils!.isShowConfirmDialog = true;
return CollapsingListView( return CollapsingListView(
title: LocaleKeys.emergencyServices.tr(), title: LocaleKeys.emergencyServices.tr(),
requests: () { requests: () {
@ -64,7 +60,9 @@ class EmergencyServicesPage extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
"Call Ambulance".needTranslation.toText16(isBold: true, color: AppColors.blackColor), "Call Ambulance".needTranslation.toText16(isBold: true, color: AppColors.blackColor),
"Request an ambulance in emergency from home or hospital".needTranslation.toText12(color: AppColors.greyTextColor, fontWeight: FontWeight.w500), "Request an ambulance in emergency from home or hospital"
.needTranslation
.toText12(color: AppColors.greyTextColor, fontWeight: FontWeight.w500),
], ],
), ),
), ),
@ -72,13 +70,10 @@ class EmergencyServicesPage extends StatelessWidget {
Utils.buildSvgWithAssets(icon: AppAssets.forward_chevron_icon, width: 13.h, height: 13.h), Utils.buildSvgWithAssets(icon: AppAssets.forward_chevron_icon, width: 13.h, height: 13.h),
], ],
).onPress(() { ).onPress(() {
showCommonBottomSheetWithoutHeight( showCommonBottomSheetWithoutHeight(
context, context,
child: Container( child: Container(
decoration: decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.primaryRedColor, color: AppColors.primaryRedColor,
borderRadius: 24.h, borderRadius: 24.h,
), ),
@ -103,29 +98,20 @@ class EmergencyServicesPage extends StatelessWidget {
], ],
), ),
Lottie.asset(AppAnimations.ambulance_alert, Lottie.asset(AppAnimations.ambulance_alert,
repeat: false, repeat: false, reverse: false, frameRate: FrameRate(60), width: 120.h, height: 120.h, fit: BoxFit.contain),
reverse: false,
frameRate: FrameRate(60),
width: 120.h,
height: 120.h,
fit: BoxFit.contain),
SizedBox(height: 8.h), SizedBox(height: 8.h),
"Confirmation".needTranslation.toText28( "Confirmation".needTranslation.toText28(color: AppColors.whiteColor, isBold: true),
color: AppColors.whiteColor, isBold: true),
SizedBox(height: 8.h), SizedBox(height: 8.h),
"Are you sure you want to call an ambulance?" "Are you sure you want to call an ambulance?"
.needTranslation .needTranslation
.toText14( .toText14(color: AppColors.whiteColor, weight: FontWeight.w500),
color: AppColors.whiteColor,
weight: FontWeight.w500),
SizedBox(height: 24.h), SizedBox(height: 24.h),
CustomButton( CustomButton(
text: LocaleKeys.confirm.tr(context: context), text: LocaleKeys.confirm.tr(context: context),
onPressed: () async { onPressed: () async {
//
Navigator.of(context).pop(); Navigator.of(context).pop();
await emergencyServicesViewModel.getTransportationOption(); await emergencyServicesViewModel.getTransportationOption();
openTranportationSelectionBottomSheet(context); openTranportationSelectionBottomSheet(context);
}, },
backgroundColor: AppColors.whiteColor, backgroundColor: AppColors.whiteColor,
borderColor: AppColors.whiteColor, borderColor: AppColors.whiteColor,
@ -163,7 +149,9 @@ class EmergencyServicesPage extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
"Nearest ER Location".needTranslation.toText16(isBold: true, color: AppColors.blackColor), "Nearest ER Location".needTranslation.toText16(isBold: true, color: AppColors.blackColor),
"Get the details of nearest branch including directions".needTranslation.toText12(color: AppColors.greyTextColor, fontWeight: FontWeight.w500), "Get the details of nearest branch including directions"
.needTranslation
.toText12(color: AppColors.greyTextColor, fontWeight: FontWeight.w500),
], ],
), ),
), ),
@ -191,7 +179,8 @@ class EmergencyServicesPage extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
"Rapid Response Team (RRT)".toText16(isBold: true, color: AppColors.blackColor), "Rapid Response Team (RRT)".toText16(isBold: true, color: AppColors.blackColor),
"Comprehensive medical service for all sorts of urgent and stable cases".toText12(color: AppColors.greyTextColor, fontWeight: FontWeight.w500), "Comprehensive medical service for all sorts of urgent and stable cases"
.toText12(color: AppColors.greyTextColor, fontWeight: FontWeight.w500),
], ],
), ),
), ),
@ -215,7 +204,9 @@ class EmergencyServicesPage extends StatelessWidget {
SizedBox(height: 8.h), SizedBox(height: 8.h),
LocaleKeys.confirm.tr().toText28(color: AppColors.whiteColor, isBold: true), LocaleKeys.confirm.tr().toText28(color: AppColors.whiteColor, isBold: true),
SizedBox(height: 8.h), SizedBox(height: 8.h),
"Are you sure you want to call Rapid Response Team (RRT)?".needTranslation.toText14(color: AppColors.whiteColor, weight: FontWeight.w500), "Are you sure you want to call Rapid Response Team (RRT)?"
.needTranslation
.toText14(color: AppColors.whiteColor, weight: FontWeight.w500),
SizedBox(height: 24.h), SizedBox(height: 24.h),
CustomButton( CustomButton(
text: LocaleKeys.confirm.tr(context: context), text: LocaleKeys.confirm.tr(context: context),
@ -286,7 +277,9 @@ class EmergencyServicesPage extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
"Emergency Check-In".needTranslation.toText16(isBold: true, color: AppColors.blackColor), "Emergency Check-In".needTranslation.toText16(isBold: true, color: AppColors.blackColor),
"Prior ER Check-In to skip the line & payment at the reception.".needTranslation.toText12(color: AppColors.greyTextColor, fontWeight: FontWeight.w500), "Prior ER Check-In to skip the line & payment at the reception."
.needTranslation
.toText12(color: AppColors.greyTextColor, fontWeight: FontWeight.w500),
], ],
), ),
), ),
@ -297,10 +290,7 @@ class EmergencyServicesPage extends StatelessWidget {
showCommonBottomSheetWithoutHeight( showCommonBottomSheetWithoutHeight(
context, context,
child: Container( child: Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration( decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.primaryRedColor, borderRadius: 24.h),
color: AppColors.primaryRedColor,
borderRadius: 24.h,
),
child: Padding( child: Padding(
padding: EdgeInsets.all(24.h), padding: EdgeInsets.all(24.h),
child: Column( child: Column(
@ -321,7 +311,8 @@ class EmergencyServicesPage extends StatelessWidget {
}), }),
], ],
), ),
Lottie.asset(AppAnimations.ambulance_alert, repeat: false, reverse: false, frameRate: FrameRate(60), width: 120.h, height: 120.h, fit: BoxFit.contain), Lottie.asset(AppAnimations.ambulance_alert,
repeat: false, reverse: false, frameRate: FrameRate(60), width: 120.h, height: 120.h, fit: BoxFit.contain),
SizedBox(height: 8.h), SizedBox(height: 8.h),
LocaleKeys.confirm.tr().toText28(color: AppColors.whiteColor, isBold: true), LocaleKeys.confirm.tr().toText28(color: AppColors.whiteColor, isBold: true),
SizedBox(height: 8.h), SizedBox(height: 8.h),
@ -362,12 +353,10 @@ class EmergencyServicesPage extends StatelessWidget {
); );
} }
openPickupDetailsBottomSheet(BuildContext context){ openPickupDetailsBottomSheet(BuildContext context) {
showCommonBottomSheetWithoutHeight( showCommonBottomSheetWithoutHeight(
onCloseClicked: (){ onCloseClicked: () {
context context.read<EmergencyServicesViewModel>().flushPickupInformation();
.read<EmergencyServicesViewModel>()
.flushPickupInformation();
}, },
titleWidget: Transform.flip( titleWidget: Transform.flip(
flipX: emergencyServicesViewModel.isArabic, flipX: emergencyServicesViewModel.isArabic,
@ -377,45 +366,38 @@ class EmergencyServicesPage extends StatelessWidget {
fit: BoxFit.contain, fit: BoxFit.contain,
), ),
).onPress(() { ).onPress(() {
context context.read<EmergencyServicesViewModel>().flushPickupInformation();
.read<EmergencyServicesViewModel>()
.flushPickupInformation();
Navigator.pop(context); Navigator.pop(context);
openTranportationSelectionBottomSheet(context); openTranportationSelectionBottomSheet(context);
}), }),
context, context,
child: PickupLocation( child: PickupLocation(onTap: () {
onTap: () { Navigator.of(context).pop();
Navigator.of(context).pop(); context.read<LocationViewModel>().flushSearchPredictions();
context.read<LocationViewModel>().flushSearchPredictions(); context.read<EmergencyServicesViewModel>().navigateTOAmbulancePage();
context }),
.read<EmergencyServicesViewModel>()
.navigateTOAmbulancePage();
}),
isFullScreen: false, isFullScreen: false,
isCloseButtonVisible: true, isCloseButtonVisible: true,
hasBottomPadding: false, hasBottomPadding: false,
backgroundColor: AppColors.bottomSheetBgColor, backgroundColor: AppColors.bottomSheetBgColor,
callBackFunc: () {}, callBackFunc: () {},
); );
} }
void openTranportationSelectionBottomSheet(BuildContext context) { void openTranportationSelectionBottomSheet(BuildContext context) {
if(emergencyServicesViewModel.transportationOptions.isNotEmpty) { if (emergencyServicesViewModel.transportationOptions.isNotEmpty) {
showCommonBottomSheetWithoutHeight( showCommonBottomSheetWithoutHeight(
title: "Transport Options".needTranslation, title: "Transport Options".needTranslation,
context, context,
child: SizedBox( child: SizedBox(
height: 400.h, height: 400.h,
child: AmbulanceOptionSelectionBottomSheet( child: AmbulanceOptionSelectionBottomSheet(onTap: () {
onTap: () { Navigator.of(context).pop();
Navigator.of(context).pop(); openPickupDetailsBottomSheet(context);
openPickupDetailsBottomSheet(context); // context
// context // .read<EmergencyServicesViewModel>()
// .read<EmergencyServicesViewModel>() // .navigateTOAmbulancePage();
// .navigateTOAmbulancePage(); }),
}),
), ),
isFullScreen: false, isFullScreen: false,
isCloseButtonVisible: true, isCloseButtonVisible: true,

@ -1,10 +1,47 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/app_export.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/features/hmg_services/models/ui_models/hmg_services_component_model.dart';
import 'package:hmg_patient_app_new/presentation/hmg_services/services_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/appbar/collapsing_list_view.dart';
class ServicesPage extends StatelessWidget { class ServicesPage extends StatelessWidget {
const ServicesPage({super.key}); ServicesPage({super.key});
final List<HmgServicesComponentModel> hmgServices = [
HmgServicesComponentModel(
11,
"E Referral Services".needTranslation,
"".needTranslation,
AppAssets.eReferral,
true,
bgColor: Colors.orange,
textColor: AppColors.blackColor,
route: AppRoutes.eReferralPage,
),
HmgServicesComponentModel(
12,
"Comprehensive Checkup".needTranslation,
"".needTranslation,
AppAssets.comprehensiveCheckup,
true,
bgColor: AppColors.bgGreenColor,
textColor: AppColors.blackColor,
route: AppRoutes.comprehensiveCheckupPage,
),
HmgServicesComponentModel(
12,
"Home Health Care".needTranslation,
"".needTranslation,
AppAssets.emergency_services_icon,
true,
bgColor: AppColors.bgGreenColor,
textColor: AppColors.blackColor,
route: AppRoutes.homeHealthCarePage,
),
];
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -16,7 +53,26 @@ class ServicesPage extends StatelessWidget {
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
"Medical & Care Services".needTranslation.toText18(isBold: true) "Medical & Care Services".needTranslation.toText18(isBold: true),
SizedBox(height: 20.h),
Padding(
padding: EdgeInsets.only(left: 16.w, right: 16.w, top: 0),
child: GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3, // 4 icons per row
crossAxisSpacing: 16.w,
mainAxisSpacing: 24.h,
childAspectRatio: 0.75,
),
physics: NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemCount: hmgServices.length,
padding: EdgeInsets.zero,
itemBuilder: (BuildContext context, int index) {
return ServiceGridViewItem(hmgServices[index], index, false);
},
),
)
], ],
), ),
), ),

@ -0,0 +1,50 @@
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_export.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/features/hmg_services/models/ui_models/hmg_services_component_model.dart';
import 'package:hmg_patient_app_new/services/navigation_service.dart';
class ServiceGridViewItem extends StatelessWidget {
final HmgServicesComponentModel hmgServiceComponentModel;
final int index;
final bool isHomePage;
final bool isLocked;
const ServiceGridViewItem(this.hmgServiceComponentModel, this.index, this.isHomePage, {super.key, this.isLocked = false});
@override
Widget build(BuildContext context) {
return InkWell(
onTap: () => getIt.get<NavigationService>().pushPageRoute(hmgServiceComponentModel.route),
child: Column(
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
height: 48.h,
width: 48.w,
padding: EdgeInsets.zero,
margin: EdgeInsets.zero,
decoration: BoxDecoration(
color: hmgServiceComponentModel.bgColor,
borderRadius: BorderRadius.circular(12.r),
),
child: Utils.buildSvgWithAssets(
icon: hmgServiceComponentModel.icon,
height: 21.h,
width: 21.w,
fit: BoxFit.none,
),
),
SizedBox(height: 5.h),
hmgServiceComponentModel.title.toText12(
fontWeight: FontWeight.w500,
color: hmgServiceComponentModel.textColor,
maxLine: 1,
),
],
));
}
}

@ -0,0 +1,248 @@
import 'dart:async';
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_assets.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/hmg_services/hmg_services_view_model.dart';
import 'package:hmg_patient_app_new/features/hmg_services/models/resq_models/get_cmc_all_orders_resp_model.dart';
import 'package:hmg_patient_app_new/presentation/home_health_care/widgets/hhc_ui_selection_helper.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart';
import 'package:provider/provider.dart';
class HhcOrderDetailPage extends StatefulWidget {
const HhcOrderDetailPage({super.key});
@override
State<HhcOrderDetailPage> createState() => _HhcOrderDetailPageState();
}
class _HhcOrderDetailPageState extends State<HhcOrderDetailPage> {
@override
void initState() {
super.initState();
final hmgServicesViewModel = context.read<HmgServicesViewModel>();
scheduleMicrotask(() async {
await hmgServicesViewModel.getAllHhcOrders();
});
}
Color _getStatusColor(int? statusId) {
switch (statusId) {
case 1: // Pending
return const Color(0xffCC9B14);
case 2: // Processing
return const Color(0xff2E303A);
case 3: // Completed
return const Color(0xff359846);
case 4: // Cancelled
case 6: // Rejected
case 7: // Rejected
return const Color(0xffD02127);
default:
return AppColors.greyColor;
}
}
String _formatDate(String? dateString) {
if (dateString == null) return '';
try {
final date = DateTime.parse(dateString);
return DateFormat('MMM dd, yyyy').format(date);
} catch (e) {
return dateString;
}
}
Widget _buildLoadingShimmer() {
return ListView.separated(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: 3,
separatorBuilder: (_, __) => SizedBox(height: 12.h),
itemBuilder: (context, index) {
return _buildOrderCard(GetCMCAllOrdersResponseModel(), isLoading: true);
},
);
}
Widget _buildOrderCard(GetCMCAllOrdersResponseModel order, {bool isLoading = false}) {
final statusColor = _getStatusColor(order.statusId);
final canCancel = order.statusId == 1 || order.statusId == 2;
return AnimatedContainer(
duration: Duration(milliseconds: 300),
curve: Curves.easeInOut,
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 24.h,
hasShadow: true,
),
child: Padding(
padding: EdgeInsets.all(16.w),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Status and Date Row
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 6.h),
decoration: BoxDecoration(
color: statusColor.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(8.r),
),
child: (isLoading ? "Processing" : order.statusText ?? '')
.toText12(
color: statusColor,
fontWeight: FontWeight.w600,
)
.toShimmer2(isShow: isLoading),
),
SizedBox(width: 8.w),
(isLoading ? "Jan 15, 2024" : _formatDate(order.created))
.toText12(
color: AppColors.textColorLight,
fontWeight: FontWeight.w500,
)
.toShimmer2(isShow: isLoading),
],
),
SizedBox(height: 16.h),
// Request ID
Row(
children: [
if (!isLoading) ...[
"Request ID:".needTranslation.toText14(
color: AppColors.textColorLight,
weight: FontWeight.w500,
),
SizedBox(width: 4.w),
],
(isLoading ? "12345" : "${order.iD ?? '-'}").toText16(isBold: true).toShimmer2(isShow: isLoading),
],
),
SizedBox(height: 12.h),
// Chips for Hospital, Service, and Amount
Wrap(
spacing: 6.w,
runSpacing: 6.h,
children: [
// Service
if (order.serviceText != null || isLoading)
AppCustomChipWidget(
icon: AppAssets.servicesBottom,
labelText: isLoading ? "Service Name" : order.serviceText ?? '-',
).toShimmer2(isShow: isLoading),
],
),
// Cancel Button
if (canCancel || isLoading) ...[
SizedBox(height: 16.h),
Row(
children: [
Expanded(
child: CustomButton(
text: "Cancel Order".needTranslation,
onPressed: isLoading ? () {} : () => HhcUiSelectionHelper.showCancelConfirmationDialog(context: context, order: order),
backgroundColor: AppColors.primaryRedColor,
borderColor: AppColors.primaryRedColor,
textColor: AppColors.whiteColor,
fontSize: 14.f,
fontWeight: FontWeight.w600,
borderRadius: 10.r,
height: 44.h,
).toShimmer2(isShow: isLoading),
),
],
),
]
],
),
),
);
}
Widget _buildEmptyState() {
return Center(
child: Padding(
padding: EdgeInsets.symmetric(vertical: 40.h),
child: Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 12.r,
hasShadow: false,
),
child: Utils.getNoDataWidget(
context,
noDataText: "You don't have any Home Health Care orders yet.".needTranslation,
isSmallWidget: true,
width: 62.w,
height: 62.h,
),
),
),
);
}
@override
Widget build(BuildContext context) {
return CollapsingListView(
title: "HHC Orders".needTranslation,
isLeading: true,
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Consumer<HmgServicesViewModel>(
builder: (context, viewModel, child) {
if (viewModel.isHhcOrdersLoading) {
return _buildLoadingShimmer();
}
if (viewModel.hhcOrdersList.isEmpty) {
return _buildEmptyState();
}
return ListView.separated(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: viewModel.hhcOrdersList.length,
separatorBuilder: (_, __) => SizedBox(height: 12.h),
itemBuilder: (context, index) {
final order = viewModel.hhcOrdersList.reversed.toList()[index];
return AnimationConfiguration.staggeredList(
position: index,
duration: const Duration(milliseconds: 500),
child: SlideAnimation(
verticalOffset: 100.0,
child: FadeInAnimation(
child: _buildOrderCard(order),
),
),
);
},
);
},
),
],
).paddingSymmetrical(24.w, 0),
),
);
}
}

@ -0,0 +1,414 @@
import 'dart:async';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.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/hmg_services/hmg_services_view_model.dart';
import 'package:hmg_patient_app_new/features/hmg_services/models/resq_models/get_cmc_all_orders_resp_model.dart';
import 'package:hmg_patient_app_new/features/hmg_services/models/resq_models/get_cmc_services_resp_model.dart';
import 'package:hmg_patient_app_new/presentation/home_health_care/hhc_order_detail_page.dart';
import 'package:hmg_patient_app_new/presentation/home_health_care/hhc_selection_review_page.dart';
import 'package:hmg_patient_app_new/presentation/home_health_care/widgets/hhc_ui_selection_helper.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/routes/custom_page_route.dart';
import 'package:provider/provider.dart';
import 'package:shimmer/shimmer.dart';
class HhcProceduresPage extends StatefulWidget {
const HhcProceduresPage({super.key});
@override
State<HhcProceduresPage> createState() => _HhcProceduresPageState();
}
class _HhcProceduresPageState extends State<HhcProceduresPage> {
@override
void initState() {
super.initState();
final HmgServicesViewModel hmgServicesViewModel = context.read<HmgServicesViewModel>();
final AppState appState = getIt.get<AppState>();
scheduleMicrotask(() async {
final user = appState.getAuthenticatedUser();
if (user != null) {
// Clear previous selections when entering the page
hmgServicesViewModel.clearHhcServicesSelection();
await hmgServicesViewModel.getAllHhcOrders();
await hmgServicesViewModel.getAllHhcServices(patientID: user.patientId ?? 0);
}
});
}
GetCMCAllOrdersResponseModel? _getPendingOrder(List<GetCMCAllOrdersResponseModel> orders) {
if (orders.isEmpty) return null;
// Find pending or processing orders (status 1 or 2)
for (var order in orders) {
if (order.statusId == 1 || order.statusId == 2) {
return order;
}
}
return null;
}
Widget _buildPendingOrderCard(GetCMCAllOrdersResponseModel order) {
int status = order.statusId ?? 0;
String statusDisp = order.statusText ?? "";
Color statusColor;
if (status == 1) {
// pending
statusColor = AppColors.statusPendingColor;
} else if (status == 2) {
// processing
statusColor = AppColors.statusProcessingColor;
} else if (status == 3) {
// completed
statusColor = AppColors.statusCompletedColor;
} else {
// cancel / rejected
statusColor = AppColors.statusRejectedColor;
}
final canCancel = order.statusId == 1 || order.statusId == 2;
return Container(
width: double.infinity,
margin: EdgeInsets.all(16.w),
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 24.h,
hasShadow: true,
),
child: Padding(
padding: EdgeInsets.all(16.w),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Status and Date Row
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 6.h),
decoration: BoxDecoration(
color: statusColor.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(8.r),
),
child: statusDisp.toText12(
color: statusColor,
fontWeight: FontWeight.w600,
),
),
SizedBox(width: 8.w),
if (order.created != null)
DateFormat('MMM dd, yyyy').format(DateTime.parse(order.created!)).toText12(
color: AppColors.textColorLight,
fontWeight: FontWeight.w500,
),
],
),
SizedBox(height: 16.h),
// Request ID
Row(
children: [
"Request ID:".needTranslation.toText14(color: AppColors.textColorLight, weight: FontWeight.w500),
SizedBox(width: 4.w),
"${order.iD ?? '-'}".toText16(isBold: true),
],
),
SizedBox(height: 12.h),
// Info message
Container(
padding: EdgeInsets.all(12.w),
decoration: BoxDecoration(
color: AppColors.infoBannerBgColor,
borderRadius: BorderRadius.circular(10.r),
border: Border.all(
color: AppColors.infoBannerBorderColor,
width: 1,
),
),
child: Row(
children: [
Icon(
Icons.info_outline,
size: 20.w,
color: AppColors.infoBannerIconColor,
),
SizedBox(width: 8.w),
Expanded(
child: "You have a pending order. Please wait for it to be processed.".needTranslation.toText12(
color: AppColors.infoBannerTextColor,
fontWeight: FontWeight.w500,
),
),
],
),
),
if (canCancel) ...[
SizedBox(height: 16.h),
Row(
children: [
Expanded(
child: CustomButton(
text: "Cancel Order".needTranslation,
onPressed: () => HhcUiSelectionHelper.showCancelConfirmationDialog(context: context, order: order),
backgroundColor: AppColors.primaryRedColor,
borderColor: AppColors.primaryRedColor,
textColor: AppColors.whiteColor,
fontSize: 14.f,
fontWeight: FontWeight.w600,
borderRadius: 10.r,
height: 44.h,
),
),
],
),
]
],
),
),
);
}
Widget _buildServiceSelectionList(List<GetCMCServicesResponseModel> services) {
if (services.isEmpty) {
return Center(
child: Padding(
padding: EdgeInsets.all(24.h),
child: Text(
'No services available'.needTranslation,
style: TextStyle(
fontSize: 16.h,
color: AppColors.greyTextColor,
),
),
),
);
}
return Consumer<HmgServicesViewModel>(
builder: (context, viewModel, child) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 8.h),
if (viewModel.selectedHhcServices.isNotEmpty) ...[
SizedBox(height: 16.h),
Container(
margin: EdgeInsets.symmetric(horizontal: 16.w),
padding: EdgeInsets.all(16.w),
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.primaryRedColor.withValues(alpha: 0.1),
borderRadius: 16.r,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
"Selected Services".needTranslation.toText12(
color: AppColors.textColorLight,
fontWeight: FontWeight.w600,
),
"${viewModel.selectedHhcServices.length} service(s) selected".toText14(
isBold: true,
weight: FontWeight.bold,
),
],
),
Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
"Total Amount".needTranslation.toText12(
color: AppColors.textColorLight,
fontWeight: FontWeight.w600,
),
Utils.getPaymentAmountWithSymbol(
viewModel.getHhcSelectedServicesTotal().toStringAsFixed(2).toText16(
isBold: true,
weight: FontWeight.bold,
color: AppColors.primaryRedColor,
),
AppColors.primaryRedColor,
14,
isSaudiCurrency: true,
),
],
),
],
),
),
],
SizedBox(height: 16.h),
Text(
'Select Services'.needTranslation,
style: TextStyle(
fontSize: 20.h,
fontWeight: FontWeight.w700,
color: AppColors.blackColor,
letterSpacing: -0.8,
),
).paddingOnly(left: 16.w, right: 16.w),
SizedBox(height: 12.h),
ListView.builder(
padding: EdgeInsets.symmetric(horizontal: 16.w),
itemCount: services.length,
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemBuilder: (context, index) {
final service = services[index];
final isSelected = viewModel.isHhcServiceSelected(service);
final isArabic = getIt.get<AppState>().isArabic();
final serviceName = isArabic ? (service.textN ?? service.text ?? '') : (service.text ?? '');
return AnimatedContainer(
duration: Duration(milliseconds: 300),
curve: Curves.easeInOut,
margin: EdgeInsets.only(bottom: 12.h),
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 16.r,
hasShadow: true,
),
child: Material(
color: Colors.transparent,
child: InkWell(
onTap: () => viewModel.toggleHhcServiceSelection(service),
borderRadius: BorderRadius.circular(16.r),
child: Container(
padding: EdgeInsets.all(16.w),
child: Row(
children: [
Checkbox(
value: isSelected,
onChanged: (v) => viewModel.toggleHhcServiceSelection(service),
activeColor: AppColors.primaryRedColor,
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
visualDensity: VisualDensity.compact,
),
SizedBox(width: 12.w),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
serviceName.toText16(
weight: FontWeight.w400,
color: AppColors.blackColor,
maxlines: 2,
),
],
),
),
],
),
),
),
),
);
},
),
// Summary Section
],
);
},
);
}
void _proceedWithSelectedService() {
final hmgServicesViewModel = context.read<HmgServicesViewModel>();
if (hmgServicesViewModel.selectedHhcServices.isNotEmpty) {
hmgServicesViewModel.setSelectedServiceForHhcOrder(hmgServicesViewModel.selectedHhcServices.first);
Navigator.of(context).pushReplacement(
CustomPageRoute(
page: HhcSelectionReviewPage(selectedServices: hmgServicesViewModel.selectedHhcServices),
direction: AxisDirection.left,
),
);
}
}
Widget _buildLoadingShimmer() {
return ListView.separated(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
padding: EdgeInsets.all(16.w),
itemCount: 10,
separatorBuilder: (_, __) => SizedBox(height: 12.h),
itemBuilder: (context, index) {
return Shimmer.fromColors(
baseColor: Colors.grey[300]!,
highlightColor: Colors.grey[100]!,
child: Container(
height: 80.h,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(10.r),
),
),
);
},
);
}
@override
Widget build(BuildContext context) {
return CollapsingListView(
title: "Home Health Care".needTranslation,
history: () => Navigator.of(context).push(CustomPageRoute(page: HhcOrderDetailPage(), direction: AxisDirection.up)),
bottomChild: Consumer<HmgServicesViewModel>(
builder: (context, hmgServicesViewModel, child) {
if (hmgServicesViewModel.isHhcOrdersLoading || hmgServicesViewModel.isHhcServicesLoading) return SizedBox.shrink();
final pendingOrder = _getPendingOrder(hmgServicesViewModel.hhcOrdersList);
if (pendingOrder == null && hmgServicesViewModel.selectedHhcServices.isNotEmpty) {
return SafeArea(
top: false,
child: Padding(
padding: EdgeInsets.only(left: 16.w, right: 16.w, top: 12.h),
child: CustomButton(
borderWidth: 0,
text: "Next".needTranslation,
onPressed: _proceedWithSelectedService,
textColor: AppColors.whiteColor,
borderRadius: 12.r,
borderColor: Colors.transparent,
padding: EdgeInsets.symmetric(vertical: 14.h),
),
),
);
}
return SizedBox.shrink();
},
),
child: Consumer<HmgServicesViewModel>(
builder: (context, hmgServicesViewModel, child) {
if (hmgServicesViewModel.isHhcOrdersLoading || hmgServicesViewModel.isHhcServicesLoading) {
return _buildLoadingShimmer();
}
final pendingOrder = _getPendingOrder(hmgServicesViewModel.hhcOrdersList);
if (pendingOrder != null) {
return _buildPendingOrderCard(pendingOrder);
} else {
return _buildServiceSelectionList(hmgServicesViewModel.hhcServicesList);
}
},
),
);
}
}

@ -0,0 +1,244 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.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/route_extensions.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/hmg_services/hmg_services_view_model.dart';
import 'package:hmg_patient_app_new/features/hmg_services/models/req_models/cmc_create_new_order_req_model.dart';
import 'package:hmg_patient_app_new/features/hmg_services/models/resq_models/get_cmc_services_resp_model.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/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/loader/bottomsheet_loader.dart';
import 'package:maps_launcher/maps_launcher.dart';
import 'package:provider/provider.dart';
class HhcSelectionReviewPage extends StatefulWidget {
final List<GetCMCServicesResponseModel> selectedServices;
const HhcSelectionReviewPage({super.key, required this.selectedServices});
@override
State<HhcSelectionReviewPage> createState() => _HhcSelectionReviewPageState();
}
class _HhcSelectionReviewPageState extends State<HhcSelectionReviewPage> {
@override
void initState() {
super.initState();
// Initialize ViewModel state with selected services
WidgetsBinding.instance.addPostFrameCallback((_) {
final hmgServicesViewModel = context.read<HmgServicesViewModel>();
if (widget.selectedServices.isNotEmpty) {
hmgServicesViewModel.setSelectedServiceForHhcOrder(widget.selectedServices.first);
}
});
}
@override
Widget build(BuildContext context) {
final appState = getIt.get<AppState>();
final isArabic = appState.isArabic();
return CollapsingListView(
title: "Summary".needTranslation,
bottomChild: _buildBottomButton(),
child: SingleChildScrollView(
padding: EdgeInsets.all(16.w),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildSelectedServicesCard(isArabic),
SizedBox(height: 16.h),
],
),
),
);
}
Widget _buildSelectedServicesCard(bool isArabic) {
return Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 16.r,
),
padding: EdgeInsets.all(16.w),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
"Selected Services".needTranslation.toText14(
weight: FontWeight.w600,
color: AppColors.greyTextColor,
letterSpacing: -0.4,
),
SizedBox(height: 12.h),
...widget.selectedServices.map((service) {
final serviceName = isArabic ? (service.textN ?? service.text ?? '') : (service.text ?? '');
final price = service.priceTotal ?? 0.0;
return Padding(
padding: EdgeInsets.only(bottom: 4.h),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: serviceName.toText14(
weight: FontWeight.w600,
color: AppColors.blackColor,
letterSpacing: -0.5,
maxlines: 2,
),
),
],
),
);
}),
],
),
);
}
Widget _buildBottomButton() {
return SafeArea(
top: false,
child: Container(
padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 12.h),
decoration: BoxDecoration(
color: AppColors.whiteColor,
boxShadow: [
BoxShadow(
color: Color.fromARGB(13, 0, 0, 0),
blurRadius: 8,
offset: Offset(0, -2),
),
],
),
child: CustomButton(
text: "Confirm".needTranslation,
onPressed: _handleConfirm,
textColor: AppColors.whiteColor,
backgroundColor: AppColors.successColor,
borderRadius: 12.r,
borderColor: Colors.transparent,
borderWidth: 0,
padding: EdgeInsets.symmetric(vertical: 14.h),
),
),
);
}
void _launchDirections(HospitalsModel selectedHospital) {
final double lat = double.parse(selectedHospital.latitude ?? "0.0");
final double lng = double.parse(selectedHospital.longitude ?? "0.0");
if (lat != 0.0 && lng != 0.0) {
MapsLauncher.launchCoordinates(
lat,
lng,
selectedHospital.name ?? "Hospital",
);
}
}
showSuccessBottomSheet(int requestId, HmgServicesViewModel hmgServicesViewModel) {
return showCommonBottomSheetWithoutHeight(
context,
child: Padding(
padding: EdgeInsets.all(16.w),
child: Column(
children: [
Utils.getSuccessWidget(loadingText: "Your request has been successfully submitted.".needTranslation),
Row(
children: [
"Here is your request #: ".needTranslation.toText14(
color: AppColors.textColorLight,
weight: FontWeight.w500,
),
SizedBox(width: 4.w),
("$requestId").toText16(isBold: true),
],
),
SizedBox(height: 24.h),
Row(
children: [
Expanded(
child: CustomButton(
height: 56.h,
text: LocaleKeys.ok.tr(),
onPressed: () {
context.pop();
context.pop();
hmgServicesViewModel.getAllHhcOrders();
},
textColor: AppColors.whiteColor,
),
),
],
),
],
),
),
isCloseButtonVisible: false,
isDismissible: false,
isFullScreen: false,
);
}
void _handleConfirm() {
final hmgServicesViewModel = context.read<HmgServicesViewModel>();
final appState = getIt.get<AppState>();
return showCommonBottomSheetWithoutHeight(
title: LocaleKeys.notice.tr(context: context),
context,
child: Utils.getWarningWidget(
loadingText: "Are you sure you want to submit this request?".needTranslation,
isShowActionButtons: true,
onCancelTap: () {
Navigator.pop(context);
},
onConfirmTap: () async {
Navigator.pop(context);
LoaderBottomSheet.showLoader();
// Create the services list from all selected services
final servicesList = widget.selectedServices.map((selectedService) {
return PatientERCMCInsertServicesList(
recordID: selectedService.iD,
serviceID: selectedService.serviceID,
selectedServiceName: selectedService.text,
selectedServiceNameAR: selectedService.textN,
price: selectedService.price,
vAT: selectedService.priceVAT,
totalPrice: selectedService.priceTotal,
);
}).toList();
// For HHC, we don't need hospital selection, use a default projectID or 0
await hmgServicesViewModel.addHhcOrder(
projectID: 0,
// HHC doesn't require hospital/project selection
orderServiceID: widget.selectedServices.first.orderServiceID ?? 4,
// HHC service ID
services: servicesList,
onSuccess: (requestId) {
LoaderBottomSheet.hideLoader();
showSuccessBottomSheet(requestId, hmgServicesViewModel);
},
onError: (err) {
LoaderBottomSheet.hideLoader();
},
);
}),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
}
}

@ -0,0 +1,90 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.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/route_extensions.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/features/hmg_services/hmg_services_view_model.dart';
import 'package:hmg_patient_app_new/features/hmg_services/models/req_models/order_update_req_model.dart';
import 'package:hmg_patient_app_new/features/hmg_services/models/resq_models/get_cmc_all_orders_resp_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';
import 'package:provider/provider.dart';
class HhcUiSelectionHelper {
static void showCancelConfirmationDialog({
required BuildContext context,
required GetCMCAllOrdersResponseModel order,
}) {
final HmgServicesViewModel hmgServicesViewModel = context.read<HmgServicesViewModel>();
return showCommonBottomSheetWithoutHeight(
title: LocaleKeys.notice.tr(context: context),
context,
child: Utils.getWarningWidget(
loadingText: "Are you sure you want to cancel this order?".needTranslation,
isShowActionButtons: true,
onCancelTap: () {
Navigator.pop(context);
},
onConfirmTap: () async {
Navigator.pop(context);
LoaderBottomSheet.showLoader();
final requestModel = OrderUpdateRequestModel(
presOrderID: order.iD,
rejectionReason: "Cancelled by user",
presOrderStatus: 4, // Cancelled status
editedBy: 3,
);
await hmgServicesViewModel.updateHhcPresOrder(
requestModel: requestModel,
onSuccess: (_) async {
LoaderBottomSheet.hideLoader();
showCommonBottomSheetWithoutHeight(
context,
child: Padding(
padding: EdgeInsets.all(16.w),
child: Column(
children: [
Utils.getSuccessWidget(loadingText: "Order has been cancelled successfully".needTranslation),
SizedBox(height: 24.h),
Row(
children: [
Expanded(
child: CustomButton(
height: 56.h,
text: LocaleKeys.ok.tr(),
onPressed: () {
context.pop();
hmgServicesViewModel.getAllHhcOrders();
},
textColor: AppColors.whiteColor,
),
),
],
),
],
),
),
isCloseButtonVisible: false,
isDismissible: false,
isFullScreen: false,
);
},
onError: (error) {
LoaderBottomSheet.hideLoader();
},
);
},
),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
}
}

@ -20,7 +20,7 @@ import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.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/common_bottom_sheet.dart';
import 'package:hmg_patient_app_new/widgets/shimmer/movies_shimmer_widget.dart'; import 'package:hmg_patient_app_new/widgets/shimmer/common_shimmer_widget.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'widgets/insurance_history.dart'; import 'widgets/insurance_history.dart';

@ -12,7 +12,7 @@ import 'package:hmg_patient_app_new/presentation/lab/lab_result_item_view.dart';
import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart'; import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart';
import 'package:hmg_patient_app_new/widgets/shimmer/movies_shimmer_widget.dart'; import 'package:hmg_patient_app_new/widgets/shimmer/common_shimmer_widget.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
class InsuranceHistory extends StatelessWidget { class InsuranceHistory extends StatelessWidget {

@ -12,7 +12,7 @@ import 'package:hmg_patient_app_new/presentation/lab/lab_result_item_view.dart';
import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart'; import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart';
import 'package:hmg_patient_app_new/widgets/shimmer/movies_shimmer_widget.dart'; import 'package:hmg_patient_app_new/widgets/shimmer/common_shimmer_widget.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
class PatientInsuranceCardUpdateCard extends StatelessWidget { class PatientInsuranceCardUpdateCard extends StatelessWidget {

@ -56,7 +56,7 @@ import 'package:hmg_patient_app_new/widgets/custom_tab_bar.dart';
import 'package:hmg_patient_app_new/widgets/input_widget.dart'; import 'package:hmg_patient_app_new/widgets/input_widget.dart';
import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart'; import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart';
import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart'; import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
import 'package:hmg_patient_app_new/widgets/shimmer/movies_shimmer_widget.dart'; import 'package:hmg_patient_app_new/widgets/shimmer/common_shimmer_widget.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import '../prescriptions/prescription_detail_page.dart'; import '../prescriptions/prescription_detail_page.dart';
@ -460,7 +460,7 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
SizedBox(height: 16.h), SizedBox(height: 16.h),
Consumer<PrescriptionsViewModel>(builder: (context, prescriptionVM, child) { Consumer<PrescriptionsViewModel>(builder: (context, prescriptionVM, child) {
return prescriptionVM.isPrescriptionsOrdersLoading return prescriptionVM.isPrescriptionsOrdersLoading
? const MoviesShimmerWidget().paddingSymmetrical(24.w, 0.h) ? const CommonShimmerWidget().paddingSymmetrical(24.w, 0.h)
: prescriptionVM.patientPrescriptionOrders.isNotEmpty : prescriptionVM.patientPrescriptionOrders.isNotEmpty
? Container( ? Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration( decoration: RoundedRectangleBorder().toSmoothCornerDecoration(

@ -19,7 +19,7 @@ import 'package:hmg_patient_app_new/presentation/prescriptions/prescription_deta
import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart'; import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
import 'package:hmg_patient_app_new/widgets/shimmer/movies_shimmer_widget.dart'; import 'package:hmg_patient_app_new/widgets/shimmer/common_shimmer_widget.dart';
import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart'; import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';

@ -9,9 +9,11 @@ import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart'; import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/lab/lab_view_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/lab/lab_result_item_view.dart'; import 'package:hmg_patient_app_new/presentation/lab/lab_result_item_view.dart';
import 'package:hmg_patient_app_new/presentation/radiology/radiology_result_page.dart'; import 'package:hmg_patient_app_new/presentation/radiology/radiology_result_page.dart';
import 'package:hmg_patient_app_new/presentation/radiology/search_radiology.dart';
import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart'; import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
@ -30,7 +32,7 @@ class RadiologyOrdersPage extends StatefulWidget {
class _RadiologyOrdersPageState extends State<RadiologyOrdersPage> { class _RadiologyOrdersPageState extends State<RadiologyOrdersPage> {
late RadiologyViewModel radiologyViewModel; late RadiologyViewModel radiologyViewModel;
String selectedFilterText = '';
int? expandedIndex; int? expandedIndex;
@override @override
@ -48,6 +50,24 @@ class _RadiologyOrdersPageState extends State<RadiologyOrdersPage> {
backgroundColor: AppColors.bgScaffoldColor, backgroundColor: AppColors.bgScaffoldColor,
body: CollapsingListView( body: CollapsingListView(
title: LocaleKeys.radiology.tr(context: context), title: LocaleKeys.radiology.tr(context: context),
search: () async {
final lavVM = Provider.of<LabViewModel>(context, listen: false);
if (lavVM.isLabOrdersLoading) {
return;
} else {
String? value = await Navigator.of(context).push(
CustomPageRoute(
page: SearchRadiologyContent(radiologySuggestionsList: radiologyViewModel.radiologySuggestions),
fullScreenDialog: true,
direction: AxisDirection.down,
),
);
if (value != null) {
selectedFilterText = value;
radiologyViewModel.filterRadiologyReports(value);
}
}
},
child: SingleChildScrollView( child: SingleChildScrollView(
child: Consumer<RadiologyViewModel>( child: Consumer<RadiologyViewModel>(
builder: (context, model, child) { builder: (context, model, child) {
@ -56,7 +76,25 @@ class _RadiologyOrdersPageState extends State<RadiologyOrdersPage> {
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
// Expandable list selectedFilterText!.isNotEmpty
? AppCustomChipWidget(
padding: EdgeInsets.symmetric(horizontal: 5.h),
labelText: selectedFilterText!,
deleteIcon: 'assets/images/svg/cross_circle.svg',
backgroundColor: AppColors.alertColor,
textColor: AppColors.whiteColor,
deleteIconColor: AppColors.whiteColor,
deleteIconHasColor: true,
onDeleteTap: () {
setState(() {
selectedFilterText = '';
model.filterRadiologyReports('');
});
},
// chipType: ChipTypeEnum.alert,
// isSelected: true,
)
: SizedBox(),
ListView.builder( ListView.builder(
shrinkWrap: true, shrinkWrap: true,
physics: NeverScrollableScrollPhysics(), physics: NeverScrollableScrollPhysics(),

@ -0,0 +1,154 @@
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_export.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.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/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/input_widget.dart';
import 'package:sizer/sizer.dart';
class SearchRadiologyContent extends StatefulWidget {
final List<String> radiologySuggestionsList;
const SearchRadiologyContent({super.key, required this.radiologySuggestionsList});
@override
State<SearchRadiologyContent> createState() => _SearchRadiologyContentContentState();
}
class _SearchRadiologyContentContentState extends State<SearchRadiologyContent> {
TextEditingController searchEditingController = TextEditingController();
List<String> filteredSuggestions = [];
@override
void initState() {
super.initState();
filteredSuggestions = List.from(widget.radiologySuggestionsList);
// Listen for changes in the search field
searchEditingController.addListener(() {
filterSuggestions();
});
}
@override
void dispose() {
searchEditingController.dispose();
super.dispose();
}
void filterSuggestions() {
final query = searchEditingController.text.toLowerCase();
if (query.isEmpty) {
setState(() {
filteredSuggestions = List.from(widget.radiologySuggestionsList);
});
} else {
setState(() {
filteredSuggestions = widget.radiologySuggestionsList.where((suggestion) => suggestion.toLowerCase().contains(query)).toList();
});
}
}
@override
Widget build(BuildContext context) {
return CollapsingListView(
title: LocaleKeys.radiology.tr(),
isClose: true,
bottomChild: Container(
color: Colors.white,
padding: EdgeInsets.all(ResponsiveExtension(20).h),
child: CustomButton(
text: LocaleKeys.search.tr(),
icon: AppAssets.search_icon,
iconColor: Colors.white,
onPressed: () => Navigator.pop(context, searchEditingController.text),
),
),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.only(left: 24, right: 24, top: 24),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
TextInputWidget(
labelText: "Search radiology results",
hintText: "Type test description",
controller: searchEditingController,
isEnable: true,
prefix: null,
autoFocus: false,
isBorderAllowed: false,
keyboardType: TextInputType.text,
padding: EdgeInsets.symmetric(
vertical: ResponsiveExtension(9).h,
horizontal: ResponsiveExtension(15).h,
),
),
SizedBox(height: ResponsiveExtension(20).h),
if (filteredSuggestions.isNotEmpty) ...[
"Suggestions".toText16(isBold: true),
],
],
),
),
SingleChildScrollView(
physics: NeverScrollableScrollPhysics(),
padding: const EdgeInsets.only(left: 24, right: 24, bottom: 24, top: 16),
child: Wrap(
alignment: WrapAlignment.start,
spacing: 10,
runSpacing: 10,
children: filteredSuggestions
.map((label) => SuggestionChip(
label: label,
onTap: () {
searchEditingController.text = label;
},
))
.toList(),
),
),
],
),
);
}
}
class SuggestionChip extends StatelessWidget {
final String label;
final bool isSelected;
final VoidCallback? onTap;
const SuggestionChip({
super.key,
required this.label,
this.isSelected = false,
this.onTap,
});
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: onTap,
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 8),
decoration: BoxDecoration(
color: isSelected ? AppColors.primaryRedColor : AppColors.whiteColor,
borderRadius: BorderRadius.circular(8),
),
child: label.toText12(
color: isSelected ? Colors.white : Colors.black87,
fontWeight: FontWeight.w500,
),
),
);
}
}

@ -1,23 +0,0 @@
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
class ServicesPage extends StatelessWidget {
const ServicesPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: AppColors.bgScaffoldColor,
appBar: AppBar(
title: const Text('Appointments'),
backgroundColor: AppColors.bgScaffoldColor,
),
body: const Center(
child: Text(
'Appointments Page',
style: TextStyle(fontSize: 24),
),
),
);
}
}

@ -2,8 +2,10 @@ import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/presentation/authentication/login.dart'; import 'package:hmg_patient_app_new/presentation/authentication/login.dart';
import 'package:hmg_patient_app_new/presentation/authentication/register.dart'; import 'package:hmg_patient_app_new/presentation/authentication/register.dart';
import 'package:hmg_patient_app_new/presentation/authentication/register_step2.dart'; import 'package:hmg_patient_app_new/presentation/authentication/register_step2.dart';
import 'package:hmg_patient_app_new/presentation/home/landing_page.dart'; import 'package:hmg_patient_app_new/presentation/comprehensive_checkup/comprehensive_checkup_page.dart';
import 'package:hmg_patient_app_new/presentation/e_referral/e_referral_page_home.dart';
import 'package:hmg_patient_app_new/presentation/home/navigation_screen.dart'; import 'package:hmg_patient_app_new/presentation/home/navigation_screen.dart';
import 'package:hmg_patient_app_new/presentation/home_health_care/hhc_procedures_page.dart';
import 'package:hmg_patient_app_new/presentation/medical_file/medical_file_page.dart'; import 'package:hmg_patient_app_new/presentation/medical_file/medical_file_page.dart';
import 'package:hmg_patient_app_new/splashPage.dart'; import 'package:hmg_patient_app_new/splashPage.dart';
@ -14,6 +16,9 @@ class AppRoutes {
static const String registerStepTwo = '/registerStepTwo'; static const String registerStepTwo = '/registerStepTwo';
static const String landingScreen = '/landingScreen'; static const String landingScreen = '/landingScreen';
static const String medicalFilePage = '/medicalFilePage'; static const String medicalFilePage = '/medicalFilePage';
static const String eReferralPage = '/erReferralPage';
static const String comprehensiveCheckupPage = '/comprehensiveCheckupPage';
static const String homeHealthCarePage = '/homeHealthCarePage';
static Map<String, WidgetBuilder> get routes => { static Map<String, WidgetBuilder> get routes => {
initialRoute: (context) => SplashPage(), initialRoute: (context) => SplashPage(),
@ -21,6 +26,9 @@ class AppRoutes {
landingScreen: (context) => LandingNavigation(), landingScreen: (context) => LandingNavigation(),
register: (context) => RegisterNew(), register: (context) => RegisterNew(),
registerStepTwo: (context) => RegisterNewStep2(), registerStepTwo: (context) => RegisterNewStep2(),
medicalFilePage: (context) => MedicalFilePage(), medicalFilePage: (context) => MedicalFilePage(),
eReferralPage: (context) => EReferralPage(),
comprehensiveCheckupPage: (context) => ComprehensiveCheckupPage(),
homeHealthCarePage: (context) => HhcProceduresPage()
}; };
} }

@ -13,8 +13,8 @@ class NavigationService {
return navigatorKey.currentState!.push(route); return navigatorKey.currentState!.push(route);
} }
Future<T?> pushAndRemoveUntil<T>(Route<T> route,RoutePredicate predicate) { Future<T?> pushAndRemoveUntil<T>(Route<T> route, RoutePredicate predicate) {
return navigatorKey.currentState!.pushAndRemoveUntil(route,predicate); return navigatorKey.currentState!.pushAndRemoveUntil(route, predicate);
} }
void pop<T extends Object?>([T? result]) { void pop<T extends Object?>([T? result]) {
@ -38,11 +38,22 @@ class NavigationService {
navigatorKey.currentState?.pushReplacementNamed(routeName); navigatorKey.currentState?.pushReplacementNamed(routeName);
} }
void pushPageRoute(String routeName) {
navigatorKey.currentState?.pushNamed(routeName);
}
Future<T?> pushToOtpScreen<T>(
Future<T?> pushToOtpScreen<T>({required String phoneNumber, required Function(int code) checkActivationCode, required Function(String phoneNumber) onResendOTPPressed, bool isFormFamilyFile = false}) { {required String phoneNumber,
required Function(int code) checkActivationCode,
required Function(String phoneNumber) onResendOTPPressed,
bool isFormFamilyFile = false}) {
return navigatorKey.currentState!.push( return navigatorKey.currentState!.push(
MaterialPageRoute(builder: (_) => OTPVerificationScreen(phoneNumber: phoneNumber, checkActivationCode: checkActivationCode, onResendOTPPressed: onResendOTPPressed, isFormFamilyFile : isFormFamilyFile)), MaterialPageRoute(
builder: (_) => OTPVerificationScreen(
phoneNumber: phoneNumber,
checkActivationCode: checkActivationCode,
onResendOTPPressed: onResendOTPPressed,
isFormFamilyFile: isFormFamilyFile)),
); );
} }

@ -3,21 +3,12 @@ import 'package:flutter/material.dart';
class AppColors { class AppColors {
static const transparent = Colors.transparent; static const transparent = Colors.transparent;
static const mainPurple = Color(0xFF7954F7); static const mainPurple = Color(0xFF7954F7);
static const purpleBg = Color(0xFFAEA4FC);
static const deepPurple = Color(0xFF7C65E7);
static const logoColor = Color(0xFF7C65E7);
static const buttonColor = Color(0xFF6A46F5);
static const splashBgColor = Color(0xFF3C355D);
static const lightGray = Color(0xFFF4F5F7);
static const lightPurple = Color(0xFFB7A3E6);
static const scaffoldBgColor = Color(0xFFF8F8F8); static const scaffoldBgColor = Color(0xFFF8F8F8);
static const bottomSheetBgColor = Color(0xFFF8F8FA); static const bottomSheetBgColor = Color(0xFFF8F8FA);
static const lightGreyEFColor = Color(0xffeaeaff); static const lightGreyEFColor = Color(0xffeaeaff);
static const greyF7Color = Color(0xffF7F7F7); static const greyF7Color = Color(0xffF7F7F7);
static const lightGrayColor = Color(0xff808080); static const lightGrayColor = Color(0xff808080);
static const buttonGrayColor = Color(0xffF1F1F1);
static const lightPurpleAlpha = Color(0x5AB7A3E6);
// New UI Colors // New UI Colors
static const whiteColor = Color(0xFFffffff); static const whiteColor = Color(0xFFffffff);
@ -37,11 +28,10 @@ class AppColors {
static const Color warningColorYellow = Color(0xFFF4A308); static const Color warningColorYellow = Color(0xFFF4A308);
static const Color blackBgColor = Color(0xFF2E3039); static const Color blackBgColor = Color(0xFF2E3039);
static const blackColor = textColor; static const blackColor = textColor;
static const Color inputLabelTextColor = Color(0xff898A8D); static const Color inputLabelTextColor = Color(0xff898A8D);
static const Color greyTextColor = Color(0xFF8F9AA3); static const Color greyTextColor = Color(0xFF8F9AA3);
static const Color lightGrayBGColor = Color(0x142E3039); static const Color lightGrayBGColor = Color(0x142E3039);
static const lightGreenColor = Color(0xFF0ccedde); static const lightGreenColor = Color(0xFF0ccedde);
static const textGreenColor = Color(0xFF18C273); static const textGreenColor = Color(0xFF18C273);
static const Color ratingColorYellow = Color(0xFFFFAF15); static const Color ratingColorYellow = Color(0xFFFFAF15);
@ -57,27 +47,38 @@ class AppColors {
static const Color chipPrimaryRedBorderColor = Color(0xFFED1C2B); static const Color chipPrimaryRedBorderColor = Color(0xFFED1C2B);
static const Color chipSecondaryLightRedColor = Color(0xFFFEE9EA); static const Color chipSecondaryLightRedColor = Color(0xFFFEE9EA);
static const Color successLightColor = Color(0xFF18C273); static const Color successLightColor = Color(0xFF18C273);
static const Color errorLightColor = Color(0xFFED1C2B); static const Color errorLightColor = Color(0xFFED1C2B);
static const Color alertLightColor = Color(0xFFD48D05); static const Color alertLightColor = Color(0xFFD48D05);
static const Color infoLightColor = Color(0xFF0B85F7); static const Color infoLightColor = Color(0xFF0B85F7);
static const Color warningLightColor = Color(0xFFFFCC00); static const Color warningLightColor = Color(0xFFFFCC00);
static const Color greyLightColor = Color(0xFFEFEFF0); static const Color greyLightColor = Color(0xFFEFEFF0);
static const Color thumbColor = Color(0xFF18C273); static const Color thumbColor = Color(0xFF18C273);
static const Color switchBackgroundColor = Color(0x2618C273); static const Color switchBackgroundColor = Color(0x2618C273);
static const Color bottomNAVBorder = Color(0xFFEEEEEE);
static const Color bottomNAVBorder = Color(0xFFEEEEEE); static const Color quickLoginColor = Color(0xFF666666);
static const Color quickLoginColor = Color(0xFF666666); static const Color tooltipTextColor = Color(0xFF414D55);
static const Color graphGridColor = Color(0x4D18C273);
static const Color criticalLowAndHigh = Color(0xFFED1C2B);
static const Color highAndLow = Color(0xFFFFAF15);
static const Color labelTextColor = Color(0xFF838383);
static const Color calenderTextColor = Color(0xFFD0D0D0);
static const Color lightGreenButtonColor = Color(0x2618C273);
static const Color tooltipTextColor = Color(0xFF414D55); static const Color lightRedButtonColor = Color(0x1AED1C2B);
static const Color graphGridColor = Color(0x4D18C273);
static const Color criticalLowAndHigh = Color(0xFFED1C2B);
static const Color highAndLow = Color(0xFFFFAF15);
static const Color labelTextColor = Color(0xFF838383);
static const Color calenderTextColor = Color(0xFFD0D0D0);
static const Color lightGreenButtonColor = Color(0x2618C273);
static const Color lightRedButtonColor = Color(0x1AED1C2B); // Status Colors
static const Color statusPendingColor = Color(0xffCC9B14);
static const Color statusProcessingColor = Color(0xff2E303A);
static const Color statusCompletedColor = Color(0xff359846);
static const Color statusRejectedColor = Color(0xffD02127);
// Info Banner Colors
static const Color infoBannerBgColor = Color(0xFFFFF4E6);
static const Color infoBannerBorderColor = Color(0xFFFFE5B4);
static const Color infoBannerIconColor = Color(0xFFCC9B14);
static const Color infoBannerTextColor = Color(0xFF856404);
} }

@ -1,7 +1,6 @@
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart'; import 'package:flutter_svg/svg.dart';
import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
extension WithContainer on Widget { extension WithContainer on Widget {
Widget get insideContainer => Container( Widget get insideContainer => Container(
@ -26,20 +25,22 @@ class DefaultButton extends StatelessWidget {
final double height; final double height;
final double borderRadius; final double borderRadius;
const DefaultButton(this.text, this.onPress, const DefaultButton(
{Key? key, this.text,
this.color, this.onPress, {
this.isTextExpanded = true, super.key,
this.svgIcon, this.color,
this.disabledColor, this.isTextExpanded = true,
this.count = 0, this.svgIcon,
this.textColor = Colors.white, this.disabledColor,
this.iconData, this.count = 0,
this.fontSize, this.textColor = Colors.white,
this.colors, this.iconData,
this.height = 50, this.fontSize,
this.borderRadius = 100}) this.colors,
: super(key: key); this.height = 50,
this.borderRadius = 100,
});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -48,30 +49,20 @@ class DefaultButton extends StatelessWidget {
child: Container( child: Container(
height: height, height: height,
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: BorderRadius.circular(borderRadius), borderRadius: BorderRadius.circular(borderRadius),
gradient: onPress == null gradient: onPress == null
? LinearGradient( ? LinearGradient(
colors: <Color>[ colors: <Color>[
disabledColor ?? const Color(0xffEAEAEA), disabledColor ?? const Color(0xffEAEAEA),
disabledColor ?? const Color(0xffEAEAEA), disabledColor ?? const Color(0xffEAEAEA),
], ],
) )
: LinearGradient( : null),
transform: const GradientRotation(.83),
begin: Alignment.topRight,
end: Alignment.bottomLeft,
colors: colors ??
<Color>[
AppColors.buttonColor,
AppColors.buttonColor,
],
),
),
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[ children: <Widget>[
if (iconData != null) Icon(iconData, color: textColor), if (iconData != null) Icon(iconData, color: textColor),
if (svgIcon != null) SvgPicture.asset(svgIcon ?? "", color: textColor), if (svgIcon != null) SvgPicture.asset(svgIcon ?? ""),
if (!isTextExpanded) if (!isTextExpanded)
Padding( Padding(
padding: EdgeInsets.only( padding: EdgeInsets.only(

@ -24,6 +24,7 @@ class AppCustomChipWidget extends StatelessWidget {
this.padding = EdgeInsets.zero, this.padding = EdgeInsets.zero,
this.onChipTap, this.onChipTap,
this.labelPadding, this.labelPadding,
this.onDeleteTap,
}); });
final String? labelText; final String? labelText;
@ -42,77 +43,82 @@ class AppCustomChipWidget extends StatelessWidget {
final EdgeInsets? padding; final EdgeInsets? padding;
final EdgeInsetsDirectional? labelPadding; final EdgeInsetsDirectional? labelPadding;
final void Function()? onChipTap; final void Function()? onChipTap;
final void Function()? onDeleteTap;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final iconS = iconSize ?? 12.w; final iconS = iconSize ?? 12.w;
return GestureDetector( return GestureDetector(
onTap: onChipTap, onTap: onChipTap,
child: ChipTheme( child: SizedBox(
data: ChipThemeData( child: ChipTheme(
padding: EdgeInsets.zero, data: ChipThemeData(
shape: SmoothRectangleBorder( padding: EdgeInsets.zero,
side: BorderSide( shape: SmoothRectangleBorder(
width: 10.w, side: BorderSide(
color: Colors.transparent, // Crucially, set color to transparent width: 10.w,
style: BorderStyle.none, color: Colors.transparent, // Crucially, set color to transparent
style: BorderStyle.none,
),
borderRadius: BorderRadius.circular(8.r), // Apply a border radius of 16.0
), ),
borderRadius: BorderRadius.circular(isFoldable || isTablet ? 6.r : 8.r),
), ),
), child: icon.isNotEmpty
child: icon.isNotEmpty ? Chip(
? Chip( avatar: icon.isNotEmpty
avatar: icon.isNotEmpty ? Utils.buildSvgWithAssets(
? Padding(
padding: EdgeInsets.only(left: 8.w, right: 6.w),
child: Utils.buildSvgWithAssets(
icon: icon, icon: icon,
width: iconS, width: iconS,
height: iconS, height: iconS,
iconColor: iconHasColor ? iconColor : null, iconColor: iconHasColor ? iconColor : null,
fit: BoxFit.contain, fit: BoxFit.contain,
), )
) : SizedBox.shrink(),
: SizedBox.shrink(), label: richText ?? labelText!.toText10(weight: FontWeight.w500, letterSpacing: 0, color: textColor),
avatarBoxConstraints: BoxConstraints(), padding: padding,
label: richText ?? labelText!.toText10(weight: FontWeight.w500, letterSpacing: 0, color: textColor), materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
padding: EdgeInsets.zero, labelPadding: labelPadding ?? EdgeInsetsDirectional.only(end: deleteIcon?.isNotEmpty == true ? 2.w : 8.w),
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, backgroundColor: backgroundColor,
labelPadding: EdgeInsetsDirectional.only(end: 8.w), shape: shape ??
backgroundColor: backgroundColor, SmoothRectangleBorder(
shape: shape ?? borderRadius: BorderRadius.circular(8.r),
SmoothRectangleBorder( smoothness: 10,
borderRadius: BorderRadius.circular(8.r), side: BorderSide(color: AppColors.transparent, width: 1.5),
smoothness: 10, ),
side: BorderSide(color: AppColors.transparent, width: 1.5), deleteIcon: deleteIcon?.isNotEmpty == true
), ? InkWell(
deleteIcon: deleteIcon?.isNotEmpty == true onTap: onDeleteTap,
? Utils.buildSvgWithAssets( child: Utils.buildSvgWithAssets(
icon: deleteIcon!, icon: deleteIcon!,
width: iconS, width: iconS,
height: iconS, height: iconS,
iconColor: deleteIconHasColor ? deleteIconColor : null, iconColor: deleteIconHasColor ? deleteIconColor : null,
) ),
: null, )
onDeleted: deleteIcon?.isNotEmpty == true ? () {} : null, : null,
) onDeleted: deleteIcon?.isNotEmpty == true ? () {} : null,
: Chip( )
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, : Chip(
label: richText ?? labelText!.toText10(weight: FontWeight.w500, letterSpacing: 0, color: textColor), materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
padding: EdgeInsets.zero, label: richText ?? labelText!.toText10(weight: FontWeight.w500, letterSpacing: 0, color: textColor),
backgroundColor: backgroundColor, padding: EdgeInsets.zero,
shape: shape ?? backgroundColor: backgroundColor,
SmoothRectangleBorder( shape: shape ??
borderRadius: BorderRadius.circular(8.r), SmoothRectangleBorder(
smoothness: 10, borderRadius: BorderRadius.circular(8.r),
side: BorderSide(color: AppColors.transparent, width: 1.5), smoothness: 10,
), side: BorderSide(color: AppColors.transparent, width: 1.5),
labelPadding: EdgeInsetsDirectional.only(start: 8.w, end: 8.w), ),
deleteIcon: deleteIcon?.isNotEmpty == true labelPadding: labelPadding ?? EdgeInsetsDirectional.only(start: 2.w, end: deleteIcon?.isNotEmpty == true ? 2.w : 8.w),
? Utils.buildSvgWithAssets(icon: deleteIcon!, width: iconS, height: iconS, iconColor: deleteIconHasColor ? deleteIconColor : null) deleteIcon: deleteIcon?.isNotEmpty == true
: null, ? InkWell(
onDeleted: deleteIcon?.isNotEmpty == true ? () {} : null, onTap: onDeleteTap,
), child: Utils.buildSvgWithAssets(
icon: deleteIcon!, width: iconS, height: iconS, iconColor: deleteIconHasColor ? deleteIconColor : null))
: null,
onDeleted: deleteIcon?.isNotEmpty == true ? () {} : null,
),
),
), ),
); );
} }

@ -117,7 +117,7 @@ void showCommonBottomSheetWithoutHeight(
bool hasBottomPadding = true, bool hasBottomPadding = true,
EdgeInsets? padding, EdgeInsets? padding,
Color backgroundColor = AppColors.bottomSheetBgColor, Color backgroundColor = AppColors.bottomSheetBgColor,
VoidCallback? onCloseClicked VoidCallback? onCloseClicked,
}) { }) {
showModalBottomSheet<String>( showModalBottomSheet<String>(
sheetAnimationStyle: AnimationStyle( sheetAnimationStyle: AnimationStyle(

@ -15,7 +15,7 @@ class ConfirmDialog extends StatelessWidget {
final VoidCallback? onTap; final VoidCallback? onTap;
final VoidCallback? onCloseTap; final VoidCallback? onCloseTap;
const ConfirmDialog({Key? key, this.title, required this.message, this.okTitle, this.onTap, this.onCloseTap}) : super(key: key); const ConfirmDialog({super.key, this.title, required this.message, this.okTitle, this.onTap, this.onCloseTap});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {

@ -0,0 +1,31 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
class FullScreenImageViewer extends StatelessWidget {
final bool isSvg;
final String path;
const FullScreenImageViewer({super.key, required this.isSvg, required this.path});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.black,
leading: IconButton(
icon: Icon(Icons.close, color: AppColors.whiteColor),
onPressed: () => Navigator.of(context).pop(),
),
),
backgroundColor: Colors.black,
body: Center(
child: InteractiveViewer(
child: isSvg
? SvgPicture.asset(path, width: double.infinity, fit: BoxFit.contain)
: Image.asset(width: double.infinity, path, fit: BoxFit.contain),
),
),
);
}
}

@ -0,0 +1,89 @@
import 'package:flutter/material.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/theme/colors.dart';
class RadioListTileWidget<T> extends StatelessWidget {
final T value;
final T? groupValue;
final String title;
final Widget? subtitleWidget;
final String? subtitle;
final ValueChanged<T?> onChanged;
final bool enabled;
const RadioListTileWidget({
super.key,
required this.value,
required this.groupValue,
required this.title,
this.subtitleWidget,
this.subtitle,
required this.onChanged,
this.enabled = true,
});
@override
Widget build(BuildContext context) {
final bool isSelected = value == groupValue;
return InkWell(
onTap: enabled ? () => onChanged(value) : null,
child: Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 24.h,
hasShadow: false,
),
margin: EdgeInsets.symmetric(horizontal: 16.w, vertical: 6.h),
padding: EdgeInsets.all(16.h),
child: Row(
children: [
Container(
width: 20.h,
height: 20.h,
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(
color: isSelected ? AppColors.primaryRedColor : const Color(0xff9E9E9E),
width: 2,
),
),
child: isSelected
? Center(
child: Container(
width: 10.h,
height: 10.h,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: AppColors.primaryRedColor,
),
),
)
: null,
),
SizedBox(width: 16.w),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
title.toText15(weight: FontWeight.w500, letterSpacing: -0.64),
if (subtitleWidget != null) ...[subtitleWidget!],
if (subtitle != null) ...[
SizedBox(height: 4.h),
title.toText13(
weight: FontWeight.w400,
color: AppColors.greyTextColor,
letterSpacing: -0.56,
),
],
],
),
),
],
),
),
);
}
}

@ -1,10 +1,10 @@
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/extensions/int_extensions.dart'; import 'package:hmg_patient_app_new/extensions/int_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:flutter/material.dart';
class MoviesShimmerWidget extends StatelessWidget { class CommonShimmerWidget extends StatelessWidget {
const MoviesShimmerWidget({super.key}); const CommonShimmerWidget({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
Loading…
Cancel
Save