Compare commits

...

19 Commits

Author SHA1 Message Date
aamir-csol 5de8314f3f otp screen & register Uae & resend Activation Code. 1 month ago
aamir-csol b9b0dfd1fa Merge branch 'master' into dev_aamir
# Conflicts:
#	assets/langs/ar-SA.json
#	assets/langs/en-US.json
#	lib/generated/locale_keys.g.dart
1 month ago
aamir-csol 15c07cb4e2 otp screen & register Uae & resend Activation Code. 1 month ago
Haroon6138 88f56885ff Merge pull request 'haroon_dev' (#39) from haroon_dev into master
Reviewed-on: #39
1 month ago
haroon amjad 71a07eed8e Merge branch 'master' into haroon_dev 1 month ago
Haroon6138 6a4650d658 Merge pull request 'feature/search_by_region' (#38) from feature/search_by_region into master
Reviewed-on: #38
1 month ago
taha.alam ea2d2bf40f retaining the old data. 1 month ago
taha.alam 788887a7f1 Merge remote-tracking branch 'origin/master' into feature/search_by_region
# Conflicts:
#	assets/langs/ar-SA.json
#	assets/langs/en-US.json
#	lib/core/app_assets.dart
#	lib/generated/locale_keys.g.dart
#	lib/presentation/book_appointment/book_appointment_page.dart
#	lib/presentation/home/navigation_screen.dart
1 month ago
taha.alam 2174e0e292 viewmodel and repo updated for the appointment by region selection. 1 month ago
taha.alam 9c2dd721b4 bottom sheets added of region facitlity and hospital. 1 month ago
haroon amjad f9e7c8988a updates 1 month ago
Haroon6138 9d852aa281 Merge pull request 'no message' (#37) from dev_sultan into master
Reviewed-on: #37
1 month ago
Sultan khan b78c78ed8b no message 1 month ago
Haroon6138 a2f60aead1 Merge pull request 'dev_sultan' (#36) from dev_sultan into master
Reviewed-on: #36
1 month ago
Sultan khan 6b766e558d Merge branch 'master' of http://34.17.52.180/Haroon6138/HMG_Patient_App_New into dev_sultan
# Conflicts:
#	assets/langs/ar-SA.json
#	assets/langs/en-US.json
#	lib/core/app_assets.dart
#	lib/generated/locale_keys.g.dart
#	lib/presentation/home/landing_page.dart
#	lib/presentation/home/navigation_screen.dart
1 month ago
Sultan khan 286dfaa376 no message 1 month ago
Haroon6138 2cc331c1c8 Merge pull request 'dev_aamir' (#35) from dev_aamir into master
Reviewed-on: #35
1 month ago
Sultan khan 009fa1859e Merge branch 'dev_aamir' of http://34.17.52.180/Haroon6138/HMG_Patient_App_New into dev_sultan 1 month ago
Sultan khan fdd5d4cf6d fixes 1 month ago

@ -0,0 +1,7 @@
<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="M17.9973 6.74805C16.4813 6.74805 15.2473 5.51405 15.2473 3.99805C15.2473 2.48205 16.4813 1.24805 17.9973 1.24805C19.5133 1.24805 20.7473 2.48205 20.7473 3.99805C20.7473 5.51405 19.5133 6.74805 17.9973 6.74805ZM17.9973 2.74805C17.3083 2.74805 16.7473 3.30905 16.7473 3.99805C16.7473 4.68705 17.3083 5.24805 17.9973 5.24805C18.6863 5.24805 19.2473 4.68705 19.2473 3.99805C19.2473 3.30905 18.6863 2.74805 17.9973 2.74805Z" fill="#2B353E"/>
<path d="M3.9805 22.748C4.0375 22.75 4.0945 22.7501 4.1515 22.7501C5.3445 22.7501 6.31645 22.4411 7.19945 21.7851L7.31242 21.7045C7.39998 21.6421 7.48647 21.5804 7.55453 21.5351C7.65353 21.5731 7.78854 21.6271 7.92054 21.6811L8.07152 21.7421C8.60452 21.9621 9.21954 22.102 9.79554 22.137C11.2485 22.239 12.7885 22.239 14.2485 22.137C18.6245 21.843 22.1965 18.4601 22.7425 14.0921C22.7945 13.6811 22.5025 13.3061 22.0915 13.2551C21.6795 13.2011 21.3055 13.4951 21.2545 13.9061C20.7975 17.5631 17.8085 20.395 14.1465 20.641C12.7525 20.739 11.2845 20.739 9.89349 20.641C9.47149 20.616 9.02749 20.514 8.63949 20.354L8.48548 20.2921C7.93948 20.0701 7.66547 19.959 7.34447 20.008C7.04747 20.054 6.82849 20.209 6.39349 20.519L6.32445 20.5681C6.31945 20.5711 6.31548 20.574 6.31048 20.578C5.65848 21.064 4.9505 21.27 4.0225 21.249L3.94945 21.2471L3.75853 21.241C3.82153 21.114 3.89648 20.9721 3.96048 20.8521C4.70448 19.4551 4.81351 18.2621 4.28451 17.3061C4.27451 17.2881 4.26448 17.27 4.25248 17.253C3.35748 15.91 2.86454 14.627 2.78754 13.444C2.73954 12.649 2.73954 11.8081 2.78754 11.0181C3.04254 7.16607 6.09945 4.07109 9.90345 3.81709C10.3835 3.78209 10.9145 3.76002 11.5265 3.74902H12.5195C12.9335 3.74902 13.2695 3.41302 13.2695 2.99902C13.2695 2.58502 12.9335 2.24902 12.5195 2.24902H11.5065C10.8635 2.26002 10.3044 2.28302 9.79945 2.32002C7.58645 2.46702 5.50547 3.42105 3.94046 5.00605C2.37946 6.58705 1.43846 8.68705 1.29046 10.923C1.23846 11.789 1.23846 12.667 1.29046 13.537C1.38446 14.98 1.95448 16.5 2.98548 18.054C3.29748 18.656 2.96946 19.5221 2.63646 20.1471C2.22146 20.9291 1.92249 21.4941 2.26449 22.1051L2.27054 22.116C2.61454 22.706 3.21148 22.7251 3.90248 22.7461L3.9805 22.748Z" fill="#2B353E"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M16.0374 12.248H19.9573C21.0113 12.248 21.7364 11.681 22.1884 11.257C22.7144 10.768 22.7774 10.273 22.7374 9.944C22.6265 9.02877 21.6441 8.48097 20.9931 8.11797L20.9914 8.11704L20.9832 8.11254C20.9105 8.07232 20.8443 8.03576 20.7893 8.00298C19.0933 6.99998 16.9023 6.99998 15.2063 8.00298C15.1641 8.02733 15.1167 8.05376 15.0657 8.08222L15.0017 8.11797C14.3506 8.48098 13.3682 9.02878 13.2573 9.944C13.2183 10.273 13.2804 10.767 13.8044 11.255C14.2584 11.681 14.9834 12.248 16.0374 12.248ZM14.8283 10.159C14.8023 10.134 14.7833 10.113 14.7693 10.097H14.7704C14.9225 9.8803 15.5203 9.54694 15.7316 9.42907L15.7426 9.42298C15.8258 9.37654 15.9023 9.33385 15.9683 9.294H15.9704C17.2024 8.565 18.7943 8.56497 20.0283 9.29497C20.0941 9.33369 20.1702 9.37617 20.253 9.42238L20.2613 9.42701C20.4703 9.54301 21.0734 9.87903 21.2264 10.097C21.2124 10.114 21.1923 10.135 21.1643 10.161C20.7263 10.572 20.3653 10.748 19.9573 10.748H16.0374C15.6294 10.748 15.2693 10.572 14.8283 10.159Z" fill="#2B353E"/>
<path d="M8.49731 15.748H15.4973C15.9113 15.748 16.2473 15.412 16.2473 14.998C16.2473 14.584 15.9113 14.248 15.4973 14.248H8.49731C8.08331 14.248 7.74731 14.584 7.74731 14.998C7.74731 15.412 8.08331 15.748 8.49731 15.748Z" fill="#2B353E"/>
<path d="M8.49731 10.748H10.4973C10.9113 10.748 11.2473 10.412 11.2473 9.99805C11.2473 9.58405 10.9113 9.24805 10.4973 9.24805H8.49731C8.08331 9.24805 7.74731 9.58405 7.74731 9.99805C7.74731 10.412 8.08331 10.748 8.49731 10.748Z" fill="#2B353E"/>
</svg>

After

Width:  |  Height:  |  Size: 3.7 KiB

@ -0,0 +1,5 @@
<svg width="10" height="13" viewBox="0 0 10 13" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M5 2.85449C3.79188 2.85449 2.8125 3.83387 2.8125 5.04199C2.8125 6.25011 3.79188 7.22949 5 7.22949C6.20812 7.22949 7.1875 6.25011 7.1875 5.04199C7.1875 3.83387 6.20812 2.85449 5 2.85449ZM3.6875 5.04199C3.6875 4.31712 4.27513 3.72949 5 3.72949C5.72487 3.72949 6.3125 4.31712 6.3125 5.04199C6.3125 5.76687 5.72487 6.35449 5 6.35449C4.27513 6.35449 3.6875 5.76687 3.6875 5.04199Z" fill="#ED1C2B"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M5 0.229492C2.38798 0.229492 0.1875 2.42783 0.1875 5.09268C0.1875 7.8228 2.43772 9.64709 4.31579 10.8329L4.32334 10.8377L4.33107 10.8422C4.5346 10.9593 4.76514 11.0212 5 11.0212C5.23486 11.0212 5.4654 10.9593 5.66893 10.8422L5.67567 10.8383L5.68227 10.8342C7.56769 9.65737 9.8125 7.81358 9.8125 5.09268C9.8125 2.42783 7.61202 0.229492 5 0.229492ZM1.0625 5.09268C1.0625 2.90553 2.87676 1.10449 5 1.10449C7.12324 1.10449 8.9375 2.90553 8.9375 5.09268C8.9375 7.29808 7.10717 8.91207 5.22662 10.0871C5.15694 10.1259 5.079 10.1462 5 10.1462C4.92132 10.1462 4.84369 10.1261 4.77422 10.0876C2.89372 8.89884 1.0625 7.30546 1.0625 5.09268Z" fill="#ED1C2B"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M1.5 10.7295C1.73936 10.7295 1.93382 10.9217 1.93745 11.1602C1.94159 11.1712 1.96197 11.2114 2.04585 11.2775C2.16816 11.3739 2.37227 11.4798 2.66348 11.5769C3.24132 11.7695 4.06744 11.8962 5 11.8962C5.93256 11.8962 6.75868 11.7695 7.33652 11.5769C7.62773 11.4798 7.83184 11.3739 7.95415 11.2775C8.03803 11.2114 8.05841 11.1712 8.06255 11.1602C8.06618 10.9217 8.26064 10.7295 8.5 10.7295C8.74162 10.7295 8.9375 10.9254 8.9375 11.167C8.9375 11.5196 8.72273 11.7859 8.49575 11.9647C8.26385 12.1475 7.95539 12.2929 7.61322 12.407C6.92432 12.6366 6.00044 12.7712 5 12.7712C3.99956 12.7712 3.07568 12.6366 2.38678 12.407C2.04461 12.2929 1.73615 12.1475 1.50425 11.9647C1.27727 11.7859 1.0625 11.5196 1.0625 11.167C1.0625 10.9254 1.25838 10.7295 1.5 10.7295ZM8.06349 11.1572C8.06349 11.1571 8.06349 11.1572 8.06349 11.1572V11.1572Z" fill="#ED1C2B"/>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

@ -0,0 +1,5 @@
<svg width="14" height="13" viewBox="0 0 14 13" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M1.47635 0.357633C1.3055 0.186779 1.02849 0.186779 0.857633 0.357633C0.686779 0.528487 0.686779 0.805497 0.857633 0.976351L2.46119 2.57991C1.74908 3.50857 1.31283 4.60696 1.31283 5.87371C1.31283 7.46181 1.98475 8.7894 2.92684 9.88512C3.86488 10.9761 5.08602 11.856 6.2293 12.5637L6.23681 12.5684L6.24451 12.5727C6.47506 12.7029 6.73552 12.7712 7.00033 12.7712C7.26514 12.7712 7.52559 12.7029 7.75614 12.5727L7.76285 12.5689L7.76943 12.5649C8.63521 12.0351 9.66148 11.3872 10.4965 10.6152L12.5243 12.643C12.6952 12.8139 12.9722 12.8139 13.143 12.643C13.3139 12.4722 13.3139 12.1952 13.143 12.0243L8.48292 7.3642C8.47847 7.35955 8.47393 7.35501 8.46931 7.35059L5.56673 4.44802C5.5623 4.44339 5.55776 4.43884 5.5531 4.43438L1.47635 0.357633ZM4.72223 4.84095L3.08613 3.20485C2.51802 3.9791 2.18783 4.86436 2.18783 5.87371C2.18783 7.19521 2.74204 8.32806 3.59032 9.31467C4.44051 10.3035 5.57042 11.1262 6.68141 11.8145C6.77877 11.868 6.88851 11.8962 7.00033 11.8962C7.11248 11.8962 7.22255 11.8678 7.32013 11.814C8.18224 11.2864 9.12483 10.6862 9.87729 9.99601L8.07636 8.19508C7.73263 8.32898 7.33404 8.39615 7.00033 8.39615C5.63112 8.39615 4.52116 7.28619 4.52116 5.91698C4.52116 5.58326 4.58833 5.18468 4.72223 4.84095ZM7.36809 7.48681L5.4305 5.54922C5.40846 5.67294 5.39616 5.79917 5.39616 5.91698C5.39616 6.80294 6.11437 7.52115 7.00033 7.52115C7.11814 7.52115 7.24437 7.50884 7.36809 7.48681Z" fill="#2E3039"/>
<path d="M7.0004 1.10449C6.13419 1.10449 5.31049 1.34359 4.59613 1.76103C4.38751 1.88294 4.11957 1.81265 3.99766 1.60403C3.87575 1.39541 3.94605 1.12747 4.15466 1.00556C4.99638 0.513701 5.97157 0.229492 7.0004 0.229492C10.0778 0.229492 12.6879 2.77256 12.6879 5.87369C12.6879 6.94853 12.3781 7.91037 11.8817 8.76226C11.76 8.97103 11.4922 9.04165 11.2834 8.92C11.0746 8.79834 11.004 8.53049 11.1257 8.32172C11.5556 7.58384 11.8129 6.77241 11.8129 5.87369C11.8129 3.26657 9.60535 1.10449 7.0004 1.10449Z" fill="#2E3039"/>
<path d="M7.00038 4.31283C6.92215 4.31283 6.84547 4.31839 6.77066 4.32909C6.53147 4.36331 6.30984 4.19714 6.27562 3.95795C6.24141 3.71876 6.40758 3.49712 6.64677 3.46291C6.76248 3.44636 6.88056 3.43783 7.00038 3.43783C8.36959 3.43783 9.47955 4.54779 9.47955 5.91699C9.47955 6.03681 9.47102 6.15489 9.45446 6.27061C9.42025 6.5098 9.19861 6.67596 8.95942 6.64175C8.72023 6.60754 8.55407 6.3859 8.58828 6.14671C8.59898 6.07191 8.60455 5.99523 8.60455 5.91699C8.60455 5.03103 7.88634 4.31283 7.00038 4.31283Z" fill="#2E3039"/>
</svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

@ -0,0 +1,6 @@
<svg width="22" height="20" viewBox="0 0 22 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M13.75 6C13.75 6.41421 13.4142 6.75 13 6.75L5 6.75C4.58579 6.75 4.25 6.41421 4.25 6C4.25 5.58579 4.58579 5.25 5 5.25L13 5.25C13.4142 5.25 13.75 5.58579 13.75 6Z" fill="#2B353E"/>
<path d="M13 10.75C13.4142 10.75 13.75 10.4142 13.75 10C13.75 9.58579 13.4142 9.25 13 9.25H5C4.58579 9.25 4.25 9.58579 4.25 10C4.25 10.4142 4.58579 10.75 5 10.75H13Z" fill="#2B353E"/>
<path d="M9.75 14C9.75 14.4142 9.41421 14.75 9 14.75H5C4.58579 14.75 4.25 14.4142 4.25 14C4.25 13.5858 4.58579 13.25 5 13.25H9C9.41421 13.25 9.75 13.5858 9.75 14Z" fill="#2B353E"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M6.94513 0.250001H11.0549C12.4225 0.24998 13.5248 0.249964 14.3918 0.366525C15.2919 0.487541 16.0497 0.746435 16.6517 1.34835C17.2536 1.95027 17.5125 2.70814 17.6335 3.60825C17.6973 4.08322 17.7262 4.62883 17.7393 5.25L18.0494 5.25C18.7142 5.24997 19.2871 5.24993 19.7458 5.31161C20.2375 5.37771 20.7087 5.52677 21.091 5.90901C21.4732 6.29126 21.6223 6.76252 21.6884 7.25416C21.7501 7.7129 21.75 8.28575 21.75 8.9506L21.75 17C21.75 18.5188 20.5188 19.75 19 19.75C18.9989 19.75 18.9978 19.75 18.9967 19.75H6.94513C5.57754 19.75 4.47522 19.75 3.60825 19.6335C2.70814 19.5125 1.95027 19.2536 1.34835 18.6517C0.746435 18.0497 0.487541 17.2919 0.366525 16.3918C0.249964 15.5248 0.24998 14.4225 0.250001 13.0549V6.94513C0.24998 5.57754 0.249964 4.47521 0.366525 3.60825C0.487541 2.70814 0.746435 1.95027 1.34835 1.34835C1.95027 0.746435 2.70814 0.487541 3.60825 0.366525C4.47521 0.249964 5.57754 0.24998 6.94513 0.250001ZM18.9978 18.25C18.9985 18.25 18.9993 18.25 19 18.25C19.6904 18.25 20.25 17.6904 20.25 17V9C20.25 8.2717 20.2484 7.80091 20.2018 7.45403C20.158 7.12873 20.0874 7.02677 20.0303 6.96967C19.9732 6.91258 19.8713 6.84197 19.546 6.79823C19.1991 6.7516 18.7283 6.75 18 6.75H17.75V6.81344C17.75 6.85706 17.75 6.90096 17.75 6.94513V13C17.75 13.0006 17.75 13.0011 17.75 13.0017V17C17.75 17.6896 18.3085 18.2488 18.9978 18.25ZM16.25 17C16.25 17.4501 16.3581 17.875 16.5499 18.25H7C5.56459 18.25 4.56347 18.2484 3.80812 18.1469C3.07435 18.0482 2.68577 17.8678 2.40901 17.591C2.13225 17.3142 1.9518 16.9257 1.85315 16.1919C1.75159 15.4365 1.75 14.4354 1.75 13V7C1.75 5.56459 1.75159 4.56347 1.85315 3.80812C1.9518 3.07435 2.13225 2.68577 2.40901 2.40901C2.68577 2.13225 3.07435 1.9518 3.80812 1.85315C4.56347 1.75159 5.56459 1.75 7 1.75H11C12.4354 1.75 13.4365 1.75159 14.1919 1.85315C14.9257 1.9518 15.3142 2.13225 15.591 2.40901C15.8678 2.68577 16.0482 3.07435 16.1469 3.80812C16.2467 4.5505 16.2499 5.53028 16.25 6.92645C16.25 6.95084 16.25 6.97536 16.25 7L16.25 17Z" fill="#2B353E"/>
</svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

@ -814,7 +814,17 @@
"notNow": "ليس الآن", "notNow": "ليس الآن",
"pendingActivation": "في انتظار التنشيط", "pendingActivation": "في انتظار التنشيط",
"awaitingApproval": "انتظر القبول", "awaitingApproval": "انتظر القبول",
"news": "أخبار",
"ready": "جاهز", "ready": "جاهز",
"enterValidNationalId": "الرجاء إدخال رقم الهوية الوطنية أو رقم الملف الصحيح", "enterValidNationalId": "الرجاء إدخال رقم الهوية الوطنية أو رقم الملف الصحيح",
"enterValidPhoneNumber": "الرجاء إدخال رقم هاتف صالح" "enterValidPhoneNumber": "الرجاء إدخال رقم هاتف صالح",
"medicalCentersWithCount": "{count} مراكز طبية",
"medicalCenters": "مراكز طبية",
"hospitalsWithCount": "{count} مستشفيات",
"selectRegion": "اختر المنطقة",
"selectFacility": "اختر المرافق",
"selectFacilitiesSubTitle": "يرجى اختيار المرفق للموعد",
"selectHospitalSubTitle": "يرجى اختيار المستشفى للموعد",
"iAcceptThe" : "أوافق على",
"personalDetailsVerification": "التحقق من التفاصيل الشخصية",
} }

@ -810,7 +810,18 @@
"notNow": "Not Now", "notNow": "Not Now",
"pendingActivation": "Pending Activation", "pendingActivation": "Pending Activation",
"awaitingApproval": "Awaiting Approval", "awaitingApproval": "Awaiting Approval",
"ready": "Ready",
"enterValidNationalId": "Please enter a valid national ID or file number", "enterValidNationalId": "Please enter a valid national ID or file number",
"enterValidPhoneNumber": "Please enter a valid phone number" "enterValidPhoneNumber": "Please enter a valid phone number",
"ready": "Ready",
"medicalCentersWithCount" : "{count} Medical Centers",
"medicalCenters" : " Medical Centers",
"hospitalsWithCount" : "{count} Hospitals",
"selectRegion": "Select Region",
"selectFacility": "Select Facilities",
"selectFacilitiesSubTitle": "Please select the facility for the appointment",
"selectHospitalSubTitle": "Please select the hospital for the appointment",
"news": "News",
"iAcceptThe" : "I Accept the",
"personalDetailsVerification": "Personal Details Verification"
} }

@ -56,7 +56,7 @@ var RC_BASE_URL = 'https://rc.hmg.com/';
var PING_SERVICE = 'Services/Weather.svc/REST/CheckConnectivity'; var PING_SERVICE = 'Services/Weather.svc/REST/CheckConnectivity';
var GET_PROJECT = 'Services/Lists.svc/REST/GetProject'; var GET_PROJECT_LIST = 'Services/Lists.svc/REST/GetProject';
///Geofencing ///Geofencing
var GET_GEO_ZONES = 'Services/Patients.svc/REST/GeoF_GetAllPoints'; var GET_GEO_ZONES = 'Services/Patients.svc/REST/GeoF_GetAllPoints';
@ -217,9 +217,6 @@ var GET_CLINICS_LIST_WRT_HOSPITAL_URL = "Services/Lists.svc/REST/GetClinicFromDo
//URL to get active appointment list //URL to get active appointment list
var GET_ACTIVE_APPOINTMENTS_LIST_URL = "Services/Doctors.svc/Rest/Dr_GetAppointmentActiveNumber"; var GET_ACTIVE_APPOINTMENTS_LIST_URL = "Services/Doctors.svc/Rest/Dr_GetAppointmentActiveNumber";
//URL to get projects list
var GET_PROJECTS_LIST = 'Services/Lists.svc/REST/GetProject';
//URL to get doctors list //URL to get doctors list
var GET_DOCTORS_LIST_URL = "Services/Doctors.svc/REST/SearchDoctorsByTime"; var GET_DOCTORS_LIST_URL = "Services/Doctors.svc/REST/SearchDoctorsByTime";

@ -108,6 +108,8 @@ class AppAssets {
static const String search_by_clinic_icon = '$svgBasePath/search_by_clinic_icon.svg'; static const String search_by_clinic_icon = '$svgBasePath/search_by_clinic_icon.svg';
static const String search_by_doctor_icon = '$svgBasePath/search_by_doctor_icon.svg'; static const String search_by_doctor_icon = '$svgBasePath/search_by_doctor_icon.svg';
static const String search_by_region_icon = '$svgBasePath/search_by_region_icon.svg'; static const String search_by_region_icon = '$svgBasePath/search_by_region_icon.svg';
static const String location_red = '$svgBasePath/location_red.svg';
static const String location_unavailable = '$svgBasePath/location_unavailable.svg';
static const String livecare_clinic_icon = '$svgBasePath/livecare_clinic_icon.svg'; static const String livecare_clinic_icon = '$svgBasePath/livecare_clinic_icon.svg';
static const String immediate_service_icon = '$svgBasePath/immediate_service_icon.svg'; static const String immediate_service_icon = '$svgBasePath/immediate_service_icon.svg';
static const String no_visit_icon = '$svgBasePath/no_visit_icon.svg'; static const String no_visit_icon = '$svgBasePath/no_visit_icon.svg';
@ -123,7 +125,8 @@ class AppAssets {
static const String toDoBottom = '$svgBasePath/todo_bottom.svg'; static const String toDoBottom = '$svgBasePath/todo_bottom.svg';
static const String servicesBottom = '$svgBasePath/services_bottom.svg'; static const String servicesBottom = '$svgBasePath/services_bottom.svg';
static const String closeBottomNav = '$svgBasePath/close_bottom_nav.svg'; static const String closeBottomNav = '$svgBasePath/close_bottom_nav.svg';
static const String feedback = '$svgBasePath/feedback.svg';
static const String news = '$svgBasePath/news.svg';
// PNGS // // PNGS //
static const String hmg_logo = '$pngBasePath/hmg_logo.png'; static const String hmg_logo = '$pngBasePath/hmg_logo.png';
static const String livecare_service = '$pngBasePath/livecare_service.png'; static const String livecare_service = '$pngBasePath/livecare_service.png';
@ -145,4 +148,5 @@ class AppAnimations {
static const String loadingAnimation = '$lottieBasePath/Loader.json'; static const String loadingAnimation = '$lottieBasePath/Loader.json';
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';
} }

@ -62,7 +62,7 @@ class AppState {
SelectDeviceByImeiRespModelElement? _selectDeviceByImeiRespModelElement; SelectDeviceByImeiRespModelElement? _selectDeviceByImeiRespModelElement;
void setSelectDeviceByImeiRespModelElement(SelectDeviceByImeiRespModelElement value) { void setSelectDeviceByImeiRespModelElement(SelectDeviceByImeiRespModelElement? value) {
_selectDeviceByImeiRespModelElement = value; _selectDeviceByImeiRespModelElement = value;
} }
@ -132,4 +132,6 @@ class AppState {
set setUserRegistrationPayload(RegistrationDataModelPayload value) { set setUserRegistrationPayload(RegistrationDataModelPayload value) {
_userRegistrationPayload = value; _userRegistrationPayload = value;
} }
} }

@ -72,4 +72,5 @@ class CacheConst {
static const String isLastAppointmentRateShown = 'is-last-appointment-rate-shown'; static const String isLastAppointmentRateShown = 'is-last-appointment-rate-shown';
static const String patientOccupationList = 'patient-occupation-list'; static const String patientOccupationList = 'patient-occupation-list';
static const String hasEnabledQuickLogin = 'has-enabled-quick-login'; static const String hasEnabledQuickLogin = 'has-enabled-quick-login';
static const String quickLoginEnabled = 'quick-login-enabled';
} }

@ -128,6 +128,7 @@ class AppDependencies {
() => MyAppointmentsViewModel( () => MyAppointmentsViewModel(
myAppointmentsRepo: getIt(), myAppointmentsRepo: getIt(),
errorHandlerService: getIt(), errorHandlerService: getIt(),
appState: getIt()
), ),
); );

@ -29,7 +29,7 @@ enum CountryEnum { saudiArabia, unitedArabEmirates }
enum CalenderEnum { gregorian, hijri } enum CalenderEnum { gregorian, hijri }
enum SelectionTypeEnum { dropdown, calendar } enum SelectionTypeEnum { dropdown, calendar, search }
enum GenderTypeEnum { male, female } enum GenderTypeEnum { male, female }

@ -0,0 +1,207 @@
import 'dart:math';
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/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;
class DoctorMapper{
static Future<RegionList> getMappedDoctor(List<DoctorList> doctorList,
{bool isArabic = false}) async {
RegionList regionList = RegionList();
final lat = await Utils.getNumFromPrefs(CacheConst.userLat);
final long = await Utils.getNumFromPrefs(CacheConst.userLat);
for (var element in doctorList) {
String? region = element.getRegionName(isArabic);
if (region == null) continue;
var regionDoctorList = regionList.registeredDoctorMap?.putIfAbsent(region, () => PatientDoctorAppointmentListByRegion());
List<PatientDoctorAppointmentList>? targetList = element.isHMC == true
? regionDoctorList?.hmcDoctorList
: regionDoctorList?.hmgDoctorList;
var doctorByHospital = targetList
?.where((clinic) =>
clinic.filterName ==
element.getProjectCompleteNameWithLocale(isArabic: isArabic))
.toList() ??
[];
if (doctorByHospital.isNotEmpty) {
doctorByHospital.first.patientDoctorAppointmentList?.add(element);
} else {
var newAppointment = PatientDoctorAppointmentList(
filterName:
element.getProjectCompleteNameWithLocale(isArabic: isArabic),
distanceInKMs: element.projectDistanceInKiloMeters.toString(),
projectTopName: element.projectTopName,
projectBottomName: element.projectBottomName,
patientDoctorAppointment: element,
isHMC: element.isHMC
);
if(element.projectDistanceInKiloMeters!= null ){
if(regionDoctorList!.distance>element.projectDistanceInKiloMeters){
regionDoctorList.distance = element.projectDistanceInKiloMeters;
}
if (element.isHMC == true &&
element.projectDistanceInKiloMeters <
regionDoctorList.hmcDistance) {
regionDoctorList.hmcDistance = element.projectDistanceInKiloMeters;
} else if (element.projectDistanceInKiloMeters <
regionDoctorList.hmgDistance) {
regionDoctorList.hmgDistance = element.projectDistanceInKiloMeters;
}
}else
if (lat != 0&&
long != 0 && element.latitude != null && element.longitude != null) {
double distance = calculateDistance(lat.toDouble(), long.toDouble(), double.parse(element.latitude!), double.parse(element.longitude!));
if(distance<0){
distance *= -1;
}
if(regionDoctorList!.distance>distance){
regionDoctorList.distance = distance;
}
if (element.isHMC == true &&
element.projectDistanceInKiloMeters <
regionDoctorList.hmcDistance) {
regionDoctorList.hmcDistance = element.projectDistanceInKiloMeters;
} else if (element.projectDistanceInKiloMeters <
regionDoctorList.hmgDistance) {
regionDoctorList.hmgDistance = element.projectDistanceInKiloMeters;
}
}
targetList?.add(newAppointment);
}
regionDoctorList?.hmcSize = regionDoctorList.hmcDoctorList?.length ?? 0;
regionDoctorList?.hmgSize = regionDoctorList.hmgDoctorList?.length ?? 0;
regionList.registeredDoctorMap?[region] = regionDoctorList;
}
return regionList;
}
static double calculateDistance(double lat1, double lon1, double lat2, double lon2) {
var pi = 3.142;
const double R = 6371;
double dLat = (lat2 - lat1) * pi / 180;
double dLon = (lon2 - lon1) * pi / 180;
double a = sin(dLat / 2) * sin(dLat / 2) +
cos(lat1 * pi / 180) * cos(lat2 * pi / 180) * sin(dLon / 2) * sin(dLon / 2);
double c = 2 * atan2(sqrt(a), sqrt(1 - a));
return R * c;
}
static Future<RegionList> sortList(bool isGPSEnabled, RegionList unsorted, ) async {
if(isGPSEnabled){
if(unsorted.registeredDoctorMap == null) return unsorted;
var sortedMap = Map.fromEntries(
unsorted.registeredDoctorMap!.entries.toList()
..sort((a, b) => a.value!.distance.compareTo(b.value!.distance)),
);
unsorted.registeredDoctorMap = sortedMap;
return unsorted;
}
List<String>? keys = unsorted.registeredDoctorMap?.keys.toList();
keys?.sort();
if (keys == null) return unsorted;
Map<String, PatientDoctorAppointmentListByRegion> sortedMap = {};
for (var key in keys) {
sortedMap[key] = unsorted.registeredDoctorMap![key]!;
}
unsorted.registeredDoctorMap = sortedMap;
return unsorted;
}
static Future<RegionList> getMappedHospitals(
List<HospitalsModel> hospitalList, {
bool isArabic = false,
}) async {
final regionList = RegionList();
final lat = await Utils.getNumFromPrefs(CacheConst.userLat);
final long = await Utils.getNumFromPrefs(CacheConst.userLat);
for (final hospital in hospitalList) {
final region = hospital.getRegionName(isArabic);
if (region == null) continue;
final regionData = regionList.registeredDoctorMap?.putIfAbsent(
region,
() => PatientDoctorAppointmentListByRegion(),
);
List<PatientDoctorAppointmentList>? targetList = hospital.isHMC == true
? regionData?.hmcDoctorList
: regionData?.hmgDoctorList;
List<PatientDoctorAppointmentList> existingEntry = targetList
?.where(
(entry) => entry.filterName == hospital.getName(isArabic),
)
.toList() ??
[];
if (existingEntry.isNotEmpty) {
existingEntry.first.hospitalList.add(hospital);
} else {
final newEntry = PatientDoctorAppointmentList(
filterName: hospital.name,
distanceInKMs: hospital.distanceInKilometers?.toString(),
projectTopName: hospital.name,
projectBottomName: hospital.name,
model: hospital,
isHMC: hospital.isHMC);
final distance = hospital.distanceInKilometers;
if (distance != null) {
if (regionData!.distance > distance) {
regionData.distance = distance;
}
if (hospital.isHMC == true && distance < regionData.hmcDistance) {
regionData.hmcDistance = distance;
} else if (distance < regionData.hmgDistance) {
regionData.hmgDistance = distance;
}
} else if ( lat != 0&&
long != 0 &&
hospital.latitude != null &&
hospital.longitude != null) {
double calculatedDistance = calculateDistance(
lat.toDouble(),
long.toDouble(),
double.parse(hospital.latitude!),
double.parse(hospital.longitude!),
).abs();
if (regionData!.distance > calculatedDistance) {
regionData.distance = calculatedDistance;
}
if (hospital.isHMC == true &&
calculatedDistance < regionData.hmcDistance) {
regionData.hmcDistance = calculatedDistance;
} else if (calculatedDistance < regionData.hmgDistance) {
regionData.hmgDistance = calculatedDistance;
}
}
targetList?.add(newEntry);
}
regionData?.hmcSize = regionData.hmcDoctorList?.length ?? 0;
regionData?.hmgSize = regionData.hmgDoctorList?.length ?? 0;
regionList.registeredDoctorMap?[region] = regionData;
}
return regionList;
}
}

@ -221,10 +221,15 @@ extension EmailValidator on String {
style: TextStyle(height: 23 / 24, color: color ?? AppColors.blackColor, fontSize: 24.fSize, letterSpacing: -1, fontWeight: isBold ? FontWeight.bold : FontWeight.normal), style: TextStyle(height: 23 / 24, color: color ?? AppColors.blackColor, fontSize: 24.fSize, letterSpacing: -1, fontWeight: isBold ? FontWeight.bold : FontWeight.normal),
); );
Widget toText26({Color? color, bool isBold = false, double? height, bool isCenter = false}) => Text( Widget toText26({Color? color, bool isBold = false, double? height, bool isCenter = false, FontWeight? weight, double? letterSpacing}) => Text(
this, this,
textAlign: isCenter ? TextAlign.center : null, textAlign: isCenter ? TextAlign.center : null,
style: TextStyle(height: height ?? 23 / 26, color: color ?? AppColors.blackColor, fontSize: 26.fSize, letterSpacing: -1, fontWeight: isBold ? FontWeight.bold : FontWeight.normal), style: TextStyle(
height: height ?? 23 / 26,
color: color ?? AppColors.blackColor,
fontSize: 26.fSize,
letterSpacing: letterSpacing ?? -1,
fontWeight: weight ?? (isBold ? FontWeight.bold : FontWeight.normal)),
); );
Widget toText28({Color? color, bool isBold = false, double? height, bool isCenter = false}) => Text( Widget toText28({Color? color, bool isBold = false, double? height, bool isCenter = false}) => Text(

@ -3,6 +3,7 @@ import 'dart:developer';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/services.dart' show rootBundle; import 'package:flutter/services.dart' show rootBundle;
import 'package:flutter/material.dart'; import 'package:flutter/material.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';
@ -62,7 +63,6 @@ class AuthenticationViewModel extends ChangeNotifier {
_appState = appState, _appState = appState,
_authenticationRepo = authenticationRepo, _authenticationRepo = authenticationRepo,
_localAuthService = localAuthService; _localAuthService = localAuthService;
final TextEditingController nationalIdController = TextEditingController(), final TextEditingController nationalIdController = TextEditingController(),
phoneNumberController = TextEditingController(), phoneNumberController = TextEditingController(),
dobController = TextEditingController(), dobController = TextEditingController(),
@ -74,11 +74,14 @@ class AuthenticationViewModel extends ChangeNotifier {
bool isTermsAccepted = false; bool isTermsAccepted = false;
List<NationalityCountries>? countriesList; List<NationalityCountries>? countriesList;
String? dob = ""; String? dob = "";
final CacheService cacheService = GetIt.instance<CacheService>();
NationalityCountries? pickedCountryByUAEUser; NationalityCountries? pickedCountryByUAEUser;
CalenderEnum calenderType = CalenderEnum.gregorian; CalenderEnum calenderType = CalenderEnum.gregorian;
LoginTypeEnum loginTypeEnum = LoginTypeEnum.sms; LoginTypeEnum loginTypeEnum = LoginTypeEnum.sms;
final ValueNotifier<bool> otpScreenNotifier = ValueNotifier<bool>(false);
//================== //==================
String errorMsg = ''; String errorMsg = '';
@ -158,6 +161,10 @@ class AuthenticationViewModel extends ChangeNotifier {
notifyListeners(); notifyListeners();
} }
void clearEmailInput() {
emailController.text = "";
}
void onUAEUserCountrySelection(String? value) { void onUAEUserCountrySelection(String? value) {
pickedCountryByUAEUser = countriesList!.firstWhere((element) => element.name == value); pickedCountryByUAEUser = countriesList!.firstWhere((element) => element.name == value);
notifyListeners(); notifyListeners();
@ -393,12 +400,8 @@ class AuthenticationViewModel extends ChangeNotifier {
return isUserComingForRegister; return isUserComingForRegister;
} }
Future<void> checkActivationCode({ Future<void> checkActivationCode(
required String? activationCode, {required String? activationCode, required OTPTypeEnum otpTypeEnum, required Function(String? message) onWrongActivationCode, Function()? onResendActivation}) async {
required OTPTypeEnum otpTypeEnum,
required Function(String? message) onWrongActivationCode,
Function()? onResendActivation,
}) 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(
@ -421,6 +424,7 @@ class AuthenticationViewModel extends ChangeNotifier {
countryCode: _appState.getSelectDeviceByImeiRespModelElement != null && _appState.getSelectDeviceByImeiRespModelElement!.outSa == true countryCode: _appState.getSelectDeviceByImeiRespModelElement != null && _appState.getSelectDeviceByImeiRespModelElement!.outSa == true
? CountryEnum.unitedArabEmirates.countryCode ? CountryEnum.unitedArabEmirates.countryCode
: selectedCountrySignup.countryCode, : selectedCountrySignup.countryCode,
//TODO: Error Here IN Zip Code.
loginType: loginTypeEnum.toInt) loginType: loginTypeEnum.toInt)
.toJson(); .toJson();
LoaderBottomSheet.showLoader(); LoaderBottomSheet.showLoader();
@ -468,6 +472,7 @@ class AuthenticationViewModel extends ChangeNotifier {
failure: failure, failure: failure,
onUnHandledFailure: (failure) async { onUnHandledFailure: (failure) async {
LoaderBottomSheet.hideLoader(); LoaderBottomSheet.hideLoader();
otpScreenNotifier.value = true;
await _dialogService.showCommonBottomSheetWithoutH(message: failure.message, label: LocaleKeys.notice.tr(), onOkPressed: () {}); await _dialogService.showCommonBottomSheetWithoutH(message: failure.message, label: LocaleKeys.notice.tr(), onOkPressed: () {});
}, },
onMessageStatusFailure: (failure) async { onMessageStatusFailure: (failure) async {
@ -594,6 +599,7 @@ class AuthenticationViewModel extends ChangeNotifier {
} }
Future<void> onWrongActivationCode({String? message}) async { Future<void> onWrongActivationCode({String? message}) async {
otpScreenNotifier.value = true;
await _dialogService.showErrorBottomSheet(message: message ?? "Something went wrong. ", onOkPressed: () {}); await _dialogService.showErrorBottomSheet(message: message ?? "Something went wrong. ", onOkPressed: () {});
} }
@ -621,19 +627,23 @@ class AuthenticationViewModel extends ChangeNotifier {
} }
checkLastLoginStatus(Function() onSuccess) async { checkLastLoginStatus(Function() onSuccess) async {
Future.delayed(Duration(seconds: 1), () { Future.delayed(Duration(seconds: 1), () async {
if (_appState.getSelectDeviceByImeiRespModelElement != null && if (cacheService.getBool(key: CacheConst.quickLoginEnabled) == null) {
(_appState.getSelectDeviceByImeiRespModelElement!.logInType == 1 || _appState.getSelectDeviceByImeiRespModelElement!.logInType == 4)) { if (_appState.getSelectDeviceByImeiRespModelElement != null &&
phoneNumberController.text = (_appState.getSelectDeviceByImeiRespModelElement!.logInType == 1 || _appState.getSelectDeviceByImeiRespModelElement!.logInType == 4)) {
(_appState.getAuthenticatedUser()!.mobileNumber!.startsWith("0") ? _appState.getAuthenticatedUser()!.mobileNumber!.replaceFirst("0", "") : _appState.getAuthenticatedUser()!.mobileNumber)!; phoneNumberController.text = (_appState.getAuthenticatedUser()!.mobileNumber!.startsWith("0")
nationalIdController.text = _appState.getAuthenticatedUser()!.nationalityId!; ? _appState.getAuthenticatedUser()!.mobileNumber!.replaceFirst("0", "")
onSuccess(); : _appState.getAuthenticatedUser()!.mobileNumber)!;
} else if ((loginTypeEnum == LoginTypeEnum.sms || loginTypeEnum == LoginTypeEnum.whatsapp && _appState.getSelectDeviceByImeiRespModelElement == null) && nationalIdController.text = _appState.getAuthenticatedUser()!.nationalityId!;
_appState.getAuthenticatedUser() != null) { onSuccess();
phoneNumberController.text = } else if ((loginTypeEnum == LoginTypeEnum.sms || loginTypeEnum == LoginTypeEnum.whatsapp && _appState.getSelectDeviceByImeiRespModelElement == null) &&
(_appState.getAuthenticatedUser()!.mobileNumber!.startsWith("0") ? _appState.getAuthenticatedUser()!.mobileNumber!.replaceFirst("0", "") : _appState.getAuthenticatedUser()!.mobileNumber)!; _appState.getAuthenticatedUser() != null) {
nationalIdController.text = _appState.getAuthenticatedUser()!.nationalityId!; phoneNumberController.text = (_appState.getAuthenticatedUser()!.mobileNumber!.startsWith("0")
onSuccess(); ? _appState.getAuthenticatedUser()!.mobileNumber!.replaceFirst("0", "")
: _appState.getAuthenticatedUser()!.mobileNumber)!;
nationalIdController.text = _appState.getAuthenticatedUser()!.nationalityId!;
onSuccess();
}
} }
}); });
} }
@ -670,21 +680,27 @@ class AuthenticationViewModel extends ChangeNotifier {
} }
Future<void> onRegistrationComplete() async { Future<void> onRegistrationComplete() async {
LoaderBottomSheet.showLoader(); // LoaderBottomSheet.showLoader();
LoadingUtils.showFullScreenLoader(loadingText: "Setting up your medical file.\nMay take a moment.");
var request = RequestUtils.getUserSignupCompletionRequest(fullName: nameController.text, emailAddress: emailController.text, gender: genderType, maritalStatus: maritalStatus); var request = RequestUtils.getUserSignupCompletionRequest(fullName: nameController.text, emailAddress: emailController.text, gender: genderType, maritalStatus: maritalStatus);
final resultEither = await _authenticationRepo.registerUser(registrationPayloadDataModelRequest: request); final resultEither = await _authenticationRepo.registerUser(registrationPayloadDataModelRequest: request);
resultEither.fold((failure) async => await _errorHandlerService.handleError(failure: failure), (apiResponse) async { resultEither.fold((failure) async => await _errorHandlerService.handleError(failure: failure), (apiResponse) async {
if (apiResponse.data is String) { if (apiResponse.data is String) {
//TODO: This Section Need to Be Testing. //TODO: This Section Need to Be Testing.
LoadingUtils.hideFullScreenLoader();
_dialogService.showExceptionBottomSheet(message: apiResponse.data, onOkPressed: () {}, onCancelPressed: () {}); _dialogService.showExceptionBottomSheet(message: apiResponse.data, onOkPressed: () {}, onCancelPressed: () {});
//TODO: Here We Need to Show a Dialog Of Something in the case of Fail With OK and Cancel and the Display Variable WIll be result. //TODO: Here We Need to Show a Dialog Of Something in the case of Fail With OK and Cancel and the Display Variable WIll be result.
} else { } else {
print(apiResponse.data as Map<String, dynamic>); LoadingUtils.hideFullScreenLoader();
if (apiResponse.data["MessageStatus"] == 1) { if (apiResponse.data["MessageStatus"] == 1) {
LoaderBottomSheet.hideLoader(); LoadingUtils.showFullScreenLoader(isSuccessDialog: true);
//TODO: Here We Need to Show a Dialog Of Something in the case of Success. //TODO: Here We Need to Show a Dialog Of Something in the case of Success.
await clearDefaultInputValues(); // This will Clear All Default Values Of User. await clearDefaultInputValues(); // This will Clear All Default Values Of User.
_navigationService.pushAndReplace(AppRoutes.loginScreen); Future.delayed(Duration(seconds: 1), () {
LoadingUtils.hideFullScreenLoader();
_navigationService.pushAndReplace(AppRoutes.loginScreen);
});
} }
} }
}); });
@ -732,6 +748,8 @@ class AuthenticationViewModel extends ChangeNotifier {
} else { } else {
//TODO: Here Hide Loader And Show TOAST //TODO: Here Hide Loader And Show TOAST
//TODO: if (response['ErrorCode'] == '-986') Toast With OK, And Show response as Output. //TODO: if (response['ErrorCode'] == '-986') Toast With OK, And Show response as Output.
LoaderBottomSheet.hideLoader();
_dialogService.showErrorBottomSheet(message: response['ErrorMessage']);
} }
} }
@ -917,4 +935,6 @@ class AuthenticationViewModel extends ChangeNotifier {
}, },
); );
} }
// === OTP Widget Logics ===//
} }

@ -7,8 +7,10 @@ import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.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/extensions/widget_extensions.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/authentication/authentication_view_model.dart';
import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/appbar/app_bar_widget.dart'; import 'package:hmg_patient_app_new/widgets/appbar/app_bar_widget.dart';
import 'package:provider/provider.dart';
typedef OnDone = void Function(String text); typedef OnDone = void Function(String text);
@ -90,6 +92,7 @@ class OTPWidgetState extends State<OTPWidget> with SingleTickerProviderStateMixi
int currentIndex = 0; int currentIndex = 0;
List<String> strList = []; List<String> strList = [];
bool hasFocus = false; bool hasFocus = false;
AuthenticationViewModel? authVm;
@override @override
void didUpdateWidget(OTPWidget oldWidget) { void didUpdateWidget(OTPWidget oldWidget) {
@ -124,6 +127,7 @@ class OTPWidgetState extends State<OTPWidget> with SingleTickerProviderStateMixi
@override @override
void initState() { void initState() {
super.initState(); super.initState();
authVm = context.read<AuthenticationViewModel>();
focusNode = widget.focusNode ?? FocusNode(); focusNode = widget.focusNode ?? FocusNode();
_highlightAnimationController = AnimationController(vsync: this); _highlightAnimationController = AnimationController(vsync: this);
_initTextController(); _initTextController();
@ -132,12 +136,15 @@ class OTPWidgetState extends State<OTPWidget> with SingleTickerProviderStateMixi
widget.controller!.addListener(_controllerListener); widget.controller!.addListener(_controllerListener);
} }
focusNode.addListener(_focusListener); focusNode.addListener(_focusListener);
authVm?.otpScreenNotifier.addListener(_onOtpScreenNotifierChanged);
} }
void _controllerListener() { void _controllerListener() {
if (mounted == true) { if (mounted == true) {
setState(() { setState(() {
_initTextController(); _initTextController();
text = widget.controller?.text ?? "";
currentIndex = text.length;
}); });
var onTextChanged = widget.onTextChanged; var onTextChanged = widget.onTextChanged;
if (onTextChanged != null) { if (onTextChanged != null) {
@ -154,6 +161,41 @@ class OTPWidgetState extends State<OTPWidget> with SingleTickerProviderStateMixi
} }
} }
void onWrongOtpClear() {
if (mounted) {
setState(() {
text = "";
currentIndex = 0;
strList.clear();
_calculateStrList();
});
// Clear the controller if it exists
if (widget.controller != null) {
widget.controller!.clear();
}
// Remove focus from the input
if (focusNode.hasFocus) {
focusNode.unfocus();
}
// Optionally refocus after a short delay to allow user to re-enter OTP
Future.delayed(const Duration(milliseconds: 100), () {
if (mounted && widget.autoFocus) {
FocusScope.of(context).requestFocus(focusNode);
}
});
}
}
void _onOtpScreenNotifierChanged() {
if (authVm?.otpScreenNotifier.value == true) {
onWrongOtpClear();
authVm?.otpScreenNotifier.value = false;
}
}
void _initTextController() { void _initTextController() {
if (widget.controller == null) { if (widget.controller == null) {
return; return;
@ -304,19 +346,17 @@ class OTPWidgetState extends State<OTPWidget> with SingleTickerProviderStateMixi
); );
} }
Widget _buildPinCode(int i, BuildContext context) { Widget _buildPinCode(int i, BuildContext context) {
Color pinBoxColor; Color pinBoxColor;
if (widget.hasError) { if (widget.hasError) {
pinBoxColor = widget.errorBorderColor; pinBoxColor = widget.errorBorderColor;
} else if (text.length == widget.maxLength) { } else if (text.length == widget.maxLength) {
// Check for completion first, before individual box logic
pinBoxColor = AppColors.successColor; pinBoxColor = AppColors.successColor;
} else if (i < text.length) { } else if (i < text.length) {
pinBoxColor = AppColors.blackBgColor; // Custom color for filled boxes pinBoxColor = AppColors.blackBgColor;
} else { } else {
pinBoxColor = widget.pinBoxColor; // Default white color pinBoxColor = widget.pinBoxColor;
} }
EdgeInsets insets; EdgeInsets insets;
@ -355,7 +395,6 @@ class OTPWidgetState extends State<OTPWidget> with SingleTickerProviderStateMixi
); );
} }
// Widget _buildPinCode(int i, BuildContext context) { // Widget _buildPinCode(int i, BuildContext context) {
// Color pinBoxColor = widget.pinBoxColor; // Color pinBoxColor = widget.pinBoxColor;
// //
@ -455,7 +494,7 @@ class _OTPVerificationScreenState extends State<OTPVerificationScreen> {
Timer? _resendTimer; Timer? _resendTimer;
int _resendTime = 120; int _resendTime = 120;
bool _isOtpComplete = false; bool _isOtpComplete = false;
bool _isVerifying = false; // Flag to prevent multiple verification calls bool _isVerifying = false;
@override @override
void initState() { void initState() {
@ -504,11 +543,18 @@ class _OTPVerificationScreenState extends State<OTPVerificationScreen> {
}); });
_otpController.clear(); _otpController.clear();
_startResendTimer(); _startResendTimer();
// autoFillOtp("1234");
widget.onResendOTPPressed(widget.phoneNumber); widget.onResendOTPPressed(widget.phoneNumber);
} }
} }
void _clearOtpAndResetState() {
setState(() {
_isVerifying = false;
_isOtpComplete = false;
});
_otpController.clear();
}
String _getMaskedPhoneNumber() { String _getMaskedPhoneNumber() {
final phone = widget.phoneNumber; final phone = widget.phoneNumber;
return phone.length > 4 ? '05xxxxxx${phone.substring(phone.length - 2)}' : phone; return phone.length > 4 ? '05xxxxxx${phone.substring(phone.length - 2)}' : phone;

@ -0,0 +1,66 @@
import 'package:flutter/foundation.dart' show ChangeNotifier;
enum AppointmentViaRegionState {
REGION_SELECTION,
TYPE_SELECTION,
HOSPITAL_SELECTION,
CLINIC_SELECTION,
DOCTOR_SELECTION
}
class AppointmentViaRegionViewmodel extends ChangeNotifier {
String? selectedRegionId;
String? selectedFacilityType;
AppointmentViaRegionState bottomSheetState =
AppointmentViaRegionState.REGION_SELECTION;
void setSelectedRegionId(String? regionId) {
selectedRegionId = regionId;
notifyListeners();
}
void setFacility(String? facility) {
selectedFacilityType = facility;
notifyListeners();
}
void setBottomSheetState(AppointmentViaRegionState state) {
bottomSheetState = state;
notifyListeners();
}
void handleBackPress() {
switch (bottomSheetState) {
case AppointmentViaRegionState.REGION_SELECTION:
// Do nothing or exit the bottom sheet
break;
case AppointmentViaRegionState.TYPE_SELECTION:
setBottomSheetState(AppointmentViaRegionState.REGION_SELECTION);
setSelectedRegionId(null);
break;
case AppointmentViaRegionState.HOSPITAL_SELECTION:
setBottomSheetState(AppointmentViaRegionState.TYPE_SELECTION);
break;
// case AppointmentViaRegionState.HOSPITAL_SELECTION:
// case AppointmentViaRegionState.CLINIC_SELECTION:
// setBottomSheetState(AppointmentViaRegionState.TYPE_SELECTION);
// setFacility(null);
// break;
// case AppointmentViaRegionState.DOCTOR_SELECTION:
// if (selectedFacilityType == 'Hospital') {
// setBottomSheetState(AppointmentViaRegionState.HOSPITAL_SELECTION);
// } else if (selectedFacilityType == 'Medical Center') {
// setBottomSheetState(AppointmentViaRegionState.CLINIC_SELECTION);
// }
// break;
default:
}
}
void flush() {
setSelectedRegionId(null);
setFacility(null);
setBottomSheetState(AppointmentViaRegionState.REGION_SELECTION);
}
}

@ -0,0 +1,8 @@
enum FacilitySelection{
ALL('ALL'),
HMG('hmg'),
HMC('hmc');
final String value;
const FacilitySelection(this.value);
}

@ -0,0 +1,274 @@
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/hospital_model.dart' show HospitalsModel;
class DoctorList {
int? clinicID;
dynamic appointmentNo;
String? clinicName;
String? doctorTitle;
num? iD;
String? name;
int? projectID;
String? projectName;
int? actualDoctorRate;
num? clinicRoomNo;
dynamic date;
dynamic appointmentDate;
dynamic dayName;
int? doctorID;
String? doctorImageURL;
dynamic doctorProfile;
dynamic doctorProfileInfo;
int? doctorRate;
num? gender;
String? genderDescription;
bool? isAppointmentAllowed;
bool? isDoctorAllowVedioCall;
bool? isDoctorDummy;
bool? isLiveCare;
bool? isLiveCareClinic;
bool? isDoctorHasPrePostImages;
String? latitude;
String? longitude;
String? nationalityFlagURL;
String? nationalityID;
String? nationalityName;
dynamic nearestFreeSlot;
int? noOfPatientsRate;
num? originalClinicID;
num? personRate;
dynamic projectDistanceInKiloMeters;
String? qR;
dynamic qRString;
num? rateNumber;
dynamic serviceID;
String? setupID;
List<String>? speciality;
List<String>? specialityN;
dynamic workingHours;
dynamic decimalDoctorRate;
String? projectBottomName;
String? projectTopName;
bool? isHMC;
String? region;
String? regionArabic;
String? regionEnglish;
String? regionID;
DoctorList(
{this.clinicID,
this.appointmentNo,
this.clinicName,
this.doctorTitle,
this.iD,
this.name,
this.projectID,
this.projectName,
this.actualDoctorRate,
this.clinicRoomNo,
this.date,
this.appointmentDate,
this.dayName,
this.doctorID,
this.doctorImageURL,
this.doctorProfile,
this.doctorProfileInfo,
this.doctorRate,
this.gender,
this.genderDescription,
this.isAppointmentAllowed,
this.isDoctorAllowVedioCall,
this.isDoctorDummy,
this.isLiveCare,
this.isLiveCareClinic,
this.isDoctorHasPrePostImages,
this.latitude,
this.longitude,
this.nationalityFlagURL,
this.nationalityID,
this.nationalityName,
this.nearestFreeSlot,
this.noOfPatientsRate,
this.originalClinicID,
this.personRate,
this.projectDistanceInKiloMeters,
this.qR,
this.qRString,
this.rateNumber,
this.serviceID,
this.setupID,
this.speciality,
this.specialityN,
this.workingHours,
this.decimalDoctorRate,
this.projectBottomName,
this.projectTopName,
this.isHMC,
this.region,
this.regionArabic,
this.regionEnglish,
this.regionID,
});
DoctorList.fromJson(
Map<String, dynamic> json,
) {
clinicID = json['ClinicID'];
appointmentNo = json['AppointmentNo'];
clinicName = json['ClinicName'];
doctorTitle = json['DoctorTitle'];
iD = json['ID'];
name = json['DoctorName'] ?? json['Name'];
projectID = json['ProjectID'];
projectName = json['ProjectName'];
actualDoctorRate = json['ActualDoctorRate'];
clinicRoomNo = json['ClinicRoomNo'];
date = json['Date'];
appointmentDate = json['AppointmentDate'];
dayName = json['DayName'];
doctorID = json['DoctorID'];
doctorImageURL = json['DoctorImageURL'];
doctorProfile = json['DoctorProfile'];
doctorProfileInfo = json['DoctorProfileInfo'];
doctorRate = json['DoctorRate'];
gender = json['Gender'];
genderDescription = json['GenderDescription'];
isAppointmentAllowed = json['IsAppointmentAllowed'];
isDoctorAllowVedioCall = json['IsDoctorAllowVedioCall'];
isDoctorDummy = json['IsDoctorDummy'];
isLiveCare = json['IsLiveCare'];
isLiveCareClinic = json['IsLiveCareClinic'];
isDoctorHasPrePostImages = json['IsDoctorHasPrePostImages'];
latitude = json['Latitude'];
longitude = json['Longitude'];
nationalityFlagURL = json['NationalityFlagURL'];
nationalityID = json['NationalityID'];
nationalityName = json['NationalityName'];
nearestFreeSlot = json['NearestFreeSlot'];
noOfPatientsRate = json['NoOfPatientsRate'];
originalClinicID = json['OriginalClinicID'];
personRate = json['PersonRate'];
projectDistanceInKiloMeters = json['ProjectDistanceInKiloMeters'];
qR = json['QR'];
qRString = json['QRString'];
rateNumber = json['RateNumber'];
serviceID = json['ServiceID'];
setupID = json['SetupID'];
if (json.containsKey('Speciality') && json['Speciality'] != null) speciality = json['Speciality'].cast<String>();
if (json.containsKey('SpecialityN') && json['SpecialityN'] != null) specialityN = json['SpecialityN'].cast<String>();
workingHours = json['WorkingHours'];
decimalDoctorRate = json['DecimalDoctorRate'];
projectBottomName = json['ProjectNameBottom'];
projectTopName = json['ProjectNameTop'];
this.isHMC = json["IsHMC"];
this.regionArabic = json['RegionNameN'];
this.regionEnglish = json['RegionName'];
}
String? getRegionName(bool isArabic) {
if (isArabic) {
return regionArabic;
}
return regionEnglish;
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['ClinicID'] = this.clinicID;
data['AppointmentNo'] = this.appointmentNo;
data['ClinicName'] = this.clinicName;
data['DoctorTitle'] = this.doctorTitle;
data['ID'] = this.iD;
data['Name'] = this.name;
data['ProjectID'] = this.projectID;
data['ProjectName'] = this.projectName;
data['ActualDoctorRate'] = this.actualDoctorRate;
data['ClinicRoomNo'] = this.clinicRoomNo;
data['Date'] = this.date;
data['DayName'] = this.dayName;
data['DoctorID'] = this.doctorID;
data['DoctorImageURL'] = this.doctorImageURL;
data['DoctorProfile'] = this.doctorProfile;
data['DoctorProfileInfo'] = this.doctorProfileInfo;
data['DoctorRate'] = this.doctorRate;
data['Gender'] = this.gender;
data['GenderDescription'] = this.genderDescription;
data['IsAppointmentAllowed'] = this.isAppointmentAllowed;
data['IsDoctorAllowVedioCall'] = this.isDoctorAllowVedioCall;
data['IsDoctorDummy'] = this.isDoctorDummy;
data['IsLiveCare'] = this.isLiveCare;
data['IsLiveCareClinic'] = this.isLiveCareClinic;
data['IsDoctorHasPrePostImages'] = this.isDoctorHasPrePostImages;
data['Latitude'] = this.latitude;
data['Longitude'] = this.longitude;
data['NationalityFlagURL'] = this.nationalityFlagURL;
data['NationalityID'] = this.nationalityID;
data['NationalityName'] = this.nationalityName;
data['NearestFreeSlot'] = this.nearestFreeSlot;
data['NoOfPatientsRate'] = this.noOfPatientsRate;
data['OriginalClinicID'] = this.originalClinicID;
data['PersonRate'] = this.personRate;
data['ProjectDistanceInKiloMeters'] = this.projectDistanceInKiloMeters;
data['QR'] = this.qR;
data['QRString'] = this.qRString;
data['RateNumber'] = this.rateNumber;
data['ServiceID'] = this.serviceID;
data['SetupID'] = this.setupID;
data['Speciality'] = this.speciality;
data['SpecialityN'] = this.specialityN;
data['WorkingHours'] = this.workingHours;
data['DecimalDoctorRate'] = this.decimalDoctorRate;
return data;
}
String getProjectCompleteName(){
return "${this.projectTopName} ${this.projectBottomName}";
}
String getProjectCompleteNameWithLocale({bool isArabic = false}) {
if (isArabic) {
return "${this.projectBottomName} ${this.projectTopName}";
}
return "${this.projectTopName} ${this.projectBottomName}";
}
}
class PatientDoctorAppointmentList {
String? filterName = "";
String? distanceInKMs = "";
List<DoctorList>? patientDoctorAppointmentList = [];
String? projectTopName = "";
String? projectBottomName = "";
bool? isHMC;
List<HospitalsModel> hospitalList = [];
PatientDoctorAppointmentList(
{this.filterName,
this.distanceInKMs,
this.projectTopName,
this.projectBottomName,
DoctorList? patientDoctorAppointment,
HospitalsModel? model,
this.isHMC = false}) {
if (model != null) {
hospitalList.add(model);
}
if (patientDoctorAppointment != null) {
patientDoctorAppointmentList!.add(patientDoctorAppointment!);
}
}
}
class PatientDoctorAppointmentListByRegion {
List<PatientDoctorAppointmentList>? hmgDoctorList = [];
List<PatientDoctorAppointmentList>? hmcDoctorList = [];
int hmcSize = 0;
int hmgSize = 0;
num distance = double.infinity;
num hmgDistance = double.infinity;
num hmcDistance = double.infinity;
}
class RegionList {
Map<String, PatientDoctorAppointmentListByRegion?>? registeredDoctorMap = {};
}

@ -0,0 +1,104 @@
class HospitalsModel {
String? desciption;
dynamic desciptionN;
dynamic iD;
String? legalName;
String? legalNameN;
String? name;
dynamic nameN;
String? phoneNumber;
String? setupID;
dynamic distanceInKilometers;
bool? isActive;
String? latitude;
String? longitude;
dynamic mainProjectID;
bool? projectOutSA;
bool? usingInDoctorApp;
bool? isHMC;
String? region;
String? regionArabic;
String? regionEnglish;
String? regionID;
HospitalsModel({
this.desciption,
this.desciptionN,
this.iD,
this.legalName,
this.legalNameN,
this.name,
this.nameN,
this.phoneNumber,
this.setupID,
this.distanceInKilometers,
this.isActive,
this.latitude,
this.longitude,
this.mainProjectID,
this.projectOutSA,
this.usingInDoctorApp,
this.isHMC,
this.region,
this.regionArabic,
this.regionEnglish,
this.regionID,
});
HospitalsModel.fromJson(Map<String, dynamic> json) {
desciption = json['Desciption'];
desciptionN = json['DesciptionN'];
iD = json['ID'];
legalName = json['LegalName'];
legalNameN = json['LegalNameN'];
name = json['Name'];
nameN = json['NameN'];
phoneNumber = json['PhoneNumber'];
setupID = json['SetupID'];
distanceInKilometers = json['DistanceInKilometers'];
isActive = json['IsActive'];
latitude = json['Latitude'];
longitude = json['Longitude'];
mainProjectID = json['MainProjectID'];
projectOutSA = json['ProjectOutSA'];
usingInDoctorApp = json['UsingInDoctorApp'];
this.isHMC = json["IsHMC"];
this.regionArabic = json['RegionNameN'];
this.regionEnglish = json['RegionName'];
}
String? getRegionName(bool isArabic) {
if (isArabic) {
return regionArabic;
}
return regionEnglish;
}
String? getName(bool isArabic) {
if (isArabic) {
return "$nameN";
}
return name;
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['Desciption'] = this.desciption;
data['DesciptionN'] = this.desciptionN;
data['ID'] = this.iD;
data['LegalName'] = this.legalName;
data['LegalNameN'] = this.legalNameN;
data['Name'] = this.name;
data['NameN'] = this.nameN;
data['PhoneNumber'] = this.phoneNumber;
data['SetupID'] = this.setupID;
data['DistanceInKilometers'] = this.distanceInKilometers;
data['IsActive'] = this.isActive;
data['Latitude'] = this.latitude;
data['Longitude'] = this.longitude;
data['MainProjectID'] = this.mainProjectID;
data['ProjectOutSA'] = this.projectOutSA;
data['UsingInDoctorApp'] = this.usingInDoctorApp;
return data;
}
}

@ -3,6 +3,8 @@ 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/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';
@ -36,6 +38,9 @@ abstract class MyAppointmentsRepo {
Future<Either<Failure, GenericApiModel<List<PatientAppointmentHistoryResponseModel>>>> getPatientAppointmentsForTimeLine(); Future<Either<Failure, GenericApiModel<List<PatientAppointmentHistoryResponseModel>>>> getPatientAppointmentsForTimeLine();
Future<Either<Failure, GenericApiModel<List<PatientAppointmentHistoryResponseModel>>>> getPatientDoctorsList(); Future<Either<Failure, GenericApiModel<List<PatientAppointmentHistoryResponseModel>>>> getPatientDoctorsList();
Future<Either<Failure, GenericApiModel<List<HospitalsModel>>>>
getProjectList();
} }
class MyAppointmentsRepoImp implements MyAppointmentsRepo { class MyAppointmentsRepoImp implements MyAppointmentsRepo {
@ -491,4 +496,47 @@ class MyAppointmentsRepoImp implements MyAppointmentsRepo {
return Left(UnknownFailure(e.toString())); return Left(UnknownFailure(e.toString()));
} }
} }
@override
Future<Either<Failure, GenericApiModel<List<HospitalsModel>>>>
getProjectList() async {
Map<String, dynamic> request = {};
try {
GenericApiModel<List<HospitalsModel>>? apiResponse;
Failure? failure;
await apiClient.post(
GET_PROJECT_LIST,
body: request,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
final list = response['ListProject'];
final appointmentsList = list
.map((item) =>
HospitalsModel.fromJson(item as Map<String, dynamic>))
.toList()
.cast<HospitalsModel>();
apiResponse = GenericApiModel<List<HospitalsModel>>(
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()));
}
}
} }

@ -1,38 +1,60 @@
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/cache_consts.dart' show CacheConst;
import 'package:hmg_patient_app_new/core/utils/utils.dart' show Utils;
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'
show RegionList, PatientDoctorAppointmentListByRegion;
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/services/error_handler_service.dart'; import 'package:hmg_patient_app_new/services/error_handler_service.dart';
import 'package:location/location.dart' show Location;
import '../../core/utils/doctor_response_mapper.dart' show DoctorMapper;
class MyAppointmentsViewModel extends ChangeNotifier { class MyAppointmentsViewModel extends ChangeNotifier {
int selectedTabIndex = 0; int selectedTabIndex = 0;
MyAppointmentsRepo myAppointmentsRepo; MyAppointmentsRepo myAppointmentsRepo;
ErrorHandlerService errorHandlerService; ErrorHandlerService errorHandlerService;
AppState appState;
bool isMyAppointmentsLoading = false; bool isMyAppointmentsLoading = false;
bool isAppointmentPatientShareLoading = false; bool isAppointmentPatientShareLoading = false;
bool isTimeLineAppointmentsLoading = false; bool isTimeLineAppointmentsLoading = false;
bool isPatientMyDoctorsLoading = false; bool isPatientMyDoctorsLoading = false;
List<PatientAppointmentHistoryResponseModel> patientAppointmentsHistoryList = []; List<PatientAppointmentHistoryResponseModel> patientAppointmentsHistoryList =
[];
List<PatientAppointmentHistoryResponseModel> patientUpcomingAppointmentsHistoryList = []; List<PatientAppointmentHistoryResponseModel>
List<PatientAppointmentHistoryResponseModel> patientArrivedAppointmentsHistoryList = []; patientUpcomingAppointmentsHistoryList = [];
List<PatientAppointmentHistoryResponseModel>
patientArrivedAppointmentsHistoryList = [];
List<PatientAppointmentHistoryResponseModel> patientTimelineAppointmentsList = []; List<PatientAppointmentHistoryResponseModel> patientTimelineAppointmentsList =
[];
List<PatientAppointmentHistoryResponseModel> patientMyDoctorsList = []; List<PatientAppointmentHistoryResponseModel> patientMyDoctorsList = [];
PatientAppointmentShareResponseModel? patientAppointmentShareResponseModel; PatientAppointmentShareResponseModel? patientAppointmentShareResponseModel;
MyAppointmentsViewModel({required this.myAppointmentsRepo, required this.errorHandlerService}); RegionList? hospitalList;
RegionList? filteredHospitalList;
FacilitySelection currentlySelectedFacility = FacilitySelection.ALL;
bool isRegionListLoading = false;
MyAppointmentsViewModel(
{required this.myAppointmentsRepo, required this.errorHandlerService,required this.appState});
void onTabChange(int index) { void onTabChange(int index) {
selectedTabIndex = index; selectedTabIndex = index;
notifyListeners(); notifyListeners();
} }
initAppointmentsViewModel() { initAppointmentsViewModel() {
patientAppointmentsHistoryList.clear(); patientAppointmentsHistoryList.clear();
patientUpcomingAppointmentsHistoryList.clear(); patientUpcomingAppointmentsHistoryList.clear();
@ -43,6 +65,7 @@ class MyAppointmentsViewModel extends ChangeNotifier {
isAppointmentPatientShareLoading = true; isAppointmentPatientShareLoading = true;
isTimeLineAppointmentsLoading = true; isTimeLineAppointmentsLoading = true;
isPatientMyDoctorsLoading = true; isPatientMyDoctorsLoading = true;
isRegionListLoading = true;
notifyListeners(); notifyListeners();
} }
@ -66,7 +89,8 @@ class MyAppointmentsViewModel extends ChangeNotifier {
notifyListeners(); 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) {
patientAppointmentsHistoryList[index].hasReminder = value; patientAppointmentsHistoryList[index].hasReminder = value;
@ -74,12 +98,18 @@ class MyAppointmentsViewModel extends ChangeNotifier {
} }
} }
Future<void> getPatientAppointments(bool isActiveAppointment, bool isArrivedAppointments, {Function(dynamic)? onSuccess, Function(String)? onError}) async { Future<void> getPatientAppointments(
final result = await myAppointmentsRepo.getPatientAppointments(isActiveAppointment: isActiveAppointment, isArrivedAppointments: isArrivedAppointments); bool isActiveAppointment, bool isArrivedAppointments,
final resultArrived = await myAppointmentsRepo.getPatientAppointments(isActiveAppointment: false, isArrivedAppointments: true); {Function(dynamic)? onSuccess, Function(String)? onError}) async {
final result = await myAppointmentsRepo.getPatientAppointments(
isActiveAppointment: isActiveAppointment,
isArrivedAppointments: isArrivedAppointments);
final resultArrived = await myAppointmentsRepo.getPatientAppointments(
isActiveAppointment: false, isArrivedAppointments: true);
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: () {});
@ -95,7 +125,8 @@ class MyAppointmentsViewModel extends ChangeNotifier {
); );
resultArrived.fold( resultArrived.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: () {});
@ -110,19 +141,27 @@ class MyAppointmentsViewModel extends ChangeNotifier {
}, },
); );
patientAppointmentsHistoryList.addAll(patientUpcomingAppointmentsHistoryList); patientAppointmentsHistoryList
patientAppointmentsHistoryList.addAll(patientArrivedAppointmentsHistoryList); .addAll(patientUpcomingAppointmentsHistoryList);
patientAppointmentsHistoryList
.addAll(patientArrivedAppointmentsHistoryList);
print('Upcoming Appointments: ${patientUpcomingAppointmentsHistoryList.length}'); print(
print('Arrived Appointments: ${patientArrivedAppointmentsHistoryList.length}'); 'Upcoming Appointments: ${patientUpcomingAppointmentsHistoryList.length}');
print(
'Arrived Appointments: ${patientArrivedAppointmentsHistoryList.length}');
print('All Appointments: ${patientAppointmentsHistoryList.length}'); print('All Appointments: ${patientAppointmentsHistoryList.length}');
} }
Future<void> getPatientShareAppointment(int projectID, int clinicID, String appointmentNo, {Function(dynamic)? onSuccess, Function(String)? onError}) async { Future<void> getPatientShareAppointment(
final result = await myAppointmentsRepo.getPatientShareAppointment(projectID: projectID, clinicID: clinicID, appointmentNo: appointmentNo); int projectID, int clinicID, String appointmentNo,
{Function(dynamic)? onSuccess, Function(String)? onError}) async {
final result = await myAppointmentsRepo.getPatientShareAppointment(
projectID: projectID, clinicID: clinicID, appointmentNo: appointmentNo);
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: () {});
@ -139,11 +178,19 @@ class MyAppointmentsViewModel extends ChangeNotifier {
} }
Future<void> addAdvanceNumberRequest( Future<void> addAdvanceNumberRequest(
{required String advanceNumber, required String paymentReference, required String appointmentNo, Function(dynamic)? onSuccess, Function(String)? onError}) async { {required String advanceNumber,
final result = await myAppointmentsRepo.addAdvanceNumberRequest(advanceNumber: advanceNumber, paymentReference: paymentReference, appointmentNo: appointmentNo); required String paymentReference,
required String appointmentNo,
Function(dynamic)? onSuccess,
Function(String)? onError}) async {
final result = await myAppointmentsRepo.addAdvanceNumberRequest(
advanceNumber: advanceNumber,
paymentReference: paymentReference,
appointmentNo: appointmentNo);
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: () {});
@ -158,11 +205,21 @@ class MyAppointmentsViewModel extends ChangeNotifier {
} }
Future<void> generateAppointmentQR( Future<void> generateAppointmentQR(
{required int clinicID, required int projectID, required String appointmentNo, required int isFollowUp, Function(dynamic)? onSuccess, Function(String)? onError}) async { {required int clinicID,
final result = await myAppointmentsRepo.generateAppointmentQR(clinicID: clinicID, projectID: projectID, appointmentNo: appointmentNo, isFollowUp: isFollowUp); required int projectID,
required String appointmentNo,
required int isFollowUp,
Function(dynamic)? onSuccess,
Function(String)? onError}) async {
final result = await myAppointmentsRepo.generateAppointmentQR(
clinicID: clinicID,
projectID: projectID,
appointmentNo: appointmentNo,
isFollowUp: isFollowUp);
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: () {});
@ -176,11 +233,18 @@ class MyAppointmentsViewModel extends ChangeNotifier {
); );
} }
Future<void> cancelAppointment({required PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel, Function(dynamic)? onSuccess, Function(String)? onError}) async { Future<void> cancelAppointment(
final result = await myAppointmentsRepo.cancelAppointment(patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel); {required PatientAppointmentHistoryResponseModel
patientAppointmentHistoryResponseModel,
Function(dynamic)? onSuccess,
Function(String)? onError}) async {
final result = await myAppointmentsRepo.cancelAppointment(
patientAppointmentHistoryResponseModel:
patientAppointmentHistoryResponseModel);
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) {
onError!(apiResponse.errorMessage!); onError!(apiResponse.errorMessage!);
@ -195,11 +259,18 @@ class MyAppointmentsViewModel extends ChangeNotifier {
); );
} }
Future<void> confirmAppointment({required PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel, Function(dynamic)? onSuccess, Function(String)? onError}) async { Future<void> confirmAppointment(
final result = await myAppointmentsRepo.confirmAppointment(patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel); {required PatientAppointmentHistoryResponseModel
patientAppointmentHistoryResponseModel,
Function(dynamic)? onSuccess,
Function(String)? onError}) async {
final result = await myAppointmentsRepo.confirmAppointment(
patientAppointmentHistoryResponseModel:
patientAppointmentHistoryResponseModel);
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) {
onError!(apiResponse.errorMessage!); onError!(apiResponse.errorMessage!);
@ -236,7 +307,8 @@ class MyAppointmentsViewModel extends ChangeNotifier {
patientType: patientType); patientType: patientType);
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: () {});
@ -251,15 +323,21 @@ class MyAppointmentsViewModel extends ChangeNotifier {
} }
Future<void> sendCheckInNfcRequest( Future<void> sendCheckInNfcRequest(
{required PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel, {required PatientAppointmentHistoryResponseModel
patientAppointmentHistoryResponseModel,
required String scannedCode, required String scannedCode,
required int checkInType, required int checkInType,
Function(dynamic)? onSuccess, Function(dynamic)? onSuccess,
Function(String)? onError}) async { Function(String)? onError}) async {
final result = await myAppointmentsRepo.sendCheckInNfcRequest(patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel, scannedCode: scannedCode, checkInType: checkInType); final result = await myAppointmentsRepo.sendCheckInNfcRequest(
patientAppointmentHistoryResponseModel:
patientAppointmentHistoryResponseModel,
scannedCode: scannedCode,
checkInType: checkInType);
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) {
onError!(apiResponse.errorMessage!); onError!(apiResponse.errorMessage!);
@ -274,11 +352,13 @@ class MyAppointmentsViewModel extends ChangeNotifier {
); );
} }
Future<void> getPatientMyDoctors({Function(dynamic)? onSuccess, Function(String)? onError}) async { Future<void> getPatientMyDoctors(
{Function(dynamic)? onSuccess, Function(String)? onError}) async {
final result = await myAppointmentsRepo.getPatientDoctorsList(); final result = await myAppointmentsRepo.getPatientDoctorsList();
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: () {});
@ -293,4 +373,84 @@ class MyAppointmentsViewModel extends ChangeNotifier {
}, },
); );
} }
Future<void> getRegionMappedProjectList() async {
if(hospitalList != null && hospitalList!.registeredDoctorMap != null && hospitalList!.registeredDoctorMap!.isNotEmpty){
filteredHospitalList = hospitalList;
return;
}
isRegionListLoading = true;
notifyListeners();
final result = await myAppointmentsRepo.getProjectList();
result.fold(
(failure) async =>
await errorHandlerService.handleError(failure: failure),
(apiResponse) async {
if (apiResponse.messageStatus == 2) {
// dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
} else if (apiResponse.messageStatus == 1) {
var projectList = apiResponse.data!;
hospitalList = await DoctorMapper.getMappedHospitals(projectList,
isArabic: false);
var lat = await Utils.getNumFromPrefs(CacheConst.userLat);
var lng = await Utils.getNumFromPrefs(CacheConst.userLong);
var isLocationEnabled = (lat != 0) && (lng != 0);
hospitalList =
await DoctorMapper.sortList(isLocationEnabled, hospitalList!);
isRegionListLoading = false;
filteredHospitalList = hospitalList;
notifyListeners();
}
},
);
}
void setSelectedFacility(FacilitySelection selection) {
currentlySelectedFacility = selection;
notifyListeners();
}
void filterHospitalListByString(String? value, String? selectedRegionId, bool isHMG) {
if(value ==null || value.isEmpty){
filteredHospitalList = hospitalList;
} else {
filteredHospitalList = RegionList();
var list = isHMG
? hospitalList?.registeredDoctorMap![selectedRegionId]!.hmgDoctorList
: hospitalList?.registeredDoctorMap![selectedRegionId]!.hmcDoctorList;
if(list != null && list.isEmpty){ notifyListeners(); return;}
var filteredList = list!.where((element) =>
element.filterName!.toLowerCase().contains(value.toLowerCase())
).toList();
var regionData = PatientDoctorAppointmentListByRegion();
if(isHMG){
regionData.hmgDoctorList = filteredList;
regionData.hmgSize = filteredList.length;
} else {
regionData.hmcDoctorList = filteredList;
regionData.hmcSize = filteredList.length;
}
filteredHospitalList?.registeredDoctorMap = {
selectedRegionId! : regionData
};
}
notifyListeners();
}
Future<bool> isLocationEnabled() async{
return await Location().serviceEnabled();
}
bool getLocationStatus() {
bool isLocationAvaiable = false;
isLocationEnabled().then((value) => isLocationAvaiable = value);
return isLocationAvaiable;
}
} }

@ -812,8 +812,18 @@ abstract class LocaleKeys {
static const notNow = 'notNow'; static const notNow = 'notNow';
static const pendingActivation = 'pendingActivation'; static const pendingActivation = 'pendingActivation';
static const awaitingApproval = 'awaitingApproval'; static const awaitingApproval = 'awaitingApproval';
static const news = 'news';
static const ready = 'ready'; static const ready = 'ready';
static const enterValidNationalId = 'enterValidNationalId'; static const enterValidNationalId = 'enterValidNationalId';
static const enterValidPhoneNumber = 'enterValidPhoneNumber'; static const enterValidPhoneNumber = 'enterValidPhoneNumber';
static const medicalCentersWithCount = 'medicalCentersWithCount';
static const medicalCenters = 'medicalCenters';
static const hospitalsWithCount = 'hospitalsWithCount';
static const selectRegion = 'selectRegion';
static const selectFacility = 'selectFacility';
static const selectFacilitiesSubTitle = 'selectFacilitiesSubTitle';
static const selectHospitalSubTitle = 'selectHospitalSubTitle';
static const iAcceptThe = 'iAcceptThe';
static const personalDetailsVerification = 'personalDetailsVerification';
} }

@ -14,6 +14,7 @@ import 'package:hmg_patient_app_new/features/habib_wallet/habib_wallet_view_mode
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_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/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/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_view_model.dart'; import 'package:hmg_patient_app_new/features/prescriptions/prescriptions_view_model.dart';
@ -110,6 +111,7 @@ void main() async {
create: (_) => MyAppointmentsViewModel( create: (_) => MyAppointmentsViewModel(
myAppointmentsRepo: getIt(), myAppointmentsRepo: getIt(),
errorHandlerService: getIt(), errorHandlerService: getIt(),
appState: getIt(),
), ),
), ),
ChangeNotifierProvider<PayfortViewModel>( ChangeNotifierProvider<PayfortViewModel>(
@ -143,6 +145,8 @@ void main() async {
localAuthService: getIt(), localAuthService: getIt(),
), ),
), ),
ChangeNotifierProvider<AppointmentViaRegionViewmodel>(
create: (_) => AppointmentViaRegionViewmodel())
], child: MyApp()), ], child: MyApp()),
), ),
); );

@ -0,0 +1,88 @@
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/theme/colors.dart' show AppColors;
import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
class FacilitySelectionItem extends StatelessWidget {
final String svgPath;
final String title;
final String subTitle;
const FacilitySelectionItem(
{super.key,
required this.svgPath,
required this.subTitle,
required this.title});
@override
Widget build(BuildContext context) {
return Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 20.h,
hasShadow: false,
),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Utils.buildSvgWithAssets(
icon: svgPath,
width: 32,
height: 32,
fit: BoxFit.contain,
),
SizedBox(height: 16,),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
info,
Utils.buildSvgWithAssets(
icon: AppAssets.forward_arrow_icon,
iconColor: AppColors.blackColor,
width: 18,
height: 13,
fit: BoxFit.contain,
),
],
)
],
).paddingAll(16.h),
);
}
Widget get info => Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Align(
alignment: Alignment.centerLeft,
child: Text(
title,
style: TextStyle(
fontSize: 16.h,
fontWeight: FontWeight.w600,
color: AppColors.blackColor,
),
),
),
Align(
alignment: Alignment.centerLeft,
child: Text(
subTitle,
style: TextStyle(
fontSize: 12.h,
fontWeight: FontWeight.w500,
color: AppColors.greyTextColor,
),
),
),
],
);
}

@ -0,0 +1,79 @@
import 'package:easy_localization/easy_localization.dart'
show tr, StringTranslateExtension;
import 'package:flutter/cupertino.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/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'
show MyAppointmentsViewModel;
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/appointments/widgets/faculity_selection/facility_selection_item.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:provider/provider.dart' show Provider;
class FacilityTypeSelectionWidget extends StatelessWidget {
late MyAppointmentsViewModel myAppointmentsViewModel;
late AppointmentViaRegionViewmodel regionalViewModel;
final String selectedRegion;
FacilityTypeSelectionWidget({super.key, required this.selectedRegion});
@override
Widget build(BuildContext context) {
myAppointmentsViewModel = Provider.of<MyAppointmentsViewModel>(context);
regionalViewModel = Provider.of<AppointmentViaRegionViewmodel>(context);
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
LocaleKeys.selectFacility.tr(),
style: TextStyle(
fontSize: 21,
fontWeight: FontWeight.w600,
color: AppColors.blackColor,
),
),
Text(
LocaleKeys.selectFacilitiesSubTitle,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
color: AppColors.greyTextColor,
),
),
SizedBox(height: 24.h),
FacilitySelectionItem(
svgPath: AppAssets.hmg,
title: "HMG".needTranslation,
subTitle: LocaleKeys.hospitalsWithCount.tr(namedArgs: {
'count':
"${myAppointmentsViewModel.hospitalList?.registeredDoctorMap?[selectedRegion]?.hmgSize ?? 0}"
}),
).onPress(
() {
regionalViewModel.setFacility(FacilitySelection.HMG.name);
regionalViewModel.setBottomSheetState(
AppointmentViaRegionState.HOSPITAL_SELECTION);
},
),
SizedBox(height: 16.h),
FacilitySelectionItem(
svgPath: AppAssets.hmc,
title: "HMC".needTranslation,
subTitle: LocaleKeys.medicalCentersWithCount.tr(namedArgs: {
'count':
"${myAppointmentsViewModel.hospitalList?.registeredDoctorMap?[selectedRegion]?.hmcSize ?? 0}"
})).onPress(
() {
regionalViewModel.setFacility(FacilitySelection.HMC.name);
regionalViewModel.setBottomSheetState(
AppointmentViaRegionState.HOSPITAL_SELECTION);
},
),
],
);
}
}

@ -0,0 +1,109 @@
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/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/presentation/lab/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 HospitalBottomSheetBody extends StatelessWidget {
late MyAppointmentsViewModel appointmentsViewModel;
late AppointmentViaRegionViewmodel regionalViewModel;
final TextEditingController searchText = TextEditingController();
HospitalBottomSheetBody({super.key});
@override
Widget build(BuildContext context) {
appointmentsViewModel = Provider.of<MyAppointmentsViewModel>(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,
),
),
Text(
LocaleKeys.selectHospitalSubTitle.tr(),
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
color: AppColors.greyTextColor,
),
),
SizedBox(height: 16.h),
TextInputWidget(
labelText: LocaleKeys.search.tr(),
hintText: "Search Hospital".tr(),
controller: searchText,
onChange: (value) {
appointmentsViewModel.filterHospitalListByString(value, regionalViewModel.selectedRegionId , regionalViewModel.selectedFacilityType ==
FacilitySelection.HMG.name);
},
isEnable: true,
prefix: null,
autoFocus: false,
isBorderAllowed: false,
keyboardType: TextInputType.text,
isAllowLeadingIcon: true,
selectionType: SelectionTypeEnum.search,
padding: EdgeInsets.symmetric(
vertical: ResponsiveExtension(10).h,
horizontal: ResponsiveExtension(15).h,
),
),
SizedBox(height: 24.h),
// TypeSelectionWidget(
// hmcCount: "0",
// hmgCount: "0",
// ),
// SizedBox(height: 21.h),
SizedBox(
height: MediaQuery.sizeOf(context).height * .4,
child: ListView.separated(
itemBuilder: (_, index) => HospitalListItem(
hospitalData: regionalViewModel.selectedFacilityType ==
FacilitySelection.HMG.name
? appointmentsViewModel
.filteredHospitalList!
.registeredDoctorMap![
regionalViewModel.selectedRegionId!]!
.hmgDoctorList![index]
: appointmentsViewModel
.filteredHospitalList
?.registeredDoctorMap?[
regionalViewModel.selectedRegionId!]
?.hmcDoctorList?[index],
isLocationEnabled: appointmentsViewModel.getLocationStatus(),
),
separatorBuilder: (_, __) => SizedBox(
height: 16.h,
),
itemCount: (regionalViewModel.selectedFacilityType ==
FacilitySelection.HMG.name
? (appointmentsViewModel.filteredHospitalList?.registeredDoctorMap?[
regionalViewModel.selectedRegionId]?.hmgDoctorList)
: (appointmentsViewModel
.filteredHospitalList
?.registeredDoctorMap?[
regionalViewModel.selectedRegionId]?.hmcDoctorList))?.length ??
0),
)
],
);
}
}

@ -0,0 +1,113 @@
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/app_export.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/doctor_list_api_response.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/hospital_model.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart';
class HospitalListItem extends StatelessWidget {
final PatientDoctorAppointmentList? hospitalData;
final bool isLocationEnabled;
late AppState appState;
HospitalListItem(
{super.key, required this.hospitalData, required this.isLocationEnabled});
@override
Widget build(BuildContext context) {
appState = getIt.get<AppState>();
return DecoratedBox(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 20.h,
hasShadow: false,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
spacing: 8.h,
children: [hospitalName, distanceInfo],
),
),
Utils.buildSvgWithAssets(
icon: AppAssets.forward_arrow_icon,
iconColor: AppColors.blackColor,
width: 18,
height: 13,
fit: BoxFit.contain,
),
],
).paddingSymmetrical(16.h, 16.h),
);
}
Widget get hospitalName => Row(
children: [
Utils.buildSvgWithAssets(
icon: (hospitalData?.isHMC == true) ? AppAssets.hmc : AppAssets.hmg,
).paddingOnly(right: 10),
Expanded(
child: Text(
hospitalData?.filterName ?? "",
style: TextStyle(
fontWeight: FontWeight.w600,
fontSize: 16,
color: AppColors.blackColor,
),
),
)
],
);
Widget get distanceInfo => Row(
children: [
Visibility(
visible: (hospitalData?.distanceInKMs != "0"),
child:
AppCustomChipWidget(
labelText:
"${hospitalData?.distanceInKMs ?? ""} km".needTranslation,
deleteIcon: AppAssets.location_red,
deleteIconSize: Size(9, 12),
backgroundColor: AppColors.secondaryLightRedColor,
textColor: AppColors.errorColor,
),
),
Visibility(
visible: (hospitalData?.distanceInKMs == "0"),
child: Row(
children: [
AppCustomChipWidget(
labelText: "Distance not available".needTranslation,
textColor: AppColors.blackColor,
),
SizedBox(width: 8.h,)
],
)),
Visibility(
visible: !isLocationEnabled,
child: AppCustomChipWidget(
labelText: "Location turned off".needTranslation,
deleteIcon: AppAssets.location_unavailable,
deleteIconSize: Size(9, 12),
textColor: AppColors.blackColor,
)),
],
);
}

@ -0,0 +1,93 @@
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/facility_selection.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:provider/provider.dart' show Consumer;
class TypeSelectionWidget extends StatelessWidget {
final String hmcCount;
final String hmgCount;
const TypeSelectionWidget(
{super.key, required this.hmcCount, required this.hmgCount});
@override
Widget build(BuildContext context) {
return Consumer<MyAppointmentsViewModel>(
builder: (_, data, __) => Row(
spacing: 8,
mainAxisSize: MainAxisSize.max,
children: [
AppCustomChipWidget(
labelText: "All Facilities".needTranslation,
shape: RoundedRectangleBorder(
side: BorderSide(
color: data.currentlySelectedFacility == FacilitySelection.ALL
? AppColors.errorColor
: AppColors.chipBorderColorOpacity20,
width: 1,
),
borderRadius: BorderRadius.circular(10)),
backgroundColor:
data.currentlySelectedFacility == FacilitySelection.ALL
? AppColors.secondaryLightRedColor
: AppColors.whiteColor,
textColor: data.currentlySelectedFacility == FacilitySelection.ALL
? AppColors.errorColor
: AppColors.blackColor,
).onPress((){
data.setSelectedFacility(FacilitySelection.ALL);
}),
AppCustomChipWidget(
icon: AppAssets.hmg,
iconHasColor: false,
labelText: "Hospitals".needTranslation,
shape: RoundedRectangleBorder(
side: BorderSide(
color: data.currentlySelectedFacility == FacilitySelection.HMG
? AppColors.errorColor
: AppColors.chipBorderColorOpacity20,
width: 1,
),
borderRadius: BorderRadius.circular(10)),
backgroundColor:
data.currentlySelectedFacility == FacilitySelection.HMG
? AppColors.secondaryLightRedColor
: AppColors.whiteColor,
textColor: data.currentlySelectedFacility == FacilitySelection.HMG
? AppColors.errorColor
: AppColors.blackColor,
).onPress((){
data.setSelectedFacility(FacilitySelection.HMG);
}),
AppCustomChipWidget(
icon: AppAssets.hmc,
iconHasColor: false,
labelText: "Medical Centers".needTranslation,
shape: RoundedRectangleBorder(
side: BorderSide(
color: data.currentlySelectedFacility == FacilitySelection.HMC
? AppColors.errorColor
: AppColors.chipBorderColorOpacity20,
width: 1,
),
borderRadius: BorderRadius.circular(10)),
backgroundColor:
data.currentlySelectedFacility == FacilitySelection.HMC
? AppColors.secondaryLightRedColor
: AppColors.whiteColor,
textColor: data.currentlySelectedFacility == FacilitySelection.HMC
? AppColors.errorColor
: AppColors.blackColor,
).onPress((){
data.setSelectedFacility(FacilitySelection.HMC);
}),
],
),
);
}
}

@ -0,0 +1,114 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/theme/colors.dart' show AppColors;
import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart';
class RegionListItem extends StatelessWidget {
final String title;
final String hmcCount;
final String hmgCount;
final String subTitle;
const RegionListItem(
{super.key,
required this.title,
required this.subTitle,
required this.hmcCount,
required this.hmgCount});
@override
Widget build(BuildContext context) {
return Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 20.h,
hasShadow: false,
),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
header,
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
spacing: 8.h,
children: [
placesCountItem(
AppAssets.hmg, hmgCount, " ${LocaleKeys.hospital.tr()}"),
placesCountItem(AppAssets.hmc, hmcCount,
" ${LocaleKeys.medicalCenters.tr()}"),
],
),
Utils.buildSvgWithAssets(
icon: AppAssets.forward_arrow_icon,
iconColor: AppColors.blackColor,
width: 18,
height: 13,
fit: BoxFit.contain,
),
],
)
],
).paddingAll(16.h),
);
}
Widget placesCountItem(String svgPath, String count, String title) {
return AppCustomChipWidget(
iconSize: 14,
icon: svgPath,
iconHasColor: false,
richText: RichText(
text: TextSpan(
text: count,
style: TextStyle(
fontSize: 12.h,
fontWeight: FontWeight.w700,
color: AppColors.blackColor),
children: [
TextSpan(
text: title,
style: TextStyle(
fontSize: 12.h,
fontWeight: FontWeight.w500,
color: AppColors.blackColor))
])),
);
}
Widget get header => Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Align(
alignment: Alignment.centerLeft,
child: Text(
title,
style: TextStyle(
fontSize: 16.h,
fontWeight: FontWeight.w600,
color: AppColors.blackColor,
),
),
),
Align(
alignment: Alignment.centerLeft,
child: Text(
subTitle,
style: TextStyle(
fontSize: 12.h,
fontWeight: FontWeight.w500,
color: AppColors.greyTextColor,
),
),
),
],
);
}

@ -0,0 +1,86 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart' show Utils;
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/my_appointments/appointment_via_region_viewmodel.dart';
import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart'
show MyAppointmentsViewModel;
import 'package:hmg_patient_app_new/presentation/appointments/widgets/region_bottomsheet/region_list_item.dart'
show RegionListItem;
import 'package:provider/provider.dart';
class RegionBottomSheetBody extends StatefulWidget {
const RegionBottomSheetBody({super.key});
@override
State<RegionBottomSheetBody> createState() => _RegionBottomSheetBodyState();
}
class _RegionBottomSheetBodyState extends State<RegionBottomSheetBody> {
late MyAppointmentsViewModel myAppointmentsViewModel;
late AppointmentViaRegionViewmodel regionalViewModel;
@override
void initState() {
scheduleMicrotask(() {
myAppointmentsViewModel.getRegionMappedProjectList();
});
super.initState();
}
@override
Widget build(BuildContext context) {
myAppointmentsViewModel = Provider.of<MyAppointmentsViewModel>(context);
regionalViewModel = Provider.of<AppointmentViaRegionViewmodel>(context);
return Consumer<MyAppointmentsViewModel>(
builder: (context, myAppointmentsVM, child) {
if (myAppointmentsVM.isRegionListLoading) {
return Container(
height: MediaQuery.of(context).size.height * 0.3,
decoration: const BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.vertical(top: Radius.circular(16)),
),
child: Center(
child: Utils.getLoadingWidget(),
),
);
} else {
return SizedBox(
height: MediaQuery.of(context).size.height * 0.5,
child: ListView.separated(
itemCount:
myAppointmentsVM.hospitalList?.registeredDoctorMap?.length ??
0,
separatorBuilder: (_, __) {
return SizedBox(
height: 16.h,
);
},
itemBuilder: (_, index) {
String key = myAppointmentsVM
.hospitalList?.registeredDoctorMap?.keys
.toList()[index] ??
'';
return RegionListItem(
title: key,
subTitle: "",
hmcCount:
"${myAppointmentsVM.hospitalList?.registeredDoctorMap?[key]?.hmcSize ?? 0}",
hmgCount:
"${myAppointmentsVM.hospitalList?.registeredDoctorMap?[key]?.hmgSize ?? 0}",
).onPress(() {
regionalViewModel.setSelectedRegionId(key);
regionalViewModel.setBottomSheetState(AppointmentViaRegionState.TYPE_SELECTION);
});
},
),
);
}
},
);
}
}

@ -4,6 +4,8 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/gestures.dart'; import 'package:flutter/gestures.dart';
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/app_assets.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/core/enums.dart'; import 'package:hmg_patient_app_new/core/enums.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';
@ -153,6 +155,7 @@ class LoginScreenState extends State<LoginScreen> {
required TextEditingController? phoneNumberController, required TextEditingController? phoneNumberController,
required AuthenticationViewModel authViewModel, required AuthenticationViewModel authViewModel,
}) { }) {
AppState appState = getIt<AppState>();
context.showBottomSheet( context.showBottomSheet(
isScrollControlled: true, isScrollControlled: true,
isDismissible: false, isDismissible: false,
@ -180,6 +183,8 @@ class LoginScreenState extends State<LoginScreen> {
onOkPress: () { onOkPress: () {
Navigator.of(context).pop(); Navigator.of(context).pop();
})) { })) {
Navigator.of(context).pop();
appState.setSelectDeviceByImeiRespModelElement(null);
await authViewModel.checkUserAuthentication(otpTypeEnum: OTPTypeEnum.sms); await authViewModel.checkUserAuthentication(otpTypeEnum: OTPTypeEnum.sms);
} }
}, },
@ -209,6 +214,8 @@ class LoginScreenState extends State<LoginScreen> {
onOkPress: () { onOkPress: () {
Navigator.of(context).pop(); Navigator.of(context).pop();
})) { })) {
Navigator.of(context).pop();
appState.setSelectDeviceByImeiRespModelElement(null);
await authViewModel.checkUserAuthentication(otpTypeEnum: OTPTypeEnum.whatsapp); await authViewModel.checkUserAuthentication(otpTypeEnum: OTPTypeEnum.whatsapp);
} }
}, },

@ -2,6 +2,8 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/gestures.dart'; import 'package:flutter/gestures.dart';
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/app_assets.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/core/enums.dart'; import 'package:hmg_patient_app_new/core/enums.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';
@ -152,12 +154,36 @@ class _RegisterNew extends State<RegisterNew> {
}, },
), ),
SizedBox(width: 12.h), SizedBox(width: 12.h),
Expanded( Row(
child: Text( children: [
LocaleKeys.iAcceptTermsConditions.tr(), Text(
style: context.dynamicTextStyle(fontSize: 14.fSize, fontWeight: FontWeight.w500, color: Color(0xFF2E3039)), LocaleKeys.iAcceptThe.tr(),
), style: context.dynamicTextStyle(fontSize: 14.fSize, fontWeight: FontWeight.w500, color: Color(0xFF2E3039)),
),
GestureDetector(
onTap: () {
// Navigate to terms and conditions page
Navigator.of(context).pushNamed('/terms');
},
child: Text(
LocaleKeys.termsConditoins.tr(),
style: context.dynamicTextStyle(
fontSize: 14.fSize,
fontWeight: FontWeight.w500,
color: AppColors.primaryRedColor,
decoration: TextDecoration.underline,
decorationColor: AppColors.primaryRedBorderColor,
),
),
),
],
), ),
// Expanded(
// child: Text(
// LocaleKeys.iAcceptTermsConditions.tr().split("the").first,
// style: context.dynamicTextStyle(fontSize: 14.fSize, fontWeight: FontWeight.w500, color: Color(0xFF2E3039)),
// ),
// ),
], ],
), ),
), ),
@ -224,6 +250,7 @@ class _RegisterNew extends State<RegisterNew> {
} }
void showRegisterModel({required BuildContext context, required AuthenticationViewModel authVM}) { void showRegisterModel({required BuildContext context, required AuthenticationViewModel authVM}) {
AppState appState = getIt.get<AppState>();
showModalBottomSheet( showModalBottomSheet(
context: context, context: context,
isScrollControlled: true, isScrollControlled: true,
@ -235,7 +262,7 @@ class _RegisterNew extends State<RegisterNew> {
child: SingleChildScrollView( child: SingleChildScrollView(
child: GenericBottomSheet( child: GenericBottomSheet(
countryCode: authVM.selectedCountrySignup.countryCode, countryCode: authVM.selectedCountrySignup.countryCode,
initialPhoneNumber: "", initialPhoneNumber: authVM.phoneNumberController.text,
textController: authVM.phoneNumberController, textController: authVM.phoneNumberController,
isEnableCountryDropdown: false, isEnableCountryDropdown: false,
onCountryChange: authVM.onCountryChange, onCountryChange: authVM.onCountryChange,
@ -256,6 +283,7 @@ class _RegisterNew extends State<RegisterNew> {
Navigator.of(context).pop(); Navigator.of(context).pop();
}, },
)) { )) {
appState.setSelectDeviceByImeiRespModelElement(null);
await authVM.onRegistrationStart(otpTypeEnum: OTPTypeEnum.sms); await authVM.onRegistrationStart(otpTypeEnum: OTPTypeEnum.sms);
} }
}, },
@ -280,15 +308,14 @@ class _RegisterNew extends State<RegisterNew> {
child: CustomButton( child: CustomButton(
text: LocaleKeys.sendOTPWHATSAPP.tr(), text: LocaleKeys.sendOTPWHATSAPP.tr(),
onPressed: () async { onPressed: () async {
// Dismiss keyboard before validation
FocusScope.of(context).unfocus(); FocusScope.of(context).unfocus();
if (ValidationUtils.isValidatePhone( if (ValidationUtils.isValidatePhone(
phoneNumber: authVM.phoneNumberController.text, phoneNumber: authVM.phoneNumberController.text,
onOkPress: () { onOkPress: () {
Navigator.of(context).pop(); Navigator.of(context).pop();
}, },
)) { )) {
appState.setSelectDeviceByImeiRespModelElement(null);
await authVM.onRegistrationStart(otpTypeEnum: OTPTypeEnum.whatsapp); await authVM.onRegistrationStart(otpTypeEnum: OTPTypeEnum.whatsapp);
} }
}, },
@ -296,6 +323,7 @@ class _RegisterNew extends State<RegisterNew> {
borderColor: AppColors.borderOnlyColor, borderColor: AppColors.borderOnlyColor,
textColor: AppColors.textColor, textColor: AppColors.textColor,
icon: AppAssets.whatsapp, icon: AppAssets.whatsapp,
iconColor: null,
), ),
), ),
], ],

@ -44,20 +44,24 @@ class _RegisterNew extends State<RegisterNewStep2> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
AppState appState = getIt.get<AppState>(); AppState appState = getIt.get<AppState>();
return Scaffold( return Scaffold(
backgroundColor: AppColors.bgScaffoldColor,
appBar: CustomAppBar( appBar: CustomAppBar(
onBackPressed: () { onBackPressed: () {
Navigator.of(context).pop(); Navigator.of(context).pop();
// authVM!.clearDefaultInputValues(); // authVM!.clearDefaultInputValues();
authVM!.clearEmailInput();
}, },
onLanguageChanged: (lang) {}, onLanguageChanged: (lang) {},
hideLogoAndLang: true, hideLogoAndLang: true,
), ),
body: SingleChildScrollView( body: SingleChildScrollView(
reverse: false, reverse: false,
padding: EdgeInsets.only(left: 24.h, right: 24.h, top: 24.h), padding: EdgeInsets.only(left: 24.h, right: 24.h, top: 0.h),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[ children: <Widget>[
LocaleKeys.personalDetailsVerification.tr().toText26(color: AppColors.textColor, weight: FontWeight.w600, letterSpacing: -2),
SizedBox(height: 24.h),
Directionality( Directionality(
textDirection: Directionality.of(context), textDirection: Directionality.of(context),
child: Container( child: Container(
@ -66,18 +70,20 @@ class _RegisterNew extends State<RegisterNewStep2> {
child: Column( child: Column(
children: [ children: [
TextInputWidget( TextInputWidget(
labelText: authVM!.isUserFromUAE() ? LocaleKeys.fullName.tr() : LocaleKeys.name.tr(), labelText: authVM!.isUserFromUAE() ? LocaleKeys.fullName.tr() : LocaleKeys.name.tr(),
hintText: authVM!.isUserFromUAE() ? LocaleKeys.enterNameHere.tr() : ("${appState.getNHICUserData.firstNameEn!.toUpperCase()} ${appState.getNHICUserData.lastNameEn!.toUpperCase()}"), hintText:
controller: authVM!.isUserFromUAE() ? authVM!.nameController : null, authVM!.isUserFromUAE() ? LocaleKeys.enterNameHere.tr() : ("${appState.getNHICUserData.firstNameEn!.toUpperCase()} ${appState.getNHICUserData.lastNameEn!.toUpperCase()}"),
isEnable: true, controller: authVM!.isUserFromUAE() ? authVM!.nameController : null,
prefix: null, isEnable: true,
isAllowRadius: false, prefix: null,
isBorderAllowed: false, isAllowRadius: false,
keyboardType: TextInputType.text, isBorderAllowed: false,
isAllowLeadingIcon: true, keyboardType: TextInputType.text,
isReadOnly: authVM!.isUserFromUAE() ? false : true, isAllowLeadingIcon: true,
leadingIcon: AppAssets.user_circle) isReadOnly: authVM!.isUserFromUAE() ? false : true,
.paddingSymmetrical(0.h, 16.h), leadingIcon: AppAssets.user_circle,
labelColor: AppColors.textColor,
).paddingSymmetrical(0.h, 16.h),
Divider(height: 1, color: AppColors.greyColor), Divider(height: 1, color: AppColors.greyColor),
TextInputWidget( TextInputWidget(
labelText: LocaleKeys.nationalIdNumber.tr(), labelText: LocaleKeys.nationalIdNumber.tr(),
@ -89,6 +95,7 @@ class _RegisterNew extends State<RegisterNewStep2> {
isBorderAllowed: false, isBorderAllowed: false,
isAllowLeadingIcon: true, isAllowLeadingIcon: true,
isReadOnly: true, isReadOnly: true,
labelColor: AppColors.textColor,
leadingIcon: AppAssets.student_card) leadingIcon: AppAssets.student_card)
.paddingSymmetrical(0.h, 16.h), .paddingSymmetrical(0.h, 16.h),
Divider(height: 1, color: AppColors.greyColor), Divider(height: 1, color: AppColors.greyColor),
@ -108,6 +115,7 @@ class _RegisterNew extends State<RegisterNewStep2> {
isBorderAllowed: false, isBorderAllowed: false,
hasSelectionCustomIcon: true, hasSelectionCustomIcon: true,
isAllowRadius: false, isAllowRadius: false,
labelColor: AppColors.textColor,
padding: const EdgeInsets.only(top: 8, bottom: 8, left: 0, right: 0), padding: const EdgeInsets.only(top: 8, bottom: 8, left: 0, right: 0),
selectionCustomIcon: AppAssets.arrow_down, selectionCustomIcon: AppAssets.arrow_down,
leadingIcon: AppAssets.user_full, leadingIcon: AppAssets.user_full,
@ -124,6 +132,7 @@ class _RegisterNew extends State<RegisterNewStep2> {
isAllowLeadingIcon: true, isAllowLeadingIcon: true,
isReadOnly: authVM!.isUserFromUAE() ? false : true, isReadOnly: authVM!.isUserFromUAE() ? false : true,
leadingIcon: AppAssets.user_full, leadingIcon: AppAssets.user_full,
labelColor: AppColors.textColor,
onChange: (value) {}) onChange: (value) {})
.paddingSymmetrical(0.h, 16.h), .paddingSymmetrical(0.h, 16.h),
Divider(height: 1, color: AppColors.greyColor), Divider(height: 1, color: AppColors.greyColor),
@ -143,6 +152,7 @@ class _RegisterNew extends State<RegisterNewStep2> {
isBorderAllowed: false, isBorderAllowed: false,
hasSelectionCustomIcon: true, hasSelectionCustomIcon: true,
isAllowRadius: false, isAllowRadius: false,
labelColor: AppColors.textColor,
padding: const EdgeInsets.only(top: 8, bottom: 8, left: 0, right: 0), padding: const EdgeInsets.only(top: 8, bottom: 8, left: 0, right: 0),
selectionCustomIcon: AppAssets.arrow_down, selectionCustomIcon: AppAssets.arrow_down,
leadingIcon: AppAssets.smart_phone, leadingIcon: AppAssets.smart_phone,
@ -160,6 +170,7 @@ class _RegisterNew extends State<RegisterNewStep2> {
isBorderAllowed: false, isBorderAllowed: false,
isAllowLeadingIcon: true, isAllowLeadingIcon: true,
isReadOnly: true, isReadOnly: true,
labelColor: AppColors.textColor,
leadingIcon: AppAssets.smart_phone, leadingIcon: AppAssets.smart_phone,
onChange: (value) {}) onChange: (value) {})
.paddingSymmetrical(0.h, 16.h), .paddingSymmetrical(0.h, 16.h),
@ -190,6 +201,7 @@ class _RegisterNew extends State<RegisterNewStep2> {
onChange: authVM.onUAEUserCountrySelection, onChange: authVM.onUAEUserCountrySelection,
isBorderAllowed: false, isBorderAllowed: false,
hasSelectionCustomIcon: true, hasSelectionCustomIcon: true,
labelColor: AppColors.textColor,
isAllowRadius: false, isAllowRadius: false,
padding: const EdgeInsets.only(top: 8, bottom: 8, left: 0, right: 0), padding: const EdgeInsets.only(top: 8, bottom: 8, left: 0, right: 0),
selectionCustomIcon: AppAssets.arrow_down, selectionCustomIcon: AppAssets.arrow_down,
@ -208,6 +220,7 @@ class _RegisterNew extends State<RegisterNewStep2> {
isBorderAllowed: false, isBorderAllowed: false,
isAllowLeadingIcon: true, isAllowLeadingIcon: true,
isReadOnly: true, isReadOnly: true,
labelColor: AppColors.textColor,
leadingIcon: AppAssets.globe, leadingIcon: AppAssets.globe,
onChange: (value) {}) onChange: (value) {})
.paddingSymmetrical(0.h, 16.h), .paddingSymmetrical(0.h, 16.h),
@ -224,6 +237,7 @@ class _RegisterNew extends State<RegisterNewStep2> {
isAllowRadius: false, isAllowRadius: false,
isBorderAllowed: false, isBorderAllowed: false,
isAllowLeadingIcon: true, isAllowLeadingIcon: true,
labelColor: AppColors.textColor,
isReadOnly: true, isReadOnly: true,
leadingIcon: AppAssets.call) leadingIcon: AppAssets.call)
.paddingSymmetrical(0.h, 16.h), .paddingSymmetrical(0.h, 16.h),
@ -240,6 +254,7 @@ class _RegisterNew extends State<RegisterNewStep2> {
isBorderAllowed: false, isBorderAllowed: false,
isAllowLeadingIcon: true, isAllowLeadingIcon: true,
isReadOnly: true, isReadOnly: true,
labelColor: AppColors.textColor,
leadingIcon: AppAssets.birthday_cake, leadingIcon: AppAssets.birthday_cake,
selectionType: null, selectionType: null,
).paddingSymmetrical(0.h, 16.h), ).paddingSymmetrical(0.h, 16.h),
@ -269,12 +284,12 @@ class _RegisterNew extends State<RegisterNewStep2> {
), ),
Expanded( Expanded(
child: CustomButton( child: CustomButton(
backgroundColor: AppColors.lightGreenColor, backgroundColor: AppColors.primaryRedColor,
borderColor: AppColors.lightGreenColor, borderColor: AppColors.primaryRedColor,
textColor: AppColors.textGreenColor, textColor: AppColors.whiteColor,
text: LocaleKeys.confirm.tr(), text: LocaleKeys.confirm.tr(),
icon: AppAssets.confirm, icon: AppAssets.confirm,
iconColor: AppColors.textGreenColor, iconColor: AppColors.whiteColor,
onPressed: () { onPressed: () {
if (appState.getUserRegistrationPayload.zipCode != CountryEnum.saudiArabia.countryCode) { if (appState.getUserRegistrationPayload.zipCode != CountryEnum.saudiArabia.countryCode) {
if (ValidationUtils.validateUaeRegistration( if (ValidationUtils.validateUaeRegistration(

@ -12,6 +12,8 @@ import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/features/authentication/authentication_view_model.dart'; import 'package:hmg_patient_app_new/features/authentication/authentication_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/authentication/login.dart'; import 'package:hmg_patient_app_new/presentation/authentication/login.dart';
import 'package:hmg_patient_app_new/presentation/home/landing_page.dart';
import 'package:hmg_patient_app_new/presentation/home/navigation_screen.dart';
import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/appbar/app_bar_widget.dart'; import 'package:hmg_patient_app_new/widgets/appbar/app_bar_widget.dart';
import 'package:hmg_patient_app_new/widgets/bottomsheet/generic_bottom_sheet.dart'; import 'package:hmg_patient_app_new/widgets/bottomsheet/generic_bottom_sheet.dart';
@ -164,17 +166,16 @@ class _SavedLogin extends State<SavedLogin> {
Padding( Padding(
padding: EdgeInsets.only(bottom: 10.h), padding: EdgeInsets.only(bottom: 10.h),
child: CustomButton( child: CustomButton(
text: LocaleKeys.sendOTPSMS.tr(), text: LocaleKeys.sendOTPSMS.tr(),
onPressed: () { onPressed: () {
Navigator.of(context).pop(); Navigator.of(context).pop();
loginType = LoginTypeEnum.sms; loginType = LoginTypeEnum.sms;
authVm.checkUserAuthentication(otpTypeEnum: OTPTypeEnum.sms); authVm.checkUserAuthentication(otpTypeEnum: OTPTypeEnum.sms);
}, },
backgroundColor: AppColors.primaryRedColor, backgroundColor: AppColors.primaryRedColor,
borderColor: AppColors.primaryRedBorderColor, borderColor: AppColors.primaryRedColor,
textColor: AppColors.whiteColor, textColor: AppColors.whiteColor,
icon: AppAssets.sms, icon: AppAssets.sms),
),
), ),
Row( Row(
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
@ -247,13 +248,13 @@ class _SavedLogin extends State<SavedLogin> {
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Expanded( Expanded(
child: Container( child: SizedBox(
height: 56, height: 56,
child: CustomButton( child: CustomButton(
text: LocaleKeys.guest.tr(), text: LocaleKeys.guest.tr(),
onPressed: () { onPressed: () {
Navigator.of(context).pushReplacement( Navigator.of(context).pushReplacement(
MaterialPageRoute(builder: (BuildContext context) => LoginScreen()), MaterialPageRoute(builder: (BuildContext context) => LandingNavigation()),
); );
}, },
backgroundColor: Color(0xffFEE9EA), backgroundColor: Color(0xffFEE9EA),
@ -277,7 +278,7 @@ class _SavedLogin extends State<SavedLogin> {
text: LocaleKeys.switchAccount.tr(), text: LocaleKeys.switchAccount.tr(),
onPressed: () async { onPressed: () async {
await authVm.clearDefaultInputValues(); await authVm.clearDefaultInputValues();
Navigator.of(context).pushReplacement( Navigator.of(context).push(
MaterialPageRoute(builder: (BuildContext context) => LoginScreen()), MaterialPageRoute(builder: (BuildContext context) => LoginScreen()),
); );
}, },

@ -10,15 +10,24 @@ 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/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/my_appointments/appointment_via_region_viewmodel.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/doctor_list_api_response.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/appointments/widgets/faculity_selection/facility_type_selection_widget.dart';
import 'package:hmg_patient_app_new/presentation/appointments/widgets/region_bottomsheet/region_list_widget.dart'
show RegionBottomSheetBody;
import 'package:hmg_patient_app_new/presentation/book_appointment/search_doctor_by_name.dart'; import 'package:hmg_patient_app_new/presentation/book_appointment/search_doctor_by_name.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/lab/collapsing_list_view.dart'; import 'package:hmg_patient_app_new/presentation/lab/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/common_bottom_sheet.dart'
show showCommonBottomSheetWithoutHeight;
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/transitions/fade_page.dart'; import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import '../appointments/widgets/hospital_bottom_sheet/hospital_bottom_sheet_body.dart';
class BookAppointmentPage extends StatefulWidget { class BookAppointmentPage extends StatefulWidget {
const BookAppointmentPage({super.key}); const BookAppointmentPage({super.key});
@ -28,6 +37,7 @@ class BookAppointmentPage extends StatefulWidget {
class _BookAppointmentPageState extends State<BookAppointmentPage> { class _BookAppointmentPageState extends State<BookAppointmentPage> {
late AppState appState; late AppState appState;
late AppointmentViaRegionViewmodel regionalViewModel;
late BookAppointmentsViewModel bookAppointmentsViewModel; late BookAppointmentsViewModel bookAppointmentsViewModel;
@override @override
@ -42,6 +52,8 @@ class _BookAppointmentPageState extends State<BookAppointmentPage> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
bookAppointmentsViewModel = Provider.of<BookAppointmentsViewModel>(context, listen: false); bookAppointmentsViewModel = Provider.of<BookAppointmentsViewModel>(context, listen: false);
appState = getIt.get<AppState>(); appState = getIt.get<AppState>();
regionalViewModel =
Provider.of<AppointmentViaRegionViewmodel>(context, listen: true);
return Scaffold( return Scaffold(
backgroundColor: AppColors.bgScaffoldColor, backgroundColor: AppColors.bgScaffoldColor,
body: CollapsingListView( body: CollapsingListView(
@ -166,7 +178,9 @@ class _BookAppointmentPageState extends State<BookAppointmentPage> {
), ),
Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_icon, iconColor: AppColors.textColor, width: 15.h, height: 15.h), Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_icon, iconColor: AppColors.textColor, width: 15.h, height: 15.h),
], ],
).onPress(() {}), ).onPress(() {
openRegionListBottomSheet(context);
}),
], ],
), ),
), ),
@ -178,4 +192,45 @@ class _BookAppointmentPageState extends State<BookAppointmentPage> {
} }
return Container(); return Container();
} }
void openRegionListBottomSheet(BuildContext context) {
// AppointmentViaRegionViewmodel? viewmodel = null;
showCommonBottomSheetWithoutHeight(context,
title: "",
titleWidget: Consumer<AppointmentViaRegionViewmodel>(
builder: (_, data, __) => getTitle(data)),
isDismissible: false,
child: Consumer<AppointmentViaRegionViewmodel>(builder: (_, data, __) {
return getRegionalSelectionWidget(data);
}), callBackFunc: () {
regionalViewModel.flush();
});
}
Widget getRegionalSelectionWidget(AppointmentViaRegionViewmodel data) {
if (data.bottomSheetState == AppointmentViaRegionState.REGION_SELECTION) {
return RegionBottomSheetBody();
}
if(data.bottomSheetState == AppointmentViaRegionState.TYPE_SELECTION){
return FacilityTypeSelectionWidget(selectedRegion: data.selectedRegionId??"",);
}
if (data.bottomSheetState == AppointmentViaRegionState.HOSPITAL_SELECTION) {
return HospitalBottomSheetBody();
} else {
SizedBox.shrink();
}
return SizedBox.shrink();
}
getTitle(AppointmentViaRegionViewmodel data) {
if (data.selectedRegionId == null) {
return LocaleKeys.selectRegion.tr().toText20(weight: FontWeight.w600);
} else {
return Utils.buildSvgWithAssets(
icon: AppAssets.arrow_back, iconColor: Color(0xff2B353E))
.onPress(() {
data.handleBackPress();
});
}
}
} }

@ -3,8 +3,10 @@ import 'dart:async';
import 'package:easy_localization/easy_localization.dart'; 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:get_it/get_it.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart'; import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/app_state.dart'; import 'package:hmg_patient_app_new/core/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/dependencies.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';
@ -25,6 +27,7 @@ import 'package:hmg_patient_app_new/presentation/home/widgets/small_service_card
import 'package:hmg_patient_app_new/presentation/home/widgets/welcome_widget.dart'; import 'package:hmg_patient_app_new/presentation/home/widgets/welcome_widget.dart';
import 'package:hmg_patient_app_new/presentation/medical_file/medical_file_page.dart'; import 'package:hmg_patient_app_new/presentation/medical_file/medical_file_page.dart';
import 'package:hmg_patient_app_new/presentation/profile_settings/profile_settings.dart'; import 'package:hmg_patient_app_new/presentation/profile_settings/profile_settings.dart';
import 'package:hmg_patient_app_new/services/cache_service.dart';
import 'package:hmg_patient_app_new/services/navigation_service.dart'; import 'package:hmg_patient_app_new/services/navigation_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';
@ -49,6 +52,7 @@ class _LandingPageState extends State<LandingPage> {
late AppState appState; late AppState appState;
late MyAppointmentsViewModel myAppointmentsViewModel; late MyAppointmentsViewModel myAppointmentsViewModel;
late PrescriptionsViewModel prescriptionsViewModel; late PrescriptionsViewModel prescriptionsViewModel;
final CacheService cacheService = GetIt.instance<CacheService>();
@override @override
void initState() { void initState() {
@ -370,6 +374,7 @@ class _LandingPageState extends State<LandingPage> {
// sharedPref.setBool(HAS_ENABLED_QUICK_LOGIN, true); // sharedPref.setBool(HAS_ENABLED_QUICK_LOGIN, true);
authVM.loginWithFingerPrintFace(() { authVM.loginWithFingerPrintFace(() {
isDone = true; isDone = true;
cacheService.saveBool(key: CacheConst.quickLoginEnabled,value: true);
setState(() {}); setState(() {});
}); });
}, },

@ -1,4 +1,6 @@
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/dependencies.dart';
import 'package:hmg_patient_app_new/presentation/book_appointment/book_appointment_page.dart'; import 'package:hmg_patient_app_new/presentation/book_appointment/book_appointment_page.dart';
import 'package:hmg_patient_app_new/presentation/home/landing_page.dart'; import 'package:hmg_patient_app_new/presentation/home/landing_page.dart';
import 'package:hmg_patient_app_new/presentation/medical_file/medical_file_page.dart'; import 'package:hmg_patient_app_new/presentation/medical_file/medical_file_page.dart';
@ -13,20 +15,22 @@ class LandingNavigation extends StatefulWidget {
class _LandingNavigationState extends State<LandingNavigation> { class _LandingNavigationState extends State<LandingNavigation> {
int _currentIndex = 0; int _currentIndex = 0;
late AppState appState;
final PageController _pageController = PageController(); final PageController _pageController = PageController();
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
appState = getIt.get<AppState>();
return Scaffold( return Scaffold(
body: PageView( body: PageView(
controller: _pageController, controller: _pageController,
physics: const NeverScrollableScrollPhysics(), physics: const NeverScrollableScrollPhysics(),
children: [ children: [
const LandingPage(), const LandingPage(),
MedicalFilePage(), appState.isAuthenticated ? MedicalFilePage() :/* need add feedback page */ const LandingPage(),
BookAppointmentPage(), BookAppointmentPage(),
const LandingPage(), const LandingPage(),
const LandingPage(), appState.isAuthenticated ? /* need add news page */ LandingPage() : const LandingPage(),
], ],
), ),
bottomNavigationBar: BottomNavigation( bottomNavigationBar: BottomNavigation(

@ -22,19 +22,9 @@ class NavigationService {
navigatorKey.currentState?.pushReplacementNamed(routeName); navigatorKey.currentState?.pushReplacementNamed(routeName);
} }
Future<T?> pushToOtpScreen<T>({ Future<T?> pushToOtpScreen<T>({required String phoneNumber, required Function(int code) checkActivationCode, required Function(String phoneNumber) onResendOTPPressed}) {
required String phoneNumber,
required Function(int code) checkActivationCode,
required Function(String phoneNumber) onResendOTPPressed,
}) {
return navigatorKey.currentState!.push( return navigatorKey.currentState!.push(
MaterialPageRoute( MaterialPageRoute(builder: (_) => OTPVerificationScreen(phoneNumber: phoneNumber, checkActivationCode: checkActivationCode, onResendOTPPressed: onResendOTPPressed)),
builder: (_) => OTPVerificationScreen(
phoneNumber: phoneNumber,
checkActivationCode: checkActivationCode,
onResendOTPPressed: onResendOTPPressed,
),
),
); );
} }

@ -31,6 +31,7 @@ class AppColors {
static const Color textColor = Color(0xFF2E3039); static const Color textColor = Color(0xFF2E3039);
static const Color textColorLight = Color(0xFF5E5E5E); static const Color textColorLight = Color(0xFF5E5E5E);
static const Color borderOnlyColor = Color(0xFF2E3039); static const Color borderOnlyColor = Color(0xFF2E3039);
static const Color chipBorderColorOpacity20 = Color(0x332E3039);
static const Color dividerColor = Color(0xFFD2D2D2); static const Color dividerColor = Color(0xFFD2D2D2);
static const Color warningColorYellow = Color(0xFFF4A308); static const Color warningColorYellow = Color(0xFFF4A308);
static const Color blackBgColor = Color(0xFF2E3039); static const Color blackBgColor = Color(0xFF2E3039);

@ -1,6 +1,8 @@
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
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/app_assets.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; import 'package:hmg_patient_app_new/core/utils/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';
@ -19,16 +21,17 @@ class BottomNavigation extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
AppState appState = getIt.get<AppState>();
final items = [ final items = [
BottomNavItem(icon: AppAssets.homeBottom, label: LocaleKeys.home.tr()), BottomNavItem(icon: AppAssets.homeBottom, label: LocaleKeys.home.tr()),
BottomNavItem(icon: AppAssets.myFilesBottom, label: LocaleKeys.myFiles.tr()), appState.isAuthenticated ? BottomNavItem(icon: AppAssets.myFilesBottom, label: LocaleKeys.myFiles.tr()) : BottomNavItem(icon: AppAssets.feedback, label: LocaleKeys.feedback.tr()),
BottomNavItem( BottomNavItem(
icon: AppAssets.bookAppoBottom, icon: AppAssets.bookAppoBottom,
label: LocaleKeys.appointment.tr(), label: LocaleKeys.appointment.tr(),
iconSize: 27, iconSize: 27,
isSpecial: true, isSpecial: true,
), ),
BottomNavItem(icon: AppAssets.toDoBottom, label: LocaleKeys.todoList.tr()), appState.isAuthenticated ? BottomNavItem(icon: AppAssets.toDoBottom, label: LocaleKeys.todoList.tr()) : BottomNavItem(icon: AppAssets.news, label: LocaleKeys.news.tr()) ,
BottomNavItem(icon: AppAssets.servicesBottom, label: LocaleKeys.services2.tr()), BottomNavItem(icon: AppAssets.servicesBottom, label: LocaleKeys.services2.tr()),
]; ];

@ -10,23 +10,38 @@ import 'package:smooth_corner/smooth_corner.dart';
class AppCustomChipWidget extends StatelessWidget { class AppCustomChipWidget extends StatelessWidget {
AppCustomChipWidget({ AppCustomChipWidget({
super.key, super.key,
required this.labelText, this.labelText,
this.textColor = AppColors.textColor, this.textColor = AppColors.textColor,
this.backgroundColor = AppColors.greyColor, this.backgroundColor = AppColors.greyColor,
this.iconSize = 12, this.iconSize = 12,
this.icon = "", this.icon = "",
this.iconColor = AppColors.textColor, this.iconColor = AppColors.textColor,
this.richText,
this.iconHasColor = true,
this.shape,
this.deleteIcon,
this.deleteIconSize = const Size(12, 12),
this.deleteIconColor = AppColors.textColor,
this.deleteIconHasColor = false,
}); });
String? labelText; final String? labelText;
Color? textColor; final Widget? richText;
Color? backgroundColor; final Color? textColor;
num iconSize; final Color? backgroundColor;
String icon; final num iconSize;
Color iconColor; final String icon;
final String? deleteIcon;
final Size? deleteIconSize;
final Color iconColor;
final Color? deleteIconColor;
final bool iconHasColor;
final bool deleteIconHasColor;
final OutlinedBorder? shape;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
print("detected icon: $deleteIcon");
return ChipTheme( return ChipTheme(
data: ChipThemeData( data: ChipThemeData(
padding: EdgeInsets.all(0.0), padding: EdgeInsets.all(0.0),
@ -41,18 +56,55 @@ class AppCustomChipWidget extends StatelessWidget {
), ),
child: icon.isNotEmpty child: icon.isNotEmpty
? Chip( ? Chip(
avatar: icon.isNotEmpty ? Utils.buildSvgWithAssets(icon: icon, width: iconSize.h, height: iconSize.h, iconColor: iconColor) : SizedBox.shrink(), avatar: icon.isNotEmpty
label: labelText!.toText10(weight: FontWeight.w500, letterSpacing: -0.64, color: textColor), ? Utils.buildSvgWithAssets(
icon: icon,
width: iconSize.h,
height: iconSize.h,
iconColor: iconHasColor ? iconColor : null)
: SizedBox.shrink(),
label: richText ??
labelText!.toText10(
weight: FontWeight.w500,
letterSpacing: -0.64,
color: textColor),
padding: EdgeInsets.all(0.0), padding: EdgeInsets.all(0.0),
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
labelPadding: EdgeInsets.only(left: -4.h, right: 8.h), labelPadding: EdgeInsets.only(
left: -4.h,
right: deleteIcon?.isNotEmpty == true ? 2.h : 8.h),
backgroundColor: backgroundColor, backgroundColor: backgroundColor,
shape: shape,
deleteIcon: deleteIcon?.isNotEmpty == true
? Utils.buildSvgWithAssets(
icon: deleteIcon!,
width: deleteIconSize!.width!.h,
height: deleteIconSize!.height.h,
iconColor: deleteIconHasColor ? deleteIconColor : null)
: null,
onDeleted: deleteIcon?.isNotEmpty == true ? () {} : null,
) )
: Chip( : Chip(
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
label: labelText!.toText10(weight: FontWeight.w500, letterSpacing: -0.64, color: textColor), label: richText ??
labelText!.toText10(
weight: FontWeight.w500,
letterSpacing: -0.64,
color: textColor),
padding: EdgeInsets.all(0.0), padding: EdgeInsets.all(0.0),
backgroundColor: backgroundColor, backgroundColor: backgroundColor,
shape: shape,
labelPadding: EdgeInsets.only(
left: 8.h,
right: deleteIcon?.isNotEmpty == true ? -2.h : 8.h),
deleteIcon: deleteIcon?.isNotEmpty == true
? Utils.buildSvgWithAssets(
icon: deleteIcon!,
width: deleteIconSize!.width.h,
height: deleteIconSize!.height.h,
iconColor: deleteIconHasColor ? deleteIconColor : null)
: null,
onDeleted: deleteIcon?.isNotEmpty == true ? () {} : null,
), ),
); );
} }

@ -109,9 +109,11 @@ void showCommonBottomSheetWithoutHeight(
required Widget child, required Widget child,
required VoidCallback callBackFunc, required VoidCallback callBackFunc,
String title = "", String title = "",
bool isCloseButtonVisible = true,
bool isFullScreen = true, bool isCloseButtonVisible = true,
}) { bool isFullScreen = true,
bool isDismissible = true,
Widget? titleWidget,}) {
showModalBottomSheet<String>( showModalBottomSheet<String>(
sheetAnimationStyle: AnimationStyle( sheetAnimationStyle: AnimationStyle(
duration: Duration(milliseconds: 500), // Custom animation duration duration: Duration(milliseconds: 500), // Custom animation duration
@ -120,6 +122,7 @@ void showCommonBottomSheetWithoutHeight(
context: context, context: context,
isScrollControlled: true, isScrollControlled: true,
showDragHandle: false, showDragHandle: false,
isDismissible: isDismissible,
backgroundColor: AppColors.bottomSheetBgColor, backgroundColor: AppColors.bottomSheetBgColor,
builder: (BuildContext context) { builder: (BuildContext context) {
return SafeArea( return SafeArea(
@ -135,9 +138,10 @@ void showCommonBottomSheetWithoutHeight(
spacing: 16.h, spacing: 16.h,
children: [ children: [
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
title.toText20(weight: FontWeight.w600).expanded, titleWidget ?? title.toText20(weight: FontWeight.w600),
Utils.buildSvgWithAssets(icon: AppAssets.close_bottom_sheet_icon, iconColor: Color(0xff2B353E)).onPress(() { Utils.buildSvgWithAssets(icon: AppAssets.close_bottom_sheet_icon, iconColor: Color(0xff2B353E)).onPress(() {
Navigator.of(context).pop(); Navigator.of(context).pop();
}), }),

@ -18,29 +18,31 @@ class DropdownWidget extends StatelessWidget {
final bool hasSelectionCustomIcon; final bool hasSelectionCustomIcon;
final String? selectionCustomIcon; final String? selectionCustomIcon;
final String? leadingIcon; final String? leadingIcon;
final Color? labelColor;
const DropdownWidget({ const DropdownWidget(
Key? key, {Key? key,
required this.labelText, required this.labelText,
required this.hintText, required this.hintText,
required this.dropdownItems, required this.dropdownItems,
this.selectedValue, this.selectedValue,
this.onChange, this.onChange,
this.isEnable = true, this.isEnable = true,
this.isBorderAllowed = true, this.isBorderAllowed = true,
this.isAllowRadius = true, this.isAllowRadius = true,
this.padding, this.padding,
this.hasSelectionCustomIcon = false, this.hasSelectionCustomIcon = false,
this.selectionCustomIcon, this.selectionCustomIcon,
this.leadingIcon, this.leadingIcon,
}) : super(key: key); this.labelColor})
: super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
Widget content = Column( Widget content = Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [_buildLabelText(), _buildDropdown(context)], children: [_buildLabelText(labelColor), _buildDropdown(context)],
); );
return Container( return Container(
@ -75,13 +77,13 @@ class DropdownWidget extends StatelessWidget {
child: Utils.buildSvgWithAssets(icon: leadingIcon!)); child: Utils.buildSvgWithAssets(icon: leadingIcon!));
} }
Widget _buildLabelText() { Widget _buildLabelText(Color? labelColor) {
return Text( return Text(
labelText, labelText,
style: TextStyle( style: TextStyle(
fontSize: 12.fSize, fontSize: 12.fSize,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
color: Color(0xff898A8D), color: labelColor ?? Color(0xff898A8D),
letterSpacing: -0.2, letterSpacing: -0.2,
height: 18 / 12, height: 18 / 12,
), ),

@ -41,39 +41,41 @@ class TextInputWidget extends StatelessWidget {
final num? fontSize; final num? fontSize;
final bool? isWalletAmountInput; final bool? isWalletAmountInput;
final Widget? suffix; final Widget? suffix;
final Color? labelColor;
// final List<Country> countryList; // final List<Country> countryList;
// final Function(Country)? onCountryChange; // final Function(Country)? onCountryChange;
TextInputWidget({ TextInputWidget(
super.key, {super.key,
required this.labelText, required this.labelText,
required this.hintText, required this.hintText,
this.controller, this.controller,
this.onChange, this.onChange,
this.onCalendarTypeChanged, this.onCalendarTypeChanged,
this.prefix, this.prefix,
this.isEnable = true, this.isEnable = true,
this.isBorderAllowed = true, this.isBorderAllowed = true,
this.isAllowRadius = true, this.isAllowRadius = true,
this.isReadOnly = false, this.isReadOnly = false,
this.keyboardType = TextInputType.number, this.keyboardType = TextInputType.number,
this.focusNode, this.focusNode,
this.autoFocus = false, this.autoFocus = false,
this.padding, this.padding,
this.isAllowLeadingIcon = false, this.isAllowLeadingIcon = false,
this.leadingIcon, this.leadingIcon,
this.isCountryDropDown = false, this.isCountryDropDown = false,
this.hasError = false, this.hasError = false,
this.errorMessage, this.errorMessage,
this.onCountryChange, this.onCountryChange,
this.selectionType, this.selectionType,
this.fontSize = 14, this.fontSize = 14,
this.isWalletAmountInput = false, this.isWalletAmountInput = false,
this.suffix, this.suffix,
// this.countryList = const [], this.labelColor
// this.onCountryChange, // this.countryList = const [],
}); // this.onCountryChange,
});
final FocusNode _focusNode = FocusNode(); final FocusNode _focusNode = FocusNode();
@ -135,12 +137,13 @@ class TextInputWidget extends StatelessWidget {
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
_buildLabelText().paddingOnly(right: (appState.getLanguageCode() == "ar" ? 10 : 0)), _buildLabelText(labelColor).paddingOnly(right: (appState.getLanguageCode() == "ar" ? 10 : 0)),
_buildTextField(context), _buildTextField(context),
], ],
), ),
), ),
if (selectionType == SelectionTypeEnum.calendar) _buildTrailingIcon(context), if (selectionType == SelectionTypeEnum.calendar) _buildTrailingIcon(context),
if (selectionType == SelectionTypeEnum.search) _buildTrailingIconForSearch(context),
], ],
), ),
), ),
@ -205,13 +208,13 @@ class TextInputWidget extends StatelessWidget {
); );
} }
Widget _buildLabelText() { Widget _buildLabelText(Color? labelColor) {
return Text( return Text(
labelText, labelText,
style: TextStyle( style: TextStyle(
fontSize: 12.fSize, fontSize: 12.fSize,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
color: AppColors.inputLabelTextColor, color: labelColor ?? AppColors.inputLabelTextColor,
letterSpacing: -0.2, letterSpacing: -0.2,
height: 18 / 12, height: 18 / 12,
), ),
@ -250,4 +253,16 @@ class TextInputWidget extends StatelessWidget {
), ),
); );
} }
_buildTrailingIconForSearch(BuildContext context) {
final AppState appState = getIt.get<AppState>();
return Container(
height: 40.h,
width: 40.h,
margin: EdgeInsets.zero,
padding: EdgeInsets.all(8.h),
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(borderRadius: 10.h, color: AppColors.whiteColor),
child: Utils.buildSvgWithAssets(icon: AppAssets.search_icon),
);
}
} }

@ -0,0 +1,44 @@
import 'package:flutter/material.dart';
class ProfileSelector extends StatelessWidget {
final List<Map<String, dynamic>> profiles;
final Function(Map<String, dynamic>) onSelect;
const ProfileSelector({
Key? key,
required this.profiles,
required this.onSelect,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.min,
children: profiles.map((profile) {
return ListTile(
leading: CircleAvatar(
radius: 22,
backgroundImage: profile["GenderImage"] != null &&
profile["GenderImage"].toString().isNotEmpty
? NetworkImage(profile["GenderImage"])
: AssetImage(
profile["Gender"] == 1
? "assets/images/male.png"
: "assets/images/female.png")
as ImageProvider,
),
title: Text(
profile["PatientName"] ?? "Unknown",
style: const TextStyle(fontWeight: FontWeight.w600),
),
subtitle: Text(
profile["Relationship"] ?? "Self",
style: const TextStyle(color: Colors.grey),
),
trailing: const Icon(Icons.arrow_forward_ios, size: 16),
onTap: () => onSelect(profile),
);
}).toList(),
);
}
}

@ -0,0 +1,15 @@
import 'package:flutter/material.dart';
import '../common_bottom_sheet.dart';
import 'my_Family.dart';
class MyFamilySheet {
static void show(BuildContext context, List<Map<String, dynamic>> profiles, Function(Map<String, dynamic>) onSelect) {
showCommonBottomSheetWithoutHeight(
context,
title: 'Select Profile',
child: ProfileSelector(profiles: profiles, onSelect: (profile) {
Navigator.of(context).pop(); // Close the bottom sheet
onSelect(profile); // Call the onSelect callback
}), callBackFunc: () {},
);
}
}

@ -81,6 +81,8 @@ dependencies:
flutter_swiper_view: ^1.1.8 flutter_swiper_view: ^1.1.8
family_bottom_sheet: ^0.1.0 family_bottom_sheet: ^0.1.0
location: ^8.0.1
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:
sdk: flutter sdk: flutter

Loading…
Cancel
Save