commit
						5a1bd6a5b8
					
				| @ -0,0 +1 @@ | |||||||
|  | {"nm":"Comp 1","ddd":0,"h":100,"w":100,"meta":{"g":"@lottiefiles/toolkit-js 0.33.2"},"layers":[{"ty":4,"nm":"Shape Layer 2","sr":1,"st":0,"op":300,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[5.277,-32.723,0],"ix":1},"s":{"a":0,"k":[4.91,4.91,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":0,"k":[50.2,50.18,0],"ix":2},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":11}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"mn":"ADBE Vector Group","nm":"Ellipse 2","ix":1,"cix":2,"np":3,"it":[{"ty":"el","bm":0,"hd":false,"mn":"ADBE Vector Shape - Ellipse","nm":"Ellipse Path 1","d":1,"p":{"a":0,"k":[0,0],"ix":3},"s":{"a":0,"k":[102.555,102.555],"ix":2}},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.0941,0.7608,0.451],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[3.277,-34.527],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"gr","bm":0,"hd":false,"mn":"ADBE Vector Group","nm":"Ellipse 1","ix":2,"cix":2,"np":3,"it":[{"ty":"el","bm":0,"hd":false,"mn":"ADBE Vector Shape - Ellipse","nm":"Ellipse Path 1","d":1,"p":{"a":0,"k":[0,0],"ix":3},"s":{"a":0,"k":[102.555,102.555],"ix":2}},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.0941,0.7608,0.451],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":1,"k":[{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[100,100],"t":0},{"s":[295,295],"t":60}],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[3.277,-34.527],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":1,"k":[{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[100],"t":0},{"s":[0],"t":60}],"ix":7}}]}],"ind":1}],"v":"5.5.7","fr":60,"op":61,"ip":0,"assets":[]} | ||||||
											
												
													File diff suppressed because one or more lines are too long
												
											
										
									
								| @ -0,0 +1,5 @@ | |||||||
|  | <svg width="24" height="25" viewBox="0 0 24 25" fill="none" xmlns="http://www.w3.org/2000/svg"> | ||||||
|  |     <path d="M12 2.75H11.9278C9.92082 2.74998 8.31557 2.74996 7.0558 2.91933C5.75291 3.0945 4.67453 3.46676 3.82064 4.32064C2.96676 5.17453 2.5945 6.25291 2.41933 7.5558C2.24996 8.81557 2.24998 10.4208 2.25 12.4278V12.5722C2.24998 14.5792 2.24996 16.1844 2.41933 17.4442C2.5945 18.7471 2.96676 19.8255 3.82064 20.6794C4.67453 21.5332 5.75291 21.9055 7.0558 22.0807C8.31558 22.25 9.92083 22.25 11.9278 22.25H12.0722C14.0792 22.25 15.6844 22.25 16.9442 22.0807C18.2471 21.9055 19.3255 21.5332 20.1794 20.6794C21.0332 19.8255 21.4055 18.7471 21.5807 17.4442C21.75 16.1844 21.75 14.5792 21.75 12.5722V12.5C21.75 11.9615 21.3135 11.525 20.775 11.525C20.2365 11.525 19.8 11.9615 19.8 12.5C19.8 14.5959 19.7979 16.0697 19.6481 17.1844C19.502 18.271 19.2317 18.8693 18.8005 19.3005C18.3693 19.7317 17.771 20.002 16.6844 20.1481C15.5697 20.2979 14.0959 20.3 12 20.3C9.90415 20.3 8.43035 20.2979 7.31564 20.1481C6.22898 20.002 5.63068 19.7317 5.1995 19.3005C4.76831 18.8693 4.49804 18.271 4.35194 17.1844C4.20207 16.0697 4.2 14.5959 4.2 12.5C4.2 10.4042 4.20207 8.93035 4.35194 7.81564C4.49804 6.72898 4.76831 6.13068 5.1995 5.6995C5.63068 5.26831 6.22898 4.99804 7.31564 4.85194C8.43035 4.70207 9.90415 4.7 12 4.7C12.5385 4.7 12.975 4.26348 12.975 3.725C12.975 3.18652 12.5385 2.75 12 2.75Z" fill="#2E3039"/> | ||||||
|  |     <path d="M17.0855 3.5503C18.1526 2.48323 19.8827 2.48323 20.9497 3.5503C22.0168 4.61736 22.0168 6.34742 20.9497 7.41448L20.078 8.28615L16.2139 4.42188L17.0855 3.5503Z" fill="#2E3039"/> | ||||||
|  |     <path d="M15.1533 5.48254L19.0174 9.34682L14.2337 14.1306C13.2653 15.0992 12.6734 15.6912 11.9448 16.0983C11.5383 16.3255 10.9218 16.5239 10.291 16.6994C9.64198 16.88 8.89524 17.0578 8.18242 17.2275L8.17373 17.2296C7.92037 17.2899 7.65385 17.2145 7.46969 17.0303C7.28552 16.8462 7.21009 16.5796 7.27041 16.3263L7.27249 16.3175C7.44221 15.6047 7.62 14.858 7.8006 14.209C7.97615 13.5782 8.17455 12.9617 8.40167 12.5553C8.80884 11.8266 9.40083 11.2348 10.3694 10.2664L15.1533 5.48254Z" fill="#2E3039"/> | ||||||
|  | </svg> | ||||||
| After Width: | Height: | Size: 2.0 KiB | 
| @ -1,3 +1,3 @@ | |||||||
| <svg width="18" height="12" viewBox="0 0 18 12" fill="none" xmlns="http://www.w3.org/2000/svg"> | <svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg"> | ||||||
|     <path d="M17.7495 6.00047C17.7495 5.62691 17.5839 5.28034 17.427 5.02011C17.2577 4.7393 17.0301 4.44937 16.7803 4.16669C16.2794 3.59962 15.6257 2.99064 14.9913 2.43977C14.3532 1.88567 13.7173 1.37546 13.2423 1.00468C13.0044 0.818986 12.8059 0.66755 12.6665 0.562239C12.5968 0.509569 12.5418 0.468397 12.5039 0.440203L12.4604 0.407818L12.4488 0.399264L12.4448 0.396304C12.1113 0.150642 11.6414 0.221533 11.3957 0.555035C11.1501 0.888521 11.2213 1.358 11.5547 1.60367L11.5674 1.61308L11.6075 1.64285C11.6429 1.66925 11.6953 1.7085 11.7623 1.75911C11.8964 1.86036 12.0885 2.00695 12.3193 2.18713C12.7818 2.5481 13.3959 3.04099 14.0078 3.57235C14.6234 4.10694 15.2197 4.66576 15.6562 5.15983C15.6838 5.19108 15.7105 5.2218 15.7364 5.25195L0.999512 5.25196C0.585298 5.25196 0.249512 5.58774 0.249512 6.00195C0.249512 6.41617 0.585299 6.75196 0.999512 6.75196L15.7338 6.75195C15.7088 6.78116 15.6829 6.81088 15.6562 6.84111C15.2197 7.33518 14.6234 7.89401 14.0078 8.4286C13.3959 8.95995 12.7818 9.45285 12.3193 9.81382C12.0885 9.99399 11.8964 10.1406 11.7623 10.2418C11.6953 10.2925 11.6429 10.3317 11.6075 10.3581L11.5674 10.3879L11.5547 10.3973C11.2213 10.6429 11.1501 11.1124 11.3957 11.4459C11.6414 11.7794 12.1113 11.8503 12.4448 11.6046L12.4488 11.6017L12.4604 11.5931L12.5039 11.5607C12.5418 11.5326 12.5968 11.4914 12.6665 11.4387C12.8059 11.3334 13.0044 11.182 13.2423 10.9963C13.7173 10.6255 14.3532 10.1153 14.9913 9.56118C15.6257 9.01031 16.2794 8.40133 16.7803 7.83425C17.0301 7.55158 17.2577 7.26165 17.427 6.98083C17.5829 6.72217 17.7475 6.37819 17.7495 6.0072" fill="white"/> |     <path d="M28.7495 19.9995C28.7495 19.6259 28.5839 19.2794 28.427 19.0191C28.2577 18.7383 28.0301 18.4484 27.7803 18.1657C27.2794 17.5986 26.6257 16.9897 25.9913 16.4388C25.3532 15.8847 24.7173 15.3745 24.2423 15.0037C24.0044 14.818 23.8059 14.6666 23.6665 14.5613C23.5968 14.5086 23.5418 14.4674 23.5039 14.4392L23.4604 14.4068L23.4488 14.3983L23.4448 14.3953C23.1113 14.1497 22.6414 14.2206 22.3957 14.5541C22.1501 14.8875 22.2213 15.357 22.5547 15.6027L22.5674 15.6121L22.6075 15.6419C22.6429 15.6683 22.6953 15.7075 22.7623 15.7581C22.8964 15.8594 23.0885 16.006 23.3193 16.1862C23.7818 16.5471 24.3959 17.04 25.0078 17.5714C25.6234 18.106 26.2197 18.6648 26.6562 19.1589C26.6838 19.1901 26.7105 19.2208 26.7364 19.251L11.9995 19.251C11.5853 19.251 11.2495 19.5868 11.2495 20.001C11.2495 20.4152 11.5853 20.751 11.9995 20.751L26.7338 20.751C26.7088 20.7802 26.6829 20.8099 26.6562 20.8401C26.2197 21.3342 25.6234 21.893 25.0078 22.4276C24.3959 22.959 23.7818 23.4519 23.3193 23.8128C23.0885 23.993 22.8964 24.1396 22.7623 24.2409C22.6953 24.2915 22.6429 24.3307 22.6075 24.3571L22.5674 24.3869L22.5547 24.3963C22.2213 24.642 22.1501 25.1115 22.3957 25.4449C22.6414 25.7784 23.1113 25.8493 23.4448 25.6037L23.4488 25.6007L23.4604 25.5922L23.5039 25.5598C23.5418 25.5316 23.5968 25.4904 23.6665 25.4377C23.8059 25.3324 24.0044 25.181 24.2423 24.9953C24.7173 24.6245 25.3532 24.1143 25.9913 23.5602C26.6257 23.0093 27.2794 22.4003 27.7803 21.8333C28.0301 21.5506 28.2577 21.2607 28.427 20.9799C28.5829 20.7212 28.7475 20.3772 28.7495 20.0062" fill="#2E3039"/> | ||||||
| </svg> | </svg> | ||||||
|  | |||||||
| Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.6 KiB | 
| @ -0,0 +1,4 @@ | |||||||
|  | <svg width="20" height="14" viewBox="0 0 20 14" fill="none" xmlns="http://www.w3.org/2000/svg"> | ||||||
|  |     <path d="M12.43 13.823C12.24 13.823 12.05 13.753 11.9 13.603C11.61 13.313 11.61 12.833 11.9 12.543L17.44 7.00305L11.9 1.46305C11.61 1.17305 11.61 0.693047 11.9 0.403047C12.19 0.113047 12.67 0.113047 12.96 0.403047L19.03 6.47305C19.32 6.76305 19.32 7.24305 19.03 7.53305L12.96 13.603C12.81 13.753 12.62 13.823 12.43 13.823Z" fill="#2E3039"/> | ||||||
|  |     <path d="M18.33 7.75305H1.5C1.09 7.75305 0.75 7.41305 0.75 7.00305C0.75 6.59305 1.09 6.25305 1.5 6.25305H18.33C18.74 6.25305 19.08 6.59305 19.08 7.00305C19.08 7.41305 18.74 7.75305 18.33 7.75305Z" fill="#2E3039"/> | ||||||
|  | </svg> | ||||||
| After Width: | Height: | Size: 666 B | 
| @ -0,0 +1,4 @@ | |||||||
|  | <svg width="19" height="19" viewBox="0 0 19 19" fill="none" xmlns="http://www.w3.org/2000/svg"> | ||||||
|  |     <circle cx="9.5" cy="9.5" r="6.5" fill="#18C273"/> | ||||||
|  |     <circle cx="9.5" cy="9.5" r="8" stroke="#18C273" stroke-opacity="0.31" stroke-width="3"/> | ||||||
|  | </svg> | ||||||
| After Width: | Height: | Size: 252 B | 
| @ -0,0 +1,3 @@ | |||||||
|  | <svg width="24" height="25" viewBox="0 0 24 25" fill="none" xmlns="http://www.w3.org/2000/svg"> | ||||||
|  |     <path fill-rule="evenodd" clip-rule="evenodd" d="M4 1.75C3.58579 1.75 3.25 2.08579 3.25 2.5C3.25 2.91421 3.58579 3.25 4 3.25H4.25L4.25 5.5C4.25 8.58811 6.05618 11.2544 8.66976 12.5C6.05618 13.7456 4.25 16.4119 4.25 19.5L4.25 21.75H4C3.58579 21.75 3.25 22.0858 3.25 22.5C3.25 22.9142 3.58579 23.25 4 23.25L20 23.25C20.4142 23.25 20.75 22.9142 20.75 22.5C20.75 22.0858 20.4142 21.75 20 21.75H19.75V19.5C19.75 16.4119 17.9438 13.7456 15.3302 12.5C17.9438 11.2544 19.75 8.58811 19.75 5.5V3.25H20C20.4142 3.25 20.75 2.91421 20.75 2.5C20.75 2.08579 20.4142 1.75 20 1.75L4 1.75ZM18.25 3.25L5.75 3.25L5.75 5.5C5.75 8.95178 8.54822 11.75 12 11.75C15.4518 11.75 18.25 8.95178 18.25 5.5V3.25ZM18.25 21.75L18.25 19.5C18.25 16.0482 15.4518 13.25 12 13.25C8.54822 13.25 5.75 16.0482 5.75 19.5L5.75 21.75L18.25 21.75Z" fill="#2B353E"/> | ||||||
|  | </svg> | ||||||
| After Width: | Height: | Size: 927 B | 
| @ -0,0 +1,206 @@ | |||||||
|  | import 'dart:io'; | ||||||
|  | 
 | ||||||
|  | import 'package:dartz/dartz.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/core/api/api_client.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/core/api_consts.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/core/common_models/generic_api_model.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/core/exceptions/api_failure.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/features/immediate_livecare/models/resp_models/get_livecare_immediate_clinics_response_model.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/features/immediate_livecare/models/resp_models/get_livecare_immediate_fees_response_model.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/features/immediate_livecare/models/resp_models/get_patient_livecare_history_response_model.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/services/logger_service.dart'; | ||||||
|  | 
 | ||||||
|  | abstract class ImmediateLiveCareRepo { | ||||||
|  |   Future<Either<Failure, GenericApiModel<List<GetLiveCareClinicListResponseModel>>>> getLiveCareImmediateClinicsList(int age, int genderID, {Function(dynamic)? onSuccess, Function(String)? onError}); | ||||||
|  | 
 | ||||||
|  |   Future<Either<Failure, GenericApiModel<LiveCareImmediateAppointmentFeesList>>> getLiveCareImmediateAppointmentFees(int age, int genderID, int serviceID, | ||||||
|  |       {Function(dynamic)? onSuccess, Function(String)? onError}); | ||||||
|  | 
 | ||||||
|  |   Future<Either<Failure, GenericApiModel<dynamic>>> addNewCallRequestForImmediateLiveCare( | ||||||
|  |       int age, int gender, int serviceID, String clientRequestID, int callTypeID, bool isPharma, String deviceToken, String voipToken, | ||||||
|  |       {Function(dynamic)? onSuccess, Function(String)? onError}); | ||||||
|  | 
 | ||||||
|  |   Future<Either<Failure, GenericApiModel<List<PatientLiveCareHistory>>>> getPatientLiveCareHistory({Function(dynamic)? onSuccess, Function(String)? onError}); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | class ImmediateLiveCareRepoImp implements ImmediateLiveCareRepo { | ||||||
|  |   final ApiClient apiClient; | ||||||
|  |   final LoggerService loggerService; | ||||||
|  | 
 | ||||||
|  |   ImmediateLiveCareRepoImp({required this.loggerService, required this.apiClient}); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   Future<Either<Failure, GenericApiModel<List<GetLiveCareClinicListResponseModel>>>> getLiveCareImmediateClinicsList(int age, int genderID, | ||||||
|  |       {Function(dynamic)? onSuccess, Function(String)? onError}) async { | ||||||
|  |     Map<String, dynamic> mapDevice = { | ||||||
|  |       "Age": age, | ||||||
|  |       "Gender": genderID, | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     try { | ||||||
|  |       GenericApiModel<List<GetLiveCareClinicListResponseModel>>? apiResponse; | ||||||
|  |       Failure? failure; | ||||||
|  |       await apiClient.post( | ||||||
|  |         GET_LIVECARE_CLINICS, | ||||||
|  |         body: mapDevice, | ||||||
|  |         onFailure: (error, statusCode, {messageStatus, failureType}) { | ||||||
|  |           failure = failureType; | ||||||
|  |           onError!(error); | ||||||
|  |         }, | ||||||
|  |         onSuccess: (response, statusCode, {messageStatus, errorMessage}) { | ||||||
|  |           try { | ||||||
|  |             final list = response['PatientER_GetClinicsList']; | ||||||
|  | 
 | ||||||
|  |             final clinicsList = list.map((item) => GetLiveCareClinicListResponseModel.fromJson(item as Map<String, dynamic>)).toList().cast<GetLiveCareClinicListResponseModel>(); | ||||||
|  | 
 | ||||||
|  |             apiResponse = GenericApiModel<List<GetLiveCareClinicListResponseModel>>( | ||||||
|  |               messageStatus: messageStatus, | ||||||
|  |               statusCode: statusCode, | ||||||
|  |               errorMessage: null, | ||||||
|  |               data: clinicsList, | ||||||
|  |             ); | ||||||
|  |           } 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<LiveCareImmediateAppointmentFeesList>>> getLiveCareImmediateAppointmentFees(int age, int genderID, int serviceID, | ||||||
|  |       {Function(dynamic)? onSuccess, Function(String)? onError}) async { | ||||||
|  |     Map<String, dynamic> mapDevice = { | ||||||
|  |       "Age": age, | ||||||
|  |       "Gender": genderID, | ||||||
|  |       "ServiceID": serviceID, | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     try { | ||||||
|  |       GenericApiModel<LiveCareImmediateAppointmentFeesList>? apiResponse; | ||||||
|  |       Failure? failure; | ||||||
|  |       await apiClient.post( | ||||||
|  |         GET_ER_APPOINTMENT_FEES, | ||||||
|  |         body: mapDevice, | ||||||
|  |         onFailure: (error, statusCode, {messageStatus, failureType}) { | ||||||
|  |           failure = failureType; | ||||||
|  |           onError!(error); | ||||||
|  |         }, | ||||||
|  |         onSuccess: (response, statusCode, {messageStatus, errorMessage}) { | ||||||
|  |           try { | ||||||
|  |             final respObject = response['GetERAppointmentFeesList']; | ||||||
|  | 
 | ||||||
|  |             final liveCareFeesObj = LiveCareImmediateAppointmentFeesList.fromJson(respObject); | ||||||
|  | 
 | ||||||
|  |             apiResponse = GenericApiModel<LiveCareImmediateAppointmentFeesList>( | ||||||
|  |               messageStatus: messageStatus, | ||||||
|  |               statusCode: statusCode, | ||||||
|  |               errorMessage: null, | ||||||
|  |               data: liveCareFeesObj, | ||||||
|  |             ); | ||||||
|  |           } 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>> addNewCallRequestForImmediateLiveCare( | ||||||
|  |       int age, int gender, int serviceID, String clientRequestID, int callTypeID, bool isPharma, String deviceToken, String voipToken, | ||||||
|  |       {Function(dynamic p1)? onSuccess, Function(String p1)? onError}) async { | ||||||
|  |     Map<String, dynamic> mapDevice = { | ||||||
|  |       "IsPharmacy": isPharma, | ||||||
|  |       "ErServiceID": serviceID, | ||||||
|  |       "ClientRequestID": clientRequestID, | ||||||
|  |       "DeviceToken": deviceToken, | ||||||
|  |       "VoipToken": voipToken, | ||||||
|  |       "IsFlutter": true, | ||||||
|  |       "DeviceType": Platform.isIOS ? 'iOS' : 'Android', | ||||||
|  |       "Age": age, | ||||||
|  |       "Gender": gender, | ||||||
|  |       "IsVoip": Platform.isIOS ? true : false, | ||||||
|  |       "CallTypeID": callTypeID | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     try { | ||||||
|  |       GenericApiModel<dynamic>? apiResponse; | ||||||
|  |       Failure? failure; | ||||||
|  |       await apiClient.post( | ||||||
|  |         ADD_NEW_CALL_FOR_PATIENT_ER, | ||||||
|  |         body: mapDevice, | ||||||
|  |         onFailure: (error, statusCode, {messageStatus, failureType}) { | ||||||
|  |           failure = failureType; | ||||||
|  |           onError!(error); | ||||||
|  |         }, | ||||||
|  |         onSuccess: (response, statusCode, {messageStatus, errorMessage}) { | ||||||
|  |           try { | ||||||
|  |             apiResponse = GenericApiModel<dynamic>( | ||||||
|  |               messageStatus: messageStatus, | ||||||
|  |               statusCode: statusCode, | ||||||
|  |               errorMessage: null, | ||||||
|  |               data: true, | ||||||
|  |             ); | ||||||
|  |           } 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<PatientLiveCareHistory>>>> getPatientLiveCareHistory({Function(dynamic)? onSuccess, Function(String)? onError}) async { | ||||||
|  |     Map<String, dynamic> mapDevice = {}; | ||||||
|  | 
 | ||||||
|  |     try { | ||||||
|  |       GenericApiModel<List<PatientLiveCareHistory>>? apiResponse; | ||||||
|  |       Failure? failure; | ||||||
|  |       await apiClient.post( | ||||||
|  |         GET_LIVECARE_HISTORY, | ||||||
|  |         body: mapDevice, | ||||||
|  |         onFailure: (error, statusCode, {messageStatus, failureType}) { | ||||||
|  |           failure = failureType; | ||||||
|  |           onError!(error); | ||||||
|  |         }, | ||||||
|  |         onSuccess: (response, statusCode, {messageStatus, errorMessage}) { | ||||||
|  |           try { | ||||||
|  |             final list = response['ErRequestHistoryList']; | ||||||
|  | 
 | ||||||
|  |             final liveCareHistoryList = list.map((item) => PatientLiveCareHistory.fromJson(item as Map<String, dynamic>)).toList().cast<PatientLiveCareHistory>(); | ||||||
|  | 
 | ||||||
|  |             apiResponse = GenericApiModel<List<PatientLiveCareHistory>>( | ||||||
|  |               messageStatus: messageStatus, | ||||||
|  |               statusCode: statusCode, | ||||||
|  |               errorMessage: null, | ||||||
|  |               data: liveCareHistoryList, | ||||||
|  |             ); | ||||||
|  |           } 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())); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
| @ -0,0 +1,162 @@ | |||||||
|  | import 'package:flutter/cupertino.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/core/app_state.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/core/cache_consts.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/core/dependencies.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/core/utils/utils.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/features/immediate_livecare/immediate_livecare_repo.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/features/immediate_livecare/models/resp_models/get_livecare_immediate_clinics_response_model.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/features/immediate_livecare/models/resp_models/get_livecare_immediate_fees_response_model.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/features/immediate_livecare/models/resp_models/get_patient_livecare_history_response_model.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/services/error_handler_service.dart'; | ||||||
|  | 
 | ||||||
|  | import '../../services/navigation_service.dart'; | ||||||
|  | 
 | ||||||
|  | class ImmediateLiveCareViewModel extends ChangeNotifier { | ||||||
|  |   ImmediateLiveCareViewModel({ | ||||||
|  |     required this.immediateLiveCareRepo, | ||||||
|  |     required this.errorHandlerService, | ||||||
|  |     required this.navigationService, | ||||||
|  |     required this.myAppointmentsViewModel, | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   ImmediateLiveCareRepo immediateLiveCareRepo; | ||||||
|  |   ErrorHandlerService errorHandlerService; | ||||||
|  |   final NavigationService navigationService; | ||||||
|  |   MyAppointmentsViewModel myAppointmentsViewModel; | ||||||
|  | 
 | ||||||
|  |   List<GetLiveCareClinicListResponseModel> immediateLiveCareClinicsList = []; | ||||||
|  |   bool isImmediateLiveCareClinicsLoading = false; | ||||||
|  |   int liveCareSelectedCallType = 0; // 1- Video, 2- Audio, 3- Phone | ||||||
|  |   late GetLiveCareClinicListResponseModel immediateLiveCareSelectedClinic; | ||||||
|  |   late LiveCareImmediateAppointmentFeesList liveCareImmediateAppointmentFeesList; | ||||||
|  | 
 | ||||||
|  |   List<PatientLiveCareHistory> patientLiveCareHistoryList = []; | ||||||
|  |   bool patientHasPendingLiveCareRequest = false; | ||||||
|  | 
 | ||||||
|  |   late AppState _appState; | ||||||
|  | 
 | ||||||
|  |   initImmediateLiveCare() { | ||||||
|  |     _appState = getIt<AppState>(); | ||||||
|  |     immediateLiveCareClinicsList = []; | ||||||
|  |     patientLiveCareHistoryList = []; | ||||||
|  |     isImmediateLiveCareClinicsLoading = true; | ||||||
|  |     patientHasPendingLiveCareRequest = false; | ||||||
|  |     liveCareSelectedCallType = 0; // 1- Video, 2- Audio, 3- Phone | ||||||
|  |     immediateLiveCareSelectedClinic = GetLiveCareClinicListResponseModel(); | ||||||
|  |     liveCareImmediateAppointmentFeesList = LiveCareImmediateAppointmentFeesList(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   setLiveCareSelectedCallType(int value) { | ||||||
|  |     liveCareSelectedCallType = value; | ||||||
|  |     notifyListeners(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   setImmediateLiveCareSelectedClinic(GetLiveCareClinicListResponseModel clinic) { | ||||||
|  |     immediateLiveCareSelectedClinic = clinic; | ||||||
|  |     notifyListeners(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   Future<void> getLiveCareImmediateClinicsList({Function(dynamic)? onSuccess, Function(String)? onError}) async { | ||||||
|  |     immediateLiveCareClinicsList.clear(); | ||||||
|  |     isImmediateLiveCareClinicsLoading = true; | ||||||
|  |     notifyListeners(); | ||||||
|  | 
 | ||||||
|  |     final result = await immediateLiveCareRepo.getLiveCareImmediateClinicsList(_appState.getAuthenticatedUser()!.age!, _appState.getAuthenticatedUser()!.gender!); | ||||||
|  | 
 | ||||||
|  |     result.fold( | ||||||
|  |       (failure) async => await errorHandlerService.handleError(failure: failure), | ||||||
|  |       (apiResponse) { | ||||||
|  |         if (apiResponse.messageStatus == 2) { | ||||||
|  |           // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {}); | ||||||
|  |         } else if (apiResponse.messageStatus == 1) { | ||||||
|  |           immediateLiveCareClinicsList = apiResponse.data!; | ||||||
|  | 
 | ||||||
|  |           immediateLiveCareClinicsList.sort((a, b) => b.isOnline!.compareTo(a.isOnline!)); | ||||||
|  | 
 | ||||||
|  |           isImmediateLiveCareClinicsLoading = false; | ||||||
|  |           notifyListeners(); | ||||||
|  |           if (onSuccess != null) { | ||||||
|  |             onSuccess(apiResponse); | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |       }, | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   Future<void> getLiveCareImmediateAppointmentFees({Function(dynamic)? onSuccess, Function(String)? onError}) async { | ||||||
|  |     final result = | ||||||
|  |         await immediateLiveCareRepo.getLiveCareImmediateAppointmentFees(_appState.getAuthenticatedUser()!.age!, _appState.getAuthenticatedUser()!.gender!, immediateLiveCareSelectedClinic.serviceID!); | ||||||
|  | 
 | ||||||
|  |     result.fold( | ||||||
|  |       (failure) async { | ||||||
|  |         onError!(failure.message); | ||||||
|  |       }, | ||||||
|  |       (apiResponse) { | ||||||
|  |         if (apiResponse.messageStatus == 2) { | ||||||
|  |           onError!(apiResponse.errorMessage ?? "Unknown error occurred"); | ||||||
|  |           // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {}); | ||||||
|  |         } else if (apiResponse.messageStatus == 1) { | ||||||
|  |           liveCareImmediateAppointmentFeesList = apiResponse.data!; | ||||||
|  |           notifyListeners(); | ||||||
|  |           if (onSuccess != null) { | ||||||
|  |             onSuccess(apiResponse); | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |       }, | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   Future<void> addNewCallRequestForImmediateLiveCare(String transID, {Function(dynamic)? onSuccess, Function(String)? onError}) async { | ||||||
|  |     final result = await immediateLiveCareRepo.addNewCallRequestForImmediateLiveCare(_appState.getAuthenticatedUser()!.age!, _appState.getAuthenticatedUser()!.gender!, | ||||||
|  |         immediateLiveCareSelectedClinic.serviceID!, transID, liveCareSelectedCallType, false, _appState.deviceToken, await Utils.getStringFromPrefs(CacheConst.voipToken)); | ||||||
|  | 
 | ||||||
|  |     result.fold( | ||||||
|  |       (failure) async { | ||||||
|  |         onError!(failure.message); | ||||||
|  |       }, | ||||||
|  |       (apiResponse) { | ||||||
|  |         if (apiResponse.messageStatus == 2) { | ||||||
|  |           onError!(apiResponse.errorMessage ?? "Unknown error occurred"); | ||||||
|  |           // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {}); | ||||||
|  |         } else if (apiResponse.messageStatus == 1) { | ||||||
|  |           notifyListeners(); | ||||||
|  |           if (onSuccess != null) { | ||||||
|  |             onSuccess(apiResponse); | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |       }, | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   Future<void> getPatientLiveCareHistory({Function(dynamic)? onSuccess, Function(String)? onError}) async { | ||||||
|  |     final result = await immediateLiveCareRepo.getPatientLiveCareHistory(); | ||||||
|  | 
 | ||||||
|  |     result.fold( | ||||||
|  |       (failure) async { | ||||||
|  |         onError!(failure.message); | ||||||
|  |       }, | ||||||
|  |       (apiResponse) { | ||||||
|  |         if (apiResponse.messageStatus == 2) { | ||||||
|  |           onError!(apiResponse.errorMessage ?? "Unknown error occurred"); | ||||||
|  |           // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {}); | ||||||
|  |         } else if (apiResponse.messageStatus == 1) { | ||||||
|  |           patientLiveCareHistoryList = apiResponse.data!; | ||||||
|  |           if (patientLiveCareHistoryList.isNotEmpty) { | ||||||
|  |             if (patientLiveCareHistoryList[0].callStatus! < 4) { | ||||||
|  |               patientHasPendingLiveCareRequest = true; | ||||||
|  |             } else { | ||||||
|  |               patientHasPendingLiveCareRequest = false; | ||||||
|  |             } | ||||||
|  |           } else { | ||||||
|  |             patientHasPendingLiveCareRequest = false; | ||||||
|  |           } | ||||||
|  |           notifyListeners(); | ||||||
|  |           if (onSuccess != null) { | ||||||
|  |             onSuccess(apiResponse); | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |       }, | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | } | ||||||
| @ -0,0 +1,97 @@ | |||||||
|  | class GetLiveCareClinicListResponseModel { | ||||||
|  |   int? iD; | ||||||
|  |   int? serviceID; | ||||||
|  |   String? serviceName; | ||||||
|  |   String? serviceNameN; | ||||||
|  |   int? clinicID; | ||||||
|  |   int? age; | ||||||
|  |   bool? isCheckAgeBelow; | ||||||
|  |   int? gender; | ||||||
|  |   bool? isActive; | ||||||
|  |   String? createdOn; | ||||||
|  |   String? createdBy; | ||||||
|  |   int? isOnline; | ||||||
|  |   bool? projectOutSA; | ||||||
|  |   List<ShiftTimings>? shiftTimings; | ||||||
|  | 
 | ||||||
|  |   GetLiveCareClinicListResponseModel( | ||||||
|  |       {this.iD, | ||||||
|  |       this.serviceID, | ||||||
|  |       this.serviceName, | ||||||
|  |       this.serviceNameN, | ||||||
|  |       this.clinicID, | ||||||
|  |       this.age, | ||||||
|  |       this.isCheckAgeBelow, | ||||||
|  |       this.gender, | ||||||
|  |       this.isActive, | ||||||
|  |       this.createdOn, | ||||||
|  |       this.createdBy, | ||||||
|  |       this.isOnline, | ||||||
|  |       this.projectOutSA, | ||||||
|  |       this.shiftTimings}); | ||||||
|  | 
 | ||||||
|  |   GetLiveCareClinicListResponseModel.fromJson(Map<String, dynamic> json) { | ||||||
|  |     iD = json['ID']; | ||||||
|  |     serviceID = json['ServiceID']; | ||||||
|  |     serviceName = json['ServiceName']; | ||||||
|  |     serviceNameN = json['ServiceNameN']; | ||||||
|  |     clinicID = json['ClinicID']; | ||||||
|  |     age = json['Age']; | ||||||
|  |     isCheckAgeBelow = json['IsCheckAgeBelow']; | ||||||
|  |     gender = json['Gender']; | ||||||
|  |     isActive = json['IsActive']; | ||||||
|  |     createdOn = json['CreatedOn']; | ||||||
|  |     createdBy = json['CreatedBy']; | ||||||
|  |     isOnline = json['IsOnline']; | ||||||
|  |     projectOutSA = json['ProjectOutSA']; | ||||||
|  |     if (json['ShiftTimings'] != null) { | ||||||
|  |       shiftTimings = <ShiftTimings>[]; | ||||||
|  |       json['ShiftTimings'].forEach((v) { | ||||||
|  |         shiftTimings!.add(new ShiftTimings.fromJson(v)); | ||||||
|  |       }); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   Map<String, dynamic> toJson() { | ||||||
|  |     final Map<String, dynamic> data = Map<String, dynamic>(); | ||||||
|  |     data['ID'] = this.iD; | ||||||
|  |     data['ServiceID'] = this.serviceID; | ||||||
|  |     data['ServiceName'] = this.serviceName; | ||||||
|  |     data['ServiceNameN'] = this.serviceNameN; | ||||||
|  |     data['ClinicID'] = this.clinicID; | ||||||
|  |     data['Age'] = this.age; | ||||||
|  |     data['IsCheckAgeBelow'] = this.isCheckAgeBelow; | ||||||
|  |     data['Gender'] = this.gender; | ||||||
|  |     data['IsActive'] = this.isActive; | ||||||
|  |     data['CreatedOn'] = this.createdOn; | ||||||
|  |     data['CreatedBy'] = this.createdBy; | ||||||
|  |     data['IsOnline'] = this.isOnline; | ||||||
|  |     data['ProjectOutSA'] = this.projectOutSA; | ||||||
|  |     if (this.shiftTimings != null) { | ||||||
|  |       data['ShiftTimings'] = this.shiftTimings!.map((v) => v.toJson()).toList(); | ||||||
|  |     } | ||||||
|  |     return data; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | class ShiftTimings { | ||||||
|  |   String? endTime; | ||||||
|  |   int? shiftID; | ||||||
|  |   String? startTime; | ||||||
|  | 
 | ||||||
|  |   ShiftTimings({this.endTime, this.shiftID, this.startTime}); | ||||||
|  | 
 | ||||||
|  |   ShiftTimings.fromJson(Map<String, dynamic> json) { | ||||||
|  |     endTime = json['EndTime']; | ||||||
|  |     shiftID = json['ShiftID']; | ||||||
|  |     startTime = json['StartTime']; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   Map<String, dynamic> toJson() { | ||||||
|  |     final Map<String, dynamic> data = Map<String, dynamic>(); | ||||||
|  |     data['EndTime'] = this.endTime; | ||||||
|  |     data['ShiftID'] = this.shiftID; | ||||||
|  |     data['StartTime'] = this.startTime; | ||||||
|  |     return data; | ||||||
|  |   } | ||||||
|  | } | ||||||
| @ -0,0 +1,37 @@ | |||||||
|  | class LiveCareImmediateAppointmentFeesList { | ||||||
|  |   String? amount; | ||||||
|  |   String? companyName; | ||||||
|  |   bool? isInsured; | ||||||
|  |   bool? isShowInsuranceUpdateModule; | ||||||
|  |   bool? isCash; | ||||||
|  |   bool? isEligible; | ||||||
|  |   String? tax; | ||||||
|  |   String? total; | ||||||
|  |   String? currency; | ||||||
|  | 
 | ||||||
|  |   LiveCareImmediateAppointmentFeesList({this.amount, this.companyName, this.isInsured, this.isShowInsuranceUpdateModule, this.tax, this.total, this.currency}); | ||||||
|  | 
 | ||||||
|  |   LiveCareImmediateAppointmentFeesList.fromJson(Map<String, dynamic> json) { | ||||||
|  |     amount = json['Amount']; | ||||||
|  |     companyName = json['CompanyName']; | ||||||
|  |     isInsured = json['IsInsured']; | ||||||
|  |     isCash = json['IsCash']; | ||||||
|  |     isEligible = json['IsEligible']; | ||||||
|  |     isShowInsuranceUpdateModule = json['IsShowInsuranceUpdateModule']; | ||||||
|  |     tax = json['Tax']; | ||||||
|  |     total = json['Total']; | ||||||
|  |     currency = json['currency']; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   Map<String, dynamic> toJson() { | ||||||
|  |     final Map<String, dynamic> data = new Map<String, dynamic>(); | ||||||
|  |     data['Amount'] = this.amount; | ||||||
|  |     data['CompanyName'] = this.companyName; | ||||||
|  |     data['IsInsured'] = this.isInsured; | ||||||
|  |     data['IsShowInsuranceUpdateModule'] = this.isShowInsuranceUpdateModule; | ||||||
|  |     data['Tax'] = this.tax; | ||||||
|  |     data['Total'] = this.total; | ||||||
|  |     data['currency'] = this.currency; | ||||||
|  |     return data; | ||||||
|  |   } | ||||||
|  | } | ||||||
| @ -0,0 +1,84 @@ | |||||||
|  | class PatientLiveCareHistory { | ||||||
|  |   String? appointmentNo; | ||||||
|  |   String? arrivalTime; | ||||||
|  |   num? callDuration; | ||||||
|  |   int? callStatus; | ||||||
|  |   String? clientRequestID; | ||||||
|  |   String? doctorID; | ||||||
|  |   String? doctorName; | ||||||
|  |   String? doctorNameN; | ||||||
|  |   String? doctorTitle; | ||||||
|  |   String? exWaitingTime; | ||||||
|  |   bool? isAppointmentHaveRating; | ||||||
|  |   int? patCount; | ||||||
|  |   int? projectID; | ||||||
|  |   String? sArrivalTime; | ||||||
|  |   int? serviceID; | ||||||
|  |   String? stringCallStatus; | ||||||
|  |   int? vCID; | ||||||
|  |   int? watingtimeInteger; | ||||||
|  | 
 | ||||||
|  |   PatientLiveCareHistory( | ||||||
|  |       {this.appointmentNo, | ||||||
|  |       this.arrivalTime, | ||||||
|  |       this.callDuration, | ||||||
|  |       this.callStatus, | ||||||
|  |       this.clientRequestID, | ||||||
|  |       this.doctorID, | ||||||
|  |       this.doctorName, | ||||||
|  |       this.doctorNameN, | ||||||
|  |       this.doctorTitle, | ||||||
|  |       this.exWaitingTime, | ||||||
|  |       this.isAppointmentHaveRating, | ||||||
|  |       this.patCount, | ||||||
|  |       this.projectID, | ||||||
|  |       this.sArrivalTime, | ||||||
|  |       this.serviceID, | ||||||
|  |       this.stringCallStatus, | ||||||
|  |       this.vCID, | ||||||
|  |       this.watingtimeInteger}); | ||||||
|  | 
 | ||||||
|  |   PatientLiveCareHistory.fromJson(Map<String, dynamic> json) { | ||||||
|  |     appointmentNo = json['AppointmentNo']; | ||||||
|  |     arrivalTime = json['ArrivalTime']; | ||||||
|  |     callDuration = json['CallDuration']; | ||||||
|  |     callStatus = json['CallStatus']; | ||||||
|  |     clientRequestID = json['ClientRequestID']; | ||||||
|  |     doctorID = json['DoctorID']; | ||||||
|  |     doctorName = json['DoctorName']; | ||||||
|  |     doctorNameN = json['DoctorNameN']; | ||||||
|  |     doctorTitle = json['DoctorTitle']; | ||||||
|  |     exWaitingTime = json['Ex_WaitingTime']; | ||||||
|  |     isAppointmentHaveRating = json['IsAppointmentHaveRating']; | ||||||
|  |     patCount = json['Pat_Count']; | ||||||
|  |     projectID = json['ProjectID']; | ||||||
|  |     sArrivalTime = json['SArrivalTime']; | ||||||
|  |     serviceID = json['ServiceID']; | ||||||
|  |     stringCallStatus = json['StringCallStatus']; | ||||||
|  |     vCID = json['VC_ID']; | ||||||
|  |     watingtimeInteger = json['WatingtimeInteger']; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   Map<String, dynamic> toJson() { | ||||||
|  |     final Map<String, dynamic> data = new Map<String, dynamic>(); | ||||||
|  |     data['AppointmentNo'] = this.appointmentNo; | ||||||
|  |     data['ArrivalTime'] = this.arrivalTime; | ||||||
|  |     data['CallDuration'] = this.callDuration; | ||||||
|  |     data['CallStatus'] = this.callStatus; | ||||||
|  |     data['ClientRequestID'] = this.clientRequestID; | ||||||
|  |     data['DoctorID'] = this.doctorID; | ||||||
|  |     data['DoctorName'] = this.doctorName; | ||||||
|  |     data['DoctorNameN'] = this.doctorNameN; | ||||||
|  |     data['DoctorTitle'] = this.doctorTitle; | ||||||
|  |     data['Ex_WaitingTime'] = this.exWaitingTime; | ||||||
|  |     data['IsAppointmentHaveRating'] = this.isAppointmentHaveRating; | ||||||
|  |     data['Pat_Count'] = this.patCount; | ||||||
|  |     data['ProjectID'] = this.projectID; | ||||||
|  |     data['SArrivalTime'] = this.sArrivalTime; | ||||||
|  |     data['ServiceID'] = this.serviceID; | ||||||
|  |     data['StringCallStatus'] = this.stringCallStatus; | ||||||
|  |     data['VC_ID'] = this.vCID; | ||||||
|  |     data['WatingtimeInteger'] = this.watingtimeInteger; | ||||||
|  |     return data; | ||||||
|  |   } | ||||||
|  | } | ||||||
| @ -0,0 +1,304 @@ | |||||||
|  | import 'dart:io'; | ||||||
|  | 
 | ||||||
|  | import 'package:easy_localization/easy_localization.dart'; | ||||||
|  | import 'package:flutter/material.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/core/app_assets.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/core/app_state.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/core/dependencies.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/core/utils/utils.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/features/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/generated/locale_keys.g.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/presentation/book_appointment/livecare/immediate_livecare_payment_page.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/presentation/book_appointment/livecare/widgets/select_livecare_call_type.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/presentation/insurance/insurance_home_page.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/theme/colors.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart'; | ||||||
|  | import 'package:permission_handler/permission_handler.dart'; | ||||||
|  | import 'package:provider/provider.dart'; | ||||||
|  | import 'package:smooth_corner/smooth_corner.dart'; | ||||||
|  | 
 | ||||||
|  | class ImmediateLiveCarePaymentDetails extends StatelessWidget { | ||||||
|  |   ImmediateLiveCarePaymentDetails({super.key}); | ||||||
|  | 
 | ||||||
|  |   late ImmediateLiveCareViewModel immediateLiveCareViewModel; | ||||||
|  |   late AppState appState; | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   Widget build(BuildContext context) { | ||||||
|  |     immediateLiveCareViewModel = Provider.of<ImmediateLiveCareViewModel>(context, listen: false); | ||||||
|  |     appState = getIt.get<AppState>(); | ||||||
|  |     return Scaffold( | ||||||
|  |       backgroundColor: AppColors.scaffoldBgColor, | ||||||
|  |       body: Column( | ||||||
|  |         children: [ | ||||||
|  |           Expanded( | ||||||
|  |             child: CollapsingListView( | ||||||
|  |               title: "Review LiveCare Request".needTranslation, | ||||||
|  |               child: SingleChildScrollView( | ||||||
|  |                 padding: EdgeInsets.symmetric(horizontal: 24.h), | ||||||
|  |                 child: Column( | ||||||
|  |                   crossAxisAlignment: CrossAxisAlignment.start, | ||||||
|  |                   children: [ | ||||||
|  |                     SizedBox(height: 24.h), | ||||||
|  |                     LocaleKeys.patientInfo.tr(context: context).toText16(isBold: true), | ||||||
|  |                     SizedBox(height: 16.h), | ||||||
|  |                     Container( | ||||||
|  |                       decoration: RoundedRectangleBorder().toSmoothCornerDecoration( | ||||||
|  |                         color: AppColors.whiteColor, | ||||||
|  |                         borderRadius: 24.h, | ||||||
|  |                         hasShadow: false, | ||||||
|  |                       ), | ||||||
|  |                       child: Padding( | ||||||
|  |                         padding: EdgeInsets.all(16.h), | ||||||
|  |                         child: Row( | ||||||
|  |                           children: [ | ||||||
|  |                             Image.asset( | ||||||
|  |                               appState.getAuthenticatedUser()?.gender == 1 ? AppAssets.male_img : AppAssets.femaleImg, | ||||||
|  |                               width: 52.h, | ||||||
|  |                               height: 52.h, | ||||||
|  |                             ), | ||||||
|  |                             SizedBox(width: 8.h), | ||||||
|  |                             Column( | ||||||
|  |                               crossAxisAlignment: CrossAxisAlignment.start, | ||||||
|  |                               children: [ | ||||||
|  |                                 "${appState.getAuthenticatedUser()!.firstName} ${appState.getAuthenticatedUser()!.lastName}".toText16(isBold: true), | ||||||
|  |                                 SizedBox(height: 8.h), | ||||||
|  |                                 AppCustomChipWidget(labelText: "${appState.getAuthenticatedUser()!.age} Years Old"), | ||||||
|  |                               ], | ||||||
|  |                             ), | ||||||
|  |                           ], | ||||||
|  |                         ), | ||||||
|  |                       ), | ||||||
|  |                     ), | ||||||
|  |                     SizedBox(height: 24.h), | ||||||
|  |                     "Clinic Information".needTranslation.toText16(isBold: true), | ||||||
|  |                     SizedBox(height: 16.h), | ||||||
|  |                     Container( | ||||||
|  |                       decoration: RoundedRectangleBorder().toSmoothCornerDecoration( | ||||||
|  |                         color: AppColors.whiteColor, | ||||||
|  |                         borderRadius: 24.h, | ||||||
|  |                         hasShadow: false, | ||||||
|  |                       ), | ||||||
|  |                       child: Padding( | ||||||
|  |                         padding: EdgeInsets.all(16.h), | ||||||
|  |                         child: Row( | ||||||
|  |                           children: [ | ||||||
|  |                             Utils.buildSvgWithAssets(icon: AppAssets.generic_clinic_icon, width: 32.h, height: 32.h, fit: BoxFit.contain), | ||||||
|  |                             SizedBox(width: 8.h), | ||||||
|  |                             Column( | ||||||
|  |                               crossAxisAlignment: CrossAxisAlignment.start, | ||||||
|  |                               children: [ | ||||||
|  |                                 (appState.isArabic() | ||||||
|  |                                         ? immediateLiveCareViewModel.immediateLiveCareSelectedClinic.serviceNameN | ||||||
|  |                                         : immediateLiveCareViewModel.immediateLiveCareSelectedClinic.serviceName)! | ||||||
|  |                                     .toText16(isBold: true), | ||||||
|  |                                 // SizedBox(height: 8.h), | ||||||
|  |                                 // AppCustomChipWidget(labelText: "${appState.getAuthenticatedUser()!.age} Years Old"), | ||||||
|  |                               ], | ||||||
|  |                             ), | ||||||
|  |                           ], | ||||||
|  |                         ), | ||||||
|  |                       ), | ||||||
|  |                     ), | ||||||
|  |                     SizedBox(height: 24.h), | ||||||
|  |                     "Selected LiveCare Type".needTranslation.toText16(isBold: true), | ||||||
|  |                     SizedBox(height: 16.h), | ||||||
|  |                     Consumer<BookAppointmentsViewModel>(builder: (context, bookAppointmentsVM, child) { | ||||||
|  |                       return Container( | ||||||
|  |                         decoration: RoundedRectangleBorder().toSmoothCornerDecoration( | ||||||
|  |                           color: AppColors.whiteColor, | ||||||
|  |                           borderRadius: 24.h, | ||||||
|  |                           hasShadow: false, | ||||||
|  |                         ), | ||||||
|  |                         child: Padding( | ||||||
|  |                           padding: EdgeInsets.all(16.h), | ||||||
|  |                           child: Row( | ||||||
|  |                             mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||||
|  |                             children: [ | ||||||
|  |                               Row( | ||||||
|  |                                 children: [ | ||||||
|  |                                   Utils.buildSvgWithAssets(icon: AppAssets.livecare_clinic_icon, width: 32.h, height: 32.h, fit: BoxFit.contain), | ||||||
|  |                                   SizedBox(width: 8.h), | ||||||
|  |                                   getLiveCareType(immediateLiveCareViewModel.liveCareSelectedCallType).toText16(isBold: true), | ||||||
|  |                                 ], | ||||||
|  |                               ), | ||||||
|  |                               Utils.buildSvgWithAssets(icon: AppAssets.edit_icon, width: 24.h, height: 24.h, fit: BoxFit.contain), | ||||||
|  |                             ], | ||||||
|  |                           ), | ||||||
|  |                         ), | ||||||
|  |                       ).onPress(() { | ||||||
|  |                         showCommonBottomSheetWithoutHeight(context, child: SelectLiveCareCallType(immediateLiveCareViewModel: immediateLiveCareViewModel), callBackFunc: () async { | ||||||
|  |                           debugPrint("Selected Call Type: ${immediateLiveCareViewModel.liveCareSelectedCallType}"); | ||||||
|  |                         }, title: "Select LiveCare call type".needTranslation, isCloseButtonVisible: true, isFullScreen: false); | ||||||
|  |                       }); | ||||||
|  |                     }), | ||||||
|  |                     SizedBox(height: 24.h) | ||||||
|  |                   ], | ||||||
|  |                 ), | ||||||
|  |               ), | ||||||
|  |             ), | ||||||
|  |           ), | ||||||
|  |           Container( | ||||||
|  |             decoration: RoundedRectangleBorder().toSmoothCornerDecoration( | ||||||
|  |               color: AppColors.whiteColor, | ||||||
|  |               borderRadius: 24.h, | ||||||
|  |               hasShadow: false, | ||||||
|  |             ), | ||||||
|  |             child: Column( | ||||||
|  |               crossAxisAlignment: CrossAxisAlignment.start, | ||||||
|  |               children: [ | ||||||
|  |                 (immediateLiveCareViewModel.liveCareImmediateAppointmentFeesList.isCash ?? true) | ||||||
|  |                     ? Container( | ||||||
|  |                         height: 50.h, | ||||||
|  |                         decoration: ShapeDecoration( | ||||||
|  |                           color: AppColors.secondaryLightRedBorderColor, | ||||||
|  |                           shape: SmoothRectangleBorder( | ||||||
|  |                             borderRadius: BorderRadius.only(topLeft: Radius.circular(24), topRight: Radius.circular(24)), | ||||||
|  |                             smoothness: 1, | ||||||
|  |                           ), | ||||||
|  |                         ), | ||||||
|  |                         child: Row( | ||||||
|  |                           mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||||
|  |                           children: [ | ||||||
|  |                             "Insurance expired or inactive".needTranslation.toText14(color: AppColors.primaryRedColor, weight: FontWeight.w500).paddingSymmetrical(24.h, 0.h), | ||||||
|  |                             CustomButton( | ||||||
|  |                               text: LocaleKeys.updateInsurance.tr(context: context), | ||||||
|  |                               onPressed: () { | ||||||
|  |                                 Navigator.of(context).push( | ||||||
|  |                                   CustomPageRoute( | ||||||
|  |                                     page: InsuranceHomePage(), | ||||||
|  |                                   ), | ||||||
|  |                                 ); | ||||||
|  |                               }, | ||||||
|  |                               backgroundColor: AppColors.primaryRedColor, | ||||||
|  |                               borderColor: AppColors.secondaryLightRedBorderColor, | ||||||
|  |                               textColor: AppColors.whiteColor, | ||||||
|  |                               fontSize: 10, | ||||||
|  |                               fontWeight: FontWeight.w500, | ||||||
|  |                               borderRadius: 8, | ||||||
|  |                               padding: EdgeInsets.fromLTRB(15, 0, 15, 0), | ||||||
|  |                               height: 30.h, | ||||||
|  |                             ).paddingSymmetrical(24.h, 0.h), | ||||||
|  |                           ], | ||||||
|  |                         ), | ||||||
|  |                       ) | ||||||
|  |                     : const SizedBox(), | ||||||
|  |                 SizedBox(height: 24.h), | ||||||
|  |                 "Total amount to pay".needTranslation.toText18(isBold: true).paddingSymmetrical(24.h, 0.h), | ||||||
|  |                 SizedBox(height: 17.h), | ||||||
|  |                 Row( | ||||||
|  |                   mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||||
|  |                   children: [ | ||||||
|  |                     "Amount before tax".needTranslation.toText14(isBold: true), | ||||||
|  |                     Utils.getPaymentAmountWithSymbol(immediateLiveCareViewModel.liveCareImmediateAppointmentFeesList.amount!.toText16(isBold: true), AppColors.blackColor, 13, | ||||||
|  |                         isSaudiCurrency: immediateLiveCareViewModel.liveCareImmediateAppointmentFeesList.currency!.toLowerCase() == "sar"), | ||||||
|  |                   ], | ||||||
|  |                 ).paddingSymmetrical(24.h, 0.h), | ||||||
|  |                 Row( | ||||||
|  |                   mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||||
|  |                   children: [ | ||||||
|  |                     "VAT 15%".needTranslation.toText14(isBold: true, color: AppColors.greyTextColor), | ||||||
|  |                     Utils.getPaymentAmountWithSymbol( | ||||||
|  |                         immediateLiveCareViewModel.liveCareImmediateAppointmentFeesList.tax!.toText14(isBold: true, color: AppColors.greyTextColor), AppColors.greyTextColor, 13, | ||||||
|  |                         isSaudiCurrency: immediateLiveCareViewModel.liveCareImmediateAppointmentFeesList.currency!.toLowerCase() == "sar"), | ||||||
|  |                   ], | ||||||
|  |                 ).paddingSymmetrical(24.h, 0.h), | ||||||
|  |                 SizedBox(height: 17.h), | ||||||
|  |                 Row( | ||||||
|  |                   mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||||
|  |                   children: [ | ||||||
|  |                     SizedBox(width: 150.h, child: Utils.getPaymentMethods()), | ||||||
|  |                     Utils.getPaymentAmountWithSymbol(immediateLiveCareViewModel.liveCareImmediateAppointmentFeesList.total!.toText24(isBold: true), AppColors.blackColor, 17, | ||||||
|  |                         isSaudiCurrency: immediateLiveCareViewModel.liveCareImmediateAppointmentFeesList.currency!.toLowerCase() == "sar"), | ||||||
|  |                   ], | ||||||
|  |                 ).paddingSymmetrical(24.h, 0.h), | ||||||
|  |                 CustomButton( | ||||||
|  |                   text: LocaleKeys.payNow.tr(context: context), | ||||||
|  |                   onPressed: () async { | ||||||
|  |                     await askVideoCallPermission().then((val) { | ||||||
|  |                       if (val) { | ||||||
|  |                         Navigator.of(context).push( | ||||||
|  |                           CustomPageRoute( | ||||||
|  |                             page: ImmediateLiveCarePaymentPage(), | ||||||
|  |                           ), | ||||||
|  |                         ); | ||||||
|  |                       } else { | ||||||
|  |                         showCommonBottomSheetWithoutHeight( | ||||||
|  |                           title: LocaleKeys.notice.tr(context: context), | ||||||
|  |                           context, | ||||||
|  |                           child: Utils.getWarningWidget( | ||||||
|  |                               loadingText: | ||||||
|  |                                   "LiveCare requires Camera, Microphone & Location permissions to enable virtual consultation between patient & doctor, Please allow these to proceed.".needTranslation, | ||||||
|  |                               isShowActionButtons: true, | ||||||
|  |                               onCancelTap: () { | ||||||
|  |                                 Navigator.pop(context); | ||||||
|  |                               }, | ||||||
|  |                               onConfirmTap: () async { | ||||||
|  |                                 openAppSettings(); | ||||||
|  |                               }), | ||||||
|  |                           callBackFunc: () {}, | ||||||
|  |                           isFullScreen: false, | ||||||
|  |                           isCloseButtonVisible: true, | ||||||
|  |                         ); | ||||||
|  |                       } | ||||||
|  |                     }); | ||||||
|  |                   }, | ||||||
|  |                   backgroundColor: AppColors.infoColor, | ||||||
|  |                   borderColor: AppColors.infoColor, | ||||||
|  |                   textColor: AppColors.whiteColor, | ||||||
|  |                   fontSize: 16, | ||||||
|  |                   fontWeight: FontWeight.w500, | ||||||
|  |                   borderRadius: 12, | ||||||
|  |                   padding: EdgeInsets.fromLTRB(10, 0, 10, 0), | ||||||
|  |                   height: 50.h, | ||||||
|  |                   icon: AppAssets.appointment_pay_icon, | ||||||
|  |                   iconColor: AppColors.whiteColor, | ||||||
|  |                   iconSize: 18.h, | ||||||
|  |                 ).paddingSymmetrical(24.h, 24.h), | ||||||
|  |               ], | ||||||
|  |             ), | ||||||
|  |           ), | ||||||
|  |         ], | ||||||
|  |       ), | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   Future<bool> askVideoCallPermission() async { | ||||||
|  |     Map<Permission, PermissionStatus> statuses = await [ | ||||||
|  |       Permission.camera, | ||||||
|  |       Permission.microphone, | ||||||
|  |     ].request(); | ||||||
|  | 
 | ||||||
|  |     if (statuses[Permission.camera] == PermissionStatus.granted && statuses[Permission.microphone] == PermissionStatus.granted) { | ||||||
|  |       // Camera permission granted | ||||||
|  |       return true; | ||||||
|  |     } else { | ||||||
|  |       return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // if (!(await Permission.camera.request().isGranted) || !(await Permission.microphone.request().isGranted)) { | ||||||
|  |     //   return false; | ||||||
|  |     // } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   String getLiveCareType(int callType) { | ||||||
|  |     switch (callType) { | ||||||
|  |       case 1: | ||||||
|  |         return "Video Call".needTranslation; | ||||||
|  |       case 2: | ||||||
|  |         return "Audio Call".needTranslation; | ||||||
|  |       case 3: | ||||||
|  |         return "Phone Call".needTranslation; | ||||||
|  |       default: | ||||||
|  |         return "Video Call".needTranslation; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
| @ -0,0 +1,519 @@ | |||||||
|  | import 'dart:async'; | ||||||
|  | import 'dart:developer'; | ||||||
|  | import 'dart:io'; | ||||||
|  | 
 | ||||||
|  | import 'package:easy_localization/easy_localization.dart'; | ||||||
|  | import 'package:flutter/material.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/core/api_consts.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/core/app_assets.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/core/app_state.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/core/cache_consts.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/core/dependencies.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/core/enums.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/core/utils/date_util.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/payfort/models/apple_pay_request_insert_model.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/core/utils/utils.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/patient_appointment_history_response_model.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/features/payfort/payfort_view_model.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/presentation/appointments/my_appointments_page.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/presentation/book_appointment/livecare/immediate_livecare_pending_request_page.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/presentation/home/navigation_screen.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/presentation/insurance/insurance_home_page.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/services/cache_service.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/theme/colors.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/widgets/in_app_browser/InAppBrowser.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:permission_handler/permission_handler.dart'; | ||||||
|  | import 'package:provider/provider.dart'; | ||||||
|  | import 'package:smooth_corner/smooth_corner.dart'; | ||||||
|  | 
 | ||||||
|  | class ImmediateLiveCarePaymentPage extends StatefulWidget { | ||||||
|  |   ImmediateLiveCarePaymentPage({super.key}); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   State<ImmediateLiveCarePaymentPage> createState() => _ImmediateLiveCarePaymentPageState(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | class _ImmediateLiveCarePaymentPageState extends State<ImmediateLiveCarePaymentPage> { | ||||||
|  |   late PayfortViewModel payfortViewModel; | ||||||
|  |   late ImmediateLiveCareViewModel immediateLiveCareViewModel; | ||||||
|  |   late MyAppointmentsViewModel myAppointmentsViewModel; | ||||||
|  |   late AppState appState; | ||||||
|  | 
 | ||||||
|  |   MyInAppBrowser? browser; | ||||||
|  |   String selectedPaymentMethod = ""; | ||||||
|  | 
 | ||||||
|  |   String transID = ""; | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   void initState() { | ||||||
|  |     scheduleMicrotask(() { | ||||||
|  |       payfortViewModel.initPayfortViewModel(); | ||||||
|  |     }); | ||||||
|  |     super.initState(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   Widget build(BuildContext context) { | ||||||
|  |     appState = getIt.get<AppState>(); | ||||||
|  |     myAppointmentsViewModel = Provider.of<MyAppointmentsViewModel>(context); | ||||||
|  |     immediateLiveCareViewModel = Provider.of<ImmediateLiveCareViewModel>(context, listen: false); | ||||||
|  |     payfortViewModel = Provider.of<PayfortViewModel>(context); | ||||||
|  |     return Scaffold( | ||||||
|  |       backgroundColor: AppColors.bgScaffoldColor, | ||||||
|  |       body: Consumer<MyAppointmentsViewModel>(builder: (context, myAppointmentsVM, child) { | ||||||
|  |         return Column( | ||||||
|  |           children: [ | ||||||
|  |             Expanded( | ||||||
|  |               child: CollapsingListView( | ||||||
|  |                 title: "LiveCare Payment".needTranslation, | ||||||
|  |                 child: SingleChildScrollView( | ||||||
|  |                   child: Column( | ||||||
|  |                     crossAxisAlignment: CrossAxisAlignment.start, | ||||||
|  |                     children: [ | ||||||
|  |                       SizedBox(height: 24.h), | ||||||
|  |                       Container( | ||||||
|  |                         decoration: RoundedRectangleBorder().toSmoothCornerDecoration( | ||||||
|  |                           color: AppColors.whiteColor, | ||||||
|  |                           borderRadius: 20.h, | ||||||
|  |                           hasShadow: false, | ||||||
|  |                         ), | ||||||
|  |                         child: Row( | ||||||
|  |                           mainAxisSize: MainAxisSize.max, | ||||||
|  |                           children: [ | ||||||
|  |                             Column( | ||||||
|  |                               crossAxisAlignment: CrossAxisAlignment.start, | ||||||
|  |                               children: [ | ||||||
|  |                                 Image.asset(AppAssets.mada, width: 72.h, height: 25.h), | ||||||
|  |                                 SizedBox(height: 16.h), | ||||||
|  |                                 "Mada".needTranslation.toText16(isBold: true), | ||||||
|  |                               ], | ||||||
|  |                             ), | ||||||
|  |                             SizedBox(width: 8.h), | ||||||
|  |                             const Spacer(), | ||||||
|  |                             Transform.flip( | ||||||
|  |                               flipX: appState.isArabic() ? true : false, | ||||||
|  |                               child: Utils.buildSvgWithAssets( | ||||||
|  |                                 icon: AppAssets.forward_arrow_icon, | ||||||
|  |                                 iconColor: AppColors.blackColor, | ||||||
|  |                                 width: 40.h, | ||||||
|  |                                 height: 40.h, | ||||||
|  |                                 fit: BoxFit.contain, | ||||||
|  |                               ), | ||||||
|  |                             ), | ||||||
|  |                           ], | ||||||
|  |                         ).paddingSymmetrical(16.h, 16.h), | ||||||
|  |                       ).paddingSymmetrical(24.h, 0.h).onPress(() { | ||||||
|  |                         selectedPaymentMethod = "MADA"; | ||||||
|  |                         openPaymentURL("mada"); | ||||||
|  |                       }), | ||||||
|  |                       SizedBox(height: 16.h), | ||||||
|  |                       Container( | ||||||
|  |                         decoration: RoundedRectangleBorder().toSmoothCornerDecoration( | ||||||
|  |                           color: AppColors.whiteColor, | ||||||
|  |                           borderRadius: 20.h, | ||||||
|  |                           hasShadow: false, | ||||||
|  |                         ), | ||||||
|  |                         child: Row( | ||||||
|  |                           mainAxisSize: MainAxisSize.max, | ||||||
|  |                           children: [ | ||||||
|  |                             Column( | ||||||
|  |                               crossAxisAlignment: CrossAxisAlignment.start, | ||||||
|  |                               children: [ | ||||||
|  |                                 Row( | ||||||
|  |                                   children: [ | ||||||
|  |                                     Image.asset(AppAssets.visa, width: 50.h, height: 50.h), | ||||||
|  |                                     SizedBox(width: 8.h), | ||||||
|  |                                     Image.asset(AppAssets.Mastercard, width: 40.h, height: 40.h), | ||||||
|  |                                   ], | ||||||
|  |                                 ), | ||||||
|  |                                 SizedBox(height: 16.h), | ||||||
|  |                                 "Visa or Mastercard".needTranslation.toText16(isBold: true), | ||||||
|  |                               ], | ||||||
|  |                             ), | ||||||
|  |                             SizedBox(width: 8.h), | ||||||
|  |                             const Spacer(), | ||||||
|  |                             Transform.flip( | ||||||
|  |                               flipX: appState.isArabic() ? true : false, | ||||||
|  |                               child: Utils.buildSvgWithAssets( | ||||||
|  |                                 icon: AppAssets.forward_arrow_icon, | ||||||
|  |                                 iconColor: AppColors.blackColor, | ||||||
|  |                                 width: 40.h, | ||||||
|  |                                 height: 40.h, | ||||||
|  |                                 fit: BoxFit.contain, | ||||||
|  |                               ), | ||||||
|  |                             ), | ||||||
|  |                           ], | ||||||
|  |                         ).paddingSymmetrical(16.h, 16.h), | ||||||
|  |                       ).paddingSymmetrical(24.h, 0.h).onPress(() { | ||||||
|  |                         selectedPaymentMethod = "VISA"; | ||||||
|  |                         openPaymentURL("visa"); | ||||||
|  |                       }), | ||||||
|  |                       SizedBox(height: 16.h), | ||||||
|  |                       Container( | ||||||
|  |                         decoration: RoundedRectangleBorder().toSmoothCornerDecoration( | ||||||
|  |                           color: AppColors.whiteColor, | ||||||
|  |                           borderRadius: 20.h, | ||||||
|  |                           hasShadow: false, | ||||||
|  |                         ), | ||||||
|  |                         child: Row( | ||||||
|  |                           mainAxisSize: MainAxisSize.max, | ||||||
|  |                           children: [ | ||||||
|  |                             Column( | ||||||
|  |                               crossAxisAlignment: CrossAxisAlignment.start, | ||||||
|  |                               children: [ | ||||||
|  |                                 Image.asset(AppAssets.tamara_en, width: 72.h, height: 25.h), | ||||||
|  |                                 SizedBox(height: 16.h), | ||||||
|  |                                 "Tamara".needTranslation.toText16(isBold: true), | ||||||
|  |                               ], | ||||||
|  |                             ), | ||||||
|  |                             SizedBox(width: 8.h), | ||||||
|  |                             const Spacer(), | ||||||
|  |                             Transform.flip( | ||||||
|  |                               flipX: appState.isArabic() ? true : false, | ||||||
|  |                               child: Utils.buildSvgWithAssets( | ||||||
|  |                                 icon: AppAssets.forward_arrow_icon, | ||||||
|  |                                 iconColor: AppColors.blackColor, | ||||||
|  |                                 width: 40.h, | ||||||
|  |                                 height: 40.h, | ||||||
|  |                                 fit: BoxFit.contain, | ||||||
|  |                               ), | ||||||
|  |                             ), | ||||||
|  |                           ], | ||||||
|  |                         ).paddingSymmetrical(16.h, 16.h), | ||||||
|  |                       ).paddingSymmetrical(24.h, 0.h).onPress(() { | ||||||
|  |                         selectedPaymentMethod = "TAMARA"; | ||||||
|  |                         openPaymentURL("tamara"); | ||||||
|  |                       }), | ||||||
|  |                     ], | ||||||
|  |                   ), | ||||||
|  |                 ), | ||||||
|  |               ), | ||||||
|  |             ), | ||||||
|  |             Container( | ||||||
|  |               decoration: RoundedRectangleBorder().toSmoothCornerDecoration( | ||||||
|  |                 color: AppColors.whiteColor, | ||||||
|  |                 borderRadius: 24.h, | ||||||
|  |                 hasShadow: false, | ||||||
|  |               ), | ||||||
|  |               child: Consumer<PayfortViewModel>(builder: (context, payfortVM, child) { | ||||||
|  |                 //TODO: Need to add loading state & animation for Apple Pay Configuration | ||||||
|  |                 return Column( | ||||||
|  |                   crossAxisAlignment: CrossAxisAlignment.start, | ||||||
|  |                   children: [ | ||||||
|  |                     (immediateLiveCareViewModel.liveCareImmediateAppointmentFeesList.isCash ?? true) | ||||||
|  |                         ? Container( | ||||||
|  |                             height: 50.h, | ||||||
|  |                             decoration: ShapeDecoration( | ||||||
|  |                               color: AppColors.secondaryLightRedBorderColor, | ||||||
|  |                               shape: SmoothRectangleBorder( | ||||||
|  |                                 borderRadius: BorderRadius.only(topLeft: Radius.circular(24), topRight: Radius.circular(24)), | ||||||
|  |                                 smoothness: 1, | ||||||
|  |                               ), | ||||||
|  |                             ), | ||||||
|  |                             child: Row( | ||||||
|  |                               mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||||
|  |                               children: [ | ||||||
|  |                                 "Insurance expired or inactive".needTranslation.toText14(color: AppColors.primaryRedColor, weight: FontWeight.w500).paddingSymmetrical(24.h, 0.h), | ||||||
|  |                                 CustomButton( | ||||||
|  |                                   text: LocaleKeys.updateInsurance.tr(context: context), | ||||||
|  |                                   onPressed: () { | ||||||
|  |                                     Navigator.of(context).push( | ||||||
|  |                                       CustomPageRoute( | ||||||
|  |                                         page: InsuranceHomePage(), | ||||||
|  |                                       ), | ||||||
|  |                                     ); | ||||||
|  |                                   }, | ||||||
|  |                                   backgroundColor: AppColors.primaryRedColor, | ||||||
|  |                                   borderColor: AppColors.secondaryLightRedBorderColor, | ||||||
|  |                                   textColor: AppColors.whiteColor, | ||||||
|  |                                   fontSize: 10, | ||||||
|  |                                   fontWeight: FontWeight.w500, | ||||||
|  |                                   borderRadius: 8, | ||||||
|  |                                   padding: EdgeInsets.fromLTRB(15, 0, 15, 0), | ||||||
|  |                                   height: 30.h, | ||||||
|  |                                 ).paddingSymmetrical(24.h, 0.h), | ||||||
|  |                               ], | ||||||
|  |                             ), | ||||||
|  |                           ) | ||||||
|  |                         : const SizedBox(), | ||||||
|  |                     SizedBox(height: 24.h), | ||||||
|  |                     "Total amount to pay".needTranslation.toText18(isBold: true).paddingSymmetrical(24.h, 0.h), | ||||||
|  |                     SizedBox(height: 17.h), | ||||||
|  |                     Row( | ||||||
|  |                       mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||||
|  |                       children: [ | ||||||
|  |                         "Amount before tax".needTranslation.toText14(isBold: true), | ||||||
|  |                         Utils.getPaymentAmountWithSymbol(immediateLiveCareViewModel.liveCareImmediateAppointmentFeesList.amount!.toString().toText16(isBold: true), AppColors.blackColor, 13, | ||||||
|  |                             isSaudiCurrency: true), | ||||||
|  |                       ], | ||||||
|  |                     ).paddingSymmetrical(24.h, 0.h), | ||||||
|  |                     Row( | ||||||
|  |                       mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||||
|  |                       children: [ | ||||||
|  |                         "VAT 15%".needTranslation.toText14(isBold: true, color: AppColors.greyTextColor), | ||||||
|  |                         Utils.getPaymentAmountWithSymbol( | ||||||
|  |                             immediateLiveCareViewModel.liveCareImmediateAppointmentFeesList.tax!.toString().toText14(isBold: true, color: AppColors.greyTextColor), AppColors.greyTextColor, 13, | ||||||
|  |                             isSaudiCurrency: true), | ||||||
|  |                       ], | ||||||
|  |                     ).paddingSymmetrical(24.h, 0.h), | ||||||
|  |                     SizedBox(height: 17.h), | ||||||
|  |                     Row( | ||||||
|  |                       mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||||
|  |                       children: [ | ||||||
|  |                         "".needTranslation.toText14(isBold: true), | ||||||
|  |                         Utils.getPaymentAmountWithSymbol(immediateLiveCareViewModel.liveCareImmediateAppointmentFeesList.total!.toString().toText24(isBold: true), AppColors.blackColor, 17, | ||||||
|  |                             isSaudiCurrency: true), | ||||||
|  |                       ], | ||||||
|  |                     ).paddingSymmetrical(24.h, 0.h), | ||||||
|  |                     Platform.isIOS | ||||||
|  |                         ? Utils.buildSvgWithAssets( | ||||||
|  |                             icon: AppAssets.apple_pay_button, | ||||||
|  |                             width: 200.h, | ||||||
|  |                             height: 80.h, | ||||||
|  |                             fit: BoxFit.contain, | ||||||
|  |                           ).paddingSymmetrical(24.h, 0.h).onPress(() { | ||||||
|  |                             // payfortVM.setIsApplePayConfigurationLoading(true); | ||||||
|  |                             if (Utils.havePrivilege(103)) { | ||||||
|  |                               startApplePay(); | ||||||
|  |                             } else { | ||||||
|  |                               openPaymentURL("ApplePay"); | ||||||
|  |                             } | ||||||
|  |                           }) | ||||||
|  |                         : SizedBox(height: 12.h), | ||||||
|  |                     SizedBox(height: 12.h), | ||||||
|  |                   ], | ||||||
|  |                 ); | ||||||
|  |               }), | ||||||
|  |             ), | ||||||
|  |           ], | ||||||
|  |         ); | ||||||
|  |       }), | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   onBrowserLoadStart(String url) { | ||||||
|  |     print("onBrowserLoadStart"); | ||||||
|  |     print(url); | ||||||
|  | 
 | ||||||
|  |     if (selectedPaymentMethod == "tamara") { | ||||||
|  |       if (Platform.isAndroid) { | ||||||
|  |         Uri uri = new Uri.dataFromString(url); | ||||||
|  |         // tamaraPaymentStatus = uri.queryParameters['status']!; | ||||||
|  |         // tamaraOrderID = uri.queryParameters['AuthorizePaymentId']!; | ||||||
|  |       } else { | ||||||
|  |         Uri uri = new Uri.dataFromString(url); | ||||||
|  |         // tamaraPaymentStatus = uri.queryParameters['paymentStatus']!; | ||||||
|  |         // tamaraOrderID = uri.queryParameters['orderId']!; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // if(selectedPaymentMethod != "TAMARA") { | ||||||
|  |     MyInAppBrowser.successURLS.forEach((element) { | ||||||
|  |       if (url.contains(element)) { | ||||||
|  |         browser?.close(); | ||||||
|  |         MyInAppBrowser.isPaymentDone = true; | ||||||
|  |         return; | ||||||
|  |       } | ||||||
|  |     }); | ||||||
|  |     // } | ||||||
|  | 
 | ||||||
|  |     // if(selectedPaymentMethod != "TAMARA") { | ||||||
|  |     MyInAppBrowser.errorURLS.forEach((element) { | ||||||
|  |       if (url.contains(element)) { | ||||||
|  |         browser?.close(); | ||||||
|  |         MyInAppBrowser.isPaymentDone = false; | ||||||
|  |         return; | ||||||
|  |       } | ||||||
|  |     }); | ||||||
|  |     // } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   onBrowserExit(bool isPaymentMade) async { | ||||||
|  |     print("onBrowserExit Called!!!!"); | ||||||
|  |     if (selectedPaymentMethod == "TAMARA") { | ||||||
|  |       // checkTamaraPaymentStatus(transID!, appo); | ||||||
|  |       // if (tamaraPaymentStatus != null && tamaraPaymentStatus.toLowerCase() == "approved") { | ||||||
|  |       //   updateTamaraRequestStatus("success", "14", Utils.getAppointmentTransID(appo.projectID, appo.clinicID, appo.appointmentNo), tamaraOrderID, num.parse(selectedInstallments), appo); | ||||||
|  |       // } else { | ||||||
|  |       //   updateTamaraRequestStatus("Failed", "00", Utils.getAppointmentTransID(appo.projectID, appo.clinicID, appo.appointmentNo), tamaraOrderID, num.parse(selectedInstallments), appo); | ||||||
|  |       // } | ||||||
|  |     } else { | ||||||
|  |       checkPaymentStatus(); | ||||||
|  |       // checkPaymentStatus(appo); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   void checkPaymentStatus() async { | ||||||
|  |     LoaderBottomSheet.showLoader(loadingText: "Checking payment status, Please wait...".needTranslation); | ||||||
|  |     await payfortViewModel.checkPaymentStatus( | ||||||
|  |         transactionID: transID, | ||||||
|  |         onSuccess: (apiResponse) async { | ||||||
|  |           debugPrint(apiResponse.data.toString()); | ||||||
|  |           if (payfortViewModel.payfortCheckPaymentStatusResponseModel!.responseMessage!.toLowerCase() == "success") { | ||||||
|  |             await immediateLiveCareViewModel.addNewCallRequestForImmediateLiveCare(transID); | ||||||
|  |             await immediateLiveCareViewModel.getPatientLiveCareHistory(); | ||||||
|  |             LoaderBottomSheet.hideLoader(); | ||||||
|  |             if (immediateLiveCareViewModel.patientHasPendingLiveCareRequest) { | ||||||
|  |               Navigator.pushAndRemoveUntil( | ||||||
|  |                   context, | ||||||
|  |                   CustomPageRoute( | ||||||
|  |                     page: LandingNavigation(), | ||||||
|  |                   ), | ||||||
|  |                   (r) => false); | ||||||
|  |               Navigator.of(context).push( | ||||||
|  |                 CustomPageRoute( | ||||||
|  |                   page: ImmediateLiveCarePendingRequestPage(), | ||||||
|  |                 ), | ||||||
|  |               ); | ||||||
|  |             } else { | ||||||
|  |               showCommonBottomSheetWithoutHeight( | ||||||
|  |                 context, | ||||||
|  |                 child: Utils.getErrorWidget(loadingText: "Unknown error occurred...".needTranslation), | ||||||
|  |                 callBackFunc: () {}, | ||||||
|  |                 isFullScreen: false, | ||||||
|  |                 isCloseButtonVisible: true, | ||||||
|  |               ); | ||||||
|  |             } | ||||||
|  |           } else { | ||||||
|  |             showCommonBottomSheetWithoutHeight( | ||||||
|  |               context, | ||||||
|  |               child: Utils.getErrorWidget(loadingText: "Payment Failed! Please try again.".needTranslation), | ||||||
|  |               callBackFunc: () {}, | ||||||
|  |               isFullScreen: false, | ||||||
|  |               isCloseButtonVisible: true, | ||||||
|  |             ); | ||||||
|  |           } | ||||||
|  |         }); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   openPaymentURL(String paymentMethod) { | ||||||
|  |     browser = MyInAppBrowser(onExitCallback: onBrowserExit, onLoadStartCallback: onBrowserLoadStart, context: context); | ||||||
|  |     transID = Utils.getAppointmentTransID( | ||||||
|  |       immediateLiveCareViewModel.immediateLiveCareSelectedClinic.serviceID!, | ||||||
|  |       ApiConsts.appEnvironmentType == AppEnvironmentTypeEnum.uat ? 15 : 12, | ||||||
|  |       DateTime.now().millisecondsSinceEpoch, | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  |     //TODO: Need to pass dynamic params to the payment request instead of static values | ||||||
|  |     browser?.openPaymentBrowser( | ||||||
|  |         num.parse(immediateLiveCareViewModel.liveCareImmediateAppointmentFeesList.total!), | ||||||
|  |         "LiveCare Payment", | ||||||
|  |         transID, | ||||||
|  |         "12", | ||||||
|  |         "CustID_${appState.getAuthenticatedUser()!.patientId.toString()}@HMG.com", | ||||||
|  |         selectedPaymentMethod, | ||||||
|  |         appState.getAuthenticatedUser()!.patientType.toString(), | ||||||
|  |         "${appState.getAuthenticatedUser()!.firstName} ${appState.getAuthenticatedUser()!.lastName}", | ||||||
|  |         appState.getAuthenticatedUser()!.patientId.toString(), | ||||||
|  |         appState.getAuthenticatedUser()!, | ||||||
|  |         browser!, | ||||||
|  |         false, | ||||||
|  |         "4", | ||||||
|  |         immediateLiveCareViewModel.immediateLiveCareSelectedClinic.serviceID.toString(), | ||||||
|  |         context, | ||||||
|  |         "3"); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   startApplePay() async { | ||||||
|  |     LoaderBottomSheet.showLoader(loadingText: "Fetching Apple Pay details, Please wait...".needTranslation); | ||||||
|  |     transID = Utils.getAppointmentTransID( | ||||||
|  |       immediateLiveCareViewModel.immediateLiveCareSelectedClinic.serviceID!, | ||||||
|  |       ApiConsts.appEnvironmentType == AppEnvironmentTypeEnum.uat ? 15 : 12, | ||||||
|  |       DateTime.now().millisecondsSinceEpoch, | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  |     ApplePayInsertRequest applePayInsertRequest = ApplePayInsertRequest(); | ||||||
|  | 
 | ||||||
|  |     await payfortViewModel.getPayfortConfigurations( | ||||||
|  |         serviceId: ServiceTypeEnum.appointmentPayment.getIdFromServiceEnum(), projectId: ApiConsts.appEnvironmentType == AppEnvironmentTypeEnum.uat ? 15 : 12, integrationId: 2); | ||||||
|  | 
 | ||||||
|  |     applePayInsertRequest.clientRequestID = transID; | ||||||
|  |     applePayInsertRequest.clinicID = immediateLiveCareViewModel.immediateLiveCareSelectedClinic.serviceID!; | ||||||
|  | 
 | ||||||
|  |     applePayInsertRequest.currency = appState.getAuthenticatedUser()!.outSa! == 0 ? "SAR" : "AED"; | ||||||
|  |     applePayInsertRequest.customerEmail = "CustID_${appState.getAuthenticatedUser()!.patientId.toString()}@HMG.com"; | ||||||
|  |     applePayInsertRequest.customerID = appState.getAuthenticatedUser()!.patientId.toString(); | ||||||
|  |     applePayInsertRequest.customerName = "${appState.getAuthenticatedUser()!.firstName} ${appState.getAuthenticatedUser()!.lastName}"; | ||||||
|  | 
 | ||||||
|  |     applePayInsertRequest.deviceToken = await Utils.getStringFromPrefs(CacheConst.pushToken); | ||||||
|  |     applePayInsertRequest.voipToken = await Utils.getStringFromPrefs(CacheConst.voipToken); | ||||||
|  |     applePayInsertRequest.doctorID = 0; | ||||||
|  |     applePayInsertRequest.projectID = "12"; | ||||||
|  |     applePayInsertRequest.serviceID = ServiceTypeEnum.appointmentPayment.getIdFromServiceEnum().toString(); | ||||||
|  |     applePayInsertRequest.channelID = 3; | ||||||
|  |     applePayInsertRequest.patientID = appState.getAuthenticatedUser()!.patientId.toString(); | ||||||
|  |     applePayInsertRequest.patientTypeID = appState.getAuthenticatedUser()!.patientType; | ||||||
|  |     applePayInsertRequest.patientOutSA = appState.getAuthenticatedUser()!.outSa; | ||||||
|  |     applePayInsertRequest.appointmentDate = DateUtil.convertDateToString(DateTime.now()); | ||||||
|  |     applePayInsertRequest.appointmentNo = 0; | ||||||
|  |     applePayInsertRequest.orderDescription = "LiveCare Payment"; | ||||||
|  |     applePayInsertRequest.liveServiceID = immediateLiveCareViewModel.immediateLiveCareSelectedClinic.serviceID!.toString(); | ||||||
|  |     applePayInsertRequest.latitude = "0.0"; | ||||||
|  |     applePayInsertRequest.longitude = "0.0"; | ||||||
|  |     applePayInsertRequest.amount = immediateLiveCareViewModel.liveCareImmediateAppointmentFeesList.total.toString(); | ||||||
|  |     applePayInsertRequest.isSchedule = "0"; | ||||||
|  |     applePayInsertRequest.language = appState.isArabic() ? 'ar' : 'en'; | ||||||
|  |     applePayInsertRequest.languageID = appState.isArabic() ? 1 : 2; | ||||||
|  |     applePayInsertRequest.userName = appState.getAuthenticatedUser()!.patientId; | ||||||
|  |     applePayInsertRequest.responseContinueURL = "http://hmg.com/Documents/success.html"; | ||||||
|  |     applePayInsertRequest.backClickUrl = "http://hmg.com/Documents/success.html"; | ||||||
|  |     applePayInsertRequest.paymentOption = "ApplePay"; | ||||||
|  | 
 | ||||||
|  |     applePayInsertRequest.isMobSDK = true; | ||||||
|  |     applePayInsertRequest.merchantReference = transID; | ||||||
|  |     applePayInsertRequest.merchantIdentifier = payfortViewModel.payfortProjectDetailsRespModel!.merchantIdentifier; | ||||||
|  |     applePayInsertRequest.commandType = "PURCHASE"; | ||||||
|  |     applePayInsertRequest.signature = payfortViewModel.payfortProjectDetailsRespModel!.signature; | ||||||
|  |     applePayInsertRequest.accessCode = payfortViewModel.payfortProjectDetailsRespModel!.accessCode; | ||||||
|  |     applePayInsertRequest.shaRequestPhrase = payfortViewModel.payfortProjectDetailsRespModel!.shaRequest; | ||||||
|  |     applePayInsertRequest.shaResponsePhrase = payfortViewModel.payfortProjectDetailsRespModel!.shaResponse; | ||||||
|  |     applePayInsertRequest.returnURL = ""; | ||||||
|  | 
 | ||||||
|  |     //TODO: Need to pass dynamic params to the Apple Pay instead of static values | ||||||
|  |     await payfortViewModel.applePayRequestInsert(applePayInsertRequest: applePayInsertRequest).then((value) { | ||||||
|  |       payfortViewModel.paymentWithApplePay( | ||||||
|  |         customerName: "${appState.getAuthenticatedUser()!.firstName} ${appState.getAuthenticatedUser()!.lastName}", | ||||||
|  |         // customerEmail: projectViewModel.authenticatedUserObject.user.emailAddress, | ||||||
|  |         customerEmail: "CustID_${appState.getAuthenticatedUser()!.patientId.toString()}@HMG.com", | ||||||
|  |         orderDescription: "LiveCare Payment", | ||||||
|  |         orderAmount: double.parse(immediateLiveCareViewModel.liveCareImmediateAppointmentFeesList.total!), | ||||||
|  |         merchantReference: transID, | ||||||
|  |         merchantIdentifier: payfortViewModel.payfortProjectDetailsRespModel!.merchantIdentifier, | ||||||
|  |         applePayAccessCode: payfortViewModel.payfortProjectDetailsRespModel!.accessCode, | ||||||
|  |         applePayShaRequestPhrase: payfortViewModel.payfortProjectDetailsRespModel!.shaRequest, | ||||||
|  |         currency: appState.getAuthenticatedUser()!.outSa! == 0 ? "SAR" : "AED", | ||||||
|  |         onFailed: (failureResult) async { | ||||||
|  |           log("failureResult: ${failureResult.message.toString()}"); | ||||||
|  |           LoaderBottomSheet.hideLoader(); | ||||||
|  |           showCommonBottomSheetWithoutHeight( | ||||||
|  |             context, | ||||||
|  |             child: Utils.getErrorWidget(loadingText: failureResult.message.toString()), | ||||||
|  |             callBackFunc: () {}, | ||||||
|  |             isFullScreen: false, | ||||||
|  |             isCloseButtonVisible: true, | ||||||
|  |           ); | ||||||
|  |         }, | ||||||
|  |         onSucceeded: (successResult) async { | ||||||
|  |           LoaderBottomSheet.hideLoader(); | ||||||
|  |           log("successResult: ${successResult.responseMessage.toString()}"); | ||||||
|  |           selectedPaymentMethod = successResult.paymentOption ?? "VISA"; | ||||||
|  |           checkPaymentStatus(); | ||||||
|  |         }, | ||||||
|  |         // projectId: appo.projectID, | ||||||
|  |         // serviceTypeEnum: ServiceTypeEnum.appointmentPayment, | ||||||
|  |       ); | ||||||
|  |     }); | ||||||
|  |   } | ||||||
|  | } | ||||||
| @ -0,0 +1,305 @@ | |||||||
|  | import 'dart:async'; | ||||||
|  | 
 | ||||||
|  | import 'package:easy_localization/easy_localization.dart'; | ||||||
|  | import 'package:flutter/material.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/core/app_assets.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/core/app_state.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/core/dependencies.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/core/utils/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/immediate_livecare/immediate_livecare_view_model.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/theme/colors.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart'; | ||||||
|  | import 'package:lottie/lottie.dart'; | ||||||
|  | import 'package:provider/provider.dart'; | ||||||
|  | import 'package:url_launcher/url_launcher.dart'; | ||||||
|  | 
 | ||||||
|  | class ImmediateLiveCarePendingRequestPage extends StatefulWidget { | ||||||
|  |   ImmediateLiveCarePendingRequestPage({super.key}); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   State<ImmediateLiveCarePendingRequestPage> createState() => _ImmediateLiveCarePendingRequestPageState(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | class _ImmediateLiveCarePendingRequestPageState extends State<ImmediateLiveCarePendingRequestPage> { | ||||||
|  |   late ImmediateLiveCareViewModel immediateLiveCareViewModel; | ||||||
|  | 
 | ||||||
|  |   late AppState appState; | ||||||
|  | 
 | ||||||
|  |   static Duration countdownDuration = Duration(minutes: 1, seconds: 0); | ||||||
|  |   ValueNotifier<Duration> durationNotifier = ValueNotifier<Duration>(countdownDuration); | ||||||
|  |   Timer? timer; | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   void initState() { | ||||||
|  |     super.initState(); | ||||||
|  |     scheduleMicrotask(() { | ||||||
|  |       countdownDuration = Duration(minutes: immediateLiveCareViewModel.patientLiveCareHistoryList[0].watingtimeInteger!, seconds: 0); | ||||||
|  |       durationNotifier = ValueNotifier<Duration>(countdownDuration); | ||||||
|  |       startTimer(); | ||||||
|  |     }); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   void dispose() { | ||||||
|  |     timer?.cancel(); | ||||||
|  |     durationNotifier.dispose(); | ||||||
|  |     super.dispose(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   Widget build(BuildContext context) { | ||||||
|  |     immediateLiveCareViewModel = Provider.of<ImmediateLiveCareViewModel>(context, listen: false); | ||||||
|  |     appState = getIt.get<AppState>(); | ||||||
|  |     return Scaffold( | ||||||
|  |       backgroundColor: AppColors.bgScaffoldColor, | ||||||
|  |       body: Consumer<ImmediateLiveCareViewModel>(builder: (context, immediateLiveCareVM, child) { | ||||||
|  |         return Column( | ||||||
|  |           children: [ | ||||||
|  |             Expanded( | ||||||
|  |               child: CollapsingListView( | ||||||
|  |                 title: "LiveCare Pending Request".needTranslation, | ||||||
|  |                 child: Padding( | ||||||
|  |                   padding: EdgeInsets.all(24.0), | ||||||
|  |                   child: Column( | ||||||
|  |                     crossAxisAlignment: CrossAxisAlignment.start, | ||||||
|  |                     children: [ | ||||||
|  |                       Container( | ||||||
|  |                         decoration: RoundedRectangleBorder().toSmoothCornerDecoration( | ||||||
|  |                           color: AppColors.whiteColor, | ||||||
|  |                           borderRadius: 20.h, | ||||||
|  |                           hasShadow: false, | ||||||
|  |                           side: BorderSide(color: AppColors.ratingColorYellow, width: 3.h), | ||||||
|  |                         ), | ||||||
|  |                         child: Padding( | ||||||
|  |                           padding: EdgeInsets.all(16.h), | ||||||
|  |                           child: Column( | ||||||
|  |                             crossAxisAlignment: CrossAxisAlignment.start, | ||||||
|  |                             children: [ | ||||||
|  |                               "Expected waiting time: ".toText16(isBold: true), | ||||||
|  |                               SizedBox(height: 8.h), | ||||||
|  |                               ValueListenableBuilder<Duration>( | ||||||
|  |                                 valueListenable: durationNotifier, | ||||||
|  |                                 builder: (context, duration, child) { | ||||||
|  |                                   return Column( | ||||||
|  |                                     mainAxisAlignment: MainAxisAlignment.center, | ||||||
|  |                                     children: [ | ||||||
|  |                                       buildTime(duration), | ||||||
|  |                                     ], | ||||||
|  |                                   ); | ||||||
|  |                                 }, | ||||||
|  |                               ), | ||||||
|  |                               SizedBox(height: 8.h), | ||||||
|  |                             ], | ||||||
|  |                           ), | ||||||
|  |                         ), | ||||||
|  |                       ), | ||||||
|  |                       SizedBox(height: 16.h), | ||||||
|  |                       Container( | ||||||
|  |                         decoration: RoundedRectangleBorder().toSmoothCornerDecoration( | ||||||
|  |                           color: AppColors.whiteColor, | ||||||
|  |                           borderRadius: 20.h, | ||||||
|  |                           hasShadow: false, | ||||||
|  |                           side: BorderSide(color: AppColors.ratingColorYellow, width: 3.h), | ||||||
|  |                         ), | ||||||
|  |                         child: Padding( | ||||||
|  |                           padding: EdgeInsets.all(16.h), | ||||||
|  |                           child: Column( | ||||||
|  |                             crossAxisAlignment: CrossAxisAlignment.start, | ||||||
|  |                             children: [ | ||||||
|  |                               Row( | ||||||
|  |                                 mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||||
|  |                                 children: [ | ||||||
|  |                                   AppCustomChipWidget( | ||||||
|  |                                     labelText: immediateLiveCareViewModel.patientLiveCareHistoryList[0].stringCallStatus, | ||||||
|  |                                     backgroundColor: AppColors.warningColorYellow.withValues(alpha: 0.20), | ||||||
|  |                                     textColor: AppColors.alertColor, | ||||||
|  |                                   ), | ||||||
|  |                                   Utils.buildSvgWithAssets(icon: AppAssets.waiting_icon, width: 24.h, height: 24.h), | ||||||
|  |                                   // Lottie.asset(AppAnimations.pending_loading_animation, repeat: true, reverse: false, frameRate: FrameRate(60), width: 80.h, height: 80.h, fit: BoxFit.cover), | ||||||
|  |                                 ], | ||||||
|  |                               ), | ||||||
|  |                               SizedBox(height: 8.h), | ||||||
|  |                               "Hala ${appState.getAuthenticatedUser()!.firstName}!!!".needTranslation.toText16(isBold: true), | ||||||
|  |                               SizedBox(height: 8.h), | ||||||
|  |                               AppCustomChipWidget( | ||||||
|  |                                   icon: AppAssets.appointment_calendar_icon, | ||||||
|  |                                   labelText: DateUtil.formatDateToDate(DateUtil.convertStringToDate(immediateLiveCareViewModel.patientLiveCareHistoryList[0].arrivalTime), false)), | ||||||
|  |                               SizedBox(height: 8.h), | ||||||
|  |                               "Your turn is after ${immediateLiveCareViewModel.patientLiveCareHistoryList[0].patCount} patients.".toText16(isBold: true), | ||||||
|  |                               SizedBox(height: 8.h), | ||||||
|  |                             ], | ||||||
|  |                           ), | ||||||
|  |                         ), | ||||||
|  |                       ) | ||||||
|  |                     ], | ||||||
|  |                   ), | ||||||
|  |                 ), | ||||||
|  |               ), | ||||||
|  |             ), | ||||||
|  |             Container( | ||||||
|  |               decoration: RoundedRectangleBorder().toSmoothCornerDecoration( | ||||||
|  |                 color: AppColors.whiteColor, | ||||||
|  |                 borderRadius: 24.h, | ||||||
|  |                 hasShadow: true, | ||||||
|  |               ), | ||||||
|  |               child: CustomButton( | ||||||
|  |                 text: "Call LiveCare Support".needTranslation, | ||||||
|  |                 onPressed: () async { | ||||||
|  |                   launchUrl(Uri.parse("tel://" + "011 525 9553")); | ||||||
|  |                 }, | ||||||
|  |                 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.call_fill, | ||||||
|  |                 iconColor: AppColors.whiteColor, | ||||||
|  |                 iconSize: 21.h, | ||||||
|  |               ).paddingSymmetrical(24.h, 24.h), | ||||||
|  |             ), | ||||||
|  |           ], | ||||||
|  |         ); | ||||||
|  |       }), | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   void startTimer() { | ||||||
|  |     timer = Timer.periodic(const Duration(seconds: 1), (_) => addTime()); | ||||||
|  |     setState(() {}); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   void addTime() { | ||||||
|  |     final seconds = durationNotifier.value.inSeconds - 1; | ||||||
|  |     if (seconds < 0) { | ||||||
|  |       timer?.cancel(); | ||||||
|  |       // Handle end of timer here | ||||||
|  |       // showEndMessage(); | ||||||
|  |     } else { | ||||||
|  |       durationNotifier.value = Duration(seconds: seconds); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   Future<bool> _onWillPop() async { | ||||||
|  |     timer?.cancel(); | ||||||
|  |     Navigator.of(context).pop(); | ||||||
|  |     return true; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   Widget buildTime(Duration duration) { | ||||||
|  |     String twoDigits(int n) => n.toString().padLeft(2, '0'); | ||||||
|  |     final hours = twoDigits(duration.inHours); | ||||||
|  |     final minutes = twoDigits(duration.inMinutes.remainder(60)); | ||||||
|  |     final seconds = twoDigits(duration.inSeconds.remainder(60)); | ||||||
|  | 
 | ||||||
|  |     return Row( | ||||||
|  |       mainAxisAlignment: MainAxisAlignment.center, | ||||||
|  |       children: [ | ||||||
|  |         buildTimeColumn(hours, "Hours".needTranslation), | ||||||
|  |         buildTimeColumn(minutes, "Mins".needTranslation), | ||||||
|  |         buildTimeColumn(seconds, "Secs".needTranslation, isLast: true), | ||||||
|  |       ], | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   Widget buildTimeColumn(String time, String label, {bool isLast = false}) { | ||||||
|  |     return Column( | ||||||
|  |       mainAxisAlignment: MainAxisAlignment.center, | ||||||
|  |       children: [ | ||||||
|  |         Row( | ||||||
|  |           children: [ | ||||||
|  |             buildDigit(time[0]), | ||||||
|  |             buildDigit(time[1]), | ||||||
|  |             if (!isLast) buildTimeSeparator(), | ||||||
|  |           ], | ||||||
|  |         ), | ||||||
|  |         buildLabel(label), | ||||||
|  |       ], | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   Widget buildDigit(String digit) { | ||||||
|  |     return Container( | ||||||
|  |       padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4), | ||||||
|  |       // margin: const EdgeInsets.symmetric(horizontal: 2), | ||||||
|  |       decoration: BoxDecoration( | ||||||
|  |         color: Colors.white, | ||||||
|  |         borderRadius: BorderRadius.circular(8), | ||||||
|  |       ), | ||||||
|  |       child: ClipRect( | ||||||
|  |         child: AnimatedSwitcher( | ||||||
|  |           duration: const Duration(milliseconds: 600), | ||||||
|  |           switchInCurve: Curves.easeOutExpo, | ||||||
|  |           switchOutCurve: Curves.easeInExpo, | ||||||
|  |           transitionBuilder: (Widget child, Animation<double> animation) { | ||||||
|  |             return Stack( | ||||||
|  |               children: <Widget>[ | ||||||
|  |                 SlideTransition( | ||||||
|  |                   position: Tween<Offset>( | ||||||
|  |                     begin: const Offset(0, -1), | ||||||
|  |                     end: const Offset(0, 1), | ||||||
|  |                   ).animate(CurvedAnimation( | ||||||
|  |                     parent: animation, | ||||||
|  |                     curve: Curves.easeOutCubic, | ||||||
|  |                   )), | ||||||
|  |                   child: FadeTransition( | ||||||
|  |                     opacity: animation, | ||||||
|  |                     child: child, | ||||||
|  |                   ), | ||||||
|  |                 ), | ||||||
|  |                 SlideTransition( | ||||||
|  |                   position: Tween<Offset>( | ||||||
|  |                     begin: const Offset(0, -1), | ||||||
|  |                     end: const Offset(0, 0), | ||||||
|  |                   ).animate(CurvedAnimation( | ||||||
|  |                     parent: animation, | ||||||
|  |                     curve: Curves.bounceIn, | ||||||
|  |                   )), | ||||||
|  |                   child: FadeTransition( | ||||||
|  |                     opacity: animation, | ||||||
|  |                     child: child, | ||||||
|  |                   ), | ||||||
|  |                 ), | ||||||
|  |               ], | ||||||
|  |             ); | ||||||
|  |           }, | ||||||
|  |           child: Text( | ||||||
|  |             digit, | ||||||
|  |             key: ValueKey<String>(digit), | ||||||
|  |             style: TextStyle( | ||||||
|  |               fontWeight: FontWeight.bold, | ||||||
|  |               color: Colors.black, | ||||||
|  |               fontSize: 20.fSize, | ||||||
|  |             ), | ||||||
|  |           ), | ||||||
|  |         ), | ||||||
|  |       ), | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   Widget buildLabel(String label) { | ||||||
|  |     return label.toText14(isBold: true); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   Widget buildTimeSeparator() { | ||||||
|  |     return const Padding( | ||||||
|  |       padding: EdgeInsets.symmetric(horizontal: 2.0), | ||||||
|  |       child: Text( | ||||||
|  |         ":", | ||||||
|  |         style: TextStyle( | ||||||
|  |           color: Colors.black, | ||||||
|  |           fontSize: 20, | ||||||
|  |         ), | ||||||
|  |       ), | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | } | ||||||
| @ -0,0 +1,173 @@ | |||||||
|  | import 'dart:async'; | ||||||
|  | 
 | ||||||
|  | import 'package:flutter/material.dart'; | ||||||
|  | import 'package:flutter_staggered_animations/flutter_staggered_animations.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/core/app_state.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/core/dependencies.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/core/utils/utils.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_view_model.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/features/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/immediate_livecare/models/resp_models/get_livecare_immediate_clinics_response_model.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/features/immediate_livecare/immediate_livecare_view_model.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/presentation/book_appointment/livecare/immediate_livecare_payment_details.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/presentation/book_appointment/livecare/widgets/select_livecare_call_type.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/presentation/book_appointment/widgets/clinic_card.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/presentation/book_appointment/livecare/widgets/livecare_clinic_card.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/theme/colors.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart'; | ||||||
|  | import 'package:provider/provider.dart'; | ||||||
|  | 
 | ||||||
|  | class SelectImmediateLiveCareClinicPage extends StatefulWidget { | ||||||
|  |   const SelectImmediateLiveCareClinicPage({super.key}); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   State<SelectImmediateLiveCareClinicPage> createState() => _SelectImmediateLiveCareClinicPageState(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | class _SelectImmediateLiveCareClinicPageState extends State<SelectImmediateLiveCareClinicPage> { | ||||||
|  |   TextEditingController searchEditingController = TextEditingController(); | ||||||
|  |   FocusNode textFocusNode = FocusNode(); | ||||||
|  |   late AppState appState; | ||||||
|  |   late ImmediateLiveCareViewModel immediateLiveCareViewModel; | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   void initState() { | ||||||
|  |     scheduleMicrotask(() { | ||||||
|  |       immediateLiveCareViewModel.getLiveCareImmediateClinicsList(); | ||||||
|  |       immediateLiveCareViewModel.setLiveCareSelectedCallType(0); | ||||||
|  |     }); | ||||||
|  |     super.initState(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   void dispose() { | ||||||
|  |     textFocusNode.dispose(); | ||||||
|  |     super.dispose(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   Widget build(BuildContext context) { | ||||||
|  |     immediateLiveCareViewModel = Provider.of<ImmediateLiveCareViewModel>(context, listen: false); | ||||||
|  |     appState = getIt.get<AppState>(); | ||||||
|  |     return Scaffold( | ||||||
|  |       backgroundColor: AppColors.bgScaffoldColor, | ||||||
|  |       body: CollapsingListView( | ||||||
|  |         title: "Select LiveCare Clinic".needTranslation, | ||||||
|  |         child: SingleChildScrollView( | ||||||
|  |           child: Padding( | ||||||
|  |             padding: EdgeInsets.all(24.h), | ||||||
|  |             child: Consumer<ImmediateLiveCareViewModel>(builder: (context, immediateLiveCareVM, child) { | ||||||
|  |               return Column( | ||||||
|  |                 children: [ | ||||||
|  |                   // SizedBox(height: 16.h), | ||||||
|  |                   // TextInputWidget( | ||||||
|  |                   //   labelText: LocaleKeys.search.tr(context: context), | ||||||
|  |                   //   hintText: LocaleKeys.clinicName.tr(context: context), | ||||||
|  |                   //   controller: searchEditingController, | ||||||
|  |                   //   isEnable: true, | ||||||
|  |                   //   prefix: null, | ||||||
|  |                   //   autoFocus: false, | ||||||
|  |                   //   isBorderAllowed: false, | ||||||
|  |                   //   keyboardType: TextInputType.text, | ||||||
|  |                   //   focusNode: textFocusNode, | ||||||
|  |                   //   suffix: searchEditingController.text.isNotEmpty | ||||||
|  |                   //       ? GestureDetector( | ||||||
|  |                   //           onTap: () { | ||||||
|  |                   //             searchEditingController.clear(); | ||||||
|  |                   //             bookAppointmentsViewModel.filterClinics(""); | ||||||
|  |                   //             textFocusNode.unfocus(); | ||||||
|  |                   //           }, | ||||||
|  |                   //           child: Utils.buildSvgWithAssets(icon: AppAssets.close_bottom_sheet_icon, width: 20.h, height: 20.h, fit: BoxFit.scaleDown), | ||||||
|  |                   //         ) | ||||||
|  |                   //       : null, | ||||||
|  |                   //   onChange: (value) { | ||||||
|  |                   //     bookAppointmentsViewModel.filterClinics(value!); | ||||||
|  |                   //   }, | ||||||
|  |                   //   padding: EdgeInsets.symmetric( | ||||||
|  |                   //     vertical: ResponsiveExtension(10).h, | ||||||
|  |                   //     horizontal: ResponsiveExtension(15).h, | ||||||
|  |                   //   ), | ||||||
|  |                   // ), | ||||||
|  |                   ListView.separated( | ||||||
|  |                     padding: EdgeInsets.only(top: 16.h), | ||||||
|  |                     shrinkWrap: true, | ||||||
|  |                     physics: NeverScrollableScrollPhysics(), | ||||||
|  |                     itemCount: immediateLiveCareVM.isImmediateLiveCareClinicsLoading ? 5 : immediateLiveCareVM.immediateLiveCareClinicsList.length, | ||||||
|  |                     itemBuilder: (context, index) { | ||||||
|  |                       return immediateLiveCareVM.isImmediateLiveCareClinicsLoading | ||||||
|  |                           ? ClinicCard( | ||||||
|  |                               bookAppointmentsVM: getIt.get<BookAppointmentsViewModel>(), | ||||||
|  |                               liveCareClinicsResponseModel: GetLiveCareClinicsResponseModel(), | ||||||
|  |                               clinicsListResponseModel: GetClinicsListResponseModel(), | ||||||
|  |                               isLoading: immediateLiveCareVM.isImmediateLiveCareClinicsLoading, | ||||||
|  |                             ) | ||||||
|  |                           : 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: LiveCareClinicCard( | ||||||
|  |                                       immediateLiveCareViewModel: immediateLiveCareVM, | ||||||
|  |                                       liveCareClinicListResponseModel: immediateLiveCareVM.immediateLiveCareClinicsList[index], | ||||||
|  |                                       isLoading: immediateLiveCareVM.isImmediateLiveCareClinicsLoading, | ||||||
|  |                                     ).onPress(() { | ||||||
|  |                                       onImmediateLiveCareClinicSelected(immediateLiveCareVM.immediateLiveCareClinicsList[index]); | ||||||
|  |                                     }), | ||||||
|  |                                   ), | ||||||
|  |                                 ), | ||||||
|  |                               ), | ||||||
|  |                             ); | ||||||
|  |                     }, | ||||||
|  |                     separatorBuilder: (BuildContext cxt, int index) => SizedBox(height: 16.h), | ||||||
|  |                   ), | ||||||
|  |                 ], | ||||||
|  |               ); | ||||||
|  |             }), | ||||||
|  |           ), | ||||||
|  |         ), | ||||||
|  |       ), | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   onImmediateLiveCareClinicSelected(GetLiveCareClinicListResponseModel liveCareClinic) { | ||||||
|  |     //TODO: add implementation to show clinic schedule | ||||||
|  |     if (liveCareClinic.isOnline == 1) { | ||||||
|  |       showCommonBottomSheetWithoutHeight(context, child: SelectLiveCareCallType(immediateLiveCareViewModel: immediateLiveCareViewModel), callBackFunc: () async { | ||||||
|  |         if (immediateLiveCareViewModel.liveCareSelectedCallType != 0) { | ||||||
|  |           immediateLiveCareViewModel.setImmediateLiveCareSelectedClinic(liveCareClinic); | ||||||
|  |           LoaderBottomSheet.showLoader(loadingText: "Fetching fees, Please wait...".needTranslation); | ||||||
|  |           await immediateLiveCareViewModel.getLiveCareImmediateAppointmentFees(onSuccess: (val) { | ||||||
|  |             LoaderBottomSheet.hideLoader(); | ||||||
|  |             Navigator.of(context).push( | ||||||
|  |               CustomPageRoute( | ||||||
|  |                 page: ImmediateLiveCarePaymentDetails(), | ||||||
|  |               ), | ||||||
|  |             ); | ||||||
|  |           }, onError: (err) { | ||||||
|  |             LoaderBottomSheet.hideLoader(); | ||||||
|  |           }); | ||||||
|  |         } | ||||||
|  |       }, title: "Select LiveCare call type".needTranslation, isCloseButtonVisible: true, isFullScreen: false); | ||||||
|  |     } else { | ||||||
|  |       showCommonBottomSheetWithoutHeight(context, | ||||||
|  |           child: Utils.getErrorWidget( | ||||||
|  |               loadingText: "The selected clinic is only available between ${liveCareClinic.shiftTimings!.first.startTime} & ${liveCareClinic.shiftTimings!.first.endTime} hours.".needTranslation), | ||||||
|  |           callBackFunc: () {}, | ||||||
|  |           title: "", | ||||||
|  |           isCloseButtonVisible: true, | ||||||
|  |           isFullScreen: false); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
| @ -0,0 +1,67 @@ | |||||||
|  | import 'package:easy_localization/easy_localization.dart'; | ||||||
|  | import 'package:flutter/material.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/core/app_assets.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/core/app_state.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/core/dependencies.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/core/utils/utils.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_view_model.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/features/immediate_livecare/models/resp_models/get_livecare_immediate_clinics_response_model.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/features/immediate_livecare/immediate_livecare_view_model.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/theme/colors.dart'; | ||||||
|  | 
 | ||||||
|  | class LiveCareClinicCard extends StatelessWidget { | ||||||
|  |   LiveCareClinicCard({super.key, required this.liveCareClinicListResponseModel, required this.isLoading, required this.immediateLiveCareViewModel}); | ||||||
|  | 
 | ||||||
|  |   GetLiveCareClinicListResponseModel liveCareClinicListResponseModel; | ||||||
|  |   bool isLoading; | ||||||
|  |   ImmediateLiveCareViewModel immediateLiveCareViewModel; | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   Widget build(BuildContext context) { | ||||||
|  |     AppState appState = getIt.get<AppState>(); | ||||||
|  |     return Container( | ||||||
|  |       padding: EdgeInsets.all(16.h), | ||||||
|  |       decoration: RoundedRectangleBorder().toSmoothCornerDecoration( | ||||||
|  |         color: AppColors.whiteColor, | ||||||
|  |         borderRadius: 24.h, | ||||||
|  |         hasShadow: false, | ||||||
|  |       ), | ||||||
|  |       child: Column( | ||||||
|  |         children: [ | ||||||
|  |           Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ | ||||||
|  |             Utils.buildSvgWithAssets(icon: AppAssets.generic_clinic_icon, width: 24.h, height: 24.h, fit: BoxFit.contain).toShimmer2(isShow: isLoading), | ||||||
|  |             Column( | ||||||
|  |               children: [ | ||||||
|  |                 Utils.buildSvgWithAssets( | ||||||
|  |                         icon: AppAssets.livecare_online_icon, | ||||||
|  |                         width: 16.h, | ||||||
|  |                         height: 16.h, | ||||||
|  |                         fit: BoxFit.contain, | ||||||
|  |                         iconColor: liveCareClinicListResponseModel.isOnline == 1 ? AppColors.successColor : AppColors.primaryRedColor) | ||||||
|  |                     .toShimmer2(isShow: isLoading), | ||||||
|  |                 SizedBox(height: 4.h), | ||||||
|  |                 liveCareClinicListResponseModel.isOnline == 1 | ||||||
|  |                     ? LocaleKeys.online.tr(context: context).toText10(isBold: true, color: AppColors.successColor).toShimmer2(isShow: isLoading) | ||||||
|  |                     : "Offline".toText10(isBold: true, color: AppColors.primaryRedColor).toShimmer2(isShow: isLoading), | ||||||
|  |               ], | ||||||
|  |             ), | ||||||
|  |           ]), | ||||||
|  |           SizedBox(height: 8.h), | ||||||
|  |           Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ | ||||||
|  |             Expanded( | ||||||
|  |                 child: (isLoading ? "Cardiology" : (appState.isArabic() ? liveCareClinicListResponseModel.serviceNameN : liveCareClinicListResponseModel.serviceName))! | ||||||
|  |                     .toText16(isBold: true) | ||||||
|  |                     .toShimmer2(isShow: isLoading)), | ||||||
|  |             Transform.flip( | ||||||
|  |                 flipX: appState.isArabic() ? true : false, | ||||||
|  |                 child: Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_icon, width: 40.h, height: 40.h, fit: BoxFit.contain, iconColor: AppColors.textColor).toShimmer2(isShow: isLoading)), | ||||||
|  |           ]), | ||||||
|  |         ], | ||||||
|  |       ), | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | } | ||||||
| @ -0,0 +1,66 @@ | |||||||
|  | import 'package:flutter/cupertino.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/core/app_assets.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_view_model.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/features/immediate_livecare/immediate_livecare_view_model.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/presentation/medical_file/widgets/medical_file_card.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/theme/colors.dart'; | ||||||
|  | 
 | ||||||
|  | class SelectLiveCareCallType extends StatelessWidget { | ||||||
|  |   SelectLiveCareCallType({super.key, required this.immediateLiveCareViewModel}); | ||||||
|  | 
 | ||||||
|  |   ImmediateLiveCareViewModel immediateLiveCareViewModel; | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   Widget build(BuildContext context) { | ||||||
|  |     //TODO: Replace with actual icons | ||||||
|  |     return GridView( | ||||||
|  |       gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( | ||||||
|  |         crossAxisCount: 3, | ||||||
|  |         crossAxisSpacing: 16, | ||||||
|  |         mainAxisSpacing: 16, | ||||||
|  |         mainAxisExtent: 130, | ||||||
|  |       ), | ||||||
|  |       physics: NeverScrollableScrollPhysics(), | ||||||
|  |       padding: EdgeInsets.zero, | ||||||
|  |       shrinkWrap: true, | ||||||
|  |       children: [ | ||||||
|  |         MedicalFileCard( | ||||||
|  |           label: "Video     Call".needTranslation, | ||||||
|  |           textColor: AppColors.blackColor, | ||||||
|  |           backgroundColor: AppColors.whiteColor, | ||||||
|  |           svgIcon: AppAssets.eye_result_icon, | ||||||
|  |           isLargeText: true, | ||||||
|  |           iconSize: 36.h, | ||||||
|  |         ).onPress(() { | ||||||
|  |           Navigator.of(context).pop(); | ||||||
|  |           immediateLiveCareViewModel.setLiveCareSelectedCallType(1); | ||||||
|  |         }), | ||||||
|  |         MedicalFileCard( | ||||||
|  |           label: "Audio        Call".needTranslation, | ||||||
|  |           textColor: AppColors.blackColor, | ||||||
|  |           backgroundColor: AppColors.whiteColor, | ||||||
|  |           svgIcon: AppAssets.allergy_info_icon, | ||||||
|  |           isLargeText: true, | ||||||
|  |           iconSize: 36.h, | ||||||
|  |         ).onPress(() { | ||||||
|  |           Navigator.of(context).pop(); | ||||||
|  |           immediateLiveCareViewModel.setLiveCareSelectedCallType(2); | ||||||
|  |         }), | ||||||
|  |         MedicalFileCard( | ||||||
|  |           label: "Phone       Call".needTranslation, | ||||||
|  |           textColor: AppColors.blackColor, | ||||||
|  |           backgroundColor: AppColors.whiteColor, | ||||||
|  |           svgIcon: AppAssets.vaccine_info_icon, | ||||||
|  |           isLargeText: true, | ||||||
|  |           iconSize: 36.h, | ||||||
|  |         ).onPress(() { | ||||||
|  |           Navigator.of(context).pop(); | ||||||
|  |           immediateLiveCareViewModel.setLiveCareSelectedCallType(3); | ||||||
|  |         }), | ||||||
|  |       ], | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | } | ||||||
| @ -1,94 +0,0 @@ | |||||||
| import 'package:flutter/material.dart'; |  | ||||||
| import 'package:flutter_staggered_animations/flutter_staggered_animations.dart'; |  | ||||||
| import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; |  | ||||||
| import 'package:hmg_patient_app_new/extensions/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'; |  | ||||||
| import 'package:hmg_patient_app_new/features/medical_file/models/patient_medical_response_model.dart'; |  | ||||||
| import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart'; |  | ||||||
| import 'package:hmg_patient_app_new/presentation/medical_file/widgets/patient_medical_report_card.dart'; |  | ||||||
| import 'package:hmg_patient_app_new/theme/colors.dart'; |  | ||||||
| import 'package:hmg_patient_app_new/widgets/custom_tab_bar.dart'; |  | ||||||
| import 'package:provider/provider.dart'; |  | ||||||
| 
 |  | ||||||
| class MedicalReportsPage extends StatefulWidget { |  | ||||||
|   const MedicalReportsPage({super.key}); |  | ||||||
| 
 |  | ||||||
|   @override |  | ||||||
|   State<MedicalReportsPage> createState() => _MedicalReportsPageState(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| class _MedicalReportsPageState extends State<MedicalReportsPage> { |  | ||||||
|   late MedicalFileViewModel medicalFileViewModel; |  | ||||||
| 
 |  | ||||||
|   @override |  | ||||||
|   Widget build(BuildContext context) { |  | ||||||
|     medicalFileViewModel = Provider.of<MedicalFileViewModel>(context, listen: false); |  | ||||||
|     return Scaffold( |  | ||||||
|       backgroundColor: AppColors.bgScaffoldColor, |  | ||||||
|       body: CollapsingListView( |  | ||||||
|         title: "Medical Reports".needTranslation, |  | ||||||
|         child: SingleChildScrollView( |  | ||||||
|           child: Column( |  | ||||||
|             children: [ |  | ||||||
|               SizedBox(height: 16.h), |  | ||||||
|               CustomTabBar( |  | ||||||
|                 activeTextColor: Color(0xffED1C2B), |  | ||||||
|                 activeBackgroundColor: Color(0xffED1C2B).withValues(alpha: .1), |  | ||||||
|                 tabs: [ |  | ||||||
|                   CustomTabBarModel(null, "Requested".needTranslation), |  | ||||||
|                   CustomTabBarModel(null, "Ready".needTranslation), |  | ||||||
|                   CustomTabBarModel(null, "Cancelled".needTranslation), |  | ||||||
|                 ], |  | ||||||
|                 onTabChange: (index) { |  | ||||||
|                   medicalFileViewModel.onMedicalReportTabChange(index); |  | ||||||
|                 }, |  | ||||||
|               ).paddingSymmetrical(24.h, 0.h), |  | ||||||
|               Consumer<MedicalFileViewModel>(builder: (context, medicalFileVM, child) { |  | ||||||
|                 return ListView.separated( |  | ||||||
|                   padding: EdgeInsets.only(top: 24.h), |  | ||||||
|                   shrinkWrap: true, |  | ||||||
|                   physics: NeverScrollableScrollPhysics(), |  | ||||||
|                   itemCount: medicalFileViewModel.isPatientMedicalReportsListLoading ? 3 : medicalFileViewModel.patientMedicalReportList.length, |  | ||||||
|                   // medicalFileViewModel.patientMedicalReportList.isNotEmpty |  | ||||||
|                   //         ? medicalFileViewModel.patientMedicalReportList.length |  | ||||||
|                   //         : 1, |  | ||||||
|                   itemBuilder: (context, index) { |  | ||||||
|                     return medicalFileViewModel.isPatientMedicalReportsListLoading |  | ||||||
|                         ? PatientMedicalReportCard( |  | ||||||
|                             patientMedicalReportResponseModel: PatientMedicalReportResponseModel(), |  | ||||||
|                             medicalFileViewModel: medicalFileVM, |  | ||||||
|                             isLoading: true, |  | ||||||
|                           ).paddingSymmetrical(24.h, 0.h) |  | ||||||
|                         : AnimationConfiguration.staggeredList( |  | ||||||
|                             position: index, |  | ||||||
|                             duration: const Duration(milliseconds: 500), |  | ||||||
|                             child: SlideAnimation( |  | ||||||
|                               verticalOffset: 100.0, |  | ||||||
|                               child: FadeInAnimation( |  | ||||||
|                                 child: AnimatedContainer( |  | ||||||
|                                   duration: Duration(milliseconds: 300), |  | ||||||
|                                   curve: Curves.easeInOut, |  | ||||||
|                                   decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: true), |  | ||||||
|                                   child: PatientMedicalReportCard( |  | ||||||
|                                     patientMedicalReportResponseModel: medicalFileVM.patientMedicalReportList[index], |  | ||||||
|                                     medicalFileViewModel: medicalFileVM, |  | ||||||
| 
 |  | ||||||
|                                     isLoading: false, |  | ||||||
|                                   ), |  | ||||||
|                                 ).paddingSymmetrical(24.h, 0.h), |  | ||||||
|                               ), |  | ||||||
|                             ), |  | ||||||
|                           ); |  | ||||||
|                   }, |  | ||||||
|                   separatorBuilder: (BuildContext cxt, int index) => SizedBox(height: 16.h), |  | ||||||
|                 ); |  | ||||||
|               }), |  | ||||||
|               SizedBox(height: 24.h), |  | ||||||
|             ], |  | ||||||
|           ), |  | ||||||
|         ), |  | ||||||
|       ), |  | ||||||
|     ); |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| @ -0,0 +1,62 @@ | |||||||
|  | import 'package:flutter/material.dart'; | ||||||
|  | import 'package:flutter_staggered_animations/flutter_staggered_animations.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/extensions/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'; | ||||||
|  | import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/presentation/appointments/widgets/appointment_card.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/theme/colors.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart'; | ||||||
|  | import 'package:provider/provider.dart'; | ||||||
|  | 
 | ||||||
|  | class MedicalReportRequestPage extends StatelessWidget { | ||||||
|  |   MedicalReportRequestPage({super.key}); | ||||||
|  | 
 | ||||||
|  |   late MedicalFileViewModel medicalFileViewModel; | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   Widget build(BuildContext context) { | ||||||
|  |     medicalFileViewModel = Provider.of<MedicalFileViewModel>(context, listen: false); | ||||||
|  |     return CollapsingListView( | ||||||
|  |       title: "Medical Reports".needTranslation, | ||||||
|  |       isClose: true, | ||||||
|  |       child: Column( | ||||||
|  |         children: [ | ||||||
|  |           ListView.separated( | ||||||
|  |             padding: EdgeInsets.only(top: 24.h), | ||||||
|  |             shrinkWrap: true, | ||||||
|  |             physics: NeverScrollableScrollPhysics(), | ||||||
|  |             itemCount: medicalFileViewModel.patientMedicalReportAppointmentHistoryList.length, | ||||||
|  |             itemBuilder: (context, index) { | ||||||
|  |               return AnimationConfiguration.staggeredList( | ||||||
|  |                 position: index, | ||||||
|  |                 duration: const Duration(milliseconds: 500), | ||||||
|  |                 child: SlideAnimation( | ||||||
|  |                   verticalOffset: 100.0, | ||||||
|  |                   child: FadeInAnimation( | ||||||
|  |                     child: AnimatedContainer( | ||||||
|  |                       duration: Duration(milliseconds: 300), | ||||||
|  |                       curve: Curves.easeInOut, | ||||||
|  |                       decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: true), | ||||||
|  |                       child: AppointmentCard( | ||||||
|  |                         patientAppointmentHistoryResponseModel: medicalFileViewModel.patientMedicalReportAppointmentHistoryList[index], | ||||||
|  |                         myAppointmentsViewModel: Provider.of<MyAppointmentsViewModel>(context, listen: false), | ||||||
|  |                         medicalFileViewModel: medicalFileViewModel, | ||||||
|  |                         isLoading: false, | ||||||
|  |                         isFromHomePage: false, | ||||||
|  |                         isFromMedicalReport: true, | ||||||
|  |                       ), | ||||||
|  |                     ).paddingSymmetrical(24.h, 0.h), | ||||||
|  |                   ), | ||||||
|  |                 ), | ||||||
|  |               ); | ||||||
|  |             }, | ||||||
|  |             separatorBuilder: (BuildContext cxt, int index) => SizedBox(height: 16.h), | ||||||
|  |           ), | ||||||
|  |           SizedBox(height: 24.h), | ||||||
|  |         ], | ||||||
|  |       ), | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | } | ||||||
| @ -0,0 +1,240 @@ | |||||||
|  | import 'package:easy_localization/easy_localization.dart'; | ||||||
|  | import 'package:flutter/material.dart'; | ||||||
|  | import 'package:flutter_staggered_animations/flutter_staggered_animations.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/core/app_assets.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/core/utils/utils.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/features/medical_file/medical_file_view_model.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/features/medical_file/models/patient_medical_response_model.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/presentation/medical_report/medical_report_request_page.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/presentation/medical_report/widgets/patient_medical_report_card.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/theme/colors.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/widgets/custom_tab_bar.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart'; | ||||||
|  | import 'package:provider/provider.dart'; | ||||||
|  | 
 | ||||||
|  | class MedicalReportsPage extends StatefulWidget { | ||||||
|  |   const MedicalReportsPage({super.key}); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   State<MedicalReportsPage> createState() => _MedicalReportsPageState(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | class _MedicalReportsPageState extends State<MedicalReportsPage> { | ||||||
|  |   late MedicalFileViewModel medicalFileViewModel; | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   Widget build(BuildContext context) { | ||||||
|  |     medicalFileViewModel = Provider.of<MedicalFileViewModel>(context, listen: false); | ||||||
|  |     return Scaffold( | ||||||
|  |       backgroundColor: AppColors.bgScaffoldColor, | ||||||
|  |       body: Column( | ||||||
|  |         children: [ | ||||||
|  |           Expanded( | ||||||
|  |             child: CollapsingListView( | ||||||
|  |               title: "Medical Reports".needTranslation, | ||||||
|  |               child: SingleChildScrollView( | ||||||
|  |                 child: Consumer<MedicalFileViewModel>(builder: (context, medicalFileVM, child) { | ||||||
|  |                   return Column( | ||||||
|  |                     children: [ | ||||||
|  |                       SizedBox(height: 16.h), | ||||||
|  |                       Row( | ||||||
|  |                         children: [ | ||||||
|  |                           CustomButton( | ||||||
|  |                             text: "Requested".needTranslation, | ||||||
|  |                             onPressed: () { | ||||||
|  |                               medicalFileViewModel.onMedicalReportTabChange(0); | ||||||
|  |                             }, | ||||||
|  |                             backgroundColor: medicalFileVM.selectedMedicalReportsTabIndex == 0 ? AppColors.bgRedLightColor : AppColors.whiteColor, | ||||||
|  |                             borderColor: medicalFileVM.selectedMedicalReportsTabIndex == 0 ? AppColors.primaryRedColor : AppColors.textColor.withOpacity(0.2), | ||||||
|  |                             textColor: medicalFileVM.selectedMedicalReportsTabIndex == 0 ? AppColors.primaryRedColor : AppColors.blackColor, | ||||||
|  |                             fontSize: 12, | ||||||
|  |                             fontWeight: FontWeight.w500, | ||||||
|  |                             borderRadius: 10, | ||||||
|  |                             padding: EdgeInsets.fromLTRB(10, 0, 10, 0), | ||||||
|  |                             height: 40.h, | ||||||
|  |                           ), | ||||||
|  |                           SizedBox(width: 8.h), | ||||||
|  |                           CustomButton( | ||||||
|  |                             text: LocaleKeys.ready.tr(context: context), | ||||||
|  |                             onPressed: () { | ||||||
|  |                               medicalFileViewModel.onMedicalReportTabChange(1); | ||||||
|  |                             }, | ||||||
|  |                             backgroundColor: medicalFileVM.selectedMedicalReportsTabIndex == 1 ? AppColors.bgRedLightColor : AppColors.whiteColor, | ||||||
|  |                             borderColor: medicalFileVM.selectedMedicalReportsTabIndex == 1 ? AppColors.primaryRedColor : AppColors.textColor.withOpacity(0.2), | ||||||
|  |                             textColor: medicalFileVM.selectedMedicalReportsTabIndex == 1 ? AppColors.primaryRedColor : AppColors.blackColor, | ||||||
|  |                             fontSize: 12, | ||||||
|  |                             fontWeight: FontWeight.w500, | ||||||
|  |                             borderRadius: 10, | ||||||
|  |                             padding: EdgeInsets.fromLTRB(10, 0, 10, 0), | ||||||
|  |                             height: 40.h, | ||||||
|  |                           ), | ||||||
|  |                           SizedBox(width: 8.h), | ||||||
|  |                           CustomButton( | ||||||
|  |                             text: LocaleKeys.cancelled.tr(context: context), | ||||||
|  |                             onPressed: () { | ||||||
|  |                               medicalFileViewModel.onMedicalReportTabChange(2); | ||||||
|  |                             }, | ||||||
|  |                             backgroundColor: medicalFileVM.selectedMedicalReportsTabIndex == 2 ? AppColors.bgRedLightColor : AppColors.whiteColor, | ||||||
|  |                             borderColor: medicalFileVM.selectedMedicalReportsTabIndex == 2 ? AppColors.primaryRedColor : AppColors.textColor.withOpacity(0.2), | ||||||
|  |                             textColor: medicalFileVM.selectedMedicalReportsTabIndex == 2 ? AppColors.primaryRedColor : AppColors.blackColor, | ||||||
|  |                             fontSize: 12, | ||||||
|  |                             fontWeight: FontWeight.w500, | ||||||
|  |                             borderRadius: 10, | ||||||
|  |                             padding: EdgeInsets.fromLTRB(10, 0, 10, 0), | ||||||
|  |                             height: 40.h, | ||||||
|  |                           ), | ||||||
|  |                         ], | ||||||
|  |                       ).paddingSymmetrical(24.h, 0.h), | ||||||
|  |                       // CustomTabBar( | ||||||
|  |                       //   activeTextColor: Color(0xffED1C2B), | ||||||
|  |                       //   activeBackgroundColor: Color(0xffED1C2B).withValues(alpha: .1), | ||||||
|  |                       //   tabs: [ | ||||||
|  |                       //     CustomTabBarModel(null, "Requested".needTranslation), | ||||||
|  |                       //     CustomTabBarModel(null, "Ready".needTranslation), | ||||||
|  |                       //     CustomTabBarModel(null, "Cancelled".needTranslation), | ||||||
|  |                       //   ], | ||||||
|  |                       //   onTabChange: (index) { | ||||||
|  |                       //     medicalFileViewModel.onMedicalReportTabChange(index); | ||||||
|  |                       //   }, | ||||||
|  |                       // ).paddingSymmetrical(24.h, 0.h), | ||||||
|  |                       ListView.separated( | ||||||
|  |                         padding: EdgeInsets.only(top: 24.h), | ||||||
|  |                         shrinkWrap: true, | ||||||
|  |                         physics: NeverScrollableScrollPhysics(), | ||||||
|  |                         itemCount: medicalFileViewModel.isPatientMedicalReportsListLoading | ||||||
|  |                             ? 3 | ||||||
|  |                             : medicalFileViewModel.patientMedicalReportList.isNotEmpty | ||||||
|  |                                 ? medicalFileViewModel.patientMedicalReportList.length | ||||||
|  |                                 : 1, | ||||||
|  |                         itemBuilder: (context, index) { | ||||||
|  |                           return medicalFileViewModel.isPatientMedicalReportsListLoading | ||||||
|  |                               ? PatientMedicalReportCard( | ||||||
|  |                                   patientMedicalReportResponseModel: PatientMedicalReportResponseModel(), | ||||||
|  |                                   medicalFileViewModel: medicalFileVM, | ||||||
|  |                                   isLoading: true, | ||||||
|  |                                 ).paddingSymmetrical(24.h, 0.h) | ||||||
|  |                               : medicalFileViewModel.patientMedicalReportList.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: PatientMedicalReportCard( | ||||||
|  |                                               patientMedicalReportResponseModel: medicalFileVM.patientMedicalReportList[index], | ||||||
|  |                                               medicalFileViewModel: medicalFileVM, | ||||||
|  |                                               isLoading: false, | ||||||
|  |                                             ), | ||||||
|  |                                           ).paddingSymmetrical(24.h, 0.h), | ||||||
|  |                                         ), | ||||||
|  |                                       ), | ||||||
|  |                                     ) | ||||||
|  |                                   : Utils.getNoDataWidget(context, noDataText: "You don't have any medical reports yet.".needTranslation).paddingSymmetrical(24.h, 24.h); | ||||||
|  |                         }, | ||||||
|  |                         separatorBuilder: (BuildContext cxt, int index) => SizedBox(height: 16.h), | ||||||
|  |                       ), | ||||||
|  |                       SizedBox(height: 24.h), | ||||||
|  |                     ], | ||||||
|  |                   ); | ||||||
|  |                 }), | ||||||
|  |               ), | ||||||
|  |             ), | ||||||
|  |           ), | ||||||
|  |           Container( | ||||||
|  |             decoration: RoundedRectangleBorder().toSmoothCornerDecoration( | ||||||
|  |               color: AppColors.whiteColor, | ||||||
|  |               borderRadius: 24.h, | ||||||
|  |               hasShadow: true, | ||||||
|  |             ), | ||||||
|  |             child: CustomButton( | ||||||
|  |               text: "Request medical report".needTranslation, | ||||||
|  |               onPressed: () async { | ||||||
|  |                 LoaderBottomSheet.showLoader(); | ||||||
|  |                 await medicalFileViewModel.getPatientMedicalReportAppointmentsList(onSuccess: (val) async { | ||||||
|  |                   LoaderBottomSheet.hideLoader(); | ||||||
|  |                   bool? value = await Navigator.of(context).push( | ||||||
|  |                     CustomPageRoute( | ||||||
|  |                       page: MedicalReportRequestPage(), | ||||||
|  |                       fullScreenDialog: true, | ||||||
|  |                       direction: AxisDirection.down, | ||||||
|  |                     ), | ||||||
|  |                   ); | ||||||
|  |                   if (value != null) { | ||||||
|  |                     showConfirmRequestMedicalReportBottomSheet(); | ||||||
|  |                   } | ||||||
|  |                 }, onError: (err) { | ||||||
|  |                   LoaderBottomSheet.hideLoader(); | ||||||
|  |                   showCommonBottomSheetWithoutHeight( | ||||||
|  |                     context, | ||||||
|  |                     child: Utils.getErrorWidget(loadingText: "You do not have any appointments to request a medical report.".needTranslation), | ||||||
|  |                     callBackFunc: () {}, | ||||||
|  |                     isFullScreen: false, | ||||||
|  |                     isCloseButtonVisible: true, | ||||||
|  |                   ); | ||||||
|  |                 }); | ||||||
|  |               }, | ||||||
|  |               backgroundColor: AppColors.primaryRedColor, | ||||||
|  |               borderColor: AppColors.primaryRedColor, | ||||||
|  |               textColor: AppColors.whiteColor, | ||||||
|  |               fontSize: 16, | ||||||
|  |               fontWeight: FontWeight.w500, | ||||||
|  |               borderRadius: 12, | ||||||
|  |               padding: EdgeInsets.fromLTRB(10, 0, 10, 0), | ||||||
|  |               height: 45.h, | ||||||
|  |               icon: AppAssets.requests, | ||||||
|  |               iconColor: AppColors.whiteColor, | ||||||
|  |               iconSize: 20.h, | ||||||
|  |             ).paddingSymmetrical(24.h, 24.h), | ||||||
|  |           ), | ||||||
|  |         ], | ||||||
|  |       ), | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   showConfirmRequestMedicalReportBottomSheet() { | ||||||
|  |     showCommonBottomSheetWithoutHeight( | ||||||
|  |       title: LocaleKeys.notice.tr(context: context), | ||||||
|  |       context, | ||||||
|  |       child: Utils.getWarningWidget( | ||||||
|  |           loadingText: "Are you sure you want to request a medical report for this appointment?".needTranslation, | ||||||
|  |           isShowActionButtons: true, | ||||||
|  |           onCancelTap: () { | ||||||
|  |             Navigator.pop(context); | ||||||
|  |           }, | ||||||
|  |           onConfirmTap: () async { | ||||||
|  |             Navigator.pop(context); | ||||||
|  |             LoaderBottomSheet.showLoader(); | ||||||
|  |             await medicalFileViewModel.insertRequestForMedicalReport(onSuccess: (val) { | ||||||
|  |               LoaderBottomSheet.hideLoader(); | ||||||
|  |               showCommonBottomSheetWithoutHeight(context, child: Utils.getSuccessWidget(loadingText: "Your medical report request has been successfully submitted.".needTranslation), callBackFunc: () { | ||||||
|  |                 medicalFileViewModel.setIsPatientMedicalReportsLoading(true); | ||||||
|  |                 medicalFileViewModel.onMedicalReportTabChange(0); | ||||||
|  |                 medicalFileViewModel.getPatientMedicalReportList(); | ||||||
|  |               }); | ||||||
|  |             }, onError: (err) { | ||||||
|  |               LoaderBottomSheet.hideLoader(); | ||||||
|  |               showCommonBottomSheetWithoutHeight(context, child: Utils.getErrorWidget(loadingText: err), callBackFunc: () { | ||||||
|  |                 medicalFileViewModel.setIsPatientMedicalReportsLoading(true); | ||||||
|  |                 medicalFileViewModel.onMedicalReportTabChange(0); | ||||||
|  |                 medicalFileViewModel.getPatientMedicalReportList(); | ||||||
|  |               }); | ||||||
|  |             }); | ||||||
|  |           }), | ||||||
|  |       callBackFunc: () {}, | ||||||
|  |       isFullScreen: false, | ||||||
|  |       isCloseButtonVisible: true, | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | } | ||||||
					Loading…
					
					
				
		Reference in New Issue