diff --git a/lib/core/api/api_client.dart b/lib/core/api/api_client.dart index 3fb1470..aa534f6 100644 --- a/lib/core/api/api_client.dart +++ b/lib/core/api/api_client.dart @@ -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); diff --git a/lib/core/api_consts.dart b/lib/core/api_consts.dart index 64d9d28..a1f4062 100644 --- a/lib/core/api_consts.dart +++ b/lib/core/api_consts.dart @@ -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"; diff --git a/lib/core/dependencies.dart b/lib/core/dependencies.dart index 8fa6f89..73a93c6 100644 --- a/lib/core/dependencies.dart +++ b/lib/core/dependencies.dart @@ -122,7 +122,13 @@ class AppDependencies { getIt.registerLazySingleton(() => LabViewModel(labRepo: getIt(), errorHandlerService: getIt(), navigationService: getIt())); - getIt.registerLazySingleton(() => RadiologyViewModel(radiologyRepo: getIt(), errorHandlerService: getIt())); + getIt.registerLazySingleton( + () => RadiologyViewModel( + radiologyRepo: getIt(), + errorHandlerService: getIt(), + navigationService: getIt() + ), + ); getIt.registerLazySingleton(() => PrescriptionsViewModel(prescriptionsRepo: getIt(), errorHandlerService: getIt())); diff --git a/lib/extensions/string_extensions.dart b/lib/extensions/string_extensions.dart index 3c1765d..250453d 100644 --- a/lib/extensions/string_extensions.dart +++ b/lib/extensions/string_extensions.dart @@ -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( diff --git a/lib/features/contact_us/contact_us_repo.dart b/lib/features/contact_us/contact_us_repo.dart index f2b1169..3e96f91 100644 --- a/lib/features/contact_us/contact_us_repo.dart +++ b/lib/features/contact_us/contact_us_repo.dart @@ -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>>> getHMGLocations(); Future>>> getLiveChatProjectsList(); + + Future>> 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)).toList().cast(); + final liveChatProjectsList = list.map((item) => GetPatientICProjectsModel.fromJson(item as Map)).toList().cast(); apiResponse = GenericApiModel>( 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>> insertCOCItem({required RequestInsertCOCItem requestInsertCOCItem, PatientAppointmentHistoryResponseModel? patientSelectedAppointment}) async { + final Map 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? 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( + messageStatus: messageStatus, + statusCode: statusCode, + errorMessage: null, + data: response, ); } catch (e) { failure = DataParsingFailure(e.toString()); diff --git a/lib/features/contact_us/contact_us_view_model.dart b/lib/features/contact_us/contact_us_view_model.dart index 7826bd1..1185700 100644 --- a/lib/features/contact_us/contact_us_view_model.dart +++ b/lib/features/contact_us/contact_us_view_model.dart @@ -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 hmgHospitalsLocationsList = []; List hmgPharmacyLocationsList = []; + List liveChatProjectsList = []; + + int selectedLiveChatProjectIndex = -1; + + List feedbackAttachmentList = []; + + PatientAppointmentHistoryResponseModel? patientFeedbackSelectedAppointment; + + List 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 getHMGLocations({Function(dynamic)? onSuccess, Function(String)? onError}) async { isHMGLocationsListLoading = true; hmgHospitalsLocationsList.clear(); @@ -62,4 +126,73 @@ class ContactUsViewModel extends ChangeNotifier { }, ); } + + Future 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 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); + } + } + }, + ); + } } diff --git a/lib/features/contact_us/models/feedback_type.dart b/lib/features/contact_us/models/feedback_type.dart new file mode 100644 index 0000000..ff1025a --- /dev/null +++ b/lib/features/contact_us/models/feedback_type.dart @@ -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, + }); +} diff --git a/lib/features/contact_us/models/req_models/request_insert_coc_item.dart b/lib/features/contact_us/models/req_models/request_insert_coc_item.dart new file mode 100644 index 0000000..e285999 --- /dev/null +++ b/lib/features/contact_us/models/req_models/request_insert_coc_item.dart @@ -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 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 toJson() { + final Map data = new Map(); + 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; + } +} diff --git a/lib/features/emergency_services/emergency_services_view_model.dart b/lib/features/emergency_services/emergency_services_view_model.dart index 5dbb89d..ad72ef4 100644 --- a/lib/features/emergency_services/emergency_services_view_model.dart +++ b/lib/features/emergency_services/emergency_services_view_model.dart @@ -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(); + GeocodeResponse? response = locationViewModel.geocodeResponse; + PlaceDetails? placeDetails = locationViewModel.placeDetails; + PlacePrediction? placePrediction = locationViewModel.selectedPrediction; + submitRRTRequest(response?.results.first, placeDetails, placePrediction); + } + }); } else{ dialogService.showErrorBottomSheet( diff --git a/lib/features/lab/lab_repo.dart b/lib/features/lab/lab_repo.dart index 3bb793b..e661157 100644 --- a/lib/features/lab/lab_repo.dart +++ b/lib/features/lab/lab_repo.dart @@ -21,6 +21,7 @@ abstract class LabRepo { Future>> getLabResultReportPDF({required PatientLabOrdersResponseModel labOrder}); + Future>> 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>? 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>? apiResponse; Failure? failure; @@ -278,4 +281,41 @@ class LabRepoImp implements LabRepo { return Left(UnknownFailure(e.toString())); } } + + @override + Future> getLabResultsByAppointmentNo({required num appointmentNo, required num projectID, required num clinicID}) async { + Map request = {}; + request['AppointmentNo'] = appointmentNo; + request['ProjectID'] = projectID; + request['ClinicID'] = clinicID; + + try { + GenericApiModel? 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( + 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())); + } + } } diff --git a/lib/features/lab/lab_view_model.dart b/lib/features/lab/lab_view_model.dart index 12f0f27..bad4f89 100644 --- a/lib/features/lab/lab_view_model.dart +++ b/lib/features/lab/lab_view_model.dart @@ -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 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 labels = patientLabOrders - .expand((order) => order.testDetails!) + .expand((order) => order.testDetails ?? []) .map((detail) => detail.description) .whereType() .toList(); @@ -198,6 +200,69 @@ class LabViewModel extends ChangeNotifier { } } + Future 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 getPatientLabResultByHospital( PatientLabOrdersResponseModel laborder) async { isLabResultByHospitalLoading = true; diff --git a/lib/features/location/location_view_model.dart b/lib/features/location/location_view_model.dart index bf5f18a..c6ea34e 100644 --- a/lib/features/location/location_view_model.dart +++ b/lib/features/location/location_view_model.dart @@ -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 predictions = []; - PlacePrediction? selectedPrediction; + List predictions = []; + PlacePrediction? selectedPrediction; bool isPredictionLoading = false; GeocodeResponse? geocodeResponse; PlaceDetails? placeDetails; Location? mapCapturedLocation; + Completer? gmsController; + Completer? hmsController; + + HMSCameraServices.CameraPosition getHMSLocation() { + return HMSCameraServices.CameraPosition(target: HMSCameraServices.LatLng(getIt().userLat, getIt().userLong), zoom: 18); + } + + + GMSMapServices.CameraPosition getGMSLocation() { + return GMSMapServices.CameraPosition(target: GMSMapServices.LatLng(getIt().userLat, getIt().userLong), zoom: 18); + } + + void placeValueInController() async{ + if (await getIt().isGMSAvailable) { + gmsController = Completer(); + } else { + hmsController = Completer(); + } + } + FutureOr 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 selectPlacePrediction(PlacePrediction placePrediction) async { - selectedPrediction = placePrediction; + FutureOr selectPlacePrediction(PlacePrediction placePrediction) async{ + selectedPrediction= placePrediction; await getPlaceDetails(placePrediction.placeID); } -} + + void moveToCurrentLocation() { + moveController(Location(lat: getIt().userLat, lng: getIt().userLong)); + } + + void moveController(Location location) { + print("moving to location"); + print("gmsController is null or not $gmsController"); + if (getIt().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, + ), + ), + ); + }); + } + } +} \ No newline at end of file diff --git a/lib/features/radiology/radiology_repo.dart b/lib/features/radiology/radiology_repo.dart index b81fd50..2af3e0f 100644 --- a/lib/features/radiology/radiology_repo.dart +++ b/lib/features/radiology/radiology_repo.dart @@ -15,6 +15,8 @@ abstract class RadiologyRepo { Future>> getRadiologyReportPDF( {required PatientRadiologyResponseModel patientRadiologyResponseModel, required AuthenticatedUser authenticatedUser}); + + Future>>> 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>>> getPatientRadiologyOrderByAppointment({required num appointmentNo, required num projectID}) async { + Map mapDevice = { + "AppointmentNo": appointmentNo, + "ProjectID": projectID, + }; + + try { + GenericApiModel>? 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)).toList().cast(); + } else { + final list = response['FinalRadiologyListAPI']; + radOrders = list.map((item) => PatientRadiologyResponseModel.fromJson(item as Map)).toList().cast(); + } + + apiResponse = GenericApiModel>( + 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())); + } + } } diff --git a/lib/features/radiology/radiology_view_model.dart b/lib/features/radiology/radiology_view_model.dart index d39d84f..4a87906 100644 --- a/lib/features/radiology/radiology_view_model.dart +++ b/lib/features/radiology/radiology_view_model.dart @@ -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 patientRadiologyOrders = []; List filteredRadiologyOrders = []; @@ -22,7 +27,9 @@ class RadiologyViewModel extends ChangeNotifier { List 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 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 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 labels = patientRadiologyOrders - .map((detail) => detail.description) - .whereType() - .toList(); + final List labels = patientRadiologyOrders.map((detail) => detail.description).whereType().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(); } - } diff --git a/lib/presentation/appointments/appointment_details_page.dart b/lib/presentation/appointments/appointment_details_page.dart index 12f3589..e620783 100644 --- a/lib/presentation/appointments/appointment_details_page.dart +++ b/lib/presentation/appointments/appointment_details_page.dart @@ -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 { 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 { myAppointmentsViewModel = Provider.of(context, listen: false); prescriptionsViewModel = Provider.of(context, listen: false); bookAppointmentsViewModel = Provider.of(context, listen: false); + contactUsViewModel = Provider.of(context, listen: false); + labViewModel = Provider.of(context, listen: false); + radiologyViewModel = Provider.of(context, listen: false); return Scaffold( backgroundColor: AppColors.bgScaffoldColor, body: Column( @@ -75,7 +87,16 @@ class _AppointmentDetailsPageState extends State { 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 { 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(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(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), ), diff --git a/lib/presentation/appointments/widgets/appointment_card.dart b/lib/presentation/appointments/widgets/appointment_card.dart index e451088..3cf56a6 100644 --- a/lib/presentation/appointments/widgets/appointment_card.dart +++ b/lib/presentation/appointments/widgets/appointment_card.dart @@ -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( diff --git a/lib/presentation/authentication/quick_login.dart b/lib/presentation/authentication/quick_login.dart index f10d84f..bdeb0ff 100644 --- a/lib/presentation/authentication/quick_login.dart +++ b/lib/presentation/authentication/quick_login.dart @@ -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 { + final CacheService cacheService = GetIt.instance(); + @override Widget build(BuildContext context) { NavigationService navigationService = getIt.get(); - 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 { 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 { 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", )), ], diff --git a/lib/presentation/book_appointment/doctor_filter/clinic_item.dart b/lib/presentation/book_appointment/doctor_filter/clinic_item.dart index 0d5ba76..0771d1b 100644 --- a/lib/presentation/book_appointment/doctor_filter/clinic_item.dart +++ b/lib/presentation/book_appointment/doctor_filter/clinic_item.dart @@ -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, diff --git a/lib/presentation/book_appointment/widgets/appointment_calendar.dart b/lib/presentation/book_appointment/widgets/appointment_calendar.dart index d695ea6..54ff282 100644 --- a/lib/presentation/book_appointment/widgets/appointment_calendar.dart +++ b/lib/presentation/book_appointment/widgets/appointment_calendar.dart @@ -140,8 +140,9 @@ class _AppointmentCalendarState extends State { }, ), ), + 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 diff --git a/lib/presentation/book_appointment/widgets/doctor_card.dart b/lib/presentation/book_appointment/widgets/doctor_card.dart index 1a91f01..4cd147a 100644 --- a/lib/presentation/book_appointment/widgets/doctor_card.dart +++ b/lib/presentation/book_appointment/widgets/doctor_card.dart @@ -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), ], ), ), diff --git a/lib/presentation/contact_us/contact_us.dart b/lib/presentation/contact_us/contact_us.dart index 6890fb4..d7ea9c5 100644 --- a/lib/presentation/contact_us/contact_us.dart +++ b/lib/presentation/contact_us/contact_us.dart @@ -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( diff --git a/lib/presentation/contact_us/feedback_page.dart b/lib/presentation/contact_us/feedback_page.dart new file mode 100644 index 0000000..db7c218 --- /dev/null +++ b/lib/presentation/contact_us/feedback_page.dart @@ -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(context, listen: false); + medicalFileViewModel = Provider.of(context, listen: false); + return Scaffold( + backgroundColor: AppColors.bgScaffoldColor, + body: Consumer(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().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( + title: Text( + getIt.get().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(); + } + } +} diff --git a/lib/presentation/contact_us/live_chat_page.dart b/lib/presentation/contact_us/live_chat_page.dart index aced678..7cbdee3 100644 --- a/lib/presentation/contact_us/live_chat_page.dart +++ b/lib/presentation/contact_us/live_chat_page.dart @@ -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(); return Scaffold( backgroundColor: AppColors.bgScaffoldColor, - body: Column( - children: [ - Expanded( - child: CollapsingListView( - title: LocaleKeys.liveChat.tr(), - child: SingleChildScrollView(), + body: Consumer(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().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() - ], - ), + ], + ); + }), ); } } diff --git a/lib/presentation/contact_us/widgets/feedback_appointment_selection.dart b/lib/presentation/contact_us/widgets/feedback_appointment_selection.dart new file mode 100644 index 0000000..ca04069 --- /dev/null +++ b/lib/presentation/contact_us/widgets/feedback_appointment_selection.dart @@ -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(context, listen: false); + contactUsViewModel = Provider.of(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(context, listen: false), + bookAppointmentsViewModel: Provider.of(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), + ], + ), + ); + } +} diff --git a/lib/presentation/emergency_services/RRT/rrt_map_screen.dart b/lib/presentation/emergency_services/RRT/rrt_map_screen.dart index b38b9f2..3a17e5d 100644 --- a/lib/presentation/emergency_services/RRT/rrt_map_screen.dart +++ b/lib/presentation/emergency_services/RRT/rrt_map_screen.dart @@ -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().isGMSAvailable ) + if (context.read().isGMSAvailable) GMSMap( currentLocation: context.read().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().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); } diff --git a/lib/presentation/emergency_services/call_ambulance/call_ambulance_page.dart b/lib/presentation/emergency_services/call_ambulance/call_ambulance_page.dart index fea52a3..043c231 100644 --- a/lib/presentation/emergency_services/call_ambulance/call_ambulance_page.dart +++ b/lib/presentation/emergency_services/call_ambulance/call_ambulance_page.dart @@ -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'; diff --git a/lib/presentation/emergency_services/call_ambulance/tracking_screen.dart b/lib/presentation/emergency_services/call_ambulance/tracking_screen.dart index 4a9231a..e5dc0b9 100644 --- a/lib/presentation/emergency_services/call_ambulance/tracking_screen.dart +++ b/lib/presentation/emergency_services/call_ambulance/tracking_screen.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'; diff --git a/lib/presentation/emergency_services/emergency_services_page.dart b/lib/presentation/emergency_services/emergency_services_page.dart index 8f6fd23..bce7daf 100644 --- a/lib/presentation/emergency_services/emergency_services_page.dart +++ b/lib/presentation/emergency_services/emergency_services_page.dart @@ -256,7 +256,9 @@ class EmergencyServicesPage extends StatelessWidget { isCloseButtonVisible: false, hasBottomPadding: false, backgroundColor: AppColors.primaryRedColor, - callBackFunc: () {}, + callBackFunc: () { + context.read().setTermsAndConditions(false); + }, ); }), ), diff --git a/lib/presentation/emergency_services/history/widget/rrt_item.dart b/lib/presentation/emergency_services/history/widget/rrt_item.dart index 0a3058d..61ecced 100644 --- a/lib/presentation/emergency_services/history/widget/rrt_item.dart +++ b/lib/presentation/emergency_services/history/widget/rrt_item.dart @@ -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), diff --git a/lib/presentation/habib_wallet/wallet_payment_confirm_page.dart b/lib/presentation/habib_wallet/wallet_payment_confirm_page.dart index b414799..8be6ce2 100644 --- a/lib/presentation/habib_wallet/wallet_payment_confirm_page.dart +++ b/lib/presentation/habib_wallet/wallet_payment_confirm_page.dart @@ -90,7 +90,7 @@ class _WalletPaymentConfirmPageState extends State { 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 { 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, diff --git a/lib/presentation/hmg_services/services_page.dart b/lib/presentation/hmg_services/services_page.dart index 65bed10..af576aa 100644 --- a/lib/presentation/hmg_services/services_page.dart +++ b/lib/presentation/hmg_services/services_page.dart @@ -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( diff --git a/lib/presentation/home/landing_page.dart b/lib/presentation/home/landing_page.dart index 3347902..1007547 100644 --- a/lib/presentation/home/landing_page.dart +++ b/lib/presentation/home/landing_page.dart @@ -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 { 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 { }), 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 { 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 { void showQuickLogin(BuildContext context) { showCommonBottomSheetWithoutHeight( context, - title: "", + // title: "", isCloseButtonVisible: false, - child: StatefulBuilder( builder: (context, setState) { return QuickLogin( diff --git a/lib/presentation/home/navigation_screen.dart b/lib/presentation/home/navigation_screen.dart index 0599969..18803cc 100644 --- a/lib/presentation/home/navigation_screen.dart +++ b/lib/presentation/home/navigation_screen.dart @@ -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 { 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(), diff --git a/lib/presentation/lab/lab_orders_page.dart b/lib/presentation/lab/lab_orders_page.dart index 0b9d093..4ffd979 100644 --- a/lib/presentation/lab/lab_orders_page.dart +++ b/lib/presentation/lab/lab_orders_page.dart @@ -1 +1 @@ -import 'dart:async'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_staggered_animations/flutter_staggered_animations.dart'; import 'package:hmg_patient_app_new/core/app_state.dart'; import 'package:hmg_patient_app_new/core/dependencies.dart'; import 'package:hmg_patient_app_new/core/enums.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/lab/models/resp_models/patient_lab_orders_response_model.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/features/lab/lab_view_model.dart'; import 'package:hmg_patient_app_new/presentation/lab/lab_order_by_test.dart'; import 'package:hmg_patient_app_new/presentation/lab/lab_result_item_view.dart'; import 'package:hmg_patient_app_new/presentation/lab/lab_result_via_clinic/LabResultByClinic.dart'; import 'package:hmg_patient_app_new/presentation/lab/search_lab_report.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/widgets/chip/custom_chip_widget.dart'; import 'package:hmg_patient_app_new/widgets/date_range_selector/viewmodel/date_range_view_model.dart'; import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart'; import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart'; import 'package:provider/provider.dart'; import 'package:hmg_patient_app_new/widgets/custom_tab_bar.dart'; import '../../widgets/appbar/collapsing_list_view.dart'; class LabOrdersPage extends StatefulWidget { const LabOrdersPage({super.key}); @override State createState() => _LabOrdersPageState(); } class _LabOrdersPageState extends State { late LabViewModel labProvider; late DateRangeSelectorRangeViewModel rangeViewModel; late AppState _appState; List?> labSuggestions = []; int? expandedIndex; String? selectedFilterText = ''; int activeIndex = 0; @override void initState() { scheduleMicrotask(() { labProvider.initLabProvider(); }); super.initState(); } @override Widget build(BuildContext context) { labProvider = Provider.of(context, listen: false); rangeViewModel = Provider.of(context); _appState = getIt(); return Scaffold( backgroundColor: AppColors.bgScaffoldColor, body: CollapsingListView( title: LocaleKeys.labResults.tr(), search: () async { final lavVM = Provider.of(context, listen: false); if (lavVM.isLabOrdersLoading) { return; } else { String? value = await Navigator.of(context).push( CustomPageRoute( page: SearchLabResultsContent(labSuggestionsList: lavVM.labSuggestions), fullScreenDialog: true, direction: AxisDirection.down, ), ); if (value != null) { selectedFilterText = value; lavVM.filterLabReports(value); } } }, child: SingleChildScrollView( padding: EdgeInsets.all(24.h), physics: NeverScrollableScrollPhysics(), child: Consumer( builder: (context, model, child) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox(height: 16.h), CustomTabBar( activeTextColor: Color(0xffED1C2B), activeBackgroundColor: Color(0xffED1C2B).withValues(alpha: .1), tabs: [ CustomTabBarModel(null, "By Visit".needTranslation), CustomTabBarModel(null, "By Test".needTranslation), // CustomTabBarModel(null, "Completed".needTranslation), ], onTabChange: (index) { activeIndex = index; setState(() {}); }, ), SizedBox(height: 16.h), selectedFilterText!.isNotEmpty ? CustomChipWidget( chipText: selectedFilterText!, chipType: ChipTypeEnum.alert, isSelected: true, ) : SizedBox(), activeIndex == 0 ? ListView.builder( shrinkWrap: true, physics: NeverScrollableScrollPhysics(), padding: EdgeInsets.zero, itemCount: model.isLabOrdersLoading ? 5 : model.patientLabOrders.isNotEmpty ? model.patientLabOrders.length : 1, itemBuilder: (context, index) { final isExpanded = expandedIndex == index; return model.isLabOrdersLoading ? LabResultItemView( onTap: () {}, labOrder: null, index: index, isLoading: true, ) : model.patientLabOrders.isNotEmpty ? AnimationConfiguration.staggeredList( position: index, duration: const Duration(milliseconds: 500), child: SlideAnimation( verticalOffset: 100.0, child: FadeInAnimation( child: LabResultItemView( onTap: () { model.currentlySelectedPatientOrder = model.patientLabOrders[ index]; labProvider.getPatientLabResultByHospital(model.patientLabOrders[ index]); labProvider .getPatientSpecialResult( model.patientLabOrders[ index]); Navigator.push( context, CustomPageRoute( page: LabResultByClinic(labOrder: model.patientLabOrders[index]), )); }, labOrder: model.patientLabOrders[index], index: index, isExpanded: isExpanded), ), ), ) : Utils.getNoDataWidget(context, noDataText: "You don't have any lab results yet.".needTranslation); }, ) : ListView.builder( shrinkWrap: true, physics: NeverScrollableScrollPhysics(), padding: EdgeInsets.zero, itemCount: model.isLabOrdersLoading ? 5 : model.uniqueTests.toList().isNotEmpty ? model.uniqueTests.toList().length : 1, itemBuilder: (context, index) { final isExpanded = expandedIndex == index; return model.isLabOrdersLoading ? LabResultItemView( onTap: () {}, labOrder: null, index: index, isLoading: true, ) : model.uniqueTests.toList().isNotEmpty ? AnimationConfiguration.staggeredList( position: index, duration: const Duration(milliseconds: 500), child: SlideAnimation( verticalOffset: 100.0, child: FadeInAnimation( child: LabOrderByTest( appState: _appState, onTap: () { if (model.uniqueTests.toList()[index].model != null) { rangeViewModel.flush(); model.getPatientLabResult(model.uniqueTests.toList()[index].model!, model.uniqueTests.toList()[index].description!, (_appState.isArabic() ? model.uniqueTests.toList()[index].testDescriptionAr! : model.uniqueTests.toList()[index].testDescriptionEn!)); } }, tests: model.uniqueTests.toList()[index], index: index, isExpanded: isExpanded)), ), ) : Utils.getNoDataWidget(context, noDataText: "You don't have any lab results yet.".needTranslation); }, ) ], ); }, ), ), )); } Color getLabOrderStatusColor(num status) { switch (status) { case 44: return AppColors.warningColorYellow; case 45: return AppColors.warningColorYellow; case 16: return AppColors.successColor; case 17: return AppColors.successColor; default: return AppColors.greyColor; } } String getLabOrderStatusText(num status) { switch (status) { case 44: return LocaleKeys.resultsPending.tr(context: context); case 45: return LocaleKeys.resultsPending.tr(context: context); case 16: return LocaleKeys.resultsAvailable.tr(context: context); case 17: return LocaleKeys.resultsAvailable.tr(context: context); default: return ""; } } getLabSuggestions(LabViewModel model) { if (model.patientLabOrders.isEmpty) { return []; } return model.patientLabOrders.map((m) => m.testDetails).toList(); } } \ No newline at end of file +import 'dart:async'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_staggered_animations/flutter_staggered_animations.dart'; import 'package:hmg_patient_app_new/core/app_state.dart'; import 'package:hmg_patient_app_new/core/dependencies.dart'; import 'package:hmg_patient_app_new/core/enums.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/lab/models/resp_models/patient_lab_orders_response_model.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/features/lab/lab_view_model.dart'; import 'package:hmg_patient_app_new/presentation/lab/lab_order_by_test.dart'; import 'package:hmg_patient_app_new/presentation/lab/lab_result_item_view.dart'; import 'package:hmg_patient_app_new/presentation/lab/lab_result_via_clinic/LabResultByClinic.dart'; import 'package:hmg_patient_app_new/presentation/lab/search_lab_report.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/widgets/chip/custom_chip_widget.dart'; import 'package:hmg_patient_app_new/widgets/date_range_selector/viewmodel/date_range_view_model.dart'; import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart'; import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart'; import 'package:provider/provider.dart'; import 'package:hmg_patient_app_new/widgets/custom_tab_bar.dart'; import '../../widgets/appbar/collapsing_list_view.dart'; class LabOrdersPage extends StatefulWidget { const LabOrdersPage({super.key}); @override State createState() => _LabOrdersPageState(); } class _LabOrdersPageState extends State { late LabViewModel labProvider; late DateRangeSelectorRangeViewModel rangeViewModel; late AppState _appState; List?> labSuggestions = []; int? expandedIndex; String? selectedFilterText = ''; int activeIndex = 0; @override void initState() { scheduleMicrotask(() { labProvider.initLabProvider(); }); super.initState(); } @override Widget build(BuildContext context) { labProvider = Provider.of(context, listen: false); rangeViewModel = Provider.of(context); _appState = getIt(); return Scaffold( backgroundColor: AppColors.bgScaffoldColor, body: CollapsingListView( title: LocaleKeys.labResults.tr(), search: () async { final lavVM = Provider.of(context, listen: false); if (lavVM.isLabOrdersLoading) { return; } else { String? value = await Navigator.of(context).push( CustomPageRoute( page: SearchLabResultsContent(labSuggestionsList: lavVM.labSuggestions), fullScreenDialog: true, direction: AxisDirection.down, ), ); if (value != null) { selectedFilterText = value; lavVM.filterLabReports(value); } } }, child: SingleChildScrollView( padding: EdgeInsets.all(24.h), physics: NeverScrollableScrollPhysics(), child: Consumer( builder: (context, model, child) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ CustomTabBar( activeTextColor: Color(0xffED1C2B), activeBackgroundColor: Color(0xffED1C2B).withValues(alpha: .1), tabs: [ CustomTabBarModel(null, "By Visit".needTranslation), CustomTabBarModel(null, "By Test".needTranslation), // CustomTabBarModel(null, "Completed".needTranslation), ], onTabChange: (index) { activeIndex = index; setState(() {}); }, ), SizedBox(height: 8.h), selectedFilterText!.isNotEmpty ? CustomChipWidget( chipText: selectedFilterText!, chipType: ChipTypeEnum.alert, isSelected: true, ) : SizedBox(), activeIndex == 0 ? ListView.builder( shrinkWrap: true, physics: NeverScrollableScrollPhysics(), padding: EdgeInsets.zero, itemCount: model.isLabOrdersLoading ? 5 : model.patientLabOrders.isNotEmpty ? model.patientLabOrders.length : 1, itemBuilder: (context, index) { final isExpanded = expandedIndex == index; return model.isLabOrdersLoading ? LabResultItemView( onTap: () {}, labOrder: null, index: index, isLoading: true, ) : model.patientLabOrders.isNotEmpty ? AnimationConfiguration.staggeredList( position: index, duration: const Duration(milliseconds: 500), child: SlideAnimation( verticalOffset: 100.0, child: FadeInAnimation( child: LabResultItemView( onTap: () { model.currentlySelectedPatientOrder = model.patientLabOrders[ index]; labProvider.getPatientLabResultByHospital(model.patientLabOrders[ index]); labProvider .getPatientSpecialResult( model.patientLabOrders[ index]); Navigator.push( context, CustomPageRoute( page: LabResultByClinic(labOrder: model.patientLabOrders[index]), )); }, labOrder: model.patientLabOrders[index], index: index, isExpanded: isExpanded), ), ), ) : Utils.getNoDataWidget(context, noDataText: "You don't have any lab results yet.".needTranslation); }, ) : ListView.builder( shrinkWrap: true, physics: NeverScrollableScrollPhysics(), padding: EdgeInsets.zero, itemCount: model.isLabOrdersLoading ? 5 : model.uniqueTests.toList().isNotEmpty ? model.uniqueTests.toList().length : 1, itemBuilder: (context, index) { final isExpanded = expandedIndex == index; return model.isLabOrdersLoading ? LabResultItemView( onTap: () {}, labOrder: null, index: index, isLoading: true, ) : model.uniqueTests.toList().isNotEmpty ? AnimationConfiguration.staggeredList( position: index, duration: const Duration(milliseconds: 500), child: SlideAnimation( verticalOffset: 100.0, child: FadeInAnimation( child: LabOrderByTest( appState: _appState, onTap: () { if (model.uniqueTests.toList()[index].model != null) { rangeViewModel.flush(); model.getPatientLabResult(model.uniqueTests.toList()[index].model!, model.uniqueTests.toList()[index].description!, (_appState.isArabic() ? model.uniqueTests.toList()[index].testDescriptionAr! : model.uniqueTests.toList()[index].testDescriptionEn!)); } }, tests: model.uniqueTests.toList()[index], index: index, isExpanded: isExpanded)), ), ) : Utils.getNoDataWidget(context, noDataText: "You don't have any lab results yet.".needTranslation); }, ) ], ); }, ), ), )); } Color getLabOrderStatusColor(num status) { switch (status) { case 44: return AppColors.warningColorYellow; case 45: return AppColors.warningColorYellow; case 16: return AppColors.successColor; case 17: return AppColors.successColor; default: return AppColors.greyColor; } } String getLabOrderStatusText(num status) { switch (status) { case 44: return LocaleKeys.resultsPending.tr(context: context); case 45: return LocaleKeys.resultsPending.tr(context: context); case 16: return LocaleKeys.resultsAvailable.tr(context: context); case 17: return LocaleKeys.resultsAvailable.tr(context: context); default: return ""; } } getLabSuggestions(LabViewModel model) { if (model.patientLabOrders.isEmpty) { return []; } return model.patientLabOrders.map((m) => m.testDetails).toList(); } } \ No newline at end of file diff --git a/lib/presentation/lab/lab_result_via_clinic/lab_order_result_item.dart b/lib/presentation/lab/lab_result_via_clinic/lab_order_result_item.dart index 36cdc2b..bc1d6b1 100644 --- a/lib/presentation/lab/lab_result_via_clinic/lab_order_result_item.dart +++ b/lib/presentation/lab/lab_result_via_clinic/lab_order_result_item.dart @@ -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().getColor( - tests?.calculatedResultFlag ?? "", - ), + color: tests!.checkIfGraphShouldBeDisplayed() + ? context.read().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, diff --git a/lib/presentation/medical_file/medical_file_page.dart b/lib/presentation/medical_file/medical_file_page.dart index 193b639..883ca37 100644 --- a/lib/presentation/medical_file/medical_file_page.dart +++ b/lib/presentation/medical_file/medical_file_page.dart @@ -344,7 +344,7 @@ class _MedicalFilePageState extends State { Consumer(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 { 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 { Navigator.of(context).push( CustomPageRoute( page: PrescriptionDetailPage( + isFromAppointments: false, prescriptionsResponseModel: prescriptionVM.patientPrescriptionOrders[index]), ), ); @@ -564,7 +565,7 @@ class _MedicalFilePageState extends State { 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 { 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, diff --git a/lib/presentation/medical_file/widgets/lab_rad_card.dart b/lib/presentation/medical_file/widgets/lab_rad_card.dart index 42f5bff..1a56baf 100644 --- a/lib/presentation/medical_file/widgets/lab_rad_card.dart +++ b/lib/presentation/medical_file/widgets/lab_rad_card.dart @@ -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, diff --git a/lib/presentation/medical_file/widgets/medical_file_card.dart b/lib/presentation/medical_file/widgets/medical_file_card.dart index b96026b..8c38363 100644 --- a/lib/presentation/medical_file/widgets/medical_file_card.dart +++ b/lib/presentation/medical_file/widgets/medical_file_card.dart @@ -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( diff --git a/lib/presentation/prescriptions/prescription_detail_page.dart b/lib/presentation/prescriptions/prescription_detail_page.dart index e0e78c2..d5e8138 100644 --- a/lib/presentation/prescriptions/prescription_detail_page.dart +++ b/lib/presentation/prescriptions/prescription_detail_page.dart @@ -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 createState() => _PrescriptionDetailPageState(); @@ -43,10 +44,12 @@ class _PrescriptionDetailPageState extends State { 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 { 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 { ), ), ).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) { diff --git a/lib/presentation/prescriptions/prescriptions_list_page.dart b/lib/presentation/prescriptions/prescriptions_list_page.dart index 87af39b..58c5206 100644 --- a/lib/presentation/prescriptions/prescriptions_list_page.dart +++ b/lib/presentation/prescriptions/prescriptions_list_page.dart @@ -255,22 +255,20 @@ class _PrescriptionsListPageState extends State { 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 { model.setPrescriptionsDetailsLoading(); Navigator.of(context).push( CustomPageRoute( - page: PrescriptionDetailPage(prescriptionsResponseModel: prescription), - ), - ); + page: PrescriptionDetailPage( + prescriptionsResponseModel: prescription, + isFromAppointments: false, + ), + ), + ); }), ), ], diff --git a/lib/presentation/todo_section/todo_page.dart b/lib/presentation/todo_section/todo_page.dart index f2eb50c..11e258f 100644 --- a/lib/presentation/todo_section/todo_page.dart +++ b/lib/presentation/todo_section/todo_page.dart @@ -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 { + + late AppState appState; + @override void initState() { final TodoSectionViewModel todoSectionViewModel = context.read(); scheduleMicrotask(() async { - await todoSectionViewModel.initializeTodoSectionViewModel(); + if (appState.isAuthenticated) { + await todoSectionViewModel.initializeTodoSectionViewModel(); + } }); super.initState(); } @@ -51,6 +58,7 @@ class _ToDoPageState extends State { @override Widget build(BuildContext context) { + appState = getIt.get(); return CollapsingListView( title: "ToDo List".needTranslation, isLeading: false, diff --git a/lib/widgets/appbar/collapsing_list_view.dart b/lib/widgets/appbar/collapsing_list_view.dart index 8e07631..8fa5f99 100644 --- a/lib/widgets/appbar/collapsing_list_view.dart +++ b/lib/widgets/appbar/collapsing_list_view.dart @@ -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!, ], diff --git a/lib/widgets/chip/app_custom_chip_widget.dart b/lib/widgets/chip/app_custom_chip_widget.dart index 4e655f0..6904edc 100644 --- a/lib/widgets/chip/app_custom_chip_widget.dart +++ b/lib/widgets/chip/app_custom_chip_widget.dart @@ -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, diff --git a/lib/widgets/common_bottom_sheet.dart b/lib/widgets/common_bottom_sheet.dart index e5c5baa..99ff230 100644 --- a/lib/widgets/common_bottom_sheet.dart +++ b/lib/widgets/common_bottom_sheet.dart @@ -183,7 +183,7 @@ void showCommonBottomSheetWithoutHeight( ], ), ), - SizedBox(height: 16.h), + isCloseButtonVisible ? SizedBox(height: 16.h) : SizedBox.shrink(), child, ], ), diff --git a/lib/widgets/input_widget.dart b/lib/widgets/input_widget.dart index d943731..4b3f091 100644 --- a/lib/widgets/input_widget.dart +++ b/lib/widgets/input_widget.dart @@ -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 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, diff --git a/lib/widgets/map/HMSMap.dart b/lib/widgets/map/HMSMap.dart index f655479..7b9c553 100644 --- a/lib/widgets/map/HMSMap.dart +++ b/lib/widgets/map/HMSMap.dart @@ -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 { 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), ), ) ], diff --git a/lib/widgets/map/map.dart b/lib/widgets/map/gms_map.dart similarity index 74% rename from lib/widgets/map/map.dart rename to lib/widgets/map/gms_map.dart index 0c67f1b..6d04692 100644 --- a/lib/widgets/map/map.dart +++ b/lib/widgets/map/gms_map.dart @@ -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? 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), + ), + ) + ], + ); } } \ No newline at end of file diff --git a/lib/widgets/map/map_utility_screen.dart b/lib/widgets/map/map_utility_screen.dart new file mode 100644 index 0000000..87c99fc --- /dev/null +++ b/lib/widgets/map/map_utility_screen.dart @@ -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() + .moveToCurrentLocation(); + }), + ), + ), + bottomSheet: FixedBottomSheet(context), + body: Stack( + children: [ + if (isGmsAvailable) + GMSMap( + currentLocation: + context.read().getGMSLocation(), + onCameraMoved: (value) => context + .read() + .handleGMSMapCameraMoved(value), + onCameraIdle: + context.read().handleOnCameraIdle, + myLocationEnabled: true, + inputController: + context.read().gmsController, + showCenterMarker: true, + ) + else + HMSMap( + currentLocation: + context.read().getHMSLocation(), + onCameraMoved: (value) => context + .read() + .handleHMSMapCameraMoved(value), + onCameraIdle: + context.read().handleOnCameraIdle, + myLocationEnabled: false, + inputController: + context.read().hmsController, + showCenterMarker: true, + ), + Align( + alignment: AlignmentDirectional.topStart, + child: Utils.buildSvgWithAssets( + icon: AppAssets.closeBottomNav, width: 32.h, height: 32.h) + .onPress(() { + onCrossClicked?.call(); + // context + // .read() + // .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(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().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: () {}, + ); + } +}