Merge branch 'master' into dev_aamir
# Conflicts: # lib/core/api/api_client.dart # lib/core/app_assets.dart # lib/features/medical_file/medical_file_repo.dart # lib/features/medical_file/medical_file_view_model.dart # lib/presentation/home/landing_page.dart # lib/presentation/medical_file/medical_file_page.dart # lib/presentation/profile_settings/profile_settings.dart # lib/widgets/chip/app_custom_chip_widget.dartpull/76/head
| @ -0,0 +1 @@ | ||||
| {"nm":"Comp 1","ddd":0,"h":100,"w":100,"meta":{"g":"@lottiefiles/toolkit-js 0.33.2"},"layers":[{"ty":4,"nm":"Shape Layer 2","sr":1,"st":0,"op":300,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[5.277,-32.723,0],"ix":1},"s":{"a":0,"k":[4.91,4.91,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":0,"k":[50.2,50.18,0],"ix":2},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":11}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"mn":"ADBE Vector Group","nm":"Ellipse 2","ix":1,"cix":2,"np":3,"it":[{"ty":"el","bm":0,"hd":false,"mn":"ADBE Vector Shape - Ellipse","nm":"Ellipse Path 1","d":1,"p":{"a":0,"k":[0,0],"ix":3},"s":{"a":0,"k":[102.555,102.555],"ix":2}},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.0941,0.7608,0.451],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[3.277,-34.527],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"gr","bm":0,"hd":false,"mn":"ADBE Vector Group","nm":"Ellipse 1","ix":2,"cix":2,"np":3,"it":[{"ty":"el","bm":0,"hd":false,"mn":"ADBE Vector Shape - Ellipse","nm":"Ellipse Path 1","d":1,"p":{"a":0,"k":[0,0],"ix":3},"s":{"a":0,"k":[102.555,102.555],"ix":2}},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.0941,0.7608,0.451],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":1,"k":[{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[100,100],"t":0},{"s":[295,295],"t":60}],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[3.277,-34.527],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":1,"k":[{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[100],"t":0},{"s":[0],"t":60}],"ix":7}}]}],"ind":1}],"v":"5.5.7","fr":60,"op":61,"ip":0,"assets":[]} | ||||
| @ -0,0 +1,3 @@ | ||||
| <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> | ||||
|     <path fill-rule="evenodd" clip-rule="evenodd" d="M12 1.25C6.06294 1.25 1.25 6.06294 1.25 12C1.25 17.9371 6.06294 22.75 12 22.75C17.9371 22.75 22.75 17.9371 22.75 12C22.75 6.06294 17.9371 1.25 12 1.25ZM9.20711 7.79289C8.81658 7.40237 8.18342 7.40237 7.79289 7.79289C7.40237 8.18342 7.40237 8.81658 7.79289 9.20711L10.5858 12L7.79289 14.7929C7.40237 15.1834 7.40237 15.8166 7.79289 16.2071C8.18342 16.5976 8.81658 16.5976 9.20711 16.2071L12 13.4142L14.7929 16.2071C15.1834 16.5976 15.8166 16.5976 16.2071 16.2071C16.5976 15.8166 16.5976 15.1834 16.2071 14.7929L13.4142 12L16.2071 9.20711C16.5976 8.81658 16.5976 8.18342 16.2071 7.79289C15.8166 7.40237 15.1834 7.40237 14.7929 7.79289L12 10.5858L9.20711 7.79289Z" fill="#8F9AA3"/> | ||||
| </svg> | ||||
| After Width: | Height: | Size: 835 B | 
| @ -0,0 +1,5 @@ | ||||
| <svg width="24" height="25" viewBox="0 0 24 25" fill="none" xmlns="http://www.w3.org/2000/svg"> | ||||
|     <path d="M12 2.75H11.9278C9.92082 2.74998 8.31557 2.74996 7.0558 2.91933C5.75291 3.0945 4.67453 3.46676 3.82064 4.32064C2.96676 5.17453 2.5945 6.25291 2.41933 7.5558C2.24996 8.81557 2.24998 10.4208 2.25 12.4278V12.5722C2.24998 14.5792 2.24996 16.1844 2.41933 17.4442C2.5945 18.7471 2.96676 19.8255 3.82064 20.6794C4.67453 21.5332 5.75291 21.9055 7.0558 22.0807C8.31558 22.25 9.92083 22.25 11.9278 22.25H12.0722C14.0792 22.25 15.6844 22.25 16.9442 22.0807C18.2471 21.9055 19.3255 21.5332 20.1794 20.6794C21.0332 19.8255 21.4055 18.7471 21.5807 17.4442C21.75 16.1844 21.75 14.5792 21.75 12.5722V12.5C21.75 11.9615 21.3135 11.525 20.775 11.525C20.2365 11.525 19.8 11.9615 19.8 12.5C19.8 14.5959 19.7979 16.0697 19.6481 17.1844C19.502 18.271 19.2317 18.8693 18.8005 19.3005C18.3693 19.7317 17.771 20.002 16.6844 20.1481C15.5697 20.2979 14.0959 20.3 12 20.3C9.90415 20.3 8.43035 20.2979 7.31564 20.1481C6.22898 20.002 5.63068 19.7317 5.1995 19.3005C4.76831 18.8693 4.49804 18.271 4.35194 17.1844C4.20207 16.0697 4.2 14.5959 4.2 12.5C4.2 10.4042 4.20207 8.93035 4.35194 7.81564C4.49804 6.72898 4.76831 6.13068 5.1995 5.6995C5.63068 5.26831 6.22898 4.99804 7.31564 4.85194C8.43035 4.70207 9.90415 4.7 12 4.7C12.5385 4.7 12.975 4.26348 12.975 3.725C12.975 3.18652 12.5385 2.75 12 2.75Z" fill="#2E3039"/> | ||||
|     <path d="M17.0855 3.5503C18.1526 2.48323 19.8827 2.48323 20.9497 3.5503C22.0168 4.61736 22.0168 6.34742 20.9497 7.41448L20.078 8.28615L16.2139 4.42188L17.0855 3.5503Z" fill="#2E3039"/> | ||||
|     <path d="M15.1533 5.48254L19.0174 9.34682L14.2337 14.1306C13.2653 15.0992 12.6734 15.6912 11.9448 16.0983C11.5383 16.3255 10.9218 16.5239 10.291 16.6994C9.64198 16.88 8.89524 17.0578 8.18242 17.2275L8.17373 17.2296C7.92037 17.2899 7.65385 17.2145 7.46969 17.0303C7.28552 16.8462 7.21009 16.5796 7.27041 16.3263L7.27249 16.3175C7.44221 15.6047 7.62 14.858 7.8006 14.209C7.97615 13.5782 8.17455 12.9617 8.40167 12.5553C8.80884 11.8266 9.40083 11.2348 10.3694 10.2664L15.1533 5.48254Z" fill="#2E3039"/> | ||||
| </svg> | ||||
| After Width: | Height: | Size: 2.0 KiB | 
| @ -0,0 +1,8 @@ | ||||
| <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> | ||||
|     <path fill-rule="evenodd" clip-rule="evenodd" d="M16.75 3.47825C16.75 3.26399 16.75 3.06711 16.7387 2.90179C16.7266 2.72415 16.699 2.52881 16.6168 2.33031C16.4392 1.90151 16.0985 1.56083 15.6697 1.38321C15.4712 1.30099 15.2759 1.27338 15.0982 1.26126C14.9329 1.24998 14.7361 1.24999 14.5218 1.25L14.4782 1.25C14.264 1.24999 14.0671 1.24998 13.9018 1.26126C13.7241 1.27338 13.5288 1.30099 13.3303 1.38321C12.9015 1.56083 12.5608 1.90151 12.3832 2.33031C12.301 2.52881 12.2734 2.72415 12.2613 2.90179C12.2541 3.00643 12.2515 3.12369 12.2505 3.25L3 3.25C2.58579 3.25 2.25 3.58579 2.25 4C2.25 4.41421 2.58579 4.75 3 4.75L12.2505 4.75C12.2515 4.87632 12.2541 4.99358 12.2613 5.09821C12.2734 5.27585 12.301 5.4712 12.3832 5.6697C12.5608 6.0985 12.9015 6.43918 13.3303 6.61679C13.5288 6.69902 13.7241 6.72663 13.9018 6.73875C14.0671 6.75003 14.264 6.75002 14.4782 6.75H14.5218C14.736 6.75002 14.9329 6.75003 15.0982 6.73875C15.2759 6.72663 15.4712 6.69902 15.6697 6.61679C16.0985 6.43918 16.4392 6.0985 16.6168 5.6697C16.699 5.4712 16.7266 5.27586 16.7387 5.09821C16.75 4.9329 16.75 4.73607 16.75 4.52182V3.47825ZM13.75 4.00171V4.5C13.75 4.74323 13.7504 4.8881 13.7578 4.9961C13.7623 5.06293 13.7684 5.09134 13.7703 5.09869C13.7956 5.15753 13.8425 5.20442 13.9013 5.2297C13.9087 5.23157 13.9371 5.23767 14.0039 5.24223C14.1119 5.2496 14.2568 5.25 14.5 5.25C14.7432 5.25 14.8881 5.2496 14.9961 5.24223C15.0629 5.23767 15.0913 5.23157 15.0987 5.2297C15.1575 5.20442 15.2044 5.15753 15.2297 5.09869C15.2316 5.09134 15.2377 5.06294 15.2422 4.9961C15.2496 4.8881 15.25 4.74323 15.25 4.5V3.5C15.25 3.25677 15.2496 3.11191 15.2422 3.0039C15.2377 2.93707 15.2316 2.90867 15.2297 2.90131C15.2044 2.84248 15.1575 2.79558 15.0987 2.77031C15.0913 2.76844 15.0629 2.76234 14.9961 2.75778C14.8881 2.75041 14.7432 2.75 14.5 2.75C14.2568 2.75 14.1119 2.75041 14.0039 2.75778C13.9371 2.76234 13.9087 2.76844 13.9013 2.77031C13.8425 2.79558 13.7956 2.84248 13.7703 2.90131C13.7684 2.90867 13.7623 2.93707 13.7578 3.0039C13.7504 3.11191 13.75 3.25677 13.75 3.5V3.99829L13.75 4L13.75 4.00171Z" fill="#2E3039"/> | ||||
|     <path fill-rule="evenodd" clip-rule="evenodd" d="M10.2505 19.75L3 19.75C2.58579 19.75 2.25 19.4142 2.25 19C2.25 18.5858 2.58579 18.25 3 18.25L10.2505 18.25C10.2515 18.1237 10.2541 18.0064 10.2613 17.9018C10.2734 17.7241 10.301 17.5288 10.3832 17.3303C10.5608 16.9015 10.9015 16.5608 11.3303 16.3832C11.5288 16.301 11.7241 16.2734 11.9018 16.2613C12.0671 16.25 12.264 16.25 12.4782 16.25H12.5218C12.736 16.25 12.9329 16.25 13.0982 16.2613C13.2759 16.2734 13.4712 16.301 13.6697 16.3832C14.0985 16.5608 14.4392 16.9015 14.6168 17.3303C14.699 17.5288 14.7266 17.7241 14.7387 17.9018C14.75 18.0671 14.75 18.2639 14.75 18.4782L14.75 19.5218C14.75 19.736 14.75 19.9329 14.7387 20.0982C14.7266 20.2759 14.699 20.4712 14.6168 20.6697C14.4392 21.0985 14.0985 21.4392 13.6697 21.6168C13.4712 21.699 13.2759 21.7266 13.0982 21.7387C12.9329 21.75 12.736 21.75 12.5218 21.75H12.4782C12.264 21.75 12.0671 21.75 11.9018 21.7387C11.7241 21.7266 11.5288 21.699 11.3303 21.6168C10.9015 21.4392 10.5608 21.0985 10.3832 20.6697C10.301 20.4712 10.2734 20.2759 10.2613 20.0982C10.2541 19.9936 10.2515 19.8763 10.2505 19.75ZM11.75 19.5L11.75 19.0017L11.75 19L11.75 18.9983V18.5C11.75 18.2568 11.7504 18.1119 11.7578 18.0039C11.7623 17.9371 11.7684 17.9087 11.7703 17.9013C11.7956 17.8425 11.8425 17.7956 11.9013 17.7703C11.9087 17.7684 11.9371 17.7623 12.0039 17.7578C12.1119 17.7504 12.2568 17.75 12.5 17.75C12.7432 17.75 12.8881 17.7504 12.9961 17.7578C13.0629 17.7623 13.0913 17.7684 13.0987 17.7703C13.1575 17.7956 13.2044 17.8425 13.2297 17.9013C13.2316 17.9087 13.2377 17.9371 13.2422 18.0039C13.2496 18.1119 13.25 18.2568 13.25 18.5L13.25 19.5C13.25 19.7432 13.2496 19.8881 13.2422 19.9961C13.2377 20.0629 13.2316 20.0913 13.2297 20.0987C13.2044 20.1575 13.1575 20.2044 13.0987 20.2297C13.0913 20.2316 13.0629 20.2377 12.9961 20.2422C12.8881 20.2496 12.7432 20.25 12.5 20.25C12.2568 20.25 12.1119 20.2496 12.0039 20.2422C11.9371 20.2377 11.9087 20.2316 11.9013 20.2297C11.8425 20.2044 11.7956 20.1575 11.7703 20.0987C11.7684 20.0913 11.7623 20.0629 11.7578 19.9961C11.7504 19.8881 11.75 19.7432 11.75 19.5Z" fill="#2E3039"/> | ||||
|     <path d="M21 19.75C21.4142 19.75 21.75 19.4142 21.75 19C21.75 18.5858 21.4142 18.25 21 18.25H17C16.5858 18.25 16.25 18.5858 16.25 19C16.25 19.4142 16.5858 19.75 17 19.75H21Z" fill="#2E3039"/> | ||||
|     <path fill-rule="evenodd" clip-rule="evenodd" d="M21.75 11.5C21.75 11.9142 21.4142 12.25 21 12.25L11.7495 12.25C11.7485 12.3763 11.7459 12.4936 11.7387 12.5982C11.7266 12.7759 11.699 12.9712 11.6168 13.1697C11.4392 13.5985 11.0985 13.9392 10.6697 14.1168C10.4712 14.199 10.2759 14.2266 10.0982 14.2387C9.93288 14.25 9.73603 14.25 9.52176 14.25H9.47824C9.26396 14.25 9.06711 14.25 8.90179 14.2387C8.72415 14.2266 8.52881 14.199 8.3303 14.1168C7.9015 13.9392 7.56082 13.5985 7.38321 13.1697C7.30099 12.9712 7.27337 12.7759 7.26125 12.5982C7.24997 12.4329 7.24998 12.236 7.25 12.0218V10.9782C7.24998 10.764 7.24997 10.5671 7.26125 10.4018C7.27337 10.2242 7.30099 10.0288 7.38321 9.83031C7.56082 9.40151 7.9015 9.06083 8.3303 8.88321C8.52881 8.80099 8.72415 8.77338 8.90179 8.76126C9.06711 8.74998 9.26396 8.74999 9.47824 8.75H9.52175C9.73603 8.74999 9.93288 8.74998 10.0982 8.76126C10.2759 8.77338 10.4712 8.80099 10.6697 8.88321C11.0985 9.06083 11.4392 9.40151 11.6168 9.83031C11.699 10.0288 11.7266 10.2242 11.7387 10.4018C11.7459 10.5064 11.7485 10.6237 11.7495 10.75L21 10.75C21.4142 10.75 21.75 11.0858 21.75 11.5ZM10.0987 10.2703C10.1575 10.2956 10.2044 10.3425 10.2297 10.4013C10.2316 10.4087 10.2377 10.4371 10.2422 10.5039C10.2496 10.6119 10.25 10.7568 10.25 11V12C10.25 12.2432 10.2496 12.3881 10.2422 12.4961C10.2377 12.5629 10.2316 12.5913 10.2297 12.5987C10.2044 12.6575 10.1575 12.7044 10.0987 12.7297C10.0913 12.7316 10.0629 12.7377 9.9961 12.7422C9.88809 12.7496 9.74323 12.75 9.5 12.75C9.25677 12.75 9.1119 12.7496 9.0039 12.7422C8.93707 12.7377 8.90867 12.7316 8.90131 12.7297C8.84248 12.7044 8.79558 12.6575 8.7703 12.5987C8.76843 12.5913 8.76233 12.5629 8.75777 12.4961C8.7504 12.3881 8.75 12.2432 8.75 12V11C8.75 10.7568 8.7504 10.6119 8.75777 10.5039C8.76233 10.4371 8.76843 10.4087 8.7703 10.4013C8.79558 10.3425 8.84247 10.2956 8.90131 10.2703C8.90866 10.2684 8.93706 10.2623 9.0039 10.2578C9.1119 10.2504 9.25677 10.25 9.5 10.25C9.74323 10.25 9.88809 10.2504 9.9961 10.2578C10.0629 10.2623 10.0913 10.2684 10.0987 10.2703Z" fill="#2E3039"/> | ||||
|     <path d="M21 4.75C21.4142 4.75 21.75 4.41421 21.75 4C21.75 3.58579 21.4142 3.25 21 3.25L19 3.25C18.5858 3.25 18.25 3.58579 18.25 4C18.25 4.41421 18.5858 4.75 19 4.75L21 4.75Z" fill="#2E3039"/> | ||||
|     <path d="M5.75 11.5C5.75 11.9142 5.41421 12.25 5 12.25H3C2.58579 12.25 2.25 11.9142 2.25 11.5C2.25 11.0858 2.58579 10.75 3 10.75H5C5.41421 10.75 5.75 11.0858 5.75 11.5Z" fill="#2E3039"/> | ||||
| </svg> | ||||
| After Width: | Height: | Size: 6.8 KiB | 
| @ -1,3 +1,3 @@ | ||||
| <svg width="18" height="12" viewBox="0 0 18 12" fill="none" xmlns="http://www.w3.org/2000/svg"> | ||||
|     <path d="M17.7495 6.00047C17.7495 5.62691 17.5839 5.28034 17.427 5.02011C17.2577 4.7393 17.0301 4.44937 16.7803 4.16669C16.2794 3.59962 15.6257 2.99064 14.9913 2.43977C14.3532 1.88567 13.7173 1.37546 13.2423 1.00468C13.0044 0.818986 12.8059 0.66755 12.6665 0.562239C12.5968 0.509569 12.5418 0.468397 12.5039 0.440203L12.4604 0.407818L12.4488 0.399264L12.4448 0.396304C12.1113 0.150642 11.6414 0.221533 11.3957 0.555035C11.1501 0.888521 11.2213 1.358 11.5547 1.60367L11.5674 1.61308L11.6075 1.64285C11.6429 1.66925 11.6953 1.7085 11.7623 1.75911C11.8964 1.86036 12.0885 2.00695 12.3193 2.18713C12.7818 2.5481 13.3959 3.04099 14.0078 3.57235C14.6234 4.10694 15.2197 4.66576 15.6562 5.15983C15.6838 5.19108 15.7105 5.2218 15.7364 5.25195L0.999512 5.25196C0.585298 5.25196 0.249512 5.58774 0.249512 6.00195C0.249512 6.41617 0.585299 6.75196 0.999512 6.75196L15.7338 6.75195C15.7088 6.78116 15.6829 6.81088 15.6562 6.84111C15.2197 7.33518 14.6234 7.89401 14.0078 8.4286C13.3959 8.95995 12.7818 9.45285 12.3193 9.81382C12.0885 9.99399 11.8964 10.1406 11.7623 10.2418C11.6953 10.2925 11.6429 10.3317 11.6075 10.3581L11.5674 10.3879L11.5547 10.3973C11.2213 10.6429 11.1501 11.1124 11.3957 11.4459C11.6414 11.7794 12.1113 11.8503 12.4448 11.6046L12.4488 11.6017L12.4604 11.5931L12.5039 11.5607C12.5418 11.5326 12.5968 11.4914 12.6665 11.4387C12.8059 11.3334 13.0044 11.182 13.2423 10.9963C13.7173 10.6255 14.3532 10.1153 14.9913 9.56118C15.6257 9.01031 16.2794 8.40133 16.7803 7.83425C17.0301 7.55158 17.2577 7.26165 17.427 6.98083C17.5829 6.72217 17.7475 6.37819 17.7495 6.0072" fill="white"/> | ||||
| <svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg"> | ||||
|     <path d="M28.7495 19.9995C28.7495 19.6259 28.5839 19.2794 28.427 19.0191C28.2577 18.7383 28.0301 18.4484 27.7803 18.1657C27.2794 17.5986 26.6257 16.9897 25.9913 16.4388C25.3532 15.8847 24.7173 15.3745 24.2423 15.0037C24.0044 14.818 23.8059 14.6666 23.6665 14.5613C23.5968 14.5086 23.5418 14.4674 23.5039 14.4392L23.4604 14.4068L23.4488 14.3983L23.4448 14.3953C23.1113 14.1497 22.6414 14.2206 22.3957 14.5541C22.1501 14.8875 22.2213 15.357 22.5547 15.6027L22.5674 15.6121L22.6075 15.6419C22.6429 15.6683 22.6953 15.7075 22.7623 15.7581C22.8964 15.8594 23.0885 16.006 23.3193 16.1862C23.7818 16.5471 24.3959 17.04 25.0078 17.5714C25.6234 18.106 26.2197 18.6648 26.6562 19.1589C26.6838 19.1901 26.7105 19.2208 26.7364 19.251L11.9995 19.251C11.5853 19.251 11.2495 19.5868 11.2495 20.001C11.2495 20.4152 11.5853 20.751 11.9995 20.751L26.7338 20.751C26.7088 20.7802 26.6829 20.8099 26.6562 20.8401C26.2197 21.3342 25.6234 21.893 25.0078 22.4276C24.3959 22.959 23.7818 23.4519 23.3193 23.8128C23.0885 23.993 22.8964 24.1396 22.7623 24.2409C22.6953 24.2915 22.6429 24.3307 22.6075 24.3571L22.5674 24.3869L22.5547 24.3963C22.2213 24.642 22.1501 25.1115 22.3957 25.4449C22.6414 25.7784 23.1113 25.8493 23.4448 25.6037L23.4488 25.6007L23.4604 25.5922L23.5039 25.5598C23.5418 25.5316 23.5968 25.4904 23.6665 25.4377C23.8059 25.3324 24.0044 25.181 24.2423 24.9953C24.7173 24.6245 25.3532 24.1143 25.9913 23.5602C26.6257 23.0093 27.2794 22.4003 27.7803 21.8333C28.0301 21.5506 28.2577 21.2607 28.427 20.9799C28.5829 20.7212 28.7475 20.3772 28.7495 20.0062" fill="#2E3039"/> | ||||
| </svg> | ||||
|  | ||||
| Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.6 KiB | 
| @ -0,0 +1,4 @@ | ||||
| <svg width="20" height="14" viewBox="0 0 20 14" fill="none" xmlns="http://www.w3.org/2000/svg"> | ||||
|     <path d="M12.43 13.823C12.24 13.823 12.05 13.753 11.9 13.603C11.61 13.313 11.61 12.833 11.9 12.543L17.44 7.00305L11.9 1.46305C11.61 1.17305 11.61 0.693047 11.9 0.403047C12.19 0.113047 12.67 0.113047 12.96 0.403047L19.03 6.47305C19.32 6.76305 19.32 7.24305 19.03 7.53305L12.96 13.603C12.81 13.753 12.62 13.823 12.43 13.823Z" fill="#2E3039"/> | ||||
|     <path d="M18.33 7.75305H1.5C1.09 7.75305 0.75 7.41305 0.75 7.00305C0.75 6.59305 1.09 6.25305 1.5 6.25305H18.33C18.74 6.25305 19.08 6.59305 19.08 7.00305C19.08 7.41305 18.74 7.75305 18.33 7.75305Z" fill="#2E3039"/> | ||||
| </svg> | ||||
| After Width: | Height: | Size: 666 B | 
| @ -0,0 +1,3 @@ | ||||
| <svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg"> | ||||
|     <path fill-rule="evenodd" clip-rule="evenodd" d="M7.29289 7.29289C7.68342 6.90237 8.31658 6.90237 8.70711 7.29289L16 14.5858L23.2929 7.29289C23.6834 6.90237 24.3166 6.90237 24.7071 7.29289C25.0976 7.68342 25.0976 8.31658 24.7071 8.70711L17.4142 16L24.7071 23.2929C25.0976 23.6834 25.0976 24.3166 24.7071 24.7071C24.3166 25.0976 23.6834 25.0976 23.2929 24.7071L16 17.4142L8.70711 24.7071C8.31658 25.0976 7.68342 25.0976 7.29289 24.7071C6.90237 24.3166 6.90237 23.6834 7.29289 23.2929L14.5858 16L7.29289 8.70711C6.90237 8.31658 6.90237 7.68342 7.29289 7.29289Z" fill="#2E3039"/> | ||||
| </svg> | ||||
| After Width: | Height: | Size: 684 B | 
| @ -0,0 +1,4 @@ | ||||
| <svg width="19" height="19" viewBox="0 0 19 19" fill="none" xmlns="http://www.w3.org/2000/svg"> | ||||
|     <circle cx="9.5" cy="9.5" r="6.5" fill="#18C273"/> | ||||
|     <circle cx="9.5" cy="9.5" r="8" stroke="#18C273" stroke-opacity="0.31" stroke-width="3"/> | ||||
| </svg> | ||||
| After Width: | Height: | Size: 252 B | 
| @ -0,0 +1,3 @@ | ||||
| <svg width="24" height="25" viewBox="0 0 24 25" fill="none" xmlns="http://www.w3.org/2000/svg"> | ||||
|     <path fill-rule="evenodd" clip-rule="evenodd" d="M4 1.75C3.58579 1.75 3.25 2.08579 3.25 2.5C3.25 2.91421 3.58579 3.25 4 3.25H4.25L4.25 5.5C4.25 8.58811 6.05618 11.2544 8.66976 12.5C6.05618 13.7456 4.25 16.4119 4.25 19.5L4.25 21.75H4C3.58579 21.75 3.25 22.0858 3.25 22.5C3.25 22.9142 3.58579 23.25 4 23.25L20 23.25C20.4142 23.25 20.75 22.9142 20.75 22.5C20.75 22.0858 20.4142 21.75 20 21.75H19.75V19.5C19.75 16.4119 17.9438 13.7456 15.3302 12.5C17.9438 11.2544 19.75 8.58811 19.75 5.5V3.25H20C20.4142 3.25 20.75 2.91421 20.75 2.5C20.75 2.08579 20.4142 1.75 20 1.75L4 1.75ZM18.25 3.25L5.75 3.25L5.75 5.5C5.75 8.95178 8.54822 11.75 12 11.75C15.4518 11.75 18.25 8.95178 18.25 5.5V3.25ZM18.25 21.75L18.25 19.5C18.25 16.0482 15.4518 13.25 12 13.25C8.54822 13.25 5.75 16.0482 5.75 19.5L5.75 21.75L18.25 21.75Z" fill="#2B353E"/> | ||||
| </svg> | ||||
| After Width: | Height: | Size: 927 B | 
| @ -0,0 +1,24 @@ | ||||
| class DentalChiefComplaintsListResponseModel { | ||||
|   int? projectID; | ||||
|   int? iD; | ||||
|   String? name; | ||||
|   dynamic nameN; | ||||
| 
 | ||||
|   DentalChiefComplaintsListResponseModel({this.projectID, this.iD, this.name, this.nameN}); | ||||
| 
 | ||||
|   DentalChiefComplaintsListResponseModel.fromJson(Map<String, dynamic> json) { | ||||
|     projectID = json['ProjectID']; | ||||
|     iD = json['ID']; | ||||
|     name = json['Name']; | ||||
|     nameN = json['NameN']; | ||||
|   } | ||||
| 
 | ||||
|   Map<String, dynamic> toJson() { | ||||
|     final Map<String, dynamic> data = new Map<String, dynamic>(); | ||||
|     data['ProjectID'] = this.projectID; | ||||
|     data['ID'] = this.iD; | ||||
|     data['Name'] = this.name; | ||||
|     data['NameN'] = this.nameN; | ||||
|     return data; | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,40 @@ | ||||
| class PatientDentalPlanEstimationResponseModel { | ||||
|   dynamic setupID; | ||||
|   dynamic estimationNo; | ||||
|   int? projectID; | ||||
|   String? procedureId; | ||||
|   int? patientID; | ||||
|   int? sequenceNo; | ||||
|   int? neededTime; | ||||
|   String? procedureName; | ||||
|   String? procedureNameN; | ||||
| 
 | ||||
|   PatientDentalPlanEstimationResponseModel( | ||||
|       {this.setupID, this.estimationNo, this.projectID, this.procedureId, this.patientID, this.sequenceNo, this.neededTime, this.procedureName, this.procedureNameN}); | ||||
| 
 | ||||
|   PatientDentalPlanEstimationResponseModel.fromJson(Map<String, dynamic> json) { | ||||
|     setupID = json['SetupID']; | ||||
|     estimationNo = json['EstimationNo']; | ||||
|     projectID = json['ProjectID']; | ||||
|     procedureId = json['ProcedureId']; | ||||
|     patientID = json['PatientID']; | ||||
|     sequenceNo = json['sequenceNo']; | ||||
|     neededTime = json['NeededTime']; | ||||
|     procedureName = json['ProcedureName']; | ||||
|     procedureNameN = json['ProcedureNameN']; | ||||
|   } | ||||
| 
 | ||||
|   Map<String, dynamic> toJson() { | ||||
|     final Map<String, dynamic> data = new Map<String, dynamic>(); | ||||
|     data['SetupID'] = this.setupID; | ||||
|     data['EstimationNo'] = this.estimationNo; | ||||
|     data['ProjectID'] = this.projectID; | ||||
|     data['ProcedureId'] = this.procedureId; | ||||
|     data['PatientID'] = this.patientID; | ||||
|     data['sequenceNo'] = this.sequenceNo; | ||||
|     data['NeededTime'] = this.neededTime; | ||||
|     data['ProcedureName'] = this.procedureName; | ||||
|     data['ProcedureNameN'] = this.procedureNameN; | ||||
|     return data; | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,110 @@ | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:hmg_patient_app_new/core/app_state.dart'; | ||||
| import 'package:hmg_patient_app_new/core/dependencies.dart'; | ||||
| import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/doctors_list_response_model.dart'; | ||||
| import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/doctor_list_api_response.dart' show PatientDoctorAppointmentList; | ||||
| 
 | ||||
| class DoctorFilterViewModel extends ChangeNotifier{ | ||||
| 
 | ||||
|   late AppState appState; | ||||
|   DoctorFilterViewModel(){ | ||||
|     appState = getIt(); | ||||
|   } | ||||
|   List<String> searchedRegionList = []; | ||||
|   List<String> facilityList = ["hmgHospitals", "hmcMedicalClinic"]; | ||||
|   List<String> searchedHospitalList = []; | ||||
|   List<PatientDoctorAppointmentList> | ||||
|   searchedPatientDoctorAppointmentHospitalsList = []; | ||||
|   List<String> searchedClinicList = []; | ||||
| 
 | ||||
|   PatientDoctorAppointmentList? selectedHospitalForFilters; | ||||
|   List<String>? selectedFacilityForFilters = [] , selectedRegionForFilters = []; | ||||
|   String? selectedClinicForFilters; | ||||
|   bool applyFilters = false; | ||||
| 
 | ||||
|   void clearSearchFilters() { | ||||
|     searchedRegionList.clear(); | ||||
|     searchedHospitalList.clear(); | ||||
|     searchedPatientDoctorAppointmentHospitalsList.clear(); | ||||
|     searchedClinicList.clear(); | ||||
|     notifyListeners(); | ||||
|   } | ||||
| 
 | ||||
|   void clearSelection() { | ||||
|     selectedFacilityForFilters = []; | ||||
|     selectedClinicForFilters = null; | ||||
|     selectedHospitalForFilters = null; | ||||
|     selectedRegionForFilters = []; | ||||
|     applyFilters = false; | ||||
|     notifyListeners(); | ||||
|   } | ||||
| 
 | ||||
|   void getFiltersFromDoctorList(List<DoctorsListResponseModel> doctorsList) { | ||||
|     doctorsList.forEach((element) { | ||||
|       if (!searchedRegionList | ||||
|           .contains(element.getRegionName(appState.isArabic()))) { | ||||
|         searchedRegionList | ||||
|             .add(element.getRegionName(appState.isArabic()) ?? ""); | ||||
|       } | ||||
|       if (!searchedHospitalList.contains(element.projectName)) { | ||||
|         searchedPatientDoctorAppointmentHospitalsList | ||||
|             .add(PatientDoctorAppointmentList() | ||||
|           ..filterName = element.projectName | ||||
|           ..isHMC = element.isHMC | ||||
|           ..distanceInKMs = "0"); | ||||
|         searchedHospitalList.add(element.projectName ?? ""); | ||||
|       } | ||||
|       if (!searchedClinicList.contains(element.clinicName)) { | ||||
|         searchedClinicList.add(element.clinicName ?? ""); | ||||
|       } | ||||
|     }); | ||||
|   } | ||||
| 
 | ||||
|   void updateApplyFilters(bool applyFilters) { | ||||
|     this.applyFilters = applyFilters; | ||||
|     notifyListeners(); | ||||
|   } | ||||
| 
 | ||||
|   void setSelectedRegion(String region) { | ||||
|     if (selectedRegionForFilters?.contains(region) == true) { | ||||
|       selectedRegionForFilters?.remove(region); | ||||
|     } else { | ||||
|       selectedRegionForFilters?.add(region); | ||||
|     } | ||||
|     notifyListeners(); | ||||
|   } | ||||
| 
 | ||||
|   void setSelectedHospital(PatientDoctorAppointmentList? hospital) { | ||||
|     selectedHospitalForFilters = hospital; | ||||
|     notifyListeners(); | ||||
|   } | ||||
| 
 | ||||
|   void setSelectedFacilityForFilter(String facility) { | ||||
|     if (selectedFacilityForFilters?.contains(facility) == true) { | ||||
|       selectedFacilityForFilters?.remove(facility); | ||||
|     } else { | ||||
|       selectedFacilityForFilters?.add(facility); | ||||
|     } | ||||
| 
 | ||||
|     notifyListeners(); | ||||
|   } | ||||
| 
 | ||||
|   void setSelectedClinicForFilter(String? clinic) { | ||||
|     selectedClinicForFilters = clinic; | ||||
|     notifyListeners(); | ||||
|   } | ||||
| 
 | ||||
|   void setSelections( | ||||
|       List<String>? selectedFacilityForFilters, | ||||
|       List<String>? selectedRegionForFilters, | ||||
|       String? selectedClinicForFilters, | ||||
|       PatientDoctorAppointmentList? selectedHospitalForFilters, | ||||
|       bool applyFilters) { | ||||
|     this.selectedFacilityForFilters = selectedFacilityForFilters; | ||||
|     this.selectedClinicForFilters = selectedClinicForFilters; | ||||
|     this.selectedHospitalForFilters = selectedHospitalForFilters; | ||||
|     this.selectedRegionForFilters = selectedRegionForFilters; | ||||
|     this.applyFilters = applyFilters; | ||||
|     notifyListeners(); | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,206 @@ | ||||
| import 'dart:io'; | ||||
| 
 | ||||
| import 'package:dartz/dartz.dart'; | ||||
| import 'package:hmg_patient_app_new/core/api/api_client.dart'; | ||||
| import 'package:hmg_patient_app_new/core/api_consts.dart'; | ||||
| import 'package:hmg_patient_app_new/core/common_models/generic_api_model.dart'; | ||||
| import 'package:hmg_patient_app_new/core/exceptions/api_failure.dart'; | ||||
| import 'package:hmg_patient_app_new/features/immediate_livecare/models/resp_models/get_livecare_immediate_clinics_response_model.dart'; | ||||
| import 'package:hmg_patient_app_new/features/immediate_livecare/models/resp_models/get_livecare_immediate_fees_response_model.dart'; | ||||
| import 'package:hmg_patient_app_new/features/immediate_livecare/models/resp_models/get_patient_livecare_history_response_model.dart'; | ||||
| import 'package:hmg_patient_app_new/services/logger_service.dart'; | ||||
| 
 | ||||
| abstract class ImmediateLiveCareRepo { | ||||
|   Future<Either<Failure, GenericApiModel<List<GetLiveCareClinicListResponseModel>>>> getLiveCareImmediateClinicsList(int age, int genderID, {Function(dynamic)? onSuccess, Function(String)? onError}); | ||||
| 
 | ||||
|   Future<Either<Failure, GenericApiModel<LiveCareImmediateAppointmentFeesList>>> getLiveCareImmediateAppointmentFees(int age, int genderID, int serviceID, | ||||
|       {Function(dynamic)? onSuccess, Function(String)? onError}); | ||||
| 
 | ||||
|   Future<Either<Failure, GenericApiModel<dynamic>>> addNewCallRequestForImmediateLiveCare( | ||||
|       int age, int gender, int serviceID, String clientRequestID, int callTypeID, bool isPharma, String deviceToken, String voipToken, | ||||
|       {Function(dynamic)? onSuccess, Function(String)? onError}); | ||||
| 
 | ||||
|   Future<Either<Failure, GenericApiModel<List<PatientLiveCareHistory>>>> getPatientLiveCareHistory({Function(dynamic)? onSuccess, Function(String)? onError}); | ||||
| } | ||||
| 
 | ||||
| class ImmediateLiveCareRepoImp implements ImmediateLiveCareRepo { | ||||
|   final ApiClient apiClient; | ||||
|   final LoggerService loggerService; | ||||
| 
 | ||||
|   ImmediateLiveCareRepoImp({required this.loggerService, required this.apiClient}); | ||||
| 
 | ||||
|   @override | ||||
|   Future<Either<Failure, GenericApiModel<List<GetLiveCareClinicListResponseModel>>>> getLiveCareImmediateClinicsList(int age, int genderID, | ||||
|       {Function(dynamic)? onSuccess, Function(String)? onError}) async { | ||||
|     Map<String, dynamic> mapDevice = { | ||||
|       "Age": age, | ||||
|       "Gender": genderID, | ||||
|     }; | ||||
| 
 | ||||
|     try { | ||||
|       GenericApiModel<List<GetLiveCareClinicListResponseModel>>? apiResponse; | ||||
|       Failure? failure; | ||||
|       await apiClient.post( | ||||
|         GET_LIVECARE_CLINICS, | ||||
|         body: mapDevice, | ||||
|         onFailure: (error, statusCode, {messageStatus, failureType}) { | ||||
|           failure = failureType; | ||||
|           onError!(error); | ||||
|         }, | ||||
|         onSuccess: (response, statusCode, {messageStatus, errorMessage}) { | ||||
|           try { | ||||
|             final list = response['PatientER_GetClinicsList']; | ||||
| 
 | ||||
|             final clinicsList = list.map((item) => GetLiveCareClinicListResponseModel.fromJson(item as Map<String, dynamic>)).toList().cast<GetLiveCareClinicListResponseModel>(); | ||||
| 
 | ||||
|             apiResponse = GenericApiModel<List<GetLiveCareClinicListResponseModel>>( | ||||
|               messageStatus: messageStatus, | ||||
|               statusCode: statusCode, | ||||
|               errorMessage: null, | ||||
|               data: clinicsList, | ||||
|             ); | ||||
|           } catch (e) { | ||||
|             failure = DataParsingFailure(e.toString()); | ||||
|           } | ||||
|         }, | ||||
|       ); | ||||
|       if (failure != null) return Left(failure!); | ||||
|       if (apiResponse == null) return Left(ServerFailure("Unknown error")); | ||||
|       return Right(apiResponse!); | ||||
|     } catch (e) { | ||||
|       return Left(UnknownFailure(e.toString())); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   @override | ||||
|   Future<Either<Failure, GenericApiModel<LiveCareImmediateAppointmentFeesList>>> getLiveCareImmediateAppointmentFees(int age, int genderID, int serviceID, | ||||
|       {Function(dynamic)? onSuccess, Function(String)? onError}) async { | ||||
|     Map<String, dynamic> mapDevice = { | ||||
|       "Age": age, | ||||
|       "Gender": genderID, | ||||
|       "ServiceID": serviceID, | ||||
|     }; | ||||
| 
 | ||||
|     try { | ||||
|       GenericApiModel<LiveCareImmediateAppointmentFeesList>? apiResponse; | ||||
|       Failure? failure; | ||||
|       await apiClient.post( | ||||
|         GET_ER_APPOINTMENT_FEES, | ||||
|         body: mapDevice, | ||||
|         onFailure: (error, statusCode, {messageStatus, failureType}) { | ||||
|           failure = failureType; | ||||
|           onError!(error); | ||||
|         }, | ||||
|         onSuccess: (response, statusCode, {messageStatus, errorMessage}) { | ||||
|           try { | ||||
|             final respObject = response['GetERAppointmentFeesList']; | ||||
| 
 | ||||
|             final liveCareFeesObj = LiveCareImmediateAppointmentFeesList.fromJson(respObject); | ||||
| 
 | ||||
|             apiResponse = GenericApiModel<LiveCareImmediateAppointmentFeesList>( | ||||
|               messageStatus: messageStatus, | ||||
|               statusCode: statusCode, | ||||
|               errorMessage: null, | ||||
|               data: liveCareFeesObj, | ||||
|             ); | ||||
|           } catch (e) { | ||||
|             failure = DataParsingFailure(e.toString()); | ||||
|           } | ||||
|         }, | ||||
|       ); | ||||
|       if (failure != null) return Left(failure!); | ||||
|       if (apiResponse == null) return Left(ServerFailure("Unknown error")); | ||||
|       return Right(apiResponse!); | ||||
|     } catch (e) { | ||||
|       return Left(UnknownFailure(e.toString())); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   @override | ||||
|   Future<Either<Failure, GenericApiModel>> addNewCallRequestForImmediateLiveCare( | ||||
|       int age, int gender, int serviceID, String clientRequestID, int callTypeID, bool isPharma, String deviceToken, String voipToken, | ||||
|       {Function(dynamic p1)? onSuccess, Function(String p1)? onError}) async { | ||||
|     Map<String, dynamic> mapDevice = { | ||||
|       "IsPharmacy": isPharma, | ||||
|       "ErServiceID": serviceID, | ||||
|       "ClientRequestID": clientRequestID, | ||||
|       "DeviceToken": deviceToken, | ||||
|       "VoipToken": voipToken, | ||||
|       "IsFlutter": true, | ||||
|       "DeviceType": Platform.isIOS ? 'iOS' : 'Android', | ||||
|       "Age": age, | ||||
|       "Gender": gender, | ||||
|       "IsVoip": Platform.isIOS ? true : false, | ||||
|       "CallTypeID": callTypeID | ||||
|     }; | ||||
| 
 | ||||
|     try { | ||||
|       GenericApiModel<dynamic>? apiResponse; | ||||
|       Failure? failure; | ||||
|       await apiClient.post( | ||||
|         ADD_NEW_CALL_FOR_PATIENT_ER, | ||||
|         body: mapDevice, | ||||
|         onFailure: (error, statusCode, {messageStatus, failureType}) { | ||||
|           failure = failureType; | ||||
|           onError!(error); | ||||
|         }, | ||||
|         onSuccess: (response, statusCode, {messageStatus, errorMessage}) { | ||||
|           try { | ||||
|             apiResponse = GenericApiModel<dynamic>( | ||||
|               messageStatus: messageStatus, | ||||
|               statusCode: statusCode, | ||||
|               errorMessage: null, | ||||
|               data: true, | ||||
|             ); | ||||
|           } catch (e) { | ||||
|             failure = DataParsingFailure(e.toString()); | ||||
|           } | ||||
|         }, | ||||
|       ); | ||||
|       if (failure != null) return Left(failure!); | ||||
|       if (apiResponse == null) return Left(ServerFailure("Unknown error")); | ||||
|       return Right(apiResponse!); | ||||
|     } catch (e) { | ||||
|       return Left(UnknownFailure(e.toString())); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   @override | ||||
|   Future<Either<Failure, GenericApiModel<List<PatientLiveCareHistory>>>> getPatientLiveCareHistory({Function(dynamic)? onSuccess, Function(String)? onError}) async { | ||||
|     Map<String, dynamic> mapDevice = {}; | ||||
| 
 | ||||
|     try { | ||||
|       GenericApiModel<List<PatientLiveCareHistory>>? apiResponse; | ||||
|       Failure? failure; | ||||
|       await apiClient.post( | ||||
|         GET_LIVECARE_HISTORY, | ||||
|         body: mapDevice, | ||||
|         onFailure: (error, statusCode, {messageStatus, failureType}) { | ||||
|           failure = failureType; | ||||
|           onError!(error); | ||||
|         }, | ||||
|         onSuccess: (response, statusCode, {messageStatus, errorMessage}) { | ||||
|           try { | ||||
|             final list = response['ErRequestHistoryList']; | ||||
| 
 | ||||
|             final liveCareHistoryList = list.map((item) => PatientLiveCareHistory.fromJson(item as Map<String, dynamic>)).toList().cast<PatientLiveCareHistory>(); | ||||
| 
 | ||||
|             apiResponse = GenericApiModel<List<PatientLiveCareHistory>>( | ||||
|               messageStatus: messageStatus, | ||||
|               statusCode: statusCode, | ||||
|               errorMessage: null, | ||||
|               data: liveCareHistoryList, | ||||
|             ); | ||||
|           } catch (e) { | ||||
|             failure = DataParsingFailure(e.toString()); | ||||
|           } | ||||
|         }, | ||||
|       ); | ||||
|       if (failure != null) return Left(failure!); | ||||
|       if (apiResponse == null) return Left(ServerFailure("Unknown error")); | ||||
|       return Right(apiResponse!); | ||||
|     } catch (e) { | ||||
|       return Left(UnknownFailure(e.toString())); | ||||
|     } | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,162 @@ | ||||
| import 'package:flutter/cupertino.dart'; | ||||
| import 'package:hmg_patient_app_new/core/app_state.dart'; | ||||
| import 'package:hmg_patient_app_new/core/cache_consts.dart'; | ||||
| import 'package:hmg_patient_app_new/core/dependencies.dart'; | ||||
| import 'package:hmg_patient_app_new/core/utils/utils.dart'; | ||||
| import 'package:hmg_patient_app_new/features/immediate_livecare/immediate_livecare_repo.dart'; | ||||
| import 'package:hmg_patient_app_new/features/immediate_livecare/models/resp_models/get_livecare_immediate_clinics_response_model.dart'; | ||||
| import 'package:hmg_patient_app_new/features/immediate_livecare/models/resp_models/get_livecare_immediate_fees_response_model.dart'; | ||||
| import 'package:hmg_patient_app_new/features/immediate_livecare/models/resp_models/get_patient_livecare_history_response_model.dart'; | ||||
| import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart'; | ||||
| import 'package:hmg_patient_app_new/services/error_handler_service.dart'; | ||||
| 
 | ||||
| import '../../services/navigation_service.dart'; | ||||
| 
 | ||||
| class ImmediateLiveCareViewModel extends ChangeNotifier { | ||||
|   ImmediateLiveCareViewModel({ | ||||
|     required this.immediateLiveCareRepo, | ||||
|     required this.errorHandlerService, | ||||
|     required this.navigationService, | ||||
|     required this.myAppointmentsViewModel, | ||||
|   }); | ||||
| 
 | ||||
|   ImmediateLiveCareRepo immediateLiveCareRepo; | ||||
|   ErrorHandlerService errorHandlerService; | ||||
|   final NavigationService navigationService; | ||||
|   MyAppointmentsViewModel myAppointmentsViewModel; | ||||
| 
 | ||||
|   List<GetLiveCareClinicListResponseModel> immediateLiveCareClinicsList = []; | ||||
|   bool isImmediateLiveCareClinicsLoading = false; | ||||
|   int liveCareSelectedCallType = 0; // 1- Video, 2- Audio, 3- Phone | ||||
|   late GetLiveCareClinicListResponseModel immediateLiveCareSelectedClinic; | ||||
|   late LiveCareImmediateAppointmentFeesList liveCareImmediateAppointmentFeesList; | ||||
| 
 | ||||
|   List<PatientLiveCareHistory> patientLiveCareHistoryList = []; | ||||
|   bool patientHasPendingLiveCareRequest = false; | ||||
| 
 | ||||
|   late AppState _appState; | ||||
| 
 | ||||
|   initImmediateLiveCare() { | ||||
|     _appState = getIt<AppState>(); | ||||
|     immediateLiveCareClinicsList = []; | ||||
|     patientLiveCareHistoryList = []; | ||||
|     isImmediateLiveCareClinicsLoading = true; | ||||
|     patientHasPendingLiveCareRequest = false; | ||||
|     liveCareSelectedCallType = 0; // 1- Video, 2- Audio, 3- Phone | ||||
|     immediateLiveCareSelectedClinic = GetLiveCareClinicListResponseModel(); | ||||
|     liveCareImmediateAppointmentFeesList = LiveCareImmediateAppointmentFeesList(); | ||||
|   } | ||||
| 
 | ||||
|   setLiveCareSelectedCallType(int value) { | ||||
|     liveCareSelectedCallType = value; | ||||
|     notifyListeners(); | ||||
|   } | ||||
| 
 | ||||
|   setImmediateLiveCareSelectedClinic(GetLiveCareClinicListResponseModel clinic) { | ||||
|     immediateLiveCareSelectedClinic = clinic; | ||||
|     notifyListeners(); | ||||
|   } | ||||
| 
 | ||||
|   Future<void> getLiveCareImmediateClinicsList({Function(dynamic)? onSuccess, Function(String)? onError}) async { | ||||
|     immediateLiveCareClinicsList.clear(); | ||||
|     isImmediateLiveCareClinicsLoading = true; | ||||
|     notifyListeners(); | ||||
| 
 | ||||
|     final result = await immediateLiveCareRepo.getLiveCareImmediateClinicsList(_appState.getAuthenticatedUser()!.age!, _appState.getAuthenticatedUser()!.gender!); | ||||
| 
 | ||||
|     result.fold( | ||||
|       (failure) async => await errorHandlerService.handleError(failure: failure), | ||||
|       (apiResponse) { | ||||
|         if (apiResponse.messageStatus == 2) { | ||||
|           // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {}); | ||||
|         } else if (apiResponse.messageStatus == 1) { | ||||
|           immediateLiveCareClinicsList = apiResponse.data!; | ||||
| 
 | ||||
|           immediateLiveCareClinicsList.sort((a, b) => b.isOnline!.compareTo(a.isOnline!)); | ||||
| 
 | ||||
|           isImmediateLiveCareClinicsLoading = false; | ||||
|           notifyListeners(); | ||||
|           if (onSuccess != null) { | ||||
|             onSuccess(apiResponse); | ||||
|           } | ||||
|         } | ||||
|       }, | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   Future<void> getLiveCareImmediateAppointmentFees({Function(dynamic)? onSuccess, Function(String)? onError}) async { | ||||
|     final result = | ||||
|         await immediateLiveCareRepo.getLiveCareImmediateAppointmentFees(_appState.getAuthenticatedUser()!.age!, _appState.getAuthenticatedUser()!.gender!, immediateLiveCareSelectedClinic.serviceID!); | ||||
| 
 | ||||
|     result.fold( | ||||
|       (failure) async { | ||||
|         onError!(failure.message); | ||||
|       }, | ||||
|       (apiResponse) { | ||||
|         if (apiResponse.messageStatus == 2) { | ||||
|           onError!(apiResponse.errorMessage ?? "Unknown error occurred"); | ||||
|           // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {}); | ||||
|         } else if (apiResponse.messageStatus == 1) { | ||||
|           liveCareImmediateAppointmentFeesList = apiResponse.data!; | ||||
|           notifyListeners(); | ||||
|           if (onSuccess != null) { | ||||
|             onSuccess(apiResponse); | ||||
|           } | ||||
|         } | ||||
|       }, | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   Future<void> addNewCallRequestForImmediateLiveCare(String transID, {Function(dynamic)? onSuccess, Function(String)? onError}) async { | ||||
|     final result = await immediateLiveCareRepo.addNewCallRequestForImmediateLiveCare(_appState.getAuthenticatedUser()!.age!, _appState.getAuthenticatedUser()!.gender!, | ||||
|         immediateLiveCareSelectedClinic.serviceID!, transID, liveCareSelectedCallType, false, _appState.deviceToken, await Utils.getStringFromPrefs(CacheConst.voipToken)); | ||||
| 
 | ||||
|     result.fold( | ||||
|       (failure) async { | ||||
|         onError!(failure.message); | ||||
|       }, | ||||
|       (apiResponse) { | ||||
|         if (apiResponse.messageStatus == 2) { | ||||
|           onError!(apiResponse.errorMessage ?? "Unknown error occurred"); | ||||
|           // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {}); | ||||
|         } else if (apiResponse.messageStatus == 1) { | ||||
|           notifyListeners(); | ||||
|           if (onSuccess != null) { | ||||
|             onSuccess(apiResponse); | ||||
|           } | ||||
|         } | ||||
|       }, | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   Future<void> getPatientLiveCareHistory({Function(dynamic)? onSuccess, Function(String)? onError}) async { | ||||
|     final result = await immediateLiveCareRepo.getPatientLiveCareHistory(); | ||||
| 
 | ||||
|     result.fold( | ||||
|       (failure) async { | ||||
|         onError!(failure.message); | ||||
|       }, | ||||
|       (apiResponse) { | ||||
|         if (apiResponse.messageStatus == 2) { | ||||
|           onError!(apiResponse.errorMessage ?? "Unknown error occurred"); | ||||
|           // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {}); | ||||
|         } else if (apiResponse.messageStatus == 1) { | ||||
|           patientLiveCareHistoryList = apiResponse.data!; | ||||
|           if (patientLiveCareHistoryList.isNotEmpty) { | ||||
|             if (patientLiveCareHistoryList[0].callStatus! < 4) { | ||||
|               patientHasPendingLiveCareRequest = true; | ||||
|             } else { | ||||
|               patientHasPendingLiveCareRequest = false; | ||||
|             } | ||||
|           } else { | ||||
|             patientHasPendingLiveCareRequest = false; | ||||
|           } | ||||
|           notifyListeners(); | ||||
|           if (onSuccess != null) { | ||||
|             onSuccess(apiResponse); | ||||
|           } | ||||
|         } | ||||
|       }, | ||||
|     ); | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,97 @@ | ||||
| class GetLiveCareClinicListResponseModel { | ||||
|   int? iD; | ||||
|   int? serviceID; | ||||
|   String? serviceName; | ||||
|   String? serviceNameN; | ||||
|   int? clinicID; | ||||
|   int? age; | ||||
|   bool? isCheckAgeBelow; | ||||
|   int? gender; | ||||
|   bool? isActive; | ||||
|   String? createdOn; | ||||
|   String? createdBy; | ||||
|   int? isOnline; | ||||
|   bool? projectOutSA; | ||||
|   List<ShiftTimings>? shiftTimings; | ||||
| 
 | ||||
|   GetLiveCareClinicListResponseModel( | ||||
|       {this.iD, | ||||
|       this.serviceID, | ||||
|       this.serviceName, | ||||
|       this.serviceNameN, | ||||
|       this.clinicID, | ||||
|       this.age, | ||||
|       this.isCheckAgeBelow, | ||||
|       this.gender, | ||||
|       this.isActive, | ||||
|       this.createdOn, | ||||
|       this.createdBy, | ||||
|       this.isOnline, | ||||
|       this.projectOutSA, | ||||
|       this.shiftTimings}); | ||||
| 
 | ||||
|   GetLiveCareClinicListResponseModel.fromJson(Map<String, dynamic> json) { | ||||
|     iD = json['ID']; | ||||
|     serviceID = json['ServiceID']; | ||||
|     serviceName = json['ServiceName']; | ||||
|     serviceNameN = json['ServiceNameN']; | ||||
|     clinicID = json['ClinicID']; | ||||
|     age = json['Age']; | ||||
|     isCheckAgeBelow = json['IsCheckAgeBelow']; | ||||
|     gender = json['Gender']; | ||||
|     isActive = json['IsActive']; | ||||
|     createdOn = json['CreatedOn']; | ||||
|     createdBy = json['CreatedBy']; | ||||
|     isOnline = json['IsOnline']; | ||||
|     projectOutSA = json['ProjectOutSA']; | ||||
|     if (json['ShiftTimings'] != null) { | ||||
|       shiftTimings = <ShiftTimings>[]; | ||||
|       json['ShiftTimings'].forEach((v) { | ||||
|         shiftTimings!.add(new ShiftTimings.fromJson(v)); | ||||
|       }); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   Map<String, dynamic> toJson() { | ||||
|     final Map<String, dynamic> data = Map<String, dynamic>(); | ||||
|     data['ID'] = this.iD; | ||||
|     data['ServiceID'] = this.serviceID; | ||||
|     data['ServiceName'] = this.serviceName; | ||||
|     data['ServiceNameN'] = this.serviceNameN; | ||||
|     data['ClinicID'] = this.clinicID; | ||||
|     data['Age'] = this.age; | ||||
|     data['IsCheckAgeBelow'] = this.isCheckAgeBelow; | ||||
|     data['Gender'] = this.gender; | ||||
|     data['IsActive'] = this.isActive; | ||||
|     data['CreatedOn'] = this.createdOn; | ||||
|     data['CreatedBy'] = this.createdBy; | ||||
|     data['IsOnline'] = this.isOnline; | ||||
|     data['ProjectOutSA'] = this.projectOutSA; | ||||
|     if (this.shiftTimings != null) { | ||||
|       data['ShiftTimings'] = this.shiftTimings!.map((v) => v.toJson()).toList(); | ||||
|     } | ||||
|     return data; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| class ShiftTimings { | ||||
|   String? endTime; | ||||
|   int? shiftID; | ||||
|   String? startTime; | ||||
| 
 | ||||
|   ShiftTimings({this.endTime, this.shiftID, this.startTime}); | ||||
| 
 | ||||
|   ShiftTimings.fromJson(Map<String, dynamic> json) { | ||||
|     endTime = json['EndTime']; | ||||
|     shiftID = json['ShiftID']; | ||||
|     startTime = json['StartTime']; | ||||
|   } | ||||
| 
 | ||||
|   Map<String, dynamic> toJson() { | ||||
|     final Map<String, dynamic> data = Map<String, dynamic>(); | ||||
|     data['EndTime'] = this.endTime; | ||||
|     data['ShiftID'] = this.shiftID; | ||||
|     data['StartTime'] = this.startTime; | ||||
|     return data; | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,37 @@ | ||||
| class LiveCareImmediateAppointmentFeesList { | ||||
|   String? amount; | ||||
|   String? companyName; | ||||
|   bool? isInsured; | ||||
|   bool? isShowInsuranceUpdateModule; | ||||
|   bool? isCash; | ||||
|   bool? isEligible; | ||||
|   String? tax; | ||||
|   String? total; | ||||
|   String? currency; | ||||
| 
 | ||||
|   LiveCareImmediateAppointmentFeesList({this.amount, this.companyName, this.isInsured, this.isShowInsuranceUpdateModule, this.tax, this.total, this.currency}); | ||||
| 
 | ||||
|   LiveCareImmediateAppointmentFeesList.fromJson(Map<String, dynamic> json) { | ||||
|     amount = json['Amount']; | ||||
|     companyName = json['CompanyName']; | ||||
|     isInsured = json['IsInsured']; | ||||
|     isCash = json['IsCash']; | ||||
|     isEligible = json['IsEligible']; | ||||
|     isShowInsuranceUpdateModule = json['IsShowInsuranceUpdateModule']; | ||||
|     tax = json['Tax']; | ||||
|     total = json['Total']; | ||||
|     currency = json['currency']; | ||||
|   } | ||||
| 
 | ||||
|   Map<String, dynamic> toJson() { | ||||
|     final Map<String, dynamic> data = new Map<String, dynamic>(); | ||||
|     data['Amount'] = this.amount; | ||||
|     data['CompanyName'] = this.companyName; | ||||
|     data['IsInsured'] = this.isInsured; | ||||
|     data['IsShowInsuranceUpdateModule'] = this.isShowInsuranceUpdateModule; | ||||
|     data['Tax'] = this.tax; | ||||
|     data['Total'] = this.total; | ||||
|     data['currency'] = this.currency; | ||||
|     return data; | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,84 @@ | ||||
| class PatientLiveCareHistory { | ||||
|   String? appointmentNo; | ||||
|   String? arrivalTime; | ||||
|   num? callDuration; | ||||
|   int? callStatus; | ||||
|   String? clientRequestID; | ||||
|   String? doctorID; | ||||
|   String? doctorName; | ||||
|   String? doctorNameN; | ||||
|   String? doctorTitle; | ||||
|   String? exWaitingTime; | ||||
|   bool? isAppointmentHaveRating; | ||||
|   int? patCount; | ||||
|   int? projectID; | ||||
|   String? sArrivalTime; | ||||
|   int? serviceID; | ||||
|   String? stringCallStatus; | ||||
|   int? vCID; | ||||
|   int? watingtimeInteger; | ||||
| 
 | ||||
|   PatientLiveCareHistory( | ||||
|       {this.appointmentNo, | ||||
|       this.arrivalTime, | ||||
|       this.callDuration, | ||||
|       this.callStatus, | ||||
|       this.clientRequestID, | ||||
|       this.doctorID, | ||||
|       this.doctorName, | ||||
|       this.doctorNameN, | ||||
|       this.doctorTitle, | ||||
|       this.exWaitingTime, | ||||
|       this.isAppointmentHaveRating, | ||||
|       this.patCount, | ||||
|       this.projectID, | ||||
|       this.sArrivalTime, | ||||
|       this.serviceID, | ||||
|       this.stringCallStatus, | ||||
|       this.vCID, | ||||
|       this.watingtimeInteger}); | ||||
| 
 | ||||
|   PatientLiveCareHistory.fromJson(Map<String, dynamic> json) { | ||||
|     appointmentNo = json['AppointmentNo']; | ||||
|     arrivalTime = json['ArrivalTime']; | ||||
|     callDuration = json['CallDuration']; | ||||
|     callStatus = json['CallStatus']; | ||||
|     clientRequestID = json['ClientRequestID']; | ||||
|     doctorID = json['DoctorID']; | ||||
|     doctorName = json['DoctorName']; | ||||
|     doctorNameN = json['DoctorNameN']; | ||||
|     doctorTitle = json['DoctorTitle']; | ||||
|     exWaitingTime = json['Ex_WaitingTime']; | ||||
|     isAppointmentHaveRating = json['IsAppointmentHaveRating']; | ||||
|     patCount = json['Pat_Count']; | ||||
|     projectID = json['ProjectID']; | ||||
|     sArrivalTime = json['SArrivalTime']; | ||||
|     serviceID = json['ServiceID']; | ||||
|     stringCallStatus = json['StringCallStatus']; | ||||
|     vCID = json['VC_ID']; | ||||
|     watingtimeInteger = json['WatingtimeInteger']; | ||||
|   } | ||||
| 
 | ||||
|   Map<String, dynamic> toJson() { | ||||
|     final Map<String, dynamic> data = new Map<String, dynamic>(); | ||||
|     data['AppointmentNo'] = this.appointmentNo; | ||||
|     data['ArrivalTime'] = this.arrivalTime; | ||||
|     data['CallDuration'] = this.callDuration; | ||||
|     data['CallStatus'] = this.callStatus; | ||||
|     data['ClientRequestID'] = this.clientRequestID; | ||||
|     data['DoctorID'] = this.doctorID; | ||||
|     data['DoctorName'] = this.doctorName; | ||||
|     data['DoctorNameN'] = this.doctorNameN; | ||||
|     data['DoctorTitle'] = this.doctorTitle; | ||||
|     data['Ex_WaitingTime'] = this.exWaitingTime; | ||||
|     data['IsAppointmentHaveRating'] = this.isAppointmentHaveRating; | ||||
|     data['Pat_Count'] = this.patCount; | ||||
|     data['ProjectID'] = this.projectID; | ||||
|     data['SArrivalTime'] = this.sArrivalTime; | ||||
|     data['ServiceID'] = this.serviceID; | ||||
|     data['StringCallStatus'] = this.stringCallStatus; | ||||
|     data['VC_ID'] = this.vCID; | ||||
|     data['WatingtimeInteger'] = this.watingtimeInteger; | ||||
|     return data; | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,32 @@ | ||||
| class PatientLabSpecialResult { | ||||
|   String? invoiceNo; | ||||
|   String? moduleID; | ||||
|   String? resultData; | ||||
|   String? resultDataHTML; | ||||
|   dynamic resultDataTxt; | ||||
| 
 | ||||
|   PatientLabSpecialResult( | ||||
|       {this.invoiceNo, | ||||
|         this.moduleID, | ||||
|         this.resultData, | ||||
|         this.resultDataHTML, | ||||
|         this.resultDataTxt}); | ||||
| 
 | ||||
|   PatientLabSpecialResult.fromJson(Map<String, dynamic> json) { | ||||
|     invoiceNo = json['InvoiceNo']; | ||||
|     moduleID = json['ModuleID']; | ||||
|     resultData = json['ResultData']; | ||||
|     resultDataHTML = json['ResultDataHTML']; | ||||
|     resultDataTxt = json['ResultDataTxt']; | ||||
|   } | ||||
| 
 | ||||
|   Map<String, dynamic> toJson() { | ||||
|     final Map<String, dynamic> data = new Map<String, dynamic>(); | ||||
|     data['InvoiceNo'] = this.invoiceNo; | ||||
|     data['ModuleID'] = this.moduleID; | ||||
|     data['ResultData'] = this.resultData; | ||||
|     data['ResultDataHTML'] = this.resultDataHTML; | ||||
|     data['ResultDataTxt'] = this.resultDataTxt; | ||||
|     return data; | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,17 @@ | ||||
| import 'package:hmg_patient_app_new/core/app_assets.dart'; | ||||
| 
 | ||||
| enum  AppointmentListingFilters{ | ||||
|   WALKIN("walkin", AppAssets.walkin_appointment_icon), | ||||
|   BOOKED("booked", AppAssets.calendar), | ||||
|   CONFIRMED("confirmed", AppAssets.calendar), | ||||
|   ARRIVED("arrived", AppAssets.calendar), | ||||
|   LIVECARE("livecare", AppAssets.small_livecare_icon), | ||||
|   DATESELECTION("",AppAssets.calendar, trailingIcon: AppAssets.arrow_down); | ||||
| 
 | ||||
|   final String labelText; | ||||
|   final String leadingIcon; | ||||
|   final String trailingIcon; | ||||
| 
 | ||||
|   const AppointmentListingFilters(this.labelText, this.leadingIcon, | ||||
|       {this.trailingIcon = ""}); | ||||
| } | ||||
| @ -0,0 +1,83 @@ | ||||
| class GetTamaraInstallmentsDetailsResponseModel { | ||||
|   String? name; | ||||
|   String? description; | ||||
|   MinLimit? minLimit; | ||||
|   MinLimit? maxLimit; | ||||
|   List<SupportedInstalments>? supportedInstalments; | ||||
| 
 | ||||
|   GetTamaraInstallmentsDetailsResponseModel({this.name, this.description, this.minLimit, this.maxLimit, this.supportedInstalments}); | ||||
| 
 | ||||
|   GetTamaraInstallmentsDetailsResponseModel.fromJson(Map<String, dynamic> json) { | ||||
|     name = json['name']; | ||||
|     description = json['description']; | ||||
|     minLimit = json['minLimit'] != null ? new MinLimit.fromJson(json['minLimit']) : null; | ||||
|     maxLimit = json['maxLimit'] != null ? new MinLimit.fromJson(json['maxLimit']) : null; | ||||
|     if (json['supportedInstalments'] != null) { | ||||
|       supportedInstalments = <SupportedInstalments>[]; | ||||
|       json['supportedInstalments'].forEach((v) { | ||||
|         supportedInstalments!.add(new SupportedInstalments.fromJson(v)); | ||||
|       }); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   Map<String, dynamic> toJson() { | ||||
|     final Map<String, dynamic> data = new Map<String, dynamic>(); | ||||
|     data['name'] = this.name; | ||||
|     data['description'] = this.description; | ||||
|     if (this.minLimit != null) { | ||||
|       data['minLimit'] = this.minLimit!.toJson(); | ||||
|     } | ||||
|     if (this.maxLimit != null) { | ||||
|       data['maxLimit'] = this.maxLimit!.toJson(); | ||||
|     } | ||||
|     if (this.supportedInstalments != null) { | ||||
|       data['supportedInstalments'] = this.supportedInstalments!.map((v) => v.toJson()).toList(); | ||||
|     } | ||||
|     return data; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| class MinLimit { | ||||
|   String? currency; | ||||
|   num? amount; | ||||
| 
 | ||||
|   MinLimit({this.currency, this.amount}); | ||||
| 
 | ||||
|   MinLimit.fromJson(Map<String, dynamic> json) { | ||||
|     currency = json['currency']; | ||||
|     amount = json['amount']; | ||||
|   } | ||||
| 
 | ||||
|   Map<String, dynamic> toJson() { | ||||
|     final Map<String, dynamic> data = new Map<String, dynamic>(); | ||||
|     data['currency'] = this.currency; | ||||
|     data['amount'] = this.amount; | ||||
|     return data; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| class SupportedInstalments { | ||||
|   int? instalments; | ||||
|   MinLimit? minLimit; | ||||
|   MinLimit? maxLimit; | ||||
| 
 | ||||
|   SupportedInstalments({this.instalments, this.minLimit, this.maxLimit}); | ||||
| 
 | ||||
|   SupportedInstalments.fromJson(Map<String, dynamic> json) { | ||||
|     instalments = json['instalments']; | ||||
|     minLimit = json['minLimit'] != null ? new MinLimit.fromJson(json['minLimit']) : null; | ||||
|     maxLimit = json['maxLimit'] != null ? new MinLimit.fromJson(json['maxLimit']) : null; | ||||
|   } | ||||
| 
 | ||||
|   Map<String, dynamic> toJson() { | ||||
|     final Map<String, dynamic> data = new Map<String, dynamic>(); | ||||
|     data['instalments'] = this.instalments; | ||||
|     if (this.minLimit != null) { | ||||
|       data['minLimit'] = this.minLimit!.toJson(); | ||||
|     } | ||||
|     if (this.maxLimit != null) { | ||||
|       data['maxLimit'] = this.maxLimit!.toJson(); | ||||
|     } | ||||
|     return data; | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,48 @@ | ||||
| import 'package:easy_localization/easy_localization.dart' show tr, StringTranslateExtension; | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; | ||||
| import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; | ||||
| import 'package:hmg_patient_app_new/features/my_appointments/models/appointemnet_filters.dart'; | ||||
| import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart'; | ||||
| import 'package:hmg_patient_app_new/theme/colors.dart'; | ||||
| import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart'; | ||||
| import 'package:smooth_corner/smooth_corner.dart'; | ||||
| 
 | ||||
| class AppointmentFilters extends StatelessWidget { | ||||
|   final AppointmentListingFilters item; | ||||
|   final List<AppointmentListingFilters>? selectedFilter; | ||||
|   final VoidCallback onClicked; | ||||
| 
 | ||||
|   const AppointmentFilters( | ||||
|       {super.key, | ||||
|       required this.item, | ||||
|       required this.onClicked, | ||||
|       required this.selectedFilter}); | ||||
| 
 | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return AppCustomChipWidget( | ||||
|         backgroundColor:  selectedFilter?.contains(item) == true?AppColors.chipSecondaryLightRedColor:AppColors.whiteColor, | ||||
|         icon: item.leadingIcon, | ||||
|         textColor: selectedFilter?.contains(item) == true | ||||
|             ? AppColors.chipPrimaryRedBorderColor: AppColors.blackColor, | ||||
|         labelText: item.labelText.isNotEmpty?item.labelText.tr():"", | ||||
|         iconHasColor: true, | ||||
|         iconColor: selectedFilter?.contains(item) == true | ||||
|             ? AppColors.chipPrimaryRedBorderColor:AppColors.blackColor, | ||||
|         iconSize: 16, | ||||
|         deleteIcon: item.trailingIcon, | ||||
|         labelPadding: EdgeInsetsDirectional.only(start: 8.h, end: 0.h), | ||||
|         padding: EdgeInsets.symmetric(vertical: 12.h, horizontal: 8.h), | ||||
|         deleteIconSize: Size(18.h, 18.h), | ||||
|         shape: SmoothRectangleBorder( | ||||
|           borderRadius: BorderRadius.circular(10 ), | ||||
|           smoothness: 10, | ||||
|           side: BorderSide( | ||||
|               color: selectedFilter?.contains(item) == true | ||||
|                   ? AppColors.chipPrimaryRedBorderColor | ||||
|                   : AppColors.borderGrayColor, | ||||
|               width: 1), | ||||
|         )).onPress(onClicked); | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,66 @@ | ||||
| import 'package:easy_localization/easy_localization.dart' | ||||
|     show tr, StringTranslateExtension; | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:hmg_patient_app_new/core/enums.dart'; | ||||
| import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; | ||||
| import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; | ||||
| import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; | ||||
| import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_view_model.dart'; | ||||
| import 'package:hmg_patient_app_new/features/doctor_filter/doctor_filter_view_model.dart'; | ||||
| import 'package:hmg_patient_app_new/features/my_appointments/appointment_via_region_viewmodel.dart'; | ||||
| import 'package:hmg_patient_app_new/features/my_appointments/models/facility_selection.dart'; | ||||
| import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart'; | ||||
| import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; | ||||
| import 'package:hmg_patient_app_new/presentation/appointments/widgets/hospital_bottom_sheet/hospital_list_items.dart'; | ||||
| import 'package:hmg_patient_app_new/presentation/appointments/widgets/hospital_bottom_sheet/type_selection_widget.dart'; | ||||
| import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart'; | ||||
| import 'package:hmg_patient_app_new/theme/colors.dart' show AppColors; | ||||
| import 'package:hmg_patient_app_new/widgets/input_widget.dart'; | ||||
| import 'package:provider/provider.dart'; | ||||
| 
 | ||||
| class HospitalBottomSheetBodyForDoctorFilter extends StatelessWidget { | ||||
|   late BookAppointmentsViewModel appointmentsViewModel; | ||||
|   late AppointmentViaRegionViewmodel regionalViewModel; | ||||
|   final TextEditingController searchText = TextEditingController(); | ||||
| 
 | ||||
|   HospitalBottomSheetBodyForDoctorFilter({super.key}); | ||||
| 
 | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     appointmentsViewModel = Provider.of<BookAppointmentsViewModel>(context); | ||||
|     regionalViewModel = Provider.of<AppointmentViaRegionViewmodel>(context); | ||||
|     return Column( | ||||
|       crossAxisAlignment: CrossAxisAlignment.start, | ||||
|       children: [ | ||||
|         Text( | ||||
|           LocaleKeys.selectHospital.tr(), | ||||
|           style: TextStyle( | ||||
|             fontSize: 21, | ||||
|             fontWeight: FontWeight.w600, | ||||
|             color: AppColors.blackColor, | ||||
|           ), | ||||
|         ), | ||||
|         SizedBox(height: 24.h), | ||||
|         SizedBox( | ||||
|           height: MediaQuery.sizeOf(context).height * .4, | ||||
|           child: ListView.separated( | ||||
|               itemBuilder: (_, index) | ||||
|               { | ||||
|                 var hospital = appointmentsViewModel.searchedPatientDoctorAppointmentHospitalsList[index]; | ||||
|                 return HospitalListItem( | ||||
|                 hospitalData: hospital, | ||||
|                 isLocationEnabled: appointmentsViewModel.isLocationEnabled(), | ||||
|               ).onPress(() { | ||||
|                 regionalViewModel.setHospitalModel(hospital); | ||||
|                 context.read<DoctorFilterViewModel>().setSelectedHospital(hospital); | ||||
|                  Navigator.pop(context); | ||||
|                 });}, | ||||
|               separatorBuilder: (_, __) => SizedBox( | ||||
|                     height: 16.h, | ||||
|                   ), | ||||
|               itemCount: appointmentsViewModel.searchedPatientDoctorAppointmentHospitalsList?.length ?? 0), | ||||
|         ) | ||||
|       ], | ||||
|     ); | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,95 @@ | ||||
| import 'dart:async'; | ||||
| 
 | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:flutter_staggered_animations/flutter_staggered_animations.dart'; | ||||
| import 'package:hmg_patient_app_new/core/app_state.dart'; | ||||
| import 'package:hmg_patient_app_new/core/dependencies.dart'; | ||||
| import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; | ||||
| import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; | ||||
| import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; | ||||
| import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_view_model.dart'; | ||||
| import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/dental_chief_complaints_response_model.dart'; | ||||
| import 'package:hmg_patient_app_new/presentation/book_appointment/select_doctor_page.dart'; | ||||
| import 'package:hmg_patient_app_new/presentation/book_appointment/widgets/chief_complaint_card.dart'; | ||||
| import 'package:hmg_patient_app_new/theme/colors.dart'; | ||||
| import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart'; | ||||
| import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart'; | ||||
| import 'package:provider/provider.dart'; | ||||
| 
 | ||||
| class DentalChiefComplaintsPage extends StatefulWidget { | ||||
|   const DentalChiefComplaintsPage({super.key}); | ||||
| 
 | ||||
|   @override | ||||
|   State<DentalChiefComplaintsPage> createState() => _DentalChiefComplaintsPageState(); | ||||
| } | ||||
| 
 | ||||
| class _DentalChiefComplaintsPageState extends State<DentalChiefComplaintsPage> { | ||||
|   late AppState appState; | ||||
|   late BookAppointmentsViewModel bookAppointmentsViewModel; | ||||
| 
 | ||||
|   @override | ||||
|   void initState() { | ||||
|     scheduleMicrotask(() { | ||||
|       bookAppointmentsViewModel.getDentalChiefComplaintsList(); | ||||
|     }); | ||||
|     super.initState(); | ||||
|   } | ||||
| 
 | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     bookAppointmentsViewModel = Provider.of<BookAppointmentsViewModel>(context, listen: false); | ||||
|     appState = getIt.get<AppState>(); | ||||
|     return CollapsingListView( | ||||
|       title: "Dental Chief Complaints".needTranslation, | ||||
|       child: SingleChildScrollView( | ||||
|         child: Padding( | ||||
|           padding: EdgeInsets.symmetric(horizontal: 24.h), | ||||
|           child: Consumer<BookAppointmentsViewModel>(builder: (context, bookAppointmentsVM, child) { | ||||
|             return ListView.separated( | ||||
|               padding: EdgeInsets.only(top: 24.h), | ||||
|               shrinkWrap: true, | ||||
|               physics: NeverScrollableScrollPhysics(), | ||||
|               itemCount: bookAppointmentsVM.isChiefComplaintsListLoading ? 5 : bookAppointmentsVM.dentalChiefComplaintsList.length, | ||||
|               itemBuilder: (context, index) { | ||||
|                 return bookAppointmentsVM.isChiefComplaintsListLoading | ||||
|                     ? ChiefComplaintCard( | ||||
|                         bookAppointmentsVM: bookAppointmentsVM, | ||||
|                         dentalChiefComplaintsListResponseModel: DentalChiefComplaintsListResponseModel(), | ||||
|                         isLoading: bookAppointmentsVM.isChiefComplaintsListLoading, | ||||
|                       ) | ||||
|                     : AnimationConfiguration.staggeredList( | ||||
|                         position: index, | ||||
|                         duration: const Duration(milliseconds: 500), | ||||
|                         child: SlideAnimation( | ||||
|                           verticalOffset: 100.0, | ||||
|                           child: FadeInAnimation( | ||||
|                             child: AnimatedContainer( | ||||
|                               duration: Duration(milliseconds: 300), | ||||
|                               curve: Curves.easeInOut, | ||||
|                               decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: true), | ||||
|                               child: ChiefComplaintCard( | ||||
|                                 bookAppointmentsVM: bookAppointmentsVM, | ||||
|                                 dentalChiefComplaintsListResponseModel: bookAppointmentsVM.dentalChiefComplaintsList[index], | ||||
|                                 isLoading: bookAppointmentsVM.isChiefComplaintsListLoading, | ||||
|                               ).onPress(() { | ||||
|                                 bookAppointmentsVM.setSelectedChiefComplaintID(bookAppointmentsVM.dentalChiefComplaintsList[index].iD!); | ||||
|                                 bookAppointmentsViewModel.setIsDoctorsListLoading(true); | ||||
|                                 Navigator.of(context).push( | ||||
|                                   CustomPageRoute( | ||||
|                                     page: SelectDoctorPage(), | ||||
|                                   ), | ||||
|                                 ); | ||||
|                               }), | ||||
|                             ), | ||||
|                           ), | ||||
|                         ), | ||||
|                       ); | ||||
|               }, | ||||
|               separatorBuilder: (BuildContext cxt, int index) => SizedBox(height: 16.h), | ||||
|             ); | ||||
|           }), | ||||
|         ), | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,60 @@ | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; | ||||
| import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; | ||||
| import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_view_model.dart' | ||||
|     show BookAppointmentsViewModel; | ||||
| import 'package:hmg_patient_app_new/features/doctor_filter/doctor_filter_view_model.dart'; | ||||
| import 'package:hmg_patient_app_new/theme/colors.dart'; | ||||
| import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart'; | ||||
| import 'package:provider/provider.dart'; | ||||
| 
 | ||||
| class RegionChips extends StatelessWidget { | ||||
|   const RegionChips({super.key}); | ||||
| 
 | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return Selector<DoctorFilterViewModel, List<String>>( | ||||
|         selector: (_, model) => model.searchedRegionList, | ||||
|         builder: (__, data, ___) => | ||||
|             Selector<DoctorFilterViewModel, List<String>?>( | ||||
|               selector: (_, model) => model.selectedRegionForFilters, | ||||
|               builder: (context, selectRegion, ___) => Row( | ||||
|                 children: [ | ||||
|                   Expanded( | ||||
|                       child: ListView.separated( | ||||
|                         scrollDirection: Axis.horizontal, | ||||
|                           itemCount: data.length, | ||||
|                           separatorBuilder: (_, __)=>SizedBox(width: 8.h,), | ||||
| 
 | ||||
|                           itemBuilder: (_, index) => AppCustomChipWidget( | ||||
|                                       labelText: data[index], | ||||
|                                       textColor: selectRegion?.any((selectedRegion)=>data[index] == selectedRegion) == true | ||||
|                                           ? AppColors.primaryRedColor | ||||
| 
 | ||||
|                                           : AppColors.textColor, | ||||
|                                       backgroundColor: | ||||
|                                       selectRegion?.any((selectedRegion)=>data[index] == selectedRegion) == true | ||||
|                                               ? AppColors.primaryRedColor | ||||
|                                                   .withOpacity(0.1) | ||||
|                                               : AppColors.whiteColor, | ||||
|                                       shape: RoundedRectangleBorder( | ||||
|                                           side: BorderSide( | ||||
|                                             color: selectRegion?.any((selectedRegion)=>data[index] == selectedRegion) == true | ||||
|                                                 ? AppColors | ||||
|                                                     .primaryRedBorderColor | ||||
|                                                 : AppColors | ||||
|                                                     .chipBorderColorOpacity20, | ||||
|                                             width: 1, | ||||
|                                           ), | ||||
|                                           borderRadius: | ||||
|                                               BorderRadius.circular(10))) | ||||
|                                   .onPress(() { | ||||
|                                 context | ||||
|                                     .read<DoctorFilterViewModel>() | ||||
|                                     .setSelectedRegion(data[index]); | ||||
|                               }))) | ||||
|                 ], | ||||
|               ), | ||||
|             )); | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,85 @@ | ||||
| import 'package:easy_localization/easy_localization.dart' | ||||
|     show tr, StringTranslateExtension; | ||||
| 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/extensions/string_extensions.dart'; | ||||
| import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; | ||||
| import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_view_model.dart'; | ||||
| import 'package:hmg_patient_app_new/features/doctor_filter/doctor_filter_view_model.dart'; | ||||
| import 'package:hmg_patient_app_new/features/my_appointments/appointment_via_region_viewmodel.dart'; | ||||
| import 'package:hmg_patient_app_new/features/my_appointments/models/facility_selection.dart'; | ||||
| import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart'; | ||||
| import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; | ||||
| import 'package:hmg_patient_app_new/presentation/appointments/widgets/hospital_bottom_sheet/hospital_list_items.dart'; | ||||
| import 'package:hmg_patient_app_new/presentation/appointments/widgets/hospital_bottom_sheet/type_selection_widget.dart'; | ||||
| import 'package:hmg_patient_app_new/presentation/book_appointment/doctor_filter/clinic_item.dart'; | ||||
| import 'package:hmg_patient_app_new/presentation/book_appointment/widgets/clinic_card.dart'; | ||||
| import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart'; | ||||
| import 'package:hmg_patient_app_new/theme/colors.dart' show AppColors; | ||||
| import 'package:hmg_patient_app_new/widgets/input_widget.dart'; | ||||
| import 'package:provider/provider.dart'; | ||||
| 
 | ||||
| import '../../../features/book_appointments/models/resp_models/get_clinic_list_response_model.dart' show GetClinicsListResponseModel; | ||||
| 
 | ||||
| class ClinicBottomSheet extends StatelessWidget { | ||||
|   late BookAppointmentsViewModel appointmentsViewModel; | ||||
|   late AppointmentViaRegionViewmodel regionalViewModel; | ||||
|   final TextEditingController searchText = TextEditingController(); | ||||
| 
 | ||||
|   ClinicBottomSheet({super.key}); | ||||
| 
 | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     appointmentsViewModel = Provider.of<BookAppointmentsViewModel>(context); | ||||
|     regionalViewModel = Provider.of<AppointmentViaRegionViewmodel>(context); | ||||
|     return Column( | ||||
|       crossAxisAlignment: CrossAxisAlignment.start, | ||||
|       children: [ | ||||
|         Text( | ||||
|           LocaleKeys.selectClinic.tr(), | ||||
|           style: TextStyle( | ||||
|             fontSize: 21, | ||||
|             fontWeight: FontWeight.w600, | ||||
|             color: AppColors.blackColor, | ||||
|           ), | ||||
|         ), | ||||
|         SizedBox(height: 24.h), | ||||
|         SizedBox( | ||||
|           height: MediaQuery.sizeOf(context).height * .4, | ||||
|           child: ListView.separated( | ||||
|               itemBuilder: (_, index) | ||||
|               { | ||||
|                 return AnimationConfiguration.staggeredList( | ||||
|                   position: index, | ||||
|                   duration: const Duration(milliseconds: 500), | ||||
|                   child: SlideAnimation( | ||||
|                     verticalOffset: 100.0, | ||||
|                     child: FadeInAnimation( | ||||
|                       child: AnimatedContainer( | ||||
|                         duration: Duration(milliseconds: 300), | ||||
|                         curve: Curves.easeInOut, | ||||
|                         decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: true), | ||||
|                         child: ClinicItem( | ||||
|                           isArabic: appointmentsViewModel.isArabic(), | ||||
|                           clinicName: appointmentsViewModel.searchedClinicList[index], | ||||
|                         ), | ||||
|                       ), | ||||
|                     ), | ||||
|                   ), | ||||
| 
 | ||||
|               ).onPress(() { | ||||
|                   context.read<DoctorFilterViewModel>() | ||||
|                     .setSelectedClinicForFilter(appointmentsViewModel.searchedClinicList[index]); | ||||
|                  Navigator.pop(context); | ||||
|                 });}, | ||||
|               separatorBuilder: (_, __) => SizedBox( | ||||
|                     height: 16.h, | ||||
|                   ), | ||||
|               itemCount: appointmentsViewModel.searchedClinicList.length ?? 0), | ||||
|         ) | ||||
|       ], | ||||
|     ); | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,52 @@ | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:hmg_patient_app_new/core/app_assets.dart'; | ||||
| import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; | ||||
| import 'package:hmg_patient_app_new/core/utils/utils.dart'; | ||||
| import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; | ||||
| import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; | ||||
| import 'package:hmg_patient_app_new/theme/colors.dart' show AppColors; | ||||
| 
 | ||||
| class ClinicItem extends StatelessWidget { | ||||
|   final String clinicName; | ||||
|   final bool isArabic; | ||||
| 
 | ||||
|   ClinicItem({super.key, required this.clinicName, required this.isArabic}); | ||||
| 
 | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return Container( | ||||
|       padding: EdgeInsets.all(16.h), | ||||
|       decoration: RoundedRectangleBorder().toSmoothCornerDecoration( | ||||
|         color: AppColors.whiteColor, | ||||
|         borderRadius: 24.h, | ||||
|         hasShadow: false, | ||||
|       ), | ||||
|       child: Column( | ||||
|         crossAxisAlignment: CrossAxisAlignment.start, | ||||
|         children: [ | ||||
|           Utils.buildSvgWithAssets( | ||||
|               icon: AppAssets.generic_clinic_icon, | ||||
|               width: 24.h, | ||||
|               height: 24.h, | ||||
|               fit: BoxFit.contain), | ||||
|           SizedBox(height: 16.h), | ||||
|           Row( | ||||
|             mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||
|             children: [ | ||||
|               Expanded(child: clinicName.toText16(isBold: true)), | ||||
|               Transform.flip( | ||||
|                 flipX: isArabic, | ||||
|                 child: Utils.buildSvgWithAssets( | ||||
|                     icon: AppAssets.forward_arrow_icon, | ||||
|                     width: 15.h, | ||||
|                     height: 15.h, | ||||
|                     fit: BoxFit.contain, | ||||
|                     iconColor: AppColors.textColor), | ||||
|               ), | ||||
|             ], | ||||
|           ), | ||||
|         ], | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,249 @@ | ||||
| import 'package:easy_localization/easy_localization.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:hmg_patient_app_new/core/app_assets.dart'; | ||||
| import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; | ||||
| import 'package:hmg_patient_app_new/core/utils/utils.dart'; | ||||
| import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; | ||||
| import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; | ||||
| import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_view_model.dart'; | ||||
| import 'package:hmg_patient_app_new/features/doctor_filter/doctor_filter_view_model.dart'; | ||||
| import 'package:hmg_patient_app_new/features/my_appointments/appointment_via_region_viewmodel.dart'; | ||||
| import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; | ||||
| import 'package:hmg_patient_app_new/presentation/appointments/widgets/hospital_bottom_sheet/hospital_bottom_sheet_body.dart'; | ||||
| import 'package:hmg_patient_app_new/presentation/appointments/widgets/hospital_bottom_sheet/hospital_bottom_sheet_body_for_doctor_filter.dart'; | ||||
| import 'package:hmg_patient_app_new/presentation/book_appointment/doctor_filter/RegionChips.dart'; | ||||
| import 'package:hmg_patient_app_new/presentation/book_appointment/doctor_filter/clinic_bottomsheet.dart'; | ||||
| import 'package:hmg_patient_app_new/presentation/book_appointment/doctor_filter/facility_Chips.dart'; | ||||
| import 'package:hmg_patient_app_new/theme/colors.dart'; | ||||
| import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart'; | ||||
| import 'package:hmg_patient_app_new/widgets/input_widget.dart'; | ||||
| import 'package:provider/provider.dart'; | ||||
| 
 | ||||
| import '../../../widgets/buttons/custom_button.dart'; | ||||
| 
 | ||||
| class DoctorsFilters extends StatelessWidget{ | ||||
| 
 | ||||
| 
 | ||||
|   TextEditingController hospitalController = TextEditingController(); | ||||
|   TextEditingController clinicController = TextEditingController(); | ||||
|   DoctorsFilters({super.key,}); | ||||
| 
 | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return Scaffold( | ||||
|       backgroundColor: AppColors.bgScaffoldColor, | ||||
|       appBar: AppBar( | ||||
|           backgroundColor: AppColors.bgScaffoldColor, | ||||
| 
 | ||||
|         automaticallyImplyLeading: false, | ||||
|       centerTitle: false, | ||||
|       title:  Utils.buildSvgWithAssets(icon: AppAssets.ic_close, height: 32.h, width: 32.h).onPress((){ | ||||
|         context.read<DoctorFilterViewModel>() | ||||
|             .clearSelection() | ||||
|         ; | ||||
|         Navigator.pop(context); | ||||
|       }) | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|       ), | ||||
| 
 | ||||
|       body: Column( | ||||
|         children: [ | ||||
|           Column( | ||||
|             crossAxisAlignment: CrossAxisAlignment.start, | ||||
|             children: [ | ||||
| 
 | ||||
|               Row( | ||||
|                 mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||
|                 children: [ | ||||
|                   Text( | ||||
|                       LocaleKeys.filters.tr(), | ||||
|                     style:TextStyle( | ||||
|                       fontFamily: 'Poppins', | ||||
|                       fontWeight: FontWeight.w600, | ||||
|                       fontSize: 27.fSize, | ||||
|                       color: AppColors.textColor, | ||||
|                       letterSpacing: -1 | ||||
|                     ) | ||||
|                   ), | ||||
|                   Text( | ||||
|                       LocaleKeys.clearAllFilters.tr(), | ||||
|                       style:TextStyle( | ||||
|                           fontFamily: 'Poppins', | ||||
|                           fontWeight: FontWeight.w500, | ||||
|                           fontSize: 14.fSize, | ||||
|                           color: AppColors.errorColor | ||||
|                       ) | ||||
|                   ).onPress((){ | ||||
|                     context.read<DoctorFilterViewModel>().clearSelection(); | ||||
|                     context.read<DoctorFilterViewModel>().updateApplyFilters(false); | ||||
|                     // context.read<BookAppointmentsViewModel>().setSelections( | ||||
|                     //     context.read<DoctorFilterViewModel>().selectedFacilityForFilters, | ||||
|                     //     context.read<DoctorFilterViewModel>().selectedRegionForFilters, | ||||
|                     //     context.read<DoctorFilterViewModel>().selectedClinicForFilters, | ||||
|                     //     context.read<DoctorFilterViewModel>().selectedHospitalForFilters, | ||||
|                     //     context.read<DoctorFilterViewModel>().applyFilters); | ||||
|                     context.read<BookAppointmentsViewModel>().updateList(); | ||||
|                   }) | ||||
|                 ], | ||||
|               ), | ||||
|               titleWidget(LocaleKeys.regionAndLocation.tr()), | ||||
|               SizedBox( | ||||
|                   height: 42.h, | ||||
|                   child: RegionChips()), | ||||
|               titleWidget(LocaleKeys.facilityAndLocation.tr()), | ||||
|               SizedBox( | ||||
|                   height: 42.h, | ||||
|                   child: FacilityChip()), | ||||
|               titleWidget(LocaleKeys.hospital.tr()), | ||||
|               TextInputWidget( | ||||
|                 controller: TextEditingController()..text =context.watch<DoctorFilterViewModel>().selectedHospitalForFilters?.filterName??'', | ||||
|                 labelText: LocaleKeys.hospital.tr(context: context), | ||||
|                 hintText: LocaleKeys.searchHospital.tr(context: context), | ||||
|                 isEnable: false, | ||||
|                 prefix: null, | ||||
|                 autoFocus: false, | ||||
|                 isBorderAllowed: false, | ||||
|                 keyboardType: TextInputType.text, | ||||
|                 suffix:context.watch<DoctorFilterViewModel>().selectedHospitalForFilters !=  null | ||||
|                     ? GestureDetector( | ||||
|                   onTap: () { | ||||
|                     context.read<DoctorFilterViewModel>().setSelectedHospital(null); | ||||
|                   }, | ||||
|                   child: Utils.buildSvgWithAssets(icon: AppAssets.ic_cross_circle, width: 24.h, height: 24.h, fit: BoxFit.scaleDown), | ||||
|                 ) | ||||
|                     : null, | ||||
|                 onChange: (value) { | ||||
|                   // DoctorFilterViewModel.filterClinics(value!); | ||||
|                 }, | ||||
|                 padding: EdgeInsets.symmetric( | ||||
|                   vertical: ResponsiveExtension(8).h, | ||||
|                   horizontal: ResponsiveExtension(10).h, | ||||
|                 ), | ||||
|               ).onPress((){ | ||||
|                 openRegionListBottomSheet(context, RegionBottomSheetType.FOR_REGION); | ||||
|               }), | ||||
| 
 | ||||
|               titleWidget(LocaleKeys.clinic.tr()), | ||||
|               TextInputWidget( | ||||
|                 controller: TextEditingController()..text =context.watch<DoctorFilterViewModel>().selectedClinicForFilters ??'', | ||||
|                 labelText: LocaleKeys.clinicName.tr(context: context), | ||||
|                 hintText: LocaleKeys.searchClinic.tr().needTranslation, | ||||
|                 isEnable: false, | ||||
|                 prefix: null, | ||||
|                 autoFocus: false, | ||||
|                 isBorderAllowed: false, | ||||
|                 keyboardType: TextInputType.text, | ||||
|                 suffix:context.read<DoctorFilterViewModel>().selectedClinicForFilters?.isNotEmpty == true | ||||
|                     ? GestureDetector( | ||||
|                   onTap: () { | ||||
|                     context.read<DoctorFilterViewModel>().setSelectedClinicForFilter(null); | ||||
|                   }, | ||||
|                   child: Utils.buildSvgWithAssets(icon: AppAssets.ic_cross_circle, width: 20.h, height: 20.h, fit: BoxFit.scaleDown), | ||||
|                 ) | ||||
|                     : null, | ||||
|                 onChange: (value) { | ||||
|                   // DoctorFilterViewModel.filterClinics(value!); | ||||
|                 }, | ||||
|                 padding: EdgeInsets.symmetric( | ||||
|                   vertical: 8.h, | ||||
|                   horizontal: 10.h, | ||||
|                 ), | ||||
|               ).onPress((){ | ||||
|                 openClinicListBottomSheet(context,); | ||||
|               }), | ||||
| 
 | ||||
| 
 | ||||
|             ], | ||||
|           ).paddingSymmetrical(24.h, 0.h), | ||||
|           Spacer(), | ||||
|           DecoratedBox( | ||||
|             decoration: RoundedRectangleBorder().toSmoothCornerDecoration( | ||||
|               color: Colors.white, | ||||
|               customBorder:  BorderRadius.only(topLeft: Radius.circular(24.h), topRight: Radius.circular(24.h)) , | ||||
| 
 | ||||
|             ), | ||||
|             child: CustomButton( | ||||
|               text: LocaleKeys.applyFilter.tr(), | ||||
|               onPressed: () { | ||||
|                 context.read<DoctorFilterViewModel>().updateApplyFilters(true); | ||||
|                 context.read<BookAppointmentsViewModel>().setSelections( | ||||
|                     context.read<DoctorFilterViewModel>().selectedFacilityForFilters?.toList()??[], | ||||
|                     context.read<DoctorFilterViewModel>().selectedRegionForFilters?.toList()??[], | ||||
|                     context.read<DoctorFilterViewModel>().selectedClinicForFilters, | ||||
|                     context.read<DoctorFilterViewModel>().selectedHospitalForFilters, | ||||
|                     context.read<DoctorFilterViewModel>().applyFilters); | ||||
|                 context.read<BookAppointmentsViewModel>().updateList(); | ||||
|                 Navigator.pop(context); | ||||
|               }, | ||||
|               backgroundColor: AppColors.primaryRedColor, | ||||
|               borderColor: AppColors.primaryRedColor, | ||||
|               textColor: Colors.white, | ||||
|               fontSize: 16, | ||||
|               padding: EdgeInsets.zero, | ||||
|               fontWeight: FontWeight.w500, | ||||
|               borderRadius: 12, | ||||
|               icon: AppAssets.add_icon, | ||||
|               iconColor: AppColors.primaryRedColor, | ||||
|             ).paddingAll(24.h), | ||||
|           ), | ||||
|         ], | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   Widget titleWidget(String title){ | ||||
|     return Column( | ||||
|       children: [ | ||||
|         SizedBox(height: 24.h,), | ||||
|         Text( | ||||
|             title, | ||||
|             style:TextStyle( | ||||
|                 fontFamily: 'Poppins', | ||||
|                 fontWeight: FontWeight.w600, | ||||
|                 fontSize: 16.fSize, | ||||
|                 color: AppColors.textColor, | ||||
|                 letterSpacing:-1 | ||||
|             ) | ||||
|         ), | ||||
|         SizedBox(height: 8.h,), | ||||
|       ], | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|    void openRegionListBottomSheet(BuildContext context, RegionBottomSheetType type) { | ||||
|      context.read<AppointmentViaRegionViewmodel>().flush(); | ||||
|      context.read<AppointmentViaRegionViewmodel>().setBottomSheetType(type); | ||||
|      context.read<AppointmentViaRegionViewmodel>().setBottomSheetState(AppointmentViaRegionState.HOSPITAL_SELECTION); | ||||
|      // AppointmentViaRegionViewmodel? viewmodel = null; | ||||
|      showCommonBottomSheetWithoutHeight(context, title: "", titleWidget: Consumer<AppointmentViaRegionViewmodel>(builder: (_, data, __) => getTitle(data)), isDismissible: false, | ||||
|          child: Consumer<AppointmentViaRegionViewmodel>(builder: (_, data, __) { | ||||
|            return getRegionalSelectionWidget(data); | ||||
|          }), callBackFunc: () {}); | ||||
|    } | ||||
| 
 | ||||
|    Widget getRegionalSelectionWidget(AppointmentViaRegionViewmodel data) { | ||||
| 
 | ||||
|      if (data.bottomSheetState == AppointmentViaRegionState.HOSPITAL_SELECTION) { | ||||
|        return HospitalBottomSheetBodyForDoctorFilter(); | ||||
|      } | ||||
|      if (data.bottomSheetState == AppointmentViaRegionState.CLINIC_SELECTION) { | ||||
|       } else { | ||||
|        SizedBox.shrink(); | ||||
|      } | ||||
|      return SizedBox.shrink(); | ||||
|    } | ||||
| 
 | ||||
|    getTitle(AppointmentViaRegionViewmodel data) { | ||||
|     return SizedBox.shrink(); | ||||
|    } | ||||
| 
 | ||||
|   void openClinicListBottomSheet(BuildContext context) { | ||||
|     showCommonBottomSheetWithoutHeight(context, title: "", titleWidget: Consumer<AppointmentViaRegionViewmodel>(builder: (_, data, __) => getTitle(data)), isDismissible: false, | ||||
|         child: Consumer<AppointmentViaRegionViewmodel>(builder: (_, data, __) { | ||||
|           return ClinicBottomSheet(); | ||||
|         }), callBackFunc: () {}); | ||||
|   } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,65 @@ | ||||
| 
 | ||||
| import 'package:easy_localization/easy_localization.dart' show tr, StringTranslateExtension; | ||||
| 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/extensions/widget_extensions.dart'; | ||||
| import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_view_model.dart' | ||||
|     show BookAppointmentsViewModel; | ||||
| import 'package:hmg_patient_app_new/features/doctor_filter/doctor_filter_view_model.dart'; | ||||
| import 'package:hmg_patient_app_new/theme/colors.dart'; | ||||
| import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart'; | ||||
| import 'package:provider/provider.dart'; | ||||
| 
 | ||||
| class FacilityChip extends StatelessWidget { | ||||
|   const FacilityChip({super.key}); | ||||
| 
 | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return Selector<DoctorFilterViewModel, List<String>>( | ||||
|         selector: (_, model) => model.facilityList, | ||||
|         builder: (__, data, ___) => | ||||
|             Selector<DoctorFilterViewModel, List<String>?>( | ||||
|               selector: (_, model) => model.selectedFacilityForFilters, | ||||
|               builder: (context, selectRegion, ___) => Row( | ||||
|                 children: [ | ||||
|                   Expanded( | ||||
|                       child: ListView.separated( | ||||
|                           scrollDirection: Axis.horizontal, | ||||
| 
 | ||||
|                           itemCount: data.length, | ||||
|                           separatorBuilder: (_, __)=>SizedBox(width: 8.h,), | ||||
|                           itemBuilder: (_, index) => AppCustomChipWidget( | ||||
|                             icon: data[index].contains("hmg")?AppAssets.hmg: AppAssets.hmc, | ||||
|                               iconHasColor: false, | ||||
|                               iconSize: 18, | ||||
|                               labelText: data[index].tr(), | ||||
|                               textColor: selectRegion?.any((selectedRegion)=>data[index] == selectedRegion) == true | ||||
|                                   ? AppColors.primaryRedColor | ||||
|                                   : AppColors.textColor, | ||||
|                               backgroundColor: | ||||
|                               data[index] == selectRegion | ||||
|                                   ? AppColors.primaryRedColor | ||||
|                                   .withOpacity(0.1) | ||||
|                                   : AppColors.whiteColor, | ||||
|                               shape: RoundedRectangleBorder( | ||||
|                                   side: BorderSide( | ||||
|                                     color: selectRegion?.any((selectedRegion)=>data[index] == selectedRegion) == true | ||||
|                                         ? AppColors | ||||
|                                         .primaryRedBorderColor | ||||
|                                         : AppColors | ||||
|                                         .chipBorderColorOpacity20, | ||||
|                                     width: 1, | ||||
|                                   ), | ||||
|                                   borderRadius: | ||||
|                                   BorderRadius.circular(10))) | ||||
|                               .onPress(() { | ||||
|                             context | ||||
|                                 .read<DoctorFilterViewModel>() | ||||
|                                 .setSelectedFacilityForFilter(data[index]); | ||||
|                           }))) | ||||
|                 ], | ||||
|               ), | ||||
|             )); | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,304 @@ | ||||
| import 'dart:io'; | ||||
| 
 | ||||
| import 'package:easy_localization/easy_localization.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:hmg_patient_app_new/core/app_assets.dart'; | ||||
| import 'package:hmg_patient_app_new/core/app_state.dart'; | ||||
| import 'package:hmg_patient_app_new/core/dependencies.dart'; | ||||
| import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; | ||||
| import 'package:hmg_patient_app_new/core/utils/utils.dart'; | ||||
| import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; | ||||
| import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; | ||||
| import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_view_model.dart'; | ||||
| import 'package:hmg_patient_app_new/features/immediate_livecare/immediate_livecare_view_model.dart'; | ||||
| import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; | ||||
| import 'package:hmg_patient_app_new/presentation/book_appointment/livecare/immediate_livecare_payment_page.dart'; | ||||
| import 'package:hmg_patient_app_new/presentation/book_appointment/livecare/widgets/select_livecare_call_type.dart'; | ||||
| import 'package:hmg_patient_app_new/presentation/insurance/insurance_home_page.dart'; | ||||
| import 'package:hmg_patient_app_new/theme/colors.dart'; | ||||
| import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart'; | ||||
| import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; | ||||
| import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart'; | ||||
| import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart'; | ||||
| import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart'; | ||||
| import 'package:permission_handler/permission_handler.dart'; | ||||
| import 'package:provider/provider.dart'; | ||||
| import 'package:smooth_corner/smooth_corner.dart'; | ||||
| 
 | ||||
| class ImmediateLiveCarePaymentDetails extends StatelessWidget { | ||||
|   ImmediateLiveCarePaymentDetails({super.key}); | ||||
| 
 | ||||
|   late ImmediateLiveCareViewModel immediateLiveCareViewModel; | ||||
|   late AppState appState; | ||||
| 
 | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     immediateLiveCareViewModel = Provider.of<ImmediateLiveCareViewModel>(context, listen: false); | ||||
|     appState = getIt.get<AppState>(); | ||||
|     return Scaffold( | ||||
|       backgroundColor: AppColors.scaffoldBgColor, | ||||
|       body: Column( | ||||
|         children: [ | ||||
|           Expanded( | ||||
|             child: CollapsingListView( | ||||
|               title: "Review LiveCare Request".needTranslation, | ||||
|               child: SingleChildScrollView( | ||||
|                 padding: EdgeInsets.symmetric(horizontal: 24.h), | ||||
|                 child: Column( | ||||
|                   crossAxisAlignment: CrossAxisAlignment.start, | ||||
|                   children: [ | ||||
|                     SizedBox(height: 24.h), | ||||
|                     LocaleKeys.patientInfo.tr(context: context).toText16(isBold: true), | ||||
|                     SizedBox(height: 16.h), | ||||
|                     Container( | ||||
|                       decoration: RoundedRectangleBorder().toSmoothCornerDecoration( | ||||
|                         color: AppColors.whiteColor, | ||||
|                         borderRadius: 24.h, | ||||
|                         hasShadow: false, | ||||
|                       ), | ||||
|                       child: Padding( | ||||
|                         padding: EdgeInsets.all(16.h), | ||||
|                         child: Row( | ||||
|                           children: [ | ||||
|                             Image.asset( | ||||
|                               appState.getAuthenticatedUser()?.gender == 1 ? AppAssets.male_img : AppAssets.femaleImg, | ||||
|                               width: 52.h, | ||||
|                               height: 52.h, | ||||
|                             ), | ||||
|                             SizedBox(width: 8.h), | ||||
|                             Column( | ||||
|                               crossAxisAlignment: CrossAxisAlignment.start, | ||||
|                               children: [ | ||||
|                                 "${appState.getAuthenticatedUser()!.firstName} ${appState.getAuthenticatedUser()!.lastName}".toText16(isBold: true), | ||||
|                                 SizedBox(height: 8.h), | ||||
|                                 AppCustomChipWidget(labelText: "${appState.getAuthenticatedUser()!.age} Years Old"), | ||||
|                               ], | ||||
|                             ), | ||||
|                           ], | ||||
|                         ), | ||||
|                       ), | ||||
|                     ), | ||||
|                     SizedBox(height: 24.h), | ||||
|                     "Clinic Information".needTranslation.toText16(isBold: true), | ||||
|                     SizedBox(height: 16.h), | ||||
|                     Container( | ||||
|                       decoration: RoundedRectangleBorder().toSmoothCornerDecoration( | ||||
|                         color: AppColors.whiteColor, | ||||
|                         borderRadius: 24.h, | ||||
|                         hasShadow: false, | ||||
|                       ), | ||||
|                       child: Padding( | ||||
|                         padding: EdgeInsets.all(16.h), | ||||
|                         child: Row( | ||||
|                           children: [ | ||||
|                             Utils.buildSvgWithAssets(icon: AppAssets.generic_clinic_icon, width: 32.h, height: 32.h, fit: BoxFit.contain), | ||||
|                             SizedBox(width: 8.h), | ||||
|                             Column( | ||||
|                               crossAxisAlignment: CrossAxisAlignment.start, | ||||
|                               children: [ | ||||
|                                 (appState.isArabic() | ||||
|                                         ? immediateLiveCareViewModel.immediateLiveCareSelectedClinic.serviceNameN | ||||
|                                         : immediateLiveCareViewModel.immediateLiveCareSelectedClinic.serviceName)! | ||||
|                                     .toText16(isBold: true), | ||||
|                                 // SizedBox(height: 8.h), | ||||
|                                 // AppCustomChipWidget(labelText: "${appState.getAuthenticatedUser()!.age} Years Old"), | ||||
|                               ], | ||||
|                             ), | ||||
|                           ], | ||||
|                         ), | ||||
|                       ), | ||||
|                     ), | ||||
|                     SizedBox(height: 24.h), | ||||
|                     "Selected LiveCare Type".needTranslation.toText16(isBold: true), | ||||
|                     SizedBox(height: 16.h), | ||||
|                     Consumer<BookAppointmentsViewModel>(builder: (context, bookAppointmentsVM, child) { | ||||
|                       return Container( | ||||
|                         decoration: RoundedRectangleBorder().toSmoothCornerDecoration( | ||||
|                           color: AppColors.whiteColor, | ||||
|                           borderRadius: 24.h, | ||||
|                           hasShadow: false, | ||||
|                         ), | ||||
|                         child: Padding( | ||||
|                           padding: EdgeInsets.all(16.h), | ||||
|                           child: Row( | ||||
|                             mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||
|                             children: [ | ||||
|                               Row( | ||||
|                                 children: [ | ||||
|                                   Utils.buildSvgWithAssets(icon: AppAssets.livecare_clinic_icon, width: 32.h, height: 32.h, fit: BoxFit.contain), | ||||
|                                   SizedBox(width: 8.h), | ||||
|                                   getLiveCareType(immediateLiveCareViewModel.liveCareSelectedCallType).toText16(isBold: true), | ||||
|                                 ], | ||||
|                               ), | ||||
|                               Utils.buildSvgWithAssets(icon: AppAssets.edit_icon, width: 24.h, height: 24.h, fit: BoxFit.contain), | ||||
|                             ], | ||||
|                           ), | ||||
|                         ), | ||||
|                       ).onPress(() { | ||||
|                         showCommonBottomSheetWithoutHeight(context, child: SelectLiveCareCallType(immediateLiveCareViewModel: immediateLiveCareViewModel), callBackFunc: () async { | ||||
|                           debugPrint("Selected Call Type: ${immediateLiveCareViewModel.liveCareSelectedCallType}"); | ||||
|                         }, title: "Select LiveCare call type".needTranslation, isCloseButtonVisible: true, isFullScreen: false); | ||||
|                       }); | ||||
|                     }), | ||||
|                     SizedBox(height: 24.h) | ||||
|                   ], | ||||
|                 ), | ||||
|               ), | ||||
|             ), | ||||
|           ), | ||||
|           Container( | ||||
|             decoration: RoundedRectangleBorder().toSmoothCornerDecoration( | ||||
|               color: AppColors.whiteColor, | ||||
|               borderRadius: 24.h, | ||||
|               hasShadow: false, | ||||
|             ), | ||||
|             child: Column( | ||||
|               crossAxisAlignment: CrossAxisAlignment.start, | ||||
|               children: [ | ||||
|                 (immediateLiveCareViewModel.liveCareImmediateAppointmentFeesList.isCash ?? true) | ||||
|                     ? Container( | ||||
|                         height: 50.h, | ||||
|                         decoration: ShapeDecoration( | ||||
|                           color: AppColors.secondaryLightRedBorderColor, | ||||
|                           shape: SmoothRectangleBorder( | ||||
|                             borderRadius: BorderRadius.only(topLeft: Radius.circular(24), topRight: Radius.circular(24)), | ||||
|                             smoothness: 1, | ||||
|                           ), | ||||
|                         ), | ||||
|                         child: Row( | ||||
|                           mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||
|                           children: [ | ||||
|                             "Insurance expired or inactive".needTranslation.toText14(color: AppColors.primaryRedColor, weight: FontWeight.w500).paddingSymmetrical(24.h, 0.h), | ||||
|                             CustomButton( | ||||
|                               text: LocaleKeys.updateInsurance.tr(context: context), | ||||
|                               onPressed: () { | ||||
|                                 Navigator.of(context).push( | ||||
|                                   CustomPageRoute( | ||||
|                                     page: InsuranceHomePage(), | ||||
|                                   ), | ||||
|                                 ); | ||||
|                               }, | ||||
|                               backgroundColor: AppColors.primaryRedColor, | ||||
|                               borderColor: AppColors.secondaryLightRedBorderColor, | ||||
|                               textColor: AppColors.whiteColor, | ||||
|                               fontSize: 10, | ||||
|                               fontWeight: FontWeight.w500, | ||||
|                               borderRadius: 8, | ||||
|                               padding: EdgeInsets.fromLTRB(15, 0, 15, 0), | ||||
|                               height: 30.h, | ||||
|                             ).paddingSymmetrical(24.h, 0.h), | ||||
|                           ], | ||||
|                         ), | ||||
|                       ) | ||||
|                     : const SizedBox(), | ||||
|                 SizedBox(height: 24.h), | ||||
|                 "Total amount to pay".needTranslation.toText18(isBold: true).paddingSymmetrical(24.h, 0.h), | ||||
|                 SizedBox(height: 17.h), | ||||
|                 Row( | ||||
|                   mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||
|                   children: [ | ||||
|                     "Amount before tax".needTranslation.toText14(isBold: true), | ||||
|                     Utils.getPaymentAmountWithSymbol(immediateLiveCareViewModel.liveCareImmediateAppointmentFeesList.amount!.toText16(isBold: true), AppColors.blackColor, 13, | ||||
|                         isSaudiCurrency: immediateLiveCareViewModel.liveCareImmediateAppointmentFeesList.currency!.toLowerCase() == "sar"), | ||||
|                   ], | ||||
|                 ).paddingSymmetrical(24.h, 0.h), | ||||
|                 Row( | ||||
|                   mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||
|                   children: [ | ||||
|                     "VAT 15%".needTranslation.toText14(isBold: true, color: AppColors.greyTextColor), | ||||
|                     Utils.getPaymentAmountWithSymbol( | ||||
|                         immediateLiveCareViewModel.liveCareImmediateAppointmentFeesList.tax!.toText14(isBold: true, color: AppColors.greyTextColor), AppColors.greyTextColor, 13, | ||||
|                         isSaudiCurrency: immediateLiveCareViewModel.liveCareImmediateAppointmentFeesList.currency!.toLowerCase() == "sar"), | ||||
|                   ], | ||||
|                 ).paddingSymmetrical(24.h, 0.h), | ||||
|                 SizedBox(height: 17.h), | ||||
|                 Row( | ||||
|                   mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||
|                   children: [ | ||||
|                     SizedBox(width: 150.h, child: Utils.getPaymentMethods()), | ||||
|                     Utils.getPaymentAmountWithSymbol(immediateLiveCareViewModel.liveCareImmediateAppointmentFeesList.total!.toText24(isBold: true), AppColors.blackColor, 17, | ||||
|                         isSaudiCurrency: immediateLiveCareViewModel.liveCareImmediateAppointmentFeesList.currency!.toLowerCase() == "sar"), | ||||
|                   ], | ||||
|                 ).paddingSymmetrical(24.h, 0.h), | ||||
|                 CustomButton( | ||||
|                   text: LocaleKeys.payNow.tr(context: context), | ||||
|                   onPressed: () async { | ||||
|                     await askVideoCallPermission().then((val) { | ||||
|                       if (val) { | ||||
|                         Navigator.of(context).push( | ||||
|                           CustomPageRoute( | ||||
|                             page: ImmediateLiveCarePaymentPage(), | ||||
|                           ), | ||||
|                         ); | ||||
|                       } else { | ||||
|                         showCommonBottomSheetWithoutHeight( | ||||
|                           title: LocaleKeys.notice.tr(context: context), | ||||
|                           context, | ||||
|                           child: Utils.getWarningWidget( | ||||
|                               loadingText: | ||||
|                                   "LiveCare requires Camera, Microphone & Location permissions to enable virtual consultation between patient & doctor, Please allow these to proceed.".needTranslation, | ||||
|                               isShowActionButtons: true, | ||||
|                               onCancelTap: () { | ||||
|                                 Navigator.pop(context); | ||||
|                               }, | ||||
|                               onConfirmTap: () async { | ||||
|                                 openAppSettings(); | ||||
|                               }), | ||||
|                           callBackFunc: () {}, | ||||
|                           isFullScreen: false, | ||||
|                           isCloseButtonVisible: true, | ||||
|                         ); | ||||
|                       } | ||||
|                     }); | ||||
|                   }, | ||||
|                   backgroundColor: AppColors.infoColor, | ||||
|                   borderColor: AppColors.infoColor, | ||||
|                   textColor: AppColors.whiteColor, | ||||
|                   fontSize: 16, | ||||
|                   fontWeight: FontWeight.w500, | ||||
|                   borderRadius: 12, | ||||
|                   padding: EdgeInsets.fromLTRB(10, 0, 10, 0), | ||||
|                   height: 50.h, | ||||
|                   icon: AppAssets.appointment_pay_icon, | ||||
|                   iconColor: AppColors.whiteColor, | ||||
|                   iconSize: 18.h, | ||||
|                 ).paddingSymmetrical(24.h, 24.h), | ||||
|               ], | ||||
|             ), | ||||
|           ), | ||||
|         ], | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   Future<bool> askVideoCallPermission() async { | ||||
|     Map<Permission, PermissionStatus> statuses = await [ | ||||
|       Permission.camera, | ||||
|       Permission.microphone, | ||||
|     ].request(); | ||||
| 
 | ||||
|     if (statuses[Permission.camera] == PermissionStatus.granted && statuses[Permission.microphone] == PermissionStatus.granted) { | ||||
|       // Camera permission granted | ||||
|       return true; | ||||
|     } else { | ||||
|       return false; | ||||
|     } | ||||
| 
 | ||||
|     // if (!(await Permission.camera.request().isGranted) || !(await Permission.microphone.request().isGranted)) { | ||||
|     //   return false; | ||||
|     // } | ||||
|   } | ||||
| 
 | ||||
|   String getLiveCareType(int callType) { | ||||
|     switch (callType) { | ||||
|       case 1: | ||||
|         return "Video Call".needTranslation; | ||||
|       case 2: | ||||
|         return "Audio Call".needTranslation; | ||||
|       case 3: | ||||
|         return "Phone Call".needTranslation; | ||||
|       default: | ||||
|         return "Video Call".needTranslation; | ||||
|     } | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,580 @@ | ||||
| import 'dart:async'; | ||||
| import 'dart:developer'; | ||||
| import 'dart:io'; | ||||
| 
 | ||||
| import 'package:easy_localization/easy_localization.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:hmg_patient_app_new/core/api_consts.dart'; | ||||
| import 'package:hmg_patient_app_new/core/app_assets.dart'; | ||||
| import 'package:hmg_patient_app_new/core/app_state.dart'; | ||||
| import 'package:hmg_patient_app_new/core/cache_consts.dart'; | ||||
| import 'package:hmg_patient_app_new/core/dependencies.dart'; | ||||
| import 'package:hmg_patient_app_new/core/enums.dart'; | ||||
| import 'package:hmg_patient_app_new/core/utils/date_util.dart'; | ||||
| import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_view_model.dart'; | ||||
| import 'package:hmg_patient_app_new/features/immediate_livecare/immediate_livecare_view_model.dart'; | ||||
| import 'package:hmg_patient_app_new/features/payfort/models/apple_pay_request_insert_model.dart'; | ||||
| import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; | ||||
| import 'package:hmg_patient_app_new/core/utils/utils.dart'; | ||||
| import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; | ||||
| import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; | ||||
| import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/patient_appointment_history_response_model.dart'; | ||||
| import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart'; | ||||
| import 'package:hmg_patient_app_new/features/payfort/payfort_view_model.dart'; | ||||
| import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; | ||||
| import 'package:hmg_patient_app_new/presentation/appointments/my_appointments_page.dart'; | ||||
| import 'package:hmg_patient_app_new/presentation/book_appointment/livecare/immediate_livecare_pending_request_page.dart'; | ||||
| import 'package:hmg_patient_app_new/presentation/home/navigation_screen.dart'; | ||||
| import 'package:hmg_patient_app_new/presentation/insurance/insurance_home_page.dart'; | ||||
| import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart'; | ||||
| import 'package:hmg_patient_app_new/services/cache_service.dart'; | ||||
| import 'package:hmg_patient_app_new/theme/colors.dart'; | ||||
| import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; | ||||
| import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart'; | ||||
| import 'package:hmg_patient_app_new/widgets/in_app_browser/InAppBrowser.dart'; | ||||
| import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart'; | ||||
| import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart'; | ||||
| import 'package:permission_handler/permission_handler.dart'; | ||||
| import 'package:provider/provider.dart'; | ||||
| import 'package:smooth_corner/smooth_corner.dart'; | ||||
| 
 | ||||
| class ImmediateLiveCarePaymentPage extends StatefulWidget { | ||||
|   ImmediateLiveCarePaymentPage({super.key}); | ||||
| 
 | ||||
|   @override | ||||
|   State<ImmediateLiveCarePaymentPage> createState() => _ImmediateLiveCarePaymentPageState(); | ||||
| } | ||||
| 
 | ||||
| class _ImmediateLiveCarePaymentPageState extends State<ImmediateLiveCarePaymentPage> { | ||||
|   late PayfortViewModel payfortViewModel; | ||||
|   late ImmediateLiveCareViewModel immediateLiveCareViewModel; | ||||
|   late MyAppointmentsViewModel myAppointmentsViewModel; | ||||
|   late AppState appState; | ||||
| 
 | ||||
|   MyInAppBrowser? browser; | ||||
|   String selectedPaymentMethod = ""; | ||||
| 
 | ||||
|   String transID = ""; | ||||
| 
 | ||||
|   bool isShowTamara = false; | ||||
|   String tamaraPaymentStatus = ""; | ||||
|   String tamaraOrderID = ""; | ||||
| 
 | ||||
|   @override | ||||
|   void initState() { | ||||
|     scheduleMicrotask(() { | ||||
|       payfortViewModel.initPayfortViewModel(); | ||||
|       myAppointmentsViewModel.getTamaraInstallmentsDetails().then((val) { | ||||
|         if (num.parse(immediateLiveCareViewModel.liveCareImmediateAppointmentFeesList.total!) >= myAppointmentsViewModel.getTamaraInstallmentsDetailsResponseModel!.minLimit!.amount! && | ||||
|             num.parse(immediateLiveCareViewModel.liveCareImmediateAppointmentFeesList.total!) <= myAppointmentsViewModel.getTamaraInstallmentsDetailsResponseModel!.maxLimit!.amount!) { | ||||
|           setState(() { | ||||
|             isShowTamara = true; | ||||
|           }); | ||||
|         } | ||||
|       }); | ||||
|     }); | ||||
|     super.initState(); | ||||
|   } | ||||
| 
 | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     appState = getIt.get<AppState>(); | ||||
|     myAppointmentsViewModel = Provider.of<MyAppointmentsViewModel>(context, listen: false); | ||||
|     immediateLiveCareViewModel = Provider.of<ImmediateLiveCareViewModel>(context, listen: false); | ||||
|     payfortViewModel = Provider.of<PayfortViewModel>(context, listen: false); | ||||
|     return Scaffold( | ||||
|       backgroundColor: AppColors.bgScaffoldColor, | ||||
|       body: Consumer<MyAppointmentsViewModel>(builder: (context, myAppointmentsVM, child) { | ||||
|         return Column( | ||||
|           children: [ | ||||
|             Expanded( | ||||
|               child: CollapsingListView( | ||||
|                 title: "LiveCare Payment".needTranslation, | ||||
|                 child: SingleChildScrollView( | ||||
|                   child: Column( | ||||
|                     crossAxisAlignment: CrossAxisAlignment.start, | ||||
|                     children: [ | ||||
|                       SizedBox(height: 24.h), | ||||
|                       Container( | ||||
|                         decoration: RoundedRectangleBorder().toSmoothCornerDecoration( | ||||
|                           color: AppColors.whiteColor, | ||||
|                           borderRadius: 20.h, | ||||
|                           hasShadow: false, | ||||
|                         ), | ||||
|                         child: Row( | ||||
|                           mainAxisSize: MainAxisSize.max, | ||||
|                           children: [ | ||||
|                             Column( | ||||
|                               crossAxisAlignment: CrossAxisAlignment.start, | ||||
|                               children: [ | ||||
|                                 Image.asset(AppAssets.mada, width: 72.h, height: 25.h), | ||||
|                                 SizedBox(height: 16.h), | ||||
|                                 "Mada".needTranslation.toText16(isBold: true), | ||||
|                               ], | ||||
|                             ), | ||||
|                             SizedBox(width: 8.h), | ||||
|                             const Spacer(), | ||||
|                             Transform.flip( | ||||
|                               flipX: appState.isArabic() ? true : false, | ||||
|                               child: Utils.buildSvgWithAssets( | ||||
|                                 icon: AppAssets.forward_arrow_icon, | ||||
|                                 iconColor: AppColors.blackColor, | ||||
|                                 width: 40.h, | ||||
|                                 height: 40.h, | ||||
|                                 fit: BoxFit.contain, | ||||
|                               ), | ||||
|                             ), | ||||
|                           ], | ||||
|                         ).paddingSymmetrical(16.h, 16.h), | ||||
|                       ).paddingSymmetrical(24.h, 0.h).onPress(() { | ||||
|                         selectedPaymentMethod = "MADA"; | ||||
|                         openPaymentURL("mada"); | ||||
|                       }), | ||||
|                       SizedBox(height: 16.h), | ||||
|                       Container( | ||||
|                         decoration: RoundedRectangleBorder().toSmoothCornerDecoration( | ||||
|                           color: AppColors.whiteColor, | ||||
|                           borderRadius: 20.h, | ||||
|                           hasShadow: false, | ||||
|                         ), | ||||
|                         child: Row( | ||||
|                           mainAxisSize: MainAxisSize.max, | ||||
|                           children: [ | ||||
|                             Column( | ||||
|                               crossAxisAlignment: CrossAxisAlignment.start, | ||||
|                               children: [ | ||||
|                                 Row( | ||||
|                                   children: [ | ||||
|                                     Image.asset(AppAssets.visa, width: 50.h, height: 50.h), | ||||
|                                     SizedBox(width: 8.h), | ||||
|                                     Image.asset(AppAssets.Mastercard, width: 40.h, height: 40.h), | ||||
|                                   ], | ||||
|                                 ), | ||||
|                                 SizedBox(height: 16.h), | ||||
|                                 "Visa or Mastercard".needTranslation.toText16(isBold: true), | ||||
|                               ], | ||||
|                             ), | ||||
|                             SizedBox(width: 8.h), | ||||
|                             const Spacer(), | ||||
|                             Transform.flip( | ||||
|                               flipX: appState.isArabic() ? true : false, | ||||
|                               child: Utils.buildSvgWithAssets( | ||||
|                                 icon: AppAssets.forward_arrow_icon, | ||||
|                                 iconColor: AppColors.blackColor, | ||||
|                                 width: 40.h, | ||||
|                                 height: 40.h, | ||||
|                                 fit: BoxFit.contain, | ||||
|                               ), | ||||
|                             ), | ||||
|                           ], | ||||
|                         ).paddingSymmetrical(16.h, 16.h), | ||||
|                       ).paddingSymmetrical(24.h, 0.h).onPress(() { | ||||
|                         selectedPaymentMethod = "VISA"; | ||||
|                         openPaymentURL("visa"); | ||||
|                       }), | ||||
|                       SizedBox(height: 16.h), | ||||
|                       isShowTamara | ||||
|                           ? Container( | ||||
|                               decoration: RoundedRectangleBorder().toSmoothCornerDecoration( | ||||
|                                 color: AppColors.whiteColor, | ||||
|                           borderRadius: 20.h, | ||||
|                           hasShadow: false, | ||||
|                         ), | ||||
|                         child: Row( | ||||
|                           mainAxisSize: MainAxisSize.max, | ||||
|                           children: [ | ||||
|                             Column( | ||||
|                               crossAxisAlignment: CrossAxisAlignment.start, | ||||
|                               children: [ | ||||
|                                 Image.asset(AppAssets.tamara_en, width: 72.h, height: 25.h), | ||||
|                                 SizedBox(height: 16.h), | ||||
|                                 "Tamara".needTranslation.toText16(isBold: true), | ||||
|                               ], | ||||
|                             ), | ||||
|                             SizedBox(width: 8.h), | ||||
|                             const Spacer(), | ||||
|                             Transform.flip( | ||||
|                               flipX: appState.isArabic() ? true : false, | ||||
|                               child: Utils.buildSvgWithAssets( | ||||
|                                 icon: AppAssets.forward_arrow_icon, | ||||
|                                 iconColor: AppColors.blackColor, | ||||
|                                 width: 40.h, | ||||
|                                 height: 40.h, | ||||
|                                 fit: BoxFit.contain, | ||||
|                               ), | ||||
|                             ), | ||||
|                           ], | ||||
|                         ).paddingSymmetrical(16.h, 16.h), | ||||
|                       ).paddingSymmetrical(24.h, 0.h).onPress(() { | ||||
|                         selectedPaymentMethod = "TAMARA"; | ||||
|                         openPaymentURL("tamara"); | ||||
|                             }) | ||||
|                           : SizedBox.shrink(), | ||||
|                     ], | ||||
|                   ), | ||||
|                 ), | ||||
|               ), | ||||
|             ), | ||||
|             Container( | ||||
|               decoration: RoundedRectangleBorder().toSmoothCornerDecoration( | ||||
|                 color: AppColors.whiteColor, | ||||
|                 borderRadius: 24.h, | ||||
|                 hasShadow: false, | ||||
|               ), | ||||
|               child: Consumer<PayfortViewModel>(builder: (context, payfortVM, child) { | ||||
|                 //TODO: Need to add loading state & animation for Apple Pay Configuration | ||||
|                 return Column( | ||||
|                   crossAxisAlignment: CrossAxisAlignment.start, | ||||
|                   children: [ | ||||
|                     (immediateLiveCareViewModel.liveCareImmediateAppointmentFeesList.isCash ?? true) | ||||
|                         ? Container( | ||||
|                             height: 50.h, | ||||
|                             decoration: ShapeDecoration( | ||||
|                               color: AppColors.secondaryLightRedBorderColor, | ||||
|                               shape: SmoothRectangleBorder( | ||||
|                                 borderRadius: BorderRadius.only(topLeft: Radius.circular(24), topRight: Radius.circular(24)), | ||||
|                                 smoothness: 1, | ||||
|                               ), | ||||
|                             ), | ||||
|                             child: Row( | ||||
|                               mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||
|                               children: [ | ||||
|                                 "Insurance expired or inactive".needTranslation.toText14(color: AppColors.primaryRedColor, weight: FontWeight.w500).paddingSymmetrical(24.h, 0.h), | ||||
|                                 CustomButton( | ||||
|                                   text: LocaleKeys.updateInsurance.tr(context: context), | ||||
|                                   onPressed: () { | ||||
|                                     Navigator.of(context).push( | ||||
|                                       CustomPageRoute( | ||||
|                                         page: InsuranceHomePage(), | ||||
|                                       ), | ||||
|                                     ); | ||||
|                                   }, | ||||
|                                   backgroundColor: AppColors.primaryRedColor, | ||||
|                                   borderColor: AppColors.secondaryLightRedBorderColor, | ||||
|                                   textColor: AppColors.whiteColor, | ||||
|                                   fontSize: 10, | ||||
|                                   fontWeight: FontWeight.w500, | ||||
|                                   borderRadius: 8, | ||||
|                                   padding: EdgeInsets.fromLTRB(15, 0, 15, 0), | ||||
|                                   height: 30.h, | ||||
|                                 ).paddingSymmetrical(24.h, 0.h), | ||||
|                               ], | ||||
|                             ), | ||||
|                           ) | ||||
|                         : const SizedBox(), | ||||
|                     SizedBox(height: 24.h), | ||||
|                     "Total amount to pay".needTranslation.toText18(isBold: true).paddingSymmetrical(24.h, 0.h), | ||||
|                     SizedBox(height: 17.h), | ||||
|                     Row( | ||||
|                       mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||
|                       children: [ | ||||
|                         "Amount before tax".needTranslation.toText14(isBold: true), | ||||
|                         Utils.getPaymentAmountWithSymbol(immediateLiveCareViewModel.liveCareImmediateAppointmentFeesList.amount!.toString().toText16(isBold: true), AppColors.blackColor, 13, | ||||
|                             isSaudiCurrency: true), | ||||
|                       ], | ||||
|                     ).paddingSymmetrical(24.h, 0.h), | ||||
|                     Row( | ||||
|                       mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||
|                       children: [ | ||||
|                         "VAT 15%".needTranslation.toText14(isBold: true, color: AppColors.greyTextColor), | ||||
|                         Utils.getPaymentAmountWithSymbol( | ||||
|                             immediateLiveCareViewModel.liveCareImmediateAppointmentFeesList.tax!.toString().toText14(isBold: true, color: AppColors.greyTextColor), AppColors.greyTextColor, 13, | ||||
|                             isSaudiCurrency: true), | ||||
|                       ], | ||||
|                     ).paddingSymmetrical(24.h, 0.h), | ||||
|                     SizedBox(height: 17.h), | ||||
|                     Row( | ||||
|                       mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||
|                       children: [ | ||||
|                         "".needTranslation.toText14(isBold: true), | ||||
|                         Utils.getPaymentAmountWithSymbol(immediateLiveCareViewModel.liveCareImmediateAppointmentFeesList.total!.toString().toText24(isBold: true), AppColors.blackColor, 17, | ||||
|                             isSaudiCurrency: true), | ||||
|                       ], | ||||
|                     ).paddingSymmetrical(24.h, 0.h), | ||||
|                     Platform.isIOS | ||||
|                         ? Utils.buildSvgWithAssets( | ||||
|                             icon: AppAssets.apple_pay_button, | ||||
|                             width: 200.h, | ||||
|                             height: 80.h, | ||||
|                             fit: BoxFit.contain, | ||||
|                           ).paddingSymmetrical(24.h, 0.h).onPress(() { | ||||
|                             // payfortVM.setIsApplePayConfigurationLoading(true); | ||||
|                             if (Utils.havePrivilege(103)) { | ||||
|                               startApplePay(); | ||||
|                             } else { | ||||
|                               openPaymentURL("ApplePay"); | ||||
|                             } | ||||
|                           }) | ||||
|                         : SizedBox(height: 12.h), | ||||
|                     SizedBox(height: 12.h), | ||||
|                   ], | ||||
|                 ); | ||||
|               }), | ||||
|             ), | ||||
|           ], | ||||
|         ); | ||||
|       }), | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   onBrowserLoadStart(String url) { | ||||
|     print("onBrowserLoadStart"); | ||||
|     print(url); | ||||
| 
 | ||||
|     if (selectedPaymentMethod == "TAMARA") { | ||||
|       if (Platform.isAndroid) { | ||||
|         Uri uri = new Uri.dataFromString(url); | ||||
|         tamaraPaymentStatus = uri.queryParameters['status']!; | ||||
|         tamaraOrderID = uri.queryParameters['AuthorizePaymentId']!; | ||||
|       } else { | ||||
|         Uri uri = new Uri.dataFromString(url); | ||||
|         tamaraPaymentStatus = uri.queryParameters['paymentStatus']!; | ||||
|         tamaraOrderID = uri.queryParameters['orderId']!; | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     // if(selectedPaymentMethod != "TAMARA") { | ||||
|     MyInAppBrowser.successURLS.forEach((element) { | ||||
|       if (url.contains(element)) { | ||||
|         browser?.close(); | ||||
|         MyInAppBrowser.isPaymentDone = true; | ||||
|         return; | ||||
|       } | ||||
|     }); | ||||
|     // } | ||||
| 
 | ||||
|     // if(selectedPaymentMethod != "TAMARA") { | ||||
|     MyInAppBrowser.errorURLS.forEach((element) { | ||||
|       if (url.contains(element)) { | ||||
|         browser?.close(); | ||||
|         MyInAppBrowser.isPaymentDone = false; | ||||
|         return; | ||||
|       } | ||||
|     }); | ||||
|     // } | ||||
|   } | ||||
| 
 | ||||
|   onBrowserExit(bool isPaymentMade) async { | ||||
|     debugPrint("onBrowserExit Called!!!!"); | ||||
|     checkPaymentStatus(); | ||||
|   } | ||||
| 
 | ||||
|   void checkPaymentStatus() async { | ||||
|     LoaderBottomSheet.showLoader(loadingText: "Checking payment status, Please wait...".needTranslation); | ||||
| 
 | ||||
|     if (selectedPaymentMethod == "TAMARA") { | ||||
|       await payfortViewModel.checkTamaraPaymentStatus( | ||||
|           transactionID: transID, | ||||
|           onSuccess: (apiResponse) async { | ||||
|             if (apiResponse.data["status"].toString().toLowerCase() == "success") { | ||||
|               tamaraOrderID = apiResponse.data["tamara_order_id"].toString(); | ||||
|               await payfortViewModel.updateTamaraRequestStatus(responseMessage: "success", status: "14", clientRequestID: transID, tamaraOrderID: tamaraOrderID); | ||||
|               await immediateLiveCareViewModel.addNewCallRequestForImmediateLiveCare(transID); | ||||
|               await immediateLiveCareViewModel.getPatientLiveCareHistory(); | ||||
|               LoaderBottomSheet.hideLoader(); | ||||
|               if (immediateLiveCareViewModel.patientHasPendingLiveCareRequest) { | ||||
|                 Navigator.pushAndRemoveUntil( | ||||
|                     context, | ||||
|                     CustomPageRoute( | ||||
|                       page: LandingNavigation(), | ||||
|                     ), | ||||
|                     (r) => false); | ||||
|                 Navigator.of(context).push( | ||||
|                   CustomPageRoute( | ||||
|                     page: ImmediateLiveCarePendingRequestPage(), | ||||
|                   ), | ||||
|                 ); | ||||
|               } else { | ||||
|                 showCommonBottomSheetWithoutHeight( | ||||
|                   context, | ||||
|                   child: Utils.getErrorWidget(loadingText: "Unknown error occurred...".needTranslation), | ||||
|                   callBackFunc: () {}, | ||||
|                   isFullScreen: false, | ||||
|                   isCloseButtonVisible: true, | ||||
|                 ); | ||||
|               } | ||||
|             } else { | ||||
|               await payfortViewModel.updateTamaraRequestStatus(responseMessage: "Failed", status: "00", clientRequestID: transID, tamaraOrderID: tamaraOrderID); | ||||
|               LoaderBottomSheet.hideLoader(); | ||||
|               showCommonBottomSheetWithoutHeight( | ||||
|                 context, | ||||
|                 child: Utils.getErrorWidget(loadingText: "Payment Failed! Please try again.".needTranslation), | ||||
|                 callBackFunc: () {}, | ||||
|                 isFullScreen: false, | ||||
|                 isCloseButtonVisible: true, | ||||
|               ); | ||||
|             } | ||||
|           }, | ||||
|           onError: (err) { | ||||
|             LoaderBottomSheet.hideLoader(); | ||||
|             showCommonBottomSheetWithoutHeight( | ||||
|               context, | ||||
|               child: Utils.getErrorWidget(loadingText: err), | ||||
|               callBackFunc: () {}, | ||||
|               isFullScreen: false, | ||||
|               isCloseButtonVisible: true, | ||||
|             ); | ||||
|           }); | ||||
|     } else { | ||||
|       await payfortViewModel.checkPaymentStatus( | ||||
|           transactionID: transID, | ||||
|           onSuccess: (apiResponse) async { | ||||
|             debugPrint(apiResponse.data.toString()); | ||||
|             if (payfortViewModel.payfortCheckPaymentStatusResponseModel!.responseMessage!.toLowerCase() == "success") { | ||||
|               await immediateLiveCareViewModel.addNewCallRequestForImmediateLiveCare(transID); | ||||
|               await immediateLiveCareViewModel.getPatientLiveCareHistory(); | ||||
|               LoaderBottomSheet.hideLoader(); | ||||
|               if (immediateLiveCareViewModel.patientHasPendingLiveCareRequest) { | ||||
|                 Navigator.pushAndRemoveUntil( | ||||
|                     context, | ||||
|                     CustomPageRoute( | ||||
|                       page: LandingNavigation(), | ||||
|                     ), | ||||
|                     (r) => false); | ||||
|                 Navigator.of(context).push( | ||||
|                   CustomPageRoute( | ||||
|                     page: ImmediateLiveCarePendingRequestPage(), | ||||
|                   ), | ||||
|                 ); | ||||
|               } else { | ||||
|                 showCommonBottomSheetWithoutHeight( | ||||
|                   context, | ||||
|                   child: Utils.getErrorWidget(loadingText: "Unknown error occurred...".needTranslation), | ||||
|                   callBackFunc: () {}, | ||||
|                   isFullScreen: false, | ||||
|                   isCloseButtonVisible: true, | ||||
|                 ); | ||||
|               } | ||||
|             } else { | ||||
|               showCommonBottomSheetWithoutHeight( | ||||
|                 context, | ||||
|                 child: Utils.getErrorWidget(loadingText: "Payment Failed! Please try again.".needTranslation), | ||||
|                 callBackFunc: () {}, | ||||
|                 isFullScreen: false, | ||||
|                 isCloseButtonVisible: true, | ||||
|               ); | ||||
|             } | ||||
|           }); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   openPaymentURL(String paymentMethod) { | ||||
|     browser = MyInAppBrowser(onExitCallback: onBrowserExit, onLoadStartCallback: onBrowserLoadStart, context: context); | ||||
|     transID = Utils.getAppointmentTransID( | ||||
|       immediateLiveCareViewModel.immediateLiveCareSelectedClinic.serviceID!, | ||||
|       ApiConsts.appEnvironmentType == AppEnvironmentTypeEnum.uat ? 15 : 12, | ||||
|       DateTime.now().millisecondsSinceEpoch, | ||||
|     ); | ||||
| 
 | ||||
|     //TODO: Need to pass dynamic params to the payment request instead of static values | ||||
|     browser?.openPaymentBrowser( | ||||
|         num.parse(immediateLiveCareViewModel.liveCareImmediateAppointmentFeesList.total!), | ||||
|         "LiveCare Payment", | ||||
|         transID, | ||||
|         "12", | ||||
|         "CustID_${appState.getAuthenticatedUser()!.patientId.toString()}@HMG.com", | ||||
|         selectedPaymentMethod, | ||||
|         appState.getAuthenticatedUser()!.patientType.toString(), | ||||
|         "${appState.getAuthenticatedUser()!.firstName} ${appState.getAuthenticatedUser()!.lastName}", | ||||
|         appState.getAuthenticatedUser()!.patientId.toString(), | ||||
|         appState.getAuthenticatedUser()!, | ||||
|         browser!, | ||||
|         false, | ||||
|         "4", | ||||
|         immediateLiveCareViewModel.immediateLiveCareSelectedClinic.serviceID.toString(), | ||||
|         context, | ||||
|         "3"); | ||||
|   } | ||||
| 
 | ||||
|   startApplePay() async { | ||||
|     LoaderBottomSheet.showLoader(loadingText: "Fetching Apple Pay details, Please wait...".needTranslation); | ||||
|     transID = Utils.getAppointmentTransID( | ||||
|       immediateLiveCareViewModel.immediateLiveCareSelectedClinic.serviceID!, | ||||
|       ApiConsts.appEnvironmentType == AppEnvironmentTypeEnum.uat ? 15 : 12, | ||||
|       DateTime.now().millisecondsSinceEpoch, | ||||
|     ); | ||||
| 
 | ||||
|     ApplePayInsertRequest applePayInsertRequest = ApplePayInsertRequest(); | ||||
| 
 | ||||
|     await payfortViewModel.getPayfortConfigurations( | ||||
|         serviceId: ServiceTypeEnum.appointmentPayment.getIdFromServiceEnum(), projectId: ApiConsts.appEnvironmentType == AppEnvironmentTypeEnum.uat ? 15 : 12, integrationId: 2); | ||||
| 
 | ||||
|     applePayInsertRequest.clientRequestID = transID; | ||||
|     applePayInsertRequest.clinicID = immediateLiveCareViewModel.immediateLiveCareSelectedClinic.serviceID!; | ||||
| 
 | ||||
|     // TODO: Need to pass dynamic currency coming from the API | ||||
|     applePayInsertRequest.currency = appState.getAuthenticatedUser()!.outSa! == 0 ? "SAR" : "AED"; | ||||
|     applePayInsertRequest.customerEmail = "CustID_${appState.getAuthenticatedUser()!.patientId.toString()}@HMG.com"; | ||||
|     applePayInsertRequest.customerID = appState.getAuthenticatedUser()!.patientId.toString(); | ||||
|     applePayInsertRequest.customerName = "${appState.getAuthenticatedUser()!.firstName} ${appState.getAuthenticatedUser()!.lastName}"; | ||||
| 
 | ||||
|     applePayInsertRequest.deviceToken = await Utils.getStringFromPrefs(CacheConst.pushToken); | ||||
|     applePayInsertRequest.voipToken = await Utils.getStringFromPrefs(CacheConst.voipToken); | ||||
|     applePayInsertRequest.doctorID = 0; | ||||
|     applePayInsertRequest.projectID = "12"; | ||||
|     applePayInsertRequest.serviceID = ServiceTypeEnum.appointmentPayment.getIdFromServiceEnum().toString(); | ||||
|     applePayInsertRequest.channelID = 3; | ||||
|     applePayInsertRequest.patientID = appState.getAuthenticatedUser()!.patientId.toString(); | ||||
|     applePayInsertRequest.patientTypeID = appState.getAuthenticatedUser()!.patientType; | ||||
|     applePayInsertRequest.patientOutSA = appState.getAuthenticatedUser()!.outSa; | ||||
|     applePayInsertRequest.appointmentDate = DateUtil.convertDateToString(DateTime.now()); | ||||
|     applePayInsertRequest.appointmentNo = 0; | ||||
|     applePayInsertRequest.orderDescription = "LiveCare Payment"; | ||||
|     applePayInsertRequest.liveServiceID = immediateLiveCareViewModel.immediateLiveCareSelectedClinic.serviceID!.toString(); | ||||
|     applePayInsertRequest.latitude = "0.0"; | ||||
|     applePayInsertRequest.longitude = "0.0"; | ||||
|     applePayInsertRequest.amount = immediateLiveCareViewModel.liveCareImmediateAppointmentFeesList.total.toString(); | ||||
|     applePayInsertRequest.isSchedule = "0"; | ||||
|     applePayInsertRequest.language = appState.isArabic() ? 'ar' : 'en'; | ||||
|     applePayInsertRequest.languageID = appState.isArabic() ? 1 : 2; | ||||
|     applePayInsertRequest.userName = appState.getAuthenticatedUser()!.patientId; | ||||
|     applePayInsertRequest.responseContinueURL = "http://hmg.com/Documents/success.html"; | ||||
|     applePayInsertRequest.backClickUrl = "http://hmg.com/Documents/success.html"; | ||||
|     applePayInsertRequest.paymentOption = "ApplePay"; | ||||
| 
 | ||||
|     applePayInsertRequest.isMobSDK = true; | ||||
|     applePayInsertRequest.merchantReference = transID; | ||||
|     applePayInsertRequest.merchantIdentifier = payfortViewModel.payfortProjectDetailsRespModel!.merchantIdentifier; | ||||
|     applePayInsertRequest.commandType = "PURCHASE"; | ||||
|     applePayInsertRequest.signature = payfortViewModel.payfortProjectDetailsRespModel!.signature; | ||||
|     applePayInsertRequest.accessCode = payfortViewModel.payfortProjectDetailsRespModel!.accessCode; | ||||
|     applePayInsertRequest.shaRequestPhrase = payfortViewModel.payfortProjectDetailsRespModel!.shaRequest; | ||||
|     applePayInsertRequest.shaResponsePhrase = payfortViewModel.payfortProjectDetailsRespModel!.shaResponse; | ||||
|     applePayInsertRequest.returnURL = ""; | ||||
| 
 | ||||
|     //TODO: Need to pass dynamic params to the Apple Pay instead of static values | ||||
|     await payfortViewModel.applePayRequestInsert(applePayInsertRequest: applePayInsertRequest).then((value) { | ||||
|       payfortViewModel.paymentWithApplePay( | ||||
|         customerName: "${appState.getAuthenticatedUser()!.firstName} ${appState.getAuthenticatedUser()!.lastName}", | ||||
|         // customerEmail: projectViewModel.authenticatedUserObject.user.emailAddress, | ||||
|         customerEmail: "CustID_${appState.getAuthenticatedUser()!.patientId.toString()}@HMG.com", | ||||
|         orderDescription: "LiveCare Payment", | ||||
|         orderAmount: double.parse(immediateLiveCareViewModel.liveCareImmediateAppointmentFeesList.total!), | ||||
|         merchantReference: transID, | ||||
|         merchantIdentifier: payfortViewModel.payfortProjectDetailsRespModel!.merchantIdentifier, | ||||
|         applePayAccessCode: payfortViewModel.payfortProjectDetailsRespModel!.accessCode, | ||||
|         applePayShaRequestPhrase: payfortViewModel.payfortProjectDetailsRespModel!.shaRequest, | ||||
|         currency: appState.getAuthenticatedUser()!.outSa! == 0 ? "SAR" : "AED", | ||||
|         onFailed: (failureResult) async { | ||||
|           log("failureResult: ${failureResult.message.toString()}"); | ||||
|           LoaderBottomSheet.hideLoader(); | ||||
|           showCommonBottomSheetWithoutHeight( | ||||
|             context, | ||||
|             child: Utils.getErrorWidget(loadingText: failureResult.message.toString()), | ||||
|             callBackFunc: () {}, | ||||
|             isFullScreen: false, | ||||
|             isCloseButtonVisible: true, | ||||
|           ); | ||||
|         }, | ||||
|         onSucceeded: (successResult) async { | ||||
|           LoaderBottomSheet.hideLoader(); | ||||
|           log("successResult: ${successResult.responseMessage.toString()}"); | ||||
|           selectedPaymentMethod = successResult.paymentOption ?? "VISA"; | ||||
|           checkPaymentStatus(); | ||||
|         }, | ||||
|         // projectId: appo.projectID, | ||||
|         // serviceTypeEnum: ServiceTypeEnum.appointmentPayment, | ||||
|       ); | ||||
|     }); | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,305 @@ | ||||
| import 'dart:async'; | ||||
| 
 | ||||
| import 'package:easy_localization/easy_localization.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:hmg_patient_app_new/core/app_assets.dart'; | ||||
| import 'package:hmg_patient_app_new/core/app_state.dart'; | ||||
| import 'package:hmg_patient_app_new/core/dependencies.dart'; | ||||
| import 'package:hmg_patient_app_new/core/utils/date_util.dart'; | ||||
| import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; | ||||
| import 'package:hmg_patient_app_new/core/utils/utils.dart'; | ||||
| import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; | ||||
| import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; | ||||
| import 'package:hmg_patient_app_new/features/immediate_livecare/immediate_livecare_view_model.dart'; | ||||
| import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; | ||||
| import 'package:hmg_patient_app_new/theme/colors.dart'; | ||||
| import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart'; | ||||
| import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; | ||||
| import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart'; | ||||
| import 'package:lottie/lottie.dart'; | ||||
| import 'package:provider/provider.dart'; | ||||
| import 'package:url_launcher/url_launcher.dart'; | ||||
| 
 | ||||
| class ImmediateLiveCarePendingRequestPage extends StatefulWidget { | ||||
|   ImmediateLiveCarePendingRequestPage({super.key}); | ||||
| 
 | ||||
|   @override | ||||
|   State<ImmediateLiveCarePendingRequestPage> createState() => _ImmediateLiveCarePendingRequestPageState(); | ||||
| } | ||||
| 
 | ||||
| class _ImmediateLiveCarePendingRequestPageState extends State<ImmediateLiveCarePendingRequestPage> { | ||||
|   late ImmediateLiveCareViewModel immediateLiveCareViewModel; | ||||
| 
 | ||||
|   late AppState appState; | ||||
| 
 | ||||
|   static Duration countdownDuration = Duration(minutes: 1, seconds: 0); | ||||
|   ValueNotifier<Duration> durationNotifier = ValueNotifier<Duration>(countdownDuration); | ||||
|   Timer? timer; | ||||
| 
 | ||||
|   @override | ||||
|   void initState() { | ||||
|     super.initState(); | ||||
|     scheduleMicrotask(() { | ||||
|       countdownDuration = Duration(minutes: immediateLiveCareViewModel.patientLiveCareHistoryList[0].watingtimeInteger!, seconds: 0); | ||||
|       durationNotifier = ValueNotifier<Duration>(countdownDuration); | ||||
|       startTimer(); | ||||
|     }); | ||||
|   } | ||||
| 
 | ||||
|   @override | ||||
|   void dispose() { | ||||
|     timer?.cancel(); | ||||
|     durationNotifier.dispose(); | ||||
|     super.dispose(); | ||||
|   } | ||||
| 
 | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     immediateLiveCareViewModel = Provider.of<ImmediateLiveCareViewModel>(context, listen: false); | ||||
|     appState = getIt.get<AppState>(); | ||||
|     return Scaffold( | ||||
|       backgroundColor: AppColors.bgScaffoldColor, | ||||
|       body: Consumer<ImmediateLiveCareViewModel>(builder: (context, immediateLiveCareVM, child) { | ||||
|         return Column( | ||||
|           children: [ | ||||
|             Expanded( | ||||
|               child: CollapsingListView( | ||||
|                 title: "LiveCare Pending Request".needTranslation, | ||||
|                 child: Padding( | ||||
|                   padding: EdgeInsets.all(24.0), | ||||
|                   child: Column( | ||||
|                     crossAxisAlignment: CrossAxisAlignment.start, | ||||
|                     children: [ | ||||
|                       Container( | ||||
|                         decoration: RoundedRectangleBorder().toSmoothCornerDecoration( | ||||
|                           color: AppColors.whiteColor, | ||||
|                           borderRadius: 20.h, | ||||
|                           hasShadow: false, | ||||
|                           side: BorderSide(color: AppColors.ratingColorYellow, width: 3.h), | ||||
|                         ), | ||||
|                         child: Padding( | ||||
|                           padding: EdgeInsets.all(16.h), | ||||
|                           child: Column( | ||||
|                             crossAxisAlignment: CrossAxisAlignment.start, | ||||
|                             children: [ | ||||
|                               "Expected waiting time: ".toText16(isBold: true), | ||||
|                               SizedBox(height: 8.h), | ||||
|                               ValueListenableBuilder<Duration>( | ||||
|                                 valueListenable: durationNotifier, | ||||
|                                 builder: (context, duration, child) { | ||||
|                                   return Column( | ||||
|                                     mainAxisAlignment: MainAxisAlignment.center, | ||||
|                                     children: [ | ||||
|                                       buildTime(duration), | ||||
|                                     ], | ||||
|                                   ); | ||||
|                                 }, | ||||
|                               ), | ||||
|                               SizedBox(height: 8.h), | ||||
|                             ], | ||||
|                           ), | ||||
|                         ), | ||||
|                       ), | ||||
|                       SizedBox(height: 16.h), | ||||
|                       Container( | ||||
|                         decoration: RoundedRectangleBorder().toSmoothCornerDecoration( | ||||
|                           color: AppColors.whiteColor, | ||||
|                           borderRadius: 20.h, | ||||
|                           hasShadow: false, | ||||
|                           side: BorderSide(color: AppColors.ratingColorYellow, width: 3.h), | ||||
|                         ), | ||||
|                         child: Padding( | ||||
|                           padding: EdgeInsets.all(16.h), | ||||
|                           child: Column( | ||||
|                             crossAxisAlignment: CrossAxisAlignment.start, | ||||
|                             children: [ | ||||
|                               Row( | ||||
|                                 mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||
|                                 children: [ | ||||
|                                   AppCustomChipWidget( | ||||
|                                     labelText: immediateLiveCareViewModel.patientLiveCareHistoryList[0].stringCallStatus, | ||||
|                                     backgroundColor: AppColors.warningColorYellow.withValues(alpha: 0.20), | ||||
|                                     textColor: AppColors.alertColor, | ||||
|                                   ), | ||||
|                                   Utils.buildSvgWithAssets(icon: AppAssets.waiting_icon, width: 24.h, height: 24.h), | ||||
|                                   // Lottie.asset(AppAnimations.pending_loading_animation, repeat: true, reverse: false, frameRate: FrameRate(60), width: 80.h, height: 80.h, fit: BoxFit.cover), | ||||
|                                 ], | ||||
|                               ), | ||||
|                               SizedBox(height: 8.h), | ||||
|                               "Hala ${appState.getAuthenticatedUser()!.firstName}!!!".needTranslation.toText16(isBold: true), | ||||
|                               SizedBox(height: 8.h), | ||||
|                               AppCustomChipWidget( | ||||
|                                   icon: AppAssets.appointment_calendar_icon, | ||||
|                                   labelText: DateUtil.formatDateToDate(DateUtil.convertStringToDate(immediateLiveCareViewModel.patientLiveCareHistoryList[0].arrivalTime), false)), | ||||
|                               SizedBox(height: 8.h), | ||||
|                               "Your turn is after ${immediateLiveCareViewModel.patientLiveCareHistoryList[0].patCount} patients.".toText16(isBold: true), | ||||
|                               SizedBox(height: 8.h), | ||||
|                             ], | ||||
|                           ), | ||||
|                         ), | ||||
|                       ) | ||||
|                     ], | ||||
|                   ), | ||||
|                 ), | ||||
|               ), | ||||
|             ), | ||||
|             Container( | ||||
|               decoration: RoundedRectangleBorder().toSmoothCornerDecoration( | ||||
|                 color: AppColors.whiteColor, | ||||
|                 borderRadius: 24.h, | ||||
|                 hasShadow: true, | ||||
|               ), | ||||
|               child: CustomButton( | ||||
|                 text: "Call LiveCare Support".needTranslation, | ||||
|                 onPressed: () async { | ||||
|                   launchUrl(Uri.parse("tel://" + "011 525 9553")); | ||||
|                 }, | ||||
|                 backgroundColor: AppColors.primaryRedColor, | ||||
|                 borderColor: AppColors.primaryRedColor, | ||||
|                 textColor: AppColors.whiteColor, | ||||
|                 fontSize: 16, | ||||
|                 fontWeight: FontWeight.w500, | ||||
|                 borderRadius: 12, | ||||
|                 padding: EdgeInsets.fromLTRB(10, 0, 10, 0), | ||||
|                 height: 50.h, | ||||
|                 icon: AppAssets.call_fill, | ||||
|                 iconColor: AppColors.whiteColor, | ||||
|                 iconSize: 21.h, | ||||
|               ).paddingSymmetrical(24.h, 24.h), | ||||
|             ), | ||||
|           ], | ||||
|         ); | ||||
|       }), | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   void startTimer() { | ||||
|     timer = Timer.periodic(const Duration(seconds: 1), (_) => addTime()); | ||||
|     setState(() {}); | ||||
|   } | ||||
| 
 | ||||
|   void addTime() { | ||||
|     final seconds = durationNotifier.value.inSeconds - 1; | ||||
|     if (seconds < 0) { | ||||
|       timer?.cancel(); | ||||
|       // Handle end of timer here | ||||
|       // showEndMessage(); | ||||
|     } else { | ||||
|       durationNotifier.value = Duration(seconds: seconds); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   Future<bool> _onWillPop() async { | ||||
|     timer?.cancel(); | ||||
|     Navigator.of(context).pop(); | ||||
|     return true; | ||||
|   } | ||||
| 
 | ||||
|   Widget buildTime(Duration duration) { | ||||
|     String twoDigits(int n) => n.toString().padLeft(2, '0'); | ||||
|     final hours = twoDigits(duration.inHours); | ||||
|     final minutes = twoDigits(duration.inMinutes.remainder(60)); | ||||
|     final seconds = twoDigits(duration.inSeconds.remainder(60)); | ||||
| 
 | ||||
|     return Row( | ||||
|       mainAxisAlignment: MainAxisAlignment.center, | ||||
|       children: [ | ||||
|         buildTimeColumn(hours, "Hours".needTranslation), | ||||
|         buildTimeColumn(minutes, "Mins".needTranslation), | ||||
|         buildTimeColumn(seconds, "Secs".needTranslation, isLast: true), | ||||
|       ], | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   Widget buildTimeColumn(String time, String label, {bool isLast = false}) { | ||||
|     return Column( | ||||
|       mainAxisAlignment: MainAxisAlignment.center, | ||||
|       children: [ | ||||
|         Row( | ||||
|           children: [ | ||||
|             buildDigit(time[0]), | ||||
|             buildDigit(time[1]), | ||||
|             if (!isLast) buildTimeSeparator(), | ||||
|           ], | ||||
|         ), | ||||
|         buildLabel(label), | ||||
|       ], | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   Widget buildDigit(String digit) { | ||||
|     return Container( | ||||
|       padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4), | ||||
|       // margin: const EdgeInsets.symmetric(horizontal: 2), | ||||
|       decoration: BoxDecoration( | ||||
|         color: Colors.white, | ||||
|         borderRadius: BorderRadius.circular(8), | ||||
|       ), | ||||
|       child: ClipRect( | ||||
|         child: AnimatedSwitcher( | ||||
|           duration: const Duration(milliseconds: 600), | ||||
|           switchInCurve: Curves.easeOutExpo, | ||||
|           switchOutCurve: Curves.easeInExpo, | ||||
|           transitionBuilder: (Widget child, Animation<double> animation) { | ||||
|             return Stack( | ||||
|               children: <Widget>[ | ||||
|                 SlideTransition( | ||||
|                   position: Tween<Offset>( | ||||
|                     begin: const Offset(0, -1), | ||||
|                     end: const Offset(0, 1), | ||||
|                   ).animate(CurvedAnimation( | ||||
|                     parent: animation, | ||||
|                     curve: Curves.easeOutCubic, | ||||
|                   )), | ||||
|                   child: FadeTransition( | ||||
|                     opacity: animation, | ||||
|                     child: child, | ||||
|                   ), | ||||
|                 ), | ||||
|                 SlideTransition( | ||||
|                   position: Tween<Offset>( | ||||
|                     begin: const Offset(0, -1), | ||||
|                     end: const Offset(0, 0), | ||||
|                   ).animate(CurvedAnimation( | ||||
|                     parent: animation, | ||||
|                     curve: Curves.bounceIn, | ||||
|                   )), | ||||
|                   child: FadeTransition( | ||||
|                     opacity: animation, | ||||
|                     child: child, | ||||
|                   ), | ||||
|                 ), | ||||
|               ], | ||||
|             ); | ||||
|           }, | ||||
|           child: Text( | ||||
|             digit, | ||||
|             key: ValueKey<String>(digit), | ||||
|             style: TextStyle( | ||||
|               fontWeight: FontWeight.bold, | ||||
|               color: Colors.black, | ||||
|               fontSize: 20.fSize, | ||||
|             ), | ||||
|           ), | ||||
|         ), | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   Widget buildLabel(String label) { | ||||
|     return label.toText14(isBold: true); | ||||
|   } | ||||
| 
 | ||||
|   Widget buildTimeSeparator() { | ||||
|     return const Padding( | ||||
|       padding: EdgeInsets.symmetric(horizontal: 2.0), | ||||
|       child: Text( | ||||
|         ":", | ||||
|         style: TextStyle( | ||||
|           color: Colors.black, | ||||
|           fontSize: 20, | ||||
|         ), | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,173 @@ | ||||
| import 'dart:async'; | ||||
| 
 | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:flutter_staggered_animations/flutter_staggered_animations.dart'; | ||||
| import 'package:hmg_patient_app_new/core/app_state.dart'; | ||||
| import 'package:hmg_patient_app_new/core/dependencies.dart'; | ||||
| import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; | ||||
| import 'package:hmg_patient_app_new/core/utils/utils.dart'; | ||||
| import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; | ||||
| import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; | ||||
| import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_view_model.dart'; | ||||
| import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/get_clinic_list_response_model.dart'; | ||||
| import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/get_livecare_clinics_response_model.dart'; | ||||
| import 'package:hmg_patient_app_new/features/immediate_livecare/models/resp_models/get_livecare_immediate_clinics_response_model.dart'; | ||||
| import 'package:hmg_patient_app_new/features/immediate_livecare/immediate_livecare_view_model.dart'; | ||||
| import 'package:hmg_patient_app_new/presentation/book_appointment/livecare/immediate_livecare_payment_details.dart'; | ||||
| import 'package:hmg_patient_app_new/presentation/book_appointment/livecare/widgets/select_livecare_call_type.dart'; | ||||
| import 'package:hmg_patient_app_new/presentation/book_appointment/widgets/clinic_card.dart'; | ||||
| import 'package:hmg_patient_app_new/presentation/book_appointment/livecare/widgets/livecare_clinic_card.dart'; | ||||
| import 'package:hmg_patient_app_new/theme/colors.dart'; | ||||
| import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart'; | ||||
| import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart'; | ||||
| import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart'; | ||||
| import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart'; | ||||
| import 'package:provider/provider.dart'; | ||||
| 
 | ||||
| class SelectImmediateLiveCareClinicPage extends StatefulWidget { | ||||
|   const SelectImmediateLiveCareClinicPage({super.key}); | ||||
| 
 | ||||
|   @override | ||||
|   State<SelectImmediateLiveCareClinicPage> createState() => _SelectImmediateLiveCareClinicPageState(); | ||||
| } | ||||
| 
 | ||||
| class _SelectImmediateLiveCareClinicPageState extends State<SelectImmediateLiveCareClinicPage> { | ||||
|   TextEditingController searchEditingController = TextEditingController(); | ||||
|   FocusNode textFocusNode = FocusNode(); | ||||
|   late AppState appState; | ||||
|   late ImmediateLiveCareViewModel immediateLiveCareViewModel; | ||||
| 
 | ||||
|   @override | ||||
|   void initState() { | ||||
|     scheduleMicrotask(() { | ||||
|       immediateLiveCareViewModel.getLiveCareImmediateClinicsList(); | ||||
|       immediateLiveCareViewModel.setLiveCareSelectedCallType(0); | ||||
|     }); | ||||
|     super.initState(); | ||||
|   } | ||||
| 
 | ||||
|   @override | ||||
|   void dispose() { | ||||
|     textFocusNode.dispose(); | ||||
|     super.dispose(); | ||||
|   } | ||||
| 
 | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     immediateLiveCareViewModel = Provider.of<ImmediateLiveCareViewModel>(context, listen: false); | ||||
|     appState = getIt.get<AppState>(); | ||||
|     return Scaffold( | ||||
|       backgroundColor: AppColors.bgScaffoldColor, | ||||
|       body: CollapsingListView( | ||||
|         title: "Select LiveCare Clinic".needTranslation, | ||||
|         child: SingleChildScrollView( | ||||
|           child: Padding( | ||||
|             padding: EdgeInsets.all(24.h), | ||||
|             child: Consumer<ImmediateLiveCareViewModel>(builder: (context, immediateLiveCareVM, child) { | ||||
|               return Column( | ||||
|                 children: [ | ||||
|                   // SizedBox(height: 16.h), | ||||
|                   // TextInputWidget( | ||||
|                   //   labelText: LocaleKeys.search.tr(context: context), | ||||
|                   //   hintText: LocaleKeys.clinicName.tr(context: context), | ||||
|                   //   controller: searchEditingController, | ||||
|                   //   isEnable: true, | ||||
|                   //   prefix: null, | ||||
|                   //   autoFocus: false, | ||||
|                   //   isBorderAllowed: false, | ||||
|                   //   keyboardType: TextInputType.text, | ||||
|                   //   focusNode: textFocusNode, | ||||
|                   //   suffix: searchEditingController.text.isNotEmpty | ||||
|                   //       ? GestureDetector( | ||||
|                   //           onTap: () { | ||||
|                   //             searchEditingController.clear(); | ||||
|                   //             bookAppointmentsViewModel.filterClinics(""); | ||||
|                   //             textFocusNode.unfocus(); | ||||
|                   //           }, | ||||
|                   //           child: Utils.buildSvgWithAssets(icon: AppAssets.close_bottom_sheet_icon, width: 20.h, height: 20.h, fit: BoxFit.scaleDown), | ||||
|                   //         ) | ||||
|                   //       : null, | ||||
|                   //   onChange: (value) { | ||||
|                   //     bookAppointmentsViewModel.filterClinics(value!); | ||||
|                   //   }, | ||||
|                   //   padding: EdgeInsets.symmetric( | ||||
|                   //     vertical: ResponsiveExtension(10).h, | ||||
|                   //     horizontal: ResponsiveExtension(15).h, | ||||
|                   //   ), | ||||
|                   // ), | ||||
|                   ListView.separated( | ||||
|                     padding: EdgeInsets.only(top: 16.h), | ||||
|                     shrinkWrap: true, | ||||
|                     physics: NeverScrollableScrollPhysics(), | ||||
|                     itemCount: immediateLiveCareVM.isImmediateLiveCareClinicsLoading ? 5 : immediateLiveCareVM.immediateLiveCareClinicsList.length, | ||||
|                     itemBuilder: (context, index) { | ||||
|                       return immediateLiveCareVM.isImmediateLiveCareClinicsLoading | ||||
|                           ? ClinicCard( | ||||
|                               bookAppointmentsVM: getIt.get<BookAppointmentsViewModel>(), | ||||
|                               liveCareClinicsResponseModel: GetLiveCareClinicsResponseModel(), | ||||
|                               clinicsListResponseModel: GetClinicsListResponseModel(), | ||||
|                               isLoading: immediateLiveCareVM.isImmediateLiveCareClinicsLoading, | ||||
|                             ) | ||||
|                           : AnimationConfiguration.staggeredList( | ||||
|                               position: index, | ||||
|                               duration: const Duration(milliseconds: 500), | ||||
|                               child: SlideAnimation( | ||||
|                                 verticalOffset: 100.0, | ||||
|                                 child: FadeInAnimation( | ||||
|                                   child: AnimatedContainer( | ||||
|                                     duration: Duration(milliseconds: 300), | ||||
|                                     curve: Curves.easeInOut, | ||||
|                                     decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: true), | ||||
|                                     child: LiveCareClinicCard( | ||||
|                                       immediateLiveCareViewModel: immediateLiveCareVM, | ||||
|                                       liveCareClinicListResponseModel: immediateLiveCareVM.immediateLiveCareClinicsList[index], | ||||
|                                       isLoading: immediateLiveCareVM.isImmediateLiveCareClinicsLoading, | ||||
|                                     ).onPress(() { | ||||
|                                       onImmediateLiveCareClinicSelected(immediateLiveCareVM.immediateLiveCareClinicsList[index]); | ||||
|                                     }), | ||||
|                                   ), | ||||
|                                 ), | ||||
|                               ), | ||||
|                             ); | ||||
|                     }, | ||||
|                     separatorBuilder: (BuildContext cxt, int index) => SizedBox(height: 16.h), | ||||
|                   ), | ||||
|                 ], | ||||
|               ); | ||||
|             }), | ||||
|           ), | ||||
|         ), | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   onImmediateLiveCareClinicSelected(GetLiveCareClinicListResponseModel liveCareClinic) { | ||||
|     //TODO: add implementation to show clinic schedule | ||||
|     if (liveCareClinic.isOnline == 1) { | ||||
|       showCommonBottomSheetWithoutHeight(context, child: SelectLiveCareCallType(immediateLiveCareViewModel: immediateLiveCareViewModel), callBackFunc: () async { | ||||
|         if (immediateLiveCareViewModel.liveCareSelectedCallType != 0) { | ||||
|           immediateLiveCareViewModel.setImmediateLiveCareSelectedClinic(liveCareClinic); | ||||
|           LoaderBottomSheet.showLoader(loadingText: "Fetching fees, Please wait...".needTranslation); | ||||
|           await immediateLiveCareViewModel.getLiveCareImmediateAppointmentFees(onSuccess: (val) { | ||||
|             LoaderBottomSheet.hideLoader(); | ||||
|             Navigator.of(context).push( | ||||
|               CustomPageRoute( | ||||
|                 page: ImmediateLiveCarePaymentDetails(), | ||||
|               ), | ||||
|             ); | ||||
|           }, onError: (err) { | ||||
|             LoaderBottomSheet.hideLoader(); | ||||
|           }); | ||||
|         } | ||||
|       }, title: "Select LiveCare call type".needTranslation, isCloseButtonVisible: true, isFullScreen: false); | ||||
|     } else { | ||||
|       showCommonBottomSheetWithoutHeight(context, | ||||
|           child: Utils.getErrorWidget( | ||||
|               loadingText: "The selected clinic is only available between ${liveCareClinic.shiftTimings!.first.startTime} & ${liveCareClinic.shiftTimings!.first.endTime} hours.".needTranslation), | ||||
|           callBackFunc: () {}, | ||||
|           title: "", | ||||
|           isCloseButtonVisible: true, | ||||
|           isFullScreen: false); | ||||
|     } | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,67 @@ | ||||
| import 'package:easy_localization/easy_localization.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:hmg_patient_app_new/core/app_assets.dart'; | ||||
| import 'package:hmg_patient_app_new/core/app_state.dart'; | ||||
| import 'package:hmg_patient_app_new/core/dependencies.dart'; | ||||
| import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; | ||||
| import 'package:hmg_patient_app_new/core/utils/utils.dart'; | ||||
| import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; | ||||
| import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; | ||||
| import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_view_model.dart'; | ||||
| import 'package:hmg_patient_app_new/features/immediate_livecare/models/resp_models/get_livecare_immediate_clinics_response_model.dart'; | ||||
| import 'package:hmg_patient_app_new/features/immediate_livecare/immediate_livecare_view_model.dart'; | ||||
| import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; | ||||
| import 'package:hmg_patient_app_new/theme/colors.dart'; | ||||
| 
 | ||||
| class LiveCareClinicCard extends StatelessWidget { | ||||
|   LiveCareClinicCard({super.key, required this.liveCareClinicListResponseModel, required this.isLoading, required this.immediateLiveCareViewModel}); | ||||
| 
 | ||||
|   GetLiveCareClinicListResponseModel liveCareClinicListResponseModel; | ||||
|   bool isLoading; | ||||
|   ImmediateLiveCareViewModel immediateLiveCareViewModel; | ||||
| 
 | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     AppState appState = getIt.get<AppState>(); | ||||
|     return Container( | ||||
|       padding: EdgeInsets.all(16.h), | ||||
|       decoration: RoundedRectangleBorder().toSmoothCornerDecoration( | ||||
|         color: AppColors.whiteColor, | ||||
|         borderRadius: 24.h, | ||||
|         hasShadow: false, | ||||
|       ), | ||||
|       child: Column( | ||||
|         children: [ | ||||
|           Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ | ||||
|             Utils.buildSvgWithAssets(icon: AppAssets.generic_clinic_icon, width: 24.h, height: 24.h, fit: BoxFit.contain).toShimmer2(isShow: isLoading), | ||||
|             Column( | ||||
|               children: [ | ||||
|                 Utils.buildSvgWithAssets( | ||||
|                         icon: AppAssets.livecare_online_icon, | ||||
|                         width: 16.h, | ||||
|                         height: 16.h, | ||||
|                         fit: BoxFit.contain, | ||||
|                         iconColor: liveCareClinicListResponseModel.isOnline == 1 ? AppColors.successColor : AppColors.primaryRedColor) | ||||
|                     .toShimmer2(isShow: isLoading), | ||||
|                 SizedBox(height: 4.h), | ||||
|                 liveCareClinicListResponseModel.isOnline == 1 | ||||
|                     ? LocaleKeys.online.tr(context: context).toText10(isBold: true, color: AppColors.successColor).toShimmer2(isShow: isLoading) | ||||
|                     : "Offline".toText10(isBold: true, color: AppColors.primaryRedColor).toShimmer2(isShow: isLoading), | ||||
|               ], | ||||
|             ), | ||||
|           ]), | ||||
|           SizedBox(height: 8.h), | ||||
|           Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ | ||||
|             Expanded( | ||||
|                 child: (isLoading ? "Cardiology" : (appState.isArabic() ? liveCareClinicListResponseModel.serviceNameN : liveCareClinicListResponseModel.serviceName))! | ||||
|                     .toText16(isBold: true) | ||||
|                     .toShimmer2(isShow: isLoading)), | ||||
|             Transform.flip( | ||||
|                 flipX: appState.isArabic() ? true : false, | ||||
|                 child: Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_icon, width: 40.h, height: 40.h, fit: BoxFit.contain, iconColor: AppColors.textColor).toShimmer2(isShow: isLoading)), | ||||
|           ]), | ||||
|         ], | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,66 @@ | ||||
| import 'package:flutter/cupertino.dart'; | ||||
| import 'package:hmg_patient_app_new/core/app_assets.dart'; | ||||
| import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; | ||||
| import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; | ||||
| import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; | ||||
| import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_view_model.dart'; | ||||
| import 'package:hmg_patient_app_new/features/immediate_livecare/immediate_livecare_view_model.dart'; | ||||
| import 'package:hmg_patient_app_new/presentation/medical_file/widgets/medical_file_card.dart'; | ||||
| import 'package:hmg_patient_app_new/theme/colors.dart'; | ||||
| 
 | ||||
| class SelectLiveCareCallType extends StatelessWidget { | ||||
|   SelectLiveCareCallType({super.key, required this.immediateLiveCareViewModel}); | ||||
| 
 | ||||
|   ImmediateLiveCareViewModel immediateLiveCareViewModel; | ||||
| 
 | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     //TODO: Replace with actual icons | ||||
|     return GridView( | ||||
|       gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( | ||||
|         crossAxisCount: 3, | ||||
|         crossAxisSpacing: 16, | ||||
|         mainAxisSpacing: 16, | ||||
|         mainAxisExtent: 130, | ||||
|       ), | ||||
|       physics: NeverScrollableScrollPhysics(), | ||||
|       padding: EdgeInsets.zero, | ||||
|       shrinkWrap: true, | ||||
|       children: [ | ||||
|         MedicalFileCard( | ||||
|           label: "Video     Call".needTranslation, | ||||
|           textColor: AppColors.blackColor, | ||||
|           backgroundColor: AppColors.whiteColor, | ||||
|           svgIcon: AppAssets.eye_result_icon, | ||||
|           isLargeText: true, | ||||
|           iconSize: 36.h, | ||||
|         ).onPress(() { | ||||
|           Navigator.of(context).pop(); | ||||
|           immediateLiveCareViewModel.setLiveCareSelectedCallType(1); | ||||
|         }), | ||||
|         MedicalFileCard( | ||||
|           label: "Audio        Call".needTranslation, | ||||
|           textColor: AppColors.blackColor, | ||||
|           backgroundColor: AppColors.whiteColor, | ||||
|           svgIcon: AppAssets.allergy_info_icon, | ||||
|           isLargeText: true, | ||||
|           iconSize: 36.h, | ||||
|         ).onPress(() { | ||||
|           Navigator.of(context).pop(); | ||||
|           immediateLiveCareViewModel.setLiveCareSelectedCallType(2); | ||||
|         }), | ||||
|         MedicalFileCard( | ||||
|           label: "Phone       Call".needTranslation, | ||||
|           textColor: AppColors.blackColor, | ||||
|           backgroundColor: AppColors.whiteColor, | ||||
|           svgIcon: AppAssets.vaccine_info_icon, | ||||
|           isLargeText: true, | ||||
|           iconSize: 36.h, | ||||
|         ).onPress(() { | ||||
|           Navigator.of(context).pop(); | ||||
|           immediateLiveCareViewModel.setLiveCareSelectedCallType(3); | ||||
|         }), | ||||
|       ], | ||||
|     ); | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,38 @@ | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:hmg_patient_app_new/core/app_assets.dart'; | ||||
| import 'package:hmg_patient_app_new/core/app_state.dart'; | ||||
| import 'package:hmg_patient_app_new/core/dependencies.dart'; | ||||
| import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; | ||||
| import 'package:hmg_patient_app_new/core/utils/utils.dart'; | ||||
| import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; | ||||
| import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; | ||||
| import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_view_model.dart'; | ||||
| import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/dental_chief_complaints_response_model.dart'; | ||||
| import 'package:hmg_patient_app_new/theme/colors.dart'; | ||||
| 
 | ||||
| class ChiefComplaintCard extends StatelessWidget { | ||||
|   ChiefComplaintCard({super.key, required this.isLoading, required this.bookAppointmentsVM, required this.dentalChiefComplaintsListResponseModel}); | ||||
| 
 | ||||
|   bool isLoading; | ||||
|   BookAppointmentsViewModel bookAppointmentsVM; | ||||
|   DentalChiefComplaintsListResponseModel dentalChiefComplaintsListResponseModel; | ||||
| 
 | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     AppState appState = getIt.get<AppState>(); | ||||
|     return Container( | ||||
|       padding: EdgeInsets.all(16.h), | ||||
|       decoration: RoundedRectangleBorder().toSmoothCornerDecoration( | ||||
|         color: AppColors.whiteColor, | ||||
|         borderRadius: 24.h, | ||||
|         hasShadow: false, | ||||
|       ), | ||||
|       child: Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ | ||||
|         Expanded(child: (isLoading ? "Cardiology" : dentalChiefComplaintsListResponseModel.name)!.toText16(isBold: true).toShimmer2(isShow: isLoading)), | ||||
|         Transform.flip( | ||||
|             flipX: appState.isArabic(), | ||||
|             child: Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_icon, width: 40.h, height: 40.h, fit: BoxFit.contain, iconColor: AppColors.textColor).toShimmer2(isShow: isLoading)), | ||||
|       ]), | ||||
|     ); | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,69 @@ | ||||
| import 'package:easy_localization/easy_localization.dart' | ||||
|     show tr, StringTranslateExtension; | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:flutter_widget_from_html/flutter_widget_from_html.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/lab/lab_view_model.dart'; | ||||
| import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; | ||||
| import 'package:hmg_patient_app_new/presentation/lab/lab_result_item_view.dart'; | ||||
| import 'package:hmg_patient_app_new/presentation/lab/lab_result_via_hospital/LabResultList.dart'; | ||||
| import 'package:hmg_patient_app_new/presentation/lab/lab_result_via_hospital/lab_order_specialResult.dart'; | ||||
| import 'package:hmg_patient_app_new/theme/colors.dart'; | ||||
| import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart'; | ||||
| import 'package:provider/provider.dart'; | ||||
| 
 | ||||
| class LabResultByHospitals extends StatelessWidget { | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return CollapsingListView( | ||||
|         title: LocaleKeys.labResults.tr(), | ||||
|         child: SingleChildScrollView( | ||||
|           child: Column( | ||||
|             spacing: 8.h, | ||||
|             crossAxisAlignment: CrossAxisAlignment.start, | ||||
|             children: [ | ||||
|               Selector<LabViewModel, bool>( | ||||
|                 selector: (_, model) => model.isLabResultByHospitalLoading, | ||||
|                 builder: (_, isLoading, __) { | ||||
|                   if (isLoading) { | ||||
|                     return Column( | ||||
|                       children: [ | ||||
|                         LabResultItemView( | ||||
|                           onTap: () {}, | ||||
|                           labOrder: null, | ||||
|                           index: 0, | ||||
|                           isLoading: true, | ||||
|                         ), | ||||
|                         LabResultItemView( | ||||
|                           onTap: () {}, | ||||
|                           labOrder: null, | ||||
|                           index: 0, | ||||
|                           isLoading: true, | ||||
|                         ), | ||||
|                         LabResultItemView( | ||||
|                           onTap: () {}, | ||||
|                           labOrder: null, | ||||
|                           index: 0, | ||||
|                           isLoading: true, | ||||
|                         ), | ||||
|                         LabResultItemView( | ||||
|                           onTap: () {}, | ||||
|                           labOrder: null, | ||||
|                           index: 0, | ||||
|                           isLoading: true, | ||||
|                         ), | ||||
|                       ], | ||||
|                     ); | ||||
|                   } else { | ||||
|                     return LabResultList(); | ||||
|                   } | ||||
|                 }, | ||||
|               ), | ||||
|               LabOrderSpecialResult() | ||||
|             ], | ||||
|           ).paddingAll(24.h), | ||||
|         )); | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,40 @@ | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:hmg_patient_app_new/core/utils/utils.dart'; | ||||
| import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; | ||||
| import 'package:hmg_patient_app_new/features/lab/lab_view_model.dart'; | ||||
| import 'package:hmg_patient_app_new/features/lab/models/resp_models/lab_result.dart'; | ||||
| import 'package:hmg_patient_app_new/presentation/lab/lab_result_via_hospital/lab_order_result_item.dart'; | ||||
| import 'package:provider/provider.dart' show Selector, Provider, ReadContext; | ||||
| 
 | ||||
| class LabResultList extends StatelessWidget { | ||||
|   late LabViewModel model; | ||||
| 
 | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     model = Provider.of<LabViewModel>(context); | ||||
|     return Selector<LabViewModel, List<LabResult>>( | ||||
|       selector: (_, model) => model.mainLabResultsByHospitals, | ||||
|       builder: (__, list, ___) { | ||||
|         if (list.isEmpty && context.read<LabViewModel>().labSpecialResult.isEmpty) { | ||||
|           return Utils.getNoDataWidget(context, | ||||
|               noDataText: "You don't have any lab results yet." | ||||
|                   .needTranslation); | ||||
|         } else { | ||||
|           return ListView.builder( | ||||
|               physics: NeverScrollableScrollPhysics(), | ||||
|               padding: EdgeInsets.zero, | ||||
|               shrinkWrap: true,itemCount: list.length,itemBuilder: (____, index) { | ||||
|             var labItem = list[index]; | ||||
|             return LabOrderResultItem(onTap: () { | ||||
|                       model.getPatientLabResult(model.currentlySelectedPatientOrder!, labItem.description ?? "", labItem.packageShortDescription!); | ||||
|                     }, | ||||
|                 tests: labItem, | ||||
|                 index: index, | ||||
|                 iconColor: model.getColor(labItem.calculatedResultFlag ?? "N"), | ||||
|                 severityText: model.getSeverityText(labItem.calculatedResultFlag ?? "N")); | ||||
|           }); | ||||
|         } | ||||
|       }, | ||||
|     ); | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,132 @@ | ||||
| import 'package:easy_localization/easy_localization.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:hmg_patient_app_new/core/app_assets.dart'; | ||||
| import 'package:hmg_patient_app_new/core/app_export.dart'; | ||||
| import 'package:hmg_patient_app_new/core/enums.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/extensions/string_extensions.dart'; | ||||
| import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; | ||||
| import 'package:hmg_patient_app_new/features/lab/lab_view_model.dart' show LabViewModel; | ||||
| import 'package:hmg_patient_app_new/features/lab/models/resp_models/lab_result.dart'; | ||||
| import 'package:hmg_patient_app_new/features/lab/models/resp_models/patient_lab_orders_response_model.dart'; | ||||
| import 'package:hmg_patient_app_new/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/chip/custom_chip_widget.dart'; | ||||
| import 'package:provider/provider.dart'; | ||||
| 
 | ||||
| class LabOrderResultItem extends StatelessWidget { | ||||
|   final VoidCallback onTap; | ||||
|   final int index; | ||||
|   final LabResult? tests; | ||||
|   final String severityText; | ||||
|   final bool isLoading; | ||||
|   final bool isExpanded; | ||||
|   final Color iconColor; | ||||
| 
 | ||||
|   const LabOrderResultItem({super.key, required this.onTap, this.tests, required this.index, this.isLoading = false, this.isExpanded = false,required this.iconColor, required this.severityText}); | ||||
| 
 | ||||
|   @override | ||||
|   build(BuildContext context) { | ||||
|     return 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: Container( | ||||
|           key: ValueKey<int>(index), | ||||
|           padding: EdgeInsets.symmetric(horizontal: 16.h, vertical: 16.h), | ||||
|           child: Column( | ||||
|             crossAxisAlignment: CrossAxisAlignment.start, | ||||
|             children: [ | ||||
|               // ...labOrder!.testDetails!.map((detail) { | ||||
|               Padding( | ||||
|                 padding: EdgeInsets.only(bottom: 8.h), | ||||
|                 child: '${tests!.description}'.toText14(weight: FontWeight.w500), | ||||
|               ), | ||||
|               '${tests!.packageShortDescription}'.toText12(fontWeight: FontWeight.w500, color: AppColors.textColorLight), | ||||
|               SizedBox(height: 12.h), | ||||
|               Row( | ||||
|                 mainAxisSize: MainAxisSize.max, | ||||
|                 children: [ | ||||
|                   Flexible( | ||||
|                     child: Text( | ||||
|                       tests?.resultValue ?? "", | ||||
|                       style: TextStyle( | ||||
|                         fontSize: 24.fSize, | ||||
|                         fontWeight: FontWeight.w600, | ||||
|                         fontFamily: 'Poppins', | ||||
|                         color: context.read<LabViewModel>().getColor( | ||||
|                           tests?.calculatedResultFlag ?? "", | ||||
|                         ), | ||||
|                         letterSpacing: -2, | ||||
|                       ), | ||||
|                       overflow: TextOverflow.ellipsis, // prevent overflow | ||||
|                       maxLines: 1, | ||||
|                       softWrap: false, | ||||
|                     ), | ||||
|                   ), | ||||
|                   SizedBox(width: 4.h,), | ||||
|                   Expanded( | ||||
|                     flex: 2, | ||||
|                     child: Visibility( | ||||
|                       visible: tests?.referanceRange != null, | ||||
|                       child: Text( | ||||
|                         "(Reference range ${tests?.referanceRange})".needTranslation, | ||||
|                         style: TextStyle( | ||||
|                           fontSize: 12.fSize, | ||||
|                           fontWeight: FontWeight.w500, | ||||
|                           fontFamily: 'Poppins', | ||||
|                           color: AppColors.greyTextColor, | ||||
|                         ), | ||||
|                         // overflow: TextOverflow.ellipsis, | ||||
|                         // maxLines: 2, | ||||
|                         softWrap: true, | ||||
|                       ), | ||||
|                     ), | ||||
|                   ), | ||||
|                 ], | ||||
|               ), | ||||
|               SizedBox(height: 12.h), | ||||
|               Row( | ||||
|                 mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||
|                 children: [ | ||||
|                   Column( | ||||
|                     spacing: 6.h, | ||||
|                     crossAxisAlignment: CrossAxisAlignment.start, | ||||
|                     children: [ | ||||
|                       severityText.tr().toText10(weight: FontWeight.w500, color: AppColors.greyTextColor), | ||||
|                       Utils.buildSvgWithAssets( | ||||
|                         icon: AppAssets.lab_result_indicator, | ||||
|                         width: 21, | ||||
|                         height: 23, | ||||
|                         iconColor: iconColor | ||||
|                       ), | ||||
|                     ], | ||||
|                   ), | ||||
|                   CustomButton( | ||||
|                     icon: AppAssets.view_report_icon, | ||||
|                     iconColor: AppColors.primaryRedColor, | ||||
|                     iconSize: 16.h, | ||||
|                     text: LocaleKeys.viewReport.tr(context: context), | ||||
|                     onPressed: () { | ||||
|                         onTap(); | ||||
|                     }, | ||||
|                     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, | ||||
|                   ), | ||||
|                 ], | ||||
|               ), | ||||
|             ], | ||||
|           ), | ||||
|         )); | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,120 @@ | ||||
| import 'package:easy_localization/easy_localization.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:flutter_widget_from_html/flutter_widget_from_html.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/lab/lab_view_model.dart'; | ||||
| import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; | ||||
| import 'package:hmg_patient_app_new/theme/colors.dart'; | ||||
| import 'package:provider/provider.dart'; | ||||
| 
 | ||||
| class LabOrderSpecialResult extends StatelessWidget { | ||||
|   const LabOrderSpecialResult({super.key}); | ||||
| 
 | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return Selector<LabViewModel, bool>( | ||||
|         selector: (_, model) => model.isSpecialResultsLoading, | ||||
|         builder: (_, isLoading, __) { | ||||
|           return Selector<LabViewModel, String>( | ||||
|               selector: (_, model) => model.labSpecialResult, | ||||
|               builder: (_, data, __) { | ||||
|                 if(isLoading){ | ||||
|                   return Container( | ||||
|                       margin: EdgeInsets.symmetric(vertical: 8.h), | ||||
|                       padding: EdgeInsets.symmetric( | ||||
|                           horizontal: 16.h, vertical: 16.h), | ||||
|                       width: MediaQuery.sizeOf(context).width - 24, | ||||
|                       decoration: RoundedRectangleBorder() | ||||
|                           .toSmoothCornerDecoration( | ||||
|                           color: AppColors.whiteColor, | ||||
|                           borderRadius: 20.h, | ||||
|                           hasShadow: true), | ||||
|                       child:Column( | ||||
|                         crossAxisAlignment: CrossAxisAlignment.start, | ||||
|                         spacing: 12.h, | ||||
|                         children: [ | ||||
|                           "loading".toText14().toShimmer2(isShow: isLoading), | ||||
|                           "loading".toText14().toShimmer2(isShow: isLoading), | ||||
|                         ], | ||||
|                       ) | ||||
|                   ); | ||||
|                 } | ||||
|                 if(data.isNotEmpty ) { | ||||
|                   return 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: Container( | ||||
|                         padding: EdgeInsets.symmetric( | ||||
|                             horizontal: 16.h, vertical: 16.h), | ||||
|                         width: MediaQuery.sizeOf(context).width - 24, | ||||
|                         child: Column( | ||||
|                           spacing: 8.h, | ||||
|                           crossAxisAlignment: CrossAxisAlignment.start, | ||||
|                           children: [ | ||||
|                             // ...labOrder!.testDetails!.map((detail) { | ||||
|                             LocaleKeys.specialResult | ||||
|                                 .tr() | ||||
|                                 .toText14(weight: FontWeight.w500) | ||||
|                                 .toShimmer2(isShow: isLoading), | ||||
| 
 | ||||
|                             data.isEmpty | ||||
|                                 ? LocaleKeys.noSpecialResult | ||||
|                                     .tr() | ||||
|                                     .toText12( | ||||
|                                         fontWeight: FontWeight.w500, | ||||
|                                         color: AppColors.textColorLight) | ||||
|                                     .toShimmer2(isShow: isLoading) | ||||
|                                 : HtmlWidget(data).toShimmer2(isShow: isLoading) | ||||
| 
 | ||||
|                             // | ||||
|                           ], | ||||
|                         ), | ||||
|                       )); | ||||
|                 } return SizedBox.shrink(); | ||||
|               }); | ||||
| 
 | ||||
|         }); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| /*Text( | ||||
|                 "Special Results", | ||||
|                 style: TextStyle( | ||||
|                     fontSize: 18.fSize, | ||||
|                     fontWeight: FontWeight.w600, | ||||
|                     color: AppColors.blackColor), | ||||
|               ), | ||||
|               Container( | ||||
|                   decoration: RoundedRectangleBorder().toSmoothCornerDecoration( | ||||
|                     color: AppColors.whiteColor, | ||||
|                     borderRadius: 20.h, | ||||
|                   hasShadow: true | ||||
|                   ), | ||||
|                   padding: EdgeInsets.all(16.h), | ||||
|                   width: MediaQuery.sizeOf(context).width-24, | ||||
|                   child: Selector<LabViewModel, bool>( | ||||
|                       selector: (_, model) => | ||||
|                           model.isLabResultByHospitalLoading, | ||||
|                       builder: (_, isLoading, __) { | ||||
|                         return Selector<LabViewModel, String>( | ||||
|                             selector: (_, model) => model.labSpecialResult, | ||||
|                             builder: (_, data, __) { | ||||
|                               return (data.isEmpty) | ||||
|                                   ? Text("No result available".needTranslation, | ||||
|                                           style: TextStyle( | ||||
|                                               fontSize: 12.fSize, | ||||
|                                               fontWeight: FontWeight.w500, | ||||
|                                               color: AppColors.textColorLight)) | ||||
|                                       .toShimmer2(isShow: isLoading) | ||||
|                                   : HtmlWidget(data) | ||||
|                                       .toShimmer2(isShow: isLoading); | ||||
|                             }); | ||||
|                       }))*/ | ||||
| @ -1,94 +0,0 @@ | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:flutter_staggered_animations/flutter_staggered_animations.dart'; | ||||
| import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; | ||||
| import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; | ||||
| import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; | ||||
| import 'package:hmg_patient_app_new/features/medical_file/medical_file_view_model.dart'; | ||||
| import 'package:hmg_patient_app_new/features/medical_file/models/patient_medical_response_model.dart'; | ||||
| import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart'; | ||||
| import 'package:hmg_patient_app_new/presentation/medical_file/widgets/patient_medical_report_card.dart'; | ||||
| import 'package:hmg_patient_app_new/theme/colors.dart'; | ||||
| import 'package:hmg_patient_app_new/widgets/custom_tab_bar.dart'; | ||||
| import 'package:provider/provider.dart'; | ||||
| 
 | ||||
| class MedicalReportsPage extends StatefulWidget { | ||||
|   const MedicalReportsPage({super.key}); | ||||
| 
 | ||||
|   @override | ||||
|   State<MedicalReportsPage> createState() => _MedicalReportsPageState(); | ||||
| } | ||||
| 
 | ||||
| class _MedicalReportsPageState extends State<MedicalReportsPage> { | ||||
|   late MedicalFileViewModel medicalFileViewModel; | ||||
| 
 | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     medicalFileViewModel = Provider.of<MedicalFileViewModel>(context, listen: false); | ||||
|     return Scaffold( | ||||
|       backgroundColor: AppColors.bgScaffoldColor, | ||||
|       body: CollapsingListView( | ||||
|         title: "Medical Reports".needTranslation, | ||||
|         child: SingleChildScrollView( | ||||
|           child: Column( | ||||
|             children: [ | ||||
|               SizedBox(height: 16.h), | ||||
|               CustomTabBar( | ||||
|                 activeTextColor: Color(0xffED1C2B), | ||||
|                 activeBackgroundColor: Color(0xffED1C2B).withValues(alpha: .1), | ||||
|                 tabs: [ | ||||
|                   CustomTabBarModel(null, "Requested".needTranslation), | ||||
|                   CustomTabBarModel(null, "Ready".needTranslation), | ||||
|                   CustomTabBarModel(null, "Cancelled".needTranslation), | ||||
|                 ], | ||||
|                 onTabChange: (index) { | ||||
|                   medicalFileViewModel.onMedicalReportTabChange(index); | ||||
|                 }, | ||||
|               ).paddingSymmetrical(24.h, 0.h), | ||||
|               Consumer<MedicalFileViewModel>(builder: (context, medicalFileVM, child) { | ||||
|                 return ListView.separated( | ||||
|                   padding: EdgeInsets.only(top: 24.h), | ||||
|                   shrinkWrap: true, | ||||
|                   physics: NeverScrollableScrollPhysics(), | ||||
|                   itemCount: medicalFileViewModel.isPatientMedicalReportsListLoading ? 3 : medicalFileViewModel.patientMedicalReportList.length, | ||||
|                   // medicalFileViewModel.patientMedicalReportList.isNotEmpty | ||||
|                   //         ? medicalFileViewModel.patientMedicalReportList.length | ||||
|                   //         : 1, | ||||
|                   itemBuilder: (context, index) { | ||||
|                     return medicalFileViewModel.isPatientMedicalReportsListLoading | ||||
|                         ? PatientMedicalReportCard( | ||||
|                             patientMedicalReportResponseModel: PatientMedicalReportResponseModel(), | ||||
|                             medicalFileViewModel: medicalFileVM, | ||||
|                             isLoading: true, | ||||
|                           ).paddingSymmetrical(24.h, 0.h) | ||||
|                         : AnimationConfiguration.staggeredList( | ||||
|                             position: index, | ||||
|                             duration: const Duration(milliseconds: 500), | ||||
|                             child: SlideAnimation( | ||||
|                               verticalOffset: 100.0, | ||||
|                               child: FadeInAnimation( | ||||
|                                 child: AnimatedContainer( | ||||
|                                   duration: Duration(milliseconds: 300), | ||||
|                                   curve: Curves.easeInOut, | ||||
|                                   decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: true), | ||||
|                                   child: PatientMedicalReportCard( | ||||
|                                     patientMedicalReportResponseModel: medicalFileVM.patientMedicalReportList[index], | ||||
|                                     medicalFileViewModel: medicalFileVM, | ||||
| 
 | ||||
|                                     isLoading: false, | ||||
|                                   ), | ||||
|                                 ).paddingSymmetrical(24.h, 0.h), | ||||
|                               ), | ||||
|                             ), | ||||
|                           ); | ||||
|                   }, | ||||
|                   separatorBuilder: (BuildContext cxt, int index) => SizedBox(height: 16.h), | ||||
|                 ); | ||||
|               }), | ||||
|               SizedBox(height: 24.h), | ||||
|             ], | ||||
|           ), | ||||
|         ), | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
| } | ||||