Merge branch 'master' into faiz_dev

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

@ -161,10 +161,11 @@ class ApiClientImp implements ApiClient {
// body['VersionID'] = ApiConsts.appVersionID.toString();
if (!isExternal) {
body['VersionID'] = ApiConsts.appVersionID;
body['Channel'] = ApiConsts.appChannelId;
body['VersionID'] = ApiConsts.appVersionID.toString();
body['Channel'] = ApiConsts.appChannelId.toString();
body['IPAdress'] = ApiConsts.appIpAddress;
body['generalid'] = ApiConsts.appGeneralId;
body['LanguageID'] = _appState.getLanguageID().toString();
body['Latitude'] = _appState.userLat.toString();
body['Longitude'] = _appState.userLong.toString();
@ -183,6 +184,9 @@ class ApiClientImp implements ApiClient {
}
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);

@ -14,8 +14,8 @@ var PACKAGES_ORDERS = '/api/orders';
var PACKAGES_ORDER_HISTORY = '/api/orders/items';
var PACKAGES_TAMARA_OPT = '/api/orders/paymentoptions/tamara';
// var BASE_URL = 'http://10.50.100.198:2018/';
var BASE_URL = 'https://uat.hmgwebservices.com/';
// var BASE_URL = 'https://hmgwebservices.com/';
// var BASE_URL = 'https://uat.hmgwebservices.com/';
var BASE_URL = 'https://hmgwebservices.com/';
// var BASE_URL = 'http://10.201.204.103/';
// var BASE_URL = 'https://orash.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";
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=';
rcBaseUrl = 'https://rc.hmg.com/';
rcBaseUrl = 'https://rc.hmg.com/uat/';
break;
case AppEnvironmentTypeEnum.uat:
baseUrl = "https://uat.hmgwebservices.com/";
@ -764,7 +764,7 @@ class ApiConsts {
TAMARA_URL = "https://epharmacy.hmg.com/tamara/Home/Checkout";
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=';
rcBaseUrl = 'https://rc.hmg.com/';
rcBaseUrl = 'https://rc.hmg.com/uat/';
break;
case AppEnvironmentTypeEnum.preProd:
baseUrl = "https://webservices.hmg.com/";
@ -784,7 +784,7 @@ class ApiConsts {
TAMARA_URL = "https://epharmacy.hmg.com/tamara/Home/Checkout";
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=';
rcBaseUrl = 'https://rc.hmg.com/';
rcBaseUrl = 'https://rc.hmg.com/uat/';
break;
case AppEnvironmentTypeEnum.staging:
baseUrl = "https://uat.hmgwebservices.com/";
@ -794,7 +794,7 @@ class ApiConsts {
TAMARA_URL = "https://epharmacy.hmg.com/tamara/Home/Checkout";
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=';
rcBaseUrl = 'https://rc.hmg.com/';
rcBaseUrl = 'https://rc.hmg.com/uat/';
break;
}
}
@ -852,7 +852,7 @@ class ApiConsts {
static final String addHHCOrder = 'api/HHC/add';
// ************ static values for Api ****************
static final double appVersionID = 20.0;
static final double appVersionID = 19.3;
static final int appChannelId = 3;
static final String appIpAddress = "10.20.10.20";
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<RadiologyViewModel>(() => RadiologyViewModel(radiologyRepo: getIt(), errorHandlerService: getIt()));
getIt.registerLazySingleton<RadiologyViewModel>(
() => RadiologyViewModel(
radiologyRepo: getIt(),
errorHandlerService: getIt(),
navigationService: getIt()
),
);
getIt.registerLazySingleton<PrescriptionsViewModel>(() => PrescriptionsViewModel(prescriptionsRepo: getIt(), errorHandlerService: getIt()));

@ -41,12 +41,14 @@ extension EmailValidator on String {
FontWeight? weight,
bool isBold = false,
bool isUnderLine = false,
bool isCenter = false,
int? maxlines,
FontStyle? fontStyle,
TextOverflow? textOverflow,
double letterSpacing = 0}) =>
Text(
this,
textAlign: isCenter ? TextAlign.center : null,
maxLines: maxlines,
overflow: textOverflow,
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/common_models/generic_api_model.dart';
import 'package:hmg_patient_app_new/core/exceptions/api_failure.dart';
import 'package:hmg_patient_app_new/features/contact_us/models/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_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';
abstract class ContactUsRepo {
Future<Either<Failure, GenericApiModel<List<GetHMGLocationsModel>>>> getHMGLocations();
Future<Either<Failure, GenericApiModel<List<GetPatientICProjectsModel>>>> getLiveChatProjectsList();
Future<Either<Failure, GenericApiModel<dynamic>>> insertCOCItem({required RequestInsertCOCItem requestInsertCOCItem, PatientAppointmentHistoryResponseModel? patientSelectedAppointment});
}
class ContactUsRepoImp implements ContactUsRepo {
@ -72,13 +76,57 @@ class ContactUsRepoImp implements ContactUsRepo {
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
final list = response['List_PatientICProjects'];
final hmgLocations = list.map((item) => GetPatientICProjectsModel.fromJson(item as Map<String, dynamic>)).toList().cast<GetPatientICProjectsModel>();
final liveChatProjectsList = list.map((item) => GetPatientICProjectsModel.fromJson(item as Map<String, dynamic>)).toList().cast<GetPatientICProjectsModel>();
apiResponse = GenericApiModel<List<GetPatientICProjectsModel>>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: null,
data: hmgLocations,
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) {
failure = DataParsingFailure(e.toString());

@ -1,7 +1,15 @@
import 'dart:io';
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/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/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_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';
class ContactUsViewModel extends ChangeNotifier {
@ -11,17 +19,43 @@ class ContactUsViewModel extends ChangeNotifier {
bool isHMGLocationsListLoading = false;
bool isHMGHospitalsListSelected = true;
bool isLiveChatProjectsListLoading = false;
bool isSendFeedbackTabSelected = true;
List<GetHMGLocationsModel> hmgHospitalsLocationsList = [];
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});
initContactUsViewModel() {
isHMGLocationsListLoading = true;
isHMGHospitalsListSelected = true;
isLiveChatProjectsListLoading = true;
hmgHospitalsLocationsList.clear();
hmgPharmacyLocationsList.clear();
liveChatProjectsList.clear();
feedbackAttachmentList.clear();
selectedFeedbackType = FeedbackType(id: 5, nameEN: "Not classified", nameAR: 'غير محدد');
setPatientFeedbackSelectedAppointment(null);
getHMGLocations();
notifyListeners();
}
@ -31,6 +65,36 @@ class ContactUsViewModel extends ChangeNotifier {
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 {
isHMGLocationsListLoading = true;
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/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/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/resp_models/hospital_model.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/expandable_bottom_sheet/model/BottomSheetType.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/routes/custom_page_route.dart';
import 'package:huawei_map/huawei_map.dart' as HMSCameraServices;
@ -1047,12 +1049,26 @@ class EmergencyServicesViewModel extends ChangeNotifier {
placeValueInController();
locationUtils!.getLocation(
isShowConfirmDialog: true,
onSuccess: (position) {
onSuccess: (position) async {
updateBottomSheetState(BottomSheetType.FIXED);
navServices.push(
bool result = await navServices.push(
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{
dialogService.showErrorBottomSheet(

@ -21,6 +21,7 @@ abstract class LabRepo {
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 {
@ -135,6 +136,7 @@ class LabRepoImp implements LabRepo {
request['SetupID'] = laborder!.setupID;
request['ProjectID'] = laborder.projectID;
request['ClinicID'] = laborder.clinicID;
request['InvoiceType'] = laborder.invoiceType ?? "";
try {
GenericApiModel<List<LabResult>>? apiResponse;
Failure? failure;
@ -184,6 +186,7 @@ class LabRepoImp implements LabRepo {
request['SetupID'] = laborder!.setupID;
request['ProjectID'] = laborder.projectID;
request['ClinicID'] = laborder.clinicID;
request['InvoiceType'] = laborder.invoiceType ?? "";
try {
GenericApiModel<List<PatientLabSpecialResult>>? apiResponse;
Failure? failure;
@ -278,4 +281,41 @@ class LabRepoImp implements LabRepo {
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/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/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/services/error_handler_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/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:logger/logger.dart';
@ -75,14 +77,14 @@ class LabViewModel extends ChangeNotifier {
required this.navigationService});
initLabProvider() {
if (isLabNeedToLoad) {
// if (isLabNeedToLoad) {
patientLabOrders.clear();
filteredLabOrders.clear();
labOrderTests.clear();
isLabOrdersLoading = true;
isLabResultsLoading = true;
getPatientLabOrders();
}
// }
notifyListeners();
}
@ -92,7 +94,7 @@ class LabViewModel extends ChangeNotifier {
}
Future<void> getPatientLabOrders({Function(dynamic)? onSuccess, Function(String)? onError}) async {
if (!isLabNeedToLoad) return;
// if (!isLabNeedToLoad) return;
isLabOrdersLoading = true;
patientLabOrders.clear();
@ -158,7 +160,7 @@ class LabViewModel extends ChangeNotifier {
filterSuggestions() {
final List<String> labels = patientLabOrders
.expand((order) => order.testDetails!)
.expand((order) => order.testDetails ?? [])
.map((detail) => detail.description)
.whereType<String>()
.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(
PatientLabOrdersResponseModel laborder) async {
isLabResultByHospitalLoading = true;

@ -2,12 +2,17 @@ import 'dart:async';
import 'package:flutter/foundation.dart' show ChangeNotifier;
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/PlaceDetails.dart';
import 'package:hmg_patient_app_new/features/location/location_repo.dart';
import 'package:hmg_patient_app_new/services/error_handler_service.dart';
import 'package:huawei_map/huawei_map.dart' as HMSCameraServices;
import 'package:google_maps_flutter/google_maps_flutter.dart' as GMSMapServices;
import 'PlacePrediction.dart';
@ -15,26 +20,48 @@ class LocationViewModel extends ChangeNotifier {
final LocationRepo locationRepo;
final ErrorHandlerService errorHandlerService;
LocationViewModel({required this.locationRepo, required this.errorHandlerService});
LocationViewModel({required this.locationRepo, required this.errorHandlerService}){
placeValueInController();
}
List<PlacePrediction> predictions = [];
PlacePrediction? selectedPrediction;
List<PlacePrediction> predictions = [];
PlacePrediction? selectedPrediction;
bool isPredictionLoading = false;
GeocodeResponse? geocodeResponse;
PlaceDetails? placeDetails;
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 {
predictions = [];
isPredictionLoading = true;
isPredictionLoading= true;
final result = await locationRepo.getPlacePredictionsAsInput(input);
result.fold(
(failure) {
errorHandlerService.handleError(failure: failure);
},
(apiModel) {
predictions = apiModel.data ?? [];
predictions = apiModel.data??[];
},
);
isPredictionLoading = false;
@ -72,20 +99,21 @@ class LocationViewModel extends ChangeNotifier {
handleGMSMapCameraMoved(GMSMapServices.CameraPosition value) {
mapCapturedLocation = Location(lat: value.target.latitude, lng: value.target.longitude);
}
handleHMSMapCameraMoved(HMSCameraServices.CameraPosition value) {
mapCapturedLocation = Location(lat: value.target.lat, lng: value.target.lng);
}
handleOnCameraIdle() {
if (mapCapturedLocation != null) {
handleOnCameraIdle(){
if(mapCapturedLocation != null) {
getPlaceEncodedData(mapCapturedLocation!.lat, mapCapturedLocation!.lng);
}
}
void updateSearchQuery(String? value) {
if (value == null || value.isEmpty) {
if(value == null || value.isEmpty){
predictions = [];
return;
}
@ -95,16 +123,50 @@ class LocationViewModel extends ChangeNotifier {
void flushSearchPredictions() {
predictions = [];
mapCapturedLocation = null;
placeDetails = null;
geocodeResponse = null;
selectedPrediction = null;
mapCapturedLocation= null;
placeDetails= null;
geocodeResponse= null;
selectedPrediction= null;
notifyListeners();
}
FutureOr<void> selectPlacePrediction(PlacePrediction placePrediction) async {
selectedPrediction = placePrediction;
FutureOr<void> selectPlacePrediction(PlacePrediction placePrediction) async{
selectedPrediction= placePrediction;
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(
{required PatientRadiologyResponseModel patientRadiologyResponseModel, required AuthenticatedUser authenticatedUser});
Future<Either<Failure, GenericApiModel<List<PatientRadiologyResponseModel>>>> getPatientRadiologyOrderByAppointment({required num appointmentNo, required num projectID});
}
class RadiologyRepoImp implements RadiologyRepo {
@ -168,4 +170,50 @@ class RadiologyRepoImp implements RadiologyRepo {
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: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/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/navigation_service.dart';
import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
import 'models/resp_models/patient_radiology_response_model.dart';
@ -11,6 +15,7 @@ class RadiologyViewModel extends ChangeNotifier {
RadiologyRepo radiologyRepo;
ErrorHandlerService errorHandlerService;
NavigationService navigationService;
List<PatientRadiologyResponseModel> patientRadiologyOrders = [];
List<PatientRadiologyResponseModel> filteredRadiologyOrders = [];
@ -22,7 +27,9 @@ class RadiologyViewModel extends ChangeNotifier {
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() {
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(
{required PatientRadiologyResponseModel patientRadiologyResponseModel, Function(dynamic)? onSuccess, Function(String)? onError}) async {
final result = await radiologyRepo.getRadiologyImage(patientRadiologyResponseModel: patientRadiologyResponseModel);
@ -107,23 +143,20 @@ class RadiologyViewModel extends ChangeNotifier {
},
);
}
filterSuggestions() {
final List<String> labels = patientRadiologyOrders
.map((detail) => detail.description)
.whereType<String>()
.toList();
final List<String> labels = patientRadiologyOrders.map((detail) => detail.description).whereType<String>().toList();
_radiologySuggestionsList = labels.toSet().toList();
notifyListeners();
}
filterRadiologyReports(String query) {
if (query.isEmpty) {
patientRadiologyOrders =tempRadiologyOrders;// reset
patientRadiologyOrders = tempRadiologyOrders; // reset
} else {
filteredRadiologyOrders =
filteredRadiologyOrders.where((desc) => desc.description!.toLowerCase().contains(query.toLowerCase())).toList();
filteredRadiologyOrders = filteredRadiologyOrders.where((desc) => desc.description!.toLowerCase().contains(query.toLowerCase())).toList();
patientRadiologyOrders = filteredRadiologyOrders;
}
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/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/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/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/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/radiology/radiology_view_model.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/widgets/appointment_checkin_bottom_sheet.dart';
import 'package:hmg_patient_app_new/presentation/appointments/widgets/appointment_doctor_card.dart';
import 'package:hmg_patient_app_new/presentation/appointments/widgets/ask_doctor_request_type_select.dart';
import 'package:hmg_patient_app_new/presentation/book_appointment/widgets/appointment_calendar.dart';
import 'package:hmg_patient_app_new/presentation/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/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/widgets/appbar/collapsing_list_view.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart';
import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart';
import 'package: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:provider/provider.dart';
@ -50,14 +56,17 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
late MyAppointmentsViewModel myAppointmentsViewModel;
late PrescriptionsViewModel prescriptionsViewModel;
late BookAppointmentsViewModel bookAppointmentsViewModel;
late ContactUsViewModel contactUsViewModel;
late LabViewModel labViewModel;
late RadiologyViewModel radiologyViewModel;
@override
void initState() {
scheduleMicrotask(() {
if (AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel)) {
prescriptionsViewModel.setPrescriptionsDetailsLoading();
prescriptionsViewModel.getPrescriptionDetails(getPrescriptionRequestModel());
}
// if (AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel)) {
// prescriptionsViewModel.setPrescriptionsDetailsLoading();
// prescriptionsViewModel.getPrescriptionDetails(getPrescriptionRequestModel());
// }
});
super.initState();
}
@ -68,6 +77,9 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
myAppointmentsViewModel = Provider.of<MyAppointmentsViewModel>(context, listen: false);
prescriptionsViewModel = Provider.of<PrescriptionsViewModel>(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(
backgroundColor: AppColors.bgScaffoldColor,
body: Column(
@ -75,7 +87,16 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
Expanded(
child: CollapsingListView(
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: Column(
crossAxisAlignment: CrossAxisAlignment.start,
@ -263,219 +284,345 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
SizedBox(height: 16.h),
],
)
: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
"Lab & Radiology".needTranslation.toText18(isBold: true),
SizedBox(height: 16.h),
GridView(
padding: EdgeInsets.zero,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: isTablet || isFoldable ? 3 : 2,
crossAxisSpacing: 13.w,
mainAxisSpacing: 13.w,
),
physics: NeverScrollableScrollPhysics(),
shrinkWrap: true,
children: [
MedicalFileCard(
label: LocaleKeys.labResults.tr(context: context),
textColor: AppColors.blackColor,
backgroundColor: AppColors.whiteColor,
svgIcon: AppAssets.lab_result_icon,
iconSize: 40.w,
isLargeText: true,
),
MedicalFileCard(
label: "Radiology Results".needTranslation,
textColor: AppColors.blackColor,
backgroundColor: AppColors.whiteColor,
svgIcon: AppAssets.radiology_icon,
iconSize: 40.w,
isLargeText: true,
),
MedicalFileCard(
label: LocaleKeys.labResults.tr(context: context),
textColor: AppColors.blackColor,
backgroundColor: AppColors.whiteColor,
svgIcon: AppAssets.lab_result_icon,
iconSize: 40.w,
isLargeText: true,
),
MedicalFileCard(
label: "Radiology Results".needTranslation,
textColor: AppColors.blackColor,
backgroundColor: AppColors.whiteColor,
svgIcon: AppAssets.radiology_icon,
iconSize: 40.w,
isLargeText: true,
),
],
),
SizedBox(height: 16.h),
LocaleKeys.prescriptions.tr().toText18(isBold: true),
SizedBox(height: 16.h),
Consumer<PrescriptionsViewModel>(builder: (context, prescriptionVM, child) {
return prescriptionVM.isPrescriptionsDetailsLoading
? const CommonShimmerWidget()
: 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,
),
),
],
),
],
// : SizedBox.shrink()
: GridView(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
crossAxisSpacing: 16.h,
mainAxisSpacing: 16.w,
mainAxisExtent: 115.h,
),
physics: NeverScrollableScrollPhysics(),
padding: EdgeInsets.zero,
shrinkWrap: true,
children: [
MedicalFileCard(
label: LocaleKeys.labResults.tr(context: context),
textColor: AppColors.blackColor,
backgroundColor: AppColors.whiteColor,
svgIcon: AppAssets.lab_result_icon,
isLargeText: true,
iconSize: 36.w,
).onPress(() async {
LoaderBottomSheet.showLoader(loadingText: "Fetching Lab Results...".needTranslation);
await labViewModel.getLabResultsByAppointmentNo(
appointmentNo: widget.patientAppointmentHistoryResponseModel.appointmentNo,
projectID: widget.patientAppointmentHistoryResponseModel.projectID,
clinicID: widget.patientAppointmentHistoryResponseModel.clinicID,
doctorID: widget.patientAppointmentHistoryResponseModel.doctorID,
doctorName: widget.patientAppointmentHistoryResponseModel.doctorNameObj!,
clinicName: widget.patientAppointmentHistoryResponseModel.clinicName!,
projectName: widget.patientAppointmentHistoryResponseModel.projectName!,
appointmentDate: widget.patientAppointmentHistoryResponseModel.appointmentDate!,
onSuccess: (val) {
LoaderBottomSheet.hideLoader();
},
onError: (err) {
LoaderBottomSheet.hideLoader();
showCommonBottomSheetWithoutHeight(
context,
child: Utils.getErrorWidget(loadingText: err),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
},
);
}),
MedicalFileCard(
label: "${LocaleKeys.radiology.tr(context: context)} ${LocaleKeys.radiologySubtitle.tr(context: context)}",
textColor: AppColors.blackColor,
backgroundColor: AppColors.whiteColor,
svgIcon: AppAssets.allergy_info_icon,
isLargeText: true,
iconSize: 36.w,
).onPress(() async {
LoaderBottomSheet.showLoader(loadingText: "Fetching Radiology Results...".needTranslation);
await radiologyViewModel.getPatientRadiologyOrdersByAppointment(
appointmentNo: widget.patientAppointmentHistoryResponseModel.appointmentNo,
projectID: widget.patientAppointmentHistoryResponseModel.projectID,
onSuccess: (val) {
LoaderBottomSheet.hideLoader();
},
onError: (err) {
LoaderBottomSheet.hideLoader();
showCommonBottomSheetWithoutHeight(
context,
child: Utils.getErrorWidget(loadingText: err),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
},
);
}),
MedicalFileCard(
label: LocaleKeys.prescriptions.tr(context: context),
textColor: AppColors.blackColor,
backgroundColor: AppColors.whiteColor,
svgIcon: AppAssets.prescription_item_icon,
isLargeText: true,
iconSize: 36.w,
).onPress(() async {
LoaderBottomSheet.showLoader(loadingText: "Fetching Appointment Prescriptions...".needTranslation);
await prescriptionsViewModel.getPrescriptionDetails(
getPrescriptionRequestModel(),
onSuccess: (val) {
LoaderBottomSheet.hideLoader();
if (val.data.isNotEmpty) {
PatientPrescriptionsResponseModel patientPrescriptionsResponseModel = PatientPrescriptionsResponseModel(
doctorImageURL: widget.patientAppointmentHistoryResponseModel.doctorImageURL,
doctorName: widget.patientAppointmentHistoryResponseModel.doctorNameObj,
appointmentDate: widget.patientAppointmentHistoryResponseModel.appointmentDate,
clinicDescription: widget.patientAppointmentHistoryResponseModel.clinicName,
decimalDoctorRate: widget.patientAppointmentHistoryResponseModel.decimalDoctorRate,
name: widget.patientAppointmentHistoryResponseModel.projectName,
isHomeMedicineDeliverySupported: false,
projectID: widget.patientAppointmentHistoryResponseModel.projectID,
clinicID: widget.patientAppointmentHistoryResponseModel.clinicID,
doctorID: widget.patientAppointmentHistoryResponseModel.doctorID,
setupID: widget.patientAppointmentHistoryResponseModel.setupID,
);
Navigator.of(context).push(
CustomPageRoute(
page: PrescriptionDetailPage(isFromAppointments: true, prescriptionsResponseModel: patientPrescriptionsResponseModel),
),
);
}),
],
),
} 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),
),

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

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

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

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

@ -65,25 +65,28 @@ class DoctorCard extends StatelessWidget {
.toString()
.toText16(isBold: true, maxlines: 1),
).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(
isLoading
? "https://hmgwebservices.com/Images/flag/SYR.png"
: doctorsListResponseModel.nationalityFlagURL ?? "https://hmgwebservices.com/Images/flag/SYR.png",
isLoading ? "https://hmgwebservices.com/Images/flag/SYR.png" : doctorsListResponseModel.nationalityFlagURL ?? "https://hmgwebservices.com/Images/flag/SYR.png",
width: 20.h,
height: 15.h,
fit: BoxFit.fill,
).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/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/models/feedback_type.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/live_chat_page.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
@ -51,7 +53,17 @@ class ContactUs extends StatelessWidget {
AppAssets.checkin_location_icon,
LocaleKeys.feedback.tr(),
"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),
checkInOptionCard(
AppAssets.checkin_location_icon,
@ -59,6 +71,7 @@ class ContactUs extends StatelessWidget {
"Live chat option with HMG".needTranslation,
).onPress(() {
locationUtils.getCurrentLocation(onSuccess: (value) {
contactUsViewModel.getLiveChatProjectsList();
Navigator.pop(context);
Navigator.of(context).push(
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: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/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart';
import 'package:provider/provider.dart';
import 'package:url_launcher/url_launcher.dart';
class LiveChatPage extends StatelessWidget {
const LiveChatPage({super.key});
LiveChatPage({super.key});
String chatURL = "";
late AppState appState;
@override
Widget build(BuildContext context) {
appState = getIt.get<AppState>();
return Scaffold(
backgroundColor: AppColors.bgScaffoldColor,
body: Column(
children: [
Expanded(
child: CollapsingListView(
title: LocaleKeys.liveChat.tr(),
child: SingleChildScrollView(),
body: Consumer<ContactUsViewModel>(builder: (context, contactUsVM, child) {
return Column(
children: [
Expanded(
child: CollapsingListView(
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/input_widget.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 '../../../widgets/common_bottom_sheet.dart';
@ -61,10 +61,10 @@ class RrtMapScreen extends StatelessWidget {
BottomSheetType.EXPANDED: ExpanedBottomSheet(context),
BottomSheetType.FIXED: FixedBottomSheet(context),
},
).paddingAll(16.h),
),
body: Stack(
children: [
if (context.read<EmergencyServicesViewModel>().isGMSAvailable )
if (context.read<EmergencyServicesViewModel>().isGMSAvailable)
GMSMap(
currentLocation:
context.read<EmergencyServicesViewModel>().getGMSLocation(),
@ -116,15 +116,13 @@ class RrtMapScreen extends StatelessWidget {
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
spacing: 24.h,
children: [
inputFields(context),
inputFields(context).paddingSymmetrical(16.h, 0.h),
SizedBox(
height: 200.h,
child: DecoratedBox(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.scaffoldBgColor,
@ -140,16 +138,14 @@ class RrtMapScreen extends StatelessWidget {
Column(
spacing: 4.h,
children: [
"Select Pickup Details".needTranslation.toText21(
weight: FontWeight.w600,
color: AppColors.textColor,
),
" Please select the details of pickup"
.needTranslation
.toText12(
fontWeight: FontWeight.w500,
color: AppColors.greyTextColor,
)
"Select Location".needTranslation.toText21(
weight: FontWeight.w600,
color: AppColors.textColor,
),
"Please select the location".needTranslation.toText12(
fontWeight: FontWeight.w500,
color: AppColors.greyTextColor,
)
],
),
CustomButton(
@ -160,7 +156,8 @@ class RrtMapScreen extends StatelessWidget {
PlaceDetails? placeDetails = locationViewModel.placeDetails;
PlacePrediction? placePrediction = locationViewModel.selectedPrediction;
context.read<EmergencyServicesViewModel>().submitRRTRequest(response?.results.first, placeDetails, placePrediction);
})
},
)
],
).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
inputFields(BuildContext context) {
Widget inputFields(BuildContext 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/input_widget.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 '../../../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/chip/app_custom_chip_widget.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/request_tracking.dart';
import 'package:lottie/lottie.dart';

@ -256,7 +256,9 @@ class EmergencyServicesPage extends StatelessWidget {
isCloseButtonVisible: false,
hasBottomPadding: false,
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),
],
),
SizedBox(height: 4.h),
if (order.statusId == 1 || order.statusId == 2)
CustomButton(
text: "Cancel Request".needTranslation,
@ -55,8 +55,8 @@ class RRTItem extends StatelessWidget {
borderColor: AppColors.primaryRedColor,
textColor: Colors.white,
icon: AppAssets.cancel,
iconSize: 20.h,
height: 40.h,
iconSize: 18.w,
),
],
).paddingAll(16.h),

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

@ -47,7 +47,7 @@ class ServicesPage extends StatelessWidget {
Widget build(BuildContext context) {
return CollapsingListView(
title: "Explore Services".needTranslation,
isLeading: false,
isLeading: Navigator.canPop(context),
child: Padding(
padding: EdgeInsets.all(24.h),
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/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/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/widgets/habib_wallet_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,
axisDirection: AxisDirection.right,
controller: _controller,
itemHeight: 210 + 25,
itemHeight: 200.h,
pagination: const SwiperPagination(
alignment: Alignment.bottomCenter,
margin: EdgeInsets.only(top: 210 + 8 + 24),
@ -439,7 +440,7 @@ class _LandingPageState extends State<LandingPage> {
}),
SizedBox(height: 16.h),
Container(
height: 120.h,
height: 121.h,
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.r),
child: Column(
children: [
@ -526,11 +527,13 @@ class _LandingPageState extends State<LandingPage> {
SizedBox(width: 2.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),
SizedBox(
height: 280.h,
height: 340.h,
child: ListView.separated(
scrollDirection: Axis.horizontal,
itemCount: LandingPageData.getServiceCardsList.length,
@ -567,9 +570,8 @@ class _LandingPageState extends State<LandingPage> {
void showQuickLogin(BuildContext context) {
showCommonBottomSheetWithoutHeight(
context,
title: "",
// title: "",
isCloseButtonVisible: false,
child: StatefulBuilder(
builder: (context, setState) {
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/dependencies.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/home/landing_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(),
children: [
const LandingPage(),
appState.isAuthenticated ? MedicalFilePage() : /* need add feedback page */ const LandingPage(),
appState.isAuthenticated ? MedicalFilePage() : /* need add feedback page */ FeedbackPage(),
BookAppointmentPage(),
const ToDoPage(),
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),
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),
Row(
mainAxisSize: MainAxisSize.max,
@ -58,22 +58,25 @@ class LabOrderResultItem extends StatelessWidget {
fontSize: 24.f,
fontWeight: FontWeight.w600,
fontFamily: 'Poppins',
color: context.read<LabViewModel>().getColor(
tests?.calculatedResultFlag ?? "",
),
color: tests!.checkIfGraphShouldBeDisplayed()
? context.read<LabViewModel>().getColor(
tests?.calculatedResultFlag ?? "",
)
: Colors.grey.shade700,
letterSpacing: -2,
),
),
),
SizedBox(width: 4.h,),
Visibility(
visible: tests?.checkIfGraphShouldBeDisplayed() == true,
// visible: tests?.checkIfGraphShouldBeDisplayed() == true,
visible: true,
child: Expanded(
flex: 2,
child: Visibility(
visible: tests?.referanceRange != null,
child: Text(
"(Reference range ${tests?.referanceRange})".needTranslation,
"(Reference range: ${tests?.referanceRange})".needTranslation,
style: TextStyle(
fontSize: 12.f,
fontWeight: FontWeight.w500,

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

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

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

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

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

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

@ -54,7 +54,7 @@ class CollapsingListView extends StatelessWidget {
SliverAppBar(
automaticallyImplyLeading: false,
pinned: true,
expandedHeight: MediaQuery.of(context).size.height * 0.12.h,
expandedHeight: MediaQuery.of(context).size.height * 0.11.h,
stretch: true,
systemOverlayStyle: SystemUiOverlayStyle(statusBarBrightness: Brightness.light),
surfaceTintColor: Colors.transparent,
@ -92,8 +92,7 @@ class CollapsingListView extends StatelessWidget {
t,
)!,
child: Padding(
padding: EdgeInsets.only(
left: appState.isArabic() ? 0 : leftPadding, right: appState.isArabic() ? leftPadding : 0, bottom: bottomPadding),
padding: EdgeInsets.only(left: appState.isArabic() ? 0 : leftPadding, right: appState.isArabic() ? leftPadding : 0, bottom: bottomPadding),
child: Row(
spacing: 4.h,
children: [
@ -110,18 +109,11 @@ class CollapsingListView extends StatelessWidget {
color: AppColors.blackColor,
letterSpacing: -0.5),
).expanded,
if (logout != null)
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 (history != null)
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 (logout != null) 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 (history != null) 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 (trailing != null) trailing!,
],

@ -100,7 +100,7 @@ class AppCustomChipWidget extends StatelessWidget {
)
: Chip(
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,
backgroundColor: backgroundColor,
shape: shape ??
@ -109,7 +109,7 @@ class AppCustomChipWidget extends StatelessWidget {
smoothness: 10,
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
? InkWell(
onTap: onDeleteTap,

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

@ -43,6 +43,11 @@ class TextInputWidget extends StatelessWidget {
final Color? labelColor;
final Function(String)? onSubmitted;
// new multiline options
final bool isMultiline;
final int minLines;
final int maxLines;
// final List<Country> countryList;
// final Function(Country)? onCountryChange;
@ -73,10 +78,14 @@ class TextInputWidget extends StatelessWidget {
this.isWalletAmountInput = false,
this.suffix,
this.labelColor,
this.onSubmitted
// this.countryList = const [],
// this.onCountryChange,
});
this.onSubmitted,
// multiline defaults
this.isMultiline = false,
this.minLines = 3,
this.maxLines = 6,
// this.countryList = const [],
// this.onCountryChange,
});
final FocusNode _focusNode = FocusNode();
@ -113,7 +122,7 @@ class TextInputWidget extends StatelessWidget {
children: [
Container(
padding: padding,
height: 64.h,
height: isMultiline ? null : 64.h,
alignment: Alignment.center,
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: Colors.white,
@ -205,9 +214,7 @@ class TextInputWidget extends StatelessWidget {
initialDate: DateTime.now(),
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)),
cancelWidget: Padding(
padding: EdgeInsets.only(right: 8.h),
child: Utils.buildSvgWithAssets(icon: AppAssets.cancel, iconColor: Colors.white, width: 24.h, height: 24.h)),
cancelWidget: Padding(padding: EdgeInsets.only(right: 8.h), child: Utils.buildSvgWithAssets(icon: AppAssets.cancel, iconColor: Colors.white, width: 24.h, height: 24.h)),
onCalendarTypeChanged: (bool value) {
isGregorian = value;
});
@ -240,7 +247,7 @@ class TextInputWidget extends StatelessWidget {
return TextField(
enabled: isEnable,
scrollPadding: EdgeInsets.zero,
keyboardType: keyboardType,
keyboardType: isMultiline ? TextInputType.multiline : keyboardType,
controller: controller,
readOnly: isReadOnly,
textAlignVertical: TextAlignVertical.top,
@ -255,7 +262,15 @@ class TextInputWidget extends StatelessWidget {
FocusManager.instance.primaryFocus?.unfocus();
},
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(
isDense: true,
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_export.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' ;
class HMSMap extends StatefulWidget{
@ -55,7 +57,7 @@ class _HMSMapState extends State<HMSMap> {
visible: widget.showCenterMarker,
child: Align(
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/utils/size_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{
Completer<GoogleMapController>? controller;
@ -29,14 +31,15 @@ class GMSMap extends StatelessWidget{
children: [
GoogleMap(
mapType: mapType,
zoomControlsEnabled: false,
myLocationEnabled: myLocationEnabled,
myLocationButtonEnabled: false,
zoomControlsEnabled: true,
myLocationEnabled: myLocationEnabled,
myLocationButtonEnabled: false,
compassEnabled: compassEnabled,
initialCameraPosition: currentLocation,
onCameraMove: (value) => onCameraMoved(value),
onCameraIdle: ()=>onCameraIdle(),
onMapCreated: (GoogleMapController controller) {
// padding: EdgeInsets.only(bottom: 300.h),
onMapCreated: (GoogleMapController controller) {
this.controller?.complete(controller);
},
),
@ -44,10 +47,10 @@ class GMSMap extends StatelessWidget{
visible: showCenterMarker,
child: Align(
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