diff --git a/assets/images/png/body_parts/female/abdomin.png b/assets/images/png/body_parts/female/abdomin.png new file mode 100644 index 0000000..f4918d7 Binary files /dev/null and b/assets/images/png/body_parts/female/abdomin.png differ diff --git a/assets/images/png/body_parts/female/anal.png b/assets/images/png/body_parts/female/anal.png new file mode 100644 index 0000000..d96ef28 Binary files /dev/null and b/assets/images/png/body_parts/female/anal.png differ diff --git a/assets/images/png/body_parts/female/axilla.png b/assets/images/png/body_parts/female/axilla.png new file mode 100644 index 0000000..f38be6b Binary files /dev/null and b/assets/images/png/body_parts/female/axilla.png differ diff --git a/assets/images/png/body_parts/female/back.png b/assets/images/png/body_parts/female/back.png new file mode 100644 index 0000000..65d9ef1 Binary files /dev/null and b/assets/images/png/body_parts/female/back.png differ diff --git a/assets/images/png/body_parts/female/bikini.png b/assets/images/png/body_parts/female/bikini.png new file mode 100644 index 0000000..a1a6842 Binary files /dev/null and b/assets/images/png/body_parts/female/bikini.png differ diff --git a/assets/images/png/body_parts/female/bikini_line.png b/assets/images/png/body_parts/female/bikini_line.png new file mode 100644 index 0000000..12fc0e6 Binary files /dev/null and b/assets/images/png/body_parts/female/bikini_line.png differ diff --git a/assets/images/png/body_parts/female/buttocks.png b/assets/images/png/body_parts/female/buttocks.png new file mode 100644 index 0000000..b83576f Binary files /dev/null and b/assets/images/png/body_parts/female/buttocks.png differ diff --git a/assets/images/png/body_parts/female/cheeks.svg b/assets/images/png/body_parts/female/cheeks.svg new file mode 100644 index 0000000..6305ce6 --- /dev/null +++ b/assets/images/png/body_parts/female/cheeks.svg @@ -0,0 +1,130 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/images/png/body_parts/female/chest.png b/assets/images/png/body_parts/female/chest.png new file mode 100644 index 0000000..727ebe7 Binary files /dev/null and b/assets/images/png/body_parts/female/chest.png differ diff --git a/assets/images/png/body_parts/female/chest_line.png b/assets/images/png/body_parts/female/chest_line.png new file mode 100644 index 0000000..0b4fecb Binary files /dev/null and b/assets/images/png/body_parts/female/chest_line.png differ diff --git a/assets/images/png/body_parts/female/chin.svg b/assets/images/png/body_parts/female/chin.svg new file mode 100644 index 0000000..37da45b --- /dev/null +++ b/assets/images/png/body_parts/female/chin.svg @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/images/png/body_parts/female/ears.svg b/assets/images/png/body_parts/female/ears.svg new file mode 100644 index 0000000..758a69e --- /dev/null +++ b/assets/images/png/body_parts/female/ears.svg @@ -0,0 +1,130 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/images/png/body_parts/female/eyebrows.svg b/assets/images/png/body_parts/female/eyebrows.svg new file mode 100644 index 0000000..9657daa --- /dev/null +++ b/assets/images/png/body_parts/female/eyebrows.svg @@ -0,0 +1,130 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/images/png/body_parts/female/full_body_female.png b/assets/images/png/body_parts/female/full_body_female.png new file mode 100644 index 0000000..fb6a375 Binary files /dev/null and b/assets/images/png/body_parts/female/full_body_female.png differ diff --git a/assets/images/png/body_parts/female/full_legs.png b/assets/images/png/body_parts/female/full_legs.png new file mode 100644 index 0000000..5a365a7 Binary files /dev/null and b/assets/images/png/body_parts/female/full_legs.png differ diff --git a/assets/images/png/body_parts/female/full_neck.svg b/assets/images/png/body_parts/female/full_neck.svg new file mode 100644 index 0000000..f73f07f --- /dev/null +++ b/assets/images/png/body_parts/female/full_neck.svg @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/images/png/body_parts/female/half_neck.svg b/assets/images/png/body_parts/female/half_neck.svg new file mode 100644 index 0000000..78e4cfa --- /dev/null +++ b/assets/images/png/body_parts/female/half_neck.svg @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/images/png/body_parts/female/hydra_facial.svg b/assets/images/png/body_parts/female/hydra_facial.svg new file mode 100644 index 0000000..6a46821 --- /dev/null +++ b/assets/images/png/body_parts/female/hydra_facial.svg @@ -0,0 +1,128 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/images/png/body_parts/female/lower_arm.png b/assets/images/png/body_parts/female/lower_arm.png new file mode 100644 index 0000000..717b2cd Binary files /dev/null and b/assets/images/png/body_parts/female/lower_arm.png differ diff --git a/assets/images/png/body_parts/female/lower_leg.png b/assets/images/png/body_parts/female/lower_leg.png new file mode 100644 index 0000000..88ccace Binary files /dev/null and b/assets/images/png/body_parts/female/lower_leg.png differ diff --git a/assets/images/png/body_parts/female/shoulders.png b/assets/images/png/body_parts/female/shoulders.png new file mode 100644 index 0000000..e1f253a Binary files /dev/null and b/assets/images/png/body_parts/female/shoulders.png differ diff --git a/assets/images/png/body_parts/female/side_burn.svg b/assets/images/png/body_parts/female/side_burn.svg new file mode 100644 index 0000000..123216e --- /dev/null +++ b/assets/images/png/body_parts/female/side_burn.svg @@ -0,0 +1,130 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/images/png/body_parts/female/upper_arm.png b/assets/images/png/body_parts/female/upper_arm.png new file mode 100644 index 0000000..1edb873 Binary files /dev/null and b/assets/images/png/body_parts/female/upper_arm.png differ diff --git a/assets/images/png/body_parts/female/upper_legs.png b/assets/images/png/body_parts/female/upper_legs.png new file mode 100644 index 0000000..a8aa2ec Binary files /dev/null and b/assets/images/png/body_parts/female/upper_legs.png differ diff --git a/assets/images/png/body_parts/female/upper_lips.svg b/assets/images/png/body_parts/female/upper_lips.svg new file mode 100644 index 0000000..7204337 --- /dev/null +++ b/assets/images/png/body_parts/female/upper_lips.svg @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/images/png/body_parts/male/abdomin.png b/assets/images/png/body_parts/male/abdomin.png new file mode 100644 index 0000000..1b180b4 Binary files /dev/null and b/assets/images/png/body_parts/male/abdomin.png differ diff --git a/assets/images/png/body_parts/male/axilla.png b/assets/images/png/body_parts/male/axilla.png new file mode 100644 index 0000000..61af949 Binary files /dev/null and b/assets/images/png/body_parts/male/axilla.png differ diff --git a/assets/images/png/body_parts/male/back.png b/assets/images/png/body_parts/male/back.png new file mode 100644 index 0000000..224147e Binary files /dev/null and b/assets/images/png/body_parts/male/back.png differ diff --git a/assets/images/png/body_parts/male/back.svg b/assets/images/png/body_parts/male/back.svg new file mode 100644 index 0000000..7d9aeb4 --- /dev/null +++ b/assets/images/png/body_parts/male/back.svg @@ -0,0 +1,188 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/images/png/body_parts/male/bikini.png b/assets/images/png/body_parts/male/bikini.png new file mode 100644 index 0000000..aa98ea7 Binary files /dev/null and b/assets/images/png/body_parts/male/bikini.png differ diff --git a/assets/images/png/body_parts/male/bikini_line.png b/assets/images/png/body_parts/male/bikini_line.png new file mode 100644 index 0000000..60ed64e Binary files /dev/null and b/assets/images/png/body_parts/male/bikini_line.png differ diff --git a/assets/images/png/body_parts/male/cheek.svg b/assets/images/png/body_parts/male/cheek.svg new file mode 100644 index 0000000..984a59f --- /dev/null +++ b/assets/images/png/body_parts/male/cheek.svg @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/images/png/body_parts/male/chest.png b/assets/images/png/body_parts/male/chest.png new file mode 100644 index 0000000..b7cec07 Binary files /dev/null and b/assets/images/png/body_parts/male/chest.png differ diff --git a/assets/images/png/body_parts/male/chest_line.png b/assets/images/png/body_parts/male/chest_line.png new file mode 100644 index 0000000..41e779f Binary files /dev/null and b/assets/images/png/body_parts/male/chest_line.png differ diff --git a/assets/images/png/body_parts/male/chin.svg b/assets/images/png/body_parts/male/chin.svg new file mode 100644 index 0000000..9fdee93 --- /dev/null +++ b/assets/images/png/body_parts/male/chin.svg @@ -0,0 +1,86 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/images/png/body_parts/male/ears.svg b/assets/images/png/body_parts/male/ears.svg new file mode 100644 index 0000000..1c9600c --- /dev/null +++ b/assets/images/png/body_parts/male/ears.svg @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/images/png/body_parts/male/eyebrows.svg b/assets/images/png/body_parts/male/eyebrows.svg new file mode 100644 index 0000000..ea38523 --- /dev/null +++ b/assets/images/png/body_parts/male/eyebrows.svg @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/images/png/body_parts/male/full_body_male.png b/assets/images/png/body_parts/male/full_body_male.png new file mode 100644 index 0000000..df30472 Binary files /dev/null and b/assets/images/png/body_parts/male/full_body_male.png differ diff --git a/assets/images/png/body_parts/male/full_legs.png b/assets/images/png/body_parts/male/full_legs.png new file mode 100644 index 0000000..eee2fa0 Binary files /dev/null and b/assets/images/png/body_parts/male/full_legs.png differ diff --git a/assets/images/png/body_parts/male/full_neck.png b/assets/images/png/body_parts/male/full_neck.png new file mode 100644 index 0000000..a75159f Binary files /dev/null and b/assets/images/png/body_parts/male/full_neck.png differ diff --git a/assets/images/png/body_parts/male/full_neck.svg b/assets/images/png/body_parts/male/full_neck.svg new file mode 100644 index 0000000..33b755e --- /dev/null +++ b/assets/images/png/body_parts/male/full_neck.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/images/png/body_parts/male/half_neck.png b/assets/images/png/body_parts/male/half_neck.png new file mode 100644 index 0000000..5ee0249 Binary files /dev/null and b/assets/images/png/body_parts/male/half_neck.png differ diff --git a/assets/images/png/body_parts/male/half_neck.svg b/assets/images/png/body_parts/male/half_neck.svg new file mode 100644 index 0000000..26b6bc3 --- /dev/null +++ b/assets/images/png/body_parts/male/half_neck.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/images/png/body_parts/male/hydra_facial.svg b/assets/images/png/body_parts/male/hydra_facial.svg new file mode 100644 index 0000000..1051630 --- /dev/null +++ b/assets/images/png/body_parts/male/hydra_facial.svg @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/images/png/body_parts/male/lower_arm.png b/assets/images/png/body_parts/male/lower_arm.png new file mode 100644 index 0000000..120242e Binary files /dev/null and b/assets/images/png/body_parts/male/lower_arm.png differ diff --git a/assets/images/png/body_parts/male/lower_legs.png b/assets/images/png/body_parts/male/lower_legs.png new file mode 100644 index 0000000..8367aa8 Binary files /dev/null and b/assets/images/png/body_parts/male/lower_legs.png differ diff --git a/assets/images/png/body_parts/male/shoulders.png b/assets/images/png/body_parts/male/shoulders.png new file mode 100644 index 0000000..907f2dd Binary files /dev/null and b/assets/images/png/body_parts/male/shoulders.png differ diff --git a/assets/images/png/body_parts/male/side_burn.svg b/assets/images/png/body_parts/male/side_burn.svg new file mode 100644 index 0000000..156b30f --- /dev/null +++ b/assets/images/png/body_parts/male/side_burn.svg @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/images/png/body_parts/male/upper_arm.png b/assets/images/png/body_parts/male/upper_arm.png new file mode 100644 index 0000000..f80a75d Binary files /dev/null and b/assets/images/png/body_parts/male/upper_arm.png differ diff --git a/assets/images/png/body_parts/male/upper_legs.png b/assets/images/png/body_parts/male/upper_legs.png new file mode 100644 index 0000000..e0c973b Binary files /dev/null and b/assets/images/png/body_parts/male/upper_legs.png differ diff --git a/assets/images/png/body_parts/male/upper_lips.svg b/assets/images/png/body_parts/male/upper_lips.svg new file mode 100644 index 0000000..b7ae6e0 --- /dev/null +++ b/assets/images/png/body_parts/male/upper_lips.svg @@ -0,0 +1,86 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/images/png/dummy_user.png b/assets/images/png/dummy_user.png new file mode 100644 index 0000000..b4bace4 Binary files /dev/null and b/assets/images/png/dummy_user.png differ diff --git a/assets/images/svg/active-check.svg b/assets/images/svg/active-check.svg new file mode 100644 index 0000000..c94a3a0 --- /dev/null +++ b/assets/images/svg/active-check.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/svg/alert-square.svg b/assets/images/svg/alert-square.svg new file mode 100644 index 0000000..87b52e3 --- /dev/null +++ b/assets/images/svg/alert-square.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/svg/arrow-right.svg b/assets/images/svg/arrow-right.svg new file mode 100644 index 0000000..8818d72 --- /dev/null +++ b/assets/images/svg/arrow-right.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/svg/delete_icon.svg b/assets/images/svg/delete_icon.svg new file mode 100644 index 0000000..2e37d0d --- /dev/null +++ b/assets/images/svg/delete_icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/svg/switch_user.svg b/assets/images/svg/switch_user.svg new file mode 100644 index 0000000..fa8e47e --- /dev/null +++ b/assets/images/svg/switch_user.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/assets/langs/ar-SA.json b/assets/langs/ar-SA.json index 811a1af..787a718 100644 --- a/assets/langs/ar-SA.json +++ b/assets/langs/ar-SA.json @@ -874,5 +874,7 @@ "to": "إلى", "startDate": "تاريخ البدء", "endDate": "تاريخ الانتهاء", - "walkin": "زيارة بدون موعد" + "walkin": "زيارة بدون موعد", + "laserClinic": "عيادة الليزر", + "continueString": "يكمل" } \ No newline at end of file diff --git a/assets/langs/en-US.json b/assets/langs/en-US.json index 336c06c..7b8c4b8 100644 --- a/assets/langs/en-US.json +++ b/assets/langs/en-US.json @@ -685,8 +685,8 @@ "body": "Body Mass", "bodyString": "Body", "face": "Face", - "retouch": "روتوش", - "bikini": "بيكيني", + "retouch": "Re-touch", + "bikini": "Bikini", "totalMinutes": "Total Minutes", "feedback": "Feedback", "send": "أرسل", @@ -870,6 +870,8 @@ "clearAllFilters": "Clear all filters", "filters": "Filters", "searchClinic": "Search Clinic", - "walkin": "Walk In" + "walkin": "Walk In", + "continueString": "Continue", + "laserClinic": "Laser Clinic" } \ No newline at end of file diff --git a/lib/core/api/api_client.dart b/lib/core/api/api_client.dart index cd7b673..24f682b 100644 --- a/lib/core/api/api_client.dart +++ b/lib/core/api/api_client.dart @@ -88,16 +88,16 @@ class ApiClientImp implements ApiClient { @override post( - String endPoint, { - required Map body, - required Function(dynamic response, int statusCode, {int? messageStatus, String? errorMessage}) onSuccess, - required Function(String error, int statusCode, {int? messageStatus, Failure? failureType}) onFailure, - bool isAllowAny = false, - bool isExternal = false, - bool isRCService = false, - bool isPaymentServices = false, - bool bypassConnectionCheck = false, - }) async { + String endPoint, { + required Map body, + required Function(dynamic response, int statusCode, {int? messageStatus, String? errorMessage}) onSuccess, + required Function(String error, int statusCode, {int? messageStatus, Failure? failureType}) onFailure, + bool isAllowAny = false, + bool isExternal = false, + bool isRCService = false, + bool isPaymentServices = false, + bool bypassConnectionCheck = false, + }) async { String url; if (isExternal) { url = endPoint; @@ -173,8 +173,8 @@ class ApiClientImp implements ApiClient { body[_appState.isAuthenticated ? 'TokenID' : 'LogInTokenID'] = _appState.appAuthToken; } - body['TokenID'] = "@dm!n"; - body['PatientID'] = 4767884; + // body['TokenID'] = "@dm!n"; + // body['PatientID'] = 4772172; // body['PatientTypeID'] = 1; // // body['PatientOutSA'] = 0; diff --git a/lib/core/api_consts.dart b/lib/core/api_consts.dart index 511cb37..9591cfe 100644 --- a/lib/core/api_consts.dart +++ b/lib/core/api_consts.dart @@ -727,7 +727,7 @@ const FAMILY_FILES= 'Services/Authentication.svc/REST/GetAllSharedRecordsByStatu class ApiConsts { static const maxSmallScreen = 660; - static AppEnvironmentTypeEnum appEnvironmentType = AppEnvironmentTypeEnum.uat; + static AppEnvironmentTypeEnum appEnvironmentType = AppEnvironmentTypeEnum.prod; // static String baseUrl = 'https://uat.hmgwebservices.com/'; // HIS API URL UAT @@ -745,6 +745,7 @@ class ApiConsts { static String TAMARA_URL = "https://mdlaboratories.com/tamaralive/Home/Checkout"; static String GET_TAMARA_INSTALLMENTS_URL = "https://mdlaboratories.com/tamaralive/Home/GetInstallments"; + static String GET_TAMARA_PAYMENT_STATUS = 'https://mdlaboratories.com/tamaralive/api/OnlineTamara/order_status?orderid='; // static String GET_TAMARA_INSTALLMENTS_URL = "https://epharmacy.hmg.com/tamara/Home/getinstallments"; @@ -760,6 +761,7 @@ class ApiConsts { SERVICE_URL = "https://hmgwebservices.com/PayFortWebLive/pages/SendPayFortRequest.aspx"; TAMARA_URL = "https://mdlaboratories.com/tamaralive/Home/Checkout"; GET_TAMARA_INSTALLMENTS_URL = "https://mdlaboratories.com/tamaralive/Home/GetInstallments"; + GET_TAMARA_PAYMENT_STATUS = 'https://mdlaboratories.com/tamaralive/api/OnlineTamara/order_status?orderid='; break; case AppEnvironmentTypeEnum.dev: baseUrl = "https://uat.hmgwebservices.com/"; @@ -768,6 +770,7 @@ class ApiConsts { SERVICE_URL = 'https://hmgwebservices.com/PayFortWeb/pages/SendPayFortRequest.aspx'; TAMARA_URL = "https://epharmacy.hmg.com/tamara/Home/Checkout"; GET_TAMARA_INSTALLMENTS_URL = "https://epharmacy.hmg.com/tamara/Home/getinstallments"; + GET_TAMARA_PAYMENT_STATUS = 'https://epharmacy.hmg.com/tamara/api/OnlineTamara/order_status?orderid='; break; case AppEnvironmentTypeEnum.uat: baseUrl = "https://uat.hmgwebservices.com/"; @@ -776,6 +779,7 @@ class ApiConsts { SERVICE_URL = 'https://hmgwebservices.com/PayFortWeb/pages/SendPayFortRequest.aspx'; TAMARA_URL = "https://epharmacy.hmg.com/tamara/Home/Checkout"; GET_TAMARA_INSTALLMENTS_URL = "https://epharmacy.hmg.com/tamara/Home/getinstallments"; + GET_TAMARA_PAYMENT_STATUS = 'https://epharmacy.hmg.com/tamara/api/OnlineTamara/order_status?orderid='; break; case AppEnvironmentTypeEnum.preProd: @@ -785,6 +789,7 @@ class ApiConsts { SERVICE_URL = "https://hmgwebservices.com/PayFortWebLive/pages/SendPayFortRequest.aspx"; TAMARA_URL = "https://epharmacy.hmg.com/tamara/Home/Checkout"; GET_TAMARA_INSTALLMENTS_URL = "https://epharmacy.hmg.com/tamara/Home/getinstallments"; + GET_TAMARA_PAYMENT_STATUS = 'https://epharmacy.hmg.com/tamara/api/OnlineTamara/order_status?orderid='; break; case AppEnvironmentTypeEnum.qa: baseUrl = "https://uat.hmgwebservices.com/"; @@ -793,6 +798,7 @@ class ApiConsts { SERVICE_URL = 'https://hmgwebservices.com/PayFortWeb/pages/SendPayFortRequest.aspx'; TAMARA_URL = "https://epharmacy.hmg.com/tamara/Home/Checkout"; GET_TAMARA_INSTALLMENTS_URL = "https://epharmacy.hmg.com/tamara/Home/getinstallments"; + GET_TAMARA_PAYMENT_STATUS = 'https://epharmacy.hmg.com/tamara/api/OnlineTamara/order_status?orderid='; break; case AppEnvironmentTypeEnum.staging: baseUrl = "https://uat.hmgwebservices.com/"; @@ -801,6 +807,7 @@ class ApiConsts { SERVICE_URL = 'https://hmgwebservices.com/PayFortWeb/pages/SendPayFortRequest.aspx'; TAMARA_URL = "https://epharmacy.hmg.com/tamara/Home/Checkout"; GET_TAMARA_INSTALLMENTS_URL = "https://epharmacy.hmg.com/tamara/Home/getinstallments"; + GET_TAMARA_PAYMENT_STATUS = 'https://epharmacy.hmg.com/tamara/api/OnlineTamara/order_status?orderid='; break; } } @@ -825,6 +832,13 @@ class ApiConsts { static final String registerUser = 'Services/Authentication.svc/REST/PatientRegistration'; static final String addFamilyFile = 'Services/Patients.svc/REST/ShareFamilyFileService'; + static final String sendFamilyFileActivation = 'Services/Authentication.svc/REST/SendActivationCodeForFamilyFile'; + static final String checkActivationCodeForFamily = 'Services/Authentication.svc/REST/CheckActivationCodeForFamilyFile'; + static final String getAllPendingRecordsByResponseId = 'Services/Authentication.svc/REST/GetAllPendingRecordsByResponseId'; + static final String getAllSharedRecordsByStatus = 'Services/Authentication.svc/REST/GetAllSharedRecordsByStatus'; + static final String removeFileFromFamilyMembers = 'Services/Authentication.svc/REST/ActiveDeactive_PatientFile'; + static final String acceptAndRejectFamilyFile = 'Services/Authentication.svc/REST/Update_FileStatus'; + // static values for Api static final double appVersionID = 18.7; diff --git a/lib/core/app_assets.dart b/lib/core/app_assets.dart index d1b8b93..1e9eaa4 100644 --- a/lib/core/app_assets.dart +++ b/lib/core/app_assets.dart @@ -152,6 +152,9 @@ class AppAssets { static const String ic_filters = '$svgBasePath/filters.svg'; static const String ic_close = '$svgBasePath/ic_close.svg'; static const String ic_cross_circle = '$svgBasePath/cross_circle.svg'; + static const String switch_user = '$svgBasePath/switch_user.svg'; + static const String activeCheck = '$svgBasePath/active-check.svg'; + static const String deleteIcon = '$svgBasePath/delete_icon.svg'; //bottom navigation// static const String homeBottom = '$svgBasePath/home_bottom.svg'; @@ -163,6 +166,8 @@ class AppAssets { static const String feedback = '$svgBasePath/feedback.svg'; static const String news = '$svgBasePath/news.svg'; static const String heart = '$svgBasePath/heart.svg'; + static const String alertSquare = '$svgBasePath/alert-square.svg'; + static const String arrowRight = '$svgBasePath/arrow-right.svg'; // PNGS // static const String hmg_logo = '$pngBasePath/hmg_logo.png'; @@ -177,6 +182,7 @@ class AppAssets { static const String tamara_en = '$pngBasePath/tamara_en.png'; static const String visa = '$pngBasePath/visa.png'; static const String lockIcon = '$pngBasePath/lock-icon.png'; + static const String dummy_user = '$pngBasePath/dummy_user.png'; } class AppAnimations { diff --git a/lib/core/app_state.dart b/lib/core/app_state.dart index c906374..404e22b 100644 --- a/lib/core/app_state.dart +++ b/lib/core/app_state.dart @@ -37,14 +37,16 @@ class AppState { AuthenticatedUser? _authenticatedRootUser; AuthenticatedUser? _authenticatedChildUser; - void setAuthenticatedUser(AuthenticatedUser authenticatedUser, {bool isFamily = false}) { + int? _superUserID; + bool isChildLoggedIn = false; + + void setAuthenticatedUser(AuthenticatedUser? authenticatedUser, {bool isFamily = false}) { if (isFamily) { _authenticatedChildUser = authenticatedUser; } else { setIsAuthenticated = true; _authenticatedRootUser = authenticatedUser; } - } AuthenticatedUser? getAuthenticatedUser({bool isFamily = false}) { @@ -55,6 +57,14 @@ class AppState { } } + int? get getSuperUserID => _superUserID; + + bool get getIsChildLoggedIn => isChildLoggedIn; + + set setSuperUserID(int? value) => _superUserID = value; + + set setIsChildLoggedIn(bool value) => isChildLoggedIn = value; + String _userBloodGroup = ""; String get getUserBloodGroup => _userBloodGroup; @@ -97,6 +107,14 @@ class AppState { set setDeviceTypeID(v) => deviceTypeID = v; + String _familyFileTokenID = ""; + + String get getFamilyFileTokenID => _familyFileTokenID; + + set setFamilyFileTokenID(String value) { + _familyFileTokenID = value; + } + List vidaPlusProjectList = []; List privilegeModelList = []; List hMCProjectListModel = []; @@ -135,9 +153,8 @@ class AppState { } ///this will be called if there is any problem in getting the user location - void resetLocation(){ + void resetLocation() { userLong = 0.0; userLong = 0.0; } - } diff --git a/lib/core/dependencies.dart b/lib/core/dependencies.dart index f2f6183..7337dfa 100644 --- a/lib/core/dependencies.dart +++ b/lib/core/dependencies.dart @@ -8,6 +8,7 @@ import 'package:hmg_patient_app_new/features/authentication/authentication_view_ import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_repo.dart'; import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_view_model.dart'; import 'package:hmg_patient_app_new/features/common/common_repo.dart'; +import 'package:hmg_patient_app_new/features/doctor_filter/doctor_filter_view_model.dart'; import 'package:hmg_patient_app_new/features/habib_wallet/habib_wallet_repo.dart'; import 'package:hmg_patient_app_new/features/habib_wallet/habib_wallet_view_model.dart'; import 'package:hmg_patient_app_new/features/immediate_livecare/immediate_livecare_repo.dart'; @@ -18,12 +19,14 @@ import 'package:hmg_patient_app_new/features/lab/lab_repo.dart'; import 'package:hmg_patient_app_new/features/lab/lab_view_model.dart'; import 'package:hmg_patient_app_new/features/medical_file/medical_file_repo.dart'; import 'package:hmg_patient_app_new/features/medical_file/medical_file_view_model.dart'; +import 'package:hmg_patient_app_new/features/my_appointments/appointment_via_region_viewmodel.dart'; import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_repo.dart'; import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart'; import 'package:hmg_patient_app_new/features/payfort/payfort_repo.dart'; import 'package:hmg_patient_app_new/features/payfort/payfort_view_model.dart'; import 'package:hmg_patient_app_new/features/prescriptions/prescriptions_repo.dart'; import 'package:hmg_patient_app_new/features/prescriptions/prescriptions_view_model.dart'; +import 'package:hmg_patient_app_new/features/profile_settings/profile_settings_view_model.dart'; import 'package:hmg_patient_app_new/features/radiology/radiology_repo.dart'; import 'package:hmg_patient_app_new/features/radiology/radiology_view_model.dart'; import 'package:hmg_patient_app_new/services/analytics/analytics_service.dart'; @@ -34,6 +37,7 @@ import 'package:hmg_patient_app_new/services/firebase_service.dart'; import 'package:hmg_patient_app_new/services/localauth_service.dart'; import 'package:hmg_patient_app_new/services/logger_service.dart'; import 'package:hmg_patient_app_new/services/navigation_service.dart'; +import 'package:hmg_patient_app_new/widgets/date_range_selector/viewmodel/date_range_view_model.dart'; import 'package:local_auth/local_auth.dart'; import 'package:logger/web.dart'; import 'package:shared_preferences/shared_preferences.dart'; @@ -100,11 +104,7 @@ class AppDependencies { // Global/shared VMs → LazySingleton getIt.registerLazySingleton( - () => LabViewModel( - labRepo: getIt(), - errorHandlerService: getIt(), - navigationService: getIt() - ), + () => LabViewModel(labRepo: getIt(), errorHandlerService: getIt(), navigationService: getIt()), ); getIt.registerLazySingleton( @@ -154,7 +154,7 @@ class AppDependencies { ); getIt.registerLazySingleton( - () => BookAppointmentsViewModel(bookAppointmentsRepo: getIt(), errorHandlerService: getIt(), navigationService: getIt(), myAppointmentsViewModel: getIt(), locationUtils: getIt()), + () => BookAppointmentsViewModel(bookAppointmentsRepo: getIt(), errorHandlerService: getIt(), navigationService: getIt(), myAppointmentsViewModel: getIt(), locationUtils: getIt(), dialogService: getIt()), ); getIt.registerLazySingleton( @@ -165,11 +165,30 @@ class AppDependencies { myAppointmentsViewModel: getIt(), ), ); + getIt.registerLazySingleton( + () => AppointmentViaRegionViewmodel( + navigationService: getIt(), + appState: getIt(), + ), + ); getIt.registerLazySingleton( () => AuthenticationViewModel( authenticationRepo: getIt(), cacheService: getIt(), navigationService: getIt(), dialogService: getIt(), appState: getIt(), errorHandlerService: getIt(), localAuthService: getIt()), ); + getIt.registerLazySingleton(() => ProfileSettingsViewModel()); + + getIt.registerLazySingleton( + () => DateRangeSelectorRangeViewModel(), + ); + + getIt.registerLazySingleton( + () => DoctorFilterViewModel(), + ); + + getIt.registerLazySingleton( + () => AppointmentViaRegionViewmodel(navigationService: getIt(), appState: getIt()), + ); // Screen-specific VMs → Factory // getIt.registerFactory( diff --git a/lib/core/enums.dart b/lib/core/enums.dart index bb6c6fe..4151bed 100644 --- a/lib/core/enums.dart +++ b/lib/core/enums.dart @@ -30,6 +30,8 @@ enum LoginTypeEnum { sms, whatsapp, face, fingerprint } enum AppEnvironmentTypeEnum { dev, uat, preProd, qa, staging, prod } +enum FamilyFileEnum { active, inactive, blocked, deleted, pending, rejected } + extension CalenderExtension on CalenderEnum { int get toInt { switch (this) { @@ -138,6 +140,61 @@ extension OTPTypeEnumExtension on OTPTypeEnum { } } +extension FamilyFileEnumExtenshion on FamilyFileEnum { + int get toInt { + switch (this) { + case FamilyFileEnum.active: + return 3; + case FamilyFileEnum.blocked: + return 1; + case FamilyFileEnum.deleted: + return 0; + case FamilyFileEnum.pending: + return 2; + case FamilyFileEnum.inactive: + return 6; + case FamilyFileEnum.rejected: + return 4; + } + } + + String get displayName { + AppState appState = getIt.get(); + bool isArabic = appState.getLanguageID() == 1 ? true : false; + switch (this) { + case FamilyFileEnum.active: + return isArabic ? 'نشط' : 'Active'; + case FamilyFileEnum.inactive: + return isArabic ? 'غير نشط' : 'Inactive'; + case FamilyFileEnum.blocked: + return isArabic ? 'محظور' : 'Blocked'; + case FamilyFileEnum.deleted: + return isArabic ? 'محذوف' : 'Deleted'; + case FamilyFileEnum.pending: + return isArabic ? 'قيد الانتظار' : 'Pending'; + case FamilyFileEnum.rejected: + return isArabic ? 'مرفوض' : 'Rejected'; + } + } + + static FamilyFileEnum? fromValue(int value) { + switch (value) { + case 0: + return FamilyFileEnum.pending; + case 2: + return FamilyFileEnum.blocked; + case 1: + return FamilyFileEnum.deleted; + case 3: + return FamilyFileEnum.active; + case 4: + return FamilyFileEnum.inactive; + default: + return null; + } + } +} + enum ServiceTypeEnum { advancePayment, //3 ancillaryOrder, //3 diff --git a/lib/core/utils/date_util.dart b/lib/core/utils/date_util.dart index 1eb3d9f..2fbbd09 100644 --- a/lib/core/utils/date_util.dart +++ b/lib/core/utils/date_util.dart @@ -485,3 +485,11 @@ class DateUtil { return ""; } } + + +extension OnlyDate on DateTime{ + + DateTime provideDateOnly(){ + return DateTime(this.year, month, day); + } +} \ No newline at end of file diff --git a/lib/core/utils/request_utils.dart b/lib/core/utils/request_utils.dart index 8b993f1..a4ea936 100644 --- a/lib/core/utils/request_utils.dart +++ b/lib/core/utils/request_utils.dart @@ -9,6 +9,7 @@ import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/features/authentication/models/request_models/registration_payload_model.dart'; import 'package:hmg_patient_app_new/features/authentication/models/request_models/send_activation_request_model.dart'; import 'package:hmg_patient_app_new/features/common/models/commong_authanticated_req_model.dart'; +import 'package:hmg_patient_app_new/features/common/models/family_file_request.dart'; class RequestUtils { static dynamic getPatientAuthenticationRequest({ @@ -125,6 +126,9 @@ class RequestUtils { required bool isForRegister, required bool isFileNo, dynamic payload, + required bool isExcludedUser, + required bool isFormFamilyFile, + int? responseID, }) { AppState _appState = getIt.get(); var request = SendActivationRequest(); @@ -156,6 +160,15 @@ class RequestUtils { request.isRegister = false; } request.deviceTypeID = request.searchType; + + if (isFormFamilyFile) { + //INFO: Only for Excluded User Family Member Addition + request.isPatientExcluded = isExcludedUser; + request.responseID = responseID; + request.status = 2; + request.familyRegionID = zipCode == CountryEnum.saudiArabia.countryCode ? 1 : 2; + } + return request; } @@ -247,19 +260,29 @@ class RequestUtils { }; } - static dynamic getAddFamilyRequest({required String nationalIDorFile, required String mobileNo, required String countryCode, required int loginType}) { - var request = {}; + static Future getAddFamilyRequest({required String nationalIDorFile, required String mobileNo, required String countryCode}) async { + FamilyFileRequest request = FamilyFileRequest(); + int? loginType = 0; + + if (countryCode == CountryEnum.saudiArabia.countryCode || countryCode == '+966') { + loginType = (nationalIDorFile.length == 10) ? 1 : 2; + } else if (countryCode == CountryEnum.unitedArabEmirates.countryCode || countryCode == '+971') { + loginType = (nationalIDorFile.length == 15) ? 1 : 2; + } + if (loginType == 1) { - request["sharedPatientID"] = 0; - request["sharedPatientIdentificationID"] = nationalIDorFile; + request.sharedPatientId = 0; + request.sharedPatientIdentificationId = nationalIDorFile; } else if (loginType == 2) { - request["sharedPatientID"] = int.parse(nationalIDorFile); - request["sharedPatientIdentificationID"] = ''; + request.sharedPatientId = int.parse(nationalIDorFile); + request.sharedPatientIdentificationId = ''; } - request["searchType"] = loginType; - request["sharedPatientMobileNumber"] = mobileNo; - request["zipCode"] = countryCode; - request["isRegister"] = false; - request["patientStatus"] = 2; + request.searchType = loginType; + request.sharedPatientMobileNumber = mobileNo; + request.zipCode = countryCode; + request.isRegister = false; + request.patientStatus = 2; + request.isDentalAllowedBackend = false; + return request; } } diff --git a/lib/core/utils/size_utils.dart b/lib/core/utils/size_utils.dart index 6ad1835..e569021 100644 --- a/lib/core/utils/size_utils.dart +++ b/lib/core/utils/size_utils.dart @@ -1,8 +1,8 @@ import 'package:flutter/material.dart'; // These are the Viewport values of your Figma Design. // These are used in the code as a reference to create your UI Responsively. -const num FIGMA_DESIGN_WIDTH = 375; -const num FIGMA_DESIGN_HEIGHT = 667; +final num FIGMA_DESIGN_WIDTH = SizeUtils.width; +final num FIGMA_DESIGN_HEIGHT = SizeUtils.height; const num FIGMA_DESIGN_STATUS_BAR = 0; extension ResponsiveExtension on num { @@ -70,10 +70,10 @@ class SizeUtils { static late DeviceType deviceType; /// Device's Height - static late double height; + static double height = 667; /// Device's Width - static late double width; + static double width = 375; static void setScreenSize( BoxConstraints constraints, diff --git a/lib/core/utils/utils.dart b/lib/core/utils/utils.dart index 2bff2ac..c4a2db8 100644 --- a/lib/core/utils/utils.dart +++ b/lib/core/utils/utils.dart @@ -629,8 +629,17 @@ class Utils { double width = 24, double height = 24, BoxFit fit = BoxFit.cover, + double? border, + double? borderRadius, }) { - return Image.asset(icon, width: width, height: height, fit: fit); + return Container( + decoration: BoxDecoration( + border: border != null ? Border.all(color: AppColors.whiteColor, width: border) : null, + borderRadius: border != null ? BorderRadius.circular(borderRadius ?? 0) : null, + ), + child: Image.asset(icon, width: width, height: height, fit: fit), + ); + // return Image.asset(icon, width: width, height: height, fit: fit, ); } /// Widget to build an SVG from network @@ -757,4 +766,5 @@ class Utils { } return isHavePrivilege; } + } diff --git a/lib/extensions/string_extensions.dart b/lib/extensions/string_extensions.dart index 059e553..4432737 100644 --- a/lib/extensions/string_extensions.dart +++ b/lib/extensions/string_extensions.dart @@ -1,4 +1,6 @@ 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/enums.dart'; import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; import 'package:auto_size_text/auto_size_text.dart'; @@ -365,7 +367,8 @@ extension DynamicTextStyleExtension on BuildContext { TextBaseline? textBaseline, FontStyle? fontStyle, bool isLanguageSwitcher = false}) { - final family = FontUtils.getFontFamilyForLanguage(false); // TODO: @Aamir make it dynamic based on app language + AppState appState = getIt.get(); + final family = appState.getLanguageCode() == "ar" ? 'GESSTwo' : 'Poppins'; return TextStyle( fontFamily: family, fontSize: fontSize, diff --git a/lib/features/authentication/authentication_repo.dart b/lib/features/authentication/authentication_repo.dart index 2eaeb80..bec5470 100644 --- a/lib/features/authentication/authentication_repo.dart +++ b/lib/features/authentication/authentication_repo.dart @@ -3,8 +3,10 @@ import 'dart:async'; 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/app_state.dart'; import 'package:hmg_patient_app_new/core/common_models/generic_api_model.dart'; import 'package:hmg_patient_app_new/core/common_models/privilege/PrivilegeModel.dart'; +import 'package:hmg_patient_app_new/core/dependencies.dart'; import 'package:hmg_patient_app_new/core/exceptions/api_failure.dart'; import 'package:hmg_patient_app_new/features/authentication/models/request_models/check_activation_code_register_request_model.dart'; import 'package:hmg_patient_app_new/features/authentication/models/resp_models/select_device_by_imei.dart'; @@ -17,12 +19,18 @@ abstract class AuthenticationRepo { Future>> checkPatientAuthentication({required dynamic checkPatientAuthenticationReq}); - Future>> sendActivationCodeRepo({required dynamic sendActivationCodeReq, String? languageID, bool isRegister = false}); + Future>> sendActivationCodeRepo({required dynamic sendActivationCodeReq, String? languageID, bool isRegister = false, bool isFormFamilyFile = false}); Future>> checkActivationCodeRepo( - {required dynamic newRequest, // could be CheckActivationCodeReq or CheckActivationCodeRegisterReq + {required dynamic newRequest, required String? activationCode, - required bool isRegister}); + required bool isRegister, + bool isFormFamilyFile = false, + int? patientShareRequestID, + int? responseID, + bool isSwitchUser = false, + int? patientID, + int? loginType}); Future>> checkIfUserAgreed({required dynamic commonAuthanticatedRequest}); @@ -134,18 +142,33 @@ class AuthenticationRepoImp implements AuthenticationRepo { required dynamic sendActivationCodeReq, String? languageID, bool isRegister = false, + bool isFormFamilyFile = false, }) async { int isOutKsa = (sendActivationCodeReq.zipCode == '966' || sendActivationCodeReq.zipCode == '+966') ? 0 : 1; sendActivationCodeReq.patientOutSA = isOutKsa; sendActivationCodeReq.isDentalAllowedBackend = false; + final payload = sendActivationCodeReq.toJson(); + if (isFormFamilyFile) { + payload.remove("MobileNo"); + payload.remove("NationalID"); + payload.remove("SMSSignature"); + payload.remove("ResponseID"); + print("=================== Final Payload ==================="); + print(payload); + print("====================================================="); + } try { GenericApiModel? apiResponse; Failure? failure; await apiClient.post( - isRegister ? ApiConsts.sendActivationCodeRegister : ApiConsts.sendActivationCode, - body: sendActivationCodeReq.toJson(), + isFormFamilyFile + ? ApiConsts.sendFamilyFileActivation + : isRegister + ? ApiConsts.sendActivationCodeRegister + : ApiConsts.sendActivationCode, + body: isFormFamilyFile ? payload : sendActivationCodeReq.toJson(), onFailure: (error, statusCode, {messageStatus, failureType}) { failure = failureType; }, @@ -172,24 +195,122 @@ class AuthenticationRepoImp implements AuthenticationRepo { } @override - Future>> checkActivationCodeRepo({ - required dynamic newRequest, // could be CheckActivationCodeReq or CheckActivationCodeRegisterReq - required String? activationCode, - required bool isRegister, - }) async { + Future>> checkActivationCodeRepo( + {required dynamic newRequest, // could be CheckActivationCodeReq or CheckActivationCodeRegisterReq + required String? activationCode, + required bool isRegister, + bool isFormFamilyFile = false, + int? patientShareRequestID, + int? responseID, + bool isSwitchUser = false, + int? patientID, + int? loginType}) async { + AppState appState = getIt.get(); + // if (isRegister) { + // newRequest["activationCode"] = activationCode ?? "0000"; + // newRequest["isSilentLogin"] = activationCode != null ? false : true; + // } else { + // newRequest.activationCode = activationCode ?? "0000"; + // newRequest.isSilentLogin = activationCode != null ? false : true; + // newRequest.projectOutSA = newRequest.zipCode == '966' ? false : true; + // newRequest.isDentalAllowedBackend = false; + // newRequest.forRegisteration = newRequest.isRegister ?? false; + // newRequest.isRegister = false; + // } + // Map familyRequest = {}; + // if (isFormFamilyFile) { + // AppState appState = getIt.get(); + // familyRequest = {}; + // familyRequest['PatientShareRequestID'] = patientShareRequestID; + // familyRequest['ResponseID'] = responseID; + // familyRequest['Status'] = 3; + // familyRequest["PatientID"] = appState.getAuthenticatedUser()!.patientId ?? 0; + // familyRequest["LogInTokenID"] = appState.getFamilyFileTokenID; + // familyRequest["activationCode"] = activationCode ?? "0000"; + // familyRequest["PatientMobileNumber"] = newRequest.patientMobileNumber; + // familyRequest["PatientIdentificationID"] = newRequest.patientIdentificationID; + // } + // Map switchRequest = {}; + // if (isSwitchUser) { + // switchRequest = newRequest.toJson(); + // + // switchRequest['PatientID'] = responseID; + // switchRequest['IsSilentLogin'] = true; + // switchRequest['LogInTokenID'] = null; + // switchRequest['SearchType'] = 2; + // if (loginType != 0) { + // switchRequest['SuperUser'] = patientID; + // switchRequest['DeviceToken'] = null; + // } else { + // switchRequest["LoginType"] = 2; + // } + // + // if (appState.getSuperUserID == responseID) { + // // switchRequest['LoginType'] = 3; + // switchRequest['PatientIdentificationID'] = ""; + // // switchRequest['ProjectOutSA'] = newRequest.zipCode == '966' ? false : true; + // switchRequest.remove('NationalID'); + // switchRequest.remove('isDentalAllowedBackend'); + // switchRequest.remove('ProjectOutSA'); + // switchRequest.remove('ForRegisteration'); + // appState.setSuperUserID = null; + // } + // } + if (isRegister) { newRequest["activationCode"] = activationCode ?? "0000"; - newRequest["isSilentLogin"] = activationCode != null ? false : true; + newRequest["isSilentLogin"] = activationCode == null; } else { newRequest.activationCode = activationCode ?? "0000"; - newRequest.isSilentLogin = activationCode != null ? false : true; - newRequest.projectOutSA = newRequest.zipCode == '966' ? false : true; + newRequest.isSilentLogin = activationCode == null; + newRequest.projectOutSA = newRequest.zipCode != '966'; newRequest.isDentalAllowedBackend = false; newRequest.forRegisteration = newRequest.isRegister ?? false; newRequest.isRegister = false; } - final endpoint = isRegister ? ApiConsts.checkActivationCodeRegister : ApiConsts.checkActivationCode; + Map familyRequest = {}; + if (isFormFamilyFile) { + familyRequest = { + 'PatientShareRequestID': patientShareRequestID, + 'ResponseID': responseID, + 'Status': 3, + // 'PatientID': appState.getAuthenticatedUser()?.patientId ?? 0, + 'LogInTokenID': appState.getFamilyFileTokenID, + 'activationCode': activationCode ?? "0000", + 'PatientMobileNumber': newRequest.patientMobileNumber, + 'PatientIdentificationID': newRequest.patientIdentificationID, + }; + } + + Map switchRequest = {}; + if (isSwitchUser) { + switchRequest = newRequest.toJson(); + switchRequest.addAll({ + 'PatientID': responseID, + 'IsSilentLogin': true, + 'LogInTokenID': null, + 'SearchType': 2, + }); + + if (loginType != 0) { + switchRequest['SuperUser'] = patientID; + switchRequest['DeviceToken'] = null; + } else { + switchRequest['LoginType'] = 2; + } + + if (appState.getSuperUserID == responseID) { + switchRequest['PatientIdentificationID'] = ""; + switchRequest.removeWhere((key, value) => ['NationalID', 'isDentalAllowedBackend', 'ProjectOutSA', 'ForRegisteration'].contains(key)); + } + } + + final endpoint = isFormFamilyFile + ? ApiConsts.checkActivationCodeForFamily + : isRegister + ? ApiConsts.checkActivationCodeRegister + : ApiConsts.checkActivationCode; try { GenericApiModel? apiResponse; @@ -197,7 +318,13 @@ class AuthenticationRepoImp implements AuthenticationRepo { await apiClient.post( endpoint, - body: isRegister ? newRequest : newRequest.toJson(), + body: isFormFamilyFile + ? familyRequest + : isRegister + ? newRequest + : isSwitchUser + ? switchRequest + : newRequest.toJson(), onFailure: (error, statusCode, {messageStatus, failureType}) { failure = failureType; }, @@ -226,7 +353,6 @@ class AuthenticationRepoImp implements AuthenticationRepo { @override Future>> checkIfUserAgreed({required dynamic commonAuthanticatedRequest}) async { commonAuthanticatedRequest['Region'] = 1; - print("dsfsdd"); try { GenericApiModel? apiResponse; Failure? failure; @@ -261,7 +387,6 @@ class AuthenticationRepoImp implements AuthenticationRepo { @override Future>> getUserAgreementContent({required dynamic commonAuthanticatedRequest}) async { commonAuthanticatedRequest['Region'] = 1; - print("dsfsdd"); try { GenericApiModel? apiResponse; Failure? failure; diff --git a/lib/features/authentication/authentication_view_model.dart b/lib/features/authentication/authentication_view_model.dart index b43858c..e643c5d 100644 --- a/lib/features/authentication/authentication_view_model.dart +++ b/lib/features/authentication/authentication_view_model.dart @@ -13,6 +13,7 @@ import 'package:hmg_patient_app_new/core/common_models/nationality_country_model import 'package:hmg_patient_app_new/core/common_models/privilege/HMCProjectListModel.dart'; import 'package:hmg_patient_app_new/core/common_models/privilege/ProjectDetailListModel.dart'; import 'package:hmg_patient_app_new/core/common_models/privilege/VidaPlusProjectListModel.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/loading_utils.dart'; import 'package:hmg_patient_app_new/core/utils/request_utils.dart'; @@ -26,6 +27,9 @@ import 'package:hmg_patient_app_new/features/authentication/models/resp_models/a import 'package:hmg_patient_app_new/features/authentication/models/resp_models/check_activation_code_resp_model.dart'; import 'package:hmg_patient_app_new/features/authentication/models/resp_models/check_user_staus_nhic_response_model.dart'; import 'package:hmg_patient_app_new/features/authentication/models/resp_models/select_device_by_imei.dart'; +import 'package:hmg_patient_app_new/features/medical_file/medical_file_repo.dart'; +import 'package:hmg_patient_app_new/features/medical_file/medical_file_view_model.dart'; +import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/presentation/authentication/login.dart'; import 'package:hmg_patient_app_new/presentation/authentication/saved_login_screen.dart'; @@ -243,7 +247,7 @@ class AuthenticationViewModel extends ChangeNotifier { await selectDeviceImei(onSuccess: (dynamic respData) async { try { if (respData != null) { - dynamic data = SelectDeviceByImeiRespModelElement.fromJson(respData.toJson()); + dynamic data = await SelectDeviceByImeiRespModelElement.fromJson(respData.toJson()); _appState.setSelectDeviceByImeiRespModelElement(data); LoaderBottomSheet.hideLoader(); @@ -342,24 +346,33 @@ class AuthenticationViewModel extends ChangeNotifier { ); } - Future sendActivationCode( - {required OTPTypeEnum otpTypeEnum, required String nationalIdOrFileNumber, required String phoneNumber, required bool isForRegister, dynamic payload, bool isComingFromResendOTP = false}) async { + Future sendActivationCode({required OTPTypeEnum otpTypeEnum, + required String nationalIdOrFileNumber, + required String phoneNumber, + required bool isForRegister, + dynamic payload, + bool isComingFromResendOTP = false, + bool isExcludedUser = false, + bool isFormFamilyFile = false, + int? responseID}) async { var request = RequestUtils.getCommonRequestSendActivationCode( - otpTypeEnum: otpTypeEnum, - mobileNumber: phoneNumber, - selectedLoginType: otpTypeEnum.toInt(), - zipCode: selectedCountrySignup.countryCode, - nationalId: int.parse(nationalIdOrFileNumber), - isFileNo: isForRegister ? isPatientHasFile(request: payload) : false, - patientId: 0, - isForRegister: isForRegister, - patientOutSA: isForRegister - ? isPatientOutsideSA(request: payload) - : selectedCountrySignup.countryCode == CountryEnum.saudiArabia - ? false - : true, - payload: payload, - ); + otpTypeEnum: otpTypeEnum, + mobileNumber: phoneNumber, + selectedLoginType: otpTypeEnum.toInt(), + zipCode: selectedCountrySignup.countryCode, + nationalId: int.parse(nationalIdOrFileNumber), + isFileNo: isForRegister ? isPatientHasFile(request: payload) : false, + patientId: 0, + isForRegister: isForRegister, + patientOutSA: isForRegister + ? isPatientOutsideSA(request: payload) + : selectedCountrySignup.countryCode == CountryEnum.saudiArabia.countryCode + ? false + : true, + payload: payload, + isExcludedUser: isExcludedUser, + isFormFamilyFile: isFormFamilyFile, + responseID: responseID); // TODO: GET APP SMS SIGNATURE HERE request.sMSSignature = await getSignature(); @@ -368,11 +381,17 @@ class AuthenticationViewModel extends ChangeNotifier { _appState.setUserRegistrationPayload = RegistrationDataModelPayload.fromJson(payload); } - final resultEither = await _authenticationRepo.sendActivationCodeRepo(sendActivationCodeReq: request, isRegister: checkIsUserComingForRegister(request: payload), languageID: 'er'); + final resultEither = await _authenticationRepo.sendActivationCodeRepo( + sendActivationCodeReq: request, isRegister: checkIsUserComingForRegister(request: payload), languageID: 'er', isFormFamilyFile: isFormFamilyFile); resultEither.fold( (failure) async => await _errorHandlerService.handleError(failure: failure), (apiResponse) async { + int? patientShareRequestID = 0; + if (isFormFamilyFile) { + patientShareRequestID = apiResponse.data['PatientShareRequestID']; + _appState.setFamilyFileTokenID = apiResponse.data['LogInTokenID']; + } if (apiResponse.messageStatus == 2) { LoaderBottomSheet.hideLoader(); await _dialogService.showCommonBottomSheetWithoutH( @@ -383,7 +402,18 @@ class AuthenticationViewModel extends ChangeNotifier { } else { if (apiResponse.data != null && apiResponse.data['isSMSSent'] == true) { LoaderBottomSheet.hideLoader(); - if (!isComingFromResendOTP) navigateToOTPScreen(otpTypeEnum: otpTypeEnum, phoneNumber: phoneNumber, isComingFromRegister: checkIsUserComingForRegister(request: payload), payload: payload); + if (!isComingFromResendOTP) { + navigateToOTPScreen( + otpTypeEnum: otpTypeEnum, + phoneNumber: phoneNumber, + isComingFromRegister: checkIsUserComingForRegister(request: payload), + payload: payload, + isFormFamilyFile: isFormFamilyFile, + isExcludedUser: isExcludedUser, + responseID: responseID, + patientShareRequestID: patientShareRequestID, + ); + } } else { // TODO: Handle isSMSSent false // navigateToOTPScreen(otpTypeEnum: otpTypeEnum, phoneNumber: phoneNumber); @@ -401,8 +431,17 @@ class AuthenticationViewModel extends ChangeNotifier { return isUserComingForRegister; } - Future checkActivationCode( - {required String? activationCode, required OTPTypeEnum otpTypeEnum, required Function(String? message) onWrongActivationCode, Function()? onResendActivation}) async { + Future checkActivationCode({ + required String? activationCode, + required OTPTypeEnum otpTypeEnum, + required Function(String? message) onWrongActivationCode, + Function()? onResendActivation, + bool isFormFamilyFile = false, + dynamic patientShareRequestID, + dynamic responseID, + bool isSwitchUser = false, + int? patientID, + }) async { bool isForRegister = (_appState.getUserRegistrationPayload.healthId != null || _appState.getUserRegistrationPayload.patientOutSa == true || _appState.getUserRegistrationPayload.patientOutSa == 1); final request = RequestUtils.getCommonRequestWelcome( @@ -428,6 +467,7 @@ class AuthenticationViewModel extends ChangeNotifier { //TODO: Error Here IN Zip Code. loginType: loginTypeEnum.toInt) .toJson(); + LoaderBottomSheet.showLoader(); if (isForRegister) { if (_appState.getUserRegistrationPayload.patientOutSa == 0) request['DOB'] = _appState.getUserRegistrationPayload.dob; @@ -463,7 +503,17 @@ class AuthenticationViewModel extends ChangeNotifier { } }); } else { - final resultEither = await _authenticationRepo.checkActivationCodeRepo(newRequest: CheckActivationCodeRegisterReq.fromJson(request), activationCode: activationCode, isRegister: false); + final resultEither = await _authenticationRepo.checkActivationCodeRepo( + newRequest: CheckActivationCodeRegisterReq.fromJson(request), + activationCode: activationCode, + isRegister: false, + isFormFamilyFile: isFormFamilyFile, + patientShareRequestID: patientShareRequestID, + responseID: responseID, + isSwitchUser: isSwitchUser, + patientID: patientID, + loginType: _appState.getSuperUserID != null ? 0 : 2, + ); resultEither.fold( (failure) async => await _errorHandlerService.handleError( @@ -494,48 +544,86 @@ class AuthenticationViewModel extends ChangeNotifier { // Navigator.popUntil(context, (route) => Utils.route(route, equalsTo: RegisterNew)); return; } else { - if (activation.list != null && activation.list!.isNotEmpty) { - _appState.setAuthenticatedUser(activation.list!.first); - _appState.setPrivilegeModelList(activation.list!.first.listPrivilege!); - } - _appState.setUserBloodGroup = (activation.patientBlodType ?? ""); - _appState.setAppAuthToken = activation.authenticationTokenId; - final request = RequestUtils.getAuthanticatedCommonRequest().toJson(); - bool isUserAgreedBefore = await checkIfUserAgreedBefore(request: request); - - //updating the last login type in app state to show the fingerprint/face id option on home screen - if (_appState.getSelectDeviceByImeiRespModelElement != null) { - _appState.getSelectDeviceByImeiRespModelElement!.logInType = loginTypeEnum.toInt; - } - LoaderBottomSheet.hideLoader(); - insertPatientIMEIData(loginTypeEnum.toInt); - await clearDefaultInputValues(); - if (isUserAgreedBefore) { - navigateToHomeScreen(); + if (isFormFamilyFile) { + // await navigateToFamilyFilePage(); + MedicalFileViewModel medicalVm = getIt(); + if (!_appState.getIsChildLoggedIn) { + await medicalVm.getFamilyFiles(status: 0); + await medicalVm.getAllPendingRecordsByResponseId(); + _navigationService.popUntilNamed(AppRoutes.landingScreen); + } } else { - navigateToHomeScreen(); - //Agreement page not designed yet so we are navigating to home screen directly - // getUserAgreementContent(request: request); + if (activation.list != null && activation.list!.isNotEmpty) { + if (isSwitchUser) { + if (_appState.getIsChildLoggedIn) { + _appState.setSuperUserID = null; + _appState.setIsChildLoggedIn = false; + activation.list!.first.isParentUser = true; + } else { + _appState.setSuperUserID = _appState.getAuthenticatedUser()?.patientId; + _appState.setIsChildLoggedIn = true; + activation.list!.first.isParentUser = false; + } + } else { + activation.list!.first.isParentUser = true; + } + activation.list!.first.bloodGroup = activation.patientBlodType; + _appState.setAuthenticatedUser(activation.list!.first); + _appState.setPrivilegeModelList(activation.list!.first.listPrivilege!); + } + // _appState.setUserBloodGroup = (activation.patientBlodType ?? ""); + _appState.setAppAuthToken = activation.authenticationTokenId; + final request = await RequestUtils.getAuthanticatedCommonRequest().toJson(); + bool isUserAgreedBefore = await checkIfUserAgreedBefore(request: request); + + //updating the last login type in app state to show the fingerprint/face id option on home screen + if (_appState.getSelectDeviceByImeiRespModelElement != null) { + _appState.getSelectDeviceByImeiRespModelElement!.logInType = loginTypeEnum.toInt; + } + LoaderBottomSheet.hideLoader(); +// + if (!isSwitchUser && !_appState.getIsChildLoggedIn) { + MedicalFileViewModel medicalVm = getIt(); + await insertPatientIMEIData(loginTypeEnum.toInt); + await medicalVm.getFamilyFiles(status: 0); //TODO: Remove status: 1 by Aamir Need to Discuss With Sultan + await medicalVm.getAllPendingRecordsByResponseId(); + } + + await clearDefaultInputValues(); + if (isUserAgreedBefore) { + navigateToHomeScreen(); + } else { + MyAppointmentsViewModel myAppointmentsVM = getIt(); + myAppointmentsVM.setIsAppointmentDataToBeLoaded(true); + navigateToHomeScreen(); + //Agreement page not designed yet so we are navigating to home screen directly + // getUserAgreementContent(request: request); + } + // TODO: setPreferences and stuff + // sharedPref.remove(FAMILY_FILE); + // activation.list!.isFamily = false; + // userData = activation.list; + // sharedPref.setString(BLOOD_TYPE, activation.patientBloodType ?? ""); + // authenticatedUserObject.user = activation.list!; + // projectViewModel.setPrivilege(privilegeList: res); + // await sharedPref.setObject(MAIN_USER, activation.list); + // await sharedPref.setObject(USER_PROFILE, activation.list); + // loginTokenID = activation.logInTokenID; + // await sharedPref.setObject(LOGIN_TOKEN_ID, activation.logInTokenID); + // await sharedPref.setString(TOKEN, activation.authenticationTokenID!); + + // projectViewModel.analytics.loginRegistration.login_successful(); } - // TODO: setPreferences and stuff - // sharedPref.remove(FAMILY_FILE); - // activation.list!.isFamily = false; - // userData = activation.list; - // sharedPref.setString(BLOOD_TYPE, activation.patientBloodType ?? ""); - // authenticatedUserObject.user = activation.list!; - // projectViewModel.setPrivilege(privilegeList: res); - // await sharedPref.setObject(MAIN_USER, activation.list); - // await sharedPref.setObject(USER_PROFILE, activation.list); - // loginTokenID = activation.logInTokenID; - // await sharedPref.setObject(LOGIN_TOKEN_ID, activation.logInTokenID); - // await sharedPref.setString(TOKEN, activation.authenticationTokenID!); - - // projectViewModel.analytics.loginRegistration.login_successful(); } }); } } + Future navigateToFamilyFilePage() async { + MedicalFileViewModel medicalFileVM = GetIt.instance(); + medicalFileVM.handleFamilyFileRequestOTPVerification(); + } + Future checkIfUserAgreedBefore({required dynamic request}) async { bool isAgreed = false; if (havePrivilege(109)) { @@ -573,13 +661,25 @@ class AuthenticationViewModel extends ChangeNotifier { _navigationService.pushAndReplace(AppRoutes.landingScreen); } - Future navigateToOTPScreen({required OTPTypeEnum otpTypeEnum, required String phoneNumber, required bool isComingFromRegister, dynamic payload}) async { + Future navigateToOTPScreen( + {required OTPTypeEnum otpTypeEnum, + required String phoneNumber, + required bool isComingFromRegister, + dynamic payload, + bool isFormFamilyFile = false, + bool isExcludedUser = false, + int? responseID, + int? patientShareRequestID}) async { _navigationService.pushToOtpScreen( phoneNumber: phoneNumber, + isFormFamilyFile: isFormFamilyFile, checkActivationCode: (int activationCode) async { await checkActivationCode( activationCode: activationCode.toString(), + isFormFamilyFile: isFormFamilyFile, otpTypeEnum: otpTypeEnum, + responseID: responseID, + patientShareRequestID: patientShareRequestID, onWrongActivationCode: (String? value) { onWrongActivationCode(message: value); }, @@ -587,12 +687,16 @@ class AuthenticationViewModel extends ChangeNotifier { }, onResendOTPPressed: (String phoneNumber) async { await sendActivationCode( - otpTypeEnum: otpTypeEnum, - phoneNumber: phoneNumberController.text, - nationalIdOrFileNumber: nationalIdController.text, - isForRegister: isComingFromRegister, - isComingFromResendOTP: true, - payload: payload); + otpTypeEnum: otpTypeEnum, + phoneNumber: phoneNumberController.text, + nationalIdOrFileNumber: nationalIdController.text, + isForRegister: isComingFromRegister, + isComingFromResendOTP: true, + payload: payload, + isFormFamilyFile: isFormFamilyFile, + isExcludedUser: isExcludedUser, + responseID: responseID, + ); }, ); } diff --git a/lib/features/authentication/models/resp_models/authenticated_user_resp_model.dart b/lib/features/authentication/models/resp_models/authenticated_user_resp_model.dart index 8848fde..8209a0f 100644 --- a/lib/features/authentication/models/resp_models/authenticated_user_resp_model.dart +++ b/lib/features/authentication/models/resp_models/authenticated_user_resp_model.dart @@ -69,6 +69,8 @@ class AuthenticatedUser { dynamic authenticatedUserPatientPayType; dynamic authenticatedUserPatientType; dynamic authenticatedUserStatus; + int? superUser; + bool? isParentUser; AuthenticatedUser({ this.setupId, @@ -139,6 +141,8 @@ class AuthenticatedUser { this.authenticatedUserPatientPayType, this.authenticatedUserPatientType, this.authenticatedUserStatus, + this.superUser, + this.isParentUser, }); factory AuthenticatedUser.fromRawJson(String str) => AuthenticatedUser.fromJson(json.decode(str)); @@ -146,146 +150,150 @@ class AuthenticatedUser { String toRawJson() => json.encode(toJson()); factory AuthenticatedUser.fromJson(Map json) => AuthenticatedUser( - setupId: json["SetupID"], - patientType: json["PatientType"], - patientId: json["PatientID"], - firstName: json["FirstName"], - middleName: json["MiddleName"], - lastName: json["LastName"], - firstNameN: json["FirstNameN"], - middleNameN: json["MiddleNameN"], - lastNameN: json["LastNameN"], - relationshipId: json["RelationshipID"], - gender: json["Gender"], - dateofBirth: json["DateofBirth"], - dateofBirthN: json["DateofBirthN"], - nationalityId: json["NationalityID"], - phoneResi: json["PhoneResi"], - phoneOffice: json["PhoneOffice"], - mobileNumber: json["MobileNumber"], - faxNumber: json["FaxNumber"], - emailAddress: json["EmailAddress"], - bloodGroup: json["BloodGroup"], - rhFactor: json["RHFactor"], - isEmailAlertRequired: json["IsEmailAlertRequired"], - isSmsAlertRequired: json["IsSMSAlertRequired"], - preferredLanguage: json["PreferredLanguage"], - isPrivilegedMember: json["IsPrivilegedMember"], - memberId: json["MemberID"], - expiryDate: json["ExpiryDate"], - isHmgEmployee: json["IsHmgEmployee"], - employeeId: json["EmployeeID"], - emergencyContactName: json["EmergencyContactName"], - emergencyContactNo: json["EmergencyContactNo"], - patientPayType: json["PatientPayType"], - dhccPatientRefId: json["DHCCPatientRefID"], - isPatientDummy: json["IsPatientDummy"], - status: json["Status"], - isStatusCleared: json["IsStatusCleared"], - patientIdentificationType: json["PatientIdentificationType"], - patientIdentificationNo: json["PatientIdentificationNo"], - projectId: json["ProjectID"], - infoSourceId: json["InfoSourceID"], - address: json["Address"], - age: json["Age"], - ageDesc: json["AgeDesc"], - areaId: json["AreaID"], - crsVerificationStatus: json["CRSVerificationStatus"], - crsVerificationStatusDesc: json["CRSVerificationStatusDesc"], - crsVerificationStatusDescN: json["CRSVerificationStatusDescN"], - createdBy: json["CreatedBy"], - genderDescription: json["GenderDescription"], - healthIdFromNhicViaVida: json["HealthIDFromNHICViaVida"], - ir: json["IR"], - isoCityId: json["ISOCityID"], - isoCountryId: json["ISOCountryID"], - isVerfiedFromNhic: json["IsVerfiedFromNHIC"], - listPrivilege: json["ListPrivilege"] == null ? [] : List.from(json["ListPrivilege"]!.map((x) => ListPrivilege.fromJson(x))), - marital: json["Marital"], - occupationId: json["OccupationID"], - outSa: json["OutSA"], - poBox: json["POBox"], - receiveHealthSummaryReport: json["ReceiveHealthSummaryReport"], - sourceType: json["SourceType"], - strDateofBirth: json["StrDateofBirth"], - tempAddress: json["TempAddress"], - zipCode: json["ZipCode"], - eHealthIdField: json["eHealthIDField"], - authenticatedUserPatientPayType: json["patientPayType"], - authenticatedUserPatientType: json["patientType"], - authenticatedUserStatus: json["status"], - ); + setupId: json["SetupID"], + patientType: json["PatientType"], + patientId: json["PatientID"], + firstName: json["FirstName"], + middleName: json["MiddleName"], + lastName: json["LastName"], + firstNameN: json["FirstNameN"], + middleNameN: json["MiddleNameN"], + lastNameN: json["LastNameN"], + relationshipId: json["RelationshipID"], + gender: json["Gender"], + dateofBirth: json["DateofBirth"], + dateofBirthN: json["DateofBirthN"], + nationalityId: json["NationalityID"], + phoneResi: json["PhoneResi"], + phoneOffice: json["PhoneOffice"], + mobileNumber: json["MobileNumber"], + faxNumber: json["FaxNumber"], + emailAddress: json["EmailAddress"], + bloodGroup: json["BloodGroup"], + rhFactor: json["RHFactor"], + isEmailAlertRequired: json["IsEmailAlertRequired"], + isSmsAlertRequired: json["IsSMSAlertRequired"], + preferredLanguage: json["PreferredLanguage"], + isPrivilegedMember: json["IsPrivilegedMember"], + memberId: json["MemberID"], + expiryDate: json["ExpiryDate"], + isHmgEmployee: json["IsHmgEmployee"], + employeeId: json["EmployeeID"], + emergencyContactName: json["EmergencyContactName"], + emergencyContactNo: json["EmergencyContactNo"], + patientPayType: json["PatientPayType"], + dhccPatientRefId: json["DHCCPatientRefID"], + isPatientDummy: json["IsPatientDummy"], + status: json["Status"], + isStatusCleared: json["IsStatusCleared"], + patientIdentificationType: json["PatientIdentificationType"], + patientIdentificationNo: json["PatientIdentificationNo"], + projectId: json["ProjectID"], + infoSourceId: json["InfoSourceID"], + address: json["Address"], + age: json["Age"], + ageDesc: json["AgeDesc"], + areaId: json["AreaID"], + crsVerificationStatus: json["CRSVerificationStatus"], + crsVerificationStatusDesc: json["CRSVerificationStatusDesc"], + crsVerificationStatusDescN: json["CRSVerificationStatusDescN"], + createdBy: json["CreatedBy"], + genderDescription: json["GenderDescription"], + healthIdFromNhicViaVida: json["HealthIDFromNHICViaVida"], + ir: json["IR"], + isoCityId: json["ISOCityID"], + isoCountryId: json["ISOCountryID"], + isVerfiedFromNhic: json["IsVerfiedFromNHIC"], + listPrivilege: json["ListPrivilege"] == null ? [] : List.from(json["ListPrivilege"]!.map((x) => ListPrivilege.fromJson(x))), + marital: json["Marital"], + occupationId: json["OccupationID"], + outSa: json["OutSA"], + poBox: json["POBox"], + receiveHealthSummaryReport: json["ReceiveHealthSummaryReport"], + sourceType: json["SourceType"], + strDateofBirth: json["StrDateofBirth"], + tempAddress: json["TempAddress"], + zipCode: json["ZipCode"], + eHealthIdField: json["eHealthIDField"], + authenticatedUserPatientPayType: json["patientPayType"], + authenticatedUserPatientType: json["patientType"], + authenticatedUserStatus: json["status"], + superUser: json["superUser"], + isParentUser: json["isParentUser"] ?? false, + ); Map toJson() => { - "SetupID": setupId, - "PatientType": patientType, - "PatientID": patientId, - "FirstName": firstName, - "MiddleName": middleName, - "LastName": lastName, - "FirstNameN": firstNameN, - "MiddleNameN": middleNameN, - "LastNameN": lastNameN, - "RelationshipID": relationshipId, - "Gender": gender, - "DateofBirth": dateofBirth, - "DateofBirthN": dateofBirthN, - "NationalityID": nationalityId, - "PhoneResi": phoneResi, - "PhoneOffice": phoneOffice, - "MobileNumber": mobileNumber, - "FaxNumber": faxNumber, - "EmailAddress": emailAddress, - "BloodGroup": bloodGroup, - "RHFactor": rhFactor, - "IsEmailAlertRequired": isEmailAlertRequired, - "IsSMSAlertRequired": isSmsAlertRequired, - "PreferredLanguage": preferredLanguage, - "IsPrivilegedMember": isPrivilegedMember, - "MemberID": memberId, - "ExpiryDate": expiryDate, - "IsHmgEmployee": isHmgEmployee, - "EmployeeID": employeeId, - "EmergencyContactName": emergencyContactName, - "EmergencyContactNo": emergencyContactNo, - "PatientPayType": patientPayType, - "DHCCPatientRefID": dhccPatientRefId, - "IsPatientDummy": isPatientDummy, - "Status": status, - "IsStatusCleared": isStatusCleared, - "PatientIdentificationType": patientIdentificationType, - "PatientIdentificationNo": patientIdentificationNo, - "ProjectID": projectId, - "InfoSourceID": infoSourceId, - "Address": address, - "Age": age, - "AgeDesc": ageDesc, - "AreaID": areaId, - "CRSVerificationStatus": crsVerificationStatus, - "CRSVerificationStatusDesc": crsVerificationStatusDesc, - "CRSVerificationStatusDescN": crsVerificationStatusDescN, - "CreatedBy": createdBy, - "GenderDescription": genderDescription, - "HealthIDFromNHICViaVida": healthIdFromNhicViaVida, - "IR": ir, - "ISOCityID": isoCityId, - "ISOCountryID": isoCountryId, - "IsVerfiedFromNHIC": isVerfiedFromNhic, - "ListPrivilege": listPrivilege == null ? [] : List.from(listPrivilege!.map((x) => x.toJson())), - "Marital": marital, - "OccupationID": occupationId, - "OutSA": outSa, - "POBox": poBox, - "ReceiveHealthSummaryReport": receiveHealthSummaryReport, - "SourceType": sourceType, - "StrDateofBirth": strDateofBirth, - "TempAddress": tempAddress, - "ZipCode": zipCode, - "eHealthIDField": eHealthIdField, - "patientPayType": authenticatedUserPatientPayType, - "patientType": authenticatedUserPatientType, - "status": authenticatedUserStatus, - }; + "SetupID": setupId, + "PatientType": patientType, + "PatientID": patientId, + "FirstName": firstName, + "MiddleName": middleName, + "LastName": lastName, + "FirstNameN": firstNameN, + "MiddleNameN": middleNameN, + "LastNameN": lastNameN, + "RelationshipID": relationshipId, + "Gender": gender, + "DateofBirth": dateofBirth, + "DateofBirthN": dateofBirthN, + "NationalityID": nationalityId, + "PhoneResi": phoneResi, + "PhoneOffice": phoneOffice, + "MobileNumber": mobileNumber, + "FaxNumber": faxNumber, + "EmailAddress": emailAddress, + "BloodGroup": bloodGroup, + "RHFactor": rhFactor, + "IsEmailAlertRequired": isEmailAlertRequired, + "IsSMSAlertRequired": isSmsAlertRequired, + "PreferredLanguage": preferredLanguage, + "IsPrivilegedMember": isPrivilegedMember, + "MemberID": memberId, + "ExpiryDate": expiryDate, + "IsHmgEmployee": isHmgEmployee, + "EmployeeID": employeeId, + "EmergencyContactName": emergencyContactName, + "EmergencyContactNo": emergencyContactNo, + "PatientPayType": patientPayType, + "DHCCPatientRefID": dhccPatientRefId, + "IsPatientDummy": isPatientDummy, + "Status": status, + "IsStatusCleared": isStatusCleared, + "PatientIdentificationType": patientIdentificationType, + "PatientIdentificationNo": patientIdentificationNo, + "ProjectID": projectId, + "InfoSourceID": infoSourceId, + "Address": address, + "Age": age, + "AgeDesc": ageDesc, + "AreaID": areaId, + "CRSVerificationStatus": crsVerificationStatus, + "CRSVerificationStatusDesc": crsVerificationStatusDesc, + "CRSVerificationStatusDescN": crsVerificationStatusDescN, + "CreatedBy": createdBy, + "GenderDescription": genderDescription, + "HealthIDFromNHICViaVida": healthIdFromNhicViaVida, + "IR": ir, + "ISOCityID": isoCityId, + "ISOCountryID": isoCountryId, + "IsVerfiedFromNHIC": isVerfiedFromNhic, + "ListPrivilege": listPrivilege == null ? [] : List.from(listPrivilege!.map((x) => x.toJson())), + "Marital": marital, + "OccupationID": occupationId, + "OutSA": outSa, + "POBox": poBox, + "ReceiveHealthSummaryReport": receiveHealthSummaryReport, + "SourceType": sourceType, + "StrDateofBirth": strDateofBirth, + "TempAddress": tempAddress, + "ZipCode": zipCode, + "eHealthIDField": eHealthIdField, + "patientPayType": authenticatedUserPatientPayType, + "patientType": authenticatedUserPatientType, + "status": authenticatedUserStatus, + "superUser": superUser, + "isParentUser": isParentUser, + }; } class ListPrivilege { @@ -306,16 +314,16 @@ class ListPrivilege { String toRawJson() => json.encode(toJson()); factory ListPrivilege.fromJson(Map json) => ListPrivilege( - id: json["ID"], - serviceName: json["ServiceName"], - previlege: json["Previlege"], - region: json["Region"], - ); + id: json["ID"], + serviceName: json["ServiceName"], + previlege: json["Previlege"], + region: json["Region"], + ); Map toJson() => { - "ID": id, - "ServiceName": serviceName, - "Previlege": previlege, - "Region": region, - }; + "ID": id, + "ServiceName": serviceName, + "Previlege": previlege, + "Region": region, + }; } diff --git a/lib/features/authentication/widgets/otp_verification_screen.dart b/lib/features/authentication/widgets/otp_verification_screen.dart index e7f8d72..10e34cd 100644 --- a/lib/features/authentication/widgets/otp_verification_screen.dart +++ b/lib/features/authentication/widgets/otp_verification_screen.dart @@ -428,13 +428,9 @@ class OTPVerificationScreen extends StatefulWidget { final String phoneNumber; final Function(int code) checkActivationCode; final Function(String phoneNumber) onResendOTPPressed; + final bool isFormFamilyFile; - const OTPVerificationScreen({ - super.key, - required this.phoneNumber, - required this.checkActivationCode, - required this.onResendOTPPressed, - }); + const OTPVerificationScreen({super.key, required this.phoneNumber, required this.checkActivationCode, required this.onResendOTPPressed, required this.isFormFamilyFile}); @override State createState() => _OTPVerificationScreenState(); @@ -559,7 +555,7 @@ class _OTPVerificationScreenState extends State { LocaleKeys.weHaveSendOTP.tr().toText15(color: AppColors.inputLabelTextColor, letterSpacing: -0.4), _getMaskedPhoneNumber().toText15(color: AppColors.inputLabelTextColor, isBold: true), LocaleKeys.via.tr().toText15(color: AppColors.inputLabelTextColor, letterSpacing: -0.4), - authVM.loginTypeEnum.displayName.toText15(color: AppColors.inputLabelTextColor, isBold: true, letterSpacing: -0.4), + (widget.isFormFamilyFile ? LoginTypeEnum.sms.displayName : authVM.loginTypeEnum.displayName).toText15(color: AppColors.inputLabelTextColor, isBold: true, letterSpacing: -0.4), appState.getUserRegistrationPayload.isRegister != null && appState.getUserRegistrationPayload.isRegister == true ? LocaleKeys.forRegistrationVerification.tr().toText15(color: AppColors.inputLabelTextColor, letterSpacing: -0.4) : LocaleKeys.forLoginVerification.tr().toText15(color: AppColors.inputLabelTextColor, letterSpacing: -0.4), diff --git a/lib/features/book_appointments/book_appointments_repo.dart b/lib/features/book_appointments/book_appointments_repo.dart index 82ca921..1783556 100644 --- a/lib/features/book_appointments/book_appointments_repo.dart +++ b/lib/features/book_appointments/book_appointments_repo.dart @@ -6,6 +6,7 @@ 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/core/utils/date_util.dart'; +import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/dental_chief_complaints_response_model.dart'; import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/doctor_profile_response_model.dart'; import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/doctors_list_response_model.dart'; import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/get_clinic_list_response_model.dart'; @@ -15,6 +16,8 @@ import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/ import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/patient_appointment_history_response_model.dart'; import 'package:hmg_patient_app_new/services/logger_service.dart'; +import 'models/resp_models/laser_body_parts.dart'; + abstract class BookAppointmentsRepo { Future>>> getClinics(); @@ -72,6 +75,15 @@ abstract class BookAppointmentsRepo { Future>>> getPatientDentalEstimation( {required int projectID, Function(dynamic)? onSuccess, Function(String)? onError}); + + Future>>> getDentalChiefComplaintsList( + {required int projectID, required int clinicID, required int patientID, Function(dynamic)? onSuccess, Function(String)? onError}); + + Future>>> getDentalChiefComplaintDoctorsList(int projectID, int chiefComplaintID, + {Function(dynamic)? onSuccess, Function(String)? onError}); + + Future>>> getLaserClinics(int laserCategoryID, int projectID, int languageID, + {Function(dynamic)? onSuccess, Function(String)? onError}); } class BookAppointmentsRepoImp implements BookAppointmentsRepo { @@ -701,4 +713,139 @@ class BookAppointmentsRepoImp implements BookAppointmentsRepo { return Left(UnknownFailure(e.toString())); } } + + @override + Future>>> getDentalChiefComplaintsList( + {required int projectID, required int clinicID, required int patientID, Function(dynamic)? onSuccess, Function(String)? onError}) async { + Map mapDevice = { + "PatientID": patientID, + "ClinicID": clinicID, + "ProjectID": projectID, + "isDentalAllowedBackend": true, + "ContinueDentalPlan": false, + "IsSearchAppointmnetByClinicID": false, + }; + + try { + GenericApiModel>? apiResponse; + Failure? failure; + await apiClient.post( + GET_DOCTORS_LIST_URL, + body: mapDevice, + onFailure: (error, statusCode, {messageStatus, failureType}) { + failure = failureType; + onError!(error); + }, + onSuccess: (response, statusCode, {messageStatus, errorMessage}) { + try { + final list = response['List_DentalChiefComplain']; + + final chiefComplaintsList = list.map((item) => DentalChiefComplaintsListResponseModel.fromJson(item as Map)).toList().cast(); + + apiResponse = GenericApiModel>( + messageStatus: messageStatus, + statusCode: statusCode, + errorMessage: null, + data: chiefComplaintsList, + ); + } 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>>> getDentalChiefComplaintDoctorsList(int projectID, int chiefComplaintID, + {Function(dynamic)? onSuccess, Function(String)? onError}) async { + Map mapDevice = { + "ProjectID": projectID, + "ChiefComplaintID": chiefComplaintID, + "isDentalAllowedBackend": true, + "IsPublicRequest": true, + }; + + try { + GenericApiModel>? apiResponse; + Failure? failure; + await apiClient.post( + GET_DENTAL_DOCTORS_LIST_URL, + body: mapDevice, + onFailure: (error, statusCode, {messageStatus, failureType}) { + failure = failureType; + onError!(error); + }, + onSuccess: (response, statusCode, {messageStatus, errorMessage}) { + try { + final list = response['List_DentalDoctorChiefComplaintMapping']; + + final doctorsList = list.map((item) => DoctorsListResponseModel.fromJson(item as Map)).toList().cast(); + + apiResponse = GenericApiModel>( + messageStatus: messageStatus, + statusCode: statusCode, + errorMessage: null, + data: doctorsList, + ); + } 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>>> getLaserClinics(int laserCategoryID, int projectID, int languageID, {Function(dynamic p1)? onSuccess, Function(String p1)? onError}) async { + Map mapDevice = { + "LaserCategoryID": laserCategoryID, + "ProjectID": projectID, + "LanguageID": languageID, + }; + + try { + GenericApiModel>? apiResponse; + Failure? failure; + await apiClient.post( + LASER_BODY_PARTS, + body: mapDevice, + onFailure: (error, statusCode, {messageStatus, failureType}) { + failure = failureType; + onError!(error); + }, + onSuccess: (response, statusCode, {messageStatus, errorMessage}) { + try { + final list = response['Laser_GetBodyPartsByCategoryList']; + + final doctorsList = list.map((item) => LaserBodyPart.fromJson(item as Map)).toList().cast(); + + apiResponse = GenericApiModel>( + messageStatus: messageStatus, + statusCode: statusCode, + errorMessage: null, + data: doctorsList, + ); + } catch (e) { + failure = DataParsingFailure(e.toString()); + } + }, + ); + if (failure != null) return Left(failure!); + if (apiResponse == null) return Left(ServerFailure("Unknown error")); + return Right(apiResponse!); + } catch (e) { + return Left(UnknownFailure(e.toString())); + } + } } diff --git a/lib/features/book_appointments/book_appointments_view_model.dart b/lib/features/book_appointments/book_appointments_view_model.dart index a6c741f..20b64fd 100644 --- a/lib/features/book_appointments/book_appointments_view_model.dart +++ b/lib/features/book_appointments/book_appointments_view_model.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:hmg_patient_app_new/core/app_state.dart'; @@ -11,11 +13,14 @@ 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/features/book_appointments/book_appointments_repo.dart'; +import 'package:hmg_patient_app_new/features/book_appointments/models/LaserCategoryType.dart'; import 'package:hmg_patient_app_new/features/book_appointments/models/free_slot.dart'; +import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/dental_chief_complaints_response_model.dart'; import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/doctor_profile_response_model.dart'; import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/doctors_list_response_model.dart'; import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/get_clinic_list_response_model.dart'; import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/get_patient_dental_plan_response_model.dart'; +import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/laser_body_parts.dart'; import 'package:hmg_patient_app_new/features/book_appointments/models/timeslots.dart'; import 'package:hmg_patient_app_new/features/my_appointments/models/facility_selection.dart'; import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/doctor_list_api_response.dart'; @@ -23,6 +28,7 @@ import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/ import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/presentation/home/navigation_screen.dart'; +import 'package:hmg_patient_app_new/services/dialog_service.dart'; import 'package:hmg_patient_app_new/services/error_handler_service.dart'; import 'package:hmg_patient_app_new/services/navigation_service.dart'; import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart'; @@ -31,6 +37,7 @@ import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart'; import 'models/resp_models/get_livecare_clinics_response_model.dart'; class BookAppointmentsViewModel extends ChangeNotifier { + final DialogService dialogService; int selectedTabIndex = 0; bool isClinicsListLoading = false; @@ -57,8 +64,11 @@ class BookAppointmentsViewModel extends ChangeNotifier { List liveCareDoctorsList = []; List patientDentalPlanEstimationList = []; + List dentalChiefComplaintsList = []; int totalTimeNeededForDentalProcedure = 0; bool isContinueDentalPlan = false; + bool isChiefComplaintsListLoading = false; + int selectedChiefComplaintID = 0; GetClinicsListResponseModel selectedClinic = GetClinicsListResponseModel(); DoctorsListResponseModel selectedDoctor = DoctorsListResponseModel(); @@ -104,8 +114,31 @@ class BookAppointmentsViewModel extends ChangeNotifier { String? selectedClinicForFilters; bool applyFilters = false; + + ///variables for laser clinic + List femaleLaserCategory = [ + LaserCategoryType(1, 'bodyString'), + LaserCategoryType(2, 'face'), + LaserCategoryType(10,'bikini'), + LaserCategoryType(11, 'retouch'), + ]; + List maleLaserCategory =[ + LaserCategoryType(1, 'body'), + LaserCategoryType(2, 'face'), + LaserCategoryType(11, 'retouch'), + ]; + List> bodyTypes = []; + + List laserBodyPartsList = []; + List selectedBodyPartList = []; + int selectedBodyTypeIndex = 0; + int selectedCategory = 0; + bool isBodyPartsLoading = false; + int duration = 0; + + BookAppointmentsViewModel( - {required this.bookAppointmentsRepo, required this.errorHandlerService, required this.navigationService, required this.myAppointmentsViewModel, required this.locationUtils}) { + {required this.bookAppointmentsRepo, required this.errorHandlerService, required this.navigationService, required this.myAppointmentsViewModel, required this.locationUtils, required this.dialogService }) { initBookAppointmentViewModel(); } @@ -134,7 +167,10 @@ class BookAppointmentsViewModel extends ChangeNotifier { doctorsList.clear(); liveCareClinicsList.clear(); patientDentalPlanEstimationList.clear(); + dentalChiefComplaintsList.clear(); isContinueDentalPlan = false; + isChiefComplaintsListLoading = true; + bodyTypes = [maleLaserCategory, femaleLaserCategory]; // getLocation(); notifyListeners(); } @@ -201,6 +237,16 @@ class BookAppointmentsViewModel extends ChangeNotifier { notifyListeners(); } + setIsChiefComplaintsListLoading(bool value) { + isChiefComplaintsListLoading = value; + notifyListeners(); + } + + setSelectedChiefComplaintID(int id) { + selectedChiefComplaintID = id; + notifyListeners(); + } + void onTabChanged(int index) { selectedTabIndex = index; notifyListeners(); @@ -294,7 +340,10 @@ class BookAppointmentsViewModel extends ChangeNotifier { result.fold( (failure) async { + isDoctorsListLoading = false; + onError!("No doctors found for the search criteria".needTranslation); + notifyListeners(); }, (apiResponse) { if (apiResponse.messageStatus == 2) { @@ -326,7 +375,7 @@ class BookAppointmentsViewModel extends ChangeNotifier { result.fold( (failure) async { - onError!("No doctors found for the search criteria".needTranslation); + onError?.call("No doctors found for the search criteria".needTranslation); }, (apiResponse) async { if (apiResponse.messageStatus == 2) { @@ -949,4 +998,157 @@ class BookAppointmentsViewModel extends ChangeNotifier { }, ); } + + Future getDentalChiefComplaintsList({Function(dynamic)? onSuccess, Function(String)? onError}) async { + dentalChiefComplaintsList.clear(); + notifyListeners(); + int patientID = _appState.isAuthenticated ? _appState.getAuthenticatedUser()!.patientId ?? -1 : -1; + final result = await bookAppointmentsRepo.getDentalChiefComplaintsList(patientID: patientID, projectID: int.parse(currentlySelectedHospitalFromRegionFlow ?? "0"), clinicID: 17); + + result.fold( + (failure) async => await errorHandlerService.handleError(failure: failure), + (apiResponse) { + if (apiResponse.messageStatus == 2) { + onError!(apiResponse.errorMessage!); + // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {}); + } else if (apiResponse.messageStatus == 1) { + dentalChiefComplaintsList = apiResponse.data!; + isChiefComplaintsListLoading = false; + notifyListeners(); + if (onSuccess != null) { + onSuccess(apiResponse); + } + } + }, + ); + } + + Future getDentalChiefComplaintDoctorsList({int projectID = 0, Function(dynamic)? onSuccess, Function(String)? onError}) async { + doctorsList.clear(); + projectID = currentlySelectedHospitalFromRegionFlow != null ? int.parse(currentlySelectedHospitalFromRegionFlow!) : projectID; + final result = await bookAppointmentsRepo.getDentalChiefComplaintDoctorsList(projectID, selectedChiefComplaintID); + + result.fold( + (failure) async { + onError!("No doctors found for the search criteria...".needTranslation); + }, + (apiResponse) { + if (apiResponse.messageStatus == 2) { + // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {}); + } else if (apiResponse.messageStatus == 1) { + doctorsList = apiResponse.data!; + filteredDoctorList = doctorsList; + isDoctorsListLoading = false; + // initializeFilteredList(); + // clearSearchFilters(); + // getFiltersFromDoctorList(); + notifyListeners(); + if (onSuccess != null) { + onSuccess(apiResponse); + } + } + }, + ); + } + + setBodyType(int bodyType){ + selectedBodyTypeIndex = bodyType; + selectedCategory = 0; + selectedBodyPartList = []; + duration = 0; + notifyListeners(); + } + + FutureOr getLaserClinic() async{ + isBodyPartsLoading = true; + int id = bodyTypes[selectedBodyTypeIndex][selectedCategory].laserCategoryID; + int projectID = currentlySelectedHospitalFromRegionFlow != null ? int.parse(currentlySelectedHospitalFromRegionFlow!) : 0; + int languageID = _appState.isArabic() ? 1 : 0; + final result = await bookAppointmentsRepo.getLaserClinics(id, projectID, languageID); + result.fold( + (failure) { + isBodyPartsLoading = false; + notifyListeners(); + }, + (apiResponse) {isBodyPartsLoading = false; + if (apiResponse.messageStatus == 2) { + // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {}); + } else if (apiResponse.messageStatus == 1) { + List response =apiResponse.data!; + if(response.first.category == 2 || response.first.category == 10 ) response.remove(response.first); + laserBodyPartsList = response; + } + notifyListeners(); + + }, + ); + } + + int getDuration() { + var duration = 0; + + var lowerUpperLegsList = selectedBodyPartList.where((element) => element.mappingCode == "47" || element.mappingCode == "48")?.toList() ?? []; + var upperLowerArmsList = selectedBodyPartList.where((element) => element.mappingCode == "40" || element.mappingCode == "41")?.toList() ?? []; + + if (selectedBodyPartList.isNotEmpty) { + duration = selectedBodyPartList.fold(0, (previousValue, element) => previousValue + int.parse(element.timeDuration!)); + } + if (lowerUpperLegsList.length == 2) { + duration -= 30; + } + if (upperLowerArmsList.length == 2) { + duration -= 15; + } + + return duration; + } + + void setSelectedCategory(category) { + selectedCategory = category; + notifyListeners(); + getLaserClinic(); + } + + void setSelectedBodyPart(LaserBodyPart part) { + if(selectedBodyPartList.contains(part)){ + selectedBodyPartList.remove(part); + this.duration = getDuration(); + + notifyListeners(); + } else { + if(this.duration == 90){ + dialogService.showErrorBottomSheet(message: "Duration can not exceed 90 min".needTranslation,); + return; + } + selectedBodyPartList.add(part); + var duration = getDuration(); + if(duration > 90){ + selectedBodyPartList.remove(part); + dialogService.showErrorBottomSheet(message: "Duration Exceeds 90 min".needTranslation,); + return; + } + this.duration = duration; + notifyListeners(); + } + } + + readCurrentLaserCategoryID() { + return bodyTypes[selectedBodyTypeIndex][selectedCategory].laserCategoryID; + } + + String getLaserProcedureNameWRTLanguage(LaserBodyPart part) { + if(_appState.isArabic()){ + return part.bodyPartN??""; + }else { + return part.bodyPart??""; + } + } + + void resetLaserData() { + selectedCategory = 0; + selectedBodyTypeIndex = 0; + selectedBodyPartList = []; + laserBodyPartsList = []; + duration = 0; + } } diff --git a/lib/features/book_appointments/models/LaserCategoryType.dart b/lib/features/book_appointments/models/LaserCategoryType.dart new file mode 100644 index 0000000..df85620 --- /dev/null +++ b/lib/features/book_appointments/models/LaserCategoryType.dart @@ -0,0 +1,6 @@ +class LaserCategoryType { + int laserCategoryID; + String title; + + LaserCategoryType(this.laserCategoryID, this.title); +} diff --git a/lib/features/book_appointments/models/laser_body_parts_data.dart b/lib/features/book_appointments/models/laser_body_parts_data.dart new file mode 100644 index 0000000..2f8b32d --- /dev/null +++ b/lib/features/book_appointments/models/laser_body_parts_data.dart @@ -0,0 +1,135 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; + +class LaserBodyParts { + static final LaserBodyParts _instance = LaserBodyParts._internal(); + + LaserBodyParts._internal(); + + factory LaserBodyParts() => _instance; + + static Widget image(String assetUri) { + return new SvgPicture.asset(assetUri, fit: BoxFit.cover, width: 88, height: 88); + } + + static Widget imagePng(String assetUri) { + return Image.asset(assetUri, fit: BoxFit.cover, width: 88, height: 88); + } + + Map maleBodyMap = { + "1": imagePng("assets/images/png/body_parts/male/full_body_male.png"), + "40": imagePng("assets/images/png/body_parts/male/upper_arm.png"), + "41": imagePng("assets/images/png/body_parts/male/lower_arm.png"), + "42": imagePng("assets/images/png/body_parts/male/chest.png"), + "43": imagePng("assets/images/png/body_parts/male/chest_line.png"), + "44": imagePng("assets/images/png/body_parts/male/shoulders.png"), + "45": imagePng("assets/images/png/body_parts/male/back.png"), + "46": imagePng("assets/images/png/body_parts/male/abdomin.png"), + "47": imagePng("assets/images/png/body_parts/male/upper_legs.png"), + "48": imagePng("assets/images/png/body_parts/male/lower_legs.png"), + "49": imagePng("assets/images/png/body_parts/male/axilla.png") + }; + + Map maleFaceMap = { + "62": image("assets/images/png/body_parts/male/hydra_facial.svg"), + "5": image("assets/images/png/body_parts/male/upper_lips.svg"), + "6": image("assets/images/png/body_parts/male/chin.svg"), + "7": image("assets/images/png/body_parts/male/cheek.svg"), + "8": image("assets/images/png/body_parts/male/side_burn.svg"), + "9": image("assets/images/png/body_parts/male/ears.svg"), + "10": image("assets/images/png/body_parts/male/full_neck.svg"), + "11": image("assets/images/png/body_parts/male/half_neck.svg"), + "12": image("assets/images/png/body_parts/male/eyebrows.svg") + }; + + Map maleBodyRetouchMap = { + "50": imagePng("assets/images/png/body_parts/male/full_body_male.png"), + "51": imagePng("assets/images/png/body_parts/male/upper_arm.png"), + "52": imagePng("assets/images/png/body_parts/male/lower_arm.png"), + "53": imagePng("assets/images/png/body_parts/male/chest.png"), + "54": imagePng("assets/images/png/body_parts/male/shoulders.png"), + "55": imagePng("assets/images/png/body_parts/male/back.png"), + "56": imagePng("assets/images/png/body_parts/male/abdomin.png"), + "57": imagePng("assets/images/png/body_parts/male/full_legs.png"), + "58": imagePng("assets/images/png/body_parts/male/upper_legs.png"), + "59": imagePng("assets/images/png/body_parts/male/lower_legs.png"), + "60": imagePng("assets/images/png/body_parts/male/bikini.png"), + "61": imagePng("assets/images/png/body_parts/male/bikini_line.png") + }; + + Map femaleBodyMap = { + "1": imagePng("assets/images/png/body_parts/female/full_body_female.png"), + "40": imagePng("assets/images/png/body_parts/female/upper_arm.png"), + "41": imagePng("assets/images/png/body_parts/female/lower_arm.png"), + "42": imagePng("assets/images/png/body_parts/female/chest.png"), + "43": imagePng("assets/images/png/body_parts/female/chest_line.png"), + "44": imagePng("assets/images/png/body_parts/female/shoulders.png"), + "45": imagePng("assets/images/png/body_parts/female/back.png"), + "46": imagePng("assets/images/png/body_parts/female/abdomin.png"), + "47": imagePng("assets/images/png/body_parts/female/upper_legs.png"), + "48": imagePng("assets/images/png/body_parts/female/lower_leg.png"), + "49": imagePng("assets/images/png/body_parts/female/axilla.png") + }; + + Map femaleFaceMap = { + "62": image("assets/images/png/body_parts/female/hydra_facial.svg"), + "5": image("assets/images/png/body_parts/female/upper_lips.svg"), + "6": image("assets/images/png/body_parts/female/chin.svg"), + "7": image("assets/images/png/body_parts/female/cheeks.svg"), + "8": image("assets/images/png/body_parts/female/side_burn.svg"), + "9": image("assets/images/png/body_parts/female/ears.svg"), + "10": image("assets/images/png/body_parts/female/full_neck.svg"), + "11": image("assets/images/png/body_parts/female/half_neck.svg"), + "12": image("assets/images/png/body_parts/female/eyebrows.svg") + }; + + Map femaleBodyBikiniMap = { + "34": imagePng("assets/images/png/body_parts/female/bikini.png"), + "36": imagePng("assets/images/png/body_parts/female/bikini_line.png"), + "38": imagePng("assets/images/png/body_parts/female/buttocks.png"), + "39": imagePng("assets/images/png/body_parts/female/anal.png") + }; + + Map femaleBodyRetouchMap = { + "50": imagePng("assets/images/png/body_parts/female/full_body_female.png"), + "51": imagePng("assets/images/png/body_parts/female/upper_arm.png"), + "52": imagePng("assets/images/png/body_parts/female/lower_arm.png"), + "53": imagePng("assets/images/png/body_parts/female/chest.png"), + "54": imagePng("assets/images/png/body_parts/female/shoulders.png"), + "55": imagePng("assets/images/png/body_parts/female/back.png"), + "56": imagePng("assets/images/png/body_parts/female/abdomin.png"), + "57": imagePng("assets/images/png/body_parts/female/full_legs.png"), + "58": imagePng("assets/images/png/body_parts/female/upper_legs.png"), + "59": imagePng("assets/images/png/body_parts/female/lower_leg.png"), + "60": imagePng("assets/images/png/body_parts/female/bikini.png"), + "61": imagePng("assets/images/png/body_parts/female/bikini_line.png") + }; + + Widget? getCategoryImage(bool isMale, int category, String mappingCode) { + if (isMale) { + if (category == 1) { + return maleBodyMap![mappingCode]!; + } + if (category == 2) { + return maleFaceMap![mappingCode]!; + } + if (category == 11) { + return maleBodyRetouchMap![mappingCode]!; + } + } else { + if (category == 1) { + return femaleBodyMap![mappingCode]!; + } + if (category == 2) { + return femaleFaceMap![mappingCode]!; + } + if (category == 10) { + return femaleBodyBikiniMap![mappingCode]!; + } + if (category == 11) { + return femaleBodyRetouchMap![mappingCode]!; + } + } + return null; + } +} \ No newline at end of file diff --git a/lib/features/book_appointments/models/resp_models/dental_chief_complaints_response_model.dart b/lib/features/book_appointments/models/resp_models/dental_chief_complaints_response_model.dart new file mode 100644 index 0000000..99d5cd7 --- /dev/null +++ b/lib/features/book_appointments/models/resp_models/dental_chief_complaints_response_model.dart @@ -0,0 +1,24 @@ +class DentalChiefComplaintsListResponseModel { + int? projectID; + int? iD; + String? name; + dynamic nameN; + + DentalChiefComplaintsListResponseModel({this.projectID, this.iD, this.name, this.nameN}); + + DentalChiefComplaintsListResponseModel.fromJson(Map json) { + projectID = json['ProjectID']; + iD = json['ID']; + name = json['Name']; + nameN = json['NameN']; + } + + Map toJson() { + final Map data = new Map(); + data['ProjectID'] = this.projectID; + data['ID'] = this.iD; + data['Name'] = this.name; + data['NameN'] = this.nameN; + return data; + } +} diff --git a/lib/features/book_appointments/models/resp_models/laser_body_parts.dart b/lib/features/book_appointments/models/resp_models/laser_body_parts.dart new file mode 100644 index 0000000..d7d3fbd --- /dev/null +++ b/lib/features/book_appointments/models/resp_models/laser_body_parts.dart @@ -0,0 +1,64 @@ +class LaserBodyPart { + int? id; + int? category; + String? bodyPart; + bool? fullBoadyPart; + bool? isRetouch; + String? timeDuration; + String? bodyPartN; + bool? isActive; + String? mappingCode; + int? procedureID; + int? orderNum; + String? categoryName; + String? categoryNameN; + + LaserBodyPart( + {this.id, + this.category, + this.bodyPart, + this.fullBoadyPart, + this.isRetouch, + this.timeDuration, + this.bodyPartN, + this.isActive, + this.mappingCode, + this.procedureID, + this.orderNum, + this.categoryName, + this.categoryNameN}); + + LaserBodyPart.fromJson(Map json) { + id = json['Id']; + category = json['Category']; + bodyPart = json['BodyPart']; + fullBoadyPart = json['FullBoadyPart']; + isRetouch = json['IsRetouch']; + timeDuration = json['TimeDuration']; + bodyPartN = json['BodyPartN']; + isActive = json['IsActive']; + mappingCode = json['MappingCode']; + procedureID = json['ProcedureID']; + orderNum = json['OrderNum']; + categoryName = json['CategoryName']; + categoryNameN = json['CategoryNameN']; + } + + Map toJson() { + final Map data = new Map(); + data['Id'] = this.id; + data['Category'] = this.category; + data['BodyPart'] = this.bodyPart; + data['FullBoadyPart'] = this.fullBoadyPart; + data['IsRetouch'] = this.isRetouch; + data['TimeDuration'] = this.timeDuration; + data['BodyPartN'] = this.bodyPartN; + data['IsActive'] = this.isActive; + data['MappingCode'] = this.mappingCode; + data['ProcedureID'] = this.procedureID; + data['OrderNum'] = this.orderNum; + data['CategoryName'] = this.categoryName; + data['CategoryNameN'] = this.categoryNameN; + return data; + } +} diff --git a/lib/features/common/models/family_file_request.dart b/lib/features/common/models/family_file_request.dart new file mode 100644 index 0000000..047b9af --- /dev/null +++ b/lib/features/common/models/family_file_request.dart @@ -0,0 +1,57 @@ +import 'dart:convert'; + +class FamilyFileRequest { + int? sharedPatientId; + String? sharedPatientIdentificationId; + int? searchType; + String? sharedPatientMobileNumber; + String? zipCode; + bool? isRegister; + int? patientStatus; + bool? isDentalAllowedBackend; + bool? isPatientExcluded; + int? responseID; + + FamilyFileRequest({ + this.sharedPatientId, + this.sharedPatientIdentificationId, + this.searchType, + this.sharedPatientMobileNumber, + this.zipCode, + this.isRegister, + this.patientStatus, + this.isDentalAllowedBackend, + this.isPatientExcluded, + this.responseID, + }); + + factory FamilyFileRequest.fromRawJson(String str) => FamilyFileRequest.fromJson(json.decode(str)); + + String toRawJson() => json.encode(toJson()); + + factory FamilyFileRequest.fromJson(Map json) => FamilyFileRequest( + sharedPatientId: json["sharedPatientID"], + sharedPatientIdentificationId: json["sharedPatientIdentificationID"], + searchType: json["searchType"], + sharedPatientMobileNumber: json["sharedPatientMobileNumber"], + zipCode: json["zipCode"], + isRegister: json["isRegister"], + patientStatus: json["patientStatus"], + isDentalAllowedBackend: json["isDentalAllowedBackend"], + isPatientExcluded: json["IsPatientExcluded"], + responseID: json["ReponseID"], + ); + + Map toJson() => { + "SharedPatientID": sharedPatientId, + "SharedPatientIdentificationID": sharedPatientIdentificationId, + "SearchType": searchType, + "SharedPatientMobileNumber": sharedPatientMobileNumber, + "zipCode": zipCode, + "isRegister": isRegister, + "PatientStatus": patientStatus, + "isDentalAllowedBackend": isDentalAllowedBackend, + "IsPatientExcluded": isPatientExcluded, + "ReponseID": responseID, + }; +} diff --git a/lib/features/insurance/insurance_repo.dart b/lib/features/insurance/insurance_repo.dart index d367509..1aeb769 100644 --- a/lib/features/insurance/insurance_repo.dart +++ b/lib/features/insurance/insurance_repo.dart @@ -3,6 +3,7 @@ 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/insurance/models/resp_models/patient_insurance_approval_response_model.dart'; import 'package:hmg_patient_app_new/features/insurance/models/resp_models/patient_insurance_card_history.dart'; import 'package:hmg_patient_app_new/features/insurance/models/resp_models/patient_insurance_details_response_model.dart'; import 'package:hmg_patient_app_new/features/insurance/models/resp_models/patient_insurance_update_response_model.dart'; @@ -14,6 +15,8 @@ abstract class InsuranceRepo { Future>>> getPatientInsuranceCardHistory(); Future>> getPatientInsuranceDetailsForUpdate({required String patientId, required String identificationNo}); + + Future>>> getPatientInsuranceApprovalsList(); } class InsuranceRepoImp implements InsuranceRepo { @@ -66,7 +69,6 @@ class InsuranceRepoImp implements InsuranceRepo { @override Future>>> getPatientInsuranceCardHistory() async { Map mapDevice = {}; - try { GenericApiModel>? apiResponse; Failure? failure; @@ -144,4 +146,41 @@ class InsuranceRepoImp implements InsuranceRepo { return Left(UnknownFailure(e.toString())); } } + + @override + Future>>> getPatientInsuranceApprovalsList() async { + Map mapDevice = {}; + try { + GenericApiModel>? apiResponse; + Failure? failure; + await apiClient.post( + GET_PAtIENTS_INSURANCE_APPROVALS, + body: mapDevice, + onFailure: (error, statusCode, {messageStatus, failureType}) { + failure = failureType; + }, + onSuccess: (response, statusCode, {messageStatus, errorMessage}) { + try { + final list = response['HIS_Approval_List']; + + final insuranceCardsList = list.map((item) => InsuranceApprovalResponseModel.fromJson(item as Map)).toList().cast(); + + apiResponse = GenericApiModel>( + messageStatus: messageStatus, + statusCode: statusCode, + errorMessage: null, + data: insuranceCardsList, + ); + } catch (e) { + failure = DataParsingFailure(e.toString()); + } + }, + ); + if (failure != null) return Left(failure!); + if (apiResponse == null) return Left(ServerFailure("Unknown error")); + return Right(apiResponse!); + } catch (e) { + return Left(UnknownFailure(e.toString())); + } + } } diff --git a/lib/features/insurance/insurance_view_model.dart b/lib/features/insurance/insurance_view_model.dart index 0bcf30f..363ad28 100644 --- a/lib/features/insurance/insurance_view_model.dart +++ b/lib/features/insurance/insurance_view_model.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:hmg_patient_app_new/features/insurance/insurance_repo.dart'; +import 'package:hmg_patient_app_new/features/insurance/models/resp_models/patient_insurance_approval_response_model.dart'; import 'package:hmg_patient_app_new/features/insurance/models/resp_models/patient_insurance_card_history.dart'; import 'package:hmg_patient_app_new/features/insurance/models/resp_models/patient_insurance_details_response_model.dart'; import 'package:hmg_patient_app_new/features/insurance/models/resp_models/patient_insurance_update_response_model.dart'; @@ -14,11 +15,14 @@ class InsuranceViewModel extends ChangeNotifier { bool isInsuranceDataToBeLoaded = true; + bool isInsuranceApprovalsLoading = false; + InsuranceRepo insuranceRepo; ErrorHandlerService errorHandlerService; List patientInsuranceList = []; List patientInsuranceCardHistoryList = []; + List patientInsuranceApprovalsList = []; PatientInsuranceUpdateResponseModel? patientInsuranceUpdateResponseModel; @@ -34,6 +38,7 @@ class InsuranceViewModel extends ChangeNotifier { isInsuranceHistoryLoading = true; isInsuranceDetailsLoading = true; isInsuranceUpdateDetailsLoading = true; + isInsuranceApprovalsLoading = true; notifyListeners(); } @@ -57,6 +62,11 @@ class InsuranceViewModel extends ChangeNotifier { notifyListeners(); } + setIsInsuranceApprovalsLoading(bool val) { + isInsuranceApprovalsLoading = val; + notifyListeners(); + } + Future getPatientInsuranceDetails({Function(dynamic)? onSuccess, Function(String)? onError}) async { if (!isInsuranceDataToBeLoaded) return; @@ -130,4 +140,27 @@ class InsuranceViewModel extends ChangeNotifier { }, ); } + + Future getPatientInsuranceApprovalsList({Function(dynamic)? onSuccess, Function(String)? onError}) async { + final result = await insuranceRepo.getPatientInsuranceApprovalsList(); + + result.fold( + (failure) async { + isInsuranceApprovalsLoading = false; + notifyListeners(); + }, + (apiResponse) { + if (apiResponse.messageStatus == 2) { + // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {}); + } else if (apiResponse.messageStatus == 1) { + patientInsuranceApprovalsList = apiResponse.data!; + isInsuranceApprovalsLoading = false; + notifyListeners(); + if (onSuccess != null) { + onSuccess(apiResponse); + } + } + }, + ); + } } diff --git a/lib/features/insurance/models/resp_models/patient_insurance_approval_response_model.dart b/lib/features/insurance/models/resp_models/patient_insurance_approval_response_model.dart new file mode 100644 index 0000000..f272f2d --- /dev/null +++ b/lib/features/insurance/models/resp_models/patient_insurance_approval_response_model.dart @@ -0,0 +1,465 @@ +class InsuranceApprovalResponseModel { + String? setupID; + int? projectID; + int? approvalNo; + String? approvalDate; + int? patientType; + int? patientID; + int? companyID; + int? subCategoryID; + int? doctorID; + int? clinicID; + int? approvalType; + String? inpatientApprovalSubType; + String? validFrom; + String? vaildDays; + String? validTo; + bool? isApprovalOnGross; + bool? isPackage; + int? requestedAmount; + int? actualApprovedAmount; + int? aviliableAmount; + String? companyApprovalNo; + int? progNoteOrderNo; + String? submitOn; + String? receiptOn; + String? remarks1; + String? remarks2; + int? status; + String? feedbackStatusBy; + String? feedbackStatus; + String? feedbackStatusOn; + int? authorizerID; + String? expiryDate; + int? appointmentNo; + int? admissionNo; + int? admissionRequestNo; + int? createdBy; + String? createdOn; + int? editedBy; + String? editedOn; + String? rowVer; + bool? isAddlDiscApplied; + int? inProgressReasonID; + String? extendedBy; + String? extendedOn; + int? subPolicyNo; + int? noOrderAuthorizerID; + bool? isVerbalApproval; + String? subStatus; + bool? isNotificationSend; + String? eApprovalStatus; + String? eApprovalRemarks; + bool? isEmailSentOnDelayApproval; + int? eAuthorizationID; + InsuranceApprovalDetails? apporvalDetails; + String? approvalStatusDescption; + String? clinicName; + String? clinicNameN; + String? companyName; + String? doctorImageURL; + String? doctorName; + String? doctorNameN; + int? doctorRate; + String? doctorTitle; + int? gender; + String? genderDescription; + bool? isActiveDoctorProfile; + bool? isExecludeDoctor; + bool? isInOutPatient; + String? isInOutPatientDescription; + String? isInOutPatientDescriptionN; + bool? isLiveCareAppointment; + String? projectName; + String? projectNameN; + String? qR; + List? speciality; + String? strApprovalDate; + String? strExpiryDate; + String? strSubmitDate; + int? totaUnUsedCount; + int? unUsedCount; + + InsuranceApprovalResponseModel( + {this.setupID, + this.projectID, + this.approvalNo, + this.approvalDate, + this.patientType, + this.patientID, + this.companyID, + this.subCategoryID, + this.doctorID, + this.clinicID, + this.approvalType, + this.inpatientApprovalSubType, + this.validFrom, + this.vaildDays, + this.validTo, + this.isApprovalOnGross, + this.isPackage, + this.requestedAmount, + this.actualApprovedAmount, + this.aviliableAmount, + this.companyApprovalNo, + this.progNoteOrderNo, + this.submitOn, + this.receiptOn, + this.remarks1, + this.remarks2, + this.status, + this.feedbackStatusBy, + this.feedbackStatus, + this.feedbackStatusOn, + this.authorizerID, + this.expiryDate, + this.appointmentNo, + this.admissionNo, + this.admissionRequestNo, + this.createdBy, + this.createdOn, + this.editedBy, + this.editedOn, + this.rowVer, + this.isAddlDiscApplied, + this.inProgressReasonID, + this.extendedBy, + this.extendedOn, + this.subPolicyNo, + this.noOrderAuthorizerID, + this.isVerbalApproval, + this.subStatus, + this.isNotificationSend, + this.eApprovalStatus, + this.eApprovalRemarks, + this.isEmailSentOnDelayApproval, + this.eAuthorizationID, + this.apporvalDetails, + this.approvalStatusDescption, + this.clinicName, + this.clinicNameN, + this.companyName, + this.doctorImageURL, + this.doctorName, + this.doctorNameN, + this.doctorRate, + this.doctorTitle, + this.gender, + this.genderDescription, + this.isActiveDoctorProfile, + this.isExecludeDoctor, + this.isInOutPatient, + this.isInOutPatientDescription, + this.isInOutPatientDescriptionN, + this.isLiveCareAppointment, + this.projectName, + this.projectNameN, + this.qR, + this.speciality, + this.strApprovalDate, + this.strExpiryDate, + this.strSubmitDate, + this.totaUnUsedCount, + this.unUsedCount}); + + InsuranceApprovalResponseModel.fromJson(Map json) { + setupID = json['SetupID']; + projectID = json['ProjectID']; + approvalNo = json['ApprovalNo']; + approvalDate = json['ApprovalDate']; + patientType = json['PatientType']; + patientID = json['PatientID']; + companyID = json['CompanyID']; + subCategoryID = json['SubCategoryID']; + doctorID = json['DoctorID']; + clinicID = json['ClinicID']; + approvalType = json['ApprovalType']; + inpatientApprovalSubType = json['InpatientApprovalSubType']; + validFrom = json['ValidFrom']; + vaildDays = json['VaildDays']; + validTo = json['ValidTo']; + isApprovalOnGross = json['IsApprovalOnGross']; + isPackage = json['IsPackage']; + requestedAmount = json['RequestedAmount']; + actualApprovedAmount = json['ActualApprovedAmount']; + aviliableAmount = json['AviliableAmount']; + companyApprovalNo = json['CompanyApprovalNo']; + progNoteOrderNo = json['ProgNoteOrderNo']; + submitOn = json['SubmitOn']; + receiptOn = json['ReceiptOn']; + remarks1 = json['Remarks1']; + remarks2 = json['Remarks2']; + status = json['Status']; + feedbackStatusBy = json['FeedbackStatusBy']; + feedbackStatus = json['FeedbackStatus']; + feedbackStatusOn = json['FeedbackStatusOn']; + authorizerID = json['AuthorizerID']; + expiryDate = json['ExpiryDate']; + appointmentNo = json['AppointmentNo']; + admissionNo = json['AdmissionNo']; + admissionRequestNo = json['AdmissionRequestNo']; + createdBy = json['CreatedBy']; + createdOn = json['CreatedOn']; + editedBy = json['EditedBy']; + editedOn = json['EditedOn']; + rowVer = json['RowVer']; + isAddlDiscApplied = json['IsAddlDiscApplied']; + inProgressReasonID = json['InProgressReasonID']; + extendedBy = json['ExtendedBy']; + extendedOn = json['ExtendedOn']; + subPolicyNo = json['SubPolicyNo']; + noOrderAuthorizerID = json['NoOrderAuthorizerID']; + isVerbalApproval = json['isVerbalApproval']; + subStatus = json['SubStatus']; + isNotificationSend = json['IsNotificationSend']; + eApprovalStatus = json['EApprovalStatus']; + eApprovalRemarks = json['EApprovalRemarks']; + isEmailSentOnDelayApproval = json['IsEmailSentOnDelayApproval']; + eAuthorizationID = json['EAuthorizationID']; + if (json['ApporvalDetails'] != null && json['ApporvalDetails'].length != 0) { + apporvalDetails = InsuranceApprovalDetails.fromJson(json['ApporvalDetails'][0]); + } + approvalStatusDescption = json['ApprovalStatusDescption']; + clinicName = json['ClinicName']; + clinicNameN = json['ClinicNameN']; + companyName = json['CompanyName']; + doctorImageURL = json['DoctorImageURL']; + doctorName = json['DoctorName']; + doctorNameN = json['DoctorNameN']; + doctorRate = json['DoctorRate']; + doctorTitle = json['DoctorTitle']; + gender = json['Gender']; + genderDescription = json['GenderDescription']; + isActiveDoctorProfile = json['IsActiveDoctorProfile']; + isExecludeDoctor = json['IsExecludeDoctor']; + isInOutPatient = json['IsInOutPatient']; + isInOutPatientDescription = json['IsInOutPatientDescription']; + isInOutPatientDescriptionN = json['IsInOutPatientDescriptionN']; + isLiveCareAppointment = json['IsLiveCareAppointment']; + projectName = json['ProjectName']; + projectNameN = json['ProjectNameN']; + qR = json['QR']; + if (json['Speciality'] != null) speciality = json['Speciality'].cast(); + strApprovalDate = json['StrApprovalDate']; + strExpiryDate = json['StrExpiryDate']; + strSubmitDate = json['StrSubmitDate']; + totaUnUsedCount = json['TotaUnUsedCount']; + unUsedCount = json['UnUsedCount']; + } + + Map toJson() { + final Map data = new Map(); + data['SetupID'] = this.setupID; + data['ProjectID'] = this.projectID; + data['ApprovalNo'] = this.approvalNo; + data['ApprovalDate'] = this.approvalDate; + data['PatientType'] = this.patientType; + data['PatientID'] = this.patientID; + data['CompanyID'] = this.companyID; + data['SubCategoryID'] = this.subCategoryID; + data['DoctorID'] = this.doctorID; + data['ClinicID'] = this.clinicID; + data['ApprovalType'] = this.approvalType; + data['InpatientApprovalSubType'] = this.inpatientApprovalSubType; + data['ValidFrom'] = this.validFrom; + data['VaildDays'] = this.vaildDays; + data['ValidTo'] = this.validTo; + data['IsApprovalOnGross'] = this.isApprovalOnGross; + data['IsPackage'] = this.isPackage; + data['RequestedAmount'] = this.requestedAmount; + data['ActualApprovedAmount'] = this.actualApprovedAmount; + data['AviliableAmount'] = this.aviliableAmount; + data['CompanyApprovalNo'] = this.companyApprovalNo; + data['ProgNoteOrderNo'] = this.progNoteOrderNo; + data['SubmitOn'] = this.submitOn; + data['ReceiptOn'] = this.receiptOn; + data['Remarks1'] = this.remarks1; + data['Remarks2'] = this.remarks2; + data['Status'] = this.status; + data['FeedbackStatusBy'] = this.feedbackStatusBy; + data['FeedbackStatus'] = this.feedbackStatus; + data['FeedbackStatusOn'] = this.feedbackStatusOn; + data['AuthorizerID'] = this.authorizerID; + data['ExpiryDate'] = this.expiryDate; + data['AppointmentNo'] = this.appointmentNo; + data['AdmissionNo'] = this.admissionNo; + data['AdmissionRequestNo'] = this.admissionRequestNo; + data['CreatedBy'] = this.createdBy; + data['CreatedOn'] = this.createdOn; + data['EditedBy'] = this.editedBy; + data['EditedOn'] = this.editedOn; + data['RowVer'] = this.rowVer; + data['IsAddlDiscApplied'] = this.isAddlDiscApplied; + data['InProgressReasonID'] = this.inProgressReasonID; + data['ExtendedBy'] = this.extendedBy; + data['ExtendedOn'] = this.extendedOn; + data['SubPolicyNo'] = this.subPolicyNo; + data['NoOrderAuthorizerID'] = this.noOrderAuthorizerID; + data['isVerbalApproval'] = this.isVerbalApproval; + data['SubStatus'] = this.subStatus; + data['IsNotificationSend'] = this.isNotificationSend; + data['EApprovalStatus'] = this.eApprovalStatus; + data['EApprovalRemarks'] = this.eApprovalRemarks; + data['IsEmailSentOnDelayApproval'] = this.isEmailSentOnDelayApproval; + data['EAuthorizationID'] = this.eAuthorizationID; + if (this.apporvalDetails != null) { + data['ApporvalDetails'] = this.apporvalDetails!.toJson; + } + data['ApprovalStatusDescption'] = this.approvalStatusDescption; + data['ClinicName'] = this.clinicName; + data['ClinicNameN'] = this.clinicNameN; + data['CompanyName'] = this.companyName; + data['DoctorImageURL'] = this.doctorImageURL; + data['DoctorName'] = this.doctorName; + data['DoctorNameN'] = this.doctorNameN; + data['DoctorRate'] = this.doctorRate; + data['DoctorTitle'] = this.doctorTitle; + data['Gender'] = this.gender; + data['GenderDescription'] = this.genderDescription; + data['IsActiveDoctorProfile'] = this.isActiveDoctorProfile; + data['IsExecludeDoctor'] = this.isExecludeDoctor; + data['IsInOutPatient'] = this.isInOutPatient; + data['IsInOutPatientDescription'] = this.isInOutPatientDescription; + data['IsInOutPatientDescriptionN'] = this.isInOutPatientDescriptionN; + data['IsLiveCareAppointment'] = this.isLiveCareAppointment; + data['ProjectName'] = this.projectName; + data['ProjectNameN'] = this.projectNameN; + data['QR'] = this.qR; + data['Speciality'] = this.speciality; + data['StrApprovalDate'] = this.strApprovalDate; + data['StrExpiryDate'] = this.strExpiryDate; + data['StrSubmitDate'] = this.strSubmitDate; + data['TotaUnUsedCount'] = this.totaUnUsedCount; + data['UnUsedCount'] = this.unUsedCount; + return data; + } +} + +class InsuranceApprovalDetails { + int? setupID; + int? projectID; + int? approvalNo; + int? lineItemNo; + int? orderType; + int? procedureID; + int? toothNo; + int? price; + int? approvedAmount; + String? unapprovedPatientShare; + int? waivedAmount; + String? discountType; + int? discountValue; + String? shareType; + String? patientShareTypeValue; + String? companyShareTypeValue; + String? patientShare; + String? companyShare; + int? deductableAmount; + String? disapprovedRemarks; + int? progNoteOrderNo; + int? progNoteLineItemNo; + String? invoiceTransactionType; + int? invoiceNo; + String? procedureName; + String? procedureNameN; + String? status; + bool? isInvoiced; + String? isInvoicedDesc; + + InsuranceApprovalDetails( + {this.setupID, + this.projectID, + this.approvalNo, + this.lineItemNo, + this.orderType, + this.procedureID, + this.toothNo, + this.price, + this.approvedAmount, + this.unapprovedPatientShare, + this.waivedAmount, + this.discountType, + this.discountValue, + this.shareType, + this.patientShareTypeValue, + this.companyShareTypeValue, + this.patientShare, + this.companyShare, + this.deductableAmount, + this.disapprovedRemarks, + this.progNoteOrderNo, + this.progNoteLineItemNo, + this.invoiceTransactionType, + this.invoiceNo, + this.procedureName, + this.procedureNameN, + this.status, + this.isInvoiced, + this.isInvoicedDesc}); + + InsuranceApprovalDetails.fromJson(Map json) { + setupID = json['SetupID']; + projectID = json['ProjectID']; + approvalNo = json['ApprovalNo']; + lineItemNo = json['LineItemNo']; + orderType = json['OrderType']; + procedureID = json['ProcedureID']; + toothNo = json['ToothNo']; + price = json['Price']; + approvedAmount = json['ApprovedAmount']; + unapprovedPatientShare = json['UnapprovedPatientShare']; + waivedAmount = json['WaivedAmount']; + discountType = json['DiscountType']; + discountValue = json['DiscountValue']; + shareType = json['ShareType']; + patientShareTypeValue = json['PatientShareTypeValue']; + companyShareTypeValue = json['CompanyShareTypeValue']; + patientShare = json['PatientShare']; + companyShare = json['CompanyShare']; + deductableAmount = json['DeductableAmount']; + disapprovedRemarks = json['DisapprovedRemarks']; + progNoteOrderNo = json['ProgNoteOrderNo']; + progNoteLineItemNo = json['ProgNoteLineItemNo']; + invoiceTransactionType = json['InvoiceTransactionType']; + invoiceNo = json['InvoiceNo']; + procedureName = json['ProcedureName']; + procedureNameN = json['ProcedureNameN']; + status = json['Status']; + isInvoiced = json['IsInvoiced']; + isInvoicedDesc = json['IsInvoicedDesc']; + } + + Map toJson() { + final Map data = new Map(); + data['SetupID'] = this.setupID; + data['ProjectID'] = this.projectID; + data['ApprovalNo'] = this.approvalNo; + data['LineItemNo'] = this.lineItemNo; + data['OrderType'] = this.orderType; + data['ProcedureID'] = this.procedureID; + data['ToothNo'] = this.toothNo; + data['Price'] = this.price; + data['ApprovedAmount'] = this.approvedAmount; + data['UnapprovedPatientShare'] = this.unapprovedPatientShare; + data['WaivedAmount'] = this.waivedAmount; + data['DiscountType'] = this.discountType; + data['DiscountValue'] = this.discountValue; + data['ShareType'] = this.shareType; + data['PatientShareTypeValue'] = this.patientShareTypeValue; + data['CompanyShareTypeValue'] = this.companyShareTypeValue; + data['PatientShare'] = this.patientShare; + data['CompanyShare'] = this.companyShare; + data['DeductableAmount'] = this.deductableAmount; + data['DisapprovedRemarks'] = this.disapprovedRemarks; + data['ProgNoteOrderNo'] = this.progNoteOrderNo; + data['ProgNoteLineItemNo'] = this.progNoteLineItemNo; + data['InvoiceTransactionType'] = this.invoiceTransactionType; + data['InvoiceNo'] = this.invoiceNo; + data['ProcedureName'] = this.procedureName; + data['ProcedureNameN'] = this.procedureNameN; + data['Status'] = this.status; + data['IsInvoiced'] = this.isInvoiced; + data['IsInvoicedDesc'] = this.isInvoicedDesc; + return data; + } +} diff --git a/lib/features/lab/lab_view_model.dart b/lib/features/lab/lab_view_model.dart index 8a397e3..9f713ac 100644 --- a/lib/features/lab/lab_view_model.dart +++ b/lib/features/lab/lab_view_model.dart @@ -25,6 +25,8 @@ class LabViewModel extends ChangeNotifier { bool isGraphVisible = true; bool shouldShowGraph = true; + bool isLabNeedToLoad = true; + LabRepo labRepo; ErrorHandlerService errorHandlerService; NavigationService navigationService; @@ -33,6 +35,7 @@ class LabViewModel extends ChangeNotifier { List filteredLabOrders = []; List tempLabOrdersList = []; String labSpecialResult = ""; + List labOrderTests = []; PatientLabOrdersResponseModel? currentlySelectedPatientOrder; @@ -71,17 +74,29 @@ class LabViewModel extends ChangeNotifier { required this.navigationService}); initLabProvider() { - patientLabOrders.clear(); - filteredLabOrders.clear(); - isLabOrdersLoading = true; - isLabResultsLoading = true; - getPatientLabOrders(); + if (isLabNeedToLoad) { + patientLabOrders.clear(); + filteredLabOrders.clear(); + labOrderTests.clear(); + isLabOrdersLoading = true; + isLabResultsLoading = true; + getPatientLabOrders(); + } + notifyListeners(); + } + + setIsLabNeedToLoad(bool value) { + isLabNeedToLoad = value; notifyListeners(); } Future getPatientLabOrders({Function(dynamic)? onSuccess, Function(String)? onError}) async { + if (!isLabNeedToLoad) return; + + isLabOrdersLoading = true; patientLabOrders.clear(); uniqueTests.clear(); + labOrderTests.clear(); uniqueTests = {}; notifyListeners(); @@ -105,6 +120,7 @@ class LabViewModel extends ChangeNotifier { isLabResultsLoading = false; filterSuggestions(); getUniqueTestDescription(); + isLabNeedToLoad = false; notifyListeners(); if (onSuccess != null) { onSuccess(apiResponse); @@ -151,6 +167,9 @@ class LabViewModel extends ChangeNotifier { createdOn: item.createdOn, model: item)) }; + for (var element in uniqueTests) { + labOrderTests.add(element.description ?? ""); + } } Future getPatientLabResultByHospital( @@ -165,6 +184,7 @@ class LabViewModel extends ChangeNotifier { result.fold( (failure) async { isLabResultByHospitalLoading = false; + notifyListeners(); // await errorHandlerService.handleError(failure: failure); }, (apiResponse) { diff --git a/lib/features/medical_file/medical_file_repo.dart b/lib/features/medical_file/medical_file_repo.dart index 7077fd0..a460d9f 100644 --- a/lib/features/medical_file/medical_file_repo.dart +++ b/lib/features/medical_file/medical_file_repo.dart @@ -25,13 +25,19 @@ abstract class MedicalFileRepo { Future>> getPatientMedicalReportPDF(PatientMedicalReportResponseModel patientMedicalReportResponseModel, AuthenticatedUser authenticatedUser); - Future>>> getPatientFamilyFiles(); + Future>>> getPatientFamilyFiles(int? status, int patientId); - Future>>> addFamilyFile({required dynamic request}); + Future>>> getAllPendingRecordsByResponseId({required Map request}); + + Future>> addFamilyFile({required dynamic request}); Future>>> getPatientAppointmentsForMedicalReport(); Future>> insertRequestForMedicalReport({required PatientAppointmentHistoryResponseModel appointmentHistoryResponseModel}); + + Future>> removeFamilyFile({required int? id}); + + Future>> acceptRejectFamilyFile({required int? id, required int? status}); } class MedicalFileRepoImp implements MedicalFileRepo { @@ -279,13 +285,13 @@ class MedicalFileRepoImp implements MedicalFileRepo { } @override - Future>>> getPatientFamilyFiles() async { + Future>>> getPatientFamilyFiles(int? status, int patientID) async { try { GenericApiModel>? apiResponse; Failure? failure; await apiClient.post( - FAMILY_FILES, - body: {"Status": 3}, + ApiConsts.getAllSharedRecordsByStatus, + body: {if (status != null) "Status": status, "PatientID": patientID}, onFailure: (error, statusCode, {messageStatus, failureType}) { failure = failureType; }, @@ -318,9 +324,44 @@ class MedicalFileRepoImp implements MedicalFileRepo { } @override - Future>>> addFamilyFile({dynamic request}) async { + Future>>> getAllPendingRecordsByResponseId({required Map request}) async { try { - GenericApiModel>? apiResponse; + GenericApiModel>? apiResponse; + Failure? failure; + await apiClient.post( + ApiConsts.getAllPendingRecordsByResponseId, + body: request, + onFailure: (error, statusCode, {messageStatus, failureType}) { + failure = failureType; + }, + onSuccess: (response, statusCode, {messageStatus, errorMessage}) { + try { + final list = response['GetAllPendingRecordsList']; + final familyLists = list.map((item) => FamilyFileResponseModelLists.fromJson(item as Map)).toList().cast(); + + apiResponse = GenericApiModel>( + messageStatus: messageStatus, + statusCode: statusCode, + errorMessage: null, + data: familyLists, + ); + } 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>> addFamilyFile({dynamic request}) async { + try { + GenericApiModel? apiResponse; Failure? failure; await apiClient.post( ApiConsts.addFamilyFile, @@ -330,17 +371,82 @@ class MedicalFileRepoImp implements MedicalFileRepo { }, onSuccess: (response, statusCode, {messageStatus, errorMessage}) { try { - print(response); - // final list = response['GetAllSharedRecordsByStatusList']; - // - // final familyLists = list.map((item) => FamilyFileResponseModelLists.fromJson(item as Map)).toList().cast(); - // - // apiResponse = GenericApiModel>( - // messageStatus: messageStatus, - // statusCode: statusCode, - // errorMessage: null, - // data: familyLists, - // ); + apiResponse = GenericApiModel( + messageStatus: messageStatus, + statusCode: statusCode, + errorMessage: errorMessage, + data: response["ShareFamilyFileObj"] ?? null, + ); + } 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>> removeFamilyFile({required int? id}) async { + Map request = {}; + request["ID"] = id; + request['IsActive'] = false; + try { + GenericApiModel? apiResponse; + Failure? failure; + await apiClient.post( + ApiConsts.removeFileFromFamilyMembers, + body: request, + onFailure: (error, statusCode, {messageStatus, failureType}) { + failure = failureType; + }, + onSuccess: (response, statusCode, {messageStatus, errorMessage}) { + try { + apiResponse = GenericApiModel( + messageStatus: messageStatus, + statusCode: statusCode, + errorMessage: errorMessage, + data: response, + ); + } 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>> acceptRejectFamilyFile({required int? id, required int? status}) async { + Map request = {}; + request["ID"] = id; + request['Status'] = status; + try { + GenericApiModel? apiResponse; + Failure? failure; + await apiClient.post( + ApiConsts.acceptAndRejectFamilyFile, + body: request, + onFailure: (error, statusCode, {messageStatus, failureType}) { + failure = failureType; + }, + onSuccess: (response, statusCode, {messageStatus, errorMessage}) { + try { + apiResponse = GenericApiModel( + messageStatus: messageStatus, + statusCode: statusCode, + errorMessage: errorMessage, + data: response, + ); } catch (e) { failure = DataParsingFailure(e.toString()); } diff --git a/lib/features/medical_file/medical_file_view_model.dart b/lib/features/medical_file/medical_file_view_model.dart index 3f0573f..b5d9d03 100644 --- a/lib/features/medical_file/medical_file_view_model.dart +++ b/lib/features/medical_file/medical_file_view_model.dart @@ -1,7 +1,15 @@ +import 'dart:convert'; +import 'dart:developer'; + import 'package:flutter/material.dart'; import 'package:hmg_patient_app_new/core/app_state.dart'; import 'package:hmg_patient_app_new/core/dependencies.dart'; +import 'package:hmg_patient_app_new/core/enums.dart'; +import 'package:hmg_patient_app_new/core/utils/request_utils.dart'; +import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; +import 'package:hmg_patient_app_new/features/authentication/authentication_view_model.dart'; import 'package:hmg_patient_app_new/features/authentication/models/resp_models/authenticated_user_resp_model.dart'; +import 'package:hmg_patient_app_new/features/common/models/family_file_request.dart'; import 'package:hmg_patient_app_new/features/medical_file/medical_file_repo.dart'; import 'package:hmg_patient_app_new/features/medical_file/models/family_file_response_model.dart'; import 'package:hmg_patient_app_new/features/medical_file/models/patient_medical_response_model.dart'; @@ -10,6 +18,8 @@ import 'package:hmg_patient_app_new/features/medical_file/models/patient_vaccine import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/patient_appointment_history_response_model.dart'; import 'package:hmg_patient_app_new/services/dialog_service.dart'; import 'package:hmg_patient_app_new/services/error_handler_service.dart'; +import 'package:hmg_patient_app_new/services/navigation_service.dart'; +import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart'; class MedicalFileViewModel extends ChangeNotifier { int selectedTabIndex = 0; @@ -34,13 +44,26 @@ class MedicalFileViewModel extends ChangeNotifier { PatientAppointmentHistoryResponseModel? patientMedicalReportSelectedAppointment; List patientFamilyFiles = []; + List pendingFamilyFiles = []; String patientSickLeavePDFBase64 = ""; String patientMedicalReportPDFBase64 = ""; int selectedMedicalReportsTabIndex = 0; + int _selectedFamilyFileTabIndex = 0; + + int get getSelectedFamilyFileTabIndex => _selectedFamilyFileTabIndex; + + set setSelectedFamilyFileTabIndex(int value) { + if (_selectedFamilyFileTabIndex != value) { + _selectedFamilyFileTabIndex = value; + notifyListeners(); + } + } + static final DialogService _dialogService = getIt.get(); - AppState _appState = getIt(); + final AppState _appState = getIt(); + AuthenticationViewModel authVM = getIt.get(); MedicalFileViewModel({required this.medicalFileRepo, required this.errorHandlerService}); @@ -64,6 +87,11 @@ class MedicalFileViewModel extends ChangeNotifier { notifyListeners(); } + void onFamilyFileTabChange(int index) { + setSelectedFamilyFileTabIndex = index; + notifyListeners(); + } + setIsPatientVaccineListLoading(bool isLoading) { isPatientVaccineListLoading = isLoading; notifyListeners(); @@ -247,8 +275,8 @@ class MedicalFileViewModel extends ChangeNotifier { ); } - Future getFamilyFiles({Function(dynamic)? onSuccess, Function(String)? onError}) async { - final result = await medicalFileRepo.getPatientFamilyFiles(); + Future getFamilyFiles({int? status, Function(dynamic)? onSuccess, Function(String)? onError}) async { + final result = await medicalFileRepo.getPatientFamilyFiles(status, _appState.getSuperUserID != null ? _appState.getSuperUserID! : _appState.getAuthenticatedUser()!.patientId!); result.fold( (failure) async => await errorHandlerService.handleError( @@ -261,16 +289,85 @@ class MedicalFileViewModel extends ChangeNotifier { if (apiResponse.messageStatus == 2) { _dialogService.showErrorBottomSheet(message: apiResponse.errorMessage!, onOkPressed: () {}); } else if (apiResponse.messageStatus == 1) { - patientFamilyFiles = apiResponse.data!; - patientFamilyFiles.insert( - 0, - FamilyFileResponseModelLists( - patientId: _appState.getAuthenticatedUser()!.patientId, - patientName: '${_appState.getAuthenticatedUser()!.firstName!} ${_appState.getAuthenticatedUser()!.lastName!}', - isActive: true, - gender: _appState.getAuthenticatedUser()!.gender!, - responseId: _appState.getAuthenticatedUser()!.patientId), - ); + if (apiResponse.data != null) { + // Add current user as the first active family file + final currentUser = _appState.getAuthenticatedUser()!; + final currentUserFamilyFile = FamilyFileResponseModelLists( + patientId: currentUser.patientId, + patientName: '${currentUser.firstName!} ${currentUser.lastName!}', + isActive: true, + gender: currentUser.gender!, + responseId: currentUser.patientId, + age: currentUser.age, + mobileNumber: currentUser.mobileNumber, + patientIdenficationNumber: currentUser.patientIdentificationNo, + emaiLAddress: currentUser.emailAddress, + genderDescription: currentUser.genderDescription, + ); + + // Clear and start fresh with current user + patientFamilyFiles.clear(); + patientFamilyFiles.add(currentUserFamilyFile); + + final List activeFamilyFiles = []; + final List pendingFamilyFiles = []; + + for (var element in apiResponse.data!) { + if (element.status == null) { + continue; + } + + final isPending = element.status == FamilyFileEnum.pending.toInt || element.status == FamilyFileEnum.rejected.toInt; + final isActive = element.status == FamilyFileEnum.active.toInt; + + if (!isPending && !isActive) { + continue; + } + + final familyFile = FamilyFileResponseModelLists( + id: element.id, + patientId: element.patientId, + patientName: element.patientName!, + isActive: element.isActive, + gender: element.gender!, + responseId: element.responseId, + mobileNumber: element.mobileNumber, + age: element.age, + patientIdenficationNumber: element.patientIdenficationNumber, + relationship: element.relationship, + relationshipId: element.relationshipId, + relationshipN: element.relationshipN, + status: element.status, + statusDescription: element.statusDescription, + createdOn: element.createdOn, + editedOn: element.editedOn, + patientDataVerified: element.patientDataVerified, + regionId: element.regionId, + familyRegionId: element.familyRegionId, + genderDescription: element.genderDescription, + genderImage: element.genderImage, + emaiLAddress: element.emaiLAddress, + ); + + if (isPending) { + familyFile.isRequestFromMySide = true; + pendingFamilyFiles.add(familyFile); + } + if (isActive) { + activeFamilyFiles.add(familyFile); + } + } + + for (var activeFile in activeFamilyFiles) { + if (!patientFamilyFiles.any((e) => e.responseId == activeFile.responseId)) { + patientFamilyFiles.add(activeFile); + } + } + + this.pendingFamilyFiles.clear(); + this.pendingFamilyFiles.addAll(pendingFamilyFiles); + } + notifyListeners(); if (onSuccess != null) { onSuccess(apiResponse); @@ -280,39 +377,151 @@ class MedicalFileViewModel extends ChangeNotifier { ); } - Future switchFamilyFiles({Function(dynamic)? onSuccess, Function(String)? onError}) async { - final result = await medicalFileRepo.getPatientFamilyFiles(); + Future getAllPendingRecordsByResponseId() async { + AppState appState = getIt(); + final result = await medicalFileRepo.getAllPendingRecordsByResponseId(request: {'ResponseID': appState.getAuthenticatedUser()!.patientId ?? "0", "Status": 2}); result.fold( - (failure) async => await errorHandlerService.handleError( - failure: failure, - onOkPressed: () { - onError!(failure.message); - }, - ), + (failure) async => await errorHandlerService.handleError(failure: failure), (apiResponse) { if (apiResponse.messageStatus == 2) { _dialogService.showErrorBottomSheet(message: apiResponse.errorMessage!, onOkPressed: () {}); } else if (apiResponse.messageStatus == 1) { - patientFamilyFiles = apiResponse.data!; - patientFamilyFiles.insert( - 0, - FamilyFileResponseModelLists( - patientId: _appState.getAuthenticatedUser()!.patientId, - patientName: '${_appState.getAuthenticatedUser()!.firstName!} ${_appState.getAuthenticatedUser()!.lastName!}', - isActive: true, - gender: _appState.getAuthenticatedUser()!.gender!, - responseId: _appState.getAuthenticatedUser()!.patientId), - ); - notifyListeners(); - if (onSuccess != null) { - onSuccess(apiResponse); + if (apiResponse.data != null) { + final List tempPendingFamilyFiles = []; + for (var element in apiResponse.data!) { + if (element.status != null && element.status == FamilyFileEnum.pending.toInt || element.status == FamilyFileEnum.active.toInt) { + tempPendingFamilyFiles.add(FamilyFileResponseModelLists( + id: element.id, + patientId: element.patientId, + patientName: element.patientName!, + isActive: element.status, + gender: element.gender, + responseId: element.patientId, + mobileNumber: element.mobileNumber, + age: element.age, + patientIdenficationNumber: element.patientIdenficationNumber, + relationship: element.relationship, + relationshipId: element.relationshipId, + relationshipN: element.relationshipN, + status: element.status, + statusDescription: element.statusDescription, + createdOn: element.createdOn, + editedOn: element.editedOn, + patientDataVerified: element.patientDataVerified, + regionId: element.regionId, + familyRegionId: element.familyRegionId, + genderDescription: element.genderDescription, + genderImage: element.genderImage, + emaiLAddress: element.emaiLAddress)); + } + } + // pendingFamilyFiles.addAll(tempPendingFamilyFiles.where((element) => !pendingFamilyFiles.any((e) => e.responseId == element.responseId))); + pendingFamilyFiles.addAll(tempPendingFamilyFiles.where((element) => !pendingFamilyFiles.any((e) => e.patientId == element.patientId))); } + notifyListeners(); } }, ); } + Future switchFamilyFiles({Function(dynamic)? onSuccess, int? responseID, int? patientID, String? phoneNumber, Function(String)? onError}) async { + authVM.phoneNumberController.text = phoneNumber!.startsWith("0") ? phoneNumber.replaceFirst("0", "") : phoneNumber; + + await authVM.checkActivationCode( + activationCode: '0000', + otpTypeEnum: OTPTypeEnum.sms, + onWrongActivationCode: (String? str) {}, + responseID: responseID, + isFormFamilyFile: false, + isSwitchUser: true, + patientID: patientID, + ); + } + + Future addFamilyFile({required OTPTypeEnum otpTypeEnum}) async { + LoaderBottomSheet.showLoader(); + AuthenticationViewModel authVM = getIt.get(); + NavigationService navigationService = getIt.get(); + FamilyFileRequest request = + await RequestUtils.getAddFamilyRequest(nationalIDorFile: authVM.nationalIdController.text, mobileNo: authVM.phoneNumberController.text, countryCode: authVM.selectedCountrySignup.countryCode); + + final resultEither = await medicalFileRepo.addFamilyFile(request: request.toJson()); + resultEither.fold((failure) async => await errorHandlerService.handleError(failure: failure), (apiResponse) async { + if (apiResponse.messageStatus == 2) { + LoaderBottomSheet.hideLoader(); + _dialogService.showErrorBottomSheet( + message: apiResponse.errorMessage!, + onOkPressed: () { + navigationService.pop(); + }); + } else if (apiResponse.messageStatus == 1) { + if (apiResponse.data != null) { + request.isPatientExcluded = apiResponse.data["IsPatientExcluded"]; + request.responseID = apiResponse.data["ReponseID"]; + LoaderBottomSheet.hideLoader(); + _dialogService.showExceptionBottomSheet( + message: apiResponse.data["Message"], + onOkPressed: () { + LoaderBottomSheet.showLoader(); + authVM.sendActivationCode( + otpTypeEnum: otpTypeEnum, + nationalIdOrFileNumber: request.sharedPatientIdentificationId!, + phoneNumber: request.sharedPatientMobileNumber!, + isForRegister: false, + isExcludedUser: apiResponse.data['IsPatientExcluded'], + responseID: apiResponse.data["ReponseID"], + isFormFamilyFile: true); + }, + onCancelPressed: () { + navigationService.pop(); + }); + } + } + }); + } + + Future handleFamilyFileRequestOTPVerification() async { + LoaderBottomSheet.showLoader(); + if (!_appState.getIsChildLoggedIn) { + await getFamilyFiles(status: 0); + await getAllPendingRecordsByResponseId(); + } + + LoaderBottomSheet.hideLoader(); + } + + Future removeFileFromFamilyMembers({int? id}) async { + NavigationService navigationService = getIt.get(); + _dialogService.showExceptionBottomSheet( + message: "Remove this member?", + onOkPressed: () async { + LoaderBottomSheet.showLoader(); + final result = await medicalFileRepo.removeFamilyFile(id: id); + result.fold( + (failure) async => await errorHandlerService.handleError(failure: failure), + (apiResponse) { + if (apiResponse.messageStatus == 2) { + LoaderBottomSheet.hideLoader(); + _dialogService.showErrorBottomSheet( + message: apiResponse.errorMessage!, + onOkPressed: () { + navigationService.pop(); + }); + } else if (apiResponse.messageStatus == 1) { + patientFamilyFiles.removeWhere((element) => element.id == id); + LoaderBottomSheet.hideLoader(); + notifyListeners(); + navigationService.pop(); + } + }, + ); + }, + onCancelPressed: () { + navigationService.pop(); + }); + } + Future getPatientMedicalReportAppointmentsList({Function(dynamic)? onSuccess, Function(String)? onError}) async { patientMedicalReportAppointmentHistoryList.clear(); notifyListeners(); @@ -357,8 +566,33 @@ class MedicalFileViewModel extends ChangeNotifier { ); } - Future addFamilyFile() async { - final resultEither = await medicalFileRepo.addFamilyFile(request: {}); - resultEither.fold((failure) async => await errorHandlerService.handleError(failure: failure), (apiResponse) async {}); + Future acceptRejectFileFromFamilyMembers({int? id, int? status}) async { + NavigationService navigationService = getIt.get(); + LoaderBottomSheet.showLoader(); + final result = await medicalFileRepo.acceptRejectFamilyFile(id: id, status: status); + result.fold( + (failure) async => await errorHandlerService.handleError(failure: failure), + (apiResponse) { + if (apiResponse.messageStatus == 2) { + LoaderBottomSheet.hideLoader(); + _dialogService.showErrorBottomSheet( + message: apiResponse.errorMessage!, + onOkPressed: () { + navigationService.pop(); + }); + } else if (apiResponse.messageStatus == 1) { + // FamilyFileResponseModelLists moveProfile = pendingFamilyFiles.firstWhere((element) => element.id == patientID); + // moveProfile.status = 3; + // moveProfile.statusDescription = "Approved"; + // patientFamilyFiles.add(moveProfile); + pendingFamilyFiles.removeWhere((element) => element.id == id); + //TODO: Call Api Here To Load Family Members + getFamilyFiles(status: 0); + getAllPendingRecordsByResponseId(); + LoaderBottomSheet.hideLoader(); + onFamilyFileTabChange(0); + } + }, + ); } } diff --git a/lib/features/medical_file/models/family_file_response_model.dart b/lib/features/medical_file/models/family_file_response_model.dart index 82fe3d8..f4aa557 100644 --- a/lib/features/medical_file/models/family_file_response_model.dart +++ b/lib/features/medical_file/models/family_file_response_model.dart @@ -23,83 +23,90 @@ class FamilyFileResponseModelLists { String? patientIdenficationNumber; String? patientName; String? statusDescription; + bool? isSuperUser = false; + bool? isRequestFromMySide; - FamilyFileResponseModelLists({ - this.id, - this.patientId, - this.responseId, - this.relationshipId, - this.relationship, - this.relationshipN, - this.regionId, - this.familyRegionId, - this.status, - this.isActive, - this.editedOn, - this.createdOn, - this.age, - this.emaiLAddress, - this.gender, - this.genderDescription, - this.genderImage, - this.mobileNumber, - this.patientDataVerified, - this.patientIdenficationNumber, - this.patientName, - this.statusDescription, - }); + FamilyFileResponseModelLists( + {this.id, + this.patientId, + this.responseId, + this.relationshipId, + this.relationship, + this.relationshipN, + this.regionId, + this.familyRegionId, + this.status, + this.isActive, + this.editedOn, + this.createdOn, + this.age, + this.emaiLAddress, + this.gender, + this.genderDescription, + this.genderImage, + this.mobileNumber, + this.patientDataVerified, + this.patientIdenficationNumber, + this.patientName, + this.statusDescription, + this.isSuperUser, + this.isRequestFromMySide}); factory FamilyFileResponseModelLists.fromRawJson(String str) => FamilyFileResponseModelLists.fromJson(json.decode(str)); String toRawJson() => json.encode(toJson()); factory FamilyFileResponseModelLists.fromJson(Map json) => FamilyFileResponseModelLists( - id: json["ID"], - patientId: json["PatientID"], - responseId: json["ResponseID"], - relationshipId: json["RelationshipID"], - relationship: json["Relationship"], - relationshipN: json["RelationshipN"], - regionId: json["RegionID"], - familyRegionId: json["FamilyRegionID"], - status: json["Status"], - isActive: json["IsActive"], - editedOn: json["EditedOn"], - createdOn: json["CreatedOn"], - age: json["Age"], - emaiLAddress: json["EmaiLAddress"], - gender: json["Gender"], - genderDescription: json["GenderDescription"], - genderImage: json["GenderImage"], - mobileNumber: json["MobileNumber"], - patientDataVerified: json["PatientDataVerified"], - patientIdenficationNumber: json["PatientIdenficationNumber"], - patientName: json["PatientName"], - statusDescription: json["StatusDescription"], - ); + id: json["ID"], + patientId: json["PatientID"], + responseId: json["ResponseID"], + relationshipId: json["RelationshipID"], + relationship: json["Relationship"], + relationshipN: json["RelationshipN"], + regionId: json["RegionID"], + familyRegionId: json["FamilyRegionID"], + status: json["Status"], + isActive: json["IsActive"], + editedOn: json["EditedOn"], + createdOn: json["CreatedOn"], + age: json["Age"], + emaiLAddress: json["EmaiLAddress"], + gender: json["Gender"], + genderDescription: json["GenderDescription"], + genderImage: json["GenderImage"], + mobileNumber: json["MobileNumber"], + patientDataVerified: json["PatientDataVerified"], + patientIdenficationNumber: json["PatientIdenficationNumber"], + patientName: json["PatientName"], + statusDescription: json["StatusDescription"], + isSuperUser: json["isSuperUser"] ?? false, + isRequestFromMySide: json["isRequestFromMySide"] ?? false, + ); Map toJson() => { - "ID": id, - "PatientID": patientId, - "ResponseID": responseId, - "RelationshipID": relationshipId, - "Relationship": relationship, - "RelationshipN": relationshipN, - "RegionID": regionId, - "FamilyRegionID": familyRegionId, - "Status": status, - "IsActive": isActive, - "EditedOn": editedOn, - "CreatedOn": createdOn, - "Age": age, - "EmaiLAddress": emaiLAddress, - "Gender": gender, - "GenderDescription": genderDescription, - "GenderImage": genderImage, - "MobileNumber": mobileNumber, - "PatientDataVerified": patientDataVerified, - "PatientIdenficationNumber": patientIdenficationNumber, - "PatientName": patientName, - "StatusDescription": statusDescription, - }; + "ID": id, + "PatientID": patientId, + "ResponseID": responseId, + "RelationshipID": relationshipId, + "Relationship": relationship, + "RelationshipN": relationshipN, + "RegionID": regionId, + "FamilyRegionID": familyRegionId, + "Status": status, + "IsActive": isActive, + "EditedOn": editedOn, + "CreatedOn": createdOn, + "Age": age, + "EmaiLAddress": emaiLAddress, + "Gender": gender, + "GenderDescription": genderDescription, + "GenderImage": genderImage, + "MobileNumber": mobileNumber, + "PatientDataVerified": patientDataVerified, + "PatientIdenficationNumber": patientIdenficationNumber, + "PatientName": patientName, + "StatusDescription": statusDescription, + "isSuperUser": isSuperUser, + "isRequestFromMySide": isRequestFromMySide, + }; } diff --git a/lib/features/my_appointments/appointment_via_region_viewmodel.dart b/lib/features/my_appointments/appointment_via_region_viewmodel.dart index 9ffb544..3e3c3cd 100644 --- a/lib/features/my_appointments/appointment_via_region_viewmodel.dart +++ b/lib/features/my_appointments/appointment_via_region_viewmodel.dart @@ -1,6 +1,8 @@ import 'package:flutter/foundation.dart' show ChangeNotifier; import 'package:hmg_patient_app_new/core/app_state.dart' show AppState; import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/doctor_list_api_response.dart'; +import 'package:hmg_patient_app_new/presentation/book_appointment/dental_chief_complaints_page.dart'; +import 'package:hmg_patient_app_new/presentation/book_appointment/laser/laser_appointment.dart'; import 'package:hmg_patient_app_new/presentation/book_appointment/select_clinic_page.dart'; import 'package:hmg_patient_app_new/presentation/book_appointment/select_doctor_page.dart'; import 'package:hmg_patient_app_new/services/navigation_service.dart'; @@ -96,9 +98,30 @@ class AppointmentViaRegionViewmodel extends ChangeNotifier { ),); } - void handleLastStepForClinicForDentalAndLaser() { - //todo handle the routing here + void handleLastStepForClinicForDentalAndLaser(int clinicId) { navigationService.pop(); + + if (clinicId == 17) { + if (!appState.isAuthenticated) { + navigationService.push( + CustomPageRoute( + page: DentalChiefComplaintsPage(), + ), + ); + } + } + + if (clinicId == 253) { + { + + + navigationService.push( + CustomPageRoute( + page: LaserAppointment(), + ), + ); + } + } } void handleLastStepForDentalAndLaser() { diff --git a/lib/features/my_appointments/my_appointments_view_model.dart b/lib/features/my_appointments/my_appointments_view_model.dart index 916327c..60fbff2 100644 --- a/lib/features/my_appointments/my_appointments_view_model.dart +++ b/lib/features/my_appointments/my_appointments_view_model.dart @@ -50,6 +50,8 @@ class MyAppointmentsViewModel extends ChangeNotifier { void onTabChange(int index) { previouslySelectedTab = selectedTabIndex; selectedTabIndex = index; + start = null; + end = null; notifyListeners(); } @@ -90,6 +92,9 @@ class MyAppointmentsViewModel extends ChangeNotifier { } setIsAppointmentDataToBeLoaded(bool val) { + if (val) { + isMyAppointmentsLoading = true; + } isAppointmentDataToBeLoaded = val; notifyListeners(); } @@ -110,9 +115,11 @@ class MyAppointmentsViewModel extends ChangeNotifier { Future getPatientAppointments(bool isActiveAppointment, bool isArrivedAppointments, {Function(dynamic)? onSuccess, Function(String)? onError}) async { if (!isAppointmentDataToBeLoaded) return; + filteredAppointmentList.clear(); patientAppointmentsHistoryList.clear(); patientUpcomingAppointmentsHistoryList.clear(); patientArrivedAppointmentsHistoryList.clear(); + notifyListeners(); final result = await myAppointmentsRepo.getPatientAppointments(isActiveAppointment: isActiveAppointment, isArrivedAppointments: isArrivedAppointments); final resultArrived = await myAppointmentsRepo.getPatientAppointments(isActiveAppointment: false, isArrivedAppointments: true); @@ -153,6 +160,7 @@ class MyAppointmentsViewModel extends ChangeNotifier { patientAppointmentsHistoryList.addAll(patientUpcomingAppointmentsHistoryList); patientAppointmentsHistoryList.addAll(patientArrivedAppointmentsHistoryList); filteredAppointmentList.addAll(patientAppointmentsHistoryList); + print('Upcoming Appointments: ${patientUpcomingAppointmentsHistoryList.length}'); print('Arrived Appointments: ${patientArrivedAppointmentsHistoryList.length}'); print('All Appointments: ${patientAppointmentsHistoryList.length}'); @@ -451,12 +459,12 @@ class MyAppointmentsViewModel extends ChangeNotifier { filteredAppointmentList.add(element); } }); - filteredAppointmentList.addAll(sourceList); } else { filteredAppointmentList.clear(); sourceList.forEach((element) { try { - var dateTime = DateUtil.convertStringToDate(element.appointmentDate); + var dateTime = DateUtil.convertStringToDate(element.appointmentDate).provideDateOnly(); + if (start != null && end == null) { if (dateTime.isAtSameMomentAs(start)) { if (isUnderFilter(element)) { @@ -464,7 +472,7 @@ class MyAppointmentsViewModel extends ChangeNotifier { } } } else if (start != null && end != null) { - if ((dateTime.isAfter(start)) && (dateTime.isBefore(end))) { + if ((dateTime.isAfter(start)) && ((dateTime.isBefore(end))||((dateTime.isAtSameMomentAs(end))))) { if (isUnderFilter(element)) { filteredAppointmentList.add(element); } diff --git a/lib/features/payfort/payfort_repo.dart b/lib/features/payfort/payfort_repo.dart index 9f836d1..74a323a 100644 --- a/lib/features/payfort/payfort_repo.dart +++ b/lib/features/payfort/payfort_repo.dart @@ -18,6 +18,13 @@ abstract class PayfortRepo { Future>> generateSdkSignatureFromAPI({required SdkTokenRequest tokenRequest}); Future>> checkPaymentStatus({required String transactionID}); + + Future>> checkTamaraPaymentStatus({required String transactionID}); + + Future>> markAppointmentAsTamaraPaid({required int projectID, required int appointmentNo}); + + Future>> updateTamaraRequestStatus( + {required String responseMessage, required String status, required String clientRequestID, required String tamaraOrderID}); } class PayfortRepoImp implements PayfortRepo { @@ -147,4 +154,100 @@ class PayfortRepoImp implements PayfortRepo { return Left(UnknownFailure(e.toString())); } } + + @override + Future> checkTamaraPaymentStatus({required String transactionID}) async { + try { + GenericApiModel? apiResponse; + Failure? failure; + await apiClient.get( + '${ApiConsts.GET_TAMARA_PAYMENT_STATUS}$transactionID', + isExternal: true, + isAllowAny: true, + onFailure: (error, statusCode, {messageStatus, failureType}) { + failure = failureType; + }, + onSuccess: (response, statusCode, {messageStatus, errorMessage}) { + try { + apiResponse = GenericApiModel( + messageStatus: messageStatus, + statusCode: statusCode, + errorMessage: null, + data: response, + ); + } catch (e) { + failure = DataParsingFailure(e.toString()); + } + }, + ); + 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> updateTamaraRequestStatus({required String responseMessage, required String status, required String clientRequestID, required String tamaraOrderID}) async { + Map body = { + "Response_Message": responseMessage, + "ClientRequestID": clientRequestID, + "Status": status, + "FortID": tamaraOrderID, // Tamara order ID + "LanguageID": 1, + "Installments_Number": 3, + }; + try { + GenericApiModel? apiResponse; + Failure? failure; + await apiClient.post(UPDATE_TAMARA_STATUS, body: body, onFailure: (error, statusCode, {messageStatus, failureType}) { + failure = failureType; + }, onSuccess: (response, statusCode, {messageStatus, errorMessage}) { + try { + apiResponse = GenericApiModel( + messageStatus: messageStatus, + statusCode: statusCode, + errorMessage: null, + data: response, + ); + } catch (e) { + failure = DataParsingFailure(e.toString()); + } + }, isAllowAny: true, isPaymentServices: true); + 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> markAppointmentAsTamaraPaid({required int projectID, required int appointmentNo}) async { + Map body = {"ProjectID": projectID, "AppointmentNo": appointmentNo, "LanguageID": 1}; + try { + GenericApiModel? apiResponse; + Failure? failure; + await apiClient.post(MARK_APPOINTMENT_TAMARA_STATUS, body: body, onFailure: (error, statusCode, {messageStatus, failureType}) { + failure = failureType; + }, onSuccess: (response, statusCode, {messageStatus, errorMessage}) { + try { + apiResponse = GenericApiModel( + messageStatus: messageStatus, + statusCode: statusCode, + errorMessage: null, + data: response, + ); + } catch (e) { + failure = DataParsingFailure(e.toString()); + } + }, isAllowAny: true, isPaymentServices: true); + if (failure != null) return Left(failure!); + if (apiResponse == null) return Left(ServerFailure("Unknown error")); + return Right(apiResponse!); + } catch (e) { + return Left(UnknownFailure(e.toString())); + } + } } diff --git a/lib/features/payfort/payfort_view_model.dart b/lib/features/payfort/payfort_view_model.dart index 6b67ce9..89effcd 100644 --- a/lib/features/payfort/payfort_view_model.dart +++ b/lib/features/payfort/payfort_view_model.dart @@ -94,6 +94,40 @@ class PayfortViewModel extends ChangeNotifier { ); } + Future checkTamaraPaymentStatus({required String transactionID, Function(dynamic)? onSuccess, Function(String)? onError}) async { + final result = await payfortRepo.checkTamaraPaymentStatus(transactionID: transactionID); + + result.fold( + (failure) async { + onError!(failure.message); + }, + (apiResponse) { + print(apiResponse.data); + if (onSuccess != null) { + onSuccess(apiResponse); + } + // } + }, + ); + } + + Future updateTamaraRequestStatus( + {required String responseMessage, required String status, required String clientRequestID, required String tamaraOrderID, Function(dynamic)? onSuccess, Function(String)? onError}) async { + final result = await payfortRepo.updateTamaraRequestStatus(responseMessage: responseMessage, status: status, clientRequestID: clientRequestID, tamaraOrderID: tamaraOrderID); + + result.fold( + (failure) async { + onError!(failure.message); + }, + (apiResponse) { + print(apiResponse.data); + if (onSuccess != null) { + onSuccess(apiResponse); + } + }, + ); + } + Future _generateSdkResponse({ String? applePayAccessCode, String? merchantIdentifier, @@ -199,4 +233,20 @@ class PayfortViewModel extends ChangeNotifier { onFailed!(e.toString() as PayFortFailureResult); } } + + Future markAppointmentAsTamaraPaid({required int projectID, required int appointmentNo, Function(dynamic)? onSuccess, Function(String)? onError}) async { + final result = await payfortRepo.markAppointmentAsTamaraPaid(projectID: projectID, appointmentNo: appointmentNo); + + result.fold( + (failure) async { + onError!(failure.message); + }, + (apiResponse) { + print(apiResponse.data); + if (onSuccess != null) { + onSuccess(apiResponse); + } + }, + ); + } } diff --git a/lib/generated/locale_keys.g.dart b/lib/generated/locale_keys.g.dart index d14a021..798f94d 100644 --- a/lib/generated/locale_keys.g.dart +++ b/lib/generated/locale_keys.g.dart @@ -189,6 +189,7 @@ abstract class LocaleKeys { static const firstName = 'firstName'; static const middleName = 'middleName'; static const lastName = 'lastName'; + static const female = 'female'; static const preferredLanguage = 'preferredLanguage'; static const locationsRegister = 'locationsRegister'; static const ksa = 'ksa'; @@ -863,7 +864,6 @@ abstract class LocaleKeys { static const clearAllFilters = 'clearAllFilters'; static const filters = 'filters'; static const searchClinic = 'searchClinic'; - static const walkin = 'walkin'; static const normal = 'normal'; static const attention = 'attention'; static const monitor = 'monitor'; @@ -873,5 +873,8 @@ abstract class LocaleKeys { static const to = 'to'; static const startDate = 'startDate'; static const endDate = 'endDate'; + static const walkin = 'walkin'; + static const laserClinic = 'laserClinic'; + static const continueString = 'continueString'; } diff --git a/lib/main.dart b/lib/main.dart index ac11f8f..20507d0 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -79,94 +79,53 @@ void main() async { fallbackLocale: Locale('en', 'US'), child: MultiProvider(providers: [ ChangeNotifierProvider( - create: (_) => LabViewModel( - labRepo: getIt(), - errorHandlerService: getIt(), - navigationService: getIt()), + create: (_) => getIt.get(), ), ChangeNotifierProvider( - create: (_) => RadiologyViewModel( - radiologyRepo: getIt(), - errorHandlerService: getIt(), - ), + create: (_) => getIt.get(), ), ChangeNotifierProvider( - create: (_) => PrescriptionsViewModel( - prescriptionsRepo: getIt(), - errorHandlerService: getIt(), - ), + create: (_) => getIt.get(), ), ChangeNotifierProvider( - create: (_) => InsuranceViewModel( - insuranceRepo: getIt(), - errorHandlerService: getIt(), - ), + create: (_) => getIt.get(), ), ChangeNotifierProvider( - create: (_) => MedicalFileViewModel( - medicalFileRepo: getIt(), - errorHandlerService: getIt(), - ), + create: (_) => getIt.get(), ), ChangeNotifierProvider( - create: (_) => ProfileSettingsViewModel(), + create: (_) => getIt.get(), ), ChangeNotifierProvider( - create: (_) => MyAppointmentsViewModel( - myAppointmentsRepo: getIt(), - errorHandlerService: getIt(), - appState: getIt(), - ), + create: (_) => getIt.get(), ), ChangeNotifierProvider( - create: (_) => PayfortViewModel( - payfortRepo: getIt(), - errorHandlerService: getIt(), - ), + create: (_) => getIt.get(), ), ChangeNotifierProvider( - create: (_) => HabibWalletViewModel( - habibWalletRepo: getIt(), - errorHandlerService: getIt(), - ), + create: (_) => getIt.get(), ), ChangeNotifierProvider( - create: (_) => BookAppointmentsViewModel( - bookAppointmentsRepo: getIt(), - errorHandlerService: getIt(), - navigationService: getIt(), - myAppointmentsViewModel: getIt(), - locationUtils: getIt(), - ), + create: (_) => getIt.get(), ), ChangeNotifierProvider( - create: (_) => ImmediateLiveCareViewModel( - immediateLiveCareRepo: getIt(), - errorHandlerService: getIt(), - navigationService: getIt(), - myAppointmentsViewModel: getIt(), - ), + create: (_) => getIt.get(), ), ChangeNotifierProvider( - create: (_) => AuthenticationViewModel( - authenticationRepo: getIt(), - appState: getIt(), - dialogService: getIt(), - errorHandlerService: getIt(), - navigationService: getIt(), - cacheService: getIt(), - localAuthService: getIt(), - ), + create: (_) => getIt.get(), ), ChangeNotifierProvider( - create: (_) => AppointmentViaRegionViewmodel( - navigationService: getIt(), appState: getIt())), + create: (_) => getIt.get(), + ), ChangeNotifierProvider( - create: (_) => LabHistoryViewModel()), + create: (_) => getIt.get(), + ), ChangeNotifierProvider( - create: (_) => DateRangeSelectorRangeViewModel()) , + create: (_) => getIt.get(), + ), ChangeNotifierProvider( - create: (_) => DoctorFilterViewModel()) + create: (_) => getIt.get(), + ) ], child: MyApp()), ), ); diff --git a/lib/presentation/appointments/appointment_details_page.dart b/lib/presentation/appointments/appointment_details_page.dart index 54353a4..c87039c 100644 --- a/lib/presentation/appointments/appointment_details_page.dart +++ b/lib/presentation/appointments/appointment_details_page.dart @@ -313,7 +313,7 @@ class _AppointmentDetailsPageState extends State { ), SizedBox(width: 68.h), Transform.flip( - flipX: appState.isArabic() ? true : false, + flipX: appState.isArabic(), child: Utils.buildSvgWithAssets( icon: AppAssets.forward_arrow_icon, iconColor: AppColors.blackColor, diff --git a/lib/presentation/appointments/appointment_payment_page.dart b/lib/presentation/appointments/appointment_payment_page.dart index ee50ab4..467a480 100644 --- a/lib/presentation/appointments/appointment_payment_page.dart +++ b/lib/presentation/appointments/appointment_payment_page.dart @@ -52,6 +52,8 @@ class _AppointmentPaymentPageState extends State { String transID = ""; bool isShowTamara = false; + String tamaraPaymentStatus = ""; + String tamaraOrderID = ""; @override void initState() { @@ -115,9 +117,9 @@ class _AppointmentPaymentPageState extends State { SizedBox(width: 8.h), const Spacer(), Transform.flip( - flipX: appState.isArabic() ? true : false, + flipX: appState.isArabic(), child: Utils.buildSvgWithAssets( - icon: AppAssets.forward_arrow_icon, + icon: AppAssets.forward_arrow_icon_small, iconColor: AppColors.blackColor, width: 18.h, height: 13.h, @@ -157,9 +159,9 @@ class _AppointmentPaymentPageState extends State { SizedBox(width: 8.h), const Spacer(), Transform.flip( - flipX: appState.isArabic() ? true : false, + flipX: appState.isArabic(), child: Utils.buildSvgWithAssets( - icon: AppAssets.forward_arrow_icon, + icon: AppAssets.forward_arrow_icon_small, iconColor: AppColors.blackColor, width: 18.h, height: 13.h, @@ -194,20 +196,20 @@ class _AppointmentPaymentPageState extends State { SizedBox(width: 8.h), const Spacer(), Transform.flip( - flipX: appState.isArabic() ? true : false, + flipX: appState.isArabic(), child: Utils.buildSvgWithAssets( - icon: AppAssets.forward_arrow_icon, - iconColor: AppColors.blackColor, - width: 18.h, - height: 13.h, - fit: BoxFit.contain, - ).toShimmer2(isShow: myAppointmentsVM.isAppointmentPatientShareLoading), - ), - ], - ).paddingSymmetrical(16.h, 16.h), - ).paddingSymmetrical(24.h, 0.h).onPress(() { - selectedPaymentMethod = "TAMARA"; - openPaymentURL("tamara"); + icon: AppAssets.forward_arrow_icon_small, + iconColor: AppColors.blackColor, + width: 18.h, + height: 13.h, + fit: BoxFit.contain, + ).toShimmer2(isShow: myAppointmentsVM.isAppointmentPatientShareLoading), + ), + ], + ).paddingSymmetrical(16.h, 16.h), + ).paddingSymmetrical(24.h, 0.h).onPress(() { + selectedPaymentMethod = "TAMARA"; + openPaymentURL("tamara"); }) : SizedBox.shrink(), ], @@ -328,12 +330,12 @@ class _AppointmentPaymentPageState extends State { if (selectedPaymentMethod == "tamara") { if (Platform.isAndroid) { Uri uri = new Uri.dataFromString(url); - // tamaraPaymentStatus = uri.queryParameters['status']!; - // tamaraOrderID = uri.queryParameters['AuthorizePaymentId']!; + tamaraPaymentStatus = uri.queryParameters['status']!; + tamaraOrderID = uri.queryParameters['AuthorizePaymentId']!; } else { Uri uri = new Uri.dataFromString(url); - // tamaraPaymentStatus = uri.queryParameters['paymentStatus']!; - // tamaraOrderID = uri.queryParameters['orderId']!; + tamaraPaymentStatus = uri.queryParameters['paymentStatus']!; + tamaraOrderID = uri.queryParameters['orderId']!; } } @@ -359,95 +361,155 @@ class _AppointmentPaymentPageState extends State { } 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); - } + checkPaymentStatus(); } void checkPaymentStatus() async { - LoaderBottomSheet.showLoader(); - await payfortViewModel.checkPaymentStatus( - transactionID: transID, - onSuccess: (apiResponse) async { - print(apiResponse.data); - if (payfortViewModel.payfortCheckPaymentStatusResponseModel!.responseMessage!.toLowerCase() == "success") { - await myAppointmentsViewModel.createAdvancePayment( - paymentMethodName: selectedPaymentMethod, - projectID: widget.patientAppointmentHistoryResponseModel.projectID, - clinicID: widget.patientAppointmentHistoryResponseModel.clinicID, - appointmentNo: widget.patientAppointmentHistoryResponseModel.appointmentNo.toString(), - payedAmount: payfortViewModel.payfortCheckPaymentStatusResponseModel!.amount!, - paymentReference: payfortViewModel.payfortCheckPaymentStatusResponseModel!.fortId!, - patientID: appState.getAuthenticatedUser()!.patientId.toString(), - patientType: appState.getAuthenticatedUser()!.patientType!, - onSuccess: (value) async { - print(value); - await myAppointmentsViewModel.addAdvanceNumberRequest( - advanceNumber: Utils.isVidaPlusProject(widget.patientAppointmentHistoryResponseModel.projectID) - ? value.data['OnlineCheckInAppointments'][0]['AdvanceNumber_VP'].toString() - : value.data['OnlineCheckInAppointments'][0]['AdvanceNumber'].toString(), - paymentReference: payfortViewModel.payfortCheckPaymentStatusResponseModel!.fortId!, - appointmentNo: widget.patientAppointmentHistoryResponseModel.appointmentNo.toString(), - onSuccess: (value) async { - if (widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment!) { - //TODO: Implement LiveCare Check-In API Call - await myAppointmentsViewModel.insertLiveCareVIDARequest( - clientRequestID: transID, - patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel, - onSuccess: (apiResponse) { - Future.delayed(Duration(milliseconds: 500), () { - LoaderBottomSheet.hideLoader(); - Navigator.pushAndRemoveUntil( - context, - CustomPageRoute( - page: LandingNavigation(), - ), - (r) => false); - }); - }, - onError: (error) {}); - } else { - await myAppointmentsViewModel.generateAppointmentQR( - clinicID: widget.patientAppointmentHistoryResponseModel.clinicID, - projectID: widget.patientAppointmentHistoryResponseModel.projectID, - appointmentNo: widget.patientAppointmentHistoryResponseModel.appointmentNo.toString(), - isFollowUp: myAppointmentsViewModel.patientAppointmentShareResponseModel!.isFollowup!, - onSuccess: (apiResponse) { - Future.delayed(Duration(milliseconds: 500), () { - LoaderBottomSheet.hideLoader(); - Navigator.pushAndRemoveUntil( - context, - CustomPageRoute( - page: LandingNavigation(), - ), - (r) => false); - // Navigator.of(context).push( - // CustomPageRoute(page: MyAppointmentsPage()), - // ); - }); - }); - } - }); - }); - } else { + LoaderBottomSheet.showLoader(loadingText: "Checking payment status, Please wait...".needTranslation); + if (selectedPaymentMethod == "TAMARA") { + await payfortViewModel.checkTamaraPaymentStatus( + transactionID: transID, + onSuccess: (apiResponse) async { + if (apiResponse.data["status"].toString().toLowerCase() == "success") { + tamaraOrderID = apiResponse.data["tamara_order_id"].toString(); + await payfortViewModel.updateTamaraRequestStatus(responseMessage: "success", status: "14", clientRequestID: transID, tamaraOrderID: tamaraOrderID); + await payfortViewModel.markAppointmentAsTamaraPaid( + projectID: widget.patientAppointmentHistoryResponseModel.projectID, appointmentNo: widget.patientAppointmentHistoryResponseModel.appointmentNo); + await myAppointmentsViewModel.addAdvanceNumberRequest( + advanceNumber: "Tamara-Advance-0000", + paymentReference: tamaraOrderID, + appointmentNo: widget.patientAppointmentHistoryResponseModel.appointmentNo.toString(), + onSuccess: (value) async { + if (widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment!) { + //TODO: Implement LiveCare Check-In API Call + await myAppointmentsViewModel.insertLiveCareVIDARequest( + clientRequestID: tamaraOrderID, + patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel, + onSuccess: (apiResponse) { + Future.delayed(Duration(milliseconds: 500), () { + LoaderBottomSheet.hideLoader(); + Navigator.pushAndRemoveUntil( + context, + CustomPageRoute( + page: LandingNavigation(), + ), + (r) => false); + }); + }, + onError: (error) {}); + } else { + await myAppointmentsViewModel.generateAppointmentQR( + clinicID: widget.patientAppointmentHistoryResponseModel.clinicID, + projectID: widget.patientAppointmentHistoryResponseModel.projectID, + appointmentNo: widget.patientAppointmentHistoryResponseModel.appointmentNo.toString(), + isFollowUp: myAppointmentsViewModel.patientAppointmentShareResponseModel!.isFollowup!, + onSuccess: (apiResponse) { + Future.delayed(Duration(milliseconds: 500), () { + LoaderBottomSheet.hideLoader(); + Navigator.pushAndRemoveUntil( + context, + CustomPageRoute( + page: LandingNavigation(), + ), + (r) => false); + }); + }); + } + }); + } else { + await payfortViewModel.updateTamaraRequestStatus(responseMessage: "Failed", status: "00", clientRequestID: transID, tamaraOrderID: tamaraOrderID); + LoaderBottomSheet.hideLoader(); + showCommonBottomSheetWithoutHeight( + context, + child: Utils.getErrorWidget(loadingText: "Payment Failed! Please try again.".needTranslation), + callBackFunc: () {}, + isFullScreen: false, + isCloseButtonVisible: true, + ); + } + }, + onError: (err) { + LoaderBottomSheet.hideLoader(); showCommonBottomSheetWithoutHeight( context, - child: Utils.getErrorWidget(loadingText: "Payment Failed! Please try again.".needTranslation), + child: Utils.getErrorWidget(loadingText: err), callBackFunc: () {}, isFullScreen: false, isCloseButtonVisible: true, ); - } - }); + }); + } else { + await payfortViewModel.checkPaymentStatus( + transactionID: transID, + onSuccess: (apiResponse) async { + print(apiResponse.data); + if (payfortViewModel.payfortCheckPaymentStatusResponseModel!.responseMessage!.toLowerCase() == "success") { + await myAppointmentsViewModel.createAdvancePayment( + paymentMethodName: selectedPaymentMethod, + projectID: widget.patientAppointmentHistoryResponseModel.projectID, + clinicID: widget.patientAppointmentHistoryResponseModel.clinicID, + appointmentNo: widget.patientAppointmentHistoryResponseModel.appointmentNo.toString(), + payedAmount: payfortViewModel.payfortCheckPaymentStatusResponseModel!.amount!, + paymentReference: payfortViewModel.payfortCheckPaymentStatusResponseModel!.fortId!, + patientID: appState.getAuthenticatedUser()!.patientId.toString(), + patientType: appState.getAuthenticatedUser()!.patientType!, + onSuccess: (value) async { + print(value); + await myAppointmentsViewModel.addAdvanceNumberRequest( + advanceNumber: Utils.isVidaPlusProject(widget.patientAppointmentHistoryResponseModel.projectID) + ? value.data['OnlineCheckInAppointments'][0]['AdvanceNumber_VP'].toString() + : value.data['OnlineCheckInAppointments'][0]['AdvanceNumber'].toString(), + paymentReference: payfortViewModel.payfortCheckPaymentStatusResponseModel!.fortId!, + appointmentNo: widget.patientAppointmentHistoryResponseModel.appointmentNo.toString(), + onSuccess: (value) async { + if (widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment!) { + //TODO: Implement LiveCare Check-In API Call + await myAppointmentsViewModel.insertLiveCareVIDARequest( + clientRequestID: transID, + patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel, + onSuccess: (apiResponse) { + Future.delayed(Duration(milliseconds: 500), () { + LoaderBottomSheet.hideLoader(); + Navigator.pushAndRemoveUntil( + context, + CustomPageRoute( + page: LandingNavigation(), + ), + (r) => false); + }); + }, + onError: (error) {}); + } else { + await myAppointmentsViewModel.generateAppointmentQR( + clinicID: widget.patientAppointmentHistoryResponseModel.clinicID, + projectID: widget.patientAppointmentHistoryResponseModel.projectID, + appointmentNo: widget.patientAppointmentHistoryResponseModel.appointmentNo.toString(), + isFollowUp: myAppointmentsViewModel.patientAppointmentShareResponseModel!.isFollowup!, + onSuccess: (apiResponse) { + Future.delayed(Duration(milliseconds: 500), () { + LoaderBottomSheet.hideLoader(); + Navigator.pushAndRemoveUntil( + context, + CustomPageRoute( + page: LandingNavigation(), + ), + (r) => false); + }); + }); + } + }); + }); + } else { + showCommonBottomSheetWithoutHeight( + context, + child: Utils.getErrorWidget(loadingText: "Payment Failed! Please try again.".needTranslation), + callBackFunc: () {}, + isFullScreen: false, + isCloseButtonVisible: true, + ); + } + }); + } } openPaymentURL(String paymentMethod) { diff --git a/lib/presentation/appointments/my_appointments_page.dart b/lib/presentation/appointments/my_appointments_page.dart index c939234..1305ea0 100644 --- a/lib/presentation/appointments/my_appointments_page.dart +++ b/lib/presentation/appointments/my_appointments_page.dart @@ -8,6 +8,7 @@ import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; import 'package:hmg_patient_app_new/core/utils/utils.dart'; import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; +import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_view_model.dart'; import 'package:hmg_patient_app_new/features/my_appointments/models/appointemnet_filters.dart'; import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/patient_appointment_history_response_model.dart'; import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart'; @@ -37,6 +38,7 @@ class MyAppointmentsPage extends StatefulWidget { class _MyAppointmentsPageState extends State { late MyAppointmentsViewModel myAppointmentsViewModel; + late BookAppointmentsViewModel bookAppointmentsViewModel; @override void initState() { @@ -50,6 +52,7 @@ class _MyAppointmentsPageState extends State { @override Widget build(BuildContext context) { myAppointmentsViewModel = Provider.of(context, listen: false); + bookAppointmentsViewModel = Provider.of(context, listen: false); return Scaffold( backgroundColor: AppColors.bgScaffoldColor, body: CollapsingListView( @@ -92,7 +95,6 @@ class _MyAppointmentsPageState extends State { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Visibility( visible: myAppointmentsVM.availableFilters.isNotEmpty, child: getAppointmentFilters(myAppointmentsVM)), @@ -117,6 +119,7 @@ class _MyAppointmentsPageState extends State { patientAppointmentHistoryResponseModel: PatientAppointmentHistoryResponseModel(), myAppointmentsViewModel: myAppointmentsViewModel, + bookAppointmentsViewModel: bookAppointmentsViewModel, isLoading: true, isFromHomePage: false, ), @@ -141,6 +144,7 @@ class _MyAppointmentsPageState extends State { filteredAppointmentList[index], myAppointmentsViewModel: myAppointmentsViewModel, + bookAppointmentsViewModel: bookAppointmentsViewModel, isLoading: false, isFromHomePage: false, ), @@ -177,6 +181,7 @@ class _MyAppointmentsPageState extends State { separatorBuilder: (BuildContext cxt, int index) => SizedBox(height: 16.h), ), + SizedBox(height: 24.h), ], ); } diff --git a/lib/presentation/appointments/my_doctors_page.dart b/lib/presentation/appointments/my_doctors_page.dart index 2fedfd8..06257c7 100644 --- a/lib/presentation/appointments/my_doctors_page.dart +++ b/lib/presentation/appointments/my_doctors_page.dart @@ -61,7 +61,7 @@ class MyDoctorsPage extends StatelessWidget { "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png", width: 63.h, height: 63.h, - fit: BoxFit.fill, + fit: BoxFit.cover, ).circle(100).toShimmer2(isShow: true), SizedBox(width: 16.h), Expanded( @@ -112,7 +112,7 @@ class MyDoctorsPage extends StatelessWidget { myAppointmentsVM.patientMyDoctorsList[index].doctorImageURL!, width: 63.h, height: 63.h, - fit: BoxFit.fill, + fit: BoxFit.cover, ).circle(100).toShimmer2(isShow: false), SizedBox(width: 16.h), Expanded( @@ -140,7 +140,7 @@ class MyDoctorsPage extends StatelessWidget { children: [ "".toText16(), Transform.flip( - flipX: appState.isArabic() ? true : false, + flipX: appState.isArabic(), child: Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_icon, width: 15.h, height: 15.h, fit: BoxFit.contain, iconColor: AppColors.textColor)), ], ), diff --git a/lib/presentation/appointments/widgets/AppointmentFilter.dart b/lib/presentation/appointments/widgets/AppointmentFilter.dart index fd2a9fe..98da22d 100644 --- a/lib/presentation/appointments/widgets/AppointmentFilter.dart +++ b/lib/presentation/appointments/widgets/AppointmentFilter.dart @@ -36,8 +36,8 @@ class AppointmentFilters extends StatelessWidget { padding: EdgeInsets.symmetric(vertical: 12.h, horizontal: 8.h), deleteIconSize: Size(18.h, 18.h), shape: SmoothRectangleBorder( - borderRadius: BorderRadius.circular(10 ), - smoothness: 10, + borderRadius: BorderRadius.circular(10.h), + smoothness: 10.h, side: BorderSide( color: selectedFilter?.contains(item) == true ? AppColors.chipPrimaryRedBorderColor diff --git a/lib/presentation/appointments/widgets/appointment_card.dart b/lib/presentation/appointments/widgets/appointment_card.dart index 3a65124..aa67a76 100644 --- a/lib/presentation/appointments/widgets/appointment_card.dart +++ b/lib/presentation/appointments/widgets/appointment_card.dart @@ -8,15 +8,20 @@ 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/doctors_list_response_model.dart'; import 'package:hmg_patient_app_new/features/medical_file/medical_file_view_model.dart'; import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/patient_appointment_history_response_model.dart'; import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart'; import 'package:hmg_patient_app_new/features/my_appointments/utils/appointment_type.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/presentation/appointments/appointment_details_page.dart'; +import 'package:hmg_patient_app_new/presentation/book_appointment/widgets/appointment_calendar.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart'; +import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart'; +import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart'; import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart'; import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart'; import 'package:smooth_corner/smooth_corner.dart'; @@ -26,6 +31,7 @@ class AppointmentCard extends StatefulWidget { {super.key, required this.patientAppointmentHistoryResponseModel, required this.myAppointmentsViewModel, + required this.bookAppointmentsViewModel, this.isLoading = false, this.isFromHomePage = false, this.isFromMedicalReport = false, @@ -37,6 +43,7 @@ class AppointmentCard extends StatefulWidget { bool isFromHomePage; bool isFromMedicalReport; MedicalFileViewModel? medicalFileViewModel; + BookAppointmentsViewModel bookAppointmentsViewModel; @override State createState() => _AppointmentCardState(); @@ -140,7 +147,7 @@ class _AppointmentCardState extends State { widget.isLoading ? "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png" : widget.patientAppointmentHistoryResponseModel.doctorImageURL!, width: 63.h, height: 63.h, - fit: BoxFit.fill, + fit: BoxFit.cover, ).circle(100).toShimmer2(isShow: widget.isLoading), SizedBox(width: 16.h), Expanded( @@ -244,7 +251,7 @@ class _AppointmentCardState extends State { borderRadius: 10.h, ), child: Transform.flip( - flipX: appState.isArabic() ? true : false, + flipX: appState.isArabic(), child: Utils.buildSvgWithAssets( icon: AppAssets.forward_arrow_icon, iconColor: AppColors.whiteColor, @@ -293,7 +300,9 @@ class _AppointmentCardState extends State { ) : CustomButton( text: "Rebook with same doctor".needTranslation, - onPressed: () {}, + onPressed: () { + openDoctorScheduleCalendar(); + }, backgroundColor: AppColors.greyColor, borderColor: AppColors.greyColor, textColor: AppColors.blackColor, @@ -308,5 +317,45 @@ class _AppointmentCardState extends State { ); } + openDoctorScheduleCalendar() async { + DoctorsListResponseModel doctor = DoctorsListResponseModel( + clinicID: widget.patientAppointmentHistoryResponseModel.clinicID, + projectID: widget.patientAppointmentHistoryResponseModel.projectID, + doctorID: widget.patientAppointmentHistoryResponseModel.doctorID, + doctorImageURL: widget.patientAppointmentHistoryResponseModel.doctorImageURL, + doctorTitle: widget.patientAppointmentHistoryResponseModel.doctorTitle, + name: widget.patientAppointmentHistoryResponseModel.doctorNameObj, + nationalityFlagURL: "https://hmgwebservices.com/Images/flag/SYR.png", + speciality: [], + clinicName: widget.patientAppointmentHistoryResponseModel.clinicName, + projectName: widget.patientAppointmentHistoryResponseModel.projectName, + ); + widget.bookAppointmentsViewModel.setSelectedDoctor(doctor); + LoaderBottomSheet.showLoader(); + await widget.bookAppointmentsViewModel.getDoctorFreeSlots( + isBookingForLiveCare: false, + onSuccess: (dynamic respData) async { + LoaderBottomSheet.hideLoader(); + showCommonBottomSheetWithoutHeight( + title: "Pick a Date".needTranslation, + context, + child: AppointmentCalendar(), + isFullScreen: false, + isCloseButtonVisible: true, + callBackFunc: () {}, + ); + }, + onError: (err) { + LoaderBottomSheet.hideLoader(); + showCommonBottomSheetWithoutHeight( + context, + child: Utils.getErrorWidget(loadingText: err), + callBackFunc: () {}, + isFullScreen: false, + isCloseButtonVisible: true, + ); + }); + } + void performAppointmentNextAction(nextAction) {} } diff --git a/lib/presentation/appointments/widgets/appointment_checkin_bottom_sheet.dart b/lib/presentation/appointments/widgets/appointment_checkin_bottom_sheet.dart index e79f26f..1863875 100644 --- a/lib/presentation/appointments/widgets/appointment_checkin_bottom_sheet.dart +++ b/lib/presentation/appointments/widgets/appointment_checkin_bottom_sheet.dart @@ -122,9 +122,9 @@ class AppointmentCheckinBottomSheet extends StatelessWidget { ), ), Transform.flip( - flipX: appState.isArabic() ? true : false, + flipX: appState.isArabic(), child: Utils.buildSvgWithAssets( - icon: AppAssets.forward_arrow_icon, + icon: AppAssets.forward_arrow_icon_small, iconColor: AppColors.blackColor, width: 18.h, height: 13.h, diff --git a/lib/presentation/appointments/widgets/appointment_doctor_card.dart b/lib/presentation/appointments/widgets/appointment_doctor_card.dart index b35305f..efc5a6b 100644 --- a/lib/presentation/appointments/widgets/appointment_doctor_card.dart +++ b/lib/presentation/appointments/widgets/appointment_doctor_card.dart @@ -40,7 +40,7 @@ class AppointmentDoctorCard extends StatelessWidget { patientAppointmentHistoryResponseModel.doctorImageURL!, width: 63.h, height: 63.h, - fit: BoxFit.fill, + fit: BoxFit.cover, ).circle(100), SizedBox(width: 16.h), Expanded( diff --git a/lib/presentation/appointments/widgets/faculity_selection/facility_selection_item.dart b/lib/presentation/appointments/widgets/faculity_selection/facility_selection_item.dart index b267ee4..c7ef3a9 100644 --- a/lib/presentation/appointments/widgets/faculity_selection/facility_selection_item.dart +++ b/lib/presentation/appointments/widgets/faculity_selection/facility_selection_item.dart @@ -46,9 +46,9 @@ class FacilitySelectionItem extends StatelessWidget { children: [ info, Transform.flip( - flipX: appState.isArabic() ? true : false, + flipX: appState.isArabic(), child: Utils.buildSvgWithAssets( - icon: AppAssets.forward_arrow_icon, + icon: AppAssets.forward_arrow_icon_small, iconColor: AppColors.blackColor, width: 18, height: 13, diff --git a/lib/presentation/appointments/widgets/hospital_bottom_sheet/hospital_bottom_sheet_body.dart b/lib/presentation/appointments/widgets/hospital_bottom_sheet/hospital_bottom_sheet_body.dart index 7a4d7e9..29a7b96 100644 --- a/lib/presentation/appointments/widgets/hospital_bottom_sheet/hospital_bottom_sheet_body.dart +++ b/lib/presentation/appointments/widgets/hospital_bottom_sheet/hospital_bottom_sheet_body.dart @@ -104,7 +104,7 @@ class HospitalBottomSheetBody extends StatelessWidget { regionalViewModel.handleLastStepForClinic(); } else if (regionalViewModel.regionBottomSheetType == RegionBottomSheetType.REGION_FOR_DENTAL_AND_LASER) { regionalViewModel.setBottomSheetState(AppointmentViaRegionState.DOCTOR_SELECTION); - regionalViewModel.handleLastStepForClinicForDentalAndLaser(); + regionalViewModel.handleLastStepForClinicForDentalAndLaser(appointmentsViewModel.selectedClinic.clinicID??-1); // regionalViewModel.handleLastStepForClinic(); } });}, diff --git a/lib/presentation/appointments/widgets/hospital_bottom_sheet/hospital_list_items.dart b/lib/presentation/appointments/widgets/hospital_bottom_sheet/hospital_list_items.dart index cad0309..31dc74e 100644 --- a/lib/presentation/appointments/widgets/hospital_bottom_sheet/hospital_list_items.dart +++ b/lib/presentation/appointments/widgets/hospital_bottom_sheet/hospital_list_items.dart @@ -39,7 +39,7 @@ class HospitalListItem extends StatelessWidget { ), ), Transform.flip( - flipX: appState.isArabic() ? true : false, + flipX: appState.isArabic(), child: Utils.buildSvgWithAssets( icon: AppAssets.forward_arrow_icon_small, iconColor: AppColors.blackColor, diff --git a/lib/presentation/appointments/widgets/region_bottomsheet/region_list_item.dart b/lib/presentation/appointments/widgets/region_bottomsheet/region_list_item.dart index ab9cd9b..8b35921 100644 --- a/lib/presentation/appointments/widgets/region_bottomsheet/region_list_item.dart +++ b/lib/presentation/appointments/widgets/region_bottomsheet/region_list_item.dart @@ -43,9 +43,9 @@ class RegionListItem extends StatelessWidget { ], ), Transform.flip( - flipX: appState.isArabic() ? true : false, + flipX: appState.isArabic(), child: Utils.buildSvgWithAssets( - icon: AppAssets.forward_arrow_icon, + icon: AppAssets.forward_arrow_icon_small, iconColor: AppColors.blackColor, width: 18, height: 13, diff --git a/lib/presentation/appointments/widgets/region_bottomsheet/region_list_widget.dart b/lib/presentation/appointments/widgets/region_bottomsheet/region_list_widget.dart index 174fe10..6bc8007 100644 --- a/lib/presentation/appointments/widgets/region_bottomsheet/region_list_widget.dart +++ b/lib/presentation/appointments/widgets/region_bottomsheet/region_list_widget.dart @@ -10,6 +10,8 @@ import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_vie import 'package:hmg_patient_app_new/presentation/appointments/widgets/region_bottomsheet/region_list_item.dart' show RegionListItem; import 'package:provider/provider.dart'; +import '../../../../widgets/common_bottom_sheet.dart'; + class RegionBottomSheetBody extends StatefulWidget { const RegionBottomSheetBody({super.key}); @@ -27,7 +29,16 @@ class _RegionBottomSheetBodyState extends State { if (regionalViewModel.regionBottomSheetType == RegionBottomSheetType.FOR_REGION || regionalViewModel.regionBottomSheetType == RegionBottomSheetType.REGION_FOR_DENTAL_AND_LASER ) { myAppointmentsViewModel.getRegionMappedProjectList(); } else if (regionalViewModel.regionBottomSheetType == RegionBottomSheetType.FOR_CLINIIC) { - myAppointmentsViewModel.getMappedDoctors(); + myAppointmentsViewModel.getMappedDoctors(onError: (err) { + Navigator.pop(context); + showCommonBottomSheetWithoutHeight( + context, + child: Utils.getErrorWidget(loadingText: err), + callBackFunc: () {}, + isFullScreen: false, + isCloseButtonVisible: true, + ); + }); } }); super.initState(); diff --git a/lib/presentation/book_appointment/book_appointment_page.dart b/lib/presentation/book_appointment/book_appointment_page.dart index b124438..b39c51e 100644 --- a/lib/presentation/book_appointment/book_appointment_page.dart +++ b/lib/presentation/book_appointment/book_appointment_page.dart @@ -130,7 +130,7 @@ class _BookAppointmentPageState extends State { ], ), Transform.flip( - flipX: appState.isArabic() ? true : false, child: Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_icon, iconColor: AppColors.textColor, width: 40.h, height: 40.h)), + flipX: appState.isArabic(), child: Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_icon, iconColor: AppColors.textColor, width: 40.h, height: 40.h)), ], ).onPress(() { bookAppointmentsViewModel.setIsClinicsListLoading(true); @@ -163,7 +163,7 @@ class _BookAppointmentPageState extends State { ], ), Transform.flip( - flipX: appState.isArabic() ? true : false, child: Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_icon, iconColor: AppColors.textColor, width: 40.h, height: 40.h)), + flipX: appState.isArabic(), child: Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_icon, iconColor: AppColors.textColor, width: 40.h, height: 40.h)), ], ).onPress(() { bookAppointmentsViewModel.setIsDoctorSearchByNameStarted(false); @@ -194,7 +194,7 @@ class _BookAppointmentPageState extends State { ], ), Transform.flip( - flipX: appState.isArabic() ? true : false, child: Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_icon, iconColor: AppColors.textColor, width: 40.h, height: 40.h)), + flipX: appState.isArabic(), child: Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_icon, iconColor: AppColors.textColor, width: 40.h, height: 40.h)), ], ).onPress(() { bookAppointmentsViewModel.setProjectID(null); @@ -239,7 +239,7 @@ class _BookAppointmentPageState extends State { ], ), Transform.flip( - flipX: appState.isArabic() ? true : false, child: Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_icon, iconColor: AppColors.textColor, width: 40.h, height: 40.h)), + flipX: appState.isArabic(), child: Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_icon, iconColor: AppColors.textColor, width: 40.h, height: 40.h)), ], ).onPress(() async { //TODO Implement API to check for existing LiveCare Requests @@ -282,7 +282,7 @@ class _BookAppointmentPageState extends State { ], ), Transform.flip( - flipX: appState.isArabic() ? true : false, child: Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_icon, iconColor: AppColors.textColor, width: 40.h, height: 40.h)), + flipX: appState.isArabic(), child: Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_icon, iconColor: AppColors.textColor, width: 40.h, height: 40.h)), ], ).onPress(() { bookAppointmentsViewModel.setIsClinicsListLoading(true); @@ -313,7 +313,7 @@ class _BookAppointmentPageState extends State { ], ), Transform.flip( - flipX: appState.isArabic() ? true : false, child: Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_icon, iconColor: AppColors.textColor, width: 40.h, height: 40.h)), + flipX: appState.isArabic(), child: Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_icon, iconColor: AppColors.textColor, width: 40.h, height: 40.h)), ], ).onPress(() { openRegionListBottomSheet(context, RegionBottomSheetType.FOR_REGION); diff --git a/lib/presentation/book_appointment/dental_chief_complaints_page.dart b/lib/presentation/book_appointment/dental_chief_complaints_page.dart new file mode 100644 index 0000000..4dc3881 --- /dev/null +++ b/lib/presentation/book_appointment/dental_chief_complaints_page.dart @@ -0,0 +1,95 @@ +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/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/dental_chief_complaints_response_model.dart'; +import 'package:hmg_patient_app_new/presentation/book_appointment/select_doctor_page.dart'; +import 'package:hmg_patient_app_new/presentation/book_appointment/widgets/chief_complaint_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/routes/custom_page_route.dart'; +import 'package:provider/provider.dart'; + +class DentalChiefComplaintsPage extends StatefulWidget { + const DentalChiefComplaintsPage({super.key}); + + @override + State createState() => _DentalChiefComplaintsPageState(); +} + +class _DentalChiefComplaintsPageState extends State { + late AppState appState; + late BookAppointmentsViewModel bookAppointmentsViewModel; + + @override + void initState() { + scheduleMicrotask(() { + bookAppointmentsViewModel.getDentalChiefComplaintsList(); + }); + super.initState(); + } + + @override + Widget build(BuildContext context) { + bookAppointmentsViewModel = Provider.of(context, listen: false); + appState = getIt.get(); + return CollapsingListView( + title: "Dental Chief Complaints".needTranslation, + child: SingleChildScrollView( + child: Padding( + padding: EdgeInsets.symmetric(horizontal: 24.h), + child: Consumer(builder: (context, bookAppointmentsVM, child) { + return ListView.separated( + padding: EdgeInsets.only(top: 24.h), + shrinkWrap: true, + physics: NeverScrollableScrollPhysics(), + itemCount: bookAppointmentsVM.isChiefComplaintsListLoading ? 5 : bookAppointmentsVM.dentalChiefComplaintsList.length, + itemBuilder: (context, index) { + return bookAppointmentsVM.isChiefComplaintsListLoading + ? ChiefComplaintCard( + bookAppointmentsVM: bookAppointmentsVM, + dentalChiefComplaintsListResponseModel: DentalChiefComplaintsListResponseModel(), + isLoading: bookAppointmentsVM.isChiefComplaintsListLoading, + ) + : 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: ChiefComplaintCard( + bookAppointmentsVM: bookAppointmentsVM, + dentalChiefComplaintsListResponseModel: bookAppointmentsVM.dentalChiefComplaintsList[index], + isLoading: bookAppointmentsVM.isChiefComplaintsListLoading, + ).onPress(() { + bookAppointmentsVM.setSelectedChiefComplaintID(bookAppointmentsVM.dentalChiefComplaintsList[index].iD!); + bookAppointmentsViewModel.setIsDoctorsListLoading(true); + Navigator.of(context).push( + CustomPageRoute( + page: SelectDoctorPage(), + ), + ); + }), + ), + ), + ), + ); + }, + separatorBuilder: (BuildContext cxt, int index) => SizedBox(height: 16.h), + ); + }), + ), + ), + ); + } +} diff --git a/lib/presentation/book_appointment/doctor_profile_page.dart b/lib/presentation/book_appointment/doctor_profile_page.dart index 6084c3d..1223842 100644 --- a/lib/presentation/book_appointment/doctor_profile_page.dart +++ b/lib/presentation/book_appointment/doctor_profile_page.dart @@ -53,7 +53,7 @@ class DoctorProfilePage extends StatelessWidget { bookAppointmentsViewModel.doctorsProfileResponseModel.doctorImageURL!, width: 63.h, height: 63.h, - fit: BoxFit.fill, + fit: BoxFit.cover, ).circle(100), SizedBox(width: 8.h), Column( @@ -73,7 +73,7 @@ class DoctorProfilePage extends StatelessWidget { bookAppointmentsViewModel.doctorsProfileResponseModel.nationalityFlagURL!, width: 32.h, height: 32.h, - fit: BoxFit.fill, + fit: BoxFit.cover, ), ], ), diff --git a/lib/presentation/book_appointment/laser/laser_appointment.dart b/lib/presentation/book_appointment/laser/laser_appointment.dart new file mode 100644 index 0000000..bad0591 --- /dev/null +++ b/lib/presentation/book_appointment/laser/laser_appointment.dart @@ -0,0 +1,129 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.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/book_appointments/models/resp_models/laser_body_parts.dart'; +import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; +import 'package:hmg_patient_app_new/presentation/book_appointment/laser/widgets/body_part_listing.dart' show BodyPartsListing; +import 'package:hmg_patient_app_new/presentation/book_appointment/laser/widgets/body_type_listing.dart' + show BodyTypeListing; +import 'package:hmg_patient_app_new/presentation/book_appointment/select_doctor_page.dart'; +import 'package:hmg_patient_app_new/theme/colors.dart' show AppColors; +import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart'; +import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; +import 'package:hmg_patient_app_new/widgets/custom_tab_bar.dart'; +import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart'; +import 'package:provider/provider.dart'; + +class LaserAppointment extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: AppColors.bgScaffoldColor, + bottomNavigationBar: + DecoratedBox( + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: AppColors.whiteColor, + borderRadius: 20.h, + hasShadow: true, + ), + child: Selector(selector: (_, vm)=>vm.duration,builder: (_, duration, __)=> Row( + children: [ + Expanded( + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + LocaleKeys.totalMinutes.tr(), + style: TextStyle( + fontSize: 12.fSize, + fontWeight: FontWeight.w600, + color: AppColors.textColor, + letterSpacing: -0.48, + ), + ), + Text( + "$duration / 90".tr(), + style: TextStyle( + fontSize: 16.fSize, + fontWeight: FontWeight.bold, + color: AppColors.textColor, + letterSpacing: -0.64, + ), + ), + ], + ), + ), + Expanded( + child: CustomButton( + isDisabled: duration==0, + text: LocaleKeys.continueString.tr(), onPressed: () { + Navigator.of(context).push( + CustomPageRoute( + page: SelectDoctorPage(), + ), + ); + }, + ), + ), + ], + )).paddingSymmetrical(24.h, 12.h), + ), + body: CollapsingListView( + title: LocaleKeys.laserClinic.tr(), + child: SingleChildScrollView( + child: Column( + children: [ + CustomTabBar( + activeTextColor: Color(0xffED1C2B), + activeBackgroundColor: Color(0xffED1C2B).withValues(alpha: .1), + tabs: [ + CustomTabBarModel(null,LocaleKeys.malE.tr()), + CustomTabBarModel(null,LocaleKeys.female.tr()), + ], + onTabChange: (index) { + var viewmodel = context.read(); + viewmodel.setBodyType(index); + viewmodel.setSelectedCategory(0); + viewmodel.getLaserClinic(); + }, + ), + Selector( + selector: (_, vm) => vm.selectedBodyTypeIndex, + builder: (_, bodyType, __) { + return BodyTypeListing( + availableBodyArea: context + .read() + .bodyTypes[bodyType], + selectedBodyAreaIndex: context + .read() + .selectedCategory, + onCategoryChanged: (category) { + context + .read() + .setSelectedCategory(category); + }); + }, + ), + Selector>(selector: (_, vm) => vm.laserBodyPartsList, builder:(_, parts,__){ + return BodyPartsListing( + parts: parts, + onPartSelected: (part){ + context.read().setSelectedBodyPart(part); + }, + selectedPart: context.watch().selectedBodyPartList, + isLoading: context.watch().isBodyPartsLoading, + isMale: context.watch().selectedBodyTypeIndex == 0, + laserCategoryID: context.watch().readCurrentLaserCategoryID(), + ); + }), + ], + ).paddingSymmetrical(24.h, 12.h), + ), + ), + ); + } +} diff --git a/lib/presentation/book_appointment/laser/widgets/body_part_listing.dart b/lib/presentation/book_appointment/laser/widgets/body_part_listing.dart new file mode 100644 index 0000000..564c013 --- /dev/null +++ b/lib/presentation/book_appointment/laser/widgets/body_part_listing.dart @@ -0,0 +1,129 @@ +import 'package:flutter/material.dart'; +import 'package:hmg_patient_app_new/core/app_export.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/laser_body_parts_data.dart'; +import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/laser_body_parts.dart'; +import 'package:hmg_patient_app_new/theme/colors.dart'; +import 'package:provider/provider.dart'; + +class BodyPartsListing extends StatelessWidget { + final List parts; + final Function(LaserBodyPart) onPartSelected; + final List selectedPart; + final int laserCategoryID; + final bool isMale; + final bool isLoading; + + const BodyPartsListing( + {super.key, + required this.parts, + required this.onPartSelected, + required this.selectedPart, + required this.laserCategoryID, + required this.isMale, required this.isLoading}); + + @override + Widget build(BuildContext context) { + return Column( + children: [ + Visibility( + visible: isLoading, + child: GridView.builder( + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 3, + childAspectRatio: 85 / 107, + crossAxisSpacing: 4.h, + mainAxisSpacing: 21.h), + physics: NeverScrollableScrollPhysics(), + shrinkWrap: true, + itemCount: 20, + // padding: fullBody != null ? EdgeInsets.only(top: 16) : EdgeInsets.zero, + padding: EdgeInsets.zero, + itemBuilder: (BuildContext context, int index) { + return SizedBox( height: 80, width: 80,).toShimmer2(isShow: isLoading); + })), + GridView.builder( + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 3, + childAspectRatio: 85 / 107, + crossAxisSpacing: 4.h, + mainAxisSpacing: 21.h), + physics: NeverScrollableScrollPhysics(), + shrinkWrap: true, + itemCount: parts.length, + // padding: fullBody != null ? EdgeInsets.only(top: 16) : EdgeInsets.zero, + padding: EdgeInsets.zero, + itemBuilder: (BuildContext context, int index) { + bool _isSelected = + selectedPart.any((file) => file.id == parts[index].id); + return InkWell( + onTap: () { + onPartSelected(parts[index]); + }, + child: AnimatedOpacity( + opacity: 1.0, + duration: Duration(milliseconds: 200), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + AspectRatio( + aspectRatio: 97 / 97, + child: FittedBox( + fit: BoxFit.fitWidth, + child: Stack( + alignment: Alignment.topRight, + children: [ + Container( + margin: EdgeInsets.only(top: 5.h, right: 5.h), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(15.0), + border: Border.all( + color: _isSelected + ? AppColors.primaryRedColor + : AppColors.borderGrayColor, + width: 2.h), + ), + child: LaserBodyParts().getCategoryImage(isMale, + laserCategoryID, parts[index].mappingCode!), + ), + if (_isSelected) + Container( + width: 18.h, + height: 18.h, + child: Icon(Icons.done, + color: Colors.white, size: 12.h), + decoration: BoxDecoration( + color: AppColors.primaryRedColor, + borderRadius: BorderRadius.circular(30.h), + ), + ), + ], + ), + ), + ), + SizedBox(height: 6.h), + Expanded( + child: Text( + context + .read() + .getLaserProcedureNameWRTLanguage(parts[index]), + style: TextStyle( + fontSize: 12.fSize, + fontWeight: FontWeight.w600, + color: Color(0xff2B353E), + letterSpacing: -0.48, + ), + maxLines: 1, + ), + ), + ], + ), + ), + ); + }, + ), + ], + ); + } +} diff --git a/lib/presentation/book_appointment/laser/widgets/body_type_listing.dart b/lib/presentation/book_appointment/laser/widgets/body_type_listing.dart new file mode 100644 index 0000000..d8fa736 --- /dev/null +++ b/lib/presentation/book_appointment/laser/widgets/body_type_listing.dart @@ -0,0 +1,66 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.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/models/LaserCategoryType.dart'; +import 'package:hmg_patient_app_new/theme/colors.dart'; +import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart'; +import 'package:smooth_corner/smooth_corner.dart' show SmoothRectangleBorder; + +class BodyTypeListing extends StatelessWidget { + final List availableBodyArea; + final Function(int) onCategoryChanged; + final int selectedBodyAreaIndex; + + const BodyTypeListing( + {super.key, + required this.availableBodyArea, + required this.onCategoryChanged, + required this.selectedBodyAreaIndex}); + + @override + Widget build(BuildContext context) { + return SizedBox( + height: 56.h, + child: Row( + children: [ + Expanded( + child: ListView.separated( + scrollDirection: Axis.horizontal, + itemCount: availableBodyArea.length, + separatorBuilder: (_, index) => SizedBox( + width: 8.h, + ), + itemBuilder: (_, index) => AppCustomChipWidget( + backgroundColor: index == selectedBodyAreaIndex + ? AppColors.chipSecondaryLightRedColor + : AppColors.whiteColor, + textColor: index == selectedBodyAreaIndex + ? AppColors.chipPrimaryRedBorderColor + : AppColors.blackColor, + labelText: availableBodyArea[index].title.tr(), + iconHasColor: false, + iconColor: index == selectedBodyAreaIndex + ? AppColors.chipPrimaryRedBorderColor + : AppColors.blackColor, + iconSize: 16, + labelPadding: + EdgeInsetsDirectional.only(start: 8.h, end: 0.h), + padding: + EdgeInsets.symmetric(vertical: 16.h, horizontal: 12.h), + deleteIconSize: Size(18.h, 18.h), + shape: SmoothRectangleBorder( + borderRadius: BorderRadius.circular(10), + smoothness: 10, + side: BorderSide( + color: index == selectedBodyAreaIndex + ? AppColors.chipPrimaryRedBorderColor + : AppColors.borderGrayColor, + width: 1), + )).onPress(() => onCategoryChanged(index)))) + ], + ), + ); + } +} diff --git a/lib/presentation/book_appointment/livecare/immediate_livecare_payment_page.dart b/lib/presentation/book_appointment/livecare/immediate_livecare_payment_page.dart index 5437945..914ac4a 100644 --- a/lib/presentation/book_appointment/livecare/immediate_livecare_payment_page.dart +++ b/lib/presentation/book_appointment/livecare/immediate_livecare_payment_page.dart @@ -57,6 +57,8 @@ class _ImmediateLiveCarePaymentPageState extends State false); + Navigator.of(context).push( CustomPageRoute( - page: LandingNavigation(), + page: ImmediateLiveCarePendingRequestPage(), ), - (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 { + await payfortViewModel.updateTamaraRequestStatus(responseMessage: "Failed", status: "00", clientRequestID: transID, tamaraOrderID: tamaraOrderID); + LoaderBottomSheet.hideLoader(); showCommonBottomSheetWithoutHeight( context, - child: Utils.getErrorWidget(loadingText: "Unknown error occurred...".needTranslation), + child: Utils.getErrorWidget(loadingText: "Payment Failed! Please try again.".needTranslation), callBackFunc: () {}, isFullScreen: false, isCloseButtonVisible: true, ); } - } else { + }, + onError: (err) { + LoaderBottomSheet.hideLoader(); showCommonBottomSheetWithoutHeight( context, - child: Utils.getErrorWidget(loadingText: "Payment Failed! Please try again.".needTranslation), + child: Utils.getErrorWidget(loadingText: err), callBackFunc: () {}, isFullScreen: false, isCloseButtonVisible: true, ); - } - }); + }); + } else { + 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) { diff --git a/lib/presentation/book_appointment/livecare/widgets/livecare_clinic_card.dart b/lib/presentation/book_appointment/livecare/widgets/livecare_clinic_card.dart index b181b49..7385c6b 100644 --- a/lib/presentation/book_appointment/livecare/widgets/livecare_clinic_card.dart +++ b/lib/presentation/book_appointment/livecare/widgets/livecare_clinic_card.dart @@ -57,7 +57,7 @@ class LiveCareClinicCard extends StatelessWidget { .toText16(isBold: true) .toShimmer2(isShow: isLoading)), Transform.flip( - flipX: appState.isArabic() ? true : false, + flipX: appState.isArabic(), child: Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_icon, width: 40.h, height: 40.h, fit: BoxFit.contain, iconColor: AppColors.textColor).toShimmer2(isShow: isLoading)), ]), ], diff --git a/lib/presentation/book_appointment/review_appointment_page.dart b/lib/presentation/book_appointment/review_appointment_page.dart index 77cd249..0cb4b2d 100644 --- a/lib/presentation/book_appointment/review_appointment_page.dart +++ b/lib/presentation/book_appointment/review_appointment_page.dart @@ -71,7 +71,7 @@ class _ReviewAppointmentPageState extends State { bookAppointmentsViewModel.selectedDoctor.doctorImageURL!, width: 50.h, height: 50.h, - fit: BoxFit.fill, + fit: BoxFit.cover, ).circle(100), SizedBox(width: 8.h), Column( @@ -88,7 +88,7 @@ class _ReviewAppointmentPageState extends State { bookAppointmentsViewModel.selectedDoctor.nationalityFlagURL!, width: 20.h, height: 15.h, - fit: BoxFit.fill, + fit: BoxFit.cover, ), ], ), diff --git a/lib/presentation/book_appointment/select_clinic_page.dart b/lib/presentation/book_appointment/select_clinic_page.dart index 1c33dd6..f5e534e 100644 --- a/lib/presentation/book_appointment/select_clinic_page.dart +++ b/lib/presentation/book_appointment/select_clinic_page.dart @@ -18,6 +18,8 @@ import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/presentation/appointments/widgets/faculity_selection/facility_type_selection_widget.dart'; import 'package:hmg_patient_app_new/presentation/appointments/widgets/hospital_bottom_sheet/hospital_bottom_sheet_body.dart'; import 'package:hmg_patient_app_new/presentation/appointments/widgets/region_bottomsheet/region_list_widget.dart'; +import 'package:hmg_patient_app_new/presentation/book_appointment/dental_chief_complaints_page.dart'; +import 'package:hmg_patient_app_new/presentation/book_appointment/laser/laser_appointment.dart'; import 'package:hmg_patient_app_new/presentation/book_appointment/select_doctor_page.dart'; import 'package:hmg_patient_app_new/presentation/book_appointment/select_livecare_clinic_page.dart'; import 'package:hmg_patient_app_new/presentation/book_appointment/widgets/clinic_card.dart'; @@ -223,121 +225,34 @@ class _SelectClinicPageState extends State { if (widget.isFromRegionFlow) { //Dental Clinic Flow if (clinic.clinicID == 17) { - LoaderBottomSheet.showLoader(loadingText: "Checking for an existing dental plan, Please wait...".needTranslation); - await bookAppointmentsViewModel.getPatientDentalEstimation(projectID: int.parse(bookAppointmentsViewModel.currentlySelectedHospitalFromRegionFlow ?? "0")).then((value) { - LoaderBottomSheet.hideLoader(); - if (bookAppointmentsViewModel.patientDentalPlanEstimationList.isNotEmpty) { - showCommonBottomSheetWithoutHeight( - // title: LocaleKeys.notice.tr(context: context), - title: "Dental treatment plan".needTranslation, - context, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - "You have an existing treatment plan: ".needTranslation.toText14(weight: FontWeight.w500), - SizedBox(height: 8.h), - Container( - width: double.infinity, - padding: EdgeInsets.all(16.h), - decoration: RoundedRectangleBorder().toSmoothCornerDecoration( - color: AppColors.whiteColor, - borderRadius: 20.h, - hasShadow: true, - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - ListView.separated( - shrinkWrap: true, - physics: NeverScrollableScrollPhysics(), - itemCount: bookAppointmentsViewModel.patientDentalPlanEstimationList.length, - separatorBuilder: (_, __) { - return Column( - children: [ - SizedBox(height: 8.h), - Divider(height: 1, color: AppColors.greyColor), - SizedBox(height: 8.h), - ], - ); - }, - itemBuilder: (context, index) { - return Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - bookAppointmentsViewModel.patientDentalPlanEstimationList[index].procedureName!.toText12(isBold: true), - AppCustomChipWidget(icon: AppAssets.appointment_time_icon, labelText: "${bookAppointmentsViewModel.totalTimeNeededForDentalProcedure} Mins".needTranslation), - ], - ); - }, - ), - SizedBox( - height: 16.h, - ), - Divider(height: 1, color: AppColors.greyColor), - SizedBox( - height: 8.h, - ), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - "Total time required".needTranslation.toText14(isBold: true), - AppCustomChipWidget(icon: AppAssets.appointment_time_icon, labelText: "30 Mins".needTranslation), - ], - ) - ], - ), - ), - SizedBox(height: 16.h), - "Would you like to continue it?".needTranslation.toText14(weight: FontWeight.w500), - SizedBox(height: 16.h), - Row( - children: [ - Expanded( - child: CustomButton( - text: LocaleKeys.cancel.tr(), - onPressed: () { - bookAppointmentsViewModel.setIsContinueDentalPlan(false); - Navigator.of(context).pop(); - }, - backgroundColor: AppColors.primaryRedColor, - borderColor: AppColors.primaryRedColor, - textColor: AppColors.whiteColor, - icon: AppAssets.cancel, - iconColor: AppColors.whiteColor, - ), - ), - SizedBox(width: 8.h), - Expanded( - child: CustomButton( - text: LocaleKeys.confirm.tr(), - onPressed: () async { - bookAppointmentsViewModel.setIsContinueDentalPlan(true); - Navigator.of(context).push( - CustomPageRoute( - page: SelectDoctorPage(), - ), - ); - }, - backgroundColor: AppColors.bgGreenColor, - borderColor: AppColors.bgGreenColor, - textColor: Colors.white, - icon: AppAssets.confirm, - ), - ), - ], - ) - ], - ), - callBackFunc: () {}, - isFullScreen: false, - isCloseButtonVisible: true, - ); - } else { - // Navigate to Chief Complaint Screen - } - }); + if (appState.isAuthenticated) { + initDentalAppointmentBookingFlow(int.parse(bookAppointmentsViewModel + .currentlySelectedHospitalFromRegionFlow ?? + "0")); + return; + } else { + bookAppointmentsViewModel.setIsChiefComplaintsListLoading(true); + Navigator.of(context).push( + CustomPageRoute( + page: DentalChiefComplaintsPage(), + ), + ); + return; + } + } + else if (clinic.clinicID == 253) { + bookAppointmentsViewModel.resetLaserData(); + bookAppointmentsViewModel.getLaserClinic(); + Navigator.push( + context, + CustomPageRoute( + page: LaserAppointment(), + ), + ); + return; } else { - Navigator.of(context).push( + Navigator.push( + context, CustomPageRoute( page: SelectDoctorPage(), ), @@ -345,14 +260,15 @@ class _SelectClinicPageState extends State { } } else { var bottomSheetType = RegionBottomSheetType.FOR_CLINIIC; - if (clinic.clinicID == 17 || clinic.clinicID == 235) { + if (clinic.clinicID == 17 || clinic.clinicID == 253) { bottomSheetType = RegionBottomSheetType.REGION_FOR_DENTAL_AND_LASER; } openRegionListBottomSheet(context, bottomSheetType); } } - void openRegionListBottomSheet(BuildContext context, RegionBottomSheetType type) { + void openRegionListBottomSheet( + BuildContext context, RegionBottomSheetType type) { bookAppointmentsViewModel.setProjectID(null); regionalViewModel.flush(); @@ -361,7 +277,8 @@ class _SelectClinicPageState extends State { showCommonBottomSheetWithoutHeight(context, title: "", titleWidget: Consumer(builder: (_, data, __) => getTitle(data)), isDismissible: false, child: Consumer(builder: (_, data, __) { return getRegionalSelectionWidget(data); - }), callBackFunc: () {}); + }), callBackFunc: () { + }); } Widget getRegionalSelectionWidget(AppointmentViaRegionViewmodel data) { @@ -378,12 +295,24 @@ class _SelectClinicPageState extends State { return HospitalBottomSheetBody(); } if (data.bottomSheetState == AppointmentViaRegionState.DOCTOR_SELECTION) { - //if the region screen is opened for the dental clinic thenthe project id will be in the hospital list as the list is formed form the get project api + //if the region screen is opened for the dental clinic then the project id will be in the hospital list as the list is formed form the get project api var id = ""; if (data.regionBottomSheetType == RegionBottomSheetType.REGION_FOR_DENTAL_AND_LASER) { - id = regionalViewModel.selectedHospital?.hospitalList?.first?.iD?.toString() ?? ""; + id = regionalViewModel.selectedHospital?.hospitalList.first.iD?.toString() ?? ""; } else { - id = regionalViewModel.selectedHospital?.patientDoctorAppointmentList?.first?.projectID?.toString() ?? ""; + id = regionalViewModel.selectedHospital?.patientDoctorAppointmentList?.first.projectID?.toString() ?? ""; + } + if (bookAppointmentsViewModel.selectedClinic.clinicID == 17) { + if (!appState.isAuthenticated) { + bookAppointmentsViewModel.setIsChiefComplaintsListLoading(true); + } + } + + if (bookAppointmentsViewModel.selectedClinic.clinicID == 253) { + { + bookAppointmentsViewModel.resetLaserData(); + bookAppointmentsViewModel.getLaserClinic(); + } } bookAppointmentsViewModel.setProjectID(id); return SizedBox.shrink(); @@ -410,4 +339,128 @@ class _SelectClinicPageState extends State { }); } } + + void initDentalAppointmentBookingFlow(int projectID) async { + bookAppointmentsViewModel.setProjectID(projectID.toString()); + LoaderBottomSheet.showLoader(loadingText: "Checking for an existing dental plan, Please wait...".needTranslation); + await bookAppointmentsViewModel.getPatientDentalEstimation(projectID: projectID).then((value) { + LoaderBottomSheet.hideLoader(); + if (bookAppointmentsViewModel.patientDentalPlanEstimationList.isNotEmpty) { + showCommonBottomSheetWithoutHeight( + // title: LocaleKeys.notice.tr(context: context), + title: "Dental treatment plan".needTranslation, + context, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + "You have an existing treatment plan: ".needTranslation.toText14(weight: FontWeight.w500), + SizedBox(height: 8.h), + Container( + width: double.infinity, + padding: EdgeInsets.all(16.h), + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: AppColors.whiteColor, + borderRadius: 20.h, + hasShadow: true, + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ListView.separated( + shrinkWrap: true, + physics: NeverScrollableScrollPhysics(), + itemCount: bookAppointmentsViewModel.patientDentalPlanEstimationList.length, + separatorBuilder: (_, __) { + return Column( + children: [ + SizedBox(height: 8.h), + Divider(height: 1, color: AppColors.greyColor), + SizedBox(height: 8.h), + ], + ); + }, + itemBuilder: (context, index) { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + bookAppointmentsViewModel.patientDentalPlanEstimationList[index].procedureName!.toText12(isBold: true), + AppCustomChipWidget(icon: AppAssets.appointment_time_icon, labelText: "${bookAppointmentsViewModel.totalTimeNeededForDentalProcedure} Mins".needTranslation), + ], + ); + }, + ), + SizedBox( + height: 16.h, + ), + Divider(height: 1, color: AppColors.greyColor), + SizedBox( + height: 8.h, + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + "Total time required".needTranslation.toText14(isBold: true), + AppCustomChipWidget(icon: AppAssets.appointment_time_icon, labelText: "30 Mins".needTranslation), + ], + ) + ], + ), + ), + SizedBox(height: 16.h), + "Would you like to continue it?".needTranslation.toText14(weight: FontWeight.w500), + SizedBox(height: 16.h), + Row( + children: [ + Expanded( + child: CustomButton( + text: LocaleKeys.cancel.tr(), + onPressed: () { + bookAppointmentsViewModel.setIsContinueDentalPlan(false); + bookAppointmentsViewModel.setIsChiefComplaintsListLoading(true); + Navigator.of(context).pop(); + Navigator.of(context).push( + CustomPageRoute( + page: DentalChiefComplaintsPage(), + ), + ); + }, + backgroundColor: AppColors.primaryRedColor, + borderColor: AppColors.primaryRedColor, + textColor: AppColors.whiteColor, + icon: AppAssets.cancel, + iconColor: AppColors.whiteColor, + ), + ), + SizedBox(width: 8.h), + Expanded( + child: CustomButton( + text: LocaleKeys.confirm.tr(), + onPressed: () async { + bookAppointmentsViewModel.setIsContinueDentalPlan(true); + Navigator.of(context).pop(); + Navigator.of(context).push( + CustomPageRoute( + page: SelectDoctorPage(), + ), + ); + }, + backgroundColor: AppColors.bgGreenColor, + borderColor: AppColors.bgGreenColor, + textColor: Colors.white, + icon: AppAssets.confirm, + ), + ), + ], + ) + ], + ), + callBackFunc: () {}, + isFullScreen: false, + isCloseButtonVisible: true, + ); + } else { + // Navigate to Chief Complaint Screen + } + }); + } } diff --git a/lib/presentation/book_appointment/select_doctor_page.dart b/lib/presentation/book_appointment/select_doctor_page.dart index 8d24b43..b0b2a45 100644 --- a/lib/presentation/book_appointment/select_doctor_page.dart +++ b/lib/presentation/book_appointment/select_doctor_page.dart @@ -21,7 +21,6 @@ import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart'; import 'package:hmg_patient_app_new/widgets/input_widget.dart'; import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart'; import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart'; -import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart'; import 'package:provider/provider.dart'; class SelectDoctorPage extends StatefulWidget { @@ -45,7 +44,11 @@ class _SelectDoctorPageState extends State { if (bookAppointmentsViewModel.isLiveCareSchedule) { bookAppointmentsViewModel.getLiveCareDoctorsList(); } else { - bookAppointmentsViewModel.getDoctorsList(); + if (bookAppointmentsViewModel.selectedClinic.clinicID == 17) { + bookAppointmentsViewModel.getDentalChiefComplaintDoctorsList(); + } else { + bookAppointmentsViewModel.getDoctorsList(); + } } }); super.initState(); @@ -66,7 +69,6 @@ class _SelectDoctorPageState extends State { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - // TODO: Implement doctor filter functionality SizedBox(height: 16.h), Row( spacing: 8.h, diff --git a/lib/presentation/book_appointment/widgets/chief_complaint_card.dart b/lib/presentation/book_appointment/widgets/chief_complaint_card.dart new file mode 100644 index 0000000..e80596c --- /dev/null +++ b/lib/presentation/book_appointment/widgets/chief_complaint_card.dart @@ -0,0 +1,38 @@ +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/book_appointments/models/resp_models/dental_chief_complaints_response_model.dart'; +import 'package:hmg_patient_app_new/theme/colors.dart'; + +class ChiefComplaintCard extends StatelessWidget { + ChiefComplaintCard({super.key, required this.isLoading, required this.bookAppointmentsVM, required this.dentalChiefComplaintsListResponseModel}); + + bool isLoading; + BookAppointmentsViewModel bookAppointmentsVM; + DentalChiefComplaintsListResponseModel dentalChiefComplaintsListResponseModel; + + @override + Widget build(BuildContext context) { + AppState appState = getIt.get(); + return Container( + padding: EdgeInsets.all(16.h), + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: AppColors.whiteColor, + borderRadius: 24.h, + hasShadow: false, + ), + child: Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ + Expanded(child: (isLoading ? "Cardiology" : dentalChiefComplaintsListResponseModel.name)!.toText16(isBold: true).toShimmer2(isShow: isLoading)), + Transform.flip( + flipX: appState.isArabic(), + child: Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_icon, width: 40.h, height: 40.h, fit: BoxFit.contain, iconColor: AppColors.textColor).toShimmer2(isShow: isLoading)), + ]), + ); + } +} diff --git a/lib/presentation/book_appointment/widgets/clinic_card.dart b/lib/presentation/book_appointment/widgets/clinic_card.dart index c157d05..cab3b1a 100644 --- a/lib/presentation/book_appointment/widgets/clinic_card.dart +++ b/lib/presentation/book_appointment/widgets/clinic_card.dart @@ -36,7 +36,8 @@ class ClinicCard extends StatelessWidget { (clinicsListResponseModel.isLiveCareClinicAndOnline ?? true) ? Utils.buildSvgWithAssets(icon: AppAssets.livecare_clinic_icon, width: 32.h, height: 32.h, fit: BoxFit.contain).toShimmer2(isShow: isLoading) : SizedBox.shrink(), - ]), + ], + ), SizedBox(height: 16.h), Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Expanded( @@ -44,7 +45,7 @@ class ClinicCard extends StatelessWidget { .toText16(isBold: true) .toShimmer2(isShow: isLoading)), Transform.flip( - flipX: appState.isArabic() ? true : false, + flipX: appState.isArabic(), child: Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_icon, width: 40.h, height: 40.h, fit: BoxFit.contain, iconColor: AppColors.textColor).toShimmer2(isShow: isLoading)), ]), ], diff --git a/lib/presentation/habib_wallet/wallet_payment_confirm_page.dart b/lib/presentation/habib_wallet/wallet_payment_confirm_page.dart index 63aa313..b414799 100644 --- a/lib/presentation/habib_wallet/wallet_payment_confirm_page.dart +++ b/lib/presentation/habib_wallet/wallet_payment_confirm_page.dart @@ -88,7 +88,7 @@ class _WalletPaymentConfirmPageState extends State { SizedBox(width: 8.h), const Spacer(), Transform.flip( - flipX: appState.isArabic() ? true : false, + flipX: appState.isArabic(), child: Utils.buildSvgWithAssets( icon: AppAssets.forward_arrow_icon, iconColor: AppColors.blackColor, @@ -130,7 +130,7 @@ class _WalletPaymentConfirmPageState extends State { SizedBox(width: 8.h), const Spacer(), Transform.flip( - flipX: appState.isArabic() ? true : false, + flipX: appState.isArabic(), child: Utils.buildSvgWithAssets( icon: AppAssets.forward_arrow_icon, iconColor: AppColors.blackColor, diff --git a/lib/presentation/habib_wallet/widgets/hospital_list_item.dart b/lib/presentation/habib_wallet/widgets/hospital_list_item.dart index c5583e2..8e10e28 100644 --- a/lib/presentation/habib_wallet/widgets/hospital_list_item.dart +++ b/lib/presentation/habib_wallet/widgets/hospital_list_item.dart @@ -36,7 +36,7 @@ class HospitalListItemAdvancePayment extends StatelessWidget { ), ), Transform.flip( - flipX: appState.isArabic() ? true : false, + flipX: appState.isArabic(), child: Utils.buildSvgWithAssets( icon: AppAssets.forward_arrow_icon, iconColor: AppColors.blackColor, diff --git a/lib/presentation/home/landing_page.dart b/lib/presentation/home/landing_page.dart index 258e8e0..7de153f 100644 --- a/lib/presentation/home/landing_page.dart +++ b/lib/presentation/home/landing_page.dart @@ -15,9 +15,11 @@ import 'package:hmg_patient_app_new/extensions/int_extensions.dart'; import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; import 'package:hmg_patient_app_new/features/authentication/authentication_view_model.dart'; +import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_view_model.dart'; import 'package:hmg_patient_app_new/features/habib_wallet/habib_wallet_view_model.dart'; import 'package:hmg_patient_app_new/features/immediate_livecare/immediate_livecare_view_model.dart'; import 'package:hmg_patient_app_new/features/insurance/insurance_view_model.dart'; +import 'package:hmg_patient_app_new/features/medical_file/medical_file_view_model.dart'; import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/patient_appointment_history_response_model.dart'; import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart'; import 'package:hmg_patient_app_new/features/prescriptions/prescriptions_view_model.dart'; @@ -67,6 +69,7 @@ class _LandingPageState extends State { late InsuranceViewModel insuranceViewModel; late ImmediateLiveCareViewModel immediateLiveCareViewModel; + late BookAppointmentsViewModel bookAppointmentsViewModel; final SwiperController _controller = SwiperController(); @@ -74,6 +77,11 @@ class _LandingPageState extends State { void initState() { authVM = context.read(); habibWalletVM = context.read(); + // myAppointmentsViewModel = context.read(); + // prescriptionsViewModel = context.read(); + // insuranceViewModel = context.read(); + // immediateLiveCareViewModel = context.read(); + authVM.savePushTokenToAppState(); if (mounted) { authVM.checkLastLoginStatus(() { @@ -98,12 +106,12 @@ class _LandingPageState extends State { @override Widget build(BuildContext context) { - appState = getIt.get(); - NavigationService navigationService = getIt.get(); + bookAppointmentsViewModel = Provider.of(context, listen: false); myAppointmentsViewModel = Provider.of(context, listen: false); prescriptionsViewModel = Provider.of(context, listen: false); insuranceViewModel = Provider.of(context, listen: false); immediateLiveCareViewModel = Provider.of(context, listen: false); + appState = getIt.get(); return Scaffold( backgroundColor: AppColors.bgScaffoldColor, body: SingleChildScrollView( @@ -200,6 +208,7 @@ class _LandingPageState extends State { child: AppointmentCard( patientAppointmentHistoryResponseModel: PatientAppointmentHistoryResponseModel(), myAppointmentsViewModel: myAppointmentsViewModel, + bookAppointmentsViewModel: bookAppointmentsViewModel, isLoading: true, isFromHomePage: true, ), @@ -211,6 +220,7 @@ class _LandingPageState extends State { child: AppointmentCard( patientAppointmentHistoryResponseModel: myAppointmentsVM.patientAppointmentsHistoryList.first, myAppointmentsViewModel: myAppointmentsViewModel, + bookAppointmentsViewModel: bookAppointmentsViewModel, isLoading: false, isFromHomePage: true, ), @@ -239,6 +249,7 @@ class _LandingPageState extends State { child: AppointmentCard( patientAppointmentHistoryResponseModel: myAppointmentsVM.patientAppointmentsHistoryList[index], myAppointmentsViewModel: myAppointmentsViewModel, + bookAppointmentsViewModel: bookAppointmentsViewModel, isLoading: false, isFromHomePage: true, ), diff --git a/lib/presentation/insurance/insurance_approvals_page.dart b/lib/presentation/insurance/insurance_approvals_page.dart new file mode 100644 index 0000000..50e659c --- /dev/null +++ b/lib/presentation/insurance/insurance_approvals_page.dart @@ -0,0 +1,109 @@ +import 'dart:async'; + +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_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/insurance/insurance_view_model.dart'; +import 'package:hmg_patient_app_new/features/insurance/models/resp_models/patient_insurance_approval_response_model.dart'; +import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; +import 'package:hmg_patient_app_new/presentation/insurance/widgets/insurance_approval_card.dart'; +import 'package:hmg_patient_app_new/presentation/insurance/widgets/insurance_approval_details_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/routes/custom_page_route.dart'; +import 'package:provider/provider.dart'; + +class InsuranceApprovalsPage extends StatefulWidget { + const InsuranceApprovalsPage({super.key}); + + @override + State createState() => _InsuranceApprovalsPageState(); +} + +class _InsuranceApprovalsPageState extends State { + late InsuranceViewModel insuranceViewModel; + late AppState appState; + + @override + void initState() { + scheduleMicrotask(() { + insuranceViewModel.setIsInsuranceApprovalsLoading(true); + insuranceViewModel.getPatientInsuranceApprovalsList(); + }); + super.initState(); + } + + @override + Widget build(BuildContext context) { + appState = getIt.get(); + insuranceViewModel = Provider.of(context, listen: false); + return CollapsingListView( + title: "${LocaleKeys.insurance.tr(context: context)} ${LocaleKeys.approvals.tr(context: context)}", + child: SingleChildScrollView( + child: Consumer(builder: (context, insuranceVM, child) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ListView.separated( + padding: EdgeInsets.only(top: 24.h), + shrinkWrap: true, + physics: NeverScrollableScrollPhysics(), + itemCount: insuranceVM.isInsuranceApprovalsLoading + ? 5 + : insuranceVM.patientInsuranceApprovalsList.isNotEmpty + ? insuranceVM.patientInsuranceApprovalsList.length + : 1, + itemBuilder: (context, index) { + return insuranceVM.isInsuranceApprovalsLoading + ? Container( + decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: true), + child: InsuranceApprovalCard( + insuranceApprovalResponseModel: InsuranceApprovalResponseModel(), + appState: appState, + isLoading: true, + ), + ).paddingSymmetrical(24.h, 0.h) + : insuranceVM.patientInsuranceApprovalsList.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: InsuranceApprovalCard( + insuranceApprovalResponseModel: insuranceVM.patientInsuranceApprovalsList[index], + appState: appState, + isLoading: false, + ).onPress(() { + Navigator.of(context).push( + CustomPageRoute( + page: InsuranceApprovalDetailsPage(insuranceApprovalResponseModel: insuranceVM.patientInsuranceApprovalsList[index]), + ), + ); + }), + ).paddingSymmetrical(24.h, 0.h), + ), + ), + ) + : Utils.getNoDataWidget(context, noDataText: "You don't have any insurance approvals yet.".needTranslation); + }, + separatorBuilder: (BuildContext cxt, int index) => SizedBox(height: 16.h), + ), + SizedBox(height: 24.h), + ], + ); + }), + ), + ); + } +} diff --git a/lib/presentation/insurance/insurance_home_page.dart b/lib/presentation/insurance/insurance_home_page.dart index bc56473..bd195c3 100644 --- a/lib/presentation/insurance/insurance_home_page.dart +++ b/lib/presentation/insurance/insurance_home_page.dart @@ -34,7 +34,6 @@ class InsuranceHomePage extends StatefulWidget { class _InsuranceHomePageState extends State { late InsuranceViewModel insuranceViewModel; - late AppState appState; @override diff --git a/lib/presentation/insurance/widgets/insurance_approval_card.dart b/lib/presentation/insurance/widgets/insurance_approval_card.dart new file mode 100644 index 0000000..71fce58 --- /dev/null +++ b/lib/presentation/insurance/widgets/insurance_approval_card.dart @@ -0,0 +1,129 @@ +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/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/insurance/models/resp_models/patient_insurance_approval_response_model.dart'; +import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/patient_appointment_history_response_model.dart'; +import 'package:hmg_patient_app_new/features/my_appointments/utils/appointment_type.dart'; +import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; +import 'package:hmg_patient_app_new/theme/colors.dart'; +import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; +import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart'; + +class InsuranceApprovalCard extends StatelessWidget { + InsuranceApprovalCard({super.key, required this.insuranceApprovalResponseModel, required this.isLoading, required this.appState}); + + InsuranceApprovalResponseModel insuranceApprovalResponseModel; + AppState appState; + bool isLoading; + + @override + Widget build(BuildContext context) { + return Container( + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: AppColors.whiteColor, + borderRadius: 20.h, + hasShadow: true, + ), + child: Padding( + padding: EdgeInsets.all(14.h), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Wrap( + alignment: WrapAlignment.start, + direction: Axis.horizontal, + spacing: 6.h, + runSpacing: 6.h, + children: [ + AppCustomChipWidget( + labelText: isLoading ? "Approved" : insuranceApprovalResponseModel.approvalStatusDescption, + backgroundColor: insuranceApprovalResponseModel.status == 9 ? AppColors.successColor.withOpacity(0.1) : AppColors.primaryRedColor.withOpacity(0.1), + textColor: insuranceApprovalResponseModel.status == 9 ? AppColors.successColor : AppColors.primaryRedColor, + ).toShimmer2(isShow: isLoading), + AppCustomChipWidget( + icon: isLoading ? AppAssets.walkin_appointment_icon : (!insuranceApprovalResponseModel.isLiveCareAppointment! ? AppAssets.walkin_appointment_icon : AppAssets.small_livecare_icon), + iconColor: isLoading + ? AppColors.textColor + : !insuranceApprovalResponseModel.isLiveCareAppointment! + ? AppColors.textColor + : AppColors.whiteColor, + labelText: isLoading + ? "Walk In" + : insuranceApprovalResponseModel.isLiveCareAppointment! + ? LocaleKeys.livecare.tr(context: context) + : "Walk In".needTranslation, + backgroundColor: isLoading ? AppColors.greyColor : (!insuranceApprovalResponseModel.isLiveCareAppointment! ? AppColors.greyColor : AppColors.successColor), + textColor: isLoading ? AppColors.textColor : (!insuranceApprovalResponseModel.isLiveCareAppointment! ? AppColors.textColor : AppColors.whiteColor), + ).toShimmer2(isShow: isLoading), + AppCustomChipWidget( + labelText: isLoading + ? "OutPatient" + : appState.isArabic() + ? insuranceApprovalResponseModel.isInOutPatientDescriptionN! + : insuranceApprovalResponseModel.isInOutPatientDescription!, + backgroundColor: AppColors.primaryRedColor.withOpacity(0.1), + textColor: AppColors.primaryRedColor, + ).toShimmer2(isShow: isLoading), + ], + ).toShimmer2(isShow: isLoading), + SizedBox(height: 16.h), + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Image.network( + isLoading ? "https://hmgwebservices.com/Images/MobileImages/OALAY/1439.png" : insuranceApprovalResponseModel.doctorImageURL!, + width: 63.h, + height: 63.h, + fit: BoxFit.cover, + ).circle(100).toShimmer2(isShow: isLoading), + SizedBox(width: 16.h), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + (isLoading ? "John Smith" : insuranceApprovalResponseModel.doctorName!).toText16(isBold: true).toShimmer2(isShow: isLoading), + Wrap( + direction: Axis.horizontal, + spacing: 3.h, + runSpacing: 4.h, + children: [ + // AppCustomChipWidget(labelText: isLoading ? "Approved" : insuranceApprovalResponseModel.approvalStatusDescption!, + // backgroundColor: insuranceApprovalResponseModel.status == 9 ? AppColors.successColor.withValues(alpha: 0.1) : AppColors.primaryRedColor.withValues(alpha: 0.1), + // textColor: insuranceApprovalResponseModel.status == 9 ? AppColors.successColor : AppColors.primaryRedColor, + // ).toShimmer2(isShow: isLoading), + AppCustomChipWidget(labelText: isLoading ? "Cardiology" : insuranceApprovalResponseModel.clinicName!).toShimmer2(isShow: isLoading), + AppCustomChipWidget( + icon: AppAssets.doctor_calendar_icon, + labelText: isLoading ? "Cardiology" : DateUtil.formatDateToDate(DateUtil.convertStringToDate(insuranceApprovalResponseModel.submitOn), false)) + .toShimmer2(isShow: isLoading), + isLoading + ? SizedBox.shrink() + : AppCustomChipWidget( + labelText: isLoading ? LocaleKeys.approvalNo.tr(context: context) : "${LocaleKeys.approvalNo.tr(context: context)} ${insuranceApprovalResponseModel.approvalNo}") + .toShimmer2(isShow: isLoading), + ], + ), + ], + ), + ), + ], + ), + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Transform.flip( + flipX: appState.isArabic(), child: Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_icon_small, width: 15.h, height: 15.h, fit: BoxFit.contain, iconColor: AppColors.textColor)), + ], + ), + ], + ), + ), + ); + } +} diff --git a/lib/presentation/insurance/widgets/insurance_approval_details_page.dart b/lib/presentation/insurance/widgets/insurance_approval_details_page.dart new file mode 100644 index 0000000..c9b1517 --- /dev/null +++ b/lib/presentation/insurance/widgets/insurance_approval_details_page.dart @@ -0,0 +1,135 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:hmg_patient_app_new/core/app_assets.dart'; +import 'package:hmg_patient_app_new/core/app_export.dart'; +import 'package:hmg_patient_app_new/core/app_state.dart'; +import 'package:hmg_patient_app_new/core/dependencies.dart'; +import 'package:hmg_patient_app_new/core/utils/date_util.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/insurance/insurance_view_model.dart'; +import 'package:hmg_patient_app_new/features/insurance/models/resp_models/patient_insurance_approval_response_model.dart'; +import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; +import 'package:hmg_patient_app_new/theme/colors.dart'; +import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart'; +import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart'; +import 'package:provider/provider.dart'; + +class InsuranceApprovalDetailsPage extends StatelessWidget { + InsuranceApprovalDetailsPage({super.key, required this.insuranceApprovalResponseModel}); + + InsuranceApprovalResponseModel insuranceApprovalResponseModel; + late InsuranceViewModel insuranceViewModel; + late AppState appState; + + @override + Widget build(BuildContext context) { + appState = getIt.get(); + insuranceViewModel = Provider.of(context, listen: false); + return CollapsingListView( + title: "${LocaleKeys.insurance.tr(context: context)} ${LocaleKeys.approvals.tr(context: context)}", + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: AppColors.whiteColor, + borderRadius: 24.h, + hasShadow: true, + ), + child: Padding( + padding: EdgeInsets.all(14.h), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Wrap( + alignment: WrapAlignment.start, + direction: Axis.horizontal, + spacing: 6.h, + runSpacing: 6.h, + children: [ + AppCustomChipWidget( + labelText: insuranceApprovalResponseModel.approvalStatusDescption, + backgroundColor: insuranceApprovalResponseModel.status == 9 ? AppColors.successColor.withOpacity(0.1) : AppColors.primaryRedColor.withOpacity(0.1), + textColor: insuranceApprovalResponseModel.status == 9 ? AppColors.successColor : AppColors.primaryRedColor, + ), + AppCustomChipWidget( + icon: (!insuranceApprovalResponseModel.isLiveCareAppointment! ? AppAssets.walkin_appointment_icon : AppAssets.small_livecare_icon), + iconColor: !insuranceApprovalResponseModel.isLiveCareAppointment! ? AppColors.textColor : AppColors.whiteColor, + labelText: insuranceApprovalResponseModel.isLiveCareAppointment! ? LocaleKeys.livecare.tr(context: context) : "Walk In".needTranslation, + backgroundColor: (!insuranceApprovalResponseModel.isLiveCareAppointment! ? AppColors.greyColor : AppColors.successColor), + textColor: (!insuranceApprovalResponseModel.isLiveCareAppointment! ? AppColors.textColor : AppColors.whiteColor), + ), + AppCustomChipWidget( + labelText: appState.isArabic() ? insuranceApprovalResponseModel.isInOutPatientDescriptionN! : insuranceApprovalResponseModel.isInOutPatientDescription!, + backgroundColor: AppColors.primaryRedColor.withOpacity(0.1), + textColor: AppColors.primaryRedColor, + ), + ], + ), + SizedBox(height: 16.h), + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Image.network( + insuranceApprovalResponseModel.doctorImageURL!, + width: 63.h, + height: 63.h, + fit: BoxFit.cover, + ).circle(100), + SizedBox(width: 16.h), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + (insuranceApprovalResponseModel.doctorName!).toText16(isBold: true), + Wrap( + direction: Axis.horizontal, + spacing: 3.h, + runSpacing: 4.h, + children: [ + AppCustomChipWidget(labelText: insuranceApprovalResponseModel.clinicName!), + AppCustomChipWidget(labelText: "${LocaleKeys.approvalNo.tr(context: context)} ${insuranceApprovalResponseModel.approvalNo}"), + AppCustomChipWidget(labelText: "${LocaleKeys.unusedCount.tr(context: context)} ${insuranceApprovalResponseModel.unUsedCount}"), + AppCustomChipWidget(labelText: "${LocaleKeys.companyName.tr(context: context)} ${insuranceApprovalResponseModel.companyName}"), + AppCustomChipWidget( + labelText: + "${LocaleKeys.receiptOn.tr(context: context)} ${DateUtil.formatDateToDate(DateUtil.convertStringToDate(insuranceApprovalResponseModel.receiptOn), false)}"), + AppCustomChipWidget( + labelText: + "${LocaleKeys.expiryOn.tr(context: context)} ${DateUtil.formatDateToDate(DateUtil.convertStringToDate(insuranceApprovalResponseModel.expiryDate), false)}"), + ], + ), + ], + ), + ), + ], + ), + ], + ), + ), + ), + SizedBox(height: 16.h), + Container( + width: double.infinity, + height: 200.h, + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: AppColors.whiteColor, + borderRadius: 24.h, + hasShadow: true, + ), + child: Padding( + padding: EdgeInsets.all(16.h), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + "Approval Details".toText16(isBold: true), + ], + ), + ), + ) + ], + ).paddingSymmetrical(24.h, 24.h), + ); + } +} diff --git a/lib/presentation/lab/lab_orders_page.dart b/lib/presentation/lab/lab_orders_page.dart index fcaaa71..ef8d77d 100644 --- a/lib/presentation/lab/lab_orders_page.dart +++ b/lib/presentation/lab/lab_orders_page.dart @@ -1 +1 @@ -import 'dart:async'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_staggered_animations/flutter_staggered_animations.dart'; import 'package:hmg_patient_app_new/core/app_state.dart'; import 'package:hmg_patient_app_new/core/dependencies.dart'; import 'package:hmg_patient_app_new/core/enums.dart'; import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; import 'package:hmg_patient_app_new/core/utils/utils.dart'; import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; import 'package:hmg_patient_app_new/features/lab/models/resp_models/patient_lab_orders_response_model.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/features/lab/lab_view_model.dart'; import 'package:hmg_patient_app_new/presentation/lab/lab_order_by_test.dart'; import 'package:hmg_patient_app_new/presentation/lab/lab_result_item_view.dart'; import 'package:hmg_patient_app_new/presentation/lab/lab_result_via_clinic/LabResultByClinic.dart'; import 'package:hmg_patient_app_new/presentation/lab/search_lab_report.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/widgets/chip/custom_chip_widget.dart'; import 'package:hmg_patient_app_new/widgets/date_range_selector/viewmodel/date_range_view_model.dart'; import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart'; import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart'; import 'package:provider/provider.dart'; import 'package:hmg_patient_app_new/widgets/custom_tab_bar.dart'; import '../../widgets/appbar/collapsing_list_view.dart'; class LabOrdersPage extends StatefulWidget { const LabOrdersPage({super.key}); @override State createState() => _LabOrdersPageState(); } class _LabOrdersPageState extends State { late LabViewModel labProvider; late DateRangeSelectorRangeViewModel rangeViewModel; late AppState _appState; List?> labSuggestions = []; int? expandedIndex; String? selectedFilterText = ''; int activeIndex = 0; @override void initState() { scheduleMicrotask(() { labProvider.initLabProvider(); }); super.initState(); } @override Widget build(BuildContext context) { labProvider = Provider.of(context); rangeViewModel = Provider.of(context); _appState = getIt(); return Scaffold( backgroundColor: AppColors.bgScaffoldColor, body: CollapsingListView( title: LocaleKeys.labResults.tr(), search: () async { final lavVM = Provider.of(context, listen: false); if (lavVM.isLabOrdersLoading) { return; } else { String? value = await Navigator.of(context).push( CustomPageRoute( page: SearchLabResultsContent(labSuggestionsList: lavVM.labSuggestions), fullScreenDialog: true, direction: AxisDirection.down, ), ); if (value != null) { selectedFilterText = value; lavVM.filterLabReports(value); } } }, child: SingleChildScrollView( padding: EdgeInsets.all(24.h), physics: NeverScrollableScrollPhysics(), child: Consumer( builder: (context, model, child) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox(height: 16.h), CustomTabBar( activeTextColor: Color(0xffED1C2B), activeBackgroundColor: Color(0xffED1C2B).withValues(alpha: .1), tabs: [ CustomTabBarModel(null, "By Visit".needTranslation), CustomTabBarModel(null, "By Test".needTranslation), // CustomTabBarModel(null, "Completed".needTranslation), ], onTabChange: (index) { activeIndex = index; setState(() {}); }, ), SizedBox(height: 16.h), selectedFilterText!.isNotEmpty ? CustomChipWidget( chipText: selectedFilterText!, chipType: ChipTypeEnum.alert, isSelected: true, ) : SizedBox(), activeIndex == 0 ? ListView.builder( shrinkWrap: true, physics: NeverScrollableScrollPhysics(), padding: EdgeInsets.zero, itemCount: model.isLabOrdersLoading ? 5 : model.patientLabOrders.isNotEmpty ? model.patientLabOrders.length : 1, itemBuilder: (context, index) { final isExpanded = expandedIndex == index; return model.isLabOrdersLoading ? LabResultItemView( onTap: () {}, labOrder: null, index: index, isLoading: true, ) : model.patientLabOrders.isNotEmpty ? AnimationConfiguration.staggeredList( position: index, duration: const Duration(milliseconds: 500), child: SlideAnimation( verticalOffset: 100.0, child: FadeInAnimation( child: LabResultItemView( onTap: () { model.currentlySelectedPatientOrder = model.patientLabOrders[ index]; labProvider.getPatientLabResultByHospital(model.patientLabOrders[ index]); labProvider .getPatientSpecialResult( model.patientLabOrders[ index]); Navigator.push( context, CustomPageRoute( page: LabResultByClinic(), )); }, labOrder: model.patientLabOrders[index], index: index, isExpanded: isExpanded), ), ), ) : Utils.getNoDataWidget(context, noDataText: "You don't have any lab results yet.".needTranslation); }, ) : ListView.builder( shrinkWrap: true, physics: NeverScrollableScrollPhysics(), padding: EdgeInsets.zero, itemCount: model.isLabOrdersLoading ? 5 : model.uniqueTests.toList().isNotEmpty ? model.uniqueTests.toList().length : 1, itemBuilder: (context, index) { final isExpanded = expandedIndex == index; return model.isLabOrdersLoading ? LabResultItemView( onTap: () {}, labOrder: null, index: index, isLoading: true, ) : model.uniqueTests.toList().isNotEmpty ? AnimationConfiguration.staggeredList( position: index, duration: const Duration(milliseconds: 500), child: SlideAnimation( verticalOffset: 100.0, child: FadeInAnimation( child: LabOrderByTest( appState: _appState, onTap: () { if (model.uniqueTests.toList()[index].model != null) { rangeViewModel.flush(); model.getPatientLabResult(model.uniqueTests.toList()[index].model!, model.uniqueTests.toList()[index].description!, (_appState.isArabic() ? model.uniqueTests.toList()[index].testDescriptionAr! : model.uniqueTests.toList()[index].testDescriptionEn!)); } }, tests: model.uniqueTests.toList()[index], index: index, isExpanded: isExpanded)), ), ) : Utils.getNoDataWidget(context, noDataText: "You don't have any lab results yet.".needTranslation); }, ) ], ); }, ), ), )); } Color getLabOrderStatusColor(num status) { switch (status) { case 44: return AppColors.warningColorYellow; case 45: return AppColors.warningColorYellow; case 16: return AppColors.successColor; case 17: return AppColors.successColor; default: return AppColors.greyColor; } } String getLabOrderStatusText(num status) { switch (status) { case 44: return LocaleKeys.resultsPending.tr(context: context); case 45: return LocaleKeys.resultsPending.tr(context: context); case 16: return LocaleKeys.resultsAvailable.tr(context: context); case 17: return LocaleKeys.resultsAvailable.tr(context: context); default: return ""; } } getLabSuggestions(LabViewModel model) { if (model.patientLabOrders.isEmpty) { return []; } return model.patientLabOrders.map((m) => m.testDetails).toList(); } } \ No newline at end of file +import 'dart:async'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_staggered_animations/flutter_staggered_animations.dart'; import 'package:hmg_patient_app_new/core/app_state.dart'; import 'package:hmg_patient_app_new/core/dependencies.dart'; import 'package:hmg_patient_app_new/core/enums.dart'; import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; import 'package:hmg_patient_app_new/core/utils/utils.dart'; import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; import 'package:hmg_patient_app_new/features/lab/models/resp_models/patient_lab_orders_response_model.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/features/lab/lab_view_model.dart'; import 'package:hmg_patient_app_new/presentation/lab/lab_order_by_test.dart'; import 'package:hmg_patient_app_new/presentation/lab/lab_result_item_view.dart'; import 'package:hmg_patient_app_new/presentation/lab/lab_result_via_clinic/LabResultByClinic.dart'; import 'package:hmg_patient_app_new/presentation/lab/search_lab_report.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/widgets/chip/custom_chip_widget.dart'; import 'package:hmg_patient_app_new/widgets/date_range_selector/viewmodel/date_range_view_model.dart'; import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart'; import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart'; import 'package:provider/provider.dart'; import 'package:hmg_patient_app_new/widgets/custom_tab_bar.dart'; import '../../widgets/appbar/collapsing_list_view.dart'; class LabOrdersPage extends StatefulWidget { const LabOrdersPage({super.key}); @override State createState() => _LabOrdersPageState(); } class _LabOrdersPageState extends State { late LabViewModel labProvider; late DateRangeSelectorRangeViewModel rangeViewModel; late AppState _appState; List?> labSuggestions = []; int? expandedIndex; String? selectedFilterText = ''; int activeIndex = 0; @override void initState() { scheduleMicrotask(() { labProvider.initLabProvider(); }); super.initState(); } @override Widget build(BuildContext context) { labProvider = Provider.of(context, listen: false); rangeViewModel = Provider.of(context); _appState = getIt(); return Scaffold( backgroundColor: AppColors.bgScaffoldColor, body: CollapsingListView( title: LocaleKeys.labResults.tr(), search: () async { final lavVM = Provider.of(context, listen: false); if (lavVM.isLabOrdersLoading) { return; } else { String? value = await Navigator.of(context).push( CustomPageRoute( page: SearchLabResultsContent(labSuggestionsList: lavVM.labSuggestions), fullScreenDialog: true, direction: AxisDirection.down, ), ); if (value != null) { selectedFilterText = value; lavVM.filterLabReports(value); } } }, child: SingleChildScrollView( padding: EdgeInsets.all(24.h), physics: NeverScrollableScrollPhysics(), child: Consumer( builder: (context, model, child) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox(height: 16.h), CustomTabBar( activeTextColor: Color(0xffED1C2B), activeBackgroundColor: Color(0xffED1C2B).withValues(alpha: .1), tabs: [ CustomTabBarModel(null, "By Visit".needTranslation), CustomTabBarModel(null, "By Test".needTranslation), // CustomTabBarModel(null, "Completed".needTranslation), ], onTabChange: (index) { activeIndex = index; setState(() {}); }, ), SizedBox(height: 16.h), selectedFilterText!.isNotEmpty ? CustomChipWidget( chipText: selectedFilterText!, chipType: ChipTypeEnum.alert, isSelected: true, ) : SizedBox(), activeIndex == 0 ? ListView.builder( shrinkWrap: true, physics: NeverScrollableScrollPhysics(), padding: EdgeInsets.zero, itemCount: model.isLabOrdersLoading ? 5 : model.patientLabOrders.isNotEmpty ? model.patientLabOrders.length : 1, itemBuilder: (context, index) { final isExpanded = expandedIndex == index; return model.isLabOrdersLoading ? LabResultItemView( onTap: () {}, labOrder: null, index: index, isLoading: true, ) : model.patientLabOrders.isNotEmpty ? AnimationConfiguration.staggeredList( position: index, duration: const Duration(milliseconds: 500), child: SlideAnimation( verticalOffset: 100.0, child: FadeInAnimation( child: LabResultItemView( onTap: () { model.currentlySelectedPatientOrder = model.patientLabOrders[ index]; labProvider.getPatientLabResultByHospital(model.patientLabOrders[ index]); labProvider .getPatientSpecialResult( model.patientLabOrders[ index]); Navigator.push( context, CustomPageRoute( page: LabResultByClinic(), )); }, labOrder: model.patientLabOrders[index], index: index, isExpanded: isExpanded), ), ), ) : Utils.getNoDataWidget(context, noDataText: "You don't have any lab results yet.".needTranslation); }, ) : ListView.builder( shrinkWrap: true, physics: NeverScrollableScrollPhysics(), padding: EdgeInsets.zero, itemCount: model.isLabOrdersLoading ? 5 : model.uniqueTests.toList().isNotEmpty ? model.uniqueTests.toList().length : 1, itemBuilder: (context, index) { final isExpanded = expandedIndex == index; return model.isLabOrdersLoading ? LabResultItemView( onTap: () {}, labOrder: null, index: index, isLoading: true, ) : model.uniqueTests.toList().isNotEmpty ? AnimationConfiguration.staggeredList( position: index, duration: const Duration(milliseconds: 500), child: SlideAnimation( verticalOffset: 100.0, child: FadeInAnimation( child: LabOrderByTest( appState: _appState, onTap: () { if (model.uniqueTests.toList()[index].model != null) { rangeViewModel.flush(); model.getPatientLabResult(model.uniqueTests.toList()[index].model!, model.uniqueTests.toList()[index].description!, (_appState.isArabic() ? model.uniqueTests.toList()[index].testDescriptionAr! : model.uniqueTests.toList()[index].testDescriptionEn!)); } }, tests: model.uniqueTests.toList()[index], index: index, isExpanded: isExpanded)), ), ) : Utils.getNoDataWidget(context, noDataText: "You don't have any lab results yet.".needTranslation); }, ) ], ); }, ), ), )); } Color getLabOrderStatusColor(num status) { switch (status) { case 44: return AppColors.warningColorYellow; case 45: return AppColors.warningColorYellow; case 16: return AppColors.successColor; case 17: return AppColors.successColor; default: return AppColors.greyColor; } } String getLabOrderStatusText(num status) { switch (status) { case 44: return LocaleKeys.resultsPending.tr(context: context); case 45: return LocaleKeys.resultsPending.tr(context: context); case 16: return LocaleKeys.resultsAvailable.tr(context: context); case 17: return LocaleKeys.resultsAvailable.tr(context: context); default: return ""; } } getLabSuggestions(LabViewModel model) { if (model.patientLabOrders.isEmpty) { return []; } return model.patientLabOrders.map((m) => m.testDetails).toList(); } } \ No newline at end of file diff --git a/lib/presentation/medical_file/medical_file_page.dart b/lib/presentation/medical_file/medical_file_page.dart index dd80513..1eff927 100644 --- a/lib/presentation/medical_file/medical_file_page.dart +++ b/lib/presentation/medical_file/medical_file_page.dart @@ -8,7 +8,6 @@ import 'package:hmg_patient_app_new/core/app_export.dart'; import 'package:hmg_patient_app_new/core/app_state.dart'; import 'package:hmg_patient_app_new/core/dependencies.dart'; import 'package:hmg_patient_app_new/core/utils/date_util.dart'; -import 'package:hmg_patient_app_new/core/utils/request_utils.dart'; import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; import 'package:hmg_patient_app_new/core/utils/utils.dart'; import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; @@ -16,6 +15,7 @@ import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_view_model.dart'; import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/doctors_list_response_model.dart'; import 'package:hmg_patient_app_new/features/insurance/insurance_view_model.dart'; +import 'package:hmg_patient_app_new/features/lab/lab_view_model.dart'; import 'package:hmg_patient_app_new/features/medical_file/medical_file_view_model.dart'; import 'package:hmg_patient_app_new/features/medical_file/models/family_file_response_model.dart'; import 'package:hmg_patient_app_new/features/medical_file/models/patient_sickleave_response_model.dart'; @@ -27,10 +27,15 @@ import 'package:hmg_patient_app_new/presentation/appointments/my_appointments_pa import 'package:hmg_patient_app_new/presentation/appointments/my_doctors_page.dart'; import 'package:hmg_patient_app_new/presentation/book_appointment/book_appointment_page.dart'; import 'package:hmg_patient_app_new/presentation/book_appointment/widgets/appointment_calendar.dart'; +import 'package:hmg_patient_app_new/presentation/insurance/insurance_approvals_page.dart'; import 'package:hmg_patient_app_new/presentation/insurance/insurance_home_page.dart'; import 'package:hmg_patient_app_new/presentation/insurance/widgets/insurance_update_details_card.dart'; import 'package:hmg_patient_app_new/presentation/insurance/widgets/patient_insurance_card.dart'; +import 'package:hmg_patient_app_new/presentation/lab/lab_orders_page.dart'; import 'package:hmg_patient_app_new/presentation/lab/lab_result_item_view.dart'; +import 'package:hmg_patient_app_new/presentation/my_family/my_family.dart'; +import 'package:hmg_patient_app_new/services/dialog_service.dart'; +import 'package:hmg_patient_app_new/presentation/radiology/radiology_orders_page.dart'; import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart'; import 'package:hmg_patient_app_new/presentation/medical_report/medical_reports_page.dart'; import 'package:hmg_patient_app_new/presentation/medical_file/patient_sickleaves_list_page.dart'; @@ -38,8 +43,6 @@ import 'package:hmg_patient_app_new/presentation/medical_file/vaccine_list_page. import 'package:hmg_patient_app_new/presentation/medical_file/widgets/lab_rad_card.dart'; import 'package:hmg_patient_app_new/presentation/medical_file/widgets/medical_file_card.dart'; import 'package:hmg_patient_app_new/presentation/medical_file/widgets/patient_sick_leave_card.dart'; -import 'package:hmg_patient_app_new/presentation/my_family/my_family.dart'; -import 'package:hmg_patient_app_new/presentation/my_family/widget/my_family_sheet.dart'; import 'package:hmg_patient_app_new/presentation/prescriptions/prescriptions_list_page.dart'; import 'package:hmg_patient_app_new/services/navigation_service.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; @@ -69,17 +72,19 @@ class _MedicalFilePageState extends State { late MyAppointmentsViewModel myAppointmentsViewModel; late MedicalFileViewModel medicalFileViewModel; late BookAppointmentsViewModel bookAppointmentsViewModel; + late LabViewModel labViewModel; int currentIndex = 0; @override void initState() { + appState = getIt.get(); scheduleMicrotask(() { if (appState.isAuthenticated) { + labViewModel.initLabProvider(); insuranceViewModel.initInsuranceProvider(); medicalFileViewModel.setIsPatientSickLeaveListLoading(true); medicalFileViewModel.getPatientSickLeaveList(); - // medicalFileViewModel.getFamilyFiles(); medicalFileViewModel.onTabChanged(0); } }); @@ -88,121 +93,145 @@ class _MedicalFilePageState extends State { @override Widget build(BuildContext context) { + labViewModel = Provider.of(context, listen: false); insuranceViewModel = Provider.of(context, listen: false); myAppointmentsViewModel = Provider.of(context, listen: false); medicalFileViewModel = Provider.of(context, listen: false); bookAppointmentsViewModel = Provider.of(context, listen: false); - appState = getIt.get(); NavigationService navigationService = getIt.get(); - return Scaffold( - backgroundColor: AppColors.bgScaffoldColor, - body: CollapsingListView( - title: LocaleKeys.medicalFile.tr(context: context), - isLeading: false, - child: SingleChildScrollView( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - SizedBox(height: 16.h), - TextInputWidget( - labelText: LocaleKeys.search.tr(context: context), - hintText: "Type any record", - controller: TextEditingController(), - keyboardType: TextInputType.number, - isEnable: true, - prefix: null, - autoFocus: false, - isBorderAllowed: false, - isAllowLeadingIcon: true, - padding: EdgeInsets.symmetric(vertical: 8.h, horizontal: 8.h), - leadingIcon: AppAssets.student_card, - ).paddingSymmetrical(24.h, 0.0), - SizedBox(height: 16.h), - Container( - width: double.infinity, - decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24), - child: Padding( - padding: EdgeInsets.all(16.h), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Image.asset(appState.getAuthenticatedUser()?.gender == 1 ? AppAssets.male_img : AppAssets.femaleImg, width: 56.h, height: 56.h), - SizedBox(width: 8.h), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - "${appState.getAuthenticatedUser()!.firstName} ${appState.getAuthenticatedUser()!.lastName}".toText18(isBold: true), - SizedBox(height: 4.h), - Wrap( - direction: Axis.horizontal, - spacing: 4.h, - runSpacing: 4.h, - children: [ - AppCustomChipWidget( - icon: AppAssets.file_icon, - labelText: "${LocaleKeys.fileNo.tr(context: context)}: ${appState.getAuthenticatedUser()!.patientId}", - ), - AppCustomChipWidget( - icon: AppAssets.checkmark_icon, - labelText: LocaleKeys.verified.tr(context: context), - iconColor: AppColors.successColor, - ), - ], - ), - ], - ) - ], - ), - SizedBox(height: 16.h), - Divider(color: AppColors.dividerColor, height: 1.h), - SizedBox(height: 16.h), - Wrap( - direction: Axis.horizontal, - spacing: 4.h, - runSpacing: 4.h, - children: [ - AppCustomChipWidget( - labelText: "${appState.getAuthenticatedUser()!.age} Years Old", - ), - AppCustomChipWidget( - icon: AppAssets.blood_icon, - labelText: "${LocaleKeys.bloodType.tr(context: context)}: ${appState.getUserBloodGroup}", - iconColor: AppColors.primaryRedColor, - ), - ], - ), - ], - ), - ), - ).paddingSymmetrical(24.h, 0.0), - SizedBox(height: 16.h), - Consumer(builder: (context, medicalFileVM, child) { - return Column( + return CollapsingListView( + title: "Medical File".needTranslation, + trailing: Row( + children: [ + Wrap(spacing: -15, children: [ + Utils.buildImgWithAssets(icon: AppAssets.babyGirlImg, width: 32.h, height: 32.h, border: 1.5.h, borderRadius: 50.h), + Utils.buildImgWithAssets(icon: AppAssets.femaleImg, width: 32.h, height: 32.h, border: 1.5.h, borderRadius: 50.h), + Utils.buildImgWithAssets(icon: AppAssets.male_img, width: 32.h, height: 32.h, border: 1.5.h, borderRadius: 50.h), + ]), + SizedBox(width: 4.h), + Utils.buildSvgWithAssets(icon: AppAssets.arrow_down) + ], + ).onPress(() { + DialogService dialogService = getIt.get(); + dialogService.showFamilyBottomSheetWithoutH( + label: "Who do you want to book for?".needTranslation, + message: "This clinic or doctor is only available for the below eligible profiles.".needTranslation, + onSwitchPress: (FamilyFileResponseModelLists profile) { + medicalFileViewModel.switchFamilyFiles(responseID: profile.responseId, patientID: profile.patientId, phoneNumber: profile.mobileNumber); + }, + profiles: medicalFileViewModel.patientFamilyFiles); + }), + isLeading: false, + child: SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox(height: 16.h), + TextInputWidget( + labelText: LocaleKeys.search.tr(context: context), + hintText: "Type any record".needTranslation, + controller: TextEditingController(), + keyboardType: TextInputType.number, + isEnable: true, + prefix: null, + autoFocus: false, + isBorderAllowed: false, + isAllowLeadingIcon: true, + padding: EdgeInsets.symmetric(vertical: 8.h, horizontal: 8.h), + leadingIcon: AppAssets.student_card, + ).paddingSymmetrical(24.h, 0.0), + SizedBox(height: 16.h), + Container( + width: double.infinity, + decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24), + child: Padding( + padding: EdgeInsets.all(16.h), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - CustomTabBar( - activeTextColor: Color(0xffED1C2B), - activeBackgroundColor: Color(0xffED1C2B).withValues(alpha: .1), - tabs: [ - CustomTabBarModel(AppAssets.myFilesBottom, LocaleKeys.general.tr(context: context).needTranslation), - CustomTabBarModel(AppAssets.insurance, LocaleKeys.insurance.tr(context: context)), - CustomTabBarModel(AppAssets.requests, LocaleKeys.request.tr(context: context).needTranslation), - CustomTabBarModel(AppAssets.more, "More".needTranslation), + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Image.asset(appState.getAuthenticatedUser()?.gender == 1 ? AppAssets.male_img : AppAssets.femaleImg, width: 56.h, height: 56.h), + SizedBox(width: 8.h), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + "${appState.getAuthenticatedUser()!.firstName} ${appState.getAuthenticatedUser()!.lastName}" + .toText18(isBold: true, weight: FontWeight.w600, textOverflow: TextOverflow.ellipsis, maxlines: 1), + SizedBox(height: 4.h), + Wrap( + direction: Axis.horizontal, + spacing: 4.h, + runSpacing: 4.h, + children: [ + AppCustomChipWidget( + icon: AppAssets.file_icon, + labelText: "${LocaleKeys.fileNo.tr(context: context)}: ${appState.getAuthenticatedUser()!.patientId}", + onChipTap: () { + navigationService.pushPage( + page: FamilyMedicalScreen( + profiles: medicalFileViewModel.patientFamilyFiles, + onSelect: (FamilyFileResponseModelLists p1) {}, + )); + }, + ), + AppCustomChipWidget( + icon: AppAssets.checkmark_icon, + labelText: LocaleKeys.verified.tr(context: context), + iconColor: AppColors.successColor, + ), + ], + ), + ], + ) + ], + ), + SizedBox(height: 16.h), + Divider(color: AppColors.dividerColor, height: 1.h), + SizedBox(height: 16.h), + Wrap( + direction: Axis.horizontal, + spacing: 4.h, + runSpacing: 4.h, + children: [ + AppCustomChipWidget( + labelText: "${appState.getAuthenticatedUser()!.age} Years Old", + ), + AppCustomChipWidget( + icon: AppAssets.blood_icon, + labelText: "${LocaleKeys.bloodType.tr(context: context)}: ${appState.getUserBloodGroup}", + iconColor: AppColors.primaryRedColor, + ), ], - onTabChange: (index) { - print(index); - medicalFileVM.onTabChanged(index); - }, - ).paddingSymmetrical(24.h, 0.0), - SizedBox(height: 24.h), - getSelectedTabData(medicalFileVM.selectedTabIndex), + ), ], - ); - }), - ], - ), + ), + ), + ).paddingSymmetrical(24.h, 0.0), + SizedBox(height: 16.h), + Consumer(builder: (context, medicalFileVM, child) { + return Column( + children: [ + CustomTabBar( + activeTextColor: AppColors.primaryRedColor, + activeBackgroundColor: AppColors.primaryRedColor.withValues(alpha: .1), + tabs: [ + CustomTabBarModel(AppAssets.myFilesBottom, LocaleKeys.general.tr(context: context).needTranslation), + CustomTabBarModel(AppAssets.insurance, LocaleKeys.insurance.tr(context: context)), + CustomTabBarModel(AppAssets.requests, LocaleKeys.request.tr(context: context).needTranslation), + CustomTabBarModel(AppAssets.more, "More".needTranslation), + ], + onTabChange: (index) { + medicalFileVM.onTabChanged(index); + }, + ).paddingSymmetrical(24.h, 0.0), + SizedBox(height: 24.h), + getSelectedTabData(medicalFileVM.selectedTabIndex), + ], + ); + }), + ], ), ), ); @@ -361,16 +390,32 @@ class _MedicalFilePageState extends State { child: LabRadCard( icon: AppAssets.lab_result_icon, labelText: LocaleKeys.labResults.tr(context: context), - labOrderTests: ["Complete blood count", "Creatinine", "Blood Sugar"], - ), + // labOrderTests: ["Complete blood count", "Creatinine", "Blood Sugar"], + labOrderTests: labViewModel.isLabOrdersLoading ? [] : labViewModel.labOrderTests, + isLoading: labViewModel.isLabOrdersLoading, + ).onPress(() { + Navigator.of(context).push( + CustomPageRoute( + page: LabOrdersPage(), + ), + ); + }), ), SizedBox(width: 16.h), Expanded( child: LabRadCard( icon: AppAssets.radiology_icon, labelText: LocaleKeys.radiology.tr(context: context), - labOrderTests: ["Chest X-ray", "Abdominal Ultrasound", "Dental X-ray"], - ), + // labOrderTests: ["Chest X-ray", "Abdominal Ultrasound", "Dental X-ray"], + labOrderTests: [], + isLoading: false, + ).onPress(() { + Navigator.of(context).push( + CustomPageRoute( + page: RadiologyOrdersPage(), + ), + ); + }), ), ], ).paddingSymmetrical(24.h, 0.h), @@ -408,7 +453,7 @@ class _MedicalFilePageState extends State { prescriptionVM.patientPrescriptionOrders[index].doctorImageURL!, width: 40.h, height: 40.h, - fit: BoxFit.fill, + fit: BoxFit.cover, ).circle(100), SizedBox(width: 16.h), Expanded( @@ -434,8 +479,9 @@ class _MedicalFilePageState extends State { ), // SizedBox(width: 40.h), Transform.flip( - flipX: appState.isArabic() ? true : false, - child: Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_icon, width: 15.h, height: 15.h, fit: BoxFit.contain, iconColor: AppColors.textColor)), + flipX: appState.isArabic(), + child: Utils.buildSvgWithAssets( + icon: AppAssets.forward_arrow_icon_small, width: 15.h, height: 15.h, fit: BoxFit.contain, iconColor: AppColors.textColor)), ], ).onPress(() { prescriptionVM.setPrescriptionsDetailsLoading(); @@ -469,7 +515,7 @@ class _MedicalFilePageState extends State { backgroundColor: AppColors.secondaryLightRedColor, borderColor: AppColors.secondaryLightRedColor, textColor: AppColors.primaryRedColor, - fontSize: 13, + fontSize: 12.3, fontWeight: FontWeight.w500, borderRadius: 12.h, height: 40.h, @@ -505,7 +551,7 @@ class _MedicalFilePageState extends State { decoration: RoundedRectangleBorder().toSmoothCornerDecoration( color: AppColors.whiteColor, borderRadius: 20.h, - hasShadow: true, + hasShadow: false, ), child: Utils.getNoDataWidget(context, noDataText: "You don't have any prescriptions yet.".needTranslation, isSmallWidget: true, width: 62, height: 62)) .paddingSymmetrical(24.h, 0.h); @@ -601,7 +647,7 @@ class _MedicalFilePageState extends State { decoration: RoundedRectangleBorder().toSmoothCornerDecoration( color: AppColors.whiteColor, borderRadius: 20.h, - hasShadow: true, + hasShadow: false, ), child: Utils.getNoDataWidget(context, noDataText: "You don't have any completed visits yet.".needTranslation, isSmallWidget: true, width: 62, height: 62), ).paddingSymmetrical(24.h, 0.h); @@ -682,7 +728,7 @@ class _MedicalFilePageState extends State { decoration: RoundedRectangleBorder().toSmoothCornerDecoration( color: AppColors.whiteColor, borderRadius: 20.h, - hasShadow: true, + hasShadow: false, ), child: Utils.getNoDataWidget( context, @@ -740,12 +786,18 @@ class _MedicalFilePageState extends State { ); }), MedicalFileCard( - label: "Insurance Approvals".needTranslation, + label: "${LocaleKeys.insurance.tr(context: context)} ${LocaleKeys.approvals.tr(context: context)}", textColor: AppColors.blackColor, backgroundColor: AppColors.whiteColor, svgIcon: AppAssets.eye_result_icon, isLargeText: true, - iconSize: 36.h), + iconSize: 36.h).onPress(() { + Navigator.of(context).push( + CustomPageRoute( + page: InsuranceApprovalsPage(), + ), + ); + }), MedicalFileCard( label: "My Invoices List".needTranslation, textColor: AppColors.blackColor, @@ -780,13 +832,20 @@ class _MedicalFilePageState extends State { patientSickLeavesResponseModel: medicalFileVM.patientSickLeaveList.first, isLoading: false, ).paddingSymmetrical(24.h, 0.0) - : Utils.getNoDataWidget( - context, - noDataText: "You don't have any sick leaves yet.".needTranslation, - isSmallWidget: true, - width: 62, - height: 62, - ); + : Container( + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: AppColors.whiteColor, + borderRadius: 20.h, + hasShadow: false, + ), + child: Utils.getNoDataWidget( + context, + noDataText: "You don't have any sick leaves yet.".needTranslation, + isSmallWidget: true, + width: 62, + height: 62, + ), + ).paddingSymmetrical(24.h, 0.h); }), SizedBox(height: 16.h), GridView( diff --git a/lib/presentation/medical_file/vaccine_list_page.dart b/lib/presentation/medical_file/vaccine_list_page.dart index 82c9441..777426f 100644 --- a/lib/presentation/medical_file/vaccine_list_page.dart +++ b/lib/presentation/medical_file/vaccine_list_page.dart @@ -77,7 +77,7 @@ class _VaccineListPageState extends State { "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png", width: 63.h, height: 63.h, - fit: BoxFit.fill, + fit: BoxFit.cover, ).circle(100).toShimmer2(isShow: true), SizedBox(width: 16.h), Expanded( diff --git a/lib/presentation/medical_file/widgets/lab_rad_card.dart b/lib/presentation/medical_file/widgets/lab_rad_card.dart index e396d62..c7fd7dd 100644 --- a/lib/presentation/medical_file/widgets/lab_rad_card.dart +++ b/lib/presentation/medical_file/widgets/lab_rad_card.dart @@ -9,11 +9,12 @@ import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; class LabRadCard extends StatelessWidget { - LabRadCard({super.key, required this.icon, required this.labelText, required this.labOrderTests}); + LabRadCard({super.key, required this.icon, required this.labelText, required this.labOrderTests, required this.isLoading}); String icon; String labelText; List labOrderTests = []; + bool isLoading; @override Widget build(BuildContext context) { @@ -36,24 +37,26 @@ class LabRadCard extends StatelessWidget { ], ), SizedBox(height: 16.h), - ListView.separated( - scrollDirection: Axis.vertical, - padding: EdgeInsets.zero, - physics: NeverScrollableScrollPhysics(), - shrinkWrap: true, + labOrderTests.isNotEmpty + ? ListView.separated( + scrollDirection: Axis.vertical, + padding: EdgeInsets.zero, + physics: NeverScrollableScrollPhysics(), + shrinkWrap: true, itemBuilder: (cxt, index) { - return labOrderTests[index].toText12(isBold: true, maxLine: 1).toShimmer2(isShow: false, radius: 6.h, height: 24.h, width: 120.h); - }, - separatorBuilder: (cxt, index) => SizedBox(height: 8.h), - itemCount: labOrderTests.length, - ), + return labOrderTests[index].toText12(isBold: true, maxLine: 1).toShimmer2(isShow: false, radius: 6.h, height: 24.h, width: 120.h).toShimmer2(isShow: isLoading); + }, + separatorBuilder: (cxt, index) => SizedBox(height: 8.h), + itemCount: 3, + ) + : "You don't have any records yet".needTranslation.toText12(fontWeight: FontWeight.w500, color: AppColors.greyTextColor, isCenter: true), SizedBox(height: 16.h), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ SizedBox.shrink(), Transform.flip( - flipX: appState.isArabic() ? true : false, + flipX: appState.isArabic(), child: Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_icon_small, width: 15.h, height: 15.h, fit: BoxFit.contain, iconColor: AppColors.textColor) .toShimmer2(isShow: false, radius: 12.h), ), diff --git a/lib/presentation/medical_file/widgets/patient_sick_leave_card.dart b/lib/presentation/medical_file/widgets/patient_sick_leave_card.dart index 8fc3805..87794ef 100644 --- a/lib/presentation/medical_file/widgets/patient_sick_leave_card.dart +++ b/lib/presentation/medical_file/widgets/patient_sick_leave_card.dart @@ -141,7 +141,7 @@ class PatientSickLeaveCard extends StatelessWidget { child: Padding( padding: EdgeInsets.all(10.h), child: Transform.flip( - flipX: _appState.isArabic() ? true : false, + flipX: _appState.isArabic(), child: Utils.buildSvgWithAssets( icon: AppAssets.forward_arrow_icon_small, iconColor: AppColors.whiteColor, diff --git a/lib/presentation/medical_report/medical_report_request_page.dart b/lib/presentation/medical_report/medical_report_request_page.dart index 9d1bcb1..8eabcdd 100644 --- a/lib/presentation/medical_report/medical_report_request_page.dart +++ b/lib/presentation/medical_report/medical_report_request_page.dart @@ -3,6 +3,7 @@ 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/book_appointments/book_appointments_view_model.dart'; import 'package:hmg_patient_app_new/features/medical_file/medical_file_view_model.dart'; import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart'; import 'package:hmg_patient_app_new/presentation/appointments/widgets/appointment_card.dart'; @@ -42,6 +43,7 @@ class MedicalReportRequestPage extends StatelessWidget { child: AppointmentCard( patientAppointmentHistoryResponseModel: medicalFileViewModel.patientMedicalReportAppointmentHistoryList[index], myAppointmentsViewModel: Provider.of(context, listen: false), + bookAppointmentsViewModel: Provider.of(context, listen: false), medicalFileViewModel: medicalFileViewModel, isLoading: false, isFromHomePage: false, diff --git a/lib/presentation/my_family/my_family.dart b/lib/presentation/my_family/my_family.dart index 25f5f3c..8a04cd5 100644 --- a/lib/presentation/my_family/my_family.dart +++ b/lib/presentation/my_family/my_family.dart @@ -1,23 +1,29 @@ +import 'dart:convert'; + import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:hmg_patient_app_new/core/app_assets.dart'; import 'package:hmg_patient_app_new/core/app_export.dart'; +import 'package:hmg_patient_app_new/core/app_state.dart'; import 'package:hmg_patient_app_new/core/dependencies.dart'; import 'package:hmg_patient_app_new/core/enums.dart'; import 'package:hmg_patient_app_new/core/utils/validation_utils.dart'; import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; import 'package:hmg_patient_app_new/features/authentication/authentication_view_model.dart'; +import 'package:hmg_patient_app_new/features/medical_file/medical_file_view_model.dart'; import 'package:hmg_patient_app_new/features/medical_file/models/family_file_response_model.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/presentation/my_family/widget/family_cards.dart'; +import 'package:hmg_patient_app_new/services/dialog_service.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; -import 'package:hmg_patient_app_new/widgets/appbar/app_bar_widget.dart'; +import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart'; import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart'; import 'package:hmg_patient_app_new/widgets/custom_tab_bar.dart'; import 'package:hmg_patient_app_new/widgets/dropdown/country_dropdown_widget.dart'; import 'package:hmg_patient_app_new/widgets/input_widget.dart'; +import 'package:provider/provider.dart'; class FamilyMedicalScreen extends StatefulWidget { final List profiles; @@ -34,161 +40,95 @@ class FamilyMedicalScreen extends StatefulWidget { } class _FamilyMedicalScreenState extends State { - List tabs = [CustomTabBarModel("", LocaleKeys.medicalFile.tr()), CustomTabBarModel("", LocaleKeys.request.tr())]; + MedicalFileViewModel? medicalVM; + + @override + void initState() { + super.initState(); + medicalVM = context.read(); + } @override Widget build(BuildContext context) { - return Scaffold( - backgroundColor: AppColors.scaffoldBgColor, - appBar: CustomAppBar( - onBackPressed: () { - Navigator.of(context).pop(); - }, - onLanguageChanged: (lang) {}, - hideLogoAndLang: true, - ), - body: SingleChildScrollView( - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - LocaleKeys.myMedicalFile.tr().toText26(color: AppColors.textColor, weight: FontWeight.w600, letterSpacing: -2), - SizedBox(height: 25.h), - CustomTabBar( - tabs: tabs, - onTabChange: (int index) {}, - ), - SizedBox(height: 25.h), - FamilyCards(profiles: widget.profiles, onSelect: widget.onSelect, isShowDetails: true), - SizedBox(height: 20.h), - ], - ), - ).paddingSymmetrical(20, 0), - bottomSheet: Container( - decoration: RoundedRectangleBorder().toSmoothCornerDecoration( - color: AppColors.whiteColor, - customBorder: BorderRadius.only(topLeft: Radius.circular(24), topRight: Radius.circular(24)), - ), - padding: EdgeInsets.symmetric(vertical: 10.h, horizontal: 20.h), - child: CustomButton( - text: "Add a new family member", - onPressed: () { - showModelSheet(); + AppState appState = getIt.get(); + + return CollapsingListView( + title: "My Medical File".needTranslation, + bottomChild: appState.getAuthenticatedUser()!.isParentUser! + ? Container( + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: AppColors.whiteColor, + customBorder: BorderRadius.only(topLeft: Radius.circular(24), topRight: Radius.circular(24)), + ), + padding: EdgeInsets.symmetric(vertical: 10.h, horizontal: 20.h), + child: CustomButton( + text: "Add a new family member".needTranslation, + onPressed: () { + DialogService dialogService = getIt.get(); + dialogService.showAddFamilyFileSheet( + label: "Add Family Member".needTranslation, + message: "Please fill the below field to add a new family member to your profile".needTranslation, + onVerificationPress: () { + medicalVM!.addFamilyFile(otpTypeEnum: OTPTypeEnum.sms); + }); + }, + icon: AppAssets.add_icon, + height: 56.h, + fontSize: 16, + borderRadius: 12.h, + fontWeight: FontWeight.w500)) + : SizedBox(), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + CustomTabBar( + activeBackgroundColor: AppColors.secondaryLightRedColor, + activeTextColor: AppColors.primaryRedColor, + tabs: appState.isChildLoggedIn + ? [CustomTabBarModel(null, LocaleKeys.medicalFile.tr())] + : [CustomTabBarModel(null, LocaleKeys.medicalFile.tr()), CustomTabBarModel(null, LocaleKeys.request.tr())], + onTabChange: (index) { + medicalVM!.onFamilyFileTabChange(index); }, - icon: AppAssets.add_icon, - height: 56.h, - fontWeight: FontWeight.w600, - )), + ), + SizedBox(height: 25.h), + Selector(selector: (_, model) => model.getSelectedFamilyFileTabIndex, builder: (context, selectedIndex, child) => getFamilyTabs(index: selectedIndex)), + SizedBox(height: 20.h), + ], + ).paddingSymmetrical(20, 0), ); } - Future showModelSheet() async { - AuthenticationViewModel authVm = getIt.get(); - return await showCommonBottomSheetWithoutHeight(context, - title: "Add Family Member", - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.start, - children: [ - "Please fill the below field to add a new family member to your profile".toText16(color: AppColors.textColor, weight: FontWeight.w500), - SizedBox(height: 20.h), - Container( - decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(24)), - padding: EdgeInsets.symmetric(horizontal: 16.h, vertical: 8.h), - child: Column( - children: [ - CustomCountryDropdown( - countryList: CountryEnum.values, - onCountryChange: authVm.onCountryChange, - ).paddingOnly(top: 8.h, bottom: 16.h), - Divider( - height: 1.h, - color: AppColors.spacerLineColor, - ), - TextInputWidget( - labelText: LocaleKeys.nationalIdNumber.tr(), - hintText: "xxxxxxxxx", - controller: authVm.nationalIdController, - // focusNode: _nationalIdFocusNode, - isEnable: true, - prefix: null, - isAllowRadius: true, - isBorderAllowed: false, - isAllowLeadingIcon: true, - autoFocus: true, - keyboardType: TextInputType.number, - padding: EdgeInsets.symmetric(vertical: 8.h), - leadingIcon: AppAssets.student_card, - ).paddingOnly(top: 8.h, bottom: 8.h), - Divider( - height: 1.h, - color: AppColors.spacerLineColor, - ), - TextInputWidget( - labelText: LocaleKeys.phoneNumber.tr(), - hintText: "574345434", - controller: authVm.phoneNumberController, - isEnable: true, - prefix: authVm.selectedCountrySignup.countryCode, - isAllowRadius: true, - isBorderAllowed: false, - isAllowLeadingIcon: true, - autoFocus: true, - keyboardType: TextInputType.number, - padding: EdgeInsets.symmetric(vertical: 8.h), - leadingIcon: AppAssets.smart_phone, - ).paddingOnly(top: 8.h, bottom: 4), - - //TextInputWidget( - // labelText: widget.isForEmail ? LocaleKeys.email.tr() : LocaleKeys.phoneNumber.tr(), - // hintText: widget.isForEmail ? "demo@gmail.com" : "5xxxxxxxx", - // controller: widget.textController!, - // focusNode: _textFieldFocusNode, - // autoFocus: widget.autoFocus, - // padding: EdgeInsets.all(8.h), - // keyboardType: widget.isForEmail ? TextInputType.emailAddress : TextInputType.number, - // onChange: (value) { - // if (widget.onChange != null) { - // widget.onChange!(value); - // } - // }, - // onCountryChange: (value) { - // if (widget.onCountryChange != null) { - // widget.onCountryChange!(value); - // } - // }, - // isEnable: true, - // isReadOnly: widget.isFromSavedLogin, - // prefix: widget.isForEmail ? null : widget.countryCode, - // isBorderAllowed: false, - // isAllowLeadingIcon: true, - // fontSize: 13.h, - // isCountryDropDown: widget.isEnableCountryDropdown, - // leadingIcon: widget.isForEmail ? AppAssets.email : AppAssets.smart_phone, - // ) - ], - ), - ), - SizedBox(height: 20.h), - CustomButton( - text: "Verify the member", - onPressed: () { - FocusScope.of(context).unfocus(); - if (ValidationUtils.isValidatedIdAndPhoneWithCountryValidation( - nationalId: authVm.nationalIdController.text, - selectedCountry: authVm.selectedCountrySignup, - phoneNumber: authVm.phoneNumberController.text, - onOkPress: () { - Navigator.of(context).pop(); - }, - )) {} - }, - icon: AppAssets.add_icon, - height: 56.h, - fontWeight: FontWeight.w600), - SizedBox(height: 20.h), - ], - ), - callBackFunc: () {}); + Widget getFamilyTabs({required int index}) { + switch (index) { + case 0: + return FamilyCards( + profiles: medicalVM!.patientFamilyFiles, + onSelect: (FamilyFileResponseModelLists profile) { + medicalVM!.switchFamilyFiles(responseID: profile.responseId, patientID: profile.patientId, phoneNumber: profile.mobileNumber); + }, + onRemove: (FamilyFileResponseModelLists profile) { + medicalVM!.removeFileFromFamilyMembers(id: profile.id); + }, + isLeftAligned: true, + isShowDetails: true, + isShowRemoveButton: true, + ); + case 1: + return FamilyCards( + profiles: medicalVM!.pendingFamilyFiles, + isRequestDesign: true, + onSelect: (FamilyFileResponseModelLists profile) { + medicalVM!.acceptRejectFileFromFamilyMembers(id: profile.id, status: 3); + }, + onRemove: (FamilyFileResponseModelLists profile) { + medicalVM!.acceptRejectFileFromFamilyMembers(id: profile.id, status: 4); + }, + isShowDetails: true, + ); + default: + return SizedBox.shrink(); + } } } diff --git a/lib/presentation/my_family/widget/family_cards.dart b/lib/presentation/my_family/widget/family_cards.dart index 306692b..eeb82e5 100644 --- a/lib/presentation/my_family/widget/family_cards.dart +++ b/lib/presentation/my_family/widget/family_cards.dart @@ -10,6 +10,8 @@ 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/models/family_file_response_model.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; +import 'package:hmg_patient_app_new/services/dialog_service.dart'; +import 'package:hmg_patient_app_new/services/navigation_service.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; import 'package:hmg_patient_app_new/widgets/chip/custom_chip_widget.dart'; @@ -17,10 +19,23 @@ import 'package:hmg_patient_app_new/widgets/chip/custom_chip_widget.dart'; class FamilyCards extends StatefulWidget { final List profiles; final Function(FamilyFileResponseModelLists) onSelect; + final Function(FamilyFileResponseModelLists) onRemove; final bool isShowDetails; final bool isBottomSheet; + final bool isRequestDesign; + final bool isLeftAligned; + final bool isShowRemoveButton; - const FamilyCards({super.key, required this.profiles, required this.onSelect, this.isShowDetails = false, this.isBottomSheet = false}); + const FamilyCards( + {super.key, + required this.profiles, + required this.onSelect, + required this.onRemove, + this.isShowDetails = false, + this.isBottomSheet = false, + this.isRequestDesign = false, + this.isLeftAligned = false, + this.isShowRemoveButton = false}); @override State createState() => _FamilyCardsState(); @@ -31,79 +46,294 @@ class _FamilyCardsState extends State { @override Widget build(BuildContext context) { - return GridView.builder( + DialogService dialogService = getIt.get(); + if (widget.isRequestDesign) { + return Column( + children: [ + Row( + children: [ + Utils.buildSvgWithAssets(icon: AppAssets.alertSquare), + SizedBox(width: 8.h), + "Who can view my medical file ?".needTranslation.toText14(color: AppColors.textColor, isUnderLine: true, weight: FontWeight.w500).onPress(() { + dialogService.showFamilyBottomSheetWithoutHWithChild( + label: "Manage Family".needTranslation, + message: "", + child: manageFamily(), + onOkPressed: () {}, + ); + }), + SizedBox(width: 4.h), + Utils.buildSvgWithAssets(icon: AppAssets.arrowRight), + ], + ), + SizedBox(height: 24.h), + ListView.builder( + shrinkWrap: true, + physics: NeverScrollableScrollPhysics(), + padding: EdgeInsets.zero, + itemCount: widget.profiles.where((profile) => profile.isRequestFromMySide ?? false).length, + itemBuilder: (context, index) { + final mySideProfiles = widget.profiles.where((profile) => profile.isRequestFromMySide ?? false).toList(); + FamilyFileResponseModelLists profile = mySideProfiles[index]; + return Container( + margin: EdgeInsets.only(bottom: 12.h), + padding: EdgeInsets.symmetric(vertical: 15.h, horizontal: 15.h), + decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24), + child: Opacity( + opacity: 1.0, + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + CustomChipWidget( + height: 30.h, + chipType: ChipTypeEnum.alert, + backgroundColor: profile.status == FamilyFileEnum.pending.toInt + ? AppColors.alertLightColor.withValues(alpha: 0.20) + : profile.status == FamilyFileEnum.rejected.toInt + ? AppColors.primaryRedColor.withValues(alpha: 0.20) + : profile.status == FamilyFileEnum.active.toInt + ? AppColors.lightGreenColor + : AppColors.lightGrayBGColor, + chipText: profile.statusDescription ?? "N/A", + iconAsset: null, + isShowBorder: false, + borderRadius: 8.h, + textColor: profile.status == FamilyFileEnum.pending.toInt + ? AppColors.alertLightColor + : profile.status == FamilyFileEnum.rejected.toInt + ? AppColors.primaryRedColor + : profile.status == FamilyFileEnum.active.toInt + ? AppColors.textGreenColor + : AppColors.alertColor), + SizedBox(height: 8.h), + Wrap(alignment: WrapAlignment.start, crossAxisAlignment: WrapCrossAlignment.start, runAlignment: WrapAlignment.start, spacing: 0.h, children: [ + (profile.patientName ?? "").toText14(isBold: false, isCenter: false, maxlines: 1, weight: FontWeight.w600), + (getStatusTextByRequest(FamilyFileEnum.values.firstWhere((e) => e.toInt == profile.status), profile.isRequestFromMySide ?? false)) + .toText12(isBold: false, isCenter: false, maxLine: 1, fontWeight: FontWeight.w500, color: AppColors.greyTextColor), + ]), + SizedBox(height: 8.h), + CustomChipWidget( + height: 30.h, + chipType: ChipTypeEnum.alert, + backgroundColor: AppColors.lightGrayBGColor, + chipText: "Medical File: ${profile.responseId ?? "N/A"}", + iconAsset: null, + isShowBorder: false, + borderRadius: 8.h, + textColor: AppColors.textColor), + ], + ), + ), + ); + }, + ), + SizedBox(height: 20.h), + ], + ); + } else { + return GridView.builder( + shrinkWrap: true, + physics: NeverScrollableScrollPhysics(), + itemCount: widget.profiles.length, + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 2, + crossAxisSpacing: 10.h, + mainAxisSpacing: 10.h, + childAspectRatio: widget.isShowDetails ? 0.56.h : 0.65.h, + ), + padding: EdgeInsets.only(bottom: 20.h), + itemBuilder: (context, index) { + final profile = widget.profiles[index]; + final isActive = (profile.responseId == appState.getAuthenticatedUser()?.patientId); + final isParentUser = appState.getAuthenticatedUser()?.isParentUser ?? false; + final canSwitch = isParentUser || (!isParentUser && profile.responseId == appState.getSuperUserID); + return Container( + padding: EdgeInsets.symmetric(vertical: 15.h, horizontal: 15.h), + decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24), + child: Opacity( + opacity: isActive || profile.status == FamilyFileEnum.pending.toInt || !canSwitch ? 0.4 : 1.0, // Fade all content if active + child: Stack( + children: [ + Column( + mainAxisSize: MainAxisSize.min, + children: [ + Utils.buildImgWithAssets( + icon: profile.gender == null + ? AppAssets.dummy_user + : profile.gender == 1 + ? ((profile.age ?? 0) < 7 ? AppAssets.babyBoyImg : AppAssets.male_img) + : (profile.age! < 7 ? AppAssets.babyGirlImg : AppAssets.femaleImg), + width: 80.h, + height: 78.h), + SizedBox(height: 8.h), + (profile.patientName ?? "Unknown").toText14(isBold: false, isCenter: true, maxlines: 1, weight: FontWeight.w600), + SizedBox(height: 8.h), + CustomChipWidget( + chipType: ChipTypeEnum.alert, + backgroundColor: AppColors.lightGrayBGColor, + chipText: "Relation:${profile.relationship ?? "N/A"}", + iconAsset: AppAssets.heart, + isShowBorder: false, + borderRadius: 8.h, + textColor: AppColors.textColor), + widget.isShowDetails ? SizedBox(height: 4.h) : SizedBox(), + widget.isShowDetails + ? CustomChipWidget( + chipType: ChipTypeEnum.alert, + backgroundColor: AppColors.lightGrayBGColor, + chipText: "Age:${profile.age ?? "N/A"} Years", + isShowBorder: false, + borderRadius: 8.h, + textColor: AppColors.textColor, + ) + : SizedBox(), + widget.isShowDetails + ? SizedBox(height: 8.h) + : SizedBox( + height: 4.h, + ), + Spacer(), + CustomButton( + height: 40.h, + onPressed: () { + if (canSwitch) widget.onSelect(profile); + }, + text: isActive ? "Active".needTranslation : "Switch".needTranslation, + backgroundColor: isActive || !canSwitch ? Colors.grey.shade200 : AppColors.secondaryLightRedColor, + borderColor: isActive || !canSwitch ? Colors.grey.shade200 : AppColors.secondaryLightRedColor, + textColor: isActive || !canSwitch ? AppColors.greyTextColor : AppColors.primaryRedColor, + fontSize: 13.h, + icon: isActive ? AppAssets.activeCheck : AppAssets.switch_user, + iconColor: isActive || !canSwitch ? (isActive ? null : AppColors.greyTextColor) : AppColors.primaryRedColor, + padding: EdgeInsets.symmetric(vertical: 0, horizontal: 0), + ).paddingOnly(top: 0, bottom: 0), + ], + ), + if (widget.isShowRemoveButton) ...[ + Positioned( + top: 0, + right: 0, + child: Utils.buildSvgWithAssets(icon: AppAssets.deleteIcon).onPress(() { + if (!isActive) widget.onRemove(profile); + }), + ), + ], + ], + ), + ), + ); + }, + ); + } + } + + Widget manageFamily() { + NavigationService navigationService = getIt(); + return ListView.builder( shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - itemCount: widget.profiles.length, - gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: 2, - crossAxisSpacing: 10.h, - mainAxisSpacing: 10.h, - childAspectRatio: widget.isShowDetails ? 0.56.h : 0.74.h, - ), + physics: NeverScrollableScrollPhysics(), + padding: EdgeInsetsGeometry.zero, + itemCount: widget.profiles.where((profile) => !(profile.isRequestFromMySide ?? false)).length, itemBuilder: (context, index) { - final profile = widget.profiles[index]; - final isActive = (profile.responseId == appState.getAuthenticatedUser()?.patientId); + final otherProfiles = widget.profiles.where((profile) => !(profile.isRequestFromMySide ?? false)).toList(); + FamilyFileResponseModelLists profile = otherProfiles[index]; return Container( + margin: EdgeInsets.only(bottom: 12.h), padding: EdgeInsets.symmetric(vertical: 15.h, horizontal: 15.h), decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24), child: Opacity( - opacity: isActive ? 0.4 : 1.0, // Fade all content if active + opacity: 1.0, child: Column( mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, children: [ - SizedBox(height: 5.h), - Utils.buildImgWithAssets( - icon: profile.gender == 1 ? ((profile.age ?? 0) < 7 ? AppAssets.babyBoyImg : AppAssets.male_img) : (profile.age! < 7 ? AppAssets.babyGirlImg : AppAssets.femaleImg), - width: 80.h, - height: 78.h), + CustomChipWidget( + height: 30.h, + chipType: ChipTypeEnum.alert, + backgroundColor: profile.status == FamilyFileEnum.pending.toInt + ? AppColors.alertLightColor.withValues(alpha: 0.20) + : profile.status == FamilyFileEnum.rejected.toInt + ? AppColors.primaryRedColor.withValues(alpha: 0.20) + : profile.status == FamilyFileEnum.active.toInt + ? AppColors.lightGreenColor + : AppColors.lightGrayBGColor, + chipText: profile.statusDescription ?? "N/A", + iconAsset: null, + isShowBorder: false, + borderRadius: 8.h, + textColor: profile.status == FamilyFileEnum.pending.toInt + ? AppColors.alertLightColor + : profile.status == FamilyFileEnum.rejected.toInt + ? AppColors.primaryRedColor + : profile.status == FamilyFileEnum.active.toInt + ? AppColors.textGreenColor + : AppColors.alertColor, + ), + SizedBox(height: 8.h), + Wrap( + alignment: WrapAlignment.start, + children: [ + (profile.patientName ?? "").toText14(isBold: false, isCenter: true, maxlines: 1, weight: FontWeight.w600), + (getStatusTextByRequest(FamilyFileEnum.values.firstWhere((e) => e.toInt == profile.status), profile.isRequestFromMySide ?? false)).toText14( + isBold: false, + isCenter: true, + maxlines: 1, + weight: FontWeight.w500, + color: AppColors.greyTextColor, + ), + ], + ), SizedBox(height: 8.h), - (profile.patientName ?? "Unknown").toText16(isBold: false, isCenter: true, maxlines: 1, weight: FontWeight.w600), - SizedBox(height: 4.h), CustomChipWidget( - chipType: ChipTypeEnum.alert, - backgroundColor: AppColors.lightGrayBGColor, - chipText: "Relation: ${profile.relationship ?? "N/A"}", - iconAsset: AppAssets.heart, - isShowBorder: false, - borderRadius: 8.h, - textColor: AppColors.textColor), - widget.isShowDetails ? SizedBox(height: 4.h) : SizedBox(), - widget.isShowDetails - ? CustomChipWidget( - chipType: ChipTypeEnum.alert, - backgroundColor: AppColors.lightGrayBGColor, - chipText: "Age: ${profile.age ?? "N/A"} Years", - isShowBorder: false, - borderRadius: 8.h, - textColor: AppColors.textColor, - ) - : SizedBox(), - widget.isShowDetails ? SizedBox(height: 8.h) : SizedBox(), - Spacer(), - if (isActive) - CustomButton( - height: 40.h, - onPressed: () {}, - text: LocaleKeys.active.tr(), - backgroundColor: Colors.grey.shade200, - borderColor: Colors.grey.shade200, - textColor: AppColors.greyTextColor, - fontSize: 13.h, - ).paddingOnly(top: 0, bottom: 0) - else - CustomButton( - height: 40.h, - onPressed: () => widget.onSelect(profile), - text: LocaleKeys.select.tr(), - backgroundColor: AppColors.secondaryLightRedColor, - borderColor: AppColors.secondaryLightRedColor, - textColor: AppColors.primaryRedColor, - fontSize: 13.h, - icon: widget.isBottomSheet ? null : AppAssets.heart, - iconColor: AppColors.primaryRedColor, - padding: EdgeInsets.symmetric(vertical: 0, horizontal: 0), - ).paddingOnly(top: 0, bottom: 0), + height: 30.h, + chipType: ChipTypeEnum.alert, + backgroundColor: AppColors.lightGrayBGColor, + chipText: "Medical File: ${profile.patientId ?? "N/A".needTranslation}", + iconAsset: null, + isShowBorder: false, + borderRadius: 8.h, + textColor: AppColors.textColor, + ), + SizedBox(height: 16.h), + Row( + children: [ + profile.status == FamilyFileEnum.active.toInt + ? SizedBox() + : Expanded( + child: CustomButton( + height: 40.h, + text: LocaleKeys.confirm.tr(), + onPressed: () { + navigationService.pop(); + widget.onSelect(profile); + }, + backgroundColor: AppColors.lightGreenButtonColor, + borderColor: AppColors.lightGreenButtonColor, + textColor: AppColors.textGreenColor, + icon: null, + ), + ), + profile.status == FamilyFileEnum.active.toInt ? SizedBox() : SizedBox(width: 8.h), + Expanded( + child: CustomButton( + height: 40.h, + text: profile.status == FamilyFileEnum.active.toInt ? LocaleKeys.removeMember.tr() : LocaleKeys.cancel.tr(), + onPressed: () { + navigationService.pop(); + widget.onRemove(profile); + }, + backgroundColor: AppColors.secondaryLightRedColor, + borderColor: AppColors.secondaryLightRedColor, + textColor: AppColors.primaryRedColor, + icon: null, + iconColor: AppColors.primaryRedColor, + ), + ), + ], + ), ], ), ), @@ -111,4 +341,31 @@ class _FamilyCardsState extends State { }, ); } + + String getStatusTextByRequest(FamilyFileEnum status, bool isRequestFromMySide) { + switch (status) { + case FamilyFileEnum.active: + if (isRequestFromMySide) { + return "${status.displayName} your request to be your family member".needTranslation; + } else { + return "can view your file".needTranslation; + } + case FamilyFileEnum.pending: + if (isRequestFromMySide) { + return "has a request ${status.displayName} to be your family member".needTranslation; + } else { + return "wants to add you as their family member".needTranslation; + } + case FamilyFileEnum.rejected: + if (isRequestFromMySide) { + return "${status.displayName} your request to be your family member".needTranslation; + } else { + return "${status.displayName} your family member request".needTranslation; + } + case FamilyFileEnum.inactive: + return "Inactive".needTranslation; + default: + return "N/A".needTranslation; + } + } } diff --git a/lib/presentation/my_family/widget/my_family_sheet.dart b/lib/presentation/my_family/widget/my_family_sheet.dart index da822b1..d469ab2 100644 --- a/lib/presentation/my_family/widget/my_family_sheet.dart +++ b/lib/presentation/my_family/widget/my_family_sheet.dart @@ -1,27 +1,31 @@ import 'package:flutter/material.dart'; +import 'package:hmg_patient_app_new/core/dependencies.dart'; import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/features/medical_file/models/family_file_response_model.dart'; import 'package:hmg_patient_app_new/presentation/my_family/widget/family_cards.dart'; +import 'package:hmg_patient_app_new/services/navigation_service.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart'; class MyFamilySheet { static Future show(BuildContext context, List familyLists, Function(FamilyFileResponseModelLists) onSelect) async { - return await showCommonBottomSheetWithoutHeight( + NavigationService navigationService = getIt(); + return showCommonBottomSheetWithoutHeight( context, titleWidget: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - 'Please select a profile'.toText21(isBold: true), - 'switch from the below list of medical file'.toText16(weight: FontWeight.w100, color: AppColors.greyTextColor), + 'Please select a profile'.needTranslation.toText21(isBold: true), + 'switch from the below list of medical file'.needTranslation.toText16(weight: FontWeight.w100, color: AppColors.greyTextColor), ], ), child: FamilyCards( profiles: familyLists, onSelect: (profile) { - Navigator.of(context).pop(); // Close the bottom sheet - onSelect(profile); // Call the onSelect callback + navigationService.pop(); + onSelect(profile); }, + onRemove: (profile) {}, isBottomSheet: true), callBackFunc: () {}, ); diff --git a/lib/presentation/prescriptions/prescriptions_list_page.dart b/lib/presentation/prescriptions/prescriptions_list_page.dart index 87f880d..1293c9c 100644 --- a/lib/presentation/prescriptions/prescriptions_list_page.dart +++ b/lib/presentation/prescriptions/prescriptions_list_page.dart @@ -264,9 +264,10 @@ class _PrescriptionsListPageState extends State { child: Padding( padding: EdgeInsets.all(8.h), child: Transform.flip( - flipX: appState.isArabic() ? true : false, + flipX: appState.isArabic(), child: Utils.buildSvgWithAssets( - icon: AppAssets.forward_arrow_icon, + icon: AppAssets.forward_arrow_icon_small, + iconColor: AppColors.whiteColor, width: 10.h, height: 10.h, fit: BoxFit.contain, diff --git a/lib/presentation/profile_settings/profile_settings.dart b/lib/presentation/profile_settings/profile_settings.dart index e5ec389..efc02ad 100644 --- a/lib/presentation/profile_settings/profile_settings.dart +++ b/lib/presentation/profile_settings/profile_settings.dart @@ -1,3 +1,6 @@ +import 'dart:convert'; +import 'dart:developer'; + import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter_swiper_view/flutter_swiper_view.dart'; @@ -5,16 +8,22 @@ import 'package:hmg_patient_app_new/core/app_assets.dart'; import 'package:hmg_patient_app_new/core/app_export.dart'; import 'package:hmg_patient_app_new/core/app_state.dart'; import 'package:hmg_patient_app_new/core/utils/date_util.dart'; +import 'package:hmg_patient_app_new/core/app_state.dart'; +import 'package:hmg_patient_app_new/core/dependencies.dart'; +import 'package:hmg_patient_app_new/core/enums.dart'; import 'package:hmg_patient_app_new/core/utils/utils.dart'; import 'package:hmg_patient_app_new/extensions/int_extensions.dart'; import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; import 'package:hmg_patient_app_new/features/habib_wallet/habib_wallet_view_model.dart'; import 'package:hmg_patient_app_new/features/insurance/insurance_view_model.dart'; +import 'package:hmg_patient_app_new/features/medical_file/medical_file_view_model.dart'; +import 'package:hmg_patient_app_new/features/medical_file/models/family_file_response_model.dart'; import 'package:hmg_patient_app_new/features/profile_settings/profile_settings_view_model.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/presentation/habib_wallet/habib_wallet_page.dart'; import 'package:hmg_patient_app_new/presentation/habib_wallet/recharge_wallet_page.dart'; +import 'package:hmg_patient_app_new/services/dialog_service.dart'; import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/widgets/app_language_change.dart'; @@ -50,8 +59,6 @@ class _ProfileSettingsState extends State { int length = 3; final SwiperController _controller = SwiperController(); - int _index = 0; - @override Widget build(BuildContext context) { return CollapsingListView( @@ -61,27 +68,41 @@ class _ProfileSettingsState extends State { child: SingleChildScrollView( padding: EdgeInsets.only(top: 24, bottom: 24), physics: NeverScrollableScrollPhysics(), - child: Consumer( - builder: (context, model, child) { + child: Consumer2( + builder: (context, profileVm, medicalVm, child) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Swiper( - itemCount: length, + itemCount: medicalVm.patientFamilyFiles.length, layout: SwiperLayout.STACK, loop: true, itemWidth: MediaQuery.of(context).size.width - 42, indicatorLayout: PageIndicatorLayout.COLOR, axisDirection: AxisDirection.right, controller: _controller, - itemHeight: 210 + 16, + itemHeight: 200.h, pagination: const SwiperPagination( alignment: Alignment.bottomCenter, margin: EdgeInsets.only(top: 210 + 8 + 24), builder: DotSwiperPaginationBuilder(color: Color(0xffD9D9D9), activeColor: AppColors.blackBgColor), ), itemBuilder: (BuildContext context, int index) { - return FamilyCardWidget(isRootUser: true).paddingOnly(right: 16); + return FamilyCardWidget( + profile: medicalVm.patientFamilyFiles[index], + onAddFamilyMemberPress: () { + DialogService dialogService = getIt.get(); + dialogService.showAddFamilyFileSheet( + label: "Add Family Member".needTranslation, + message: "Please fill the below field to add a new family member to your profile".needTranslation, + onVerificationPress: () { + medicalVm.addFamilyFile(otpTypeEnum: OTPTypeEnum.sms); + }); + }, + onFamilySwitchPress: (FamilyFileResponseModelLists profile) { + medicalVm.switchFamilyFiles(responseID: profile.responseId, patientID: profile.patientId, phoneNumber: profile.mobileNumber); + }, + ).paddingOnly(right: 16); }, ), GridView( @@ -230,115 +251,300 @@ class _ProfileSettingsState extends State { } class FamilyCardWidget extends StatelessWidget { - FamilyCardWidget({this.isRootUser = true, Key? key}) : super(key: key); - - bool isRootUser; late AppState appState; + final Function() onAddFamilyMemberPress; + final Function(FamilyFileResponseModelLists member) onFamilySwitchPress; + final FamilyFileResponseModelLists profile; + + FamilyCardWidget({super.key, required this.onAddFamilyMemberPress, required this.profile, required this.onFamilySwitchPress(FamilyFileResponseModelLists member)}); @override Widget build(BuildContext context) { - appState = getIt.get(); + AppState appState = getIt.get(); + final isActive = (profile.responseId == appState.getAuthenticatedUser()?.patientId); + final isParentUser = appState.getAuthenticatedUser()?.isParentUser ?? false; + final canSwitch = isParentUser || (!isParentUser && profile.responseId == appState.getSuperUserID); return Container( - decoration: RoundedRectangleBorder().toSmoothCornerDecoration( - color: AppColors.whiteColor, - borderRadius: 20.h, - hasShadow: true, - ), + decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 20.h, hasShadow: true), child: Column( children: [ Column( + crossAxisAlignment: CrossAxisAlignment.start, spacing: 8.h, children: [ Row( crossAxisAlignment: CrossAxisAlignment.start, spacing: 8.h, children: [ - Image.asset(appState.getAuthenticatedUser()?.gender == 1 ? AppAssets.male_img : AppAssets.femaleImg, width: 56.h, height: 56.h), + Image.asset(profile.gender == 1 ? AppAssets.male_img : AppAssets.femaleImg, width: 56.h, height: 56.h), Column( crossAxisAlignment: CrossAxisAlignment.start, spacing: 0.h, mainAxisSize: MainAxisSize.min, children: [ - "${appState.getAuthenticatedUser()!.firstName} ${appState.getAuthenticatedUser()!.lastName}" - .toText18(isBold: true, weight: FontWeight.w600, textOverflow: TextOverflow.ellipsis, maxlines: 1), + "${profile.patientName}".toText18(isBold: true, weight: FontWeight.w600, textOverflow: TextOverflow.ellipsis, maxlines: 1), AppCustomChipWidget( icon: AppAssets.file_icon, - labelText: "${LocaleKeys.fileNo.tr(context: context)}: ${appState.getAuthenticatedUser()!.patientId}", + labelText: "${LocaleKeys.fileNo.tr(context: context)}: ${profile.responseId}", iconSize: 12, ), ], ).expanded, Icon(Icons.qr_code, size: 56) ], - ).expanded, + ), + SizedBox(height: 4.h), SizedBox( - width: double.infinity, child: Wrap( alignment: WrapAlignment.start, - spacing: 8.h, + spacing: 4.h, + runSpacing: 4.h, children: [ - AppCustomChipWidget(labelText: "${appState.getAuthenticatedUser()!.age} Years Old"), + AppCustomChipWidget(labelText: "${profile.age} Years Old".needTranslation), AppCustomChipWidget( - icon: AppAssets.blood_icon, - labelText: "${LocaleKeys.bloodType.tr(context: context)}: ${appState.getUserBloodGroup}", - iconColor: AppColors.primaryRedColor, - ), - Consumer(builder: (context, insuranceVM, child) { - return AppCustomChipWidget( - icon: insuranceVM.isInsuranceLoading - ? AppAssets.cancel_circle_icon - : (DateTime.now().isAfter( - DateUtil.convertStringToDate(insuranceVM.patientInsuranceList.first.cardValidTo), - )) - ? AppAssets.cancel_circle_icon - : AppAssets.insurance_active_icon, - labelText: insuranceVM.isInsuranceLoading - ? "Insurance" - : (DateTime.now().isAfter( - DateUtil.convertStringToDate(insuranceVM.patientInsuranceList.first.cardValidTo), - ) - ? "Insurance Expired".needTranslation - : "Insurance Active".needTranslation), - iconColor: insuranceVM.isInsuranceLoading - ? AppColors.primaryRedColor - : (DateTime.now().isAfter( - DateUtil.convertStringToDate(insuranceVM.patientInsuranceList.first.cardValidTo), - )) - ? AppColors.primaryRedColor - : AppColors.successColor, - iconSize: 14, - backgroundColor: insuranceVM.isInsuranceLoading - ? AppColors.primaryRedColor - : (DateTime.now().isAfter( - DateUtil.convertStringToDate(insuranceVM.patientInsuranceList.first.cardValidTo), - )) - ? AppColors.primaryRedColor.withValues(alpha: 0.15) - : AppColors.successColor.withValues(alpha: 0.15), - ).toShimmer2(isShow: insuranceVM.isInsuranceLoading); - }), + icon: AppAssets.blood_icon, + labelPadding: EdgeInsetsDirectional.only(start: -6.h, end: 8.h), + labelText: "Blood: ${appState.getAuthenticatedUser()!.bloodGroup ?? "."}", + iconColor: AppColors.primaryRedColor), + Selector( + selector: (context, insuranceVM) => ( + isEmpty: insuranceVM.patientInsuranceList.isEmpty, + patientID: insuranceVM.patientInsuranceList.isNotEmpty ? insuranceVM.patientInsuranceList.first.patientID : null, + isLoading: insuranceVM.isInsuranceLoading, + cardValidTo: insuranceVM.patientInsuranceList.isNotEmpty ? insuranceVM.patientInsuranceList.first.cardValidTo : null + ), + builder: (context, data, child) { + if (data.isEmpty) { + return const SizedBox(); + } else if (profile.responseId != data.patientID) { + return SizedBox(); + } + + final isLoading = data.isLoading; + final isExpired = !isLoading && DateTime.now().isAfter(DateUtil.convertStringToDate(data.cardValidTo)); + + final String icon; + final String labelText; + final Color iconColor; + final Color backgroundColor; + + if (isLoading) { + icon = AppAssets.cancel_circle_icon; + labelText = "Insurance"; + iconColor = AppColors.primaryRedColor; + backgroundColor = AppColors.primaryRedColor; + } else if (isExpired) { + icon = AppAssets.cancel_circle_icon; + labelText = "Insurance Expired".needTranslation; + iconColor = AppColors.primaryRedColor; + backgroundColor = AppColors.primaryRedColor.withValues(alpha: 0.15); + } else { + icon = AppAssets.insurance_active_icon; + labelText = "Insurance Active".needTranslation; + iconColor = AppColors.successColor; + backgroundColor = AppColors.successColor.withValues(alpha: 0.15); + } + + return AppCustomChipWidget( + icon: icon, + labelText: labelText, + iconColor: iconColor, + iconSize: 12, + backgroundColor: backgroundColor, + labelPadding: EdgeInsetsDirectional.only(start: -6.h, end: 8.h), + // padding: EdgeInsets.zero, + ).toShimmer2(isShow: isLoading); + }, + ) + + // Consumer(builder: (context, insuranceVM, child) { + // if (insuranceVM.patientInsuranceList.isEmpty) { + // return const SizedBox(); + // } else if (profile.responseId != insuranceVM.patientInsuranceList.first.patientID) { + // return SizedBox(); + // } + // + // final isLoading = insuranceVM.isInsuranceLoading; + // final isExpired = !isLoading && DateTime.now().isAfter(DateUtil.convertStringToDate(insuranceVM.patientInsuranceList.first.cardValidTo)); + // + // final String icon; + // final String labelText; + // final Color iconColor; + // final Color backgroundColor; + // + // if (isLoading) { + // icon = AppAssets.cancel_circle_icon; + // labelText = "Insurance"; + // iconColor = AppColors.primaryRedColor; + // backgroundColor = AppColors.primaryRedColor; + // } else if (isExpired) { + // icon = AppAssets.cancel_circle_icon; + // labelText = "Insurance Expired".needTranslation; + // iconColor = AppColors.primaryRedColor; + // backgroundColor = AppColors.primaryRedColor.withValues(alpha: 0.15); + // } else { + // icon = AppAssets.insurance_active_icon; + // labelText = "Insurance Active".needTranslation; + // iconColor = AppColors.successColor; + // backgroundColor = AppColors.successColor.withValues(alpha: 0.15); + // } + // + // return AppCustomChipWidget( + // icon: icon, + // labelText: labelText, + // iconColor: iconColor, + // iconSize: 12, + // backgroundColor: backgroundColor, + // // padding: EdgeInsets.zero, + // ).toShimmer2(isShow: isLoading); + // }) + + // Consumer(builder: (context, insuranceVM, child) { + // return insuranceVM.patientInsuranceList. isNotEmpty ? AppCustomChipWidget( + // icon: insuranceVM.isInsuranceLoading + // ? AppAssets.cancel_circle_icon + // : (DateTime.now().isAfter( + // DateUtil.convertStringToDate(insuranceVM.patientInsuranceList.first.cardValidTo), + // )) + // ? AppAssets.cancel_circle_icon + // : AppAssets.insurance_active_icon, + // labelText: insuranceVM.isInsuranceLoading + // ? "Insurance" + // : (DateTime.now().isAfter( + // DateUtil.convertStringToDate(insuranceVM.patientInsuranceList.first.cardValidTo), + // ) + // ? "Insurance Expired".needTranslation + // : "Insurance Active".needTranslation), + // iconColor: insuranceVM.isInsuranceLoading + // ? AppColors.primaryRedColor + // : (DateTime.now().isAfter( + // DateUtil.convertStringToDate(insuranceVM.patientInsuranceList.first.cardValidTo), + // )) + // ? AppColors.primaryRedColor + // : AppColors.successColor, + // iconSize: 14, + // backgroundColor: insuranceVM.isInsuranceLoading + // ? AppColors.primaryRedColor + // : (DateTime.now().isAfter( + // DateUtil.convertStringToDate(insuranceVM.patientInsuranceList.first.cardValidTo), + // )) + // ? AppColors.primaryRedColor.withValues(alpha: 0.15) + // : AppColors.successColor.withValues(alpha: 0.15), + // ).toShimmer2(isShow: insuranceVM.isInsuranceLoading) : SizedBox(); + // }), ], ), ), ], - ).paddingOnly(top: 16, right: 16, left: 16, bottom: 12).expanded, + ).paddingOnly(top: 16.h, right: 16.h, left: 16.h, bottom: 12.h).expanded, 1.divider, - //TODO: Add family file switch logic here - isRootUser - ? CustomButton(icon: AppAssets.add_family, text: "Add a new family member".needTranslation, height: 40.h, fontSize: 14, onPressed: () {}) - .paddingOnly(top: 12, right: 16, left: 16, bottom: 16) - : CustomButton( - icon: AppAssets.add_family, - backgroundColor: AppColors.secondaryLightRedColor, - borderColor: AppColors.secondaryLightRedColor, - textColor: AppColors.primaryRedColor, - iconColor: AppColors.primaryRedColor, - text: "Switch to this medical file".needTranslation, - height: 40.h, - fontSize: 14, - onPressed: () {}) - .paddingOnly(top: 12, right: 16, left: 16, bottom: 16), + _buildActionButton(appState), + + // if (appState.getAuthenticatedUser()!.isParentUser ?? false) ...[ + // if (member!.responseId != appState.getAuthenticatedUser()!.patientId) ...[ + // CustomButton( + // icon: AppAssets.switch_user, + // text: "Switch Family File".needTranslation, + // onPressed: () { + // onFamilySwitchPress(member!); + // }, + // ).paddingOnly(top: 12, right: 16, left: 16, bottom: 16), + // ] else + // ...[ + // CustomButton( + // icon: AppAssets.add_family, + // text: "Add a new family member".needTranslation, + // onPressed: () { + // onAddFamilyMemberPress(); + // }, + // ).paddingOnly(top: 12, right: 16, left: 16, bottom: 16), + // ] + // ] else + // ...[ + // if (appState.getSuperUserID != null && appState.getSuperUserID == member!.responseId) ...[ + // CustomButton( + // icon: AppAssets.switch_user, + // text: "Switch Back To Family File".needTranslation, + // onPressed: () { + // onFamilySwitchPress(member!); + // }, + // ).paddingOnly(top: 12, right: 16, left: 16, bottom: 16), + // ] else + // ...[ + // CustomButton( + // icon: AppAssets.switch_user, + // text: "Disabled".needTranslation, + // backgroundColor: Colors.grey.shade200, + // borderColor: Colors.grey.shade200, + // textColor: AppColors.greyTextColor, + // onPressed: () {}, + // iconColor: AppColors.greyTextColor, + // ).paddingOnly(top: 12, right: 16, left: 16, bottom: 16), + // ] + // ] ], ), ); } + + Widget _buildActionButton(AppState appState) { + final isParentUser = appState.getAuthenticatedUser()?.isParentUser ?? false; + final int? currentUserId = appState.getAuthenticatedUser()?.patientId; + final int? superUserId = appState.getSuperUserID; + + if (isParentUser) { + return _buildParentUserButton(currentUserId); + } else { + return _buildNonParentUserButton(superUserId); + } + } + + Widget _buildParentUserButton(int? currentUserId) { + final canSwitch = profile.responseId != currentUserId; + + return CustomButton( + icon: canSwitch ? AppAssets.switch_user : AppAssets.add_family, + text: canSwitch ? "Switch Family File".needTranslation : "Add a new family member".needTranslation, + onPressed: canSwitch ? () => onFamilySwitchPress(profile) : onAddFamilyMemberPress, + backgroundColor: canSwitch ? AppColors.secondaryLightRedColor : AppColors.primaryRedColor, + borderColor: canSwitch ? AppColors.secondaryLightRedColor : AppColors.primaryRedColor, + textColor: canSwitch ? AppColors.primaryRedColor : AppColors.whiteColor, + iconColor: canSwitch ? AppColors.primaryRedColor : AppColors.whiteColor, + height: 40.h, + fontSize: 14, + ).paddingOnly(top: 12, right: 16, left: 16, bottom: 16); + } + + Widget _buildNonParentUserButton(int? superUserId) { + final canSwitchBack = superUserId != null && superUserId == profile.responseId; + + return CustomButton( + icon: AppAssets.switch_user, + text: canSwitchBack ? "Switch Back To Family File".needTranslation : "Switch".needTranslation, + backgroundColor: canSwitchBack ? AppColors.primaryRedColor : Colors.grey.shade200, + borderColor: canSwitchBack ? AppColors.primaryRedColor : Colors.grey.shade200, + textColor: canSwitchBack ? AppColors.whiteColor : AppColors.greyTextColor, + iconColor: canSwitchBack ? AppColors.whiteColor : AppColors.greyTextColor, + onPressed: canSwitchBack ? () => onFamilySwitchPress(profile) : () {}, + height: 40.h, + fontSize: 14, + ).paddingOnly(top: 12, right: 16, left: 16, bottom: 16); + } + +// //TODO: Add family file switch logic here +// isRootUser +// ? CustomButton(icon: AppAssets.add_family, text: "Add a new family member".needTranslation, height: 40.h, fontSize: 14, onPressed: () {}) +// .paddingOnly(top: 12, right: 16, left: 16, bottom: 16) +// : CustomButton( +// icon: AppAssets.add_family, +// backgroundColor: AppColors.secondaryLightRedColor, +// borderColor: AppColors.secondaryLightRedColor, +// textColor: AppColors.primaryRedColor, +// iconColor: AppColors.primaryRedColor, +// text: "Switch to this medical file".needTranslation, +// height: 40.h, +// fontSize: 14, +// onPressed: () {}) +// .paddingOnly(top: 12, right: 16, left: 16, bottom: 16), +// } diff --git a/lib/routes/app_routes.dart b/lib/routes/app_routes.dart index 67bf208..5a93216 100644 --- a/lib/routes/app_routes.dart +++ b/lib/routes/app_routes.dart @@ -4,6 +4,7 @@ import 'package:hmg_patient_app_new/presentation/authentication/register.dart'; import 'package:hmg_patient_app_new/presentation/authentication/register_step2.dart'; import 'package:hmg_patient_app_new/presentation/home/landing_page.dart'; import 'package:hmg_patient_app_new/presentation/home/navigation_screen.dart'; +import 'package:hmg_patient_app_new/presentation/medical_file/medical_file_page.dart'; import 'package:hmg_patient_app_new/splashPage.dart'; class AppRoutes { @@ -12,6 +13,7 @@ class AppRoutes { static const String register = '/register'; static const String registerStepTwo = '/registerStepTwo'; static const String landingScreen = '/landingScreen'; + static const String medicalFilePage = '/medicalFilePage'; static Map get routes => { initialRoute: (context) => SplashPage(), @@ -19,5 +21,6 @@ class AppRoutes { landingScreen: (context) => LandingNavigation(), register: (context) => RegisterNew(), registerStepTwo: (context) => RegisterNewStep2(), + medicalFilePage: (context) => MedicalFilePage(), }; } diff --git a/lib/services/dialog_service.dart b/lib/services/dialog_service.dart index 6ee0ddc..cc55ca6 100644 --- a/lib/services/dialog_service.dart +++ b/lib/services/dialog_service.dart @@ -4,12 +4,15 @@ 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/route_extensions.dart'; import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; +import 'package:hmg_patient_app_new/features/medical_file/models/family_file_response_model.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; +import 'package:hmg_patient_app_new/presentation/my_family/widget/family_cards.dart'; import 'package:hmg_patient_app_new/services/navigation_service.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/widgets/bottomsheet/exception_bottom_sheet.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/family_files/family_file_add_widget.dart'; abstract class DialogService { Future showErrorBottomSheet({String title = "", required String message, Function()? onOkPressed, Function()? onCancelPressed}); @@ -18,7 +21,14 @@ abstract class DialogService { Future showCommonBottomSheetWithoutH({String? label, required String message, required Function() onOkPressed, Function()? onCancelPressed}); + Future showFamilyBottomSheetWithoutH( + {String? label, required String message, required Function(FamilyFileResponseModelLists response) onSwitchPress, required List profiles}); + + Future showFamilyBottomSheetWithoutHWithChild({String? label, required String message, Widget? child, required Function() onOkPressed, Function()? onCancelPressed}); + Future showPhoneNumberPickerSheet({String? label, String? message, required Function() onSMSPress, required Function() onWhatsappPress}); + + Future showAddFamilyFileSheet({String? label, String? message, required Function() onVerificationPress}); // TODO : Need to be Fixed showPhoneNumberPickerSheet ( From Login ADn Signup Bottom Sheet Move Here } @@ -50,6 +60,7 @@ class DialogServiceImp implements DialogService { if (onOkPressed != null) { onOkPressed(); } + context.pop(); }, onCancelPressed: () { if (onCancelPressed != null) { @@ -93,6 +104,36 @@ class DialogServiceImp implements DialogService { title: label ?? "", child: exceptionBottomSheetWidget(context: context, message: message, onOkPressed: onOkPressed, onCancelPressed: onCancelPressed), callBackFunc: () {}); } + @override + Future showFamilyBottomSheetWithoutH( + {String? label, required String message, required Function(FamilyFileResponseModelLists response) onSwitchPress, required List profiles}) async { + final context = navigationService.navigatorKey.currentContext; + if (context == null) return; + showCommonBottomSheetWithoutHeight(context, + title: label ?? "", + child: FamilyCards( + profiles: profiles, + onSelect: (FamilyFileResponseModelLists profile) { + onSwitchPress(profile); + }, + onRemove: (FamilyFileResponseModelLists profile) {}, + isShowDetails: false, + ), + callBackFunc: () {}); + } + + @override + Future showFamilyBottomSheetWithoutHWithChild({String? label, required String message, Widget? child, required Function() onOkPressed, Function()? onCancelPressed}) async { + final context = navigationService.navigatorKey.currentContext; + if (context == null) return; + showCommonBottomSheetWithoutHeight( + context, + title: label ?? "", + child: child ?? SizedBox(), + callBackFunc: () {}, + ); + } + @override Future showPhoneNumberPickerSheet({String? label, String? message, required Function() onSMSPress, required Function() onWhatsappPress}) async { final context = navigationService.navigatorKey.currentContext; @@ -100,6 +141,18 @@ class DialogServiceImp implements DialogService { showCommonBottomSheetWithoutHeight(context, title: label ?? "", child: showPhoneNumberPickerWidget(context: context, message: message, onSMSPress: onSMSPress, onWhatsappPress: onWhatsappPress), callBackFunc: () {}); } + + @override + Future showAddFamilyFileSheet({String? label, String? message, required Function() onVerificationPress}) async { + final context = navigationService.navigatorKey.currentContext; + if (context == null) return; + showCommonBottomSheetWithoutHeight(context, + title: label ?? "", + child: FamilyFileAddWidget(() { + onVerificationPress(); + }, message ?? ""), + callBackFunc: () {}); + } } Widget exceptionBottomSheetWidget({required BuildContext context, required String message, required Function() onOkPressed, Function()? onCancelPressed}) { @@ -216,3 +269,12 @@ Widget showPhoneNumberPickerWidget({required BuildContext context, String? messa // ); }); } + +// Widget familyMemberAddWidget() { +// AuthenticationViewModel authVm = getIt.get(); +// return showCommonBottomSheetWithoutHeight(context, +// title: "Add Family Member".needTranslation, +// useSafeArea: true, +// child: +// callBackFunc: () {}); +// } diff --git a/lib/services/navigation_service.dart b/lib/services/navigation_service.dart index bfdedef..b5c5e03 100644 --- a/lib/services/navigation_service.dart +++ b/lib/services/navigation_service.dart @@ -34,9 +34,9 @@ class NavigationService { navigatorKey.currentState?.pushReplacementNamed(routeName); } - Future pushToOtpScreen({required String phoneNumber, required Function(int code) checkActivationCode, required Function(String phoneNumber) onResendOTPPressed}) { + Future pushToOtpScreen({required String phoneNumber, required Function(int code) checkActivationCode, required Function(String phoneNumber) onResendOTPPressed, bool isFormFamilyFile = false}) { return navigatorKey.currentState!.push( - MaterialPageRoute(builder: (_) => OTPVerificationScreen(phoneNumber: phoneNumber, checkActivationCode: checkActivationCode, onResendOTPPressed: onResendOTPPressed)), + MaterialPageRoute(builder: (_) => OTPVerificationScreen(phoneNumber: phoneNumber, checkActivationCode: checkActivationCode, onResendOTPPressed: onResendOTPPressed, isFormFamilyFile : isFormFamilyFile)), ); } diff --git a/lib/widgets/appbar/collapsing_list_view.dart b/lib/widgets/appbar/collapsing_list_view.dart index b18c6ee..2687944 100644 --- a/lib/widgets/appbar/collapsing_list_view.dart +++ b/lib/widgets/appbar/collapsing_list_view.dart @@ -22,10 +22,11 @@ class CollapsingListView extends StatelessWidget { VoidCallback? logout; VoidCallback? history; Widget? bottomChild; + Widget? trailing; bool isClose; bool isLeading; - CollapsingListView({required this.title, this.child, this.search, this.isClose = false, this.bottomChild, this.report, this.logout, this.history, this.isLeading = true}); + CollapsingListView({required this.title, this.child, this.search, this.isClose = false, this.bottomChild, this.report, this.logout, this.history, this.isLeading = true, this.trailing}); @override Widget build(BuildContext context) { @@ -46,7 +47,7 @@ class CollapsingListView extends StatelessWidget { backgroundColor: AppColors.bgScaffoldColor, leading: isLeading ? Transform.flip( - flipX: appState.isArabic() ? true : false, + flipX: appState.isArabic(), child: IconButton( icon: Utils.buildSvgWithAssets(icon: isClose ? AppAssets.closeBottomNav : AppAssets.arrow_back, width: 32.h, height: 32.h), padding: EdgeInsets.only(left: 12), @@ -97,7 +98,8 @@ class CollapsingListView extends StatelessWidget { if (logout != null) actionButton(context, t, title: "Logout".needTranslation, icon: AppAssets.logout).onPress(logout!), if (report != null) actionButton(context, t, title: "Report".needTranslation, icon: AppAssets.report_icon).onPress(report!), if (history != null) actionButton(context, t, title: "History".needTranslation, icon: AppAssets.insurance_history_icon).onPress(history!), - if (search != null) Utils.buildSvgWithAssets(icon: AppAssets.search_icon).onPress(search!).paddingOnly(right: 24) + if (search != null) Utils.buildSvgWithAssets(icon: AppAssets.search_icon).onPress(search!).paddingOnly(right: 24), + if (trailing != null) trailing!, ], )), ), diff --git a/lib/widgets/buttons/custom_button.dart b/lib/widgets/buttons/custom_button.dart index a4bec4c..73885b0 100644 --- a/lib/widgets/buttons/custom_button.dart +++ b/lib/widgets/buttons/custom_button.dart @@ -23,28 +23,30 @@ class CustomButton extends StatelessWidget { final double? width; final double iconSize; final TextOverflow? textOverflow; + final BorderSide? borderSide; - CustomButton({ - Key? key, - required this.text, - required this.onPressed, - this.backgroundColor = const Color(0xFFED1C2B), - this.borderColor = const Color(0xFFED1C2B), - this.textColor = Colors.white, - this.borderRadius = 12, - this.borderWidth = 2, - this.padding = const EdgeInsets.fromLTRB(8, 10, 8, 10), - this.fontSize = 16, - this.fontFamily, - this.fontWeight = FontWeight.w500, - this.isDisabled = false, - this.icon, - this.iconColor = Colors.white, - this.height = 56, - this.width, - this.iconSize = 24, - this.textOverflow, - }) : super(key: key); + CustomButton( + {Key? key, + required this.text, + required this.onPressed, + this.backgroundColor = const Color(0xFFED1C2B), + this.borderColor = const Color(0xFFED1C2B), + this.textColor = Colors.white, + this.borderRadius = 12, + this.borderWidth = 2, + this.padding = const EdgeInsets.fromLTRB(8, 10, 8, 10), + this.fontSize = 16, + this.fontFamily, + this.fontWeight = FontWeight.w500, + this.isDisabled = false, + this.icon, + this.iconColor = Colors.white, + this.height = 56, + this.width, + this.iconSize = 24, + this.textOverflow, + this.borderSide}) + : super(key: key); @override Widget build(BuildContext context) { @@ -55,19 +57,17 @@ class CustomButton extends StatelessWidget { width: width, padding: padding, decoration: RoundedRectangleBorder().toSmoothCornerDecoration( - color: isDisabled ? Colors.transparent : backgroundColor, + color: isDisabled ? backgroundColor.withOpacity(.5) : backgroundColor, borderRadius: borderRadius, - side: BorderSide( - width: borderWidth.h, - color: isDisabled ? borderColor.withOpacity(0.5) : borderColor, - )), + customBorder: BorderRadius.circular(borderRadius), + side: borderSide ?? BorderSide(width: borderWidth.h, color: isDisabled ? borderColor.withValues(alpha: 0.5) : borderColor)), child: Row( crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center, children: [ if (icon != null) Padding( - padding: const EdgeInsets.only(right: 8.0, left: 8.0), + padding: EdgeInsets.only(right: 8.h, left: 8.h), child: Utils.buildSvgWithAssets(icon: icon!, iconColor: iconColor, isDisabled: isDisabled, width: iconSize, height: iconSize), ), Padding( @@ -85,17 +85,6 @@ class CustomButton extends StatelessWidget { ), ], ), - ) - - // .toSmoothContainer( - // smoothness: 1, - // side: BorderSide(width: borderWidth, color: backgroundColor), - // borderRadius: BorderRadius.circular(borderRadius * 1.2), - // foregroundDecoration: BoxDecoration( - // color: isDisabled ? backgroundColor.withOpacity(0.5) : Colors.transparent, - // borderRadius: BorderRadius.circular(borderRadius), - // ), - // ), - ); + )); } } diff --git a/lib/widgets/chip/app_custom_chip_widget.dart b/lib/widgets/chip/app_custom_chip_widget.dart index 751b01c..2cfd5ce 100644 --- a/lib/widgets/chip/app_custom_chip_widget.dart +++ b/lib/widgets/chip/app_custom_chip_widget.dart @@ -24,7 +24,8 @@ class AppCustomChipWidget extends StatelessWidget { this.deleteIconColor = AppColors.textColor, this.deleteIconHasColor = false, this.padding = EdgeInsets.zero, - this.labelPadding , + this.onChipTap, + this.labelPadding, }); final String? labelText; @@ -42,57 +43,61 @@ class AppCustomChipWidget extends StatelessWidget { final OutlinedBorder? shape; final EdgeInsets? padding; final EdgeInsetsDirectional? labelPadding; + final void Function()? onChipTap; @override Widget build(BuildContext context) { - return ChipTheme( - data: ChipThemeData( - padding: EdgeInsets.all(0.0), - shape: SmoothRectangleBorder( - side: BorderSide( - width: 10.0, - color: Colors.transparent, // Crucially, set color to transparent - style: BorderStyle.none, + return GestureDetector( + onTap: onChipTap, + child: ChipTheme( + data: ChipThemeData( + padding: EdgeInsets.all(0.0), + shape: SmoothRectangleBorder( + side: BorderSide( + width: 10.0, + color: Colors.transparent, // Crucially, set color to transparent + style: BorderStyle.none, + ), + borderRadius: BorderRadius.circular(8.0), // Apply a border radius of 16.0 ), - borderRadius: BorderRadius.circular(8.0), // Apply a border radius of 16.0 ), - ), - child: icon.isNotEmpty - ? Chip( - avatar: icon.isNotEmpty ? Utils.buildSvgWithAssets(icon: icon, width: iconSize.h, height: iconSize.h, iconColor: iconHasColor ? iconColor : null) : SizedBox.shrink(), - label: richText ?? labelText!.toText10(weight: FontWeight.w500, letterSpacing: 0, color: textColor), - // padding: EdgeInsets.all(0.0), - padding: padding, - materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, - labelPadding: labelPadding??EdgeInsetsDirectional.only(start: 0.h, end: deleteIcon?.isNotEmpty == true ? 2.h : 8.h), - backgroundColor: backgroundColor, - shape: shape ?? - SmoothRectangleBorder( - borderRadius: BorderRadius.circular(8 ?? 0), - smoothness: 10, - side: BorderSide(color: AppColors.transparent, width: 1.5), - ), - deleteIcon: deleteIcon?.isNotEmpty == true - ? Utils.buildSvgWithAssets(icon: deleteIcon!, width: deleteIconSize!.width!.h, height: deleteIconSize!.height.h, iconColor: deleteIconHasColor ? deleteIconColor : null) - : null, - onDeleted: deleteIcon?.isNotEmpty == true ? () {} : null, - ) - : Chip( - materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, - label: richText ?? labelText!.toText10(weight: FontWeight.w500, letterSpacing: 0, color: textColor), - padding: EdgeInsets.all(0.0), - backgroundColor: backgroundColor, - shape: shape ?? SmoothRectangleBorder( - borderRadius: BorderRadius.circular(8 ?? 0), - smoothness: 10, - side: BorderSide(color: AppColors.transparent, width: 1.5), + child: icon.isNotEmpty + ? Chip( + avatar: icon.isNotEmpty ? Utils.buildSvgWithAssets(icon: icon, width: iconSize.h, height: iconSize.h, iconColor: iconHasColor ? iconColor : null, fit: BoxFit.contain) : SizedBox.shrink(), + label: richText ?? labelText!.toText10(weight: FontWeight.w500, letterSpacing: 0, color: textColor), + padding: padding, + materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, + labelPadding: labelPadding ?? EdgeInsetsDirectional.only(start: 0.h, end: deleteIcon?.isNotEmpty == true ? 2.h : 8.h), + backgroundColor: backgroundColor, + shape: shape ?? + SmoothRectangleBorder( + borderRadius: BorderRadius.circular(8 ?? 0), + smoothness: 10, + side: BorderSide(color: AppColors.transparent, width: 1.5), + ), + deleteIcon: deleteIcon?.isNotEmpty == true + ? Utils.buildSvgWithAssets(icon: deleteIcon!, width: deleteIconSize!.width!.h, height: deleteIconSize!.height.h, iconColor: deleteIconHasColor ? deleteIconColor : null) + : null, + onDeleted: deleteIcon?.isNotEmpty == true ? () {} : null, + ) + : Chip( + materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, + label: richText ?? labelText!.toText10(weight: FontWeight.w500, letterSpacing: 0, color: textColor), + padding: EdgeInsets.all(0.0), + backgroundColor: backgroundColor, + shape: shape ?? + SmoothRectangleBorder( + borderRadius: BorderRadius.circular(8 ?? 0), + smoothness: 10, + side: BorderSide(color: AppColors.transparent, width: 1.5), + ), + labelPadding: EdgeInsetsDirectional.only(start: 8.h, end: deleteIcon?.isNotEmpty == true ? -2.h : 8.h), + deleteIcon: deleteIcon?.isNotEmpty == true + ? Utils.buildSvgWithAssets(icon: deleteIcon!, width: deleteIconSize!.width.h, height: deleteIconSize!.height.h, iconColor: deleteIconHasColor ? deleteIconColor : null) + : null, + onDeleted: deleteIcon?.isNotEmpty == true ? () {} : null, ), - labelPadding: EdgeInsetsDirectional.only(start: 8.h, end: deleteIcon?.isNotEmpty == true ? -2.h : 8.h), - deleteIcon: deleteIcon?.isNotEmpty == true - ? Utils.buildSvgWithAssets(icon: deleteIcon!, width: deleteIconSize!.width.h, height: deleteIconSize!.height.h, iconColor: deleteIconHasColor ? deleteIconColor : null) - : null, - onDeleted: deleteIcon?.isNotEmpty == true ? () {} : null, - ), + ), ); } } diff --git a/lib/widgets/chip/custom_chip_widget.dart b/lib/widgets/chip/custom_chip_widget.dart index 1d0b5ef..5dee4f0 100644 --- a/lib/widgets/chip/custom_chip_widget.dart +++ b/lib/widgets/chip/custom_chip_widget.dart @@ -17,6 +17,7 @@ class CustomChipWidget extends StatelessWidget { final Color? textColor; final Color? borderColor; final bool isShowBorder; + final double? height; const CustomChipWidget({ super.key, @@ -31,6 +32,7 @@ class CustomChipWidget extends StatelessWidget { this.textColor, this.borderColor, this.isShowBorder = false, + this.height, }); @override @@ -39,6 +41,7 @@ class CustomChipWidget extends StatelessWidget { final hasOnTap = onTap != null || hasIcon; return Container( + height: height, decoration: BoxDecoration( borderRadius: BorderRadius.circular(borderRadius), color: isSelected ? chipType.color : backgroundColor ?? chipType.backgroundColor, diff --git a/lib/widgets/common_bottom_sheet.dart b/lib/widgets/common_bottom_sheet.dart index 29df19b..c6db474 100644 --- a/lib/widgets/common_bottom_sheet.dart +++ b/lib/widgets/common_bottom_sheet.dart @@ -104,55 +104,137 @@ class ButtonSheetContent extends StatelessWidget { } } -Future showCommonBottomSheetWithoutHeight( - BuildContext context, { - required Widget child, - required VoidCallback callBackFunc, - String title = "", - bool isCloseButtonVisible = true, - bool isFullScreen = true, - bool isDismissible = true, - Widget? titleWidget, -}) async { +void showCommonBottomSheetWithoutHeight( + BuildContext context, { + required Widget child, + required VoidCallback callBackFunc, + String title = "", + bool isCloseButtonVisible = true, + bool isFullScreen = true, + bool isDismissible = true, + Widget? titleWidget, + bool useSafeArea = false, + }) { showModalBottomSheet( - sheetAnimationStyle: AnimationStyle( - duration: Duration(milliseconds: 500), // Custom animation duration - reverseDuration: Duration(milliseconds: 300), // Custom reverse animation duration - ), - context: context, - isScrollControlled: true, - showDragHandle: false, - isDismissible: isDismissible, - backgroundColor: AppColors.bottomSheetBgColor, - builder: (BuildContext context) { - return SafeArea( - top: false, - left: false, - right: false, - child: isCloseButtonVisible - ? Container( - padding: EdgeInsets.only(left: 24, top: 24, right: 24, bottom: 12), - decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.bottomSheetBgColor, borderRadius: 24.h), - child: Column( - mainAxisSize: MainAxisSize.min, - spacing: 16.h, + sheetAnimationStyle: AnimationStyle( + duration: Duration(milliseconds: 500), + reverseDuration: Duration(milliseconds: 300), + ), + context: context, + isScrollControlled: true, + showDragHandle: false, + isDismissible: isDismissible, + backgroundColor: AppColors.bottomSheetBgColor, + useSafeArea: useSafeArea, + builder: (BuildContext context) { + return SafeArea( + top: false, + left: false, + right: false, + child: Padding( + padding: EdgeInsets.only( + bottom: MediaQuery.of(context).viewInsets.bottom, + ), + child: SingleChildScrollView( + physics: ClampingScrollPhysics(), + child: isCloseButtonVisible + ? Container( + padding: EdgeInsets.only( + left: 24, + top: 24, + right: 24, + bottom: 12, + ), + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: AppColors.bottomSheetBgColor, + borderRadius: 24.h, + ), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.center, children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - titleWidget ?? Expanded(child: title.toText20(weight: FontWeight.w600)), - Utils.buildSvgWithAssets(icon: AppAssets.close_bottom_sheet_icon, iconColor: Color(0xff2B353E)).onPress(() { - Navigator.of(context).pop(); - }), - ], - ), - child, + titleWidget ?? + Expanded( + child: title.toText20(weight: FontWeight.w600), + ), + Utils.buildSvgWithAssets( + icon: AppAssets.close_bottom_sheet_icon, + iconColor: Color(0xff2B353E), + ).onPress(() { + Navigator.of(context).pop(); + }), ], - )) - : child, - ); - }).then((value) { + ), + SizedBox(height: 16.h), + child, + ], + ), + ) + : child, + ), + ), + ); + }, + ).then((value) { callBackFunc(); }); } + +// void showCommonBottomSheetWithoutHeight( +// BuildContext context, { +// required Widget child, +// required VoidCallback callBackFunc, +// String title = "", +// bool isCloseButtonVisible = true, +// bool isFullScreen = true, +// bool isDismissible = true, +// Widget? titleWidget, +// bool useSafeArea = false, +// +// }) { +// showModalBottomSheet( +// sheetAnimationStyle: AnimationStyle( +// duration: Duration(milliseconds: 500), // Custom animation duration +// reverseDuration: Duration(milliseconds: 300), // Custom reverse animation duration +// ), +// context: context, +// isScrollControlled: true, +// showDragHandle: false, +// isDismissible: isDismissible, +// backgroundColor: AppColors.bottomSheetBgColor, +// useSafeArea: useSafeArea, +// builder: (BuildContext context) { +// return SafeArea( +// top: false, +// left: false, +// right: false, +// child: isCloseButtonVisible +// ? Container( +// padding: EdgeInsets.only(left: 24, top: 24, right: 24, bottom: 12), +// decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.bottomSheetBgColor, borderRadius: 24.h), +// child: Column( +// mainAxisSize: MainAxisSize.min, +// spacing: 16.h, +// children: [ +// Row( +// mainAxisAlignment: MainAxisAlignment.spaceBetween, +// crossAxisAlignment: CrossAxisAlignment.center, +// children: [ +// titleWidget ?? Expanded(child: title.toText20(weight: FontWeight.w600)), +// Utils.buildSvgWithAssets(icon: AppAssets.close_bottom_sheet_icon, iconColor: Color(0xff2B353E)).onPress(() { +// Navigator.of(context).pop(); +// }), +// ], +// ), +// child, +// ], +// )) +// : child, +// ); +// }).then((value) { +// callBackFunc(); +// }); +// } diff --git a/lib/widgets/date_range_selector/viewmodel/date_range_view_model.dart b/lib/widgets/date_range_selector/viewmodel/date_range_view_model.dart index 398a809..d295987 100644 --- a/lib/widgets/date_range_selector/viewmodel/date_range_view_model.dart +++ b/lib/widgets/date_range_selector/viewmodel/date_range_view_model.dart @@ -1,5 +1,6 @@ import 'package:dartz/dartz.dart'; import 'package:flutter/material.dart'; +import 'package:hmg_patient_app_new/core/utils/date_util.dart'; import 'package:hmg_patient_app_new/features/lab/models/Range.dart'; class DateRangeSelectorRangeViewModel extends ChangeNotifier { @@ -50,7 +51,7 @@ class DateRangeSelectorRangeViewModel extends ChangeNotifier { get getCurrentYear => DateTime.now().year; calculateDatesFromRange() { - _toDate = DateTime.now(); + _toDate = DateTime.now().provideDateOnly(); switch (_currentlySelectedRange) { case Range.WEEKLY: _fromDate = _toDate!.subtract(Duration(days: 7)); diff --git a/lib/widgets/family_files/family_file_add_widget.dart b/lib/widgets/family_files/family_file_add_widget.dart new file mode 100644 index 0000000..8e57f91 --- /dev/null +++ b/lib/widgets/family_files/family_file_add_widget.dart @@ -0,0 +1,105 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:hmg_patient_app_new/core/app_assets.dart'; +import 'package:hmg_patient_app_new/core/dependencies.dart'; +import 'package:hmg_patient_app_new/core/enums.dart'; +import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; +import 'package:hmg_patient_app_new/core/utils/validation_utils.dart'; +import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; +import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; +import 'package:hmg_patient_app_new/features/authentication/authentication_view_model.dart'; +import 'package:hmg_patient_app_new/features/medical_file/medical_file_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/buttons/custom_button.dart'; +import 'package:hmg_patient_app_new/widgets/dropdown/country_dropdown_widget.dart'; +import 'package:hmg_patient_app_new/widgets/input_widget.dart'; + +class FamilyFileAddWidget extends StatelessWidget { + final Function()? onVerificationPress; + final String message; + + const FamilyFileAddWidget(this.onVerificationPress, this.message, {super.key}); + + @override + Widget build(BuildContext context) { + AuthenticationViewModel authVm = getIt.get(); + MedicalFileViewModel? medicalVM = getIt.get(); + // TODO: implement build + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + message.toText16(color: AppColors.textColor, weight: FontWeight.w500), + SizedBox(height: 20.h), + Container( + decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(24)), + padding: EdgeInsets.symmetric(horizontal: 16.h, vertical: 8.h), + child: Column( + children: [ + CustomCountryDropdown( + countryList: CountryEnum.values, + onCountryChange: authVm.onCountryChange, + ).paddingOnly(top: 8.h, bottom: 16.h), + Divider(height: 1.h, color: AppColors.spacerLineColor), + TextInputWidget( + labelText: LocaleKeys.nationalIdNumber.tr(), + hintText: "xxxxxxxxx", + controller: authVm.nationalIdController, + isEnable: true, + prefix: null, + isAllowRadius: true, + isBorderAllowed: false, + isAllowLeadingIcon: true, + autoFocus: true, + keyboardType: TextInputType.number, + padding: EdgeInsets.symmetric(vertical: 8.h), + leadingIcon: AppAssets.student_card, + ).paddingOnly(top: 8.h, bottom: 8.h), + Divider(height: 1.h, color: AppColors.spacerLineColor), + TextInputWidget( + labelText: LocaleKeys.phoneNumber.tr(), + hintText: "", + controller: authVm.phoneNumberController, + isEnable: true, + prefix: authVm.selectedCountrySignup.countryCode, + isAllowRadius: true, + isBorderAllowed: false, + isAllowLeadingIcon: true, + autoFocus: true, + keyboardType: TextInputType.number, + padding: EdgeInsets.symmetric(vertical: 8.h), + leadingIcon: AppAssets.smart_phone, + ).paddingOnly(top: 8.h, bottom: 4.h), + ], + ), + ), + SizedBox(height: 20.h), + CustomButton( + text: "Verify the member".needTranslation, + onPressed: () { + FocusScope.of(context).unfocus(); + if (ValidationUtils.isValidatedIdAndPhoneWithCountryValidation( + nationalId: authVm.nationalIdController.text, + selectedCountry: authVm.selectedCountrySignup, + phoneNumber: authVm.phoneNumberController.text, + onOkPress: () { + Navigator.of(context).pop(); + }, + )) { + // authVm.addFamilyMember(otpTypeEnum: OTPTypeEnum.sms, isExcludedUser: true); + if (onVerificationPress != null) { + onVerificationPress!(); + } + } + }, + icon: AppAssets.add_icon, + height: 56.h, + fontWeight: FontWeight.w600), + SizedBox(height: 20.h), + ], + ); + } +} diff --git a/pubspec.yaml b/pubspec.yaml index aad6de7..8265141 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -101,6 +101,8 @@ flutter: - assets/fonts/ - assets/langs/ - assets/images/ + - assets/images/png/body_parts/male/ + - assets/images/png/body_parts/female/ - assets/images/svg/ - assets/images/png/ - assets/animations/