Compare commits

...

88 Commits

Author SHA1 Message Date
aamir-csol 24362f67cb family screen & widgets 3 weeks ago
aamir-csol aaa8222b1a Merge branch 'master' into dev_aamir
# Conflicts:
#	lib/core/api/api_client.dart
#	lib/core/app_assets.dart
#	lib/features/medical_file/medical_file_repo.dart
#	lib/features/medical_file/medical_file_view_model.dart
#	lib/presentation/home/landing_page.dart
#	lib/presentation/medical_file/medical_file_page.dart
#	lib/presentation/profile_settings/profile_settings.dart
#	lib/widgets/chip/app_custom_chip_widget.dart
3 weeks ago
aamir-csol d918b6e464 family screen & widgets 3 weeks ago
Haroon6138 0eaa50dcb3 Merge pull request 'haroon_dev' (#75) from haroon_dev into master
Reviewed-on: #75
4 weeks ago
haroon amjad 9d48a00b21 Merge branch 'master' into haroon_dev 4 weeks ago
Haroon6138 3bbc014251 Merge pull request 'appointment date filter handled' (#74) from appointment_filter into master
Reviewed-on: #74
4 weeks ago
Haroon6138 a4dc0648e8 Merge pull request 'lab prescription details are added andgraph visibility added to the graph page for result that are IRR' (#73) from feature/lab_result into master
Reviewed-on: #73
4 weeks ago
haroon amjad f1a60a9dfb updates 4 weeks ago
tahaalam 61007302c9 appointment date filter handled 4 weeks ago
tahaalam e5dcabacce lab prescription details are added andgraph visibility added to the graph page for result that are IRR 4 weeks ago
haroon amjad d220be7283 Tamara payment flow done 4 weeks ago
haroon amjad 30eb8bf5de dental booking flow implemented 4 weeks ago
Haroon6138 a661448d27 Merge pull request 'haroon_dev' (#72) from haroon_dev into master
Reviewed-on: #72
4 weeks ago
haroon amjad c0606fb530 Merge branch 'master' into haroon_dev
# Conflicts:
#	lib/features/my_appointments/my_appointments_view_model.dart
#	lib/presentation/appointments/widgets/hospital_bottom_sheet/hospital_bottom_sheet_body.dart
#	lib/presentation/book_appointment/select_clinic_page.dart
#	lib/presentation/lab/lab_orders_page.dart
4 weeks ago
Haroon6138 6e24698031 Merge pull request 'appointment filters added.' (#71) from appointment_filter into master
Reviewed-on: #71
4 weeks ago
haroon amjad b05e4e2a15 Dental appointment booking flow implementation contd. 4 weeks ago
haroon amjad 2b21086cc1 Dental appointment booking flow implementation contd. 4 weeks ago
tahaalam 483a1571d9 Merge remote-tracking branch 'origin/master' into appointment_filter
# Conflicts:
#	assets/langs/ar-SA.json
#	lib/generated/locale_keys.g.dart
#	lib/presentation/lab/lab_orders_page.dart
#	lib/presentation/lab/lab_results/lab_result_details.dart
4 weeks ago
tahaalam 6cf08c04d2 appointment filters added. 4 weeks ago
Haroon6138 156d7b4834 Merge pull request 'id selection from the appropiate list' (#70) from feature/search_by_region into master
Reviewed-on: #70
4 weeks ago
haroon amjad 415c0eb3c7 Dental appointment booking implementation contd. 4 weeks ago
tahaalam 35e145ddf9 id selection from the appropiate list 4 weeks ago
haroon amjad 8291e89ee7 Merge branch 'master' into haroon_dev
# Conflicts:
#	lib/presentation/book_appointment/select_clinic_page.dart
4 weeks ago
haroon amjad 903248133e Lab order & results enhancements 4 weeks ago
Haroon6138 7e92cad55b Merge pull request 'bottom handled when dental and laser clinic is selected from clinic page' (#69) from feature/search_by_region into master
Reviewed-on: #69
4 weeks ago
tahaalam bbf83d4dc9 bottom handled when dental and laser clinic is selected from clinic page 4 weeks ago
haroon amjad 3d64ddf78b Merge branch 'master' into haroon_dev 4 weeks ago
Haroon6138 51c787c92a Merge pull request 'feature/lab_result' (#68) from feature/lab_result into master
Reviewed-on: #68
4 weeks ago
haroon amjad db979977ca Tamara implementation & enhancements 4 weeks ago
tahaalam 641df8b51b logs removed 4 weeks ago
tahaalam 36bf229787 Merge remote-tracking branch 'origin/master' into feature/lab_result
# Conflicts:
#	assets/langs/ar-SA.json
#	assets/langs/en-US.json
#	lib/core/api/api_client.dart
#	lib/generated/locale_keys.g.dart
#	lib/presentation/lab/lab_results/lab_result_details.dart
4 weeks ago
tahaalam 42abf9ca1b login bypass removed 4 weeks ago
tahaalam 2e9a462a1c listing resizing if the data is in small numbers. 4 weeks ago
Haroon6138 5a1bd6a5b8 Merge pull request 'haroon_dev' (#67) from haroon_dev into master
Reviewed-on: #67
4 weeks ago
haroon amjad 8166369f85 Merge branch 'master' into haroon_dev
# Conflicts:
#	lib/core/app_assets.dart
#	lib/widgets/input_widget.dart
4 weeks ago
haroon amjad 5e000db556 updates 4 weeks ago
Haroon6138 4909246958 Merge pull request 'feature/doctor_filter' (#66) from feature/doctor_filter into master
Reviewed-on: #66
4 weeks ago
tahaalam 786aa05b19 doctor list handled to be cleared when going back 4 weeks ago
tahaalam a4a32cca44 doctor filter icon removed from the doctor listing from clininc 4 weeks ago
haroon amjad 1d22e53f63 Merge branch 'master' into haroon_dev
# Conflicts:
#	ios/Runner.xcodeproj/project.pbxproj
#	lib/presentation/medical_file/medical_file_page.dart
4 weeks ago
haroon amjad 8a7f46441c updates 4 weeks ago
tahaalam 6ef9ae813d doctors search filter added 4 weeks ago
haroon amjad 3418480122 Immediate LiveCare implementation completed 4 weeks ago
haroon amjad 10b77206f6 Immediate LiveCare consultation implementation contd. 4 weeks ago
tahaalam da89c41925 lab result by hospital and translations are added 1 month ago
haroon amjad 2f4d6f5553 immediate livecare consultation implementation contd. 1 month ago
haroon amjad 093e645f60 request medical report implementation completed. 1 month ago
Haroon Amjad 8bb0e495cc request medical report implementation contd. 1 month ago
haroon amjad 2de02102c2 profile settings update 1 month ago
aamir-csol e5488299b8 Merge branch 'master' into dev_aamir
# Conflicts:
#	lib/extensions/string_extensions.dart
#	lib/presentation/my_family/widget/family_cards.dart
#	lib/widgets/chip/app_custom_chip_widget.dart
#	lib/widgets/common_bottom_sheet.dart
1 month ago
aamir-csol 1335d3a7c8 family screen & widgets 1 month ago
Haroon6138 dbe67834b8 Merge pull request 'Improved Error Handling' (#64) from faiz_dev1 into master
Reviewed-on: #64
1 month ago
faizatflutter 4cc4a7b0d9 merged main 1 month ago
faizatflutter 23942dc7bc Merge branch 'master' into faiz_dev
# Conflicts:
#	lib/core/api/api_client.dart
1 month ago
haroon amjad 6565ba719d Merge branch 'master' into haroon_dev 1 month ago
haroon amjad 1af2db5c09 patient profile settings page integration implementation contd. 1 month ago
Haroon6138 153bbf27b9 Merge pull request 'feature/lab_result' (#63) from feature/lab_result into master
Reviewed-on: #63
1 month ago
tahaalam 51a4f83347 Merge remote-tracking branch 'origin/master' into feature/lab_result
# Conflicts:
#	lib/presentation/lab/lab_results/lab_result_details.dart
1 month ago
tahaalam 048f0bb459 graph design change 1 month ago
Haroon6138 0aa0aeba98 Merge pull request 'haroon_dev' (#62) from haroon_dev into master
Reviewed-on: #62
1 month ago
aamir-csol 060e7a8d68 Merge branch 'master' into dev_aamir 1 month ago
aamir-csol 288a0018ce family screen & widgets 1 month ago
haroon amjad 5f43063f2d Merge branch 'master' into haroon_dev
# Conflicts:
#	lib/core/app_assets.dart
1 month ago
Haroon6138 eaee656a74 Merge pull request 'Lab result view along with the listing of items is added' (#61) from feature/lab_result into master
Reviewed-on: #61
1 month ago
haroon amjad 6b28b57307 empty state implementation contd. 1 month ago
tahaalam b05b03a6f6 divider changed 1 month ago
tahaalam 512628fb8b Lab result view along with the listing of items is added 1 month ago
aamir-csol 36193d349c Merge remote-tracking branch 'origin/dev_sultan' into dev_aamir
# Conflicts:
#	lib/features/authentication/authentication_repo.dart
#	lib/features/authentication/authentication_view_model.dart
#	lib/features/medical_file/medical_file_view_model.dart
1 month ago
aamir-csol 02433e1bef family screen & widgets 1 month ago
Haroon Amjad 62e82a7a31 Empty data state implementation contd. 1 month ago
Sultan khan e2188b5a69 switch family member with super user 1 month ago
faizatflutter dc16a684c5 Merge branch 'master' into faiz_dev 1 month ago
faizatflutter 94313b8870 Improved Error Handling 1 month ago
haroon amjad 918eb18b5f Region flow in select clinic implemented 1 month ago
haroon amjad a07369165d Updates 1 month ago
Sultan khan 318441c77f Merge branch 'master' of http://34.17.52.180/Haroon6138/HMG_Patient_App_New into dev_sultan 1 month ago
aamir-csol 45a1ce9254 Merge branch 'master' into dev_aamir 1 month ago
haroon amjad f7e26da6f4 Merge branch 'master' into haroon_dev 1 month ago
Haroon6138 90d6e08fde Merge pull request 'dev_sikander' (#58) from dev_sikander into master
Reviewed-on: #58
1 month ago
haroon amjad 344ecaba18 LiveCare Schedule implementation done 1 month ago
Sultan khan 3642523fdf Merge branch 'dev_aamir' of http://34.17.52.180/Haroon6138/HMG_Patient_App_New into dev_sultan
# Conflicts:
#	lib/features/medical_file/medical_file_view_model.dart
1 month ago
Sikander Saleem 7c90d966c1 improvement 1 month ago
Sikander Saleem 434185afa7 Merge branch 'refs/heads/master' into dev_sikander 1 month ago
Sikander Saleem 604e503e52 flow improvement & translation added. 1 month ago
Sultan khan 3acd549de7 no message 1 month ago
haroon amjad b1af9e1779 Merge branch 'master' into haroon_dev 1 month ago
haroon amjad a711d3a6dd LiveCare shcedule implementation contd. 1 month ago
Sultan khan d5bd99f525 Merge branch 'dev_aamir' of http://34.17.52.180/Haroon6138/HMG_Patient_App_New into dev_sultan
# Conflicts:
#	lib/presentation/medical_file/medical_file_page.dart
1 month ago

File diff suppressed because one or more lines are too long

@ -0,0 +1 @@
{"nm":"Comp 1","ddd":0,"h":100,"w":100,"meta":{"g":"@lottiefiles/toolkit-js 0.33.2"},"layers":[{"ty":4,"nm":"Shape Layer 2","sr":1,"st":0,"op":300,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[5.277,-32.723,0],"ix":1},"s":{"a":0,"k":[4.91,4.91,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":0,"k":[50.2,50.18,0],"ix":2},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":11}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"mn":"ADBE Vector Group","nm":"Ellipse 2","ix":1,"cix":2,"np":3,"it":[{"ty":"el","bm":0,"hd":false,"mn":"ADBE Vector Shape - Ellipse","nm":"Ellipse Path 1","d":1,"p":{"a":0,"k":[0,0],"ix":3},"s":{"a":0,"k":[102.555,102.555],"ix":2}},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.0941,0.7608,0.451],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[3.277,-34.527],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"gr","bm":0,"hd":false,"mn":"ADBE Vector Group","nm":"Ellipse 1","ix":2,"cix":2,"np":3,"it":[{"ty":"el","bm":0,"hd":false,"mn":"ADBE Vector Shape - Ellipse","nm":"Ellipse Path 1","d":1,"p":{"a":0,"k":[0,0],"ix":3},"s":{"a":0,"k":[102.555,102.555],"ix":2}},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.0941,0.7608,0.451],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":1,"k":[{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[100,100],"t":0},{"s":[295,295],"t":60}],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[3.277,-34.527],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":1,"k":[{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[100],"t":0},{"s":[0],"t":60}],"ix":7}}]}],"ind":1}],"v":"5.5.7","fr":60,"op":61,"ip":0,"assets":[]}

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

@ -0,0 +1,3 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M0.9375 9C0.9375 13.4528 4.54725 17.0625 9 17.0625C13.4528 17.0625 17.0625 13.4528 17.0625 9C17.0625 4.54725 13.4528 0.9375 9 0.9375C4.54725 0.9375 0.9375 4.54725 0.9375 9ZM12.507 6.19724C12.8123 6.47699 12.8325 6.95173 12.5528 7.25698L8.42776 11.757C8.28976 11.9077 8.0955 11.9955 7.8915 12C7.68675 12.0045 7.4895 11.925 7.34475 11.7803L5.46975 9.90527C5.1765 9.61277 5.1765 9.13723 5.46975 8.84473C5.76225 8.55148 6.23776 8.55148 6.53026 8.84473L7.85175 10.1655L11.4472 6.24302C11.727 5.93777 12.2018 5.91749 12.507 6.19724Z" fill="#8F9AA3"/>
</svg>

After

Width:  |  Height:  |  Size: 697 B

@ -0,0 +1,3 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.04288 1.3125C10.6859 1.31249 11.9764 1.31248 12.984 1.44795C14.0165 1.58676 14.8357 1.87704 15.4793 2.52069C16.123 3.16433 16.4132 3.98352 16.5521 5.01602C16.6875 6.02358 16.6875 7.31408 16.6875 8.9571V9.04288C16.6875 10.6859 16.6875 11.9764 16.5521 12.984C16.4132 14.0165 16.123 14.8357 15.4793 15.4793C14.8357 16.123 14.0165 16.4132 12.984 16.5521C11.9764 16.6875 10.6859 16.6875 9.0429 16.6875H8.95712C7.3141 16.6875 6.02358 16.6875 5.01602 16.5521C3.98352 16.4132 3.16433 16.123 2.52068 15.4793C1.87704 14.8357 1.58676 14.0165 1.44795 12.984C1.31248 11.9764 1.31249 10.6859 1.3125 9.04288V8.95712C1.31249 7.31409 1.31248 6.02358 1.44795 5.01602C1.58676 3.98352 1.87704 3.16433 2.52068 2.52069C3.16433 1.87704 3.98352 1.58676 5.01602 1.44795C6.02358 1.31248 7.31409 1.31249 8.95712 1.3125H9.04288ZM8.25 12.375C8.25 11.9608 8.58428 11.625 8.99665 11.625H9.00335C9.41571 11.625 9.75 11.9608 9.75 12.375C9.75 12.7892 9.41571 13.125 9.00335 13.125H8.99665C8.58428 13.125 8.25 12.7892 8.25 12.375ZM8.25009 9.375C8.25009 9.78921 8.58588 10.125 9.00009 10.125C9.4143 10.125 9.75009 9.78921 9.75009 9.375V6.375C9.75009 5.96079 9.4143 5.625 9.00009 5.625C8.58588 5.625 8.25009 5.96079 8.25009 6.375V9.375Z" fill="#ED1C2B"/>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

@ -0,0 +1,3 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6.41841 13.0471C6.53944 12.9557 6.90075 12.6828 7.10903 12.5202C7.52619 12.1947 8.08051 11.7498 8.63318 11.27C9.18864 10.7877 9.72961 10.2811 10.1273 9.83098C10.3267 9.6053 10.4782 9.40705 10.5769 9.24345C10.6696 9.08959 10.6889 8.9987 10.6889 8.9987C10.6889 8.9987 10.6696 8.91048 10.5769 8.75662C10.4782 8.59303 10.3267 8.39477 10.1273 8.16909C9.72961 7.71901 9.18864 7.21237 8.63317 6.73009C8.08049 6.25024 7.52617 5.80541 7.10901 5.47985C6.90072 5.31731 6.53992 5.04475 6.41889 4.95334C6.16875 4.76911 6.1148 4.41661 6.29902 4.16647C6.48325 3.91633 6.83537 3.86289 7.08551 4.04711L7.08742 4.04855C7.21436 4.14443 7.58862 4.42711 7.80114 4.59296C8.22772 4.92586 8.7984 5.38369 9.37073 5.8806C9.94026 6.37509 10.5243 6.91935 10.9704 7.42418C11.1928 7.67596 11.3929 7.93118 11.5403 8.1758C11.6785 8.40499 11.8144 8.69516 11.8144 9.00004C11.8144 9.30492 11.6785 9.59509 11.5403 9.82427C11.3929 10.0689 11.1928 10.3241 10.9704 10.5759C10.5243 11.0807 9.94027 11.625 9.37074 12.1195C8.79842 12.6164 8.22775 13.0742 7.80117 13.4071C7.5885 13.5731 7.21423 13.8558 7.08755 13.9515L7.08591 13.9527C6.83577 14.1369 6.48329 14.0837 6.29906 13.8336C6.11484 13.5835 6.16829 13.2313 6.41841 13.0471Z" fill="#161616"/>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

@ -0,0 +1,3 @@
<svg width="30" height="26" viewBox="0 0 30 26" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M15.7787 1.62383C17.9327 0.00618458 21.4984 -0.965477 25.473 1.47256C28.2213 3.15838 29.7678 6.67982 29.2251 10.7258C28.6799 14.7914 26.0484 19.3896 20.4756 23.5144L20.3369 23.6171C18.4017 25.0503 17.1201 25.9994 15.0001 25.9994C12.88 25.9994 11.5984 25.0503 9.66323 23.6171L9.5245 23.5144C3.95169 19.3896 1.32025 14.7914 0.77498 10.7258C0.232334 6.67982 1.77878 3.15838 4.5271 1.47256C8.50173 -0.965478 12.0674 0.00618442 14.2214 1.62383C14.5758 1.88996 14.819 2.07209 15.0001 2.19508C15.1811 2.07209 15.4243 1.88996 15.7787 1.62383ZM15.2118 2.3251C15.2117 2.3251 15.2108 2.32473 15.209 2.32395L15.2072 2.32309C15.2104 2.32437 15.2119 2.32511 15.2118 2.3251ZM14.7929 2.32309C14.7875 2.32524 14.7868 2.32588 14.7911 2.32395L14.7929 2.32309ZM24.4273 3.17739C21.2508 1.22896 18.5751 2.02499 16.9797 3.22308L16.9519 3.24398C16.5914 3.51473 16.2816 3.74735 16.0339 3.90952C15.9052 3.99381 15.7626 4.07964 15.6146 4.1465C15.4742 4.20996 15.2585 4.28934 15.0001 4.28934C14.7416 4.28934 14.5259 4.20996 14.3855 4.1465C14.2375 4.07964 14.0949 3.99381 13.9662 3.90952C13.7185 3.74736 13.4089 3.51481 13.0484 3.24407L13.0204 3.22308C11.4251 2.02499 8.74927 1.22896 5.57284 3.17739C3.62867 4.36994 2.29796 7.03563 2.75723 10.46C2.76742 10.536 2.7785 10.6123 2.79049 10.689C2.85811 10.6746 2.92826 10.667 3.00018 10.667L5.7991 10.6669C6.42367 10.666 6.97406 10.6652 7.47333 10.8468C8.17556 11.1023 8.66627 11.6989 8.95799 12.0536C8.982 12.0828 9.00493 12.1106 9.02624 12.1362C9.04161 12.1547 9.05631 12.1736 9.07032 12.1932L10.2586 13.8481L13.476 8.48583C13.6606 8.17823 13.9958 7.99305 14.3544 8.00054C14.7131 8.00804 15.0402 8.20707 15.2118 8.52212L17.452 12.6363C17.7498 13.112 17.7869 13.1449 17.8184 13.1691C17.9144 13.2428 18.0296 13.289 18.2138 13.3136C18.3868 13.3367 18.5557 13.3358 18.7968 13.3344C18.8593 13.334 18.9267 13.3337 19.0002 13.3337H20.3335C20.8858 13.3337 21.3335 13.7814 21.3335 14.3337C21.3335 14.8859 20.8858 15.3337 20.3335 15.3337H19.0002C18.9535 15.3337 18.9012 15.3345 18.8442 15.3355C18.3195 15.3445 17.388 15.3603 16.6002 14.7552C16.2445 14.4821 16.0142 14.1116 15.7919 13.7542L15.7402 13.6711C15.7296 13.6541 15.7195 13.6369 15.7099 13.6193L14.2914 11.0141L11.191 16.1815C11.0178 16.4701 10.711 16.6523 10.3747 16.6661C10.0383 16.68 9.71756 16.5237 9.52122 16.2502L7.46691 13.3891C7.08115 12.9274 6.93061 12.7776 6.78959 12.7263C6.66628 12.6815 6.50179 12.667 5.66685 12.667H3.28376C4.27649 15.5775 6.55282 18.8266 10.7144 21.9068C12.8239 23.4682 13.5939 23.9994 15.0001 23.9994C16.4062 23.9994 17.1762 23.4682 19.2858 21.9068C24.5367 18.0202 26.7862 13.8648 27.2429 10.46C27.7021 7.03563 26.3714 4.36994 24.4273 3.17739Z" fill="#ED1C2B"/>
</svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

@ -0,0 +1,3 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M9 17.0625C4.5472 17.0625 0.9375 13.4528 0.9375 9C0.9375 4.5472 4.5472 0.9375 9 0.9375C13.4528 0.9375 17.0625 4.5472 17.0625 9C17.0625 13.4528 13.4528 17.0625 9 17.0625ZM9 5.25C9.41421 5.25 9.75 5.58579 9.75 6V9.37499H9.87151C10.0893 9.37492 10.3198 9.37484 10.5016 9.39662C10.6342 9.41249 11.1221 9.47231 11.3472 9.92903C11.5725 10.3861 11.3138 10.7931 11.2436 10.903C11.1469 11.0544 11.0021 11.2289 10.8651 11.3939L10.8416 11.4223C10.626 11.6824 10.3693 11.9784 10.1118 12.2139C9.98322 12.3315 9.83449 12.4526 9.67303 12.5484C9.52334 12.6371 9.28695 12.75 9 12.75C8.71306 12.75 8.47666 12.6371 8.32697 12.5484C8.16551 12.4526 8.01679 12.3315 7.88821 12.2139C7.63072 11.9784 7.37397 11.6824 7.15837 11.4223L7.13486 11.3939C6.99791 11.2289 6.85315 11.0544 6.75644 10.903C6.68621 10.7931 6.42751 10.3861 6.65279 9.92903C6.87788 9.47231 7.36577 9.41249 7.49837 9.39662C7.68024 9.37484 7.9107 9.37492 8.1285 9.37499H8.25L8.25 6C8.25 5.58579 8.58579 5.25 9 5.25Z" fill="#F43333"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 1.25C6.06294 1.25 1.25 6.06294 1.25 12C1.25 17.9371 6.06294 22.75 12 22.75C17.9371 22.75 22.75 17.9371 22.75 12C22.75 6.06294 17.9371 1.25 12 1.25ZM9.20711 7.79289C8.81658 7.40237 8.18342 7.40237 7.79289 7.79289C7.40237 8.18342 7.40237 8.81658 7.79289 9.20711L10.5858 12L7.79289 14.7929C7.40237 15.1834 7.40237 15.8166 7.79289 16.2071C8.18342 16.5976 8.81658 16.5976 9.20711 16.2071L12 13.4142L14.7929 16.2071C15.1834 16.5976 15.8166 16.5976 16.2071 16.2071C16.5976 15.8166 16.5976 15.1834 16.2071 14.7929L13.4142 12L16.2071 9.20711C16.5976 8.81658 16.5976 8.18342 16.2071 7.79289C15.8166 7.40237 15.1834 7.40237 14.7929 7.79289L12 10.5858L9.20711 7.79289Z" fill="#8F9AA3"/>
</svg>

After

Width:  |  Height:  |  Size: 835 B

@ -0,0 +1,3 @@
<svg width="22" height="22" viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M11 0.25C5.06294 0.25 0.25 5.06294 0.25 11C0.25 16.9371 5.06294 21.75 11 21.75C16.9371 21.75 21.75 16.9371 21.75 11C21.75 5.06294 16.9371 0.25 11 0.25ZM7 10C6.44772 10 6 10.4477 6 11C6 11.5523 6.44772 12 7 12H15C15.5523 12 16 11.5523 16 11C16 10.4477 15.5523 10 15 10H7Z" fill="#ED1C2B"/>
</svg>

After

Width:  |  Height:  |  Size: 441 B

@ -0,0 +1,5 @@
<svg width="24" height="25" viewBox="0 0 24 25" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12 2.75H11.9278C9.92082 2.74998 8.31557 2.74996 7.0558 2.91933C5.75291 3.0945 4.67453 3.46676 3.82064 4.32064C2.96676 5.17453 2.5945 6.25291 2.41933 7.5558C2.24996 8.81557 2.24998 10.4208 2.25 12.4278V12.5722C2.24998 14.5792 2.24996 16.1844 2.41933 17.4442C2.5945 18.7471 2.96676 19.8255 3.82064 20.6794C4.67453 21.5332 5.75291 21.9055 7.0558 22.0807C8.31558 22.25 9.92083 22.25 11.9278 22.25H12.0722C14.0792 22.25 15.6844 22.25 16.9442 22.0807C18.2471 21.9055 19.3255 21.5332 20.1794 20.6794C21.0332 19.8255 21.4055 18.7471 21.5807 17.4442C21.75 16.1844 21.75 14.5792 21.75 12.5722V12.5C21.75 11.9615 21.3135 11.525 20.775 11.525C20.2365 11.525 19.8 11.9615 19.8 12.5C19.8 14.5959 19.7979 16.0697 19.6481 17.1844C19.502 18.271 19.2317 18.8693 18.8005 19.3005C18.3693 19.7317 17.771 20.002 16.6844 20.1481C15.5697 20.2979 14.0959 20.3 12 20.3C9.90415 20.3 8.43035 20.2979 7.31564 20.1481C6.22898 20.002 5.63068 19.7317 5.1995 19.3005C4.76831 18.8693 4.49804 18.271 4.35194 17.1844C4.20207 16.0697 4.2 14.5959 4.2 12.5C4.2 10.4042 4.20207 8.93035 4.35194 7.81564C4.49804 6.72898 4.76831 6.13068 5.1995 5.6995C5.63068 5.26831 6.22898 4.99804 7.31564 4.85194C8.43035 4.70207 9.90415 4.7 12 4.7C12.5385 4.7 12.975 4.26348 12.975 3.725C12.975 3.18652 12.5385 2.75 12 2.75Z" fill="#2E3039"/>
<path d="M17.0855 3.5503C18.1526 2.48323 19.8827 2.48323 20.9497 3.5503C22.0168 4.61736 22.0168 6.34742 20.9497 7.41448L20.078 8.28615L16.2139 4.42188L17.0855 3.5503Z" fill="#2E3039"/>
<path d="M15.1533 5.48254L19.0174 9.34682L14.2337 14.1306C13.2653 15.0992 12.6734 15.6912 11.9448 16.0983C11.5383 16.3255 10.9218 16.5239 10.291 16.6994C9.64198 16.88 8.89524 17.0578 8.18242 17.2275L8.17373 17.2296C7.92037 17.2899 7.65385 17.2145 7.46969 17.0303C7.28552 16.8462 7.21009 16.5796 7.27041 16.3263L7.27249 16.3175C7.44221 15.6047 7.62 14.858 7.8006 14.209C7.97615 13.5782 8.17455 12.9617 8.40167 12.5553C8.80884 11.8266 9.40083 11.2348 10.3694 10.2664L15.1533 5.48254Z" fill="#2E3039"/>
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

@ -0,0 +1,8 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M16.75 3.47825C16.75 3.26399 16.75 3.06711 16.7387 2.90179C16.7266 2.72415 16.699 2.52881 16.6168 2.33031C16.4392 1.90151 16.0985 1.56083 15.6697 1.38321C15.4712 1.30099 15.2759 1.27338 15.0982 1.26126C14.9329 1.24998 14.7361 1.24999 14.5218 1.25L14.4782 1.25C14.264 1.24999 14.0671 1.24998 13.9018 1.26126C13.7241 1.27338 13.5288 1.30099 13.3303 1.38321C12.9015 1.56083 12.5608 1.90151 12.3832 2.33031C12.301 2.52881 12.2734 2.72415 12.2613 2.90179C12.2541 3.00643 12.2515 3.12369 12.2505 3.25L3 3.25C2.58579 3.25 2.25 3.58579 2.25 4C2.25 4.41421 2.58579 4.75 3 4.75L12.2505 4.75C12.2515 4.87632 12.2541 4.99358 12.2613 5.09821C12.2734 5.27585 12.301 5.4712 12.3832 5.6697C12.5608 6.0985 12.9015 6.43918 13.3303 6.61679C13.5288 6.69902 13.7241 6.72663 13.9018 6.73875C14.0671 6.75003 14.264 6.75002 14.4782 6.75H14.5218C14.736 6.75002 14.9329 6.75003 15.0982 6.73875C15.2759 6.72663 15.4712 6.69902 15.6697 6.61679C16.0985 6.43918 16.4392 6.0985 16.6168 5.6697C16.699 5.4712 16.7266 5.27586 16.7387 5.09821C16.75 4.9329 16.75 4.73607 16.75 4.52182V3.47825ZM13.75 4.00171V4.5C13.75 4.74323 13.7504 4.8881 13.7578 4.9961C13.7623 5.06293 13.7684 5.09134 13.7703 5.09869C13.7956 5.15753 13.8425 5.20442 13.9013 5.2297C13.9087 5.23157 13.9371 5.23767 14.0039 5.24223C14.1119 5.2496 14.2568 5.25 14.5 5.25C14.7432 5.25 14.8881 5.2496 14.9961 5.24223C15.0629 5.23767 15.0913 5.23157 15.0987 5.2297C15.1575 5.20442 15.2044 5.15753 15.2297 5.09869C15.2316 5.09134 15.2377 5.06294 15.2422 4.9961C15.2496 4.8881 15.25 4.74323 15.25 4.5V3.5C15.25 3.25677 15.2496 3.11191 15.2422 3.0039C15.2377 2.93707 15.2316 2.90867 15.2297 2.90131C15.2044 2.84248 15.1575 2.79558 15.0987 2.77031C15.0913 2.76844 15.0629 2.76234 14.9961 2.75778C14.8881 2.75041 14.7432 2.75 14.5 2.75C14.2568 2.75 14.1119 2.75041 14.0039 2.75778C13.9371 2.76234 13.9087 2.76844 13.9013 2.77031C13.8425 2.79558 13.7956 2.84248 13.7703 2.90131C13.7684 2.90867 13.7623 2.93707 13.7578 3.0039C13.7504 3.11191 13.75 3.25677 13.75 3.5V3.99829L13.75 4L13.75 4.00171Z" fill="#2E3039"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.2505 19.75L3 19.75C2.58579 19.75 2.25 19.4142 2.25 19C2.25 18.5858 2.58579 18.25 3 18.25L10.2505 18.25C10.2515 18.1237 10.2541 18.0064 10.2613 17.9018C10.2734 17.7241 10.301 17.5288 10.3832 17.3303C10.5608 16.9015 10.9015 16.5608 11.3303 16.3832C11.5288 16.301 11.7241 16.2734 11.9018 16.2613C12.0671 16.25 12.264 16.25 12.4782 16.25H12.5218C12.736 16.25 12.9329 16.25 13.0982 16.2613C13.2759 16.2734 13.4712 16.301 13.6697 16.3832C14.0985 16.5608 14.4392 16.9015 14.6168 17.3303C14.699 17.5288 14.7266 17.7241 14.7387 17.9018C14.75 18.0671 14.75 18.2639 14.75 18.4782L14.75 19.5218C14.75 19.736 14.75 19.9329 14.7387 20.0982C14.7266 20.2759 14.699 20.4712 14.6168 20.6697C14.4392 21.0985 14.0985 21.4392 13.6697 21.6168C13.4712 21.699 13.2759 21.7266 13.0982 21.7387C12.9329 21.75 12.736 21.75 12.5218 21.75H12.4782C12.264 21.75 12.0671 21.75 11.9018 21.7387C11.7241 21.7266 11.5288 21.699 11.3303 21.6168C10.9015 21.4392 10.5608 21.0985 10.3832 20.6697C10.301 20.4712 10.2734 20.2759 10.2613 20.0982C10.2541 19.9936 10.2515 19.8763 10.2505 19.75ZM11.75 19.5L11.75 19.0017L11.75 19L11.75 18.9983V18.5C11.75 18.2568 11.7504 18.1119 11.7578 18.0039C11.7623 17.9371 11.7684 17.9087 11.7703 17.9013C11.7956 17.8425 11.8425 17.7956 11.9013 17.7703C11.9087 17.7684 11.9371 17.7623 12.0039 17.7578C12.1119 17.7504 12.2568 17.75 12.5 17.75C12.7432 17.75 12.8881 17.7504 12.9961 17.7578C13.0629 17.7623 13.0913 17.7684 13.0987 17.7703C13.1575 17.7956 13.2044 17.8425 13.2297 17.9013C13.2316 17.9087 13.2377 17.9371 13.2422 18.0039C13.2496 18.1119 13.25 18.2568 13.25 18.5L13.25 19.5C13.25 19.7432 13.2496 19.8881 13.2422 19.9961C13.2377 20.0629 13.2316 20.0913 13.2297 20.0987C13.2044 20.1575 13.1575 20.2044 13.0987 20.2297C13.0913 20.2316 13.0629 20.2377 12.9961 20.2422C12.8881 20.2496 12.7432 20.25 12.5 20.25C12.2568 20.25 12.1119 20.2496 12.0039 20.2422C11.9371 20.2377 11.9087 20.2316 11.9013 20.2297C11.8425 20.2044 11.7956 20.1575 11.7703 20.0987C11.7684 20.0913 11.7623 20.0629 11.7578 19.9961C11.7504 19.8881 11.75 19.7432 11.75 19.5Z" fill="#2E3039"/>
<path d="M21 19.75C21.4142 19.75 21.75 19.4142 21.75 19C21.75 18.5858 21.4142 18.25 21 18.25H17C16.5858 18.25 16.25 18.5858 16.25 19C16.25 19.4142 16.5858 19.75 17 19.75H21Z" fill="#2E3039"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M21.75 11.5C21.75 11.9142 21.4142 12.25 21 12.25L11.7495 12.25C11.7485 12.3763 11.7459 12.4936 11.7387 12.5982C11.7266 12.7759 11.699 12.9712 11.6168 13.1697C11.4392 13.5985 11.0985 13.9392 10.6697 14.1168C10.4712 14.199 10.2759 14.2266 10.0982 14.2387C9.93288 14.25 9.73603 14.25 9.52176 14.25H9.47824C9.26396 14.25 9.06711 14.25 8.90179 14.2387C8.72415 14.2266 8.52881 14.199 8.3303 14.1168C7.9015 13.9392 7.56082 13.5985 7.38321 13.1697C7.30099 12.9712 7.27337 12.7759 7.26125 12.5982C7.24997 12.4329 7.24998 12.236 7.25 12.0218V10.9782C7.24998 10.764 7.24997 10.5671 7.26125 10.4018C7.27337 10.2242 7.30099 10.0288 7.38321 9.83031C7.56082 9.40151 7.9015 9.06083 8.3303 8.88321C8.52881 8.80099 8.72415 8.77338 8.90179 8.76126C9.06711 8.74998 9.26396 8.74999 9.47824 8.75H9.52175C9.73603 8.74999 9.93288 8.74998 10.0982 8.76126C10.2759 8.77338 10.4712 8.80099 10.6697 8.88321C11.0985 9.06083 11.4392 9.40151 11.6168 9.83031C11.699 10.0288 11.7266 10.2242 11.7387 10.4018C11.7459 10.5064 11.7485 10.6237 11.7495 10.75L21 10.75C21.4142 10.75 21.75 11.0858 21.75 11.5ZM10.0987 10.2703C10.1575 10.2956 10.2044 10.3425 10.2297 10.4013C10.2316 10.4087 10.2377 10.4371 10.2422 10.5039C10.2496 10.6119 10.25 10.7568 10.25 11V12C10.25 12.2432 10.2496 12.3881 10.2422 12.4961C10.2377 12.5629 10.2316 12.5913 10.2297 12.5987C10.2044 12.6575 10.1575 12.7044 10.0987 12.7297C10.0913 12.7316 10.0629 12.7377 9.9961 12.7422C9.88809 12.7496 9.74323 12.75 9.5 12.75C9.25677 12.75 9.1119 12.7496 9.0039 12.7422C8.93707 12.7377 8.90867 12.7316 8.90131 12.7297C8.84248 12.7044 8.79558 12.6575 8.7703 12.5987C8.76843 12.5913 8.76233 12.5629 8.75777 12.4961C8.7504 12.3881 8.75 12.2432 8.75 12V11C8.75 10.7568 8.7504 10.6119 8.75777 10.5039C8.76233 10.4371 8.76843 10.4087 8.7703 10.4013C8.79558 10.3425 8.84247 10.2956 8.90131 10.2703C8.90866 10.2684 8.93706 10.2623 9.0039 10.2578C9.1119 10.2504 9.25677 10.25 9.5 10.25C9.74323 10.25 9.88809 10.2504 9.9961 10.2578C10.0629 10.2623 10.0913 10.2684 10.0987 10.2703Z" fill="#2E3039"/>
<path d="M21 4.75C21.4142 4.75 21.75 4.41421 21.75 4C21.75 3.58579 21.4142 3.25 21 3.25L19 3.25C18.5858 3.25 18.25 3.58579 18.25 4C18.25 4.41421 18.5858 4.75 19 4.75L21 4.75Z" fill="#2E3039"/>
<path d="M5.75 11.5C5.75 11.9142 5.41421 12.25 5 12.25H3C2.58579 12.25 2.25 11.9142 2.25 11.5C2.25 11.0858 2.58579 10.75 3 10.75H5C5.41421 10.75 5.75 11.0858 5.75 11.5Z" fill="#2E3039"/>
</svg>

After

Width:  |  Height:  |  Size: 6.8 KiB

@ -1,3 +1,3 @@
<svg width="18" height="12" viewBox="0 0 18 12" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M17.7495 6.00047C17.7495 5.62691 17.5839 5.28034 17.427 5.02011C17.2577 4.7393 17.0301 4.44937 16.7803 4.16669C16.2794 3.59962 15.6257 2.99064 14.9913 2.43977C14.3532 1.88567 13.7173 1.37546 13.2423 1.00468C13.0044 0.818986 12.8059 0.66755 12.6665 0.562239C12.5968 0.509569 12.5418 0.468397 12.5039 0.440203L12.4604 0.407818L12.4488 0.399264L12.4448 0.396304C12.1113 0.150642 11.6414 0.221533 11.3957 0.555035C11.1501 0.888521 11.2213 1.358 11.5547 1.60367L11.5674 1.61308L11.6075 1.64285C11.6429 1.66925 11.6953 1.7085 11.7623 1.75911C11.8964 1.86036 12.0885 2.00695 12.3193 2.18713C12.7818 2.5481 13.3959 3.04099 14.0078 3.57235C14.6234 4.10694 15.2197 4.66576 15.6562 5.15983C15.6838 5.19108 15.7105 5.2218 15.7364 5.25195L0.999512 5.25196C0.585298 5.25196 0.249512 5.58774 0.249512 6.00195C0.249512 6.41617 0.585299 6.75196 0.999512 6.75196L15.7338 6.75195C15.7088 6.78116 15.6829 6.81088 15.6562 6.84111C15.2197 7.33518 14.6234 7.89401 14.0078 8.4286C13.3959 8.95995 12.7818 9.45285 12.3193 9.81382C12.0885 9.99399 11.8964 10.1406 11.7623 10.2418C11.6953 10.2925 11.6429 10.3317 11.6075 10.3581L11.5674 10.3879L11.5547 10.3973C11.2213 10.6429 11.1501 11.1124 11.3957 11.4459C11.6414 11.7794 12.1113 11.8503 12.4448 11.6046L12.4488 11.6017L12.4604 11.5931L12.5039 11.5607C12.5418 11.5326 12.5968 11.4914 12.6665 11.4387C12.8059 11.3334 13.0044 11.182 13.2423 10.9963C13.7173 10.6255 14.3532 10.1153 14.9913 9.56118C15.6257 9.01031 16.2794 8.40133 16.7803 7.83425C17.0301 7.55158 17.2577 7.26165 17.427 6.98083C17.5829 6.72217 17.7475 6.37819 17.7495 6.0072" fill="white"/> <path d="M28.7495 19.9995C28.7495 19.6259 28.5839 19.2794 28.427 19.0191C28.2577 18.7383 28.0301 18.4484 27.7803 18.1657C27.2794 17.5986 26.6257 16.9897 25.9913 16.4388C25.3532 15.8847 24.7173 15.3745 24.2423 15.0037C24.0044 14.818 23.8059 14.6666 23.6665 14.5613C23.5968 14.5086 23.5418 14.4674 23.5039 14.4392L23.4604 14.4068L23.4488 14.3983L23.4448 14.3953C23.1113 14.1497 22.6414 14.2206 22.3957 14.5541C22.1501 14.8875 22.2213 15.357 22.5547 15.6027L22.5674 15.6121L22.6075 15.6419C22.6429 15.6683 22.6953 15.7075 22.7623 15.7581C22.8964 15.8594 23.0885 16.006 23.3193 16.1862C23.7818 16.5471 24.3959 17.04 25.0078 17.5714C25.6234 18.106 26.2197 18.6648 26.6562 19.1589C26.6838 19.1901 26.7105 19.2208 26.7364 19.251L11.9995 19.251C11.5853 19.251 11.2495 19.5868 11.2495 20.001C11.2495 20.4152 11.5853 20.751 11.9995 20.751L26.7338 20.751C26.7088 20.7802 26.6829 20.8099 26.6562 20.8401C26.2197 21.3342 25.6234 21.893 25.0078 22.4276C24.3959 22.959 23.7818 23.4519 23.3193 23.8128C23.0885 23.993 22.8964 24.1396 22.7623 24.2409C22.6953 24.2915 22.6429 24.3307 22.6075 24.3571L22.5674 24.3869L22.5547 24.3963C22.2213 24.642 22.1501 25.1115 22.3957 25.4449C22.6414 25.7784 23.1113 25.8493 23.4448 25.6037L23.4488 25.6007L23.4604 25.5922L23.5039 25.5598C23.5418 25.5316 23.5968 25.4904 23.6665 25.4377C23.8059 25.3324 24.0044 25.181 24.2423 24.9953C24.7173 24.6245 25.3532 24.1143 25.9913 23.5602C26.6257 23.0093 27.2794 22.4003 27.7803 21.8333C28.0301 21.5506 28.2577 21.2607 28.427 20.9799C28.5829 20.7212 28.7475 20.3772 28.7495 20.0062" fill="#2E3039"/>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

@ -0,0 +1,4 @@
<svg width="20" height="14" viewBox="0 0 20 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12.43 13.823C12.24 13.823 12.05 13.753 11.9 13.603C11.61 13.313 11.61 12.833 11.9 12.543L17.44 7.00305L11.9 1.46305C11.61 1.17305 11.61 0.693047 11.9 0.403047C12.19 0.113047 12.67 0.113047 12.96 0.403047L19.03 6.47305C19.32 6.76305 19.32 7.24305 19.03 7.53305L12.96 13.603C12.81 13.753 12.62 13.823 12.43 13.823Z" fill="#2E3039"/>
<path d="M18.33 7.75305H1.5C1.09 7.75305 0.75 7.41305 0.75 7.00305C0.75 6.59305 1.09 6.25305 1.5 6.25305H18.33C18.74 6.25305 19.08 6.59305 19.08 7.00305C19.08 7.41305 18.74 7.75305 18.33 7.75305Z" fill="#2E3039"/>
</svg>

After

Width:  |  Height:  |  Size: 666 B

@ -0,0 +1,3 @@
<svg width="26" height="28" viewBox="0 0 26 28" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M0.666748 8.66634C0.666748 12.9303 3.86919 16.4463 8.00008 16.9403V20.333C8.00008 24.3831 11.2833 27.6663 15.3334 27.6663C19.3835 27.6663 22.6667 24.3831 22.6667 20.333V19.5283C24.2058 19.093 25.3334 17.678 25.3334 15.9997C25.3334 13.9746 23.6918 12.333 21.6667 12.333C19.6417 12.333 18.0001 13.9746 18.0001 15.9997C18.0001 17.678 19.1277 19.093 20.6667 19.5283V20.333C20.6667 23.2785 18.2789 25.6663 15.3334 25.6663C12.3879 25.6663 10.0001 23.2785 10.0001 20.333V16.9403C14.131 16.4463 17.3334 12.9303 17.3334 8.66634V3.99967C17.3334 2.71101 16.2887 1.66634 15.0001 1.66634H13.3334V1.33301C13.3334 0.780724 12.8857 0.333008 12.3334 0.333008C11.7811 0.333008 11.3334 0.780724 11.3334 1.33301V3.99967C11.3334 4.55196 11.7811 4.99967 12.3334 4.99967C12.8857 4.99967 13.3334 4.55196 13.3334 3.99967V3.66634H15.0001C15.1842 3.66634 15.3334 3.81558 15.3334 3.99967V8.66634C15.3334 12.1641 12.4979 14.9997 9.00008 14.9997C5.50228 14.9997 2.66675 12.1641 2.66675 8.66634V3.99967C2.66675 3.81558 2.81599 3.66634 3.00008 3.66634H4.66675V3.99967C4.66675 4.55196 5.11446 4.99967 5.66675 4.99967C6.21903 4.99967 6.66675 4.55196 6.66675 3.99967V1.33301C6.66675 0.780724 6.21903 0.333008 5.66675 0.333008C5.11446 0.333008 4.66675 0.780724 4.66675 1.33301V1.66634H3.00008C1.71142 1.66634 0.666748 2.71101 0.666748 3.99967V8.66634ZM21.6667 17.6663C20.7463 17.6663 20.0001 16.9202 20.0001 15.9997C20.0001 15.0792 20.7463 14.333 21.6667 14.333C22.5872 14.333 23.3334 15.0792 23.3334 15.9997C23.3334 16.9202 22.5872 17.6663 21.6667 17.6663Z" fill="#ED1C2B"/>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

@ -0,0 +1,5 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3 2C3.55229 2 4 2.44772 4 3L4 14C4 15.6782 4.00213 16.8362 4.11923 17.7072C4.17092 18.0917 4.23383 18.5232 4.45131 18.8563C4.60954 18.5142 4.76255 18.1696 4.91553 17.8252C5.29664 16.967 5.67762 16.1092 6.13888 15.2891C6.58328 14.499 7.11953 13.7058 7.77477 13.1011C8.43871 12.4882 9.28219 12.0189 10.3058 12.0189C11.6975 12.0189 12.5705 12.9129 13.1308 13.4867L13.1883 13.5455C13.8415 14.2124 14.1538 14.4717 14.6132 14.4717C15.0859 14.4717 15.4115 14.2943 15.733 13.9424C16.1205 13.5181 16.396 12.9986 16.6658 12.49C16.7134 12.4003 16.7608 12.3109 16.8086 12.2224C17.5056 10.9328 18.5501 9 20.9995 9C21.5518 9 21.9995 9.44772 21.9995 10C21.9995 10.5523 21.5518 11 20.9995 11C19.8601 11 19.3296 11.7648 18.5475 13.2114L18.4738 13.348C18.1403 13.9674 17.7401 14.7105 17.2097 15.2911C16.5941 15.9651 15.7622 16.4717 14.6132 16.4717C13.2476 16.4717 12.3831 15.5847 11.8309 15.0182C11.7865 14.9725 11.7412 14.925 11.6952 14.8766C11.3159 14.4783 10.8784 14.0189 10.3058 14.0189C9.93769 14.0189 9.55589 14.1788 9.13128 14.5707C8.69797 14.9707 8.28242 15.5577 7.88206 16.2696C7.45308 17.0322 7.09983 17.8308 6.7465 18.6295C6.56362 19.043 6.38072 19.4565 6.18729 19.865C6.22215 19.8708 6.25731 19.876 6.2928 19.8808C7.16378 19.9979 8.32182 20 10 20H21C21.5523 20 22 20.4477 22 21C22 21.5523 21.5523 22 21 22H9.928C8.33933 22 7.04616 22.0001 6.0263 21.8629C4.96232 21.7199 4.04736 21.4113 3.31802 20.682C2.58869 19.9526 2.28011 19.0377 2.13706 17.9737C1.99995 16.9539 1.99997 15.6607 2 14.0721L2 3C2 2.44772 2.44772 2 3 2Z" fill="#2E3039"/>
<path d="M8 4C8.55229 4 9 3.55228 9 3C9 2.44772 8.55229 2 8 2L7 2C6.44772 2 6 2.44772 6 3C6 3.55228 6.44772 4 7 4L8 4Z" fill="#2E3039"/>
<path d="M11 8C11.5523 8 12 7.55228 12 7C12 6.44772 11.5523 6 11 6H7C6.44772 6 6 6.44772 6 7C6 7.55228 6.44772 8 7 8L11 8Z" fill="#2E3039"/>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

@ -1,8 +1,5 @@
<svg data-name="Group 8404" height="26" id="Group_8404" viewBox="0 0 26 26" <svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
width="26" xmlns="http://www.w3.org/2000/svg"> <rect width="14" height="14" rx="3" fill="#0B85F7"/>
<rect data-name="Rectangle 17531" fill="#69a6e2" height="26" id="Rectangle_17531" rx="4" <path d="M2.54806 11.4469C2.54806 11.4469 2.98492 11.4437 3.29241 11.4437L5.6242 11.455C5.90252 11.455 6.13431 11.1634 6.14737 10.7422L6.15002 8.82227C6.15002 8.41291 6.3357 8.09262 6.6236 8.07432L7.33877 8.06869C7.62708 8.08618 7.825 8.4105 7.825 8.81725L7.82316 10.7474C7.83581 11.1685 8.03006 11.4433 8.30981 11.4433L11.4488 11.4518L11.4545 9.07923C11.4545 8.67107 11.2554 8.354 10.9648 8.3353L9.70382 8.32907C9.4249 8.32907 9.20555 8.03893 9.19372 7.6173L9.18882 6.97551C9.20269 6.55449 9.42326 6.26496 9.7028 6.26496L10.9707 6.26858C11.2588 6.25028 11.4465 5.91511 11.4465 5.50755L11.4496 2.5459C11.346 2.71309 11.2043 2.85421 11.0357 2.95807C10.8671 3.06193 10.6763 3.12568 10.4784 3.14426L8.34613 3.14245C8.06843 3.14245 7.83602 3.44083 7.82296 3.86185L7.82296 5.76371C7.8099 6.18393 7.59178 6.4813 7.31285 6.4813L6.66767 6.47647C6.38793 6.47647 6.1643 6.18875 6.15165 5.76974L6.15328 3.84698C6.14063 3.42575 5.91904 3.14326 5.63991 3.14326L2.54562 3.14466L2.54562 5.50997C2.54562 5.91853 2.73925 6.24565 3.02817 6.26455L4.29609 6.25933C4.58277 6.27782 4.78579 6.61481 4.78579 7.02115L4.78192 7.56865C4.78192 7.97701 4.58604 8.30373 4.29793 8.32223L3.02797 8.31921C2.73905 8.33932 2.54541 8.66102 2.54541 9.06938L2.54806 11.4469Z" fill="white"/>
width="26" /> <path d="M2.54806 11.4469C2.54806 11.4469 2.98492 11.4437 3.29241 11.4437L5.6242 11.455C5.90252 11.455 6.13431 11.1634 6.14737 10.7422L6.15002 8.82227C6.15002 8.41291 6.3357 8.09262 6.6236 8.07432L7.33877 8.06869C7.62708 8.08618 7.825 8.4105 7.825 8.81725L7.82316 10.7474C7.83581 11.1685 8.03006 11.4433 8.30981 11.4433L11.4488 11.4518L11.4545 9.07923C11.4545 8.67107 11.2554 8.354 10.9648 8.3353L9.70382 8.32907C9.4249 8.32907 9.20555 8.03893 9.19372 7.6173L9.18882 6.97551C9.20269 6.55449 9.42326 6.26496 9.7028 6.26496L10.9707 6.26858C11.2588 6.25028 11.4465 5.91511 11.4465 5.50755L11.4496 2.5459C11.346 2.71309 11.2043 2.85421 11.0357 2.95807C10.8671 3.06193 10.6763 3.12568 10.4784 3.14426L8.34613 3.14245C8.06843 3.14245 7.83602 3.44083 7.82296 3.86185L7.82296 5.76371C7.8099 6.18393 7.59178 6.4813 7.31285 6.4813L6.66767 6.47647C6.38793 6.47647 6.1643 6.18875 6.15165 5.76974L6.15328 3.84698C6.14063 3.42575 5.91904 3.14326 5.63991 3.14326L2.54562 3.14466L2.54562 5.50997C2.54562 5.91853 2.73925 6.24565 3.02817 6.26455L4.29609 6.25933C4.58277 6.27782 4.78579 6.61481 4.78579 7.02115L4.78192 7.56865C4.78192 7.97701 4.58604 8.30373 4.29793 8.32223L3.02797 8.31921C2.73905 8.33932 2.54541 8.66102 2.54541 9.06938L2.54806 11.4469Z" fill="white"/>
<path d="M2.914,17.807s.731-.005,1.246-.005l3.9.019c.466,0,.854-.488.876-1.193l0-3.213c0-.685.311-1.221.793-1.252l1.2-.009c.483.029.814.572.814,1.253l0,3.231c.021.7.346,1.165.814,1.165l5.254.014.01-3.971c0-.683-.333-1.214-.82-1.245l-2.11-.01c-.467,0-.834-.486-.854-1.191l-.008-1.074c.023-.7.392-1.189.86-1.189l2.122.006c.482-.031.8-.592.8-1.274l.005-4.957a2.126,2.126,0,0,1-.693.69,2.164,2.164,0,0,1-.933.312l-3.569,0c-.465,0-.854.5-.876,1.2V8.295c-.022.7-.387,1.2-.854,1.2l-1.08-.008c-.468,0-.842-.482-.864-1.183l0-3.218c-.021-.705-.392-1.178-.859-1.178l-5.179,0V7.87c0,.684.324,1.231.808,1.263l2.122-.009c.48.031.82.595.82,1.275l-.007.916c0,.683-.328,1.23-.81,1.261l-2.126-.005c-.484.034-.808.572-.808,1.256Z" data-name="Path 5276"
fill="#fff"
id="Path_5276" transform="translate(2.327 2.327)" />
</svg> </svg>

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

@ -1,8 +1,5 @@
<svg data-name="Group 8404" height="26" id="Group_8404" viewBox="0 0 26 26" <svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
width="26" xmlns="http://www.w3.org/2000/svg"> <rect width="14" height="14" rx="3" fill="#ED1C2B"/>
<rect data-name="Rectangle 17531" fill="#cc2229" height="26" id="Rectangle_17531" rx="4" <path d="M2.54806 11.4469C2.54806 11.4469 2.98492 11.4437 3.29241 11.4437L5.6242 11.455C5.90252 11.455 6.13431 11.1634 6.14737 10.7422L6.15002 8.82227C6.15002 8.41291 6.3357 8.09262 6.6236 8.07432L7.33877 8.06869C7.62708 8.08618 7.825 8.4105 7.825 8.81725L7.82316 10.7474C7.83581 11.1685 8.03006 11.4433 8.30981 11.4433L11.4488 11.4518L11.4545 9.07923C11.4545 8.67107 11.2554 8.354 10.9648 8.3353L9.70382 8.32907C9.4249 8.32907 9.20555 8.03893 9.19372 7.6173L9.18882 6.97551C9.20269 6.55449 9.42326 6.26496 9.7028 6.26496L10.9707 6.26858C11.2588 6.25028 11.4465 5.91511 11.4465 5.50755L11.4496 2.5459C11.346 2.71309 11.2043 2.85421 11.0357 2.95807C10.8671 3.06193 10.6763 3.12568 10.4784 3.14426L8.34613 3.14245C8.06843 3.14245 7.83602 3.44083 7.82296 3.86185L7.82296 5.76371C7.8099 6.18393 7.59178 6.4813 7.31285 6.4813L6.66767 6.47647C6.38793 6.47647 6.1643 6.18875 6.15165 5.76974L6.15328 3.84698C6.14063 3.42575 5.91904 3.14326 5.63991 3.14326L2.54562 3.14466L2.54562 5.50997C2.54562 5.91853 2.73925 6.24565 3.02817 6.26455L4.29609 6.25933C4.58277 6.27782 4.78579 6.61481 4.78579 7.02115L4.78192 7.56865C4.78192 7.97701 4.58604 8.30373 4.29793 8.32223L3.02797 8.31921C2.73905 8.33932 2.54541 8.66102 2.54541 9.06938L2.54806 11.4469Z" fill="white"/>
width="26" /> <path d="M2.54806 11.4469C2.54806 11.4469 2.98492 11.4437 3.29241 11.4437L5.6242 11.455C5.90252 11.455 6.13431 11.1634 6.14737 10.7422L6.15002 8.82227C6.15002 8.41291 6.3357 8.09262 6.6236 8.07432L7.33877 8.06869C7.62708 8.08618 7.825 8.4105 7.825 8.81725L7.82316 10.7474C7.83581 11.1685 8.03006 11.4433 8.30981 11.4433L11.4488 11.4518L11.4545 9.07923C11.4545 8.67107 11.2554 8.354 10.9648 8.3353L9.70382 8.32907C9.4249 8.32907 9.20555 8.03893 9.19372 7.6173L9.18882 6.97551C9.20269 6.55449 9.42326 6.26496 9.7028 6.26496L10.9707 6.26858C11.2588 6.25028 11.4465 5.91511 11.4465 5.50755L11.4496 2.5459C11.346 2.71309 11.2043 2.85421 11.0357 2.95807C10.8671 3.06193 10.6763 3.12568 10.4784 3.14426L8.34613 3.14245C8.06843 3.14245 7.83602 3.44083 7.82296 3.86185L7.82296 5.76371C7.8099 6.18393 7.59178 6.4813 7.31285 6.4813L6.66767 6.47647C6.38793 6.47647 6.1643 6.18875 6.15165 5.76974L6.15328 3.84698C6.14063 3.42575 5.91904 3.14326 5.63991 3.14326L2.54562 3.14466L2.54562 5.50997C2.54562 5.91853 2.73925 6.24565 3.02817 6.26455L4.29609 6.25933C4.58277 6.27782 4.78579 6.61481 4.78579 7.02115L4.78192 7.56865C4.78192 7.97701 4.58604 8.30373 4.29793 8.32223L3.02797 8.31921C2.73905 8.33932 2.54541 8.66102 2.54541 9.06938L2.54806 11.4469Z" fill="white"/>
<path d="M2.914,17.807s.731-.005,1.246-.005l3.9.019c.466,0,.854-.488.876-1.193l0-3.213c0-.685.311-1.221.793-1.252l1.2-.009c.483.029.814.572.814,1.253l0,3.231c.021.7.346,1.165.814,1.165l5.254.014.01-3.971c0-.683-.333-1.214-.82-1.245l-2.11-.01c-.467,0-.834-.486-.854-1.191l-.008-1.074c.023-.7.392-1.189.86-1.189l2.122.006c.482-.031.8-.592.8-1.274l.005-4.957a2.126,2.126,0,0,1-.693.69,2.164,2.164,0,0,1-.933.312l-3.569,0c-.465,0-.854.5-.876,1.2V8.295c-.022.7-.387,1.2-.854,1.2l-1.08-.008c-.468,0-.842-.482-.864-1.183l0-3.218c-.021-.705-.392-1.178-.859-1.178l-5.179,0V7.87c0,.684.324,1.231.808,1.263l2.122-.009c.48.031.82.595.82,1.275l-.007.916c0,.683-.328,1.23-.81,1.261l-2.126-.005c-.484.034-.808.572-.808,1.256Z" data-name="Path 5276"
fill="#fff"
id="Path_5276" transform="translate(2.327 2.327)" />
</svg> </svg>

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

@ -0,0 +1,3 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.29289 7.29289C7.68342 6.90237 8.31658 6.90237 8.70711 7.29289L16 14.5858L23.2929 7.29289C23.6834 6.90237 24.3166 6.90237 24.7071 7.29289C25.0976 7.68342 25.0976 8.31658 24.7071 8.70711L17.4142 16L24.7071 23.2929C25.0976 23.6834 25.0976 24.3166 24.7071 24.7071C24.3166 25.0976 23.6834 25.0976 23.2929 24.7071L16 17.4142L8.70711 24.7071C8.31658 25.0976 7.68342 25.0976 7.29289 24.7071C6.90237 24.3166 6.90237 23.6834 7.29289 23.2929L14.5858 16L7.29289 8.70711C6.90237 8.31658 6.90237 7.68342 7.29289 7.29289Z" fill="#2E3039"/>
</svg>

After

Width:  |  Height:  |  Size: 684 B

@ -0,0 +1,7 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10 13C10 12.4477 10.4477 12 11 12L16 12C16.5523 12 17 12.4477 17 13C17 13.5523 16.5523 14 16 14L11 14C10.4477 14 10 13.5523 10 13Z" fill="#2E3039"/>
<path d="M7 13C7 12.4477 7.44772 12 8 12H8.00898C8.56127 12 9.00898 12.4477 9.00898 13C9.00898 13.5523 8.56127 14 8.00898 14H8C7.44772 14 7 13.5523 7 13Z" fill="#2E3039"/>
<path d="M7 17C7 16.4477 7.44772 16 8 16L13 16C13.5523 16 14 16.4477 14 17C14 17.5523 13.5523 18 13 18L8 18C7.44772 18 7 17.5523 7 17Z" fill="#2E3039"/>
<path d="M14.991 17C14.991 16.4477 15.4387 16 15.991 16H16C16.5523 16 17 16.4477 17 17C17 17.5523 16.5523 18 16 18H15.991C15.4387 18 14.991 17.5523 14.991 17Z" fill="#2E3039"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M7 2C7 1.44772 6.55229 1 6 1C5.44772 1 5 1.44772 5 2V2.44885C4.23882 2.6903 3.57734 3.06994 3.01802 3.6746C2.18949 4.57031 1.83279 5.69272 1.66416 7.04866C1.49997 8.36894 1.49998 10.0541 1.5 12.1739L1.5 12.8261C1.49998 14.9459 1.49997 16.6311 1.66416 17.9513C1.83279 19.3073 2.18949 20.4297 3.01802 21.3254C3.8568 22.2322 4.92535 22.6329 6.21533 22.8204C7.45142 23.0001 9.02292 23 10.9712 23H13.0288C14.977 23 16.5486 23.0001 17.7847 22.8204C19.0747 22.6329 20.1432 22.2322 20.982 21.3254C21.8105 20.4297 22.1672 19.3073 22.3358 17.9513C22.5 16.6311 22.5 14.9459 22.5 12.8261V12.1739C22.5 10.0541 22.5 8.36895 22.3358 7.04866C22.1672 5.69272 21.8105 4.57031 20.982 3.6746C20.4227 3.06993 19.7612 2.6903 19 2.44885V2C19 1.44772 18.5523 1 18 1C17.4477 1 17 1.44772 17 2V2.09173C15.903 1.99995 14.5863 1.99998 13.0288 2L10.9712 2C9.41375 1.99998 8.09705 1.99995 7 2.09173V2ZM3.66385 9.14417C3.8099 9 4.03921 9 4.49783 9L19.5022 9C19.9608 9 20.1901 9 20.3362 9.14417C20.4822 9.28833 20.4851 9.51472 20.491 9.96751C20.4997 10.6407 20.5 11.3942 20.5 12.2432V12.7568C20.5 14.9616 20.4982 16.5221 20.3511 17.7045C20.2067 18.8656 19.9374 19.5094 19.5138 19.9673C19.1004 20.4142 18.5353 20.6903 17.497 20.8412C16.4214 20.9975 14.9957 21 12.95 21H11.05C9.00425 21 7.57858 20.9975 6.503 20.8412C5.4647 20.6903 4.89956 20.4142 4.48622 19.9673C4.06263 19.5094 3.79327 18.8656 3.64887 17.7045C3.50182 16.5221 3.5 14.9616 3.5 12.7568V12.2432C3.5 11.3942 3.50027 10.6407 3.509 9.96751C3.51487 9.51472 3.51781 9.28833 3.66385 9.14417Z" fill="#2E3039"/>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

@ -0,0 +1,8 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3 4C2.44772 4 2 4.44772 2 5C2 5.55228 2.44772 6 3 6L5 6C5.55228 6 6 5.55229 6 5C6 4.44772 5.55228 4 5 4L3 4Z" fill="#2E3039"/>
<path d="M9 4C8.44772 4 8 4.44772 8 5C8 5.55228 8.44772 6 9 6L21 6C21.5523 6 22 5.55229 22 5C22 4.44772 21.5523 4 21 4L9 4Z" fill="#2E3039"/>
<path d="M8 12C8 11.4477 8.44772 11 9 11L21 11C21.5523 11 22 11.4477 22 12C22 12.5523 21.5523 13 21 13L9 13C8.44772 13 8 12.5523 8 12Z" fill="#2E3039"/>
<path d="M3 11C2.44772 11 2 11.4477 2 12C2 12.5523 2.44772 13 3 13H5C5.55228 13 6 12.5523 6 12C6 11.4477 5.55228 11 5 11L3 11Z" fill="#2E3039"/>
<path d="M8 19C8 18.4477 8.44772 18 9 18L21 18C21.5523 18 22 18.4477 22 19C22 19.5523 21.5523 20 21 20L9 20C8.44772 20 8 19.5523 8 19Z" fill="#2E3039"/>
<path d="M3 18C2.44772 18 2 18.4477 2 19C2 19.5523 2.44772 20 3 20H5C5.55228 20 6 19.5523 6 19C6 18.4477 5.55228 18 5 18H3Z" fill="#2E3039"/>
</svg>

After

Width:  |  Height:  |  Size: 975 B

@ -0,0 +1,4 @@
<svg width="19" height="19" viewBox="0 0 19 19" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="9.5" cy="9.5" r="6.5" fill="#18C273"/>
<circle cx="9.5" cy="9.5" r="8" stroke="#18C273" stroke-opacity="0.31" stroke-width="3"/>
</svg>

After

Width:  |  Height:  |  Size: 252 B

@ -0,0 +1,3 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M9 17.0625C4.5472 17.0625 0.9375 13.4528 0.9375 9C0.9375 4.5472 4.5472 0.9375 9 0.9375C13.4528 0.9375 17.0625 4.5472 17.0625 9C17.0625 13.4528 13.4528 17.0625 9 17.0625ZM9 5.25C9.41421 5.25 9.75 5.58579 9.75 6V9.37499H9.87151C10.0893 9.37492 10.3198 9.37484 10.5016 9.39662C10.6342 9.41249 11.1221 9.47231 11.3472 9.92903C11.5725 10.3861 11.3138 10.7931 11.2436 10.903C11.1469 11.0544 11.0021 11.2289 10.8651 11.3939L10.8416 11.4223C10.626 11.6824 10.3693 11.9784 10.1118 12.2139C9.98322 12.3315 9.83449 12.4526 9.67303 12.5484C9.52334 12.6371 9.28695 12.75 9 12.75C8.71306 12.75 8.47666 12.6371 8.32697 12.5484C8.16551 12.4526 8.01679 12.3315 7.88821 12.2139C7.63072 11.9784 7.37397 11.6824 7.15837 11.4223L7.13486 11.3939C6.99791 11.2289 6.85315 11.0544 6.75644 10.903C6.68621 10.7931 6.42751 10.3861 6.65279 9.92903C6.87788 9.47231 7.36577 9.41249 7.49837 9.39662C7.68024 9.37484 7.9107 9.37492 8.1285 9.37499H8.25L8.25 6C8.25 5.58579 8.58579 5.25 9 5.25Z" fill="#FF9E15"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

@ -0,0 +1,3 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M0.9375 9C0.9375 13.4528 4.54725 17.0625 9 17.0625C13.4528 17.0625 17.0625 13.4528 17.0625 9C17.0625 4.54725 13.4528 0.9375 9 0.9375C4.54725 0.9375 0.9375 4.54725 0.9375 9ZM12.507 6.19724C12.8123 6.47699 12.8325 6.95173 12.5528 7.25698L8.42776 11.757C8.28976 11.9077 8.0955 11.9955 7.8915 12C7.68675 12.0045 7.4895 11.925 7.34475 11.7803L5.46975 9.90527C5.1765 9.61277 5.1765 9.13723 5.46975 8.84473C5.76225 8.55148 6.23776 8.55148 6.53026 8.84473L7.85175 10.1655L11.4472 6.24302C11.727 5.93777 12.2018 5.91749 12.507 6.19724Z" fill="#18C273"/>
</svg>

After

Width:  |  Height:  |  Size: 701 B

@ -0,0 +1,8 @@
<svg width="22" height="22" viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M7 10.75C6.30964 10.75 5.75 11.3096 5.75 12C5.75 12.6904 6.30964 13.25 7 13.25H7.00897C7.69933 13.25 8.25897 12.6904 8.25897 12C8.25897 11.3096 7.69933 10.75 7.00897 10.75H7Z" fill="#2E3039"/>
<path d="M10.9955 10.75C10.3052 10.75 9.74551 11.3096 9.74551 12C9.74551 12.6904 10.3052 13.25 10.9955 13.25H11.0045C11.6948 13.25 12.2545 12.6904 12.2545 12C12.2545 11.3096 11.6948 10.75 11.0045 10.75H10.9955Z" fill="#2E3039"/>
<path d="M14.991 10.75C14.3007 10.75 13.741 11.3096 13.741 12C13.741 12.6904 14.3007 13.25 14.991 13.25H15C15.6904 13.25 16.25 12.6904 16.25 12C16.25 11.3096 15.6904 10.75 15 10.75H14.991Z" fill="#2E3039"/>
<path d="M7 14.75C6.30964 14.75 5.75 15.3096 5.75 16C5.75 16.6904 6.30964 17.25 7 17.25H7.00897C7.69933 17.25 8.25897 16.6904 8.25897 16C8.25897 15.3096 7.69933 14.75 7.00897 14.75H7Z" fill="#2E3039"/>
<path d="M10.9955 14.75C10.3052 14.75 9.74551 15.3096 9.74551 16C9.74551 16.6904 10.3052 17.25 10.9955 17.25H11.0045C11.6948 17.25 12.2545 16.6904 12.2545 16C12.2545 15.3096 11.6948 14.75 11.0045 14.75H10.9955Z" fill="#2E3039"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M6 1C6 0.447715 5.55229 0 5 0C4.44772 0 4 0.447715 4 1V1.44885C3.23882 1.6903 2.57734 2.06994 2.01802 2.6746C1.18949 3.57031 0.83279 4.69272 0.664161 6.04866C0.499967 7.36894 0.499982 9.05406 0.5 11.1739L0.5 11.8261C0.499982 13.9459 0.499967 15.6311 0.664161 16.9513C0.83279 18.3073 1.18949 19.4297 2.01802 20.3254C2.8568 21.2322 3.92535 21.6329 5.21533 21.8204C6.45141 22.0001 8.02291 22 9.97119 22H12.0288C13.977 22 15.5486 22.0001 16.7847 21.8204C18.0747 21.6329 19.1432 21.2322 19.982 20.3254C20.8105 19.4297 21.1672 18.3073 21.3358 16.9513C21.5 15.6311 21.5 13.9459 21.5 11.8261V11.1739C21.5 9.05408 21.5 7.36895 21.3358 6.04866C21.1672 4.69272 20.8105 3.57031 19.982 2.6746C19.4227 2.06993 18.7612 1.6903 18 1.44885V1C18 0.447715 17.5523 0 17 0C16.4477 0 16 0.447715 16 1V1.09173C14.903 0.999955 13.5863 0.999976 12.0288 1L9.97122 1C8.41376 0.999976 7.09704 0.999955 6 1.09173V1ZM3.49783 8C3.03921 8 2.8099 8 2.66385 8.14417C2.51781 8.28833 2.51487 8.51472 2.509 8.96751C2.50027 9.64067 2.5 10.3942 2.5 11.2432V11.7568C2.5 13.9616 2.50182 15.5221 2.64887 16.7045C2.79327 17.8656 3.06263 18.5094 3.48622 18.9673C3.89956 19.4142 4.4647 19.6903 5.503 19.8412C6.57858 19.9975 8.00425 20 10.05 20H11.95C13.9957 20 15.4214 19.9975 16.497 19.8412C17.5353 19.6903 18.1004 19.4142 18.5138 18.9673C18.9374 18.5094 19.2067 17.8656 19.3511 16.7045C19.4982 15.5221 19.5 13.9616 19.5 11.7568V11.2432C19.5 10.3942 19.4997 9.64067 19.491 8.96751C19.4851 8.51472 19.4822 8.28833 19.3362 8.14417C19.1901 8 18.9608 8 18.5022 8L3.49783 8Z" fill="#2E3039"/>
</svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

@ -0,0 +1,7 @@
<svg width="21" height="24" viewBox="0 0 21 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect y="0.5" width="21" height="3" rx="1.5" fill="#18C273"/>
<rect y="5.5" width="21" height="3" rx="1.5" fill="#18C273" fill-opacity="0.7"/>
<rect y="10.5" width="21" height="3" rx="1.5" fill="#18C273" fill-opacity="0.5"/>
<rect y="15.5" width="21" height="3" rx="1.5" fill="#18C273" fill-opacity="0.35"/>
<rect y="20.5" width="21" height="3" rx="1.5" fill="#18C273" fill-opacity="0.2"/>
</svg>

After

Width:  |  Height:  |  Size: 493 B

@ -0,0 +1,3 @@
<svg width="18" height="14" viewBox="0 0 18 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.55403 0.250001H6.69598C5.50451 0.249979 4.5346 0.249962 3.76973 0.352797C2.97174 0.460084 2.28552 0.691519 1.73852 1.23852C1.19152 1.78552 0.960084 2.47174 0.852797 3.26973C0.749962 4.0346 0.749979 5.00446 0.750001 6.19592V7.80403C0.749979 8.9955 0.749962 9.9654 0.852797 10.7303C0.960084 11.5283 1.19152 12.2145 1.73852 12.7615C2.28552 13.3085 2.97174 13.5399 3.76973 13.6472C4.53462 13.75 5.5045 13.75 6.696 13.75H7.55401C8.74551 13.75 9.71539 13.75 10.4803 13.6472C11.2783 13.5399 11.9645 13.3085 12.5115 12.7615C13.0406 12.2323 13.2745 11.5729 13.3863 10.808C13.7298 11.078 14.0404 11.3056 14.3208 11.4749C14.846 11.7922 15.5117 12.0579 16.1947 11.7194C16.8671 11.386 17.0755 10.7028 17.1622 10.0899C17.2493 9.47366 17.2493 8.64442 17.2493 7.64834V6.35366C17.2493 5.35758 17.2493 4.52834 17.1622 3.91212C17.0755 3.29915 16.8671 2.61597 16.1947 2.28264C15.5117 1.94405 14.846 2.20976 14.3208 2.52707C14.0405 2.69639 13.7299 2.92386 13.3866 3.19378C13.2748 2.42814 13.0411 1.76809 12.5115 1.23852C11.9645 0.691519 11.2783 0.460084 10.4803 0.352797C9.7154 0.249962 8.7455 0.249979 7.55403 0.250001ZM13.4957 5.03688C13.5 5.39712 13.5 5.78326 13.5 6.196V7.80401C13.5 8.2175 13.5 8.6043 13.4957 8.9651C14.2136 9.55664 14.7094 9.95725 15.0964 10.191C15.2999 10.314 15.4227 10.3584 15.4898 10.3717C15.5046 10.3746 15.5147 10.3756 15.5207 10.3759C15.5237 10.376 15.527 10.376 15.527 10.376L15.5285 10.3754C15.5298 10.3748 15.5308 10.3741 15.5316 10.3736C15.5326 10.3729 15.5331 10.3724 15.5331 10.3724C15.5352 10.3702 15.5464 10.3581 15.5624 10.3268C15.5983 10.2567 15.6425 10.1231 15.6769 9.87979C15.7474 9.38156 15.7493 8.66013 15.7493 7.58744V6.41456C15.7493 5.34186 15.7474 4.62044 15.6769 4.12221C15.6425 3.87891 15.5983 3.74533 15.5624 3.67518C15.5464 3.64392 15.5355 3.63212 15.5334 3.6299C15.5334 3.6299 15.5318 3.6282 15.5285 3.62659L15.5274 3.62604C15.5274 3.62604 15.5237 3.62597 15.5207 3.62613C15.5147 3.62643 15.5046 3.62741 15.4898 3.63035C15.4227 3.64362 15.2999 3.68801 15.0964 3.81097C14.7094 4.04475 14.2136 4.44534 13.4957 5.03688ZM7.5 4C7.5 3.58579 7.83579 3.25 8.25 3.25L9.75 3.25C10.1642 3.25 10.5 3.58579 10.5 4C10.5 4.41422 10.1642 4.75 9.75 4.75L8.25 4.75C7.83579 4.75 7.5 4.41422 7.5 4Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

@ -0,0 +1,8 @@
<svg width="17" height="18" viewBox="0 0 17 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.37427 0.9375C3.12631 0.9375 2.12427 1.95016 2.12427 3.1875C2.12427 4.42484 3.12631 5.4375 4.37427 5.4375C5.62222 5.4375 6.62427 4.42484 6.62427 3.1875C6.62427 1.95016 5.62222 0.9375 4.37427 0.9375Z" fill="#ED1C2B"/>
<path d="M6.67566 6.3702C5.2645 5.6266 3.48447 5.6266 2.0733 6.3702C2.03796 6.38882 1.99128 6.41204 1.93632 6.43938C1.67362 6.57008 1.22162 6.79494 0.913308 7.062C0.724359 7.22567 0.488314 7.48646 0.444083 7.84456C0.394558 8.24552 0.602042 8.58044 0.898469 8.83036C1.3683 9.22647 1.95554 9.5625 2.72106 9.5625L6.02791 9.5625C6.79342 9.5625 7.38067 9.22647 7.85049 8.83035C8.14692 8.58044 8.35441 8.24552 8.30488 7.84456C8.26065 7.48646 8.0246 7.22567 7.83565 7.062C7.52733 6.79493 7.07534 6.57007 6.81264 6.43938C6.75768 6.41204 6.71101 6.38882 6.67566 6.3702Z" fill="#ED1C2B"/>
<path d="M9.36201 1.8557C9.49867 1.63459 9.74007 1.5 10 1.5C12.9021 1.5 15.25 3.84793 15.25 6.75C15.25 7.16421 14.9142 7.5 14.5 7.5C14.0858 7.5 13.75 7.16421 13.75 6.75C13.75 5.30962 12.9412 4.06093 11.7522 3.43259L11.1504 4.03441C11.0364 4.14842 10.9065 4.25631 10.7469 4.27867C10.4366 4.32214 10.1199 4.16679 9.97204 3.87112L9.32918 2.58541C9.21293 2.35292 9.22536 2.07681 9.36201 1.8557Z" fill="#ED1C2B"/>
<path d="M2.5 10.5C2.91421 10.5 3.25 10.8358 3.25 11.25C3.25 12.6904 4.05882 13.9391 5.24779 14.5674L5.84961 13.9656C5.96361 13.8516 6.09348 13.7437 6.25314 13.7213C6.56336 13.6779 6.88013 13.8332 7.02796 14.1289L7.67082 15.4146C7.78707 15.6471 7.77464 15.9232 7.63799 16.1443C7.50133 16.3654 7.25993 16.5 7 16.5C4.09793 16.5 1.75 14.1521 1.75 11.25C1.75 10.8358 2.08579 10.5 2.5 10.5Z" fill="#ED1C2B"/>
<path d="M10.375 10.6875C10.375 9.45016 11.377 8.4375 12.625 8.4375C13.873 8.4375 14.875 9.45016 14.875 10.6875C14.875 11.9248 13.873 12.9375 12.625 12.9375C11.377 12.9375 10.375 11.9248 10.375 10.6875Z" fill="#ED1C2B"/>
<path d="M10.324 13.8702C11.7352 13.1266 13.5152 13.1266 14.9264 13.8702C14.9617 13.8888 15.0084 13.912 15.0634 13.9394C15.3261 14.0701 15.7781 14.2949 16.0864 14.562C16.2753 14.7257 16.5114 14.9865 16.5556 15.3446C16.6051 15.7455 16.3977 16.0804 16.1012 16.3304C15.6314 16.7265 15.0442 17.0625 14.2786 17.0625H10.9718C10.2063 17.0625 9.61903 16.7265 9.1492 16.3304C8.85277 16.0804 8.64529 15.7455 8.69482 15.3446C8.73905 14.9865 8.97509 14.7257 9.16404 14.562C9.47235 14.2949 9.92433 14.0701 10.187 13.9394C10.242 13.9121 10.2887 13.8888 10.324 13.8702Z" fill="#ED1C2B"/>
</svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

@ -0,0 +1,3 @@
<svg width="24" height="25" viewBox="0 0 24 25" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M4 1.75C3.58579 1.75 3.25 2.08579 3.25 2.5C3.25 2.91421 3.58579 3.25 4 3.25H4.25L4.25 5.5C4.25 8.58811 6.05618 11.2544 8.66976 12.5C6.05618 13.7456 4.25 16.4119 4.25 19.5L4.25 21.75H4C3.58579 21.75 3.25 22.0858 3.25 22.5C3.25 22.9142 3.58579 23.25 4 23.25L20 23.25C20.4142 23.25 20.75 22.9142 20.75 22.5C20.75 22.0858 20.4142 21.75 20 21.75H19.75V19.5C19.75 16.4119 17.9438 13.7456 15.3302 12.5C17.9438 11.2544 19.75 8.58811 19.75 5.5V3.25H20C20.4142 3.25 20.75 2.91421 20.75 2.5C20.75 2.08579 20.4142 1.75 20 1.75L4 1.75ZM18.25 3.25L5.75 3.25L5.75 5.5C5.75 8.95178 8.54822 11.75 12 11.75C15.4518 11.75 18.25 8.95178 18.25 5.5V3.25ZM18.25 21.75L18.25 19.5C18.25 16.0482 15.4518 13.25 12 13.25C8.54822 13.25 5.75 16.0482 5.75 19.5L5.75 21.75L18.25 21.75Z" fill="#2B353E"/>
</svg>

After

Width:  |  Height:  |  Size: 927 B

@ -0,0 +1,4 @@
<svg width="8" height="13" viewBox="0 0 8 13" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3.99992 0.228516C2.7918 0.228516 1.81242 1.20789 1.81242 2.41602C1.81242 3.04229 2.13626 3.65197 2.52655 4.09024C2.72604 4.31425 2.95689 4.50994 3.19989 4.65217C3.43884 4.79203 3.71565 4.89518 3.99992 4.89518C4.28419 4.89518 4.56099 4.79203 4.79995 4.65217C5.04295 4.50994 5.2738 4.31425 5.47329 4.09024C5.86358 3.65197 6.18742 3.04229 6.18742 2.41602C6.18742 1.20789 5.20804 0.228516 3.99992 0.228516Z" fill="#2E3039"/>
<path d="M1.61723 4.36783C1.63636 4.38209 1.65653 4.40812 1.69687 4.46017C1.75478 4.53488 1.8138 4.6057 1.8731 4.67228C2.12459 4.95469 2.42491 5.21256 2.7579 5.40745C3.07856 5.59513 3.50915 5.77031 3.99992 5.77031C4.49069 5.77031 4.92128 5.59513 5.24194 5.40745C5.57493 5.21256 5.87525 4.95469 6.12674 4.67228C6.18603 4.60571 6.24504 4.5349 6.30294 4.4602C6.3434 4.408 6.36363 4.3819 6.38283 4.36761C6.4397 4.3253 6.51163 4.32078 6.57335 4.35564C6.59419 4.36741 6.61516 4.38841 6.6571 4.4304C7.62953 5.28979 8.31909 6.79331 7.70799 8.24712C7.52984 8.67092 7.11767 8.95186 6.65499 8.95186L6.49354 8.95055C6.3607 8.94948 6.29428 8.94894 6.24626 8.9863C6.19823 9.02366 6.18241 9.08817 6.15078 9.21719L5.58834 11.511C5.40816 12.2458 4.75708 12.7704 4.00002 12.7704C3.24297 12.7704 2.59188 12.2458 2.41171 11.511L1.84927 9.21719C1.81763 9.08817 1.80181 9.02366 1.75379 8.9863C1.70576 8.94894 1.63934 8.94948 1.5065 8.95055L1.34506 8.95186C0.882379 8.95186 0.470201 8.67092 0.292058 8.24712C-0.319046 6.7933 0.370548 5.28979 1.34297 4.43041C1.38478 4.38854 1.40569 4.3676 1.42646 4.35585C1.48825 4.32088 1.5603 4.32541 1.61723 4.36783Z" fill="#2E3039"/>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

@ -126,7 +126,7 @@
"gregorianDate": "التاريخ الميلادي", "gregorianDate": "التاريخ الميلادي",
"verifyLoginWith": "يرجى اختيار واحدة من الخيارات التالية للتحقق", "verifyLoginWith": "يرجى اختيار واحدة من الخيارات التالية للتحقق",
"registerUser": "تسجيل", "registerUser": "تسجيل",
"verifyWithFingerprint":"البيومترية", "verifyWithFingerprint": "البيومترية",
"verifyWithFaceid": "معرف الوجه", "verifyWithFaceid": "معرف الوجه",
"verifyWithSms": "رسالة قصيرة", "verifyWithSms": "رسالة قصيرة",
"verifyWithWhatsapp": "واتساب", "verifyWithWhatsapp": "واتساب",
@ -850,6 +850,29 @@
"pleaseEnterAValidEmailFormat": "يرجى إدخال تنسيق بريد إلكتروني صالح", "pleaseEnterAValidEmailFormat": "يرجى إدخال تنسيق بريد إلكتروني صالح",
"selectCountry": "اختر الدولة", "selectCountry": "اختر الدولة",
"forLoginVerification": "للتحقق من تسجيل الدخول", "forLoginVerification": "للتحقق من تسجيل الدخول",
"searchHospital": "بحث في المستشفى" "searchHospital": "بحث في المستشفى",
"skip": "تخطي",
"getStarted": "ابدأ الآن",
"onboardingHeading1": "حجز المواعيد لم يكن أسهل من قبل",
"onboardingBody1": "ببضع نقرات فقط يمكنك استشارة الطبيب الذي تختاره.",
"onboardingHeading2": "الوصول إلى السجل الطبي بين يديك",
"onboardingBody2": "تتبع تاريخك الطبي بما في ذلك الفحوصات المخبرية، الوصفات الطبية، التأمين، وغيرها.",
"hmgHospitals": "مستشفيات HMG",
"hmcMedicalClinic": "مراكز HMC الطبية",
"applyFilter": "تطبيق الفلتر",
"facilityAndLocation": "المرفق والموقع",
"regionAndLocation": "المنطقة والمواقع",
"clearAllFilters": "مسح جميع الفلاتر",
"filters": "فلاتر",
"searchClinic": "بحث عن عيادة",
"normal": "عادي",
"attention": "انتباه",
"monitor": "مراقبة",
"noSpecialResult": "لا توجد نتائج خاصة",
"setTheDateRange": "تعيين النطاق الزمني",
"historyFlowchart": "مخطط تدفق التاريخ",
"to": "إلى",
"startDate": "تاريخ البدء",
"endDate": "تاريخ الانتهاء",
"walkin": "زيارة بدون موعد"
} }

@ -846,5 +846,30 @@
"selectCountry": "Select Country", "selectCountry": "Select Country",
"forLoginVerification": "for login verification", "forLoginVerification": "for login verification",
"lastLoginBy": "Last login by", "lastLoginBy": "Last login by",
"searchHospital": "Search Hospital" "searchHospital": "Search Hospital",
"skip": "Skip",
"getStarted": "Get Started",
"onboardingHeading1": "Booking appointment has never been easy",
"onboardingBody1": "In few clicks find yourself having consultation with the doctor of your choice.",
"onboardingHeading2": "Access the medical history on finger tips",
"onboardingBody2": "Keep track on your medical history including labs, prescription, insurance, etc",
"normal": "Normal",
"attention": "Attention",
"monitor": "Monitor",
"noSpecialResult": "No Special Results",
"setTheDateRange": "Set The Date Range",
"historyFlowchart": "History FlowChart",
"to": "to",
"startDate" : "Start Date",
"endDate": "End Date",
"hmgHospitals": "HMG Hospitals",
"hmcMedicalClinic": "HMC Medical Centers",
"applyFilter": "AppLy Filter",
"facilityAndLocation": "Facility and Location",
"regionAndLocation": "Region And Locations",
"clearAllFilters": "Clear all filters",
"filters": "Filters",
"searchClinic": "Search Clinic",
"walkin": "Walk In"
} }

@ -52,6 +52,7 @@
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; }; 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; }; 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
7595037DD52211B91157B0F3 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; }; 7595037DD52211B91157B0F3 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
769C9BF82E6F106D009F68A9 /* RunnerDebug.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = RunnerDebug.entitlements; sourceTree = "<group>"; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
8E12CEEB8E334EE22D5259D7 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; }; 8E12CEEB8E334EE22D5259D7 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
@ -130,6 +131,7 @@
97C146F01CF9000F007C117D /* Runner */ = { 97C146F01CF9000F007C117D /* Runner */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
769C9BF82E6F106D009F68A9 /* RunnerDebug.entitlements */,
478CFA952E6E20A60064F3D7 /* Runner.entitlements */, 478CFA952E6E20A60064F3D7 /* Runner.entitlements */,
478CFA932E638C8E0064F3D7 /* GoogleService-Info.plist */, 478CFA932E638C8E0064F3D7 /* GoogleService-Info.plist */,
97C146FA1CF9000F007C117D /* Main.storyboard */, 97C146FA1CF9000F007C117D /* Main.storyboard */,
@ -635,7 +637,7 @@
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_ENTITLEMENTS = Runner/RunnerDebug.entitlements;
CURRENT_PROJECT_VERSION = 2; CURRENT_PROJECT_VERSION = 2;
DEVELOPMENT_TEAM = 3A359E86ZF; DEVELOPMENT_TEAM = 3A359E86ZF;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;

@ -9,7 +9,6 @@ import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart'; import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/routes/app_routes.dart'; import 'package:hmg_patient_app_new/routes/app_routes.dart';
import 'package:hmg_patient_app_new/services/analytics/analytics_service.dart'; import 'package:hmg_patient_app_new/services/analytics/analytics_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/services/navigation_service.dart';
import 'package:http/http.dart' as http; import 'package:http/http.dart' as http;
@ -35,6 +34,7 @@ abstract class ApiClient {
required Function(dynamic response, int statusCode) onSuccess, required Function(dynamic response, int statusCode) onSuccess,
required Function(String error, int statusCode) onFailure, required Function(String error, int statusCode) onFailure,
Map<String, dynamic>? queryParams, Map<String, dynamic>? queryParams,
bool isAllowAny,
bool isExternal, bool isExternal,
bool isRCService, bool isRCService,
}); });
@ -80,27 +80,24 @@ abstract class ApiClient {
class ApiClientImp implements ApiClient { class ApiClientImp implements ApiClient {
final _analytics = getIt<GAnalytics>(); final _analytics = getIt<GAnalytics>();
final LoggerService _loggerService;
final AppState _appState; final AppState _appState;
ApiClientImp({ ApiClientImp({
required LoggerService loggerService,
required AppState appState, required AppState appState,
}) : _appState = appState, }) : _appState = appState;
_loggerService = loggerService;
@override @override
post( post(
String endPoint, { String endPoint, {
required Map<String, dynamic> body, required Map<String, dynamic> body,
required Function(dynamic response, int statusCode, {int? messageStatus, String? errorMessage}) onSuccess, required Function(dynamic response, int statusCode, {int? messageStatus, String? errorMessage}) onSuccess,
required Function(String error, int statusCode, {int? messageStatus, Failure? failureType}) onFailure, required Function(String error, int statusCode, {int? messageStatus, Failure? failureType}) onFailure,
bool isAllowAny = false, bool isAllowAny = false,
bool isExternal = false, bool isExternal = false,
bool isRCService = false, bool isRCService = false,
bool isPaymentServices = false, bool isPaymentServices = false,
bool bypassConnectionCheck = false, bool bypassConnectionCheck = false,
}) async { }) async {
String url; String url;
if (isExternal) { if (isExternal) {
url = endPoint; url = endPoint;
@ -177,7 +174,11 @@ class ApiClientImp implements ApiClient {
} }
// body['TokenID'] = "@dm!n"; // body['TokenID'] = "@dm!n";
// body['PatientID'] = 3628599; // body['PatientID'] = 4767884;
// body['PatientTypeID'] = 1;
//
// body['PatientOutSA'] = 0;
// body['SessionID'] = "45786230487560q";
} }
body.removeWhere((key, value) => value == null); body.removeWhere((key, value) => value == null);
@ -200,7 +201,6 @@ class ApiClientImp implements ApiClient {
final int statusCode = response.statusCode; final int statusCode = response.statusCode;
log("response.body: ${response.body}"); log("response.body: ${response.body}");
if (statusCode < 200 || statusCode >= 400) { if (statusCode < 200 || statusCode >= 400) {
var parsed = json.decode(utf8.decode(response.bodyBytes));
onFailure('Error While Fetching data', statusCode, failureType: StatusCodeFailure("Error While Fetching data")); onFailure('Error While Fetching data', statusCode, failureType: StatusCodeFailure("Error While Fetching data"));
logApiEndpointError(endPoint, 'Error While Fetching data', statusCode); logApiEndpointError(endPoint, 'Error While Fetching data', statusCode);
} else { } else {
@ -218,6 +218,7 @@ class ApiClientImp implements ApiClient {
} else { } else {
if (parsed['ErrorType'] == 4) { if (parsed['ErrorType'] == 4) {
//TODO : handle app update //TODO : handle app update
onFailure(parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage'], statusCode, failureType: AppUpdateFailure("parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']"));
logApiEndpointError(endPoint, parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage'], statusCode); logApiEndpointError(endPoint, parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage'], statusCode);
} }
if (parsed['ErrorType'] == 2) { if (parsed['ErrorType'] == 2) {
@ -225,9 +226,9 @@ class ApiClientImp implements ApiClient {
onFailure( onFailure(
parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage'], parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage'],
statusCode, statusCode,
failureType: MessageStatusFailure(parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']), failureType: UnAuthenticatedUserFailure(parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage'] ?? "User is not Authenticated", url: url),
); );
// logApiEndpointError(endPoint, "session logged out", statusCode); // logApiEndpointError(endPoint, "session logged out", statusCode);
} }
if (isAllowAny) { if (isAllowAny) {
onSuccess(parsed, statusCode, messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']); onSuccess(parsed, statusCode, messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']);
@ -248,6 +249,12 @@ class ApiClientImp implements ApiClient {
} }
} else if (parsed['MessageStatus'] == 1 || parsed['SMSLoginRequired'] == true) { } else if (parsed['MessageStatus'] == 1 || parsed['SMSLoginRequired'] == true) {
onSuccess(parsed, statusCode, messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']); onSuccess(parsed, statusCode, messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']);
} else if (parsed['IsAuthenticated'] == false) {
onFailure(
"User is not Authenticated",
statusCode,
failureType: UnAuthenticatedUserFailure(parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage'] ?? "User is not Authenticated", url: url),
);
} else if (parsed['MessageStatus'] == 2 && parsed['IsAuthenticated']) { } else if (parsed['MessageStatus'] == 2 && parsed['IsAuthenticated']) {
if (parsed['SameClinicApptList'] != null) { if (parsed['SameClinicApptList'] != null) {
onSuccess(parsed, statusCode, messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']); onSuccess(parsed, statusCode, messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']);
@ -277,7 +284,6 @@ class ApiClientImp implements ApiClient {
logApiEndpointError(endPoint, parsed['message'] ?? parsed['message'], statusCode); logApiEndpointError(endPoint, parsed['message'] ?? parsed['message'], statusCode);
} }
} }
} else if (!parsed['IsAuthenticated']) {
} else { } else {
if (parsed['SameClinicApptList'] != null) { if (parsed['SameClinicApptList'] != null) {
onSuccess(parsed, statusCode, messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']); onSuccess(parsed, statusCode, messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']);
@ -323,6 +329,7 @@ class ApiClientImp implements ApiClient {
{required Function(dynamic response, int statusCode) onSuccess, {required Function(dynamic response, int statusCode) onSuccess,
required Function(String error, int statusCode) onFailure, required Function(String error, int statusCode) onFailure,
Map<String, dynamic>? queryParams, Map<String, dynamic>? queryParams,
bool isAllowAny = false,
bool isExternal = false, bool isExternal = false,
bool isRCService = false}) async { bool isRCService = false}) async {
String url; String url;

@ -119,7 +119,7 @@ var GET_STATUS_FOR_COCO = 'Services/COCWS.svc/REST/GetStatusforCOC';
// var GET_PATIENT_AppointmentHistory = 'Services' // var GET_PATIENT_AppointmentHistory = 'Services'
// '/Doctors.svc/REST/PateintHasAppoimentHistory'; // '/Doctors.svc/REST/PateintHasAppoimentHistory';
var GET_PATIENT_AppointmentHistory = 'Services' var GET_PATIENT_APPOINTMENT_HISTORY_ASYNC = 'Services'
'/Doctors.svc/REST/PateintHasAppoimentHistory_Async'; '/Doctors.svc/REST/PateintHasAppoimentHistory_Async';
///VITAL SIGN ///VITAL SIGN
@ -743,6 +743,12 @@ class ApiConsts {
static String SERVICE_URL = 'https://hmgwebservices.com/PayFortWebLive/pages/SendPayFortRequest.aspx'; //Payfort Payment Gateway URL LIVE static String SERVICE_URL = 'https://hmgwebservices.com/PayFortWebLive/pages/SendPayFortRequest.aspx'; //Payfort Payment Gateway URL LIVE
// static String SERVICE_URL = 'https://hmgwebservices.com/PayFortWeb/pages/SendPayFortRequest.aspx'; // Payfort Payment Gateway URL UAT // static String SERVICE_URL = 'https://hmgwebservices.com/PayFortWeb/pages/SendPayFortRequest.aspx'; // Payfort Payment Gateway URL UAT
static String TAMARA_URL = "https://mdlaboratories.com/tamaralive/Home/Checkout";
static String GET_TAMARA_INSTALLMENTS_URL = "https://mdlaboratories.com/tamaralive/Home/GetInstallments";
static String GET_TAMARA_PAYMENT_STATUS = 'https://mdlaboratories.com/tamaralive/api/OnlineTamara/order_status?orderid=';
// static String GET_TAMARA_INSTALLMENTS_URL = "https://epharmacy.hmg.com/tamara/Home/getinstallments";
// var payFortEnvironment = FortEnvironment.test; // var payFortEnvironment = FortEnvironment.test;
// var applePayMerchantId = "merchant.com.hmgwebservices.uat"; // var applePayMerchantId = "merchant.com.hmgwebservices.uat";
@ -753,37 +759,55 @@ class ApiConsts {
payFortEnvironment = FortEnvironment.production; payFortEnvironment = FortEnvironment.production;
applePayMerchantId = "merchant.com.hmgwebservices"; applePayMerchantId = "merchant.com.hmgwebservices";
SERVICE_URL = "https://hmgwebservices.com/PayFortWebLive/pages/SendPayFortRequest.aspx"; 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; break;
case AppEnvironmentTypeEnum.dev: case AppEnvironmentTypeEnum.dev:
baseUrl = "https://uat.hmgwebservices.com/"; baseUrl = "https://uat.hmgwebservices.com/";
payFortEnvironment = FortEnvironment.test; payFortEnvironment = FortEnvironment.test;
applePayMerchantId = "merchant.com.hmgwebservices.uat"; applePayMerchantId = "merchant.com.hmgwebservices.uat";
SERVICE_URL = 'https://hmgwebservices.com/PayFortWeb/pages/SendPayFortRequest.aspx'; 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; break;
case AppEnvironmentTypeEnum.uat: case AppEnvironmentTypeEnum.uat:
baseUrl = "https://uat.hmgwebservices.com/"; baseUrl = "https://uat.hmgwebservices.com/";
payFortEnvironment = FortEnvironment.test; payFortEnvironment = FortEnvironment.test;
applePayMerchantId = "merchant.com.hmgwebservices.uat"; applePayMerchantId = "merchant.com.hmgwebservices.uat";
SERVICE_URL = 'https://hmgwebservices.com/PayFortWeb/pages/SendPayFortRequest.aspx'; 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; break;
case AppEnvironmentTypeEnum.preProd: case AppEnvironmentTypeEnum.preProd:
baseUrl = "https://webservices.hmg.com/"; baseUrl = "https://webservices.hmg.com/";
payFortEnvironment = FortEnvironment.production; payFortEnvironment = FortEnvironment.production;
applePayMerchantId = "merchant.com.hmgwebservices"; applePayMerchantId = "merchant.com.hmgwebservices";
SERVICE_URL = "https://hmgwebservices.com/PayFortWebLive/pages/SendPayFortRequest.aspx"; 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; break;
case AppEnvironmentTypeEnum.qa: case AppEnvironmentTypeEnum.qa:
baseUrl = "https://uat.hmgwebservices.com/"; baseUrl = "https://uat.hmgwebservices.com/";
payFortEnvironment = FortEnvironment.test; payFortEnvironment = FortEnvironment.test;
applePayMerchantId = "merchant.com.hmgwebservices.uat"; applePayMerchantId = "merchant.com.hmgwebservices.uat";
SERVICE_URL = 'https://hmgwebservices.com/PayFortWeb/pages/SendPayFortRequest.aspx'; 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; break;
case AppEnvironmentTypeEnum.staging: case AppEnvironmentTypeEnum.staging:
baseUrl = "https://uat.hmgwebservices.com/"; baseUrl = "https://uat.hmgwebservices.com/";
payFortEnvironment = FortEnvironment.test; payFortEnvironment = FortEnvironment.test;
applePayMerchantId = "merchant.com.hmgwebservices.uat"; applePayMerchantId = "merchant.com.hmgwebservices.uat";
SERVICE_URL = 'https://hmgwebservices.com/PayFortWeb/pages/SendPayFortRequest.aspx'; 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; break;
} }
} }
@ -810,6 +834,10 @@ class ApiConsts {
static final String addFamilyFile = 'Services/Patients.svc/REST/ShareFamilyFileService'; static final String addFamilyFile = 'Services/Patients.svc/REST/ShareFamilyFileService';
static final String sendFamilyFileActivation = 'Services/Authentication.svc/REST/SendActivationCodeForFamilyFile'; static final String sendFamilyFileActivation = 'Services/Authentication.svc/REST/SendActivationCodeForFamilyFile';
static final String checkActivationCodeForFamily = 'Services/Authentication.svc/REST/CheckActivationCodeForFamilyFile'; 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 values for Api

@ -7,6 +7,7 @@ class AppAssets {
static const String arrow_forward = '$svgBasePath/arrow_forward.svg'; static const String arrow_forward = '$svgBasePath/arrow_forward.svg';
static const String externalLink = '$svgBasePath/external_link.svg'; static const String externalLink = '$svgBasePath/external_link.svg';
static const String calendar = '$svgBasePath/calendar.svg'; static const String calendar = '$svgBasePath/calendar.svg';
static const String rangeCalendar = '$svgBasePath/range_calender.svg';
static const String hmc = '$svgBasePath/hmc.svg'; static const String hmc = '$svgBasePath/hmc.svg';
static const String ksa = '$svgBasePath/ksa.svg'; static const String ksa = '$svgBasePath/ksa.svg';
static const String sms = '$svgBasePath/sms.svg'; static const String sms = '$svgBasePath/sms.svg';
@ -133,6 +134,27 @@ class AppAssets {
static const String minus = '$svgBasePath/minus.svg'; static const String minus = '$svgBasePath/minus.svg';
static const String home_lab_result_icon = '$svgBasePath/home_lab_result_icon.svg'; static const String home_lab_result_icon = '$svgBasePath/home_lab_result_icon.svg';
static const String visa_mastercard_icon = '$svgBasePath/visa_mastercard.svg'; static const String visa_mastercard_icon = '$svgBasePath/visa_mastercard.svg';
static const String small_livecare_icon = '$svgBasePath/small_livecare_icon.svg';
static const String walkin_appointment_icon = '$svgBasePath/walkin_appointment_icon.svg';
static const String cardiology_clinic_icon = '$svgBasePath/cardiology_clinic_icon.svg';
static const String generic_clinic_icon = '$svgBasePath/generic_clinic_icon.svg';
static const String lab_result_indicator = '$svgBasePath/refernce_range_indicator.svg';
static const String ic_date_filter = '$svgBasePath/ic_date_filter.svg';
static const String ic_list = '$svgBasePath/ic_list.svg';
static const String ic_graph = '$svgBasePath/graph.svg';
static const String ic_normal_result = '$svgBasePath/normal_result.svg';
static const String ic_low_result = '$svgBasePath/low_result.svg';
static const String ic_critical_low_result = '$svgBasePath/critical_low_result.svg';
static const String livecare_online_icon = '$svgBasePath/livecare_online_icon.svg';
static const String edit_icon = '$svgBasePath/edit_icon.svg';
static const String waiting_icon = '$svgBasePath/waiting_icon.svg';
static const String forward_arrow_icon_small = '$svgBasePath/forward_arrow_icon_small.svg';
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// //bottom navigation//
static const String homeBottom = '$svgBasePath/home_bottom.svg'; static const String homeBottom = '$svgBasePath/home_bottom.svg';
@ -144,6 +166,8 @@ class AppAssets {
static const String feedback = '$svgBasePath/feedback.svg'; static const String feedback = '$svgBasePath/feedback.svg';
static const String news = '$svgBasePath/news.svg'; static const String news = '$svgBasePath/news.svg';
static const String heart = '$svgBasePath/heart.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 // // PNGS //
static const String hmg_logo = '$pngBasePath/hmg_logo.png'; static const String hmg_logo = '$pngBasePath/hmg_logo.png';
@ -158,6 +182,7 @@ class AppAssets {
static const String tamara_en = '$pngBasePath/tamara_en.png'; static const String tamara_en = '$pngBasePath/tamara_en.png';
static const String visa = '$pngBasePath/visa.png'; static const String visa = '$pngBasePath/visa.png';
static const String lockIcon = '$pngBasePath/lock-icon.png'; static const String lockIcon = '$pngBasePath/lock-icon.png';
static const String dummy_user = '$pngBasePath/dummy_user.png';
} }
class AppAnimations { class AppAnimations {
@ -171,4 +196,7 @@ class AppAnimations {
static const String errorAnimation = '$lottieBasePath/ErrorAnimation.json'; static const String errorAnimation = '$lottieBasePath/ErrorAnimation.json';
static const String warningAnimation = '$lottieBasePath/warningAnimation.json'; static const String warningAnimation = '$lottieBasePath/warningAnimation.json';
static const String splashLaunching = '$lottieBasePath/splash_launching.json'; static const String splashLaunching = '$lottieBasePath/splash_launching.json';
static const String noData = '$lottieBasePath/Nodata.json';
static const String ripple = '$lottieBasePath/Ripple.json';
static const String pending_loading_animation = '$lottieBasePath/pending_loading_animation.json';
} }

@ -37,14 +37,16 @@ class AppState {
AuthenticatedUser? _authenticatedRootUser; AuthenticatedUser? _authenticatedRootUser;
AuthenticatedUser? _authenticatedChildUser; AuthenticatedUser? _authenticatedChildUser;
void setAuthenticatedUser(AuthenticatedUser authenticatedUser, {bool isFamily = false}) { int? _superUserID;
bool isChildLoggedIn = false;
void setAuthenticatedUser(AuthenticatedUser? authenticatedUser, {bool isFamily = false}) {
if (isFamily) { if (isFamily) {
_authenticatedChildUser = authenticatedUser; _authenticatedChildUser = authenticatedUser;
} else { } else {
setIsAuthenticated = true; setIsAuthenticated = true;
_authenticatedRootUser = authenticatedUser; _authenticatedRootUser = authenticatedUser;
} }
} }
AuthenticatedUser? getAuthenticatedUser({bool isFamily = false}) { 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 _userBloodGroup = "";
String get getUserBloodGroup => _userBloodGroup; String get getUserBloodGroup => _userBloodGroup;
@ -97,6 +107,14 @@ class AppState {
set setDeviceTypeID(v) => deviceTypeID = v; set setDeviceTypeID(v) => deviceTypeID = v;
String _familyFileTokenID = "";
String get getFamilyFileTokenID => _familyFileTokenID;
set setFamilyFileTokenID(String value) {
_familyFileTokenID = value;
}
List<VidaPlusProjectListModel> vidaPlusProjectList = []; List<VidaPlusProjectListModel> vidaPlusProjectList = [];
List<ListPrivilege> privilegeModelList = []; List<ListPrivilege> privilegeModelList = [];
List<HMCProjectListModel> hMCProjectListModel = []; List<HMCProjectListModel> hMCProjectListModel = [];
@ -135,9 +153,8 @@ class AppState {
} }
///this will be called if there is any problem in getting the user location ///this will be called if there is any problem in getting the user location
void resetLocation(){ void resetLocation() {
userLong = 0.0; userLong = 0.0;
userLong = 0.0; userLong = 0.0;
} }
} }

@ -11,6 +11,7 @@ class CacheConst {
static const String lastLoginDate = "lastLoginDate"; static const String lastLoginDate = "lastLoginDate";
static const String lastLoginTime = "lastLoginTime"; static const String lastLoginTime = "lastLoginTime";
static const String memberModel = "memberModel"; static const String memberModel = "memberModel";
static const String firstLaunch = "firstLaunch";
static const String isShowOnboarding = "is_show_onboarding"; static const String isShowOnboarding = "is_show_onboarding";
static const String appAuthToken = "app_auth_token"; static const String appAuthToken = "app_auth_token";

@ -6,9 +6,22 @@ class DataPoint {
final double value; final double value;
///label shown on the bottom of the graph ///label shown on the bottom of the graph
String label; String label;
String referenceValue;
String actualValue;
DateTime time;
String displayTime;
DataPoint( DataPoint(
{required this.value, {required this.value,
required this.label, required this.label,
required this.referenceValue,
required this.actualValue,
required this.time,
required this.displayTime,
}); });
@override
String toString() {
return "the time is $time";
}
} }

@ -10,6 +10,8 @@ import 'package:hmg_patient_app_new/features/book_appointments/book_appointments
import 'package:hmg_patient_app_new/features/common/common_repo.dart'; import 'package:hmg_patient_app_new/features/common/common_repo.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_repo.dart';
import 'package:hmg_patient_app_new/features/habib_wallet/habib_wallet_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_repo.dart';
import 'package:hmg_patient_app_new/features/immediate_livecare/immediate_livecare_view_model.dart';
import 'package:hmg_patient_app_new/features/insurance/insurance_repo.dart'; import 'package:hmg_patient_app_new/features/insurance/insurance_repo.dart';
import 'package:hmg_patient_app_new/features/insurance/insurance_view_model.dart'; import 'package:hmg_patient_app_new/features/insurance/insurance_view_model.dart';
import 'package:hmg_patient_app_new/features/lab/lab_repo.dart'; import 'package:hmg_patient_app_new/features/lab/lab_repo.dart';
@ -22,6 +24,7 @@ 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/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_repo.dart';
import 'package:hmg_patient_app_new/features/prescriptions/prescriptions_view_model.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_repo.dart';
import 'package:hmg_patient_app_new/features/radiology/radiology_view_model.dart'; import 'package:hmg_patient_app_new/features/radiology/radiology_view_model.dart';
import 'package:hmg_patient_app_new/services/analytics/analytics_service.dart'; import 'package:hmg_patient_app_new/services/analytics/analytics_service.dart';
@ -77,7 +80,7 @@ class AppDependencies {
final sharedPreferences = await SharedPreferences.getInstance(); final sharedPreferences = await SharedPreferences.getInstance();
getIt.registerLazySingleton<CacheService>(() => CacheServiceImp(sharedPreferences: sharedPreferences, loggerService: getIt())); getIt.registerLazySingleton<CacheService>(() => CacheServiceImp(sharedPreferences: sharedPreferences, loggerService: getIt()));
getIt.registerLazySingleton<ApiClient>(() => ApiClientImp(loggerService: getIt(), appState: getIt())); getIt.registerLazySingleton<ApiClient>(() => ApiClientImp(appState: getIt()));
// Repositories // Repositories
getIt.registerLazySingleton<CommonRepo>(() => CommonRepoImp(loggerService: getIt())); getIt.registerLazySingleton<CommonRepo>(() => CommonRepoImp(loggerService: getIt()));
@ -92,15 +95,13 @@ class AppDependencies {
getIt.registerLazySingleton<LocalAuthService>(() => LocalAuthService(loggerService: getIt<LoggerService>(), localAuth: getIt<LocalAuthentication>())); getIt.registerLazySingleton<LocalAuthService>(() => LocalAuthService(loggerService: getIt<LoggerService>(), localAuth: getIt<LocalAuthentication>()));
getIt.registerLazySingleton<HabibWalletRepo>(() => HabibWalletRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt())); getIt.registerLazySingleton<HabibWalletRepo>(() => HabibWalletRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt()));
getIt.registerLazySingleton<MedicalFileRepo>(() => MedicalFileRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt())); getIt.registerLazySingleton<MedicalFileRepo>(() => MedicalFileRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt()));
getIt.registerLazySingleton<ImmediateLiveCareRepo>(() => ImmediateLiveCareRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt()));
// ViewModels // ViewModels
// Global/shared VMs LazySingleton // Global/shared VMs LazySingleton
getIt.registerLazySingleton<LabViewModel>( getIt.registerLazySingleton<LabViewModel>(
() => LabViewModel( () => LabViewModel(labRepo: getIt(), errorHandlerService: getIt(), navigationService: getIt()),
labRepo: getIt(),
errorHandlerService: getIt(),
),
); );
getIt.registerLazySingleton<RadiologyViewModel>( getIt.registerLazySingleton<RadiologyViewModel>(
@ -125,11 +126,7 @@ class AppDependencies {
); );
getIt.registerLazySingleton<MyAppointmentsViewModel>( getIt.registerLazySingleton<MyAppointmentsViewModel>(
() => MyAppointmentsViewModel( () => MyAppointmentsViewModel(myAppointmentsRepo: getIt(), errorHandlerService: getIt(), appState: getIt()),
myAppointmentsRepo: getIt(),
errorHandlerService: getIt(),
appState: getIt()
),
); );
getIt.registerLazySingleton<PayfortViewModel>( getIt.registerLazySingleton<PayfortViewModel>(
@ -154,12 +151,15 @@ class AppDependencies {
); );
getIt.registerLazySingleton<BookAppointmentsViewModel>( getIt.registerLazySingleton<BookAppointmentsViewModel>(
() => BookAppointmentsViewModel( () => BookAppointmentsViewModel(bookAppointmentsRepo: getIt(), errorHandlerService: getIt(), navigationService: getIt(), myAppointmentsViewModel: getIt(), locationUtils: getIt()),
bookAppointmentsRepo: getIt(), );
getIt.registerLazySingleton<ImmediateLiveCareViewModel>(
() => ImmediateLiveCareViewModel(
immediateLiveCareRepo: getIt(),
errorHandlerService: getIt(), errorHandlerService: getIt(),
navigationService: getIt(), navigationService: getIt(),
myAppointmentsViewModel: getIt(), myAppointmentsViewModel: getIt(),
locationUtils: getIt()
), ),
); );
@ -167,6 +167,7 @@ class AppDependencies {
() => AuthenticationViewModel( () => AuthenticationViewModel(
authenticationRepo: getIt(), cacheService: getIt(), navigationService: getIt(), dialogService: getIt(), appState: getIt(), errorHandlerService: getIt(), localAuthService: getIt()), authenticationRepo: getIt(), cacheService: getIt(), navigationService: getIt(), dialogService: getIt(), appState: getIt(), errorHandlerService: getIt(), localAuthService: getIt()),
); );
getIt.registerLazySingleton<ProfileSettingsViewModel>(() => ProfileSettingsViewModel());
// Screen-specific VMs Factory // Screen-specific VMs Factory
// getIt.registerFactory<BookAppointmentsViewModel>( // getIt.registerFactory<BookAppointmentsViewModel>(

@ -30,6 +30,8 @@ enum LoginTypeEnum { sms, whatsapp, face, fingerprint }
enum AppEnvironmentTypeEnum { dev, uat, preProd, qa, staging, prod } enum AppEnvironmentTypeEnum { dev, uat, preProd, qa, staging, prod }
enum FamilyFileEnum { active, inactive, blocked, deleted, pending, rejected }
extension CalenderExtension on CalenderEnum { extension CalenderExtension on CalenderEnum {
int get toInt { int get toInt {
switch (this) { 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<AppState>();
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 { enum ServiceTypeEnum {
advancePayment, //3 advancePayment, //3
ancillaryOrder, //3 ancillaryOrder, //3

@ -7,7 +7,9 @@ abstract class Failure extends Equatable implements Exception {
} }
class ServerFailure extends Failure { class ServerFailure extends Failure {
const ServerFailure(super.message); final String url;
const ServerFailure(super.message, {this.url = ""});
@override @override
List<Object?> get props => [message]; List<Object?> get props => [message];
@ -27,6 +29,13 @@ class MessageStatusFailure extends Failure {
List<Object?> get props => [message]; List<Object?> get props => [message];
} }
class AppUpdateFailure extends Failure {
const AppUpdateFailure(super.message);
@override
List<Object?> get props => [message];
}
class StatusCodeFailure extends Failure { class StatusCodeFailure extends Failure {
const StatusCodeFailure(super.message); const StatusCodeFailure(super.message);
@ -34,6 +43,15 @@ class StatusCodeFailure extends Failure {
List<Object?> get props => [message]; List<Object?> get props => [message];
} }
class UnAuthenticatedUserFailure extends Failure {
final String url;
const UnAuthenticatedUserFailure(super.message, {this.url = ""});
@override
List<Object?> get props => [message];
}
class ConnectivityFailure extends Failure { class ConnectivityFailure extends Failure {
const ConnectivityFailure(super.message); const ConnectivityFailure(super.message);
@ -56,7 +74,9 @@ class DataParsingFailure extends Failure {
} }
class UnknownFailure extends Failure { class UnknownFailure extends Failure {
const UnknownFailure(super.message); final String url;
const UnknownFailure(super.message, {this.url = ""});
@override @override
List<Object?> get props => [message]; List<Object?> get props => [message];

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

@ -2,11 +2,12 @@ import 'dart:math';
import 'package:hmg_patient_app_new/core/cache_consts.dart' show CacheConst; import 'package:hmg_patient_app_new/core/cache_consts.dart' show CacheConst;
import 'package:hmg_patient_app_new/core/utils/utils.dart' show Utils; import 'package:hmg_patient_app_new/core/utils/utils.dart' show Utils;
import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/doctors_list_response_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/doctor_list_api_response.dart' show RegionList, PatientDoctorAppointmentList, DoctorList, PatientDoctorAppointmentListByRegion; import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/doctor_list_api_response.dart' show RegionList, PatientDoctorAppointmentList, DoctorList, PatientDoctorAppointmentListByRegion;
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/hospital_model.dart' show HospitalsModel; import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/hospital_model.dart' show HospitalsModel;
class DoctorMapper{ class DoctorMapper{
static Future<RegionList> getMappedDoctor(List<DoctorList> doctorList, static Future<RegionList> getMappedDoctor(List<DoctorsListResponseModel> doctorList,
{bool isArabic = false,double lat = 0.0,double long = 0.0}) async { {bool isArabic = false,double lat = 0.0,double long = 0.0}) async {
RegionList regionList = RegionList(); RegionList regionList = RegionList();
@ -41,16 +42,16 @@ class DoctorMapper{
isHMC: element.isHMC isHMC: element.isHMC
); );
if(element.projectDistanceInKiloMeters!= null ){ if(element.projectDistanceInKiloMeters!= null ){
if(regionDoctorList!.distance>element.projectDistanceInKiloMeters){ if(regionDoctorList!.distance>element.projectDistanceInKiloMeters!){
regionDoctorList.distance = element.projectDistanceInKiloMeters; regionDoctorList.distance = element.projectDistanceInKiloMeters!;
} }
if (element.isHMC == true && if (element.isHMC == true &&
element.projectDistanceInKiloMeters < element.projectDistanceInKiloMeters! <
regionDoctorList.hmcDistance) { regionDoctorList.hmcDistance) {
regionDoctorList.hmcDistance = element.projectDistanceInKiloMeters; regionDoctorList.hmcDistance = element.projectDistanceInKiloMeters!;
} else if (element.projectDistanceInKiloMeters < } else if (element.projectDistanceInKiloMeters! <
regionDoctorList.hmgDistance) { regionDoctorList.hmgDistance) {
regionDoctorList.hmgDistance = element.projectDistanceInKiloMeters; regionDoctorList.hmgDistance = element.projectDistanceInKiloMeters!;
} }
}else }else
if (lat != 0&& if (lat != 0&&
@ -65,12 +66,12 @@ class DoctorMapper{
regionDoctorList.distance = distance; regionDoctorList.distance = distance;
} }
if (element.isHMC == true && if (element.isHMC == true &&
element.projectDistanceInKiloMeters < (element.projectDistanceInKiloMeters??0) <
regionDoctorList.hmcDistance) { regionDoctorList.hmcDistance) {
regionDoctorList.hmcDistance = element.projectDistanceInKiloMeters; regionDoctorList.hmcDistance = element.projectDistanceInKiloMeters??0;
} else if (element.projectDistanceInKiloMeters < } else if ((element.projectDistanceInKiloMeters??0) <
regionDoctorList.hmgDistance) { regionDoctorList.hmgDistance) {
regionDoctorList.hmgDistance = element.projectDistanceInKiloMeters; regionDoctorList.hmgDistance = element.projectDistanceInKiloMeters??0;
} }
} }
targetList?.add(newAppointment); targetList?.add(newAppointment);

@ -8,11 +8,14 @@ import 'package:firebase_messaging/firebase_messaging.dart' as fir;
import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_ios_voip_kit_karmm/call_state_type.dart';
import 'package:flutter_ios_voip_kit_karmm/flutter_ios_voip_kit.dart';
// import 'package:flutter_callkit_incoming/flutter_callkit_incoming.dart'; // import 'package:flutter_callkit_incoming/flutter_callkit_incoming.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:hmg_patient_app_new/core/utils/local_notifications.dart'; import 'package:hmg_patient_app_new/core/utils/local_notifications.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart'; import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/services/cache_service.dart';
import 'package:permission_handler/permission_handler.dart'; import 'package:permission_handler/permission_handler.dart';
import '../cache_consts.dart'; import '../cache_consts.dart';
@ -139,7 +142,7 @@ class PushNotificationHandler {
// late HmsApiAvailability hmsApiAvailability; // late HmsApiAvailability hmsApiAvailability;
// final voIPKit = FlutterIOSVoIPKit.instance; final voIPKit = FlutterIOSVoIPKit.instance;
late Timer timeOutTimer; late Timer timeOutTimer;
bool isTalking = false; bool isTalking = false;
@ -188,55 +191,56 @@ class PushNotificationHandler {
this.context = context; this.context = context;
if (Platform.isIOS) { if (Platform.isIOS) {
// voIPKit.getVoIPToken().then((value) { voIPKit.getVoIPToken().then((value) {
// print("APNS VOIP KIT TOKEN: $value"); print("🎈 APNS VOIP KIT TOKEN: $value");
// AppSharedPreferences().setString(APNS_TOKEN, value!); Utils.saveStringFromPrefs(CacheConst.voipToken, value ?? "");
// }); // AppSharedPreferences().setString(APNS_TOKEN, value!);
// });
// voIPKit.onDidUpdatePushToken = (String token) {
// print('🎈 example: onDidUpdatePushToken: $token'); voIPKit.onDidUpdatePushToken = (String token) {
// }; print('🎈 example: onDidUpdatePushToken: $token');
// };
// voIPKit.onDidReceiveIncomingPush = (
// Map<String, dynamic> payload, voIPKit.onDidReceiveIncomingPush = (
// ) async { Map<String, dynamic> payload,
// print('🎈 example: onDidReceiveIncomingPush $payload'); ) async {
// _timeOut(); print('🎈 example: onDidReceiveIncomingPush $payload');
// }; // _timeOut();
// };
// voIPKit.onDidRejectIncomingCall = (
// String uuid, voIPKit.onDidRejectIncomingCall = (
// String callerId, String uuid,
// ) async { String callerId,
// try { ) async {
// print('🎈 example: onDidRejectIncomingCall $uuid - $callerId'); try {
// timeOutTimer.cancel(); print('🎈 example: onDidRejectIncomingCall $uuid - $callerId');
// } catch (err) {} timeOutTimer.cancel();
// }; } catch (err) {}
// };
// voIPKit.onDidAcceptIncomingCall = (
// String uuid, voIPKit.onDidAcceptIncomingCall = (
// String callerId, String uuid,
// ) async { String callerId,
// print('🎈 example: onDidAcceptIncomingCall $uuid - $callerId'); ) async {
// await voIPKit.acceptIncomingCall(callerState: CallStateType.calling); print('🎈 example: onDidAcceptIncomingCall $uuid - $callerId');
// await voIPKit.callConnected(); await voIPKit.acceptIncomingCall(callerState: CallStateType.calling);
// await Future.delayed(Duration(seconds: 1)); await voIPKit.callConnected();
// await Future.delayed(Duration(seconds: 1));
// Navigator.pushNamed(
// locator<NavigationService>().navigatorKey.currentContext!, // Navigator.pushNamed(
// "zoom_call_page", // locator<NavigationService>().navigatorKey.currentContext!,
// arguments: CallArguments("hoover-dam", "123", "Patient", "40", "1", false), // "zoom_call_page",
// ); // arguments: CallArguments("hoover-dam", "123", "Patient", "40", "1", false),
// // );
// await voIPKit.endCall();
// await voIPKit.endCall();
// // Navigator.pushNamed(navigatorKey.currentContext!, VIDEO_CALL_SCREEN,
// // arguments: VideoArgus( // Navigator.pushNamed(navigatorKey.currentContext!, VIDEO_CALL_SCREEN,
// // reservationId: int.parse(callerId), token: null, isVideo: true)); // arguments: VideoArgus(
// // reservationId: int.parse(callerId), token: null, isVideo: true));
// timeOutTimer.cancel();
// }; // timeOutTimer.cancel();
};
} }
if (Platform.isAndroid) { if (Platform.isAndroid) {

@ -262,7 +262,7 @@ class RequestUtils {
static Future<FamilyFileRequest> getAddFamilyRequest({required String nationalIDorFile, required String mobileNo, required String countryCode}) async { static Future<FamilyFileRequest> getAddFamilyRequest({required String nationalIDorFile, required String mobileNo, required String countryCode}) async {
FamilyFileRequest request = FamilyFileRequest(); FamilyFileRequest request = FamilyFileRequest();
int? loginType = 0; // Default to National ID int? loginType = 0;
if (countryCode == CountryEnum.saudiArabia.countryCode || countryCode == '+966') { if (countryCode == CountryEnum.saudiArabia.countryCode || countryCode == '+966') {
loginType = (nationalIDorFile.length == 10) ? 1 : 2; loginType = (nationalIDorFile.length == 10) ? 1 : 2;

@ -1,8 +1,8 @@
import 'package:flutter/material.dart'; // These are the Viewport values of your Figma Design. 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. // These are used in the code as a reference to create your UI Responsively.
const num FIGMA_DESIGN_WIDTH = 375; final num FIGMA_DESIGN_WIDTH = SizeUtils.width;
const num FIGMA_DESIGN_HEIGHT = 667; final num FIGMA_DESIGN_HEIGHT = SizeUtils.height;
const num FIGMA_DESIGN_STATUS_BAR = 0; const num FIGMA_DESIGN_STATUS_BAR = 0;
extension ResponsiveExtension on num { extension ResponsiveExtension on num {
@ -70,10 +70,10 @@ class SizeUtils {
static late DeviceType deviceType; static late DeviceType deviceType;
/// Device's Height /// Device's Height
static late double height; static double height = 667;
/// Device's Width /// Device's Width
static late double width; static double width = 375;
static void setScreenSize( static void setScreenSize(
BoxConstraints constraints, BoxConstraints constraints,

@ -162,6 +162,16 @@ class Utils {
} }
} }
static Future<bool> getBoolFromPrefs(String key) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
return prefs.getBool(key) ?? true;
}
static Future<bool> saveBoolFromPrefs(String key, bool value) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
return await prefs.setBool(key, value);
}
static Future<String> getStringFromPrefs(String key) async { static Future<String> getStringFromPrefs(String key) async {
SharedPreferences prefs = await SharedPreferences.getInstance(); SharedPreferences prefs = await SharedPreferences.getInstance();
return prefs.getString(key) ?? ""; return prefs.getString(key) ?? "";
@ -291,13 +301,17 @@ class Utils {
return false; return false;
} }
static Widget getNoDataWidget(BuildContext context, {String? errorText}) { static Widget getNoDataWidget(BuildContext context, {double width = 124, double height = 124, String? noDataText, Widget callToActionButton = const SizedBox.shrink(), bool isSmallWidget = false}) {
return Column( return Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
SvgPicture.asset('assets/images/svg/not_found.svg', width: 150.0, height: 150.0), SizedBox(height: isSmallWidget ? 0.h : 48.h),
(errorText ?? LocaleKeys.noDataAvailable.tr()).toText16(isCenter: true).paddingOnly(top: 15), Lottie.asset(AppAnimations.noData, repeat: false, reverse: false, frameRate: FrameRate(60), width: width.h, height: height.h, fit: BoxFit.fill),
SizedBox(height: 16.h),
(noDataText ?? LocaleKeys.noDataAvailable.tr()).toText16(weight: FontWeight.w500, color: AppColors.greyTextColor, isCenter: true).paddingSymmetrical(64.h, 0.h),
SizedBox(height: 16.h),
callToActionButton
], ],
).center; ).center;
} }
@ -309,7 +323,7 @@ class Utils {
children: [ children: [
Lottie.asset(AppAnimations.loadingAnimation, repeat: true, reverse: false, frameRate: FrameRate(60), width: 100.h, height: 100.h, fit: BoxFit.fill), Lottie.asset(AppAnimations.loadingAnimation, repeat: true, reverse: false, frameRate: FrameRate(60), width: 100.h, height: 100.h, fit: BoxFit.fill),
SizedBox(height: 8.h), SizedBox(height: 8.h),
(loadingText ?? LocaleKeys.loadingText.tr()).toText16(color: AppColors.blackColor), (loadingText ?? LocaleKeys.loadingText.tr()).toText16(color: AppColors.blackColor, isCenter: true),
SizedBox(height: 8.h), SizedBox(height: 8.h),
], ],
).center; ).center;
@ -341,15 +355,17 @@ class Utils {
).center; ).center;
} }
static Widget getWarningWidget({String? loadingText, bool isShowActionButtons = false, Function? onConfirmTap, Function? onCancelTap}) { static Widget getWarningWidget({String? loadingText, bool isShowActionButtons = false, Widget? bodyWidget, Function? onConfirmTap, Function? onCancelTap}) {
return Column( return Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
Lottie.asset(AppAnimations.warningAnimation, repeat: true, reverse: false, frameRate: FrameRate(60), width: 128.h, height: 128.h, fit: BoxFit.fill), Lottie.asset(AppAnimations.warningAnimation, repeat: false, reverse: false, frameRate: FrameRate(60), width: 128.h, height: 128.h, fit: BoxFit.fill),
SizedBox(height: 8.h), SizedBox(height: 8.h),
(loadingText ?? LocaleKeys.loadingText.tr()).toText14(color: AppColors.blackColor, letterSpacing: 0), (loadingText ?? LocaleKeys.loadingText.tr()).toText14(color: AppColors.blackColor, letterSpacing: 0),
SizedBox(height: 16.h), SizedBox(height: 16.h),
bodyWidget ?? SizedBox.shrink(),
SizedBox(height: 16.h),
isShowActionButtons isShowActionButtons
? Row( ? Row(
children: [ children: [
@ -613,8 +629,17 @@ class Utils {
double width = 24, double width = 24,
double height = 24, double height = 24,
BoxFit fit = BoxFit.cover, 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 /// Widget to build an SVG from network
@ -644,7 +669,6 @@ class Utils {
static Widget getPaymentAmountWithSymbol(Widget paymentAmountWidget, Color iconColor, double iconSize, {bool isSaudiCurrency = true, bool isExpanded = true}) { static Widget getPaymentAmountWithSymbol(Widget paymentAmountWidget, Color iconColor, double iconSize, {bool isSaudiCurrency = true, bool isExpanded = true}) {
return Row( return Row(
mainAxisAlignment: isExpanded ? MainAxisAlignment.spaceBetween : MainAxisAlignment.start, mainAxisAlignment: isExpanded ? MainAxisAlignment.spaceBetween : MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.end,
children: [ children: [
appState.isArabic() appState.isArabic()
? Container() ? Container()
@ -671,7 +695,8 @@ class Utils {
); );
} }
static Widget getPaymentAmountWithSymbol2(num habibWalletAmount, {double iconSize = 14, Color iconColor = AppColors.textColor, Color textColor = AppColors.blackColor, bool isSaudiCurrency = true, bool isExpanded = true}) { static Widget getPaymentAmountWithSymbol2(num habibWalletAmount,
{double iconSize = 14, Color iconColor = AppColors.textColor, Color textColor = AppColors.blackColor, bool isSaudiCurrency = true, bool isExpanded = true}) {
return RichText( return RichText(
maxLines: 1, maxLines: 1,
text: TextSpan( text: TextSpan(
@ -741,4 +766,5 @@ class Utils {
} }
return isHavePrivilege; return isHavePrivilege;
} }
} }

@ -22,7 +22,15 @@ abstract class AuthenticationRepo {
Future<Either<Failure, GenericApiModel<dynamic>>> sendActivationCodeRepo({required dynamic sendActivationCodeReq, String? languageID, bool isRegister = false, bool isFormFamilyFile = false}); Future<Either<Failure, GenericApiModel<dynamic>>> sendActivationCodeRepo({required dynamic sendActivationCodeReq, String? languageID, bool isRegister = false, bool isFormFamilyFile = false});
Future<Either<Failure, GenericApiModel<dynamic>>> checkActivationCodeRepo( Future<Either<Failure, GenericApiModel<dynamic>>> checkActivationCodeRepo(
{required dynamic newRequest, required String? activationCode, required bool isRegister, bool isFormFamilyFile = false, int? patientShareRequestID, int? responseID, String? familyFileTokenID}); {required dynamic newRequest,
required String? activationCode,
required bool isRegister,
bool isFormFamilyFile = false,
int? patientShareRequestID,
int? responseID,
bool isSwitchUser = false,
int? patientID,
int? loginType});
Future<Either<Failure, GenericApiModel<dynamic>>> checkIfUserAgreed({required dynamic commonAuthanticatedRequest}); Future<Either<Failure, GenericApiModel<dynamic>>> checkIfUserAgreed({required dynamic commonAuthanticatedRequest});
@ -139,6 +147,16 @@ class AuthenticationRepoImp implements AuthenticationRepo {
int isOutKsa = (sendActivationCodeReq.zipCode == '966' || sendActivationCodeReq.zipCode == '+966') ? 0 : 1; int isOutKsa = (sendActivationCodeReq.zipCode == '966' || sendActivationCodeReq.zipCode == '+966') ? 0 : 1;
sendActivationCodeReq.patientOutSA = isOutKsa; sendActivationCodeReq.patientOutSA = isOutKsa;
sendActivationCodeReq.isDentalAllowedBackend = false; 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 { try {
GenericApiModel<dynamic>? apiResponse; GenericApiModel<dynamic>? apiResponse;
@ -150,7 +168,7 @@ class AuthenticationRepoImp implements AuthenticationRepo {
: isRegister : isRegister
? ApiConsts.sendActivationCodeRegister ? ApiConsts.sendActivationCodeRegister
: ApiConsts.sendActivationCode, : ApiConsts.sendActivationCode,
body: sendActivationCodeReq.toJson(), body: isFormFamilyFile ? payload : sendActivationCodeReq.toJson(),
onFailure: (error, statusCode, {messageStatus, failureType}) { onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType; failure = failureType;
}, },
@ -184,27 +202,108 @@ class AuthenticationRepoImp implements AuthenticationRepo {
bool isFormFamilyFile = false, bool isFormFamilyFile = false,
int? patientShareRequestID, int? patientShareRequestID,
int? responseID, int? responseID,
String? familyFileTokenID}) async { bool isSwitchUser = false,
int? patientID,
int? loginType}) async {
AppState appState = getIt.get<AppState>();
// 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<String, dynamic> familyRequest = {};
// if (isFormFamilyFile) {
// AppState appState = getIt.get<AppState>();
// 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<String, dynamic> 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) { if (isRegister) {
newRequest["activationCode"] = activationCode ?? "0000"; newRequest["activationCode"] = activationCode ?? "0000";
newRequest["isSilentLogin"] = activationCode != null ? false : true; newRequest["isSilentLogin"] = activationCode == null;
} else { } else {
newRequest.activationCode = activationCode ?? "0000"; newRequest.activationCode = activationCode ?? "0000";
newRequest.isSilentLogin = activationCode != null ? false : true; newRequest.isSilentLogin = activationCode == null;
newRequest.projectOutSA = newRequest.zipCode == '966' ? false : true; newRequest.projectOutSA = newRequest.zipCode != '966';
newRequest.isDentalAllowedBackend = false; newRequest.isDentalAllowedBackend = false;
newRequest.forRegisteration = newRequest.isRegister ?? false; newRequest.forRegisteration = newRequest.isRegister ?? false;
newRequest.isRegister = false; newRequest.isRegister = false;
} }
Map<String, dynamic> familyRequest = {}; Map<String, dynamic> familyRequest = {};
if (isFormFamilyFile) { if (isFormFamilyFile) {
AppState appState = getIt.get<AppState>(); familyRequest = {
familyRequest = newRequest.toJson(); 'PatientShareRequestID': patientShareRequestID,
familyRequest['PatientShareRequestID'] = patientShareRequestID; 'ResponseID': responseID,
familyRequest['ResponseID'] = responseID; 'Status': 3,
familyRequest['Status'] = 3; // 'PatientID': appState.getAuthenticatedUser()?.patientId ?? 0,
familyRequest["PatientID"] = appState.getAuthenticatedUser()!.patientId ?? 0; 'LogInTokenID': appState.getFamilyFileTokenID,
familyRequest["LogInTokenID"] = familyFileTokenID; 'activationCode': activationCode ?? "0000",
'PatientMobileNumber': newRequest.patientMobileNumber,
'PatientIdentificationID': newRequest.patientIdentificationID,
};
}
Map<String, dynamic> 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 final endpoint = isFormFamilyFile
@ -223,7 +322,9 @@ class AuthenticationRepoImp implements AuthenticationRepo {
? familyRequest ? familyRequest
: isRegister : isRegister
? newRequest ? newRequest
: newRequest.toJson(), : isSwitchUser
? switchRequest
: newRequest.toJson(),
onFailure: (error, statusCode, {messageStatus, failureType}) { onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType; failure = failureType;
}, },
@ -252,7 +353,6 @@ class AuthenticationRepoImp implements AuthenticationRepo {
@override @override
Future<Either<Failure, GenericApiModel<dynamic>>> checkIfUserAgreed({required dynamic commonAuthanticatedRequest}) async { Future<Either<Failure, GenericApiModel<dynamic>>> checkIfUserAgreed({required dynamic commonAuthanticatedRequest}) async {
commonAuthanticatedRequest['Region'] = 1; commonAuthanticatedRequest['Region'] = 1;
print("dsfsdd");
try { try {
GenericApiModel<dynamic>? apiResponse; GenericApiModel<dynamic>? apiResponse;
Failure? failure; Failure? failure;
@ -287,7 +387,6 @@ class AuthenticationRepoImp implements AuthenticationRepo {
@override @override
Future<Either<Failure, GenericApiModel<dynamic>>> getUserAgreementContent({required dynamic commonAuthanticatedRequest}) async { Future<Either<Failure, GenericApiModel<dynamic>>> getUserAgreementContent({required dynamic commonAuthanticatedRequest}) async {
commonAuthanticatedRequest['Region'] = 1; commonAuthanticatedRequest['Region'] = 1;
print("dsfsdd");
try { try {
GenericApiModel<dynamic>? apiResponse; GenericApiModel<dynamic>? apiResponse;
Failure? failure; Failure? failure;

@ -1,16 +1,16 @@
import 'dart:convert'; import 'dart:convert';
import 'dart:developer'; import 'dart:developer';
import 'dart:io'; import 'dart:io';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/services.dart' show rootBundle;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart' show rootBundle;
import 'package:get_it/get_it.dart'; import 'package:get_it/get_it.dart';
import 'package:hijri_gregorian_calendar/hijri_gregorian_calendar.dart'; import 'package:hijri_gregorian_calendar/hijri_gregorian_calendar.dart';
import 'package:hmg_patient_app_new/core/app_state.dart'; import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/cache_consts.dart'; import 'package:hmg_patient_app_new/core/cache_consts.dart';
import 'package:hmg_patient_app_new/core/common_models/nationality_country_model.dart'; import 'package:hmg_patient_app_new/core/common_models/nationality_country_model.dart';
import 'package:hmg_patient_app_new/core/common_models/privilege/HMCProjectListModel.dart'; import 'package:hmg_patient_app_new/core/common_models/privilege/HMCProjectListModel.dart';
import 'package:hmg_patient_app_new/core/common_models/privilege/PrivilegeModel.dart';
import 'package:hmg_patient_app_new/core/common_models/privilege/ProjectDetailListModel.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/common_models/privilege/VidaPlusProjectListModel.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart'; import 'package:hmg_patient_app_new/core/dependencies.dart';
@ -19,7 +19,6 @@ import 'package:hmg_patient_app_new/core/utils/loading_utils.dart';
import 'package:hmg_patient_app_new/core/utils/request_utils.dart'; import 'package:hmg_patient_app_new/core/utils/request_utils.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart'; import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/core/utils/validation_utils.dart'; import 'package:hmg_patient_app_new/core/utils/validation_utils.dart';
import 'package:hmg_patient_app_new/extensions/context_extensions.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/features/authentication/authentication_repo.dart'; import 'package:hmg_patient_app_new/features/authentication/authentication_repo.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/request_models/check_activation_code_register_request_model.dart';
@ -40,7 +39,6 @@ import 'package:hmg_patient_app_new/services/error_handler_service.dart';
import 'package:hmg_patient_app_new/services/localauth_service.dart'; import 'package:hmg_patient_app_new/services/localauth_service.dart';
import 'package:hmg_patient_app_new/services/navigation_service.dart'; import 'package:hmg_patient_app_new/services/navigation_service.dart';
import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart'; import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart';
import 'package:hmg_patient_app_new/widgets/bottomsheet/exception_bottom_sheet.dart';
import 'package:sms_otp_auto_verify/sms_otp_auto_verify.dart'; import 'package:sms_otp_auto_verify/sms_otp_auto_verify.dart';
import 'models/request_models/get_user_mobile_device_data.dart'; import 'models/request_models/get_user_mobile_device_data.dart';
@ -248,7 +246,7 @@ class AuthenticationViewModel extends ChangeNotifier {
await selectDeviceImei(onSuccess: (dynamic respData) async { await selectDeviceImei(onSuccess: (dynamic respData) async {
try { try {
if (respData != null) { if (respData != null) {
dynamic data = SelectDeviceByImeiRespModelElement.fromJson(respData.toJson()); dynamic data = await SelectDeviceByImeiRespModelElement.fromJson(respData.toJson());
_appState.setSelectDeviceByImeiRespModelElement(data); _appState.setSelectDeviceByImeiRespModelElement(data);
LoaderBottomSheet.hideLoader(); LoaderBottomSheet.hideLoader();
@ -368,7 +366,7 @@ class AuthenticationViewModel extends ChangeNotifier {
isForRegister: isForRegister, isForRegister: isForRegister,
patientOutSA: isForRegister patientOutSA: isForRegister
? isPatientOutsideSA(request: payload) ? isPatientOutsideSA(request: payload)
: selectedCountrySignup.countryCode == CountryEnum.saudiArabia : selectedCountrySignup.countryCode == CountryEnum.saudiArabia.countryCode
? false ? false
: true, : true,
payload: payload, payload: payload,
@ -390,10 +388,9 @@ class AuthenticationViewModel extends ChangeNotifier {
(failure) async => await _errorHandlerService.handleError(failure: failure), (failure) async => await _errorHandlerService.handleError(failure: failure),
(apiResponse) async { (apiResponse) async {
int? patientShareRequestID = 0; int? patientShareRequestID = 0;
String? familyFileTokenID;
if (isFormFamilyFile) { if (isFormFamilyFile) {
patientShareRequestID = apiResponse.data['PatientShareRequestID']; patientShareRequestID = apiResponse.data['PatientShareRequestID'];
familyFileTokenID = apiResponse.data['LogInTokenID']; _appState.setFamilyFileTokenID = apiResponse.data['LogInTokenID'];
} }
if (apiResponse.messageStatus == 2) { if (apiResponse.messageStatus == 2) {
LoaderBottomSheet.hideLoader(); LoaderBottomSheet.hideLoader();
@ -407,15 +404,15 @@ class AuthenticationViewModel extends ChangeNotifier {
LoaderBottomSheet.hideLoader(); LoaderBottomSheet.hideLoader();
if (!isComingFromResendOTP) { if (!isComingFromResendOTP) {
navigateToOTPScreen( navigateToOTPScreen(
otpTypeEnum: otpTypeEnum, otpTypeEnum: otpTypeEnum,
phoneNumber: phoneNumber, phoneNumber: phoneNumber,
isComingFromRegister: checkIsUserComingForRegister(request: payload), isComingFromRegister: checkIsUserComingForRegister(request: payload),
payload: payload, payload: payload,
isFormFamilyFile: isFormFamilyFile, isFormFamilyFile: isFormFamilyFile,
isExcludedUser: isExcludedUser, isExcludedUser: isExcludedUser,
responseID: responseID, responseID: responseID,
patientShareRequestID: patientShareRequestID, patientShareRequestID: patientShareRequestID,
familyFileTokenID: familyFileTokenID); );
} }
} else { } else {
// TODO: Handle isSMSSent false // TODO: Handle isSMSSent false
@ -434,15 +431,17 @@ class AuthenticationViewModel extends ChangeNotifier {
return isUserComingForRegister; return isUserComingForRegister;
} }
Future<void> checkActivationCode( Future<void> checkActivationCode({
{required String? activationCode, required String? activationCode,
required OTPTypeEnum otpTypeEnum, required OTPTypeEnum otpTypeEnum,
required Function(String? message) onWrongActivationCode, required Function(String? message) onWrongActivationCode,
Function()? onResendActivation, Function()? onResendActivation,
bool isFormFamilyFile = false, bool isFormFamilyFile = false,
dynamic patientShareRequestID, dynamic patientShareRequestID,
dynamic responseID, dynamic responseID,
String? familyFileTokenID}) async { bool isSwitchUser = false,
int? patientID,
}) async {
bool isForRegister = (_appState.getUserRegistrationPayload.healthId != null || _appState.getUserRegistrationPayload.patientOutSa == true || _appState.getUserRegistrationPayload.patientOutSa == 1); bool isForRegister = (_appState.getUserRegistrationPayload.healthId != null || _appState.getUserRegistrationPayload.patientOutSa == true || _appState.getUserRegistrationPayload.patientOutSa == 1);
final request = RequestUtils.getCommonRequestWelcome( final request = RequestUtils.getCommonRequestWelcome(
@ -478,11 +477,7 @@ class AuthenticationViewModel extends ChangeNotifier {
request["ForRegisteration"] = _appState.getUserRegistrationPayload.isRegister; request["ForRegisteration"] = _appState.getUserRegistrationPayload.isRegister;
request["isRegister"] = false; request["isRegister"] = false;
final resultEither = await _authenticationRepo.checkActivationCodeRepo( final resultEither = await _authenticationRepo.checkActivationCodeRepo(newRequest: request, activationCode: activationCode.toString(), isRegister: true);
newRequest: request,
activationCode: activationCode.toString(),
isRegister: true,
);
LoaderBottomSheet.hideLoader(); LoaderBottomSheet.hideLoader();
@ -509,13 +504,16 @@ class AuthenticationViewModel extends ChangeNotifier {
}); });
} else { } else {
final resultEither = await _authenticationRepo.checkActivationCodeRepo( final resultEither = await _authenticationRepo.checkActivationCodeRepo(
newRequest: CheckActivationCodeRegisterReq.fromJson(request), newRequest: CheckActivationCodeRegisterReq.fromJson(request),
activationCode: activationCode, activationCode: activationCode,
isRegister: false, isRegister: false,
isFormFamilyFile: isFormFamilyFile, isFormFamilyFile: isFormFamilyFile,
patientShareRequestID: patientShareRequestID, patientShareRequestID: patientShareRequestID,
responseID: responseID, responseID: responseID,
familyFileTokenID: familyFileTokenID); isSwitchUser: isSwitchUser,
patientID: patientID,
loginType: _appState.getSuperUserID != null ? 0 : 2,
);
resultEither.fold( resultEither.fold(
(failure) async => await _errorHandlerService.handleError( (failure) async => await _errorHandlerService.handleError(
@ -529,8 +527,6 @@ class AuthenticationViewModel extends ChangeNotifier {
LoaderBottomSheet.hideLoader(); LoaderBottomSheet.hideLoader();
await _dialogService.showCommonBottomSheetWithoutH(message: failure.message, label: LocaleKeys.notice.tr(), onOkPressed: () {}); await _dialogService.showCommonBottomSheetWithoutH(message: failure.message, label: LocaleKeys.notice.tr(), onOkPressed: () {});
}), (apiResponse) async { }), (apiResponse) async {
print("API Response Data: ${apiResponse.data}");
final activation = CheckActivationCode.fromJson(apiResponse.data as Map<String, dynamic>); final activation = CheckActivationCode.fromJson(apiResponse.data as Map<String, dynamic>);
if (activation.errorCode == '699') { if (activation.errorCode == '699') {
@ -549,59 +545,83 @@ class AuthenticationViewModel extends ChangeNotifier {
return; return;
} else { } else {
if (isFormFamilyFile) { if (isFormFamilyFile) {
_dialogService.showCommonBottomSheetWithoutH( // await navigateToFamilyFilePage();
message: "Family File Added Successfully", MedicalFileViewModel medicalVm = getIt<MedicalFileViewModel>();
onOkPressed: () { if (!_appState.getIsChildLoggedIn) {
LoaderBottomSheet.showLoader(); await medicalVm.getFamilyFiles(status: 0);
MedicalFileViewModel medicalFileVM = GetIt.instance<MedicalFileViewModel>(); await medicalVm.getAllPendingRecordsByResponseId();
medicalFileVM.getFamilyFiles(); _navigationService.popUntilNamed(AppRoutes.landingScreen);
LoaderBottomSheet.hideLoader(); }
_navigationService.popUntilNamed(AppRoutes.medicalFilePage);
});
}
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();
} else { } else {
navigateToHomeScreen(); if (activation.list != null && activation.list!.isNotEmpty) {
//Agreement page not designed yet so we are navigating to home screen directly if (isSwitchUser) {
// getUserAgreementContent(request: request); 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<MedicalFileViewModel>();
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 {
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<void> navigateToFamilyFilePage() async {
MedicalFileViewModel medicalFileVM = GetIt.instance<MedicalFileViewModel>();
medicalFileVM.handleFamilyFileRequestOTPVerification();
}
Future<bool> checkIfUserAgreedBefore({required dynamic request}) async { Future<bool> checkIfUserAgreedBefore({required dynamic request}) async {
bool isAgreed = false; bool isAgreed = false;
if (havePrivilege(109)) { if (havePrivilege(109)) {
@ -647,8 +667,7 @@ class AuthenticationViewModel extends ChangeNotifier {
bool isFormFamilyFile = false, bool isFormFamilyFile = false,
bool isExcludedUser = false, bool isExcludedUser = false,
int? responseID, int? responseID,
int? patientShareRequestID, int? patientShareRequestID}) async {
String? familyFileTokenID}) async {
_navigationService.pushToOtpScreen( _navigationService.pushToOtpScreen(
phoneNumber: phoneNumber, phoneNumber: phoneNumber,
isFormFamilyFile: isFormFamilyFile, isFormFamilyFile: isFormFamilyFile,
@ -659,7 +678,6 @@ class AuthenticationViewModel extends ChangeNotifier {
otpTypeEnum: otpTypeEnum, otpTypeEnum: otpTypeEnum,
responseID: responseID, responseID: responseID,
patientShareRequestID: patientShareRequestID, patientShareRequestID: patientShareRequestID,
familyFileTokenID: familyFileTokenID,
onWrongActivationCode: (String? value) { onWrongActivationCode: (String? value) {
onWrongActivationCode(message: value); onWrongActivationCode(message: value);
}, },

@ -69,6 +69,8 @@ class AuthenticatedUser {
dynamic authenticatedUserPatientPayType; dynamic authenticatedUserPatientPayType;
dynamic authenticatedUserPatientType; dynamic authenticatedUserPatientType;
dynamic authenticatedUserStatus; dynamic authenticatedUserStatus;
int? superUser;
bool? isParentUser;
AuthenticatedUser({ AuthenticatedUser({
this.setupId, this.setupId,
@ -139,6 +141,8 @@ class AuthenticatedUser {
this.authenticatedUserPatientPayType, this.authenticatedUserPatientPayType,
this.authenticatedUserPatientType, this.authenticatedUserPatientType,
this.authenticatedUserStatus, this.authenticatedUserStatus,
this.superUser,
this.isParentUser,
}); });
factory AuthenticatedUser.fromRawJson(String str) => AuthenticatedUser.fromJson(json.decode(str)); factory AuthenticatedUser.fromRawJson(String str) => AuthenticatedUser.fromJson(json.decode(str));
@ -146,146 +150,150 @@ class AuthenticatedUser {
String toRawJson() => json.encode(toJson()); String toRawJson() => json.encode(toJson());
factory AuthenticatedUser.fromJson(Map<String, dynamic> json) => AuthenticatedUser( factory AuthenticatedUser.fromJson(Map<String, dynamic> json) => AuthenticatedUser(
setupId: json["SetupID"], setupId: json["SetupID"],
patientType: json["PatientType"], patientType: json["PatientType"],
patientId: json["PatientID"], patientId: json["PatientID"],
firstName: json["FirstName"], firstName: json["FirstName"],
middleName: json["MiddleName"], middleName: json["MiddleName"],
lastName: json["LastName"], lastName: json["LastName"],
firstNameN: json["FirstNameN"], firstNameN: json["FirstNameN"],
middleNameN: json["MiddleNameN"], middleNameN: json["MiddleNameN"],
lastNameN: json["LastNameN"], lastNameN: json["LastNameN"],
relationshipId: json["RelationshipID"], relationshipId: json["RelationshipID"],
gender: json["Gender"], gender: json["Gender"],
dateofBirth: json["DateofBirth"], dateofBirth: json["DateofBirth"],
dateofBirthN: json["DateofBirthN"], dateofBirthN: json["DateofBirthN"],
nationalityId: json["NationalityID"], nationalityId: json["NationalityID"],
phoneResi: json["PhoneResi"], phoneResi: json["PhoneResi"],
phoneOffice: json["PhoneOffice"], phoneOffice: json["PhoneOffice"],
mobileNumber: json["MobileNumber"], mobileNumber: json["MobileNumber"],
faxNumber: json["FaxNumber"], faxNumber: json["FaxNumber"],
emailAddress: json["EmailAddress"], emailAddress: json["EmailAddress"],
bloodGroup: json["BloodGroup"], bloodGroup: json["BloodGroup"],
rhFactor: json["RHFactor"], rhFactor: json["RHFactor"],
isEmailAlertRequired: json["IsEmailAlertRequired"], isEmailAlertRequired: json["IsEmailAlertRequired"],
isSmsAlertRequired: json["IsSMSAlertRequired"], isSmsAlertRequired: json["IsSMSAlertRequired"],
preferredLanguage: json["PreferredLanguage"], preferredLanguage: json["PreferredLanguage"],
isPrivilegedMember: json["IsPrivilegedMember"], isPrivilegedMember: json["IsPrivilegedMember"],
memberId: json["MemberID"], memberId: json["MemberID"],
expiryDate: json["ExpiryDate"], expiryDate: json["ExpiryDate"],
isHmgEmployee: json["IsHmgEmployee"], isHmgEmployee: json["IsHmgEmployee"],
employeeId: json["EmployeeID"], employeeId: json["EmployeeID"],
emergencyContactName: json["EmergencyContactName"], emergencyContactName: json["EmergencyContactName"],
emergencyContactNo: json["EmergencyContactNo"], emergencyContactNo: json["EmergencyContactNo"],
patientPayType: json["PatientPayType"], patientPayType: json["PatientPayType"],
dhccPatientRefId: json["DHCCPatientRefID"], dhccPatientRefId: json["DHCCPatientRefID"],
isPatientDummy: json["IsPatientDummy"], isPatientDummy: json["IsPatientDummy"],
status: json["Status"], status: json["Status"],
isStatusCleared: json["IsStatusCleared"], isStatusCleared: json["IsStatusCleared"],
patientIdentificationType: json["PatientIdentificationType"], patientIdentificationType: json["PatientIdentificationType"],
patientIdentificationNo: json["PatientIdentificationNo"], patientIdentificationNo: json["PatientIdentificationNo"],
projectId: json["ProjectID"], projectId: json["ProjectID"],
infoSourceId: json["InfoSourceID"], infoSourceId: json["InfoSourceID"],
address: json["Address"], address: json["Address"],
age: json["Age"], age: json["Age"],
ageDesc: json["AgeDesc"], ageDesc: json["AgeDesc"],
areaId: json["AreaID"], areaId: json["AreaID"],
crsVerificationStatus: json["CRSVerificationStatus"], crsVerificationStatus: json["CRSVerificationStatus"],
crsVerificationStatusDesc: json["CRSVerificationStatusDesc"], crsVerificationStatusDesc: json["CRSVerificationStatusDesc"],
crsVerificationStatusDescN: json["CRSVerificationStatusDescN"], crsVerificationStatusDescN: json["CRSVerificationStatusDescN"],
createdBy: json["CreatedBy"], createdBy: json["CreatedBy"],
genderDescription: json["GenderDescription"], genderDescription: json["GenderDescription"],
healthIdFromNhicViaVida: json["HealthIDFromNHICViaVida"], healthIdFromNhicViaVida: json["HealthIDFromNHICViaVida"],
ir: json["IR"], ir: json["IR"],
isoCityId: json["ISOCityID"], isoCityId: json["ISOCityID"],
isoCountryId: json["ISOCountryID"], isoCountryId: json["ISOCountryID"],
isVerfiedFromNhic: json["IsVerfiedFromNHIC"], isVerfiedFromNhic: json["IsVerfiedFromNHIC"],
listPrivilege: json["ListPrivilege"] == null ? [] : List<ListPrivilege>.from(json["ListPrivilege"]!.map((x) => ListPrivilege.fromJson(x))), listPrivilege: json["ListPrivilege"] == null ? [] : List<ListPrivilege>.from(json["ListPrivilege"]!.map((x) => ListPrivilege.fromJson(x))),
marital: json["Marital"], marital: json["Marital"],
occupationId: json["OccupationID"], occupationId: json["OccupationID"],
outSa: json["OutSA"], outSa: json["OutSA"],
poBox: json["POBox"], poBox: json["POBox"],
receiveHealthSummaryReport: json["ReceiveHealthSummaryReport"], receiveHealthSummaryReport: json["ReceiveHealthSummaryReport"],
sourceType: json["SourceType"], sourceType: json["SourceType"],
strDateofBirth: json["StrDateofBirth"], strDateofBirth: json["StrDateofBirth"],
tempAddress: json["TempAddress"], tempAddress: json["TempAddress"],
zipCode: json["ZipCode"], zipCode: json["ZipCode"],
eHealthIdField: json["eHealthIDField"], eHealthIdField: json["eHealthIDField"],
authenticatedUserPatientPayType: json["patientPayType"], authenticatedUserPatientPayType: json["patientPayType"],
authenticatedUserPatientType: json["patientType"], authenticatedUserPatientType: json["patientType"],
authenticatedUserStatus: json["status"], authenticatedUserStatus: json["status"],
); superUser: json["superUser"],
isParentUser: json["isParentUser"] ?? false,
);
Map<String, dynamic> toJson() => { Map<String, dynamic> toJson() => {
"SetupID": setupId, "SetupID": setupId,
"PatientType": patientType, "PatientType": patientType,
"PatientID": patientId, "PatientID": patientId,
"FirstName": firstName, "FirstName": firstName,
"MiddleName": middleName, "MiddleName": middleName,
"LastName": lastName, "LastName": lastName,
"FirstNameN": firstNameN, "FirstNameN": firstNameN,
"MiddleNameN": middleNameN, "MiddleNameN": middleNameN,
"LastNameN": lastNameN, "LastNameN": lastNameN,
"RelationshipID": relationshipId, "RelationshipID": relationshipId,
"Gender": gender, "Gender": gender,
"DateofBirth": dateofBirth, "DateofBirth": dateofBirth,
"DateofBirthN": dateofBirthN, "DateofBirthN": dateofBirthN,
"NationalityID": nationalityId, "NationalityID": nationalityId,
"PhoneResi": phoneResi, "PhoneResi": phoneResi,
"PhoneOffice": phoneOffice, "PhoneOffice": phoneOffice,
"MobileNumber": mobileNumber, "MobileNumber": mobileNumber,
"FaxNumber": faxNumber, "FaxNumber": faxNumber,
"EmailAddress": emailAddress, "EmailAddress": emailAddress,
"BloodGroup": bloodGroup, "BloodGroup": bloodGroup,
"RHFactor": rhFactor, "RHFactor": rhFactor,
"IsEmailAlertRequired": isEmailAlertRequired, "IsEmailAlertRequired": isEmailAlertRequired,
"IsSMSAlertRequired": isSmsAlertRequired, "IsSMSAlertRequired": isSmsAlertRequired,
"PreferredLanguage": preferredLanguage, "PreferredLanguage": preferredLanguage,
"IsPrivilegedMember": isPrivilegedMember, "IsPrivilegedMember": isPrivilegedMember,
"MemberID": memberId, "MemberID": memberId,
"ExpiryDate": expiryDate, "ExpiryDate": expiryDate,
"IsHmgEmployee": isHmgEmployee, "IsHmgEmployee": isHmgEmployee,
"EmployeeID": employeeId, "EmployeeID": employeeId,
"EmergencyContactName": emergencyContactName, "EmergencyContactName": emergencyContactName,
"EmergencyContactNo": emergencyContactNo, "EmergencyContactNo": emergencyContactNo,
"PatientPayType": patientPayType, "PatientPayType": patientPayType,
"DHCCPatientRefID": dhccPatientRefId, "DHCCPatientRefID": dhccPatientRefId,
"IsPatientDummy": isPatientDummy, "IsPatientDummy": isPatientDummy,
"Status": status, "Status": status,
"IsStatusCleared": isStatusCleared, "IsStatusCleared": isStatusCleared,
"PatientIdentificationType": patientIdentificationType, "PatientIdentificationType": patientIdentificationType,
"PatientIdentificationNo": patientIdentificationNo, "PatientIdentificationNo": patientIdentificationNo,
"ProjectID": projectId, "ProjectID": projectId,
"InfoSourceID": infoSourceId, "InfoSourceID": infoSourceId,
"Address": address, "Address": address,
"Age": age, "Age": age,
"AgeDesc": ageDesc, "AgeDesc": ageDesc,
"AreaID": areaId, "AreaID": areaId,
"CRSVerificationStatus": crsVerificationStatus, "CRSVerificationStatus": crsVerificationStatus,
"CRSVerificationStatusDesc": crsVerificationStatusDesc, "CRSVerificationStatusDesc": crsVerificationStatusDesc,
"CRSVerificationStatusDescN": crsVerificationStatusDescN, "CRSVerificationStatusDescN": crsVerificationStatusDescN,
"CreatedBy": createdBy, "CreatedBy": createdBy,
"GenderDescription": genderDescription, "GenderDescription": genderDescription,
"HealthIDFromNHICViaVida": healthIdFromNhicViaVida, "HealthIDFromNHICViaVida": healthIdFromNhicViaVida,
"IR": ir, "IR": ir,
"ISOCityID": isoCityId, "ISOCityID": isoCityId,
"ISOCountryID": isoCountryId, "ISOCountryID": isoCountryId,
"IsVerfiedFromNHIC": isVerfiedFromNhic, "IsVerfiedFromNHIC": isVerfiedFromNhic,
"ListPrivilege": listPrivilege == null ? [] : List<dynamic>.from(listPrivilege!.map((x) => x.toJson())), "ListPrivilege": listPrivilege == null ? [] : List<dynamic>.from(listPrivilege!.map((x) => x.toJson())),
"Marital": marital, "Marital": marital,
"OccupationID": occupationId, "OccupationID": occupationId,
"OutSA": outSa, "OutSA": outSa,
"POBox": poBox, "POBox": poBox,
"ReceiveHealthSummaryReport": receiveHealthSummaryReport, "ReceiveHealthSummaryReport": receiveHealthSummaryReport,
"SourceType": sourceType, "SourceType": sourceType,
"StrDateofBirth": strDateofBirth, "StrDateofBirth": strDateofBirth,
"TempAddress": tempAddress, "TempAddress": tempAddress,
"ZipCode": zipCode, "ZipCode": zipCode,
"eHealthIDField": eHealthIdField, "eHealthIDField": eHealthIdField,
"patientPayType": authenticatedUserPatientPayType, "patientPayType": authenticatedUserPatientPayType,
"patientType": authenticatedUserPatientType, "patientType": authenticatedUserPatientType,
"status": authenticatedUserStatus, "status": authenticatedUserStatus,
}; "superUser": superUser,
"isParentUser": isParentUser,
};
} }
class ListPrivilege { class ListPrivilege {
@ -306,16 +314,16 @@ class ListPrivilege {
String toRawJson() => json.encode(toJson()); String toRawJson() => json.encode(toJson());
factory ListPrivilege.fromJson(Map<String, dynamic> json) => ListPrivilege( factory ListPrivilege.fromJson(Map<String, dynamic> json) => ListPrivilege(
id: json["ID"], id: json["ID"],
serviceName: json["ServiceName"], serviceName: json["ServiceName"],
previlege: json["Previlege"], previlege: json["Previlege"],
region: json["Region"], region: json["Region"],
); );
Map<String, dynamic> toJson() => { Map<String, dynamic> toJson() => {
"ID": id, "ID": id,
"ServiceName": serviceName, "ServiceName": serviceName,
"Previlege": previlege, "Previlege": previlege,
"Region": region, "Region": region,
}; };
} }

@ -1,12 +1,17 @@
import 'dart:io';
import 'package:dartz/dartz.dart'; import 'package:dartz/dartz.dart';
import 'package:hmg_patient_app_new/core/api/api_client.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/api_consts.dart';
import 'package:hmg_patient_app_new/core/common_models/generic_api_model.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/exceptions/api_failure.dart';
import 'package:hmg_patient_app_new/core/utils/date_util.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/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/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_clinic_list_response_model.dart';
import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/get_livecare_clinics_response_model.dart';
import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/get_patient_dental_plan_response_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/hospital_model.dart'; import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/hospital_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/patient_appointment_history_response_model.dart'; import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/patient_appointment_history_response_model.dart';
import 'package:hmg_patient_app_new/services/logger_service.dart'; import 'package:hmg_patient_app_new/services/logger_service.dart';
@ -20,7 +25,7 @@ abstract class BookAppointmentsRepo {
Future<Either<Failure, GenericApiModel<DoctorsProfileResponseModel>>> getDoctorProfile(int clinicID, int projectID, int doctorId, {Function(dynamic)? onSuccess, Function(String)? onError}); Future<Either<Failure, GenericApiModel<DoctorsProfileResponseModel>>> getDoctorProfile(int clinicID, int projectID, int doctorId, {Function(dynamic)? onSuccess, Function(String)? onError});
Future<Either<Failure, GenericApiModel<dynamic>>> getDoctorFreeSlots(int clinicID, int projectID, int doctorId, bool isBookingForLiveCare, {Function(dynamic)? onSuccess, Function(String)? onError}); Future<Either<Failure, GenericApiModel<dynamic>>> getDoctorFreeSlots(int clinicID, int projectID, int doctorId, bool isBookingForLiveCare, {bool continueDentalPlan = false, Function(dynamic)? onSuccess, Function(String)? onError});
Future<Either<Failure, GenericApiModel<dynamic>>> cancelAppointment({required PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel}); Future<Either<Failure, GenericApiModel<dynamic>>> cancelAppointment({required PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel});
@ -45,6 +50,35 @@ abstract class BookAppointmentsRepo {
Future<Either<Failure, GenericApiModel<List<HospitalsModel>>>> getProjectList(); Future<Either<Failure, GenericApiModel<List<HospitalsModel>>>> getProjectList();
Future<Either<Failure, GenericApiModel<List<GetClinicsListResponseModel>>>> getClinicsWithRespectToClinicId(String projectID); Future<Either<Failure, GenericApiModel<List<GetClinicsListResponseModel>>>> getClinicsWithRespectToClinicId(String projectID);
Future<Either<Failure, GenericApiModel<List<GetLiveCareClinicsResponseModel>>>> getLiveCareScheduleClinics(int age, int genderID);
Future<Either<Failure, GenericApiModel<List<DoctorsListResponseModel>>>> getLiveCareDoctorsList(int serviceID, int age, int genderID, {Function(dynamic)? onSuccess, Function(String)? onError});
Future<Either<Failure, GenericApiModel<dynamic>>> getLiveCareDoctorFreeSlots(int clinicID, int serviceID, int projectID, int doctorId, bool isBookingForLiveCare,
{Function(dynamic)? onSuccess, Function(String)? onError});
Future<Either<Failure, GenericApiModel<dynamic>>> insertSpecificAppointmentForLiveCare(
{required int docID,
required int clinicID,
required int projectID,
required String selectedTime,
required String selectedDate,
required int initialSlotDuration,
required int genderID,
required int userAge,
required int serviceID,
Function(dynamic)? onSuccess,
Function(String)? onError});
Future<Either<Failure, GenericApiModel<List<PatientDentalPlanEstimationResponseModel>>>> getPatientDentalEstimation(
{required int projectID, Function(dynamic)? onSuccess, Function(String)? onError});
Future<Either<Failure, GenericApiModel<List<DentalChiefComplaintsListResponseModel>>>> getDentalChiefComplaintsList(
{required int projectID, required int clinicID, required int patientID, Function(dynamic)? onSuccess, Function(String)? onError});
Future<Either<Failure, GenericApiModel<List<DoctorsListResponseModel>>>> getDentalChiefComplaintDoctorsList(int projectID, int chiefComplaintID,
{Function(dynamic)? onSuccess, Function(String)? onError});
} }
class BookAppointmentsRepoImp implements BookAppointmentsRepo { class BookAppointmentsRepoImp implements BookAppointmentsRepo {
@ -128,9 +162,6 @@ class BookAppointmentsRepoImp implements BookAppointmentsRepo {
onSuccess: (response, statusCode, {messageStatus, errorMessage}) { onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try { try {
final list = response['DoctorList']; final list = response['DoctorList'];
// if (list == null || list.isEmpty) {
// throw Exception("lab list is empty");
// }
final doctorsList = list.map((item) => DoctorsListResponseModel.fromJson(item as Map<String, dynamic>)).toList().cast<DoctorsListResponseModel>(); final doctorsList = list.map((item) => DoctorsListResponseModel.fromJson(item as Map<String, dynamic>)).toList().cast<DoctorsListResponseModel>();
@ -205,7 +236,7 @@ class BookAppointmentsRepoImp implements BookAppointmentsRepo {
//TODO: Implement the logic for Dental & laser clinics //TODO: Implement the logic for Dental & laser clinics
@override @override
Future<Either<Failure, GenericApiModel<dynamic>>> getDoctorFreeSlots(int clinicID, int projectID, int doctorId, bool isBookingForLiveCare, Future<Either<Failure, GenericApiModel<dynamic>>> getDoctorFreeSlots(int clinicID, int projectID, int doctorId, bool isBookingForLiveCare,
{Function(dynamic)? onSuccess, Function(String)? onError}) async { {bool continueDentalPlan = false, Function(dynamic)? onSuccess, Function(String)? onError}) async {
Map<String, dynamic> mapDevice = { Map<String, dynamic> mapDevice = {
"DoctorID": doctorId, "DoctorID": doctorId,
"IsBookingForLiveCare": isBookingForLiveCare, "IsBookingForLiveCare": isBookingForLiveCare,
@ -214,6 +245,7 @@ class BookAppointmentsRepoImp implements BookAppointmentsRepo {
"OriginalClinicID": clinicID, "OriginalClinicID": clinicID,
"days": 0, "days": 0,
"isReschadual": false, "isReschadual": false,
"ContinueDentalPlan": continueDentalPlan
}; };
try { try {
@ -443,4 +475,329 @@ class BookAppointmentsRepoImp implements BookAppointmentsRepo {
return Left(UnknownFailure(e.toString())); return Left(UnknownFailure(e.toString()));
} }
} }
@override
Future<Either<Failure, GenericApiModel<List<GetLiveCareClinicsResponseModel>>>> getLiveCareScheduleClinics(int age, int genderID) async {
Map<String, dynamic> mapDevice = {"Age": age, "Gender": genderID};
try {
GenericApiModel<List<GetLiveCareClinicsResponseModel>>? apiResponse;
Failure? failure;
await apiClient.post(
GET_LIVECARE_SCHEDULE_CLINICS,
body: mapDevice,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
final list = response['ClinicsHaveScheduleList'];
// if (list == null || list.isEmpty) {
// throw Exception("lab list is empty");
// }
final clinicsList = list.map((item) => GetLiveCareClinicsResponseModel.fromJson(item as Map<String, dynamic>)).toList().cast<GetLiveCareClinicsResponseModel>();
apiResponse = GenericApiModel<List<GetLiveCareClinicsResponseModel>>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: null,
data: clinicsList,
);
} catch (e) {
failure = DataParsingFailure(e.toString());
}
},
);
if (failure != null) return Left(failure!);
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
return Right(apiResponse!);
} catch (e) {
return Left(UnknownFailure(e.toString()));
}
}
@override
Future<Either<Failure, GenericApiModel<List<DoctorsListResponseModel>>>> getLiveCareDoctorsList(int serviceID, int age, int genderID,
{Function(dynamic)? onSuccess, Function(String)? onError}) async {
Map<String, dynamic> mapDevice = {
"ServiceID": serviceID,
"Age": age,
"Gender": genderID,
};
try {
GenericApiModel<List<DoctorsListResponseModel>>? apiResponse;
Failure? failure;
await apiClient.post(
GET_LIVECARE_SCHEDULE_CLINIC_DOCTOR_LIST,
body: mapDevice,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
onError!(error);
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
final list = response['DoctorByClinicIDList'];
final doctorsList = list.map((item) => DoctorsListResponseModel.fromJson(item as Map<String, dynamic>)).toList().cast<DoctorsListResponseModel>();
apiResponse = GenericApiModel<List<DoctorsListResponseModel>>(
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<Either<Failure, GenericApiModel>> getLiveCareDoctorFreeSlots(int clinicID, int serviceID, int projectID, int doctorId, bool isBookingForLiveCare,
{Function(dynamic)? onSuccess, Function(String)? onError}) async {
Map<String, dynamic> mapDevice = {
"DoctorID": doctorId,
"IsBookingForLiveCare": true,
"ClinicID": clinicID,
"ServiceID": serviceID,
"ProjectID": projectID,
"OriginalClinicID": clinicID,
"days": 50,
"isReschadual": false,
};
try {
GenericApiModel<dynamic>? apiResponse;
Failure? failure;
await apiClient.post(
GET_LIVECARE_SCHEDULE_DOCTOR_TIME_SLOTS,
body: mapDevice,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
apiResponse = GenericApiModel<dynamic>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: null,
data: response,
);
} catch (e) {
failure = DataParsingFailure(e.toString());
}
},
);
if (failure != null) return Left(failure!);
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
return Right(apiResponse!);
} catch (e) {
return Left(UnknownFailure(e.toString()));
}
}
@override
Future<Either<Failure, GenericApiModel>> insertSpecificAppointmentForLiveCare(
{required int docID,
required int clinicID,
required int projectID,
required String selectedTime,
required String selectedDate,
required int initialSlotDuration,
required int genderID,
required int userAge,
required int serviceID,
Function(dynamic)? onSuccess,
Function(String)? onError}) async {
Map<String, dynamic> mapDevice = {
"IsForLiveCare": true,
"ProjectID": projectID,
"ClinicID": clinicID,
"DoctorID": docID,
"ServiceID": serviceID,
"StartTime": selectedTime,
"SelectedTime": selectedTime,
"EndTime": selectedTime,
"InitialSlotDuration": initialSlotDuration,
"StrAppointmentDate": selectedDate,
"IsVirtual": false,
"BookedBy": 102,
"VisitType": 1,
"VisitFor": 1,
"GenderID": genderID,
"Age": userAge
};
try {
GenericApiModel<dynamic>? apiResponse;
Failure? failure;
await apiClient.post(
INSERT_LIVECARE_SCHEDULE_APPOINTMENT,
body: mapDevice,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
final appointmentNo = response['AppointmentNo'];
apiResponse = GenericApiModel<dynamic>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: response["ErrorEndUserMessage"],
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<Either<Failure, GenericApiModel<List<PatientDentalPlanEstimationResponseModel>>>> getPatientDentalEstimation(
{required int projectID, Function(dynamic)? onSuccess, Function(String)? onError}) async {
Map<String, dynamic> mapDevice = {
"ProjectID": projectID,
};
try {
GenericApiModel<List<PatientDentalPlanEstimationResponseModel>>? apiResponse;
Failure? failure;
await apiClient.post(
HAS_DENTAL_PLAN,
body: mapDevice,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
onError!(error);
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
final list = response['List_IsPatientHasOnGoingEstimation'];
final estimationList = list.map((item) => PatientDentalPlanEstimationResponseModel.fromJson(item as Map<String, dynamic>)).toList().cast<PatientDentalPlanEstimationResponseModel>();
apiResponse = GenericApiModel<List<PatientDentalPlanEstimationResponseModel>>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: null,
data: estimationList,
);
} catch (e) {
failure = DataParsingFailure(e.toString());
}
},
);
if (failure != null) return Left(failure!);
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
return Right(apiResponse!);
} catch (e) {
return Left(UnknownFailure(e.toString()));
}
}
@override
Future<Either<Failure, GenericApiModel<List<DentalChiefComplaintsListResponseModel>>>> getDentalChiefComplaintsList(
{required int projectID, required int clinicID, required int patientID, Function(dynamic)? onSuccess, Function(String)? onError}) async {
Map<String, dynamic> mapDevice = {
"PatientID": patientID,
"ClinicID": clinicID,
"ProjectID": projectID,
"isDentalAllowedBackend": true,
"ContinueDentalPlan": false,
"IsSearchAppointmnetByClinicID": false,
};
try {
GenericApiModel<List<DentalChiefComplaintsListResponseModel>>? 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<String, dynamic>)).toList().cast<DentalChiefComplaintsListResponseModel>();
apiResponse = GenericApiModel<List<DentalChiefComplaintsListResponseModel>>(
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<Either<Failure, GenericApiModel<List<DoctorsListResponseModel>>>> getDentalChiefComplaintDoctorsList(int projectID, int chiefComplaintID,
{Function(dynamic)? onSuccess, Function(String)? onError}) async {
Map<String, dynamic> mapDevice = {
"ProjectID": projectID,
"ChiefComplaintID": chiefComplaintID,
"isDentalAllowedBackend": true,
"IsPublicRequest": true,
};
try {
GenericApiModel<List<DoctorsListResponseModel>>? 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<String, dynamic>)).toList().cast<DoctorsListResponseModel>();
apiResponse = GenericApiModel<List<DoctorsListResponseModel>>(
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()));
}
}
} }

@ -12,9 +12,11 @@ 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/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/book_appointments_repo.dart';
import 'package:hmg_patient_app_new/features/book_appointments/models/free_slot.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/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/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_clinic_list_response_model.dart';
import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/get_patient_dental_plan_response_model.dart';
import 'package:hmg_patient_app_new/features/book_appointments/models/timeslots.dart'; import 'package:hmg_patient_app_new/features/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/facility_selection.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/doctor_list_api_response.dart'; import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/doctor_list_api_response.dart';
@ -26,8 +28,8 @@ 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/services/navigation_service.dart';
import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart'; import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart';
import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart'; import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart';
import 'package:location/location.dart' show Location; import 'models/resp_models/get_livecare_clinics_response_model.dart';
class BookAppointmentsViewModel extends ChangeNotifier { class BookAppointmentsViewModel extends ChangeNotifier {
int selectedTabIndex = 0; int selectedTabIndex = 0;
@ -37,6 +39,8 @@ class BookAppointmentsViewModel extends ChangeNotifier {
bool isDoctorProfileLoading = false; bool isDoctorProfileLoading = false;
bool isDoctorSearchByNameStarted = false; bool isDoctorSearchByNameStarted = false;
bool isLiveCareSchedule = false;
int initialSlotDuration = 0; int initialSlotDuration = 0;
LocationUtils locationUtils; LocationUtils locationUtils;
@ -44,12 +48,25 @@ class BookAppointmentsViewModel extends ChangeNotifier {
List<GetClinicsListResponseModel> clinicsList = []; List<GetClinicsListResponseModel> clinicsList = [];
List<GetClinicsListResponseModel> _filteredClinicsList = []; List<GetClinicsListResponseModel> _filteredClinicsList = [];
List<GetLiveCareClinicsResponseModel> liveCareClinicsList = [];
List<GetClinicsListResponseModel> get filteredClinicsList => _filteredClinicsList; List<GetClinicsListResponseModel> get filteredClinicsList => _filteredClinicsList;
List<DoctorsListResponseModel> doctorsList = []; List<DoctorsListResponseModel> doctorsList = [];
List<DoctorsListResponseModel> filteredDoctorList = [];
List<DoctorsListResponseModel> liveCareDoctorsList = [];
List<PatientDentalPlanEstimationResponseModel> patientDentalPlanEstimationList = [];
List<DentalChiefComplaintsListResponseModel> dentalChiefComplaintsList = [];
int totalTimeNeededForDentalProcedure = 0;
bool isContinueDentalPlan = false;
bool isChiefComplaintsListLoading = false;
int selectedChiefComplaintID = 0;
GetClinicsListResponseModel selectedClinic = GetClinicsListResponseModel(); GetClinicsListResponseModel selectedClinic = GetClinicsListResponseModel();
DoctorsListResponseModel selectedDoctor = DoctorsListResponseModel(); DoctorsListResponseModel selectedDoctor = DoctorsListResponseModel();
GetLiveCareClinicsResponseModel selectedLiveCareClinic = GetLiveCareClinicsResponseModel();
late DoctorsProfileResponseModel doctorsProfileResponseModel; late DoctorsProfileResponseModel doctorsProfileResponseModel;
@ -78,7 +95,21 @@ class BookAppointmentsViewModel extends ChangeNotifier {
bool shouldLoadSpecificClinic = false; bool shouldLoadSpecificClinic = false;
String? currentlySelectedHospitalFromRegionFlow; String? currentlySelectedHospitalFromRegionFlow;
BookAppointmentsViewModel({required this.bookAppointmentsRepo, required this.errorHandlerService, required this.navigationService, required this.myAppointmentsViewModel, required this.locationUtils}) {; ///variables for doctor filter
List<String> searchedRegionList = [];
List<String> facilityList = ["hmgHospitals", "hmcMedicalClinic"];
List<String> searchedHospitalList = [];
List<PatientDoctorAppointmentList>
searchedPatientDoctorAppointmentHospitalsList = [];
List<String> searchedClinicList = [];
PatientDoctorAppointmentList? selectedHospitalForFilters;
List<String>? selectedFacilityForFilters = [], selectedRegionForFilters = [];
String? selectedClinicForFilters;
bool applyFilters = false;
BookAppointmentsViewModel(
{required this.bookAppointmentsRepo, required this.errorHandlerService, required this.navigationService, required this.myAppointmentsViewModel, required this.locationUtils}) {
initBookAppointmentViewModel(); initBookAppointmentViewModel();
} }
@ -101,8 +132,15 @@ class BookAppointmentsViewModel extends ChangeNotifier {
isClinicsListLoading = true; isClinicsListLoading = true;
isDoctorsListLoading = true; isDoctorsListLoading = true;
isDoctorProfileLoading = true; isDoctorProfileLoading = true;
isLiveCareSchedule = false;
currentlySelectedHospitalFromRegionFlow = null;
clinicsList.clear(); clinicsList.clear();
doctorsList.clear(); doctorsList.clear();
liveCareClinicsList.clear();
patientDentalPlanEstimationList.clear();
dentalChiefComplaintsList.clear();
isContinueDentalPlan = false;
isChiefComplaintsListLoading = true;
// getLocation(); // getLocation();
notifyListeners(); notifyListeners();
} }
@ -154,6 +192,31 @@ class BookAppointmentsViewModel extends ChangeNotifier {
notifyListeners(); notifyListeners();
} }
setIsLiveCareSchedule(bool value) {
isLiveCareSchedule = value;
notifyListeners();
}
setLiveCareSelectedClinic(GetLiveCareClinicsResponseModel clinic) {
selectedLiveCareClinic = clinic;
notifyListeners();
}
setIsContinueDentalPlan(bool value) {
isContinueDentalPlan = value;
notifyListeners();
}
setIsChiefComplaintsListLoading(bool value) {
isChiefComplaintsListLoading = value;
notifyListeners();
}
setSelectedChiefComplaintID(int id) {
selectedChiefComplaintID = id;
notifyListeners();
}
void onTabChanged(int index) { void onTabChanged(int index) {
selectedTabIndex = index; selectedTabIndex = index;
notifyListeners(); notifyListeners();
@ -161,10 +224,11 @@ class BookAppointmentsViewModel extends ChangeNotifier {
/// this function will decide which clinic api to be called /// this function will decide which clinic api to be called
/// either api for region flow or the select clinic api /// either api for region flow or the select clinic api
Future<void> getClinics() async Future<void> getClinics() async {
{ if (shouldLoadSpecificClinic) {
if(shouldLoadSpecificClinic) {
getRegionSelectedClinics(); getRegionSelectedClinics();
} else if (isLiveCareSchedule) {
getLiveCareScheduleClinics();
} else { } else {
getAllClinics(); getAllClinics();
} }
@ -191,12 +255,58 @@ class BookAppointmentsViewModel extends ChangeNotifier {
); );
} }
Future<void> getLiveCareScheduleClinics({Function(dynamic)? onSuccess, Function(String)? onError}) async {
liveCareClinicsList.clear();
final result = await bookAppointmentsRepo.getLiveCareScheduleClinics(_appState.getAuthenticatedUser()!.age!, _appState.getAuthenticatedUser()!.gender!);
result.fold(
(failure) async => await errorHandlerService.handleError(failure: failure),
(apiResponse) {
if (apiResponse.messageStatus == 2) {
// dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
} else if (apiResponse.messageStatus == 1) {
liveCareClinicsList = apiResponse.data!;
isClinicsListLoading = false;
initializeFilteredList();
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse);
}
}
},
);
}
Future<void> getLiveCareDoctorsList({Function(dynamic)? onSuccess, Function(String)? onError}) async {
doctorsList.clear();
final result =
await bookAppointmentsRepo.getLiveCareDoctorsList(selectedLiveCareClinic.serviceID!, _appState.getAuthenticatedUser()!.age!, _appState.getAuthenticatedUser()!.gender!, onError: onError);
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) {
liveCareDoctorsList = apiResponse.data!;
isDoctorsListLoading = false;
// initializeFilteredList();
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse);
}
}
},
);
}
//TODO: Make the API dynamic with parameters for ProjectID, isNearest, languageID, doctorId, doctorName //TODO: Make the API dynamic with parameters for ProjectID, isNearest, languageID, doctorId, doctorName
Future<void> getDoctorsList( Future<void> getDoctorsList({int projectID = 0, bool isNearest = false, int doctorId = 0, String doctorName = "", Function(dynamic)? onSuccess, Function(String)? onError}) async {
{int projectID = 0, bool isNearest = false, int doctorId = 0, String doctorName = "", isContinueDentalPlan = false, Function(dynamic)? onSuccess, Function(String)? onError}) async {
doctorsList.clear(); doctorsList.clear();
projectID = currentlySelectedHospitalFromRegionFlow != null?int.parse(currentlySelectedHospitalFromRegionFlow!):projectID; projectID = currentlySelectedHospitalFromRegionFlow != null ? int.parse(currentlySelectedHospitalFromRegionFlow!) : projectID;
final result = await bookAppointmentsRepo.getDoctorsList(selectedClinic.clinicID ?? 0, projectID, isNearest, doctorId, doctorName); final result = await bookAppointmentsRepo.getDoctorsList(selectedClinic.clinicID ?? 0, projectID, isNearest, doctorId, doctorName, isContinueDentalPlan: isContinueDentalPlan);
result.fold( result.fold(
(failure) async { (failure) async {
@ -207,8 +317,11 @@ class BookAppointmentsViewModel extends ChangeNotifier {
// dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {}); // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
} else if (apiResponse.messageStatus == 1) { } else if (apiResponse.messageStatus == 1) {
doctorsList = apiResponse.data!; doctorsList = apiResponse.data!;
filteredDoctorList = doctorsList;
isDoctorsListLoading = false; isDoctorsListLoading = false;
initializeFilteredList(); initializeFilteredList();
clearSearchFilters();
getFiltersFromDoctorList();
notifyListeners(); notifyListeners();
if (onSuccess != null) { if (onSuccess != null) {
onSuccess(apiResponse); onSuccess(apiResponse);
@ -218,6 +331,42 @@ class BookAppointmentsViewModel extends ChangeNotifier {
); );
} }
Future<void> getMappedDoctors(
{int projectID = 0, bool isNearest = false, int doctorId = 0, String doctorName = "", isContinueDentalPlan = false, Function(dynamic)? onSuccess, Function(String)? onError}) async {
filteredHospitalList = null;
hospitalList = null;
isRegionListLoading = true;
notifyListeners();
projectID = currentlySelectedHospitalFromRegionFlow != null ? int.parse(currentlySelectedHospitalFromRegionFlow!) : projectID;
final result = await bookAppointmentsRepo.getDoctorsList(selectedClinic.clinicID ?? 0, projectID, isNearest, doctorId, doctorName);
result.fold(
(failure) async {
onError!("No doctors found for the search criteria".needTranslation);
},
(apiResponse) async {
if (apiResponse.messageStatus == 2) {
// dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
} else if (apiResponse.messageStatus == 1) {
var doctorList = apiResponse.data!;
hospitalList = await DoctorMapper.getMappedDoctor(
doctorList,
isArabic: _appState.isArabic(),
lat: _appState.userLat,
long: _appState.userLong,
);
var isLocationEnabled = (_appState.userLat != 0) && (_appState.userLong != 0);
hospitalList = await DoctorMapper.sortList(isLocationEnabled, hospitalList!);
isRegionListLoading = false;
filteredHospitalList = hospitalList;
notifyListeners();
}
},
);
}
Future<void> getDoctorProfile({Function(dynamic)? onSuccess, Function(String)? onError}) async { Future<void> getDoctorProfile({Function(dynamic)? onSuccess, Function(String)? onError}) async {
final result = await bookAppointmentsRepo.getDoctorProfile(selectedDoctor.clinicID ?? 0, selectedDoctor.projectID ?? 0, selectedDoctor.doctorID ?? 0, onError: onError); final result = await bookAppointmentsRepo.getDoctorProfile(selectedDoctor.clinicID ?? 0, selectedDoctor.projectID ?? 0, selectedDoctor.doctorID ?? 0, onError: onError);
@ -238,7 +387,6 @@ class BookAppointmentsViewModel extends ChangeNotifier {
); );
} }
//TODO: Handle the cases for LiveCare Schedule
Future<void> getDoctorFreeSlots({bool isBookingForLiveCare = false, Function(dynamic)? onSuccess, Function(String)? onError}) async { Future<void> getDoctorFreeSlots({bool isBookingForLiveCare = false, Function(dynamic)? onSuccess, Function(String)? onError}) async {
docFreeSlots.clear(); docFreeSlots.clear();
DateTime date; DateTime date;
@ -246,7 +394,14 @@ class BookAppointmentsViewModel extends ChangeNotifier {
final DateFormat dateFormatter = DateFormat('yyyy-MM-dd'); final DateFormat dateFormatter = DateFormat('yyyy-MM-dd');
Map<DateTime, List> _eventsParsed; Map<DateTime, List> _eventsParsed;
final result = await bookAppointmentsRepo.getDoctorFreeSlots(selectedDoctor.clinicID ?? 0, selectedDoctor.projectID ?? 0, selectedDoctor.doctorID ?? 0, isBookingForLiveCare, onError: onError); final result = await bookAppointmentsRepo.getDoctorFreeSlots(
selectedDoctor.clinicID ?? 0,
selectedDoctor.projectID ?? 0,
selectedDoctor.doctorID ?? 0,
isBookingForLiveCare,
continueDentalPlan: isContinueDentalPlan,
onError: onError,
);
result.fold( result.fold(
(failure) async { (failure) async {
@ -281,6 +436,50 @@ class BookAppointmentsViewModel extends ChangeNotifier {
); );
} }
Future<void> getLiveCareDoctorFreeSlots({bool isBookingForLiveCare = false, Function(dynamic)? onSuccess, Function(String)? onError}) async {
docFreeSlots.clear();
DateTime date;
final DateFormat formatter = DateFormat('HH:mm');
final DateFormat dateFormatter = DateFormat('yyyy-MM-dd');
Map<DateTime, List> _eventsParsed;
final result = await bookAppointmentsRepo.getLiveCareDoctorFreeSlots(
selectedDoctor.clinicID ?? 0, selectedLiveCareClinic.serviceID ?? 0, selectedDoctor.projectID ?? 0, selectedDoctor.doctorID ?? 0, isBookingForLiveCare,
onError: onError);
result.fold(
(failure) async {
print(failure);
},
(apiResponse) {
if (apiResponse.messageStatus == 2) {
onError!(apiResponse.errorMessage ?? "Unknown error occurred");
// dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
} else if (apiResponse.messageStatus == 1) {
if (apiResponse.data['PatientER_DoctorFreeSlots'] == null || apiResponse.data['PatientER_DoctorFreeSlots'].isEmpty) {
onError!("No free slots available".tr());
return;
}
initialSlotDuration = apiResponse.data["InitialSlotDuration"];
freeSlotsResponse = apiResponse.data['PatientER_DoctorFreeSlots'];
freeSlotsResponse.forEach((element) {
// date = (isLiveCareSchedule != null && isLiveCareSchedule)
// ? DateUtil.convertStringToDate(element)
// :
date = DateUtil.convertStringToDateSaudiTimezone(element, int.parse(selectedDoctor.projectID.toString()));
slotsList.add(FreeSlot(date, ['slot']));
docFreeSlots.add(TimeSlot(isoTime: formatter.format(date), start: new DateTime(date.year, date.month, date.day, 0, 0, 0, 0), end: date, vidaDate: element));
});
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse);
}
}
},
);
}
Future<void> cancelAppointment({required PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel, Function(dynamic)? onSuccess, Function(String)? onError}) async { Future<void> cancelAppointment({required PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel, Function(dynamic)? onSuccess, Function(String)? onError}) async {
final result = await bookAppointmentsRepo.cancelAppointment(patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel); final result = await bookAppointmentsRepo.cancelAppointment(patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel);
@ -300,18 +499,9 @@ class BookAppointmentsViewModel extends ChangeNotifier {
); );
} }
//TODO: Handle the cases for LiveCare Schedule, Dental & Laser Clinics //TODO: Handle the cases for Dental & Laser Clinics
Future<void> insertSpecificAppointment( Future<void> insertSpecificAppointment(
{ {String? procedureID,
// required int docID,
// required int clinicID,
// required int projectID,
// required String selectedTime,
// required String selectedDate,
// required int initialSlotDuration,
// required int genderID,
// required int userAge,
String? procedureID,
num? testTypeEnum, num? testTypeEnum,
num? testProcedureEnum, num? testProcedureEnum,
int? invoiceNumber, int? invoiceNumber,
@ -413,28 +603,123 @@ class BookAppointmentsViewModel extends ChangeNotifier {
); );
} }
Future<void> insertSpecificAppointmentForLiveCare({Function(dynamic p1)? onSuccess, Function(dynamic p1)? onError}) async {
_appState = getIt<AppState>();
final result = await bookAppointmentsRepo.insertSpecificAppointmentForLiveCare(
docID: selectedDoctor.doctorID!,
clinicID: selectedDoctor.clinicID!,
projectID: selectedDoctor.projectID!,
serviceID: selectedLiveCareClinic.serviceID!,
selectedDate: selectedAppointmentDate,
selectedTime: selectedAppointmentTime,
initialSlotDuration: initialSlotDuration,
genderID: _appState.getAuthenticatedUser()!.gender!,
userAge: _appState.getAuthenticatedUser()!.age!,
onError: onError);
result.fold(
(failure) async {
print(failure);
},
(apiResponse) {
if (apiResponse.messageStatus == 2) {
// onError!(apiResponse);
LoadingUtils.hideFullScreenLoader();
showCommonBottomSheetWithoutHeight(
title: LocaleKeys.notice.tr(context: navigationService.navigatorKey.currentContext!),
navigationService.navigatorKey.currentContext!,
child: Utils.getWarningWidget(
loadingText: apiResponse.data["ErrorEndUserMessage"],
isShowActionButtons: true,
onCancelTap: () {
navigationService.pop();
},
onConfirmTap: () async {
navigationService.pop();
PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel = PatientAppointmentHistoryResponseModel(
appointmentNo: apiResponse.data["SameClinicApptList"][0]['AppointmentNo'],
clinicID: apiResponse.data["SameClinicApptList"][0]['ClinicID'],
projectID: apiResponse.data["SameClinicApptList"][0]['ProjectID'],
endDate: apiResponse.data["SameClinicApptList"][0]['EndTime'],
startTime: apiResponse.data["SameClinicApptList"][0]['StartTime'],
doctorID: apiResponse.data["SameClinicApptList"][0]['DoctorID'],
isLiveCareAppointment: apiResponse.data["SameClinicApptList"][0]['IsLiveCareAppointment'],
originalClinicID: 0,
originalProjectID: 0,
appointmentDate: apiResponse.data["SameClinicApptList"][0]['AppointmentDate'],
);
showCommonBottomSheet(navigationService.navigatorKey.currentContext!,
child: Utils.getLoadingWidget(loadingText: "Cancelling your previous appointment....".needTranslation),
callBackFunc: (str) {},
title: "",
height: ResponsiveExtension.screenHeight * 0.3,
isCloseButtonVisible: false,
isDismissible: false,
isFullScreen: false);
await cancelAppointment(patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel).then((val) async {
navigationService.pop();
Future.delayed(Duration(milliseconds: 50)).then((value) async {});
LoadingUtils.showFullScreenLoader(barrierDismissible: true, isSuccessDialog: false, loadingText: "Booking your appointment...".needTranslation);
await insertSpecificAppointment(
onError: (err) {},
onSuccess: (apiResp) async {
LoadingUtils.hideFullScreenLoader();
await Future.delayed(Duration(milliseconds: 50)).then((value) async {
LoadingUtils.showFullScreenLoader(barrierDismissible: true, isSuccessDialog: true, loadingText: LocaleKeys.appointmentSuccess.tr());
await Future.delayed(Duration(milliseconds: 4000)).then((value) {
LoadingUtils.hideFullScreenLoader();
Navigator.pushAndRemoveUntil(
navigationService.navigatorKey.currentContext!,
CustomPageRoute(
page: LandingNavigation(),
),
(r) => false);
});
});
});
});
}),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
} else if (apiResponse.messageStatus == 1) {
if (apiResponse.data == null || apiResponse.data!.isEmpty) {
onError!("No free slots available".tr());
return;
}
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse);
}
}
},
);
}
Future<void> getRegionMappedProjectList() async { Future<void> getRegionMappedProjectList() async {
isRegionListLoading = true; isRegionListLoading = true;
notifyListeners(); notifyListeners();
final result = await bookAppointmentsRepo.getProjectList(); final result = await bookAppointmentsRepo.getProjectList();
result.fold( result.fold(
(failure) async => (failure) async => await errorHandlerService.handleError(failure: failure),
await errorHandlerService.handleError(failure: failure), (apiResponse) async {
(apiResponse) async {
if (apiResponse.messageStatus == 2) { if (apiResponse.messageStatus == 2) {
// dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {}); // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
} else if (apiResponse.messageStatus == 1) { } else if (apiResponse.messageStatus == 1) {
var projectList = apiResponse.data!; var projectList = apiResponse.data!;
hospitalList = await DoctorMapper.getMappedHospitals(projectList, hospitalList = await DoctorMapper.getMappedHospitals(
projectList,
isArabic: _appState.isArabic(), isArabic: _appState.isArabic(),
lat: _appState.userLat, lat: _appState.userLat,
lng: _appState.userLong, lng: _appState.userLong,
); );
var isLocationEnabled = (_appState.userLat != 0) && (_appState.userLong != 0); var isLocationEnabled = (_appState.userLat != 0) && (_appState.userLong != 0);
hospitalList = hospitalList = await DoctorMapper.sortList(isLocationEnabled, hospitalList!);
await DoctorMapper.sortList(isLocationEnabled, hospitalList!);
isRegionListLoading = false; isRegionListLoading = false;
filteredHospitalList = hospitalList; filteredHospitalList = hospitalList;
@ -450,22 +735,21 @@ class BookAppointmentsViewModel extends ChangeNotifier {
} }
void filterHospitalListByString(String? value, String? selectedRegionId, bool isHMG) { void filterHospitalListByString(String? value, String? selectedRegionId, bool isHMG) {
if(value ==null || value.isEmpty){ if (value == null || value.isEmpty) {
filteredHospitalList = hospitalList; filteredHospitalList = hospitalList;
} else { } else {
filteredHospitalList = RegionList(); filteredHospitalList = RegionList();
var list = isHMG var list = isHMG ? hospitalList?.registeredDoctorMap![selectedRegionId]!.hmgDoctorList : hospitalList?.registeredDoctorMap![selectedRegionId]!.hmcDoctorList;
? hospitalList?.registeredDoctorMap![selectedRegionId]!.hmgDoctorList
: hospitalList?.registeredDoctorMap![selectedRegionId]!.hmcDoctorList;
if(list != null && list.isEmpty){ notifyListeners(); return;} if (list != null && list.isEmpty) {
notifyListeners();
return;
}
var filteredList = list!.where((element) => var filteredList = list!.where((element) => element.filterName!.toLowerCase().contains(value.toLowerCase())).toList();
element.filterName!.toLowerCase().contains(value.toLowerCase())
).toList();
var regionData = PatientDoctorAppointmentListByRegion(); var regionData = PatientDoctorAppointmentListByRegion();
if(isHMG){ if (isHMG) {
regionData.hmgDoctorList = filteredList; regionData.hmgDoctorList = filteredList;
regionData.hmgSize = filteredList.length; regionData.hmgSize = filteredList.length;
} else { } else {
@ -473,9 +757,7 @@ class BookAppointmentsViewModel extends ChangeNotifier {
regionData.hmcSize = filteredList.length; regionData.hmcSize = filteredList.length;
} }
filteredHospitalList?.registeredDoctorMap = { filteredHospitalList?.registeredDoctorMap = {selectedRegionId!: regionData};
selectedRegionId! : regionData
};
} }
notifyListeners(); notifyListeners();
} }
@ -498,12 +780,12 @@ class BookAppointmentsViewModel extends ChangeNotifier {
currentlySelectedHospitalFromRegionFlow = mainProjectID; currentlySelectedHospitalFromRegionFlow = mainProjectID;
} }
Future<void> getRegionSelectedClinics() async{ Future<void> getRegionSelectedClinics() async {
final result = await bookAppointmentsRepo.getClinicsWithRespectToClinicId(currentlySelectedHospitalFromRegionFlow??""); final result = await bookAppointmentsRepo.getClinicsWithRespectToClinicId(currentlySelectedHospitalFromRegionFlow ?? "");
result.fold( result.fold(
(failure) async => await errorHandlerService.handleError(failure: failure), (failure) async => await errorHandlerService.handleError(failure: failure),
(apiResponse) { (apiResponse) {
if (apiResponse.messageStatus == 2) { if (apiResponse.messageStatus == 2) {
// dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {}); // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
} else if (apiResponse.messageStatus == 1) { } else if (apiResponse.messageStatus == 1) {
@ -515,12 +797,224 @@ class BookAppointmentsViewModel extends ChangeNotifier {
}, },
); );
} }
void resetFilterList(){
void resetFilterList() {
filteredHospitalList = hospitalList; filteredHospitalList = hospitalList;
} }
void getLocation() {
void getLocation(){
locationUtils.getLocation(); locationUtils.getLocation();
} }
void clearSearchFilters() {
searchedRegionList.clear();
searchedHospitalList.clear();
searchedPatientDoctorAppointmentHospitalsList.clear();
searchedClinicList.clear();
notifyListeners();
}
void clearSelection() {
selectedFacilityForFilters = [];
selectedClinicForFilters = null;
selectedHospitalForFilters = null;
selectedRegionForFilters = [];
applyFilters = false;
notifyListeners();
}
void setSelections(
List<String>? selectedFacilityForFilters,
List<String>? selectedRegionForFilters,
String? selectedClinicForFilters,
PatientDoctorAppointmentList? selectedHospitalForFilters,
bool applyFilters) {
this.selectedFacilityForFilters = selectedFacilityForFilters;
this.selectedClinicForFilters = selectedClinicForFilters;
this.selectedHospitalForFilters = selectedHospitalForFilters;
this.selectedRegionForFilters = selectedRegionForFilters;
this.applyFilters = applyFilters;
notifyListeners();
}
void getFiltersFromDoctorList() {
doctorsList.forEach((element) {
if (!searchedRegionList
.contains(element.getRegionName(_appState.isArabic()))) {
searchedRegionList
.add(element.getRegionName(_appState.isArabic()) ?? "");
}
if (!searchedHospitalList.contains(element.projectName)) {
searchedPatientDoctorAppointmentHospitalsList
.add(PatientDoctorAppointmentList()
..filterName = element.projectName
..isHMC = element.isHMC
..distanceInKMs = "0");
searchedHospitalList.add(element.projectName ?? "");
}
if (!searchedClinicList.contains(element.clinicName)) {
searchedClinicList.add(element.clinicName ?? "");
}
});
}
void updateApplyFilters(bool applyFilters) {
this.applyFilters = applyFilters;
notifyListeners();
}
void setSelectedRegion(String region) {
if (selectedRegionForFilters?.contains(region) == true) {
selectedRegionForFilters?.remove(region);
} else {
selectedRegionForFilters?.add(region);
}
notifyListeners();
}
void setSelectedHospital(PatientDoctorAppointmentList? hospital) {
selectedHospitalForFilters = hospital;
notifyListeners();
}
void setSelectedFacilityForFilter(String facility) {
if (selectedFacilityForFilters?.contains(facility) == true) {
selectedFacilityForFilters?.remove(facility);
} else {
selectedFacilityForFilters?.add(facility);
}
notifyListeners();
}
void setSelectedClinicForFilter(String? clinic) {
selectedClinicForFilters = clinic;
notifyListeners();
}
bool isArabic() {
return _appState.isArabic();
}
List<DoctorsListResponseModel> getDoctorListAsPerSelection() {
if (!applyFilters) return doctorsList;
if ((selectedRegionForFilters?.isEmpty == true) &&
(selectedFacilityForFilters?.isEmpty == true) &&
selectedClinicForFilters == null &&
selectedHospitalForFilters == null) {
return doctorsList;
}
var list = doctorsList.where((element) {
var isInSelectedRegion = (selectedRegionForFilters?.isEmpty == true)
? true
: selectedRegionForFilters
?.any((region) => region == element.getRegionName(isArabic()));
var shouldApplyFacilityFilter =
(selectedFacilityForFilters?.isEmpty == true) ? false : true;
var isHMC = (selectedFacilityForFilters?.isEmpty == true)
? true
: selectedFacilityForFilters?.any((item) => item.contains("hmc"));
var isInSelectedClinic = (selectedClinicForFilters == null)
? true
: selectedClinicForFilters == element.clinicName;
var isInSelectedHospital = (selectedHospitalForFilters == null)
? true
: element.projectName == selectedHospitalForFilters?.filterName;
var facilityFilter = ((shouldApplyFacilityFilter == true) ? isHMC : true);
return (isInSelectedRegion ?? true) &&
(facilityFilter ?? true) &&
isInSelectedClinic &&
isInSelectedHospital;
}).toList();
return list;
}
void updateList() {
filteredDoctorList = getDoctorListAsPerSelection();
notifyListeners();
}
Future<void> getPatientDentalEstimation({required int projectID, Function(dynamic)? onSuccess, Function(String)? onError}) async {
patientDentalPlanEstimationList.clear();
totalTimeNeededForDentalProcedure = 0;
isContinueDentalPlan = false;
notifyListeners();
final result = await bookAppointmentsRepo.getPatientDentalEstimation(projectID: projectID);
result.fold(
(failure) async => await errorHandlerService.handleError(failure: failure),
(apiResponse) {
if (apiResponse.messageStatus == 2) {
onError!(apiResponse.errorMessage!);
// dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
} else if (apiResponse.messageStatus == 1) {
patientDentalPlanEstimationList = apiResponse.data!;
patientDentalPlanEstimationList.forEach((v) {
totalTimeNeededForDentalProcedure += (v.neededTime ?? 0);
});
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse);
}
}
},
);
}
Future<void> 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<void> 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);
}
}
},
);
}
} }

@ -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<String, dynamic> json) {
projectID = json['ProjectID'];
iD = json['ID'];
name = json['Name'];
nameN = json['NameN'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['ProjectID'] = this.projectID;
data['ID'] = this.iD;
data['Name'] = this.name;
data['NameN'] = this.nameN;
return data;
}
}

@ -59,6 +59,13 @@ class DoctorsListResponseModel {
int? virtualEmploymentType; int? virtualEmploymentType;
dynamic workingHours; dynamic workingHours;
dynamic vida3Id; dynamic vida3Id;
String? region;
String? regionArabic;
String? regionEnglish;
String? regionID;
String? projectBottomName;
String? projectTopName;
DoctorsListResponseModel( DoctorsListResponseModel(
{this.clinicID, {this.clinicID,
@ -120,7 +127,13 @@ class DoctorsListResponseModel {
this.transactionType, this.transactionType,
this.virtualEmploymentType, this.virtualEmploymentType,
this.workingHours, this.workingHours,
this.vida3Id}); this.vida3Id,
this.region,
this.regionArabic,
this.regionEnglish,
this.regionID,
this.projectBottomName,
this.projectTopName,});
DoctorsListResponseModel.fromJson(Map<String, dynamic> json) { DoctorsListResponseModel.fromJson(Map<String, dynamic> json) {
clinicID = json['ClinicID']; clinicID = json['ClinicID'];
@ -183,6 +196,10 @@ class DoctorsListResponseModel {
virtualEmploymentType = json['VirtualEmploymentType']; virtualEmploymentType = json['VirtualEmploymentType'];
workingHours = json['WorkingHours']; workingHours = json['WorkingHours'];
vida3Id = json['vida3Id']; vida3Id = json['vida3Id'];
regionArabic = json['RegionNameN'];
regionEnglish = json['RegionName'];
projectBottomName = json['ProjectNameBottom'];
projectTopName = json['ProjectNameTop'];
} }
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
@ -249,4 +266,21 @@ class DoctorsListResponseModel {
data['vida3Id'] = this.vida3Id; data['vida3Id'] = this.vida3Id;
return data; return data;
} }
String? getRegionName(bool isArabic) {
if (isArabic) {
return regionArabic;
}
return regionEnglish;
}
String getProjectCompleteName(){
return "${this.projectTopName} ${this.projectBottomName}";
}
String getProjectCompleteNameWithLocale({bool isArabic = false}) {
if (isArabic) {
return "${this.projectBottomName} ${this.projectTopName}";
}
return "${this.projectTopName} ${this.projectBottomName}";
}
} }

@ -12,8 +12,8 @@ class GetClinicsListResponseModel {
GetClinicsListResponseModel.fromJson(Map<String, dynamic> json) { GetClinicsListResponseModel.fromJson(Map<String, dynamic> json) {
clinicID = json['ClinicID']; clinicID = json['ClinicID'];
clinicDescription = json['ClinicDescription']; clinicDescription = json['ClinicDescription'] ?? "LiveCare Clinic";
clinicDescriptionN = json['ClinicDescriptionN']; clinicDescriptionN = json['ClinicDescriptionN'] ?? "LiveCare Clinic";
age = json['Age']; age = json['Age'];
gender = json['Gender']; gender = json['Gender'];
isLiveCareClinicAndOnline = json['IsLiveCareClinicAndOnline']; isLiveCareClinicAndOnline = json['IsLiveCareClinicAndOnline'];

@ -0,0 +1,33 @@
class GetLiveCareClinicsResponseModel {
int? clinicID;
int? serviceID;
int? projectID;
String? clinicDesc;
String? clinicDescN;
String? projectDesc;
String? projectDescN;
GetLiveCareClinicsResponseModel({this.clinicID, this.serviceID, this.projectID, this.clinicDesc, this.clinicDescN, this.projectDesc, this.projectDescN});
GetLiveCareClinicsResponseModel.fromJson(Map<String, dynamic> json) {
clinicID = json['ClinicID'];
serviceID = json['ServiceID'];
projectID = json['ProjectID'];
clinicDesc = json['ClinicDesc'] ?? "LiveCare Clinic";
clinicDescN = json['ClinicDescN'];
projectDesc = json['ProjectDesc'];
projectDescN = json['ProjectDescN'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['ClinicID'] = this.clinicID;
data['ServiceID'] = this.serviceID;
data['ProjectID'] = this.projectID;
data['ClinicDesc'] = this.clinicDesc;
data['ClinicDescN'] = this.clinicDescN;
data['ProjectDesc'] = this.projectDesc;
data['ProjectDescN'] = this.projectDescN;
return data;
}
}

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

@ -0,0 +1,110 @@
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/features/book_appointments/models/resp_models/doctors_list_response_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/doctor_list_api_response.dart' show PatientDoctorAppointmentList;
class DoctorFilterViewModel extends ChangeNotifier{
late AppState appState;
DoctorFilterViewModel(){
appState = getIt();
}
List<String> searchedRegionList = [];
List<String> facilityList = ["hmgHospitals", "hmcMedicalClinic"];
List<String> searchedHospitalList = [];
List<PatientDoctorAppointmentList>
searchedPatientDoctorAppointmentHospitalsList = [];
List<String> searchedClinicList = [];
PatientDoctorAppointmentList? selectedHospitalForFilters;
List<String>? selectedFacilityForFilters = [] , selectedRegionForFilters = [];
String? selectedClinicForFilters;
bool applyFilters = false;
void clearSearchFilters() {
searchedRegionList.clear();
searchedHospitalList.clear();
searchedPatientDoctorAppointmentHospitalsList.clear();
searchedClinicList.clear();
notifyListeners();
}
void clearSelection() {
selectedFacilityForFilters = [];
selectedClinicForFilters = null;
selectedHospitalForFilters = null;
selectedRegionForFilters = [];
applyFilters = false;
notifyListeners();
}
void getFiltersFromDoctorList(List<DoctorsListResponseModel> doctorsList) {
doctorsList.forEach((element) {
if (!searchedRegionList
.contains(element.getRegionName(appState.isArabic()))) {
searchedRegionList
.add(element.getRegionName(appState.isArabic()) ?? "");
}
if (!searchedHospitalList.contains(element.projectName)) {
searchedPatientDoctorAppointmentHospitalsList
.add(PatientDoctorAppointmentList()
..filterName = element.projectName
..isHMC = element.isHMC
..distanceInKMs = "0");
searchedHospitalList.add(element.projectName ?? "");
}
if (!searchedClinicList.contains(element.clinicName)) {
searchedClinicList.add(element.clinicName ?? "");
}
});
}
void updateApplyFilters(bool applyFilters) {
this.applyFilters = applyFilters;
notifyListeners();
}
void setSelectedRegion(String region) {
if (selectedRegionForFilters?.contains(region) == true) {
selectedRegionForFilters?.remove(region);
} else {
selectedRegionForFilters?.add(region);
}
notifyListeners();
}
void setSelectedHospital(PatientDoctorAppointmentList? hospital) {
selectedHospitalForFilters = hospital;
notifyListeners();
}
void setSelectedFacilityForFilter(String facility) {
if (selectedFacilityForFilters?.contains(facility) == true) {
selectedFacilityForFilters?.remove(facility);
} else {
selectedFacilityForFilters?.add(facility);
}
notifyListeners();
}
void setSelectedClinicForFilter(String? clinic) {
selectedClinicForFilters = clinic;
notifyListeners();
}
void setSelections(
List<String>? selectedFacilityForFilters,
List<String>? selectedRegionForFilters,
String? selectedClinicForFilters,
PatientDoctorAppointmentList? selectedHospitalForFilters,
bool applyFilters) {
this.selectedFacilityForFilters = selectedFacilityForFilters;
this.selectedClinicForFilters = selectedClinicForFilters;
this.selectedHospitalForFilters = selectedHospitalForFilters;
this.selectedRegionForFilters = selectedRegionForFilters;
this.applyFilters = applyFilters;
notifyListeners();
}
}

@ -0,0 +1,206 @@
import 'dart:io';
import 'package:dartz/dartz.dart';
import 'package:hmg_patient_app_new/core/api/api_client.dart';
import 'package:hmg_patient_app_new/core/api_consts.dart';
import 'package:hmg_patient_app_new/core/common_models/generic_api_model.dart';
import 'package:hmg_patient_app_new/core/exceptions/api_failure.dart';
import 'package:hmg_patient_app_new/features/immediate_livecare/models/resp_models/get_livecare_immediate_clinics_response_model.dart';
import 'package:hmg_patient_app_new/features/immediate_livecare/models/resp_models/get_livecare_immediate_fees_response_model.dart';
import 'package:hmg_patient_app_new/features/immediate_livecare/models/resp_models/get_patient_livecare_history_response_model.dart';
import 'package:hmg_patient_app_new/services/logger_service.dart';
abstract class ImmediateLiveCareRepo {
Future<Either<Failure, GenericApiModel<List<GetLiveCareClinicListResponseModel>>>> getLiveCareImmediateClinicsList(int age, int genderID, {Function(dynamic)? onSuccess, Function(String)? onError});
Future<Either<Failure, GenericApiModel<LiveCareImmediateAppointmentFeesList>>> getLiveCareImmediateAppointmentFees(int age, int genderID, int serviceID,
{Function(dynamic)? onSuccess, Function(String)? onError});
Future<Either<Failure, GenericApiModel<dynamic>>> addNewCallRequestForImmediateLiveCare(
int age, int gender, int serviceID, String clientRequestID, int callTypeID, bool isPharma, String deviceToken, String voipToken,
{Function(dynamic)? onSuccess, Function(String)? onError});
Future<Either<Failure, GenericApiModel<List<PatientLiveCareHistory>>>> getPatientLiveCareHistory({Function(dynamic)? onSuccess, Function(String)? onError});
}
class ImmediateLiveCareRepoImp implements ImmediateLiveCareRepo {
final ApiClient apiClient;
final LoggerService loggerService;
ImmediateLiveCareRepoImp({required this.loggerService, required this.apiClient});
@override
Future<Either<Failure, GenericApiModel<List<GetLiveCareClinicListResponseModel>>>> getLiveCareImmediateClinicsList(int age, int genderID,
{Function(dynamic)? onSuccess, Function(String)? onError}) async {
Map<String, dynamic> mapDevice = {
"Age": age,
"Gender": genderID,
};
try {
GenericApiModel<List<GetLiveCareClinicListResponseModel>>? apiResponse;
Failure? failure;
await apiClient.post(
GET_LIVECARE_CLINICS,
body: mapDevice,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
onError!(error);
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
final list = response['PatientER_GetClinicsList'];
final clinicsList = list.map((item) => GetLiveCareClinicListResponseModel.fromJson(item as Map<String, dynamic>)).toList().cast<GetLiveCareClinicListResponseModel>();
apiResponse = GenericApiModel<List<GetLiveCareClinicListResponseModel>>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: null,
data: clinicsList,
);
} catch (e) {
failure = DataParsingFailure(e.toString());
}
},
);
if (failure != null) return Left(failure!);
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
return Right(apiResponse!);
} catch (e) {
return Left(UnknownFailure(e.toString()));
}
}
@override
Future<Either<Failure, GenericApiModel<LiveCareImmediateAppointmentFeesList>>> getLiveCareImmediateAppointmentFees(int age, int genderID, int serviceID,
{Function(dynamic)? onSuccess, Function(String)? onError}) async {
Map<String, dynamic> mapDevice = {
"Age": age,
"Gender": genderID,
"ServiceID": serviceID,
};
try {
GenericApiModel<LiveCareImmediateAppointmentFeesList>? apiResponse;
Failure? failure;
await apiClient.post(
GET_ER_APPOINTMENT_FEES,
body: mapDevice,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
onError!(error);
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
final respObject = response['GetERAppointmentFeesList'];
final liveCareFeesObj = LiveCareImmediateAppointmentFeesList.fromJson(respObject);
apiResponse = GenericApiModel<LiveCareImmediateAppointmentFeesList>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: null,
data: liveCareFeesObj,
);
} catch (e) {
failure = DataParsingFailure(e.toString());
}
},
);
if (failure != null) return Left(failure!);
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
return Right(apiResponse!);
} catch (e) {
return Left(UnknownFailure(e.toString()));
}
}
@override
Future<Either<Failure, GenericApiModel>> addNewCallRequestForImmediateLiveCare(
int age, int gender, int serviceID, String clientRequestID, int callTypeID, bool isPharma, String deviceToken, String voipToken,
{Function(dynamic p1)? onSuccess, Function(String p1)? onError}) async {
Map<String, dynamic> mapDevice = {
"IsPharmacy": isPharma,
"ErServiceID": serviceID,
"ClientRequestID": clientRequestID,
"DeviceToken": deviceToken,
"VoipToken": voipToken,
"IsFlutter": true,
"DeviceType": Platform.isIOS ? 'iOS' : 'Android',
"Age": age,
"Gender": gender,
"IsVoip": Platform.isIOS ? true : false,
"CallTypeID": callTypeID
};
try {
GenericApiModel<dynamic>? apiResponse;
Failure? failure;
await apiClient.post(
ADD_NEW_CALL_FOR_PATIENT_ER,
body: mapDevice,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
onError!(error);
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
apiResponse = GenericApiModel<dynamic>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: null,
data: true,
);
} catch (e) {
failure = DataParsingFailure(e.toString());
}
},
);
if (failure != null) return Left(failure!);
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
return Right(apiResponse!);
} catch (e) {
return Left(UnknownFailure(e.toString()));
}
}
@override
Future<Either<Failure, GenericApiModel<List<PatientLiveCareHistory>>>> getPatientLiveCareHistory({Function(dynamic)? onSuccess, Function(String)? onError}) async {
Map<String, dynamic> mapDevice = {};
try {
GenericApiModel<List<PatientLiveCareHistory>>? apiResponse;
Failure? failure;
await apiClient.post(
GET_LIVECARE_HISTORY,
body: mapDevice,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
onError!(error);
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
final list = response['ErRequestHistoryList'];
final liveCareHistoryList = list.map((item) => PatientLiveCareHistory.fromJson(item as Map<String, dynamic>)).toList().cast<PatientLiveCareHistory>();
apiResponse = GenericApiModel<List<PatientLiveCareHistory>>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: null,
data: liveCareHistoryList,
);
} catch (e) {
failure = DataParsingFailure(e.toString());
}
},
);
if (failure != null) return Left(failure!);
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
return Right(apiResponse!);
} catch (e) {
return Left(UnknownFailure(e.toString()));
}
}
}

@ -0,0 +1,162 @@
import 'package:flutter/cupertino.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/cache_consts.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/features/immediate_livecare/immediate_livecare_repo.dart';
import 'package:hmg_patient_app_new/features/immediate_livecare/models/resp_models/get_livecare_immediate_clinics_response_model.dart';
import 'package:hmg_patient_app_new/features/immediate_livecare/models/resp_models/get_livecare_immediate_fees_response_model.dart';
import 'package:hmg_patient_app_new/features/immediate_livecare/models/resp_models/get_patient_livecare_history_response_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart';
import 'package:hmg_patient_app_new/services/error_handler_service.dart';
import '../../services/navigation_service.dart';
class ImmediateLiveCareViewModel extends ChangeNotifier {
ImmediateLiveCareViewModel({
required this.immediateLiveCareRepo,
required this.errorHandlerService,
required this.navigationService,
required this.myAppointmentsViewModel,
});
ImmediateLiveCareRepo immediateLiveCareRepo;
ErrorHandlerService errorHandlerService;
final NavigationService navigationService;
MyAppointmentsViewModel myAppointmentsViewModel;
List<GetLiveCareClinicListResponseModel> immediateLiveCareClinicsList = [];
bool isImmediateLiveCareClinicsLoading = false;
int liveCareSelectedCallType = 0; // 1- Video, 2- Audio, 3- Phone
late GetLiveCareClinicListResponseModel immediateLiveCareSelectedClinic;
late LiveCareImmediateAppointmentFeesList liveCareImmediateAppointmentFeesList;
List<PatientLiveCareHistory> patientLiveCareHistoryList = [];
bool patientHasPendingLiveCareRequest = false;
late AppState _appState;
initImmediateLiveCare() {
_appState = getIt<AppState>();
immediateLiveCareClinicsList = [];
patientLiveCareHistoryList = [];
isImmediateLiveCareClinicsLoading = true;
patientHasPendingLiveCareRequest = false;
liveCareSelectedCallType = 0; // 1- Video, 2- Audio, 3- Phone
immediateLiveCareSelectedClinic = GetLiveCareClinicListResponseModel();
liveCareImmediateAppointmentFeesList = LiveCareImmediateAppointmentFeesList();
}
setLiveCareSelectedCallType(int value) {
liveCareSelectedCallType = value;
notifyListeners();
}
setImmediateLiveCareSelectedClinic(GetLiveCareClinicListResponseModel clinic) {
immediateLiveCareSelectedClinic = clinic;
notifyListeners();
}
Future<void> getLiveCareImmediateClinicsList({Function(dynamic)? onSuccess, Function(String)? onError}) async {
immediateLiveCareClinicsList.clear();
isImmediateLiveCareClinicsLoading = true;
notifyListeners();
final result = await immediateLiveCareRepo.getLiveCareImmediateClinicsList(_appState.getAuthenticatedUser()!.age!, _appState.getAuthenticatedUser()!.gender!);
result.fold(
(failure) async => await errorHandlerService.handleError(failure: failure),
(apiResponse) {
if (apiResponse.messageStatus == 2) {
// dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
} else if (apiResponse.messageStatus == 1) {
immediateLiveCareClinicsList = apiResponse.data!;
immediateLiveCareClinicsList.sort((a, b) => b.isOnline!.compareTo(a.isOnline!));
isImmediateLiveCareClinicsLoading = false;
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse);
}
}
},
);
}
Future<void> getLiveCareImmediateAppointmentFees({Function(dynamic)? onSuccess, Function(String)? onError}) async {
final result =
await immediateLiveCareRepo.getLiveCareImmediateAppointmentFees(_appState.getAuthenticatedUser()!.age!, _appState.getAuthenticatedUser()!.gender!, immediateLiveCareSelectedClinic.serviceID!);
result.fold(
(failure) async {
onError!(failure.message);
},
(apiResponse) {
if (apiResponse.messageStatus == 2) {
onError!(apiResponse.errorMessage ?? "Unknown error occurred");
// dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
} else if (apiResponse.messageStatus == 1) {
liveCareImmediateAppointmentFeesList = apiResponse.data!;
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse);
}
}
},
);
}
Future<void> addNewCallRequestForImmediateLiveCare(String transID, {Function(dynamic)? onSuccess, Function(String)? onError}) async {
final result = await immediateLiveCareRepo.addNewCallRequestForImmediateLiveCare(_appState.getAuthenticatedUser()!.age!, _appState.getAuthenticatedUser()!.gender!,
immediateLiveCareSelectedClinic.serviceID!, transID, liveCareSelectedCallType, false, _appState.deviceToken, await Utils.getStringFromPrefs(CacheConst.voipToken));
result.fold(
(failure) async {
onError!(failure.message);
},
(apiResponse) {
if (apiResponse.messageStatus == 2) {
onError!(apiResponse.errorMessage ?? "Unknown error occurred");
// dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
} else if (apiResponse.messageStatus == 1) {
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse);
}
}
},
);
}
Future<void> getPatientLiveCareHistory({Function(dynamic)? onSuccess, Function(String)? onError}) async {
final result = await immediateLiveCareRepo.getPatientLiveCareHistory();
result.fold(
(failure) async {
onError!(failure.message);
},
(apiResponse) {
if (apiResponse.messageStatus == 2) {
onError!(apiResponse.errorMessage ?? "Unknown error occurred");
// dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
} else if (apiResponse.messageStatus == 1) {
patientLiveCareHistoryList = apiResponse.data!;
if (patientLiveCareHistoryList.isNotEmpty) {
if (patientLiveCareHistoryList[0].callStatus! < 4) {
patientHasPendingLiveCareRequest = true;
} else {
patientHasPendingLiveCareRequest = false;
}
} else {
patientHasPendingLiveCareRequest = false;
}
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse);
}
}
},
);
}
}

@ -0,0 +1,97 @@
class GetLiveCareClinicListResponseModel {
int? iD;
int? serviceID;
String? serviceName;
String? serviceNameN;
int? clinicID;
int? age;
bool? isCheckAgeBelow;
int? gender;
bool? isActive;
String? createdOn;
String? createdBy;
int? isOnline;
bool? projectOutSA;
List<ShiftTimings>? shiftTimings;
GetLiveCareClinicListResponseModel(
{this.iD,
this.serviceID,
this.serviceName,
this.serviceNameN,
this.clinicID,
this.age,
this.isCheckAgeBelow,
this.gender,
this.isActive,
this.createdOn,
this.createdBy,
this.isOnline,
this.projectOutSA,
this.shiftTimings});
GetLiveCareClinicListResponseModel.fromJson(Map<String, dynamic> json) {
iD = json['ID'];
serviceID = json['ServiceID'];
serviceName = json['ServiceName'];
serviceNameN = json['ServiceNameN'];
clinicID = json['ClinicID'];
age = json['Age'];
isCheckAgeBelow = json['IsCheckAgeBelow'];
gender = json['Gender'];
isActive = json['IsActive'];
createdOn = json['CreatedOn'];
createdBy = json['CreatedBy'];
isOnline = json['IsOnline'];
projectOutSA = json['ProjectOutSA'];
if (json['ShiftTimings'] != null) {
shiftTimings = <ShiftTimings>[];
json['ShiftTimings'].forEach((v) {
shiftTimings!.add(new ShiftTimings.fromJson(v));
});
}
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = Map<String, dynamic>();
data['ID'] = this.iD;
data['ServiceID'] = this.serviceID;
data['ServiceName'] = this.serviceName;
data['ServiceNameN'] = this.serviceNameN;
data['ClinicID'] = this.clinicID;
data['Age'] = this.age;
data['IsCheckAgeBelow'] = this.isCheckAgeBelow;
data['Gender'] = this.gender;
data['IsActive'] = this.isActive;
data['CreatedOn'] = this.createdOn;
data['CreatedBy'] = this.createdBy;
data['IsOnline'] = this.isOnline;
data['ProjectOutSA'] = this.projectOutSA;
if (this.shiftTimings != null) {
data['ShiftTimings'] = this.shiftTimings!.map((v) => v.toJson()).toList();
}
return data;
}
}
class ShiftTimings {
String? endTime;
int? shiftID;
String? startTime;
ShiftTimings({this.endTime, this.shiftID, this.startTime});
ShiftTimings.fromJson(Map<String, dynamic> json) {
endTime = json['EndTime'];
shiftID = json['ShiftID'];
startTime = json['StartTime'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = Map<String, dynamic>();
data['EndTime'] = this.endTime;
data['ShiftID'] = this.shiftID;
data['StartTime'] = this.startTime;
return data;
}
}

@ -0,0 +1,37 @@
class LiveCareImmediateAppointmentFeesList {
String? amount;
String? companyName;
bool? isInsured;
bool? isShowInsuranceUpdateModule;
bool? isCash;
bool? isEligible;
String? tax;
String? total;
String? currency;
LiveCareImmediateAppointmentFeesList({this.amount, this.companyName, this.isInsured, this.isShowInsuranceUpdateModule, this.tax, this.total, this.currency});
LiveCareImmediateAppointmentFeesList.fromJson(Map<String, dynamic> json) {
amount = json['Amount'];
companyName = json['CompanyName'];
isInsured = json['IsInsured'];
isCash = json['IsCash'];
isEligible = json['IsEligible'];
isShowInsuranceUpdateModule = json['IsShowInsuranceUpdateModule'];
tax = json['Tax'];
total = json['Total'];
currency = json['currency'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['Amount'] = this.amount;
data['CompanyName'] = this.companyName;
data['IsInsured'] = this.isInsured;
data['IsShowInsuranceUpdateModule'] = this.isShowInsuranceUpdateModule;
data['Tax'] = this.tax;
data['Total'] = this.total;
data['currency'] = this.currency;
return data;
}
}

@ -0,0 +1,84 @@
class PatientLiveCareHistory {
String? appointmentNo;
String? arrivalTime;
num? callDuration;
int? callStatus;
String? clientRequestID;
String? doctorID;
String? doctorName;
String? doctorNameN;
String? doctorTitle;
String? exWaitingTime;
bool? isAppointmentHaveRating;
int? patCount;
int? projectID;
String? sArrivalTime;
int? serviceID;
String? stringCallStatus;
int? vCID;
int? watingtimeInteger;
PatientLiveCareHistory(
{this.appointmentNo,
this.arrivalTime,
this.callDuration,
this.callStatus,
this.clientRequestID,
this.doctorID,
this.doctorName,
this.doctorNameN,
this.doctorTitle,
this.exWaitingTime,
this.isAppointmentHaveRating,
this.patCount,
this.projectID,
this.sArrivalTime,
this.serviceID,
this.stringCallStatus,
this.vCID,
this.watingtimeInteger});
PatientLiveCareHistory.fromJson(Map<String, dynamic> json) {
appointmentNo = json['AppointmentNo'];
arrivalTime = json['ArrivalTime'];
callDuration = json['CallDuration'];
callStatus = json['CallStatus'];
clientRequestID = json['ClientRequestID'];
doctorID = json['DoctorID'];
doctorName = json['DoctorName'];
doctorNameN = json['DoctorNameN'];
doctorTitle = json['DoctorTitle'];
exWaitingTime = json['Ex_WaitingTime'];
isAppointmentHaveRating = json['IsAppointmentHaveRating'];
patCount = json['Pat_Count'];
projectID = json['ProjectID'];
sArrivalTime = json['SArrivalTime'];
serviceID = json['ServiceID'];
stringCallStatus = json['StringCallStatus'];
vCID = json['VC_ID'];
watingtimeInteger = json['WatingtimeInteger'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['AppointmentNo'] = this.appointmentNo;
data['ArrivalTime'] = this.arrivalTime;
data['CallDuration'] = this.callDuration;
data['CallStatus'] = this.callStatus;
data['ClientRequestID'] = this.clientRequestID;
data['DoctorID'] = this.doctorID;
data['DoctorName'] = this.doctorName;
data['DoctorNameN'] = this.doctorNameN;
data['DoctorTitle'] = this.doctorTitle;
data['Ex_WaitingTime'] = this.exWaitingTime;
data['IsAppointmentHaveRating'] = this.isAppointmentHaveRating;
data['Pat_Count'] = this.patCount;
data['ProjectID'] = this.projectID;
data['SArrivalTime'] = this.sArrivalTime;
data['ServiceID'] = this.serviceID;
data['StringCallStatus'] = this.stringCallStatus;
data['VC_ID'] = this.vCID;
data['WatingtimeInteger'] = this.watingtimeInteger;
return data;
}
}

@ -11,7 +11,7 @@ import 'package:hmg_patient_app_new/services/logger_service.dart';
abstract class InsuranceRepo { abstract class InsuranceRepo {
Future<Either<Failure, GenericApiModel<List<PatientInsuranceDetailsResponseModel>>>> getPatientInsuranceDetails(); Future<Either<Failure, GenericApiModel<List<PatientInsuranceDetailsResponseModel>>>> getPatientInsuranceDetails();
Future<Either<Failure, GenericApiModel<List<PatientInsuranceCardHistoryResponseModel>>>> getPatientInsuranceCardHistory({required String patientId}); Future<Either<Failure, GenericApiModel<List<PatientInsuranceCardHistoryResponseModel>>>> getPatientInsuranceCardHistory();
Future<Either<Failure, GenericApiModel<PatientInsuranceUpdateResponseModel>>> getPatientInsuranceDetailsForUpdate({required String patientId, required String identificationNo}); Future<Either<Failure, GenericApiModel<PatientInsuranceUpdateResponseModel>>> getPatientInsuranceDetailsForUpdate({required String patientId, required String identificationNo});
} }
@ -64,7 +64,7 @@ class InsuranceRepoImp implements InsuranceRepo {
} }
@override @override
Future<Either<Failure, GenericApiModel<List<PatientInsuranceCardHistoryResponseModel>>>> getPatientInsuranceCardHistory({required String patientId}) async { Future<Either<Failure, GenericApiModel<List<PatientInsuranceCardHistoryResponseModel>>>> getPatientInsuranceCardHistory() async {
Map<String, dynamic> mapDevice = {}; Map<String, dynamic> mapDevice = {};
try { try {

@ -12,6 +12,8 @@ class InsuranceViewModel extends ChangeNotifier {
bool isInsuranceDetailsLoading = false; bool isInsuranceDetailsLoading = false;
bool isInsuranceUpdateDetailsLoading = false; bool isInsuranceUpdateDetailsLoading = false;
bool isInsuranceDataToBeLoaded = true;
InsuranceRepo insuranceRepo; InsuranceRepo insuranceRepo;
ErrorHandlerService errorHandlerService; ErrorHandlerService errorHandlerService;
@ -23,13 +25,15 @@ class InsuranceViewModel extends ChangeNotifier {
InsuranceViewModel({required this.insuranceRepo, required this.errorHandlerService}); InsuranceViewModel({required this.insuranceRepo, required this.errorHandlerService});
initInsuranceProvider() { initInsuranceProvider() {
patientInsuranceList.clear(); if (isInsuranceDataToBeLoaded) {
patientInsuranceList.clear();
isInsuranceLoading = true;
getPatientInsuranceDetails();
}
patientInsuranceCardHistoryList.clear(); patientInsuranceCardHistoryList.clear();
isInsuranceLoading = true;
isInsuranceHistoryLoading = true; isInsuranceHistoryLoading = true;
isInsuranceDetailsLoading = true; isInsuranceDetailsLoading = true;
isInsuranceUpdateDetailsLoading = true; isInsuranceUpdateDetailsLoading = true;
getPatientInsuranceDetails();
notifyListeners(); notifyListeners();
} }
@ -48,13 +52,21 @@ class InsuranceViewModel extends ChangeNotifier {
notifyListeners(); notifyListeners();
} }
setIsInsuranceDataToBeLoaded(bool val) {
isInsuranceDataToBeLoaded = val;
notifyListeners();
}
Future<void> getPatientInsuranceDetails({Function(dynamic)? onSuccess, Function(String)? onError}) async { Future<void> getPatientInsuranceDetails({Function(dynamic)? onSuccess, Function(String)? onError}) async {
if (!isInsuranceDataToBeLoaded) return;
final result = await insuranceRepo.getPatientInsuranceDetails(); final result = await insuranceRepo.getPatientInsuranceDetails();
result.fold( result.fold(
// (failure) async => await errorHandlerService.handleError(failure: failure), // (failure) async => await errorHandlerService.handleError(failure: failure),
(failure) async { (failure) async {
isInsuranceLoading = false; isInsuranceLoading = false;
notifyListeners();
}, },
(apiResponse) { (apiResponse) {
if (apiResponse.messageStatus == 2) { if (apiResponse.messageStatus == 2) {
@ -62,6 +74,7 @@ class InsuranceViewModel extends ChangeNotifier {
} else if (apiResponse.messageStatus == 1) { } else if (apiResponse.messageStatus == 1) {
patientInsuranceList = apiResponse.data!; patientInsuranceList = apiResponse.data!;
isInsuranceLoading = false; isInsuranceLoading = false;
isInsuranceDataToBeLoaded = false;
notifyListeners(); notifyListeners();
if (onSuccess != null) { if (onSuccess != null) {
onSuccess(apiResponse); onSuccess(apiResponse);
@ -72,10 +85,13 @@ class InsuranceViewModel extends ChangeNotifier {
} }
Future<void> getPatientInsuranceCardHistory({Function(dynamic)? onSuccess, Function(String)? onError}) async { Future<void> getPatientInsuranceCardHistory({Function(dynamic)? onSuccess, Function(String)? onError}) async {
final result = await insuranceRepo.getPatientInsuranceCardHistory(patientId: "1231755"); final result = await insuranceRepo.getPatientInsuranceCardHistory();
result.fold( result.fold(
(failure) async => await errorHandlerService.handleError(failure: failure), (failure) async {
isInsuranceHistoryLoading = false;
notifyListeners();
},
(apiResponse) { (apiResponse) {
if (apiResponse.messageStatus == 2) { if (apiResponse.messageStatus == 2) {
// dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {}); // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
@ -96,7 +112,10 @@ class InsuranceViewModel extends ChangeNotifier {
final result = await insuranceRepo.getPatientInsuranceDetailsForUpdate(patientId: patientID, identificationNo: identificationNo); final result = await insuranceRepo.getPatientInsuranceDetailsForUpdate(patientId: patientID, identificationNo: identificationNo);
result.fold( result.fold(
(failure) async => await errorHandlerService.handleError(failure: failure), (failure) async {
isInsuranceUpdateDetailsLoading = false;
notifyListeners();
},
(apiResponse) { (apiResponse) {
if (apiResponse.messageStatus == 2) { if (apiResponse.messageStatus == 2) {
// dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {}); // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});

@ -0,0 +1,7 @@
import 'package:flutter/material.dart';
class LabHistoryViewModel extends ChangeNotifier{
bool isGraphShowing = false;
}

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

@ -1,25 +1,74 @@
import 'dart:core';
import 'dart:math';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/common_models/data_points.dart';
import 'package:hmg_patient_app_new/core/utils/date_util.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart' show Utils;
import 'package:hmg_patient_app_new/features/lab/lab_repo.dart'; import 'package:hmg_patient_app_new/features/lab/lab_repo.dart';
import 'package:hmg_patient_app_new/features/lab/models/resp_models/lab_result.dart';
import 'package:hmg_patient_app_new/features/lab/models/resp_models/patient_lab_orders_response_model.dart'; import 'package:hmg_patient_app_new/features/lab/models/resp_models/patient_lab_orders_response_model.dart';
import 'package:hmg_patient_app_new/presentation/lab/lab_results/lab_result_details.dart';
import 'package:hmg_patient_app_new/services/error_handler_service.dart'; import 'package:hmg_patient_app_new/services/error_handler_service.dart';
import 'package:hmg_patient_app_new/services/navigation_service.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart';
import 'package:intl/intl.dart' show DateFormat;
import 'package:logger/logger.dart';
class LabViewModel extends ChangeNotifier { class LabViewModel extends ChangeNotifier {
bool isLabOrdersLoading = false; bool isLabOrdersLoading = false;
bool isLabResultsLoading = false; bool isLabResultsLoading = false;
bool isLabResultByHospitalLoading = false;
bool isSpecialResultsLoading = false;
bool isGraphVisible = true;
bool shouldShowGraph = true;
LabRepo labRepo; LabRepo labRepo;
ErrorHandlerService errorHandlerService; ErrorHandlerService errorHandlerService;
NavigationService navigationService;
List<PatientLabOrdersResponseModel> patientLabOrders = []; List<PatientLabOrdersResponseModel> patientLabOrders = [];
List<PatientLabOrdersResponseModel> filteredLabOrders = []; List<PatientLabOrdersResponseModel> filteredLabOrders = [];
List<PatientLabOrdersResponseModel> tempLabOrdersList = []; List<PatientLabOrdersResponseModel> tempLabOrdersList = [];
String labSpecialResult = "";
PatientLabOrdersResponseModel? currentlySelectedPatientOrder;
List<LabResult> mainLabResultsByHospitals = [];
List<LabResult> mainLabResults = [];
List<DataPoint> mainGraphPoints = [];
List<DataPoint> filteredGraphValues = [];
List months = [
'Jan',
'Feb',
'Mar',
'April',
'May',
'Jun',
'July',
'Aug',
'Sep',
'Oct',
'Nov',
'Dec'
];
late List<String> _labSuggestionsList = []; late List<String> _labSuggestionsList = [];
List<String> get labSuggestions => _labSuggestionsList; List<String> get labSuggestions => _labSuggestionsList;
Set<TestDetails> uniqueTests = {}; Set<TestDetails> uniqueTests = {};
double maxY = 0.0;
double maxX = double.infinity;
LabViewModel({required this.labRepo, required this.errorHandlerService}); LabViewModel(
{required this.labRepo,
required this.errorHandlerService,
required this.navigationService});
initLabProvider() { initLabProvider() {
patientLabOrders.clear(); patientLabOrders.clear();
@ -31,10 +80,20 @@ class LabViewModel extends ChangeNotifier {
} }
Future<void> getPatientLabOrders({Function(dynamic)? onSuccess, Function(String)? onError}) async { Future<void> getPatientLabOrders({Function(dynamic)? onSuccess, Function(String)? onError}) async {
patientLabOrders.clear();
uniqueTests.clear();
uniqueTests = {};
notifyListeners();
final result = await labRepo.getPatientLabOrders(); final result = await labRepo.getPatientLabOrders();
result.fold( result.fold(
(failure) async => await errorHandlerService.handleError(failure: failure), (failure) async {
isLabOrdersLoading = false;
isLabResultsLoading = false;
notifyListeners();
},
// => await errorHandlerService.handleError(failure: failure),
(apiResponse) { (apiResponse) {
if (apiResponse.messageStatus == 2) { if (apiResponse.messageStatus == 2) {
// dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {}); // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
@ -80,13 +139,430 @@ class LabViewModel extends ChangeNotifier {
} }
getUniqueTestDescription() { getUniqueTestDescription() {
uniqueTests = {
uniqueTests = {
for (var item in patientLabOrders) for (var item in patientLabOrders)
if (item.testDetails != null) if (item.testDetails != null)
...?item.testDetails?.map<TestDetails>((test) => ...?item.testDetails?.map<TestDetails>((test) => TestDetails(
TestDetails(description: test.description.toString(), testCode: test.testCode.toString(), testID: test.testID, createdOn: item.createdOn)) testDescriptionEn: test.testDescriptionEn.toString(),
testDescriptionAr: test.testDescriptionAr.toString(),
description: test.description.toString(),
testCode: test.testCode.toString(),
testID: test.testID,
createdOn: item.createdOn,
model: item))
}; };
uniqueTests.forEach(print); }
Future<void> getPatientLabResultByHospital(
PatientLabOrdersResponseModel laborder) async {
isLabResultByHospitalLoading = true;
notifyListeners();
mainLabResultsByHospitals.clear;
final result = await labRepo.getPatientLabResultsByHospitals(laborder,
Utils.isVidaPlusProject(int.parse(laborder.projectID ?? "0")));
result.fold(
(failure) async {
isLabResultByHospitalLoading = false;
// await errorHandlerService.handleError(failure: failure);
},
(apiResponse) {
isLabResultByHospitalLoading = false;
if (apiResponse.messageStatus == 2) {
} else if (apiResponse.messageStatus == 1) {
mainLabResultsByHospitals = apiResponse.data ?? [];
notifyListeners();
}
},
);
}
Future<void> getPatientLabResult(PatientLabOrdersResponseModel laborder, String procedureName, String testDescription) async {
LoaderBottomSheet.showLoader();
mainLabResults.clear();
filteredGraphValues.clear();
maxY = double.negativeInfinity;
final result = await labRepo.getPatientLabResults(
laborder,
Utils.isVidaPlusProject(int.parse(laborder.projectID ?? "0")),
procedureName);
result.fold(
(failure) async {
LoaderBottomSheet.hideLoader();
await errorHandlerService.handleError(failure: failure);
},
(apiResponse) {
LoaderBottomSheet.hideLoader();
if (apiResponse.messageStatus == 2) {
} else if (apiResponse.messageStatus == 1) {
var sortedResponse = sortByFlagAndValue(apiResponse.data!);
var recentThree = sort(sortedResponse);
mainLabResults = recentThree;
double counter = 1;
recentThree.reversed.forEach((element) {
try {
var dateTime =
DateUtil.convertStringToDate(element.verifiedOnDateTime!);
var resultValue = double.parse(element.resultValue!);
var transformedValue = transformValueInRange(double.parse(element.resultValue!), element.calculatedResultFlag??"");
if (resultValue>maxY) {
maxY = resultValue;
maxX = maxY;
}
filteredGraphValues.add(DataPoint(
value: transformedValue,
actualValue:element.resultValue!,
label: formatDateAsMMYY(dateTime),
displayTime: resultDate(dateTime),
time: DateUtil.convertStringToDate(element.verifiedOnDateTime),
referenceValue: element.calculatedResultFlag ?? "",
));
counter++;
} catch (e) {}
});
LabResult recentResult = recentThree.first;
checkIfGraphShouldBeDisplayed(recentResult);
recentResult.verifiedOn = resultDate(DateUtil.convertStringToDate(recentResult.verifiedOnDateTime!));
// filteredGraphValues = [filteredGraphValues.first];
navigationService.push(MaterialPageRoute(
builder: (_) => LabResultDetails(recentLabResult: recentResult, testDescription: testDescription),
),
);
notifyListeners();
}
},
);
}
void checkIfGraphShouldBeDisplayed(LabResult recentResult){
shouldShowGraph = recentResult.checkIfGraphShouldBeDisplayed();
isGraphVisible = shouldShowGraph;
notifyListeners();
}
Future<void> getPatientSpecialResult(
PatientLabOrdersResponseModel laborder) async {
isSpecialResultsLoading = true;
labSpecialResult = "";
notifyListeners();
final result = await labRepo.getSpecialLabResult(
laborder,
Utils.isVidaPlusProject(int.parse(laborder.projectID ?? "0")),
);
result.fold(
(failure) async {
isSpecialResultsLoading = false;
notifyListeners();
// await errorHandlerService.handleError(failure: failure);
},
(apiResponse) {
isSpecialResultsLoading = false;
if (apiResponse.messageStatus == 2) {
} else if (apiResponse.messageStatus == 1) {
StringBuffer htmlbuffer = StringBuffer("");
apiResponse.data?.forEach((element) {
if(element.resultDataHTML != null && element.resultDataHTML?.isNotEmpty == true)
htmlbuffer.write("${element.resultDataHTML} <br/> <br/>");
});
labSpecialResult = htmlbuffer.toString();
notifyListeners();
}
notifyListeners();
},
);
}
String resultDate(DateTime date){
return '${date.day} ${months[date.month-1]},${date.year}';
}
double transformValueInRange(double inputValue, String flag) {
// Define range boundaries
double rangeStart, rangeEnd;
switch (flag) {
case'LCL':
case 'CL':
rangeStart = 0.0;
rangeEnd = 19.0;
break;
case 'L':
rangeStart = 20.0;
rangeEnd = 39.0;
break;
case 'N':
rangeStart = 40.0;
rangeEnd = 59.0;
break;
case 'H':
rangeStart = 60.0;
rangeEnd = 79.0;
break;
case 'HCH':
case 'CH':
rangeStart = 80.0;
rangeEnd = 100.0;
break;
default:
throw ArgumentError('Invalid flag: $flag');
}
// Clamp input value to 0-100 and map it to the range bounds
final clampedValue = inputValue.clamp(0.0, 100.0);
final normalizedValue = clampedValue / 100.0; // Normalize input to 0-1
// Map the normalized value to the target range bounds
final transformedValue = rangeStart + ((normalizedValue * (rangeEnd - rangeStart)));
debugPrint("the actual value is $inputValue");
debugPrint("the flag is $flag");
debugPrint("the transformed value is $transformedValue");
return transformedValue;
}
void getSelectedDateRange(DateTime? start, DateTime? end) {
maxY = double.negativeInfinity;
if(start == null && end == null) {
mainLabResults.forEach((element) {
final time = DateUtil.convertStringToDate(element.verifiedOnDateTime!);
try{
var resultValue = double.parse(element.resultValue!);
var transformedValue = transformValueInRange(double.parse(element.resultValue!), element.calculatedResultFlag??"");
if (resultValue > maxY) {
maxY = resultValue;
}
filteredGraphValues.add(DataPoint(
value: transformedValue,
actualValue: element.resultValue!,
label: formatDateAsMMYY(time),
displayTime: resultDate(time),
time: DateUtil.convertStringToDate(element.verifiedOnDateTime),
referenceValue: element.calculatedResultFlag ?? "",
));
}catch(e){
}
});
}else {
filteredGraphValues.clear();
mainLabResults.forEach((element) {
try {
var dateTime =
DateUtil.convertStringToDate(element.verifiedOnDateTime!);
var resultValue = double.parse(element.resultValue!);
var transformedValue = transformValueInRange(double.parse(element.resultValue!), element.calculatedResultFlag??"");
if (resultValue > maxY) {
maxY = resultValue;
}
if (start != null && end == null) {
if (dateTime.isAtSameMomentAs(start)) {
filteredGraphValues.add(DataPoint(
value: transformedValue,
actualValue: element.resultValue!,
label: formatDateAsMMYY(dateTime),
displayTime: resultDate(dateTime),
time:
DateUtil.convertStringToDate(element.verifiedOnDateTime),
referenceValue: element.calculatedResultFlag ?? ""));
}
} else if (start != null && end != null) {
if ((dateTime.isAfter(start)) && (dateTime.isBefore(end))) {
filteredGraphValues.add(DataPoint(
value: transformedValue,
actualValue: element.resultValue!,
label: formatDateAsMMYY(dateTime),
displayTime: resultDate(dateTime),
time:
DateUtil.convertStringToDate(element.verifiedOnDateTime),
referenceValue: element.calculatedResultFlag ?? ""));
}
}
} catch (e) {}
});
}
filteredGraphValues = sortFilteredList(filteredGraphValues).reversed.toList();
notifyListeners();
}
String formatDateAsMMYY(DateTime date) {
return '${months[date.month-1]}, ${date.year}';
}
List<LabResult> sortByFlagAndValue(List<LabResult> original) {
const priorityOrder = ['LCL', 'CL', 'L', 'N', 'H', 'CH', 'HCH'];
int getFlagPriority(String? flag) {
if (flag == null) return priorityOrder.length;
final index = priorityOrder.indexOf(flag);
return index == -1 ? priorityOrder.length : index;
}
double parseResultValue(String? value) {
if (value == null) return double.nan;
return double.tryParse(value) ?? double.nan;
}
final copy = List<LabResult>.from(original);
copy.sort((a, b) {
final aFlagPriority = getFlagPriority(a.calculatedResultFlag);
final bFlagPriority = getFlagPriority(b.calculatedResultFlag);
if (aFlagPriority != bFlagPriority) {
return aFlagPriority.compareTo(bFlagPriority);
}
final aValue = parseResultValue(a.resultValue);
final bValue = parseResultValue(b.resultValue);
return aValue.compareTo(bValue);
});
return copy;
}
List<LabResult> sort(List<LabResult> original) {
DateTime? parseVerifiedDate(String? raw) {
if (raw == null) return null;
final regex = RegExp(r'\/Date\((\d+)\)\/');
final match = regex.firstMatch(raw);
if (match != null) {
final millis = int.tryParse(match.group(1)!);
if (millis != null) {
return DateTime.fromMillisecondsSinceEpoch(millis);
}
}
return null;
}
final copy = List<LabResult>.from(original);
copy.sort((a, b) {
final aDate = DateUtil.convertStringToDate(a.verifiedOnDateTime);
final bDate = DateUtil.convertStringToDate(b.verifiedOnDateTime);
final now = DateTime.now();
if (aDate == now && bDate == now) return 0;
if (aDate == now) return 1;
if (bDate == now) return -1;
return bDate.compareTo(aDate); // descending
});
return copy.toList();
}
List<DataPoint> sortFilteredList(List<DataPoint> original) {
final copy = List<DataPoint>.from(original);
copy.sort((a, b) {
final aDate =a.time;
final bDate = a.time;
final now = DateTime.now();
if (aDate == now && bDate == now) return 0;
if (aDate == now) return 1;
if (bDate == now) return -1;
return bDate.compareTo(aDate); // descending
});
return copy.toList();
}
Color getColor(String flag) {
switch (flag) {
case 'LCL':
return AppColors.criticalLowAndHigh;
case 'CL':
return AppColors.criticalLowAndHigh;
case 'L':
return AppColors.highAndLow;
case 'N':
return AppColors.bgGreenColor;
case 'H':
return AppColors.highAndLow;
case 'CH':
return AppColors.criticalLowAndHigh;
case 'HCH':
return AppColors.criticalLowAndHigh;
default:
return Colors.grey;
}
}
String getFormattedDate(DateTime date){
return DateFormat('EEEE, dd MMMM. yyyy').format(date);
}
String getAssetUrlWRTResult(String refernceValue) {
switch (refernceValue) {
case 'CL':
case 'LCL':
return AppAssets.ic_critical_low_result;
case 'L':
return AppAssets.ic_low_result;
case 'N':
return AppAssets.ic_normal_result;
case 'H':
return AppAssets.ic_low_result;
case 'CH':
case 'HCH':
return AppAssets.ic_critical_low_result;
default:
return AppAssets.ic_normal_result;
}
}
bool getRotationWRTResult(String refernceValue) {
switch (refernceValue) {
case 'CL':
case 'LCL':
case 'L':
case 'N':
return false;
case 'H':
case 'CH':
case 'HCH':
return true;
default:
return true;
}
}
String getSeverityText(String refernceValue) {
switch (refernceValue) {
case 'N':
return "normal";
case 'L':
case 'H':
return "monitor";
case 'CL':
case 'LCL':
case 'CH':
case 'HCH':
return "attention";
default:
return "normal";
}
}
alterGraphVisibility(){
isGraphVisible = !isGraphVisible;
notifyListeners();
} }
} }

@ -0,0 +1,6 @@
enum Range{
WEEKLY,
LAST_MONTH,
LAST_6MONTH,
THIS_YEAR,
}

@ -0,0 +1,135 @@
class LabResult {
String? description;
dynamic femaleInterpretativeData;
int? gender;
int? lineItemNo;
dynamic maleInterpretativeData;
dynamic notes;
String? packageID;
int? patientID;
String? projectID;
String? referanceRange;
String? resultValue;
String? sampleCollectedOn;
String? sampleReceivedOn;
String? setupID;
dynamic superVerifiedOn;
String? testCode;
String? uOM;
String? verifiedOn;
String? verifiedOnDateTime;
String? changeResult;
String? calculatedResultFlag;
String? criticalHigh;
String? referenceHigh;
String? criticalLow;
String? referenceLow;
num? resultTypeID;
String? packageShortDescription;
LabResult(
{this.description,
this.femaleInterpretativeData,
this.gender,
this.lineItemNo,
this.maleInterpretativeData,
this.notes,
this.packageID,
this.patientID,
this.projectID,
this.referanceRange,
this.resultValue,
this.sampleCollectedOn,
this.sampleReceivedOn,
this.setupID,
this.superVerifiedOn,
this.testCode,
this.uOM,
this.verifiedOn,
this.calculatedResultFlag,
this.verifiedOnDateTime,
this.criticalHigh,
this.referenceHigh,
this.criticalLow,
this.referenceLow,
});
LabResult.fromJson(Map<String, dynamic> json) {
description = json['Description'];
femaleInterpretativeData = json['FemaleInterpretativeData'];
gender = json['Gender'];
lineItemNo = json['LineItemNo'];
maleInterpretativeData = json['MaleInterpretativeData'];
notes = json['Notes'];
packageID = json['PackageID'];
patientID = json['PatientID'];
projectID = json['ProjectID'];
referanceRange = json['ReferanceRange'];
resultValue = json['ResultValue'];
sampleCollectedOn = json['SampleCollectedOn'];
sampleReceivedOn = json['SampleReceivedOn'];
setupID = json['SetupID'];
superVerifiedOn = json['SuperVerifiedOn'];
testCode = json['TestCode'];
uOM = json['UOM'];
verifiedOn = json['VerifiedOn'];
verifiedOnDateTime = json['VerifiedOnDateTime'];
changeResult = json['ChangeResult'];
calculatedResultFlag = json['CalculatedResultFlag'];
criticalHigh = json['CriticalHigh'];
referenceHigh = json['ReferenceHigh'];
criticalLow = json['CriticalLow'];
referenceLow = json['ReferenceLow'];
packageShortDescription = json['PackageShortDescription'];
resultTypeID = json['ResultTypeID'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['Description'] = this.description;
data['FemaleInterpretativeData'] = this.femaleInterpretativeData;
data['Gender'] = this.gender;
data['LineItemNo'] = this.lineItemNo;
data['MaleInterpretativeData'] = this.maleInterpretativeData;
data['Notes'] = this.notes;
data['PackageID'] = this.packageID;
data['PatientID'] = this.patientID;
data['ProjectID'] = this.projectID;
data['ReferanceRange'] = this.referanceRange;
data['ResultValue'] = this.resultValue;
data['SampleCollectedOn'] = this.sampleCollectedOn;
data['SampleReceivedOn'] = this.sampleReceivedOn;
data['SetupID'] = this.setupID;
data['SuperVerifiedOn'] = this.superVerifiedOn;
data['TestCode'] = this.testCode;
data['UOM'] = this.uOM;
data['VerifiedOn'] = this.verifiedOn;
data['VerifiedOnDateTime'] = this.verifiedOnDateTime;
data['ChangeResult'] = this.changeResult;
data['CriticalHigh'] = this.criticalHigh;
data['ReferenceHigh'] = this.referenceHigh;
data['CriticalLow'] = this.criticalLow;
data['ReferenceLow'] = this.referenceLow;
return data;
}
bool checkIfGraphShouldBeDisplayed(){
if (resultTypeID == null) return false;
if (resultTypeID == 6) return false;
if (referanceRange == null || referanceRange == "" || referanceRange == "\n") return false;
bool isDigit = RegExp(r"\\d+").hasMatch("$resultValue");
if(isDigit) return true;
try {
num.parse(resultValue ?? "");
} catch (e) {
return false;
}
return true;
}
@override
String toString() {
return 'LabOrderResult(flag: $calculatedResultFlag, value: $resultValue, verifiedOn: $verifiedOnDateTime)';
}
}

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

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

@ -8,6 +8,7 @@ import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/features/medical_file/models/patient_medical_response_model.dart'; import 'package:hmg_patient_app_new/features/medical_file/models/patient_medical_response_model.dart';
import 'package:hmg_patient_app_new/features/medical_file/models/patient_sickleave_response_model.dart'; import 'package:hmg_patient_app_new/features/medical_file/models/patient_sickleave_response_model.dart';
import 'package:hmg_patient_app_new/features/medical_file/models/patient_vaccine_response_model.dart'; import 'package:hmg_patient_app_new/features/medical_file/models/patient_vaccine_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/services/logger_service.dart'; import 'package:hmg_patient_app_new/services/logger_service.dart';
import '../authentication/models/resp_models/authenticated_user_resp_model.dart'; import '../authentication/models/resp_models/authenticated_user_resp_model.dart';
@ -24,9 +25,19 @@ abstract class MedicalFileRepo {
Future<Either<Failure, GenericApiModel<dynamic>>> getPatientMedicalReportPDF(PatientMedicalReportResponseModel patientMedicalReportResponseModel, AuthenticatedUser authenticatedUser); Future<Either<Failure, GenericApiModel<dynamic>>> getPatientMedicalReportPDF(PatientMedicalReportResponseModel patientMedicalReportResponseModel, AuthenticatedUser authenticatedUser);
Future<Either<Failure, GenericApiModel<List<FamilyFileResponseModelLists>>>> getPatientFamilyFiles(); Future<Either<Failure, GenericApiModel<List<FamilyFileResponseModelLists>>>> getPatientFamilyFiles(int? status, int patientId);
Future<Either<Failure, GenericApiModel<List<FamilyFileResponseModelLists>>>> getAllPendingRecordsByResponseId({required Map<String, dynamic> request});
Future<Either<Failure, GenericApiModel<dynamic>>> addFamilyFile({required dynamic request}); Future<Either<Failure, GenericApiModel<dynamic>>> addFamilyFile({required dynamic request});
Future<Either<Failure, GenericApiModel<List<PatientAppointmentHistoryResponseModel>>>> getPatientAppointmentsForMedicalReport();
Future<Either<Failure, GenericApiModel<dynamic>>> insertRequestForMedicalReport({required PatientAppointmentHistoryResponseModel appointmentHistoryResponseModel});
Future<Either<Failure, GenericApiModel<dynamic>>> removeFamilyFile({required int? id});
Future<Either<Failure, GenericApiModel<dynamic>>> acceptRejectFamilyFile({required int? id, required int? status});
} }
class MedicalFileRepoImp implements MedicalFileRepo { class MedicalFileRepoImp implements MedicalFileRepo {
@ -274,13 +285,13 @@ class MedicalFileRepoImp implements MedicalFileRepo {
} }
@override @override
Future<Either<Failure, GenericApiModel<List<FamilyFileResponseModelLists>>>> getPatientFamilyFiles() async { Future<Either<Failure, GenericApiModel<List<FamilyFileResponseModelLists>>>> getPatientFamilyFiles(int? status, int patientID) async {
try { try {
GenericApiModel<List<FamilyFileResponseModelLists>>? apiResponse; GenericApiModel<List<FamilyFileResponseModelLists>>? apiResponse;
Failure? failure; Failure? failure;
await apiClient.post( await apiClient.post(
FAMILY_FILES, ApiConsts.getAllSharedRecordsByStatus,
body: {"Status": 3}, body: {if (status != null) "Status": status, "PatientID": patientID},
onFailure: (error, statusCode, {messageStatus, failureType}) { onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType; failure = failureType;
}, },
@ -312,6 +323,41 @@ class MedicalFileRepoImp implements MedicalFileRepo {
} }
} }
@override
Future<Either<Failure, GenericApiModel<List<FamilyFileResponseModelLists>>>> getAllPendingRecordsByResponseId({required Map<String, dynamic> request}) async {
try {
GenericApiModel<List<FamilyFileResponseModelLists>>? 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<String, dynamic>)).toList().cast<FamilyFileResponseModelLists>();
apiResponse = GenericApiModel<List<FamilyFileResponseModelLists>>(
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 @override
Future<Either<Failure, GenericApiModel<dynamic>>> addFamilyFile({dynamic request}) async { Future<Either<Failure, GenericApiModel<dynamic>>> addFamilyFile({dynamic request}) async {
try { try {
@ -343,4 +389,166 @@ class MedicalFileRepoImp implements MedicalFileRepo {
return Left(UnknownFailure(e.toString())); return Left(UnknownFailure(e.toString()));
} }
} }
@override
Future<Either<Failure, GenericApiModel<dynamic>>> removeFamilyFile({required int? id}) async {
Map<String, dynamic> request = {};
request["ID"] = id;
request['IsActive'] = false;
try {
GenericApiModel<dynamic>? 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<dynamic>(
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<Either<Failure, GenericApiModel<dynamic>>> acceptRejectFamilyFile({required int? id, required int? status}) async {
Map<String, dynamic> request = {};
request["ID"] = id;
request['Status'] = status;
try {
GenericApiModel<dynamic>? 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<dynamic>(
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<Either<Failure, GenericApiModel<List<PatientAppointmentHistoryResponseModel>>>> getPatientAppointmentsForMedicalReport() async {
Map<String, dynamic> mapDevice = {
"IsActiveAppointment": false,
"IsComingFromCOC": false,
"isForUpcomming": false,
"IsForMedicalReport": true,
"IsForArrived": false,
};
try {
GenericApiModel<List<PatientAppointmentHistoryResponseModel>>? apiResponse;
Failure? failure;
await apiClient.post(
GET_PATIENT_APPOINTMENT_HISTORY_ASYNC,
body: mapDevice,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
final list = response['AppoimentAllHistoryResultList'];
final appointmentsList = list.map((item) => PatientAppointmentHistoryResponseModel.fromJson(item as Map<String, dynamic>)).toList().cast<PatientAppointmentHistoryResponseModel>();
apiResponse = GenericApiModel<List<PatientAppointmentHistoryResponseModel>>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: null,
data: appointmentsList,
);
} catch (e) {
failure = DataParsingFailure(e.toString());
}
},
);
if (failure != null) return Left(failure!);
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
return Right(apiResponse!);
} catch (e) {
return Left(UnknownFailure(e.toString()));
}
}
@override
Future<Either<Failure, GenericApiModel<dynamic>>> insertRequestForMedicalReport({required PatientAppointmentHistoryResponseModel appointmentHistoryResponseModel}) async {
Map<String, dynamic> mapDevice = {
"ClinicID": appointmentHistoryResponseModel.clinicID,
"DoctorID": appointmentHistoryResponseModel.doctorID,
"SetupID": appointmentHistoryResponseModel.setupID,
"EncounterNo": appointmentHistoryResponseModel.appointmentNo,
"EncounterType": 1,
"IsActive": appointmentHistoryResponseModel.isActiveDoctor,
"ProjectID": appointmentHistoryResponseModel.projectID,
"Remarks": "",
"ProcedureId": "",
"RequestType": 1,
"Source": 2,
"Status": 1,
"CreatedBy": 102
};
try {
GenericApiModel<dynamic>? apiResponse;
Failure? failure;
await apiClient.post(
INSERT_REQUEST_FOR_MEDICAL_REPORT,
body: mapDevice,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
apiResponse = GenericApiModel<dynamic>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: null,
data: response,
);
} catch (e) {
failure = DataParsingFailure(e.toString());
}
},
);
if (failure != null) return Left(failure!);
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
return Right(apiResponse!);
} catch (e) {
return Left(UnknownFailure(e.toString()));
}
}
} }

@ -1,4 +1,5 @@
import 'dart:convert'; import 'dart:convert';
import 'dart:developer';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_state.dart'; import 'package:hmg_patient_app_new/core/app_state.dart';
@ -14,6 +15,7 @@ import 'package:hmg_patient_app_new/features/medical_file/models/family_file_res
import 'package:hmg_patient_app_new/features/medical_file/models/patient_medical_response_model.dart'; import 'package:hmg_patient_app_new/features/medical_file/models/patient_medical_response_model.dart';
import 'package:hmg_patient_app_new/features/medical_file/models/patient_sickleave_response_model.dart'; import 'package:hmg_patient_app_new/features/medical_file/models/patient_sickleave_response_model.dart';
import 'package:hmg_patient_app_new/features/medical_file/models/patient_vaccine_response_model.dart'; import 'package:hmg_patient_app_new/features/medical_file/models/patient_vaccine_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/services/dialog_service.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/error_handler_service.dart';
import 'package:hmg_patient_app_new/services/navigation_service.dart'; import 'package:hmg_patient_app_new/services/navigation_service.dart';
@ -38,18 +40,35 @@ class MedicalFileViewModel extends ChangeNotifier {
List<PatientMedicalReportResponseModel> patientMedicalReportReadyList = []; List<PatientMedicalReportResponseModel> patientMedicalReportReadyList = [];
List<PatientMedicalReportResponseModel> patientMedicalReportCancelledList = []; List<PatientMedicalReportResponseModel> patientMedicalReportCancelledList = [];
List<PatientAppointmentHistoryResponseModel> patientMedicalReportAppointmentHistoryList = [];
PatientAppointmentHistoryResponseModel? patientMedicalReportSelectedAppointment;
List<FamilyFileResponseModelLists> patientFamilyFiles = []; List<FamilyFileResponseModelLists> patientFamilyFiles = [];
List<FamilyFileResponseModelLists> pendingFamilyFiles = [];
String patientSickLeavePDFBase64 = ""; String patientSickLeavePDFBase64 = "";
String patientMedicalReportPDFBase64 = ""; String patientMedicalReportPDFBase64 = "";
int selectedMedicalReportsTabIndex = 0; 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<DialogService>(); static final DialogService _dialogService = getIt.get<DialogService>();
AppState _appState = getIt<AppState>(); final AppState _appState = getIt<AppState>();
AuthenticationViewModel authVM = getIt.get<AuthenticationViewModel>();
MedicalFileViewModel({required this.medicalFileRepo, required this.errorHandlerService}); MedicalFileViewModel({required this.medicalFileRepo, required this.errorHandlerService});
initMedicalFileProvider() { initMedicalFileProvider() {
patientMedicalReportAppointmentHistoryList.clear();
isPatientVaccineListLoading = true; isPatientVaccineListLoading = true;
isPatientMedicalReportsListLoading = true; isPatientMedicalReportsListLoading = true;
notifyListeners(); notifyListeners();
@ -57,6 +76,7 @@ class MedicalFileViewModel extends ChangeNotifier {
void onMedicalReportTabChange(int index) { void onMedicalReportTabChange(int index) {
selectedMedicalReportsTabIndex = index; selectedMedicalReportsTabIndex = index;
print("Selected Medical Report Tab Index: $selectedMedicalReportsTabIndex");
if (index == 0) { if (index == 0) {
patientMedicalReportList = patientMedicalReportRequestedList; patientMedicalReportList = patientMedicalReportRequestedList;
} else if (index == 1) { } else if (index == 1) {
@ -67,6 +87,11 @@ class MedicalFileViewModel extends ChangeNotifier {
notifyListeners(); notifyListeners();
} }
void onFamilyFileTabChange(int index) {
setSelectedFamilyFileTabIndex = index;
notifyListeners();
}
setIsPatientVaccineListLoading(bool isLoading) { setIsPatientVaccineListLoading(bool isLoading) {
isPatientVaccineListLoading = isLoading; isPatientVaccineListLoading = isLoading;
notifyListeners(); notifyListeners();
@ -88,6 +113,7 @@ class MedicalFileViewModel extends ChangeNotifier {
setIsPatientMedicalReportsLoading(bool val) { setIsPatientMedicalReportsLoading(bool val) {
if (val) { if (val) {
onMedicalReportTabChange(0);
patientMedicalReportList.clear(); patientMedicalReportList.clear();
patientMedicalReportPDFBase64 = ""; patientMedicalReportPDFBase64 = "";
} }
@ -95,6 +121,11 @@ class MedicalFileViewModel extends ChangeNotifier {
notifyListeners(); notifyListeners();
} }
setSelectedMedicalReportAppointment(PatientAppointmentHistoryResponseModel? val) {
patientMedicalReportSelectedAppointment = val;
notifyListeners();
}
void onTabChanged(int index) { void onTabChanged(int index) {
selectedTabIndex = index; selectedTabIndex = index;
notifyListeners(); notifyListeners();
@ -105,12 +136,17 @@ class MedicalFileViewModel extends ChangeNotifier {
final result = await medicalFileRepo.getPatientVaccinesList(); final result = await medicalFileRepo.getPatientVaccinesList();
result.fold( result.fold(
(failure) async => await errorHandlerService.handleError( // (failure) async => await errorHandlerService.handleError(
failure: failure, // failure: failure,
onOkPressed: () { // onOkPressed: () {
onError!(failure.message); // onError!(failure.message);
}, // },
), // ),
(failure) async {
// onError!(failure.message);
isPatientVaccineListLoading = false;
notifyListeners();
},
(apiResponse) { (apiResponse) {
if (apiResponse.messageStatus == 2) { if (apiResponse.messageStatus == 2) {
// dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {}); // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
@ -131,12 +167,16 @@ class MedicalFileViewModel extends ChangeNotifier {
final result = await medicalFileRepo.getPatientSickLeavesList(); final result = await medicalFileRepo.getPatientSickLeavesList();
result.fold( result.fold(
(failure) async => await errorHandlerService.handleError( // (failure) async => await errorHandlerService.handleError(
failure: failure, // failure: failure,
onOkPressed: () { // onOkPressed: () {
onError!(failure.message); // onError!(failure.message);
}, // },
), // ),
(failure) async {
isPatientSickLeaveListLoading = false;
notifyListeners();
},
(apiResponse) { (apiResponse) {
if (apiResponse.messageStatus == 2) { if (apiResponse.messageStatus == 2) {
// dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {}); // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
@ -235,8 +275,8 @@ class MedicalFileViewModel extends ChangeNotifier {
); );
} }
Future<void> getFamilyFiles({Function(dynamic)? onSuccess, Function(String)? onError}) async { Future<void> getFamilyFiles({int? status, Function(dynamic)? onSuccess, Function(String)? onError}) async {
final result = await medicalFileRepo.getPatientFamilyFiles(); final result = await medicalFileRepo.getPatientFamilyFiles(status, _appState.getSuperUserID != null ? _appState.getSuperUserID! : _appState.getAuthenticatedUser()!.patientId!);
result.fold( result.fold(
(failure) async => await errorHandlerService.handleError( (failure) async => await errorHandlerService.handleError(
@ -249,16 +289,85 @@ class MedicalFileViewModel extends ChangeNotifier {
if (apiResponse.messageStatus == 2) { if (apiResponse.messageStatus == 2) {
_dialogService.showErrorBottomSheet(message: apiResponse.errorMessage!, onOkPressed: () {}); _dialogService.showErrorBottomSheet(message: apiResponse.errorMessage!, onOkPressed: () {});
} else if (apiResponse.messageStatus == 1) { } else if (apiResponse.messageStatus == 1) {
patientFamilyFiles = apiResponse.data!; if (apiResponse.data != null) {
patientFamilyFiles.insert( // Add current user as the first active family file
0, final currentUser = _appState.getAuthenticatedUser()!;
FamilyFileResponseModelLists( final currentUserFamilyFile = FamilyFileResponseModelLists(
patientId: _appState.getAuthenticatedUser()!.patientId, patientId: currentUser.patientId,
patientName: '${_appState.getAuthenticatedUser()!.firstName!} ${_appState.getAuthenticatedUser()!.lastName!}', patientName: '${currentUser.firstName!} ${currentUser.lastName!}',
isActive: true, isActive: true,
gender: _appState.getAuthenticatedUser()!.gender!, gender: currentUser.gender!,
responseId: _appState.getAuthenticatedUser()!.patientId), 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<FamilyFileResponseModelLists> activeFamilyFiles = [];
final List<FamilyFileResponseModelLists> 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(); notifyListeners();
if (onSuccess != null) { if (onSuccess != null) {
onSuccess(apiResponse); onSuccess(apiResponse);
@ -268,40 +377,69 @@ class MedicalFileViewModel extends ChangeNotifier {
); );
} }
Future<void> switchFamilyFiles({Function(dynamic)? onSuccess, Function(String)? onError}) async { Future<void> getAllPendingRecordsByResponseId() async {
final result = await medicalFileRepo.getPatientFamilyFiles(); AppState appState = getIt<AppState>();
final result = await medicalFileRepo.getAllPendingRecordsByResponseId(request: {'ResponseID': appState.getAuthenticatedUser()!.patientId ?? "0", "Status": 2});
result.fold( result.fold(
(failure) async => await errorHandlerService.handleError( (failure) async => await errorHandlerService.handleError(failure: failure),
failure: failure,
onOkPressed: () {
onError!(failure.message);
},
),
(apiResponse) { (apiResponse) {
if (apiResponse.messageStatus == 2) { if (apiResponse.messageStatus == 2) {
_dialogService.showErrorBottomSheet(message: apiResponse.errorMessage!, onOkPressed: () {}); _dialogService.showErrorBottomSheet(message: apiResponse.errorMessage!, onOkPressed: () {});
} else if (apiResponse.messageStatus == 1) { } else if (apiResponse.messageStatus == 1) {
patientFamilyFiles = apiResponse.data!; if (apiResponse.data != null) {
patientFamilyFiles.insert( final List<FamilyFileResponseModelLists> tempPendingFamilyFiles = [];
0, for (var element in apiResponse.data!) {
FamilyFileResponseModelLists( if (element.status != null && element.status == FamilyFileEnum.pending.toInt || element.status == FamilyFileEnum.active.toInt) {
patientId: _appState.getAuthenticatedUser()!.patientId, tempPendingFamilyFiles.add(FamilyFileResponseModelLists(
patientName: '${_appState.getAuthenticatedUser()!.firstName!} ${_appState.getAuthenticatedUser()!.lastName!}', id: element.id,
isActive: true, patientId: element.patientId,
gender: _appState.getAuthenticatedUser()!.gender!, patientName: element.patientName!,
responseId: _appState.getAuthenticatedUser()!.patientId), isActive: element.status,
); gender: element.gender,
notifyListeners(); responseId: element.patientId,
if (onSuccess != null) { mobileNumber: element.mobileNumber,
onSuccess(apiResponse); 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<void> addFamilyFile({required OTPTypeEnum otpTypeEnum, required bool isExcludedUser}) async { Future<void> 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<void> addFamilyFile({required OTPTypeEnum otpTypeEnum}) async {
LoaderBottomSheet.showLoader(); LoaderBottomSheet.showLoader();
AuthenticationViewModel authVM = getIt.get<AuthenticationViewModel>(); AuthenticationViewModel authVM = getIt.get<AuthenticationViewModel>();
NavigationService navigationService = getIt.get<NavigationService>(); NavigationService navigationService = getIt.get<NavigationService>();
@ -310,29 +448,151 @@ class MedicalFileViewModel extends ChangeNotifier {
final resultEither = await medicalFileRepo.addFamilyFile(request: request.toJson()); final resultEither = await medicalFileRepo.addFamilyFile(request: request.toJson());
resultEither.fold((failure) async => await errorHandlerService.handleError(failure: failure), (apiResponse) async { resultEither.fold((failure) async => await errorHandlerService.handleError(failure: failure), (apiResponse) async {
if (apiResponse != null && apiResponse.data != null) { if (apiResponse.messageStatus == 2) {
request.isPatientExcluded = apiResponse.data["IsPatientExcluded"];
request.responseID = apiResponse.data["ReponseID"];
LoaderBottomSheet.hideLoader(); LoaderBottomSheet.hideLoader();
_dialogService.showExceptionBottomSheet( _dialogService.showErrorBottomSheet(
message: apiResponse.data['Message'], message: apiResponse.errorMessage!,
onOkPressed: () { onOkPressed: () {
LoaderBottomSheet.showLoader();
print("=================== On Press Ok ==================");
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(); 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<void> handleFamilyFileRequestOTPVerification() async {
LoaderBottomSheet.showLoader();
if (!_appState.getIsChildLoggedIn) {
await getFamilyFiles(status: 0);
await getAllPendingRecordsByResponseId();
}
LoaderBottomSheet.hideLoader();
}
Future<void> removeFileFromFamilyMembers({int? id}) async {
NavigationService navigationService = getIt.get<NavigationService>();
_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<void> getPatientMedicalReportAppointmentsList({Function(dynamic)? onSuccess, Function(String)? onError}) async {
patientMedicalReportAppointmentHistoryList.clear();
notifyListeners();
final result = await medicalFileRepo.getPatientAppointmentsForMedicalReport();
result.fold(
(failure) async {
onError!(failure.message);
},
(apiResponse) {
if (apiResponse.messageStatus == 2) {
// dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
} else if (apiResponse.messageStatus == 1) {
patientMedicalReportAppointmentHistoryList = apiResponse.data!;
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse);
}
}
},
);
}
Future<void> insertRequestForMedicalReport({Function(dynamic)? onSuccess, Function(String)? onError}) async {
final result = await medicalFileRepo.insertRequestForMedicalReport(appointmentHistoryResponseModel: patientMedicalReportSelectedAppointment!);
result.fold(
(failure) async {
onError!(failure.message);
},
(apiResponse) {
if (apiResponse.messageStatus == 2) {
// dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
} else if (apiResponse.messageStatus == 1) {
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse);
}
}
},
);
}
Future<void> acceptRejectFileFromFamilyMembers({int? id, int? status}) async {
NavigationService navigationService = getIt.get<NavigationService>();
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);
}
},
);
}
} }

@ -23,83 +23,90 @@ class FamilyFileResponseModelLists {
String? patientIdenficationNumber; String? patientIdenficationNumber;
String? patientName; String? patientName;
String? statusDescription; String? statusDescription;
bool? isSuperUser = false;
bool? isRequestFromMySide;
FamilyFileResponseModelLists({ FamilyFileResponseModelLists(
this.id, {this.id,
this.patientId, this.patientId,
this.responseId, this.responseId,
this.relationshipId, this.relationshipId,
this.relationship, this.relationship,
this.relationshipN, this.relationshipN,
this.regionId, this.regionId,
this.familyRegionId, this.familyRegionId,
this.status, this.status,
this.isActive, this.isActive,
this.editedOn, this.editedOn,
this.createdOn, this.createdOn,
this.age, this.age,
this.emaiLAddress, this.emaiLAddress,
this.gender, this.gender,
this.genderDescription, this.genderDescription,
this.genderImage, this.genderImage,
this.mobileNumber, this.mobileNumber,
this.patientDataVerified, this.patientDataVerified,
this.patientIdenficationNumber, this.patientIdenficationNumber,
this.patientName, this.patientName,
this.statusDescription, this.statusDescription,
}); this.isSuperUser,
this.isRequestFromMySide});
factory FamilyFileResponseModelLists.fromRawJson(String str) => FamilyFileResponseModelLists.fromJson(json.decode(str)); factory FamilyFileResponseModelLists.fromRawJson(String str) => FamilyFileResponseModelLists.fromJson(json.decode(str));
String toRawJson() => json.encode(toJson()); String toRawJson() => json.encode(toJson());
factory FamilyFileResponseModelLists.fromJson(Map<String, dynamic> json) => FamilyFileResponseModelLists( factory FamilyFileResponseModelLists.fromJson(Map<String, dynamic> json) => FamilyFileResponseModelLists(
id: json["ID"], id: json["ID"],
patientId: json["PatientID"], patientId: json["PatientID"],
responseId: json["ResponseID"], responseId: json["ResponseID"],
relationshipId: json["RelationshipID"], relationshipId: json["RelationshipID"],
relationship: json["Relationship"], relationship: json["Relationship"],
relationshipN: json["RelationshipN"], relationshipN: json["RelationshipN"],
regionId: json["RegionID"], regionId: json["RegionID"],
familyRegionId: json["FamilyRegionID"], familyRegionId: json["FamilyRegionID"],
status: json["Status"], status: json["Status"],
isActive: json["IsActive"], isActive: json["IsActive"],
editedOn: json["EditedOn"], editedOn: json["EditedOn"],
createdOn: json["CreatedOn"], createdOn: json["CreatedOn"],
age: json["Age"], age: json["Age"],
emaiLAddress: json["EmaiLAddress"], emaiLAddress: json["EmaiLAddress"],
gender: json["Gender"], gender: json["Gender"],
genderDescription: json["GenderDescription"], genderDescription: json["GenderDescription"],
genderImage: json["GenderImage"], genderImage: json["GenderImage"],
mobileNumber: json["MobileNumber"], mobileNumber: json["MobileNumber"],
patientDataVerified: json["PatientDataVerified"], patientDataVerified: json["PatientDataVerified"],
patientIdenficationNumber: json["PatientIdenficationNumber"], patientIdenficationNumber: json["PatientIdenficationNumber"],
patientName: json["PatientName"], patientName: json["PatientName"],
statusDescription: json["StatusDescription"], statusDescription: json["StatusDescription"],
); isSuperUser: json["isSuperUser"] ?? false,
isRequestFromMySide: json["isRequestFromMySide"] ?? false,
);
Map<String, dynamic> toJson() => { Map<String, dynamic> toJson() => {
"ID": id, "ID": id,
"PatientID": patientId, "PatientID": patientId,
"ResponseID": responseId, "ResponseID": responseId,
"RelationshipID": relationshipId, "RelationshipID": relationshipId,
"Relationship": relationship, "Relationship": relationship,
"RelationshipN": relationshipN, "RelationshipN": relationshipN,
"RegionID": regionId, "RegionID": regionId,
"FamilyRegionID": familyRegionId, "FamilyRegionID": familyRegionId,
"Status": status, "Status": status,
"IsActive": isActive, "IsActive": isActive,
"EditedOn": editedOn, "EditedOn": editedOn,
"CreatedOn": createdOn, "CreatedOn": createdOn,
"Age": age, "Age": age,
"EmaiLAddress": emaiLAddress, "EmaiLAddress": emaiLAddress,
"Gender": gender, "Gender": gender,
"GenderDescription": genderDescription, "GenderDescription": genderDescription,
"GenderImage": genderImage, "GenderImage": genderImage,
"MobileNumber": mobileNumber, "MobileNumber": mobileNumber,
"PatientDataVerified": patientDataVerified, "PatientDataVerified": patientDataVerified,
"PatientIdenficationNumber": patientIdenficationNumber, "PatientIdenficationNumber": patientIdenficationNumber,
"PatientName": patientName, "PatientName": patientName,
"StatusDescription": statusDescription, "StatusDescription": statusDescription,
}; "isSuperUser": isSuperUser,
"isRequestFromMySide": isRequestFromMySide,
};
} }

@ -2,6 +2,7 @@ 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/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/features/my_appointments/models/resp_models/doctor_list_api_response.dart';
import 'package:hmg_patient_app_new/presentation/book_appointment/select_clinic_page.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'; import 'package:hmg_patient_app_new/services/navigation_service.dart';
import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.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:hmg_patient_app_new/widgets/transitions/fade_page.dart';
@ -14,6 +15,12 @@ enum AppointmentViaRegionState {
DOCTOR_SELECTION DOCTOR_SELECTION
} }
enum RegionBottomSheetType{
FOR_REGION,
REGION_FOR_DENTAL_AND_LASER,
FOR_CLINIIC,
}
class AppointmentViaRegionViewmodel extends ChangeNotifier { class AppointmentViaRegionViewmodel extends ChangeNotifier {
String? selectedRegionId; String? selectedRegionId;
String? selectedFacilityType; String? selectedFacilityType;
@ -23,6 +30,8 @@ class AppointmentViaRegionViewmodel extends ChangeNotifier {
AppointmentViaRegionState.REGION_SELECTION; AppointmentViaRegionState.REGION_SELECTION;
final AppState appState; final AppState appState;
RegionBottomSheetType regionBottomSheetType = RegionBottomSheetType.FOR_REGION;
AppointmentViaRegionViewmodel({required this.navigationService,required this.appState}); AppointmentViaRegionViewmodel({required this.navigationService,required this.appState});
void setSelectedRegionId(String? regionId) { void setSelectedRegionId(String? regionId) {
@ -35,15 +44,20 @@ class AppointmentViaRegionViewmodel extends ChangeNotifier {
notifyListeners(); notifyListeners();
} }
void setBottomSheetType(RegionBottomSheetType type) {
regionBottomSheetType = type;
notifyListeners();
}
void setBottomSheetState(AppointmentViaRegionState state) { void setBottomSheetState(AppointmentViaRegionState state) {
bottomSheetState = state; bottomSheetState = state;
notifyListeners(); notifyListeners();
} }
void handleLastStep(){ void handleLastStepForRegion(){
navigationService.pop(); navigationService.pop();
navigationService.push(CustomPageRoute( navigationService.push(CustomPageRoute(
page: SelectClinicPage(), page: SelectClinicPage(isFromRegionFlow: true,),
),); ),);
} }
@ -65,6 +79,7 @@ class AppointmentViaRegionViewmodel extends ChangeNotifier {
void flush() { void flush() {
setSelectedRegionId(null); setSelectedRegionId(null);
setFacility(null); setFacility(null);
setBottomSheetType(RegionBottomSheetType.FOR_REGION);
setBottomSheetState(AppointmentViaRegionState.REGION_SELECTION); setBottomSheetState(AppointmentViaRegionState.REGION_SELECTION);
} }
@ -73,4 +88,26 @@ class AppointmentViaRegionViewmodel extends ChangeNotifier {
} }
bool get isArabic => appState.isArabic(); bool get isArabic => appState.isArabic();
void handleLastStepForClinic() {
navigationService.pop();
navigationService.push(CustomPageRoute(
page: SelectDoctorPage(),
),);
}
void handleLastStepForClinicForDentalAndLaser() {
//todo handle the routing here
navigationService.pop();
}
void handleLastStepForDentalAndLaser() {
//todo handle the routing here
navigationService.pop();
navigationService.push(
CustomPageRoute(
page: SelectDoctorPage(),
),
);
}
} }

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

@ -1,4 +1,5 @@
import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/doctors_list_response_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/hospital_model.dart' show HospitalsModel; import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/hospital_model.dart' show HospitalsModel;
class DoctorList { class DoctorList {
@ -236,7 +237,7 @@ class DoctorList {
class PatientDoctorAppointmentList { class PatientDoctorAppointmentList {
String? filterName = ""; String? filterName = "";
String? distanceInKMs = ""; String? distanceInKMs = "";
List<DoctorList>? patientDoctorAppointmentList = []; List<DoctorsListResponseModel>? patientDoctorAppointmentList = [];
String? projectTopName = ""; String? projectTopName = "";
String? projectBottomName = ""; String? projectBottomName = "";
bool? isHMC; bool? isHMC;
@ -247,7 +248,7 @@ class PatientDoctorAppointmentList {
this.distanceInKMs, this.distanceInKMs,
this.projectTopName, this.projectTopName,
this.projectBottomName, this.projectBottomName,
DoctorList? patientDoctorAppointment, DoctorsListResponseModel? patientDoctorAppointment,
HospitalsModel? model, HospitalsModel? model,
this.isHMC = false}) { this.isHMC = false}) {
if (model != null) { if (model != null) {

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

@ -1,10 +1,14 @@
import 'dart:io';
import 'package:dartz/dartz.dart'; import 'package:dartz/dartz.dart';
import 'package:hmg_patient_app_new/core/api/api_client.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/api_consts.dart';
import 'package:hmg_patient_app_new/core/cache_consts.dart';
import 'package:hmg_patient_app_new/core/common_models/generic_api_model.dart'; import 'package:hmg_patient_app_new/core/common_models/generic_api_model.dart';
import 'package:hmg_patient_app_new/core/exceptions/api_failure.dart'; import 'package:hmg_patient_app_new/core/exceptions/api_failure.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/hospital_model.dart' import 'package:hmg_patient_app_new/core/utils/utils.dart';
show HospitalsModel; import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/get_tamara_installments_details_response_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/hospital_model.dart' show HospitalsModel;
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/patient_appointment_history_response_model.dart'; import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/patient_appointment_history_response_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/patient_appointment_share_response_model.dart'; import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/patient_appointment_share_response_model.dart';
import 'package:hmg_patient_app_new/services/logger_service.dart'; import 'package:hmg_patient_app_new/services/logger_service.dart';
@ -12,7 +16,8 @@ import 'package:hmg_patient_app_new/services/logger_service.dart';
abstract class MyAppointmentsRepo { abstract class MyAppointmentsRepo {
Future<Either<Failure, GenericApiModel<List<PatientAppointmentHistoryResponseModel>>>> getPatientAppointments({required bool isActiveAppointment, required bool isArrivedAppointments}); Future<Either<Failure, GenericApiModel<List<PatientAppointmentHistoryResponseModel>>>> getPatientAppointments({required bool isActiveAppointment, required bool isArrivedAppointments});
Future<Either<Failure, GenericApiModel<PatientAppointmentShareResponseModel>>> getPatientShareAppointment({required int projectID, required int clinicID, required String appointmentNo}); Future<Either<Failure, GenericApiModel<PatientAppointmentShareResponseModel>>> getPatientShareAppointment(
{required int projectID, required int clinicID, required String appointmentNo, required bool isLiveCareAppointment});
Future<Either<Failure, GenericApiModel<dynamic>>> createAdvancePayment( Future<Either<Failure, GenericApiModel<dynamic>>> createAdvancePayment(
{required String paymentMethodName, {required String paymentMethodName,
@ -39,7 +44,9 @@ abstract class MyAppointmentsRepo {
Future<Either<Failure, GenericApiModel<List<PatientAppointmentHistoryResponseModel>>>> getPatientDoctorsList(); Future<Either<Failure, GenericApiModel<List<PatientAppointmentHistoryResponseModel>>>> getPatientDoctorsList();
Future<Either<Failure, GenericApiModel<dynamic>>> insertLiveCareVIDARequest({required clientRequestID, required PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel});
Future<Either<Failure, GenericApiModel<GetTamaraInstallmentsDetailsResponseModel>>> getTamaraInstallmentsDetails();
} }
class MyAppointmentsRepoImp implements MyAppointmentsRepo { class MyAppointmentsRepoImp implements MyAppointmentsRepo {
@ -52,13 +59,10 @@ class MyAppointmentsRepoImp implements MyAppointmentsRepo {
Future<Either<Failure, GenericApiModel<List<PatientAppointmentHistoryResponseModel>>>> getPatientAppointments({required bool isActiveAppointment, required bool isArrivedAppointments}) async { Future<Either<Failure, GenericApiModel<List<PatientAppointmentHistoryResponseModel>>>> getPatientAppointments({required bool isActiveAppointment, required bool isArrivedAppointments}) async {
Map<String, dynamic> mapDevice = { Map<String, dynamic> mapDevice = {
"IsActiveAppointment": isActiveAppointment, "IsActiveAppointment": isActiveAppointment,
"isDentalAllowedBackend": false,
"PatientTypeID": 1,
"IsComingFromCOC": false, "IsComingFromCOC": false,
"PatientType": 1,
"isForUpcomming": false, "isForUpcomming": false,
"IsForMedicalReport": false,
"IsForArrived": isArrivedAppointments, "IsForArrived": isArrivedAppointments,
"PatientOutSA": 0
}; };
try { try {
@ -99,14 +103,15 @@ class MyAppointmentsRepoImp implements MyAppointmentsRepo {
} }
@override @override
Future<Either<Failure, GenericApiModel<PatientAppointmentShareResponseModel>>> getPatientShareAppointment({required int projectID, required int clinicID, required String appointmentNo}) async { Future<Either<Failure, GenericApiModel<PatientAppointmentShareResponseModel>>> getPatientShareAppointment(
Map<String, dynamic> mapRequest = {"ProjectID": projectID, "ClinicID": clinicID, "AppointmentNo": appointmentNo, "IsActiveAppointment": true}; {required int projectID, required int clinicID, required String appointmentNo, required bool isLiveCareAppointment}) async {
Map<String, dynamic> mapRequest = {"ProjectID": projectID, "ClinicID": clinicID, "AppointmentNo": appointmentNo, "IsActiveAppointment": true, "IsForLiveCare": isLiveCareAppointment};
try { try {
GenericApiModel<PatientAppointmentShareResponseModel>? apiResponse; GenericApiModel<PatientAppointmentShareResponseModel>? apiResponse;
Failure? failure; Failure? failure;
await apiClient.post( await apiClient.post(
GET_PATIENT_SHARE, isLiveCareAppointment ? GET_PATIENT_SHARE_LIVECARE : GET_PATIENT_SHARE,
body: mapRequest, body: mapRequest,
onFailure: (error, statusCode, {messageStatus, failureType}) { onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType; failure = failureType;
@ -495,4 +500,87 @@ class MyAppointmentsRepoImp implements MyAppointmentsRepo {
return Left(UnknownFailure(e.toString())); return Left(UnknownFailure(e.toString()));
} }
} }
@override
Future<Either<Failure, GenericApiModel>> insertLiveCareVIDARequest({required clientRequestID, required PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel}) async {
Map<String, dynamic> requestBody = {
"AppointmentNo": patientAppointmentHistoryResponseModel.appointmentNo,
"AppointmentDate": patientAppointmentHistoryResponseModel.appointmentDate,
"ClientRequestID": clientRequestID,
"ClinicID": patientAppointmentHistoryResponseModel.clinicID,
"ProjectID": patientAppointmentHistoryResponseModel.projectID,
"ServiceID": patientAppointmentHistoryResponseModel.serviceID,
"AcceptedBy": patientAppointmentHistoryResponseModel.doctorID,
"IsFlutter": true,
"DeviceToken": await Utils.getStringFromPrefs(CacheConst.pushToken),
"VoipToken": "", // TODO: Add VoIP Token functionality
"IsVoip": Platform.isIOS ? true : false
};
try {
GenericApiModel<dynamic>? apiResponse;
Failure? failure;
await apiClient.post(
INSERT_VIDA_REQUEST,
body: requestBody,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
apiResponse = GenericApiModel<dynamic>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: null,
data: response,
);
} catch (e) {
failure = DataParsingFailure(e.toString());
}
},
);
if (failure != null) return Left(failure!);
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
return Right(apiResponse!);
} catch (e) {
return Left(UnknownFailure(e.toString()));
}
}
@override
Future<Either<Failure, GenericApiModel<GetTamaraInstallmentsDetailsResponseModel>>> getTamaraInstallmentsDetails() async {
try {
GenericApiModel<GetTamaraInstallmentsDetailsResponseModel>? apiResponse;
Failure? failure;
await apiClient.get(
ApiConsts.GET_TAMARA_INSTALLMENTS_URL,
isExternal: true,
isAllowAny: true,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
final list = response;
final tamaraInstallmentsList = GetTamaraInstallmentsDetailsResponseModel.fromJson(list.first);
apiResponse = GenericApiModel<GetTamaraInstallmentsDetailsResponseModel>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: null,
data: tamaraInstallmentsList,
);
} catch (e) {
failure = DataParsingFailure(e.toString());
}
},
);
if (failure != null) return Left(failure!);
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
return Right(apiResponse!);
} catch (e) {
return Left(UnknownFailure(e.toString()));
}
}
} }

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

@ -18,6 +18,13 @@ abstract class PayfortRepo {
Future<Either<Failure, GenericApiModel<SdkTokenResponse>>> generateSdkSignatureFromAPI({required SdkTokenRequest tokenRequest}); Future<Either<Failure, GenericApiModel<SdkTokenResponse>>> generateSdkSignatureFromAPI({required SdkTokenRequest tokenRequest});
Future<Either<Failure, GenericApiModel<PayfortCheckPaymentStatusResponseModel>>> checkPaymentStatus({required String transactionID}); Future<Either<Failure, GenericApiModel<PayfortCheckPaymentStatusResponseModel>>> checkPaymentStatus({required String transactionID});
Future<Either<Failure, GenericApiModel<dynamic>>> checkTamaraPaymentStatus({required String transactionID});
Future<Either<Failure, GenericApiModel<dynamic>>> markAppointmentAsTamaraPaid({required int projectID, required int appointmentNo});
Future<Either<Failure, GenericApiModel<dynamic>>> updateTamaraRequestStatus(
{required String responseMessage, required String status, required String clientRequestID, required String tamaraOrderID});
} }
class PayfortRepoImp implements PayfortRepo { class PayfortRepoImp implements PayfortRepo {
@ -147,4 +154,100 @@ class PayfortRepoImp implements PayfortRepo {
return Left(UnknownFailure(e.toString())); return Left(UnknownFailure(e.toString()));
} }
} }
@override
Future<Either<Failure, GenericApiModel>> checkTamaraPaymentStatus({required String transactionID}) async {
try {
GenericApiModel<dynamic>? 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<dynamic>(
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<Either<Failure, GenericApiModel>> updateTamaraRequestStatus({required String responseMessage, required String status, required String clientRequestID, required String tamaraOrderID}) async {
Map<String, dynamic> body = {
"Response_Message": responseMessage,
"ClientRequestID": clientRequestID,
"Status": status,
"FortID": tamaraOrderID, // Tamara order ID
"LanguageID": 1,
"Installments_Number": 3,
};
try {
GenericApiModel<dynamic>? 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<dynamic>(
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<Either<Failure, GenericApiModel>> markAppointmentAsTamaraPaid({required int projectID, required int appointmentNo}) async {
Map<String, dynamic> body = {"ProjectID": projectID, "AppointmentNo": appointmentNo, "LanguageID": 1};
try {
GenericApiModel<dynamic>? 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<dynamic>(
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()));
}
}
} }

@ -94,6 +94,40 @@ class PayfortViewModel extends ChangeNotifier {
); );
} }
Future<void> 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<void> 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<SdkTokenResponse?> _generateSdkResponse({ Future<SdkTokenResponse?> _generateSdkResponse({
String? applePayAccessCode, String? applePayAccessCode,
String? merchantIdentifier, String? merchantIdentifier,
@ -199,4 +233,20 @@ class PayfortViewModel extends ChangeNotifier {
onFailed!(e.toString() as PayFortFailureResult); onFailed!(e.toString() as PayFortFailureResult);
} }
} }
Future<void> 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);
}
},
);
}
} }

@ -71,6 +71,7 @@ class PrescriptionsViewModel extends ChangeNotifier {
// (failure) async => await errorHandlerService.handleError(failure: failure), // (failure) async => await errorHandlerService.handleError(failure: failure),
(failure) async { (failure) async {
isPrescriptionsOrdersLoading = false; isPrescriptionsOrdersLoading = false;
notifyListeners();
}, },
(apiResponse) { (apiResponse) {
if (apiResponse.messageStatus == 2) { if (apiResponse.messageStatus == 2) {

@ -849,5 +849,29 @@ abstract class LocaleKeys {
static const selectCountry = 'selectCountry'; static const selectCountry = 'selectCountry';
static const forLoginVerification = 'forLoginVerification'; static const forLoginVerification = 'forLoginVerification';
static const searchHospital = 'searchHospital'; static const searchHospital = 'searchHospital';
static const skip = 'skip';
static const getStarted = 'getStarted';
static const onboardingHeading1 = 'onboardingHeading1';
static const onboardingBody1 = 'onboardingBody1';
static const onboardingHeading2 = 'onboardingHeading2';
static const onboardingBody2 = 'onboardingBody2';
static const hmgHospitals = 'hmgHospitals';
static const hmcMedicalClinic = 'hmcMedicalClinic';
static const applyFilter = 'applyFilter';
static const facilityAndLocation = 'facilityAndLocation';
static const regionAndLocation = 'regionAndLocation';
static const clearAllFilters = 'clearAllFilters';
static const filters = 'filters';
static const searchClinic = 'searchClinic';
static const walkin = 'walkin';
static const normal = 'normal';
static const attention = 'attention';
static const monitor = 'monitor';
static const noSpecialResult = 'noSpecialResult';
static const setTheDateRange = 'setTheDateRange';
static const historyFlowchart = 'historyFlowchart';
static const to = 'to';
static const startDate = 'startDate';
static const endDate = 'endDate';
} }

@ -10,8 +10,11 @@ import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart'; import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/features/authentication/authentication_view_model.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/book_appointments/book_appointments_view_model.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_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/insurance/insurance_view_model.dart';
import 'package:hmg_patient_app_new/features/lab/history/lab_history_viewmodel.dart';
import 'package:hmg_patient_app_new/features/lab/lab_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/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/appointment_via_region_viewmodel.dart';
@ -24,6 +27,7 @@ import 'package:hmg_patient_app_new/routes/app_routes.dart';
import 'package:hmg_patient_app_new/services/logger_service.dart'; import 'package:hmg_patient_app_new/services/logger_service.dart';
import 'package:hmg_patient_app_new/services/navigation_service.dart'; import 'package:hmg_patient_app_new/services/navigation_service.dart';
import 'package:hmg_patient_app_new/theme/app_theme.dart'; import 'package:hmg_patient_app_new/theme/app_theme.dart';
import 'package:hmg_patient_app_new/widgets/date_range_selector/viewmodel/date_range_view_model.dart' show DateRangeSelectorRangeViewModel;
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:provider/single_child_widget.dart'; import 'package:provider/single_child_widget.dart';
@ -75,80 +79,53 @@ void main() async {
fallbackLocale: Locale('en', 'US'), fallbackLocale: Locale('en', 'US'),
child: MultiProvider(providers: <SingleChildWidget>[ child: MultiProvider(providers: <SingleChildWidget>[
ChangeNotifierProvider<LabViewModel>( ChangeNotifierProvider<LabViewModel>(
create: (_) => LabViewModel( create: (_) => getIt.get<LabViewModel>(),
labRepo: getIt(),
errorHandlerService: getIt(),
),
), ),
ChangeNotifierProvider<RadiologyViewModel>( ChangeNotifierProvider<RadiologyViewModel>(
create: (_) => RadiologyViewModel( create: (_) => getIt.get<RadiologyViewModel>(),
radiologyRepo: getIt(),
errorHandlerService: getIt(),
),
), ),
ChangeNotifierProvider<PrescriptionsViewModel>( ChangeNotifierProvider<PrescriptionsViewModel>(
create: (_) => PrescriptionsViewModel( create: (_) => getIt.get<PrescriptionsViewModel>(),
prescriptionsRepo: getIt(),
errorHandlerService: getIt(),
),
), ),
ChangeNotifierProvider<InsuranceViewModel>( ChangeNotifierProvider<InsuranceViewModel>(
create: (_) => InsuranceViewModel( create: (_) => getIt.get<InsuranceViewModel>(),
insuranceRepo: getIt(),
errorHandlerService: getIt(),
),
), ),
ChangeNotifierProvider<MedicalFileViewModel>( ChangeNotifierProvider<MedicalFileViewModel>(
create: (_) => MedicalFileViewModel( create: (_) => getIt.get<MedicalFileViewModel>(),
medicalFileRepo: getIt(),
errorHandlerService: getIt(),
),
), ),
ChangeNotifierProvider<ProfileSettingsViewModel>( ChangeNotifierProvider<ProfileSettingsViewModel>(
create: (_) => ProfileSettingsViewModel(), create: (_) => getIt.get<ProfileSettingsViewModel>(),
), ),
ChangeNotifierProvider<MyAppointmentsViewModel>( ChangeNotifierProvider<MyAppointmentsViewModel>(
create: (_) => MyAppointmentsViewModel( create: (_) => getIt.get<MyAppointmentsViewModel>(),
myAppointmentsRepo: getIt(),
errorHandlerService: getIt(),
appState: getIt(),
),
), ),
ChangeNotifierProvider<PayfortViewModel>( ChangeNotifierProvider<PayfortViewModel>(
create: (_) => PayfortViewModel( create: (_) => getIt.get<PayfortViewModel>(),
payfortRepo: getIt(),
errorHandlerService: getIt(),
),
), ),
ChangeNotifierProvider<HabibWalletViewModel>( ChangeNotifierProvider<HabibWalletViewModel>(
create: (_) => HabibWalletViewModel( create: (_) => getIt.get<HabibWalletViewModel>(),
habibWalletRepo: getIt(),
errorHandlerService: getIt(),
),
), ),
ChangeNotifierProvider<BookAppointmentsViewModel>( ChangeNotifierProvider<BookAppointmentsViewModel>(
create: (_) => BookAppointmentsViewModel( create: (_) => getIt.get<BookAppointmentsViewModel>(),
bookAppointmentsRepo: getIt(), ),
errorHandlerService: getIt(), ChangeNotifierProvider<ImmediateLiveCareViewModel>(
navigationService: getIt(), create: (_) => getIt.get<ImmediateLiveCareViewModel>(),
myAppointmentsViewModel: getIt(),
locationUtils: getIt(),
),
), ),
ChangeNotifierProvider<AuthenticationViewModel>( ChangeNotifierProvider<AuthenticationViewModel>(
create: (_) => AuthenticationViewModel( create: (_) => getIt.get<AuthenticationViewModel>(),
authenticationRepo: getIt(),
appState: getIt(),
dialogService: getIt(),
errorHandlerService: getIt(),
navigationService: getIt(),
cacheService: getIt(),
localAuthService: getIt(),
),
), ),
ChangeNotifierProvider<AppointmentViaRegionViewmodel>( ChangeNotifierProvider<AppointmentViaRegionViewmodel>(
create: (_) => AppointmentViaRegionViewmodel( create: (_) => getIt.get<AppointmentViaRegionViewmodel>(),
navigationService: getIt(), appState: getIt())) ),
ChangeNotifierProvider<LabHistoryViewModel>(
create: (_) => getIt.get<LabHistoryViewModel>(),
),
ChangeNotifierProvider<DateRangeSelectorRangeViewModel>(
create: (_) => getIt.get<DateRangeSelectorRangeViewModel>(),
),
ChangeNotifierProvider<DoctorFilterViewModel>(
create: (_) => getIt.get<DoctorFilterViewModel>(),
)
], child: MyApp()), ], child: MyApp()),
), ),
); );

@ -22,7 +22,7 @@ import 'package:hmg_patient_app_new/presentation/appointments/appointment_paymen
import 'package:hmg_patient_app_new/presentation/appointments/widgets/appointment_checkin_bottom_sheet.dart'; import 'package:hmg_patient_app_new/presentation/appointments/widgets/appointment_checkin_bottom_sheet.dart';
import 'package:hmg_patient_app_new/presentation/appointments/widgets/appointment_doctor_card.dart'; import 'package:hmg_patient_app_new/presentation/appointments/widgets/appointment_doctor_card.dart';
import 'package:hmg_patient_app_new/presentation/book_appointment/widgets/appointment_calendar.dart'; import 'package:hmg_patient_app_new/presentation/book_appointment/widgets/appointment_calendar.dart';
import 'package:hmg_patient_app_new/presentation/lab/collapsing_list_view.dart'; import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
import 'package:hmg_patient_app_new/presentation/prescriptions/prescription_detail_page.dart'; import 'package:hmg_patient_app_new/presentation/prescriptions/prescription_detail_page.dart';
import 'package:hmg_patient_app_new/presentation/prescriptions/prescriptions_list_page.dart'; import 'package:hmg_patient_app_new/presentation/prescriptions/prescriptions_list_page.dart';
import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/theme/colors.dart';
@ -81,33 +81,11 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
// Row(
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
// children: [
// "Appointment Details".needTranslation.toText20(isBold: true),
// if (AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel))
// CustomButton(
// text: "Report".needTranslation,
// onPressed: () {},
// backgroundColor: AppColors.secondaryLightRedColor,
// borderColor: AppColors.secondaryLightRedColor,
// textColor: AppColors.primaryRedColor,
// fontSize: 14,
// fontWeight: FontWeight.w500,
// borderRadius: 12,
// padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
// height: 40.h,
// iconSize: 16.h,
// icon: AppAssets.report_icon,
// iconColor: AppColors.primaryRedColor,
// )
// ],
// ),
// SizedBox(height: 24.h),
AppointmentDoctorCard( AppointmentDoctorCard(
patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel, patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel,
onAskDoctorTap: () {}, onAskDoctorTap: () {},
onCancelTap: () async { onCancelTap: () async {
myAppointmentsViewModel.setIsAppointmentDataToBeLoaded(true);
showCommonBottomSheet(context, showCommonBottomSheet(context,
child: Utils.getLoadingWidget(), child: Utils.getLoadingWidget(),
callBackFunc: (str) {}, callBackFunc: (str) {},
@ -162,42 +140,60 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
? "Not Confirmed".needTranslation.toText12(color: AppColors.primaryRedColor, fontWeight: FontWeight.w500) ? "Not Confirmed".needTranslation.toText12(color: AppColors.primaryRedColor, fontWeight: FontWeight.w500)
: "Confirmed".needTranslation.toText12(color: AppColors.successColor, fontWeight: FontWeight.w500)), : "Confirmed".needTranslation.toText12(color: AppColors.successColor, fontWeight: FontWeight.w500)),
SizedBox(height: 16.h), SizedBox(height: 16.h),
Stack( //TODO Add countdown timer in case of LiveCare Appointment
children: [ widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment ?? false
ClipRRect( ? Row(
clipBehavior: Clip.hardEdge, children: [
borderRadius: BorderRadius.circular(24), Utils.buildSvgWithAssets(icon: AppAssets.livecare_clinic_icon, width: 40.h, height: 40.h),
child: Image.network( SizedBox(width: 12.h),
"https://maps.googleapis.com/maps/api/staticmap?center=${widget.patientAppointmentHistoryResponseModel.latitude},${widget.patientAppointmentHistoryResponseModel.longitude}&zoom=14&size=350x165&maptype=roadmap&markers=color:red%7C${widget.patientAppointmentHistoryResponseModel.latitude},${widget.patientAppointmentHistoryResponseModel.longitude}&key=AIzaSyB6TERnxIr0yJ3qG4ULBZbu0sAD4tGqtng", Expanded(
fit: BoxFit.contain, child: Column(
), crossAxisAlignment: CrossAxisAlignment.start,
), children: [
Positioned( "The doctor will call you once the appointment time approaches."
bottom: 0, .needTranslation
child: SizedBox( .toText14(color: AppColors.greyTextColor, weight: FontWeight.w500),
width: MediaQuery.of(context).size.width * 0.785, ],
child: CustomButton( ),
text: "Get Directions".needTranslation, ),
onPressed: () { ],
MapsLauncher.launchCoordinates(double.parse(widget.patientAppointmentHistoryResponseModel.latitude!), )
double.parse(widget.patientAppointmentHistoryResponseModel.longitude!), widget.patientAppointmentHistoryResponseModel.projectName); : Stack(
}, children: [
backgroundColor: AppColors.textColor.withOpacity(0.8), ClipRRect(
borderColor: AppointmentType.getNextActionButtonColor(widget.patientAppointmentHistoryResponseModel.nextAction).withOpacity(0.01), clipBehavior: Clip.hardEdge,
textColor: AppColors.whiteColor, borderRadius: BorderRadius.circular(24),
fontSize: 14, child: Image.network(
fontWeight: FontWeight.w500, "https://maps.googleapis.com/maps/api/staticmap?center=${widget.patientAppointmentHistoryResponseModel.latitude},${widget.patientAppointmentHistoryResponseModel.longitude}&zoom=14&size=350x165&maptype=roadmap&markers=color:red%7C${widget.patientAppointmentHistoryResponseModel.latitude},${widget.patientAppointmentHistoryResponseModel.longitude}&key=AIzaSyB6TERnxIr0yJ3qG4ULBZbu0sAD4tGqtng",
borderRadius: 12.h, fit: BoxFit.contain,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0), ),
height: 40.h, ),
icon: AppAssets.directions_icon, Positioned(
iconColor: AppColors.whiteColor, bottom: 0,
iconSize: 13.h, child: SizedBox(
).paddingAll(12.h), width: MediaQuery.of(context).size.width * 0.785,
child: CustomButton(
text: "Get Directions".needTranslation,
onPressed: () {
MapsLauncher.launchCoordinates(double.parse(widget.patientAppointmentHistoryResponseModel.latitude!),
double.parse(widget.patientAppointmentHistoryResponseModel.longitude!), widget.patientAppointmentHistoryResponseModel.projectName);
},
backgroundColor: AppColors.textColor.withOpacity(0.8),
borderColor: AppointmentType.getNextActionButtonColor(widget.patientAppointmentHistoryResponseModel.nextAction).withOpacity(0.01),
textColor: AppColors.whiteColor,
fontSize: 14,
fontWeight: FontWeight.w500,
borderRadius: 12.h,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 40.h,
icon: AppAssets.directions_icon,
iconColor: AppColors.whiteColor,
iconSize: 13.h,
).paddingAll(12.h),
),
),
],
), ),
),
],
),
], ],
), ),
), ),
@ -423,7 +419,7 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
"Total amount to pay".needTranslation.toText18(isBold: true), "Amount before tax".needTranslation.toText18(isBold: true),
Utils.getPaymentAmountWithSymbol(widget.patientAppointmentHistoryResponseModel.patientShare!.toString().toText16(isBold: true), AppColors.blackColor, 13, Utils.getPaymentAmountWithSymbol(widget.patientAppointmentHistoryResponseModel.patientShare!.toString().toText16(isBold: true), AppColors.blackColor, 13,
isSaudiCurrency: true), isSaudiCurrency: true),
], ],
@ -476,6 +472,7 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
: CustomButton( : CustomButton(
text: AppointmentType.getNextActionText(widget.patientAppointmentHistoryResponseModel.nextAction), text: AppointmentType.getNextActionText(widget.patientAppointmentHistoryResponseModel.nextAction),
onPressed: () { onPressed: () {
myAppointmentsViewModel.setIsAppointmentDataToBeLoaded(true);
handleAppointmentNextAction(widget.patientAppointmentHistoryResponseModel.nextAction); handleAppointmentNextAction(widget.patientAppointmentHistoryResponseModel.nextAction);
}, },
backgroundColor: AppointmentType.getNextActionButtonColor(widget.patientAppointmentHistoryResponseModel.nextAction), backgroundColor: AppointmentType.getNextActionButtonColor(widget.patientAppointmentHistoryResponseModel.nextAction),

@ -21,15 +21,14 @@ import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/appointments/my_appointments_page.dart'; import 'package:hmg_patient_app_new/presentation/appointments/my_appointments_page.dart';
import 'package:hmg_patient_app_new/presentation/home/navigation_screen.dart'; import 'package:hmg_patient_app_new/presentation/home/navigation_screen.dart';
import 'package:hmg_patient_app_new/presentation/insurance/insurance_home_page.dart'; import 'package:hmg_patient_app_new/presentation/insurance/insurance_home_page.dart';
import 'package:hmg_patient_app_new/presentation/lab/collapsing_list_view.dart'; import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
import 'package:hmg_patient_app_new/services/cache_service.dart'; import 'package:hmg_patient_app_new/services/cache_service.dart';
import 'package:hmg_patient_app_new/theme/colors.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/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart'; import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart';
import 'package:hmg_patient_app_new/widgets/in_app_browser/InAppBrowser.dart'; import 'package:hmg_patient_app_new/widgets/in_app_browser/InAppBrowser.dart';
import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart';
import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart'; import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
import 'package:hmg_patient_app_new/widgets/shimmer/movies_shimmer_widget.dart';
import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:smooth_corner/smooth_corner.dart'; import 'package:smooth_corner/smooth_corner.dart';
@ -52,16 +51,30 @@ class _AppointmentPaymentPageState extends State<AppointmentPaymentPage> {
String transID = ""; String transID = "";
bool isShowTamara = false;
String tamaraPaymentStatus = "";
String tamaraOrderID = "";
@override @override
void initState() { void initState() {
scheduleMicrotask(() { scheduleMicrotask(() {
payfortViewModel.initPayfortViewModel(); payfortViewModel.initPayfortViewModel();
myAppointmentsViewModel.getTamaraInstallmentsDetails().then((val) {
if (myAppointmentsViewModel.patientAppointmentShareResponseModel!.patientShareWithTax! >= myAppointmentsViewModel.getTamaraInstallmentsDetailsResponseModel!.minLimit!.amount! &&
myAppointmentsViewModel.patientAppointmentShareResponseModel!.patientShareWithTax! <= myAppointmentsViewModel.getTamaraInstallmentsDetailsResponseModel!.maxLimit!.amount!) {
setState(() {
isShowTamara = true;
});
}
});
payfortViewModel.setIsApplePayConfigurationLoading(false); payfortViewModel.setIsApplePayConfigurationLoading(false);
myAppointmentsViewModel.getPatientShareAppointment( myAppointmentsViewModel.getPatientShareAppointment(
widget.patientAppointmentHistoryResponseModel.projectID, widget.patientAppointmentHistoryResponseModel.projectID,
widget.patientAppointmentHistoryResponseModel.clinicID, widget.patientAppointmentHistoryResponseModel.clinicID,
widget.patientAppointmentHistoryResponseModel.appointmentNo.toString(), widget.patientAppointmentHistoryResponseModel.appointmentNo.toString(), widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment ?? false, onError: (err) {
); Navigator.of(context).pop();
Navigator.of(context).pop();
});
}); });
super.initState(); super.initState();
} }
@ -106,7 +119,7 @@ class _AppointmentPaymentPageState extends State<AppointmentPaymentPage> {
Transform.flip( Transform.flip(
flipX: appState.isArabic() ? true : false, flipX: appState.isArabic() ? true : false,
child: Utils.buildSvgWithAssets( child: Utils.buildSvgWithAssets(
icon: AppAssets.forward_arrow_icon, icon: AppAssets.forward_arrow_icon_small,
iconColor: AppColors.blackColor, iconColor: AppColors.blackColor,
width: 18.h, width: 18.h,
height: 13.h, height: 13.h,
@ -148,7 +161,7 @@ class _AppointmentPaymentPageState extends State<AppointmentPaymentPage> {
Transform.flip( Transform.flip(
flipX: appState.isArabic() ? true : false, flipX: appState.isArabic() ? true : false,
child: Utils.buildSvgWithAssets( child: Utils.buildSvgWithAssets(
icon: AppAssets.forward_arrow_icon, icon: AppAssets.forward_arrow_icon_small,
iconColor: AppColors.blackColor, iconColor: AppColors.blackColor,
width: 18.h, width: 18.h,
height: 13.h, height: 13.h,
@ -162,11 +175,12 @@ class _AppointmentPaymentPageState extends State<AppointmentPaymentPage> {
openPaymentURL("visa"); openPaymentURL("visa");
}), }),
SizedBox(height: 16.h), SizedBox(height: 16.h),
Container( isShowTamara
decoration: RoundedRectangleBorder().toSmoothCornerDecoration( ? Container(
color: AppColors.whiteColor, decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
borderRadius: 20.h, color: AppColors.whiteColor,
hasShadow: false, borderRadius: 20.h,
hasShadow: false,
), ),
child: Row( child: Row(
mainAxisSize: MainAxisSize.max, mainAxisSize: MainAxisSize.max,
@ -184,19 +198,20 @@ class _AppointmentPaymentPageState extends State<AppointmentPaymentPage> {
Transform.flip( Transform.flip(
flipX: appState.isArabic() ? true : false, flipX: appState.isArabic() ? true : false,
child: Utils.buildSvgWithAssets( child: Utils.buildSvgWithAssets(
icon: AppAssets.forward_arrow_icon, icon: AppAssets.forward_arrow_icon_small,
iconColor: AppColors.blackColor, iconColor: AppColors.blackColor,
width: 18.h, width: 18.h,
height: 13.h, height: 13.h,
fit: BoxFit.contain, fit: BoxFit.contain,
).toShimmer2(isShow: myAppointmentsVM.isAppointmentPatientShareLoading), ).toShimmer2(isShow: myAppointmentsVM.isAppointmentPatientShareLoading),
), ),
], ],
).paddingSymmetrical(16.h, 16.h), ).paddingSymmetrical(16.h, 16.h),
).paddingSymmetrical(24.h, 0.h).onPress(() { ).paddingSymmetrical(24.h, 0.h).onPress(() {
selectedPaymentMethod = "TAMARA"; selectedPaymentMethod = "TAMARA";
openPaymentURL("tamara"); openPaymentURL("tamara");
}), })
: SizedBox.shrink(),
], ],
), ),
), ),
@ -257,7 +272,7 @@ class _AppointmentPaymentPageState extends State<AppointmentPaymentPage> {
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
"Total amount to pay".needTranslation.toText14(isBold: true), "Amount before tax".needTranslation.toText14(isBold: true),
Utils.getPaymentAmountWithSymbol(myAppointmentsVM.patientAppointmentShareResponseModel!.patientShare!.toString().toText16(isBold: true), AppColors.blackColor, 13, Utils.getPaymentAmountWithSymbol(myAppointmentsVM.patientAppointmentShareResponseModel!.patientShare!.toString().toText16(isBold: true), AppColors.blackColor, 13,
isSaudiCurrency: true), isSaudiCurrency: true),
], ],
@ -293,7 +308,7 @@ class _AppointmentPaymentPageState extends State<AppointmentPaymentPage> {
if (Utils.havePrivilege(103)) { if (Utils.havePrivilege(103)) {
startApplePay(); startApplePay();
} else { } else {
openPaymentURL(selectedPaymentMethod); openPaymentURL("ApplePay");
} }
}) })
: SizedBox(height: 12.h), : SizedBox(height: 12.h),
@ -315,12 +330,12 @@ class _AppointmentPaymentPageState extends State<AppointmentPaymentPage> {
if (selectedPaymentMethod == "tamara") { if (selectedPaymentMethod == "tamara") {
if (Platform.isAndroid) { if (Platform.isAndroid) {
Uri uri = new Uri.dataFromString(url); Uri uri = new Uri.dataFromString(url);
// tamaraPaymentStatus = uri.queryParameters['status']!; tamaraPaymentStatus = uri.queryParameters['status']!;
// tamaraOrderID = uri.queryParameters['AuthorizePaymentId']!; tamaraOrderID = uri.queryParameters['AuthorizePaymentId']!;
} else { } else {
Uri uri = new Uri.dataFromString(url); Uri uri = new Uri.dataFromString(url);
// tamaraPaymentStatus = uri.queryParameters['paymentStatus']!; tamaraPaymentStatus = uri.queryParameters['paymentStatus']!;
// tamaraOrderID = uri.queryParameters['orderId']!; tamaraOrderID = uri.queryParameters['orderId']!;
} }
} }
@ -346,81 +361,155 @@ class _AppointmentPaymentPageState extends State<AppointmentPaymentPage> {
} }
onBrowserExit(bool isPaymentMade) async { onBrowserExit(bool isPaymentMade) async {
print("onBrowserExit Called!!!!"); checkPaymentStatus();
if (selectedPaymentMethod == "TAMARA") {
// checkTamaraPaymentStatus(transID!, appo);
// if (tamaraPaymentStatus != null && tamaraPaymentStatus.toLowerCase() == "approved") {
// updateTamaraRequestStatus("success", "14", Utils.getAppointmentTransID(appo.projectID, appo.clinicID, appo.appointmentNo), tamaraOrderID, num.parse(selectedInstallments), appo);
// } else {
// updateTamaraRequestStatus("Failed", "00", Utils.getAppointmentTransID(appo.projectID, appo.clinicID, appo.appointmentNo), tamaraOrderID, num.parse(selectedInstallments), appo);
// }
} else {
checkPaymentStatus();
// checkPaymentStatus(appo);
}
} }
void checkPaymentStatus() async { void checkPaymentStatus() async {
showCommonBottomSheet(context, LoaderBottomSheet.showLoader(loadingText: "Checking payment status, Please wait...".needTranslation);
child: Utils.getLoadingWidget(), callBackFunc: (str) {}, title: "", height: ResponsiveExtension.screenHeight * 0.3, isCloseButtonVisible: false, isDismissible: false, isFullScreen: false); if (selectedPaymentMethod == "TAMARA") {
await payfortViewModel.checkPaymentStatus( await payfortViewModel.checkTamaraPaymentStatus(
transactionID: transID, transactionID: transID,
onSuccess: (apiResponse) async { onSuccess: (apiResponse) async {
print(apiResponse.data); if (apiResponse.data["status"].toString().toLowerCase() == "success") {
if (payfortViewModel.payfortCheckPaymentStatusResponseModel!.responseMessage!.toLowerCase() == "success") { tamaraOrderID = apiResponse.data["tamara_order_id"].toString();
await myAppointmentsViewModel.createAdvancePayment( await payfortViewModel.updateTamaraRequestStatus(responseMessage: "success", status: "14", clientRequestID: transID, tamaraOrderID: tamaraOrderID);
paymentMethodName: selectedPaymentMethod, await payfortViewModel.markAppointmentAsTamaraPaid(
projectID: widget.patientAppointmentHistoryResponseModel.projectID, projectID: widget.patientAppointmentHistoryResponseModel.projectID, appointmentNo: widget.patientAppointmentHistoryResponseModel.appointmentNo);
clinicID: widget.patientAppointmentHistoryResponseModel.clinicID, await myAppointmentsViewModel.addAdvanceNumberRequest(
appointmentNo: widget.patientAppointmentHistoryResponseModel.appointmentNo.toString(), advanceNumber: "Tamara-Advance-0000",
payedAmount: payfortViewModel.payfortCheckPaymentStatusResponseModel!.amount!, paymentReference: tamaraOrderID,
paymentReference: payfortViewModel.payfortCheckPaymentStatusResponseModel!.fortId!, appointmentNo: widget.patientAppointmentHistoryResponseModel.appointmentNo.toString(),
patientID: appState.getAuthenticatedUser()!.patientId.toString(), onSuccess: (value) async {
patientType: appState.getAuthenticatedUser()!.patientType!, if (widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment!) {
onSuccess: (value) async { //TODO: Implement LiveCare Check-In API Call
print(value); await myAppointmentsViewModel.insertLiveCareVIDARequest(
await myAppointmentsViewModel.addAdvanceNumberRequest( clientRequestID: tamaraOrderID,
advanceNumber: Utils.isVidaPlusProject(widget.patientAppointmentHistoryResponseModel.projectID) patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel,
? value.data['OnlineCheckInAppointments'][0]['AdvanceNumber_VP'].toString() onSuccess: (apiResponse) {
: value.data['OnlineCheckInAppointments'][0]['AdvanceNumber'].toString(), Future.delayed(Duration(milliseconds: 500), () {
paymentReference: payfortViewModel.payfortCheckPaymentStatusResponseModel!.fortId!, LoaderBottomSheet.hideLoader();
appointmentNo: widget.patientAppointmentHistoryResponseModel.appointmentNo.toString(), Navigator.pushAndRemoveUntil(
onSuccess: (value) async { context,
if (widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment!) { CustomPageRoute(
//TODO: Implement LiveCare Check-In API Call page: LandingNavigation(),
} else { ),
await myAppointmentsViewModel.generateAppointmentQR( (r) => false);
clinicID: widget.patientAppointmentHistoryResponseModel.clinicID, });
projectID: widget.patientAppointmentHistoryResponseModel.projectID, },
appointmentNo: widget.patientAppointmentHistoryResponseModel.appointmentNo.toString(), onError: (error) {});
isFollowUp: myAppointmentsViewModel.patientAppointmentShareResponseModel!.isFollowup!, } else {
onSuccess: (apiResponse) { await myAppointmentsViewModel.generateAppointmentQR(
Future.delayed(Duration(milliseconds: 500), () { clinicID: widget.patientAppointmentHistoryResponseModel.clinicID,
Navigator.of(context).pop(); projectID: widget.patientAppointmentHistoryResponseModel.projectID,
Navigator.pushAndRemoveUntil( appointmentNo: widget.patientAppointmentHistoryResponseModel.appointmentNo.toString(),
context, isFollowUp: myAppointmentsViewModel.patientAppointmentShareResponseModel!.isFollowup!,
CustomPageRoute( onSuccess: (apiResponse) {
page: LandingNavigation(), Future.delayed(Duration(milliseconds: 500), () {
), LoaderBottomSheet.hideLoader();
(r) => false); Navigator.pushAndRemoveUntil(
Navigator.of(context).push( context,
CustomPageRoute(page: MyAppointmentsPage()), CustomPageRoute(
); page: LandingNavigation(),
}); ),
}); (r) => false);
} });
}); });
}); }
} else { });
} 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( showCommonBottomSheetWithoutHeight(
context, context,
child: Utils.getErrorWidget(loadingText: "Payment Failed! Please try again.".needTranslation), child: Utils.getErrorWidget(loadingText: err),
callBackFunc: () {}, callBackFunc: () {},
isFullScreen: false, isFullScreen: false,
isCloseButtonVisible: true, 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) { openPaymentURL(String paymentMethod) {

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

@ -10,7 +10,7 @@ import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_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/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/lab/collapsing_list_view.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/theme/colors.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';

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

@ -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/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/string_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/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/medical_file/medical_file_view_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/patient_appointment_history_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/my_appointments_view_model.dart'; import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/utils/appointment_type.dart'; import 'package:hmg_patient_app_new/features/my_appointments/utils/appointment_type.dart';
@ -18,14 +19,24 @@ import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart'; import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart';
import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.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:hmg_patient_app_new/widgets/transitions/fade_page.dart';
import 'package:smooth_corner/smooth_corner.dart';
class AppointmentCard extends StatefulWidget { class AppointmentCard extends StatefulWidget {
AppointmentCard({super.key, required this.patientAppointmentHistoryResponseModel, required this.myAppointmentsViewModel, this.isLoading = false, this.isFromHomePage = false}); AppointmentCard(
{super.key,
required this.patientAppointmentHistoryResponseModel,
required this.myAppointmentsViewModel,
this.isLoading = false,
this.isFromHomePage = false,
this.isFromMedicalReport = false,
this.medicalFileViewModel});
PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel; PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel;
MyAppointmentsViewModel myAppointmentsViewModel; MyAppointmentsViewModel myAppointmentsViewModel;
bool isLoading; bool isLoading;
bool isFromHomePage; bool isFromHomePage;
bool isFromMedicalReport;
MedicalFileViewModel? medicalFileViewModel;
@override @override
State<AppointmentCard> createState() => _AppointmentCardState(); State<AppointmentCard> createState() => _AppointmentCardState();
@ -63,44 +74,38 @@ class _AppointmentCardState extends State<AppointmentCard> {
spacing: 6.h, spacing: 6.h,
runSpacing: 6.h, runSpacing: 6.h,
children: [ children: [
Row( AppCustomChipWidget(
mainAxisSize: MainAxisSize.min, icon: widget.isLoading
children: [ ? AppAssets.walkin_appointment_icon
CustomButton( : (!widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment! ? AppAssets.walkin_appointment_icon : AppAssets.small_livecare_icon),
text: widget.isLoading iconColor: widget.isLoading
? "OutPatient" ? AppColors.textColor
: appState.isArabic() : !widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment!
? widget.patientAppointmentHistoryResponseModel.isInOutPatientDescriptionN! ? AppColors.textColor
: widget.patientAppointmentHistoryResponseModel.isInOutPatientDescription!, : AppColors.whiteColor,
onPressed: () {}, labelText: widget.isLoading
backgroundColor: AppColors.primaryRedColor.withOpacity(0.1), ? "Walk In"
borderColor: AppColors.primaryRedColor.withOpacity(0.0), : widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment!
textColor: AppColors.primaryRedColor, ? LocaleKeys.livecare.tr(context: context)
fontSize: 10, : "Walk In".needTranslation,
fontWeight: FontWeight.w500, backgroundColor:
borderRadius: 8, widget.isLoading ? AppColors.greyColor : (!widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment! ? AppColors.greyColor : AppColors.successColor),
padding: EdgeInsets.fromLTRB(10, 0, 10, 0), textColor: widget.isLoading ? AppColors.textColor : (!widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment! ? AppColors.textColor : AppColors.whiteColor),
height: 30.h, ).toShimmer2(isShow: widget.isLoading),
), AppCustomChipWidget(
], labelText: widget.isLoading
), ? "OutPatient"
Row( : appState.isArabic()
mainAxisSize: MainAxisSize.min, ? widget.patientAppointmentHistoryResponseModel.isInOutPatientDescriptionN!
children: [ : widget.patientAppointmentHistoryResponseModel.isInOutPatientDescription!,
CustomButton( backgroundColor: AppColors.primaryRedColor.withOpacity(0.1),
text: widget.isLoading ? "Booked" : AppointmentType.getAppointmentStatusType(widget.patientAppointmentHistoryResponseModel.patientStatusType!), textColor: AppColors.primaryRedColor,
onPressed: () {}, ).toShimmer2(isShow: widget.isLoading),
backgroundColor: AppColors.successColor.withOpacity(0.1), AppCustomChipWidget(
borderColor: AppColors.successColor.withOpacity(0.0), labelText: widget.isLoading ? "Booked" : AppointmentType.getAppointmentStatusType(widget.patientAppointmentHistoryResponseModel.patientStatusType!),
textColor: AppColors.successColor, backgroundColor: AppColors.successColor.withOpacity(0.1),
fontSize: 10, textColor: AppColors.successColor,
fontWeight: FontWeight.w500, ).toShimmer2(isShow: widget.isLoading),
borderRadius: 8,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 30.h,
),
],
),
], ],
).toShimmer2(isShow: widget.isLoading), ).toShimmer2(isShow: widget.isLoading),
), ),
@ -162,9 +167,11 @@ class _AppointmentCardState extends State<AppointmentCard> {
labelText: labelText:
widget.isLoading ? "Cardiology" : DateUtil.formatDateToDate(DateUtil.convertStringToDate(widget.patientAppointmentHistoryResponseModel.appointmentDate), false)) widget.isLoading ? "Cardiology" : DateUtil.formatDateToDate(DateUtil.convertStringToDate(widget.patientAppointmentHistoryResponseModel.appointmentDate), false))
.toShimmer2(isShow: widget.isLoading), .toShimmer2(isShow: widget.isLoading),
AppCustomChipWidget( widget.isFromMedicalReport
icon: AppAssets.appointment_time_icon, ? SizedBox.shrink()
labelText: widget.isLoading : AppCustomChipWidget(
icon: AppAssets.appointment_time_icon,
labelText: widget.isLoading
? "Cardiology" ? "Cardiology"
: DateUtil.formatDateToTimeLang(DateUtil.convertStringToDate(widget.patientAppointmentHistoryResponseModel.appointmentDate), false)) : DateUtil.formatDateToTimeLang(DateUtil.convertStringToDate(widget.patientAppointmentHistoryResponseModel.appointmentDate), false))
.toShimmer2(isShow: widget.isLoading), .toShimmer2(isShow: widget.isLoading),
@ -176,75 +183,91 @@ class _AppointmentCardState extends State<AppointmentCard> {
], ],
), ),
SizedBox(height: 16.h), SizedBox(height: 16.h),
Row( widget.isFromMedicalReport
children: [ ? CustomButton(
Expanded( text: "Select appointment".needTranslation,
flex: 6, onPressed: () {
child: AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel) widget.medicalFileViewModel!.setSelectedMedicalReportAppointment(widget.patientAppointmentHistoryResponseModel);
? getArrivedAppointmentButton().toShimmer2(isShow: widget.isLoading) Navigator.pop(context, false);
: CustomButton( },
text: AppointmentType.getNextActionText(widget.patientAppointmentHistoryResponseModel.nextAction), backgroundColor: AppColors.secondaryLightRedColor,
onPressed: () { borderColor: AppColors.secondaryLightRedColor,
Navigator.of(context) textColor: AppColors.primaryRedColor,
.push(CustomPageRoute( fontSize: 14,
page: AppointmentDetailsPage(patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel), fontWeight: FontWeight.w500,
)) borderRadius: 12,
.then((val) { padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
widget.myAppointmentsViewModel.initAppointmentsViewModel();
widget.myAppointmentsViewModel.getPatientAppointments(true, false);
});
},
backgroundColor: AppointmentType.getNextActionButtonColor(widget.patientAppointmentHistoryResponseModel.nextAction).withOpacity(0.15),
borderColor: AppointmentType.getNextActionButtonColor(widget.patientAppointmentHistoryResponseModel.nextAction).withOpacity(0.01),
textColor: AppointmentType.getNextActionTextColor(widget.patientAppointmentHistoryResponseModel.nextAction),
fontSize: 14,
fontWeight: FontWeight.w500,
borderRadius: 12,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 40.h,
icon: AppointmentType.getNextActionIcon(widget.patientAppointmentHistoryResponseModel.nextAction),
iconColor: AppointmentType.getNextActionTextColor(widget.patientAppointmentHistoryResponseModel.nextAction),
iconSize: 15.h,
).toShimmer2(isShow: widget.isLoading),
),
SizedBox(width: 8.h),
Expanded(
flex: 1,
child: Container(
height: 40.h, height: 40.h,
width: 40.h, icon: AppAssets.checkmark_icon,
decoration: RoundedRectangleBorder().toSmoothCornerDecoration( iconColor: AppColors.primaryRedColor,
color: AppColors.textColor, iconSize: 16.h,
borderRadius: 10.h, )
), : Row(
child: Padding( children: [
padding: EdgeInsets.all(10.h), Expanded(
child: Transform.flip( flex: 6,
flipX: appState.isArabic() ? true : false, child: AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel)
child: Utils.buildSvgWithAssets( ? getArrivedAppointmentButton().toShimmer2(isShow: widget.isLoading)
icon: AppAssets.forward_arrow_icon, : CustomButton(
iconColor: AppColors.whiteColor, text: AppointmentType.getNextActionText(widget.patientAppointmentHistoryResponseModel.nextAction),
width: 10.h, onPressed: () {
height: 10.h, Navigator.of(context)
fit: BoxFit.contain, .push(CustomPageRoute(
), page: AppointmentDetailsPage(patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel),
))
.then((val) {
widget.myAppointmentsViewModel.initAppointmentsViewModel();
widget.myAppointmentsViewModel.getPatientAppointments(true, false);
});
},
backgroundColor: AppointmentType.getNextActionButtonColor(widget.patientAppointmentHistoryResponseModel.nextAction).withOpacity(0.15),
borderColor: AppointmentType.getNextActionButtonColor(widget.patientAppointmentHistoryResponseModel.nextAction).withOpacity(0.01),
textColor: AppointmentType.getNextActionTextColor(widget.patientAppointmentHistoryResponseModel.nextAction),
fontSize: 14,
fontWeight: FontWeight.w500,
borderRadius: 12,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 40.h,
icon: AppointmentType.getNextActionIcon(widget.patientAppointmentHistoryResponseModel.nextAction),
iconColor: AppointmentType.getNextActionTextColor(widget.patientAppointmentHistoryResponseModel.nextAction),
iconSize: 15.h,
).toShimmer2(isShow: widget.isLoading),
), ),
), SizedBox(width: 8.h),
).toShimmer2(isShow: widget.isLoading).onPress(() { Expanded(
Navigator.of(context) flex: 1,
.push( child: Container(
CustomPageRoute( height: 40.h,
page: AppointmentDetailsPage(patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel), width: 40.h,
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.textColor,
borderRadius: 10.h,
),
child: Transform.flip(
flipX: appState.isArabic() ? true : false,
child: Utils.buildSvgWithAssets(
icon: AppAssets.forward_arrow_icon,
iconColor: AppColors.whiteColor,
width: 40.h,
height: 40.h,
fit: BoxFit.cover,
),
),
).toShimmer2(isShow: widget.isLoading).onPress(() {
Navigator.of(context)
.push(
CustomPageRoute(
page: AppointmentDetailsPage(patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel),
),
)
.then((val) {
widget.myAppointmentsViewModel.initAppointmentsViewModel();
widget.myAppointmentsViewModel.getPatientAppointments(true, false);
});
}),
), ),
) ],
.then((val) { ),
widget.myAppointmentsViewModel.initAppointmentsViewModel();
widget.myAppointmentsViewModel.getPatientAppointments(true, false);
});
}),
),
],
),
], ],
), ),
), ),

@ -124,7 +124,7 @@ class AppointmentCheckinBottomSheet extends StatelessWidget {
Transform.flip( Transform.flip(
flipX: appState.isArabic() ? true : false, flipX: appState.isArabic() ? true : false,
child: Utils.buildSvgWithAssets( child: Utils.buildSvgWithAssets(
icon: AppAssets.forward_arrow_icon, icon: AppAssets.forward_arrow_icon_small,
iconColor: AppColors.blackColor, iconColor: AppColors.blackColor,
width: 18.h, width: 18.h,
height: 13.h, height: 13.h,

@ -59,6 +59,13 @@ class AppointmentDoctorCard extends StatelessWidget {
icon: AppAssets.doctor_calendar_icon, icon: AppAssets.doctor_calendar_icon,
labelText: labelText:
"${DateUtil.formatDateToDate(DateUtil.convertStringToDate(patientAppointmentHistoryResponseModel.appointmentDate), false)}, ${DateUtil.formatDateToTimeLang(DateUtil.convertStringToDate(patientAppointmentHistoryResponseModel.appointmentDate), false)}"), "${DateUtil.formatDateToDate(DateUtil.convertStringToDate(patientAppointmentHistoryResponseModel.appointmentDate), false)}, ${DateUtil.formatDateToTimeLang(DateUtil.convertStringToDate(patientAppointmentHistoryResponseModel.appointmentDate), false)}"),
AppCustomChipWidget(
icon: !patientAppointmentHistoryResponseModel.isLiveCareAppointment! ? AppAssets.walkin_appointment_icon : AppAssets.small_livecare_icon,
iconColor: !patientAppointmentHistoryResponseModel.isLiveCareAppointment! ? AppColors.textColor : AppColors.whiteColor,
labelText: patientAppointmentHistoryResponseModel.isLiveCareAppointment! ? LocaleKeys.livecare.tr(context: context) : "Walk In".needTranslation,
backgroundColor: !patientAppointmentHistoryResponseModel.isLiveCareAppointment! ? AppColors.greyColor : AppColors.successColor,
textColor: !patientAppointmentHistoryResponseModel.isLiveCareAppointment! ? AppColors.textColor : AppColors.whiteColor,
),
AppCustomChipWidget(icon: AppAssets.rating_icon, iconColor: AppColors.ratingColorYellow, labelText: "Rating: ${patientAppointmentHistoryResponseModel.decimalDoctorRate}"), AppCustomChipWidget(icon: AppAssets.rating_icon, iconColor: AppColors.ratingColorYellow, labelText: "Rating: ${patientAppointmentHistoryResponseModel.decimalDoctorRate}"),
], ],
), ),
@ -113,29 +120,8 @@ class AppointmentDoctorCard extends StatelessWidget {
iconSize: 16.h, iconSize: 16.h,
); );
} else { } else {
return Row( return patientAppointmentHistoryResponseModel.isLiveCareAppointment ?? false
children: [ ? CustomButton(
Expanded(
child: CustomButton(
text: LocaleKeys.reschedule.tr(),
onPressed: () {
onRescheduleTap();
},
backgroundColor: AppColors.secondaryLightRedColor,
borderColor: AppColors.secondaryLightRedColor,
textColor: AppColors.primaryRedColor,
fontSize: 14,
fontWeight: FontWeight.w500,
borderRadius: 12.h,
height: 40.h,
icon: AppAssets.appointment_calendar_icon,
iconColor: AppColors.primaryRedColor,
iconSize: 16.h,
),
),
SizedBox(width: 16.h),
Expanded(
child: CustomButton(
text: LocaleKeys.cancel.tr(), text: LocaleKeys.cancel.tr(),
onPressed: () { onPressed: () {
onCancelTap(); onCancelTap();
@ -150,10 +136,48 @@ class AppointmentDoctorCard extends StatelessWidget {
icon: AppAssets.cancel, icon: AppAssets.cancel,
iconColor: AppColors.whiteColor, iconColor: AppColors.whiteColor,
iconSize: 16.h, iconSize: 16.h,
), )
), : Row(
], children: [
); Expanded(
child: CustomButton(
text: LocaleKeys.reschedule.tr(),
onPressed: () {
onRescheduleTap();
},
backgroundColor: AppColors.secondaryLightRedColor,
borderColor: AppColors.secondaryLightRedColor,
textColor: AppColors.primaryRedColor,
fontSize: 14,
fontWeight: FontWeight.w500,
borderRadius: 12.h,
height: 40.h,
icon: AppAssets.appointment_calendar_icon,
iconColor: AppColors.primaryRedColor,
iconSize: 16.h,
),
),
SizedBox(width: 16.h),
Expanded(
child: CustomButton(
text: LocaleKeys.cancel.tr(),
onPressed: () {
onCancelTap();
},
backgroundColor: AppColors.primaryRedColor,
borderColor: AppColors.primaryRedColor,
textColor: AppColors.whiteColor,
fontSize: 14,
fontWeight: FontWeight.w500,
borderRadius: 12.h,
height: 40.h,
icon: AppAssets.cancel,
iconColor: AppColors.whiteColor,
iconSize: 16.h,
),
),
],
);
} }
} }
} }

@ -48,7 +48,7 @@ class FacilitySelectionItem extends StatelessWidget {
Transform.flip( Transform.flip(
flipX: appState.isArabic() ? true : false, flipX: appState.isArabic() ? true : false,
child: Utils.buildSvgWithAssets( child: Utils.buildSvgWithAssets(
icon: AppAssets.forward_arrow_icon, icon: AppAssets.forward_arrow_icon_small,
iconColor: AppColors.blackColor, iconColor: AppColors.blackColor,
width: 18, width: 18,
height: 13, height: 13,

@ -12,7 +12,7 @@ import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_vie
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/appointments/widgets/hospital_bottom_sheet/hospital_list_items.dart'; import 'package:hmg_patient_app_new/presentation/appointments/widgets/hospital_bottom_sheet/hospital_list_items.dart';
import 'package:hmg_patient_app_new/presentation/appointments/widgets/hospital_bottom_sheet/type_selection_widget.dart'; import 'package:hmg_patient_app_new/presentation/appointments/widgets/hospital_bottom_sheet/type_selection_widget.dart';
import 'package:hmg_patient_app_new/presentation/lab/collapsing_list_view.dart'; import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
import 'package:hmg_patient_app_new/theme/colors.dart' show AppColors; import 'package:hmg_patient_app_new/theme/colors.dart' show AppColors;
import 'package:hmg_patient_app_new/widgets/input_widget.dart'; import 'package:hmg_patient_app_new/widgets/input_widget.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
@ -96,9 +96,18 @@ class HospitalBottomSheetBody extends StatelessWidget {
isLocationEnabled: appointmentsViewModel.isLocationEnabled(), isLocationEnabled: appointmentsViewModel.isLocationEnabled(),
).onPress(() { ).onPress(() {
regionalViewModel.setHospitalModel(hospital); regionalViewModel.setHospitalModel(hospital);
regionalViewModel.setBottomSheetState(AppointmentViaRegionState.CLINIC_SELECTION); if (regionalViewModel.regionBottomSheetType == RegionBottomSheetType.FOR_REGION) {
regionalViewModel.handleLastStep(); regionalViewModel.setBottomSheetState(AppointmentViaRegionState.CLINIC_SELECTION);
});}, regionalViewModel.handleLastStepForRegion();
} else if (regionalViewModel.regionBottomSheetType == RegionBottomSheetType.FOR_CLINIIC) {
regionalViewModel.setBottomSheetState(AppointmentViaRegionState.DOCTOR_SELECTION);
regionalViewModel.handleLastStepForClinic();
} else if (regionalViewModel.regionBottomSheetType == RegionBottomSheetType.REGION_FOR_DENTAL_AND_LASER) {
regionalViewModel.setBottomSheetState(AppointmentViaRegionState.DOCTOR_SELECTION);
regionalViewModel.handleLastStepForClinicForDentalAndLaser();
// regionalViewModel.handleLastStepForClinic();
}
});},
separatorBuilder: (_, __) => SizedBox( separatorBuilder: (_, __) => SizedBox(
height: 16.h, height: 16.h,
), ),

@ -0,0 +1,66 @@
import 'package:easy_localization/easy_localization.dart'
show tr, StringTranslateExtension;
import 'package:flutter/material.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/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/doctor_filter/doctor_filter_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/models/facility_selection.dart';
import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/appointments/widgets/hospital_bottom_sheet/hospital_list_items.dart';
import 'package:hmg_patient_app_new/presentation/appointments/widgets/hospital_bottom_sheet/type_selection_widget.dart';
import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
import 'package:hmg_patient_app_new/theme/colors.dart' show AppColors;
import 'package:hmg_patient_app_new/widgets/input_widget.dart';
import 'package:provider/provider.dart';
class HospitalBottomSheetBodyForDoctorFilter extends StatelessWidget {
late BookAppointmentsViewModel appointmentsViewModel;
late AppointmentViaRegionViewmodel regionalViewModel;
final TextEditingController searchText = TextEditingController();
HospitalBottomSheetBodyForDoctorFilter({super.key});
@override
Widget build(BuildContext context) {
appointmentsViewModel = Provider.of<BookAppointmentsViewModel>(context);
regionalViewModel = Provider.of<AppointmentViaRegionViewmodel>(context);
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
LocaleKeys.selectHospital.tr(),
style: TextStyle(
fontSize: 21,
fontWeight: FontWeight.w600,
color: AppColors.blackColor,
),
),
SizedBox(height: 24.h),
SizedBox(
height: MediaQuery.sizeOf(context).height * .4,
child: ListView.separated(
itemBuilder: (_, index)
{
var hospital = appointmentsViewModel.searchedPatientDoctorAppointmentHospitalsList[index];
return HospitalListItem(
hospitalData: hospital,
isLocationEnabled: appointmentsViewModel.isLocationEnabled(),
).onPress(() {
regionalViewModel.setHospitalModel(hospital);
context.read<DoctorFilterViewModel>().setSelectedHospital(hospital);
Navigator.pop(context);
});},
separatorBuilder: (_, __) => SizedBox(
height: 16.h,
),
itemCount: appointmentsViewModel.searchedPatientDoctorAppointmentHospitalsList?.length ?? 0),
)
],
);
}
}

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

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save