Merge branch 'refs/heads/master' into dev_sikander

# Conflicts:
#	pubspec.yaml
pull/31/head
Sikander Saleem 3 months ago
commit a25f42a43d

@ -0,0 +1,5 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.50024 0.84375C7.08603 0.84375 6.75024 1.17954 6.75024 1.59375C6.75024 2.00796 7.08603 2.34375 7.50024 2.34375H8.25024V2.56924C4.56399 2.94479 1.68774 6.05838 1.68774 9.84375C1.68774 11.4006 2.17429 12.8439 3.00361 14.0297L1.71991 15.3134C1.42702 15.6063 1.42702 16.0812 1.71991 16.3741C2.01281 16.667 2.48768 16.667 2.78057 16.3741L3.98717 15.1675C5.2962 16.4006 7.05997 17.1562 9.00024 17.1562C10.9405 17.1562 12.7043 16.4006 14.0133 15.1675L15.2199 16.3741C15.5128 16.667 15.9877 16.667 16.2806 16.3741C16.5735 16.0812 16.5735 15.6063 16.2806 15.3134L14.9969 14.0297C15.8262 12.8439 16.3127 11.4006 16.3127 9.84375C16.3127 6.05838 13.4365 2.94479 9.75024 2.56924V2.34375L10.5002 2.34375C10.9145 2.34375 11.2502 2.00796 11.2502 1.59375C11.2502 1.17954 10.9145 0.84375 10.5002 0.84375L7.50024 0.84375ZM9.75024 7.21875C9.75024 6.80454 9.41446 6.46875 9.00024 6.46875C8.58603 6.46875 8.25024 6.80454 8.25024 7.21875L8.25024 10.2187C8.25024 10.4177 8.32926 10.6084 8.46991 10.7491L9.96991 12.2491C10.2628 12.542 10.7377 12.542 11.0306 12.2491C11.3235 11.9562 11.3235 11.4813 11.0306 11.1884L9.75024 9.90809V7.21875Z" fill="#ED1C2B"/>
<path d="M3.63891 1.87677C3.31491 1.71477 2.90547 1.53727 2.44764 1.6109C1.98924 1.68461 1.65565 1.98199 1.39707 2.24057C1.13848 2.49916 0.841106 2.83274 0.767389 3.29115C0.693766 3.74898 0.871267 4.15842 1.03327 4.48242L1.25658 4.92904C1.44182 5.29952 1.89232 5.44969 2.26281 5.26445C2.63329 5.07921 2.78346 4.6287 2.59822 4.25822L2.37491 3.8116C2.30362 3.66903 2.27107 3.58504 2.25679 3.53387L2.25589 3.53059L2.25771 3.52787C2.28727 3.4839 2.34472 3.41424 2.45773 3.30123C2.57073 3.18823 2.64039 3.13077 2.68436 3.10121L2.68708 3.0994L2.69037 3.1003C2.74153 3.11457 2.82552 3.14713 2.96809 3.21841L3.41471 3.44172C3.7852 3.62697 4.2357 3.4768 4.42094 3.10631C4.60619 2.73583 4.45602 2.28532 4.08553 2.10008L3.63891 1.87677Z" fill="#ED1C2B"/>
<path d="M15.0322 3.21841C15.1747 3.14713 15.2587 3.11457 15.3099 3.1003L15.3132 3.0994L15.3159 3.10121C15.3599 3.13077 15.4295 3.18823 15.5425 3.30123C15.6555 3.41424 15.713 3.4839 15.7425 3.52787L15.7444 3.53059L15.7435 3.53387C15.7292 3.58504 15.6966 3.66903 15.6253 3.8116L15.402 4.25822C15.2168 4.6287 15.367 5.07921 15.7374 5.26445C16.1079 5.44969 16.5584 5.29952 16.7437 4.92904L16.967 4.48242C17.129 4.15842 17.3065 3.74898 17.2329 3.29115C17.1591 2.83275 16.8618 2.49916 16.6032 2.24057C16.3446 1.98199 16.011 1.68461 15.5526 1.6109C15.0948 1.53727 14.6853 1.71477 14.3613 1.87677L13.9147 2.10008C13.5442 2.28532 13.3941 2.73583 13.5793 3.10631C13.7645 3.4768 14.215 3.62697 14.5855 3.44172L15.0322 3.21841Z" fill="#ED1C2B"/>
</svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

@ -0,0 +1,5 @@
<svg width="15" height="18" viewBox="0 0 15 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M14.3341 2.73593C13.3465 0.996493 11.1438 0.387116 9.41507 1.40204L1.9783 5.76797C0.259805 6.77686 -0.316545 8.99649 0.665687 10.7264C1.25916 11.7717 2.29147 12.4098 3.39807 12.5307C3.79951 12.5746 4.16073 12.2868 4.20487 11.888C4.24902 11.4891 3.95937 11.1302 3.55794 11.0864C2.90888 11.0155 2.2965 10.6413 1.93949 10.0125C1.34085 8.95816 1.70266 7.61757 2.7223 7.01897L5.81204 5.20506L6.50525 6.3668C6.7112 6.71196 7.15978 6.82588 7.50718 6.62126C7.85458 6.41663 7.96924 5.97095 7.76329 5.62579L7.07116 4.46586L10.1591 2.65303C11.1684 2.06046 12.4671 2.40504 13.0603 3.44987C13.5393 4.29342 13.4044 5.3279 12.8 6.01304C12.5338 6.31478 12.5642 6.7738 12.8679 7.03829C13.1716 7.30278 13.6336 7.27258 13.8998 6.97083C14.9042 5.83238 15.1252 4.1292 14.3341 2.73593Z" fill="#ED1C2B"/>
<path d="M13.7153 8.93673C13.6985 9.04274 13.5953 9.10233 13.3889 9.22151L5.54435 13.7505C5.33793 13.8697 5.23473 13.9293 5.13452 13.8908C5.03432 13.8523 4.99968 13.7456 4.9304 13.5322C4.20822 11.3077 5.10997 8.80563 7.21681 7.58925C9.32364 6.37287 11.9414 6.84295 13.5068 8.58065C13.657 8.74735 13.732 8.83071 13.7153 8.93673Z" fill="#ED1C2B"/>
<path d="M14.3613 10.0554C14.2611 10.017 14.1579 10.0765 13.9515 10.1957L6.10671 14.7249C5.90032 14.8441 5.79713 14.9036 5.78034 15.0096C5.76356 15.1156 5.83862 15.199 5.98875 15.3657C7.55409 17.1039 10.1722 17.5743 12.2793 16.3578C14.3864 15.1412 15.2881 12.6387 14.5654 10.4139C14.4961 10.2006 14.4615 10.0939 14.3613 10.0554Z" fill="#ED1C2B"/>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

@ -0,0 +1,5 @@
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="40" height="40" rx="10" fill="#EFEFF0"/>
<path d="M23 14.75C22.3096 14.75 21.75 15.3096 21.75 16C21.75 16.6904 22.3096 17.25 23 17.25C23.6904 17.25 24.25 16.6904 24.25 16C24.25 15.3096 23.6904 14.75 23 14.75Z" fill="#8F9AA3"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.25 20C9.25 14.0629 14.0629 9.25 20 9.25C25.9371 9.25 30.75 14.0629 30.75 20C30.75 25.9371 25.9371 30.75 20 30.75C14.0629 30.75 9.25 25.9371 9.25 20ZM20.25 16C20.25 14.4812 21.4812 13.25 23 13.25C24.5188 13.25 25.75 14.4812 25.75 16C25.75 17.5188 24.5188 18.75 23 18.75C21.4812 18.75 20.25 17.5188 20.25 16ZM13.6495 21.2591C14.432 22.1485 15.7722 21.7914 16.2203 20.7727L16.4401 21.021C16.7157 21.3343 17.1898 21.3619 17.499 21.0828C17.8082 20.8036 17.8355 20.3234 17.5599 20.0102L17.3505 19.7721C16.568 18.8827 15.2277 19.2399 14.7796 20.2586L14.5599 20.0102C14.2843 19.697 13.8103 19.6693 13.501 19.9485C13.1918 20.2276 13.1645 20.7078 13.4401 21.021L13.6495 21.2591ZM22.3872 22.2861C22.7816 22.4128 22.9986 22.8351 22.8719 23.2295L22.7611 23.5746C22.7375 23.638 22.7358 23.7627 22.9187 23.7545C24.1221 23.6247 25.0403 24.7168 24.6653 25.8844L24.5545 26.2295C24.4279 26.6239 24.0055 26.8409 23.6111 26.7143C23.2167 26.5876 22.9997 26.1653 23.1264 25.7709L23.2372 25.4258C23.2886 25.2656 23.1536 25.2391 23.0796 25.2459C21.8762 25.3757 20.958 24.2836 21.333 23.116L21.4438 22.7709C21.5704 22.3765 21.9928 22.1595 22.3872 22.2861ZM26.9955 20C26.4457 20 26 20.4477 26 21C26 21.5523 26.4457 22 26.9955 22H27.0045C27.5543 22 28 21.5523 28 21C28 20.4477 27.5543 20 27.0045 20H26.9955ZM16 25C16 24.4477 16.4457 24 16.9955 24H17.0045C17.5543 24 18 24.4477 18 25C18 25.5523 17.5543 26 17.0045 26H16.9955C16.4457 26 16 25.5523 16 25ZM15.9955 15C15.4457 15 15 15.4477 15 16C15 16.5523 15.4457 17 15.9955 17H16.0045C16.5543 17 17 16.5523 17 16C17 15.4477 16.5543 15 16.0045 15H15.9955Z" fill="#8F9AA3"/>
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

@ -0,0 +1,4 @@
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="40" height="40" rx="10" fill="#EFEFF0"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M22.6823 10.7868C20.7265 10.7377 19.2735 10.7377 17.3177 10.7868L17.2429 10.7887C15.7335 10.8265 14.4916 10.8576 13.4906 11.0318C12.4283 11.2165 11.5388 11.5764 10.7854 12.3317C10.0359 13.0831 9.67705 13.9586 9.49526 15.0042C9.32465 15.9854 9.29883 17.1962 9.26758 18.6617L9.26597 18.7371C9.24467 19.7351 9.24468 20.265 9.26599 21.2629L9.2676 21.3383C9.29884 22.8038 9.32466 24.0146 9.49527 24.9959C9.67707 26.0414 10.0359 26.9169 10.7854 27.6683C11.5388 28.4236 12.4283 28.7835 13.4907 28.9683C14.4916 29.1424 15.7335 29.1735 17.2429 29.2113L17.3177 29.2132C19.2735 29.2623 20.7265 29.2623 22.6823 29.2132L22.7571 29.2113C24.2665 29.1735 25.5084 29.1423 26.5094 28.9682C27.5717 28.7835 28.4612 28.4236 29.2146 27.6683C29.9641 26.9169 30.323 26.0414 30.5047 24.9958C30.6754 24.0146 30.7012 22.8038 30.7324 21.3383L30.734 21.2629C30.7553 20.2649 30.7553 19.7351 30.734 18.7371L30.7324 18.6618C30.7012 17.1963 30.6753 15.9854 30.5047 15.0042C30.3229 13.9586 29.9641 13.0831 29.2146 12.3317C28.7913 11.9073 28.3249 11.6077 27.809 11.3939C27.7305 11.351 27.6464 11.3187 27.5587 11.2981C27.2289 11.1818 26.8796 11.0962 26.5093 11.0318C25.5084 10.8576 24.2665 10.8265 22.7571 10.7887L22.6823 10.7868ZM28.6798 16.8503C28.6604 16.4448 28.6507 16.242 28.5039 16.1611C28.3571 16.0801 28.1765 16.1824 27.8155 16.387L23.5798 18.787C22.2801 19.5234 21.181 20 19.9998 20C18.8186 20 17.7195 19.5234 16.4199 18.787L12.1845 16.3872C11.8234 16.1826 11.6429 16.0804 11.4961 16.1613C11.3492 16.2422 11.3396 16.445 11.3202 16.8505C11.2942 17.397 11.28 18.0306 11.264 18.7797C11.2433 19.7492 11.2433 20.2508 11.264 21.2203C11.2974 22.7803 11.3226 23.8393 11.4643 24.6543C11.5978 25.4218 11.8204 25.8777 12.2012 26.2595C12.5781 26.6374 13.0403 26.8638 13.8335 27.0018C14.6717 27.1476 15.765 27.1776 17.3679 27.2178C19.2902 27.266 20.7098 27.266 22.6321 27.2178C24.235 27.1776 25.3283 27.1476 26.1665 27.0018C26.9597 26.8638 27.4219 26.6374 27.7988 26.2595C28.1796 25.8777 28.4023 25.4218 28.5357 24.6543C28.6774 23.8393 28.7027 22.7803 28.736 21.2203C28.7567 20.2508 28.7567 19.7492 28.736 18.7797C28.72 18.0305 28.7058 17.3969 28.6798 16.8503Z" fill="#8F9AA3"/>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

@ -0,0 +1,3 @@
<svg width="8" height="14" viewBox="0 0 8 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0.555285 12.3963C0.716659 12.2744 1.1984 11.9105 1.47611 11.6938C2.03233 11.2597 2.77142 10.6666 3.50831 10.0268C4.24893 9.38373 4.97022 8.70822 5.50046 8.10811C5.76634 7.80719 5.96837 7.54286 6.09987 7.32473C6.22355 7.11958 6.24926 6.99839 6.24926 6.99839C6.24926 6.99839 6.22355 6.88077 6.09988 6.67563C5.96838 6.4575 5.76635 6.19316 5.50047 5.89225C4.97022 5.29214 4.24892 4.61663 3.5083 3.97359C2.7714 3.33378 2.0323 2.74067 1.47608 2.3066C1.19837 2.08987 0.717303 1.72647 0.555928 1.60458C0.222405 1.35895 0.150469 0.888943 0.396101 0.555421C0.641734 0.221898 1.11123 0.150649 1.44476 0.396282L1.44729 0.3982C1.61655 0.52604 2.11556 0.90294 2.39892 1.12407C2.9677 1.56795 3.72861 2.17838 4.49171 2.84093C5.25109 3.50025 6.02979 4.22592 6.62454 4.89904C6.92116 5.23474 7.18788 5.57503 7.3845 5.9012C7.56872 6.20678 7.75 6.59367 7.75 7.00018C7.75 7.40669 7.56871 7.79358 7.3845 8.09916C7.18787 8.42532 6.92116 8.76561 6.62454 9.10131C6.02979 9.77443 5.2511 10.5001 4.49173 11.1594C3.72863 11.822 2.96774 12.4324 2.39896 12.8763C2.11541 13.0976 1.61637 13.4745 1.44747 13.6021L1.44528 13.6037C1.11176 13.8494 0.641787 13.7785 0.396152 13.4449C0.150526 13.1114 0.221792 12.6419 0.555285 12.3963Z" fill="#2E3039"/>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

@ -0,0 +1,4 @@
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="40" height="40" rx="10" fill="#EFEFF0"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M20 9.25C14.0629 9.25 9.25 14.0629 9.25 20C9.25 25.9371 14.0629 30.75 20 30.75C25.9371 30.75 30.75 25.9371 30.75 20C30.75 14.0629 25.9371 9.25 20 9.25ZM19.9916 14.25C18.1958 14.25 16.7381 15.7041 16.7381 17.5C16.7381 19.2959 18.1958 20.75 19.9916 20.75C21.7875 20.75 23.2452 19.2959 23.2452 17.5C23.2452 15.7041 21.7875 14.25 19.9916 14.25ZM25.0409 24.4802C22.3735 21.6002 17.5747 21.7487 14.9638 24.4756L14.7763 24.6631C14.631 24.8084 14.5517 25.0069 14.5569 25.2123C14.562 25.4177 14.6512 25.612 14.8036 25.7498C16.1777 26.9923 18.0013 27.75 20.0001 27.75C21.9989 27.75 23.8225 26.9923 25.1966 25.7498C25.349 25.612 25.4382 25.4177 25.4433 25.2123C25.4485 25.0069 25.3692 24.8084 25.2239 24.6631L25.0409 24.4802Z" fill="#8F9AA3"/>
</svg>

After

Width:  |  Height:  |  Size: 947 B

@ -0,0 +1,9 @@
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="40" height="40" rx="10" fill="#EFEFF0" />
<path fill-rule="evenodd" clip-rule="evenodd"
d="M14 9.125C14.5523 9.125 15 9.57272 15 10.125V10.4003C15.7146 10.375 16.5263 10.375 17.4442 10.375H18V10.125C18 9.57272 18.4477 9.125 19 9.125C19.5523 9.125 20 9.57272 20 10.125V10.375H20.5558C21.4737 10.375 22.2854 10.375 23 10.4003V10.125C23 9.57272 23.4477 9.125 24 9.125C24.5523 9.125 25 9.57272 25 10.125V10.6053C25.7881 10.7723 26.4557 11.0703 27.0052 11.6198C27.6803 12.2949 27.9758 13.1484 28.1152 14.1846C28.25 15.1877 28.25 16.4667 28.25 18.0693V19.4718C28.25 19.6649 28.25 19.7614 28.1497 19.8509C28.0495 19.9404 27.9297 19.9267 27.6902 19.8994C26.9002 19.8092 26.0881 19.9694 25.3767 20.3802C24.9379 20.6335 24.5768 20.9968 24.341 21.2339L20.9295 24.6457C20.1944 25.3808 19.8859 26.2375 19.7251 26.8673C19.6482 27.1685 19.5952 27.4601 19.5554 27.6895C19.5176 27.869 19.4542 28.1907 19.3973 28.5014C19.3452 28.7858 19.272 29.1997 19.257 29.435C19.2433 29.6508 19.2408 29.9906 19.3355 30.3731C19.3797 30.552 19.4018 30.6414 19.3138 30.7559C19.2257 30.8705 19.1667 30.8716 19.0486 30.8737C18.9727 30.875 18.8861 30.875 18.7878 30.875H17.4443C15.8417 30.875 14.5627 30.875 13.5596 30.7402C12.5234 30.6008 11.6699 30.3053 10.9948 29.6302C10.3197 28.9551 10.0242 28.1017 9.88483 27.0654C9.74997 26.0623 9.74998 24.7834 9.75 23.1808V18.0692C9.74998 16.4667 9.74997 15.1877 9.88483 14.1846C10.0242 13.1484 10.3197 12.2949 10.9948 11.6198C11.5443 11.0703 12.2119 10.7723 13 10.6053V10.125C13 9.57272 13.4477 9.125 14 9.125ZM15 17.375C14.5858 17.375 14.25 17.7108 14.25 18.125C14.25 18.5392 14.5858 18.875 15 18.875H23C23.4142 18.875 23.75 18.5392 23.75 18.125C23.75 17.7108 23.4142 17.375 23 17.375H15ZM15 22.375C14.5858 22.375 14.25 22.7108 14.25 23.125C14.25 23.5392 14.5858 23.875 15 23.875H19C19.4142 23.875 19.75 23.5392 19.75 23.125C19.75 22.7108 19.4142 22.375 19 22.375H15Z"
fill="#8F9AA3" />
<path
d="M28.3976 21.6792C27.695 21.2736 26.8294 21.2736 26.1269 21.6792C25.8653 21.8303 25.631 22.065 25.3579 22.3387L21.9903 25.7064C21.525 26.1717 21.3075 26.7336 21.1786 27.2384C21.1143 27.4904 21.0684 27.7436 21.0288 27.9733C20.9632 28.2793 20.7702 29.277 20.7541 29.5302C20.7364 29.8092 20.7637 30.2137 21.0875 30.5374C21.4113 30.8612 21.8157 30.8886 22.0947 30.8708C22.348 30.8547 23.3456 30.6617 23.6516 30.5962C23.8813 30.5565 24.1345 30.5106 24.3865 30.4463C24.8913 30.3174 25.513 30.0402 25.9784 29.5749L29.2862 26.2671C29.5599 25.994 29.7947 25.7597 29.9457 25.4981C30.3513 24.7955 30.3513 23.9299 29.9457 23.2273C29.7947 22.9657 29.5001 22.6717 29.2264 22.3985C28.9533 22.1249 28.6592 21.8303 28.3976 21.6792Z"
fill="#8F9AA3" />
</svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

@ -0,0 +1,5 @@
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="40" height="40" rx="10" fill="#EFEFF0"/>
<path d="M20.75 12.5C20.75 12.0858 20.4142 11.75 20 11.75C19.5858 11.75 19.25 12.0858 19.25 12.5V13.25H18.5C18.0858 13.25 17.75 13.5858 17.75 14C17.75 14.4142 18.0858 14.75 18.5 14.75H19.25V15.5C19.25 15.9142 19.5858 16.25 20 16.25C20.4142 16.25 20.75 15.9142 20.75 15.5V14.75H21.5C21.9142 14.75 22.25 14.4142 22.25 14C22.25 13.5858 21.9142 13.25 21.5 13.25H20.75V12.5Z" fill="#8F9AA3"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M19.948 9.25H20.052C20.9505 9.24997 21.6997 9.24995 22.2945 9.32991C22.9223 9.41432 23.4891 9.59999 23.9445 10.0555C24.4 10.5109 24.5857 11.0777 24.6701 11.7055C24.695 11.8911 24.7122 12.0917 24.724 12.3072C25.8622 12.4348 26.7867 12.726 27.506 13.4474C28.1807 14.1241 28.476 14.9793 28.6152 16.018C28.75 17.0237 28.75 18.3062 28.75 19.9137V28.75H29C29.5523 28.75 30 29.1977 30 29.75C30 30.3023 29.5523 30.75 29 30.75H11C10.4477 30.75 10 30.3023 10 29.75C10 29.1977 10.4477 28.75 11 28.75H11.25V19.9137C11.25 18.3062 11.25 17.0237 11.3848 16.018C11.524 14.9793 11.8193 14.1241 12.494 13.4474C13.2133 12.726 14.1378 12.4348 15.276 12.3072C15.2878 12.0917 15.305 11.8911 15.3299 11.7055C15.4143 11.0777 15.6 10.5109 16.0555 10.0555C16.5109 9.59999 17.0777 9.41432 17.7055 9.32991C18.3003 9.24995 19.0495 9.24997 19.948 9.25ZM17.9054 10.8165C17.4439 10.8786 17.2464 10.9858 17.1161 11.1161C16.9858 11.2464 16.8786 11.4439 16.8165 11.9054C16.7516 12.3884 16.75 13.036 16.75 14C16.75 14.964 16.7516 15.6116 16.8165 16.0946C16.8786 16.5561 16.9858 16.7536 17.1161 16.8839C17.2464 17.0142 17.4439 17.1214 17.9054 17.1835C18.3884 17.2484 19.036 17.25 20 17.25C20.964 17.25 21.6116 17.2484 22.0946 17.1835C22.5561 17.1214 22.7536 17.0142 22.8839 16.8839C23.0142 16.7536 23.1214 16.5561 23.1835 16.0946C23.2484 15.6116 23.25 14.964 23.25 14C23.25 13.036 23.2484 12.3884 23.1835 11.9054C23.1214 11.4439 23.0142 11.2464 22.8839 11.1161C22.7536 10.9858 22.5561 10.8786 22.0946 10.8165C21.6116 10.7516 20.964 10.75 20 10.75C19.036 10.75 18.3884 10.7516 17.9054 10.8165ZM23.25 28.75V27.4678C23.25 27.028 23.25 26.6486 23.2218 26.3374C23.192 26.0082 23.1259 25.6822 22.9486 25.375C22.7511 25.033 22.467 24.7489 22.125 24.5514C21.8178 24.3741 21.4918 24.308 21.1627 24.2782C20.8514 24.25 20.472 24.25 20.0323 24.25C19.5925 24.25 19.1486 24.25 18.8374 24.2782C18.5082 24.308 18.1822 24.3741 17.875 24.5514C17.533 24.7489 17.2489 25.033 17.0514 25.375C16.8741 25.6822 16.808 26.0082 16.7782 26.3374C16.75 26.6486 16.75 27.028 16.75 27.4677V28.75H18.25V27.5C18.25 27.0189 18.2507 26.7082 18.2721 26.4727C18.3015 26.2392 18.4223 25.7721 18.9727 25.7721C19.2082 25.7507 19.5189 25.75 20 25.75C20.4811 25.75 20.7918 25.7507 21.0273 25.7721C21.2608 25.7843 21.7279 25.9415 21.7279 26.4727C21.7493 26.7082 21.75 27.0189 21.75 27.5V28.75H23.25ZM15 21C15 20.4477 15.4477 20 16 20H16.009C16.5612 20 17.009 20.4477 17.009 21C17.009 21.5523 16.5612 22 16.009 22H16C15.4477 22 15 21.5523 15 21ZM18.991 21C18.991 20.4477 19.4388 20 19.991 20H20C20.5523 20 21 20.4477 21 21C21 21.5523 20.5523 22 20 22H19.991C19.4388 22 18.991 21.5523 18.991 21ZM22.9922 21C22.9922 20.4477 23.4399 20 23.9922 20H24.0011C24.5534 20 25.0011 20.4477 25.0011 21C25.0011 21.5523 24.5534 22 24.0011 22H23.9922C23.4399 22 22.9922 21.5523 22.9922 21Z" fill="#8F9AA3"/>
</svg>

After

Width:  |  Height:  |  Size: 3.3 KiB

@ -0,0 +1,5 @@
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="40" height="40" rx="10" fill="#EFEFF0"/>
<path d="M18.5303 20.4696C18.3897 20.329 18.1989 20.25 18 20.25C17.7425 20.25 17.4921 20.25 17.25 20.2507V24.3535C18.4043 24.6799 19.25 25.7412 19.25 27V28C19.25 28.4142 18.9142 28.75 18.5 28.75C18.0858 28.75 17.75 28.4142 17.75 28V27C17.75 26.3096 17.1904 25.75 16.5 25.75C15.8096 25.75 15.25 26.3096 15.25 27V28C15.25 28.4142 14.9142 28.75 14.5 28.75C14.0858 28.75 13.75 28.4142 13.75 28V27C13.75 25.7412 14.5957 24.6799 15.75 24.3535V20.2764C15.3284 20.2933 14.9463 20.321 14.6083 20.3665C13.7081 20.4875 12.9503 20.7464 12.3484 21.3483C11.7464 21.9502 11.4875 22.7081 11.3665 23.6082C11.25 24.4752 11.25 25.5775 11.25 26.9451V30C11.25 30.4142 11.5858 30.75 12 30.75H28C28.4142 30.75 28.75 30.4142 28.75 30V26.9451C28.75 25.5775 28.75 24.4752 28.6335 23.6082C28.5125 22.7081 28.2536 21.9502 27.6517 21.3483C27.0497 20.7464 26.2919 20.4875 25.3918 20.3665C25.1928 20.3397 24.9786 20.3191 24.75 20.3033V25.9506C25.1984 26.21 25.5 26.6948 25.5 27.25C25.5 28.0784 24.8284 28.75 24 28.75C23.1716 28.75 22.5 28.0784 22.5 27.25C22.5 26.6948 22.8017 26.21 23.25 25.9506V20.2538C22.8569 20.25 22.4396 20.25 22.0005 20.25C21.8016 20.25 21.6103 20.329 21.4697 20.4696L20 21.9393L18.5303 20.4696Z" fill="#8F9AA3"/>
<path d="M20 9.25C17.6528 9.25 15.75 11.1528 15.75 13.5V14.5C15.75 16.8472 17.6528 18.75 20 18.75C22.3472 18.75 24.25 16.8472 24.25 14.5V13.5C24.25 11.1528 22.3472 9.25 20 9.25Z" fill="#8F9AA3"/>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

@ -0,0 +1,5 @@
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="40" height="40" rx="10" fill="#EFEFF0"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.75 17.5869C11.75 13.0186 15.5222 9.25 20 9.25C24.4778 9.25 28.25 13.0186 28.25 17.5869C28.25 22.2513 24.4018 25.4121 21.1696 27.4294L21.1583 27.4365L21.1467 27.4431C20.7978 27.644 20.4026 27.75 20 27.75C19.5974 27.75 19.2022 27.644 18.8533 27.4431L18.84 27.4355L18.8271 27.4273C15.6075 25.3945 11.75 22.2671 11.75 17.5869ZM16.5 17.5C16.5 15.567 18.067 14 20 14C21.933 14 23.5 15.567 23.5 17.5C23.5 19.433 21.933 21 20 21C18.067 21 16.5 19.433 16.5 17.5Z" fill="#8F9AA3"/>
<path d="M14.9958 27.6578C14.9493 27.1487 14.5212 26.75 14 26.75C13.4477 26.75 13 27.1977 13 27.75C13 28.4639 13.4349 28.9848 13.8525 29.3139C14.2814 29.6519 14.84 29.9125 15.4411 30.1129C16.6538 30.5171 18.2656 30.75 20 30.75C21.7344 30.75 23.3462 30.5171 24.5589 30.1129C25.16 29.9125 25.7186 29.6519 26.1475 29.3139C26.5651 28.9848 27 28.4639 27 27.75C27 27.1977 26.5523 26.75 26 26.75C25.4788 26.75 25.0507 27.1487 25.0042 27.6578C24.9869 27.6768 24.9574 27.7053 24.9095 27.7431C24.7311 27.8836 24.4111 28.054 23.9264 28.2155C22.9675 28.5352 21.5793 28.75 20 28.75C18.4207 28.75 17.0325 28.5352 16.0736 28.2155C15.5889 28.054 15.2689 27.8836 15.0905 27.7431C15.0426 27.7053 15.0131 27.6768 14.9958 27.6578Z" fill="#8F9AA3"/>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

@ -0,0 +1,5 @@
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="40" height="40" rx="10" fill="#EFEFF0"/>
<path d="M20.75 12.5C20.75 12.0858 20.4142 11.75 20 11.75C19.5858 11.75 19.25 12.0858 19.25 12.5V13.25H18.5C18.0858 13.25 17.75 13.5858 17.75 14C17.75 14.4142 18.0858 14.75 18.5 14.75H19.25V15.5C19.25 15.9142 19.5858 16.25 20 16.25C20.4142 16.25 20.75 15.9142 20.75 15.5V14.75H21.5C21.9142 14.75 22.25 14.4142 22.25 14C22.25 13.5858 21.9142 13.25 21.5 13.25H20.75V12.5Z" fill="#8F9AA3"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M19.948 9.25H20.052C20.9505 9.24997 21.6997 9.24995 22.2945 9.32991C22.9223 9.41432 23.4891 9.59999 23.9445 10.0555C24.4 10.5109 24.5857 11.0777 24.6701 11.7055C24.695 11.8911 24.7122 12.0917 24.724 12.3072C25.8622 12.4348 26.7867 12.726 27.506 13.4474C28.1807 14.1241 28.476 14.9793 28.6152 16.018C28.75 17.0237 28.75 18.3062 28.75 19.9137V28.75H29C29.5523 28.75 30 29.1977 30 29.75C30 30.3023 29.5523 30.75 29 30.75H11C10.4477 30.75 10 30.3023 10 29.75C10 29.1977 10.4477 28.75 11 28.75H11.25V19.9137C11.25 18.3062 11.25 17.0237 11.3848 16.018C11.524 14.9793 11.8193 14.1241 12.494 13.4474C13.2133 12.726 14.1378 12.4348 15.276 12.3072C15.2878 12.0917 15.305 11.8911 15.3299 11.7055C15.4143 11.0777 15.6 10.5109 16.0555 10.0555C16.5109 9.59999 17.0777 9.41432 17.7055 9.32991C18.3003 9.24995 19.0495 9.24997 19.948 9.25ZM17.9054 10.8165C17.4439 10.8786 17.2464 10.9858 17.1161 11.1161C16.9858 11.2464 16.8786 11.4439 16.8165 11.9054C16.7516 12.3884 16.75 13.036 16.75 14C16.75 14.964 16.7516 15.6116 16.8165 16.0946C16.8786 16.5561 16.9858 16.7536 17.1161 16.8839C17.2464 17.0142 17.4439 17.1214 17.9054 17.1835C18.3884 17.2484 19.036 17.25 20 17.25C20.964 17.25 21.6116 17.2484 22.0946 17.1835C22.5561 17.1214 22.7536 17.0142 22.8839 16.8839C23.0142 16.7536 23.1214 16.5561 23.1835 16.0946C23.2484 15.6116 23.25 14.964 23.25 14C23.25 13.036 23.2484 12.3884 23.1835 11.9054C23.1214 11.4439 23.0142 11.2464 22.8839 11.1161C22.7536 10.9858 22.5561 10.8786 22.0946 10.8165C21.6116 10.7516 20.964 10.75 20 10.75C19.036 10.75 18.3884 10.7516 17.9054 10.8165ZM23.25 28.75V27.4678C23.25 27.028 23.25 26.6486 23.2218 26.3374C23.192 26.0082 23.1259 25.6822 22.9486 25.375C22.7511 25.033 22.467 24.7489 22.125 24.5514C21.8178 24.3741 21.4918 24.308 21.1627 24.2782C20.8514 24.25 20.472 24.25 20.0323 24.25C19.5925 24.25 19.1486 24.25 18.8374 24.2782C18.5082 24.308 18.1822 24.3741 17.875 24.5514C17.533 24.7489 17.2489 25.033 17.0514 25.375C16.8741 25.6822 16.808 26.0082 16.7782 26.3374C16.75 26.6486 16.75 27.028 16.75 27.4677V28.75H18.25V27.5C18.25 27.0189 18.2507 26.7082 18.2721 26.4727C18.3015 26.2392 18.4223 25.7721 18.9727 25.7721C19.2082 25.7507 19.5189 25.75 20 25.75C20.4811 25.75 20.7918 25.7507 21.0273 25.7721C21.2608 25.7843 21.7279 25.9415 21.7279 26.4727C21.7493 26.7082 21.75 27.0189 21.75 27.5V28.75H23.25ZM15 21C15 20.4477 15.4477 20 16 20H16.009C16.5612 20 17.009 20.4477 17.009 21C17.009 21.5523 16.5612 22 16.009 22H16C15.4477 22 15 21.5523 15 21ZM18.991 21C18.991 20.4477 19.4388 20 19.991 20H20C20.5523 20 21 20.4477 21 21C21 21.5523 20.5523 22 20 22H19.991C19.4388 22 18.991 21.5523 18.991 21ZM22.9922 21C22.9922 20.4477 23.4399 20 23.9922 20H24.0011C24.5534 20 25.0011 20.4477 25.0011 21C25.0011 21.5523 24.5534 22 24.0011 22H23.9922C23.4399 22 22.9922 21.5523 22.9922 21Z" fill="#8F9AA3"/>
</svg>

After

Width:  |  Height:  |  Size: 3.3 KiB

@ -0,0 +1,4 @@
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="40" height="40" rx="10" fill="#EFEFF0"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M25.0357 9.25C25.5617 9.25 25.9881 9.67639 25.9881 10.2024C25.9881 10.7417 25.9966 10.8274 26.0161 10.8959C26.0315 10.9499 26.0531 11.0019 26.0803 11.051C26.115 11.1132 26.1696 11.1798 26.551 11.5612L28.4388 13.449C28.8202 13.8304 28.8868 13.885 28.949 13.9197C28.9981 13.9469 29.0501 13.9685 29.1041 13.9839C29.1726 14.0034 29.2583 14.0119 29.7976 14.0119C30.3236 14.0119 30.75 14.4383 30.75 14.9643C30.75 15.4902 30.3236 15.9166 29.7976 15.9166C29.3989 15.9172 28.94 15.9177 28.5819 15.8157C28.3873 15.7602 28.1998 15.6825 28.023 15.5841C27.897 15.5141 27.7805 15.4317 27.6686 15.3406L25.0402 18.626L26.7071 20.2929C27.0976 20.6834 27.0976 21.3166 26.7071 21.7071C26.3166 22.0976 25.6834 22.0976 25.2929 21.7071L24.9767 21.3909L20.5751 25.7925C20.066 26.3015 19.6441 26.7235 19.2685 27.0298C18.8764 27.3495 18.4719 27.5972 17.9834 27.6944C17.6106 27.7685 17.2268 27.7685 16.8541 27.6944C16.7033 27.6644 16.5606 27.6201 16.4238 27.5638L15.3831 28.3071C15.1732 28.4571 14.962 28.6081 14.7694 28.7193C14.5522 28.8447 14.2807 28.9677 13.9455 28.9941C13.9025 28.9975 13.8594 28.9981 13.8163 28.9959C13.522 28.981 13.1623 28.8608 12.7837 28.6305L10.9571 30.4571C10.5666 30.8476 9.93342 30.8476 9.54289 30.4571C9.15237 30.0666 9.15237 29.4334 9.54289 29.0429L11.3713 27.2145C11.2346 26.9877 11.1412 26.775 11.083 26.5884C11.0343 26.4305 10.9655 26.0271 11.0801 25.6775C11.1948 25.3122 11.4474 24.96 11.6601 24.6638L12.4366 23.577C12.3801 23.44 12.3357 23.297 12.3056 23.146C12.2315 22.7732 12.2315 22.3894 12.3056 22.0166C12.4028 21.5281 12.6505 21.1237 12.9702 20.7315C13.2765 20.3559 13.6984 19.934 14.2075 19.4249L18.6091 15.0233L18.2929 14.7071C17.9024 14.3166 17.9024 13.6834 18.2929 13.2929C18.6834 12.9024 19.3166 12.9024 19.7071 13.2929L21.374 14.9598L24.6594 12.3314C24.5683 12.2195 24.4859 12.103 24.4159 11.9771C24.3175 11.8002 24.2398 11.6127 24.1843 11.4181C24.0823 11.06 24.0828 10.6011 24.0834 10.2024C24.0834 9.67639 24.5098 9.25 25.0357 9.25ZM22.7291 16.3149L23.6851 17.2709L26.2992 14.0032L25.9968 13.7008L22.7291 16.3149ZM13.7094 25.2359C13.8843 25.4145 14.071 25.6012 14.2623 25.7925C14.4395 25.9697 14.6064 26.1365 14.7642 26.2914L14.2498 26.6589C14.0609 26.7938 13.9399 26.8794 13.8497 26.9378C13.7418 26.8755 13.5895 26.7678 13.4111 26.5894C13.2332 26.4115 13.1263 26.2573 13.0645 26.1483C13.1271 26.0517 13.2127 25.9314 13.3417 25.7508L13.7094 25.2359Z" fill="#8F9AA3"/>
</svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

@ -811,5 +811,8 @@
"allSet": "جاهز! الآن يمكنك تسجيل الدخول باستخدام Face ID / Biometric أو البصمة",
"enableQuickLogin":"تمكين تسجيل الدخول السريع",
"enableMsg":"تمكين تسجيل الدخول السريع سيسمح بالتحقق من خلال Face ID / Biometric الخاص بجهازك الحالي",
"notNow": "ليس الآن"
"notNow": "ليس الآن",
"pendingActivation": "في انتظار التنشيط",
"awaitingApproval": "انتظر القبول",
"ready": "جاهز"
}

@ -807,5 +807,8 @@
"allSet": "All Set! Now you can login with Face ID or Biometric",
"enableQuickLogin": "Enable Quick Login",
"enableMsg": "Enabling the quick login will verify through your existing device Face ID / Biometric",
"notNow": "Not Now"
"notNow": "Not Now",
"pendingActivation": "Pending Activation",
"awaitingApproval": "Awaiting Approval",
"ready": "Ready"
}

@ -26,6 +26,7 @@ abstract class ApiClient {
bool isAllowAny,
bool isExternal,
bool isRCService,
bool isPaymentServices,
bool bypassConnectionCheck,
});
@ -97,6 +98,7 @@ class ApiClientImp implements ApiClient {
bool isAllowAny = false,
bool isExternal = false,
bool isRCService = false,
bool isPaymentServices = false,
bool bypassConnectionCheck = false,
}) async {
String url;
@ -204,7 +206,12 @@ class ApiClientImp implements ApiClient {
} else {
var parsed = json.decode(utf8.decode(response.bodyBytes));
if (isAllowAny) {
onSuccess(parsed, statusCode, messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage']);
if (isPaymentServices) {
onSuccess(parsed, statusCode, messageStatus: 1, errorMessage: "");
} else {
onSuccess(parsed, statusCode,
messageStatus: parsed.contains('MessageStatus') ? parsed['MessageStatus'] : 1, errorMessage: parsed.contains('ErrorEndUserMessage') ? parsed['ErrorEndUserMessage'] : "");
}
} else {
if (parsed['Response_Message'] != null) {
onSuccess(parsed, statusCode, messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']);

@ -804,6 +804,7 @@ class ApiConsts {
static final String insertPatientMobileData = 'Services/MobileNotifications.svc/REST/Insert_PatientMobileDeviceInfo';
static final String getPatientMobileData = '/Services/Authentication.svc/REST/GetMobileLoginInfo';
static final String getPrivileges = 'Services/Patients.svc/REST/Service_Privilege';
static final String registerUser = 'Services/Authentication.svc/REST/PatientRegistration';
// static values for Api
static final double appVersionID = 18.7;

@ -95,7 +95,19 @@ class AppAssets {
static const String checkin_location_icon = '$svgBasePath/checkin_location_icon.svg';
static const String checkin_nfc_icon = '$svgBasePath/checkin_nfc_icon.svg';
static const String checkin_qr_icon = '$svgBasePath/checkin_qr_icon.svg';
static const String my_account_icon = '$svgBasePath/my_account_icon.svg';
static const String select_hospital_icon = '$svgBasePath/select_hospital_icon.svg';
static const String email_icon = '$svgBasePath/email_icon.svg';
static const String notes_icon = '$svgBasePath/notes_icon.svg';
static const String forward_chevron_icon = '$svgBasePath/forward_chevron_icon.svg';
static const String logout = '$svgBasePath/logout.svg';
static const String alarm_clock_icon = '$svgBasePath/alarm_clock_icon.svg';
static const String all_medications_icon = '$svgBasePath/all_medications_icon.svg';
static const String allergy_info_icon = '$svgBasePath/allergy_info_icon.svg';
static const String vaccine_info_icon = '$svgBasePath/vaccine_info_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_region_icon = '$svgBasePath/search_by_region_icon.svg';
//bottom navigation//
static const String homeBottom = '$svgBasePath/home_bottom.svg';

@ -5,6 +5,7 @@ import 'package:hmg_patient_app_new/core/common_models/privilege/HMCProjectListM
import 'package:hmg_patient_app_new/core/common_models/privilege/PrivilegeModel.dart';
import 'package:hmg_patient_app_new/core/common_models/privilege/ProjectDetailListModel.dart';
import 'package:hmg_patient_app_new/core/common_models/privilege/VidaPlusProjectListModel.dart';
import 'package:hmg_patient_app_new/features/authentication/models/request_models/send_activation_request_model.dart';
import 'package:hmg_patient_app_new/features/authentication/models/resp_models/authenticated_user_resp_model.dart';
import 'package:hmg_patient_app_new/features/authentication/models/resp_models/check_user_staus_nhic_response_model.dart';
import 'package:hmg_patient_app_new/features/authentication/models/resp_models/select_device_by_imei.dart';

@ -6,11 +6,16 @@ import 'package:hmg_patient_app_new/core/location_util.dart';
import 'package:hmg_patient_app_new/features/authentication/authentication_repo.dart';
import 'package:hmg_patient_app_new/features/authentication/authentication_view_model.dart';
import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_repo.dart';
import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_view_model.dart';
import 'package:hmg_patient_app_new/features/common/common_repo.dart';
import 'package:hmg_patient_app_new/features/habib_wallet/habib_wallet_repo.dart';
import 'package:hmg_patient_app_new/features/habib_wallet/habib_wallet_view_model.dart';
import 'package:hmg_patient_app_new/features/insurance/insurance_repo.dart';
import 'package:hmg_patient_app_new/features/insurance/insurance_view_model.dart';
import 'package:hmg_patient_app_new/features/lab/lab_repo.dart';
import 'package:hmg_patient_app_new/features/lab/lab_view_model.dart';
import 'package:hmg_patient_app_new/features/medical_file/medical_file_repo.dart';
import 'package:hmg_patient_app_new/features/medical_file/medical_file_view_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_repo.dart';
import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart';
import 'package:hmg_patient_app_new/features/payfort/payfort_repo.dart';
@ -85,6 +90,8 @@ class AppDependencies {
getIt.registerLazySingleton<InsuranceRepo>(() => InsuranceRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt()));
getIt.registerLazySingleton<PayfortRepo>(() => PayfortRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt()));
getIt.registerLazySingleton<LocalAuthService>(() => LocalAuthService(loggerService: getIt<LoggerService>(), localAuth: getIt<LocalAuthentication>()));
getIt.registerLazySingleton<HabibWalletRepo>(() => HabibWalletRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt()));
getIt.registerLazySingleton<MedicalFileRepo>(() => MedicalFileRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt()));
// ViewModels
// Global/shared VMs LazySingleton
@ -125,24 +132,45 @@ class AppDependencies {
);
getIt.registerLazySingleton<PayfortViewModel>(
() => PayfortViewModel(
() => PayfortViewModel(
payfortRepo: getIt(),
errorHandlerService: getIt(),
),
);
getIt.registerLazySingleton<AuthenticationViewModel>(
() => AuthenticationViewModel(
authenticationRepo: getIt(),
cacheService: getIt(),
navigationService: getIt(),
dialogService: getIt(),
appState: getIt(),
getIt.registerLazySingleton<HabibWalletViewModel>(
() => HabibWalletViewModel(
habibWalletRepo: getIt(),
errorHandlerService: getIt(),
),
);
getIt.registerLazySingleton<HabibWalletViewModel>(
() => HabibWalletViewModel(
habibWalletRepo: getIt(),
errorHandlerService: getIt(),
),
);
getIt.registerLazySingleton<MedicalFileViewModel>(
() => MedicalFileViewModel(
medicalFileRepo: getIt(),
errorHandlerService: getIt(),
),
);
getIt.registerLazySingleton<BookAppointmentsViewModel>(
() => BookAppointmentsViewModel(
bookAppointmentsRepo: getIt(),
errorHandlerService: getIt(),
localAuthService: getIt()
),
);
getIt.registerLazySingleton<AuthenticationViewModel>(
() => AuthenticationViewModel(
authenticationRepo: getIt(), cacheService: getIt(), navigationService: getIt(), dialogService: getIt(), appState: getIt(), errorHandlerService: getIt(), localAuthService: getIt()),
);
// Screen-specific VMs Factory
// getIt.registerFactory<BookAppointmentsViewModel>(
// () => BookAppointmentsViewModel(

@ -1,7 +1,12 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:hijri_gregorian_calendar/hijri_gregorian_calendar.dart';
import 'package:hmg_patient_app_new/core/api_consts.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/core/enums.dart';
import 'package:hmg_patient_app_new/core/utils/date_util.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/features/authentication/models/request_models/registration_payload_model.dart';
import 'package:hmg_patient_app_new/features/authentication/models/request_models/send_activation_request_model.dart';
import 'package:hmg_patient_app_new/features/common/models/commong_authanticated_req_model.dart';
@ -60,7 +65,7 @@ class RequestUtils {
required String? deviceToken,
required bool patientOutSA,
required String? loginTokenID,
required var registeredData,
RegistrationDataModelPayload? registeredData,
int? patientId,
required String nationIdText,
required String countryCode,
@ -80,9 +85,14 @@ class RequestUtils {
request.logInTokenID = loginTokenID ?? "";
if (registeredData != null) {
request.searchType = registeredData.searchType ?? 1;
request.patientID = registeredData.patientID ?? 0;
request.patientIdentificationID = request.nationalID = registeredData.patientIdentificationID ?? '0';
//TODO: Issue Here if Not Signup
request.searchType = registeredData.searchType != null
? registeredData.searchType
: fileNo
? 1
: 2;
request.patientID = registeredData.patientId ?? 0;
request.patientIdentificationID = request.nationalID = (registeredData.patientIdentificationId ?? 0);
request.dob = registeredData.dob;
request.isRegister = registeredData.isRegister;
} else {
@ -90,9 +100,11 @@ class RequestUtils {
request.patientID = patientId ?? int.parse(nationIdText);
request.patientIdentificationID = request.nationalID = 0;
request.searchType = 2;
//TODO: Issue HEre is Not Login
} else {
request.patientID = 0;
request.searchType = 1;
//TODO: Issue HEre is Not Login
request.patientIdentificationID = request.nationalID = (nationIdText.isNotEmpty ? int.parse(nationIdText) : 0);
}
request.isRegister = false;
@ -132,9 +144,10 @@ class RequestUtils {
request.patientIdentificationID = request.nationalID = payload["PatientIdentificationID"] ?? '0';
request.dob = payload["DOB"];
request.isRegister = payload["isRegister"];
request.healthId = _appState.getNHICUserData.healthId;
request.healthId = patientOutSA ? null : _appState.getNHICUserData.healthId;
request.isHijri = payload["IsHijri"];
request.deviceToken = _appState.deviceToken;
request.projectOutSA = patientOutSA;
} else {
request.searchType = isFileNo ? 2 : 1;
request.patientID = patientId ?? 0;
@ -172,4 +185,65 @@ class RequestUtils {
request.tokenId = null;
return request;
}
static dynamic getUserSignupCompletionRequest({String? fullName, String? emailAddress, GenderTypeEnum? gender, MaritalStatusTypeEnum? maritalStatus}) {
AppState appState = getIt.get<AppState>();
bool isDubai = appState.getUserRegistrationPayload.patientOutSa == 1 ? true : false;
List<String> names = fullName != null ? fullName.split(" ") : [];
var dob = appState.getUserRegistrationPayload.dob;
final DateFormat dateFormat1 = DateFormat('MM/dd/yyyy');
final DateFormat dateFormat2 = DateFormat('dd/MM/yyyy');
DateTime gregorianDate = dateFormat2.parse(dob!);
HijriGregDate hijriDate = HijriGregConverter.gregorianToHijri(gregorianDate);
String? date = "${hijriDate.day}/${hijriDate.month}/${hijriDate.year}";
return {
"Patientobject": {
"TempValue": true,
"PatientIdentificationType":
(isDubai ? appState.getUserRegistrationPayload.patientIdentificationId?.toString().substring(0, 1) : appState.getNHICUserData.idNumber!.substring(0, 1)) == "1" ? 1 : 2,
"PatientIdentificationNo": isDubai ? appState.getUserRegistrationPayload.patientIdentificationId.toString() : appState.getNHICUserData.idNumber.toString(),
"MobileNumber": appState.getUserRegistrationPayload.patientMobileNumber ?? 0,
"PatientOutSA": (appState.getUserRegistrationPayload.zipCode == CountryEnum.saudiArabia.countryCode || appState.getUserRegistrationPayload.zipCode == '+966') ? 0 : 1,
"FirstNameN": isDubai ? "..." : appState.getNHICUserData.firstNameAr,
"FirstName": isDubai ? (names.isNotEmpty ? names[0] : "...") : appState.getNHICUserData.firstNameEn,
"MiddleNameN": isDubai ? "..." : appState.getNHICUserData.secondNameAr,
"MiddleName": isDubai ? "..." : appState.getNHICUserData.secondNameEn,
"LastNameN": isDubai ? "..." : appState.getNHICUserData.lastNameAr,
"LastName": isDubai ? (names.length > 1 ? names[1] : "...") : appState.getNHICUserData.lastNameEn,
"StrDateofBirth": dateFormat1.format(dateFormat2.parse(dob)),
"DateofBirth": DateUtil.convertISODateToJsonDate((dob ?? "").replaceAll('/', '-')),
"Gender": isDubai ? (gender == GenderTypeEnum.male ? 1 : 2) : (appState.getNHICUserData.gender == 'M' ? 1 : 2),
"NationalityID": isDubai ? "UAE" : appState.getNHICUserData.nationalityCode,
"eHealthIDField": isDubai ? null : appState.getNHICUserData.healthId,
"DateofBirthN": date,
"EmailAddress": emailAddress,
"SourceType": (appState.getUserRegistrationPayload.zipCode == CountryEnum.saudiArabia.countryCode || appState.getUserRegistrationPayload.zipCode == '+966') ? "1" : "2",
"PreferredLanguage": appState.getLanguageCode() == "ar" ? (isDubai ? "1" : 1) : (isDubai ? "2" : 2),
"Marital": isDubai
? (maritalStatus == MaritalStatusTypeEnum.single
? '0'
: maritalStatus == MaritalStatusTypeEnum.married
? '1'
: '2')
: (appState.getNHICUserData.maritalStatusCode == 'U'
? '0'
: appState.getNHICUserData.maritalStatusCode == 'M'
? '1'
: '2'),
},
"PatientIdentificationID": isDubai ? appState.getUserRegistrationPayload.patientIdentificationId.toString() : appState.getNHICUserData.idNumber.toString(),
"PatientMobileNumber": appState.getUserRegistrationPayload.patientMobileNumber.toString()[0] == '0'
? appState.getUserRegistrationPayload.patientMobileNumber
: '0${appState.getUserRegistrationPayload.patientMobileNumber}',
"DOB": dob,
"IsHijri": appState.getUserRegistrationPayload.isHijri,
"PatientOutSA": (appState.getUserRegistrationPayload.zipCode == CountryEnum.saudiArabia.countryCode || appState.getUserRegistrationPayload.zipCode == '+966') ? 0 : 1,
"isDentalAllowedBackend": appState.getUserRegistrationPayload.isDentalAllowedBackend,
"ZipCode": appState.getUserRegistrationPayload.zipCode,
if (!isDubai) "HealthId": appState.getNHICUserData.healthId,
};
}
}

@ -1,5 +1,7 @@
import 'dart:convert';
import 'dart:developer';
import 'dart:io';
import 'dart:typed_data';
import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:crypto/crypto.dart' as crypto;
@ -21,6 +23,7 @@ import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/dialogs/confirm_dialog.dart';
import 'package:hmg_patient_app_new/widgets/loading_dialog.dart';
import 'package:lottie/lottie.dart';
import 'package:path_provider/path_provider.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'dart:math' as dartMath;
@ -339,7 +342,8 @@ class Utils {
).center;
}
static bool isVidaPlusProject(AppState appState, int projectID) {
static bool isVidaPlusProject(int projectID) {
AppState appState = getIt.get<AppState>();
bool isVidaPlus = false;
for (var element in appState.vidaPlusProjectList) {
if (element.projectID == projectID) {
@ -568,9 +572,10 @@ class Utils {
);
}
static Widget getPaymentAmountWithSymbol(Widget paymentAmountWidget, Color iconColor, double iconSize, {bool isSaudiCurrency = true}) {
static Widget getPaymentAmountWithSymbol(Widget paymentAmountWidget, Color iconColor, double iconSize, {bool isSaudiCurrency = true, bool isExpanded = true}) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
mainAxisAlignment: isExpanded ? MainAxisAlignment.spaceBetween : MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
appState.isArabic()
? Container()
@ -628,4 +633,13 @@ class Utils {
static String getAdvancePaymentTransID(int projectID, int fileNumber) {
return '$projectID-$fileNumber-${DateTime.now().millisecondsSinceEpoch}';
}
static Future<String> createFileFromString(String encodedStr, String ext) async {
Uint8List bytes = base64.decode(encodedStr);
String dir = (await getApplicationDocumentsDirectory()).path;
File file = File("$dir/" + DateTime.now().millisecondsSinceEpoch.toString() + "." + ext);
await file.writeAsBytes(bytes);
return file.path;
}
}

@ -32,6 +32,8 @@ abstract class AuthenticationRepo {
Future<Either<Failure, GenericApiModel<dynamic>>> checkUserStatus({required dynamic commonAuthanticatedRequest});
Future<Either<Failure, GenericApiModel<dynamic>>> registerUser({required dynamic registrationPayloadDataModelRequest});
Future<Either<Failure, GenericApiModel<dynamic>>> insertPatientIMEIData({required dynamic patientIMEIDataRequest});
Future<Either<Failure, GenericApiModel<dynamic>>> insertPatientDeviceData({required dynamic patientDeviceDataRequest});
@ -184,7 +186,6 @@ class AuthenticationRepoImp implements AuthenticationRepo {
newRequest.projectOutSA = newRequest.zipCode == '966' ? false : true;
newRequest.isDentalAllowedBackend = false;
newRequest.forRegisteration = newRequest.isRegister ?? false;
newRequest.isRegister = false;
}
@ -359,6 +360,39 @@ class AuthenticationRepoImp implements AuthenticationRepo {
}
}
@override
Future<Either<Failure, GenericApiModel<dynamic>>> registerUser({required dynamic registrationPayloadDataModelRequest}) async {
try {
GenericApiModel<dynamic>? apiResponse;
Failure? failure;
await apiClient.post(
ApiConsts.registerUser,
body: registrationPayloadDataModelRequest,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
apiResponse = GenericApiModel<dynamic>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: errorMessage,
data: response,
);
} catch (e) {
failure = DataParsingFailure(e.toString());
}
},
);
if (failure != null) return Left(failure!);
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
return Right(apiResponse!);
} catch (e) {
return Left(UnknownFailure(e.toString()));
}
}
@override
Future<Either<Failure, GenericApiModel>> insertPatientIMEIData({required patientIMEIDataRequest}) {
try {

@ -15,6 +15,7 @@ import 'package:hmg_patient_app_new/core/utils/loading_utils.dart';
import 'package:hmg_patient_app_new/core/utils/request_utils.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/core/utils/validation_utils.dart';
import 'package:hmg_patient_app_new/extensions/context_extensions.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/features/authentication/authentication_repo.dart';
import 'package:hmg_patient_app_new/features/authentication/models/request_models/check_activation_code_register_request_model.dart';
@ -31,6 +32,7 @@ import 'package:hmg_patient_app_new/services/error_handler_service.dart';
import 'package:hmg_patient_app_new/services/localauth_service.dart';
import 'package:hmg_patient_app_new/services/navigation_service.dart';
import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart';
import 'package:hmg_patient_app_new/widgets/bottomsheet/exception_bottom_sheet.dart';
import 'models/request_models/get_user_mobile_device_data.dart';
import 'models/request_models/insert_patient_mobile_deviceinfo.dart';
@ -80,7 +82,7 @@ class AuthenticationViewModel extends ChangeNotifier {
String errorMsg = '';
final FocusNode myFocusNode = FocusNode();
var healthId;
int getDeviceLastLogin =1;
int getDeviceLastLogin = 1;
Future<void> onLoginPressed() async {
try {
@ -110,6 +112,8 @@ class AuthenticationViewModel extends ChangeNotifier {
isTermsAccepted = false;
selectedCountrySignup = CountryEnum.saudiArabia;
pickedCountryByUAEUser = null;
_appState.setUserRegistrationPayload = RegistrationDataModelPayload();
_appState.setNHICUserData = CheckUserStatusResponseNHIC();
}
void onCountryChange(CountryEnum country) {
@ -191,7 +195,7 @@ class AuthenticationViewModel extends ChangeNotifier {
(failure) async {
// LoadingUtils.hideFullScreenLoader();
// await _errorHandlerService.handleError(failure: failure);
LoadingUtils.hideFullScreenLoader();
LoaderBottomSheet.hideLoader();
_navigationService.pushPage(page: LoginScreen());
},
(apiResponse) {
@ -238,6 +242,7 @@ class AuthenticationViewModel extends ChangeNotifier {
_navigationService.pushPage(page: SavedLogin());
// _navigationService.pushPage(page: LoginScreen());
} else {
print("print login........");
LoaderBottomSheet.hideLoader();
_navigationService.pushPage(page: LoginScreen());
}
@ -292,12 +297,7 @@ class AuthenticationViewModel extends ChangeNotifier {
} else if (apiResponse.messageStatus == 1) {
if (apiResponse.data['isSMSSent']) {
_appState.setAppAuthToken = apiResponse.data['LogInTokenID'];
await sendActivationCode(
otpTypeEnum: otpTypeEnum,
phoneNumber: phoneNumberController.text,
nationalIdOrFileNumber: nationalIdController.text,
);
await sendActivationCode(otpTypeEnum: otpTypeEnum, phoneNumber: phoneNumberController.text, nationalIdOrFileNumber: nationalIdController.text, isForRegister: false);
} else {
if (apiResponse.data['IsAuthenticated']) {
await checkActivationCode(
@ -305,7 +305,6 @@ class AuthenticationViewModel extends ChangeNotifier {
onWrongActivationCode: (String? message) {},
activationCode: null, //todo silent login case halded on the repo itself..
);
}
}
}
@ -313,41 +312,33 @@ class AuthenticationViewModel extends ChangeNotifier {
);
}
Future<void> sendActivationCode({required OTPTypeEnum otpTypeEnum, required String nationalIdOrFileNumber, required String phoneNumber, dynamic payload}) async {
bool isForRegister = checkIsUserComingForRegister(request: payload);
bool isPatientOutSA = isPatientOutsideSA(request: payload);
bool isFileNo = isPatientHasFile(request: payload);
Future<void> sendActivationCode({required OTPTypeEnum otpTypeEnum, required String nationalIdOrFileNumber, required String phoneNumber, required bool isForRegister, dynamic payload}) async {
var request = RequestUtils.getCommonRequestSendActivationCode(
otpTypeEnum: otpTypeEnum,
mobileNumber: phoneNumber,
selectedLoginType: otpTypeEnum.toInt(),
zipCode: selectedCountrySignup.countryCode,
nationalId: int.parse(nationalIdOrFileNumber),
isFileNo: isFileNo,
patientId: 0,
isForRegister: isForRegister,
patientOutSA: isPatientOutSA,
payload: payload);
otpTypeEnum: otpTypeEnum,
mobileNumber: phoneNumber,
selectedLoginType: otpTypeEnum.toInt(),
zipCode: selectedCountrySignup.countryCode,
nationalId: int.parse(nationalIdOrFileNumber),
isFileNo: isForRegister ? isPatientHasFile(request: payload) : false,
patientId: 0,
isForRegister: isForRegister,
patientOutSA: isForRegister
? isPatientOutsideSA(request: payload)
: selectedCountrySignup.countryCode == CountryEnum.saudiArabia
? false
: true,
payload: payload,
);
// TODO: GET APP SMS SIGNATURE HERE
request.sMSSignature = "enKTDcqbOVd";
// GifLoaderDialogUtils.showMyDialog(context);
// bool isForRegister = healthId != null || isDubai;
// if (isForRegister) {
// if (!isDubai) {
// request.dob = dob; //isHijri == 1 ? dob : dateFormat2.format(dateFormat.parse(dob));
// }
// request.healthId = healthId;
// request.isHijri = calenderType.toInt;
// } else {
// // request.dob = "";
// // request.healthId = "";
// // request.isHijri = 0;
// }
if (checkIsUserComingForRegister(request: payload)) {
_appState.setUserRegistrationPayload = RegistrationDataModelPayload.fromJson(payload);
print("====== Demo ==========");
}
final resultEither = await _authenticationRepo.sendActivationCodeRepo(sendActivationCodeReq: request, isRegister: isForRegister, languageID: 'er');
final resultEither = await _authenticationRepo.sendActivationCodeRepo(sendActivationCodeReq: request, isRegister: checkIsUserComingForRegister(request: payload), languageID: 'er');
resultEither.fold(
(failure) async => await _errorHandlerService.handleError(failure: failure),
@ -381,23 +372,43 @@ class AuthenticationViewModel extends ChangeNotifier {
required OTPTypeEnum otpTypeEnum,
required Function(String? message) onWrongActivationCode,
}) async {
bool isForRegister = (_appState.getUserRegistrationPayload.healthId != null || _appState.getUserRegistrationPayload.patientOutSa == true);
final request = RequestUtils.getCommonRequestWelcome(
phoneNumber: phoneNumberController.text,
otpTypeEnum: otpTypeEnum,
deviceToken: _appState.deviceToken,
patientOutSA: true,
// patientOutSA: _appState.getUserRegistrationPayload.projectOutSa == 1 ? true : false,
patientOutSA: isForRegister
? _appState.getUserRegistrationPayload.projectOutSa == 1
? true
: false
: _appState.getSelectDeviceByImeiRespModelElement != null
? _appState.getSelectDeviceByImeiRespModelElement!.outSa!
: selectedCountrySignup == CountryEnum.saudiArabia
? false
: true,
loginTokenID: _appState.appAuthToken,
registeredData: null,
registeredData: isForRegister ? _appState.getUserRegistrationPayload : null,
nationIdText: nationalIdController.text,
countryCode: selectedCountrySignup.countryCode,
loginType: loginTypeEnum.toInt)
.toJson();
LoaderBottomSheet.showLoader();
bool isForRegister = (_appState.getUserRegistrationPayload.healthId != null || _appState.getUserRegistrationPayload.patientOutSa == true);
LoaderBottomSheet.showLoader();
if (isForRegister) {
if (_appState.getUserRegistrationPayload.patientOutSa == true) request['DOB'] = _appState.getUserRegistrationPayload.dob;
if (_appState.getUserRegistrationPayload.patientOutSa == 0) request['DOB'] = _appState.getUserRegistrationPayload.dob;
request['HealthId'] = _appState.getUserRegistrationPayload.healthId;
request['IsHijri'] = _appState.getUserRegistrationPayload.isHijri;
request["PatientOutSA"] = _appState.getUserRegistrationPayload.projectOutSa == true ? 1 : 0;
request["ForRegisteration"] = _appState.getUserRegistrationPayload.isRegister;
request["isRegister"] = false;
// if (request.containsKey("OTP_SendType")) {
// request.remove("OTP_SendType");
// print("====== Demo: Removed OTP_SendType for Register state");
// }
print("====== Req");
final resultEither = await _authenticationRepo.checkActivationCodeRepo(newRequest: request, activationCode: activationCode.toString(), isRegister: true);
@ -423,7 +434,6 @@ class AuthenticationViewModel extends ChangeNotifier {
resultEither.fold((failure) async => await _errorHandlerService.handleError(failure: failure), (apiResponse) async {
final activation = CheckActivationCode.fromJson(apiResponse.data as Map<String, dynamic>);
if (activation.errorCode == '699') {
// Todo: Hide Loader
// GifLoaderDialogUtils.hideDialog(context);
@ -441,7 +451,6 @@ class AuthenticationViewModel extends ChangeNotifier {
// Navigator.popUntil(context, (route) => Utils.route(route, equalsTo: RegisterNew));
return;
} else {
if (activation.list != null && activation.list!.isNotEmpty) {
_appState.setAuthenticatedUser(activation.list!.first);
}
@ -451,8 +460,9 @@ class AuthenticationViewModel extends ChangeNotifier {
bool isUserAgreedBefore = await checkIfUserAgreedBefore(request: request);
//updating the last login type in app state to show the fingerprint/face id option on home screen
_appState.getSelectDeviceByImeiRespModelElement!.logInType = loginTypeEnum.toInt;
if (_appState.getSelectDeviceByImeiRespModelElement != null) {
_appState.getSelectDeviceByImeiRespModelElement!.logInType = loginTypeEnum.toInt;
}
LoaderBottomSheet.hideLoader();
insertPatientIMEIData(loginTypeEnum.toInt);
clearDefaultInputValues();
@ -548,12 +558,12 @@ class AuthenticationViewModel extends ChangeNotifier {
loginTypeEnum = (_appState.deviceTypeID == 1 ? LoginTypeEnum.face : LoginTypeEnum.fingerprint);
if (!_appState.isAuthenticated) {
//commenting this api to check either the same flow working or not because this api does not needed further if work fine we will remove this
// await getPatientDeviceData(loginTypeEnum.toInt);
// await getPatientDeviceData(loginTypeEnum.toInt);
await checkActivationCode(otpTypeEnum: OTPTypeEnum.faceIDFingerprint, activationCode: null, onWrongActivationCode: (String? message) {});
await insertPatientIMEIData(loginTypeEnum.toInt);
await insertPatientIMEIData(loginTypeEnum.toInt);
} else {
// authenticated = true;
await insertPatientIMEIData(loginTypeEnum.toInt);
await insertPatientIMEIData(loginTypeEnum.toInt);
}
LoaderBottomSheet.hideLoader();
notifyListeners();
@ -615,35 +625,63 @@ class AuthenticationViewModel extends ChangeNotifier {
}
Future<void> onRegistrationComplete() async {
// if (emailAddress.text.isEmpty) {
// Utils.showErrorToast(TranslationBase.of(context).enterEmailAddress);
// return;
// } else {
// Navigator.of(context).pop();
// registerNow();
// }
//authVM!.clearDefaultInputValues();
var request = RequestUtils.getUserSignupCompletionRequest(fullName: nameController.text, emailAddress: emailController.text, gender: genderType, maritalStatus: maritalStatus);
//
print("============= Final Payload ===============");
print(request);
print("=================== ====================");
final resultEither = await _authenticationRepo.registerUser(registrationPayloadDataModelRequest: request);
resultEither.fold((failure) async => await _errorHandlerService.handleError(failure: failure), (apiResponse) async {
if (apiResponse.data is String) {
//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 {
print(apiResponse.data as Map<String, dynamic>);
if (apiResponse.data["MessageStatus"] == 1) {
//TODO: Here We Need to Show a Dialog Of Something in the case of Success.
clearDefaultInputValues(); // This will Clear All Default Values Of User.
_navigationService.pushAndReplace(AppRoutes.loginScreen);
}
}
});
return;
}
Future<void> checkUserStatusForRegistration({required dynamic response, required dynamic request}) async {
if (response is Map) {
_appState.setAppAuthToken = response["LogInTokenID"];
if (response["MessageStatus"] == 2) {
print(response["ErrorEndUserMessage"]);
return;
}
if (response['hasFile'] == true) {
//TODO: Show Here Ok And Cancel Dialog and On OKPress it will go for sendActivationCode
_navigationService.context?.showBottomSheet(
child: ExceptionBottomSheet(
message: response["ErrorMessage"],
showCancel: true,
showOKButton: true,
onOkPressed: () {
_navigationService.popUntilNamed(AppRoutes.loginScreen);
},
onCancelPressed: () {
_navigationService.pop();
},
));
} else {
request['forRegister'] = true;
request['isRegister'] = true;
_appState.setAppAuthToken = response['LogInTokenID'];
if (isPatientOutsideSA(request: response)) {
print("=======OUT SA=======");
_appState.setAppAuthToken = response['LogInTokenID'];
sendActivationCode(
otpTypeEnum: OTPTypeEnumExtension.fromInt(request["OTP_SendType"]),
nationalIdOrFileNumber: request["PatientIdentificationID"].toString(),
phoneNumber: request["PatientMobileNumber"].toString());
otpTypeEnum: OTPTypeEnumExtension.fromInt(request["OTP_SendType"]),
nationalIdOrFileNumber: request["PatientIdentificationID"].toString(),
phoneNumber: request["PatientMobileNumber"].toString(),
payload: request,
isForRegister: true,
);
} else {
print("=======IN SA=======");
chekUserNHICData(request: request);
@ -657,13 +695,12 @@ class AuthenticationViewModel extends ChangeNotifier {
bool isPatientOutsideSA({required dynamic request}) {
bool isOutSideSa = false;
if (request is Map<String, dynamic> && request.containsKey("PatientOutSA")) {
if (request["PatientOutSA"] == true) {
isOutSideSa = true;
} else {
isOutSideSa = false;
}
if (request["PatientOutSA"] == true || request["PatientOutSA"] == 1) {
isOutSideSa = true;
} else {
isOutSideSa = false;
}
print(isOutSideSa);
return isOutSideSa;
}
@ -682,10 +719,12 @@ class AuthenticationViewModel extends ChangeNotifier {
if (apiResponse.data is Map) {
setNHICData(apiResponse.data, request);
sendActivationCode(
otpTypeEnum: OTPTypeEnumExtension.fromInt(request["OTP_SendType"]),
nationalIdOrFileNumber: request["PatientIdentificationID"].toString(),
phoneNumber: request["PatientMobileNumber"].toString(),
payload: request);
otpTypeEnum: OTPTypeEnumExtension.fromInt(request["OTP_SendType"]),
nationalIdOrFileNumber: request["PatientIdentificationID"].toString(),
phoneNumber: request["PatientMobileNumber"].toString(),
payload: request,
isForRegister: true,
);
}
});
@ -738,6 +777,7 @@ class AuthenticationViewModel extends ChangeNotifier {
deviceTypeId: _appState.getDeviceTypeID(),
patientId: _appState.getAuthenticatedUser()!.patientId!,
patientIdentificationNo: _appState.getAuthenticatedUser()!.patientIdentificationNo!,
identificationNo: _appState.getAuthenticatedUser()!.patientIdentificationNo!,
firstName: _appState.getAuthenticatedUser()!.firstName!,
lastName: _appState.getAuthenticatedUser()!.lastName!,
patientTypeId: _appState.getAuthenticatedUser()!.patientType,
@ -784,33 +824,30 @@ class AuthenticationViewModel extends ChangeNotifier {
log("Insert IMEI Failed");
}
});
}
Future<void> getPatientDeviceData(int loginType) async {
final resultEither = await _authenticationRepo.getPatientDeviceData(
patientDeviceDataRequest: GetUserMobileDeviceData(
deviceToken: _appState.deviceToken,
deviceTypeId: _appState.getDeviceTypeID(),
patientId: _appState.getSelectDeviceByImeiRespModelElement!.patientId!,
patientType: _appState.getSelectDeviceByImeiRespModelElement!.patientType,
patientOutSa:_appState.getSelectDeviceByImeiRespModelElement!.outSa == true ?1 :0,
loginType: loginType,
languageId: _appState.getLanguageID(),
latitude: _appState.userLat,
longitude: _appState.userLong,
mobileNo:_appState.getSelectDeviceByImeiRespModelElement!.mobile! ,
patientMobileNumber:int.parse(_appState.getSelectDeviceByImeiRespModelElement!.mobile!),
nationalId:_appState.getSelectDeviceByImeiRespModelElement!.identificationNo)
deviceToken: _appState.deviceToken,
deviceTypeId: _appState.getDeviceTypeID(),
patientId: _appState.getSelectDeviceByImeiRespModelElement!.patientId!,
patientType: _appState.getSelectDeviceByImeiRespModelElement!.patientType,
patientOutSa: _appState.getSelectDeviceByImeiRespModelElement!.outSa == true ? 1 : 0,
loginType: loginType,
languageId: _appState.getLanguageID(),
latitude: _appState.userLat,
longitude: _appState.userLong,
mobileNo: _appState.getSelectDeviceByImeiRespModelElement!.mobile!,
patientMobileNumber: int.parse(_appState.getSelectDeviceByImeiRespModelElement!.mobile!),
nationalId: _appState.getSelectDeviceByImeiRespModelElement!.identificationNo)
.toJson());
resultEither.fold((failure) async => await _errorHandlerService.handleError(failure: failure), (apiResponse) async {
if (apiResponse.messageStatus == 1) {
dynamic deviceInfo= apiResponse.data['List_MobileLoginInfo'];
getDeviceLastLogin = deviceInfo['LoginType'];
dynamic deviceInfo = apiResponse.data['List_MobileLoginInfo'];
getDeviceLastLogin = deviceInfo['LoginType'];
}
});
}
@override

@ -1,34 +1,170 @@
// import 'dart:convert';
//
// import 'package:hmg_patient_app_new/core/enums.dart';
//
// class RegistrationDataModelPayload {
// int? patientMobileNumber;
// String? zipCode;
// int? searchType;
// int? patientId;
// int? patientIdentificationId;
// int? otpSendType;
// int? patientOutSa;
// bool? isDentalAllowedBackend;
// String? dob;
// int? isHijri;
// bool? forRegister;
// bool? isRegister;
// String? healthId;
// String? emailAddress;
// String? nationalityCode;
// GenderTypeEnum? gender;
// MaritalStatusTypeEnum? maritalStatus;
// String? fullName;
//
// RegistrationDataModelPayload({
// this.patientMobileNumber,
// this.zipCode,
// this.searchType,
// this.patientId,
// this.patientIdentificationId,
// this.otpSendType,
// this.patientOutSa,
// this.isDentalAllowedBackend,
// this.dob,
// this.isHijri,
// this.forRegister,
// this.isRegister,
// this.healthId,
// this.emailAddress,
// this.nationalityCode,
// this.gender,
// this.maritalStatus,
// this.fullName,
// });
//
// factory RegistrationDataModelPayload.fromRawJson(String str) => RegistrationDataModelPayload.fromJson(json.decode(str));
//
// String toRawJson() => json.encode(toJson());
//
// factory RegistrationDataModelPayload.fromJson(Map<String, dynamic> json) => RegistrationDataModelPayload(
// patientMobileNumber: json["PatientMobileNumber"],
// zipCode: json["ZipCode"],
// searchType: json["SearchType"],
// patientId: json["PatientID"],
// patientIdentificationId: json["PatientIdentificationID"],
// otpSendType: json["OTP_SendType"],
// patientOutSa: json["PatientOutSA"],
// isDentalAllowedBackend: json["isDentalAllowedBackend"],
// dob: json["DOB"],
// isHijri: json["IsHijri"],
// forRegister: json["forRegister"],
// isRegister: json["isRegister"],
// healthId: json["healthId"],
// emailAddress: json["emailAddress"],
// nationalityCode: json["nationalityCode"],
// gender: json["gender"],
// maritalStatus: json["maritalStatus"],
// fullName: json["fullName"],
// );
//
// Map<String, dynamic> toJson() => {
// "PatientMobileNumber": patientMobileNumber,
// "ZipCode": zipCode,
// "SearchType": searchType,
// "PatientID": patientId,
// "PatientIdentificationID": patientIdentificationId,
// "OTP_SendType": otpSendType,
// "PatientOutSA": patientOutSa,
// "isDentalAllowedBackend": isDentalAllowedBackend,
// "DOB": dob,
// "IsHijri": isHijri,
// "forRegister": forRegister,
// "isRegister": isRegister,
// "healthId": healthId,
// "emailAddress": emailAddress,
// "nationalityCode": nationalityCode,
// "gender": gender,
// "maritalStatus": maritalStatus,
// "fullName": fullName,
// };
// }
import 'dart:convert';
import 'package:hmg_patient_app_new/core/enums.dart';
class RegistrationDataModelPayload {
int? patientMobileNumber;
String? mobileNo;
String? deviceToken;
bool? projectOutSa;
int? loginType;
String? zipCode;
bool? isRegister;
String? logInTokenId;
int? searchType;
int? patientId;
int? nationalId;
int? patientIdentificationId;
int? otpSendType;
String? languageId;
String? versionId;
String? channel;
String? ipAdress;
String? generalid;
int? patientOutSa;
bool? isDentalAllowedBackend;
String? deviceTypeId;
String? smsSignature;
String? dob;
int? isHijri;
bool? forRegister;
bool? isRegister;
String? patientType;
String? latitude;
String? longitude;
String? healthId;
String? emailAddress;
String? nationalityCode;
GenderTypeEnum? gender;
MaritalStatusTypeEnum? maritalStatus;
String? fullName;
RegistrationDataModelPayload({
this.patientMobileNumber,
this.mobileNo,
this.deviceToken,
this.projectOutSa,
this.loginType,
this.zipCode,
this.isRegister,
this.logInTokenId,
this.searchType,
this.patientId,
this.nationalId,
this.patientIdentificationId,
this.otpSendType,
this.languageId,
this.versionId,
this.channel,
this.ipAdress,
this.generalid,
this.patientOutSa,
this.isDentalAllowedBackend,
this.deviceTypeId,
this.smsSignature,
this.dob,
this.isHijri,
this.forRegister,
this.isRegister,
this.patientType,
this.latitude,
this.longitude,
this.healthId,
this.emailAddress,
this.nationalityCode,
this.gender,
this.maritalStatus,
this.fullName,
});
factory RegistrationDataModelPayload.fromRawJson(String str) => RegistrationDataModelPayload.fromJson(json.decode(str));
@ -36,34 +172,74 @@ class RegistrationDataModelPayload {
String toRawJson() => json.encode(toJson());
factory RegistrationDataModelPayload.fromJson(Map<String, dynamic> json) => RegistrationDataModelPayload(
patientMobileNumber: json["PatientMobileNumber"],
zipCode: json["ZipCode"],
searchType: json["SearchType"],
patientId: json["PatientID"],
patientIdentificationId: json["PatientIdentificationID"],
otpSendType: json["OTP_SendType"],
patientOutSa: json["PatientOutSA"],
isDentalAllowedBackend: json["isDentalAllowedBackend"],
dob: json["DOB"],
isHijri: json["IsHijri"],
forRegister: json["forRegister"],
isRegister: json["isRegister"],
healthId: json["healthId"],
);
patientMobileNumber: json["PatientMobileNumber"],
mobileNo: json["MobileNo"],
deviceToken: json["DeviceToken"],
projectOutSa: json["ProjectOutSA"],
loginType: json["LoginType"],
zipCode: json["ZipCode"],
isRegister: json["isRegister"],
logInTokenId: json["LogInTokenID"],
searchType: json["SearchType"],
patientId: json["PatientID"],
nationalId: json["NationalID"],
patientIdentificationId: json["PatientIdentificationID"],
otpSendType: json["OTP_SendType"],
languageId: json["LanguageID"],
versionId: json["VersionID"],
channel: json["Channel"],
ipAdress: json["IPAdress"],
generalid: json["generalid"],
patientOutSa: json["PatientOutSA"],
isDentalAllowedBackend: json["isDentalAllowedBackend"],
deviceTypeId: json["DeviceTypeID"],
smsSignature: json["SMSSignature"],
dob: json["DOB"],
isHijri: json["IsHijri"],
patientType: json["PatientType"],
latitude: json["Latitude"],
longitude: json["Longitude"],
healthId: json["healthId"],
emailAddress: json["emailAddress"],
nationalityCode: json["nationalityCode"],
gender: json["gender"],
maritalStatus: json["maritalStatus"],
fullName: json["fullName"],
);
Map<String, dynamic> toJson() => {
"PatientMobileNumber": patientMobileNumber,
"ZipCode": zipCode,
"SearchType": searchType,
"PatientID": patientId,
"PatientIdentificationID": patientIdentificationId,
"OTP_SendType": otpSendType,
"PatientOutSA": patientOutSa,
"isDentalAllowedBackend": isDentalAllowedBackend,
"DOB": dob,
"IsHijri": isHijri,
"forRegister": forRegister,
"isRegister": isRegister,
"healthId": healthId,
};
"PatientMobileNumber": patientMobileNumber,
"MobileNo": mobileNo,
"DeviceToken": deviceToken,
"ProjectOutSA": projectOutSa,
"LoginType": loginType,
"ZipCode": zipCode,
"isRegister": isRegister,
"LogInTokenID": logInTokenId,
"SearchType": searchType,
"PatientID": patientId,
"NationalID": nationalId,
"PatientIdentificationID": patientIdentificationId,
"OTP_SendType": otpSendType,
"LanguageID": languageId,
"VersionID": versionId,
"Channel": channel,
"IPAdress": ipAdress,
"generalid": generalid,
"PatientOutSA": patientOutSa,
"isDentalAllowedBackend": isDentalAllowedBackend,
"DeviceTypeID": deviceTypeId,
"SMSSignature": smsSignature,
"DOB": dob,
"IsHijri": isHijri,
"PatientType": patientType,
"Latitude": latitude,
"Longitude": longitude,
"healthId": healthId,
"emailAddress": emailAddress,
"nationalityCode": nationalityCode,
"gender": gender,
"maritalStatus": maritalStatus,
"fullName": fullName,
};
}

@ -0,0 +1,17 @@
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_repo.dart';
import 'package:hmg_patient_app_new/services/error_handler_service.dart';
class BookAppointmentsViewModel extends ChangeNotifier {
int selectedTabIndex = 0;
BookAppointmentsRepo bookAppointmentsRepo;
ErrorHandlerService errorHandlerService;
BookAppointmentsViewModel({required this.bookAppointmentsRepo, required this.errorHandlerService});
void onTabChanged(int index) {
selectedTabIndex = index;
notifyListeners();
}
}

@ -0,0 +1,58 @@
import 'package:dartz/dartz.dart';
import 'package:hmg_patient_app_new/core/api/api_client.dart';
import 'package:hmg_patient_app_new/core/api_consts.dart';
import 'package:hmg_patient_app_new/core/common_models/generic_api_model.dart';
import 'package:hmg_patient_app_new/core/exceptions/api_failure.dart';
import 'package:hmg_patient_app_new/services/logger_service.dart';
abstract class HabibWalletRepo {
Future<Either<Failure, GenericApiModel<dynamic>>> getPatientBalanceAmount();
}
class HabibWalletRepoImp implements HabibWalletRepo {
final ApiClient apiClient;
final LoggerService loggerService;
HabibWalletRepoImp({required this.loggerService, required this.apiClient});
@override
Future<Either<Failure, GenericApiModel<dynamic>>> getPatientBalanceAmount() async {
Map<String, dynamic> mapDevice = {};
try {
GenericApiModel<dynamic>? apiResponse;
Failure? failure;
await apiClient.post(
GET_PATIENT_AdVANCE_BALANCE_AMOUNT,
body: mapDevice,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
// final list = response['ListPLO'];
// if (list == null || list.isEmpty) {
// throw Exception("lab list is empty");
// }
// final labOrders = list.map((item) => PatientLabOrdersResponseModel.fromJson(item as Map<String, dynamic>)).toList().cast<PatientLabOrdersResponseModel>();
apiResponse = GenericApiModel<dynamic>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: null,
data: response["TotalAdvanceBalanceAmount"],
);
} catch (e) {
failure = DataParsingFailure(e.toString());
}
},
);
if (failure != null) return Left(failure!);
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
return Right(apiResponse!);
} catch (e) {
return Left(UnknownFailure(e.toString()));
}
}
}

@ -0,0 +1,39 @@
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/features/habib_wallet/habib_wallet_repo.dart';
import 'package:hmg_patient_app_new/services/error_handler_service.dart';
class HabibWalletViewModel extends ChangeNotifier {
bool isWalletAmountLoading = false;
num habibWalletAmount = 0;
HabibWalletRepo habibWalletRepo;
ErrorHandlerService errorHandlerService;
HabibWalletViewModel({required this.habibWalletRepo, required this.errorHandlerService});
initHabibWalletProvider() {
isWalletAmountLoading = true;
habibWalletAmount = 0;
notifyListeners();
}
Future<void> getPatientBalanceAmount({Function(dynamic)? onSuccess, Function(String)? onError}) async {
final result = await habibWalletRepo.getPatientBalanceAmount();
result.fold(
(failure) async => await errorHandlerService.handleError(failure: failure),
(apiResponse) {
if (apiResponse.messageStatus == 2) {
// dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
} else if (apiResponse.messageStatus == 1) {
habibWalletAmount = apiResponse.data!;
isWalletAmountLoading = false;
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse);
}
}
},
);
}
}

@ -7,7 +7,7 @@ import 'package:hmg_patient_app_new/features/lab/models/resp_models/patient_lab_
import 'package:hmg_patient_app_new/services/logger_service.dart';
abstract class LabRepo {
Future<Either<Failure, GenericApiModel<List<PatientLabOrdersResponseModel>>>> getPatientLabOrders({required String patientId});
Future<Either<Failure, GenericApiModel<List<PatientLabOrdersResponseModel>>>> getPatientLabOrders();
}
class LabRepoImp implements LabRepo {
@ -17,7 +17,7 @@ class LabRepoImp implements LabRepo {
LabRepoImp({required this.loggerService, required this.apiClient});
@override
Future<Either<Failure, GenericApiModel<List<PatientLabOrdersResponseModel>>>> getPatientLabOrders({required String patientId}) async {
Future<Either<Failure, GenericApiModel<List<PatientLabOrdersResponseModel>>>> getPatientLabOrders() async {
Map<String, dynamic> mapDevice = {};
try {

@ -29,7 +29,7 @@ class LabViewModel extends ChangeNotifier {
}
Future<void> getPatientLabOrders({Function(dynamic)? onSuccess, Function(String)? onError}) async {
final result = await labRepo.getPatientLabOrders(patientId: "1231755");
final result = await labRepo.getPatientLabOrders();
result.fold(
(failure) async => await errorHandlerService.handleError(failure: failure),

@ -0,0 +1,270 @@
import 'package:dartz/dartz.dart';
import 'package:hmg_patient_app_new/core/api/api_client.dart';
import 'package:hmg_patient_app_new/core/api_consts.dart';
import 'package:hmg_patient_app_new/core/common_models/generic_api_model.dart';
import 'package:hmg_patient_app_new/core/exceptions/api_failure.dart';
import 'package:hmg_patient_app_new/core/utils/date_util.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/features/medical_file/models/patient_medical_response_model.dart';
import 'package:hmg_patient_app_new/features/medical_file/models/patient_sickleave_response_model.dart';
import 'package:hmg_patient_app_new/features/medical_file/models/patient_vaccine_response_model.dart';
import 'package:hmg_patient_app_new/services/logger_service.dart';
import '../authentication/models/resp_models/authenticated_user_resp_model.dart';
abstract class MedicalFileRepo {
Future<Either<Failure, GenericApiModel<List<PatientVaccineResponseModel>>>> getPatientVaccinesList();
Future<Either<Failure, GenericApiModel<List<PatientSickLeavesResponseModel>>>> getPatientSickLeavesList();
Future<Either<Failure, GenericApiModel<dynamic>>> getPatientSickLeavePDF(PatientSickLeavesResponseModel patientSickLeavesResponseModel, AuthenticatedUser authenticatedUser);
Future<Either<Failure, GenericApiModel<List<PatientMedicalReportResponseModel>>>> getPatientMedicalReportsList();
Future<Either<Failure, GenericApiModel<dynamic>>> getPatientMedicalReportPDF(PatientMedicalReportResponseModel patientMedicalReportResponseModel, AuthenticatedUser authenticatedUser);
}
class MedicalFileRepoImp implements MedicalFileRepo {
final ApiClient apiClient;
final LoggerService loggerService;
MedicalFileRepoImp({required this.loggerService, required this.apiClient});
@override
Future<Either<Failure, GenericApiModel<List<PatientVaccineResponseModel>>>> getPatientVaccinesList() async {
Map<String, dynamic> mapDevice = {"To": "0", "From": "0"};
try {
GenericApiModel<List<PatientVaccineResponseModel>>? apiResponse;
Failure? failure;
await apiClient.post(
GET_VACCINES,
body: mapDevice,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
final list = response['List_DoneVaccines'];
// if (list == null || list.isEmpty) {
// throw Exception("lab list is empty");
// }
final vaccinesList = list.map((item) => PatientVaccineResponseModel.fromJson(item as Map<String, dynamic>)).toList().cast<PatientVaccineResponseModel>();
apiResponse = GenericApiModel<List<PatientVaccineResponseModel>>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: null,
data: vaccinesList,
);
} catch (e) {
failure = DataParsingFailure(e.toString());
}
},
);
if (failure != null) return Left(failure!);
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
return Right(apiResponse!);
} catch (e) {
return Left(UnknownFailure(e.toString()));
}
}
@override
Future<Either<Failure, GenericApiModel<List<PatientSickLeavesResponseModel>>>> getPatientSickLeavesList() async {
Map<String, dynamic> mapDevice = {};
try {
GenericApiModel<List<PatientSickLeavesResponseModel>>? apiResponse;
Failure? failure;
await apiClient.post(
GET_PATIENT_SICK_LEAVE_STATUS,
body: mapDevice,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
final list = response['List_SickLeave'];
// if (list == null || list.isEmpty) {
// throw Exception("lab list is empty");
// }
final vaccinesList = list.map((item) => PatientSickLeavesResponseModel.fromJson(item as Map<String, dynamic>)).toList().cast<PatientSickLeavesResponseModel>();
apiResponse = GenericApiModel<List<PatientSickLeavesResponseModel>>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: null,
data: vaccinesList,
);
} catch (e) {
failure = DataParsingFailure(e.toString());
}
},
);
if (failure != null) return Left(failure!);
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
return Right(apiResponse!);
} catch (e) {
return Left(UnknownFailure(e.toString()));
}
}
@override
Future<Either<Failure, GenericApiModel>> getPatientSickLeavePDF(PatientSickLeavesResponseModel patientSickLeavesResponseModel, AuthenticatedUser authenticatedUser) async {
Map<String, dynamic> mapDevice = {
"RequestNo": patientSickLeavesResponseModel.requestNo,
"To": authenticatedUser.emailAddress,
"DateofBirth": authenticatedUser.dateofBirth,
"PatientIditificationNum": authenticatedUser.patientIdentificationNo,
"PatientMobileNumber": authenticatedUser.mobileNumber,
"PatientName": "${authenticatedUser.firstName!} ${authenticatedUser.lastName!}",
"ProjectName": patientSickLeavesResponseModel.projectName,
"DoctorName": patientSickLeavesResponseModel.doctorName,
"ProjectID": patientSickLeavesResponseModel.projectID,
"SetupID": patientSickLeavesResponseModel.setupID,
"IsDownload": true,
};
try {
GenericApiModel<dynamic>? apiResponse;
Failure? failure;
await apiClient.post(
SendSickLeaveEmail,
body: mapDevice,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
// final list = response['List_SickLeave'];
// if (list == null || list.isEmpty) {
// throw Exception("lab list is empty");
// }
// final vaccinesList = list.map((item) => PatientSickLeavesResponseModel.fromJson(item as Map<String, dynamic>)).toList().cast<PatientSickLeavesResponseModel>();
apiResponse = GenericApiModel<dynamic>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: null,
data: response["Base64Data"],
);
} catch (e) {
failure = DataParsingFailure(e.toString());
}
},
);
if (failure != null) return Left(failure!);
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
return Right(apiResponse!);
} catch (e) {
return Left(UnknownFailure(e.toString()));
}
}
@override
Future<Either<Failure, GenericApiModel<List<PatientMedicalReportResponseModel>>>> getPatientMedicalReportsList() async {
Map<String, dynamic> mapDevice = {
"IsReport": true,
"EncounterType": 1,
"RequestType": 1,
};
try {
GenericApiModel<List<PatientMedicalReportResponseModel>>? apiResponse;
Failure? failure;
await apiClient.post(
REPORTS,
body: mapDevice,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
final list = response['GetPatientMedicalStatus'];
// if (list == null || list.isEmpty) {
// throw Exception("lab list is empty");
// }
final vaccinesList = list.map((item) => PatientMedicalReportResponseModel.fromJson(item as Map<String, dynamic>)).toList().cast<PatientMedicalReportResponseModel>();
apiResponse = GenericApiModel<List<PatientMedicalReportResponseModel>>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: null,
data: vaccinesList,
);
} catch (e) {
failure = DataParsingFailure(e.toString());
}
},
);
if (failure != null) return Left(failure!);
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
return Right(apiResponse!);
} catch (e) {
return Left(UnknownFailure(e.toString()));
}
}
@override
Future<Either<Failure, GenericApiModel>> getPatientMedicalReportPDF(PatientMedicalReportResponseModel patientMedicalReportResponseModel, AuthenticatedUser authenticatedUser) async {
Map<String, dynamic> mapDevice = {
"SetupID": patientMedicalReportResponseModel.setupId,
"PrintDate": patientMedicalReportResponseModel.requestDate!,
"ProcedureID": "05005009",
"Reporttype": "MEDICAL REPORT",
"stamp": patientMedicalReportResponseModel.requestDate!,
"To": authenticatedUser.emailAddress,
"DateofBirth": authenticatedUser.dateofBirth,
"PatientIditificationNum": authenticatedUser.patientIdentificationNo,
"PatientMobileNumber": authenticatedUser.mobileNumber,
"PatientName": "${authenticatedUser.firstName!} ${authenticatedUser.lastName!}",
"ProjectName": patientMedicalReportResponseModel.projectName,
"ClinicName": patientMedicalReportResponseModel.clinicDescription,
"ProjectID": patientMedicalReportResponseModel.projectID,
"InvoiceNo": Utils.isVidaPlusProject(patientMedicalReportResponseModel.projectID!) ? patientMedicalReportResponseModel.invoiceNoVP : patientMedicalReportResponseModel.invoiceNo,
"InvoiceNo_VP": Utils.isVidaPlusProject(patientMedicalReportResponseModel.projectID!) ? patientMedicalReportResponseModel.invoiceNoVP : patientMedicalReportResponseModel.invoiceNo,
"PrintedByName": "${authenticatedUser.firstName!} ${authenticatedUser.lastName!}",
};
try {
GenericApiModel<dynamic>? apiResponse;
Failure? failure;
await apiClient.post(
GET_MEDICAL_REPORT_PDF,
body: mapDevice,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
// final list = response['List_SickLeave'];
// if (list == null || list.isEmpty) {
// throw Exception("lab list is empty");
// }
// final vaccinesList = list.map((item) => PatientSickLeavesResponseModel.fromJson(item as Map<String, dynamic>)).toList().cast<PatientSickLeavesResponseModel>();
apiResponse = GenericApiModel<dynamic>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: null,
data: response["MedicalReportBase64"],
);
} 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,12 +1,220 @@
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/features/authentication/models/resp_models/authenticated_user_resp_model.dart';
import 'package:hmg_patient_app_new/features/medical_file/medical_file_repo.dart';
import 'package:hmg_patient_app_new/features/medical_file/models/patient_medical_response_model.dart';
import 'package:hmg_patient_app_new/features/medical_file/models/patient_sickleave_response_model.dart';
import 'package:hmg_patient_app_new/features/medical_file/models/patient_vaccine_response_model.dart';
import 'package:hmg_patient_app_new/services/error_handler_service.dart';
class MedicalFileViewModel extends ChangeNotifier {
int selectedTabIndex = 0;
bool isPatientVaccineListLoading = false;
bool isPatientSickLeaveListLoading = false;
bool isPatientSickLeavePDFLoading = false;
bool isPatientMedicalReportsListLoading = false;
MedicalFileRepo medicalFileRepo;
ErrorHandlerService errorHandlerService;
List<PatientVaccineResponseModel> patientVaccineList = [];
List<PatientSickLeavesResponseModel> patientSickLeaveList = [];
List<PatientMedicalReportResponseModel> patientMedicalReportList = [];
List<PatientMedicalReportResponseModel> patientMedicalReportRequestedList = [];
List<PatientMedicalReportResponseModel> patientMedicalReportReadyList = [];
List<PatientMedicalReportResponseModel> patientMedicalReportCancelledList = [];
String patientSickLeavePDFBase64 = "";
String patientMedicalReportPDFBase64 = "";
int selectedMedicalReportsTabIndex = 0;
MedicalFileViewModel({required this.medicalFileRepo, required this.errorHandlerService});
initMedicalFileProvider() {
isPatientVaccineListLoading = true;
isPatientMedicalReportsListLoading = true;
notifyListeners();
}
void onMedicalReportTabChange(int index) {
selectedMedicalReportsTabIndex = index;
if (index == 0) {
patientMedicalReportList = patientMedicalReportRequestedList;
} else if (index == 1) {
patientMedicalReportList = patientMedicalReportReadyList;
} else if (index == 2) {
patientMedicalReportList = patientMedicalReportCancelledList;
}
notifyListeners();
}
setIsPatientVaccineListLoading(bool isLoading) {
isPatientVaccineListLoading = isLoading;
notifyListeners();
}
setIsPatientSickLeavePDFLoading(bool isLoading) {
isPatientSickLeavePDFLoading = isLoading;
notifyListeners();
}
setIsPatientSickLeaveListLoading(bool val) {
if (val) {
patientSickLeaveList.clear();
patientSickLeavePDFBase64 = "";
}
isPatientSickLeaveListLoading = val;
notifyListeners();
}
setIsPatientMedicalReportsLoading(bool val) {
if (val) {
patientMedicalReportList.clear();
patientMedicalReportPDFBase64 = "";
}
isPatientMedicalReportsListLoading = val;
notifyListeners();
}
void onTabChanged(int index) {
selectedTabIndex = index;
notifyListeners();
}
Future<void> getPatientVaccinesList({Function(dynamic)? onSuccess, Function(String)? onError}) async {
patientVaccineList.clear();
final result = await medicalFileRepo.getPatientVaccinesList();
result.fold(
(failure) async => await errorHandlerService.handleError(
failure: failure,
onOkPressed: () {
onError!(failure.message);
},
),
(apiResponse) {
if (apiResponse.messageStatus == 2) {
// dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
} else if (apiResponse.messageStatus == 1) {
patientVaccineList = apiResponse.data!;
isPatientVaccineListLoading = false;
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse);
}
}
},
);
}
Future<void> getPatientSickLeaveList({Function(dynamic)? onSuccess, Function(String)? onError}) async {
patientSickLeaveList.clear();
final result = await medicalFileRepo.getPatientSickLeavesList();
result.fold(
(failure) async => await errorHandlerService.handleError(
failure: failure,
onOkPressed: () {
onError!(failure.message);
},
),
(apiResponse) {
if (apiResponse.messageStatus == 2) {
// dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
} else if (apiResponse.messageStatus == 1) {
patientSickLeaveList = apiResponse.data!;
isPatientSickLeaveListLoading = false;
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse);
}
}
},
);
}
Future<void> getPatientSickLeavePDF(PatientSickLeavesResponseModel patientSickLeavesResponseModel, AuthenticatedUser authenticatedUser,
{Function(dynamic)? onSuccess, Function(String)? onError}) async {
final result = await medicalFileRepo.getPatientSickLeavePDF(patientSickLeavesResponseModel, authenticatedUser);
result.fold(
(failure) async => await errorHandlerService.handleError(
failure: failure,
onOkPressed: () {
onError!(failure.message);
},
),
(apiResponse) {
if (apiResponse.messageStatus == 2) {
// dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
} else if (apiResponse.messageStatus == 1) {
patientSickLeavePDFBase64 = apiResponse.data!;
isPatientSickLeaveListLoading = false;
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse);
}
}
},
);
}
Future<void> getPatientMedicalReportList({Function(dynamic)? onSuccess, Function(String)? onError}) async {
patientMedicalReportList.clear();
final result = await medicalFileRepo.getPatientMedicalReportsList();
result.fold(
(failure) async => await errorHandlerService.handleError(
failure: failure,
onOkPressed: () {
onError!(failure.message);
},
),
(apiResponse) {
if (apiResponse.messageStatus == 2) {
// dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
} else if (apiResponse.messageStatus == 1) {
patientMedicalReportList = apiResponse.data!;
if (patientMedicalReportList.isNotEmpty) {
patientMedicalReportRequestedList = patientMedicalReportList.where((element) => element.status == 1).toList();
patientMedicalReportReadyList = patientMedicalReportList.where((element) => element.status == 2).toList();
patientMedicalReportCancelledList = patientMedicalReportList.where((element) => element.status == 4).toList();
}
onMedicalReportTabChange(0);
isPatientMedicalReportsListLoading = false;
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse);
}
}
},
);
}
Future<void> getPatientMedicalReportPDF(PatientMedicalReportResponseModel patientMedicalReportResponseModel, AuthenticatedUser authenticatedUser,
{Function(dynamic)? onSuccess, Function(String)? onError}) async {
final result = await medicalFileRepo.getPatientMedicalReportPDF(patientMedicalReportResponseModel, authenticatedUser);
result.fold(
(failure) async => await errorHandlerService.handleError(
failure: failure,
onOkPressed: () {
onError!(failure.message);
},
),
(apiResponse) {
if (apiResponse.messageStatus == 2) {
// dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
} else if (apiResponse.messageStatus == 1) {
patientMedicalReportPDFBase64 = apiResponse.data!;
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse);
}
}
},
);
}
}

@ -0,0 +1,192 @@
class PatientMedicalReportResponseModel {
int? status;
String? encounterDate;
int? projectID;
int? invoiceNo;
int? encounterNo;
String? procedureId;
int? requestType;
String? setupId;
int? patientID;
int? doctorID;
int? clinicID;
String? requestDate;
bool? isRead;
dynamic isReadOn;
num? actualDoctorRate;
String? admissionDate;
int? admissionNumber;
String? appointmentDate;
int? appointmentNO;
String? appointmentTime;
String? clinicDescription;
dynamic clinicDescriptionN;
num? decimalDoctorRate;
String? docName;
dynamic docNameN;
String? doctorImageURL;
String? doctorName;
dynamic doctorNameN;
num? doctorRate;
num? doctorStarsRate;
int? invoiceNoVP;
dynamic invoiceType;
bool? isDoctorAllowVedioCall;
bool? isExecludeDoctor;
bool? isInOutPatient;
String? isInOutPatientDescription;
String? isInOutPatientDescriptionN;
int? noOfPatientsRate;
String? projectName;
dynamic projectNameN;
int? sourceID;
dynamic sourceName;
dynamic sourceNameN;
String? statusDesc;
dynamic strAppointmentDate;
PatientMedicalReportResponseModel(
{this.status,
this.encounterDate,
this.projectID,
this.invoiceNo,
this.encounterNo,
this.procedureId,
this.requestType,
this.setupId,
this.patientID,
this.doctorID,
this.clinicID,
this.requestDate,
this.isRead,
this.isReadOn,
this.actualDoctorRate,
this.admissionDate,
this.admissionNumber,
this.appointmentDate,
this.appointmentNO,
this.appointmentTime,
this.clinicDescription,
this.clinicDescriptionN,
this.decimalDoctorRate,
this.docName,
this.docNameN,
this.doctorImageURL,
this.doctorName,
this.doctorNameN,
this.doctorRate,
this.doctorStarsRate,
this.invoiceNoVP,
this.invoiceType,
this.isDoctorAllowVedioCall,
this.isExecludeDoctor,
this.isInOutPatient,
this.isInOutPatientDescription,
this.isInOutPatientDescriptionN,
this.noOfPatientsRate,
this.projectName,
this.projectNameN,
this.sourceID,
this.sourceName,
this.sourceNameN,
this.statusDesc,
this.strAppointmentDate});
PatientMedicalReportResponseModel.fromJson(Map<String, dynamic> json) {
status = json['Status'];
encounterDate = json['EncounterDate'];
projectID = json['ProjectID'];
invoiceNo = json['InvoiceNo'];
encounterNo = json['EncounterNo'];
procedureId = json['ProcedureId'];
requestType = json['RequestType'];
setupId = json['SetupId'];
patientID = json['PatientID'];
doctorID = json['DoctorID'];
clinicID = json['ClinicID'];
requestDate = json['RequestDate'];
isRead = json['IsRead'];
isReadOn = json['IsReadOn'];
actualDoctorRate = json['ActualDoctorRate'];
admissionDate = json['AdmissionDate'];
admissionNumber = json['AdmissionNumber'];
appointmentDate = json['AppointmentDate'];
appointmentNO = json['AppointmentNO'];
appointmentTime = json['AppointmentTime'];
clinicDescription = json['ClinicDescription'];
clinicDescriptionN = json['ClinicDescriptionN'];
decimalDoctorRate = json['DecimalDoctorRate'];
docName = json['DocName'];
docNameN = json['DocNameN'];
doctorImageURL = json['DoctorImageURL'];
doctorName = json['DoctorName'];
doctorNameN = json['DoctorNameN'];
doctorRate = json['DoctorRate'];
doctorStarsRate = json['DoctorStarsRate'];
invoiceNoVP = json['InvoiceNo_VP'];
invoiceType = json['InvoiceType'];
isDoctorAllowVedioCall = json['IsDoctorAllowVedioCall'];
isExecludeDoctor = json['IsExecludeDoctor'];
isInOutPatient = json['IsInOutPatient'];
isInOutPatientDescription = json['IsInOutPatientDescription'];
isInOutPatientDescriptionN = json['IsInOutPatientDescriptionN'];
noOfPatientsRate = json['NoOfPatientsRate'];
projectName = json['ProjectName'];
projectNameN = json['ProjectNameN'];
sourceID = json['SourceID'];
sourceName = json['SourceName'];
sourceNameN = json['SourceNameN'];
statusDesc = json['StatusDesc'];
strAppointmentDate = json['StrAppointmentDate'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['Status'] = this.status;
data['EncounterDate'] = this.encounterDate;
data['ProjectID'] = this.projectID;
data['InvoiceNo'] = this.invoiceNo;
data['EncounterNo'] = this.encounterNo;
data['ProcedureId'] = this.procedureId;
data['RequestType'] = this.requestType;
data['SetupId'] = this.setupId;
data['PatientID'] = this.patientID;
data['DoctorID'] = this.doctorID;
data['ClinicID'] = this.clinicID;
data['RequestDate'] = this.requestDate;
data['IsRead'] = this.isRead;
data['IsReadOn'] = this.isReadOn;
data['ActualDoctorRate'] = this.actualDoctorRate;
data['AdmissionDate'] = this.admissionDate;
data['AdmissionNumber'] = this.admissionNumber;
data['AppointmentDate'] = this.appointmentDate;
data['AppointmentNO'] = this.appointmentNO;
data['AppointmentTime'] = this.appointmentTime;
data['ClinicDescription'] = this.clinicDescription;
data['ClinicDescriptionN'] = this.clinicDescriptionN;
data['DecimalDoctorRate'] = this.decimalDoctorRate;
data['DocName'] = this.docName;
data['DocNameN'] = this.docNameN;
data['DoctorImageURL'] = this.doctorImageURL;
data['DoctorName'] = this.doctorName;
data['DoctorNameN'] = this.doctorNameN;
data['DoctorRate'] = this.doctorRate;
data['DoctorStarsRate'] = this.doctorStarsRate;
data['InvoiceNo_VP'] = this.invoiceNoVP;
data['InvoiceType'] = this.invoiceType;
data['IsDoctorAllowVedioCall'] = this.isDoctorAllowVedioCall;
data['IsExecludeDoctor'] = this.isExecludeDoctor;
data['IsInOutPatient'] = this.isInOutPatient;
data['IsInOutPatientDescription'] = this.isInOutPatientDescription;
data['IsInOutPatientDescriptionN'] = this.isInOutPatientDescriptionN;
data['NoOfPatientsRate'] = this.noOfPatientsRate;
data['ProjectName'] = this.projectName;
data['ProjectNameN'] = this.projectNameN;
data['SourceID'] = this.sourceID;
data['SourceName'] = this.sourceName;
data['SourceNameN'] = this.sourceNameN;
data['StatusDesc'] = this.statusDesc;
data['StrAppointmentDate'] = this.strAppointmentDate;
return data;
}
}

@ -0,0 +1,176 @@
class PatientSickLeavesResponseModel {
String? setupID;
int? projectID;
int? patientID;
int? patientType;
int? clinicID;
int? doctorID;
int? requestNo;
String? requestDate;
int? sickLeaveDays;
int? appointmentNo;
int? admissionNo;
dynamic reportDate;
num? actualDoctorRate;
String? appointmentDate;
String? clinicName;
double? decimalDoctorRate;
String? doctorImageURL;
String? doctorName;
num? doctorRate;
num? doctorStarsRate;
String? doctorTitle;
int? employeeID;
String? endDate;
int? gender;
String? genderDescription;
bool? isActiveDoctorProfile;
bool? isDoctorAllowVedioCall;
bool? isExecludeDoctor;
bool? isInOutPatient;
String? isInOutPatientDescription;
String? isInOutPatientDescriptionN;
bool? isLiveCareAppointment;
dynamic medicalDirectorApprovedStatus;
int? noOfPatientsRate;
dynamic patientName;
String? projectName;
String? qR;
List<String>? speciality;
String? startDate;
int? status;
String? strRequestDate;
PatientSickLeavesResponseModel(
{this.setupID,
this.projectID,
this.patientID,
this.patientType,
this.clinicID,
this.doctorID,
this.requestNo,
this.requestDate,
this.sickLeaveDays,
this.appointmentNo,
this.admissionNo,
this.reportDate,
this.actualDoctorRate,
this.appointmentDate,
this.clinicName,
this.decimalDoctorRate,
this.doctorImageURL,
this.doctorName,
this.doctorRate,
this.doctorStarsRate,
this.doctorTitle,
this.employeeID,
this.endDate,
this.gender,
this.genderDescription,
this.isActiveDoctorProfile,
this.isDoctorAllowVedioCall,
this.isExecludeDoctor,
this.isInOutPatient,
this.isInOutPatientDescription,
this.isInOutPatientDescriptionN,
this.isLiveCareAppointment,
this.medicalDirectorApprovedStatus,
this.noOfPatientsRate,
this.patientName,
this.projectName,
this.qR,
this.speciality,
this.startDate,
this.status,
this.strRequestDate});
PatientSickLeavesResponseModel.fromJson(Map<String, dynamic> json) {
setupID = json['SetupID'];
projectID = json['ProjectID'];
patientID = json['PatientID'];
patientType = json['PatientType'];
clinicID = json['ClinicID'];
doctorID = json['DoctorID'];
requestNo = json['RequestNo'];
requestDate = json['RequestDate'];
sickLeaveDays = json['SickLeaveDays'];
appointmentNo = json['AppointmentNo'];
admissionNo = json['AdmissionNo'];
reportDate = json['ReportDate'];
actualDoctorRate = json['ActualDoctorRate'];
appointmentDate = json['AppointmentDate'];
clinicName = json['ClinicName'];
decimalDoctorRate = json['DecimalDoctorRate'];
doctorImageURL = json['DoctorImageURL'];
doctorName = json['DoctorName'];
doctorRate = json['DoctorRate'];
doctorStarsRate = json['DoctorStarsRate'];
doctorTitle = json['DoctorTitle'];
employeeID = json['EmployeeID'];
endDate = json['EndDate'];
gender = json['Gender'];
genderDescription = json['GenderDescription'];
isActiveDoctorProfile = json['IsActiveDoctorProfile'];
isDoctorAllowVedioCall = json['IsDoctorAllowVedioCall'];
isExecludeDoctor = json['IsExecludeDoctor'];
isInOutPatient = json['IsInOutPatient'];
isInOutPatientDescription = json['IsInOutPatientDescription'];
isInOutPatientDescriptionN = json['IsInOutPatientDescriptionN'];
isLiveCareAppointment = json['IsLiveCareAppointment'];
medicalDirectorApprovedStatus = json['MedicalDirectorApprovedStatus'];
noOfPatientsRate = json['NoOfPatientsRate'];
patientName = json['PatientName'];
projectName = json['ProjectName'];
qR = json['QR'];
speciality = json['Speciality'].cast<String>();
startDate = json['StartDate'];
status = json['Status'];
strRequestDate = json['StrRequestDate'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['SetupID'] = this.setupID;
data['ProjectID'] = this.projectID;
data['PatientID'] = this.patientID;
data['PatientType'] = this.patientType;
data['ClinicID'] = this.clinicID;
data['DoctorID'] = this.doctorID;
data['RequestNo'] = this.requestNo;
data['RequestDate'] = this.requestDate;
data['SickLeaveDays'] = this.sickLeaveDays;
data['AppointmentNo'] = this.appointmentNo;
data['AdmissionNo'] = this.admissionNo;
data['ReportDate'] = this.reportDate;
data['ActualDoctorRate'] = this.actualDoctorRate;
data['AppointmentDate'] = this.appointmentDate;
data['ClinicName'] = this.clinicName;
data['DecimalDoctorRate'] = this.decimalDoctorRate;
data['DoctorImageURL'] = this.doctorImageURL;
data['DoctorName'] = this.doctorName;
data['DoctorRate'] = this.doctorRate;
data['DoctorStarsRate'] = this.doctorStarsRate;
data['DoctorTitle'] = this.doctorTitle;
data['EmployeeID'] = this.employeeID;
data['EndDate'] = this.endDate;
data['Gender'] = this.gender;
data['GenderDescription'] = this.genderDescription;
data['IsActiveDoctorProfile'] = this.isActiveDoctorProfile;
data['IsDoctorAllowVedioCall'] = this.isDoctorAllowVedioCall;
data['IsExecludeDoctor'] = this.isExecludeDoctor;
data['IsInOutPatient'] = this.isInOutPatient;
data['IsInOutPatientDescription'] = this.isInOutPatientDescription;
data['IsInOutPatientDescriptionN'] = this.isInOutPatientDescriptionN;
data['IsLiveCareAppointment'] = this.isLiveCareAppointment;
data['MedicalDirectorApprovedStatus'] = this.medicalDirectorApprovedStatus;
data['NoOfPatientsRate'] = this.noOfPatientsRate;
data['PatientName'] = this.patientName;
data['ProjectName'] = this.projectName;
data['QR'] = this.qR;
data['Speciality'] = this.speciality;
data['StartDate'] = this.startDate;
data['Status'] = this.status;
data['StrRequestDate'] = this.strRequestDate;
return data;
}
}

@ -0,0 +1,160 @@
class PatientVaccineResponseModel {
String? setupID;
int? projectID;
int? patientID;
int? invoiceNo;
String? procedureID;
String? vaccineName;
Null? vaccineNameN;
String? invoiceDate;
int? doctorID;
int? clinicID;
String? firstName;
String? middleName;
String? lastName;
Null? firstNameN;
Null? middleNameN;
Null? lastNameN;
String? dateofBirth;
int? actualDoctorRate;
String? age;
String? clinicName;
Null? decimalDoctorRate;
Null? doctorImageURL;
String? doctorName;
int? doctorRate;
int? doctorStarsRate;
String? doctorTitle;
int? gender;
Null? genderDescription;
Null? invoiceNoVP;
bool? isActiveDoctorProfile;
bool? isDoctorAllowVedioCall;
bool? isExecludeDoctor;
int? noOfPatientsRate;
String? patientName;
String? projectName;
String? qR;
String? vaccinationDate;
PatientVaccineResponseModel(
{this.setupID,
this.projectID,
this.patientID,
this.invoiceNo,
this.procedureID,
this.vaccineName,
this.vaccineNameN,
this.invoiceDate,
this.doctorID,
this.clinicID,
this.firstName,
this.middleName,
this.lastName,
this.firstNameN,
this.middleNameN,
this.lastNameN,
this.dateofBirth,
this.actualDoctorRate,
this.age,
this.clinicName,
this.decimalDoctorRate,
this.doctorImageURL,
this.doctorName,
this.doctorRate,
this.doctorStarsRate,
this.doctorTitle,
this.gender,
this.genderDescription,
this.invoiceNoVP,
this.isActiveDoctorProfile,
this.isDoctorAllowVedioCall,
this.isExecludeDoctor,
this.noOfPatientsRate,
this.patientName,
this.projectName,
this.qR,
this.vaccinationDate});
PatientVaccineResponseModel.fromJson(Map<String, dynamic> json) {
setupID = json['SetupID'];
projectID = json['ProjectID'];
patientID = json['PatientID'];
invoiceNo = json['InvoiceNo'];
procedureID = json['ProcedureID'];
vaccineName = json['VaccineName'];
vaccineNameN = json['VaccineNameN'];
invoiceDate = json['InvoiceDate'];
doctorID = json['DoctorID'];
clinicID = json['ClinicID'];
firstName = json['FirstName'];
middleName = json['MiddleName'];
lastName = json['LastName'];
firstNameN = json['FirstNameN'];
middleNameN = json['MiddleNameN'];
lastNameN = json['LastNameN'];
dateofBirth = json['DateofBirth'];
actualDoctorRate = json['ActualDoctorRate'];
age = json['Age'];
clinicName = json['ClinicName'];
decimalDoctorRate = json['DecimalDoctorRate'];
doctorImageURL = json['DoctorImageURL'];
doctorName = json['DoctorName'];
doctorRate = json['DoctorRate'];
doctorStarsRate = json['DoctorStarsRate'];
doctorTitle = json['DoctorTitle'];
gender = json['Gender'];
genderDescription = json['GenderDescription'];
invoiceNoVP = json['InvoiceNo_VP'];
isActiveDoctorProfile = json['IsActiveDoctorProfile'];
isDoctorAllowVedioCall = json['IsDoctorAllowVedioCall'];
isExecludeDoctor = json['IsExecludeDoctor'];
noOfPatientsRate = json['NoOfPatientsRate'];
patientName = json['PatientName'];
projectName = json['ProjectName'];
qR = json['QR'];
vaccinationDate = json['VaccinationDate'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['SetupID'] = this.setupID;
data['ProjectID'] = this.projectID;
data['PatientID'] = this.patientID;
data['InvoiceNo'] = this.invoiceNo;
data['ProcedureID'] = this.procedureID;
data['VaccineName'] = this.vaccineName;
data['VaccineNameN'] = this.vaccineNameN;
data['InvoiceDate'] = this.invoiceDate;
data['DoctorID'] = this.doctorID;
data['ClinicID'] = this.clinicID;
data['FirstName'] = this.firstName;
data['MiddleName'] = this.middleName;
data['LastName'] = this.lastName;
data['FirstNameN'] = this.firstNameN;
data['MiddleNameN'] = this.middleNameN;
data['LastNameN'] = this.lastNameN;
data['DateofBirth'] = this.dateofBirth;
data['ActualDoctorRate'] = this.actualDoctorRate;
data['Age'] = this.age;
data['ClinicName'] = this.clinicName;
data['DecimalDoctorRate'] = this.decimalDoctorRate;
data['DoctorImageURL'] = this.doctorImageURL;
data['DoctorName'] = this.doctorName;
data['DoctorRate'] = this.doctorRate;
data['DoctorStarsRate'] = this.doctorStarsRate;
data['DoctorTitle'] = this.doctorTitle;
data['Gender'] = this.gender;
data['GenderDescription'] = this.genderDescription;
data['InvoiceNo_VP'] = this.invoiceNoVP;
data['IsActiveDoctorProfile'] = this.isActiveDoctorProfile;
data['IsDoctorAllowVedioCall'] = this.isDoctorAllowVedioCall;
data['IsExecludeDoctor'] = this.isExecludeDoctor;
data['NoOfPatientsRate'] = this.noOfPatientsRate;
data['PatientName'] = this.patientName;
data['ProjectName'] = this.projectName;
data['QR'] = this.qR;
data['VaccinationDate'] = this.vaccinationDate;
return data;
}
}

@ -32,6 +32,10 @@ abstract class MyAppointmentsRepo {
Future<Either<Failure, GenericApiModel<dynamic>>> sendCheckInNfcRequest(
{required PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel, required String scannedCode, required int checkInType});
Future<Either<Failure, GenericApiModel<List<PatientAppointmentHistoryResponseModel>>>> getPatientAppointmentsForTimeLine();
Future<Either<Failure, GenericApiModel<List<PatientAppointmentHistoryResponseModel>>>> getPatientDoctorsList();
}
class MyAppointmentsRepoImp implements MyAppointmentsRepo {
@ -395,4 +399,96 @@ class MyAppointmentsRepoImp implements MyAppointmentsRepo {
return Left(UnknownFailure(e.toString()));
}
}
@override
Future<Either<Failure, GenericApiModel<List<PatientAppointmentHistoryResponseModel>>>> getPatientAppointmentsForTimeLine() async {
Map<String, dynamic> mapDevice = {
"isDentalAllowedBackend": false,
"PatientTypeID": 1,
"IsComingFromCOC": false,
"PatientType": 1,
"IsForTimeLine": true,
};
try {
GenericApiModel<List<PatientAppointmentHistoryResponseModel>>? apiResponse;
Failure? failure;
await apiClient.post(
GET_PATIENT_APPOINTMENT_HISTORY,
body: mapDevice,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
final list = response['AppoimentAllHistoryResultList'];
// if (list == null || list.isEmpty) {
// throw Exception("Appointments list is empty");
// }
final appointmentsList = list.map((item) => PatientAppointmentHistoryResponseModel.fromJson(item as Map<String, dynamic>)).toList().cast<PatientAppointmentHistoryResponseModel>();
apiResponse = GenericApiModel<List<PatientAppointmentHistoryResponseModel>>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: null,
data: appointmentsList,
);
} catch (e) {
failure = DataParsingFailure(e.toString());
}
},
);
if (failure != null) return Left(failure!);
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
return Right(apiResponse!);
} catch (e) {
return Left(UnknownFailure(e.toString()));
}
}
@override
Future<Either<Failure, GenericApiModel<List<PatientAppointmentHistoryResponseModel>>>> getPatientDoctorsList() async {
Map<String, dynamic> mapDevice = {
"Top": 0,
"beforeDays": 0,
"exludType": 4,
};
try {
GenericApiModel<List<PatientAppointmentHistoryResponseModel>>? apiResponse;
Failure? failure;
await apiClient.post(
GET_MY_DOCTOR,
body: mapDevice,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
final list = response['PatientDoctorAppointmentResultList'];
// if (list == null || list.isEmpty) {
// throw Exception("Appointments list is empty");
// }
final appointmentsList = list.map((item) => PatientAppointmentHistoryResponseModel.fromJson(item as Map<String, dynamic>)).toList().cast<PatientAppointmentHistoryResponseModel>();
apiResponse = GenericApiModel<List<PatientAppointmentHistoryResponseModel>>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: null,
data: appointmentsList,
);
} catch (e) {
failure = DataParsingFailure(e.toString());
}
},
);
if (failure != null) return Left(failure!);
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
return Right(apiResponse!);
} catch (e) {
return Left(UnknownFailure(e.toString()));
}
}
}

@ -12,12 +12,18 @@ class MyAppointmentsViewModel extends ChangeNotifier {
bool isMyAppointmentsLoading = false;
bool isAppointmentPatientShareLoading = false;
bool isTimeLineAppointmentsLoading = false;
bool isPatientMyDoctorsLoading = false;
List<PatientAppointmentHistoryResponseModel> patientAppointmentsHistoryList = [];
List<PatientAppointmentHistoryResponseModel> patientUpcomingAppointmentsHistoryList = [];
List<PatientAppointmentHistoryResponseModel> patientArrivedAppointmentsHistoryList = [];
List<PatientAppointmentHistoryResponseModel> patientTimelineAppointmentsList = [];
List<PatientAppointmentHistoryResponseModel> patientMyDoctorsList = [];
PatientAppointmentShareResponseModel? patientAppointmentShareResponseModel;
MyAppointmentsViewModel({required this.myAppointmentsRepo, required this.errorHandlerService});
@ -31,8 +37,12 @@ class MyAppointmentsViewModel extends ChangeNotifier {
patientAppointmentsHistoryList.clear();
patientUpcomingAppointmentsHistoryList.clear();
patientArrivedAppointmentsHistoryList.clear();
patientTimelineAppointmentsList.clear();
patientMyDoctorsList.clear();
isMyAppointmentsLoading = true;
isAppointmentPatientShareLoading = true;
isTimeLineAppointmentsLoading = true;
isPatientMyDoctorsLoading = true;
notifyListeners();
}
@ -46,6 +56,16 @@ class MyAppointmentsViewModel extends ChangeNotifier {
notifyListeners();
}
setIsPatientTimeLineAppointmentLoading(bool val) {
isTimeLineAppointmentsLoading = val;
notifyListeners();
}
setIsPatientMyDoctorsLoading(bool val) {
isPatientMyDoctorsLoading = val;
notifyListeners();
}
setAppointmentReminder(bool value, PatientAppointmentHistoryResponseModel item) {
int index = patientAppointmentsHistoryList.indexOf(item);
if (index != -1) {
@ -253,4 +273,24 @@ class MyAppointmentsViewModel extends ChangeNotifier {
},
);
}
Future<void> getPatientMyDoctors({Function(dynamic)? onSuccess, Function(String)? onError}) async {
final result = await myAppointmentsRepo.getPatientDoctorsList();
result.fold(
(failure) async => await errorHandlerService.handleError(failure: failure),
(apiResponse) {
if (apiResponse.messageStatus == 2) {
// dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
} else if (apiResponse.messageStatus == 1) {
patientMyDoctorsList = apiResponse.data!;
isPatientMyDoctorsLoading = false;
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse);
}
}
},
);
}
}

@ -53,7 +53,7 @@ class PayfortRepoImp implements PayfortRepo {
} catch (e) {
failure = DataParsingFailure(e.toString());
}
}, isAllowAny: true);
}, isAllowAny: true, isPaymentServices: true);
if (failure != null) return Left(failure!);
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
return Right(apiResponse!);
@ -80,7 +80,7 @@ class PayfortRepoImp implements PayfortRepo {
} catch (e) {
failure = DataParsingFailure(e.toString());
}
}, isAllowAny: true);
}, isAllowAny: true, isPaymentServices: true);
if (failure != null) return Left(failure!);
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
return Right(apiResponse!);
@ -109,7 +109,7 @@ class PayfortRepoImp implements PayfortRepo {
} catch (e) {
failure = DataParsingFailure(e.toString());
}
}, isAllowAny: true, isExternal: true);
}, isAllowAny: true, isExternal: true, isPaymentServices: true);
if (failure != null) return Left(failure!);
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
return Right(apiResponse!);
@ -139,7 +139,7 @@ class PayfortRepoImp implements PayfortRepo {
} catch (e) {
failure = DataParsingFailure(e.toString());
}
}, isAllowAny: true);
}, isAllowAny: true, isPaymentServices: true);
if (failure != null) return Left(failure!);
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
return Right(apiResponse!);

@ -780,6 +780,9 @@ abstract class LocaleKeys {
static const resultsPending = 'resultsPending';
static const resultsAvailable = 'resultsAvailable';
static const viewReport = 'viewReport';
static const checkAvailability = 'checkAvailability';
static const readInstructions = 'readInstructions';
static const searchLabReport = 'searchLabReport';
static const prescriptionDeliveryError = 'prescriptionDeliveryError';
static const receiveOtpToast = 'receiveOtpToast';
static const enterPhoneNumber = 'enterPhoneNumber';
@ -802,12 +805,13 @@ abstract class LocaleKeys {
static const loginByOTP = 'loginByOTP';
static const guest = 'guest';
static const switchAccount = 'switchAccount';
static const checkAvailability = 'checkAvailability';
static const readInstructions = 'readInstructions';
static const searchLabReport = 'searchLabReport';
static const lastloginBy = 'lastloginBy';
static const allSet ='allSet';
static const lastLoginBy = 'lastLoginBy';
static const allSet = 'allSet';
static const enableQuickLogin = 'enableQuickLogin';
static const enableMsg = 'enableMsg';
static const notNow = 'notNow';
static const pendingActivation = 'pendingActivation';
static const awaitingApproval = 'awaitingApproval';
static const ready = 'ready';
}

@ -9,6 +9,8 @@ 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/features/authentication/authentication_view_model.dart';
import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_view_model.dart';
import 'package:hmg_patient_app_new/features/habib_wallet/habib_wallet_view_model.dart';
import 'package:hmg_patient_app_new/features/insurance/insurance_view_model.dart';
import 'package:hmg_patient_app_new/features/lab/lab_view_model.dart';
import 'package:hmg_patient_app_new/features/medical_file/medical_file_view_model.dart';
@ -96,7 +98,10 @@ void main() async {
),
),
ChangeNotifierProvider<MedicalFileViewModel>(
create: (_) => MedicalFileViewModel(),
create: (_) => MedicalFileViewModel(
medicalFileRepo: getIt(),
errorHandlerService: getIt(),
),
),
ChangeNotifierProvider<ProfileSettingsViewModel>(
create: (_) => ProfileSettingsViewModel(),
@ -113,6 +118,18 @@ void main() async {
errorHandlerService: getIt(),
),
),
ChangeNotifierProvider<HabibWalletViewModel>(
create: (_) => HabibWalletViewModel(
habibWalletRepo: getIt(),
errorHandlerService: getIt(),
),
),
ChangeNotifierProvider<BookAppointmentsViewModel>(
create: (_) => BookAppointmentsViewModel(
bookAppointmentsRepo: getIt(),
errorHandlerService: getIt(),
),
),
ChangeNotifierProvider<AuthenticationViewModel>(
create: (_) => AuthenticationViewModel(
authenticationRepo: getIt(),

@ -5,8 +5,6 @@ import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
@ -20,6 +18,7 @@ import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/appointments/appointment_payment_page.dart';
import 'package:hmg_patient_app_new/presentation/appointments/widgets/appointment_checkin_bottom_sheet.dart';
import 'package:hmg_patient_app_new/presentation/appointments/widgets/appointment_doctor_card.dart';
import 'package:hmg_patient_app_new/presentation/lab/collapsing_list_view.dart';
import 'package:hmg_patient_app_new/presentation/prescriptions/prescription_detail_page.dart';
import 'package:hmg_patient_app_new/presentation/prescriptions/prescriptions_list_page.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
@ -58,338 +57,338 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
@override
Widget build(BuildContext context) {
myAppointmentsViewModel = Provider.of<MyAppointmentsViewModel>(context);
prescriptionsViewModel = Provider.of<PrescriptionsViewModel>(context);
myAppointmentsViewModel = Provider.of<MyAppointmentsViewModel>(context, listen: false);
prescriptionsViewModel = Provider.of<PrescriptionsViewModel>(context, listen: false);
return Scaffold(
backgroundColor: AppColors.bgScaffoldColor,
appBar: AppBar(
title: "Appointment Details".needTranslation.toText18(),
backgroundColor: AppColors.bgScaffoldColor,
),
body: Column(
children: [
Expanded(
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
"Appointment Details".needTranslation.toText20(isBold: true),
if (AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel))
CustomButton(
text: "Report".needTranslation,
onPressed: () {},
backgroundColor: AppColors.secondaryLightRedColor,
borderColor: AppColors.secondaryLightRedColor,
textColor: AppColors.primaryRedColor,
fontSize: 14,
fontWeight: FontWeight.w500,
borderRadius: 12,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 40.h,
iconSize: 16.h,
icon: AppAssets.report_icon,
iconColor: AppColors.primaryRedColor,
)
],
),
SizedBox(height: 24.h),
AppointmentDoctorCard(
patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel,
onAskDoctorTap: () {},
onCancelTap: () async {
showCommonBottomSheet(context,
child: Utils.getLoadingWidget(),
callBackFunc: (str) {},
title: "",
height: ResponsiveExtension.screenHeight * 0.3,
isCloseButtonVisible: false,
isDismissible: false,
isFullScreen: false);
await myAppointmentsViewModel.cancelAppointment(
patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel,
onSuccess: (apiResponse) {
Navigator.of(context).pop();
showCommonBottomSheet(context,
child: Utils.getSuccessWidget(loadingText: "Appointment Cancelled Successfully".needTranslation),
callBackFunc: (str) {},
title: "",
height: ResponsiveExtension.screenHeight * 0.3,
isCloseButtonVisible: false,
isDismissible: false,
isFullScreen: false,
isSuccessDialog: true);
});
Navigator.of(context).pop();
Navigator.of(context).pop();
},
onRescheduleTap: () {},
),
SizedBox(height: 16.h),
!AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel)
? Column(
children: [
Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 20.h,
hasShadow: false,
child: CollapsingListView(
title: "Appointment Details".needTranslation,
report: () {},
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Row(
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
// children: [
// "Appointment Details".needTranslation.toText20(isBold: true),
// if (AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel))
// CustomButton(
// text: "Report".needTranslation,
// onPressed: () {},
// backgroundColor: AppColors.secondaryLightRedColor,
// borderColor: AppColors.secondaryLightRedColor,
// textColor: AppColors.primaryRedColor,
// fontSize: 14,
// fontWeight: FontWeight.w500,
// borderRadius: 12,
// padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
// height: 40.h,
// iconSize: 16.h,
// icon: AppAssets.report_icon,
// iconColor: AppColors.primaryRedColor,
// )
// ],
// ),
// SizedBox(height: 24.h),
AppointmentDoctorCard(
patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel,
onAskDoctorTap: () {},
onCancelTap: () async {
showCommonBottomSheet(context,
child: Utils.getLoadingWidget(),
callBackFunc: (str) {},
title: "",
height: ResponsiveExtension.screenHeight * 0.3,
isCloseButtonVisible: false,
isDismissible: false,
isFullScreen: false);
await myAppointmentsViewModel.cancelAppointment(
patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel,
onSuccess: (apiResponse) {
Navigator.of(context).pop();
showCommonBottomSheet(context,
child: Utils.getSuccessWidget(loadingText: "Appointment Cancelled Successfully".needTranslation),
callBackFunc: (str) {},
title: "",
height: ResponsiveExtension.screenHeight * 0.3,
isCloseButtonVisible: false,
isDismissible: false,
isFullScreen: false,
isSuccessDialog: true);
});
Navigator.of(context).pop();
Navigator.of(context).pop();
},
onRescheduleTap: () {},
),
SizedBox(height: 16.h),
!AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel)
? Column(
children: [
Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 20.h,
hasShadow: false,
),
child: Padding(
padding: EdgeInsets.all(16.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
"Appointment Status".needTranslation.toText16(isBold: true),
],
),
SizedBox(height: 4.h),
(!AppointmentType.isConfirmed(widget.patientAppointmentHistoryResponseModel)
? "Not Confirmed".needTranslation.toText12(color: AppColors.primaryRedColor, fontWeight: FontWeight.w500)
: "Confirmed".needTranslation.toText12(color: AppColors.successColor, fontWeight: FontWeight.w500)),
SizedBox(height: 16.h),
Stack(
children: [
ClipRRect(
clipBehavior: Clip.hardEdge,
borderRadius: BorderRadius.circular(24),
child: Image.network(
"https://maps.googleapis.com/maps/api/staticmap?center=${widget.patientAppointmentHistoryResponseModel.latitude},${widget.patientAppointmentHistoryResponseModel.longitude}&zoom=14&size=350x165&maptype=roadmap&markers=color:red%7C${widget.patientAppointmentHistoryResponseModel.latitude},${widget.patientAppointmentHistoryResponseModel.longitude}&key=AIzaSyB6TERnxIr0yJ3qG4ULBZbu0sAD4tGqtng",
fit: BoxFit.contain,
),
),
Positioned(
bottom: 0,
child: SizedBox(
width: MediaQuery.of(context).size.width * 0.785,
child: CustomButton(
text: "Get Directions".needTranslation,
onPressed: () {
MapsLauncher.launchCoordinates(double.parse(widget.patientAppointmentHistoryResponseModel.latitude!),
double.parse(widget.patientAppointmentHistoryResponseModel.longitude!), widget.patientAppointmentHistoryResponseModel.projectName);
},
backgroundColor: AppColors.textColor.withOpacity(0.8),
borderColor: AppointmentType.getNextActionButtonColor(widget.patientAppointmentHistoryResponseModel.nextAction).withOpacity(0.01),
textColor: AppColors.whiteColor,
fontSize: 14,
fontWeight: FontWeight.w500,
borderRadius: 12.h,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 40.h,
icon: AppAssets.directions_icon,
iconColor: AppColors.whiteColor,
iconSize: 13.h,
).paddingAll(12.h),
),
),
],
),
],
),
),
),
child: Padding(
padding: EdgeInsets.all(16.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
SizedBox(height: 16.h),
Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 20.h,
hasShadow: false,
),
child: Row(
mainAxisSize: MainAxisSize.max,
children: [
Row(
Utils.buildSvgWithAssets(icon: AppAssets.prescription_reminder_icon, width: 35.h, height: 35.h),
SizedBox(width: 8.h),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
"Appointment Status".needTranslation.toText16(isBold: true),
LocaleKeys.setReminder.tr(context: context).toText13(isBold: true),
"Notify me before the appointment".needTranslation.toText11(color: AppColors.textColorLight, weight: FontWeight.w500),
],
),
SizedBox(height: 4.h),
(!AppointmentType.isConfirmed(widget.patientAppointmentHistoryResponseModel)
? "Not Confirmed".needTranslation.toText12(color: AppColors.primaryRedColor, fontWeight: FontWeight.w500)
: "Confirmed".needTranslation.toText12(color: AppColors.successColor, fontWeight: FontWeight.w500)),
SizedBox(height: 16.h),
Stack(
children: [
ClipRRect(
clipBehavior: Clip.hardEdge,
borderRadius: BorderRadius.circular(24),
child: Image.network(
"https://maps.googleapis.com/maps/api/staticmap?center=${widget.patientAppointmentHistoryResponseModel.latitude},${widget.patientAppointmentHistoryResponseModel.longitude}&zoom=14&size=350x165&maptype=roadmap&markers=color:red%7C${widget.patientAppointmentHistoryResponseModel.latitude},${widget.patientAppointmentHistoryResponseModel.longitude}&key=AIzaSyB6TERnxIr0yJ3qG4ULBZbu0sAD4tGqtng",
fit: BoxFit.contain,
),
),
Positioned(
bottom: 0,
child: SizedBox(
width: MediaQuery.of(context).size.width * 0.785,
child: CustomButton(
text: "Get Directions".needTranslation,
onPressed: () {
MapsLauncher.launchCoordinates(double.parse(widget.patientAppointmentHistoryResponseModel.latitude!),
double.parse(widget.patientAppointmentHistoryResponseModel.longitude!), widget.patientAppointmentHistoryResponseModel.projectName);
},
backgroundColor: AppColors.textColor.withOpacity(0.8),
borderColor: AppointmentType.getNextActionButtonColor(widget.patientAppointmentHistoryResponseModel.nextAction).withOpacity(0.01),
textColor: AppColors.whiteColor,
fontSize: 14,
fontWeight: FontWeight.w500,
borderRadius: 12.h,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 40.h,
icon: AppAssets.directions_icon,
iconColor: AppColors.whiteColor,
iconSize: 13.h,
).paddingAll(12.h),
),
),
],
const Spacer(),
Switch(
activeColor: AppColors.successColor,
activeTrackColor: AppColors.successColor.withValues(alpha: .15),
value: widget.patientAppointmentHistoryResponseModel.hasReminder!,
onChanged: (newValue) {
setState(() {
myAppointmentsViewModel.setAppointmentReminder(newValue, widget.patientAppointmentHistoryResponseModel);
});
},
),
],
),
).paddingSymmetrical(16.h, 16.h),
),
),
SizedBox(height: 16.h),
Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 20.h,
hasShadow: false,
),
child: Row(
mainAxisSize: MainAxisSize.max,
SizedBox(height: 16.h),
],
)
: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
"Lab & Radiology".needTranslation.toText18(isBold: true),
SizedBox(height: 16.h),
GridView(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2, crossAxisSpacing: 13.h, mainAxisSpacing: 13.h, childAspectRatio: 7 / 6),
physics: NeverScrollableScrollPhysics(),
shrinkWrap: true,
children: [
Utils.buildSvgWithAssets(icon: AppAssets.prescription_reminder_icon, width: 35.h, height: 35.h),
SizedBox(width: 8.h),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
LocaleKeys.setReminder.tr(context: context).toText13(isBold: true),
"Notify me before the appointment".needTranslation.toText11(color: AppColors.textColorLight, weight: FontWeight.w500),
],
MedicalFileCard(
label: LocaleKeys.labResults.tr(context: context),
textColor: AppColors.blackColor,
backgroundColor: AppColors.whiteColor,
svgIcon: AppAssets.lab_result_icon,
iconSize: 40,
isLargeText: true,
),
const Spacer(),
Switch(
activeColor: AppColors.successColor,
activeTrackColor: AppColors.successColor.withValues(alpha: .15),
value: widget.patientAppointmentHistoryResponseModel.hasReminder!,
onChanged: (newValue) {
setState(() {
myAppointmentsViewModel.setAppointmentReminder(newValue, widget.patientAppointmentHistoryResponseModel);
});
},
MedicalFileCard(
label: LocaleKeys.radiology.tr(context: context),
textColor: AppColors.blackColor,
backgroundColor: AppColors.whiteColor,
svgIcon: AppAssets.radiology_icon,
iconSize: 40,
isLargeText: true,
),
],
).paddingSymmetrical(16.h, 16.h),
),
SizedBox(height: 16.h),
],
)
: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
"Lab & Radiology".needTranslation.toText18(isBold: true),
SizedBox(height: 16.h),
GridView(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2, crossAxisSpacing: 13.h, mainAxisSpacing: 13.h, childAspectRatio: 7 / 6),
physics: NeverScrollableScrollPhysics(),
shrinkWrap: true,
children: [
MedicalFileCard(
label: LocaleKeys.labResults.tr(context: context),
textColor: AppColors.blackColor,
backgroundColor: AppColors.whiteColor,
svgIcon: AppAssets.lab_result_icon,
iconSize: 40,
isLargeText: true,
),
MedicalFileCard(
label: LocaleKeys.radiology.tr(context: context),
textColor: AppColors.blackColor,
backgroundColor: AppColors.whiteColor,
svgIcon: AppAssets.radiology_icon,
iconSize: 40,
isLargeText: true,
),
],
),
LocaleKeys.prescriptions.tr().toText18(isBold: true),
SizedBox(height: 16.h),
Consumer<PrescriptionsViewModel>(builder: (context, prescriptionVM, child) {
return prescriptionVM.isPrescriptionsDetailsLoading
? const MoviesShimmerWidget()
: Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: Colors.white,
borderRadius: 20.h,
),
child: Padding(
padding: EdgeInsets.all(16.h),
child: Column(
children: [
ListView.separated(
itemCount: prescriptionVM.prescriptionDetailsList.length,
shrinkWrap: true,
padding: const EdgeInsets.only(left: 0, right: 8),
physics: NeverScrollableScrollPhysics(),
itemBuilder: (context, index) {
return AnimationConfiguration.staggeredList(
position: index,
duration: const Duration(milliseconds: 500),
child: SlideAnimation(
verticalOffset: 100.0,
child: FadeInAnimation(
child: Row(
children: [
Utils.buildSvgWithAssets(
icon: AppAssets.prescription_item_icon,
width: 40.h,
height: 40.h,
),
SizedBox(width: 8.h),
Row(
mainAxisSize: MainAxisSize.max,
children: [
Column(
children: [
SizedBox(width: 150.h, child: prescriptionVM.prescriptionDetailsList[index].itemDescription!.toText12(isBold: true, maxLine: 1)),
SizedBox(
width: 150.h,
child:
"Prescribed By: ${widget.patientAppointmentHistoryResponseModel.doctorTitle} ${widget.patientAppointmentHistoryResponseModel.doctorNameObj}"
.needTranslation
.toText10(weight: FontWeight.w500, color: AppColors.greyTextColor, letterSpacing: -0.4),
),
],
),
SizedBox(width: 68.h),
Utils.buildSvgWithAssets(
icon: AppAssets.forward_arrow_icon,
iconColor: AppColors.blackColor,
width: 18.h,
height: 13.h,
fit: BoxFit.contain,
),
],
),
],
),
LocaleKeys.prescriptions.tr().toText18(isBold: true),
SizedBox(height: 16.h),
Consumer<PrescriptionsViewModel>(builder: (context, prescriptionVM, child) {
return prescriptionVM.isPrescriptionsDetailsLoading
? const MoviesShimmerWidget()
: Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: Colors.white,
borderRadius: 20.h,
),
child: Padding(
padding: EdgeInsets.all(16.h),
child: Column(
children: [
ListView.separated(
itemCount: prescriptionVM.prescriptionDetailsList.length,
shrinkWrap: true,
padding: const EdgeInsets.only(left: 0, right: 8),
physics: NeverScrollableScrollPhysics(),
itemBuilder: (context, index) {
return AnimationConfiguration.staggeredList(
position: index,
duration: const Duration(milliseconds: 500),
child: SlideAnimation(
verticalOffset: 100.0,
child: FadeInAnimation(
child: Row(
children: [
Utils.buildSvgWithAssets(
icon: AppAssets.prescription_item_icon,
width: 40.h,
height: 40.h,
),
SizedBox(width: 8.h),
Row(
mainAxisSize: MainAxisSize.max,
children: [
Column(
children: [
SizedBox(width: 150.h, child: prescriptionVM.prescriptionDetailsList[index].itemDescription!.toText12(isBold: true, maxLine: 1)),
SizedBox(
width: 150.h,
child:
"Prescribed By: ${widget.patientAppointmentHistoryResponseModel.doctorTitle} ${widget.patientAppointmentHistoryResponseModel.doctorNameObj}"
.needTranslation
.toText10(weight: FontWeight.w500, color: AppColors.greyTextColor, letterSpacing: -0.4),
),
],
),
SizedBox(width: 68.h),
Utils.buildSvgWithAssets(
icon: AppAssets.forward_arrow_icon,
iconColor: AppColors.blackColor,
width: 18.h,
height: 13.h,
fit: BoxFit.contain,
),
],
),
],
),
),
),
);
},
separatorBuilder: (BuildContext cxt, int index) => SizedBox(height: 16.h),
).onPress(() {
prescriptionVM.setPrescriptionsDetailsLoading();
Navigator.of(context).push(
FadePage(
page: PrescriptionDetailPage(prescriptionsResponseModel: getPrescriptionRequestModel()),
),
);
},
separatorBuilder: (BuildContext cxt, int index) => SizedBox(height: 16.h),
).onPress(() {
prescriptionVM.setPrescriptionsDetailsLoading();
Navigator.of(context).push(
FadePage(
page: PrescriptionDetailPage(prescriptionsResponseModel: getPrescriptionRequestModel()),
),
);
}),
SizedBox(height: 16.h),
const Divider(color: AppColors.dividerColor),
SizedBox(height: 16.h),
Row(
children: [
// Expanded(
// child: CustomButton(
// text: widget.prescriptionsResponseModel.isHomeMedicineDeliverySupported! ? LocaleKeys.resendOrder.tr(context: context) : LocaleKeys.prescriptionDeliveryError.tr(context: context),
// onPressed: () {},
// backgroundColor: AppColors.secondaryLightRedColor,
// borderColor: AppColors.secondaryLightRedColor,
// textColor: AppColors.primaryRedColor,
// fontSize: 14,
// fontWeight: FontWeight.w500,
// borderRadius: 12.h,
// height: 40.h,
// icon: AppAssets.appointment_calendar_icon,
// iconColor: AppColors.primaryRedColor,
// iconSize: 16.h,
// ),
// ),
// SizedBox(width: 16.h),
Expanded(
child: CustomButton(
text: "All Prescriptions".needTranslation,
onPressed: () {
Navigator.of(context)
.push(
FadePage(
page: PrescriptionsListPage(),
),
)
.then((val) {
prescriptionsViewModel.setPrescriptionsDetailsLoading();
prescriptionsViewModel.getPrescriptionDetails(getPrescriptionRequestModel());
});
},
backgroundColor: AppColors.secondaryLightRedColor,
borderColor: AppColors.secondaryLightRedColor,
textColor: AppColors.primaryRedColor,
fontSize: 14,
fontWeight: FontWeight.w500,
borderRadius: 12.h,
height: 40.h,
icon: AppAssets.requests,
iconColor: AppColors.primaryRedColor,
iconSize: 16.h,
}),
SizedBox(height: 16.h),
const Divider(color: AppColors.dividerColor),
SizedBox(height: 16.h),
Row(
children: [
// Expanded(
// child: CustomButton(
// text: widget.prescriptionsResponseModel.isHomeMedicineDeliverySupported! ? LocaleKeys.resendOrder.tr(context: context) : LocaleKeys.prescriptionDeliveryError.tr(context: context),
// onPressed: () {},
// backgroundColor: AppColors.secondaryLightRedColor,
// borderColor: AppColors.secondaryLightRedColor,
// textColor: AppColors.primaryRedColor,
// fontSize: 14,
// fontWeight: FontWeight.w500,
// borderRadius: 12.h,
// height: 40.h,
// icon: AppAssets.appointment_calendar_icon,
// iconColor: AppColors.primaryRedColor,
// iconSize: 16.h,
// ),
// ),
// SizedBox(width: 16.h),
Expanded(
child: CustomButton(
text: "All Prescriptions".needTranslation,
onPressed: () {
Navigator.of(context)
.push(
FadePage(
page: PrescriptionsListPage(),
),
)
.then((val) {
prescriptionsViewModel.setPrescriptionsDetailsLoading();
prescriptionsViewModel.getPrescriptionDetails(getPrescriptionRequestModel());
});
},
backgroundColor: AppColors.secondaryLightRedColor,
borderColor: AppColors.secondaryLightRedColor,
textColor: AppColors.primaryRedColor,
fontSize: 14,
fontWeight: FontWeight.w500,
borderRadius: 12.h,
height: 40.h,
icon: AppAssets.requests,
iconColor: AppColors.primaryRedColor,
iconSize: 16.h,
),
),
),
],
),
],
],
),
],
),
),
),
);
}),
],
),
],
).paddingSymmetrical(24.h, 24.h),
);
}),
],
),
],
).paddingSymmetrical(24.h, 24.h),
),
),
),
Container(

@ -9,7 +9,6 @@ import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/cache_consts.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/core/enums.dart';
import 'package:hmg_patient_app_new/features/authentication/models/resp_models/authenticated_user_resp_model.dart';
import 'package:hmg_patient_app_new/features/payfort/models/apple_pay_request_insert_model.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
@ -22,6 +21,7 @@ import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/appointments/my_appointments_page.dart';
import 'package:hmg_patient_app_new/presentation/home/navigation_screen.dart';
import 'package:hmg_patient_app_new/presentation/insurance/insurance_home_page.dart';
import 'package:hmg_patient_app_new/presentation/lab/collapsing_list_view.dart';
import 'package:hmg_patient_app_new/services/cache_service.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
@ -55,6 +55,7 @@ class _AppointmentPaymentPageState extends State<AppointmentPaymentPage> {
void initState() {
scheduleMicrotask(() {
payfortViewModel.initPayfortViewModel();
payfortViewModel.setIsApplePayConfigurationLoading(false);
myAppointmentsViewModel.getPatientShareAppointment(
widget.patientAppointmentHistoryResponseModel.projectID,
widget.patientAppointmentHistoryResponseModel.clinicID,
@ -71,131 +72,131 @@ class _AppointmentPaymentPageState extends State<AppointmentPaymentPage> {
payfortViewModel = Provider.of<PayfortViewModel>(context);
return Scaffold(
backgroundColor: AppColors.bgScaffoldColor,
appBar: AppBar(
title: "Appointment Payment".needTranslation.toText18(),
backgroundColor: AppColors.bgScaffoldColor,
),
body: Consumer<MyAppointmentsViewModel>(builder: (context, myAppointmentsVM, child) {
return myAppointmentsVM.isAppointmentPatientShareLoading
? const MoviesShimmerWidget().paddingAll(24.h)
: Column(
children: [
Expanded(
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
"Appointment Payment".needTranslation.toText24(isBold: true).paddingSymmetrical(24.h, 0.h),
SizedBox(height: 24.h),
Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 20.h,
hasShadow: false,
),
child: Row(
mainAxisSize: MainAxisSize.max,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Image.asset(AppAssets.mada, width: 72.h, height: 25.h),
SizedBox(height: 16.h),
"Mada".needTranslation.toText16(isBold: true),
],
),
SizedBox(width: 8.h),
const Spacer(),
Utils.buildSvgWithAssets(
icon: AppAssets.forward_arrow_icon,
iconColor: AppColors.blackColor,
width: 18.h,
height: 13.h,
fit: BoxFit.contain,
),
],
).paddingSymmetrical(16.h, 16.h),
).paddingSymmetrical(24.h, 0.h).onPress(() {
selectedPaymentMethod = "MADA";
openPaymentURL("mada");
}),
SizedBox(height: 16.h),
Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 20.h,
hasShadow: false,
),
child: Row(
mainAxisSize: MainAxisSize.max,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Image.asset(AppAssets.visa, width: 50.h, height: 50.h),
SizedBox(width: 8.h),
Image.asset(AppAssets.Mastercard, width: 40.h, height: 40.h),
],
),
SizedBox(height: 16.h),
"Visa or Mastercard".needTranslation.toText16(isBold: true),
],
),
SizedBox(width: 8.h),
const Spacer(),
Utils.buildSvgWithAssets(
icon: AppAssets.forward_arrow_icon,
iconColor: AppColors.blackColor,
width: 18.h,
height: 13.h,
fit: BoxFit.contain,
),
],
).paddingSymmetrical(16.h, 16.h),
).paddingSymmetrical(24.h, 0.h).onPress(() {
selectedPaymentMethod = "VISA";
openPaymentURL("visa");
}),
SizedBox(height: 16.h),
Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 20.h,
hasShadow: false,
),
child: Row(
mainAxisSize: MainAxisSize.max,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Image.asset(AppAssets.tamara_en, width: 72.h, height: 25.h),
SizedBox(height: 16.h),
"Tamara".needTranslation.toText16(isBold: true),
],
),
SizedBox(width: 8.h),
const Spacer(),
Utils.buildSvgWithAssets(
icon: AppAssets.forward_arrow_icon,
iconColor: AppColors.blackColor,
width: 18.h,
height: 13.h,
fit: BoxFit.contain,
),
],
).paddingSymmetrical(16.h, 16.h),
).paddingSymmetrical(24.h, 0.h).onPress(() {
selectedPaymentMethod = "TAMARA";
openPaymentURL("tamara");
}),
],
),
),
),
Container(
return Column(
children: [
Expanded(
child: CollapsingListView(
title: "Appointment Payment".needTranslation,
child: SingleChildScrollView(
child: myAppointmentsVM.isAppointmentPatientShareLoading
? const MoviesShimmerWidget().paddingAll(24.h)
: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 24.h),
Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 20.h,
hasShadow: false,
),
child: Row(
mainAxisSize: MainAxisSize.max,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Image.asset(AppAssets.mada, width: 72.h, height: 25.h),
SizedBox(height: 16.h),
"Mada".needTranslation.toText16(isBold: true),
],
),
SizedBox(width: 8.h),
const Spacer(),
Utils.buildSvgWithAssets(
icon: AppAssets.forward_arrow_icon,
iconColor: AppColors.blackColor,
width: 18.h,
height: 13.h,
fit: BoxFit.contain,
),
],
).paddingSymmetrical(16.h, 16.h),
).paddingSymmetrical(24.h, 0.h).onPress(() {
selectedPaymentMethod = "MADA";
openPaymentURL("mada");
}),
SizedBox(height: 16.h),
Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 20.h,
hasShadow: false,
),
child: Row(
mainAxisSize: MainAxisSize.max,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Image.asset(AppAssets.visa, width: 50.h, height: 50.h),
SizedBox(width: 8.h),
Image.asset(AppAssets.Mastercard, width: 40.h, height: 40.h),
],
),
SizedBox(height: 16.h),
"Visa or Mastercard".needTranslation.toText16(isBold: true),
],
),
SizedBox(width: 8.h),
const Spacer(),
Utils.buildSvgWithAssets(
icon: AppAssets.forward_arrow_icon,
iconColor: AppColors.blackColor,
width: 18.h,
height: 13.h,
fit: BoxFit.contain,
),
],
).paddingSymmetrical(16.h, 16.h),
).paddingSymmetrical(24.h, 0.h).onPress(() {
selectedPaymentMethod = "VISA";
openPaymentURL("visa");
}),
SizedBox(height: 16.h),
Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 20.h,
hasShadow: false,
),
child: Row(
mainAxisSize: MainAxisSize.max,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Image.asset(AppAssets.tamara_en, width: 72.h, height: 25.h),
SizedBox(height: 16.h),
"Tamara".needTranslation.toText16(isBold: true),
],
),
SizedBox(width: 8.h),
const Spacer(),
Utils.buildSvgWithAssets(
icon: AppAssets.forward_arrow_icon,
iconColor: AppColors.blackColor,
width: 18.h,
height: 13.h,
fit: BoxFit.contain,
),
],
).paddingSymmetrical(16.h, 16.h),
).paddingSymmetrical(24.h, 0.h).onPress(() {
selectedPaymentMethod = "TAMARA";
openPaymentURL("tamara");
}),
],
),
),
),
),
myAppointmentsVM.isAppointmentPatientShareLoading
? SizedBox.shrink()
: Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 24.h,
@ -283,7 +284,7 @@ class _AppointmentPaymentPageState extends State<AppointmentPaymentPage> {
height: 80.h,
fit: BoxFit.contain,
).paddingSymmetrical(24.h, 0.h).onPress(() {
payfortVM.setIsApplePayConfigurationLoading(true);
// payfortVM.setIsApplePayConfigurationLoading(true);
startApplePay();
}),
SizedBox(height: 12.h),
@ -291,8 +292,8 @@ class _AppointmentPaymentPageState extends State<AppointmentPaymentPage> {
);
}),
),
],
);
],
);
}),
);
}
@ -344,62 +345,74 @@ class _AppointmentPaymentPageState extends State<AppointmentPaymentPage> {
// updateTamaraRequestStatus("Failed", "00", Utils.getAppointmentTransID(appo.projectID, appo.clinicID, appo.appointmentNo), tamaraOrderID, num.parse(selectedInstallments), appo);
// }
} else {
showCommonBottomSheet(context,
child: Utils.getLoadingWidget(), callBackFunc: (str) {}, title: "", height: ResponsiveExtension.screenHeight * 0.3, isCloseButtonVisible: false, isDismissible: false, isFullScreen: false);
await payfortViewModel.checkPaymentStatus(
transactionID: transID,
onSuccess: (apiResponse) async {
print(apiResponse.data);
if (payfortViewModel.payfortCheckPaymentStatusResponseModel!.responseMessage!.toLowerCase() == "success") {
await myAppointmentsViewModel.createAdvancePayment(
paymentMethodName: selectedPaymentMethod,
projectID: widget.patientAppointmentHistoryResponseModel.projectID,
clinicID: widget.patientAppointmentHistoryResponseModel.clinicID,
appointmentNo: widget.patientAppointmentHistoryResponseModel.appointmentNo.toString(),
payedAmount: payfortViewModel.payfortCheckPaymentStatusResponseModel!.amount!,
paymentReference: payfortViewModel.payfortCheckPaymentStatusResponseModel!.fortId!,
patientID: "4767477",
patientType: 1,
onSuccess: (value) async {
print(value);
await myAppointmentsViewModel.addAdvanceNumberRequest(
advanceNumber: Utils.isVidaPlusProject(appState, widget.patientAppointmentHistoryResponseModel.projectID)
? value.data['OnlineCheckInAppointments'][0]['AdvanceNumber_VP'].toString()
: value.data['OnlineCheckInAppointments'][0]['AdvanceNumber'].toString(),
paymentReference: payfortViewModel.payfortCheckPaymentStatusResponseModel!.fortId!,
appointmentNo: widget.patientAppointmentHistoryResponseModel.appointmentNo.toString(),
onSuccess: (value) async {
if (widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment!) {
//TODO: Implement LiveCare Check-In API Call
} else {
await myAppointmentsViewModel.generateAppointmentQR(
clinicID: widget.patientAppointmentHistoryResponseModel.clinicID,
projectID: widget.patientAppointmentHistoryResponseModel.projectID,
appointmentNo: widget.patientAppointmentHistoryResponseModel.appointmentNo.toString(),
isFollowUp: myAppointmentsViewModel.patientAppointmentShareResponseModel!.isFollowup!,
onSuccess: (apiResponse) {
Future.delayed(Duration(milliseconds: 500), () {
Navigator.of(context).pop();
Navigator.pushAndRemoveUntil(
context,
FadePage(
page: LandingNavigation(),
),
(r) => false);
Navigator.of(context).push(
FadePage(page: MyAppointmentsPage()),
);
});
});
}
});
});
} else {}
});
checkPaymentStatus();
// checkPaymentStatus(appo);
}
}
void checkPaymentStatus() async {
showCommonBottomSheet(context,
child: Utils.getLoadingWidget(), callBackFunc: (str) {}, title: "", height: ResponsiveExtension.screenHeight * 0.3, isCloseButtonVisible: false, isDismissible: false, isFullScreen: false);
await payfortViewModel.checkPaymentStatus(
transactionID: transID,
onSuccess: (apiResponse) async {
print(apiResponse.data);
if (payfortViewModel.payfortCheckPaymentStatusResponseModel!.responseMessage!.toLowerCase() == "success") {
await myAppointmentsViewModel.createAdvancePayment(
paymentMethodName: selectedPaymentMethod,
projectID: widget.patientAppointmentHistoryResponseModel.projectID,
clinicID: widget.patientAppointmentHistoryResponseModel.clinicID,
appointmentNo: widget.patientAppointmentHistoryResponseModel.appointmentNo.toString(),
payedAmount: payfortViewModel.payfortCheckPaymentStatusResponseModel!.amount!,
paymentReference: payfortViewModel.payfortCheckPaymentStatusResponseModel!.fortId!,
patientID: "4767477",
patientType: 1,
onSuccess: (value) async {
print(value);
await myAppointmentsViewModel.addAdvanceNumberRequest(
advanceNumber: Utils.isVidaPlusProject(widget.patientAppointmentHistoryResponseModel.projectID)
? value.data['OnlineCheckInAppointments'][0]['AdvanceNumber_VP'].toString()
: value.data['OnlineCheckInAppointments'][0]['AdvanceNumber'].toString(),
paymentReference: payfortViewModel.payfortCheckPaymentStatusResponseModel!.fortId!,
appointmentNo: widget.patientAppointmentHistoryResponseModel.appointmentNo.toString(),
onSuccess: (value) async {
if (widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment!) {
//TODO: Implement LiveCare Check-In API Call
} else {
await myAppointmentsViewModel.generateAppointmentQR(
clinicID: widget.patientAppointmentHistoryResponseModel.clinicID,
projectID: widget.patientAppointmentHistoryResponseModel.projectID,
appointmentNo: widget.patientAppointmentHistoryResponseModel.appointmentNo.toString(),
isFollowUp: myAppointmentsViewModel.patientAppointmentShareResponseModel!.isFollowup!,
onSuccess: (apiResponse) {
Future.delayed(Duration(milliseconds: 500), () {
Navigator.of(context).pop();
Navigator.pushAndRemoveUntil(
context,
FadePage(
page: LandingNavigation(),
),
(r) => false);
Navigator.of(context).push(
FadePage(page: MyAppointmentsPage()),
);
});
});
}
});
});
} else {
showCommonBottomSheetWithoutHeight(
context,
child: Utils.getErrorWidget(loadingText: "Payment Failed! Please try again.".needTranslation),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
}
});
}
openPaymentURL(String paymentMethod) {
browser = MyInAppBrowser(onExitCallback: onBrowserExit, onLoadStartCallback: onBrowserLoadStart, context: context);
transID = Utils.getAppointmentTransID(
@ -433,6 +446,8 @@ class _AppointmentPaymentPageState extends State<AppointmentPaymentPage> {
}
startApplePay() async {
showCommonBottomSheet(context,
child: Utils.getLoadingWidget(), callBackFunc: (str) {}, title: "", height: ResponsiveExtension.screenHeight * 0.3, isCloseButtonVisible: false, isDismissible: false, isFullScreen: false);
transID = Utils.getAppointmentTransID(
widget.patientAppointmentHistoryResponseModel.projectID,
widget.patientAppointmentHistoryResponseModel.clinicID,
@ -447,8 +462,7 @@ class _AppointmentPaymentPageState extends State<AppointmentPaymentPage> {
applePayInsertRequest.clientRequestID = transID;
applePayInsertRequest.clinicID = widget.patientAppointmentHistoryResponseModel.clinicID;
//TODO: Need to pass dynamic params to the payment request instead of static values
applePayInsertRequest.currency = "SAR";
applePayInsertRequest.currency = appState.getAuthenticatedUser()!.outSa! == 0 ? "SAR" : "AED";
applePayInsertRequest.customerEmail = "CustID_${appState.getAuthenticatedUser()!.patientId.toString()}@HMG.com";
applePayInsertRequest.customerID = appState.getAuthenticatedUser()!.patientId.toString();
applePayInsertRequest.customerName = "${appState.getAuthenticatedUser()!.firstName} ${appState.getAuthenticatedUser()!.lastName}";
@ -460,8 +474,8 @@ class _AppointmentPaymentPageState extends State<AppointmentPaymentPage> {
applePayInsertRequest.serviceID = ServiceTypeEnum.appointmentPayment.getIdFromServiceEnum().toString();
applePayInsertRequest.channelID = 3;
applePayInsertRequest.patientID = appState.getAuthenticatedUser()!.patientId.toString();
applePayInsertRequest.patientTypeID = 1;
applePayInsertRequest.patientOutSA = 0;
applePayInsertRequest.patientTypeID = appState.getAuthenticatedUser()!.patientType;
applePayInsertRequest.patientOutSA = appState.getAuthenticatedUser()!.outSa;
applePayInsertRequest.appointmentDate = widget.patientAppointmentHistoryResponseModel.appointmentDate;
applePayInsertRequest.appointmentNo = widget.patientAppointmentHistoryResponseModel.appointmentNo;
applePayInsertRequest.orderDescription = "Appointment Payment";
@ -472,7 +486,7 @@ class _AppointmentPaymentPageState extends State<AppointmentPaymentPage> {
applePayInsertRequest.isSchedule = widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment! ? "1" : "0";
applePayInsertRequest.language = appState.isArabic() ? 'ar' : 'en';
applePayInsertRequest.languageID = appState.isArabic() ? 1 : 2;
applePayInsertRequest.userName = 3628599;
applePayInsertRequest.userName = appState.getAuthenticatedUser()!.patientId;
applePayInsertRequest.responseContinueURL = "http://hmg.com/Documents/success.html";
applePayInsertRequest.backClickUrl = "http://hmg.com/Documents/success.html";
applePayInsertRequest.paymentOption = "ApplePay";
@ -499,12 +513,22 @@ class _AppointmentPaymentPageState extends State<AppointmentPaymentPage> {
merchantIdentifier: payfortViewModel.payfortProjectDetailsRespModel!.merchantIdentifier,
applePayAccessCode: payfortViewModel.payfortProjectDetailsRespModel!.accessCode,
applePayShaRequestPhrase: payfortViewModel.payfortProjectDetailsRespModel!.shaRequest,
currency: "SAR",
currency: appState.getAuthenticatedUser()!.outSa! == 0 ? "SAR" : "AED",
onFailed: (failureResult) async {
log("failureResult: ${failureResult.message.toString()}");
Navigator.of(context).pop();
showCommonBottomSheetWithoutHeight(
context,
child: Utils.getErrorWidget(loadingText: failureResult.message.toString()),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
},
onSucceeded: (successResult) async {
Navigator.of(context).pop();
log("successResult: ${successResult.responseMessage.toString()}");
checkPaymentStatus();
},
// projectId: appo.projectID,
// serviceTypeEnum: ServiceTypeEnum.appointmentPayment,

@ -1,17 +1,14 @@
import 'dart:async';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/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/appointment_card.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/widgets/custom_tab_bar.dart';
import 'package:hmg_patient_app_new/widgets/shimmer/movies_shimmer_widget.dart';
@ -38,39 +35,33 @@ class _MyAppointmentsPageState extends State<MyAppointmentsPage> {
@override
Widget build(BuildContext context) {
myAppointmentsViewModel = Provider.of<MyAppointmentsViewModel>(context);
myAppointmentsViewModel = Provider.of<MyAppointmentsViewModel>(context, listen: false);
return Scaffold(
backgroundColor: AppColors.bgScaffoldColor,
appBar: AppBar(
title: "Appointments List".needTranslation.toText18(),
backgroundColor: AppColors.bgScaffoldColor,
),
body: SingleChildScrollView(
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
"Appointments List".needTranslation.toText24(isBold: true),
],
).paddingSymmetrical(24.h, 24.h),
CustomTabBar(
activeTextColor: Color(0xffED1C2B),
activeBackgroundColor: Color(0xffED1C2B).withValues(alpha: .1),
tabs: [
CustomTabBarModel(null, "All Appt.".needTranslation),
CustomTabBarModel(null, "Upcoming".needTranslation),
CustomTabBarModel(null, "Completed".needTranslation),
],
onTabChange: (index) {
print(index);
myAppointmentsViewModel.onTabChange(index);
},
).paddingSymmetrical(24.h, 0.h),
Consumer<MyAppointmentsViewModel>(builder: (context, myAppointmentsVM, child) {
return getSelectedTabData(myAppointmentsVM.selectedTabIndex, myAppointmentsVM);
}),
],
body: CollapsingListView(
title: "Appointments List".needTranslation,
child: SingleChildScrollView(
child: Column(
children: [
SizedBox(height: 16.h),
CustomTabBar(
activeTextColor: Color(0xffED1C2B),
activeBackgroundColor: Color(0xffED1C2B).withValues(alpha: .1),
tabs: [
CustomTabBarModel(null, "All Appt.".needTranslation),
CustomTabBarModel(null, "Upcoming".needTranslation),
CustomTabBarModel(null, "Completed".needTranslation),
],
onTabChange: (index) {
print(index);
myAppointmentsViewModel.onTabChange(index);
},
).paddingSymmetrical(24.h, 0.h),
Consumer<MyAppointmentsViewModel>(builder: (context, myAppointmentsVM, child) {
return getSelectedTabData(myAppointmentsVM.selectedTabIndex, myAppointmentsVM);
}),
],
),
),
),
);
@ -83,9 +74,9 @@ class _MyAppointmentsPageState extends State<MyAppointmentsPage> {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 24.h),
// Expandable list
ListView.separated(
padding: EdgeInsets.only(top: 24.h),
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemCount: myAppointmentsVM.isMyAppointmentsLoading
@ -106,7 +97,6 @@ class _MyAppointmentsPageState extends State<MyAppointmentsPage> {
child: AnimatedContainer(
duration: Duration(milliseconds: 300),
curve: Curves.easeInOut,
margin: EdgeInsets.symmetric(vertical: 8.h),
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: true),
child: AppointmentCard(
patientAppointmentHistoryResponseModel: myAppointmentsVM.patientAppointmentsHistoryList[index],
@ -127,7 +117,6 @@ class _MyAppointmentsPageState extends State<MyAppointmentsPage> {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 24.h),
// Expandable list
ListView.separated(
shrinkWrap: true,
@ -171,7 +160,6 @@ class _MyAppointmentsPageState extends State<MyAppointmentsPage> {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 24.h),
// Expandable list
ListView.separated(
shrinkWrap: true,
@ -179,32 +167,32 @@ class _MyAppointmentsPageState extends State<MyAppointmentsPage> {
itemCount: myAppointmentsVM.isMyAppointmentsLoading
? 5
: myAppointmentsVM.patientArrivedAppointmentsHistoryList.isNotEmpty
? myAppointmentsVM.patientArrivedAppointmentsHistoryList.length
: 1,
? myAppointmentsVM.patientArrivedAppointmentsHistoryList.length
: 1,
itemBuilder: (context, index) {
return myAppointmentsVM.isMyAppointmentsLoading
? const MoviesShimmerWidget().paddingSymmetrical(24.h, 0.h)
: myAppointmentsVM.patientArrivedAppointmentsHistoryList.isNotEmpty
? AnimationConfiguration.staggeredList(
position: index,
duration: const Duration(milliseconds: 500),
child: SlideAnimation(
verticalOffset: 100.0,
child: FadeInAnimation(
child: AnimatedContainer(
duration: Duration(milliseconds: 300),
curve: Curves.easeInOut,
margin: EdgeInsets.symmetric(vertical: 8.h),
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: true),
child: AppointmentCard(
patientAppointmentHistoryResponseModel: myAppointmentsVM.patientArrivedAppointmentsHistoryList[index],
myAppointmentsViewModel: myAppointmentsViewModel,
),
).paddingSymmetrical(24.h, 0.h),
),
),
)
: Utils.getNoDataWidget(context);
? AnimationConfiguration.staggeredList(
position: index,
duration: const Duration(milliseconds: 500),
child: SlideAnimation(
verticalOffset: 100.0,
child: FadeInAnimation(
child: AnimatedContainer(
duration: Duration(milliseconds: 300),
curve: Curves.easeInOut,
margin: EdgeInsets.symmetric(vertical: 8.h),
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: true),
child: AppointmentCard(
patientAppointmentHistoryResponseModel: myAppointmentsVM.patientArrivedAppointmentsHistoryList[index],
myAppointmentsViewModel: myAppointmentsViewModel,
),
).paddingSymmetrical(24.h, 0.h),
),
),
)
: Utils.getNoDataWidget(context);
},
separatorBuilder: (BuildContext cxt, int index) => SizedBox(height: 16.h),
),

@ -0,0 +1,160 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/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/lab/collapsing_list_view.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:provider/provider.dart';
import '../../widgets/chip/app_custom_chip_widget.dart';
class MyDoctorsPage extends StatelessWidget {
MyDoctorsPage({super.key});
late MyAppointmentsViewModel myAppointmentsViewModel;
@override
Widget build(BuildContext context) {
myAppointmentsViewModel = Provider.of<MyAppointmentsViewModel>(context, listen: false);
return Scaffold(
backgroundColor: AppColors.bgScaffoldColor,
body: CollapsingListView(
title: LocaleKeys.myDoctor.tr(context: context),
child: SingleChildScrollView(
child: Consumer<MyAppointmentsViewModel>(builder: (context, myAppointmentsVM, child) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 16.h),
ListView.separated(
scrollDirection: Axis.vertical,
itemCount: myAppointmentsVM.isPatientMyDoctorsLoading ? 5 : myAppointmentsVM.patientMyDoctorsList.length,
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
padding: EdgeInsets.only(left: 24.h, right: 24.h),
itemBuilder: (context, index) {
return myAppointmentsVM.isPatientMyDoctorsLoading
? Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 20.h,
hasShadow: true,
),
child: Padding(
padding: EdgeInsets.all(14.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Image.network(
"https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png",
width: 63.h,
height: 63.h,
fit: BoxFit.fill,
).circle(100).toShimmer2(isShow: true),
SizedBox(width: 16.h),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
"Dr John Smith".toText16(isBold: true).toShimmer2(isShow: true),
SizedBox(height: 8.h),
Wrap(
direction: Axis.horizontal,
spacing: 3.h,
runSpacing: 4.h,
children: [
AppCustomChipWidget(labelText: "").toShimmer2(isShow: true, width: 16.h),
AppCustomChipWidget(labelText: "").toShimmer2(isShow: true, width: 16.h),
],
),
],
),
),
],
),
],
),
),
)
: AnimationConfiguration.staggeredList(
position: index,
duration: const Duration(milliseconds: 1000),
child: SlideAnimation(
verticalOffset: 100.0,
child: FadeInAnimation(
child: Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 20.h,
hasShadow: true,
),
child: Padding(
padding: EdgeInsets.all(14.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Image.network(
myAppointmentsVM.patientMyDoctorsList[index].doctorImageURL!,
width: 63.h,
height: 63.h,
fit: BoxFit.fill,
).circle(100).toShimmer2(isShow: false),
SizedBox(width: 16.h),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
(myAppointmentsVM.patientMyDoctorsList[index].doctorName).toString().toText16(isBold: true).toShimmer2(isShow: false),
SizedBox(height: 8.h),
Wrap(
direction: Axis.horizontal,
spacing: 3.h,
runSpacing: 4.h,
children: [
AppCustomChipWidget(labelText: myAppointmentsVM.patientMyDoctorsList[index].clinicName).toShimmer2(isShow: false, width: 16.h),
AppCustomChipWidget(labelText: myAppointmentsVM.patientMyDoctorsList[index].projectName).toShimmer2(isShow: false, width: 16.h),
],
),
],
),
),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
"".toText16(),
Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_icon, width: 15.h, height: 15.h, fit: BoxFit.contain, iconColor: AppColors.textColor),
],
),
],
),
),
),
),
),
);
},
separatorBuilder: (BuildContext cxt, int index) => SizedBox(height: 16.h),
),
SizedBox(height: 60.h),
],
);
}),
),
),
);
}
}

@ -142,7 +142,7 @@ class _AppointmentCardState extends State<AppointmentCard> {
Wrap(
direction: Axis.horizontal,
spacing: 3.h,
runSpacing: -8.h,
runSpacing: 4.h,
children: [
AppCustomChipWidget(labelText: widget.patientAppointmentHistoryResponseModel.clinicName!),
AppCustomChipWidget(labelText: widget.patientAppointmentHistoryResponseModel.projectName!),

@ -51,7 +51,7 @@ class AppointmentDoctorCard extends StatelessWidget {
Wrap(
direction: Axis.horizontal,
spacing: 3.h,
runSpacing: -8.h,
runSpacing: 4.h,
children: [
AppCustomChipWidget(labelText: patientAppointmentHistoryResponseModel.clinicName!),
AppCustomChipWidget(labelText: patientAppointmentHistoryResponseModel.projectName!),

@ -213,7 +213,7 @@ class _RegisterNew extends State<RegisterNew> {
countryCode: authVM.selectedCountrySignup.countryCode,
initialPhoneNumber: "",
textController: authVM.phoneNumberController,
isEnableCountryDropdown: true,
isEnableCountryDropdown: false,
onCountryChange: authVM.onCountryChange,
onChange: authVM.onPhoneNumberChange,
buttons: [

@ -46,7 +46,7 @@ class _RegisterNew extends State<RegisterNewStep2> {
appBar: CustomAppBar(
onBackPressed: () {
Navigator.of(context).pop();
authVM!.clearDefaultInputValues();
// authVM!.clearDefaultInputValues();
},
onLanguageChanged: (lang) {},
hideLogoAndLang: true,
@ -217,15 +217,14 @@ class _RegisterNew extends State<RegisterNewStep2> {
TextInputWidget(
labelText: LocaleKeys.mobileNumber.tr(),
hintText: (appState.getUserRegistrationPayload.patientMobileNumber.toString() ?? ""),
controller: authVM!.phoneNumberController,
isEnable: true,
controller: null,
isEnable: false,
prefix: null,
isAllowRadius: false,
isBorderAllowed: false,
isAllowLeadingIcon: true,
isReadOnly: true,
leadingIcon: AppAssets.call,
onChange: (value) {})
leadingIcon: AppAssets.call)
.paddingSymmetrical(0.h, 16.h),
Divider(
height: 1,
@ -233,15 +232,15 @@ class _RegisterNew extends State<RegisterNewStep2> {
),
TextInputWidget(
labelText: LocaleKeys.dob.tr(),
hintText: authVM!.isUserFromUAE() ? appState.getUserRegistrationPayload.dob! : (appState.getNHICUserData.dateOfBirth ?? ""),
controller: authVM!.dobController,
isEnable: authVM!.isUserFromUAE() ? true : false,
hintText: authVM!.isUserFromUAE() ? appState.getUserRegistrationPayload.dob! : appState.getNHICUserData.dateOfBirth ?? "",
controller: authVM!.isUserFromUAE() ? authVM!.dobController : null,
isEnable: false,
prefix: null,
isBorderAllowed: false,
isAllowLeadingIcon: true,
isReadOnly: true,
leadingIcon: AppAssets.birthday_cake,
selectionType: authVM!.isUserFromUAE() ? SelectionTypeEnum.calendar : null,
selectionType: null,
).paddingSymmetrical(0.h, 16.h),
],
),
@ -256,7 +255,7 @@ class _RegisterNew extends State<RegisterNewStep2> {
icon: AppAssets.cancel,
onPressed: () {
Navigator.of(context).pop();
authVM!.clearDefaultInputValues();
// authVM!.clearDefaultInputValues();
},
backgroundColor: AppColors.secondaryLightRedColor,
borderColor: AppColors.secondaryLightRedColor,

@ -84,7 +84,7 @@ class _SavedLogin extends State<SavedLogin> {
children: [
// Last login info
("${LocaleKeys.lastloginBy.tr()} ${loginType.displayName}").toText14(isBold: true, color: AppColors.greyTextColor),
("${LocaleKeys.lastLoginBy.tr()} ${loginType.displayName}").toText14(isBold: true, color: AppColors.greyTextColor),
(appState.getSelectDeviceByImeiRespModelElement!.createdOn != null
? DateUtil.getFormattedDate(DateUtil.convertStringToDate(appState.getSelectDeviceByImeiRespModelElement!.createdOn!), "d MMMM, y 'at' HH:mm")
: '--')

@ -0,0 +1,159 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_view_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.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/theme/colors.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:provider/provider.dart';
class BookAppointmentPage extends StatefulWidget {
const BookAppointmentPage({super.key});
@override
State<BookAppointmentPage> createState() => _BookAppointmentPageState();
}
class _BookAppointmentPageState extends State<BookAppointmentPage> {
late AppState appState;
@override
Widget build(BuildContext context) {
appState = getIt.get<AppState>();
return Scaffold(
backgroundColor: AppColors.bgScaffoldColor,
body: CollapsingListView(
title: LocaleKeys.bookAppo.tr(context: context),
child: SingleChildScrollView(
child: Consumer<BookAppointmentsViewModel>(builder: (context, bookAppointmentsVM, child) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 16.h),
CustomTabBar(
activeTextColor: Color(0xffED1C2B),
activeBackgroundColor: Color(0xffED1C2B).withValues(alpha: .1),
tabs: [
CustomTabBarModel(null, "General".needTranslation),
CustomTabBarModel(null, "LiveCare".needTranslation),
],
onTabChange: (index) {
bookAppointmentsVM.onTabChanged(index);
},
).paddingSymmetrical(24.h, 0.h),
SizedBox(height: 24.h),
getSelectedTabData(bookAppointmentsVM.selectedTabIndex),
],
);
}),
),
),
);
}
Widget getSelectedTabData(int index) {
switch (index) {
case 0:
return Column(
children: [
Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 24.h,
hasShadow: false,
),
child: Padding(
padding: EdgeInsets.all(16.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Utils.buildSvgWithAssets(icon: AppAssets.search_by_clinic_icon, width: 40.h, height: 40.h),
SizedBox(width: 12.h),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
"Search By Clinic".needTranslation.toText14(color: AppColors.textColor, weight: FontWeight.w500),
"Tap to select clinic".needTranslation.toText12(color: AppColors.primaryRedColor, fontWeight: FontWeight.w500),
],
),
],
),
Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_icon, iconColor: AppColors.textColor, width: 15.h, height: 15.h),
],
).onPress(() {
Navigator.of(context).push(
FadePage(
page: SelectClinicPage(),
),
);
}),
SizedBox(height: 16.h),
Divider(color: AppColors.borderOnlyColor.withValues(alpha: 0.1), height: 1.h),
SizedBox(height: 16.h),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Utils.buildSvgWithAssets(icon: AppAssets.search_by_doctor_icon, width: 40.h, height: 40.h),
SizedBox(width: 12.h),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
"Search By Doctor".needTranslation.toText14(color: AppColors.textColor, weight: FontWeight.w500),
"Tap to select".needTranslation.toText12(color: AppColors.primaryRedColor, fontWeight: FontWeight.w500),
],
),
],
),
Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_icon, iconColor: AppColors.textColor, width: 15.h, height: 15.h),
],
).onPress(() {}),
SizedBox(height: 16.h),
Divider(color: AppColors.borderOnlyColor.withValues(alpha: 0.1), height: 1.h),
SizedBox(height: 16.h),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Utils.buildSvgWithAssets(icon: AppAssets.search_by_region_icon, width: 40.h, height: 40.h),
SizedBox(width: 12.h),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
"Search By Region".needTranslation.toText14(color: AppColors.textColor, weight: FontWeight.w500),
"Central Region".needTranslation.toText12(color: AppColors.primaryRedColor, fontWeight: FontWeight.w500),
],
),
],
),
Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_icon, iconColor: AppColors.textColor, width: 15.h, height: 15.h),
],
).onPress(() {}),
],
),
),
),
],
).paddingSymmetrical(24.h, 0.h);
default:
SizedBox.shrink();
}
return Container();
}
}

@ -0,0 +1,29 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/lab/collapsing_list_view.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
class SelectClinicPage extends StatefulWidget {
const SelectClinicPage({super.key});
@override
State<SelectClinicPage> createState() => _SelectClinicPageState();
}
class _SelectClinicPageState extends State<SelectClinicPage> {
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: AppColors.bgScaffoldColor,
body: CollapsingListView(
title: LocaleKeys.selectClinic.tr(context: context),
child: SingleChildScrollView(
child: Column(
children: [],
),
),
),
);
}
}

@ -0,0 +1,109 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/habib_wallet/habib_wallet_view_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/habib_wallet/recharge_wallet_page.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/widgets/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart';
import 'package:provider/provider.dart';
class HabibWalletPage extends StatefulWidget {
const HabibWalletPage({super.key});
@override
State<HabibWalletPage> createState() => _HabibWalletState();
}
class _HabibWalletState extends State<HabibWalletPage> {
@override
Widget build(BuildContext context) {
AppState _appState = getIt.get<AppState>();
return Scaffold(
backgroundColor: AppColors.bgScaffoldColor,
body: CollapsingListView(
title: LocaleKeys.myWallet.tr(),
child: Padding(
padding: EdgeInsets.all(24.h),
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
width: double.infinity,
height: 180.h,
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.blackBgColor,
borderRadius: 24,
),
child: Padding(
padding: EdgeInsets.all(16.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
"${_appState.getAuthenticatedUser()!.firstName!} ${_appState.getAuthenticatedUser()!.lastName!}".toText19(isBold: true, color: AppColors.whiteColor),
"MRN: ${_appState.getAuthenticatedUser()!.patientId!}".toText14(weight: FontWeight.w500, color: AppColors.greyTextColor),
],
),
Utils.buildSvgWithAssets(icon: AppAssets.habiblogo, width: 24.h, height: 24.h),
],
),
Spacer(),
LocaleKeys.balanceAmount.tr(context: context).toText14(weight: FontWeight.w500, color: AppColors.whiteColor),
Consumer<HabibWalletViewModel>(builder: (context, habibWalletVM, child) {
return Utils.getPaymentAmountWithSymbol(habibWalletVM.habibWalletAmount.toString().toText32(isBold: true, color: AppColors.whiteColor), AppColors.whiteColor, 13.h,
isExpanded: false)
.toShimmer2(isShow: habibWalletVM.isWalletAmountLoading, radius: 12.h, width: 80.h, height: 40.h);
}),
],
),
),
),
SizedBox(height: 16.h),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
CustomButton(
icon: AppAssets.recharge_icon,
iconSize: 21.h,
text: "Recharge".needTranslation,
onPressed: () {
Navigator.of(context).push(
FadePage(
page: RechargeWalletPage(),
),
);
},
backgroundColor: AppColors.infoColor,
borderColor: AppColors.infoColor,
textColor: AppColors.whiteColor,
fontSize: 14,
fontWeight: FontWeight.bold,
borderRadius: 12,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 40.h,
),
],
),
],
),
),
),
),
);
}
}

@ -0,0 +1,229 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.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/widgets/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart';
import 'package:hmg_patient_app_new/widgets/input_widget.dart';
import 'widgets/select-medical_file.dart';
class RechargeWalletPage extends StatefulWidget {
const RechargeWalletPage({super.key});
@override
State<RechargeWalletPage> createState() => _RechargeWalletPageState();
}
class _RechargeWalletPageState extends State<RechargeWalletPage> {
FocusNode textFocusNode = FocusNode();
@override
Widget build(BuildContext context) {
AppState appState = getIt.get<AppState>();
return Scaffold(
backgroundColor: AppColors.bgScaffoldColor,
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: CollapsingListView(
title: LocaleKeys.createAdvancedPayment.tr(context: context),
child: SingleChildScrollView(
child: Padding(
padding: EdgeInsets.all(24.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
height: 135.h,
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 24.h,
hasShadow: false,
side: BorderSide(color: AppColors.textColor, width: 2.h),
),
child: Padding(
padding: EdgeInsets.all(16.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
"Enter an amount".needTranslation.toText14(color: AppColors.greyTextColor, weight: FontWeight.w500),
Spacer(),
Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Utils.getPaymentAmountWithSymbol(
SizedBox(
width: 150.h,
child: TextInputWidget(
labelText: "",
hintText: "",
isEnable: true,
prefix: null,
isAllowRadius: true,
isBorderAllowed: false,
isAllowLeadingIcon: true,
autoFocus: true,
fontSize: 40,
padding: EdgeInsets.symmetric(horizontal: 8.h, vertical: 0.h),
focusNode: textFocusNode,
isWalletAmountInput: true,
// leadingIcon: AppAssets.student_card,
),
),
AppColors.textColor,
13.h,
isExpanded: false),
const Spacer(),
"SAR".needTranslation.toText20(color: AppColors.greyTextColor, weight: FontWeight.w500),
],
),
],
),
),
),
SizedBox(height: 24.h),
Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 24.h,
hasShadow: false,
),
child: Padding(
padding: EdgeInsets.all(16.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Utils.buildSvgWithAssets(icon: AppAssets.my_account_icon, width: 40.h, height: 40.h),
SizedBox(width: 8.h),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
LocaleKeys.myAccount.tr(context: context).toText16(color: AppColors.textColor, weight: FontWeight.w500),
"${LocaleKeys.medicalFile.tr(context: context)}: ${appState.getAuthenticatedUser()!.patientId}"
.toText14(color: AppColors.greyTextColor, weight: FontWeight.w500),
],
),
],
),
Utils.buildSvgWithAssets(icon: AppAssets.arrow_down, width: 25.h, height: 25.h),
],
).onPress(() {
// showCommonBottomSheetWithoutHeight(context, title: "Select Medical File".needTranslation, child: SelectMedicalFile(), callBackFunc: () {}, isFullScreen: false);
showCommonBottomSheetWithoutHeight(context, title: "Select Medical File".needTranslation, child: const MultiPageBottomSheet(), callBackFunc: () {}, isFullScreen: false);
}),
SizedBox(height: 16.h),
Divider(color: AppColors.borderOnlyColor.withValues(alpha: 0.1), height: 1.h),
SizedBox(height: 16.h),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Utils.buildSvgWithAssets(icon: AppAssets.select_hospital_icon, width: 40.h, height: 40.h),
SizedBox(width: 8.h),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
LocaleKeys.hospital.tr(context: context).toText16(color: AppColors.textColor, weight: FontWeight.w500),
"Olaya".toText14(color: AppColors.greyTextColor, weight: FontWeight.w500),
],
),
],
),
Utils.buildSvgWithAssets(icon: AppAssets.arrow_down, width: 25.h, height: 25.h),
],
),
SizedBox(height: 16.h),
Divider(color: AppColors.borderOnlyColor.withValues(alpha: 0.1), height: 1.h),
SizedBox(height: 16.h),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Utils.buildSvgWithAssets(icon: AppAssets.email_icon, width: 40.h, height: 40.h),
SizedBox(width: 8.h),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
LocaleKeys.email.tr(context: context).toText12(color: AppColors.greyTextColor, fontWeight: FontWeight.w500),
"${appState.getAuthenticatedUser()!.emailAddress}".toText16(color: AppColors.textColor, weight: FontWeight.w500),
],
),
],
),
],
),
SizedBox(height: 16.h),
Divider(color: AppColors.borderOnlyColor.withValues(alpha: 0.1), height: 1.h),
SizedBox(height: 16.h),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Utils.buildSvgWithAssets(icon: AppAssets.notes_icon, width: 40.h, height: 40.h),
SizedBox(width: 8.h),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
LocaleKeys.notes.tr(context: context).toText12(color: AppColors.greyTextColor, fontWeight: FontWeight.w500),
"Lorem Ipsum".toText16(color: AppColors.textColor, weight: FontWeight.w500),
],
),
],
),
],
),
SizedBox(height: 8.h),
],
),
),
),
],
),
),
),
)),
Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 24.h,
hasShadow: false,
),
child: CustomButton(
text: LocaleKeys.next.tr(context: context),
onPressed: () {},
backgroundColor: AppColors.primaryRedColor,
borderColor: AppColors.primaryRedColor,
textColor: AppColors.whiteColor,
fontSize: 16,
fontWeight: FontWeight.w500,
borderRadius: 12,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 50.h,
icon: AppAssets.appointment_pay_icon,
iconColor: AppColors.whiteColor,
iconSize: 18.h,
).paddingSymmetrical(24.h, 24.h),
),
],
),
);
}
}

@ -0,0 +1,238 @@
// import 'package:flutter/cupertino.dart';
// import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
//
// class SelectMedicalFile extends StatelessWidget {
// const SelectMedicalFile({super.key});
//
// @override
// Widget build(BuildContext context) {
// return Padding(
// padding: EdgeInsets.all(24.h),
// child: Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// Container()
// ],
// ),
// );
// }
// }
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
class MultiPageBottomSheet extends StatefulWidget {
const MultiPageBottomSheet({Key? key}) : super(key: key);
@override
State<MultiPageBottomSheet> createState() => _MultiPageBottomSheetState();
}
class _MultiPageBottomSheetState extends State<MultiPageBottomSheet> {
final PageController _pageController = PageController();
int _currentPage = 0;
final int _totalPages = 3;
late AppState appState;
@override
void dispose() {
_pageController.dispose();
super.dispose();
}
void _nextPage() {
if (_currentPage < _totalPages - 1) {
_pageController.animateToPage(
_currentPage + 2,
duration: const Duration(milliseconds: 300),
curve: Curves.easeInOut,
);
}
}
void _previousPage() {
if (_currentPage > 0) {
_pageController.previousPage(
duration: const Duration(milliseconds: 300),
curve: Curves.easeInOut,
);
}
}
@override
Widget build(BuildContext context) {
appState = getIt.get<AppState>();
return Container(
height: MediaQuery.of(context).size.height * 0.38,
child: Column(
children: [
SizedBox(height: 12.h),
Expanded(
child: PageView(
controller: _pageController,
onPageChanged: (index) {
setState(() {
_currentPage = index;
});
},
children: [
_buildPage1(),
_buildPage2(),
_buildPage3(),
],
),
),
],
),
);
}
Widget _buildPage1() {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 16.h,
hasShadow: false,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
LocaleKeys.myMedicalFile.tr(context: context).toText16(color: AppColors.textColor, weight: FontWeight.w500),
"${LocaleKeys.fileno.tr(context: context)}: ${appState.getAuthenticatedUser()!.patientId}".toText12(color: AppColors.greyTextColor, fontWeight: FontWeight.w500),
],
),
Utils.buildSvgWithAssets(icon: AppAssets.forward_chevron_icon, iconColor: AppColors.textColor, width: 15.h, height: 15.h),
],
).paddingAll(16.h),
).onPress(() {
Navigator.of(context).pop();
}),
SizedBox(height: 16.h),
Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 16.h,
hasShadow: false,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
LocaleKeys.familyTitle.tr(context: context).toText16(color: AppColors.textColor, weight: FontWeight.w500),
"Select a medical file from your family".needTranslation.toText14(color: AppColors.greyTextColor, weight: FontWeight.w500),
],
),
Utils.buildSvgWithAssets(icon: AppAssets.forward_chevron_icon, iconColor: AppColors.textColor, width: 15.h, height: 15.h),
],
).paddingAll(16.h),
),
SizedBox(height: 16.h),
Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 16.h,
hasShadow: false,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
LocaleKeys.otherAccount.tr(context: context).toText16(color: AppColors.textColor, weight: FontWeight.w500),
"Any active medical file from HMG".toText14(color: AppColors.greyTextColor, weight: FontWeight.w500),
],
),
Utils.buildSvgWithAssets(icon: AppAssets.forward_chevron_icon, iconColor: AppColors.textColor, width: 15.h, height: 15.h),
],
).paddingAll(16.h),
).onPress(() {
_pageController.animateToPage(
2,
duration: const Duration(milliseconds: 300),
curve: Curves.easeInOut,
);
}),
],
);
}
Widget _buildPage2() {
return Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Page 2: Settings',
style: Theme.of(context).textTheme.headlineSmall,
),
const SizedBox(height: 16),
SwitchListTile(
title: const Text('Enable notifications'),
value: true,
onChanged: (value) {},
),
SwitchListTile(
title: const Text('Dark mode'),
value: false,
onChanged: (value) {},
),
const ListTile(
leading: Icon(Icons.language),
title: Text('Language'),
subtitle: Text('English'),
trailing: Icon(Icons.arrow_forward_ios),
),
],
),
);
}
Widget _buildPage3() {
return Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Page 3: Summary',
style: Theme.of(context).textTheme.headlineSmall,
),
const SizedBox(height: 16),
const Card(
child: Padding(
padding: EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Configuration Complete!'),
SizedBox(height: 8),
Text('Your settings have been saved successfully.'),
],
),
),
),
],
),
);
}
}

@ -1,3 +1,5 @@
import 'dart:async';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
@ -10,6 +12,9 @@ import 'package:hmg_patient_app_new/extensions/int_extensions.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/authentication/authentication_view_model.dart';
import 'package:hmg_patient_app_new/features/habib_wallet/habib_wallet_view_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart';
import 'package:hmg_patient_app_new/features/prescriptions/prescriptions_view_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/authentication/quick_login.dart';
import 'package:hmg_patient_app_new/presentation/home/data/landing_page_data.dart';
@ -37,23 +42,41 @@ class LandingPage extends StatefulWidget {
class _LandingPageState extends State<LandingPage> {
late final AuthenticationViewModel authVM;
bool isDone = false;
late final HabibWalletViewModel habibWalletVM;
late AppState appState;
late MyAppointmentsViewModel myAppointmentsViewModel;
late PrescriptionsViewModel prescriptionsViewModel;
@override
void initState() {
authVM = context.read<AuthenticationViewModel>();
habibWalletVM = context.read<HabibWalletViewModel>();
authVM.savePushTokenToAppState();
if (mounted) {
authVM.checkLastLoginStatus(() {
showQuickLogin(context);
showQuickLogin(context);
});
}
scheduleMicrotask(() {
if (appState.isAuthenticated) {
habibWalletVM.initHabibWalletProvider();
habibWalletVM.getPatientBalanceAmount();
myAppointmentsViewModel.initAppointmentsViewModel();
myAppointmentsViewModel.getPatientAppointments(true, false);
myAppointmentsViewModel.getPatientMyDoctors();
prescriptionsViewModel.initPrescriptionsViewModel();
}
});
super.initState();
}
@override
Widget build(BuildContext context) {
AppState appState = getIt.get<AppState>();
appState = getIt.get<AppState>();
NavigationService navigationService = getIt.get<NavigationService>();
myAppointmentsViewModel = Provider.of<MyAppointmentsViewModel>(context, listen: false);
prescriptionsViewModel = Provider.of<PrescriptionsViewModel>(context, listen: false);
return Scaffold(
backgroundColor: AppColors.bgScaffoldColor,
body: SingleChildScrollView(
@ -324,29 +347,22 @@ class _LandingPageState extends State<LandingPage> {
void showQuickLogin(BuildContext context) {
showCommonBottomSheetWithoutHeight(
context,
title: "",
isCloseButtonVisible: false,
child:
StatefulBuilder(
builder: (context, setState) {
return QuickLogin(
isDone: isDone,
onPressed: () {
// sharedPref.setBool(HAS_ENABLED_QUICK_LOGIN, true);
authVM.loginWithFingerPrintFace((){
isDone = true;
setState(() {
child: StatefulBuilder(builder: (context, setState) {
return QuickLogin(
isDone: isDone,
onPressed: () {
// sharedPref.setBool(HAS_ENABLED_QUICK_LOGIN, true);
authVM.loginWithFingerPrintFace(() {
isDone = true;
setState(() {});
});
});
},
);
},
);
}),
// height: isDone == false ? ResponsiveExtension.screenHeight * 0.5 : ResponsiveExtension.screenHeight * 0.3,
// height: isDone == false ? ResponsiveExtension.screenHeight * 0.5 : ResponsiveExtension.screenHeight * 0.3,
isFullScreen: false,
callBackFunc: () {
isDone = true;

@ -1,4 +1,5 @@
import 'package:flutter/material.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/medical_file/medical_file_page.dart';
import 'package:hmg_patient_app_new/widgets/bottom_navigation/bottom_navigation.dart';
@ -23,7 +24,7 @@ class _LandingNavigationState extends State<LandingNavigation> {
children: [
const LandingPage(),
MedicalFilePage(),
const LandingPage(),
const BookAppointmentPage(),
const LandingPage(),
const LandingPage(),
],

@ -4,8 +4,12 @@ import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/habib_wallet/habib_wallet_view_model.dart';
import 'package:hmg_patient_app_new/presentation/habib_wallet/habib_wallet_page.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/transitions/fade_page.dart';
import 'package:provider/provider.dart';
class HabibWalletCard extends StatelessWidget {
const HabibWalletCard({super.key});
@ -69,19 +73,21 @@ class HabibWalletCard extends StatelessWidget {
],
),
SizedBox(height: 4.h),
Row(
children: [
Utils.buildSvgWithAssets(
icon: AppAssets.saudi_riyal_icon,
iconColor: AppColors.dividerColor,
width: 24.h,
height: 24.h,
fit: BoxFit.contain,
),
SizedBox(width: 8.h),
"200.18".toText32(isBold: true),
],
),
Consumer<HabibWalletViewModel>(builder: (context, habibWalletVM, child) {
return Row(
children: [
Utils.buildSvgWithAssets(
icon: AppAssets.saudi_riyal_icon,
iconColor: AppColors.dividerColor,
width: 24.h,
height: 24.h,
fit: BoxFit.contain,
),
SizedBox(width: 8.h),
habibWalletVM.habibWalletAmount.toString().toText32(isBold: true).toShimmer2(isShow: habibWalletVM.isWalletAmountLoading, radius: 12.h, width: 80.h, height: 40.h),
],
);
}),
Padding(
padding: EdgeInsets.symmetric(horizontal: 50.h),
child: Row(
@ -90,7 +96,13 @@ class HabibWalletCard extends StatelessWidget {
SizedBox(width: 2.h),
Icon(Icons.arrow_forward_ios, color: AppColors.primaryRedColor, size: 10.h),
],
),
).onPress(() {
Navigator.of(context).push(
FadePage(
page: HabibWalletPage(),
),
);
}),
),
SizedBox(height: 16.h),
Row(
@ -103,7 +115,7 @@ class HabibWalletCard extends StatelessWidget {
CustomButton(
icon: AppAssets.recharge_icon,
iconSize: 18.h,
text: "Recharge",
text: "Recharge".needTranslation,
onPressed: () {},
backgroundColor: AppColors.infoColor,
borderColor: AppColors.infoColor,

@ -2,8 +2,10 @@ import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/presentation/appointments/my_doctors_page.dart';
import 'package:hmg_patient_app_new/presentation/insurance/insurance_home_page.dart';
import 'package:hmg_patient_app_new/presentation/lab/lab_orders_page.dart';
import 'package:hmg_patient_app_new/presentation/medical_file/patient_sickleaves_list_page.dart';
import 'package:hmg_patient_app_new/presentation/prescriptions/prescriptions_list_page.dart';
import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart';
@ -85,6 +87,22 @@ class SmallServiceCard extends StatelessWidget {
),
);
break;
case "my_doctors":
Navigator.of(context).push(
FadePage(
page: MyDoctorsPage(),
),
);
break;
case "sick_leaves":
Navigator.of(context).push(
FadePage(
page: PatientSickleavesListPage(),
),
);
break;
default:
// Handle unknown service
break;

@ -9,6 +9,7 @@ import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/features/insurance/insurance_view_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/insurance/widgets/patient_insurance_card.dart';
import 'package:hmg_patient_app_new/presentation/lab/collapsing_list_view.dart';
import 'package:hmg_patient_app_new/presentation/lab/search_lab_report.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
@ -39,52 +40,56 @@ class _InsuranceHomePageState extends State<InsuranceHomePage> {
@override
Widget build(BuildContext context) {
insuranceViewModel = Provider.of<InsuranceViewModel>(context);
insuranceViewModel = Provider.of<InsuranceViewModel>(context, listen: false);
return Scaffold(
backgroundColor: AppColors.bgScaffoldColor,
appBar: AppBar(
title: LocaleKeys.insurance.tr(context: context).toText18(),
backgroundColor: AppColors.bgScaffoldColor,
),
body: SingleChildScrollView(
child: Consumer<InsuranceViewModel>(builder: (context, insuranceVM, child) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
"${LocaleKeys.insurance.tr(context: context)} ${LocaleKeys.updateInsurance.tr(context: context)}".toText24(isBold: true),
CustomButton(
icon: AppAssets.insurance_history_icon,
iconColor: AppColors.primaryRedColor,
iconSize: 21.h,
text: LocaleKeys.history.tr(context: context),
onPressed: () {
insuranceVM.setIsInsuranceHistoryLoading(true);
insuranceVM.getPatientInsuranceCardHistory();
showCommonBottomSheet(context,
child: InsuranceHistory(), callBackFunc: (str){}, title: "", height: ResponsiveExtension.screenHeight * 0.65, isCloseButtonVisible: false, isFullScreen: false);
},
backgroundColor: AppColors.primaryRedColor.withOpacity(0.1),
borderColor: AppColors.primaryRedColor.withOpacity(0.0),
textColor: AppColors.primaryRedColor,
fontSize: 14,
fontWeight: FontWeight.w600,
borderRadius: 12,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 40.h,
),
],
).paddingSymmetrical(24.h, 24.h),
insuranceVM.isInsuranceLoading
? const MoviesShimmerWidget().paddingSymmetrical(24.h, 0)
: PatientInsuranceCard(
insuranceCardDetailsModel: insuranceVM.patientInsuranceList.first,
isInsuranceExpired: DateTime.now().isAfter(DateUtil.convertStringToDate(insuranceVM.patientInsuranceList.first.cardValidTo))),
],
);
}),
body: CollapsingListView(
title: "${LocaleKeys.insurance.tr(context: context)} ${LocaleKeys.updateInsurance.tr(context: context)}",
history: () {
insuranceViewModel.setIsInsuranceHistoryLoading(true);
insuranceViewModel.getPatientInsuranceCardHistory();
showCommonBottomSheet(context,
child: InsuranceHistory(), callBackFunc: (str) {}, title: "", height: ResponsiveExtension.screenHeight * 0.65, isCloseButtonVisible: false, isFullScreen: false);
},
child: SingleChildScrollView(
child: Consumer<InsuranceViewModel>(builder: (context, insuranceVM, child) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Row(
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
// children: [
// "${LocaleKeys.insurance.tr(context: context)} ${LocaleKeys.updateInsurance.tr(context: context)}".toText24(isBold: true),
// CustomButton(
// icon: AppAssets.insurance_history_icon,
// iconColor: AppColors.primaryRedColor,
// iconSize: 21.h,
// text: LocaleKeys.history.tr(context: context),
// onPressed: () {
// },
// backgroundColor: AppColors.primaryRedColor.withOpacity(0.1),
// borderColor: AppColors.primaryRedColor.withOpacity(0.0),
// textColor: AppColors.primaryRedColor,
// fontSize: 14,
// fontWeight: FontWeight.w600,
// borderRadius: 12,
// padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
// height: 40.h,
// ),
// ],
// ).paddingSymmetrical(24.h, 24.h),
insuranceVM.isInsuranceLoading
? const MoviesShimmerWidget().paddingSymmetrical(24.h, 0)
: Padding(
padding: EdgeInsets.only(top: 24.h),
child: PatientInsuranceCard(
insuranceCardDetailsModel: insuranceVM.patientInsuranceList.first,
isInsuranceExpired: DateTime.now().isAfter(DateUtil.convertStringToDate(insuranceVM.patientInsuranceList.first.cardValidTo))),
),
],
);
}),
),
),
);
}

@ -69,7 +69,7 @@ class PatientInsuranceCardUpdateCard extends StatelessWidget {
Wrap(
direction: Axis.horizontal,
spacing: 4.h,
runSpacing: -8.h,
runSpacing: 4.h,
children: [
AppCustomChipWidget(
icon: AppAssets.doctor_calendar_icon,

@ -75,7 +75,7 @@ class PatientInsuranceCard extends StatelessWidget {
Wrap(
direction: Axis.horizontal,
spacing: 4.h,
runSpacing: -8.h,
runSpacing: 4.h,
children: [
AppCustomChipWidget(
icon: AppAssets.doctor_calendar_icon,

@ -21,8 +21,9 @@ class CollapsingListView extends StatelessWidget {
VoidCallback? history;
Widget? bottomChild;
bool isClose;
bool isLeading;
CollapsingListView({required this.title, this.child, this.search, this.isClose = false, this.bottomChild, this.report, this.logout, this.history});
CollapsingListView({required this.title, this.child, this.search, this.isClose = false, this.bottomChild, this.report, this.logout, this.history, this.isLeading = true});
@override
Widget build(BuildContext context) {
@ -33,17 +34,18 @@ class CollapsingListView extends StatelessWidget {
CustomScrollView(
slivers: [
SliverAppBar(
automaticallyImplyLeading: false,
pinned: true,
expandedHeight: 100,
stretch: true,
systemOverlayStyle: SystemUiOverlayStyle(statusBarBrightness: Brightness.light),
surfaceTintColor: Colors.transparent,
backgroundColor: AppColors.bgScaffoldColor,
leading: IconButton(
leading: isLeading ? IconButton(
icon: Utils.buildSvgWithAssets(icon: isClose ? AppAssets.closeBottomNav : AppAssets.arrow_back, width: 32.h, height: 32.h),
padding: EdgeInsets.only(left: 12),
onPressed: () => Navigator.pop(context),
),
) : SizedBox.shrink(),
flexibleSpace: LayoutBuilder(
builder: (context, constraints) {
final double maxHeight = 100;

@ -5,6 +5,7 @@ import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
import 'package:hmg_patient_app_new/core/enums.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/features/lab/models/resp_models/patient_lab_orders_response_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/features/lab/lab_view_model.dart';
@ -57,7 +58,7 @@ class _LabOrdersPageState extends State<LabOrdersPage> {
}
},
child: SingleChildScrollView(
padding: EdgeInsets.all(24),
padding: EdgeInsets.all(24.h),
physics: NeverScrollableScrollPhysics(),
child: Consumer<LabViewModel>(
builder: (context, model, child) {

@ -2,27 +2,46 @@ import 'dart:async';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/core/utils/date_util.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/insurance/insurance_view_model.dart';
import 'package:hmg_patient_app_new/features/medical_file/medical_file_view_model.dart';
import 'package:hmg_patient_app_new/features/medical_file/models/patient_sickleave_response_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/patient_appointment_history_response_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart';
import 'package:hmg_patient_app_new/features/prescriptions/prescriptions_view_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/appointments/my_appointments_page.dart';
import 'package:hmg_patient_app_new/presentation/appointments/my_doctors_page.dart';
import 'package:hmg_patient_app_new/presentation/insurance/insurance_home_page.dart';
import 'package:hmg_patient_app_new/presentation/insurance/widgets/patient_insurance_card.dart';
import 'package:hmg_patient_app_new/presentation/lab/collapsing_list_view.dart';
import 'package:hmg_patient_app_new/presentation/medical_file/medical_reports_page.dart';
import 'package:hmg_patient_app_new/presentation/medical_file/patient_sickleaves_list_page.dart';
import 'package:hmg_patient_app_new/presentation/medical_file/vaccine_list_page.dart';
import 'package:hmg_patient_app_new/presentation/medical_file/widgets/lab_rad_card.dart';
import 'package:hmg_patient_app_new/presentation/medical_file/widgets/medical_file_card.dart';
import 'package:hmg_patient_app_new/presentation/medical_file/widgets/patient_sick_leave_card.dart';
import 'package:hmg_patient_app_new/presentation/prescriptions/prescriptions_list_page.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart';
import 'package:hmg_patient_app_new/widgets/custom_tab_bar.dart';
import 'package:hmg_patient_app_new/widgets/input_widget.dart';
import 'package:hmg_patient_app_new/widgets/shimmer/movies_shimmer_widget.dart';
import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart';
import 'package:provider/provider.dart';
import '../prescriptions/prescription_detail_page.dart';
import 'widgets/medical_file_appointment_card.dart';
class MedicalFilePage extends StatefulWidget {
MedicalFilePage({super.key});
@ -33,6 +52,8 @@ class MedicalFilePage extends StatefulWidget {
class _MedicalFilePageState extends State<MedicalFilePage> {
late InsuranceViewModel insuranceViewModel;
late AppState appState;
late MyAppointmentsViewModel myAppointmentsViewModel;
late MedicalFileViewModel medicalFileViewModel;
int currentIndex = 0;
@ -40,180 +61,183 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
void initState() {
scheduleMicrotask(() {
insuranceViewModel.initInsuranceProvider();
medicalFileViewModel.setIsPatientSickLeaveListLoading(true);
medicalFileViewModel.getPatientSickLeaveList();
});
super.initState();
}
@override
Widget build(BuildContext context) {
insuranceViewModel = Provider.of<InsuranceViewModel>(context);
insuranceViewModel = Provider.of<InsuranceViewModel>(context, listen: false);
myAppointmentsViewModel = Provider.of<MyAppointmentsViewModel>(context, listen: false);
medicalFileViewModel = Provider.of<MedicalFileViewModel>(context, listen: false);
appState = getIt.get<AppState>();
return Scaffold(
backgroundColor: AppColors.bgScaffoldColor,
appBar: AppBar(
title: const Text('Appointments'),
backgroundColor: AppColors.bgScaffoldColor,
),
body: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
LocaleKeys.medicalFile.tr(context: context).toText22(isBold: true).paddingSymmetrical(24.h, 0.0),
SizedBox(height: 16.h),
TextInputWidget(
labelText: LocaleKeys.search.tr(context: context),
hintText: "Type any record",
controller: TextEditingController(),
keyboardType: TextInputType.number,
isEnable: true,
prefix: null,
autoFocus: false,
isBorderAllowed: false,
isAllowLeadingIcon: true,
padding: EdgeInsets.symmetric(vertical: 8.h, horizontal: 8.h),
leadingIcon: AppAssets.student_card,
).paddingSymmetrical(24.h, 0.0),
SizedBox(height: 16.h),
Container(
width: double.infinity,
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 24,
),
child: Padding(
padding: EdgeInsets.all(16.h),
child: Column(
body: CollapsingListView(
title: LocaleKeys.medicalFile.tr(context: context),
isLeading: false,
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 16.h),
TextInputWidget(
labelText: LocaleKeys.search.tr(context: context),
hintText: "Type any record",
controller: TextEditingController(),
keyboardType: TextInputType.number,
isEnable: true,
prefix: null,
autoFocus: false,
isBorderAllowed: false,
isAllowLeadingIcon: true,
padding: EdgeInsets.symmetric(vertical: 8.h, horizontal: 8.h),
leadingIcon: AppAssets.student_card,
).paddingSymmetrical(24.h, 0.0),
SizedBox(height: 16.h),
Container(
width: double.infinity,
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 24,
),
child: Padding(
padding: EdgeInsets.all(16.h),
child: Column(
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Image.asset(
appState.getAuthenticatedUser()?.gender == 1 ? AppAssets.male_img : AppAssets.femaleImg,
width: 56.h,
height: 56.h,
),
SizedBox(width: 8.h),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
"${appState.getAuthenticatedUser()!.firstName} ${appState.getAuthenticatedUser()!.lastName}".toText18(isBold: true),
SizedBox(height: 4.h),
Row(
children: [
CustomButton(
icon: AppAssets.file_icon,
iconColor: AppColors.blackColor,
iconSize: 12.h,
text: "File no: ${appState.getAuthenticatedUser()!.patientId}",
onPressed: () {},
backgroundColor: AppColors.greyColor,
borderColor: AppColors.greyColor,
textColor: AppColors.blackColor,
fontSize: 10,
fontWeight: FontWeight.normal,
borderRadius: 12,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 30.h,
),
SizedBox(width: 4.h),
CustomButton(
text: LocaleKeys.verified.tr(context: context),
onPressed: () {},
backgroundColor: AppColors.greyColor,
borderColor: AppColors.greyColor,
textColor: AppColors.blackColor,
fontSize: 10,
fontWeight: FontWeight.normal,
borderRadius: 12,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 30.h,
),
],
),
],
)
],
),
SizedBox(height: 16.h),
Divider(color: AppColors.dividerColor, height: 1.h),
SizedBox(height: 16.h),
Row(
children: [
CustomButton(
text: "${appState.getAuthenticatedUser()!.age} Years Old",
onPressed: () {},
backgroundColor: AppColors.greyColor,
borderColor: AppColors.greyColor,
textColor: AppColors.blackColor,
fontSize: 10,
fontWeight: FontWeight.normal,
borderRadius: 12,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 30.h,
),
SizedBox(width: 4.h),
CustomButton(
icon: AppAssets.blood_icon,
iconColor: AppColors.primaryRedColor,
iconSize: 13.h,
text: "Blood: ${appState.getUserBloodGroup}",
onPressed: () {},
backgroundColor: AppColors.greyColor,
borderColor: AppColors.greyColor,
textColor: AppColors.blackColor,
fontSize: 10,
fontWeight: FontWeight.normal,
borderRadius: 12,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 30.h,
),
// SizedBox(width: 4.h),
// CustomButton(
// icon: AppAssets.insurance_active_icon,
// iconColor: AppColors.successColor,
// iconSize: 13.h,
// text: "Insurance Active",
// onPressed: () {},
// backgroundColor: AppColors.bgGreenColor.withOpacity(0.20),
// borderColor: AppColors.bgGreenColor.withOpacity(0.0),
// textColor: AppColors.blackColor,
// fontSize: 10,
// fontWeight: FontWeight.normal,
// borderRadius: 12,
// padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
// height: 30.h,
// ),
],
),
SizedBox(height: 8.h),
],
),
),
).paddingSymmetrical(24.h, 0.0),
SizedBox(height: 16.h),
Consumer<MedicalFileViewModel>(builder: (context, medicalFileVM, child) {
return Column(
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Image.asset(
appState.getAuthenticatedUser()?.gender == 1 ? AppAssets.male_img : AppAssets.femaleImg,
width: 56.h,
height: 56.h,
),
SizedBox(width: 8.h),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
"${appState.getAuthenticatedUser()!.firstName} ${appState.getAuthenticatedUser()!.lastName}".toText18(isBold: true),
SizedBox(height: 4.h),
Row(
children: [
CustomButton(
icon: AppAssets.file_icon,
iconColor: AppColors.blackColor,
iconSize: 12.h,
text: "File no: ${appState.getAuthenticatedUser()!.patientId}",
onPressed: () {},
backgroundColor: AppColors.greyColor,
borderColor: AppColors.greyColor,
textColor: AppColors.blackColor,
fontSize: 10,
fontWeight: FontWeight.normal,
borderRadius: 12,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 30.h,
),
SizedBox(width: 4.h),
CustomButton(
text: LocaleKeys.verified.tr(context: context),
onPressed: () {},
backgroundColor: AppColors.greyColor,
borderColor: AppColors.greyColor,
textColor: AppColors.blackColor,
fontSize: 10,
fontWeight: FontWeight.normal,
borderRadius: 12,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 30.h,
),
],
),
],
)
],
),
SizedBox(height: 16.h),
Divider(color: AppColors.dividerColor, height: 1.h),
SizedBox(height: 16.h),
Row(
children: [
CustomButton(
text: "${appState.getAuthenticatedUser()!.age} Years Old",
onPressed: () {},
backgroundColor: AppColors.greyColor,
borderColor: AppColors.greyColor,
textColor: AppColors.blackColor,
fontSize: 10,
fontWeight: FontWeight.normal,
borderRadius: 12,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 30.h,
),
SizedBox(width: 4.h),
CustomButton(
icon: AppAssets.blood_icon,
iconColor: AppColors.primaryRedColor,
iconSize: 13.h,
text: "Blood: ${appState.getUserBloodGroup}",
onPressed: () {},
backgroundColor: AppColors.greyColor,
borderColor: AppColors.greyColor,
textColor: AppColors.blackColor,
fontSize: 10,
fontWeight: FontWeight.normal,
borderRadius: 12,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 30.h,
),
// SizedBox(width: 4.h),
// CustomButton(
// icon: AppAssets.insurance_active_icon,
// iconColor: AppColors.successColor,
// iconSize: 13.h,
// text: "Insurance Active",
// onPressed: () {},
// backgroundColor: AppColors.bgGreenColor.withOpacity(0.20),
// borderColor: AppColors.bgGreenColor.withOpacity(0.0),
// textColor: AppColors.blackColor,
// fontSize: 10,
// fontWeight: FontWeight.normal,
// borderRadius: 12,
// padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
// height: 30.h,
// ),
CustomTabBar(
activeTextColor: Color(0xffED1C2B),
activeBackgroundColor: Color(0xffED1C2B).withValues(alpha: .1),
tabs: [
CustomTabBarModel(AppAssets.myFilesBottom, LocaleKeys.general.tr(context: context).needTranslation),
CustomTabBarModel(AppAssets.insurance, LocaleKeys.insurance.tr(context: context)),
CustomTabBarModel(AppAssets.requests, LocaleKeys.request.tr(context: context).needTranslation),
CustomTabBarModel(AppAssets.more, "More".needTranslation),
],
),
SizedBox(height: 8.h),
onTabChange: (index) {
print(index);
medicalFileVM.onTabChanged(index);
},
).paddingSymmetrical(24.h, 0.0),
SizedBox(height: 24.h),
getSelectedTabData(medicalFileVM.selectedTabIndex),
],
),
),
).paddingSymmetrical(24.h, 0.0),
SizedBox(height: 16.h),
Consumer<MedicalFileViewModel>(builder: (context, medicalFileVM, child) {
return Column(
children: [
CustomTabBar(
activeTextColor: Color(0xffED1C2B),
activeBackgroundColor: Color(0xffED1C2B).withValues(alpha: .1),
tabs: [
CustomTabBarModel(AppAssets.myFilesBottom, LocaleKeys.general.tr(context: context).needTranslation),
CustomTabBarModel(AppAssets.insurance, LocaleKeys.insurance.tr(context: context)),
CustomTabBarModel(AppAssets.requests, LocaleKeys.request.tr(context: context).needTranslation),
CustomTabBarModel(AppAssets.more, "More".needTranslation),
],
onTabChange: (index) {
print(index);
medicalFileVM.onTabChanged(index);
},
).paddingSymmetrical(24.h, 0.0),
SizedBox(height: 24.h),
getSelectedTabData(medicalFileVM.selectedTabIndex),
],
);
}),
],
);
}),
],
),
),
),
);
@ -224,6 +248,7 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
case 0:
//General Tab Data
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
@ -244,6 +269,320 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
),
);
}),
Consumer<MyAppointmentsViewModel>(builder: (context, myAppointmentsVM, child) {
return SizedBox(
height: 200.h,
child: ListView.separated(
scrollDirection: Axis.horizontal,
padding: EdgeInsets.only(top: 16.h, left: 24.h, right: 24.h, bottom: 0.h),
shrinkWrap: true,
itemCount: myAppointmentsVM.isMyAppointmentsLoading ? 5 : myAppointmentsVM.patientAppointmentsHistoryList.length,
itemBuilder: (context, index) {
return AnimationConfiguration.staggeredList(
position: index,
duration: const Duration(milliseconds: 500),
child: SlideAnimation(
horizontalOffset: 100.0,
child: FadeInAnimation(
child: AnimatedContainer(
duration: Duration(milliseconds: 300),
curve: Curves.easeInOut,
child: myAppointmentsVM.isMyAppointmentsLoading
? MedicalFileAppointmentCard(
patientAppointmentHistoryResponseModel: PatientAppointmentHistoryResponseModel(),
myAppointmentsViewModel: myAppointmentsVM,
)
: MedicalFileAppointmentCard(
patientAppointmentHistoryResponseModel: myAppointmentsVM.patientAppointmentsHistoryList[index],
myAppointmentsViewModel: myAppointmentsViewModel,
),
),
),
),
);
},
separatorBuilder: (BuildContext cxt, int index) => SizedBox(width: 12.h),
),
);
}),
SizedBox(height: 24.h),
"Lab & Radiology".needTranslation.toText18(isBold: true).paddingSymmetrical(24.h, 0.h),
SizedBox(height: 16.h),
Row(
children: [
Expanded(
child: LabRadCard(
icon: AppAssets.lab_result_icon,
labelText: LocaleKeys.labResults.tr(context: context),
labOrderTests: ["Complete blood count", "Creatinine", "Blood Sugar"],
),
),
SizedBox(width: 16.h),
Expanded(
child: LabRadCard(
icon: AppAssets.radiology_icon,
labelText: LocaleKeys.radiology.tr(context: context),
labOrderTests: ["Chest X-ray", "Abdominal Ultrasound", "Dental X-ray"],
),
),
],
).paddingSymmetrical(24.h, 0.h),
SizedBox(height: 24.h),
"Active Medications & Prescriptions".needTranslation.toText18(isBold: true).paddingSymmetrical(24.h, 0.h),
SizedBox(height: 16.h),
Consumer<PrescriptionsViewModel>(builder: (context, prescriptionVM, child) {
return prescriptionVM.isPrescriptionsOrdersLoading
? const MoviesShimmerWidget().paddingSymmetrical(24.h, 0.h)
: prescriptionVM.patientPrescriptionOrders.isNotEmpty
? Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: Colors.white,
borderRadius: 20.h,
),
child: Padding(
padding: EdgeInsets.all(16.h),
child: Column(
children: [
ListView.separated(
itemCount: prescriptionVM.patientPrescriptionOrders.length,
shrinkWrap: true,
padding: const EdgeInsets.only(left: 0, right: 8),
physics: NeverScrollableScrollPhysics(),
itemBuilder: (context, index) {
return AnimationConfiguration.staggeredList(
position: index,
duration: const Duration(milliseconds: 500),
child: SlideAnimation(
verticalOffset: 100.0,
child: FadeInAnimation(
child: Row(
children: [
Image.network(
prescriptionVM.patientPrescriptionOrders[index].doctorImageURL!,
width: 63.h,
height: 63.h,
fit: BoxFit.fill,
).circle(100),
SizedBox(width: 16.h),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
prescriptionVM.patientPrescriptionOrders[index].doctorName!.toText16(isBold: true),
SizedBox(height: 4.h),
Wrap(
direction: Axis.horizontal,
spacing: 3.h,
runSpacing: 4.h,
children: [
AppCustomChipWidget(labelText: prescriptionVM.patientPrescriptionOrders[index].clinicDescription!),
AppCustomChipWidget(
icon: AppAssets.doctor_calendar_icon,
labelText: DateUtil.formatDateToDate(DateUtil.convertStringToDate(prescriptionVM.patientPrescriptionOrders[index].appointmentDate), false),
),
],
),
],
),
),
SizedBox(width: 40.h),
Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_icon, width: 15.h, height: 15.h, fit: BoxFit.contain, iconColor: AppColors.textColor),
],
).onPress(() {
prescriptionVM.setPrescriptionsDetailsLoading();
Navigator.of(context).push(
FadePage(
page: PrescriptionDetailPage(prescriptionsResponseModel: prescriptionVM.patientPrescriptionOrders[index]),
),
);
}),
),
),
);
},
separatorBuilder: (BuildContext cxt, int index) => SizedBox(height: 16.h),
),
SizedBox(height: 16.h),
const Divider(color: AppColors.dividerColor),
SizedBox(height: 16.h),
Row(
children: [
Expanded(
child: CustomButton(
text: "All Prescriptions".needTranslation,
onPressed: () {
Navigator.of(context).push(
FadePage(
page: PrescriptionsListPage(),
),
);
},
backgroundColor: AppColors.secondaryLightRedColor,
borderColor: AppColors.secondaryLightRedColor,
textColor: AppColors.primaryRedColor,
fontSize: 14,
fontWeight: FontWeight.w500,
borderRadius: 12.h,
height: 40.h,
icon: AppAssets.requests,
iconColor: AppColors.primaryRedColor,
iconSize: 16.h,
),
),
SizedBox(width: 10.h),
Expanded(
child: CustomButton(
text: "All Medications".needTranslation,
onPressed: () {},
backgroundColor: AppColors.secondaryLightRedColor,
borderColor: AppColors.secondaryLightRedColor,
textColor: AppColors.primaryRedColor,
fontSize: 14,
fontWeight: FontWeight.w500,
borderRadius: 12.h,
height: 40.h,
icon: AppAssets.all_medications_icon,
iconColor: AppColors.primaryRedColor,
iconSize: 16.h,
),
),
],
),
],
),
),
).paddingSymmetrical(24.h, 0.h)
: SizedBox.shrink();
}),
SizedBox(height: 24.h),
//My Doctor Section
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
LocaleKeys.myDoctor.tr(context: context).toText18(isBold: true),
Row(
children: [
LocaleKeys.viewAll.tr().toText12(color: AppColors.primaryRedColor, fontWeight: FontWeight.w500),
SizedBox(width: 2.h),
Icon(Icons.arrow_forward_ios, color: AppColors.primaryRedColor, size: 10.h),
],
).onPress(() {
myAppointmentsViewModel.setIsPatientMyDoctorsLoading(true);
myAppointmentsViewModel.getPatientMyDoctors();
Navigator.of(context).push(
FadePage(
page: MyDoctorsPage(),
),
);
}),
],
).paddingSymmetrical(24.h, 0.h),
SizedBox(height: 16.h),
Consumer<MyAppointmentsViewModel>(builder: (context, myAppointmentsVM, child) {
return SizedBox(
height: 120.h,
child: ListView.separated(
scrollDirection: Axis.horizontal,
itemCount: myAppointmentsVM.isPatientMyDoctorsLoading ? 5 : myAppointmentsVM.patientMyDoctorsList.length,
shrinkWrap: true,
padding: EdgeInsets.only(left: 24.h, right: 24.h),
itemBuilder: (context, index) {
return myAppointmentsVM.isPatientMyDoctorsLoading
? SizedBox(
width: 80.h,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Image.network(
"https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png",
width: 64.h,
height: 64.h,
fit: BoxFit.fill,
).circle(100).toShimmer2(isShow: true, radius: 50.h),
SizedBox(height: 8.h),
Expanded(
child: ("Dr. John Smith Smith Smith").toString().toText12(fontWeight: FontWeight.w500, isCenter: true, maxLine: 2).toShimmer2(isShow: true),
),
],
),
)
: AnimationConfiguration.staggeredList(
position: index,
duration: const Duration(milliseconds: 1000),
child: SlideAnimation(
horizontalOffset: 100.0,
child: FadeInAnimation(
child: SizedBox(
width: 80.h,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Image.network(
myAppointmentsVM.patientMyDoctorsList[index].doctorImageURL!,
width: 64.h,
height: 64.h,
fit: BoxFit.fill,
).circle(100).toShimmer2(isShow: false, radius: 50.h),
SizedBox(height: 8.h),
Expanded(
child: (myAppointmentsVM.patientMyDoctorsList[index].doctorName)
.toString()
.toText12(fontWeight: FontWeight.w500, isCenter: true, maxLine: 2)
.toShimmer2(isShow: false),
),
],
),
),
),
),
);
},
separatorBuilder: (BuildContext cxt, int index) => SizedBox(width: 8.h),
),
);
}),
SizedBox(height: 24.h),
"Others".needTranslation.toText18(isBold: true).paddingSymmetrical(24.h, 0.h),
SizedBox(height: 16.h),
GridView(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3, crossAxisSpacing: 13, mainAxisSpacing: 13),
physics: NeverScrollableScrollPhysics(),
padding: EdgeInsets.zero,
shrinkWrap: true,
children: [
MedicalFileCard(
label: "Eye Test Results".needTranslation,
textColor: AppColors.blackColor,
backgroundColor: AppColors.whiteColor,
svgIcon: AppAssets.eye_result_icon,
isLargeText: true,
iconSize: 40.h,
),
MedicalFileCard(
label: "Allergy Info".needTranslation,
textColor: AppColors.blackColor,
backgroundColor: AppColors.whiteColor,
svgIcon: AppAssets.allergy_info_icon,
isLargeText: true,
iconSize: 40.h,
),
MedicalFileCard(
label: "Vaccine Info".needTranslation,
textColor: AppColors.blackColor,
backgroundColor: AppColors.whiteColor,
svgIcon: AppAssets.vaccine_info_icon,
isLargeText: true,
iconSize: 40.h,
).onPress(() {
Navigator.of(context).push(
FadePage(
page: VaccineListPage(),
),
);
}),
],
).paddingSymmetrical(24.h, 0.0),
SizedBox(height: 24.h),
],
);
case 1:
@ -264,17 +603,113 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
padding: EdgeInsets.only(top: 12),
shrinkWrap: true,
children: [
MedicalFileCard(label: "Update Insurance".needTranslation, textColor: AppColors.blackColor, backgroundColor: AppColors.whiteColor, svgIcon: AppAssets.eye_result_icon),
MedicalFileCard(label: "Insurance Approvals".needTranslation, textColor: AppColors.blackColor, backgroundColor: AppColors.whiteColor, svgIcon: AppAssets.eye_result_icon),
MedicalFileCard(label: "My Invoices List".needTranslation, textColor: AppColors.blackColor, backgroundColor: AppColors.whiteColor, svgIcon: AppAssets.eye_result_icon),
MedicalFileCard(label: "Ancillary Orders List".needTranslation, textColor: AppColors.blackColor, backgroundColor: AppColors.whiteColor, svgIcon: AppAssets.eye_result_icon),
MedicalFileCard(
label: "Update Insurance".needTranslation,
textColor: AppColors.blackColor,
backgroundColor: AppColors.whiteColor,
svgIcon: AppAssets.eye_result_icon,
isLargeText: false,
iconSize: 36.h)
.onPress(() {
Navigator.of(context).push(
FadePage(
page: InsuranceHomePage(),
),
);
}),
MedicalFileCard(
label: "Insurance Approvals".needTranslation,
textColor: AppColors.blackColor,
backgroundColor: AppColors.whiteColor,
svgIcon: AppAssets.eye_result_icon,
isLargeText: false,
iconSize: 36.h),
MedicalFileCard(
label: "My Invoices List".needTranslation,
textColor: AppColors.blackColor,
backgroundColor: AppColors.whiteColor,
svgIcon: AppAssets.eye_result_icon,
isLargeText: false,
iconSize: 36.h),
MedicalFileCard(
label: "Ancillary Orders List".needTranslation,
textColor: AppColors.blackColor,
backgroundColor: AppColors.whiteColor,
svgIcon: AppAssets.eye_result_icon,
isLargeText: false,
iconSize: 36.h),
],
).paddingSymmetrical(24.h, 0.0),
SizedBox(height: 16.h),
],
);
case 2:
return Container();
// Requests Tab Data
return Column(
children: [
Consumer<MedicalFileViewModel>(builder: (context, medicalFileVM, child) {
return medicalFileVM.isPatientSickLeaveListLoading
? PatientSickLeaveCard(
patientSickLeavesResponseModel: PatientSickLeavesResponseModel(),
isLoading: true,
).paddingSymmetrical(24.h, 0.0)
: medicalFileVM.patientSickLeaveList.isNotEmpty
? PatientSickLeaveCard(
patientSickLeavesResponseModel: medicalFileVM.patientSickLeaveList.first,
isLoading: false,
).paddingSymmetrical(24.h, 0.0)
: SizedBox.shrink();
}),
SizedBox(height: 16.h),
GridView(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3, crossAxisSpacing: 13, mainAxisSpacing: 13),
physics: NeverScrollableScrollPhysics(),
padding: EdgeInsets.zero,
shrinkWrap: true,
children: [
MedicalFileCard(
label: LocaleKeys.monthlyReports.tr(context: context),
textColor: AppColors.blackColor,
backgroundColor: AppColors.whiteColor,
svgIcon: AppAssets.eye_result_icon,
isLargeText: false,
iconSize: 40.h,
),
MedicalFileCard(
label: "Medical Reports".needTranslation,
textColor: AppColors.blackColor,
backgroundColor: AppColors.whiteColor,
svgIcon: AppAssets.allergy_info_icon,
isLargeText: false,
iconSize: 40.h,
).onPress(() {
medicalFileViewModel.setIsPatientMedicalReportsLoading(true);
medicalFileViewModel.getPatientMedicalReportList();
Navigator.of(context).push(
FadePage(
page: MedicalReportsPage(),
),
);
}),
MedicalFileCard(
label: "Sick Leave Report".needTranslation,
textColor: AppColors.blackColor,
backgroundColor: AppColors.whiteColor,
svgIcon: AppAssets.vaccine_info_icon,
isLargeText: false,
iconSize: 40.h,
).onPress(() {
Navigator.of(context).push(
FadePage(
page: PatientSickleavesListPage(),
),
);
}),
],
).paddingSymmetrical(24.h, 0.0),
SizedBox(height: 24.h),
],
);
case 3:
return Container();
default:

@ -0,0 +1,94 @@
import 'package:flutter/material.dart';
import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/medical_file/medical_file_view_model.dart';
import 'package:hmg_patient_app_new/features/medical_file/models/patient_medical_response_model.dart';
import 'package:hmg_patient_app_new/presentation/lab/collapsing_list_view.dart';
import 'package:hmg_patient_app_new/presentation/medical_file/widgets/patient_medical_report_card.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/custom_tab_bar.dart';
import 'package:provider/provider.dart';
class MedicalReportsPage extends StatefulWidget {
const MedicalReportsPage({super.key});
@override
State<MedicalReportsPage> createState() => _MedicalReportsPageState();
}
class _MedicalReportsPageState extends State<MedicalReportsPage> {
late MedicalFileViewModel medicalFileViewModel;
@override
Widget build(BuildContext context) {
medicalFileViewModel = Provider.of<MedicalFileViewModel>(context, listen: false);
return Scaffold(
backgroundColor: AppColors.bgScaffoldColor,
body: CollapsingListView(
title: "Medical Reports".needTranslation,
child: SingleChildScrollView(
child: Column(
children: [
SizedBox(height: 16.h),
CustomTabBar(
activeTextColor: Color(0xffED1C2B),
activeBackgroundColor: Color(0xffED1C2B).withValues(alpha: .1),
tabs: [
CustomTabBarModel(null, "Requested".needTranslation),
CustomTabBarModel(null, "Ready".needTranslation),
CustomTabBarModel(null, "Cancelled".needTranslation),
],
onTabChange: (index) {
medicalFileViewModel.onMedicalReportTabChange(index);
},
).paddingSymmetrical(24.h, 0.h),
Consumer<MedicalFileViewModel>(builder: (context, medicalFileVM, child) {
return ListView.separated(
padding: EdgeInsets.only(top: 24.h),
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemCount: medicalFileViewModel.isPatientMedicalReportsListLoading ? 3 : medicalFileViewModel.patientMedicalReportList.length,
// medicalFileViewModel.patientMedicalReportList.isNotEmpty
// ? medicalFileViewModel.patientMedicalReportList.length
// : 1,
itemBuilder: (context, index) {
return medicalFileViewModel.isPatientMedicalReportsListLoading
? PatientMedicalReportCard(
patientMedicalReportResponseModel: PatientMedicalReportResponseModel(),
medicalFileViewModel: medicalFileVM,
isLoading: true,
).paddingSymmetrical(24.h, 0.h)
: AnimationConfiguration.staggeredList(
position: index,
duration: const Duration(milliseconds: 500),
child: SlideAnimation(
verticalOffset: 100.0,
child: FadeInAnimation(
child: AnimatedContainer(
duration: Duration(milliseconds: 300),
curve: Curves.easeInOut,
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: true),
child: PatientMedicalReportCard(
patientMedicalReportResponseModel: medicalFileVM.patientMedicalReportList[index],
medicalFileViewModel: medicalFileVM,
isLoading: false,
),
).paddingSymmetrical(24.h, 0.h),
),
),
);
},
separatorBuilder: (BuildContext cxt, int index) => SizedBox(height: 16.h),
);
}),
SizedBox(height: 24.h),
],
),
),
),
);
}
}

@ -0,0 +1,88 @@
import 'dart:async';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/medical_file/medical_file_view_model.dart';
import 'package:hmg_patient_app_new/features/medical_file/models/patient_sickleave_response_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/lab/collapsing_list_view.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:provider/provider.dart';
import 'widgets/patient_sick_leave_card.dart';
class PatientSickleavesListPage extends StatefulWidget {
const PatientSickleavesListPage({super.key});
@override
State<PatientSickleavesListPage> createState() => _PatientSickleavesListPageState();
}
class _PatientSickleavesListPageState extends State<PatientSickleavesListPage> {
late MedicalFileViewModel medicalFileViewModel;
@override
void initState() {
scheduleMicrotask(() {
medicalFileViewModel.setIsPatientSickLeaveListLoading(true);
medicalFileViewModel.getPatientSickLeaveList(onError: (error) {
Navigator.of(context).pop();
Navigator.of(context).pop();
});
});
super.initState();
}
@override
Widget build(BuildContext context) {
medicalFileViewModel = Provider.of<MedicalFileViewModel>(context, listen: false);
return Scaffold(
backgroundColor: AppColors.bgScaffoldColor,
body: CollapsingListView(
title: "${LocaleKeys.sick.tr(context: context)} ${LocaleKeys.sickSubtitle.tr(context: context)}",
child: SingleChildScrollView(
child: Consumer<MedicalFileViewModel>(builder: (context, medicalFileVM, child) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ListView.separated(
scrollDirection: Axis.vertical,
itemCount: medicalFileVM.isPatientSickLeaveListLoading ? 3 : medicalFileVM.patientSickLeaveList.length,
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemBuilder: (context, index) {
return medicalFileVM.isPatientSickLeaveListLoading
? PatientSickLeaveCard(
patientSickLeavesResponseModel: PatientSickLeavesResponseModel(),
isLoading: true,
).paddingSymmetrical(24.h, 0.0)
: medicalFileVM.patientSickLeaveList.isNotEmpty
? AnimationConfiguration.staggeredList(
position: index,
duration: const Duration(milliseconds: 1000),
child: SlideAnimation(
verticalOffset: 100.0,
child: FadeInAnimation(
child: PatientSickLeaveCard(
patientSickLeavesResponseModel: medicalFileVM.patientSickLeaveList.first,
isLoading: false,
).paddingSymmetrical(24.h, 0.0),
),
),
)
: SizedBox.shrink();
},
separatorBuilder: (BuildContext cxt, int index) => SizedBox(height: 8.h),
),
SizedBox(height: 60.h),
],
);
}),
),
),
);
}
}

@ -0,0 +1,181 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/utils/date_util.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/medical_file/medical_file_view_model.dart';
import 'package:hmg_patient_app_new/presentation/lab/collapsing_list_view.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:provider/provider.dart';
import '../../widgets/chip/app_custom_chip_widget.dart';
class VaccineListPage extends StatefulWidget {
const VaccineListPage({super.key});
@override
State<VaccineListPage> createState() => _VaccineListPageState();
}
class _VaccineListPageState extends State<VaccineListPage> {
late MedicalFileViewModel medicalFileViewModel;
@override
void initState() {
scheduleMicrotask(() {
medicalFileViewModel.setIsPatientVaccineListLoading(true);
medicalFileViewModel.getPatientVaccinesList(onError: (error) {
Navigator.of(context).pop();
Navigator.of(context).pop();
});
});
super.initState();
}
@override
Widget build(BuildContext context) {
medicalFileViewModel = Provider.of<MedicalFileViewModel>(context, listen: false);
return Scaffold(
backgroundColor: AppColors.bgScaffoldColor,
body: CollapsingListView(
title: "Vaccine Info".needTranslation,
child: SingleChildScrollView(
child: Consumer<MedicalFileViewModel>(builder: (context, medicalFileVM, child) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 16.h),
ListView.separated(
scrollDirection: Axis.vertical,
itemCount: medicalFileVM.isPatientVaccineListLoading ? 5 : medicalFileVM.patientVaccineList.length,
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
padding: EdgeInsets.only(left: 24.h, right: 24.h),
itemBuilder: (context, index) {
return medicalFileVM.isPatientVaccineListLoading
? Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 20.h,
hasShadow: true,
),
child: Padding(
padding: EdgeInsets.all(14.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Image.network(
"https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png",
width: 63.h,
height: 63.h,
fit: BoxFit.fill,
).circle(100).toShimmer2(isShow: true),
SizedBox(width: 16.h),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
"Dr John Smith".toText16(isBold: true).toShimmer2(isShow: true),
SizedBox(height: 8.h),
Wrap(
direction: Axis.horizontal,
spacing: 3.h,
runSpacing: 4.h,
children: [
AppCustomChipWidget(labelText: "").toShimmer2(isShow: true, width: 16.h),
AppCustomChipWidget(labelText: "").toShimmer2(isShow: true, width: 16.h),
],
),
],
),
),
],
),
],
),
),
)
: AnimationConfiguration.staggeredList(
position: index,
duration: const Duration(milliseconds: 1000),
child: SlideAnimation(
verticalOffset: 100.0,
child: FadeInAnimation(
child: Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 20.h,
hasShadow: true,
),
child: Padding(
padding: EdgeInsets.all(14.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Image.network(
// medicalFileVM.patientVaccineList[index].doctorImageURL,
// width: 63.h,
// height: 63.h,
// fit: BoxFit.fill,
// ).circle(100).toShimmer2(isShow: false),
SizedBox(width: 16.h),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
(medicalFileVM.patientVaccineList[index].doctorName).toString().toText16(isBold: true).toShimmer2(isShow: false),
SizedBox(height: 8.h),
Wrap(
direction: Axis.horizontal,
spacing: 3.h,
runSpacing: 4.h,
children: [
AppCustomChipWidget(
icon: AppAssets.doctor_calendar_icon,
labelText: DateUtil.formatDateToDate(DateUtil.convertStringToDate(medicalFileVM.patientVaccineList[index].vaccinationDate), false)),
AppCustomChipWidget(labelText: medicalFileVM.patientVaccineList[index].vaccineName).toShimmer2(isShow: false, width: 16.h),
AppCustomChipWidget(labelText: medicalFileVM.patientVaccineList[index].clinicName).toShimmer2(isShow: false, width: 16.h),
AppCustomChipWidget(labelText: medicalFileVM.patientVaccineList[index].projectName).toShimmer2(isShow: false, width: 16.h),
],
),
],
),
),
],
),
// Row(
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
// children: [
// "".toText16(),
// Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_icon, width: 15.h, height: 15.h, fit: BoxFit.contain, iconColor: AppColors.textColor),
// ],
// ),
],
),
),
),
),
),
);
},
separatorBuilder: (BuildContext cxt, int index) => SizedBox(height: 16.h),
),
SizedBox(height: 60.h),
],
);
}),
),
),
);
}
}

@ -0,0 +1,65 @@
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';
class LabRadCard extends StatelessWidget {
LabRadCard({super.key, required this.icon, required this.labelText, required this.labOrderTests});
String icon;
String labelText;
List<String> labOrderTests = [];
@override
Widget build(BuildContext context) {
return Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: false),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Utils.buildSvgWithAssets(
icon: icon,
width: 40.h,
height: 40.h,
fit: BoxFit.contain,
).toShimmer2(isShow: false, radius: 12.h),
SizedBox(width: 8.h),
labelText.toText14(isBold: true).toShimmer2(isShow: false, radius: 6.h, height: 32.h),
],
),
SizedBox(height: 16.h),
ListView.separated(
scrollDirection: Axis.vertical,
padding: EdgeInsets.zero,
physics: NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemBuilder: (cxt, index) {
return labOrderTests[index].toText12(isBold: true, maxLine: 1).toShimmer2(isShow: false, radius: 6.h, height: 24.h, width: 120.h);
},
separatorBuilder: (cxt, index) => SizedBox(height: 8.h),
itemCount: labOrderTests.length,
),
SizedBox(height: 16.h),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SizedBox.shrink(),
Utils.buildSvgWithAssets(
icon: AppAssets.forward_arrow_icon,
width: 15.h,
height: 15.h,
fit: BoxFit.contain,
iconColor: AppColors.textColor
).toShimmer2(isShow: false, radius: 12.h),
],
)
],
).paddingAll(16.h),
);
}
}

@ -0,0 +1,181 @@
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/date_util.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/patient_appointment_history_response_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/utils/appointment_type.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/appointments/appointment_details_page.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart';
class MedicalFileAppointmentCard extends StatelessWidget {
MedicalFileAppointmentCard({super.key, required this.patientAppointmentHistoryResponseModel, required this.myAppointmentsViewModel});
PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel;
MyAppointmentsViewModel myAppointmentsViewModel;
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
CustomButton(
text: DateUtil.formatDateToDate(DateUtil.convertStringToDate(patientAppointmentHistoryResponseModel.appointmentDate), false),
onPressed: () {},
backgroundColor: AppointmentType.isArrived(patientAppointmentHistoryResponseModel) ? AppColors.greyColor : AppColors.secondaryLightRedColor,
borderColor: AppointmentType.isArrived(patientAppointmentHistoryResponseModel) ? AppColors.greyLightColor : AppColors.secondaryLightRedColor,
textColor: AppointmentType.isArrived(patientAppointmentHistoryResponseModel) ? AppColors.textColor : AppColors.primaryRedColor,
fontSize: 12,
fontWeight: FontWeight.w500,
borderRadius: 12.h,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 40.h,
icon: AppointmentType.isArrived(patientAppointmentHistoryResponseModel) ? AppAssets.appointment_calendar_icon : AppAssets.alarm_clock_icon,
iconColor: AppointmentType.isArrived(patientAppointmentHistoryResponseModel) ? AppColors.textColor : AppColors.primaryRedColor,
iconSize: 16.h,
).toShimmer2(isShow: myAppointmentsViewModel.isMyAppointmentsLoading),
SizedBox(height: 16.h),
Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: true),
width: 200.h,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Image.network(
patientAppointmentHistoryResponseModel.doctorImageURL ?? "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png",
width: 25.h,
height: 27.h,
fit: BoxFit.fill,
).circle(100).toShimmer2(isShow: myAppointmentsViewModel.isMyAppointmentsLoading),
SizedBox(width: 8.h),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
(patientAppointmentHistoryResponseModel.doctorNameObj ?? "").toText14(isBold: true, maxlines: 1).toShimmer2(isShow: myAppointmentsViewModel.isMyAppointmentsLoading),
(patientAppointmentHistoryResponseModel.clinicName ?? "")
.toText12(maxLine: 1, fontWeight: FontWeight.w500, color: AppColors.greyTextColor)
.toShimmer2(isShow: myAppointmentsViewModel.isMyAppointmentsLoading),
],
),
),
],
),
SizedBox(height: 12.h),
Row(
children: [
myAppointmentsViewModel.isMyAppointmentsLoading
? Container().toShimmer2(isShow: true, height: 40.h, width: 100.h, radius: 12.h)
: Expanded(
flex: 6,
child: AppointmentType.isArrived(patientAppointmentHistoryResponseModel)
? getArrivedAppointmentButton(context).toShimmer2(isShow: myAppointmentsViewModel.isMyAppointmentsLoading)
: CustomButton(
text: AppointmentType.getNextActionText(patientAppointmentHistoryResponseModel.nextAction),
onPressed: () {
Navigator.of(context)
.push(FadePage(
page: AppointmentDetailsPage(patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel),
))
.then((val) {
// widget.myAppointmentsViewModel.initAppointmentsViewModel();
// widget.myAppointmentsViewModel.getPatientAppointments(true, false);
});
},
backgroundColor: AppointmentType.getNextActionButtonColor(patientAppointmentHistoryResponseModel.nextAction).withOpacity(0.15),
borderColor: AppointmentType.getNextActionButtonColor(patientAppointmentHistoryResponseModel.nextAction).withOpacity(0.01),
textColor: AppointmentType.getNextActionTextColor(patientAppointmentHistoryResponseModel.nextAction),
fontSize: 14,
fontWeight: FontWeight.w500,
borderRadius: 12,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 40.h,
icon: AppointmentType.getNextActionIcon(patientAppointmentHistoryResponseModel.nextAction),
iconColor: AppointmentType.getNextActionTextColor(patientAppointmentHistoryResponseModel.nextAction),
iconSize: 14.h,
).toShimmer2(isShow: myAppointmentsViewModel.isMyAppointmentsLoading),
),
SizedBox(width: 8.h),
Expanded(
flex: 2,
child: Container(
height: 40.h,
width: 40.h,
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.textColor,
borderRadius: 10.h,
),
child: Padding(
padding: EdgeInsets.all(10.h),
child: Utils.buildSvgWithAssets(
icon: AppAssets.forward_arrow_icon,
width: 10.h,
height: 10.h,
fit: BoxFit.contain,
),
),
).toShimmer2(isShow: myAppointmentsViewModel.isMyAppointmentsLoading).onPress(() {
Navigator.of(context)
.push(
FadePage(
page: AppointmentDetailsPage(patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel),
),
)
.then((val) {
// widget.myAppointmentsViewModel.initAppointmentsViewModel();
// widget.myAppointmentsViewModel.getPatientAppointments(true, false);
});
}),
),
],
),
],
).paddingAll(16.h),
),
],
);
}
Widget getArrivedAppointmentButton(BuildContext context) {
return DateTime.now().difference(DateUtil.convertStringToDate(patientAppointmentHistoryResponseModel.appointmentDate)).inDays <= 15
? CustomButton(
text: LocaleKeys.askDoctor.tr(context: context),
onPressed: () {},
backgroundColor: AppColors.secondaryLightRedColor,
borderColor: AppColors.secondaryLightRedColor,
textColor: AppColors.primaryRedColor,
fontSize: 14,
fontWeight: FontWeight.w500,
borderRadius: 12,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 40.h,
icon: AppAssets.ask_doctor_icon,
iconColor: AppColors.primaryRedColor,
iconSize: 16.h,
)
: CustomButton(
text: "Rebook".needTranslation,
onPressed: () {},
backgroundColor: AppColors.greyColor,
borderColor: AppColors.greyColor,
textColor: AppColors.blackColor,
fontSize: 14,
fontWeight: FontWeight.w500,
borderRadius: 12,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 40.h,
icon: AppAssets.rebook_appointment_icon,
iconColor: AppColors.blackColor,
iconSize: 16.h,
);
}
}

@ -36,7 +36,7 @@ class MedicalFileCard extends StatelessWidget {
children: [
Utils.buildSvgWithAssets(icon: svgIcon, width: iconSize.h, height: iconSize.h, fit: BoxFit.contain),
SizedBox(height: 12.h),
isLargeText ? label.toText14(color: textColor, isBold: true) : label.toText11(color: textColor, isBold: true),
isLargeText ? label.toText14(color: textColor, isBold: true, maxlines: 1) : label.toText11(color: textColor, isBold: true, maxLine: 2),
],
),
),

@ -0,0 +1,159 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/cupertino.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/core/utils/date_util.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/medical_file/medical_file_view_model.dart';
import 'package:hmg_patient_app_new/features/medical_file/models/patient_medical_response_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart';
import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart';
import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart';
import 'package:open_filex/open_filex.dart';
import 'package:share_plus/share_plus.dart';
class PatientMedicalReportCard extends StatelessWidget {
PatientMedicalReportCard({super.key, required this.patientMedicalReportResponseModel, required this.medicalFileViewModel, this.isLoading = false});
PatientMedicalReportResponseModel patientMedicalReportResponseModel;
MedicalFileViewModel medicalFileViewModel;
bool isLoading = true;
@override
Widget build(BuildContext context) {
AppState _appState = getIt.get<AppState>();
return Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 20.h,
hasShadow: true,
),
child: Padding(
padding: EdgeInsets.all(16.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Image.network(
isLoading ? "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png" : patientMedicalReportResponseModel.doctorImageURL!,
width: 63.h,
height: 63.h,
fit: BoxFit.fill,
).circle(100).toShimmer2(isShow: isLoading),
SizedBox(width: 16.h),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
(isLoading ? "" : patientMedicalReportResponseModel.doctorName!).toText16(isBold: true).toShimmer2(isShow: isLoading),
SizedBox(height: 4.h),
Wrap(
direction: Axis.horizontal,
spacing: 3.h,
runSpacing: 4.h,
children: [
AppCustomChipWidget(labelText: isLoading ? "" : patientMedicalReportResponseModel.clinicDescription!).toShimmer2(isShow: isLoading),
AppCustomChipWidget(labelText: isLoading ? "" : patientMedicalReportResponseModel.projectName!).toShimmer2(isShow: isLoading),
AppCustomChipWidget(
icon: AppAssets.doctor_calendar_icon,
labelText: isLoading
? ""
: "${DateUtil.formatDateToDate(DateUtil.convertStringToDate(patientMedicalReportResponseModel.requestDate), false)}, ${DateUtil.formatDateToTimeLang(DateUtil.convertStringToDate(patientMedicalReportResponseModel.requestDate), false)}")
.toShimmer2(isShow: isLoading),
AppCustomChipWidget(
icon: AppAssets.rating_icon, iconColor: AppColors.ratingColorYellow, labelText: isLoading ? "" : "Rating: ${patientMedicalReportResponseModel.decimalDoctorRate}")
.toShimmer2(isShow: isLoading),
],
),
],
),
),
],
),
patientMedicalReportResponseModel.status == 2
? Padding(
padding: EdgeInsets.only(top: 16.h),
child: Row(
children: [
Expanded(
child: CustomButton(
text: "Share",
onPressed: () {
getMedicalReportPDF(true, context, _appState);
},
backgroundColor: AppColors.secondaryLightRedColor,
borderColor: AppColors.secondaryLightRedColor,
textColor: AppColors.primaryRedColor,
fontSize: 14,
fontWeight: FontWeight.w500,
borderRadius: 12.h,
height: 40.h,
icon: AppAssets.download_1,
iconColor: AppColors.primaryRedColor,
iconSize: 16.h,
).toShimmer2(isShow: isLoading),
),
SizedBox(width: 16.h),
Expanded(
child: CustomButton(
text: "Download",
onPressed: () async {
getMedicalReportPDF(false, context, _appState);
},
backgroundColor: AppColors.secondaryLightRedColor,
borderColor: AppColors.secondaryLightRedColor,
textColor: AppColors.primaryRedColor,
fontSize: 14,
fontWeight: FontWeight.w500,
borderRadius: 12.h,
height: 40.h,
icon: AppAssets.download_1,
iconColor: AppColors.primaryRedColor,
iconSize: 16.h,
).toShimmer2(isShow: isLoading),
),
],
),
)
: SizedBox.shrink()
],
),
),
);
}
void getMedicalReportPDF(bool isShare, BuildContext context, AppState _appState) async {
LoaderBottomSheet.showLoader();
await medicalFileViewModel.getPatientMedicalReportPDF(patientMedicalReportResponseModel, _appState.getAuthenticatedUser()!).then((val) async {
LoaderBottomSheet.hideLoader();
if (medicalFileViewModel.patientMedicalReportPDFBase64.isNotEmpty) {
String path = await Utils.createFileFromString(medicalFileViewModel.patientMedicalReportPDFBase64, "pdf");
if (isShare) {
Share.shareXFiles([XFile(path)], text: "Medical Report");
} else {
try {
OpenFilex.open(path);
} catch (ex) {
showCommonBottomSheetWithoutHeight(
context,
child: Utils.getErrorWidget(loadingText: "Cannot open file".needTranslation),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
}
}
}
});
}
}

@ -0,0 +1,191 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/core/utils/date_util.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/medical_file/medical_file_view_model.dart';
import 'package:hmg_patient_app_new/features/medical_file/models/patient_sickleave_response_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/medical_file/patient_sickleaves_list_page.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart';
import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart';
import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart';
import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart';
import 'package:open_filex/open_filex.dart';
import 'package:provider/provider.dart';
class PatientSickLeaveCard extends StatelessWidget {
PatientSickLeaveCard({super.key, required this.patientSickLeavesResponseModel, this.isLoading = false});
late MedicalFileViewModel medicalFileViewModel;
PatientSickLeavesResponseModel patientSickLeavesResponseModel;
bool isLoading;
@override
Widget build(BuildContext context) {
AppState _appState = getIt.get<AppState>();
medicalFileViewModel = Provider.of<MedicalFileViewModel>(context, listen: false);
return Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24, hasShadow: true),
child: Padding(
padding: EdgeInsets.all(16.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
"${LocaleKeys.sick.tr(context: context)} ${LocaleKeys.sickSubtitle.tr(context: context)}".toText16(isBold: true),
AppCustomChipWidget(
labelText: isLoading ? "" : getStatusText(context),
backgroundColor: getStatusColor().withOpacity(0.15),
textColor: getStatusColor(),
).toShimmer2(isShow: isLoading, width: 100.h),
],
),
SizedBox(height: 16.h),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Image.network(
isLoading ? "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png" : patientSickLeavesResponseModel.doctorImageURL!,
width: 30.h,
height: 30.h,
fit: BoxFit.fill,
).circle(100).toShimmer2(isShow: isLoading),
SizedBox(width: 16.h),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
(isLoading ? "" : patientSickLeavesResponseModel.doctorName!).toText16(isBold: true).toShimmer2(isShow: isLoading),
SizedBox(height: 8.h),
Wrap(
direction: Axis.horizontal,
spacing: 3.h,
runSpacing: 4.h,
children: [
AppCustomChipWidget(
icon: AppAssets.doctor_calendar_icon,
labelText: DateUtil.formatDateToDate(DateUtil.convertStringToDate(patientSickLeavesResponseModel.appointmentDate), false),
).toShimmer2(isShow: isLoading),
AppCustomChipWidget(labelText: isLoading ? "Pending Activation" : patientSickLeavesResponseModel.clinicName!).toShimmer2(isShow: isLoading),
],
),
],
),
),
],
),
SizedBox(height: 16.h),
Row(
children: [
isLoading
? Container().toShimmer2(isShow: true, height: 40.h, width: 100.h, radius: 12.h)
: Expanded(
flex: 6,
child: CustomButton(
text: "Download Report".needTranslation,
onPressed: () async {
LoaderBottomSheet.showLoader();
await medicalFileViewModel.getPatientSickLeavePDF(patientSickLeavesResponseModel, _appState.getAuthenticatedUser()!).then((val) async {
LoaderBottomSheet.hideLoader();
if (medicalFileViewModel.patientSickLeavePDFBase64.isNotEmpty) {
String path = await Utils.createFileFromString(medicalFileViewModel.patientSickLeavePDFBase64, "pdf");
try {
OpenFilex.open(path);
} catch (ex) {
showCommonBottomSheetWithoutHeight(
context,
child: Utils.getErrorWidget(loadingText: "Cannot open file".needTranslation),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
}
}
});
},
backgroundColor: AppColors.secondaryLightRedColor,
borderColor: AppColors.secondaryLightRedColor,
textColor: AppColors.primaryRedColor,
fontSize: 14,
fontWeight: FontWeight.w500,
borderRadius: 12,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 40.h,
icon: AppAssets.download,
iconColor: AppColors.primaryRedColor,
iconSize: 14.h,
).toShimmer2(isShow: isLoading),
),
SizedBox(width: 8.h),
Expanded(
flex: 1,
child: Container(
height: 40.h,
width: 40.h,
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.textColor,
borderRadius: 10.h,
),
child: Padding(
padding: EdgeInsets.all(10.h),
child: Utils.buildSvgWithAssets(
icon: AppAssets.forward_arrow_icon,
width: 10.h,
height: 10.h,
fit: BoxFit.contain,
),
),
).toShimmer2(isShow: isLoading).onPress(() {
Navigator.of(context).push(
FadePage(
page: PatientSickleavesListPage(),
),
);
}),
),
],
),
],
),
),
);
}
String getStatusText(BuildContext context) {
String statusText = "";
if (patientSickLeavesResponseModel.status == 1) {
statusText = LocaleKeys.pendingActivation.tr(context: context);
} else if (patientSickLeavesResponseModel.status == 2) {
statusText = LocaleKeys.ready.tr(context: context);
} else if (patientSickLeavesResponseModel.status == 3) {
statusText = LocaleKeys.awaitingApproval.tr(context: context);
} else {
statusText = "";
}
return statusText;
}
Color getStatusColor() {
Color statusColor = Colors.white;
if (patientSickLeavesResponseModel.status == 1) {
statusColor = Color(0xffCC9B14);
} else if (patientSickLeavesResponseModel.status == 2) {
statusColor = Color(0xff359846);
} else if (patientSickLeavesResponseModel.status == 3) {
statusColor = Color(0xffD02127);
} else {
statusColor = Colors.white;
}
return statusColor;
}
}

@ -13,6 +13,7 @@ import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/prescriptions/models/resp_models/patient_prescriptions_response_model.dart';
import 'package:hmg_patient_app_new/features/prescriptions/prescriptions_view_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/lab/collapsing_list_view.dart';
import 'package:hmg_patient_app_new/presentation/prescriptions/prescription_item_view.dart';
import 'package:hmg_patient_app_new/presentation/prescriptions/prescription_reminder_view.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
@ -53,94 +54,91 @@ class _PrescriptionDetailPageState extends State<PrescriptionDetailPage> {
prescriptionsViewModel = Provider.of<PrescriptionsViewModel>(context, listen: false);
return Scaffold(
backgroundColor: AppColors.bgScaffoldColor,
appBar: AppBar(
title: LocaleKeys.prescriptions.tr(context: context).toText18(),
backgroundColor: AppColors.bgScaffoldColor,
),
body: Column(
children: [
Expanded(
child: SingleChildScrollView(
child: Consumer<PrescriptionsViewModel>(builder: (context, prescriptionVM, child) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
LocaleKeys.prescriptions.tr(context: context).toText24(isBold: true).paddingSymmetrical(24.h, 0.h),
SizedBox(height: 24.h),
Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 20.h,
hasShadow: true,
),
child: Padding(
padding: EdgeInsets.all(16.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisSize: MainAxisSize.min,
children: [
Image.network(
widget.prescriptionsResponseModel.doctorImageURL!,
width: 24.h,
height: 24.h,
fit: BoxFit.fill,
).circle(100),
SizedBox(width: 8.h),
Expanded(child: widget.prescriptionsResponseModel.doctorName!.toText16(isBold: true)),
],
),
SizedBox(height: 16.h),
Wrap(
direction: Axis.horizontal,
spacing: 4.h,
runSpacing: -8.h,
children: [
AppCustomChipWidget(
icon: AppAssets.doctor_calendar_icon,
labelText: DateUtil.formatDateToDate(DateUtil.convertStringToDate(widget.prescriptionsResponseModel.appointmentDate), false),
),
AppCustomChipWidget(
labelText: widget.prescriptionsResponseModel.clinicDescription!,
),
AppCustomChipWidget(
icon: AppAssets.rating_icon,
iconColor: AppColors.ratingColorYellow,
labelText: "Rating: ${widget.prescriptionsResponseModel.decimalDoctorRate}".needTranslation,
),
AppCustomChipWidget(
labelText: widget.prescriptionsResponseModel.name!,
),
],
),
],
child: CollapsingListView(
title: LocaleKeys.prescriptions.tr(context: context),
child: SingleChildScrollView(
child: Consumer<PrescriptionsViewModel>(builder: (context, prescriptionVM, child) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 24.h),
Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 20.h,
hasShadow: true,
),
child: Padding(
padding: EdgeInsets.all(16.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisSize: MainAxisSize.min,
children: [
Image.network(
widget.prescriptionsResponseModel.doctorImageURL!,
width: 24.h,
height: 24.h,
fit: BoxFit.fill,
).circle(100),
SizedBox(width: 8.h),
Expanded(child: widget.prescriptionsResponseModel.doctorName!.toText16(isBold: true)),
],
),
SizedBox(height: 16.h),
Wrap(
direction: Axis.horizontal,
spacing: 4.h,
runSpacing: 4.h,
children: [
AppCustomChipWidget(
icon: AppAssets.doctor_calendar_icon,
labelText: DateUtil.formatDateToDate(DateUtil.convertStringToDate(widget.prescriptionsResponseModel.appointmentDate), false),
),
AppCustomChipWidget(
labelText: widget.prescriptionsResponseModel.clinicDescription!,
),
AppCustomChipWidget(
icon: AppAssets.rating_icon,
iconColor: AppColors.ratingColorYellow,
labelText: "Rating: ${widget.prescriptionsResponseModel.decimalDoctorRate}".needTranslation,
),
AppCustomChipWidget(
labelText: widget.prescriptionsResponseModel.name!,
),
],
),
],
),
),
),
).paddingSymmetrical(24.h, 0.h),
SizedBox(height: 16.h),
ListView.builder(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemCount: prescriptionVM.isPrescriptionsDetailsLoading ? 5 : prescriptionVM.prescriptionDetailsList.length,
itemBuilder: (context, index) {
return prescriptionVM.isPrescriptionsDetailsLoading
? PrescriptionItemView(prescriptionVM: prescriptionVM, index: index, isLoading: true)
: AnimationConfiguration.staggeredList(
position: index,
duration: const Duration(milliseconds: 500),
child: SlideAnimation(
verticalOffset: 100.0,
child: FadeInAnimation(
child: PrescriptionItemView(prescriptionVM: prescriptionVM, index: index),
).paddingSymmetrical(24.h, 0.h),
ListView.builder(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemCount: prescriptionVM.isPrescriptionsDetailsLoading ? 5 : prescriptionVM.prescriptionDetailsList.length,
itemBuilder: (context, index) {
return prescriptionVM.isPrescriptionsDetailsLoading
? PrescriptionItemView(prescriptionVM: prescriptionVM, index: index, isLoading: true)
: AnimationConfiguration.staggeredList(
position: index,
duration: const Duration(milliseconds: 500),
child: SlideAnimation(
verticalOffset: 100.0,
child: FadeInAnimation(
child: PrescriptionItemView(prescriptionVM: prescriptionVM, index: index),
),
),
),
);
},
).paddingSymmetrical(24.h, 0.h),
],
);
}),
);
},
).paddingSymmetrical(24.h, 0.h),
],
);
}),
),
),
),
Container(

@ -11,6 +11,7 @@ import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/prescriptions/prescriptions_view_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/lab/collapsing_list_view.dart';
import 'package:hmg_patient_app_new/presentation/prescriptions/prescription_detail_page.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
@ -43,256 +44,252 @@ class _PrescriptionsListPageState extends State<PrescriptionsListPage> {
prescriptionsViewModel = Provider.of<PrescriptionsViewModel>(context, listen: false);
return Scaffold(
backgroundColor: AppColors.bgScaffoldColor,
appBar: AppBar(
title: LocaleKeys.prescriptions.tr(context: context).toText18(),
backgroundColor: AppColors.bgScaffoldColor,
),
body: SingleChildScrollView(
child: Consumer<PrescriptionsViewModel>(builder: (context, model, child) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
LocaleKeys.prescriptions.tr(context: context).toText24(isBold: true).paddingSymmetrical(24.h, 0.h),
SizedBox(height: 16.h),
// Build Tab Bar
SizedBox(height: 16.h),
// Clinic & Hospital Sort
Row(
children: [
CustomButton(
text: LocaleKeys.byClinic.tr(context: context),
onPressed: () {
model.setIsSortByClinic(true);
},
backgroundColor: model.isSortByClinic ? AppColors.bgRedLightColor : AppColors.whiteColor,
borderColor: model.isSortByClinic ? AppColors.primaryRedColor : AppColors.textColor.withOpacity(0.2),
textColor: model.isSortByClinic ? AppColors.primaryRedColor : AppColors.blackColor,
fontSize: 12,
fontWeight: FontWeight.w500,
borderRadius: 10,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 40.h,
),
SizedBox(width: 8.h),
CustomButton(
text: LocaleKeys.byHospital.tr(context: context),
onPressed: () {
model.setIsSortByClinic(false);
},
backgroundColor: model.isSortByClinic ? AppColors.whiteColor : AppColors.bgRedLightColor,
borderColor: model.isSortByClinic ? AppColors.textColor.withOpacity(0.2) : AppColors.primaryRedColor,
textColor: model.isSortByClinic ? AppColors.blackColor : AppColors.primaryRedColor,
fontSize: 12,
fontWeight: FontWeight.w500,
borderRadius: 10,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 40.h,
),
],
).paddingSymmetrical(24.h, 0.h),
SizedBox(height: 20.h),
// Expandable list
ListView.builder(
itemCount: model.isPrescriptionsOrdersLoading ? 4 : model.patientPrescriptionOrdersViewList.length,
physics: NeverScrollableScrollPhysics(),
shrinkWrap: true,
padding: const EdgeInsets.only(left: 0, right: 8),
itemBuilder: (context, index) {
final isExpanded = expandedIndex == index;
return model.isPrescriptionsOrdersLoading
? const MoviesShimmerWidget()
: AnimationConfiguration.staggeredList(
position: index,
duration: const Duration(milliseconds: 500),
child: SlideAnimation(
verticalOffset: 100.0,
child: FadeInAnimation(
child: AnimatedContainer(
duration: Duration(milliseconds: 300),
curve: Curves.easeInOut,
margin: EdgeInsets.symmetric(vertical: 8.h),
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 20.h, hasShadow: true),
child: InkWell(
onTap: () {
setState(() {
expandedIndex = isExpanded ? null : index;
});
},
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: EdgeInsets.all(16.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
CustomButton(
text: "${model.patientPrescriptionOrdersViewList[index].prescriptionsList!.length} Prescriptions Available",
onPressed: () {},
backgroundColor: AppColors.greyColor,
borderColor: AppColors.greyColor,
textColor: AppColors.blackColor,
fontSize: 10,
fontWeight: FontWeight.w500,
borderRadius: 8,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 30.h,
),
Icon(isExpanded ? Icons.expand_less : Icons.expand_more),
],
),
SizedBox(height: 8.h),
model.patientPrescriptionOrdersViewList[index].filterName!.toText16(isBold: true)
],
body: CollapsingListView(
title: LocaleKeys.prescriptions.tr(context: context),
child: SingleChildScrollView(
child: Consumer<PrescriptionsViewModel>(builder: (context, model, child) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 16.h),
// Clinic & Hospital Sort
Row(
children: [
CustomButton(
text: LocaleKeys.byClinic.tr(context: context),
onPressed: () {
model.setIsSortByClinic(true);
},
backgroundColor: model.isSortByClinic ? AppColors.bgRedLightColor : AppColors.whiteColor,
borderColor: model.isSortByClinic ? AppColors.primaryRedColor : AppColors.textColor.withOpacity(0.2),
textColor: model.isSortByClinic ? AppColors.primaryRedColor : AppColors.blackColor,
fontSize: 12,
fontWeight: FontWeight.w500,
borderRadius: 10,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 40.h,
),
SizedBox(width: 8.h),
CustomButton(
text: LocaleKeys.byHospital.tr(context: context),
onPressed: () {
model.setIsSortByClinic(false);
},
backgroundColor: model.isSortByClinic ? AppColors.whiteColor : AppColors.bgRedLightColor,
borderColor: model.isSortByClinic ? AppColors.textColor.withOpacity(0.2) : AppColors.primaryRedColor,
textColor: model.isSortByClinic ? AppColors.blackColor : AppColors.primaryRedColor,
fontSize: 12,
fontWeight: FontWeight.w500,
borderRadius: 10,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 40.h,
),
],
).paddingSymmetrical(24.h, 0.h),
SizedBox(height: 20.h),
// Expandable list
ListView.builder(
itemCount: model.isPrescriptionsOrdersLoading ? 4 : model.patientPrescriptionOrdersViewList.length,
physics: NeverScrollableScrollPhysics(),
shrinkWrap: true,
padding: const EdgeInsets.only(left: 0, right: 8),
itemBuilder: (context, index) {
final isExpanded = expandedIndex == index;
return model.isPrescriptionsOrdersLoading
? const MoviesShimmerWidget()
: AnimationConfiguration.staggeredList(
position: index,
duration: const Duration(milliseconds: 500),
child: SlideAnimation(
verticalOffset: 100.0,
child: FadeInAnimation(
child: AnimatedContainer(
duration: Duration(milliseconds: 300),
curve: Curves.easeInOut,
margin: EdgeInsets.symmetric(vertical: 8.h),
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 20.h, hasShadow: true),
child: InkWell(
onTap: () {
setState(() {
expandedIndex = isExpanded ? null : index;
});
},
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: EdgeInsets.all(16.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
CustomButton(
text: "${model.patientPrescriptionOrdersViewList[index].prescriptionsList!.length} Prescriptions Available",
onPressed: () {},
backgroundColor: AppColors.greyColor,
borderColor: AppColors.greyColor,
textColor: AppColors.blackColor,
fontSize: 10,
fontWeight: FontWeight.w500,
borderRadius: 8,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 30.h,
),
Icon(isExpanded ? Icons.expand_less : Icons.expand_more),
],
),
SizedBox(height: 8.h),
model.patientPrescriptionOrdersViewList[index].filterName!.toText16(isBold: true)
],
),
),
),
AnimatedSwitcher(
duration: Duration(milliseconds: 500),
switchInCurve: Curves.easeIn,
switchOutCurve: Curves.easeOut,
transitionBuilder: (Widget child, Animation<double> animation) {
return FadeTransition(
opacity: animation,
child: SizeTransition(
sizeFactor: animation,
axisAlignment: 0.0,
child: child,
),
);
},
child: isExpanded
? Container(
key: ValueKey<int>(index),
padding: EdgeInsets.symmetric(horizontal: 16.h, vertical: 8.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
...model.patientPrescriptionOrdersViewList[index].prescriptionsList!.map((prescription) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisSize: MainAxisSize.min,
children: [
Image.network(
prescription.doctorImageURL!,
width: 24.h,
height: 24.h,
fit: BoxFit.fill,
).circle(100),
SizedBox(width: 8.h),
Expanded(child: prescription.doctorName!.toText14(weight: FontWeight.w500)),
],
),
SizedBox(height: 8.h),
Row(
children: [
CustomButton(
text: DateUtil.formatDateToDate(DateUtil.convertStringToDate(prescription.appointmentDate), false),
onPressed: () {},
backgroundColor: AppColors.greyColor,
borderColor: AppColors.greyColor,
textColor: AppColors.blackColor,
fontSize: 10,
fontWeight: FontWeight.w500,
borderRadius: 8,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 24.h,
),
SizedBox(width: 8.h),
CustomButton(
text: model.isSortByClinic ? prescription.name! : prescription.clinicDescription!,
onPressed: () {},
backgroundColor: AppColors.greyColor,
borderColor: AppColors.greyColor,
textColor: AppColors.blackColor,
fontSize: 10,
fontWeight: FontWeight.w500,
borderRadius: 8,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 24.h,
),
],
),
SizedBox(height: 8.h),
Row(
children: [
Expanded(
flex: 6,
child: CustomButton(
text: prescription.isHomeMedicineDeliverySupported!
? LocaleKeys.resendOrder.tr(context: context)
: LocaleKeys.prescriptionDeliveryError.tr(context: context),
AnimatedSwitcher(
duration: Duration(milliseconds: 500),
switchInCurve: Curves.easeIn,
switchOutCurve: Curves.easeOut,
transitionBuilder: (Widget child, Animation<double> animation) {
return FadeTransition(
opacity: animation,
child: SizeTransition(
sizeFactor: animation,
axisAlignment: 0.0,
child: child,
),
);
},
child: isExpanded
? Container(
key: ValueKey<int>(index),
padding: EdgeInsets.symmetric(horizontal: 16.h, vertical: 8.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
...model.patientPrescriptionOrdersViewList[index].prescriptionsList!.map((prescription) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisSize: MainAxisSize.min,
children: [
Image.network(
prescription.doctorImageURL!,
width: 24.h,
height: 24.h,
fit: BoxFit.fill,
).circle(100),
SizedBox(width: 8.h),
Expanded(child: prescription.doctorName!.toText14(weight: FontWeight.w500)),
],
),
SizedBox(height: 8.h),
Row(
children: [
CustomButton(
text: DateUtil.formatDateToDate(DateUtil.convertStringToDate(prescription.appointmentDate), false),
onPressed: () {},
backgroundColor: AppColors.greyColor,
borderColor: AppColors.greyColor,
textColor: AppColors.blackColor,
fontSize: 10,
fontWeight: FontWeight.w500,
borderRadius: 8,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 24.h,
),
SizedBox(width: 8.h),
CustomButton(
text: model.isSortByClinic ? prescription.name! : prescription.clinicDescription!,
onPressed: () {},
backgroundColor: prescription.isHomeMedicineDeliverySupported! ? AppColors.successColor.withOpacity(0.15) : AppColors.greyF7Color,
borderColor: AppColors.successColor.withOpacity(0.01),
textColor: prescription.isHomeMedicineDeliverySupported! ? AppColors.successColor : AppColors.textColor.withOpacity(0.35),
fontSize: prescription.isHomeMedicineDeliverySupported! ? 14 : 12,
backgroundColor: AppColors.greyColor,
borderColor: AppColors.greyColor,
textColor: AppColors.blackColor,
fontSize: 10,
fontWeight: FontWeight.w500,
borderRadius: 12,
borderRadius: 8,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 40.h,
icon: AppAssets.prescription_refill_icon,
iconColor: prescription.isHomeMedicineDeliverySupported! ? AppColors.successColor : AppColors.textColor.withOpacity(0.35),
iconSize: 14.h,
height: 24.h,
),
),
SizedBox(width: 8.h),
Expanded(
flex: 1,
child: Container(
height: 40.h,
width: 40.h,
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.textColor,
borderRadius: 10.h,
],
),
SizedBox(height: 8.h),
Row(
children: [
Expanded(
flex: 6,
child: CustomButton(
text: prescription.isHomeMedicineDeliverySupported!
? LocaleKeys.resendOrder.tr(context: context)
: LocaleKeys.prescriptionDeliveryError.tr(context: context),
onPressed: () {},
backgroundColor: prescription.isHomeMedicineDeliverySupported! ? AppColors.successColor.withOpacity(0.15) : AppColors.greyF7Color,
borderColor: AppColors.successColor.withOpacity(0.01),
textColor: prescription.isHomeMedicineDeliverySupported! ? AppColors.successColor : AppColors.textColor.withOpacity(0.35),
fontSize: prescription.isHomeMedicineDeliverySupported! ? 14 : 12,
fontWeight: FontWeight.w500,
borderRadius: 12,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 40.h,
icon: AppAssets.prescription_refill_icon,
iconColor: prescription.isHomeMedicineDeliverySupported! ? AppColors.successColor : AppColors.textColor.withOpacity(0.35),
iconSize: 14.h,
),
child: Padding(
padding: EdgeInsets.all(8.h),
child: Utils.buildSvgWithAssets(
icon: AppAssets.forward_arrow_icon,
width: 10.h,
height: 10.h,
fit: BoxFit.contain,
),
SizedBox(width: 8.h),
Expanded(
flex: 1,
child: Container(
height: 40.h,
width: 40.h,
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.textColor,
borderRadius: 10.h,
),
),
).onPress(() {
model.setPrescriptionsDetailsLoading();
Navigator.of(context).push(
FadePage(
page: PrescriptionDetailPage(prescriptionsResponseModel: prescription),
child: Padding(
padding: EdgeInsets.all(8.h),
child: Utils.buildSvgWithAssets(
icon: AppAssets.forward_arrow_icon,
width: 10.h,
height: 10.h,
fit: BoxFit.contain,
),
),
);
}),
),
],
),
SizedBox(height: 12.h),
Divider(color: AppColors.borderOnlyColor.withValues(alpha: 0.05), height: 1.h),
SizedBox(height: 12.h),
],
);
}).toList(),
],
),
)
: SizedBox.shrink(),
),
],
).onPress(() {
model.setPrescriptionsDetailsLoading();
Navigator.of(context).push(
FadePage(
page: PrescriptionDetailPage(prescriptionsResponseModel: prescription),
),
);
}),
),
],
),
SizedBox(height: 12.h),
Divider(color: AppColors.borderOnlyColor.withValues(alpha: 0.05), height: 1.h),
SizedBox(height: 12.h),
],
);
}).toList(),
],
),
)
: SizedBox.shrink(),
),
],
),
),
),
),
),
),
);
},
).paddingSymmetrical(24.h, 0.h),
],
);
}),
);
},
).paddingSymmetrical(24.h, 0.h),
],
);
}),
),
),
);
}

@ -11,6 +11,7 @@ import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/features/lab/lab_view_model.dart';
import 'package:hmg_patient_app_new/presentation/lab/collapsing_list_view.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/shimmer/movies_shimmer_widget.dart';
@ -43,27 +44,14 @@ class _RadiologyOrdersPageState extends State<RadiologyOrdersPage> {
radiologyViewModel = Provider.of<RadiologyViewModel>(context);
return Scaffold(
backgroundColor: AppColors.bgScaffoldColor,
appBar: AppBar(
title: LocaleKeys.radiology.tr(context: context).toText18(),
backgroundColor: AppColors.bgScaffoldColor,
),
body: Padding(
padding: EdgeInsets.all(24.h),
body: CollapsingListView(
title: LocaleKeys.radiology.tr(context: context),
child: SingleChildScrollView(
child: Consumer<RadiologyViewModel>(
builder: (context, model, child) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
LocaleKeys.radiology.tr(context: context).toText24(isBold: true),
Utils.buildSvgWithAssets(icon: AppAssets.search_icon),
],
),
SizedBox(height: 16.h),
// Build Tab Bar
SizedBox(height: 16.h),
// Expandable list
ListView.builder(

@ -47,7 +47,7 @@ class ErrorHandlerServiceImp implements ErrorHandlerService {
} else {
loggerService.errorLogs("Unhandled failure type: $failure");
await _showDialog(failure, title: "Error");
await _showDialog(failure, title: "Error", onOkPressed: onOkPressed);
}
}

@ -5,9 +5,11 @@ import 'package:hmg_patient_app_new/core/app_export.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/enums.dart';
import 'package:hmg_patient_app_new/core/utils/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';
import 'package:hmg_patient_app_new/widgets/dropdown/country_dropdown_widget.dart';
import 'package:keyboard_actions/keyboard_actions.dart';
import '../core/dependencies.dart';
@ -36,6 +38,8 @@ class TextInputWidget extends StatelessWidget {
final String? errorMessage;
Function(CountryEnum)? onCountryChange;
final SelectionTypeEnum? selectionType;
final num? fontSize;
final bool? isWalletAmountInput;
// final List<Country> countryList;
// final Function(Country)? onCountryChange;
@ -63,10 +67,37 @@ class TextInputWidget extends StatelessWidget {
this.errorMessage,
this.onCountryChange,
this.selectionType,
this.fontSize = 14,
this.isWalletAmountInput = false,
// this.countryList = const [],
// this.onCountryChange,
});
final FocusNode _focusNode = FocusNode();
KeyboardActionsConfig get _keyboardActionsConfig {
return KeyboardActionsConfig(
keyboardActionsPlatform: KeyboardActionsPlatform.ALL,
keyboardBarColor: const Color(0xFFCAD1D9), //Apple keyboard color
actions: [
KeyboardActionsItem(
focusNode: focusNode ?? _focusNode,
toolbarButtons: [
(node) {
return GestureDetector(
onTap: () => node.unfocus(),
child: Container(
padding: const EdgeInsets.all(12.0),
child: "Done".toText16(weight: FontWeight.w500, color: AppColors.infoColor),
),
);
}
],
),
],
);
}
@override
Widget build(BuildContext context) {
final errorColor = AppColors.primaryRedColor;
@ -184,40 +215,46 @@ class TextInputWidget extends StatelessWidget {
}
Widget _buildTextField(BuildContext context) {
return TextField(
enabled: isEnable,
scrollPadding: EdgeInsets.zero,
keyboardType: keyboardType,
controller: controller,
readOnly: isReadOnly,
textAlignVertical: TextAlignVertical.top,
textAlign: TextAlign.left,
textDirection: TextDirection.ltr,
onChanged: onChange,
focusNode: focusNode,
autofocus: autoFocus,
style: TextStyle(fontSize: 14.fSize, height: 21 / 14, fontWeight: FontWeight.w500, color: AppColors.textColor, letterSpacing: -0.2),
decoration: InputDecoration(
isDense: true,
hintText: hintText,
hintStyle: TextStyle(fontSize: 14.fSize, height: 21 / 16, fontWeight: FontWeight.w500, color: Color(0xff898A8D), letterSpacing: -0.2),
prefixIconConstraints: BoxConstraints(minWidth: 45.h),
prefixIcon: prefix == null
? null
: Text(
"+" + prefix!,
style: TextStyle(
fontSize: 14.fSize,
height: 21 / 14,
fontWeight: FontWeight.w500,
color: Color(0xff2E303A),
letterSpacing: -0.2,
return KeyboardActions(
config: _keyboardActionsConfig,
disableScroll: true,
child: TextField(
enabled: isEnable,
scrollPadding: EdgeInsets.zero,
keyboardType: keyboardType,
controller: controller,
readOnly: isReadOnly,
textAlignVertical: TextAlignVertical.top,
textAlign: TextAlign.left,
textDirection: TextDirection.ltr,
onChanged: onChange,
focusNode: focusNode ?? _focusNode,
autofocus: autoFocus,
textInputAction: TextInputAction.done,
cursorHeight: isWalletAmountInput! ? 40.h : 18.h,
style: TextStyle(fontSize: fontSize!.fSize, height: isWalletAmountInput! ? 1 / 4 : 21 / 14, fontWeight: FontWeight.w500, color: AppColors.textColor, letterSpacing: -0.2),
decoration: InputDecoration(
isDense: true,
hintText: hintText,
hintStyle: TextStyle(fontSize: 14.fSize, height: 21 / 16, fontWeight: FontWeight.w500, color: Color(0xff898A8D), letterSpacing: -0.2),
prefixIconConstraints: BoxConstraints(minWidth: 45.h),
prefixIcon: prefix == null
? null
: Text(
"+" + prefix!,
style: TextStyle(
fontSize: 14.fSize,
height: 21 / 14,
fontWeight: FontWeight.w500,
color: Color(0xff2E303A),
letterSpacing: -0.2,
),
),
),
contentPadding: EdgeInsets.zero,
border: InputBorder.none,
focusedBorder: InputBorder.none,
enabledBorder: InputBorder.none,
contentPadding: EdgeInsets.zero,
border: InputBorder.none,
focusedBorder: InputBorder.none,
enabledBorder: InputBorder.none,
),
),
);
}

@ -1,6 +1,8 @@
import 'package:flutter/material.dart';
import 'package:get_it/get_it.dart';
import 'package:hmg_patient_app_new/core/api_consts.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/core/enums.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/services/navigation_service.dart';
@ -15,7 +17,7 @@ class LoaderBottomSheet {
showModalBottomSheet(
context: _navService.navigatorKey.currentContext!,
isDismissible: false,
isDismissible: ApiConsts.appEnvironmentType == AppEnvironmentTypeEnum.uat ? true : false,
enableDrag: false,
backgroundColor: Colors.transparent,
builder: (_) {

@ -75,6 +75,9 @@ dependencies:
network_info_plus: ^6.1.4
flutter_nfc_kit: ^3.6.0
barcode_scan2: ^4.5.1
keyboard_actions: ^4.2.0
path_provider: ^2.0.8
open_filex: ^4.7.0
flutter_swiper_view: ^1.1.8
dev_dependencies:

Loading…
Cancel
Save