Merge branch 'master' into faiz_dev

# Conflicts:
#	lib/features/location/location_view_model.dart
pull/113/head
faizatflutter 4 months ago
commit 5e437e42a9

@ -161,10 +161,11 @@ class ApiClientImp implements ApiClient {
// body['VersionID'] = ApiConsts.appVersionID.toString(); // body['VersionID'] = ApiConsts.appVersionID.toString();
if (!isExternal) { if (!isExternal) {
body['VersionID'] = ApiConsts.appVersionID; body['VersionID'] = ApiConsts.appVersionID.toString();
body['Channel'] = ApiConsts.appChannelId; body['Channel'] = ApiConsts.appChannelId.toString();
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();
@ -183,6 +184,9 @@ 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);

@ -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/';
@ -754,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/'; rcBaseUrl = 'https://rc.hmg.com/uat/';
break; break;
case AppEnvironmentTypeEnum.uat: case AppEnvironmentTypeEnum.uat:
baseUrl = "https://uat.hmgwebservices.com/"; baseUrl = "https://uat.hmgwebservices.com/";
@ -764,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/'; rcBaseUrl = 'https://rc.hmg.com/uat/';
break; break;
case AppEnvironmentTypeEnum.preProd: case AppEnvironmentTypeEnum.preProd:
baseUrl = "https://webservices.hmg.com/"; baseUrl = "https://webservices.hmg.com/";
@ -784,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/'; rcBaseUrl = 'https://rc.hmg.com/uat/';
break; break;
case AppEnvironmentTypeEnum.staging: case AppEnvironmentTypeEnum.staging:
baseUrl = "https://uat.hmgwebservices.com/"; baseUrl = "https://uat.hmgwebservices.com/";
@ -794,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/'; rcBaseUrl = 'https://rc.hmg.com/uat/';
break; break;
} }
} }
@ -852,7 +852,7 @@ class ApiConsts {
static final String addHHCOrder = 'api/HHC/add'; static final String addHHCOrder = 'api/HHC/add';
// ************ static values for Api **************** // ************ static values for Api ****************
static final double appVersionID = 20.0; static final double appVersionID = 19.3;
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";

@ -122,7 +122,13 @@ class AppDependencies {
getIt.registerLazySingleton<LabViewModel>(() => LabViewModel(labRepo: getIt(), errorHandlerService: getIt(), navigationService: getIt())); getIt.registerLazySingleton<LabViewModel>(() => LabViewModel(labRepo: getIt(), errorHandlerService: getIt(), navigationService: getIt()));
getIt.registerLazySingleton<RadiologyViewModel>(() => RadiologyViewModel(radiologyRepo: getIt(), errorHandlerService: getIt())); getIt.registerLazySingleton<RadiologyViewModel>(
() => RadiologyViewModel(
radiologyRepo: getIt(),
errorHandlerService: getIt(),
navigationService: getIt()
),
);
getIt.registerLazySingleton<PrescriptionsViewModel>(() => PrescriptionsViewModel(prescriptionsRepo: getIt(), errorHandlerService: getIt())); getIt.registerLazySingleton<PrescriptionsViewModel>(() => PrescriptionsViewModel(prescriptionsRepo: getIt(), errorHandlerService: getIt()));

@ -41,12 +41,14 @@ extension EmailValidator on String {
FontWeight? weight, FontWeight? weight,
bool isBold = false, bool isBold = false,
bool isUnderLine = false, bool isUnderLine = false,
bool isCenter = false,
int? maxlines, int? maxlines,
FontStyle? fontStyle, FontStyle? fontStyle,
TextOverflow? textOverflow, TextOverflow? textOverflow,
double letterSpacing = 0}) => double letterSpacing = 0}) =>
Text( Text(
this, this,
textAlign: isCenter ? TextAlign.center : null,
maxLines: maxlines, maxLines: maxlines,
overflow: textOverflow, overflow: textOverflow,
style: TextStyle( style: TextStyle(

@ -3,14 +3,18 @@ import 'package:hmg_patient_app_new/core/api/api_client.dart';
import 'package:hmg_patient_app_new/core/api_consts.dart'; import 'package:hmg_patient_app_new/core/api_consts.dart';
import 'package:hmg_patient_app_new/core/common_models/generic_api_model.dart'; import 'package:hmg_patient_app_new/core/common_models/generic_api_model.dart';
import 'package:hmg_patient_app_new/core/exceptions/api_failure.dart'; import 'package:hmg_patient_app_new/core/exceptions/api_failure.dart';
import 'package:hmg_patient_app_new/features/contact_us/models/req_models/request_insert_coc_item.dart';
import 'package:hmg_patient_app_new/features/contact_us/models/resp_models/get_hmg_locations.dart'; import 'package:hmg_patient_app_new/features/contact_us/models/resp_models/get_hmg_locations.dart';
import 'package:hmg_patient_app_new/features/contact_us/models/resp_models/get_patient_ic_projects.dart'; import 'package:hmg_patient_app_new/features/contact_us/models/resp_models/get_patient_ic_projects.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/services/logger_service.dart'; import 'package:hmg_patient_app_new/services/logger_service.dart';
abstract class ContactUsRepo { abstract class ContactUsRepo {
Future<Either<Failure, GenericApiModel<List<GetHMGLocationsModel>>>> getHMGLocations(); Future<Either<Failure, GenericApiModel<List<GetHMGLocationsModel>>>> getHMGLocations();
Future<Either<Failure, GenericApiModel<List<GetPatientICProjectsModel>>>> getLiveChatProjectsList(); Future<Either<Failure, GenericApiModel<List<GetPatientICProjectsModel>>>> getLiveChatProjectsList();
Future<Either<Failure, GenericApiModel<dynamic>>> insertCOCItem({required RequestInsertCOCItem requestInsertCOCItem, PatientAppointmentHistoryResponseModel? patientSelectedAppointment});
} }
class ContactUsRepoImp implements ContactUsRepo { class ContactUsRepoImp implements ContactUsRepo {
@ -72,13 +76,57 @@ class ContactUsRepoImp implements ContactUsRepo {
onSuccess: (response, statusCode, {messageStatus, errorMessage}) { onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try { try {
final list = response['List_PatientICProjects']; final list = response['List_PatientICProjects'];
final hmgLocations = list.map((item) => GetPatientICProjectsModel.fromJson(item as Map<String, dynamic>)).toList().cast<GetPatientICProjectsModel>(); final liveChatProjectsList = list.map((item) => GetPatientICProjectsModel.fromJson(item as Map<String, dynamic>)).toList().cast<GetPatientICProjectsModel>();
apiResponse = GenericApiModel<List<GetPatientICProjectsModel>>( apiResponse = GenericApiModel<List<GetPatientICProjectsModel>>(
messageStatus: messageStatus, messageStatus: messageStatus,
statusCode: statusCode, statusCode: statusCode,
errorMessage: null, errorMessage: null,
data: hmgLocations, data: liveChatProjectsList,
);
} catch (e) {
failure = DataParsingFailure(e.toString());
}
},
);
if (failure != null) return Left(failure!);
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
return Right(apiResponse!);
} catch (e) {
return Left(UnknownFailure(e.toString()));
}
}
@override
Future<Either<Failure, GenericApiModel<dynamic>>> insertCOCItem({required RequestInsertCOCItem requestInsertCOCItem, PatientAppointmentHistoryResponseModel? patientSelectedAppointment}) async {
final Map<String, dynamic> body = requestInsertCOCItem.toJson();
if (patientSelectedAppointment != null) {
body['AppoinmentNo'] = patientSelectedAppointment.appointmentNo;
body['AppointmentDate'] = patientSelectedAppointment.appointmentDate;
body['ClinicID'] = patientSelectedAppointment.clinicID;
body['ClinicName'] = patientSelectedAppointment.clinicName;
body['DoctorID'] = patientSelectedAppointment.doctorID;
body['DoctorName'] = patientSelectedAppointment.doctorNameObj;
body['ProjectName'] = patientSelectedAppointment.projectName;
}
try {
GenericApiModel<dynamic>? apiResponse;
Failure? failure;
await apiClient.post(
SEND_FEEDBACK,
body: body,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
apiResponse = GenericApiModel<dynamic>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: null,
data: response,
); );
} catch (e) { } catch (e) {
failure = DataParsingFailure(e.toString()); failure = DataParsingFailure(e.toString());

@ -1,7 +1,15 @@
import 'dart:io';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/api_consts.dart';
import 'package:hmg_patient_app_new/core/app_state.dart'; import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/features/contact_us/contact_us_repo.dart'; import 'package:hmg_patient_app_new/features/contact_us/contact_us_repo.dart';
import 'package:hmg_patient_app_new/features/contact_us/models/feedback_type.dart';
import 'package:hmg_patient_app_new/features/contact_us/models/req_models/request_insert_coc_item.dart';
import 'package:hmg_patient_app_new/features/contact_us/models/resp_models/get_hmg_locations.dart'; import 'package:hmg_patient_app_new/features/contact_us/models/resp_models/get_hmg_locations.dart';
import 'package:hmg_patient_app_new/features/contact_us/models/resp_models/get_patient_ic_projects.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/patient_appointment_history_response_model.dart';
import 'package:hmg_patient_app_new/services/error_handler_service.dart'; import 'package:hmg_patient_app_new/services/error_handler_service.dart';
class ContactUsViewModel extends ChangeNotifier { class ContactUsViewModel extends ChangeNotifier {
@ -11,17 +19,43 @@ class ContactUsViewModel extends ChangeNotifier {
bool isHMGLocationsListLoading = false; bool isHMGLocationsListLoading = false;
bool isHMGHospitalsListSelected = true; bool isHMGHospitalsListSelected = true;
bool isLiveChatProjectsListLoading = false;
bool isSendFeedbackTabSelected = true;
List<GetHMGLocationsModel> hmgHospitalsLocationsList = []; List<GetHMGLocationsModel> hmgHospitalsLocationsList = [];
List<GetHMGLocationsModel> hmgPharmacyLocationsList = []; List<GetHMGLocationsModel> hmgPharmacyLocationsList = [];
List<GetPatientICProjectsModel> liveChatProjectsList = [];
int selectedLiveChatProjectIndex = -1;
List<String> feedbackAttachmentList = [];
PatientAppointmentHistoryResponseModel? patientFeedbackSelectedAppointment;
List<FeedbackType> feedbackTypeList = [
FeedbackType(id: 1, nameEN: "Complaint for appointment", nameAR: 'شكوى على موعد'),
FeedbackType(id: 2, nameEN: "Complaint without appointment", nameAR: 'شكوى بدون موعد'),
FeedbackType(id: 3, nameEN: "Question", nameAR: 'سؤال'),
FeedbackType(id: 4, nameEN: "Appreciation", nameAR: 'تقدير'),
FeedbackType(id: 6, nameEN: "Suggestion", nameAR: 'إقتراح'),
FeedbackType(id: 5, nameEN: "Not classified", nameAR: 'غير محدد'),
];
FeedbackType selectedFeedbackType = FeedbackType(id: 5, nameEN: "Not classified", nameAR: 'غير محدد');
ContactUsViewModel({required this.contactUsRepo, required this.errorHandlerService, required this.appState}); ContactUsViewModel({required this.contactUsRepo, required this.errorHandlerService, required this.appState});
initContactUsViewModel() { initContactUsViewModel() {
isHMGLocationsListLoading = true; isHMGLocationsListLoading = true;
isHMGHospitalsListSelected = true; isHMGHospitalsListSelected = true;
isLiveChatProjectsListLoading = true;
hmgHospitalsLocationsList.clear(); hmgHospitalsLocationsList.clear();
hmgPharmacyLocationsList.clear(); hmgPharmacyLocationsList.clear();
liveChatProjectsList.clear();
feedbackAttachmentList.clear();
selectedFeedbackType = FeedbackType(id: 5, nameEN: "Not classified", nameAR: 'غير محدد');
setPatientFeedbackSelectedAppointment(null);
getHMGLocations(); getHMGLocations();
notifyListeners(); notifyListeners();
} }
@ -31,6 +65,36 @@ class ContactUsViewModel extends ChangeNotifier {
notifyListeners(); notifyListeners();
} }
setSelectedLiveChatProjectIndex(int index) {
selectedLiveChatProjectIndex = index;
notifyListeners();
}
setIsSendFeedbackTabSelected(bool isSelected) {
isSendFeedbackTabSelected = isSelected;
notifyListeners();
}
setSelectedFeedbackType(FeedbackType feedbackType) {
selectedFeedbackType = feedbackType;
notifyListeners();
}
addFeedbackAttachment(String attachmentPath) {
feedbackAttachmentList.add(attachmentPath);
notifyListeners();
}
removeFeedbackAttachment(String attachmentPath) {
feedbackAttachmentList.remove(attachmentPath);
notifyListeners();
}
setPatientFeedbackSelectedAppointment(PatientAppointmentHistoryResponseModel? appointment) {
patientFeedbackSelectedAppointment = appointment;
notifyListeners();
}
Future<void> getHMGLocations({Function(dynamic)? onSuccess, Function(String)? onError}) async { Future<void> getHMGLocations({Function(dynamic)? onSuccess, Function(String)? onError}) async {
isHMGLocationsListLoading = true; isHMGLocationsListLoading = true;
hmgHospitalsLocationsList.clear(); hmgHospitalsLocationsList.clear();
@ -62,4 +126,73 @@ class ContactUsViewModel extends ChangeNotifier {
}, },
); );
} }
Future<void> getLiveChatProjectsList({Function(dynamic)? onSuccess, Function(String)? onError}) async {
isLiveChatProjectsListLoading = true;
liveChatProjectsList.clear();
notifyListeners();
final result = await contactUsRepo.getLiveChatProjectsList();
result.fold(
(failure) async => await errorHandlerService.handleError(failure: failure),
(apiResponse) {
if (apiResponse.messageStatus == 2) {
// dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
} else if (apiResponse.messageStatus == 1) {
liveChatProjectsList = apiResponse.data!;
liveChatProjectsList.sort((a, b) => b.distanceInKilometers.compareTo(a.distanceInKilometers));
isLiveChatProjectsListLoading = false;
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse);
}
}
},
);
}
Future<void> insertCOCItem({required String subject, required String message, Function(dynamic)? onSuccess, Function(String)? onError}) async {
RequestInsertCOCItem requestInsertCOCItem = RequestInsertCOCItem();
requestInsertCOCItem.attachment = feedbackAttachmentList.isNotEmpty ? feedbackAttachmentList.first : "";
requestInsertCOCItem.title = subject;
requestInsertCOCItem.details = message;
requestInsertCOCItem.cOCTypeName = selectedFeedbackType.id.toString();
requestInsertCOCItem.formTypeID = selectedFeedbackType.id.toString();
requestInsertCOCItem.mobileNo = "966${Utils.getPhoneNumberWithoutZero(appState.getAuthenticatedUser()!.mobileNumber!)}";
requestInsertCOCItem.isUserLoggedIn = true;
requestInsertCOCItem.projectID = 0;
requestInsertCOCItem.patientName = "${appState.getAuthenticatedUser()!.firstName!} ${appState.getAuthenticatedUser()!.lastName!}";
requestInsertCOCItem.fileName = "";
requestInsertCOCItem.appVersion = ApiConsts.appVersionID;
requestInsertCOCItem.uILanguage = appState.isArabic() ? "ar" : "en"; //TODO Change it to be dynamic
requestInsertCOCItem.browserInfo = Platform.localHostname;
requestInsertCOCItem.deviceInfo = Platform.localHostname;
requestInsertCOCItem.resolution = "400x847";
requestInsertCOCItem.projectID = 0;
requestInsertCOCItem.tokenID = "C0c@@dm!n?T&A&A@Barcha202029582948";
requestInsertCOCItem.identificationNo = int.parse(appState.getAuthenticatedUser()!.patientIdentificationNo!);
if (BASE_URL.contains('uat')) {
requestInsertCOCItem.forDemo = true;
} else {
requestInsertCOCItem.forDemo = false;
}
final result = await contactUsRepo.insertCOCItem(requestInsertCOCItem: requestInsertCOCItem, patientSelectedAppointment: patientFeedbackSelectedAppointment);
result.fold(
(failure) async => await errorHandlerService.handleError(failure: failure),
(apiResponse) {
if (apiResponse.messageStatus == 2) {
// dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
} else if (apiResponse.messageStatus == 1) {
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse);
}
}
},
);
}
} }

@ -0,0 +1,11 @@
class FeedbackType {
final int id;
final String nameEN;
final String nameAR;
FeedbackType({
required this.id,
required this.nameEN,
required this.nameAR,
});
}

@ -0,0 +1,137 @@
class RequestInsertCOCItem {
bool? isUserLoggedIn;
String? mobileNo;
int? identificationNo;
int? patientID;
int? patientOutSA;
int? patientTypeID;
String? tokenID;
String? patientName;
int? projectID;
String? fileName;
String? attachment;
String? uILanguage;
String? browserInfo;
String? cOCTypeName;
String? formTypeID;
String? details;
String? deviceInfo;
String? deviceType;
String? title;
String? resolution;
double? versionID;
int? channel;
int? languageID;
String? iPAdress;
String? generalid;
String? sessionID;
bool? isDentalAllowedBackend;
int? deviceTypeID;
int? patientType;
double? appVersion;
bool? forDemo;
RequestInsertCOCItem(
{this.isUserLoggedIn,
this.mobileNo,
this.identificationNo,
this.patientID,
this.patientOutSA,
this.patientTypeID,
this.tokenID,
this.patientName,
this.projectID,
this.fileName,
this.attachment,
this.uILanguage,
this.browserInfo,
this.cOCTypeName,
this.formTypeID,
this.details,
this.deviceInfo,
this.deviceType,
this.title,
this.resolution,
this.versionID,
this.channel,
this.languageID,
this.iPAdress,
this.generalid,
this.sessionID,
this.isDentalAllowedBackend,
this.deviceTypeID,
this.patientType,
this.appVersion,
this.forDemo});
RequestInsertCOCItem.fromJson(Map<String, dynamic> json) {
isUserLoggedIn = json['IsUserLoggedIn'];
mobileNo = json['MobileNo'];
identificationNo = json['IdentificationNo'];
patientID = json['PatientID'];
patientOutSA = json['PatientOutSA'];
patientTypeID = json['PatientTypeID'];
tokenID = json['TokenID'];
patientName = json['PatientName'];
projectID = json['ProjectID'];
fileName = json['FileName'];
attachment = json['Attachment'];
uILanguage = json['UILanguage'];
browserInfo = json['BrowserInfo'];
cOCTypeName = json['COCTypeName'];
formTypeID = json['FormTypeID'];
details = json['Details'];
deviceInfo = json['DeviceInfo'];
deviceType = json['DeviceType'];
title = json['Title'];
resolution = json['Resolution'];
versionID = json['VersionID'];
channel = json['Channel'];
languageID = json['LanguageID'];
iPAdress = json['IPAdress'];
generalid = json['generalid'];
sessionID = json['SessionID'];
isDentalAllowedBackend = json['isDentalAllowedBackend'];
deviceTypeID = json['DeviceTypeID'];
patientType = json['PatientType'];
appVersion = json['AppVersion'];
forDemo = json['ForDemo'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['IsUserLoggedIn'] = this.isUserLoggedIn;
data['MobileNo'] = this.mobileNo;
data['IdentificationNo'] = this.identificationNo;
data['PatientID'] = this.patientID;
data['PatientOutSA'] = this.patientOutSA;
data['PatientTypeID'] = this.patientTypeID;
data['TokenID'] = this.tokenID;
data['PatientName'] = this.patientName;
data['ProjectID'] = this.projectID;
data['FileName'] = this.fileName;
data['Attachment'] = this.attachment;
data['UILanguage'] = this.uILanguage;
data['BrowserInfo'] = this.browserInfo;
data['COCTypeName'] = this.cOCTypeName;
data['FormTypeID'] = this.formTypeID;
data['Details'] = this.details;
data['DeviceInfo'] = this.deviceInfo;
data['DeviceType'] = this.deviceType;
data['Title'] = this.title;
data['Resolution'] = this.resolution;
data['VersionID'] = this.versionID;
data['Channel'] = this.channel;
data['LanguageID'] = this.languageID;
data['IPAdress'] = this.iPAdress;
data['generalid'] = this.generalid;
data['SessionID'] = this.sessionID;
data['isDentalAllowedBackend'] = this.isDentalAllowedBackend;
data['DeviceTypeID'] = this.deviceTypeID;
data['PatientType'] = this.patientType;
data['AppVersion'] = this.appVersion;
data['ForDemo'] = this.forDemo;
return data;
}
}

@ -20,6 +20,7 @@ import 'package:hmg_patient_app_new/features/emergency_services/models/OrderDisp
import 'package:hmg_patient_app_new/features/emergency_services/models/request_model/RRTRequestModel.dart'; import 'package:hmg_patient_app_new/features/emergency_services/models/request_model/RRTRequestModel.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/EROnlineCheckInPaymentDetailsResponse.dart';
import 'package:hmg_patient_app_new/features/emergency_services/models/resp_model/RRTServiceData.dart'; import 'package:hmg_patient_app_new/features/emergency_services/models/resp_model/RRTServiceData.dart';
import 'package:hmg_patient_app_new/features/location/location_view_model.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/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/emergency_services/models/AmbulanceCallingPlace.dart'; import 'package:hmg_patient_app_new/features/emergency_services/models/AmbulanceCallingPlace.dart';
@ -54,6 +55,7 @@ import 'package:hmg_patient_app_new/theme/colors.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/expandable_bottom_sheet/model/BottomSheetType.dart'; import 'package:hmg_patient_app_new/widgets/expandable_bottom_sheet/model/BottomSheetType.dart';
import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart'; import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart';
import 'package:hmg_patient_app_new/widgets/map/map_utility_screen.dart';
import 'package:hmg_patient_app_new/widgets/order_tracking/order_tracking_state.dart'; import 'package:hmg_patient_app_new/widgets/order_tracking/order_tracking_state.dart';
import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart'; import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
import 'package:huawei_map/huawei_map.dart' as HMSCameraServices; import 'package:huawei_map/huawei_map.dart' as HMSCameraServices;
@ -1047,12 +1049,26 @@ class EmergencyServicesViewModel extends ChangeNotifier {
placeValueInController(); placeValueInController();
locationUtils!.getLocation( locationUtils!.getLocation(
isShowConfirmDialog: true, isShowConfirmDialog: true,
onSuccess: (position) { onSuccess: (position) async {
updateBottomSheetState(BottomSheetType.FIXED); updateBottomSheetState(BottomSheetType.FIXED);
navServices.push( bool result = await navServices.push(
CustomPageRoute( CustomPageRoute(
page: RrtMapScreen(), direction: AxisDirection.down), page: MapUtilityScreen(
confirmButtonString: "Submit Request ".needTranslation,
titleString: "Select Location",
subTitleString: "Please select the location".needTranslation,
isGmsAvailable: appState.isGMSAvailable,
),
direction: AxisDirection.down),
); );
if(result){
LocationViewModel locationViewModel = getIt.get<LocationViewModel>();
GeocodeResponse? response = locationViewModel.geocodeResponse;
PlaceDetails? placeDetails = locationViewModel.placeDetails;
PlacePrediction? placePrediction = locationViewModel.selectedPrediction;
submitRRTRequest(response?.results.first, placeDetails, placePrediction);
}
}); });
} else{ } else{
dialogService.showErrorBottomSheet( dialogService.showErrorBottomSheet(

@ -21,6 +21,7 @@ abstract class LabRepo {
Future<Either<Failure, GenericApiModel<String>>> getLabResultReportPDF({required PatientLabOrdersResponseModel labOrder}); Future<Either<Failure, GenericApiModel<String>>> getLabResultReportPDF({required PatientLabOrdersResponseModel labOrder});
Future<Either<Failure, GenericApiModel<dynamic>>> getLabResultsByAppointmentNo({required num appointmentNo, required num projectID, required num clinicID});
} }
class LabRepoImp implements LabRepo { class LabRepoImp implements LabRepo {
@ -135,6 +136,7 @@ class LabRepoImp implements LabRepo {
request['SetupID'] = laborder!.setupID; request['SetupID'] = laborder!.setupID;
request['ProjectID'] = laborder.projectID; request['ProjectID'] = laborder.projectID;
request['ClinicID'] = laborder.clinicID; request['ClinicID'] = laborder.clinicID;
request['InvoiceType'] = laborder.invoiceType ?? "";
try { try {
GenericApiModel<List<LabResult>>? apiResponse; GenericApiModel<List<LabResult>>? apiResponse;
Failure? failure; Failure? failure;
@ -184,6 +186,7 @@ class LabRepoImp implements LabRepo {
request['SetupID'] = laborder!.setupID; request['SetupID'] = laborder!.setupID;
request['ProjectID'] = laborder.projectID; request['ProjectID'] = laborder.projectID;
request['ClinicID'] = laborder.clinicID; request['ClinicID'] = laborder.clinicID;
request['InvoiceType'] = laborder.invoiceType ?? "";
try { try {
GenericApiModel<List<PatientLabSpecialResult>>? apiResponse; GenericApiModel<List<PatientLabSpecialResult>>? apiResponse;
Failure? failure; Failure? failure;
@ -278,4 +281,41 @@ class LabRepoImp implements LabRepo {
return Left(UnknownFailure(e.toString())); return Left(UnknownFailure(e.toString()));
} }
} }
@override
Future<Either<Failure, GenericApiModel>> getLabResultsByAppointmentNo({required num appointmentNo, required num projectID, required num clinicID}) async {
Map<String, dynamic> request = {};
request['AppointmentNo'] = appointmentNo;
request['ProjectID'] = projectID;
request['ClinicID'] = clinicID;
try {
GenericApiModel<dynamic>? apiResponse;
Failure? failure;
await apiClient.post(
GET_PATIENT_LAB_ORDERS_BY_APPOINTMENT,
body: request,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
apiResponse = GenericApiModel<dynamic>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: null,
data: response['ListLabResultsByAppNo'],
);
} catch (e) {
failure = DataParsingFailure(e.toString());
}
},
);
if (failure != null) return Left(failure!);
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
return Right(apiResponse!);
} catch (e) {
return Left(UnknownFailure(e.toString()));
}
}
} }

@ -9,11 +9,13 @@ import 'package:hmg_patient_app_new/core/utils/utils.dart' show Utils;
import 'package:hmg_patient_app_new/features/lab/lab_repo.dart'; import 'package:hmg_patient_app_new/features/lab/lab_repo.dart';
import 'package:hmg_patient_app_new/features/lab/models/resp_models/lab_result.dart'; import 'package:hmg_patient_app_new/features/lab/models/resp_models/lab_result.dart';
import 'package:hmg_patient_app_new/features/lab/models/resp_models/patient_lab_orders_response_model.dart'; import 'package:hmg_patient_app_new/features/lab/models/resp_models/patient_lab_orders_response_model.dart';
import 'package:hmg_patient_app_new/presentation/lab/lab_result_via_clinic/LabResultByClinic.dart';
import 'package:hmg_patient_app_new/presentation/lab/lab_results/lab_result_details.dart'; import 'package:hmg_patient_app_new/presentation/lab/lab_results/lab_result_details.dart';
import 'package:hmg_patient_app_new/services/error_handler_service.dart'; import 'package:hmg_patient_app_new/services/error_handler_service.dart';
import 'package:hmg_patient_app_new/services/navigation_service.dart'; import 'package:hmg_patient_app_new/services/navigation_service.dart';
import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/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:intl/intl.dart' show DateFormat; import 'package:intl/intl.dart' show DateFormat;
import 'package:logger/logger.dart'; import 'package:logger/logger.dart';
@ -75,14 +77,14 @@ class LabViewModel extends ChangeNotifier {
required this.navigationService}); required this.navigationService});
initLabProvider() { initLabProvider() {
if (isLabNeedToLoad) { // if (isLabNeedToLoad) {
patientLabOrders.clear(); patientLabOrders.clear();
filteredLabOrders.clear(); filteredLabOrders.clear();
labOrderTests.clear(); labOrderTests.clear();
isLabOrdersLoading = true; isLabOrdersLoading = true;
isLabResultsLoading = true; isLabResultsLoading = true;
getPatientLabOrders(); getPatientLabOrders();
} // }
notifyListeners(); notifyListeners();
} }
@ -92,7 +94,7 @@ class LabViewModel extends ChangeNotifier {
} }
Future<void> getPatientLabOrders({Function(dynamic)? onSuccess, Function(String)? onError}) async { Future<void> getPatientLabOrders({Function(dynamic)? onSuccess, Function(String)? onError}) async {
if (!isLabNeedToLoad) return; // if (!isLabNeedToLoad) return;
isLabOrdersLoading = true; isLabOrdersLoading = true;
patientLabOrders.clear(); patientLabOrders.clear();
@ -158,7 +160,7 @@ class LabViewModel extends ChangeNotifier {
filterSuggestions() { filterSuggestions() {
final List<String> labels = patientLabOrders final List<String> labels = patientLabOrders
.expand((order) => order.testDetails!) .expand((order) => order.testDetails ?? [])
.map((detail) => detail.description) .map((detail) => detail.description)
.whereType<String>() .whereType<String>()
.toList(); .toList();
@ -198,6 +200,69 @@ class LabViewModel extends ChangeNotifier {
} }
} }
Future<void> getLabResultsByAppointmentNo(
{required num appointmentNo,
required num projectID,
required num clinicID,
required int doctorID,
required String clinicName,
required String doctorName,
required String projectName,
required String appointmentDate,
Function(dynamic)? onSuccess,
Function(String)? onError}) async {
bool isVidaPlus = Utils.isVidaPlusProject(projectID.toInt());
final result = await labRepo.getLabResultsByAppointmentNo(appointmentNo: appointmentNo, projectID: projectID, clinicID: clinicID);
result.fold(
(failure) async {
// await errorHandlerService.handleError(failure: failure);
if (onError != null) {
onError(failure.message);
}
},
(apiResponse) {
if (apiResponse.messageStatus == 2) {
if (onError != null) {
onError(apiResponse.errorMessage!);
}
} else if (apiResponse.messageStatus == 1) {
if (apiResponse.data != null && apiResponse.data!.isNotEmpty) {
PatientLabOrdersResponseModel labOrder = PatientLabOrdersResponseModel();
labOrder.invoiceNoVP = isVidaPlus ? apiResponse.data[0]['InvoiceNo'].toString() : "0";
labOrder.invoiceNo = isVidaPlus ? "0" : apiResponse.data[0]['InvoiceNo'].toString();
labOrder.orderNo = apiResponse.data[0]['OrderNo'].toString();
labOrder.invoiceType = apiResponse.data[0]['InvoiceType'].toString();
labOrder.setupID = apiResponse.data[0]['SetupID'].toString();
labOrder.projectID = projectID.toString();
labOrder.clinicID = clinicID.toInt();
labOrder.doctorID = doctorID;
labOrder.clinicDescription = clinicName;
labOrder.doctorName = doctorName;
labOrder.projectName = projectName;
labOrder.orderDate = appointmentDate;
currentlySelectedPatientOrder = labOrder;
getPatientLabResultByHospital(labOrder);
getPatientSpecialResult(labOrder);
if (onSuccess != null) {
onSuccess(apiResponse);
}
navigationService.push(
CustomPageRoute(
page: LabResultByClinic(labOrder: labOrder),
),
);
} else {}
notifyListeners();
}
},
);
}
Future<void> getPatientLabResultByHospital( Future<void> getPatientLabResultByHospital(
PatientLabOrdersResponseModel laborder) async { PatientLabOrdersResponseModel laborder) async {
isLabResultByHospitalLoading = true; isLabResultByHospitalLoading = true;

@ -2,12 +2,17 @@ import 'dart:async';
import 'package:flutter/foundation.dart' show ChangeNotifier; import 'package:flutter/foundation.dart' show ChangeNotifier;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart' as GMSMapServices; import 'package:google_maps_flutter_platform_interface/src/types/camera.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/features/location/GeocodeResponse.dart'; import 'package:hmg_patient_app_new/features/location/GeocodeResponse.dart';
import 'package:hmg_patient_app_new/features/location/PlaceDetails.dart'; import 'package:hmg_patient_app_new/features/location/PlaceDetails.dart';
import 'package:hmg_patient_app_new/features/location/location_repo.dart'; import 'package:hmg_patient_app_new/features/location/location_repo.dart';
import 'package:hmg_patient_app_new/services/error_handler_service.dart'; import 'package:hmg_patient_app_new/services/error_handler_service.dart';
import 'package:huawei_map/huawei_map.dart' as HMSCameraServices; import 'package:huawei_map/huawei_map.dart' as HMSCameraServices;
import 'package:google_maps_flutter/google_maps_flutter.dart' as GMSMapServices;
import 'PlacePrediction.dart'; import 'PlacePrediction.dart';
@ -15,26 +20,48 @@ class LocationViewModel extends ChangeNotifier {
final LocationRepo locationRepo; final LocationRepo locationRepo;
final ErrorHandlerService errorHandlerService; final ErrorHandlerService errorHandlerService;
LocationViewModel({required this.locationRepo, required this.errorHandlerService}); LocationViewModel({required this.locationRepo, required this.errorHandlerService}){
placeValueInController();
}
List<PlacePrediction> predictions = []; List<PlacePrediction> predictions = [];
PlacePrediction? selectedPrediction; PlacePrediction? selectedPrediction;
bool isPredictionLoading = false; bool isPredictionLoading = false;
GeocodeResponse? geocodeResponse; GeocodeResponse? geocodeResponse;
PlaceDetails? placeDetails; PlaceDetails? placeDetails;
Location? mapCapturedLocation; Location? mapCapturedLocation;
Completer<GMSMapServices.GoogleMapController>? gmsController;
Completer<HMSCameraServices.HuaweiMapController>? hmsController;
HMSCameraServices.CameraPosition getHMSLocation() {
return HMSCameraServices.CameraPosition(target: HMSCameraServices.LatLng(getIt<AppState>().userLat, getIt<AppState>().userLong), zoom: 18);
}
GMSMapServices.CameraPosition getGMSLocation() {
return GMSMapServices.CameraPosition(target: GMSMapServices.LatLng(getIt<AppState>().userLat, getIt<AppState>().userLong), zoom: 18);
}
void placeValueInController() async{
if (await getIt<AppState>().isGMSAvailable) {
gmsController = Completer<GMSMapServices.GoogleMapController>();
} else {
hmsController = Completer<HMSCameraServices.HuaweiMapController>();
}
}
FutureOr<void> getPlacesPrediction(String input) async { FutureOr<void> getPlacesPrediction(String input) async {
predictions = []; predictions = [];
isPredictionLoading = true; isPredictionLoading= true;
final result = await locationRepo.getPlacePredictionsAsInput(input); final result = await locationRepo.getPlacePredictionsAsInput(input);
result.fold( result.fold(
(failure) { (failure) {
errorHandlerService.handleError(failure: failure); errorHandlerService.handleError(failure: failure);
}, },
(apiModel) { (apiModel) {
predictions = apiModel.data ?? []; predictions = apiModel.data??[];
}, },
); );
isPredictionLoading = false; isPredictionLoading = false;
@ -72,20 +99,21 @@ class LocationViewModel extends ChangeNotifier {
handleGMSMapCameraMoved(GMSMapServices.CameraPosition value) { handleGMSMapCameraMoved(GMSMapServices.CameraPosition value) {
mapCapturedLocation = Location(lat: value.target.latitude, lng: value.target.longitude); mapCapturedLocation = Location(lat: value.target.latitude, lng: value.target.longitude);
} }
handleHMSMapCameraMoved(HMSCameraServices.CameraPosition value) { handleHMSMapCameraMoved(HMSCameraServices.CameraPosition value) {
mapCapturedLocation = Location(lat: value.target.lat, lng: value.target.lng); mapCapturedLocation = Location(lat: value.target.lat, lng: value.target.lng);
} }
handleOnCameraIdle() { handleOnCameraIdle(){
if (mapCapturedLocation != null) { if(mapCapturedLocation != null) {
getPlaceEncodedData(mapCapturedLocation!.lat, mapCapturedLocation!.lng); getPlaceEncodedData(mapCapturedLocation!.lat, mapCapturedLocation!.lng);
} }
} }
void updateSearchQuery(String? value) { void updateSearchQuery(String? value) {
if (value == null || value.isEmpty) { if(value == null || value.isEmpty){
predictions = []; predictions = [];
return; return;
} }
@ -95,16 +123,50 @@ class LocationViewModel extends ChangeNotifier {
void flushSearchPredictions() { void flushSearchPredictions() {
predictions = []; predictions = [];
mapCapturedLocation = null; mapCapturedLocation= null;
placeDetails = null; placeDetails= null;
geocodeResponse = null; geocodeResponse= null;
selectedPrediction = null; selectedPrediction= null;
notifyListeners(); notifyListeners();
} }
FutureOr<void> selectPlacePrediction(PlacePrediction placePrediction) async { FutureOr<void> selectPlacePrediction(PlacePrediction placePrediction) async{
selectedPrediction = placePrediction; selectedPrediction= placePrediction;
await getPlaceDetails(placePrediction.placeID); await getPlaceDetails(placePrediction.placeID);
} }
}
void moveToCurrentLocation() {
moveController(Location(lat: getIt<AppState>().userLat, lng: getIt<AppState>().userLong));
}
void moveController(Location location) {
print("moving to location");
print("gmsController is null or not $gmsController");
if (getIt<AppState>().isGMSAvailable) {
gmsController?.future.then((controller) {
controller.animateCamera(
GMSMapServices.CameraUpdate.newCameraPosition(
GMSMapServices.CameraPosition(
target: GMSMapServices.LatLng(location.lat, location.lng),
zoom: 18,
),
),
);
});
} else {
print("hmsController is null or not $hmsController");
hmsController?.future.then((controller) {
controller.animateCamera(
HMSCameraServices.CameraUpdate.newCameraPosition(
HMSCameraServices.CameraPosition(
target: HMSCameraServices.LatLng(location.lat, location.lng),
zoom: 18,
),
),
);
});
}
}
}

@ -15,6 +15,8 @@ abstract class RadiologyRepo {
Future<Either<Failure, GenericApiModel<String>>> getRadiologyReportPDF( Future<Either<Failure, GenericApiModel<String>>> getRadiologyReportPDF(
{required PatientRadiologyResponseModel patientRadiologyResponseModel, required AuthenticatedUser authenticatedUser}); {required PatientRadiologyResponseModel patientRadiologyResponseModel, required AuthenticatedUser authenticatedUser});
Future<Either<Failure, GenericApiModel<List<PatientRadiologyResponseModel>>>> getPatientRadiologyOrderByAppointment({required num appointmentNo, required num projectID});
} }
class RadiologyRepoImp implements RadiologyRepo { class RadiologyRepoImp implements RadiologyRepo {
@ -168,4 +170,50 @@ class RadiologyRepoImp implements RadiologyRepo {
return Left(UnknownFailure(e.toString())); return Left(UnknownFailure(e.toString()));
} }
} }
@override
Future<Either<Failure, GenericApiModel<List<PatientRadiologyResponseModel>>>> getPatientRadiologyOrderByAppointment({required num appointmentNo, required num projectID}) async {
Map<String, dynamic> mapDevice = {
"AppointmentNo": appointmentNo,
"ProjectID": projectID,
};
try {
GenericApiModel<List<PatientRadiologyResponseModel>>? apiResponse;
Failure? failure;
await apiClient.post(
GET_PATIENT_ORDERS,
body: mapDevice,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
final radOrders;
try {
if (response['FinalRadiologyList'] != null && response['FinalRadiologyList'].length != 0) {
final list = response['FinalRadiologyList'];
radOrders = list.map((item) => PatientRadiologyResponseModel.fromJson(item as Map<String, dynamic>)).toList().cast<PatientRadiologyResponseModel>();
} else {
final list = response['FinalRadiologyListAPI'];
radOrders = list.map((item) => PatientRadiologyResponseModel.fromJson(item as Map<String, dynamic>)).toList().cast<PatientRadiologyResponseModel>();
}
apiResponse = GenericApiModel<List<PatientRadiologyResponseModel>>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: null,
data: radOrders,
);
} catch (e) {
failure = DataParsingFailure(e.toString());
}
},
);
if (failure != null) return Left(failure!);
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
return Right(apiResponse!);
} catch (e) {
return Left(UnknownFailure(e.toString()));
}
}
} }

@ -1,7 +1,11 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/features/authentication/models/resp_models/authenticated_user_resp_model.dart'; import 'package:hmg_patient_app_new/features/authentication/models/resp_models/authenticated_user_resp_model.dart';
import 'package:hmg_patient_app_new/features/radiology/radiology_repo.dart'; import 'package:hmg_patient_app_new/features/radiology/radiology_repo.dart';
import 'package:hmg_patient_app_new/presentation/radiology/radiology_result_page.dart';
import 'package:hmg_patient_app_new/services/error_handler_service.dart'; import 'package:hmg_patient_app_new/services/error_handler_service.dart';
import 'package:hmg_patient_app_new/services/navigation_service.dart';
import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
import 'models/resp_models/patient_radiology_response_model.dart'; import 'models/resp_models/patient_radiology_response_model.dart';
@ -11,6 +15,7 @@ class RadiologyViewModel extends ChangeNotifier {
RadiologyRepo radiologyRepo; RadiologyRepo radiologyRepo;
ErrorHandlerService errorHandlerService; ErrorHandlerService errorHandlerService;
NavigationService navigationService;
List<PatientRadiologyResponseModel> patientRadiologyOrders = []; List<PatientRadiologyResponseModel> patientRadiologyOrders = [];
List<PatientRadiologyResponseModel> filteredRadiologyOrders = []; List<PatientRadiologyResponseModel> filteredRadiologyOrders = [];
@ -22,7 +27,9 @@ class RadiologyViewModel extends ChangeNotifier {
List<String> get radiologySuggestions => _radiologySuggestionsList; List<String> get radiologySuggestions => _radiologySuggestionsList;
RadiologyViewModel({required this.radiologyRepo, required this.errorHandlerService}); late PatientRadiologyResponseModel patientRadiologyOrderByAppointment;
RadiologyViewModel({required this.radiologyRepo, required this.errorHandlerService, required this.navigationService});
initRadiologyViewModel() { initRadiologyViewModel() {
patientRadiologyOrders.clear(); patientRadiologyOrders.clear();
@ -57,6 +64,35 @@ class RadiologyViewModel extends ChangeNotifier {
); );
} }
Future<void> getPatientRadiologyOrdersByAppointment({required num appointmentNo, required num projectID, Function(dynamic)? onSuccess, Function(String)? onError}) async {
final result = await radiologyRepo.getPatientRadiologyOrderByAppointment(appointmentNo: appointmentNo, projectID: projectID);
result.fold(
(failure) async => await errorHandlerService.handleError(failure: failure),
(apiResponse) {
if (apiResponse.messageStatus == 2) {
// dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
} else if (apiResponse.messageStatus == 1) {
notifyListeners();
if (apiResponse.data!.isNotEmpty) {
if (onSuccess != null) {
onSuccess(apiResponse);
}
navigationService.push(
CustomPageRoute(
page: RadiologyResultPage(patientRadiologyResponseModel: apiResponse.data!.first),
),
);
} else {
if (onError != null) {
onError("No Radiology Orders Found".needTranslation);
}
}
}
},
);
}
Future<void> getRadiologyImage( Future<void> getRadiologyImage(
{required PatientRadiologyResponseModel patientRadiologyResponseModel, Function(dynamic)? onSuccess, Function(String)? onError}) async { {required PatientRadiologyResponseModel patientRadiologyResponseModel, Function(dynamic)? onSuccess, Function(String)? onError}) async {
final result = await radiologyRepo.getRadiologyImage(patientRadiologyResponseModel: patientRadiologyResponseModel); final result = await radiologyRepo.getRadiologyImage(patientRadiologyResponseModel: patientRadiologyResponseModel);
@ -107,23 +143,20 @@ class RadiologyViewModel extends ChangeNotifier {
}, },
); );
} }
filterSuggestions() { filterSuggestions() {
final List<String> labels = patientRadiologyOrders final List<String> labels = patientRadiologyOrders.map((detail) => detail.description).whereType<String>().toList();
.map((detail) => detail.description)
.whereType<String>()
.toList();
_radiologySuggestionsList = labels.toSet().toList(); _radiologySuggestionsList = labels.toSet().toList();
notifyListeners(); notifyListeners();
} }
filterRadiologyReports(String query) { filterRadiologyReports(String query) {
if (query.isEmpty) { if (query.isEmpty) {
patientRadiologyOrders =tempRadiologyOrders;// reset patientRadiologyOrders = tempRadiologyOrders; // reset
} else { } else {
filteredRadiologyOrders = filteredRadiologyOrders = filteredRadiologyOrders.where((desc) => desc.description!.toLowerCase().contains(query.toLowerCase())).toList();
filteredRadiologyOrders.where((desc) => desc.description!.toLowerCase().contains(query.toLowerCase())).toList();
patientRadiologyOrders = filteredRadiologyOrders; patientRadiologyOrders = filteredRadiologyOrders;
} }
notifyListeners(); notifyListeners();
} }
} }

@ -11,26 +11,32 @@ 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/book_appointments/models/resp_models/doctors_list_response_model.dart'; import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/doctors_list_response_model.dart';
import 'package:hmg_patient_app_new/features/contact_us/contact_us_view_model.dart';
import 'package:hmg_patient_app_new/features/lab/lab_view_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_view_model.dart'; import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/utils/appointment_type.dart'; import 'package:hmg_patient_app_new/features/my_appointments/utils/appointment_type.dart';
import 'package:hmg_patient_app_new/features/prescriptions/models/resp_models/patient_prescriptions_response_model.dart'; import 'package:hmg_patient_app_new/features/prescriptions/models/resp_models/patient_prescriptions_response_model.dart';
import 'package:hmg_patient_app_new/features/prescriptions/prescriptions_view_model.dart'; import 'package:hmg_patient_app_new/features/prescriptions/prescriptions_view_model.dart';
import 'package:hmg_patient_app_new/features/radiology/radiology_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/appointment_payment_page.dart'; import 'package:hmg_patient_app_new/presentation/appointments/appointment_payment_page.dart';
import 'package:hmg_patient_app_new/presentation/appointments/widgets/appointment_checkin_bottom_sheet.dart'; import 'package:hmg_patient_app_new/presentation/appointments/widgets/appointment_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/appointments/widgets/ask_doctor_request_type_select.dart'; import 'package:hmg_patient_app_new/presentation/appointments/widgets/ask_doctor_request_type_select.dart';
import 'package:hmg_patient_app_new/presentation/book_appointment/widgets/appointment_calendar.dart'; import 'package:hmg_patient_app_new/presentation/book_appointment/widgets/appointment_calendar.dart';
import 'package:hmg_patient_app_new/presentation/contact_us/feedback_page.dart';
import 'package:hmg_patient_app_new/presentation/lab/lab_orders_page.dart';
import 'package:hmg_patient_app_new/presentation/medical_file/widgets/lab_rad_card.dart';
import 'package:hmg_patient_app_new/presentation/prescriptions/prescription_detail_page.dart'; import 'package:hmg_patient_app_new/presentation/prescriptions/prescription_detail_page.dart';
import 'package:hmg_patient_app_new/presentation/prescriptions/prescriptions_list_page.dart'; import 'package:hmg_patient_app_new/presentation/prescriptions/prescriptions_list_page.dart';
import 'package:hmg_patient_app_new/presentation/radiology/radiology_orders_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';
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/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';
@ -50,14 +56,17 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
late MyAppointmentsViewModel myAppointmentsViewModel; late MyAppointmentsViewModel myAppointmentsViewModel;
late PrescriptionsViewModel prescriptionsViewModel; late PrescriptionsViewModel prescriptionsViewModel;
late BookAppointmentsViewModel bookAppointmentsViewModel; late BookAppointmentsViewModel bookAppointmentsViewModel;
late ContactUsViewModel contactUsViewModel;
late LabViewModel labViewModel;
late RadiologyViewModel radiologyViewModel;
@override @override
void initState() { void initState() {
scheduleMicrotask(() { scheduleMicrotask(() {
if (AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel)) { // if (AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel)) {
prescriptionsViewModel.setPrescriptionsDetailsLoading(); // prescriptionsViewModel.setPrescriptionsDetailsLoading();
prescriptionsViewModel.getPrescriptionDetails(getPrescriptionRequestModel()); // prescriptionsViewModel.getPrescriptionDetails(getPrescriptionRequestModel());
} // }
}); });
super.initState(); super.initState();
} }
@ -68,6 +77,9 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
myAppointmentsViewModel = Provider.of<MyAppointmentsViewModel>(context, listen: false); myAppointmentsViewModel = Provider.of<MyAppointmentsViewModel>(context, listen: false);
prescriptionsViewModel = Provider.of<PrescriptionsViewModel>(context, listen: false); prescriptionsViewModel = Provider.of<PrescriptionsViewModel>(context, listen: false);
bookAppointmentsViewModel = Provider.of<BookAppointmentsViewModel>(context, listen: false); bookAppointmentsViewModel = Provider.of<BookAppointmentsViewModel>(context, listen: false);
contactUsViewModel = Provider.of<ContactUsViewModel>(context, listen: false);
labViewModel = Provider.of<LabViewModel>(context, listen: false);
radiologyViewModel = Provider.of<RadiologyViewModel>(context, listen: false);
return Scaffold( return Scaffold(
backgroundColor: AppColors.bgScaffoldColor, backgroundColor: AppColors.bgScaffoldColor,
body: Column( body: Column(
@ -75,7 +87,16 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
Expanded( Expanded(
child: CollapsingListView( child: CollapsingListView(
title: "Appointment Details".needTranslation, title: "Appointment Details".needTranslation,
report: AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel) ? () {} : null, report: AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel)
? () {
contactUsViewModel.setPatientFeedbackSelectedAppointment(widget.patientAppointmentHistoryResponseModel);
Navigator.of(context).push(
CustomPageRoute(
page: FeedbackPage(),
),
);
}
: null,
child: SingleChildScrollView( child: SingleChildScrollView(
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
@ -263,219 +284,345 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
SizedBox(height: 16.h), SizedBox(height: 16.h),
], ],
) )
: Column( // : SizedBox.shrink()
crossAxisAlignment: CrossAxisAlignment.start, : GridView(
children: [ gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
"Lab & Radiology".needTranslation.toText18(isBold: true), crossAxisCount: 3,
SizedBox(height: 16.h), crossAxisSpacing: 16.h,
GridView( mainAxisSpacing: 16.w,
padding: EdgeInsets.zero, mainAxisExtent: 115.h,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( ),
crossAxisCount: isTablet || isFoldable ? 3 : 2, physics: NeverScrollableScrollPhysics(),
crossAxisSpacing: 13.w, padding: EdgeInsets.zero,
mainAxisSpacing: 13.w, shrinkWrap: true,
), children: [
physics: NeverScrollableScrollPhysics(), MedicalFileCard(
shrinkWrap: true, label: LocaleKeys.labResults.tr(context: context),
children: [ textColor: AppColors.blackColor,
MedicalFileCard( backgroundColor: AppColors.whiteColor,
label: LocaleKeys.labResults.tr(context: context), svgIcon: AppAssets.lab_result_icon,
textColor: AppColors.blackColor, isLargeText: true,
backgroundColor: AppColors.whiteColor, iconSize: 36.w,
svgIcon: AppAssets.lab_result_icon, ).onPress(() async {
iconSize: 40.w, LoaderBottomSheet.showLoader(loadingText: "Fetching Lab Results...".needTranslation);
isLargeText: true, await labViewModel.getLabResultsByAppointmentNo(
), appointmentNo: widget.patientAppointmentHistoryResponseModel.appointmentNo,
MedicalFileCard( projectID: widget.patientAppointmentHistoryResponseModel.projectID,
label: "Radiology Results".needTranslation, clinicID: widget.patientAppointmentHistoryResponseModel.clinicID,
textColor: AppColors.blackColor, doctorID: widget.patientAppointmentHistoryResponseModel.doctorID,
backgroundColor: AppColors.whiteColor, doctorName: widget.patientAppointmentHistoryResponseModel.doctorNameObj!,
svgIcon: AppAssets.radiology_icon, clinicName: widget.patientAppointmentHistoryResponseModel.clinicName!,
iconSize: 40.w, projectName: widget.patientAppointmentHistoryResponseModel.projectName!,
isLargeText: true, appointmentDate: widget.patientAppointmentHistoryResponseModel.appointmentDate!,
), onSuccess: (val) {
MedicalFileCard( LoaderBottomSheet.hideLoader();
label: LocaleKeys.labResults.tr(context: context), },
textColor: AppColors.blackColor, onError: (err) {
backgroundColor: AppColors.whiteColor, LoaderBottomSheet.hideLoader();
svgIcon: AppAssets.lab_result_icon, showCommonBottomSheetWithoutHeight(
iconSize: 40.w, context,
isLargeText: true, child: Utils.getErrorWidget(loadingText: err),
), callBackFunc: () {},
MedicalFileCard( isFullScreen: false,
label: "Radiology Results".needTranslation, isCloseButtonVisible: true,
textColor: AppColors.blackColor, );
backgroundColor: AppColors.whiteColor, },
svgIcon: AppAssets.radiology_icon, );
iconSize: 40.w, }),
isLargeText: true, MedicalFileCard(
), label: "${LocaleKeys.radiology.tr(context: context)} ${LocaleKeys.radiologySubtitle.tr(context: context)}",
], textColor: AppColors.blackColor,
), backgroundColor: AppColors.whiteColor,
SizedBox(height: 16.h), svgIcon: AppAssets.allergy_info_icon,
LocaleKeys.prescriptions.tr().toText18(isBold: true), isLargeText: true,
SizedBox(height: 16.h), iconSize: 36.w,
Consumer<PrescriptionsViewModel>(builder: (context, prescriptionVM, child) { ).onPress(() async {
return prescriptionVM.isPrescriptionsDetailsLoading LoaderBottomSheet.showLoader(loadingText: "Fetching Radiology Results...".needTranslation);
? const CommonShimmerWidget() await radiologyViewModel.getPatientRadiologyOrdersByAppointment(
: Container( appointmentNo: widget.patientAppointmentHistoryResponseModel.appointmentNo,
decoration: RoundedRectangleBorder().toSmoothCornerDecoration( projectID: widget.patientAppointmentHistoryResponseModel.projectID,
color: Colors.white, onSuccess: (val) {
borderRadius: 20.r, LoaderBottomSheet.hideLoader();
), },
padding: EdgeInsets.all(16.w), onError: (err) {
child: Column( LoaderBottomSheet.hideLoader();
children: [ showCommonBottomSheetWithoutHeight(
ListView.separated( context,
itemCount: prescriptionVM.prescriptionDetailsList.length, child: Utils.getErrorWidget(loadingText: err),
shrinkWrap: true, callBackFunc: () {},
padding: EdgeInsets.only(right: 8.w), isFullScreen: false,
physics: NeverScrollableScrollPhysics(), isCloseButtonVisible: true,
itemBuilder: (context, index) { );
return AnimationConfiguration.staggeredList( },
position: index, );
duration: const Duration(milliseconds: 500), }),
child: SlideAnimation( MedicalFileCard(
verticalOffset: 100.0, label: LocaleKeys.prescriptions.tr(context: context),
child: FadeInAnimation( textColor: AppColors.blackColor,
child: Row( backgroundColor: AppColors.whiteColor,
children: [ svgIcon: AppAssets.prescription_item_icon,
Utils.buildSvgWithAssets( isLargeText: true,
icon: AppAssets.prescription_item_icon, iconSize: 36.w,
width: 40.h, ).onPress(() async {
height: 40.h, LoaderBottomSheet.showLoader(loadingText: "Fetching Appointment Prescriptions...".needTranslation);
), await prescriptionsViewModel.getPrescriptionDetails(
SizedBox(width: 8.h), getPrescriptionRequestModel(),
Row( onSuccess: (val) {
mainAxisSize: MainAxisSize.max, LoaderBottomSheet.hideLoader();
children: [ if (val.data.isNotEmpty) {
Column( PatientPrescriptionsResponseModel patientPrescriptionsResponseModel = PatientPrescriptionsResponseModel(
children: [ doctorImageURL: widget.patientAppointmentHistoryResponseModel.doctorImageURL,
prescriptionVM.prescriptionDetailsList[index].itemDescription! doctorName: widget.patientAppointmentHistoryResponseModel.doctorNameObj,
.toText12(isBold: true, maxLine: 1), appointmentDate: widget.patientAppointmentHistoryResponseModel.appointmentDate,
"Prescribed By: ${widget.patientAppointmentHistoryResponseModel.doctorTitle} ${widget.patientAppointmentHistoryResponseModel.doctorNameObj}" clinicDescription: widget.patientAppointmentHistoryResponseModel.clinicName,
.needTranslation decimalDoctorRate: widget.patientAppointmentHistoryResponseModel.decimalDoctorRate,
.toText10( name: widget.patientAppointmentHistoryResponseModel.projectName,
weight: FontWeight.w500, isHomeMedicineDeliverySupported: false,
color: AppColors.greyTextColor, projectID: widget.patientAppointmentHistoryResponseModel.projectID,
letterSpacing: -0.4), clinicID: widget.patientAppointmentHistoryResponseModel.clinicID,
], doctorID: widget.patientAppointmentHistoryResponseModel.doctorID,
), setupID: widget.patientAppointmentHistoryResponseModel.setupID,
SizedBox(width: 68.w), );
Transform.flip( Navigator.of(context).push(
flipX: appState.isArabic(), CustomPageRoute(
child: Utils.buildSvgWithAssets( page: PrescriptionDetailPage(isFromAppointments: true, prescriptionsResponseModel: patientPrescriptionsResponseModel),
icon: AppAssets.forward_arrow_icon,
iconColor: AppColors.blackColor,
width: 18.w,
height: 13.h,
fit: BoxFit.contain,
),
),
],
),
],
),
),
),
);
},
separatorBuilder: (BuildContext cxt, int index) => SizedBox(height: 16.h),
).onPress(() {
prescriptionVM.setPrescriptionsDetailsLoading();
Navigator.of(context).push(
CustomPageRoute(
page: PrescriptionDetailPage(prescriptionsResponseModel: getPrescriptionRequestModel()),
),
);
}),
SizedBox(height: 16.h),
const Divider(color: AppColors.dividerColor),
SizedBox(height: 16.h),
Wrap(
runSpacing: 6.w,
children: [
// Expanded(
// child: CustomButton(
// text: widget.prescriptionsResponseModel.isHomeMedicineDeliverySupported! ? LocaleKeys.resendOrder.tr(context: context) : LocaleKeys.prescriptionDeliveryError.tr(context: context),
// onPressed: () {},
// backgroundColor: AppColors.secondaryLightRedColor,
// borderColor: AppColors.secondaryLightRedColor,
// textColor: AppColors.primaryRedColor,
// fontSize: 14,
// fontWeight: FontWeight.w500,
// borderRadius: 12.h,
// height: 40.h,
// icon: AppAssets.appointment_calendar_icon,
// iconColor: AppColors.primaryRedColor,
// iconSize: 16.h,
// ),
// ),
// SizedBox(width: 16.h),
Expanded(
child: CustomButton(
text: "Refill & Delivery".needTranslation,
onPressed: () {
Navigator.of(context)
.push(
CustomPageRoute(
page: PrescriptionsListPage(),
),
)
.then((val) {
prescriptionsViewModel.setPrescriptionsDetailsLoading();
prescriptionsViewModel.getPrescriptionDetails(getPrescriptionRequestModel());
});
},
backgroundColor: AppColors.secondaryLightRedColor,
borderColor: AppColors.secondaryLightRedColor,
textColor: AppColors.primaryRedColor,
fontSize: 14.f,
fontWeight: FontWeight.w500,
borderRadius: 12.r,
height: 40.h,
icon: AppAssets.requests,
iconColor: AppColors.primaryRedColor,
iconSize: 16.h,
),
),
SizedBox(width: 16.w),
Expanded(
child: CustomButton(
text: "All Prescriptions".needTranslation,
onPressed: () {
Navigator.of(context)
.push(
CustomPageRoute(
page: PrescriptionsListPage(),
),
)
.then((val) {
prescriptionsViewModel.setPrescriptionsDetailsLoading();
prescriptionsViewModel.getPrescriptionDetails(getPrescriptionRequestModel());
});
},
backgroundColor: AppColors.secondaryLightRedColor,
borderColor: AppColors.secondaryLightRedColor,
textColor: AppColors.primaryRedColor,
fontSize: 14.f,
fontWeight: FontWeight.w500,
borderRadius: 12.r,
height: 40.h,
icon: AppAssets.requests,
iconColor: AppColors.primaryRedColor,
iconSize: 16.h,
),
),
],
),
],
), ),
); );
}), } else {
], showCommonBottomSheetWithoutHeight(
), context,
child: Utils.getErrorWidget(loadingText: "You don't have any prescriptions for this appointment.".needTranslation),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
}
},
onError: (err) {
LoaderBottomSheet.hideLoader();
showCommonBottomSheetWithoutHeight(
context,
child: Utils.getErrorWidget(loadingText: err),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
},
);
// Navigator.of(context).push(
// CustomPageRoute(
// page: VaccineListPage(),
// ),
// );
}),
],
),
// Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// "Lab & Radiology".needTranslation.toText18(isBold: true),
// SizedBox(height: 16.h),
// Row(
// children: [
// Expanded(
// child: LabRadCard(
// icon: AppAssets.lab_result_icon,
// labelText: LocaleKeys.labResults.tr(context: context),
// // labOrderTests: ["Complete blood count", "Creatinine", "Blood Sugar"],
// // labOrderTests: labViewModel.isLabOrdersLoading ? [] : labViewModel.labOrderTests,
// labOrderTests: [],
// // isLoading: labViewModel.isLabOrdersLoading,
// isLoading: false,
// ).onPress(() {
// Navigator.of(context).push(
// CustomPageRoute(
// page: LabOrdersPage(),
// ),
// );
// }),
// ),
// SizedBox(width: 16.h),
// Expanded(
// child: LabRadCard(
// icon: AppAssets.radiology_icon,
// labelText: LocaleKeys.radiology.tr(context: context),
// // labOrderTests: ["Chest X-ray", "Abdominal Ultrasound", "Dental X-ray"],
// labOrderTests: [],
// isLoading: false,
// ).onPress(() {
// Navigator.of(context).push(
// CustomPageRoute(
// page: RadiologyOrdersPage(),
// ),
// );
// }),
// ),
// ],
// ),
// SizedBox(height: 16.h),
// LocaleKeys.prescriptions.tr().toText18(isBold: true),
// SizedBox(height: 16.h),
// Consumer<PrescriptionsViewModel>(builder: (context, prescriptionVM, child) {
// return prescriptionVM.isPrescriptionsDetailsLoading
// ? const MoviesShimmerWidget()
// : Container(
// decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
// color: Colors.white,
// borderRadius: 20.r,
// ),
// padding: EdgeInsets.all(16.w),
// child: Column(
// children: [
// // ListView.separated(
// // itemCount: prescriptionVM.prescriptionDetailsList.length,
// // shrinkWrap: true,
// // padding: EdgeInsets.only(right: 8.w),
// // physics: NeverScrollableScrollPhysics(),
// // itemBuilder: (context, index) {
// // return AnimationConfiguration.staggeredList(
// // position: index,
// // duration: const Duration(milliseconds: 500),
// // child: SlideAnimation(
// // verticalOffset: 100.0,
// // child: FadeInAnimation(
// // child: Row(
// // children: [
// // Utils.buildSvgWithAssets(
// // icon: AppAssets.prescription_item_icon,
// // width: 40.h,
// // height: 40.h,
// // ),
// // SizedBox(width: 8.h),
// // Row(
// // mainAxisSize: MainAxisSize.max,
// // children: [
// // Column(
// // children: [
// // prescriptionVM.prescriptionDetailsList[index].itemDescription!
// // .toText12(isBold: true, maxLine: 1),
// // "Prescribed By: ${widget.patientAppointmentHistoryResponseModel.doctorTitle} ${widget.patientAppointmentHistoryResponseModel.doctorNameObj}"
// // .needTranslation
// // .toText10(
// // weight: FontWeight.w500,
// // color: AppColors.greyTextColor,
// // letterSpacing: -0.4),
// // ],
// // ),
// // SizedBox(width: 68.w),
// // Transform.flip(
// // flipX: appState.isArabic(),
// // child: Utils.buildSvgWithAssets(
// // icon: AppAssets.forward_arrow_icon,
// // iconColor: AppColors.blackColor,
// // width: 18.w,
// // height: 13.h,
// // fit: BoxFit.contain,
// // ),
// // ),
// // ],
// // ),
// // ],
// // ),
// // ),
// // ),
// // );
// // },
// // separatorBuilder: (BuildContext cxt, int index) => SizedBox(height: 16.h),
// // ).onPress(() {
// // prescriptionVM.setPrescriptionsDetailsLoading();
// // Navigator.of(context).push(
// // CustomPageRoute(
// // page: PrescriptionDetailPage(prescriptionsResponseModel: getPrescriptionRequestModel()),
// // ),
// // );
// // }),
// SizedBox(height: 16.h),
// const Divider(color: AppColors.dividerColor),
// SizedBox(height: 16.h),
// // Wrap(
// // runSpacing: 6.w,
// // children: [
// // // Expanded(
// // // child: CustomButton(
// // // text: widget.prescriptionsResponseModel.isHomeMedicineDeliverySupported! ? LocaleKeys.resendOrder.tr(context: context) : LocaleKeys.prescriptionDeliveryError.tr(context: context),
// // // onPressed: () {},
// // // backgroundColor: AppColors.secondaryLightRedColor,
// // // borderColor: AppColors.secondaryLightRedColor,
// // // textColor: AppColors.primaryRedColor,
// // // fontSize: 14,
// // // fontWeight: FontWeight.w500,
// // // borderRadius: 12.h,
// // // height: 40.h,
// // // icon: AppAssets.appointment_calendar_icon,
// // // iconColor: AppColors.primaryRedColor,
// // // iconSize: 16.h,
// // // ),
// // // ),
// // // SizedBox(width: 16.h),
// // Expanded(
// // child: CustomButton(
// // text: "Refill & Delivery".needTranslation,
// // onPressed: () {
// // Navigator.of(context)
// // .push(
// // CustomPageRoute(
// // page: PrescriptionsListPage(),
// // ),
// // )
// // .then((val) {
// // prescriptionsViewModel.setPrescriptionsDetailsLoading();
// // prescriptionsViewModel.getPrescriptionDetails(getPrescriptionRequestModel());
// // });
// // },
// // backgroundColor: AppColors.secondaryLightRedColor,
// // borderColor: AppColors.secondaryLightRedColor,
// // textColor: AppColors.primaryRedColor,
// // fontSize: 14.f,
// // fontWeight: FontWeight.w500,
// // borderRadius: 12.r,
// // height: 40.h,
// // icon: AppAssets.requests,
// // iconColor: AppColors.primaryRedColor,
// // iconSize: 16.h,
// // ),
// // ),
// //
// // SizedBox(width: 16.w),
// // Expanded(
// // child: CustomButton(
// // text: "All Prescriptions".needTranslation,
// // onPressed: () {
// // Navigator.of(context)
// // .push(
// // CustomPageRoute(
// // page: PrescriptionsListPage(),
// // ),
// // )
// // .then((val) {
// // prescriptionsViewModel.setPrescriptionsDetailsLoading();
// // prescriptionsViewModel.getPrescriptionDetails(getPrescriptionRequestModel());
// // });
// // },
// // backgroundColor: AppColors.secondaryLightRedColor,
// // borderColor: AppColors.secondaryLightRedColor,
// // textColor: AppColors.primaryRedColor,
// // fontSize: 14.f,
// // fontWeight: FontWeight.w500,
// // borderRadius: 12.r,
// // height: 40.h,
// // icon: AppAssets.requests,
// // iconColor: AppColors.primaryRedColor,
// // iconSize: 16.h,
// // ),
// // ),
// // ],
// // ),
// ],
// ),
// );
// }),
// ],
// ),
], ],
).paddingAll(24.w), ).paddingAll(24.w),
), ),

@ -10,6 +10,7 @@ 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/book_appointments/models/resp_models/doctors_list_response_model.dart'; import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/doctors_list_response_model.dart';
import 'package:hmg_patient_app_new/features/contact_us/contact_us_view_model.dart';
import 'package:hmg_patient_app_new/features/medical_file/medical_file_view_model.dart'; import 'package:hmg_patient_app_new/features/medical_file/medical_file_view_model.dart';
import 'package:hmg_patient_app_new/features/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_view_model.dart'; import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart';
@ -33,7 +34,9 @@ class AppointmentCard extends StatelessWidget {
final bool isFromHomePage; final bool isFromHomePage;
final bool isFromMedicalReport; final bool isFromMedicalReport;
final bool isForEyeMeasurements; final bool isForEyeMeasurements;
final bool isForFeedback;
final MedicalFileViewModel? medicalFileViewModel; final MedicalFileViewModel? medicalFileViewModel;
final ContactUsViewModel? contactUsViewModel;
final BookAppointmentsViewModel bookAppointmentsViewModel; final BookAppointmentsViewModel bookAppointmentsViewModel;
const AppointmentCard({ const AppointmentCard({
@ -45,7 +48,9 @@ class AppointmentCard extends StatelessWidget {
this.isFromHomePage = false, this.isFromHomePage = false,
this.isFromMedicalReport = false, this.isFromMedicalReport = false,
this.isForEyeMeasurements = false, this.isForEyeMeasurements = false,
this.isForFeedback = false,
this.medicalFileViewModel, this.medicalFileViewModel,
this.contactUsViewModel,
}); });
@override @override
@ -179,7 +184,11 @@ class AppointmentCard extends StatelessWidget {
return CustomButton( return CustomButton(
text: 'Select appointment'.needTranslation, text: 'Select appointment'.needTranslation,
onPressed: () { onPressed: () {
medicalFileViewModel!.setSelectedMedicalReportAppointment(patientAppointmentHistoryResponseModel); if (isForFeedback) {
contactUsViewModel!.setPatientFeedbackSelectedAppointment(patientAppointmentHistoryResponseModel);
} else {
medicalFileViewModel!.setSelectedMedicalReportAppointment(patientAppointmentHistoryResponseModel);
}
Navigator.pop(context, false); Navigator.pop(context, false);
}, },
backgroundColor: AppColors.secondaryLightRedColor, backgroundColor: AppColors.secondaryLightRedColor,
@ -313,6 +322,7 @@ class AppointmentCard extends StatelessWidget {
} }
void _goToDetails(BuildContext context) { void _goToDetails(BuildContext context) {
if (isFromMedicalReport) return;
if (isForEyeMeasurements) { if (isForEyeMeasurements) {
Navigator.of(context).push( Navigator.of(context).push(
CustomPageRoute( CustomPageRoute(

@ -1,11 +1,14 @@
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get_it/get_it.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart'; import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/app_export.dart'; import 'package:hmg_patient_app_new/core/app_export.dart';
import 'package:hmg_patient_app_new/core/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/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/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/services/cache_service.dart';
import 'package:hmg_patient_app_new/services/navigation_service.dart'; import 'package:hmg_patient_app_new/services/navigation_service.dart';
import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/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';
@ -21,18 +24,13 @@ class QuickLogin extends StatefulWidget {
} }
class QuickLoginState extends State<QuickLogin> { class QuickLoginState extends State<QuickLogin> {
final CacheService cacheService = GetIt.instance<CacheService>();
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
NavigationService navigationService = getIt.get<NavigationService>(); NavigationService navigationService = getIt.get<NavigationService>();
return Container( return Padding(
decoration: const BoxDecoration( padding: EdgeInsets.all(24.h),
color: Colors.white,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(24),
topRight: Radius.circular(24),
),
),
padding: const EdgeInsets.all(24),
child: Column( child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
@ -104,11 +102,12 @@ class QuickLoginState extends State<QuickLogin> {
borderColor: Color(0xffED1C2B), borderColor: Color(0xffED1C2B),
textColor: Colors.white, textColor: Colors.white,
icon: AppAssets.apple_finder, icon: AppAssets.apple_finder,
height: 56.h,
)), )),
], ],
), ),
SizedBox( SizedBox(
height: 16, height: 16.h,
), ),
Row( Row(
mainAxisAlignment: MainAxisAlignment.end, mainAxisAlignment: MainAxisAlignment.end,
@ -117,11 +116,13 @@ class QuickLoginState extends State<QuickLogin> {
child: CustomButton( child: CustomButton(
text: LocaleKeys.notNow.tr(), text: LocaleKeys.notNow.tr(),
onPressed: () { onPressed: () {
cacheService.saveBool(key: CacheConst.quickLoginEnabled, value: false);
Navigator.pop(context, "true"); Navigator.pop(context, "true");
}, },
backgroundColor: Color(0xffFEE9EA), backgroundColor: Color(0xffFEE9EA),
borderColor: Color(0xffFEE9EA), borderColor: Color(0xffFEE9EA),
textColor: Colors.red, textColor: Colors.red,
height: 56.h,
// icon: "assets/images/svg/apple-finder.svg", // icon: "assets/images/svg/apple-finder.svg",
)), )),
], ],

@ -37,7 +37,7 @@ class ClinicItem extends StatelessWidget {
Transform.flip( Transform.flip(
flipX: isArabic, flipX: isArabic,
child: Utils.buildSvgWithAssets( child: Utils.buildSvgWithAssets(
icon: AppAssets.forward_arrow_icon, icon: AppAssets.forward_arrow_icon_small,
width: 15.h, width: 15.h,
height: 15.h, height: 15.h,
fit: BoxFit.contain, fit: BoxFit.contain,

@ -140,8 +140,9 @@ class _AppointmentCalendarState extends State<AppointmentCalendar> {
}, },
), ),
), ),
SizedBox(height: 10.h),
Transform.translate( Transform.translate(
offset: const Offset(0.0, -20.0), offset: const Offset(0.0, -10.0),
child: selectedDateDisplay.toText16(weight: FontWeight.w500), child: selectedDateDisplay.toText16(weight: FontWeight.w500),
), ),
//TODO: Add Next Day Span here //TODO: Add Next Day Span here

@ -65,25 +65,28 @@ class DoctorCard extends StatelessWidget {
.toString() .toString()
.toText16(isBold: true, maxlines: 1), .toText16(isBold: true, maxlines: 1),
).toShimmer2(isShow: isLoading), ).toShimmer2(isShow: isLoading),
],
),
SizedBox(height: 2.h),
Row(
children: [
(isLoading
? "Consultant Cardiologist"
: doctorsListResponseModel.speciality!.isNotEmpty
? doctorsListResponseModel.speciality!.first
: "")
.toString()
.toText12(fontWeight: FontWeight.w500, color: AppColors.greyTextColor, maxLine: 1)
.toShimmer2(isShow: isLoading),
SizedBox(width: 6.w),
Image.network( Image.network(
isLoading isLoading ? "https://hmgwebservices.com/Images/flag/SYR.png" : doctorsListResponseModel.nationalityFlagURL ?? "https://hmgwebservices.com/Images/flag/SYR.png",
? "https://hmgwebservices.com/Images/flag/SYR.png"
: doctorsListResponseModel.nationalityFlagURL ?? "https://hmgwebservices.com/Images/flag/SYR.png",
width: 20.h, width: 20.h,
height: 15.h, height: 15.h,
fit: BoxFit.fill, fit: BoxFit.fill,
).toShimmer2(isShow: isLoading), ).toShimmer2(isShow: isLoading),
], ],
), ),
SizedBox(height: 2.h),
(isLoading
? "Consultant Cardiologist"
: doctorsListResponseModel.speciality!.isNotEmpty
? doctorsListResponseModel.speciality!.first
: "")
.toString()
.toText12(fontWeight: FontWeight.w500, color: AppColors.greyTextColor, maxLine: 1)
.toShimmer2(isShow: isLoading),
], ],
), ),
), ),

@ -9,7 +9,9 @@ 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/contact_us/contact_us_view_model.dart'; import 'package:hmg_patient_app_new/features/contact_us/contact_us_view_model.dart';
import 'package:hmg_patient_app_new/features/contact_us/models/feedback_type.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/contact_us/feedback_page.dart';
import 'package:hmg_patient_app_new/presentation/contact_us/find_us_page.dart'; import 'package:hmg_patient_app_new/presentation/contact_us/find_us_page.dart';
import 'package:hmg_patient_app_new/presentation/contact_us/live_chat_page.dart'; import 'package:hmg_patient_app_new/presentation/contact_us/live_chat_page.dart';
import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/theme/colors.dart';
@ -51,7 +53,17 @@ class ContactUs extends StatelessWidget {
AppAssets.checkin_location_icon, AppAssets.checkin_location_icon,
LocaleKeys.feedback.tr(), LocaleKeys.feedback.tr(),
"Provide your feedback on our services".needTranslation, "Provide your feedback on our services".needTranslation,
), ).onPress(() {
contactUsViewModel.setSelectedFeedbackType(
FeedbackType(id: 5, nameEN: "Not classified", nameAR: 'غير محدد'),
);
Navigator.pop(context);
Navigator.of(context).push(
CustomPageRoute(
page: FeedbackPage(),
),
);
}),
SizedBox(height: 16.h), SizedBox(height: 16.h),
checkInOptionCard( checkInOptionCard(
AppAssets.checkin_location_icon, AppAssets.checkin_location_icon,
@ -59,6 +71,7 @@ class ContactUs extends StatelessWidget {
"Live chat option with HMG".needTranslation, "Live chat option with HMG".needTranslation,
).onPress(() { ).onPress(() {
locationUtils.getCurrentLocation(onSuccess: (value) { locationUtils.getCurrentLocation(onSuccess: (value) {
contactUsViewModel.getLiveChatProjectsList();
Navigator.pop(context); Navigator.pop(context);
Navigator.of(context).push( Navigator.of(context).push(
CustomPageRoute( CustomPageRoute(

@ -0,0 +1,400 @@
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/date_util.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_view_model.dart';
import 'package:hmg_patient_app_new/features/contact_us/contact_us_view_model.dart';
import 'package:hmg_patient_app_new/features/contact_us/models/feedback_type.dart';
import 'package:hmg_patient_app_new/features/medical_file/medical_file_view_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/appointments/widgets/appointment_card.dart';
import 'package:hmg_patient_app_new/presentation/contact_us/widgets/feedback_appointment_selection.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart';
import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart';
import 'package:hmg_patient_app_new/widgets/custom_tab_bar.dart';
import 'package:hmg_patient_app_new/widgets/image_picker.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/routes/custom_page_route.dart';
import 'package:provider/provider.dart';
class FeedbackPage extends StatelessWidget {
FeedbackPage({super.key});
late ContactUsViewModel contactUsViewModel;
late MedicalFileViewModel medicalFileViewModel;
final TextEditingController subjectTextController = TextEditingController();
final TextEditingController messageTextController = TextEditingController();
@override
Widget build(BuildContext context) {
contactUsViewModel = Provider.of<ContactUsViewModel>(context, listen: false);
medicalFileViewModel = Provider.of<MedicalFileViewModel>(context, listen: false);
return Scaffold(
backgroundColor: AppColors.bgScaffoldColor,
body: Consumer<ContactUsViewModel>(builder: (context, contactUsVM, child) {
return Column(
children: [
Expanded(
child: CollapsingListView(
isLeading: Navigator.canPop(context),
title: LocaleKeys.feedback.tr(),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 16.h),
CustomTabBar(
activeTextColor: AppColors.primaryRedColor,
activeBackgroundColor: AppColors.primaryRedColor.withValues(alpha: .1),
tabs: [
CustomTabBarModel(null, "Send".needTranslation),
CustomTabBarModel(null, "Status".needTranslation),
],
onTabChange: (index) {
contactUsViewModel.setIsSendFeedbackTabSelected(index == 0);
},
).paddingSymmetrical(24.h, 0.h),
getSelectedTabWidget(context).paddingSymmetrical(24.h, 16.w),
],
),
),
),
Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
customBorder: BorderRadius.only(
topLeft: Radius.circular(24.h),
topRight: Radius.circular(24.h),
),
hasShadow: true,
),
child: CustomButton(
text: LocaleKeys.submit.tr(context: context),
onPressed: () async {
if (subjectTextController.text.isEmpty) {
showCommonBottomSheetWithoutHeight(
context,
child: Utils.getErrorWidget(loadingText: LocaleKeys.emptySubject.tr(context: context)),
);
return;
}
if (messageTextController.text.isEmpty) {
showCommonBottomSheetWithoutHeight(
context,
child: Utils.getErrorWidget(loadingText: LocaleKeys.emptyMessage.tr(context: context)),
);
return;
}
LoaderBottomSheet.showLoader(loadingText: "Sending Feedback...".needTranslation);
contactUsViewModel.insertCOCItem(
subject: subjectTextController.text,
message: messageTextController.text,
onSuccess: (val) {
LoaderBottomSheet.hideLoader();
subjectTextController.clear();
messageTextController.clear();
contactUsViewModel.setPatientFeedbackSelectedAppointment(null);
showCommonBottomSheetWithoutHeight(context, child: Utils.getSuccessWidget(loadingText: LocaleKeys.success.tr(context: context)), callBackFunc: () {
Navigator.pop(context);
});
},
onError: (err) {
LoaderBottomSheet.hideLoader();
showCommonBottomSheetWithoutHeight(
context,
child: Utils.getSuccessWidget(loadingText: err),
);
});
},
backgroundColor: AppColors.primaryRedColor,
borderColor: AppColors.primaryRedColor,
textColor: AppColors.whiteColor,
fontSize: 16,
fontWeight: FontWeight.w500,
borderRadius: 12,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 50.h,
icon: AppAssets.feedback,
iconColor: AppColors.whiteColor,
iconSize: 20.h,
).paddingSymmetrical(24.h, 24.h),
),
],
);
}),
);
}
Widget getSelectedTabWidget(BuildContext context) {
if (contactUsViewModel.isSendFeedbackTabSelected) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
LocaleKeys.likeToHear.tr().toText14(weight: FontWeight.w500),
SizedBox(height: 16.h),
Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 16.r,
hasShadow: false,
),
child: Padding(
padding: EdgeInsets.all(16.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Utils.buildSvgWithAssets(icon: AppAssets.ask_doctor_icon, width: 24.w, height: 24.h, iconColor: AppColors.greyTextColor),
SizedBox(width: 12.w),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
LocaleKeys.feedbackType.tr().toText16(color: AppColors.textColor, weight: FontWeight.w500),
(getIt.get<AppState>().isArabic() ? contactUsViewModel.selectedFeedbackType.nameAR : contactUsViewModel.selectedFeedbackType.nameEN)
.toText14(color: AppColors.greyTextColor, weight: FontWeight.w500),
],
),
],
),
Utils.buildSvgWithAssets(icon: AppAssets.arrow_down, width: 25.h, height: 25.h),
],
).onPress(() {
showCommonBottomSheetWithoutHeight(context,
title: "Select Feedback Type".needTranslation,
child: Container(
width: double.infinity,
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24),
child: ListView.builder(
itemCount: contactUsViewModel.feedbackTypeList.length,
physics: NeverScrollableScrollPhysics(),
padding: EdgeInsets.only(top: 8, bottom: 8),
shrinkWrap: true,
itemBuilder: (innerContext, index) {
return Theme(
data: Theme.of(context).copyWith(
listTileTheme: ListTileThemeData(horizontalTitleGap: 4),
),
child: RadioListTile<FeedbackType>(
title: Text(
getIt.get<AppState>().isArabic() ? contactUsViewModel.feedbackTypeList[index].nameAR : contactUsViewModel.feedbackTypeList[index].nameEN,
style: TextStyle(
fontSize: 16.h,
fontWeight: FontWeight.w500,
),
),
value: contactUsViewModel.feedbackTypeList[index],
fillColor: WidgetStateProperty.resolveWith((states) {
if (states.contains(WidgetState.selected)) {
return AppColors.primaryRedColor;
}
return Color(0xffEEEEEE);
}),
contentPadding: EdgeInsets.only(left: 12.h, right: 12.h),
groupValue: contactUsViewModel.selectedFeedbackType,
onChanged: (FeedbackType? newValue) async {
Navigator.pop(context);
contactUsViewModel.setSelectedFeedbackType(newValue!);
if (contactUsViewModel.selectedFeedbackType.id == 1) {
LoaderBottomSheet.showLoader(loadingText: "Loading appointments list...".needTranslation);
await medicalFileViewModel.getPatientMedicalReportAppointmentsList(onSuccess: (val) async {
LoaderBottomSheet.hideLoader();
bool? value = await Navigator.of(context).push(
CustomPageRoute(
page: FeedbackAppointmentSelection(),
fullScreenDialog: true,
direction: AxisDirection.down,
),
);
if (value != null) {
// showConfirmRequestMedicalReportBottomSheet();
}
}, onError: (err) {
LoaderBottomSheet.hideLoader();
showCommonBottomSheetWithoutHeight(
context,
child: Utils.getErrorWidget(loadingText: "You do not have any appointments to submit a feedback.".needTranslation),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
});
} else {
contactUsViewModel.setPatientFeedbackSelectedAppointment(null);
}
},
),
);
},
),
),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true);
}),
]),
),
),
if (contactUsViewModel.patientFeedbackSelectedAppointment != null) ...[
SizedBox(height: 16.h),
"Selected Appointment:".needTranslation.toText16(isBold: true),
SizedBox(height: 8.h),
Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 20.r,
hasShadow: false,
),
padding: EdgeInsets.all(16.h),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Image.network(
contactUsViewModel.patientFeedbackSelectedAppointment!.doctorImageURL!,
width: 63.h,
height: 63.h,
fit: BoxFit.cover,
).circle(100).toShimmer2(isShow: false),
SizedBox(width: 16.h),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
(contactUsViewModel.patientFeedbackSelectedAppointment!.doctorNameObj!).toText16(isBold: true, maxlines: 1).toShimmer2(isShow: false),
SizedBox(height: 8.h),
Wrap(
direction: Axis.horizontal,
spacing: 3.h,
runSpacing: 4.h,
children: [
AppCustomChipWidget(labelText: contactUsViewModel.patientFeedbackSelectedAppointment!.clinicName!).toShimmer2(isShow: false),
AppCustomChipWidget(labelText: contactUsViewModel.patientFeedbackSelectedAppointment!.projectName!).toShimmer2(isShow: false),
AppCustomChipWidget(
icon: AppAssets.appointment_calendar_icon,
labelText: DateUtil.formatDateToDate(DateUtil.convertStringToDate(contactUsViewModel.patientFeedbackSelectedAppointment!.appointmentDate), false),
).toShimmer2(isShow: false),
],
),
],
),
),
],
),
),
],
SizedBox(height: 16.h),
TextInputWidget(
labelText: "Subject".needTranslation,
hintText: "Enter subject here".needTranslation,
controller: subjectTextController,
isEnable: true,
prefix: null,
autoFocus: false,
isBorderAllowed: false,
keyboardType: TextInputType.text,
padding: EdgeInsets.symmetric(
vertical: ResponsiveExtension(10).h,
horizontal: ResponsiveExtension(15).h,
),
),
SizedBox(height: 16.h),
TextInputWidget(
labelText: "Message".needTranslation,
hintText: "Enter message here".needTranslation,
controller: messageTextController,
isEnable: true,
prefix: null,
autoFocus: false,
isBorderAllowed: false,
isMultiline: true,
keyboardType: TextInputType.text,
padding: EdgeInsets.symmetric(
vertical: ResponsiveExtension(10).h,
horizontal: ResponsiveExtension(15).h,
),
),
SizedBox(height: 16.h),
CustomButton(
text: LocaleKeys.selectAttachment.tr(context: context),
onPressed: () async {
ImageOptions.showImageOptionsNew(
context,
true,
(String image, file) {
print(image);
print(file);
Navigator.pop(context);
contactUsViewModel.addFeedbackAttachment(image);
},
);
},
backgroundColor: AppColors.secondaryLightRedColor,
borderColor: AppColors.secondaryLightRedColor,
textColor: AppColors.primaryRedColor,
fontSize: 14.f,
fontWeight: FontWeight.w500,
borderRadius: 10.r,
padding: EdgeInsets.symmetric(horizontal: 10.w),
height: isTablet || isFoldable ? 46.h : 40.h,
icon: AppAssets.file_icon,
iconColor: AppColors.primaryRedColor,
iconSize: 16.h,
),
SizedBox(height: 16.h),
contactUsViewModel.feedbackAttachmentList.isNotEmpty
? Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 12.r,
hasShadow: false,
),
child: ListView.builder(
padding: EdgeInsets.all(16.h),
shrinkWrap: true,
itemCount: contactUsViewModel.feedbackAttachmentList.length,
itemBuilder: (BuildContext context, int index) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Icon(
Icons.attach_file,
color: Color(0xff2B353E),
),
SizedBox(width: 8.w),
"Image ${index + 1}".toText14().paddingOnly(bottom: 8.h),
],
),
Utils.buildSvgWithAssets(icon: AppAssets.cancel_circle_icon).onPress(() {
contactUsViewModel.removeFeedbackAttachment(contactUsViewModel.feedbackAttachmentList[index]);
}),
],
);
},
),
)
: SizedBox.shrink(),
],
);
} else {
return Container();
}
}
}

@ -1,27 +1,176 @@
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/contact_us/contact_us_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/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/chip/app_custom_chip_widget.dart';
import 'package:provider/provider.dart';
import 'package:url_launcher/url_launcher.dart';
class LiveChatPage extends StatelessWidget { class LiveChatPage extends StatelessWidget {
const LiveChatPage({super.key}); LiveChatPage({super.key});
String chatURL = "";
late AppState appState;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
appState = getIt.get<AppState>();
return Scaffold( return Scaffold(
backgroundColor: AppColors.bgScaffoldColor, backgroundColor: AppColors.bgScaffoldColor,
body: Column( body: Consumer<ContactUsViewModel>(builder: (context, contactUsVM, child) {
children: [ return Column(
Expanded( children: [
child: CollapsingListView( Expanded(
title: LocaleKeys.liveChat.tr(), child: CollapsingListView(
child: SingleChildScrollView(), title: LocaleKeys.liveChat.tr(),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 16.h),
ListView.separated(
padding: EdgeInsets.only(top: 16.h),
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemCount: contactUsVM.isLiveChatProjectsListLoading ? 5 : contactUsVM.liveChatProjectsList.length,
itemBuilder: (context, index) {
return contactUsVM.isLiveChatProjectsListLoading
? Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: true),
child: Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 20.h,
hasShadow: true,
),
child: Padding(
padding: EdgeInsets.all(14.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Image.network(
"https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png",
width: 63.h,
height: 63.h,
fit: BoxFit.cover,
).circle(100).toShimmer2(isShow: true),
SizedBox(width: 16.h),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
"Dr John Smith".toText16(isBold: true).toShimmer2(isShow: true),
SizedBox(height: 8.h),
Wrap(
direction: Axis.horizontal,
spacing: 3.h,
runSpacing: 4.h,
children: [
AppCustomChipWidget(labelText: "").toShimmer2(isShow: true, width: 16.h),
AppCustomChipWidget(labelText: "").toShimmer2(isShow: true, width: 16.h),
],
),
],
),
),
],
),
],
),
),
),
).paddingSymmetrical(24.h, 0.h)
: AnimationConfiguration.staggeredList(
position: index,
duration: const Duration(milliseconds: 500),
child: SlideAnimation(
verticalOffset: 100.0,
child: FadeInAnimation(
child: AnimatedContainer(
duration: Duration(milliseconds: 300),
curve: Curves.easeInOut,
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: true),
child: DecoratedBox(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: contactUsVM.selectedLiveChatProjectIndex == index ? AppColors.primaryRedColor : AppColors.whiteColor,
borderRadius: 16.r,
hasShadow: false,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
("${appState.isArabic() ? contactUsVM.liveChatProjectsList[index].projectNameN! : contactUsVM.liveChatProjectsList[index].projectName!}\n${contactUsVM.liveChatProjectsList[index].distanceInKilometers!} KM")
.needTranslation
.toText14(isBold: true, color: contactUsVM.selectedLiveChatProjectIndex == index ? AppColors.whiteColor : AppColors.textColor),
Transform.flip(
flipX: getIt.get<AppState>().isArabic(),
child: Utils.buildSvgWithAssets(
icon: AppAssets.forward_arrow_icon_small,
iconColor: contactUsVM.selectedLiveChatProjectIndex == index ? AppColors.whiteColor : AppColors.textColor,
width: 18.h,
height: 13.h,
fit: BoxFit.contain,
),
),
],
).paddingSymmetrical(16.h, 16.h),
).onPress(() {
contactUsVM.setSelectedLiveChatProjectIndex(index);
chatURL =
"https://chat.hmg.com/Index.aspx?Name=${appState.getAuthenticatedUser()!.firstName}&PatientID=${appState.getAuthenticatedUser()!.patientId}&MobileNo=${appState.getAuthenticatedUser()!.mobileNumber}&Language=${appState.isArabic() ? 'ar' : 'en'}&WorkGroup=${contactUsVM.liveChatProjectsList[index].value}";
debugPrint("Chat URL: $chatURL");
}),
).paddingSymmetrical(24.h, 0.h),
),
),
);
},
separatorBuilder: (BuildContext cxt, int index) => SizedBox(height: 16.h),
),
SizedBox(height: 24.h),
],
),
),
),
Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 24.h,
hasShadow: true,
),
child: CustomButton(
text: LocaleKeys.liveChat.tr(context: context),
onPressed: () async {
Uri uri = Uri.parse(chatURL);
launchUrl(uri, mode: LaunchMode.platformDefault, webOnlyWindowName: "");
},
backgroundColor: contactUsVM.selectedLiveChatProjectIndex == -1 ? AppColors.greyColor : AppColors.primaryRedColor,
borderColor: contactUsVM.selectedLiveChatProjectIndex == -1 ? AppColors.greyColor : AppColors.primaryRedColor,
textColor: AppColors.whiteColor,
fontSize: 16,
fontWeight: FontWeight.w500,
borderRadius: 12,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 50.h,
).paddingSymmetrical(24.h, 24.h),
), ),
), ],
Container() );
], }),
),
); );
} }
} }

@ -0,0 +1,70 @@
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/utils/size_utils.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/contact_us/contact_us_view_model.dart';
import 'package:hmg_patient_app_new/features/medical_file/medical_file_view_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/appointments/widgets/appointment_card.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
import 'package:provider/provider.dart';
class FeedbackAppointmentSelection extends StatelessWidget {
FeedbackAppointmentSelection({super.key});
late MedicalFileViewModel medicalFileViewModel;
late ContactUsViewModel contactUsViewModel;
@override
Widget build(BuildContext context) {
medicalFileViewModel = Provider.of<MedicalFileViewModel>(context, listen: false);
contactUsViewModel = Provider.of<ContactUsViewModel>(context, listen: false);
return CollapsingListView(
title: LocaleKeys.feedback.tr(),
isClose: true,
child: Column(
children: [
ListView.separated(
padding: EdgeInsets.only(top: 24.h),
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemCount: medicalFileViewModel.patientMedicalReportAppointmentHistoryList.length,
itemBuilder: (context, index) {
return AnimationConfiguration.staggeredList(
position: index,
duration: const Duration(milliseconds: 500),
child: SlideAnimation(
verticalOffset: 100.0,
child: FadeInAnimation(
child: AnimatedContainer(
duration: Duration(milliseconds: 300),
curve: Curves.easeInOut,
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: true),
child: AppointmentCard(
patientAppointmentHistoryResponseModel: medicalFileViewModel.patientMedicalReportAppointmentHistoryList[index],
myAppointmentsViewModel: Provider.of<MyAppointmentsViewModel>(context, listen: false),
bookAppointmentsViewModel: Provider.of<BookAppointmentsViewModel>(context, listen: false),
medicalFileViewModel: medicalFileViewModel,
contactUsViewModel: contactUsViewModel,
isLoading: false,
isFromHomePage: false,
isFromMedicalReport: true,
isForFeedback: true,
),
).paddingSymmetrical(24.h, 0.h),
),
),
);
},
separatorBuilder: (BuildContext cxt, int index) => SizedBox(height: 16.h),
),
SizedBox(height: 24.h),
],
),
);
}
}

@ -26,7 +26,7 @@ import 'package:hmg_patient_app_new/widgets/expandable_bottom_sheet/ExpandableBo
import 'package:hmg_patient_app_new/widgets/expandable_bottom_sheet/model/BottomSheetType.dart'; import 'package:hmg_patient_app_new/widgets/expandable_bottom_sheet/model/BottomSheetType.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/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/gms_map.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import '../../../widgets/common_bottom_sheet.dart'; import '../../../widgets/common_bottom_sheet.dart';
@ -61,10 +61,10 @@ class RrtMapScreen extends StatelessWidget {
BottomSheetType.EXPANDED: ExpanedBottomSheet(context), BottomSheetType.EXPANDED: ExpanedBottomSheet(context),
BottomSheetType.FIXED: FixedBottomSheet(context), BottomSheetType.FIXED: FixedBottomSheet(context),
}, },
).paddingAll(16.h), ),
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(),
@ -116,15 +116,13 @@ class RrtMapScreen extends StatelessWidget {
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.end, crossAxisAlignment: CrossAxisAlignment.end,
children: [ children: [
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
spacing: 24.h, spacing: 24.h,
children: [ children: [
inputFields(context), inputFields(context).paddingSymmetrical(16.h, 0.h),
SizedBox( SizedBox(
height: 200.h,
child: DecoratedBox( child: DecoratedBox(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration( decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.scaffoldBgColor, color: AppColors.scaffoldBgColor,
@ -140,16 +138,14 @@ class RrtMapScreen extends StatelessWidget {
Column( Column(
spacing: 4.h, spacing: 4.h,
children: [ children: [
"Select Pickup Details".needTranslation.toText21( "Select Location".needTranslation.toText21(
weight: FontWeight.w600, weight: FontWeight.w600,
color: AppColors.textColor, color: AppColors.textColor,
), ),
" Please select the details of pickup" "Please select the location".needTranslation.toText12(
.needTranslation fontWeight: FontWeight.w500,
.toText12( color: AppColors.greyTextColor,
fontWeight: FontWeight.w500, )
color: AppColors.greyTextColor,
)
], ],
), ),
CustomButton( CustomButton(
@ -160,7 +156,8 @@ class RrtMapScreen extends StatelessWidget {
PlaceDetails? placeDetails = locationViewModel.placeDetails; PlaceDetails? placeDetails = locationViewModel.placeDetails;
PlacePrediction? placePrediction = locationViewModel.selectedPrediction; PlacePrediction? placePrediction = locationViewModel.selectedPrediction;
context.read<EmergencyServicesViewModel>().submitRRTRequest(response?.results.first, placeDetails, placePrediction); context.read<EmergencyServicesViewModel>().submitRRTRequest(response?.results.first, placeDetails, placePrediction);
}) },
)
], ],
).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),
), ),
@ -561,7 +558,7 @@ class RrtMapScreen 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) { Widget inputFields(BuildContext context) {
return textPlaceInput(context); return textPlaceInput(context);
} }

@ -23,7 +23,7 @@ import 'package:hmg_patient_app_new/widgets/expandable_bottom_sheet/ExpandableBo
import 'package:hmg_patient_app_new/widgets/expandable_bottom_sheet/model/BottomSheetType.dart'; import 'package:hmg_patient_app_new/widgets/expandable_bottom_sheet/model/BottomSheetType.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/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/gms_map.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import '../../../widgets/common_bottom_sheet.dart'; import '../../../widgets/common_bottom_sheet.dart';

@ -14,7 +14,7 @@ import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart'; import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart';
import 'package:hmg_patient_app_new/widgets/map/HMSMap.dart'; import 'package:hmg_patient_app_new/widgets/map/HMSMap.dart';
import 'package:hmg_patient_app_new/widgets/map/map.dart' show GMSMap; import 'package:hmg_patient_app_new/widgets/map/gms_map.dart' show GMSMap;
import 'package:hmg_patient_app_new/widgets/order_tracking/order_tracking_state.dart'; import 'package:hmg_patient_app_new/widgets/order_tracking/order_tracking_state.dart';
import 'package:hmg_patient_app_new/widgets/order_tracking/request_tracking.dart'; import 'package:hmg_patient_app_new/widgets/order_tracking/request_tracking.dart';
import 'package:lottie/lottie.dart'; import 'package:lottie/lottie.dart';

@ -256,7 +256,9 @@ class EmergencyServicesPage extends StatelessWidget {
isCloseButtonVisible: false, isCloseButtonVisible: false,
hasBottomPadding: false, hasBottomPadding: false,
backgroundColor: AppColors.primaryRedColor, backgroundColor: AppColors.primaryRedColor,
callBackFunc: () {}, callBackFunc: () {
context.read<EmergencyServicesViewModel>().setTermsAndConditions(false);
},
); );
}), }),
), ),

@ -44,7 +44,7 @@ class RRTItem extends StatelessWidget {
chip("Rapid Response Team(RRT)".needTranslation, AppAssets.ic_rrt_vehicle, AppColors.blackBgColor), chip("Rapid Response Team(RRT)".needTranslation, AppAssets.ic_rrt_vehicle, AppColors.blackBgColor),
], ],
), ),
SizedBox(height: 4.h),
if (order.statusId == 1 || order.statusId == 2) if (order.statusId == 1 || order.statusId == 2)
CustomButton( CustomButton(
text: "Cancel Request".needTranslation, text: "Cancel Request".needTranslation,
@ -55,8 +55,8 @@ class RRTItem extends StatelessWidget {
borderColor: AppColors.primaryRedColor, borderColor: AppColors.primaryRedColor,
textColor: Colors.white, textColor: Colors.white,
icon: AppAssets.cancel, icon: AppAssets.cancel,
iconSize: 20.h,
height: 40.h, height: 40.h,
iconSize: 18.w,
), ),
], ],
).paddingAll(16.h), ).paddingAll(16.h),

@ -90,7 +90,7 @@ class _WalletPaymentConfirmPageState extends State<WalletPaymentConfirmPage> {
Transform.flip( Transform.flip(
flipX: appState.isArabic(), flipX: appState.isArabic(),
child: Utils.buildSvgWithAssets( child: Utils.buildSvgWithAssets(
icon: AppAssets.forward_arrow_icon, icon: AppAssets.forward_arrow_icon_small,
iconColor: AppColors.blackColor, iconColor: AppColors.blackColor,
width: 18.h, width: 18.h,
height: 13.h, height: 13.h,
@ -132,7 +132,7 @@ class _WalletPaymentConfirmPageState extends State<WalletPaymentConfirmPage> {
Transform.flip( Transform.flip(
flipX: appState.isArabic(), flipX: appState.isArabic(),
child: Utils.buildSvgWithAssets( child: Utils.buildSvgWithAssets(
icon: AppAssets.forward_arrow_icon, icon: AppAssets.forward_arrow_icon_small,
iconColor: AppColors.blackColor, iconColor: AppColors.blackColor,
width: 18.h, width: 18.h,
height: 13.h, height: 13.h,

@ -47,7 +47,7 @@ class ServicesPage extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return CollapsingListView( return CollapsingListView(
title: "Explore Services".needTranslation, title: "Explore Services".needTranslation,
isLeading: false, isLeading: Navigator.canPop(context),
child: Padding( child: Padding(
padding: EdgeInsets.all(24.h), padding: EdgeInsets.all(24.h),
child: Column( child: Column(

@ -31,6 +31,7 @@ import 'package:hmg_patient_app_new/presentation/book_appointment/book_appointme
import 'package:hmg_patient_app_new/presentation/book_appointment/livecare/immediate_livecare_pending_request_page.dart'; import 'package:hmg_patient_app_new/presentation/book_appointment/livecare/immediate_livecare_pending_request_page.dart';
import 'package:hmg_patient_app_new/presentation/contact_us/contact_us.dart'; import 'package:hmg_patient_app_new/presentation/contact_us/contact_us.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_home.dart';
import 'package:hmg_patient_app_new/presentation/hmg_services/services_page.dart';
import 'package:hmg_patient_app_new/presentation/home/data/landing_page_data.dart'; import 'package:hmg_patient_app_new/presentation/home/data/landing_page_data.dart';
import 'package:hmg_patient_app_new/presentation/home/widgets/habib_wallet_card.dart'; import 'package:hmg_patient_app_new/presentation/home/widgets/habib_wallet_card.dart';
import 'package:hmg_patient_app_new/presentation/home/widgets/large_service_card.dart'; import 'package:hmg_patient_app_new/presentation/home/widgets/large_service_card.dart';
@ -245,7 +246,7 @@ class _LandingPageState extends State<LandingPage> {
indicatorLayout: PageIndicatorLayout.COLOR, indicatorLayout: PageIndicatorLayout.COLOR,
axisDirection: AxisDirection.right, axisDirection: AxisDirection.right,
controller: _controller, controller: _controller,
itemHeight: 210 + 25, itemHeight: 200.h,
pagination: const SwiperPagination( pagination: const SwiperPagination(
alignment: Alignment.bottomCenter, alignment: Alignment.bottomCenter,
margin: EdgeInsets.only(top: 210 + 8 + 24), margin: EdgeInsets.only(top: 210 + 8 + 24),
@ -439,7 +440,7 @@ class _LandingPageState extends State<LandingPage> {
}), }),
SizedBox(height: 16.h), SizedBox(height: 16.h),
Container( Container(
height: 120.h, height: 121.h,
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.r), decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.r),
child: Column( child: Column(
children: [ children: [
@ -526,11 +527,13 @@ class _LandingPageState extends State<LandingPage> {
SizedBox(width: 2.h), SizedBox(width: 2.h),
Icon(Icons.arrow_forward_ios, color: AppColors.primaryRedColor, size: 10.h), Icon(Icons.arrow_forward_ios, color: AppColors.primaryRedColor, size: 10.h),
], ],
), ).onPress(() {
Navigator.of(context).push(CustomPageRoute(page: ServicesPage()));
}),
], ],
).paddingSymmetrical(24.h, 0.h), ).paddingSymmetrical(24.h, 0.h),
SizedBox( SizedBox(
height: 280.h, height: 340.h,
child: ListView.separated( child: ListView.separated(
scrollDirection: Axis.horizontal, scrollDirection: Axis.horizontal,
itemCount: LandingPageData.getServiceCardsList.length, itemCount: LandingPageData.getServiceCardsList.length,
@ -567,9 +570,8 @@ class _LandingPageState extends State<LandingPage> {
void showQuickLogin(BuildContext context) { void showQuickLogin(BuildContext context) {
showCommonBottomSheetWithoutHeight( showCommonBottomSheetWithoutHeight(
context, context,
title: "", // title: "",
isCloseButtonVisible: false, isCloseButtonVisible: false,
child: StatefulBuilder( child: StatefulBuilder(
builder: (context, setState) { builder: (context, setState) {
return QuickLogin( return QuickLogin(

@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_state.dart'; import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart'; import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/presentation/book_appointment/book_appointment_page.dart'; import 'package:hmg_patient_app_new/presentation/book_appointment/book_appointment_page.dart';
import 'package:hmg_patient_app_new/presentation/contact_us/feedback_page.dart';
import 'package:hmg_patient_app_new/presentation/hmg_services/services_page.dart'; import 'package:hmg_patient_app_new/presentation/hmg_services/services_page.dart';
import 'package:hmg_patient_app_new/presentation/home/landing_page.dart'; import 'package:hmg_patient_app_new/presentation/home/landing_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';
@ -29,7 +30,7 @@ class _LandingNavigationState extends State<LandingNavigation> {
physics: const NeverScrollableScrollPhysics(), physics: const NeverScrollableScrollPhysics(),
children: [ children: [
const LandingPage(), const LandingPage(),
appState.isAuthenticated ? MedicalFilePage() : /* need add feedback page */ const LandingPage(), appState.isAuthenticated ? MedicalFilePage() : /* need add feedback page */ FeedbackPage(),
BookAppointmentPage(), BookAppointmentPage(),
const ToDoPage(), const ToDoPage(),
appState.isAuthenticated ? /* need add news page */ ServicesPage() : const LandingPage(), appState.isAuthenticated ? /* need add news page */ ServicesPage() : const LandingPage(),

File diff suppressed because one or more lines are too long

@ -46,7 +46,7 @@ class LabOrderResultItem extends StatelessWidget {
padding: EdgeInsets.only(bottom: 8.h), padding: EdgeInsets.only(bottom: 8.h),
child: '${tests!.description}'.toText14(weight: FontWeight.w500), child: '${tests!.description}'.toText14(weight: FontWeight.w500),
), ),
'${tests!.packageShortDescription}'.toText12(fontWeight: FontWeight.w500, color: AppColors.textColorLight), (tests!.packageShortDescription ?? "").toText12(fontWeight: FontWeight.w500, color: AppColors.textColorLight),
SizedBox(height: 12.h), SizedBox(height: 12.h),
Row( Row(
mainAxisSize: MainAxisSize.max, mainAxisSize: MainAxisSize.max,
@ -58,22 +58,25 @@ class LabOrderResultItem extends StatelessWidget {
fontSize: 24.f, fontSize: 24.f,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
fontFamily: 'Poppins', fontFamily: 'Poppins',
color: context.read<LabViewModel>().getColor( color: tests!.checkIfGraphShouldBeDisplayed()
tests?.calculatedResultFlag ?? "", ? context.read<LabViewModel>().getColor(
), tests?.calculatedResultFlag ?? "",
)
: Colors.grey.shade700,
letterSpacing: -2, letterSpacing: -2,
), ),
), ),
), ),
SizedBox(width: 4.h,), SizedBox(width: 4.h,),
Visibility( Visibility(
visible: tests?.checkIfGraphShouldBeDisplayed() == true, // visible: tests?.checkIfGraphShouldBeDisplayed() == true,
visible: true,
child: Expanded( child: Expanded(
flex: 2, flex: 2,
child: Visibility( child: Visibility(
visible: tests?.referanceRange != null, visible: tests?.referanceRange != null,
child: Text( child: Text(
"(Reference range ${tests?.referanceRange})".needTranslation, "(Reference range: ${tests?.referanceRange})".needTranslation,
style: TextStyle( style: TextStyle(
fontSize: 12.f, fontSize: 12.f,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,

@ -344,7 +344,7 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
Consumer<MyAppointmentsViewModel>(builder: (context, myAppointmentsVM, child) { Consumer<MyAppointmentsViewModel>(builder: (context, myAppointmentsVM, child) {
// Provide an explicit height so the horizontal ListView has a bounded height // Provide an explicit height so the horizontal ListView has a bounded height
return SizedBox( return SizedBox(
height: 190.h, height: 192.h,
child: myAppointmentsVM.isMyAppointmentsLoading child: myAppointmentsVM.isMyAppointmentsLoading
? MedicalFileAppointmentCard( ? MedicalFileAppointmentCard(
patientAppointmentHistoryResponseModel: PatientAppointmentHistoryResponseModel(), patientAppointmentHistoryResponseModel: PatientAppointmentHistoryResponseModel(),
@ -472,7 +472,7 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
child: Column( child: Column(
children: [ children: [
ListView.separated( ListView.separated(
itemCount: prescriptionVM.patientPrescriptionOrders.length, itemCount: prescriptionVM.patientPrescriptionOrders.length <= 2 ? prescriptionVM.patientPrescriptionOrders.length : 2,
shrinkWrap: true, shrinkWrap: true,
padding: EdgeInsets.only(left: 0, right: 8.w), padding: EdgeInsets.only(left: 0, right: 8.w),
physics: NeverScrollableScrollPhysics(), physics: NeverScrollableScrollPhysics(),
@ -533,6 +533,7 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
Navigator.of(context).push( Navigator.of(context).push(
CustomPageRoute( CustomPageRoute(
page: PrescriptionDetailPage( page: PrescriptionDetailPage(
isFromAppointments: false,
prescriptionsResponseModel: prescriptionVM.patientPrescriptionOrders[index]), prescriptionsResponseModel: prescriptionVM.patientPrescriptionOrders[index]),
), ),
); );
@ -564,7 +565,7 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
fontSize: 12.f, fontSize: 12.f,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
borderRadius: 12.r, borderRadius: 12.r,
height: 56.h, height: 40.h,
icon: AppAssets.requests, icon: AppAssets.requests,
iconColor: AppColors.primaryRedColor, iconColor: AppColors.primaryRedColor,
iconSize: 16.w, iconSize: 16.w,
@ -581,7 +582,7 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
fontSize: 12.f, fontSize: 12.f,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
borderRadius: 12.h, borderRadius: 12.h,
height: 56.h, height: 40.h,
icon: AppAssets.all_medications_icon, icon: AppAssets.all_medications_icon,
iconColor: AppColors.primaryRedColor, iconColor: AppColors.primaryRedColor,
iconSize: 16.h, iconSize: 16.h,

@ -60,9 +60,7 @@ class LabRadCard extends StatelessWidget {
itemCount: 3, itemCount: 3,
) )
: "You don't have any records yet".needTranslation.toText13( : "You don't have any records yet".needTranslation.toText13(
color: AppColors.greyTextColor, color: AppColors.greyTextColor, isCenter: true),
isCenter: true,
),
SizedBox(height: 16.h), SizedBox(height: 16.h),
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,

@ -29,6 +29,7 @@ class MedicalFileCard extends StatelessWidget {
decoration: RoundedRectangleBorder().toSmoothCornerDecoration( decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: backgroundColor, color: backgroundColor,
borderRadius: 12.r, borderRadius: 12.r,
hasShadow: true
), ),
padding: EdgeInsets.all(12.w), padding: EdgeInsets.all(12.w),
child: Column( child: Column(

@ -25,9 +25,10 @@ import 'package:provider/provider.dart';
import 'package:url_launcher/url_launcher.dart'; import 'package:url_launcher/url_launcher.dart';
class PrescriptionDetailPage extends StatefulWidget { class PrescriptionDetailPage extends StatefulWidget {
PrescriptionDetailPage({super.key, required this.prescriptionsResponseModel}); PrescriptionDetailPage({super.key, required this.prescriptionsResponseModel, required this.isFromAppointments});
PatientPrescriptionsResponseModel prescriptionsResponseModel; PatientPrescriptionsResponseModel prescriptionsResponseModel;
bool isFromAppointments = false;
@override @override
State<PrescriptionDetailPage> createState() => _PrescriptionDetailPageState(); State<PrescriptionDetailPage> createState() => _PrescriptionDetailPageState();
@ -43,10 +44,12 @@ class _PrescriptionDetailPageState extends State<PrescriptionDetailPage> {
checkAndRemove(false); checkAndRemove(false);
// locationUtils = new LocationUtils(isShowConfirmDialog: true, context: context); // locationUtils = new LocationUtils(isShowConfirmDialog: true, context: context);
// WidgetsBinding.instance.addPostFrameCallback((_) => locationUtils.getCurrentLocation()); // WidgetsBinding.instance.addPostFrameCallback((_) => locationUtils.getCurrentLocation());
scheduleMicrotask(() { if (!widget.isFromAppointments) {
prescriptionsViewModel.setPrescriptionsDetailsLoading(); scheduleMicrotask(() {
prescriptionsViewModel.getPrescriptionDetails(widget.prescriptionsResponseModel); prescriptionsViewModel.setPrescriptionsDetailsLoading();
}); prescriptionsViewModel.getPrescriptionDetails(widget.prescriptionsResponseModel);
});
}
super.initState(); super.initState();
} }
@ -146,7 +149,7 @@ class _PrescriptionDetailPageState extends State<PrescriptionDetailPage> {
CustomButton( CustomButton(
text: "Download Prescription".needTranslation, text: "Download Prescription".needTranslation,
onPressed: () async { onPressed: () async {
LoaderBottomSheet.showLoader(); LoaderBottomSheet.showLoader(loadingText: "Fetching prescription PDF, Please wait...".needTranslation);
await prescriptionVM.getPrescriptionPDFBase64(widget.prescriptionsResponseModel).then((val) async { await prescriptionVM.getPrescriptionPDFBase64(widget.prescriptionsResponseModel).then((val) async {
LoaderBottomSheet.hideLoader(); LoaderBottomSheet.hideLoader();
if (prescriptionVM.prescriptionPDFBase64Data.isNotEmpty) { if (prescriptionVM.prescriptionPDFBase64Data.isNotEmpty) {
@ -181,8 +184,10 @@ class _PrescriptionDetailPageState extends State<PrescriptionDetailPage> {
), ),
), ),
).paddingSymmetrical(24.h, 0.h), ).paddingSymmetrical(24.h, 0.h),
SizedBox(height: 16.h),
ListView.builder( ListView.builder(
shrinkWrap: true, shrinkWrap: true,
padding: EdgeInsets.zero,
physics: NeverScrollableScrollPhysics(), physics: NeverScrollableScrollPhysics(),
itemCount: prescriptionVM.isPrescriptionsDetailsLoading ? 5 : prescriptionVM.prescriptionDetailsList.length, itemCount: prescriptionVM.isPrescriptionsDetailsLoading ? 5 : prescriptionVM.prescriptionDetailsList.length,
itemBuilder: (context, index) { itemBuilder: (context, index) {

@ -255,22 +255,20 @@ class _PrescriptionsListPageState extends State<PrescriptionsListPage> {
Expanded( Expanded(
flex: 1, flex: 1,
child: Container( child: Container(
height: 40.h, height: 40.h,
width: 40.h, width: 40.w,
decoration: RoundedRectangleBorder().toSmoothCornerDecoration( decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.textColor, color: AppColors.textColor,
borderRadius: 10.h, borderRadius: 12,
), ),
child: Padding( child: Padding(
padding: EdgeInsets.all(8.h), padding: EdgeInsets.all(12.h),
child: Transform.flip( child: Transform.flip(
flipX: appState.isArabic(), flipX: appState.isArabic(),
child: Utils.buildSvgWithAssets( child: Utils.buildSvgWithAssets(
icon: AppAssets.forward_arrow_icon_small, icon: AppAssets.forward_arrow_icon_small,
iconColor: AppColors.whiteColor, iconColor: AppColors.whiteColor,
width: 10.h, fit: BoxFit.contain,
height: 10.h,
fit: BoxFit.contain,
), ),
), ),
), ),
@ -278,9 +276,12 @@ class _PrescriptionsListPageState extends State<PrescriptionsListPage> {
model.setPrescriptionsDetailsLoading(); model.setPrescriptionsDetailsLoading();
Navigator.of(context).push( Navigator.of(context).push(
CustomPageRoute( CustomPageRoute(
page: PrescriptionDetailPage(prescriptionsResponseModel: prescription), page: PrescriptionDetailPage(
), prescriptionsResponseModel: prescription,
); isFromAppointments: false,
),
),
);
}), }),
), ),
], ],

@ -2,6 +2,8 @@ import 'dart:async';
import 'dart:developer'; import 'dart:developer';
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/dependencies.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
@ -21,11 +23,16 @@ class ToDoPage extends StatefulWidget {
} }
class _ToDoPageState extends State<ToDoPage> { class _ToDoPageState extends State<ToDoPage> {
late AppState appState;
@override @override
void initState() { void initState() {
final TodoSectionViewModel todoSectionViewModel = context.read<TodoSectionViewModel>(); final TodoSectionViewModel todoSectionViewModel = context.read<TodoSectionViewModel>();
scheduleMicrotask(() async { scheduleMicrotask(() async {
await todoSectionViewModel.initializeTodoSectionViewModel(); if (appState.isAuthenticated) {
await todoSectionViewModel.initializeTodoSectionViewModel();
}
}); });
super.initState(); super.initState();
} }
@ -51,6 +58,7 @@ class _ToDoPageState extends State<ToDoPage> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
appState = getIt.get<AppState>();
return CollapsingListView( return CollapsingListView(
title: "ToDo List".needTranslation, title: "ToDo List".needTranslation,
isLeading: false, isLeading: false,

@ -54,7 +54,7 @@ class CollapsingListView extends StatelessWidget {
SliverAppBar( SliverAppBar(
automaticallyImplyLeading: false, automaticallyImplyLeading: false,
pinned: true, pinned: true,
expandedHeight: MediaQuery.of(context).size.height * 0.12.h, expandedHeight: MediaQuery.of(context).size.height * 0.11.h,
stretch: true, stretch: true,
systemOverlayStyle: SystemUiOverlayStyle(statusBarBrightness: Brightness.light), systemOverlayStyle: SystemUiOverlayStyle(statusBarBrightness: Brightness.light),
surfaceTintColor: Colors.transparent, surfaceTintColor: Colors.transparent,
@ -92,8 +92,7 @@ class CollapsingListView extends StatelessWidget {
t, t,
)!, )!,
child: Padding( child: Padding(
padding: EdgeInsets.only( padding: EdgeInsets.only(left: appState.isArabic() ? 0 : leftPadding, right: appState.isArabic() ? leftPadding : 0, bottom: bottomPadding),
left: appState.isArabic() ? 0 : leftPadding, right: appState.isArabic() ? leftPadding : 0, bottom: bottomPadding),
child: Row( child: Row(
spacing: 4.h, spacing: 4.h,
children: [ children: [
@ -110,18 +109,11 @@ class CollapsingListView extends StatelessWidget {
color: AppColors.blackColor, color: AppColors.blackColor,
letterSpacing: -0.5), letterSpacing: -0.5),
).expanded, ).expanded,
if (logout != null) if (logout != null) actionButton(context, t, title: "Logout".needTranslation, icon: AppAssets.logout).onPress(logout!),
actionButton(context, t, title: "Logout".needTranslation, icon: AppAssets.logout).onPress(logout!), if (report != null) actionButton(context, t, title: "Report".needTranslation, icon: AppAssets.report_icon).onPress(report!),
if (report != null) if (history != null) actionButton(context, t, title: "History".needTranslation, icon: AppAssets.insurance_history_icon).onPress(history!),
actionButton(context, t, title: "Report".needTranslation, icon: AppAssets.report_icon).onPress(report!), if (instructions != null) actionButton(context, t, title: "Instructions".needTranslation, icon: AppAssets.requests).onPress(instructions!),
if (history != null) if (requests != null) actionButton(context, t, title: "Requests".needTranslation, icon: AppAssets.insurance_history_icon).onPress(requests!),
actionButton(context, t, title: "History".needTranslation, icon: AppAssets.insurance_history_icon)
.onPress(history!),
if (instructions != null)
actionButton(context, t, title: "Instructions".needTranslation, icon: AppAssets.requests).onPress(instructions!),
if (requests != null)
actionButton(context, t, title: "Requests".needTranslation, icon: AppAssets.insurance_history_icon)
.onPress(requests!),
if (search != null) Utils.buildSvgWithAssets(icon: AppAssets.search_icon).onPress(search!).paddingOnly(right: 24), if (search != null) Utils.buildSvgWithAssets(icon: AppAssets.search_icon).onPress(search!).paddingOnly(right: 24),
if (trailing != null) trailing!, if (trailing != null) trailing!,
], ],

@ -100,7 +100,7 @@ class AppCustomChipWidget extends StatelessWidget {
) )
: Chip( : Chip(
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
label: richText ?? labelText!.toText10(weight: FontWeight.w500, letterSpacing: 0, color: textColor), label: richText ?? labelText!.toText10(weight: FontWeight.w500, letterSpacing: 0, color: textColor, isCenter: true),
padding: EdgeInsets.zero, padding: EdgeInsets.zero,
backgroundColor: backgroundColor, backgroundColor: backgroundColor,
shape: shape ?? shape: shape ??
@ -109,7 +109,7 @@ class AppCustomChipWidget extends StatelessWidget {
smoothness: 10, smoothness: 10,
side: BorderSide(color: AppColors.transparent, width: 1.5), side: BorderSide(color: AppColors.transparent, width: 1.5),
), ),
labelPadding: labelPadding ?? EdgeInsetsDirectional.only(start: 2.w, end: deleteIcon?.isNotEmpty == true ? 2.w : 8.w), labelPadding: labelPadding ?? EdgeInsetsDirectional.only(start: 6.w, end: deleteIcon?.isNotEmpty == true ? 2.w : 8.w),
deleteIcon: deleteIcon?.isNotEmpty == true deleteIcon: deleteIcon?.isNotEmpty == true
? InkWell( ? InkWell(
onTap: onDeleteTap, onTap: onDeleteTap,

@ -183,7 +183,7 @@ void showCommonBottomSheetWithoutHeight(
], ],
), ),
), ),
SizedBox(height: 16.h), isCloseButtonVisible ? SizedBox(height: 16.h) : SizedBox.shrink(),
child, child,
], ],
), ),

@ -43,6 +43,11 @@ class TextInputWidget extends StatelessWidget {
final Color? labelColor; final Color? labelColor;
final Function(String)? onSubmitted; final Function(String)? onSubmitted;
// new multiline options
final bool isMultiline;
final int minLines;
final int maxLines;
// final List<Country> countryList; // final List<Country> countryList;
// final Function(Country)? onCountryChange; // final Function(Country)? onCountryChange;
@ -73,10 +78,14 @@ class TextInputWidget extends StatelessWidget {
this.isWalletAmountInput = false, this.isWalletAmountInput = false,
this.suffix, this.suffix,
this.labelColor, this.labelColor,
this.onSubmitted this.onSubmitted,
// this.countryList = const [], // multiline defaults
// this.onCountryChange, this.isMultiline = false,
}); this.minLines = 3,
this.maxLines = 6,
// this.countryList = const [],
// this.onCountryChange,
});
final FocusNode _focusNode = FocusNode(); final FocusNode _focusNode = FocusNode();
@ -113,7 +122,7 @@ class TextInputWidget extends StatelessWidget {
children: [ children: [
Container( Container(
padding: padding, padding: padding,
height: 64.h, height: isMultiline ? null : 64.h,
alignment: Alignment.center, alignment: Alignment.center,
decoration: RoundedRectangleBorder().toSmoothCornerDecoration( decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: Colors.white, color: Colors.white,
@ -205,9 +214,7 @@ class TextInputWidget extends StatelessWidget {
initialDate: DateTime.now(), initialDate: DateTime.now(),
fontFamily: appState.getLanguageCode() == "ar" ? "GESSTwo" : "Poppins", fontFamily: appState.getLanguageCode() == "ar" ? "GESSTwo" : "Poppins",
okWidget: Padding(padding: EdgeInsets.only(right: 8.h), child: Utils.buildSvgWithAssets(icon: AppAssets.confirm, width: 24.h, height: 24.h)), okWidget: Padding(padding: EdgeInsets.only(right: 8.h), child: Utils.buildSvgWithAssets(icon: AppAssets.confirm, width: 24.h, height: 24.h)),
cancelWidget: Padding( cancelWidget: Padding(padding: EdgeInsets.only(right: 8.h), child: Utils.buildSvgWithAssets(icon: AppAssets.cancel, iconColor: Colors.white, width: 24.h, height: 24.h)),
padding: EdgeInsets.only(right: 8.h),
child: Utils.buildSvgWithAssets(icon: AppAssets.cancel, iconColor: Colors.white, width: 24.h, height: 24.h)),
onCalendarTypeChanged: (bool value) { onCalendarTypeChanged: (bool value) {
isGregorian = value; isGregorian = value;
}); });
@ -240,7 +247,7 @@ class TextInputWidget extends StatelessWidget {
return TextField( return TextField(
enabled: isEnable, enabled: isEnable,
scrollPadding: EdgeInsets.zero, scrollPadding: EdgeInsets.zero,
keyboardType: keyboardType, keyboardType: isMultiline ? TextInputType.multiline : keyboardType,
controller: controller, controller: controller,
readOnly: isReadOnly, readOnly: isReadOnly,
textAlignVertical: TextAlignVertical.top, textAlignVertical: TextAlignVertical.top,
@ -255,7 +262,15 @@ class TextInputWidget extends StatelessWidget {
FocusManager.instance.primaryFocus?.unfocus(); FocusManager.instance.primaryFocus?.unfocus();
}, },
onSubmitted: onSubmitted, onSubmitted: onSubmitted,
style: TextStyle(fontSize: fontS, height: isWalletAmountInput! ? 1 / 4 : 0, fontWeight: FontWeight.w500, color: AppColors.textColor, letterSpacing: -1), minLines: isMultiline ? minLines : 1,
maxLines: isMultiline ? maxLines : 1,
style: TextStyle(
fontSize: fontS,
height: isMultiline ? 1.2 : (isWalletAmountInput! ? 1 / 4 : 0),
fontWeight: FontWeight.w500,
color: AppColors.textColor,
letterSpacing: -1,
),
decoration: InputDecoration( decoration: InputDecoration(
isDense: true, isDense: true,
hintText: hintText, hintText: hintText,

@ -4,6 +4,8 @@ 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/app_export.dart'; import 'package:hmg_patient_app_new/core/app_export.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart'; import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:huawei_map/huawei_map.dart' ; import 'package:huawei_map/huawei_map.dart' ;
class HMSMap extends StatefulWidget{ class HMSMap extends StatefulWidget{
@ -55,7 +57,7 @@ class _HMSMapState extends State<HMSMap> {
visible: widget.showCenterMarker, visible: widget.showCenterMarker,
child: Align( child: Align(
alignment: Alignment.center, alignment: Alignment.center,
child: Utils.buildSvgWithAssets(icon: AppAssets.pin_location, width: 24.w, height: 36.h), child: Icon(Icons.location_pin, size: 36.h, color: AppColors.primaryRedColor).paddingOnly(bottom: 24.h),
), ),
) )
], ],

@ -5,6 +5,8 @@ import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart'; import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart'; import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
class GMSMap extends StatelessWidget{ class GMSMap extends StatelessWidget{
Completer<GoogleMapController>? controller; Completer<GoogleMapController>? controller;
@ -29,14 +31,15 @@ class GMSMap extends StatelessWidget{
children: [ children: [
GoogleMap( GoogleMap(
mapType: mapType, mapType: mapType,
zoomControlsEnabled: false, zoomControlsEnabled: true,
myLocationEnabled: myLocationEnabled, myLocationEnabled: myLocationEnabled,
myLocationButtonEnabled: false, myLocationButtonEnabled: false,
compassEnabled: compassEnabled, compassEnabled: compassEnabled,
initialCameraPosition: currentLocation, initialCameraPosition: currentLocation,
onCameraMove: (value) => onCameraMoved(value), onCameraMove: (value) => onCameraMoved(value),
onCameraIdle: ()=>onCameraIdle(), onCameraIdle: ()=>onCameraIdle(),
onMapCreated: (GoogleMapController controller) { // padding: EdgeInsets.only(bottom: 300.h),
onMapCreated: (GoogleMapController controller) {
this.controller?.complete(controller); this.controller?.complete(controller);
}, },
), ),
@ -44,10 +47,10 @@ class GMSMap extends StatelessWidget{
visible: showCenterMarker, visible: showCenterMarker,
child: Align( child: Align(
alignment: Alignment.center, alignment: Alignment.center,
child: Utils.buildSvgWithAssets(icon: AppAssets.pin_location, width: 24.w, height: 36.h), child: Icon(Icons.location_pin, size: 36.h, color: AppColors.primaryRedColor).paddingOnly(bottom: 24.h),
), ),
) )
], ],
); );
} }
} }

@ -0,0 +1,245 @@
import 'dart:io';
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/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/emergency_services/emergency_services_view_model.dart';
import 'package:hmg_patient_app_new/features/emergency_services/models/AmbulanceCallingPlace.dart';
import 'package:hmg_patient_app_new/features/location/GeocodeResponse.dart';
import 'package:hmg_patient_app_new/features/location/PlaceDetails.dart';
import 'package:hmg_patient_app_new/features/location/PlacePrediction.dart';
import 'package:hmg_patient_app_new/features/location/location_view_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/appointments/widgets/appointment_doctor_card.dart';
import 'package:hmg_patient_app_new/presentation/emergency_services/call_ambulance/widgets/AddressItem.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/widgets/location_input_bottom_sheet.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/CustomSwitch.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/expandable_bottom_sheet/ExpandableBottomSheet.dart';
import 'package:hmg_patient_app_new/widgets/expandable_bottom_sheet/model/BottomSheetType.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/gms_map.dart';
import 'package:provider/provider.dart';
import '../../../widgets/common_bottom_sheet.dart';
/// screen to be used to get the location desired by the user
/// to place the values in the request.
/// [confirmButtonString] button text that will be displayed on the button
/// [titleString] bottom sheet title
/// [subTitleString] bottom sheet subtitle for details
/// [onCrossClicked] if something has to be done if the user close the screen
/// [isGmsAvailable] shows if the device that is running the application is GMS or HMS
///
/// it results [true] if the user clicks on the submit button
/// and [false] if the user closes the screen without giving the consent to proceed for the request
class MapUtilityScreen extends StatelessWidget {
final String confirmButtonString;
final String titleString;
final String subTitleString;
final bool isGmsAvailable;
final VoidCallback? onCrossClicked;
const MapUtilityScreen({super.key, required this.confirmButtonString, required this.titleString, required this.subTitleString, required this.isGmsAvailable, this.onCrossClicked});
@override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: Padding(
padding: EdgeInsetsDirectional.only(end: 8.h, bottom: 68.h),
child: DecoratedBox(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor, borderRadius: 12.h),
child: Utils.buildSvgWithAssets(
icon: AppAssets.locate_me, width: 24.h, height: 24.h)
.paddingAll(12.h)
.onPress(() {
context
.read<LocationViewModel>()
.moveToCurrentLocation();
}),
),
),
bottomSheet: FixedBottomSheet(context),
body: Stack(
children: [
if (isGmsAvailable)
GMSMap(
currentLocation:
context.read<LocationViewModel>().getGMSLocation(),
onCameraMoved: (value) => context
.read<LocationViewModel>()
.handleGMSMapCameraMoved(value),
onCameraIdle:
context.read<LocationViewModel>().handleOnCameraIdle,
myLocationEnabled: true,
inputController:
context.read<LocationViewModel>().gmsController,
showCenterMarker: true,
)
else
HMSMap(
currentLocation:
context.read<LocationViewModel>().getHMSLocation(),
onCameraMoved: (value) => context
.read<LocationViewModel>()
.handleHMSMapCameraMoved(value),
onCameraIdle:
context.read<LocationViewModel>().handleOnCameraIdle,
myLocationEnabled: false,
inputController:
context.read<LocationViewModel>().hmsController,
showCenterMarker: true,
),
Align(
alignment: AlignmentDirectional.topStart,
child: Utils.buildSvgWithAssets(
icon: AppAssets.closeBottomNav, width: 32.h, height: 32.h)
.onPress(() {
onCrossClicked?.call();
// context
// .read<EmergencyServicesViewModel>()
// .flushPickupInformation();
Navigator.pop(context, false);
}),
).paddingOnly(top: 51.h, left: 24.h),
],
),
);
}
Widget FixedBottomSheet(BuildContext context) {
return GestureDetector(
onVerticalDragUpdate: (details){
},
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
spacing: 24.h,
children: [
inputFields(context).paddingSymmetrical(16.h, 0.h),
SizedBox(
child: DecoratedBox(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.scaffoldBgColor,
customBorder: BorderRadius.only(
topLeft: Radius.circular(24.h),
topRight: Radius.circular(24.h),
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
spacing: 24.h,
children: [
Column(
spacing: 4.h,
children: [
titleString.toText21(
weight: FontWeight.w600,
color: AppColors.textColor,
),
subTitleString.needTranslation.toText12(
fontWeight: FontWeight.w500,
color: AppColors.greyTextColor,
)
],
),
CustomButton(
text: confirmButtonString.needTranslation,
onPressed: () {
///indicates that the screen has resulted success and should be closed
Navigator.pop(context,true);
},
)
],
).paddingOnly(top: 24.h, bottom: 32.h, left: 24.h, right: 24.h),
),
),
],
),
],
),
);
}
leadingIcon(String leadingIcon) {
return Container(
height: 40.h,
width: 40.h,
margin: EdgeInsets.only(right: 10.h),
padding: EdgeInsets.all(8.h),
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
borderRadius: 12.h,
color: AppColors.greyColor,
),
child: Utils.buildSvgWithAssets(icon: leadingIcon),
);
}
textPlaceInput(context) {
return Consumer<LocationViewModel>(builder: (_, vm, __) {
return SizedBox(
width: MediaQuery.sizeOf(context).width,
child: TextInputWidget(
labelText: "Enter Pickup Location Manually".needTranslation,
hintText: "Enter Pickup Location".needTranslation,
controller: TextEditingController(
text: vm.geocodeResponse?.results.first.formattedAddress ??
vm.selectedPrediction?.description,
),
leadingIcon: AppAssets.location_pickup,
isAllowLeadingIcon: true,
isEnable: false,
prefix: null,
autoFocus: false,
isBorderAllowed: false,
keyboardType: TextInputType.text,
padding: EdgeInsets.symmetric(
vertical: ResponsiveExtension(10).h,
horizontal: ResponsiveExtension(15).h,
),
).onPress(() {
openLocationInputBottomSheet(context);
}),
);
});
}
///decide which field to show first based on the selected calling place
Widget inputFields(BuildContext context) {
return textPlaceInput(context);
}
openLocationInputBottomSheet(BuildContext context) {
context.read<LocationViewModel>().flushSearchPredictions();
showCommonBottomSheetWithoutHeight(
title: "".needTranslation,
context,
child: SizedBox(
height: MediaQuery.sizeOf(context).height * .8,
child: LocationInputBottomSheet(),
),
isFullScreen: false,
isCloseButtonVisible: true,
hasBottomPadding: false,
backgroundColor: AppColors.bottomSheetBgColor,
callBackFunc: () {},
);
}
}
Loading…
Cancel
Save