Compare commits

...

26 Commits

Author SHA1 Message Date
Haroon6138 3bbc014251 Merge pull request 'appointment date filter handled' (#74) from appointment_filter into master
Reviewed-on: #74
4 weeks ago
Haroon6138 a4dc0648e8 Merge pull request 'lab prescription details are added andgraph visibility added to the graph page for result that are IRR' (#73) from feature/lab_result into master
Reviewed-on: #73
4 weeks ago
tahaalam 61007302c9 appointment date filter handled 4 weeks ago
tahaalam e5dcabacce lab prescription details are added andgraph visibility added to the graph page for result that are IRR 4 weeks ago
Haroon6138 a661448d27 Merge pull request 'haroon_dev' (#72) from haroon_dev into master
Reviewed-on: #72
4 weeks ago
haroon amjad c0606fb530 Merge branch 'master' into haroon_dev
# Conflicts:
#	lib/features/my_appointments/my_appointments_view_model.dart
#	lib/presentation/appointments/widgets/hospital_bottom_sheet/hospital_bottom_sheet_body.dart
#	lib/presentation/book_appointment/select_clinic_page.dart
#	lib/presentation/lab/lab_orders_page.dart
4 weeks ago
Haroon6138 6e24698031 Merge pull request 'appointment filters added.' (#71) from appointment_filter into master
Reviewed-on: #71
4 weeks ago
haroon amjad b05e4e2a15 Dental appointment booking flow implementation contd. 4 weeks ago
haroon amjad 2b21086cc1 Dental appointment booking flow implementation contd. 4 weeks ago
tahaalam 483a1571d9 Merge remote-tracking branch 'origin/master' into appointment_filter
# Conflicts:
#	assets/langs/ar-SA.json
#	lib/generated/locale_keys.g.dart
#	lib/presentation/lab/lab_orders_page.dart
#	lib/presentation/lab/lab_results/lab_result_details.dart
4 weeks ago
tahaalam 6cf08c04d2 appointment filters added. 4 weeks ago
Haroon6138 156d7b4834 Merge pull request 'id selection from the appropiate list' (#70) from feature/search_by_region into master
Reviewed-on: #70
4 weeks ago
haroon amjad 415c0eb3c7 Dental appointment booking implementation contd. 4 weeks ago
tahaalam 35e145ddf9 id selection from the appropiate list 4 weeks ago
haroon amjad 8291e89ee7 Merge branch 'master' into haroon_dev
# Conflicts:
#	lib/presentation/book_appointment/select_clinic_page.dart
4 weeks ago
haroon amjad 903248133e Lab order & results enhancements 4 weeks ago
Haroon6138 7e92cad55b Merge pull request 'bottom handled when dental and laser clinic is selected from clinic page' (#69) from feature/search_by_region into master
Reviewed-on: #69
4 weeks ago
tahaalam bbf83d4dc9 bottom handled when dental and laser clinic is selected from clinic page 4 weeks ago
haroon amjad 3d64ddf78b Merge branch 'master' into haroon_dev 4 weeks ago
Haroon6138 51c787c92a Merge pull request 'feature/lab_result' (#68) from feature/lab_result into master
Reviewed-on: #68
4 weeks ago
haroon amjad db979977ca Tamara implementation & enhancements 4 weeks ago
tahaalam 641df8b51b logs removed 4 weeks ago
tahaalam 36bf229787 Merge remote-tracking branch 'origin/master' into feature/lab_result
# Conflicts:
#	assets/langs/ar-SA.json
#	assets/langs/en-US.json
#	lib/core/api/api_client.dart
#	lib/generated/locale_keys.g.dart
#	lib/presentation/lab/lab_results/lab_result_details.dart
4 weeks ago
tahaalam 42abf9ca1b login bypass removed 4 weeks ago
tahaalam 2e9a462a1c listing resizing if the data is in small numbers. 4 weeks ago
tahaalam da89c41925 lab result by hospital and translations are added 1 month ago

@ -864,5 +864,15 @@
"regionAndLocation": "المنطقة والمواقع",
"clearAllFilters": "مسح جميع الفلاتر",
"filters": "فلاتر",
"searchClinic": "بحث عن عيادة"
"searchClinic": "بحث عن عيادة",
"normal": "عادي",
"attention": "انتباه",
"monitor": "مراقبة",
"noSpecialResult": "لا توجد نتائج خاصة",
"setTheDateRange": "تعيين النطاق الزمني",
"historyFlowchart": "مخطط تدفق التاريخ",
"to": "إلى",
"startDate": "تاريخ البدء",
"endDate": "تاريخ الانتهاء",
"walkin": "زيارة بدون موعد"
}

@ -853,6 +853,15 @@
"onboardingBody1": "In few clicks find yourself having consultation with the doctor of your choice.",
"onboardingHeading2": "Access the medical history on finger tips",
"onboardingBody2": "Keep track on your medical history including labs, prescription, insurance, etc",
"normal": "Normal",
"attention": "Attention",
"monitor": "Monitor",
"noSpecialResult": "No Special Results",
"setTheDateRange": "Set The Date Range",
"historyFlowchart": "History FlowChart",
"to": "to",
"startDate" : "Start Date",
"endDate": "End Date",
"hmgHospitals": "HMG Hospitals",
"hmcMedicalClinic": "HMC Medical Centers",
"applyFilter": "AppLy Filter",
@ -860,5 +869,7 @@
"regionAndLocation": "Region And Locations",
"clearAllFilters": "Clear all filters",
"filters": "Filters",
"searchClinic": "Search Clinic"
"searchClinic": "Search Clinic",
"walkin": "Walk In"
}

@ -34,6 +34,7 @@ abstract class ApiClient {
required Function(dynamic response, int statusCode) onSuccess,
required Function(String error, int statusCode) onFailure,
Map<String, dynamic>? queryParams,
bool isAllowAny,
bool isExternal,
bool isRCService,
});
@ -172,8 +173,12 @@ class ApiClientImp implements ApiClient {
body[_appState.isAuthenticated ? 'TokenID' : 'LogInTokenID'] = _appState.appAuthToken;
}
// body['TokenID'] = "@dm!n";
// body['PatientID'] = 4770714;
body['TokenID'] = "@dm!n";
body['PatientID'] = 4767884;
// body['PatientTypeID'] = 1;
//
// body['PatientOutSA'] = 0;
// body['SessionID'] = "45786230487560q";
}
body.removeWhere((key, value) => value == null);
@ -324,6 +329,7 @@ class ApiClientImp implements ApiClient {
{required Function(dynamic response, int statusCode) onSuccess,
required Function(String error, int statusCode) onFailure,
Map<String, dynamic>? queryParams,
bool isAllowAny = false,
bool isExternal = false,
bool isRCService = false}) async {
String url;

@ -743,6 +743,11 @@ class ApiConsts {
static String SERVICE_URL = 'https://hmgwebservices.com/PayFortWebLive/pages/SendPayFortRequest.aspx'; //Payfort Payment Gateway URL LIVE
// static String SERVICE_URL = 'https://hmgwebservices.com/PayFortWeb/pages/SendPayFortRequest.aspx'; // Payfort Payment Gateway URL UAT
static String TAMARA_URL = "https://mdlaboratories.com/tamaralive/Home/Checkout";
static String GET_TAMARA_INSTALLMENTS_URL = "https://mdlaboratories.com/tamaralive/Home/GetInstallments";
// static String GET_TAMARA_INSTALLMENTS_URL = "https://epharmacy.hmg.com/tamara/Home/getinstallments";
// var payFortEnvironment = FortEnvironment.test;
// var applePayMerchantId = "merchant.com.hmgwebservices.uat";
@ -753,37 +758,49 @@ class ApiConsts {
payFortEnvironment = FortEnvironment.production;
applePayMerchantId = "merchant.com.hmgwebservices";
SERVICE_URL = "https://hmgwebservices.com/PayFortWebLive/pages/SendPayFortRequest.aspx";
TAMARA_URL = "https://mdlaboratories.com/tamaralive/Home/Checkout";
GET_TAMARA_INSTALLMENTS_URL = "https://mdlaboratories.com/tamaralive/Home/GetInstallments";
break;
case AppEnvironmentTypeEnum.dev:
baseUrl = "https://uat.hmgwebservices.com/";
payFortEnvironment = FortEnvironment.test;
applePayMerchantId = "merchant.com.hmgwebservices.uat";
SERVICE_URL = 'https://hmgwebservices.com/PayFortWeb/pages/SendPayFortRequest.aspx';
TAMARA_URL = "https://epharmacy.hmg.com/tamara/Home/Checkout";
GET_TAMARA_INSTALLMENTS_URL = "https://epharmacy.hmg.com/tamara/Home/getinstallments";
break;
case AppEnvironmentTypeEnum.uat:
baseUrl = "https://uat.hmgwebservices.com/";
payFortEnvironment = FortEnvironment.test;
applePayMerchantId = "merchant.com.hmgwebservices.uat";
SERVICE_URL = 'https://hmgwebservices.com/PayFortWeb/pages/SendPayFortRequest.aspx';
TAMARA_URL = "https://epharmacy.hmg.com/tamara/Home/Checkout";
GET_TAMARA_INSTALLMENTS_URL = "https://epharmacy.hmg.com/tamara/Home/getinstallments";
break;
case AppEnvironmentTypeEnum.preProd:
baseUrl = "https://webservices.hmg.com/";
payFortEnvironment = FortEnvironment.production;
applePayMerchantId = "merchant.com.hmgwebservices";
SERVICE_URL = "https://hmgwebservices.com/PayFortWebLive/pages/SendPayFortRequest.aspx";
TAMARA_URL = "https://epharmacy.hmg.com/tamara/Home/Checkout";
GET_TAMARA_INSTALLMENTS_URL = "https://epharmacy.hmg.com/tamara/Home/getinstallments";
break;
case AppEnvironmentTypeEnum.qa:
baseUrl = "https://uat.hmgwebservices.com/";
payFortEnvironment = FortEnvironment.test;
applePayMerchantId = "merchant.com.hmgwebservices.uat";
SERVICE_URL = 'https://hmgwebservices.com/PayFortWeb/pages/SendPayFortRequest.aspx';
TAMARA_URL = "https://epharmacy.hmg.com/tamara/Home/Checkout";
GET_TAMARA_INSTALLMENTS_URL = "https://epharmacy.hmg.com/tamara/Home/getinstallments";
break;
case AppEnvironmentTypeEnum.staging:
baseUrl = "https://uat.hmgwebservices.com/";
payFortEnvironment = FortEnvironment.test;
applePayMerchantId = "merchant.com.hmgwebservices.uat";
SERVICE_URL = 'https://hmgwebservices.com/PayFortWeb/pages/SendPayFortRequest.aspx';
TAMARA_URL = "https://epharmacy.hmg.com/tamara/Home/Checkout";
GET_TAMARA_INSTALLMENTS_URL = "https://epharmacy.hmg.com/tamara/Home/getinstallments";
break;
}
}

@ -485,3 +485,11 @@ class DateUtil {
return "";
}
}
extension OnlyDate on DateTime{
DateTime provideDateOnly(){
return DateTime(this.year, month, day);
}
}

@ -323,7 +323,7 @@ class Utils {
children: [
Lottie.asset(AppAnimations.loadingAnimation, repeat: true, reverse: false, frameRate: FrameRate(60), width: 100.h, height: 100.h, fit: BoxFit.fill),
SizedBox(height: 8.h),
(loadingText ?? LocaleKeys.loadingText.tr()).toText16(color: AppColors.blackColor),
(loadingText ?? LocaleKeys.loadingText.tr()).toText16(color: AppColors.blackColor, isCenter: true),
SizedBox(height: 8.h),
],
).center;
@ -355,7 +355,7 @@ class Utils {
).center;
}
static Widget getWarningWidget({String? loadingText, bool isShowActionButtons = false, Function? onConfirmTap, Function? onCancelTap}) {
static Widget getWarningWidget({String? loadingText, bool isShowActionButtons = false, Widget? bodyWidget, Function? onConfirmTap, Function? onCancelTap}) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
@ -364,6 +364,8 @@ class Utils {
SizedBox(height: 8.h),
(loadingText ?? LocaleKeys.loadingText.tr()).toText14(color: AppColors.blackColor, letterSpacing: 0),
SizedBox(height: 16.h),
bodyWidget ?? SizedBox.shrink(),
SizedBox(height: 16.h),
isShowActionButtons
? Row(
children: [

@ -10,6 +10,7 @@ import 'package:hmg_patient_app_new/features/book_appointments/models/resp_model
import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/doctors_list_response_model.dart';
import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/get_clinic_list_response_model.dart';
import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/get_livecare_clinics_response_model.dart';
import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/get_patient_dental_plan_response_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/hospital_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/patient_appointment_history_response_model.dart';
import 'package:hmg_patient_app_new/services/logger_service.dart';
@ -23,7 +24,7 @@ abstract class BookAppointmentsRepo {
Future<Either<Failure, GenericApiModel<DoctorsProfileResponseModel>>> getDoctorProfile(int clinicID, int projectID, int doctorId, {Function(dynamic)? onSuccess, Function(String)? onError});
Future<Either<Failure, GenericApiModel<dynamic>>> getDoctorFreeSlots(int clinicID, int projectID, int doctorId, bool isBookingForLiveCare, {Function(dynamic)? onSuccess, Function(String)? onError});
Future<Either<Failure, GenericApiModel<dynamic>>> getDoctorFreeSlots(int clinicID, int projectID, int doctorId, bool isBookingForLiveCare, {bool continueDentalPlan = false, Function(dynamic)? onSuccess, Function(String)? onError});
Future<Either<Failure, GenericApiModel<dynamic>>> cancelAppointment({required PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel});
@ -68,6 +69,9 @@ abstract class BookAppointmentsRepo {
required int serviceID,
Function(dynamic)? onSuccess,
Function(String)? onError});
Future<Either<Failure, GenericApiModel<List<PatientDentalPlanEstimationResponseModel>>>> getPatientDentalEstimation(
{required int projectID, Function(dynamic)? onSuccess, Function(String)? onError});
}
class BookAppointmentsRepoImp implements BookAppointmentsRepo {
@ -225,7 +229,7 @@ class BookAppointmentsRepoImp implements BookAppointmentsRepo {
//TODO: Implement the logic for Dental & laser clinics
@override
Future<Either<Failure, GenericApiModel<dynamic>>> getDoctorFreeSlots(int clinicID, int projectID, int doctorId, bool isBookingForLiveCare,
{Function(dynamic)? onSuccess, Function(String)? onError}) async {
{bool continueDentalPlan = false, Function(dynamic)? onSuccess, Function(String)? onError}) async {
Map<String, dynamic> mapDevice = {
"DoctorID": doctorId,
"IsBookingForLiveCare": isBookingForLiveCare,
@ -234,6 +238,7 @@ class BookAppointmentsRepoImp implements BookAppointmentsRepo {
"OriginalClinicID": clinicID,
"days": 0,
"isReschadual": false,
"ContinueDentalPlan": continueDentalPlan
};
try {
@ -656,4 +661,44 @@ class BookAppointmentsRepoImp implements BookAppointmentsRepo {
return Left(UnknownFailure(e.toString()));
}
}
@override
Future<Either<Failure, GenericApiModel<List<PatientDentalPlanEstimationResponseModel>>>> getPatientDentalEstimation(
{required int projectID, Function(dynamic)? onSuccess, Function(String)? onError}) async {
Map<String, dynamic> mapDevice = {
"ProjectID": projectID,
};
try {
GenericApiModel<List<PatientDentalPlanEstimationResponseModel>>? apiResponse;
Failure? failure;
await apiClient.post(
HAS_DENTAL_PLAN,
body: mapDevice,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
onError!(error);
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
final list = response['List_IsPatientHasOnGoingEstimation'];
final estimationList = list.map((item) => PatientDentalPlanEstimationResponseModel.fromJson(item as Map<String, dynamic>)).toList().cast<PatientDentalPlanEstimationResponseModel>();
apiResponse = GenericApiModel<List<PatientDentalPlanEstimationResponseModel>>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: null,
data: estimationList,
);
} 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()));
}
}
}

@ -15,6 +15,7 @@ import 'package:hmg_patient_app_new/features/book_appointments/models/free_slot.
import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/doctor_profile_response_model.dart';
import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/doctors_list_response_model.dart';
import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/get_clinic_list_response_model.dart';
import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/get_patient_dental_plan_response_model.dart';
import 'package:hmg_patient_app_new/features/book_appointments/models/timeslots.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/facility_selection.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/doctor_list_api_response.dart';
@ -55,6 +56,10 @@ class BookAppointmentsViewModel extends ChangeNotifier {
List<DoctorsListResponseModel> liveCareDoctorsList = [];
List<PatientDentalPlanEstimationResponseModel> patientDentalPlanEstimationList = [];
int totalTimeNeededForDentalProcedure = 0;
bool isContinueDentalPlan = false;
GetClinicsListResponseModel selectedClinic = GetClinicsListResponseModel();
DoctorsListResponseModel selectedDoctor = DoctorsListResponseModel();
GetLiveCareClinicsResponseModel selectedLiveCareClinic = GetLiveCareClinicsResponseModel();
@ -128,6 +133,8 @@ class BookAppointmentsViewModel extends ChangeNotifier {
clinicsList.clear();
doctorsList.clear();
liveCareClinicsList.clear();
patientDentalPlanEstimationList.clear();
isContinueDentalPlan = false;
// getLocation();
notifyListeners();
}
@ -189,6 +196,11 @@ class BookAppointmentsViewModel extends ChangeNotifier {
notifyListeners();
}
setIsContinueDentalPlan(bool value) {
isContinueDentalPlan = value;
notifyListeners();
}
void onTabChanged(int index) {
selectedTabIndex = index;
notifyListeners();
@ -275,11 +287,10 @@ class BookAppointmentsViewModel extends ChangeNotifier {
}
//TODO: Make the API dynamic with parameters for ProjectID, isNearest, languageID, doctorId, doctorName
Future<void> getDoctorsList(
{int projectID = 0, bool isNearest = false, int doctorId = 0, String doctorName = "", isContinueDentalPlan = false, Function(dynamic)? onSuccess, Function(String)? onError}) async {
Future<void> getDoctorsList({int projectID = 0, bool isNearest = false, int doctorId = 0, String doctorName = "", Function(dynamic)? onSuccess, Function(String)? onError}) async {
doctorsList.clear();
projectID = currentlySelectedHospitalFromRegionFlow != null ? int.parse(currentlySelectedHospitalFromRegionFlow!) : projectID;
final result = await bookAppointmentsRepo.getDoctorsList(selectedClinic.clinicID ?? 0, projectID, isNearest, doctorId, doctorName);
final result = await bookAppointmentsRepo.getDoctorsList(selectedClinic.clinicID ?? 0, projectID, isNearest, doctorId, doctorName, isContinueDentalPlan: isContinueDentalPlan);
result.fold(
(failure) async {
@ -367,7 +378,14 @@ class BookAppointmentsViewModel extends ChangeNotifier {
final DateFormat dateFormatter = DateFormat('yyyy-MM-dd');
Map<DateTime, List> _eventsParsed;
final result = await bookAppointmentsRepo.getDoctorFreeSlots(selectedDoctor.clinicID ?? 0, selectedDoctor.projectID ?? 0, selectedDoctor.doctorID ?? 0, isBookingForLiveCare, onError: onError);
final result = await bookAppointmentsRepo.getDoctorFreeSlots(
selectedDoctor.clinicID ?? 0,
selectedDoctor.projectID ?? 0,
selectedDoctor.doctorID ?? 0,
isBookingForLiveCare,
continueDentalPlan: isContinueDentalPlan,
onError: onError,
);
result.fold(
(failure) async {
@ -901,4 +919,34 @@ class BookAppointmentsViewModel extends ChangeNotifier {
filteredDoctorList = getDoctorListAsPerSelection();
notifyListeners();
}
Future<void> getPatientDentalEstimation({required int projectID, Function(dynamic)? onSuccess, Function(String)? onError}) async {
patientDentalPlanEstimationList.clear();
totalTimeNeededForDentalProcedure = 0;
isContinueDentalPlan = false;
notifyListeners();
final result = await bookAppointmentsRepo.getPatientDentalEstimation(projectID: projectID);
result.fold(
(failure) async => await errorHandlerService.handleError(failure: failure),
(apiResponse) {
if (apiResponse.messageStatus == 2) {
onError!(apiResponse.errorMessage!);
// dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
} else if (apiResponse.messageStatus == 1) {
patientDentalPlanEstimationList = apiResponse.data!;
patientDentalPlanEstimationList.forEach((v) {
totalTimeNeededForDentalProcedure += (v.neededTime ?? 0);
});
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse);
}
}
},
);
}
}

@ -0,0 +1,40 @@
class PatientDentalPlanEstimationResponseModel {
dynamic setupID;
dynamic estimationNo;
int? projectID;
String? procedureId;
int? patientID;
int? sequenceNo;
int? neededTime;
String? procedureName;
String? procedureNameN;
PatientDentalPlanEstimationResponseModel(
{this.setupID, this.estimationNo, this.projectID, this.procedureId, this.patientID, this.sequenceNo, this.neededTime, this.procedureName, this.procedureNameN});
PatientDentalPlanEstimationResponseModel.fromJson(Map<String, dynamic> json) {
setupID = json['SetupID'];
estimationNo = json['EstimationNo'];
projectID = json['ProjectID'];
procedureId = json['ProcedureId'];
patientID = json['PatientID'];
sequenceNo = json['sequenceNo'];
neededTime = json['NeededTime'];
procedureName = json['ProcedureName'];
procedureNameN = json['ProcedureNameN'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['SetupID'] = this.setupID;
data['EstimationNo'] = this.estimationNo;
data['ProjectID'] = this.projectID;
data['ProcedureId'] = this.procedureId;
data['PatientID'] = this.patientID;
data['sequenceNo'] = this.sequenceNo;
data['NeededTime'] = this.neededTime;
data['ProcedureName'] = this.procedureName;
data['ProcedureNameN'] = this.procedureNameN;
return data;
}
}

@ -4,6 +4,7 @@ import 'package:hmg_patient_app_new/core/exceptions/api_failure.dart';
import 'package:hmg_patient_app_new/core/common_models/generic_api_model.dart';
import 'package:dartz/dartz.dart';
import 'package:hmg_patient_app_new/features/lab/models/resp_models/patient_lab_orders_response_model.dart';
import 'package:hmg_patient_app_new/features/lab/models/resp_models/patient_lab_special_result.dart';
import 'package:hmg_patient_app_new/services/logger_service.dart';
import 'models/resp_models/lab_result.dart' show LabResult;
@ -11,6 +12,14 @@ import 'models/resp_models/lab_result.dart' show LabResult;
abstract class LabRepo {
Future<Either<Failure, GenericApiModel<List<PatientLabOrdersResponseModel>>>> getPatientLabOrders();
Future<Either<Failure, GenericApiModel<List<LabResult>>>> getPatientLabResults(PatientLabOrdersResponseModel laborder, bool isVidaPlus, String procedureName);
Future<Either<Failure, GenericApiModel<List<LabResult>>>>
getPatientLabResultsByHospitals(
PatientLabOrdersResponseModel laborder, bool isVidaPlus);
Future<Either<Failure, GenericApiModel<List<PatientLabSpecialResult>>>>
getSpecialLabResult(
PatientLabOrdersResponseModel laborder, bool isVidaPlus);
}
class LabRepoImp implements LabRepo {
@ -73,7 +82,6 @@ class LabRepoImp implements LabRepo {
request['ProjectID'] = laborder.projectID;
request['ClinicID'] = laborder.clinicID;
request['Procedure'] = procedureName;
request['LanguageID'] = 1;
try {
GenericApiModel<List<LabResult>>? apiResponse;
Failure? failure;
@ -90,6 +98,58 @@ class LabRepoImp implements LabRepo {
throw Exception("lab list is empty");
}
final labOrders = list
.map((item) => LabResult.fromJson(item as Map<String, dynamic>))
.toList()
.cast<LabResult>();
apiResponse = GenericApiModel<List<LabResult>>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: null,
data: labOrders,
);
} catch (e) {
failure = DataParsingFailure(e.toString());
}
},
);
if (failure != null) return Left(failure!);
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
return Right(apiResponse!);
} catch (e) {
return Left(UnknownFailure(e.toString()));
}
}
@override
Future<Either<Failure, GenericApiModel<List<LabResult>>>>
getPatientLabResultsByHospitals(
PatientLabOrdersResponseModel laborder, bool isVidaPlus) async {
Map<String, dynamic> request = Map();
request['InvoiceNo_VP'] = isVidaPlus ? laborder!.invoiceNo : "0";
request['InvoiceNo'] = isVidaPlus ? "0" : laborder!.invoiceNo;
request['OrderNo'] = laborder!.orderNo;
request['isDentalAllowedBackend'] = false;
request['SetupID'] = laborder!.setupID;
request['ProjectID'] = laborder.projectID;
request['ClinicID'] = laborder.clinicID;
try {
GenericApiModel<List<LabResult>>? apiResponse;
Failure? failure;
await apiClient.post(
GET_Patient_LAB_RESULT,
body: request,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
final list = response['ListPLR'];
if (list == null || list.isEmpty) {
throw Exception("lab list is empty");
}
final labOrders = list.map((item) => LabResult.fromJson(item as Map<String, dynamic>)).toList().cast<LabResult>();
apiResponse = GenericApiModel<List<LabResult>>(
@ -110,4 +170,57 @@ class LabRepoImp implements LabRepo {
return Left(UnknownFailure(e.toString()));
}
}
@override
Future<Either<Failure, GenericApiModel<List<PatientLabSpecialResult>>>>
getSpecialLabResult(
PatientLabOrdersResponseModel laborder, bool isVidaPlus) async {
Map<String, dynamic> request = Map();
request['InvoiceNo_VP'] = isVidaPlus ? laborder!.invoiceNo : "0";
request['InvoiceNo'] = isVidaPlus ? "0" : laborder!.invoiceNo;
request['OrderNo'] = laborder!.orderNo;
request['isDentalAllowedBackend'] = false;
request['SetupID'] = laborder!.setupID;
request['ProjectID'] = laborder.projectID;
request['ClinicID'] = laborder.clinicID;
try {
GenericApiModel<List<PatientLabSpecialResult>>? apiResponse;
Failure? failure;
await apiClient.post(
GET_Patient_LAB_SPECIAL_RESULT,
body: request,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
final list = response['ListPLSR'];
if (list == null || list.isEmpty) {
throw Exception("lab list is empty");
}
final labOrders = list
.map((item) => PatientLabSpecialResult.fromJson(
item as Map<String, dynamic>))
.toList()
.cast<PatientLabSpecialResult>();
apiResponse = GenericApiModel<List<PatientLabSpecialResult>>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: null,
data: labOrders,
);
} 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()));
}
}
}

@ -20,6 +20,10 @@ import 'package:logger/logger.dart';
class LabViewModel extends ChangeNotifier {
bool isLabOrdersLoading = false;
bool isLabResultsLoading = false;
bool isLabResultByHospitalLoading = false;
bool isSpecialResultsLoading = false;
bool isGraphVisible = true;
bool shouldShowGraph = true;
LabRepo labRepo;
ErrorHandlerService errorHandlerService;
@ -28,6 +32,11 @@ class LabViewModel extends ChangeNotifier {
List<PatientLabOrdersResponseModel> patientLabOrders = [];
List<PatientLabOrdersResponseModel> filteredLabOrders = [];
List<PatientLabOrdersResponseModel> tempLabOrdersList = [];
String labSpecialResult = "";
PatientLabOrdersResponseModel? currentlySelectedPatientOrder;
List<LabResult> mainLabResultsByHospitals = [];
List<LabResult> mainLabResults = [];
List<DataPoint> mainGraphPoints = [];
@ -51,7 +60,7 @@ class LabViewModel extends ChangeNotifier {
List<String> get labSuggestions => _labSuggestionsList;
Set<TestDetails> uniqueTests = {};
Set<TestDetails> uniqueTests = {};
double maxY = 0.0;
double maxX = double.infinity;
@ -71,6 +80,11 @@ class LabViewModel extends ChangeNotifier {
}
Future<void> getPatientLabOrders({Function(dynamic)? onSuccess, Function(String)? onError}) async {
patientLabOrders.clear();
uniqueTests.clear();
uniqueTests = {};
notifyListeners();
final result = await labRepo.getPatientLabOrders();
result.fold(
@ -125,11 +139,12 @@ class LabViewModel extends ChangeNotifier {
}
getUniqueTestDescription() {
uniqueTests = {
uniqueTests = {
for (var item in patientLabOrders)
if (item.testDetails != null)
...?item.testDetails?.map<TestDetails>((test) => TestDetails(
testDescriptionEn: test.testDescriptionEn.toString(),
testDescriptionAr: test.testDescriptionAr.toString(),
description: test.description.toString(),
testCode: test.testCode.toString(),
testID: test.testID,
@ -138,8 +153,32 @@ class LabViewModel extends ChangeNotifier {
};
}
Future<void> getPatientLabResult(
PatientLabOrdersResponseModel laborder, String procedureName) async {
Future<void> getPatientLabResultByHospital(
PatientLabOrdersResponseModel laborder) async {
isLabResultByHospitalLoading = true;
notifyListeners();
mainLabResultsByHospitals.clear;
final result = await labRepo.getPatientLabResultsByHospitals(laborder,
Utils.isVidaPlusProject(int.parse(laborder.projectID ?? "0")));
result.fold(
(failure) async {
isLabResultByHospitalLoading = false;
// await errorHandlerService.handleError(failure: failure);
},
(apiResponse) {
isLabResultByHospitalLoading = false;
if (apiResponse.messageStatus == 2) {
} else if (apiResponse.messageStatus == 1) {
mainLabResultsByHospitals = apiResponse.data ?? [];
notifyListeners();
}
},
);
}
Future<void> getPatientLabResult(PatientLabOrdersResponseModel laborder, String procedureName, String testDescription) async {
LoaderBottomSheet.showLoader();
mainLabResults.clear();
filteredGraphValues.clear();
@ -188,13 +227,57 @@ class LabViewModel extends ChangeNotifier {
} catch (e) {}
});
LabResult recentResult = recentThree.first;
checkIfGraphShouldBeDisplayed(recentResult);
recentResult.verifiedOn = resultDate(DateUtil.convertStringToDate(recentResult.verifiedOnDateTime!));
// filteredGraphValues = [filteredGraphValues.first];
navigationService.push(MaterialPageRoute(
builder: (_) =>
LabResultDetails(recentLabResult: recentResult)));
builder: (_) => LabResultDetails(recentLabResult: recentResult, testDescription: testDescription),
),
);
notifyListeners();
}
},
);
}
void checkIfGraphShouldBeDisplayed(LabResult recentResult){
shouldShowGraph = recentResult.checkIfGraphShouldBeDisplayed();
isGraphVisible = shouldShowGraph;
notifyListeners();
}
Future<void> getPatientSpecialResult(
PatientLabOrdersResponseModel laborder) async {
isSpecialResultsLoading = true;
labSpecialResult = "";
notifyListeners();
final result = await labRepo.getSpecialLabResult(
laborder,
Utils.isVidaPlusProject(int.parse(laborder.projectID ?? "0")),
);
result.fold(
(failure) async {
isSpecialResultsLoading = false;
notifyListeners();
// await errorHandlerService.handleError(failure: failure);
},
(apiResponse) {
isSpecialResultsLoading = false;
if (apiResponse.messageStatus == 2) {
} else if (apiResponse.messageStatus == 1) {
StringBuffer htmlbuffer = StringBuffer("");
apiResponse.data?.forEach((element) {
if(element.resultDataHTML != null && element.resultDataHTML?.isNotEmpty == true)
htmlbuffer.write("${element.resultDataHTML} <br/> <br/>");
});
labSpecialResult = htmlbuffer.toString();
notifyListeners();
}
notifyListeners();
},
);
}
@ -460,4 +543,26 @@ class LabViewModel extends ChangeNotifier {
return true;
}
}
String getSeverityText(String refernceValue) {
switch (refernceValue) {
case 'N':
return "normal";
case 'L':
case 'H':
return "monitor";
case 'CL':
case 'LCL':
case 'CH':
case 'HCH':
return "attention";
default:
return "normal";
}
}
alterGraphVisibility(){
isGraphVisible = !isGraphVisible;
notifyListeners();
}
}

@ -24,6 +24,8 @@ class LabResult {
String? referenceHigh;
String? criticalLow;
String? referenceLow;
num? resultTypeID;
String? packageShortDescription;
LabResult(
{this.description,
@ -78,6 +80,8 @@ class LabResult {
referenceHigh = json['ReferenceHigh'];
criticalLow = json['CriticalLow'];
referenceLow = json['ReferenceLow'];
packageShortDescription = json['PackageShortDescription'];
resultTypeID = json['ResultTypeID'];
}
Map<String, dynamic> toJson() {
@ -109,6 +113,21 @@ class LabResult {
return data;
}
bool checkIfGraphShouldBeDisplayed(){
if (resultTypeID == null) return false;
if (resultTypeID == 6) return false;
if (referanceRange == null || referanceRange == "" || referanceRange == "\n") return false;
bool isDigit = RegExp(r"\\d+").hasMatch("$resultValue");
if(isDigit) return true;
try {
num.parse(resultValue ?? "");
} catch (e) {
return false;
}
return true;
}
@override
String toString() {
return 'LabOrderResult(flag: $calculatedResultFlag, value: $resultValue, verifiedOn: $verifiedOnDateTime)';

@ -224,21 +224,26 @@ class PatientLabOrdersResponseModel {
class TestDetails {
String? description;
String? testDescriptionEn;
String? testDescriptionAr;
String? testCode;
String? testID;
String? createdOn;
PatientLabOrdersResponseModel? model;
TestDetails({this.description, this.testCode, this.testID, this.createdOn, this.model});
TestDetails({this.description, this.testDescriptionEn, this.testDescriptionAr, this.testCode, this.testID, this.createdOn, this.model});
TestDetails.fromJson(Map<String, dynamic> json) {
description = json['Description'];
testDescriptionEn = json['TestDescriptionEn'] ?? "";
testDescriptionAr = json['TestDescriptionAr'] ?? "";
testCode = json['TestCode'];
testID = json['TestID'];
createdOn = json['CreatedOn'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
final Map<String, dynamic> data = <String, dynamic>{};
data['Description'] = this.description;
data['TestCode'] = this.testCode;
data['TestID'] = this.testID;

@ -0,0 +1,32 @@
class PatientLabSpecialResult {
String? invoiceNo;
String? moduleID;
String? resultData;
String? resultDataHTML;
dynamic resultDataTxt;
PatientLabSpecialResult(
{this.invoiceNo,
this.moduleID,
this.resultData,
this.resultDataHTML,
this.resultDataTxt});
PatientLabSpecialResult.fromJson(Map<String, dynamic> json) {
invoiceNo = json['InvoiceNo'];
moduleID = json['ModuleID'];
resultData = json['ResultData'];
resultDataHTML = json['ResultDataHTML'];
resultDataTxt = json['ResultDataTxt'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['InvoiceNo'] = this.invoiceNo;
data['ModuleID'] = this.moduleID;
data['ResultData'] = this.resultData;
data['ResultDataHTML'] = this.resultDataHTML;
data['ResultDataTxt'] = this.resultDataTxt;
return data;
}
}

@ -108,12 +108,17 @@ class MedicalFileViewModel extends ChangeNotifier {
final result = await medicalFileRepo.getPatientVaccinesList();
result.fold(
(failure) async => await errorHandlerService.handleError(
failure: failure,
onOkPressed: () {
onError!(failure.message);
},
),
// (failure) async => await errorHandlerService.handleError(
// failure: failure,
// onOkPressed: () {
// onError!(failure.message);
// },
// ),
(failure) async {
// onError!(failure.message);
isPatientVaccineListLoading = false;
notifyListeners();
},
(apiResponse) {
if (apiResponse.messageStatus == 2) {
// dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
@ -134,12 +139,16 @@ class MedicalFileViewModel extends ChangeNotifier {
final result = await medicalFileRepo.getPatientSickLeavesList();
result.fold(
(failure) async => await errorHandlerService.handleError(
failure: failure,
onOkPressed: () {
onError!(failure.message);
},
),
// (failure) async => await errorHandlerService.handleError(
// failure: failure,
// onOkPressed: () {
// onError!(failure.message);
// },
// ),
(failure) async {
isPatientSickLeaveListLoading = false;
notifyListeners();
},
(apiResponse) {
if (apiResponse.messageStatus == 2) {
// dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});

@ -17,7 +17,8 @@ enum AppointmentViaRegionState {
enum RegionBottomSheetType{
FOR_REGION,
FOR_CLINIIC
REGION_FOR_DENTAL_AND_LASER,
FOR_CLINIIC,
}
class AppointmentViaRegionViewmodel extends ChangeNotifier {
@ -94,4 +95,19 @@ class AppointmentViaRegionViewmodel extends ChangeNotifier {
page: SelectDoctorPage(),
),);
}
void handleLastStepForClinicForDentalAndLaser() {
//todo handle the routing here
navigationService.pop();
}
void handleLastStepForDentalAndLaser() {
//todo handle the routing here
navigationService.pop();
navigationService.push(
CustomPageRoute(
page: SelectDoctorPage(),
),
);
}
}

@ -0,0 +1,17 @@
import 'package:hmg_patient_app_new/core/app_assets.dart';
enum AppointmentListingFilters{
WALKIN("walkin", AppAssets.walkin_appointment_icon),
BOOKED("booked", AppAssets.calendar),
CONFIRMED("confirmed", AppAssets.calendar),
ARRIVED("arrived", AppAssets.calendar),
LIVECARE("livecare", AppAssets.small_livecare_icon),
DATESELECTION("",AppAssets.calendar, trailingIcon: AppAssets.arrow_down);
final String labelText;
final String leadingIcon;
final String trailingIcon;
const AppointmentListingFilters(this.labelText, this.leadingIcon,
{this.trailingIcon = ""});
}

@ -0,0 +1,83 @@
class GetTamaraInstallmentsDetailsResponseModel {
String? name;
String? description;
MinLimit? minLimit;
MinLimit? maxLimit;
List<SupportedInstalments>? supportedInstalments;
GetTamaraInstallmentsDetailsResponseModel({this.name, this.description, this.minLimit, this.maxLimit, this.supportedInstalments});
GetTamaraInstallmentsDetailsResponseModel.fromJson(Map<String, dynamic> json) {
name = json['name'];
description = json['description'];
minLimit = json['minLimit'] != null ? new MinLimit.fromJson(json['minLimit']) : null;
maxLimit = json['maxLimit'] != null ? new MinLimit.fromJson(json['maxLimit']) : null;
if (json['supportedInstalments'] != null) {
supportedInstalments = <SupportedInstalments>[];
json['supportedInstalments'].forEach((v) {
supportedInstalments!.add(new SupportedInstalments.fromJson(v));
});
}
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['name'] = this.name;
data['description'] = this.description;
if (this.minLimit != null) {
data['minLimit'] = this.minLimit!.toJson();
}
if (this.maxLimit != null) {
data['maxLimit'] = this.maxLimit!.toJson();
}
if (this.supportedInstalments != null) {
data['supportedInstalments'] = this.supportedInstalments!.map((v) => v.toJson()).toList();
}
return data;
}
}
class MinLimit {
String? currency;
num? amount;
MinLimit({this.currency, this.amount});
MinLimit.fromJson(Map<String, dynamic> json) {
currency = json['currency'];
amount = json['amount'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['currency'] = this.currency;
data['amount'] = this.amount;
return data;
}
}
class SupportedInstalments {
int? instalments;
MinLimit? minLimit;
MinLimit? maxLimit;
SupportedInstalments({this.instalments, this.minLimit, this.maxLimit});
SupportedInstalments.fromJson(Map<String, dynamic> json) {
instalments = json['instalments'];
minLimit = json['minLimit'] != null ? new MinLimit.fromJson(json['minLimit']) : null;
maxLimit = json['maxLimit'] != null ? new MinLimit.fromJson(json['maxLimit']) : null;
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['instalments'] = this.instalments;
if (this.minLimit != null) {
data['minLimit'] = this.minLimit!.toJson();
}
if (this.maxLimit != null) {
data['maxLimit'] = this.maxLimit!.toJson();
}
return data;
}
}

@ -7,6 +7,7 @@ import 'package:hmg_patient_app_new/core/cache_consts.dart';
import 'package:hmg_patient_app_new/core/common_models/generic_api_model.dart';
import 'package:hmg_patient_app_new/core/exceptions/api_failure.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/get_tamara_installments_details_response_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/hospital_model.dart' show HospitalsModel;
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/patient_appointment_history_response_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/patient_appointment_share_response_model.dart';
@ -44,6 +45,8 @@ abstract class MyAppointmentsRepo {
Future<Either<Failure, GenericApiModel<List<PatientAppointmentHistoryResponseModel>>>> getPatientDoctorsList();
Future<Either<Failure, GenericApiModel<dynamic>>> insertLiveCareVIDARequest({required clientRequestID, required PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel});
Future<Either<Failure, GenericApiModel<GetTamaraInstallmentsDetailsResponseModel>>> getTamaraInstallmentsDetails();
}
class MyAppointmentsRepoImp implements MyAppointmentsRepo {
@ -543,4 +546,41 @@ class MyAppointmentsRepoImp implements MyAppointmentsRepo {
return Left(UnknownFailure(e.toString()));
}
}
@override
Future<Either<Failure, GenericApiModel<GetTamaraInstallmentsDetailsResponseModel>>> getTamaraInstallmentsDetails() async {
try {
GenericApiModel<GetTamaraInstallmentsDetailsResponseModel>? apiResponse;
Failure? failure;
await apiClient.get(
ApiConsts.GET_TAMARA_INSTALLMENTS_URL,
isExternal: true,
isAllowAny: true,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
final list = response;
final tamaraInstallmentsList = GetTamaraInstallmentsDetailsResponseModel.fromJson(list.first);
apiResponse = GenericApiModel<GetTamaraInstallmentsDetailsResponseModel>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: null,
data: tamaraInstallmentsList,
);
} catch (e) {
failure = DataParsingFailure(e.toString());
}
},
);
if (failure != null) return Left(failure!);
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
return Right(apiResponse!);
} catch (e) {
return Left(UnknownFailure(e.toString()));
}
}
}

@ -1,14 +1,17 @@
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/get_tamara_installments_details_response_model.dart';
import 'package:hmg_patient_app_new/core/utils/date_util.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/appointemnet_filters.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/patient_appointment_history_response_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/patient_appointment_share_response_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_repo.dart';
import 'package:hmg_patient_app_new/features/my_appointments/utils/appointment_type.dart';
import 'package:hmg_patient_app_new/services/error_handler_service.dart';
import '../../core/utils/doctor_response_mapper.dart' show DoctorMapper;
class MyAppointmentsViewModel extends ChangeNotifier {
int selectedTabIndex = 0;
int previouslySelectedTab = -1;
MyAppointmentsRepo myAppointmentsRepo;
ErrorHandlerService errorHandlerService;
@ -21,7 +24,14 @@ class MyAppointmentsViewModel extends ChangeNotifier {
bool isAppointmentDataToBeLoaded = true;
List<AppointmentListingFilters> availableFilters = [];
List<AppointmentListingFilters>? selectedFilter = [];
bool isDateFilterSelected = false;
DateTime? start =null;
DateTime? end =null;
List<PatientAppointmentHistoryResponseModel> patientAppointmentsHistoryList = [];
List<PatientAppointmentHistoryResponseModel> filteredAppointmentList = [];
List<PatientAppointmentHistoryResponseModel> patientUpcomingAppointmentsHistoryList = [];
List<PatientAppointmentHistoryResponseModel> patientArrivedAppointmentsHistoryList = [];
@ -32,10 +42,16 @@ class MyAppointmentsViewModel extends ChangeNotifier {
PatientAppointmentShareResponseModel? patientAppointmentShareResponseModel;
GetTamaraInstallmentsDetailsResponseModel? getTamaraInstallmentsDetailsResponseModel;
bool isTamaraDetailsLoading = false;
MyAppointmentsViewModel({required this.myAppointmentsRepo, required this.errorHandlerService, required this.appState});
void onTabChange(int index) {
previouslySelectedTab = selectedTabIndex;
selectedTabIndex = index;
start = null;
end = null;
notifyListeners();
}
@ -50,6 +66,7 @@ class MyAppointmentsViewModel extends ChangeNotifier {
patientMyDoctorsList.clear();
isPatientMyDoctorsLoading = true;
}
isTamaraDetailsLoading = true;
isAppointmentPatientShareLoading = true;
notifyListeners();
}
@ -79,6 +96,11 @@ class MyAppointmentsViewModel extends ChangeNotifier {
notifyListeners();
}
setIsTamaraDetailsLoading(bool val) {
isTamaraDetailsLoading = val;
notifyListeners();
}
setAppointmentReminder(bool value, PatientAppointmentHistoryResponseModel item) {
int index = patientAppointmentsHistoryList.indexOf(item);
if (index != -1) {
@ -132,17 +154,56 @@ class MyAppointmentsViewModel extends ChangeNotifier {
patientAppointmentsHistoryList.addAll(patientUpcomingAppointmentsHistoryList);
patientAppointmentsHistoryList.addAll(patientArrivedAppointmentsHistoryList);
filteredAppointmentList.addAll(patientAppointmentsHistoryList);
print('Upcoming Appointments: ${patientUpcomingAppointmentsHistoryList.length}');
print('Arrived Appointments: ${patientArrivedAppointmentsHistoryList.length}');
print('All Appointments: ${patientAppointmentsHistoryList.length}');
getFiltersForSelectedAppointmentList(filteredAppointmentList);
}
void getFiltersForSelectedAppointmentList(
List<PatientAppointmentHistoryResponseModel> filteredAppointmentList) {
availableFilters.clear();
if (filteredAppointmentList.isEmpty == true) return;
availableFilters.add(AppointmentListingFilters.DATESELECTION);
if (filteredAppointmentList
.any((element) => element.isLiveCareAppointment == true)) {
availableFilters.add(AppointmentListingFilters.LIVECARE);
}
if (filteredAppointmentList
.any((element) => element.isLiveCareAppointment == false)) {
availableFilters.add(AppointmentListingFilters.WALKIN);
}
if (filteredAppointmentList
.any((element) => AppointmentType.isArrived(element) == true)) {
availableFilters.add(AppointmentListingFilters.ARRIVED);
}
if (filteredAppointmentList
.any((element) => AppointmentType.isBooked(element) == true)) {
availableFilters.add(AppointmentListingFilters.BOOKED);
}
if (filteredAppointmentList
.any((element) => AppointmentType.isConfirmed(element) == true)) {
availableFilters.add(AppointmentListingFilters.CONFIRMED);
}
notifyListeners();
}
Future<void> getPatientShareAppointment(int projectID, int clinicID, String appointmentNo, bool isLiveCareAppointment, {Function(dynamic)? onSuccess, Function(String)? onError}) async {
final result = await myAppointmentsRepo.getPatientShareAppointment(projectID: projectID, clinicID: clinicID, appointmentNo: appointmentNo, isLiveCareAppointment: isLiveCareAppointment);
result.fold(
(failure) async => await errorHandlerService.handleError(failure: failure),
(failure) async {
await errorHandlerService.handleError(
failure: failure,
onOkPressed: () {
onError!(failure.message);
});
},
(apiResponse) {
if (apiResponse.messageStatus == 2) {
// dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
@ -337,4 +398,141 @@ class MyAppointmentsViewModel extends ChangeNotifier {
},
);
}
void updateListWRTTab(int index) {
isDateFilterSelected = false;
selectedFilter = [];
// if(previouslySelectedTab == selectedTabIndex ) return;
switch (index) {
case 0:
filteredAppointmentList.clear();
filteredAppointmentList.addAll(patientAppointmentsHistoryList);
break;
case 1:
filteredAppointmentList.clear();
filteredAppointmentList.addAll(patientUpcomingAppointmentsHistoryList);
break;
case 2:
filteredAppointmentList.clear();
filteredAppointmentList.addAll(patientArrivedAppointmentsHistoryList);
break;
}
getFiltersForSelectedAppointmentList(filteredAppointmentList);
notifyListeners();
}
void setSelectedFilter(AppointmentListingFilters availableFilter) {
if (selectedFilter?.contains(availableFilter) == true) {
selectedFilter?.remove(availableFilter);
notifyListeners();
return;
}
selectedFilter?.add(availableFilter) ;
notifyListeners();
}
void getSelectedDateRange(DateTime? start, DateTime? end) {
this.start = start;
this.end = end;
isDateFilterSelected = true;
List<PatientAppointmentHistoryResponseModel> sourceList = [];
if (selectedTabIndex == 0) {
sourceList = patientAppointmentsHistoryList;
} else if (selectedTabIndex == 1) {
sourceList = patientUpcomingAppointmentsHistoryList;
} else if (selectedTabIndex == 2) {
sourceList = patientArrivedAppointmentsHistoryList;
}
// if (isDateFilterSelected) sourceList = filteredAppointmentList;
if (start == null && end == null) {
isDateFilterSelected = false;
filteredAppointmentList.clear();
sourceList.forEach((element) {
if (isUnderFilter(element)) {
filteredAppointmentList.add(element);
}
});
} else {
filteredAppointmentList.clear();
sourceList.forEach((element) {
try {
var dateTime = DateUtil.convertStringToDate(element.appointmentDate).provideDateOnly();
if (start != null && end == null) {
if (dateTime.isAtSameMomentAs(start)) {
if (isUnderFilter(element)) {
filteredAppointmentList.add(element);
}
}
} else if (start != null && end != null) {
if ((dateTime.isAfter(start)) && ((dateTime.isBefore(end))||((dateTime.isAtSameMomentAs(end))))) {
if (isUnderFilter(element)) {
filteredAppointmentList.add(element);
}
}
}
} catch (e) {}
});
}
notifyListeners();
}
void filterTheListAsPerSelection() {
getSelectedDateRange(start, end);
}
bool isUnderFilter(PatientAppointmentHistoryResponseModel element) {
bool isUnderTheFilter = false;
if (selectedFilter == null || selectedFilter!.isEmpty) return true;
int count = 0;
for (var filter in selectedFilter ?? []) {
switch (filter) {
case AppointmentListingFilters.WALKIN:
if (element.isLiveCareAppointment == false) return true;
case AppointmentListingFilters.BOOKED:
if (AppointmentType.isBooked(element))return true;
case AppointmentListingFilters.CONFIRMED:
if (AppointmentType.isConfirmed(element))return true;
case AppointmentListingFilters.ARRIVED:
if (AppointmentType.isArrived(element))return true;
case AppointmentListingFilters.LIVECARE:
if (element.isLiveCareAppointment == true) return true;
case AppointmentListingFilters.DATESELECTION:
}
}
return false;
}
Future<void> getTamaraInstallmentsDetails({Function(dynamic)? onSuccess, Function(String)? onError}) async {
final result = await myAppointmentsRepo.getTamaraInstallmentsDetails();
result.fold(
(failure) async => await errorHandlerService.handleError(failure: failure),
(apiResponse) {
getTamaraInstallmentsDetailsResponseModel = apiResponse.data!;
isTamaraDetailsLoading = false;
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse);
}
// if (apiResponse.messageStatus == 2) {
// onError!(apiResponse.errorMessage!);
// // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
// } else if (apiResponse.messageStatus == 1) {
// getTamaraInstallmentsDetailsResponseModel = apiResponse.data!;
// notifyListeners();
// if (onSuccess != null) {
// onSuccess(apiResponse);
// }
// }
},
);
}
}

@ -863,5 +863,15 @@ abstract class LocaleKeys {
static const clearAllFilters = 'clearAllFilters';
static const filters = 'filters';
static const searchClinic = 'searchClinic';
static const walkin = 'walkin';
static const normal = 'normal';
static const attention = 'attention';
static const monitor = 'monitor';
static const noSpecialResult = 'noSpecialResult';
static const setTheDateRange = 'setTheDateRange';
static const historyFlowchart = 'historyFlowchart';
static const to = 'to';
static const startDate = 'startDate';
static const endDate = 'endDate';
}

@ -15,7 +15,6 @@ import 'package:hmg_patient_app_new/features/habib_wallet/habib_wallet_view_mode
import 'package:hmg_patient_app_new/features/immediate_livecare/immediate_livecare_view_model.dart';
import 'package:hmg_patient_app_new/features/insurance/insurance_view_model.dart';
import 'package:hmg_patient_app_new/features/lab/history/lab_history_viewmodel.dart';
import 'package:hmg_patient_app_new/features/lab/lab_range_view_model.dart';
import 'package:hmg_patient_app_new/features/lab/lab_view_model.dart';
import 'package:hmg_patient_app_new/features/medical_file/medical_file_view_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/appointment_via_region_viewmodel.dart';
@ -28,6 +27,7 @@ import 'package:hmg_patient_app_new/routes/app_routes.dart';
import 'package:hmg_patient_app_new/services/logger_service.dart';
import 'package:hmg_patient_app_new/services/navigation_service.dart';
import 'package:hmg_patient_app_new/theme/app_theme.dart';
import 'package:hmg_patient_app_new/widgets/date_range_selector/viewmodel/date_range_view_model.dart' show DateRangeSelectorRangeViewModel;
import 'package:provider/provider.dart';
import 'package:provider/single_child_widget.dart';
@ -163,8 +163,8 @@ void main() async {
navigationService: getIt(), appState: getIt())),
ChangeNotifierProvider<LabHistoryViewModel>(
create: (_) => LabHistoryViewModel()),
ChangeNotifierProvider<LabRangeViewModel>(
create: (_) => LabRangeViewModel()) ,
ChangeNotifierProvider<DateRangeSelectorRangeViewModel>(
create: (_) => DateRangeSelectorRangeViewModel()) ,
ChangeNotifierProvider<DoctorFilterViewModel>(
create: (_) => DoctorFilterViewModel())
], child: MyApp()),

@ -51,17 +51,28 @@ class _AppointmentPaymentPageState extends State<AppointmentPaymentPage> {
String transID = "";
bool isShowTamara = false;
@override
void initState() {
scheduleMicrotask(() {
payfortViewModel.initPayfortViewModel();
myAppointmentsViewModel.getTamaraInstallmentsDetails().then((val) {
if (myAppointmentsViewModel.patientAppointmentShareResponseModel!.patientShareWithTax! >= myAppointmentsViewModel.getTamaraInstallmentsDetailsResponseModel!.minLimit!.amount! &&
myAppointmentsViewModel.patientAppointmentShareResponseModel!.patientShareWithTax! <= myAppointmentsViewModel.getTamaraInstallmentsDetailsResponseModel!.maxLimit!.amount!) {
setState(() {
isShowTamara = true;
});
}
});
payfortViewModel.setIsApplePayConfigurationLoading(false);
myAppointmentsViewModel.getPatientShareAppointment(
widget.patientAppointmentHistoryResponseModel.projectID,
widget.patientAppointmentHistoryResponseModel.clinicID,
widget.patientAppointmentHistoryResponseModel.appointmentNo.toString(),
widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment ?? false,
);
widget.patientAppointmentHistoryResponseModel.appointmentNo.toString(), widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment ?? false, onError: (err) {
Navigator.of(context).pop();
Navigator.of(context).pop();
});
});
super.initState();
}
@ -162,11 +173,12 @@ class _AppointmentPaymentPageState extends State<AppointmentPaymentPage> {
openPaymentURL("visa");
}),
SizedBox(height: 16.h),
Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 20.h,
hasShadow: false,
isShowTamara
? Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 20.h,
hasShadow: false,
),
child: Row(
mainAxisSize: MainAxisSize.max,
@ -196,7 +208,8 @@ class _AppointmentPaymentPageState extends State<AppointmentPaymentPage> {
).paddingSymmetrical(24.h, 0.h).onPress(() {
selectedPaymentMethod = "TAMARA";
openPaymentURL("tamara");
}),
})
: SizedBox.shrink(),
],
),
),

@ -8,19 +8,26 @@ import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/appointemnet_filters.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/patient_appointment_history_response_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/appointments/widgets/AppointmentFilter.dart';
import 'package:hmg_patient_app_new/presentation/appointments/widgets/appointment_card.dart';
import 'package:hmg_patient_app_new/presentation/book_appointment/book_appointment_page.dart';
import 'package:hmg_patient_app_new/widgets/date_range_selector/date_range_calender.dart';
import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/custom_tab_bar.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/shimmer/movies_shimmer_widget.dart';
import 'package:provider/provider.dart';
import '../../widgets/common_bottom_sheet.dart'
show showCommonBottomSheetWithoutHeight;
class MyAppointmentsPage extends StatefulWidget {
const MyAppointmentsPage({super.key});
@ -61,6 +68,8 @@ class _MyAppointmentsPageState extends State<MyAppointmentsPage> {
],
onTabChange: (index) {
myAppointmentsViewModel.onTabChange(index);
myAppointmentsViewModel.updateListWRTTab(index);
context.read<DateRangeSelectorRangeViewModel>().flush();
},
).paddingSymmetrical(24.h, 0.h),
Consumer<MyAppointmentsViewModel>(builder: (context, myAppointmentsVM, child) {
@ -74,218 +83,147 @@ class _MyAppointmentsPageState extends State<MyAppointmentsPage> {
}
Widget getSelectedTabData(int index, MyAppointmentsViewModel myAppointmentsVM) {
switch (index) {
case 0:
//All Appointments Tab Data
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Expandable list
ListView.separated(
padding: EdgeInsets.only(top: 24.h),
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemCount: myAppointmentsVM.isMyAppointmentsLoading
? 5
: myAppointmentsVM.patientAppointmentsHistoryList.isNotEmpty
? myAppointmentsVM.patientAppointmentsHistoryList.length
: 1,
itemBuilder: (context, index) {
return myAppointmentsVM.isMyAppointmentsLoading
? Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: true),
child: AppointmentCard(
patientAppointmentHistoryResponseModel: PatientAppointmentHistoryResponseModel(),
myAppointmentsViewModel: myAppointmentsViewModel,
isLoading: true,
isFromHomePage: false,
),
).paddingSymmetrical(24.h, 0.h)
: myAppointmentsVM.patientAppointmentsHistoryList.isNotEmpty
? AnimationConfiguration.staggeredList(
position: index,
duration: const Duration(milliseconds: 500),
child: SlideAnimation(
verticalOffset: 100.0,
child: FadeInAnimation(
child: AnimatedContainer(
duration: Duration(milliseconds: 300),
curve: Curves.easeInOut,
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: true),
child: AppointmentCard(
patientAppointmentHistoryResponseModel: myAppointmentsVM.patientAppointmentsHistoryList[index],
myAppointmentsViewModel: myAppointmentsViewModel,
isLoading: false,
isFromHomePage: false,
),
).paddingSymmetrical(24.h, 0.h),
return getAppointList(
myAppointmentsVM, myAppointmentsVM.filteredAppointmentList);
}
Widget getAppointList(MyAppointmentsViewModel myAppointmentsVM,
List<PatientAppointmentHistoryResponseModel> filteredAppointmentList) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Visibility(
visible: myAppointmentsVM.availableFilters.isNotEmpty,
child: getAppointmentFilters(myAppointmentsVM)),
ListView.separated(
padding: EdgeInsets.only(top: 24.h),
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemCount: myAppointmentsVM.isMyAppointmentsLoading
? 5
: filteredAppointmentList.isNotEmpty
? filteredAppointmentList.length
: 1,
itemBuilder: (context, index) {
return myAppointmentsVM.isMyAppointmentsLoading
? Container(
decoration: RoundedRectangleBorder()
.toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 24.h,
hasShadow: true),
child: AppointmentCard(
patientAppointmentHistoryResponseModel:
PatientAppointmentHistoryResponseModel(),
myAppointmentsViewModel: myAppointmentsViewModel,
isLoading: true,
isFromHomePage: false,
),
).paddingSymmetrical(24.h, 0.h)
: filteredAppointmentList.isNotEmpty
? AnimationConfiguration.staggeredList(
position: index,
duration: const Duration(milliseconds: 500),
child: SlideAnimation(
verticalOffset: 100.0,
child: FadeInAnimation(
child: AnimatedContainer(
duration: Duration(milliseconds: 300),
curve: Curves.easeInOut,
decoration: RoundedRectangleBorder()
.toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 24.h,
hasShadow: true),
child: AppointmentCard(
patientAppointmentHistoryResponseModel:
filteredAppointmentList[index],
myAppointmentsViewModel:
myAppointmentsViewModel,
isLoading: false,
isFromHomePage: false,
),
),
)
: Utils.getNoDataWidget(
context,
noDataText: "You don't have any appointments yet.".needTranslation,
callToActionButton: CustomButton(
text: LocaleKeys.bookAppo.tr(context: context),
onPressed: () {
Navigator.of(context).push(
CustomPageRoute(
page: BookAppointmentPage(),
),
);
},
backgroundColor: Color(0xffFEE9EA),
borderColor: Color(0xffFEE9EA),
textColor: Color(0xffED1C2B),
fontSize: 14,
fontWeight: FontWeight.w500,
borderRadius: 12,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 40,
icon: AppAssets.add_icon,
iconColor: AppColors.primaryRedColor,
).paddingSymmetrical(48.h, 0.h),
);
},
separatorBuilder: (BuildContext cxt, int index) => SizedBox(height: 16.h),
),
],
);
case 1:
//Upcoming Appointments Tab Data
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Expandable list
ListView.separated(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemCount: myAppointmentsVM.isMyAppointmentsLoading
? 5
: myAppointmentsVM.patientUpcomingAppointmentsHistoryList.isNotEmpty
? myAppointmentsVM.patientUpcomingAppointmentsHistoryList.length
: 1,
itemBuilder: (context, index) {
return myAppointmentsVM.isMyAppointmentsLoading
? const MoviesShimmerWidget().paddingSymmetrical(24.h, 0.h)
: myAppointmentsVM.patientUpcomingAppointmentsHistoryList.isNotEmpty
? AnimationConfiguration.staggeredList(
position: index,
duration: const Duration(milliseconds: 500),
child: SlideAnimation(
verticalOffset: 100.0,
child: FadeInAnimation(
child: AnimatedContainer(
duration: Duration(milliseconds: 300),
curve: Curves.easeInOut,
margin: EdgeInsets.symmetric(vertical: 8.h),
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: true),
child: AppointmentCard(
patientAppointmentHistoryResponseModel: myAppointmentsVM.patientUpcomingAppointmentsHistoryList[index],
myAppointmentsViewModel: myAppointmentsViewModel,
),
).paddingSymmetrical(24.h, 0.h),
).paddingSymmetrical(24.h, 0.h),
),
),
)
: Utils.getNoDataWidget(
context,
noDataText: "You don't have any appointments yet."
.needTranslation,
callToActionButton: CustomButton(
text: LocaleKeys.bookAppo.tr(context: context),
onPressed: () {
Navigator.of(context).push(
CustomPageRoute(
page: BookAppointmentPage(),
),
),
)
: Utils.getNoDataWidget(
context,
noDataText: "You don't have any appointments yet.".needTranslation,
callToActionButton: CustomButton(
text: LocaleKeys.bookAppo.tr(context: context),
onPressed: () {
Navigator.of(context).push(
CustomPageRoute(
page: BookAppointmentPage(),
),
);
},
backgroundColor: Color(0xffFEE9EA),
borderColor: Color(0xffFEE9EA),
textColor: Color(0xffED1C2B),
fontSize: 14,
fontWeight: FontWeight.w500,
borderRadius: 12,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 40,
icon: AppAssets.add_icon,
iconColor: AppColors.primaryRedColor,
).paddingSymmetrical(48.h, 0.h),
);
},
separatorBuilder: (BuildContext cxt, int index) => SizedBox(height: 16.h),
),
],
);
case 2:
//Completed Appointments Tab Data
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
);
},
backgroundColor: Color(0xffFEE9EA),
borderColor: Color(0xffFEE9EA),
textColor: Color(0xffED1C2B),
fontSize: 14,
fontWeight: FontWeight.w500,
borderRadius: 12,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 40,
icon: AppAssets.add_icon,
iconColor: AppColors.primaryRedColor,
).paddingSymmetrical(48.h, 0.h),
);
},
separatorBuilder: (BuildContext cxt, int index) =>
SizedBox(height: 16.h),
),
],
);
}
Widget getAppointmentFilters(MyAppointmentsViewModel myAppointmentsVM) {
return SizedBox(
height: 56.h,
child: Row(
children: [
// Expandable list
ListView.separated(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemCount: myAppointmentsVM.isMyAppointmentsLoading
? 5
: myAppointmentsVM.patientArrivedAppointmentsHistoryList.isNotEmpty
? myAppointmentsVM.patientArrivedAppointmentsHistoryList.length
: 1,
itemBuilder: (context, index) {
return myAppointmentsVM.isMyAppointmentsLoading
? const MoviesShimmerWidget().paddingSymmetrical(24.h, 0.h)
: myAppointmentsVM.patientArrivedAppointmentsHistoryList.isNotEmpty
? AnimationConfiguration.staggeredList(
position: index,
duration: const Duration(milliseconds: 500),
child: SlideAnimation(
verticalOffset: 100.0,
child: FadeInAnimation(
child: AnimatedContainer(
duration: Duration(milliseconds: 300),
curve: Curves.easeInOut,
margin: EdgeInsets.symmetric(vertical: 8.h),
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: true),
child: AppointmentCard(
patientAppointmentHistoryResponseModel: myAppointmentsVM.patientArrivedAppointmentsHistoryList[index],
myAppointmentsViewModel: myAppointmentsViewModel,
),
).paddingSymmetrical(24.h, 0.h),
Expanded(
child: ListView.separated(
separatorBuilder: (_, index) => SizedBox(
width: 8.h,
),
scrollDirection: Axis.horizontal,
itemCount: myAppointmentsVM.availableFilters.length,
itemBuilder: (_, index) => AppointmentFilters(
selectedFilter: myAppointmentsVM.selectedFilter,
item: myAppointmentsVM.availableFilters[index],
onClicked: () {
if (myAppointmentsVM.availableFilters[index] ==
AppointmentListingFilters.DATESELECTION) {
showCommonBottomSheetWithoutHeight(
title: "Set The Date Range".needTranslation,
context,
child: DateRangeSelector(
onRangeSelected: (start, end) {
// if (start != null) {
myAppointmentsVM.getSelectedDateRange(
start, end);
// }
},
),
),
)
: Utils.getNoDataWidget(
context,
noDataText: "You don't have any appointments yet.".needTranslation,
callToActionButton: CustomButton(
text: LocaleKeys.bookAppo.tr(context: context),
onPressed: () {
Navigator.of(context).push(
CustomPageRoute(
page: BookAppointmentPage(),
),
);
},
backgroundColor: Color(0xffFEE9EA),
borderColor: Color(0xffFEE9EA),
textColor: Color(0xffED1C2B),
fontSize: 14,
fontWeight: FontWeight.w500,
borderRadius: 12,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 40,
icon: AppAssets.add_icon,
iconColor: AppColors.primaryRedColor,
).paddingSymmetrical(48.h, 0.h),
);
},
separatorBuilder: (BuildContext cxt, int index) => SizedBox(height: 16.h),
isFullScreen: false,
isCloseButtonVisible: true,
callBackFunc: () {},
);
} else {
myAppointmentsVM.setSelectedFilter(
myAppointmentsVM.availableFilters[index]);
myAppointmentsVM.filterTheListAsPerSelection();
}
},
)),
),
],
);
default:
return Container();
}
)).paddingOnly(top: 24.h, left: 24.h, right: 24.h);
}
}

@ -0,0 +1,48 @@
import 'package:easy_localization/easy_localization.dart' show tr, StringTranslateExtension;
import 'package:flutter/material.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/my_appointments/models/appointemnet_filters.dart';
import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart';
import 'package:smooth_corner/smooth_corner.dart';
class AppointmentFilters extends StatelessWidget {
final AppointmentListingFilters item;
final List<AppointmentListingFilters>? selectedFilter;
final VoidCallback onClicked;
const AppointmentFilters(
{super.key,
required this.item,
required this.onClicked,
required this.selectedFilter});
@override
Widget build(BuildContext context) {
return AppCustomChipWidget(
backgroundColor: selectedFilter?.contains(item) == true?AppColors.chipSecondaryLightRedColor:AppColors.whiteColor,
icon: item.leadingIcon,
textColor: selectedFilter?.contains(item) == true
? AppColors.chipPrimaryRedBorderColor: AppColors.blackColor,
labelText: item.labelText.isNotEmpty?item.labelText.tr():"",
iconHasColor: true,
iconColor: selectedFilter?.contains(item) == true
? AppColors.chipPrimaryRedBorderColor:AppColors.blackColor,
iconSize: 16,
deleteIcon: item.trailingIcon,
labelPadding: EdgeInsetsDirectional.only(start: 8.h, end: 0.h),
padding: EdgeInsets.symmetric(vertical: 12.h, horizontal: 8.h),
deleteIconSize: Size(18.h, 18.h),
shape: SmoothRectangleBorder(
borderRadius: BorderRadius.circular(10 ),
smoothness: 10,
side: BorderSide(
color: selectedFilter?.contains(item) == true
? AppColors.chipPrimaryRedBorderColor
: AppColors.borderGrayColor,
width: 1),
)).onPress(onClicked);
}
}

@ -99,11 +99,13 @@ class HospitalBottomSheetBody extends StatelessWidget {
if (regionalViewModel.regionBottomSheetType == RegionBottomSheetType.FOR_REGION) {
regionalViewModel.setBottomSheetState(AppointmentViaRegionState.CLINIC_SELECTION);
regionalViewModel.handleLastStepForRegion();
}else if (regionalViewModel.regionBottomSheetType == RegionBottomSheetType.FOR_CLINIIC) {
} else if (regionalViewModel.regionBottomSheetType == RegionBottomSheetType.FOR_CLINIIC) {
regionalViewModel.setBottomSheetState(AppointmentViaRegionState.DOCTOR_SELECTION);
regionalViewModel.handleLastStepForClinic();
} else if (regionalViewModel.regionBottomSheetType == RegionBottomSheetType.REGION_FOR_DENTAL_AND_LASER) {
regionalViewModel.setBottomSheetState(AppointmentViaRegionState.DOCTOR_SELECTION);
regionalViewModel.handleLastStepForClinicForDentalAndLaser();
// regionalViewModel.handleLastStepForClinic();
}
});},
separatorBuilder: (_, __) => SizedBox(

@ -41,10 +41,10 @@ class HospitalListItem extends StatelessWidget {
Transform.flip(
flipX: appState.isArabic() ? true : false,
child: Utils.buildSvgWithAssets(
icon: AppAssets.forward_arrow_icon,
icon: AppAssets.forward_arrow_icon_small,
iconColor: AppColors.blackColor,
width: 18,
height: 13,
width: 18.h,
height: 13.h,
fit: BoxFit.contain,
),
),

@ -24,7 +24,7 @@ class _RegionBottomSheetBodyState extends State<RegionBottomSheetBody> {
@override
void initState() {
scheduleMicrotask(() {
if (regionalViewModel.regionBottomSheetType == RegionBottomSheetType.FOR_REGION) {
if (regionalViewModel.regionBottomSheetType == RegionBottomSheetType.FOR_REGION || regionalViewModel.regionBottomSheetType == RegionBottomSheetType.REGION_FOR_DENTAL_AND_LASER ) {
myAppointmentsViewModel.getRegionMappedProjectList();
} else if (regionalViewModel.regionBottomSheetType == RegionBottomSheetType.FOR_CLINIIC) {
myAppointmentsViewModel.getMappedDoctors();

@ -9,6 +9,7 @@ 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/authentication/authentication_view_model.dart';
import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_view_model.dart';
import 'package:hmg_patient_app_new/features/immediate_livecare/immediate_livecare_view_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/appointment_via_region_viewmodel.dart';
@ -22,6 +23,7 @@ import 'package:hmg_patient_app_new/presentation/book_appointment/search_doctor_
import 'package:hmg_patient_app_new/presentation/book_appointment/select_clinic_page.dart';
import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart' show showCommonBottomSheetWithoutHeight;
import 'package:hmg_patient_app_new/widgets/custom_tab_bar.dart';
import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart';
@ -43,9 +45,11 @@ class _BookAppointmentPageState extends State<BookAppointmentPage> {
late AppointmentViaRegionViewmodel regionalViewModel;
late BookAppointmentsViewModel bookAppointmentsViewModel;
late ImmediateLiveCareViewModel immediateLiveCareViewModel;
late final AuthenticationViewModel authVM;
@override
void initState() {
authVM = context.read<AuthenticationViewModel>();
scheduleMicrotask(() {
bookAppointmentsViewModel.selectedTabIndex = 0;
bookAppointmentsViewModel.initBookAppointmentViewModel();
@ -204,11 +208,12 @@ class _BookAppointmentPageState extends State<BookAppointmentPage> {
).paddingSymmetrical(24.h, 0.h);
case 1:
//TODO: Get LiveCare type Select UI from Hussain
return Column(
children: [
Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
return appState.isAuthenticated
? Column(
children: [
Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 24.h,
hasShadow: false,
),
@ -318,7 +323,8 @@ class _BookAppointmentPageState extends State<BookAppointmentPage> {
),
),
],
).paddingSymmetrical(24.h, 0.h);
).paddingSymmetrical(24.h, 0.h)
: getLiveCareNotLoggedInUI();
default:
SizedBox.shrink();
}
@ -375,4 +381,93 @@ class _BookAppointmentPageState extends State<BookAppointmentPage> {
});
}
}
Widget getLiveCareNotLoggedInUI() {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Utils.buildSvgWithAssets(icon: AppAssets.immediate_service_icon, width: 58.h, height: 58.h),
SizedBox(width: 18.h),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
"Immediate service".needTranslation.toText18(color: AppColors.textColor, isBold: true),
"No need to wait, you will get medical consultation immediately via video call".needTranslation.toText14(color: AppColors.greyTextColor, weight: FontWeight.w500),
],
),
),
],
),
SizedBox(height: 24.h),
Row(
children: [
Utils.buildSvgWithAssets(icon: AppAssets.no_visit_icon, width: 58.h, height: 58.h),
SizedBox(width: 18.h),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
"No visit required".needTranslation.toText18(color: AppColors.textColor, isBold: true),
LocaleKeys.livecarePoint5.tr(context: context).toText14(color: AppColors.greyTextColor, weight: FontWeight.w500),
],
),
),
],
),
SizedBox(height: 24.h),
Row(
children: [
Utils.buildSvgWithAssets(icon: AppAssets.doctor_contact_icon, width: 58.h, height: 58.h),
SizedBox(width: 18.h),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
"Doctor will contact".needTranslation.toText18(color: AppColors.textColor, isBold: true),
"A specialised doctor will contact you and will be able to view your medical history".needTranslation.toText14(color: AppColors.greyTextColor, weight: FontWeight.w500),
],
),
),
],
),
SizedBox(height: 24.h),
Row(
children: [
Utils.buildSvgWithAssets(icon: AppAssets.free_med_delivery_icon, width: 58.h, height: 58.h),
SizedBox(width: 18.h),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
"Free medicine delivery".needTranslation.toText18(color: AppColors.textColor, isBold: true),
"Offers free medicine delivery for the LiveCare appointment".needTranslation.toText14(color: AppColors.greyTextColor, weight: FontWeight.w500),
],
),
),
],
),
SizedBox(height: 36.h),
CustomButton(
text: "Login to use this service".needTranslation,
onPressed: () async {
await authVM.onLoginPressed();
},
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.login1,
iconColor: AppColors.whiteColor,
iconSize: 24.h,
),
],
).paddingSymmetrical(24.h, 0.h);
}
}

@ -56,10 +56,20 @@ class _ImmediateLiveCarePaymentPageState extends State<ImmediateLiveCarePaymentP
String transID = "";
bool isShowTamara = false;
@override
void initState() {
scheduleMicrotask(() {
payfortViewModel.initPayfortViewModel();
myAppointmentsViewModel.getTamaraInstallmentsDetails().then((val) {
if (num.parse(immediateLiveCareViewModel.liveCareImmediateAppointmentFeesList.total!) >= myAppointmentsViewModel.getTamaraInstallmentsDetailsResponseModel!.minLimit!.amount! &&
num.parse(immediateLiveCareViewModel.liveCareImmediateAppointmentFeesList.total!) <= myAppointmentsViewModel.getTamaraInstallmentsDetailsResponseModel!.maxLimit!.amount!) {
setState(() {
isShowTamara = true;
});
}
});
});
super.initState();
}
@ -67,9 +77,9 @@ class _ImmediateLiveCarePaymentPageState extends State<ImmediateLiveCarePaymentP
@override
Widget build(BuildContext context) {
appState = getIt.get<AppState>();
myAppointmentsViewModel = Provider.of<MyAppointmentsViewModel>(context);
myAppointmentsViewModel = Provider.of<MyAppointmentsViewModel>(context, listen: false);
immediateLiveCareViewModel = Provider.of<ImmediateLiveCareViewModel>(context, listen: false);
payfortViewModel = Provider.of<PayfortViewModel>(context);
payfortViewModel = Provider.of<PayfortViewModel>(context, listen: false);
return Scaffold(
backgroundColor: AppColors.bgScaffoldColor,
body: Consumer<MyAppointmentsViewModel>(builder: (context, myAppointmentsVM, child) {
@ -161,9 +171,10 @@ class _ImmediateLiveCarePaymentPageState extends State<ImmediateLiveCarePaymentP
openPaymentURL("visa");
}),
SizedBox(height: 16.h),
Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
isShowTamara
? Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 20.h,
hasShadow: false,
),
@ -195,7 +206,8 @@ class _ImmediateLiveCarePaymentPageState extends State<ImmediateLiveCarePaymentP
).paddingSymmetrical(24.h, 0.h).onPress(() {
selectedPaymentMethod = "TAMARA";
openPaymentURL("tamara");
}),
})
: SizedBox.shrink(),
],
),
),
@ -442,6 +454,7 @@ class _ImmediateLiveCarePaymentPageState extends State<ImmediateLiveCarePaymentP
applePayInsertRequest.clientRequestID = transID;
applePayInsertRequest.clinicID = immediateLiveCareViewModel.immediateLiveCareSelectedClinic.serviceID!;
// TODO: Need to pass dynamic currency coming from the API
applePayInsertRequest.currency = appState.getAuthenticatedUser()!.outSa! == 0 ? "SAR" : "AED";
applePayInsertRequest.customerEmail = "CustID_${appState.getAuthenticatedUser()!.patientId.toString()}@HMG.com";
applePayInsertRequest.customerID = appState.getAuthenticatedUser()!.patientId.toString();

@ -229,6 +229,7 @@ class _ReviewAppointmentPageState extends State<ReviewAppointmentPage> {
});
});
} else {
//TODO: Add patient Derma package check API Here
await bookAppointmentsViewModel.insertSpecificAppointment(onError: (err) {
print(err.data["ErrorEndUserMessage"]);
LoadingUtils.hideFullScreenLoader();

@ -23,8 +23,11 @@ import 'package:hmg_patient_app_new/presentation/book_appointment/select_livecar
import 'package:hmg_patient_app_new/presentation/book_appointment/widgets/clinic_card.dart';
import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart';
import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.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:hmg_patient_app_new/widgets/transitions/fade_page.dart';
import 'package:provider/provider.dart';
@ -206,24 +209,146 @@ class _SelectClinicPageState extends State<SelectClinicPage> {
Navigator.of(context).push(
CustomPageRoute(
page: SelectLivecareClinicPage(onNegativeClicked: (){
handleDoctorScreen();
handleDoctorScreen(clinic);
},),
),
);
} else {
handleDoctorScreen();
handleDoctorScreen(clinic);
}
}
void handleDoctorScreen() {
//17 and 235
void handleDoctorScreen(GetClinicsListResponseModel clinic) async {
if (widget.isFromRegionFlow) {
Navigator.of(context).push(
CustomPageRoute(
page: SelectDoctorPage(),
),
);
//Dental Clinic Flow
if (clinic.clinicID == 17) {
LoaderBottomSheet.showLoader(loadingText: "Checking for an existing dental plan, Please wait...".needTranslation);
await bookAppointmentsViewModel.getPatientDentalEstimation(projectID: int.parse(bookAppointmentsViewModel.currentlySelectedHospitalFromRegionFlow ?? "0")).then((value) {
LoaderBottomSheet.hideLoader();
if (bookAppointmentsViewModel.patientDentalPlanEstimationList.isNotEmpty) {
showCommonBottomSheetWithoutHeight(
// title: LocaleKeys.notice.tr(context: context),
title: "Dental treatment plan".needTranslation,
context,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
"You have an existing treatment plan: ".needTranslation.toText14(weight: FontWeight.w500),
SizedBox(height: 8.h),
Container(
width: double.infinity,
padding: EdgeInsets.all(16.h),
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 20.h,
hasShadow: true,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ListView.separated(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemCount: bookAppointmentsViewModel.patientDentalPlanEstimationList.length,
separatorBuilder: (_, __) {
return Column(
children: [
SizedBox(height: 8.h),
Divider(height: 1, color: AppColors.greyColor),
SizedBox(height: 8.h),
],
);
},
itemBuilder: (context, index) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
bookAppointmentsViewModel.patientDentalPlanEstimationList[index].procedureName!.toText12(isBold: true),
AppCustomChipWidget(icon: AppAssets.appointment_time_icon, labelText: "${bookAppointmentsViewModel.totalTimeNeededForDentalProcedure} Mins".needTranslation),
],
);
},
),
SizedBox(
height: 16.h,
),
Divider(height: 1, color: AppColors.greyColor),
SizedBox(
height: 8.h,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
"Total time required".needTranslation.toText14(isBold: true),
AppCustomChipWidget(icon: AppAssets.appointment_time_icon, labelText: "30 Mins".needTranslation),
],
)
],
),
),
SizedBox(height: 16.h),
"Would you like to continue it?".needTranslation.toText14(weight: FontWeight.w500),
SizedBox(height: 16.h),
Row(
children: [
Expanded(
child: CustomButton(
text: LocaleKeys.cancel.tr(),
onPressed: () {
bookAppointmentsViewModel.setIsContinueDentalPlan(false);
Navigator.of(context).pop();
},
backgroundColor: AppColors.primaryRedColor,
borderColor: AppColors.primaryRedColor,
textColor: AppColors.whiteColor,
icon: AppAssets.cancel,
iconColor: AppColors.whiteColor,
),
),
SizedBox(width: 8.h),
Expanded(
child: CustomButton(
text: LocaleKeys.confirm.tr(),
onPressed: () async {
bookAppointmentsViewModel.setIsContinueDentalPlan(true);
Navigator.of(context).push(
CustomPageRoute(
page: SelectDoctorPage(),
),
);
},
backgroundColor: AppColors.bgGreenColor,
borderColor: AppColors.bgGreenColor,
textColor: Colors.white,
icon: AppAssets.confirm,
),
),
],
)
],
),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
} else {
// Navigate to Chief Complaint Screen
}
});
} else {
Navigator.of(context).push(
CustomPageRoute(
page: SelectDoctorPage(),
),
);
}
} else {
openRegionListBottomSheet(context, RegionBottomSheetType.FOR_CLINIIC);
var bottomSheetType = RegionBottomSheetType.FOR_CLINIIC;
if (clinic.clinicID == 17 || clinic.clinicID == 235) {
bottomSheetType = RegionBottomSheetType.REGION_FOR_DENTAL_AND_LASER;
}
openRegionListBottomSheet(context, bottomSheetType);
}
}
@ -252,8 +377,16 @@ class _SelectClinicPageState extends State<SelectClinicPage> {
if (data.bottomSheetState == AppointmentViaRegionState.HOSPITAL_SELECTION) {
return HospitalBottomSheetBody();
}
if(data.bottomSheetState == AppointmentViaRegionState.DOCTOR_SELECTION){
bookAppointmentsViewModel.setProjectID(regionalViewModel.selectedHospital?.patientDoctorAppointmentList?.first.projectID.toString());
if (data.bottomSheetState == AppointmentViaRegionState.DOCTOR_SELECTION) {
//if the region screen is opened for the dental clinic thenthe project id will be in the hospital list as the list is formed form the get project api
var id = "";
if (data.regionBottomSheetType == RegionBottomSheetType.REGION_FOR_DENTAL_AND_LASER) {
id = regionalViewModel.selectedHospital?.hospitalList?.first?.iD?.toString() ?? "";
} else {
id = regionalViewModel.selectedHospital?.patientDoctorAppointmentList?.first?.projectID?.toString() ?? "";
}
bookAppointmentsViewModel.setProjectID(id);
return SizedBox.shrink();
}
else {
return SizedBox.shrink();

@ -89,11 +89,7 @@ class _SelectDoctorPageState extends State<SelectDoctorPage> {
bookAppointmentsViewModel.filterClinics("");
textFocusNode.unfocus();
},
child: Utils.buildSvgWithAssets(
icon: AppAssets.close_bottom_sheet_icon,
width: 20.h,
height: 20.h,
fit: BoxFit.scaleDown),
child: Utils.buildSvgWithAssets(icon: AppAssets.close_bottom_sheet_icon, width: 20.h, height: 20.h, fit: BoxFit.scaleDown),
)
: null,
onChange: (value) {
@ -111,8 +107,11 @@ class _SelectDoctorPageState extends State<SelectDoctorPage> {
padding: EdgeInsets.only(top: 24.h),
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemCount:
bookAppointmentsVM.isDoctorsListLoading ? 5 : (bookAppointmentsVM.isLiveCareSchedule ? bookAppointmentsVM.liveCareDoctorsList.length : bookAppointmentsVM.doctorsList.length),
itemCount: bookAppointmentsVM.isDoctorsListLoading
? 5
: (bookAppointmentsVM.isLiveCareSchedule
? (bookAppointmentsVM.liveCareDoctorsList.isNotEmpty ? bookAppointmentsVM.liveCareDoctorsList.length : 1)
: (bookAppointmentsVM.doctorsList.isNotEmpty ? bookAppointmentsVM.doctorsList.length : 1)),
itemBuilder: (context, index) {
return bookAppointmentsVM.isDoctorsListLoading
? DoctorCard(
@ -120,47 +119,49 @@ class _SelectDoctorPageState extends State<SelectDoctorPage> {
isLoading: true,
bookAppointmentsViewModel: bookAppointmentsViewModel,
)
: 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: DoctorCard(
doctorsListResponseModel: bookAppointmentsVM.isLiveCareSchedule ? bookAppointmentsVM.liveCareDoctorsList[index] : bookAppointmentsVM.doctorsList[index],
isLoading: false,
bookAppointmentsViewModel: bookAppointmentsViewModel,
).onPress(() async {
bookAppointmentsVM
.setSelectedDoctor(bookAppointmentsVM.isLiveCareSchedule ? bookAppointmentsVM.liveCareDoctorsList[index] : bookAppointmentsVM.doctorsList[index]);
// bookAppointmentsVM.setSelectedDoctor(DoctorsListResponseModel());
LoaderBottomSheet.showLoader();
await bookAppointmentsVM.getDoctorProfile(onSuccess: (dynamic respData) {
LoaderBottomSheet.hideLoader();
Navigator.of(context).push(
CustomPageRoute(
page: DoctorProfilePage(),
),
);
}, onError: (err) {
LoaderBottomSheet.hideLoader();
showCommonBottomSheetWithoutHeight(
context,
child: Utils.getErrorWidget(loadingText: err),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
});
}),
: checkIsDoctorsListEmpty()
? Utils.getNoDataWidget(context, noDataText: "No Doctor found for selected criteria...".needTranslation)
: 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: DoctorCard(
doctorsListResponseModel: bookAppointmentsVM.isLiveCareSchedule ? bookAppointmentsVM.liveCareDoctorsList[index] : bookAppointmentsVM.doctorsList[index],
isLoading: false,
bookAppointmentsViewModel: bookAppointmentsViewModel,
).onPress(() async {
bookAppointmentsVM
.setSelectedDoctor(bookAppointmentsVM.isLiveCareSchedule ? bookAppointmentsVM.liveCareDoctorsList[index] : bookAppointmentsVM.doctorsList[index]);
// bookAppointmentsVM.setSelectedDoctor(DoctorsListResponseModel());
LoaderBottomSheet.showLoader();
await bookAppointmentsVM.getDoctorProfile(onSuccess: (dynamic respData) {
LoaderBottomSheet.hideLoader();
Navigator.of(context).push(
CustomPageRoute(
page: DoctorProfilePage(),
),
);
}, onError: (err) {
LoaderBottomSheet.hideLoader();
showCommonBottomSheetWithoutHeight(
context,
child: Utils.getErrorWidget(loadingText: err),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
});
}),
),
),
),
),
),
);
);
},
separatorBuilder: (BuildContext cxt, int index) => SizedBox(height: 16.h),
),
@ -173,4 +174,12 @@ class _SelectDoctorPageState extends State<SelectDoctorPage> {
),
);
}
bool checkIsDoctorsListEmpty() {
if (bookAppointmentsViewModel.isLiveCareSchedule) {
return bookAppointmentsViewModel.liveCareDoctorsList.isEmpty;
} else {
return bookAppointmentsViewModel.doctorsList.isEmpty;
}
}
}

@ -6,12 +6,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/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/book_appointment/select_doctor_page.dart';
import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart';
class SelectLivecareClinicPage extends StatelessWidget {

@ -32,7 +32,6 @@ import 'package:hmg_patient_app_new/presentation/home/widgets/habib_wallet_card.
import 'package:hmg_patient_app_new/presentation/home/widgets/large_service_card.dart';
import 'package:hmg_patient_app_new/presentation/home/widgets/small_service_card.dart';
import 'package:hmg_patient_app_new/presentation/home/widgets/welcome_widget.dart';
import 'package:hmg_patient_app_new/presentation/lab/lab_results/lab_result_calender.dart';
import 'package:hmg_patient_app_new/presentation/medical_file/medical_file_page.dart';
import 'package:hmg_patient_app_new/presentation/profile_settings/profile_settings.dart';
import 'package:hmg_patient_app_new/services/cache_service.dart';
@ -135,8 +134,8 @@ class _LandingPageState extends State<LandingPage> {
fontSize: 14,
fontWeight: FontWeight.w500,
borderRadius: 12,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 40,
padding: EdgeInsets.fromLTRB(10.h, 0, 10.h, 0),
height: 40.h,
),
Row(
mainAxisSize: MainAxisSize.min,
@ -344,10 +343,10 @@ class _LandingPageState extends State<LandingPage> {
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
"Quick Links".toText16(isBold: true),
"Quick Links".needTranslation.toText16(isBold: true),
Row(
children: [
"View medical file".toText12(color: AppColors.primaryRedColor),
"View medical file".needTranslation.toText12(color: AppColors.primaryRedColor),
SizedBox(width: 2.h),
Icon(Icons.arrow_forward_ios, color: AppColors.primaryRedColor, size: 10.h),
],

@ -2,6 +2,7 @@ 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/app_state.dart';
import 'package:hmg_patient_app_new/core/enums.dart';
import 'package:hmg_patient_app_new/core/utils/date_util.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
@ -19,8 +20,9 @@ class LabOrderByTest extends StatelessWidget {
final TestDetails? tests;
final bool isLoading;
final bool isExpanded;
final AppState appState;
const LabOrderByTest({super.key, required this.onTap, this.tests, required this.index, this.isLoading = false, this.isExpanded = false});
const LabOrderByTest({super.key, required this.onTap, required this.appState, this.tests, required this.index, this.isLoading = false, this.isExpanded = false});
@override
build(BuildContext context) {
@ -35,41 +37,42 @@ class LabOrderByTest extends StatelessWidget {
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// ...labOrder!.testDetails!.map((detail) {
Padding(
padding: EdgeInsets.only(bottom: 8.h),
child: '${tests!.description}'.toText14(weight: FontWeight.w500),
),
SizedBox(height: 12.h),
'${tests!.description}'.toText16(isBold: true),
SizedBox(height: 4.h),
(appState.isArabic() ? tests!.testDescriptionAr : tests!.testDescriptionEn)!.toText12(fontWeight: FontWeight.w500),
SizedBox(height: 8.h),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
AppCustomChipWidget(
richText: '${"Last Tested:".needTranslation} ${ DateUtil.formatDateToDate(DateUtil.convertStringToDate(tests!.createdOn), false)}'.toText12(isBold: true),
// chipType: ChipTypeEnum.lightBg,
richText: '${"Last Tested:".needTranslation} ${DateUtil.formatDateToDate(DateUtil.convertStringToDate(tests!.createdOn), false)}'.toText12(fontWeight: FontWeight.w500),
backgroundColor: AppColors.greyLightColor,
textColor: AppColors.textColor,
// borderRadius: 5,
),
CustomButton(
icon: AppAssets.view_report_icon,
iconColor: AppColors.primaryRedColor,
iconSize: 16.h,
text: LocaleKeys.viewReport.tr(context: context),
onPressed: () {
],
),
SizedBox(height: 16.h),
Row(
children: [
Expanded(child: Container()),
Expanded(
child: CustomButton(
icon: AppAssets.view_report_icon,
iconColor: AppColors.primaryRedColor,
iconSize: 16.h,
text: LocaleKeys.viewReport.tr(context: context),
onPressed: () {
onTap();
},
backgroundColor: AppColors.secondaryLightRedColor,
borderColor: AppColors.secondaryLightRedColor,
textColor: AppColors.primaryRedColor,
fontSize: 14,
fontWeight: FontWeight.bold,
borderRadius: 12,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 40.h,
},
backgroundColor: AppColors.secondaryLightRedColor,
borderColor: AppColors.secondaryLightRedColor,
textColor: AppColors.primaryRedColor,
fontSize: 14,
fontWeight: FontWeight.w500,
borderRadius: 12,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 40.h,
),
),
],
),

File diff suppressed because one or more lines are too long

@ -3,6 +3,7 @@ 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/date_util.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';
@ -49,7 +50,7 @@ class LabResultItemView extends StatelessWidget {
backgroundColor: getLabOrderStatusColor(labOrder?.status ?? 0).withOpacity(0.15),
textColor: getLabOrderStatusColor(labOrder?.status ?? 0),
).toShimmer2(isShow: isLoading, width: 100),
if (!isLoading) Icon(isExpanded ? Icons.expand_less : Icons.expand_more),
// if (!isLoading) Icon(isExpanded ? Icons.expand_less : Icons.expand_more),
],
),
SizedBox(height: 8.h),
@ -59,7 +60,7 @@ class LabResultItemView extends StatelessWidget {
isLoading ? "" : labOrder!.doctorImageURL!,
width: 24.h,
height: 24.h,
fit: BoxFit.fill,
fit: BoxFit.cover,
errorBuilder: (cxt, child, tr) {
return SizedBox(height: 24, width: 24);
},
@ -69,71 +70,85 @@ class LabResultItemView extends StatelessWidget {
],
),
SizedBox(height: 8.h),
Wrap(
spacing: 8.h,
runSpacing: 0.h,
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
AppCustomChipWidget(labelText: isLoading ? "null" : DateUtil.formatDateToDate(DateUtil.convertStringToDate(labOrder!.createdOn), false)).toShimmer2(isShow: isLoading, width: 70),
AppCustomChipWidget(labelText: isLoading ? "null" : labOrder!.clinicDescription!).toShimmer2(isShow: isLoading, width: 100),
Wrap(
spacing: 8.h,
runSpacing: 0.h,
children: [
AppCustomChipWidget(labelText: isLoading ? "null" : DateUtil.formatDateToDate(DateUtil.convertStringToDate(labOrder!.createdOn), false)).toShimmer2(isShow: isLoading, width: 70),
AppCustomChipWidget(labelText: isLoading ? "null" : labOrder!.clinicDescription!).toShimmer2(isShow: isLoading, width: 100),
],
),
isLoading
? SizedBox.shrink()
: Utils.buildSvgWithAssets(
icon: AppAssets.forward_arrow_icon_small,
iconColor: AppColors.textColor,
width: 20.h,
height: 14.h,
fit: BoxFit.contain,
),
],
),
],
),
),
AnimatedSwitcher(
duration: Duration(milliseconds: 300),
switchInCurve: Curves.easeIn,
switchOutCurve: Curves.easeOut,
transitionBuilder: (Widget child, Animation<double> animation) {
return FadeTransition(
opacity: animation,
child: SizeTransition(
sizeFactor: animation,
axisAlignment: 0.0,
child: child,
),
);
},
child: isExpanded
? Container(
key: ValueKey<int>(index),
padding: EdgeInsets.symmetric(horizontal: 16.h, vertical: 8.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
...labOrder!.testDetails!.map((detail) {
return Padding(
padding: EdgeInsets.only(bottom: 8.h),
child: '${detail.description}'.toText14(weight: FontWeight.w500),
);
}).toList(),
SizedBox(height: 16.h),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SizedBox(),
CustomButton(
icon: AppAssets.view_report_icon,
iconColor: AppColors.primaryRedColor,
iconSize: 16.h,
text: LocaleKeys.viewReport.tr(context: context),
onPressed: () {},
backgroundColor: AppColors.secondaryLightRedColor,
borderColor: AppColors.secondaryLightRedColor,
textColor: AppColors.primaryRedColor,
fontSize: 14,
fontWeight: FontWeight.bold,
borderRadius: 12,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 40.h,
),
],
),
],
),
)
: SizedBox.shrink(key: ValueKey<int>(-index)),
),
// AnimatedSwitcher(
// duration: Duration(milliseconds: 300),
// switchInCurve: Curves.easeIn,
// switchOutCurve: Curves.easeOut,
// transitionBuilder: (Widget child, Animation<double> animation) {
// return FadeTransition(
// opacity: animation,
// child: SizeTransition(
// sizeFactor: animation,
// axisAlignment: 0.0,
// child: child,
// ),
// );
// },
// child: isExpanded
// ? Container(
// key: ValueKey<int>(index),
// padding: EdgeInsets.symmetric(horizontal: 16.h, vertical: 8.h),
// child: Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// ...labOrder!.testDetails!.map((detail) {
// return Padding(
// padding: EdgeInsets.only(bottom: 8.h),
// child: '${detail.description}'.toText14(weight: FontWeight.w500),
// );
// }).toList(),
// SizedBox(height: 16.h),
// Row(
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
// children: [
// SizedBox(),
// CustomButton(
// icon: AppAssets.view_report_icon,
// iconColor: AppColors.primaryRedColor,
// iconSize: 16.h,
// text: LocaleKeys.viewReport.tr(context: context),
// onPressed: () {},
// backgroundColor: AppColors.secondaryLightRedColor,
// borderColor: AppColors.secondaryLightRedColor,
// textColor: AppColors.primaryRedColor,
// fontSize: 14,
// fontWeight: FontWeight.bold,
// borderRadius: 12,
// padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
// height: 40.h,
// ),
// ],
// ),
// ],
// ),
// )
// : SizedBox.shrink(key: ValueKey<int>(-index)),
// ),
],
),
),

@ -0,0 +1,69 @@
import 'package:easy_localization/easy_localization.dart'
show tr, StringTranslateExtension;
import 'package:flutter/material.dart';
import 'package:flutter_widget_from_html/flutter_widget_from_html.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/lab/lab_view_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/lab/lab_result_item_view.dart';
import 'package:hmg_patient_app_new/presentation/lab/lab_result_via_hospital/LabResultList.dart';
import 'package:hmg_patient_app_new/presentation/lab/lab_result_via_hospital/lab_order_specialResult.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 LabResultByHospitals extends StatelessWidget {
@override
Widget build(BuildContext context) {
return CollapsingListView(
title: LocaleKeys.labResults.tr(),
child: SingleChildScrollView(
child: Column(
spacing: 8.h,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Selector<LabViewModel, bool>(
selector: (_, model) => model.isLabResultByHospitalLoading,
builder: (_, isLoading, __) {
if (isLoading) {
return Column(
children: [
LabResultItemView(
onTap: () {},
labOrder: null,
index: 0,
isLoading: true,
),
LabResultItemView(
onTap: () {},
labOrder: null,
index: 0,
isLoading: true,
),
LabResultItemView(
onTap: () {},
labOrder: null,
index: 0,
isLoading: true,
),
LabResultItemView(
onTap: () {},
labOrder: null,
index: 0,
isLoading: true,
),
],
);
} else {
return LabResultList();
}
},
),
LabOrderSpecialResult()
],
).paddingAll(24.h),
));
}
}

@ -0,0 +1,40 @@
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/features/lab/lab_view_model.dart';
import 'package:hmg_patient_app_new/features/lab/models/resp_models/lab_result.dart';
import 'package:hmg_patient_app_new/presentation/lab/lab_result_via_hospital/lab_order_result_item.dart';
import 'package:provider/provider.dart' show Selector, Provider, ReadContext;
class LabResultList extends StatelessWidget {
late LabViewModel model;
@override
Widget build(BuildContext context) {
model = Provider.of<LabViewModel>(context);
return Selector<LabViewModel, List<LabResult>>(
selector: (_, model) => model.mainLabResultsByHospitals,
builder: (__, list, ___) {
if (list.isEmpty && context.read<LabViewModel>().labSpecialResult.isEmpty) {
return Utils.getNoDataWidget(context,
noDataText: "You don't have any lab results yet."
.needTranslation);
} else {
return ListView.builder(
physics: NeverScrollableScrollPhysics(),
padding: EdgeInsets.zero,
shrinkWrap: true,itemCount: list.length,itemBuilder: (____, index) {
var labItem = list[index];
return LabOrderResultItem(onTap: () {
model.getPatientLabResult(model.currentlySelectedPatientOrder!, labItem.description ?? "", labItem.packageShortDescription!);
},
tests: labItem,
index: index,
iconColor: model.getColor(labItem.calculatedResultFlag ?? "N"),
severityText: model.getSeverityText(labItem.calculatedResultFlag ?? "N"));
});
}
},
);
}
}

@ -0,0 +1,132 @@
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/enums.dart';
import 'package:hmg_patient_app_new/core/utils/date_util.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/lab/lab_view_model.dart' show LabViewModel;
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/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart';
import 'package:hmg_patient_app_new/widgets/chip/custom_chip_widget.dart';
import 'package:provider/provider.dart';
class LabOrderResultItem extends StatelessWidget {
final VoidCallback onTap;
final int index;
final LabResult? tests;
final String severityText;
final bool isLoading;
final bool isExpanded;
final Color iconColor;
const LabOrderResultItem({super.key, required this.onTap, this.tests, required this.index, this.isLoading = false, this.isExpanded = false,required this.iconColor, required this.severityText});
@override
build(BuildContext context) {
return AnimatedContainer(
duration: Duration(milliseconds: 300),
curve: Curves.easeInOut,
margin: EdgeInsets.symmetric(vertical: 8.h),
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 20.h, hasShadow: true),
child: Container(
key: ValueKey<int>(index),
padding: EdgeInsets.symmetric(horizontal: 16.h, vertical: 16.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// ...labOrder!.testDetails!.map((detail) {
Padding(
padding: EdgeInsets.only(bottom: 8.h),
child: '${tests!.description}'.toText14(weight: FontWeight.w500),
),
'${tests!.packageShortDescription}'.toText12(fontWeight: FontWeight.w500, color: AppColors.textColorLight),
SizedBox(height: 12.h),
Row(
mainAxisSize: MainAxisSize.max,
children: [
Flexible(
child: Text(
tests?.resultValue ?? "",
style: TextStyle(
fontSize: 24.fSize,
fontWeight: FontWeight.w600,
fontFamily: 'Poppins',
color: context.read<LabViewModel>().getColor(
tests?.calculatedResultFlag ?? "",
),
letterSpacing: -2,
),
overflow: TextOverflow.ellipsis, // prevent overflow
maxLines: 1,
softWrap: false,
),
),
SizedBox(width: 4.h,),
Expanded(
flex: 2,
child: Visibility(
visible: tests?.referanceRange != null,
child: Text(
"(Reference range ${tests?.referanceRange})".needTranslation,
style: TextStyle(
fontSize: 12.fSize,
fontWeight: FontWeight.w500,
fontFamily: 'Poppins',
color: AppColors.greyTextColor,
),
// overflow: TextOverflow.ellipsis,
// maxLines: 2,
softWrap: true,
),
),
),
],
),
SizedBox(height: 12.h),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(
spacing: 6.h,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
severityText.tr().toText10(weight: FontWeight.w500, color: AppColors.greyTextColor),
Utils.buildSvgWithAssets(
icon: AppAssets.lab_result_indicator,
width: 21,
height: 23,
iconColor: iconColor
),
],
),
CustomButton(
icon: AppAssets.view_report_icon,
iconColor: AppColors.primaryRedColor,
iconSize: 16.h,
text: LocaleKeys.viewReport.tr(context: context),
onPressed: () {
onTap();
},
backgroundColor: AppColors.secondaryLightRedColor,
borderColor: AppColors.secondaryLightRedColor,
textColor: AppColors.primaryRedColor,
fontSize: 14,
fontWeight: FontWeight.w500,
borderRadius: 12,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 40.h,
),
],
),
],
),
));
}
}

@ -0,0 +1,120 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_widget_from_html/flutter_widget_from_html.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/lab/lab_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:provider/provider.dart';
class LabOrderSpecialResult extends StatelessWidget {
const LabOrderSpecialResult({super.key});
@override
Widget build(BuildContext context) {
return Selector<LabViewModel, bool>(
selector: (_, model) => model.isSpecialResultsLoading,
builder: (_, isLoading, __) {
return Selector<LabViewModel, String>(
selector: (_, model) => model.labSpecialResult,
builder: (_, data, __) {
if(isLoading){
return Container(
margin: EdgeInsets.symmetric(vertical: 8.h),
padding: EdgeInsets.symmetric(
horizontal: 16.h, vertical: 16.h),
width: MediaQuery.sizeOf(context).width - 24,
decoration: RoundedRectangleBorder()
.toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 20.h,
hasShadow: true),
child:Column(
crossAxisAlignment: CrossAxisAlignment.start,
spacing: 12.h,
children: [
"loading".toText14().toShimmer2(isShow: isLoading),
"loading".toText14().toShimmer2(isShow: isLoading),
],
)
);
}
if(data.isNotEmpty ) {
return AnimatedContainer(
duration: Duration(milliseconds: 300),
curve: Curves.easeInOut,
margin: EdgeInsets.symmetric(vertical: 8.h),
decoration: RoundedRectangleBorder()
.toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 20.h,
hasShadow: true),
child: Container(
padding: EdgeInsets.symmetric(
horizontal: 16.h, vertical: 16.h),
width: MediaQuery.sizeOf(context).width - 24,
child: Column(
spacing: 8.h,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// ...labOrder!.testDetails!.map((detail) {
LocaleKeys.specialResult
.tr()
.toText14(weight: FontWeight.w500)
.toShimmer2(isShow: isLoading),
data.isEmpty
? LocaleKeys.noSpecialResult
.tr()
.toText12(
fontWeight: FontWeight.w500,
color: AppColors.textColorLight)
.toShimmer2(isShow: isLoading)
: HtmlWidget(data).toShimmer2(isShow: isLoading)
//
],
),
));
} return SizedBox.shrink();
});
});
}
}
/*Text(
"Special Results",
style: TextStyle(
fontSize: 18.fSize,
fontWeight: FontWeight.w600,
color: AppColors.blackColor),
),
Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 20.h,
hasShadow: true
),
padding: EdgeInsets.all(16.h),
width: MediaQuery.sizeOf(context).width-24,
child: Selector<LabViewModel, bool>(
selector: (_, model) =>
model.isLabResultByHospitalLoading,
builder: (_, isLoading, __) {
return Selector<LabViewModel, String>(
selector: (_, model) => model.labSpecialResult,
builder: (_, data, __) {
return (data.isEmpty)
? Text("No result available".needTranslation,
style: TextStyle(
fontSize: 12.fSize,
fontWeight: FontWeight.w500,
color: AppColors.textColorLight))
.toShimmer2(isShow: isLoading)
: HtmlWidget(data)
.toShimmer2(isShow: isLoading);
});
}))*/

@ -6,42 +6,47 @@ 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/lab_range_view_model.dart' show LabRangeViewModel;
import 'package:hmg_patient_app_new/widgets/date_range_selector/viewmodel/date_range_view_model.dart';
import 'package:hmg_patient_app_new/features/lab/lab_view_model.dart';
import 'package:hmg_patient_app_new/features/lab/models/resp_models/lab_result.dart';
import 'package:hmg_patient_app_new/presentation/lab/lab_results/lab_result_calender.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/widgets/date_range_selector/date_range_calender.dart';
import 'package:hmg_patient_app_new/presentation/lab/lab_results/lab_result_list_item.dart';
import 'package:hmg_patient_app_new/theme/colors.dart' show AppColors;
import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
import 'package:hmg_patient_app_new/widgets/graph/custom_graph.dart';
import 'package:provider/provider.dart' show Consumer, Provider;
import 'package:provider/provider.dart' show Consumer, Provider, ReadContext;
import '../../../widgets/common_bottom_sheet.dart'
show showCommonBottomSheetWithoutHeight;
class LabResultDetails extends StatelessWidget {
final LabResult recentLabResult;
final String? testDescription;
// final List<DataPoint> graphPoint;
late LabViewModel model;
LabResultDetails({super.key, required this.recentLabResult});
const LabResultDetails(
{super.key,
required this.recentLabResult,
required this.testDescription});
@override
Widget build(BuildContext context) {
model = Provider.of<LabViewModel>(context, listen: false);
return CollapsingListView(
title: 'Lab Result Details'.needTranslation,
child: SingleChildScrollView(
child: Column(
spacing: 16.h,
children: [LabNameAndStatus, LabGraph(context)],
children: [
LabNameAndStatus(context),
getLabDescription(context),
LabGraph(context)
],
).paddingAll(24.h),
),
);
}
Widget get LabNameAndStatus => Container(
Widget LabNameAndStatus(BuildContext context) => Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 24.h,
@ -78,11 +83,12 @@ class LabResultDetails extends StatelessWidget {
//todo change the text color according to the provided test values
Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Expanded(
child: Row(
spacing: 4.h,
mainAxisSize: MainAxisSize.max,
children: [
Flexible(
child: Text(
@ -91,8 +97,8 @@ class LabResultDetails extends StatelessWidget {
fontSize: 24.fSize,
fontWeight: FontWeight.w600,
fontFamily: 'Poppins',
color: model.getColor(
recentLabResult.calculatedResultFlag ?? "",
color: context.read<LabViewModel>().getColor(
recentLabResult.calculatedResultFlag ?? "",
),
letterSpacing: -2,
),
@ -101,30 +107,37 @@ class LabResultDetails extends StatelessWidget {
softWrap: false,
),
),
Visibility(
visible: recentLabResult.referanceRange != null,
child: Text(
"(Reference range ${recentLabResult.referanceRange})".needTranslation,
style: TextStyle(
fontSize: 12.fSize,
fontWeight: FontWeight.w500,
fontFamily: 'Poppins',
color: AppColors.greyTextColor,
SizedBox(width: 4.h,),
Expanded(
flex: 2,
child: Visibility(
visible: recentLabResult.referanceRange != null,
child: Text(
"(Reference range ${recentLabResult.referanceRange})".needTranslation,
style: TextStyle(
fontSize: 12.fSize,
fontWeight: FontWeight.w500,
fontFamily: 'Poppins',
color: AppColors.greyTextColor,
),
// overflow: TextOverflow.ellipsis,
// maxLines: 2,
softWrap: true,
),
overflow: TextOverflow.ellipsis,
maxLines: 1,
softWrap: false,
),
),
],
),
),
Utils.buildSvgWithAssets(
icon: AppAssets.lab_result_indicator,
SizedBox(
width: 21,
height: 23,
iconColor: model.getColor(
recentLabResult.calculatedResultFlag ?? "",
child: Utils.buildSvgWithAssets(
icon: AppAssets.lab_result_indicator,
width: 21,
height: 23,
iconColor: context.read<LabViewModel>().getColor(
recentLabResult.calculatedResultFlag ?? "",
),
),
),
],
@ -133,73 +146,80 @@ class LabResultDetails extends StatelessWidget {
],
));
Widget LabGraph(BuildContext context) => Consumer<LabRangeViewModel>(
builder: (_, model, ___) => Consumer<LabViewModel>(
builder: (_, labmodel, ___) => Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 24.h,
hasShadow: true,
),
height: 260.h,
padding: EdgeInsets.all(16.h),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
Widget LabGraph(BuildContext context) => Consumer<LabViewModel>(
builder: (_, labmodel, ___) => Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 24.h,
hasShadow: true,
),
height: labmodel.isGraphVisible
? 260.h
: (labmodel.filteredGraphValues.length < 3)
? (labmodel.filteredGraphValues.length * 64) + 80.h
: 260.h,
padding: EdgeInsets.all(16.h),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
//title and filter icon
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
//title and filter icon
Text(
labmodel.isGraphVisible
? LocaleKeys.historyFlowchart.tr()
: LocaleKeys.history.tr(),
style: TextStyle(
fontSize: 16,
fontFamily: 'Poppins',
fontWeight: FontWeight.w600,
color: AppColors.textColor,
),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
spacing: 16.h,
children: [
Text(
model.isGraphVisible?"History FlowChart".needTranslation: "History".needTranslation,
style: TextStyle(
fontSize: 16,
fontFamily: 'Poppins',
fontWeight: FontWeight.w600,
color: AppColors.textColor,
),
),
Row(
spacing: 16.h,
children: [
//todo handle when the graph icon is being displayed
Utils.buildSvgWithAssets(
icon: model.isGraphVisible?AppAssets.ic_list:AppAssets.ic_graph,
width: 24,
height: 24)
.onPress(() {
model.alterGraphVisibility();
}),
Utils.buildSvgWithAssets(
icon: AppAssets.ic_date_filter,
width: 24,
height: 24)
.onPress(() {
showCommonBottomSheetWithoutHeight(
title: "Set The Date Range".needTranslation,
context,
child: LabResultCalender(
onRangeSelected: (start, end) {
// if (start != null) {
labmodel.getSelectedDateRange(start, end);
// }
},
),
isFullScreen: false,
isCloseButtonVisible: true,
callBackFunc: () {},
);
}),
],
)
//todo handle when the graph icon is being displayed
Utils.buildSvgWithAssets(
icon: labmodel.isGraphVisible
? AppAssets.ic_list
: AppAssets.ic_graph,
width: 24.h,
height: 24.h)
.onPress(() {
if (labmodel.shouldShowGraph) {
labmodel.alterGraphVisibility();
}
}),
Utils.buildSvgWithAssets(
icon: AppAssets.ic_date_filter,
width: 24,
height: 24)
.onPress(() {
showCommonBottomSheetWithoutHeight(
title: LocaleKeys.setTheDateRange.tr(),
context,
child: DateRangeSelector(
onRangeSelected: (start, end) {
// if (start != null) {
labmodel.getSelectedDateRange(start, end);
// }
},
),
isFullScreen: false,
isCloseButtonVisible: true,
callBackFunc: () {},
);
}),
],
).paddingOnly(bottom: model.isGraphVisible? 16.h :24.h),
historyBody(model, labmodel)
)
],
)),
));
).paddingOnly(bottom: labmodel.isGraphVisible ? 16.h : 24.h),
historyBody(labmodel)
],
)),
);
Widget leftLabels(String value) {
return Text(
@ -227,17 +247,16 @@ class LabResultDetails extends StatelessWidget {
);
}
Widget historyBody(LabRangeViewModel model, LabViewModel labmodel) {
if(model.isGraphVisible){
Widget historyBody(LabViewModel labmodel) {
if (labmodel.isGraphVisible && labmodel.shouldShowGraph) {
var graphColor = labmodel.getColor(recentLabResult.calculatedResultFlag??"N");
return CustomGraph(
dataPoints: labmodel.filteredGraphValues,
// maxY: 100,
makeGraphBasedOnActualValue: true,
leftLabelReservedSize: 40,
leftLabelInterval: getInterval(labmodel),
maxY: (labmodel.maxY)+(getInterval(labmodel)??0)/2,
maxX: labmodel.filteredGraphValues.length.toDouble()-.75,
leftLabelFormatter: (value) {
return leftLabels(value.toStringAsFixed(2).tr());
// switch (value.toInt()) {
@ -257,7 +276,7 @@ class LabResultDetails extends StatelessWidget {
// }
},
graphColor:graphColor ,
graphShadowColor: graphColor.withOpacity(.4),
graphShadowColor: graphColor.withOpacity(.1),
graphGridColor: graphColor.withOpacity(.4),
bottomLabelFormatter: (value, data) {
if(data.isEmpty) return SizedBox.shrink();
@ -276,13 +295,13 @@ class LabResultDetails extends StatelessWidget {
scrollDirection: Axis.horizontal,
height: 180.h);
}else {
return labHistoryList(model, labmodel);
return labHistoryList(labmodel);
}
}
Widget labHistoryList(LabRangeViewModel model, LabViewModel labmodel) {
Widget labHistoryList(LabViewModel labmodel) {
return SizedBox(
height: 180.h,
height: labmodel.filteredGraphValues.length<3?labmodel.filteredGraphValues.length*64:180.h,
child: ListView.separated(
padding: EdgeInsets.zero,
itemCount: labmodel.filteredGraphValues.length,itemBuilder: (context, index){
@ -304,6 +323,7 @@ class LabResultDetails extends StatelessWidget {
double? getInterval(LabViewModel labmodel) {
var maxX = labmodel.maxY;
if(maxX<1) return .5;
if(maxX >1 && maxX < 5) return 1;
if(maxX >5 && maxX < 10) return 5;
if(maxX >10 && maxX < 50) return 10;
@ -311,4 +331,27 @@ class LabResultDetails extends StatelessWidget {
if(maxX >100 && maxX < 200) return 30;
return 50;
}
Widget getLabDescription(BuildContext context) {
return Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 24.h,
hasShadow: true,
),
height: 98.h,
padding: EdgeInsets.all(16.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
spacing: 8.h,
children: [
"What is this result?"
.needTranslation
.toText16(weight: FontWeight.w600, color: AppColors.textColor),
testDescription?.toText12(
fontWeight: FontWeight.w500, color: AppColors.textColorLight) ??
SizedBox.shrink()
],
));
}
}

@ -79,7 +79,7 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
insuranceViewModel.initInsuranceProvider();
medicalFileViewModel.setIsPatientSickLeaveListLoading(true);
medicalFileViewModel.getPatientSickLeaveList();
medicalFileViewModel.getFamilyFiles();
// medicalFileViewModel.getFamilyFiles();
medicalFileViewModel.onTabChanged(0);
}
});
@ -582,7 +582,7 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
myAppointmentsVM.patientMyDoctorsList[index].doctorImageURL!,
width: 64.h,
height: 64.h,
fit: BoxFit.fill,
fit: BoxFit.cover,
).circle(100).toShimmer2(isShow: false, radius: 50.h),
SizedBox(height: 8.h),
Expanded(

@ -30,10 +30,7 @@ class _PatientSickleavesListPageState extends State<PatientSickleavesListPage> {
void initState() {
scheduleMicrotask(() {
medicalFileViewModel.setIsPatientSickLeaveListLoading(true);
medicalFileViewModel.getPatientSickLeaveList(onError: (error) {
Navigator.of(context).pop();
Navigator.of(context).pop();
});
medicalFileViewModel.getPatientSickLeaveList();
});
super.initState();
}
@ -75,6 +72,7 @@ class _PatientSickleavesListPageState extends State<PatientSickleavesListPage> {
child: PatientSickLeaveCard(
patientSickLeavesResponseModel: medicalFileVM.patientSickLeaveList.first,
isLoading: false,
isSickLeaveListPage: true,
).paddingSymmetrical(24.h, 0.0),
),
),

@ -5,6 +5,7 @@ import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/utils/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/medical_file/medical_file_view_model.dart';
@ -28,10 +29,7 @@ class _VaccineListPageState extends State<VaccineListPage> {
void initState() {
scheduleMicrotask(() {
medicalFileViewModel.setIsPatientVaccineListLoading(true);
medicalFileViewModel.getPatientVaccinesList(onError: (error) {
Navigator.of(context).pop();
Navigator.of(context).pop();
});
medicalFileViewModel.getPatientVaccinesList();
});
super.initState();
}
@ -51,7 +49,11 @@ class _VaccineListPageState extends State<VaccineListPage> {
SizedBox(height: 16.h),
ListView.separated(
scrollDirection: Axis.vertical,
itemCount: medicalFileVM.isPatientVaccineListLoading ? 5 : medicalFileVM.patientVaccineList.length,
itemCount: medicalFileVM.isPatientVaccineListLoading
? 5
: medicalFileVM.patientVaccineList.isNotEmpty
? medicalFileVM.patientVaccineList.length
: 1,
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
padding: EdgeInsets.only(left: 24.h, right: 24.h),
@ -102,8 +104,9 @@ class _VaccineListPageState extends State<VaccineListPage> {
),
),
)
: AnimationConfiguration.staggeredList(
position: index,
: medicalFileVM.patientVaccineList.isNotEmpty
? AnimationConfiguration.staggeredList(
position: index,
duration: const Duration(milliseconds: 1000),
child: SlideAnimation(
verticalOffset: 100.0,
@ -166,7 +169,8 @@ class _VaccineListPageState extends State<VaccineListPage> {
),
),
),
);
)
: Utils.getNoDataWidget(context, noDataText: "No vaccines data found...".needTranslation);
},
separatorBuilder: (BuildContext cxt, int index) => SizedBox(height: 16.h),
),

@ -54,7 +54,7 @@ class LabRadCard extends StatelessWidget {
SizedBox.shrink(),
Transform.flip(
flipX: appState.isArabic() ? true : false,
child: Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_icon, width: 15.h, height: 15.h, fit: BoxFit.contain, iconColor: AppColors.textColor)
child: Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_icon_small, width: 15.h, height: 15.h, fit: BoxFit.contain, iconColor: AppColors.textColor)
.toShimmer2(isShow: false, radius: 12.h),
),
],

@ -23,11 +23,12 @@ import 'package:open_filex/open_filex.dart';
import 'package:provider/provider.dart';
class PatientSickLeaveCard extends StatelessWidget {
PatientSickLeaveCard({super.key, required this.patientSickLeavesResponseModel, this.isLoading = false});
PatientSickLeaveCard({super.key, required this.patientSickLeavesResponseModel, this.isLoading = false, this.isSickLeaveListPage = false});
late MedicalFileViewModel medicalFileViewModel;
PatientSickLeavesResponseModel patientSickLeavesResponseModel;
bool isLoading;
bool isSickLeaveListPage = false;
@override
Widget build(BuildContext context) {
@ -128,7 +129,7 @@ class PatientSickLeaveCard extends StatelessWidget {
).toShimmer2(isShow: isLoading),
),
SizedBox(width: 8.h),
Expanded(
isSickLeaveListPage ? SizedBox.shrink() : Expanded(
flex: 1,
child: Container(
height: 40.h,

@ -6,6 +6,7 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:flutter_zoom_videosdk/native/zoom_videosdk.dart';
import 'package:hmg_patient_app_new/presentation/lab/lab_orders_page.dart';
import 'package:hmg_patient_app_new/presentation/onboarding/onboarding_screen.dart';
import 'package:hmg_patient_app_new/presentation/onboarding/splash_animation_screen.dart';
import 'package:hmg_patient_app_new/core/api_consts.dart';

@ -29,6 +29,7 @@ class AppColors {
static const Color bgRedLightColor = Color(0xFFFEE9EA);
static const Color bgGreenColor = Color(0xFF18C273);
static const Color textColor = Color(0xFF2E3039);
static const Color borderGrayColor = Color(0x332E3039);
static const Color textColorLight = Color(0xFF5E5E5E);
static const Color borderOnlyColor = Color(0xFF2E3039);
static const Color chipBorderColorOpacity20 = Color(0x332E3039);

@ -24,6 +24,7 @@ class AppCustomChipWidget extends StatelessWidget {
this.deleteIconColor = AppColors.textColor,
this.deleteIconHasColor = false,
this.padding = EdgeInsets.zero,
this.labelPadding ,
});
final String? labelText;
@ -40,6 +41,7 @@ class AppCustomChipWidget extends StatelessWidget {
final bool deleteIconHasColor;
final OutlinedBorder? shape;
final EdgeInsets? padding;
final EdgeInsetsDirectional? labelPadding;
@override
Widget build(BuildContext context) {
@ -62,7 +64,7 @@ class AppCustomChipWidget extends StatelessWidget {
// padding: EdgeInsets.all(0.0),
padding: padding,
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
labelPadding: EdgeInsetsDirectional.only(start: 0.h, end: deleteIcon?.isNotEmpty == true ? 2.h : 8.h),
labelPadding: labelPadding??EdgeInsetsDirectional.only(start: 0.h, end: deleteIcon?.isNotEmpty == true ? 2.h : 8.h),
backgroundColor: backgroundColor,
shape: shape ??
SmoothRectangleBorder(

@ -7,32 +7,32 @@ 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/lab/lab_range_view_model.dart';
import 'package:hmg_patient_app_new/features/lab/models/Range.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart';
import 'package:hmg_patient_app_new/widgets/date_range_selector/viewmodel/date_range_view_model.dart' show DateRangeSelectorRangeViewModel;
import 'package:provider/provider.dart';
import 'package:syncfusion_flutter_calendar/calendar.dart';
import 'package:syncfusion_flutter_datepicker/datepicker.dart';
typedef OnRangeSelected = void Function(DateTime? start, DateTime? end);
class LabResultCalender extends StatefulWidget {
class DateRangeSelector extends StatefulWidget {
final OnRangeSelected onRangeSelected;
const LabResultCalender({super.key, required this.onRangeSelected});
const DateRangeSelector({super.key, required this.onRangeSelected});
@override
State<LabResultCalender> createState() => _LabResultCalenderState();
State<DateRangeSelector> createState() => _DateRangeSelectorState();
}
class _LabResultCalenderState extends State<LabResultCalender> {
class _DateRangeSelectorState extends State<DateRangeSelector> {
late DateRangePickerController _calendarController;
DateTime? start;
DateTime? end;
late LabRangeViewModel model;
late DateRangeSelectorRangeViewModel model;
@override
void initState() {
_calendarController = DateRangePickerController();
@ -44,13 +44,17 @@ class _LabResultCalenderState extends State<LabResultCalender> {
@override
Widget build(BuildContext context) {
model = Provider.of<LabRangeViewModel>(context);
model = Provider.of<DateRangeSelectorRangeViewModel>(context);
_calendarController.selectedRange = PickerDateRange(model.fromDate,model.toDate);
return Padding(
padding: EdgeInsets.symmetric(horizontal: 0.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Consumer<LabRangeViewModel>(
Consumer<DateRangeSelectorRangeViewModel>(
builder: (_, model, __) => selectionChip(model),
).paddingOnly(bottom: 16.h),
Container(
@ -68,7 +72,7 @@ class _LabResultCalenderState extends State<LabResultCalender> {
children: [
fromDateComponent(),
Text(
"to".needTranslation,
LocaleKeys.to.tr(),
style: TextStyle(
color: AppColors.calenderTextColor,
fontSize: 14.h,
@ -135,13 +139,19 @@ class _LabResultCalenderState extends State<LabResultCalender> {
),
onSelectionChanged:
(DateRangePickerSelectionChangedArgs args) {
print("the value is ${args.value}");
if (args.value is PickerDateRange) {
final PickerDateRange range = args.value;
start = range.startDate;
end = range.endDate;
model.fromDate = start;
model.toDate = end;
model.resetCurrentlySelectedRange();
if(end == null) {
scheduleMicrotask((){
model.resetCurrentlySelectedRange();
});
}
}
},
),
@ -151,7 +161,7 @@ class _LabResultCalenderState extends State<LabResultCalender> {
),
Row(
children: [
Consumer<LabRangeViewModel>(
Consumer<DateRangeSelectorRangeViewModel>(
builder: (_, model, __) => Visibility(
visible: (model.fromDate != null || model.toDate != null),
child: Expanded(
@ -164,6 +174,8 @@ class _LabResultCalenderState extends State<LabResultCalender> {
_calendarController.selectedRange = null;
_calendarController.selectedDate = null;
model.flush();
Navigator.of(context).pop();
widget.onRangeSelected(null, null);
},
backgroundColor: AppColors.secondaryLightRedColor,
borderColor: AppColors.secondaryLightRedColor,
@ -203,18 +215,18 @@ class _LabResultCalenderState extends State<LabResultCalender> {
}
fromDateComponent() {
return Consumer<LabRangeViewModel>(
return Consumer<DateRangeSelectorRangeViewModel>(
builder: (_, model, __) {
return displayDate("Start Date".needTranslation,
return displayDate(LocaleKeys.startDate.tr(),
model.getDateString(model.fromDate), model.fromDate == null);
},
);
}
toDateComponent() {
return Consumer<LabRangeViewModel>(
return Consumer<DateRangeSelectorRangeViewModel>(
builder: (_, model, __) {
return displayDate("End Date".needTranslation,
return displayDate(LocaleKeys.endDate.tr(),
model.getDateString(model.toDate), model.toDate == null);
},
);
@ -254,7 +266,7 @@ class _LabResultCalenderState extends State<LabResultCalender> {
),
);
selectionChip(LabRangeViewModel model) {
selectionChip(DateRangeSelectorRangeViewModel model) {
return Row(
spacing: 8.h,
children: [
@ -311,6 +323,7 @@ class _LabResultCalenderState extends State<LabResultCalender> {
_calendarController.selectedRange = null;
model.currentlySelectedRange = Range.LAST_6MONTH;
model.calculateDatesFromRange();
}),
AppCustomChipWidget(
labelText: "Year ${model.getCurrentYear}",
@ -329,6 +342,7 @@ class _LabResultCalenderState extends State<LabResultCalender> {
_calendarController.selectedRange = null;
model.currentlySelectedRange = Range.THIS_YEAR;
model.calculateDatesFromRange();
}),
],
);

@ -1,8 +1,9 @@
import 'package:dartz/dartz.dart';
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/utils/date_util.dart';
import 'package:hmg_patient_app_new/features/lab/models/Range.dart';
class LabRangeViewModel extends ChangeNotifier {
class DateRangeSelectorRangeViewModel extends ChangeNotifier {
List months = [
'Jan',
'Feb',
@ -17,15 +18,13 @@ class LabRangeViewModel extends ChangeNotifier {
'Nov',
'Dec'
];
bool isGraphVisible = true;
Range? _currentlySelectedRange;
Range? get currentlySelectedRange => _currentlySelectedRange;
set currentlySelectedRange(Range? value) {
_currentlySelectedRange = value;
notifyListeners();
}
DateTime? _toDate;
@ -34,7 +33,7 @@ class LabRangeViewModel extends ChangeNotifier {
set toDate(DateTime? value) {
_toDate = value;
notifyListeners();
}
DateTime? _fromDate;
@ -43,16 +42,16 @@ class LabRangeViewModel extends ChangeNotifier {
set fromDate(DateTime? value) {
_fromDate = value;
notifyListeners();
}
LabRangeViewModel();
DateRangeSelectorRangeViewModel();
get getCurrentYear => DateTime.now().year;
calculateDatesFromRange() {
_toDate = DateTime.now();
_toDate = DateTime.now().provideDateOnly();
switch (_currentlySelectedRange) {
case Range.WEEKLY:
_fromDate = _toDate!.subtract(Duration(days: 7));
@ -65,6 +64,7 @@ class LabRangeViewModel extends ChangeNotifier {
_fromDate = DateTime(_toDate!.year, DateTime.january, 01);
default:
}
notifyListeners();
}
getDateString(DateTime? date){
@ -78,16 +78,12 @@ class LabRangeViewModel extends ChangeNotifier {
toDate = null;
fromDate = null;
currentlySelectedRange = null;
isGraphVisible = true;
notifyListeners();
}
resetCurrentlySelectedRange(){
currentlySelectedRange = null;
}
alterGraphVisibility(){
isGraphVisible = !isGraphVisible;
notifyListeners();
}
}

@ -236,7 +236,7 @@ class CustomGraph extends StatelessWidget {
gradient: LinearGradient(
colors: [
graphShadowColor,
Colors.transparent,
Colors.white,
],
begin: Alignment.topCenter,
end: Alignment.bottomCenter,

@ -219,12 +219,12 @@ class MyInAppBrowser extends InAppBrowser {
// service.tamaraInsertRequest(tamaraRequestModel, context).then((res) {
// // if (context != null) GifLoaderDialogUtils.hideDialog(context);
// generateTamaraURL(amount, orderDesc, transactionID, projId, emailId, paymentMethod, patientType, patientName, patientID, authenticatedUser, isLiveCareAppo, servID, LiveServID, appoDate,
// appoNo, clinicID, doctorID, "", installments)
// .then((value) {
// paymentType = _PAYMENT_TYPE.PATIENT;
// this.browser.openUrlRequest(urlRequest: URLRequest(url: WebUri.uri(Uri.parse(value))), options: _InAppBrowserOptions);
// });
generateTamaraURL(amount, orderDesc, transactionID, projId, emailId, paymentMethod, patientType, patientName, patientID, authenticatedUser, isLiveCareAppo, servID, LiveServID, appoDate, appoNo,
clinicID, doctorID, "", installments)
.then((value) {
paymentType = _PAYMENT_TYPE.PATIENT;
this.browser.openUrlRequest(urlRequest: URLRequest(url: WebUri.uri(Uri.parse(value))), options: _InAppBrowserOptions);
});
// }).catchError((err) {
// print(err);
// // if (context != null) GifLoaderDialogUtils.hideDialog(context);
@ -333,12 +333,12 @@ class MyInAppBrowser extends InAppBrowser {
form = form.replaceFirst('PROJECT_ID_VALUE', projId);
form = form.replaceFirst('PAYMENT_OPTION_VALUE', paymentMethod);
form = form.replaceFirst('LANG_VALUE', currentLanguageID);
form = form.replaceFirst('SERVICE_URL_VALUE', "https://mdlaboratories.com/tamaralive/Home/Checkout");
form = form.replaceFirst('SERVICE_URL_VALUE', ApiConsts.TAMARA_URL);
form = form.replaceFirst('INSTALLMENTS_VALUE', installments);
form = form.replaceFirst('INSTALLMENTS_VALUE', "3");
form = form.replaceFirst('CUSTNATIONALID_VALUE', authUser.patientIdentificationNo!);
form = form.replaceFirst('CUSTMOBILE_VALUE', authUser.mobileNumber!);
form = form.replaceFirst('CUSTDOB_VALUE', DateUtil.getDayMonthYearDateFormatted(authUser.strDateofBirth!));
form = form.replaceFirst('CUSTDOB_VALUE', DateUtil.getDayMonthYearDateFormatted(DateUtil.convertStringToDate(authUser.dateofBirth!)));
form = form.replaceFirst('CURRENCY_VALUE', authUser.outSa == 0 ? "SAR" : "AED");
form = form.replaceFirst('COUNTRY_CODE_VALUE', authUser.outSa == 0 ? "966" : "971");

@ -85,6 +85,7 @@ dependencies:
gms_check: ^1.0.4
huawei_location: ^6.14.2+301
intl: ^0.20.2
flutter_widget_from_html: ^0.17.1
dev_dependencies:
flutter_test:

Loading…
Cancel
Save