Merge pull request 'haroon_dev' (#21) from haroon_dev into master

Reviewed-on: #21
pull/29/head
Haroon6138 2 months ago
commit a786d38d74

File diff suppressed because one or more lines are too long

@ -0,0 +1,6 @@
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="40" height="40" rx="8" fill="#EFEFF0"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M20.0002 20.25C18.2082 20.25 16.7502 18.792 16.7502 17C16.7502 15.208 18.2082 13.75 20.0002 13.75C21.7922 13.75 23.2502 15.208 23.2502 17C23.2502 18.792 21.7922 20.25 20.0002 20.25ZM20.0002 15.25C19.0352 15.25 18.2502 16.035 18.2502 17C18.2502 17.965 19.0352 18.75 20.0002 18.75C20.9652 18.75 21.7502 17.965 21.7502 17C21.7502 16.035 20.9652 15.25 20.0002 15.25Z" fill="#2E3039"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M18.2232 26.035C18.7032 26.496 19.3342 26.75 20.0002 26.75C20.6662 26.75 21.2972 26.496 21.7772 26.034C22.0112 25.809 22.2512 25.581 22.4942 25.351C25.4901 22.5091 29.2201 18.9708 27.1592 14.016C25.9742 11.165 23.0962 9.25 20.0002 9.25C16.9042 9.25 14.0272 11.165 12.8422 14.016C10.7882 18.953 14.4872 22.474 17.4592 25.303L17.4879 25.3304C17.7361 25.5671 17.9815 25.801 18.2232 26.035ZM14.2272 14.592C15.1822 12.294 17.5022 10.75 20.0002 10.75C22.4982 10.75 24.8192 12.294 25.7752 14.592C27.4392 18.594 24.4012 21.476 21.4622 24.263C21.4065 24.316 21.3508 24.3688 21.2954 24.4216C21.107 24.6005 20.9203 24.7779 20.7372 24.954C20.5382 25.145 20.2762 25.25 20.0002 25.25C19.7242 25.25 19.4622 25.144 19.2652 24.955C19.0132 24.711 18.7552 24.465 18.4932 24.216C15.5813 21.4441 12.5693 18.5768 14.2272 14.592Z" fill="#2E3039"/>
<path d="M22.7182 30.75L17.2821 30.75C14.0511 30.75 12.4292 30.75 11.6312 29.566C11.5542 29.451 11.4871 29.332 11.4321 29.212C10.8225 27.9067 11.8042 26.5074 13.1621 24.572L13.1642 24.569C13.4022 24.23 13.8701 24.148 14.2091 24.386C14.5481 24.624 14.6302 25.092 14.3922 25.431C13.3132 26.969 12.5662 28.096 12.7942 28.583C12.8182 28.635 12.8451 28.682 12.8761 28.729C13.2271 29.25 14.8401 29.25 17.2821 29.25L22.7182 29.25C25.1601 29.25 26.7732 29.25 27.1252 28.728C27.1562 28.682 27.1832 28.635 27.2042 28.588C27.4342 28.096 26.6872 26.969 25.6082 25.431C25.3702 25.092 25.4522 24.624 25.7912 24.386C26.1302 24.148 26.5981 24.23 26.8361 24.569C28.1951 26.506 29.1772 27.906 28.5662 29.217C28.5132 29.332 28.4472 29.451 28.3702 29.565C27.5712 30.75 25.9492 30.75 22.7182 30.75Z" fill="#2E3039"/>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

@ -0,0 +1,8 @@
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="40" height="40" rx="8" fill="#EFEFF0"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M17.0686 9.25H16.9572C15.3515 9.24998 14.0704 9.24997 13.0656 9.38481C12.0278 9.52407 11.1733 9.81941 10.4972 10.4943C9.82085 11.1694 9.5247 12.0229 9.38509 13.0594C9.24997 14.0626 9.24998 15.3416 9.25 16.9442L9.25 23.0558C9.24998 24.6583 9.24997 25.9374 9.38509 26.9406C9.5247 27.9771 9.82085 28.8306 10.4972 29.5057C11.1733 30.1806 12.0278 30.4759 13.0656 30.6152C14.0704 30.75 15.3515 30.75 16.9572 30.75H17.0686C18.6742 30.75 19.9554 30.75 20.9602 30.6152C21.9979 30.4759 22.8525 30.1806 23.5286 29.5057C24.4805 28.5556 24.6872 27.2403 24.7495 25.5273C24.7646 25.1133 24.4412 24.7655 24.0273 24.7505C23.6133 24.7354 23.2655 25.0588 23.2505 25.4727C23.1882 27.1846 22.9697 27.9442 22.4689 28.4441C22.1181 28.7943 21.6326 29.0115 20.7607 29.1285C19.8673 29.2484 18.687 29.25 17.0129 29.25C15.3388 29.25 14.1585 29.2484 13.2651 29.1285C12.3932 29.0115 11.9077 28.7943 11.5569 28.4441C11.2062 28.094 10.9888 27.61 10.8717 26.7403C10.7516 25.8489 10.75 24.6712 10.75 23L10.75 17C10.75 15.3288 10.7516 14.1511 10.8717 13.2597C10.9888 12.39 11.2062 11.906 11.5569 11.5559C11.9077 11.2057 12.3932 10.9885 13.2651 10.8715C13.3037 10.8663 13.3428 10.8613 13.3825 10.8566C13.4624 11.3334 13.5361 11.7509 13.636 12.0912C13.7551 12.497 13.9337 12.8781 14.2775 13.1831C14.6331 13.4985 15.0426 13.6311 15.4813 13.6917C15.8948 13.7489 16.4052 13.75 17.0001 13.75C17.5948 13.75 18.1053 13.7489 18.5188 13.6917C18.9575 13.6311 19.367 13.4985 19.7226 13.1831C20.0664 12.8781 20.245 12.497 20.3641 12.0912C20.4642 11.7502 20.538 11.3317 20.6181 10.8536C20.6664 10.8593 20.7139 10.8652 20.7607 10.8715C21.6326 10.9885 22.1181 11.2057 22.4689 11.5559C22.9697 12.0558 23.1882 12.8154 23.2505 14.5273C23.2655 14.9412 23.6133 15.2646 24.0273 15.2495C24.4412 15.2345 24.7646 14.8867 24.7495 14.4727C24.6872 12.7597 24.4805 11.4444 23.5286 10.4943C22.8525 9.81941 21.9979 9.52407 20.9602 9.38481C19.9554 9.24997 18.6743 9.24998 17.0686 9.25ZM19.1118 10.7642C18.5114 10.7504 17.8195 10.75 17.0129 10.75C16.1948 10.75 15.4947 10.7504 14.8884 10.7648C14.9568 11.1659 15.0107 11.4487 15.0753 11.6688C15.1519 11.9296 15.2199 12.0139 15.273 12.061C15.3314 12.1129 15.4224 12.1693 15.6866 12.2058C15.976 12.2458 16.371 12.25 17.0001 12.25C17.6291 12.25 18.0241 12.2458 18.3135 12.2058C18.5777 12.1693 18.6687 12.1129 18.7271 12.061C18.7802 12.0139 18.8482 11.9296 18.9248 11.6688C18.9894 11.4486 19.0433 11.1656 19.1118 10.7642Z" fill="#2E3039"/>
<path d="M16 26.25C15.5858 26.25 15.25 26.5858 15.25 27C15.25 27.4142 15.5858 27.75 16 27.75L18 27.75C18.4142 27.75 18.75 27.4142 18.75 27C18.75 26.5858 18.4142 26.25 18 26.25L16 26.25Z" fill="#2E3039"/>
<path d="M28.5663 14.9488C29.9102 16.2323 30.75 18.0153 30.75 19.9912C30.75 21.9671 29.9102 23.7501 28.5663 25.0336C28.2667 25.3197 27.792 25.3088 27.5059 25.0092C27.2198 24.7097 27.2307 24.2349 27.5303 23.9488C28.5981 22.929 29.25 21.53 29.25 19.9912C29.25 18.4524 28.5981 17.0534 27.5303 16.0336C27.2307 15.7475 27.2198 15.2728 27.5059 14.9732C27.792 14.6737 28.2667 14.6627 28.5663 14.9488Z" fill="#2E3039"/>
<path d="M26.518 17.1988C27.259 17.9065 27.7259 18.894 27.7259 19.9912C27.7259 21.0884 27.259 22.076 26.518 22.7836C26.2184 23.0697 25.7437 23.0588 25.4576 22.7592C25.1715 22.4597 25.1825 21.9849 25.482 21.6988C25.9469 21.2548 26.2259 20.6514 26.2259 19.9912C26.2259 19.3311 25.9469 18.7276 25.482 18.2836C25.1825 17.9975 25.1715 17.5228 25.4576 17.2232C25.7437 16.9237 26.2184 16.9127 26.518 17.1988Z" fill="#2E3039"/>
<path d="M24 19.2408C24.4142 19.2408 24.75 19.5766 24.75 19.9908V19.9998C24.75 20.414 24.4142 20.7498 24 20.7498C23.5858 20.7498 23.25 20.414 23.25 19.9998V19.9908C23.25 19.5766 23.5858 19.2408 24 19.2408Z" fill="#2E3039"/>
</svg>

After

Width:  |  Height:  |  Size: 3.8 KiB

@ -0,0 +1,10 @@
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="40" height="40" rx="8" fill="#EFEFF0"/>
<path d="M16.2239 11.2491C16.6376 11.2286 16.9564 10.8766 16.9359 10.4629C16.9154 10.0492 16.5634 9.73043 16.1497 9.75094C14.0291 9.85603 12.4618 10.1708 11.3163 11.3163C10.1708 12.4618 9.85603 14.0291 9.75094 16.1497C9.73043 16.5634 10.0492 16.9154 10.4629 16.9359C10.8766 16.9564 11.2286 16.6376 11.2491 16.2239C11.3521 14.145 11.6599 13.0941 12.377 12.377C13.0941 11.6599 14.145 11.3521 16.2239 11.2491Z" fill="#2E3039"/>
<path d="M23.8504 9.75094C23.4367 9.73043 23.0847 10.0492 23.0642 10.4629C23.0437 10.8766 23.3624 11.2286 23.7761 11.2491C25.855 11.3521 26.906 11.6599 27.623 12.377C28.3401 13.0941 28.6479 14.145 28.7509 16.2239C28.7714 16.6376 29.1234 16.9564 29.5371 16.9359C29.9508 16.9154 30.2696 16.5634 30.2491 16.1497C30.144 14.0291 29.8292 12.4618 28.6837 11.3163C27.5382 10.1708 25.9709 9.85603 23.8504 9.75094Z" fill="#2E3039"/>
<path d="M11.2491 23.7761C11.2286 23.3624 10.8766 23.0437 10.4629 23.0642C10.0492 23.0847 9.73043 23.4367 9.75094 23.8504C9.85603 25.9709 10.1708 27.5382 11.3163 28.6837C12.4618 29.8292 14.0291 30.144 16.1497 30.2491C16.5634 30.2696 16.9154 29.9508 16.9359 29.5371C16.9564 29.1234 16.6376 28.7714 16.2239 28.7509C14.145 28.6479 13.0941 28.3401 12.377 27.623C11.6599 26.906 11.3521 25.855 11.2491 23.7761Z" fill="#2E3039"/>
<path d="M30.2491 23.8504C30.2696 23.4367 29.9508 23.0847 29.5371 23.0642C29.1234 23.0437 28.7714 23.3624 28.7509 23.7761C28.6479 25.855 28.3401 26.906 27.623 27.623C26.906 28.3401 25.855 28.6479 23.7761 28.7509C23.3624 28.7714 23.0437 29.1234 23.0642 29.5371C23.0847 29.9508 23.4367 30.2696 23.8504 30.2491C25.9709 30.144 27.5382 29.8292 28.6837 28.6837C29.8292 27.5382 30.144 25.9709 30.2491 23.8504Z" fill="#2E3039"/>
<path d="M20.75 15C20.75 14.5858 20.4142 14.25 20 14.25C19.5858 14.25 19.25 14.5858 19.25 15L19.25 25C19.25 25.4142 19.5858 25.75 20 25.75C20.4142 25.75 20.75 25.4142 20.75 25L20.75 15Z" fill="#2E3039"/>
<path d="M16.75 17C16.75 16.5858 16.4142 16.25 16 16.25C15.5858 16.25 15.25 16.5858 15.25 17L15.25 23C15.25 23.4142 15.5858 23.75 16 23.75C16.4142 23.75 16.75 23.4142 16.75 23L16.75 17Z" fill="#2E3039"/>
<path d="M24.75 17C24.75 16.5858 24.4142 16.25 24 16.25C23.5858 16.25 23.25 16.5858 23.25 17L23.25 23C23.25 23.4142 23.5858 23.75 24 23.75C24.4142 23.75 24.75 23.4142 24.75 23L24.75 17Z" fill="#2E3039"/>
</svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

@ -1,4 +1,5 @@
<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="M15.8333 3.99984C15.8333 3.26346 16.4303 2.6665 17.1667 2.6665H29.1667C29.903 2.6665 30.5 3.26346 30.5 3.99984C30.5 4.73622 29.903 5.33317 29.1667 5.33317L29.1667 23.3332C29.1667 26.6469 26.4804 29.3332 23.1667 29.3332C19.853 29.3332 17.1667 26.6469 17.1667 23.3332L17.1667 9.33405C17.1667 9.33376 17.1667 9.33434 17.1667 9.33405C17.1667 9.33376 17.1667 9.33259 17.1667 9.33229V5.33317C16.4303 5.33317 15.8333 4.73622 15.8333 3.99984ZM19.8333 7.99984V5.33317H26.5L26.5 13.0676C26.1378 13.317 25.8055 13.5125 25.4733 13.6324C25.0059 13.8012 24.5708 13.8095 24.0425 13.4925C22.6945 12.6837 21.4241 12.879 20.3715 13.4052C20.1897 13.4961 20.0098 13.5996 19.8333 13.7111V10.6665H22.5C23.2364 10.6665 23.8333 10.0695 23.8333 9.33317C23.8333 8.59679 23.2364 7.99984 22.5 7.99984H19.8333Z" fill="#2E3039"/>
<path d="M7.14104 12.6116C7.52794 12.2404 8.13873 12.2404 8.52562 12.6116L8.53329 12.6193C8.69353 12.7817 9.1478 13.2422 9.40228 13.5185C9.91854 14.0791 10.6087 14.8721 11.3012 15.7994C11.9917 16.7239 12.6985 17.8007 13.2361 18.9287C13.7693 20.0478 14.1667 21.2805 14.1667 22.4998C14.1667 24.7601 13.4124 26.4319 12.1558 27.5235C10.9246 28.5931 9.33325 28.9998 7.83333 28.9998C6.33342 28.9998 4.74203 28.5931 3.51084 27.5235C2.25425 26.4319 1.5 24.7601 1.5 22.4998C1.5 21.2805 1.89732 20.0478 2.4306 18.9287C2.96813 17.8007 3.67498 16.7239 4.36544 15.7994C5.05795 14.8721 5.74813 14.0791 6.26439 13.5185C6.51888 13.2422 6.97318 12.7817 7.1334 12.6193L7.14104 12.6116Z" fill="#2E3039"/>
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="40" height="40" rx="10" fill="#EFEFF0"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M19.875 11C19.875 10.4477 20.3227 10 20.875 10H29.875C30.4273 10 30.875 10.4477 30.875 11C30.875 11.5523 30.4273 12 29.875 12L29.875 25.5C29.875 27.9853 27.8603 30 25.375 30C22.8897 30 20.875 27.9853 20.875 25.5L20.875 15.0007C20.875 15.0004 20.875 15.0009 20.875 15.0007C20.875 15.0004 20.875 14.9996 20.875 14.9993V12C20.3227 12 19.875 11.5523 19.875 11ZM22.875 14V12H27.875L27.875 17.8008C27.6034 17.9878 27.3541 18.1345 27.105 18.2244C26.7545 18.351 26.4281 18.3572 26.0319 18.1195C25.0208 17.5129 24.0681 17.6593 23.2787 18.054C23.1423 18.1222 23.0074 18.1998 22.875 18.2835V16H24.875C25.4273 16 25.875 15.5523 25.875 15C25.875 14.4477 25.4273 14 24.875 14H22.875Z" fill="#8F9AA3"/>
<path d="M13.3558 17.4588C13.646 17.1804 14.104 17.1804 14.3942 17.4588L14.4 17.4646C14.5201 17.5864 14.8608 17.9318 15.0517 18.139C15.4389 18.5595 15.9565 19.1542 16.4759 19.8496C16.9938 20.5431 17.5239 21.3507 17.9271 22.1967C18.327 23.0359 18.625 23.9605 18.625 24.875C18.625 26.5702 18.0593 27.824 17.1169 28.6428C16.1935 29.445 14.9999 29.75 13.875 29.75C12.7501 29.75 11.5565 29.445 10.6331 28.6428C9.69069 27.824 9.125 26.5702 9.125 24.875C9.125 23.9605 9.42299 23.0359 9.82295 22.1967C10.2261 21.3507 10.7562 20.5431 11.2741 19.8496C11.7935 19.1542 12.3111 18.5595 12.6983 18.139C12.8892 17.9317 13.2299 17.5864 13.3501 17.4646L13.3558 17.4588Z" fill="#8F9AA3"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

@ -0,0 +1,6 @@
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="40" height="40" rx="10" fill="#EFEFF0"/>
<path d="M30.6308 10.2991C29.0945 7.59329 25.6682 6.64537 22.979 8.22413L11.4107 15.0156C8.73747 16.585 7.84093 20.0377 9.36885 22.7287C10.292 24.3547 11.8978 25.3474 13.6192 25.5354C14.2437 25.6037 14.8056 25.156 14.8742 24.5356C14.9429 23.9152 14.4924 23.3569 13.8679 23.2887C12.8583 23.1784 11.9057 22.5963 11.3503 21.6182C10.4191 19.9781 10.9819 17.8927 12.568 16.9616L17.3743 14.1399L18.4526 15.9471C18.773 16.484 19.4708 16.6612 20.0112 16.3429C20.5516 16.0246 20.7299 15.3313 20.4096 14.7944L19.3329 12.9901L24.1363 10.1701C25.7064 9.24835 27.7265 9.78436 28.6493 11.4096C29.3944 12.7218 29.1847 14.331 28.2445 15.3968C27.8304 15.8662 27.8776 16.5802 28.3501 16.9916C28.8225 17.4031 29.5412 17.3561 29.9553 16.8867C31.5176 15.1158 31.8614 12.4664 30.6308 10.2991Z" fill="#8F9AA3"/>
<path d="M29.6682 19.9448C29.6421 20.1097 29.4816 20.2024 29.1605 20.3877L16.9579 27.4329C16.6368 27.6183 16.4762 27.711 16.3204 27.6511C16.1645 27.5913 16.1106 27.4253 16.0028 27.0933C14.8794 23.6329 16.2822 19.7408 19.5595 17.8487C22.8368 15.9565 26.9088 16.6878 29.3439 19.3909C29.5775 19.6502 29.6943 19.7798 29.6682 19.9448Z" fill="#8F9AA3"/>
<path d="M30.6731 21.685C30.5173 21.6251 30.3567 21.7178 30.0357 21.9032L17.8327 28.9486C17.5116 29.1339 17.3511 29.2266 17.325 29.3915C17.2989 29.5564 17.4156 29.6861 17.6492 29.9454C20.0841 32.6493 24.1568 33.381 27.4345 31.4886C30.7122 29.5962 32.1149 25.7034 30.9907 22.2427C30.8829 21.9108 30.829 21.7448 30.6731 21.685Z" fill="#8F9AA3"/>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

@ -0,0 +1,4 @@
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="40" height="40" rx="10" fill="#EFEFF0"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M29.6263 14.5365C29.75 15.6786 29.75 17.1182 29.75 18.9548V21.0452V21.0453C29.75 22.8818 29.75 24.3214 29.6263 25.4635C29.5 26.6291 29.2377 27.5734 28.6518 28.3798C28.2972 28.8679 27.8679 29.2972 27.3798 29.6518C26.5734 30.2377 25.6291 30.5 24.4635 30.6263C23.3214 30.75 21.8818 30.75 20.0453 30.75H20.0452H19.9548C18.1182 30.75 16.6786 30.75 15.5365 30.6263C14.3709 30.5 13.4266 30.2377 12.6202 29.6518C12.1321 29.2972 11.7028 28.8679 11.3482 28.3798C10.7623 27.5734 10.5 26.6291 10.3737 25.4635C10.25 24.3214 10.25 22.8818 10.25 21.0453V21.0453V18.9547V18.9547C10.25 17.1182 10.25 15.6786 10.3737 14.5365C10.5 13.3709 10.7623 12.4266 11.3482 11.6202C11.7028 11.1321 12.1321 10.7028 12.6202 10.3482C13.4266 9.76232 14.3709 9.50001 15.5365 9.37373C16.6786 9.24999 18.1182 9.24999 19.9547 9.25H19.9547H20.0453H20.0453C21.8818 9.24999 23.3214 9.24999 24.4635 9.37373C25.6291 9.50001 26.5734 9.76232 27.3798 10.3482C27.8679 10.7028 28.2972 11.1321 28.6518 11.6202C29.2377 12.4266 29.5 13.3709 29.6263 14.5365ZM20 12.25C20.5523 12.25 21 12.6977 21 13.25V14.9327C21.2207 15.1993 21.409 15.25 21.5 15.25C21.6114 15.25 21.8682 15.1741 22.152 14.7201C22.4447 14.2517 23.0616 14.1093 23.53 14.402C23.9983 14.6947 24.1407 15.3116 23.848 15.7799C23.3587 16.563 22.5566 17.25 21.5 17.25C21.3264 17.25 21.1596 17.2315 21 17.197L21 17.7812C21.4211 18.0758 21.938 18.25 22.5 18.25C23.2077 18.25 23.8439 17.9737 24.3074 17.5287C24.7058 17.1462 25.3388 17.159 25.7213 17.5574C26.1038 17.9558 26.091 18.5888 25.6926 18.9713C24.8675 19.7635 23.7389 20.25 22.5 20.25C21.9753 20.25 21.4704 20.1628 21 20.002V21.5403C21.5294 21.4955 22.0331 21.416 22.5012 21.3072C23.7722 21.0118 25.25 21.9195 25.25 23.4049C25.25 24.1453 24.8403 24.9148 24.0547 25.2069C23.7012 25.3384 23.3259 25.4522 22.9332 25.5471C22.5379 25.6426 22.222 25.892 22.0713 26.2264L21.9649 26.4624C21.616 27.2367 20.8556 27.75 20 27.75C19.1444 27.75 18.384 27.2367 18.0351 26.4624L17.9287 26.2264C17.778 25.892 17.4621 25.6426 17.0668 25.5471C16.6741 25.4522 16.2988 25.3384 15.9453 25.2069C15.1597 24.9148 14.75 24.1453 14.75 23.4049C14.75 21.9195 16.2278 21.0118 17.4988 21.3072C17.9669 21.416 18.4706 21.4955 19 21.5403V20.002C18.5296 20.1628 18.0247 20.25 17.5 20.25C16.2611 20.25 15.1325 19.7635 14.3074 18.9713C13.909 18.5888 13.8962 17.9558 14.2787 17.5574C14.6612 17.159 15.2942 17.1462 15.6926 17.5287C16.1561 17.9737 16.7923 18.25 17.5 18.25C18.062 18.25 18.5789 18.0758 19 17.7812V17.197C18.8404 17.2315 18.6736 17.25 18.5 17.25C17.4434 17.25 16.6413 16.563 16.152 15.78C15.8593 15.3116 16.0017 14.6947 16.4701 14.402C16.9384 14.1093 17.5553 14.2517 17.848 14.72C18.1318 15.1741 18.3886 15.25 18.5 15.25C18.591 15.25 18.7793 15.1993 19 14.9327V13.25C19 12.6977 19.4477 12.25 20 12.25Z" fill="#8F9AA3"/>
</svg>

After

Width:  |  Height:  |  Size: 2.9 KiB

@ -0,0 +1,4 @@
<svg width="16" height="18" viewBox="0 0 16 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3.5 0.75C3.91421 0.75 4.25 1.08579 4.25 1.5V1.5688C5.07278 1.49997 6.06031 1.49998 7.22842 1.5L8.77158 1.5C9.93969 1.49998 10.9272 1.49997 11.75 1.5688V1.5C11.75 1.08579 12.0858 0.75 12.5 0.75C12.9142 0.75 13.25 1.08579 13.25 1.5V1.83664C13.8209 2.01772 14.317 2.30245 14.7365 2.75595C15.3213 3.3882 15.5927 4.1711 15.7281 5.10899C15.861 6.03001 15.8733 7.1875 15.8748 8.62422V9.75C15.8748 10.1642 15.539 10.5 15.1248 10.5C14.7106 10.5 14.3748 10.1642 14.3748 9.75V8.62579C14.3742 8.06266 14.3716 7.56155 14.3621 7.1128C14.3578 6.91051 14.1918 6.75 13.9895 6.75L2.01043 6.75C1.80809 6.75 1.64212 6.91043 1.63785 7.11272C1.62532 7.70596 1.625 8.3885 1.625 9.18243L1.625 9.56757C1.625 11.2212 1.62637 12.3916 1.73665 13.2784C1.84496 14.1492 2.04697 14.632 2.36467 14.9755C2.67467 15.3106 3.09852 15.5177 3.87725 15.6309C4.68393 15.7481 5.75319 15.75 7.2875 15.75C7.70171 15.75 8.0375 16.0858 8.0375 16.5C8.0375 16.9142 7.70171 17.25 7.2875 17.25H7.22842C5.76721 17.25 4.58856 17.25 3.6615 17.1153C2.69401 16.9747 1.8926 16.6741 1.26352 15.9941C0.64212 15.3223 0.374593 14.4805 0.248121 13.4635C0.124976 12.4733 0.124987 11.2094 0.125 9.6196L0.125 9.1304C0.124987 7.54055 0.124976 6.2767 0.248121 5.28649C0.374593 4.26954 0.64212 3.42773 1.26352 2.75595C1.683 2.30245 2.17912 2.01772 2.75 1.83664L2.75 1.5C2.75 1.08579 3.08579 0.75 3.5 0.75Z" fill="#2E3039"/>
<path d="M14.7502 13.8794C15.1644 13.8794 15.5002 13.5436 15.5002 13.1294C15.5002 12.7152 15.1644 12.3794 14.7502 12.3794H11.3751L11.3752 11.9337C11.3752 11.802 11.3754 11.6402 11.3588 11.5079L11.3585 11.5054C11.3467 11.4106 11.2928 10.9785 10.8686 10.7728C10.4435 10.5665 10.0684 10.793 9.98722 10.842L9.63585 11.0963C9.35398 11.3173 8.96814 11.6216 8.67488 11.9069C8.52855 12.0493 8.37762 12.2122 8.25849 12.3854C8.15259 12.5394 8.00014 12.802 8.00014 13.125C8.00014 13.448 8.15259 13.7106 8.25849 13.8646C8.37762 14.0378 8.52855 14.2007 8.67488 14.3431C8.96814 14.6284 9.35398 14.9327 9.63585 15.1537L9.98722 15.408C10.0684 15.457 10.4435 15.6835 10.8686 15.4772C11.2928 15.2715 11.3467 14.8394 11.3585 14.7446L11.3588 14.7421C11.3754 14.6098 11.3752 14.448 11.3752 14.3163L11.3751 13.8794H14.7502Z" fill="#2E3039"/>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

@ -0,0 +1,3 @@
<svg width="22" height="21" viewBox="0 0 22 21" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.36199 0.515001C10.4264 0.161666 11.5736 0.161666 12.638 0.515001C13.6981 0.866874 14.5304 1.70141 15.3665 2.87395C16.1999 4.0426 17.1208 5.67206 18.3078 7.7725L18.3544 7.85496C19.5417 9.95572 20.4625 11.5851 21.0364 12.9065C21.613 14.2343 21.9002 15.3807 21.6711 16.4821C21.4403 17.5911 20.8714 18.5995 20.0428 19.3617C19.2162 20.122 18.0907 20.4428 16.6736 20.5968C15.2645 20.75 13.4212 20.75 11.0488 20.75H10.9513C8.57882 20.75 6.73554 20.75 5.32642 20.5968C3.90927 20.4428 2.78379 20.122 1.95722 19.3617C1.12862 18.5995 0.55968 17.5911 0.328953 16.4821C0.0997984 15.3807 0.387 14.2343 0.963655 12.9065C1.53752 11.5851 2.45835 9.95572 3.64558 7.85495L3.69218 7.7725C4.87921 5.67207 5.80008 4.0426 6.63347 2.87395C7.46963 1.70141 8.30194 0.866874 9.36199 0.515001ZM10 15.5C10 14.9477 10.4457 14.5 10.9955 14.5H11.0045C11.5543 14.5 12 14.9477 12 15.5C12 16.0523 11.5543 16.5 11.0045 16.5H10.9955C10.4457 16.5 10 16.0523 10 15.5ZM10 11.5C10 12.0523 10.4477 12.5 11 12.5C11.5523 12.5 12 12.0523 12 11.5L12 7.5C12 6.94772 11.5523 6.5 11 6.5C10.4477 6.5 10 6.94772 10 7.5L10 11.5Z" fill="#ED1C2B"/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

@ -17,6 +17,7 @@ import '../exceptions/api_failure.dart';
abstract class ApiClient {
static final NavigationService _navigationService = getIt.get<NavigationService>();
Future<void> post(
String endPoint, {
required Map<String, dynamic> body,
@ -93,7 +94,7 @@ class ApiClientImp implements ApiClient {
required Map<String, dynamic> body,
required Function(dynamic response, int statusCode, {int? messageStatus, String? errorMessage}) onSuccess,
required Function(String error, int statusCode, {int? messageStatus, Failure? failureType}) onFailure,
bool isAllowAny = true,
bool isAllowAny = false,
bool isExternal = false,
bool isRCService = false,
bool bypassConnectionCheck = false,
@ -109,187 +110,186 @@ class ApiClientImp implements ApiClient {
}
}
// try {
var user = _appState.getAuthenticatedUser();
Map<String, String> headers = {'Content-Type': 'application/json', 'Accept': 'application/json'};
if (!isExternal) {
String? token = _appState.appAuthToken;
var user = _appState.getAuthenticatedUser();
Map<String, String> headers = {'Content-Type': 'application/json', 'Accept': 'application/json'};
if (!isExternal) {
String? token = _appState.appAuthToken;
if (body.containsKey('SetupID')) {
body['SetupID'] = body.containsKey('SetupID') ? body['SetupID'] ?? body[''] : SETUP_ID;
} else {}
if (body.containsKey('SetupID')) {
body['SetupID'] = body.containsKey('SetupID') ? body['SetupID'] ?? body[''] : SETUP_ID;
} else {}
if (body.containsKey('isDentalAllowedBackend')) {
body['isDentalAllowedBackend'] = body.containsKey('isDentalAllowedBackend') ? body['isDentalAllowedBackend'] ?? IS_DENTAL_ALLOWED_BACKEND : IS_DENTAL_ALLOWED_BACKEND;
}
if (body.containsKey('isDentalAllowedBackend')) {
body['isDentalAllowedBackend'] = body.containsKey('isDentalAllowedBackend') ? body['isDentalAllowedBackend'] ?? IS_DENTAL_ALLOWED_BACKEND : IS_DENTAL_ALLOWED_BACKEND;
}
if (!body.containsKey('IsPublicRequest')) {
// if (!body.containsKey('PatientType')) {
if (user != null && user.patientType != null) {
body['PatientType'] = user.patientType;
} else {
body['PatientType'] = PATIENT_TYPE.toString();
}
if (!body.containsKey('IsPublicRequest')) {
// if (!body.containsKey('PatientType')) {
if (user != null && user.patientType != null) {
body['PatientType'] = user.patientType;
} else {
body['PatientType'] = PATIENT_TYPE.toString();
}
if (user != null && user.patientType != null) {
body['PatientTypeID'] = user.patientType;
} else {
body['PatientType'] = PATIENT_TYPE_ID.toString();
}
if (user != null && user.patientType != null) {
body['PatientTypeID'] = user.patientType;
} else {
body['PatientType'] = PATIENT_TYPE_ID.toString();
}
// TODO : These should be from the appState
if (user != null) {
body['TokenID'] = body['TokenID'] ?? token;
body['PatientID'] = body['PatientID'] ?? user.patientId;
// TODO : These should be from the appState
if (user != null) {
body['TokenID'] = body['TokenID'] ?? token;
body['PatientID'] = body['PatientID'] ?? user.patientId;
body['PatientOutSA'] = body.containsKey('PatientOutSA') ? body['PatientOutSA'] ?? user.outSa : user.outSa;
body['SessionID'] = body['TokenID'] == null ? ApiConsts.sessionID : getSessionId(body['TokenID'] ?? ""); //getSe
}
// else {
// body['SessionID'] = body['TokenID'] == null ? ApiConsts.sessionID : getSessionId(body['TokenID'] ?? ""); //getSe
//
// }
body['PatientOutSA'] = body.containsKey('PatientOutSA') ? body['PatientOutSA'] ?? user.outSa : user.outSa;
body['SessionID'] = body['TokenID'] == null ? ApiConsts.sessionID : getSessionId(body['TokenID'] ?? ""); //getSe
}
// else {
// body['SessionID'] = body['TokenID'] == null ? ApiConsts.sessionID : getSessionId(body['TokenID'] ?? ""); //getSe
//
// }
}
}
// request.versionID = VERSION_ID;
// request.channel = CHANNEL;
// request.iPAdress = IP_ADDRESS;
// request.generalid = GENERAL_ID;
// request.languageID = (languageID == 'ar' ? 1 : 2);
// request.patientOutSA = (request.zipCode == '966' || request.zipCode == '+966') ? 0 : 1;
// body['VersionID'] = ApiConsts.appVersionID.toString();
if (!isExternal) {
body['VersionID'] = "50.0";
body['Channel'] = ApiConsts.appChannelId.toString();
body['IPAdress'] = ApiConsts.appIpAddress;
body['generalid'] = ApiConsts.appGeneralId;
body['LanguageID'] = _appState.getLanguageID().toString();
body['Latitude'] = _appState.userLat.toString();
body['Longitude'] = _appState.userLong.toString();
body['DeviceTypeID'] = _appState.deviceTypeID;
if (_appState.appAuthToken.isNotEmpty) {
body[_appState.isAuthenticated ? 'TokenID' : 'LogInTokenID'] = _appState.appAuthToken;
}
// body['TokenID'] = "@dm!n";
// body['PatientID'] = "4767477";
// request.versionID = VERSION_ID;
// request.channel = CHANNEL;
// request.iPAdress = IP_ADDRESS;
// request.generalid = GENERAL_ID;
// request.languageID = (languageID == 'ar' ? 1 : 2);
// request.patientOutSA = (request.zipCode == '966' || request.zipCode == '+966') ? 0 : 1;
// body['VersionID'] = ApiConsts.appVersionID.toString();
if (!isExternal) {
body['VersionID'] = "50.0";
body['Channel'] = ApiConsts.appChannelId.toString();
body['IPAdress'] = ApiConsts.appIpAddress;
body['generalid'] = ApiConsts.appGeneralId;
body['LanguageID'] = _appState.getLanguageID().toString();
body['Latitude'] = _appState.userLat.toString();
body['Longitude'] = _appState.userLong.toString();
body['DeviceTypeID'] = _appState.deviceTypeID;
if (_appState.appAuthToken.isNotEmpty) {
body[_appState.isAuthenticated ? 'TokenID' : 'LogInTokenID'] = _appState.appAuthToken;
}
body.removeWhere((key, value) => value == null);
log("body: ${json.encode(body)}");
log("uri: ${Uri.parse(url.trim())}");
// body['TokenID'] = "@dm!n";
// body['PatientID'] = "4767477";
}
final bool networkStatus = await Utils.checkConnection(bypassConnectionCheck: bypassConnectionCheck);
body.removeWhere((key, value) => value == null);
log("body: ${json.encode(body)}");
log("uri: ${Uri.parse(url.trim())}");
if (!networkStatus) {
onFailure(
'Please Check The Internet Connection 1',
-1,
failureType: ConnectivityFailure("Please Check The Internet Connection 1"),
);
_analytics.errorTracking.log("internet_connectivity", error: "no internet available");
return;
}
final bool networkStatus = await Utils.checkConnection(bypassConnectionCheck: bypassConnectionCheck);
final response = await http.post(Uri.parse(url.trim()), body: json.encode(body), headers: headers);
final int statusCode = response.statusCode;
log("response.body: ${response.body}");
if (statusCode < 200 || statusCode >= 400) {
var parsed = json.decode(utf8.decode(response.bodyBytes));
onFailure('Error While Fetching data', statusCode, failureType: StatusCodeFailure("Error While Fetching data"));
logApiEndpointError(endPoint, 'Error While Fetching data', statusCode);
if (!networkStatus) {
onFailure(
'Please Check The Internet Connection 1',
-1,
failureType: ConnectivityFailure("Please Check The Internet Connection 1"),
);
_analytics.errorTracking.log("internet_connectivity", error: "no internet available");
return;
}
final response = await http.post(Uri.parse(url.trim()), body: json.encode(body), headers: headers);
final int statusCode = response.statusCode;
log("response.body: ${response.body}");
if (statusCode < 200 || statusCode >= 400) {
var parsed = json.decode(utf8.decode(response.bodyBytes));
onFailure('Error While Fetching data', statusCode, failureType: StatusCodeFailure("Error While Fetching data"));
logApiEndpointError(endPoint, 'Error While Fetching data', statusCode);
} else {
var parsed = json.decode(utf8.decode(response.bodyBytes));
if (isAllowAny) {
onSuccess(parsed, statusCode, messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage']);
} else {
var parsed = json.decode(utf8.decode(response.bodyBytes));
if (isAllowAny) {
onSuccess(parsed, statusCode, messageStatus: 1, errorMessage: "");
if (parsed['Response_Message'] != null) {
onSuccess(parsed, statusCode, messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']);
} else {
if (parsed['Response_Message'] != null) {
if (parsed['ErrorType'] == 4) {
//TODO : handle app update
logApiEndpointError(endPoint, parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage'], statusCode);
}
if (parsed['ErrorType'] == 2) {
// todo: handle Logout
logApiEndpointError(endPoint, "session logged out", statusCode);
}
if (isAllowAny) {
onSuccess(parsed, statusCode, messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']);
} else {
if (parsed['ErrorType'] == 4) {
//TODO : handle app update
} else if (parsed['IsAuthenticated'] == null) {
if (parsed['isSMSSent'] == true) {
onSuccess(parsed, statusCode, messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']);
} else if (parsed['MessageStatus'] == 1) {
onSuccess(parsed, statusCode, messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']);
} else if (parsed['Result'] == 'OK') {
onSuccess(parsed, statusCode, messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']);
} else {
onFailure(
parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage'],
statusCode,
failureType: MessageStatusFailure(parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']),
);
logApiEndpointError(endPoint, parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage'], statusCode);
}
if (parsed['ErrorType'] == 2) {
// todo: handle Logout
logApiEndpointError(endPoint, "session logged out", statusCode);
}
if (isAllowAny) {
onSuccess(parsed, statusCode, messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']);
} else if (parsed['IsAuthenticated'] == null) {
if (parsed['isSMSSent'] == true) {
onSuccess(parsed, statusCode, messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']);
} else if (parsed['MessageStatus'] == 1) {
onSuccess(parsed, statusCode, messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']);
} else if (parsed['Result'] == 'OK') {
onSuccess(parsed, statusCode, messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']);
} else {
onFailure(
parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage'],
statusCode,
failureType: MessageStatusFailure(parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']),
);
logApiEndpointError(endPoint, parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage'], statusCode);
}
} else if (parsed['MessageStatus'] == 1 || parsed['SMSLoginRequired'] == true) {
} else if (parsed['MessageStatus'] == 1 || parsed['SMSLoginRequired'] == true) {
onSuccess(parsed, statusCode, messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']);
} else if (parsed['MessageStatus'] == 2 && parsed['IsAuthenticated']) {
if (parsed['SameClinicApptList'] != null) {
onSuccess(parsed, statusCode, messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']);
} else if (parsed['MessageStatus'] == 2 && parsed['IsAuthenticated']) {
if (parsed['SameClinicApptList'] != null) {
onSuccess(parsed, statusCode, messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']);
} else {
if (parsed['message'] == null && parsed['ErrorEndUserMessage'] == null) {
if (parsed['ErrorSearchMsg'] == null) {
onFailure(
"Server Error found with no available message",
statusCode,
failureType: ServerFailure("Error While Fetching data"),
);
logApiEndpointError(endPoint, "Server Error found with no available message", statusCode);
} else {
onFailure(
parsed['ErrorSearchMsg'],
statusCode,
failureType: ServerFailure("Error While Fetching data"),
);
logApiEndpointError(endPoint, parsed['ErrorSearchMsg'], statusCode);
}
} else {
onFailure(
parsed['message'] ?? parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage'],
statusCode,
failureType: UserIntimationFailure(parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']),
);
logApiEndpointError(endPoint, parsed['message'] ?? parsed['message'], statusCode);
}
}
} else if (!parsed['IsAuthenticated']) {
} else {
if (parsed['SameClinicApptList'] != null) {
onSuccess(parsed, statusCode, messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']);
} else {
if (parsed['message'] != null) {
if (parsed['message'] == null && parsed['ErrorEndUserMessage'] == null) {
if (parsed['ErrorSearchMsg'] == null) {
onFailure(
parsed['message'] ?? parsed['message'],
"Server Error found with no available message",
statusCode,
failureType: ServerFailure("Error While Fetching data"),
);
logApiEndpointError(endPoint, parsed['message'] ?? parsed['message'], statusCode);
logApiEndpointError(endPoint, "Server Error found with no available message", statusCode);
} else {
onFailure(
parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage'],
parsed['ErrorSearchMsg'],
statusCode,
failureType: ServerFailure("Error While Fetching data"),
);
logApiEndpointError(endPoint, parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage'], statusCode);
logApiEndpointError(endPoint, parsed['ErrorSearchMsg'], statusCode);
}
} else {
onFailure(
parsed['message'] ?? parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage'],
statusCode,
failureType: UserIntimationFailure(parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']),
);
logApiEndpointError(endPoint, parsed['message'] ?? parsed['message'], statusCode);
}
}
} else if (!parsed['IsAuthenticated']) {
} else {
if (parsed['SameClinicApptList'] != null) {
onSuccess(parsed, statusCode, messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']);
} else {
if (parsed['message'] != null) {
onFailure(
parsed['message'] ?? parsed['message'],
statusCode,
failureType: ServerFailure("Error While Fetching data"),
);
logApiEndpointError(endPoint, parsed['message'] ?? parsed['message'], statusCode);
} else {
onFailure(
parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage'],
statusCode,
failureType: ServerFailure("Error While Fetching data"),
);
logApiEndpointError(endPoint, parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage'], statusCode);
}
}
}
}
}
}
// } catch (e, stackTrace) {
// _loggerService.errorLogs(stackTrace.toString());
// if (e.toString().contains("ClientException")) {

@ -726,7 +726,7 @@ const DEACTIVATE_ACCOUNT = 'Services/Patients.svc/REST/PatientAppleActivation_In
class ApiConsts {
static const maxSmallScreen = 660;
static AppEnvironmentTypeEnum appEnvironmentType = AppEnvironmentTypeEnum.prod;
static AppEnvironmentTypeEnum appEnvironmentType = AppEnvironmentTypeEnum.uat;
// static String baseUrl = 'https://uat.hmgwebservices.com/'; // HIS API URL UAT
@ -803,6 +803,7 @@ class ApiConsts {
static final String insertPatientDeviceIMEIData = 'Services/Patients.svc/REST/Patient_INSERTDeviceIMEI';
static final String insertPatientMobileData = 'Services/MobileNotifications.svc/REST/Insert_PatientMobileDeviceInfo';
static final String getPrivileges = 'Services/Patients.svc/REST/Service_Privilege';
// static values for Api
static final double appVersionID = 18.7;

@ -86,7 +86,13 @@ class AppAssets {
static const String directions_icon = '$svgBasePath/directions_icon.svg';
static const String apple_pay_button = '$svgBasePath/pay_with_apple_pay.svg';
static const String reminder_bell = '$svgBasePath/reminder_bell.svg';
static const String rebook_appointment_icon = '$svgBasePath/rebook_appointment_icon.svg';
static const String report_icon = '$svgBasePath/report_icon.svg';
static const String radiology_icon = '$svgBasePath/radiology_icon.svg';
static const String prescription_item_icon = '$svgBasePath/prescription_item_icon.svg';
static const String checkin_location_icon = '$svgBasePath/checkin_location_icon.svg';
static const String checkin_nfc_icon = '$svgBasePath/checkin_nfc_icon.svg';
static const String checkin_qr_icon = '$svgBasePath/checkin_qr_icon.svg';
//bottom navigation//
static const String homeBottom = '$svgBasePath/home_bottom.svg';
@ -115,4 +121,5 @@ class AppAnimations {
static const String register = '$lottieBasePath/register.json';
static const String checkmark = '$lottieBasePath/checkmark.json';
static const String loadingAnimation = '$lottieBasePath/Loader.json';
static const String errorAnimation = '$lottieBasePath/ErrorAnimation.json';
}

@ -1,7 +1,10 @@
import 'dart:io';
import 'package:easy_localization/easy_localization.dart';
import 'package:hmg_patient_app_new/core/common_models/VidaPlusProjectListModel.dart';
import 'package:hmg_patient_app_new/core/common_models/privilege/HMCProjectListModel.dart';
import 'package:hmg_patient_app_new/core/common_models/privilege/PrivilegeModel.dart';
import 'package:hmg_patient_app_new/core/common_models/privilege/ProjectDetailListModel.dart';
import 'package:hmg_patient_app_new/core/common_models/privilege/VidaPlusProjectListModel.dart';
import 'package:hmg_patient_app_new/features/authentication/models/resp_models/authenticated_user_resp_model.dart';
import 'package:hmg_patient_app_new/features/authentication/models/resp_models/check_user_staus_nhic_response_model.dart';
import 'package:hmg_patient_app_new/features/authentication/models/resp_models/select_device_by_imei.dart';
@ -37,7 +40,7 @@ class AppState {
if (isFamily) {
_authenticatedChildUser = authenticatedUser;
} else {
setIsAuthenticated = true;
setIsAuthenticated = true;
_authenticatedRootUser = authenticatedUser;
}
}
@ -93,11 +96,26 @@ class AppState {
set setDeviceTypeID(v) => deviceTypeID = v;
List<VidaPlusProjectListModel> vidaPlusProjectList = [];
List<PrivilegeModel> privilegeModelList = [];
List<HMCProjectListModel> hMCProjectListModel = [];
List<ProjectDetailListModel> projectDetailListModel = [];
setVidaPlusProjectList(List<VidaPlusProjectListModel> vidaPlusProjectListModelInput) {
vidaPlusProjectList = vidaPlusProjectListModelInput;
}
setPrivilegeModelList(List<PrivilegeModel> privilegeModelListInput) {
privilegeModelList = privilegeModelListInput;
}
setHMCProjectList(List<HMCProjectListModel> hMCProjectListModelInput) {
hMCProjectListModel = hMCProjectListModelInput;
}
setProjectsDetailList(List<ProjectDetailListModel> projectDetailListModelInput) {
projectDetailListModel = projectDetailListModelInput;
}
CheckUserStatusResponseNHIC? _nHICUserData;
CheckUserStatusResponseNHIC get getNHICUserData => _nHICUserData!;
@ -106,7 +124,6 @@ class AppState {
_nHICUserData = value;
}
RegistrationDataModelPayload? _userRegistrationPayload;
RegistrationDataModelPayload get getUserRegistrationPayload => _userRegistrationPayload ?? RegistrationDataModelPayload();

@ -0,0 +1,15 @@
class HMCProjectListModel {
int? projectID;
HMCProjectListModel({this.projectID});
HMCProjectListModel.fromJson(Map<String, dynamic> json) {
projectID = json['ProjectID'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['ProjectID'] = this.projectID;
return data;
}
}

@ -0,0 +1,24 @@
class PrivilegeModel {
int? iD;
String? serviceName;
bool? privilege;
dynamic region;
PrivilegeModel({this.iD, this.serviceName, this.privilege, this.region});
PrivilegeModel.fromJson(Map<String, dynamic> json) {
iD = json['ID'];
serviceName = json['ServiceName'];
privilege = json['Previlege'];
region = json['Region'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['ID'] = this.iD;
data['ServiceName'] = this.serviceName;
data['Previlege'] = this.privilege;
data['Region'] = this.region;
return data;
}
}

@ -0,0 +1,32 @@
class ProjectDetailListModel {
int? projectID;
String? latitude;
String? longitude;
int? geofenceRadius;
String? checkInQrCode;
ProjectDetailListModel(
{this.projectID,
this.latitude,
this.longitude,
this.geofenceRadius,
this.checkInQrCode});
ProjectDetailListModel.fromJson(Map<String, dynamic> json) {
projectID = json['ProjectID'];
latitude = json['Latitude'];
longitude = json['Longitude'];
geofenceRadius = json['GeofenceRadius'];
checkInQrCode = json['CheckInQrCode'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['ProjectID'] = this.projectID;
data['Latitude'] = this.latitude;
data['Longitude'] = this.longitude;
data['GeofenceRadius'] = this.geofenceRadius;
data['CheckInQrCode'] = this.checkInQrCode;
return data;
}
}

@ -20,6 +20,9 @@ class RequestUtils {
bool fileNo = false;
if (nationId.isNotEmpty) {
fileNo = nationId.length < 10;
if (fileNo) {
patientId = int.tryParse(nationId);
}
}
var request = SendActivationRequest();
if (phoneNumber.isNotEmpty) {
@ -51,18 +54,17 @@ class RequestUtils {
return request;
}
static dynamic getCommonRequestWelcome({
required String phoneNumber,
required OTPTypeEnum otpTypeEnum,
required String? deviceToken,
required bool patientOutSA,
required String? loginTokenID,
required var registeredData,
int? patientId,
required String nationIdText,
required String countryCode,
required int loginType
}) {
static dynamic getCommonRequestWelcome(
{required String phoneNumber,
required OTPTypeEnum otpTypeEnum,
required String? deviceToken,
required bool patientOutSA,
required String? loginTokenID,
required var registeredData,
int? patientId,
required String nationIdText,
required String countryCode,
required int loginType}) {
bool fileNo = false;
if (nationIdText.isNotEmpty) {
fileNo = nationIdText.length < 10;
@ -86,7 +88,7 @@ class RequestUtils {
} else {
if (fileNo) {
request.patientID = patientId ?? int.parse(nationIdText);
request.patientIdentificationID = request.nationalID = "" as int?;
request.patientIdentificationID = request.nationalID = 0;
request.searchType = 2;
} else {
request.patientID = 0;

@ -10,6 +10,7 @@ import 'package:fluttertoast/fluttertoast.dart';
import 'package:google_api_availability/google_api_availability.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/common_models/privilege/ProjectDetailListModel.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';
@ -22,6 +23,8 @@ import 'package:hmg_patient_app_new/widgets/loading_dialog.dart';
import 'package:lottie/lottie.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'dart:math' as dartMath;
class Utils {
static AppState appState = getIt.get<AppState>();
static NavigationService navigationService = getIt.get<NavigationService>();
@ -323,6 +326,19 @@ class Utils {
).center;
}
static Widget getErrorWidget({String? loadingText}) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Lottie.asset(AppAnimations.errorAnimation, repeat: true, reverse: false, frameRate: FrameRate(60), width: 100.h, height: 100.h, fit: BoxFit.fill),
SizedBox(height: 8.h),
(loadingText ?? LocaleKeys.loadingText.tr()).toText16(color: AppColors.blackColor),
SizedBox(height: 8.h),
],
).center;
}
static bool isVidaPlusProject(AppState appState, int projectID) {
bool isVidaPlus = false;
for (var element in appState.vidaPlusProjectList) {
@ -333,6 +349,34 @@ class Utils {
return isVidaPlus;
}
static ProjectDetailListModel getProjectDetailObj(AppState appState, int projectID) {
ProjectDetailListModel projectDetailListModel = ProjectDetailListModel();
for (var element in appState.projectDetailListModel) {
if (element.projectID == projectID) {
projectDetailListModel = element;
}
}
return projectDetailListModel;
}
static double distance(double lat1, double lon1, double lat2, double lon2) {
const r = 6372.8; // Earth radius in kilometers
final dLat = _toRadians(lat2 - lat1);
final dLon = _toRadians(lon2 - lon1);
final lat1Radians = _toRadians(lat1);
final lat2Radians = _toRadians(lat2);
final a = _haversin(dLat) + dartMath.cos(lat1Radians) * dartMath.cos(lat2Radians) * _haversin(dLon);
final c = 2 * dartMath.asin(dartMath.sqrt(a));
return r * c;
}
static double _toRadians(double degrees) => degrees * dartMath.pi / 180;
static num _haversin(double radians) => dartMath.pow(dartMath.sin(radians / 2), 2);
static getPhoneNumberWithoutZero(String number) {
String newNumber = "";
if (number.startsWith('0')) {

@ -4,12 +4,15 @@ 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/common_models/privilege/PrivilegeModel.dart';
import 'package:hmg_patient_app_new/core/exceptions/api_failure.dart';
import 'package:hmg_patient_app_new/features/authentication/models/request_models/check_activation_code_register_request_model.dart';
import 'package:hmg_patient_app_new/features/authentication/models/resp_models/select_device_by_imei.dart';
import 'package:hmg_patient_app_new/services/logger_service.dart';
abstract class AuthenticationRepo {
Future<Either<Failure, GenericApiModel<dynamic>>> getServicePrivilege();
Future<Either<Failure, GenericApiModel<SelectDeviceByImeiRespModelElement>>> selectDeviceByImei({required String firebaseToken});
Future<Either<Failure, GenericApiModel<dynamic>>> checkPatientAuthentication({required dynamic checkPatientAuthenticationReq});
@ -32,7 +35,6 @@ abstract class AuthenticationRepo {
Future<Either<Failure, GenericApiModel<dynamic>>> insertPatientIMEIData({required dynamic patientIMEIDataRequest});
Future<Either<Failure, GenericApiModel<dynamic>>> insertPatientDeviceData({required dynamic patientDeviceDataRequest});
}
class AuthenticationRepoImp implements AuthenticationRepo {
@ -43,7 +45,7 @@ class AuthenticationRepoImp implements AuthenticationRepo {
@override
Future<Either<Failure, GenericApiModel<SelectDeviceByImeiRespModelElement>>> selectDeviceByImei({required String firebaseToken}) async {
final mapDevice = {"IMEI": firebaseToken};
Map<String, dynamic> mapDevice = {"IMEI": firebaseToken};
try {
GenericApiModel<SelectDeviceByImeiRespModelElement>? apiResponse;
Failure? failure;
@ -390,7 +392,6 @@ class AuthenticationRepoImp implements AuthenticationRepo {
@override
Future<Either<Failure, GenericApiModel>> insertPatientDeviceData({required patientDeviceDataRequest}) {
try {
GenericApiModel<dynamic>? apiResponse;
Failure? failure;
@ -421,4 +422,38 @@ class AuthenticationRepoImp implements AuthenticationRepo {
return Future.value(Left(UnknownFailure(e.toString())));
}
}
@override
Future<Either<Failure, GenericApiModel<dynamic>>> getServicePrivilege() {
Map<String, dynamic> mapDevice = {};
try {
GenericApiModel<dynamic>? apiResponse;
Failure? failure;
return apiClient.post(
ApiConsts.getPrivileges,
body: mapDevice,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
apiResponse = GenericApiModel<dynamic>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: errorMessage,
data: response,
);
} catch (e) {
failure = DataParsingFailure(e.toString());
}
},
).then((_) {
if (failure != null) return Left(failure!);
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
return Right(apiResponse!);
});
} catch (e) {
return Future.value(Left(UnknownFailure(e.toString())));
}
}
}

@ -6,6 +6,10 @@ import 'package:hijri_gregorian_calendar/hijri_gregorian_calendar.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/cache_consts.dart';
import 'package:hmg_patient_app_new/core/common_models/nationality_country_model.dart';
import 'package:hmg_patient_app_new/core/common_models/privilege/HMCProjectListModel.dart';
import 'package:hmg_patient_app_new/core/common_models/privilege/PrivilegeModel.dart';
import 'package:hmg_patient_app_new/core/common_models/privilege/ProjectDetailListModel.dart';
import 'package:hmg_patient_app_new/core/common_models/privilege/VidaPlusProjectListModel.dart';
import 'package:hmg_patient_app_new/core/enums.dart';
import 'package:hmg_patient_app_new/core/utils/loading_utils.dart';
import 'package:hmg_patient_app_new/core/utils/request_utils.dart';
@ -53,7 +57,11 @@ class AuthenticationViewModel extends ChangeNotifier {
_authenticationRepo = authenticationRepo,
_localAuthService = localAuthService;
final TextEditingController nationalIdController = TextEditingController(), phoneNumberController = TextEditingController(), dobController = TextEditingController(), nameController = TextEditingController(), emailController = TextEditingController();
final TextEditingController nationalIdController = TextEditingController(),
phoneNumberController = TextEditingController(),
dobController = TextEditingController(),
nameController = TextEditingController(),
emailController = TextEditingController();
CountryEnum selectedCountrySignup = CountryEnum.saudiArabia;
MaritalStatusTypeEnum? maritalStatus;
GenderTypeEnum? genderType;
@ -170,6 +178,7 @@ class AuthenticationViewModel extends ChangeNotifier {
// LoadingUtils.showFullScreenLoading();
// String firebaseToken = _appState.deviceToken;
String firebaseToken = await Utils.getStringFromPrefs(CacheConst.pushToken);
// String firebaseToken = "fY1fq_cITMmUCztA3UKKL9:APA91bEb2ZcdCPQPq3QsA0NW6a6btFvN-JjB1Pn3ZCoCzBMmVUhhh1ZQMtRn9tYPQ5G-jHDLiEpVAlBuRCVMkLDxa-zijsqbIui-4A-ynwclDWGFT4bUHTc";
// == ""
// ? "dOGRRszQQMGe_9wA5Hx3kO:APA91bFV5IcIJXvcCXXk0tc2ddtZgWwCPq7sGSuPr-YW7iiJpQZKgFGN9GAzCVOWL8MfheaP1slE8MdxB7lczdPBGdONQ7WbMmhgHcsUCUktq-hsapGXXqc"
// : _appState.deviceToken;
@ -365,18 +374,18 @@ class AuthenticationViewModel extends ChangeNotifier {
required Function(String? message) onWrongActivationCode,
}) async {
final request = RequestUtils.getCommonRequestWelcome(
phoneNumber: phoneNumberController.text,
otpTypeEnum: otpTypeEnum,
deviceToken: _appState.deviceToken,
patientOutSA: true,
loginTokenID: _appState.appAuthToken,
registeredData: null,
nationIdText: nationalIdController.text,
countryCode: selectedCountrySignup.countryCode,
loginType: loginTypeEnum.toInt
).toJson();
bool isForRegister = (_appState.getUserRegistrationPayload.healthId != null || _appState.getUserRegistrationPayload.patientOutSa == true);
phoneNumber: phoneNumberController.text,
otpTypeEnum: otpTypeEnum,
deviceToken: _appState.deviceToken,
patientOutSA: true,
loginTokenID: _appState.appAuthToken,
registeredData: null,
nationIdText: nationalIdController.text,
countryCode: selectedCountrySignup.countryCode,
loginType: loginTypeEnum.toInt)
.toJson();
bool isForRegister = (_appState.getUserRegistrationPayload.healthId != null || _appState.getUserRegistrationPayload.patientOutSa == true);
if (isForRegister) {
if (_appState.getUserRegistrationPayload.patientOutSa == true) request['DOB'] = _appState.getUserRegistrationPayload.dob;
request['HealthId'] = _appState.getUserRegistrationPayload.healthId;
@ -513,12 +522,12 @@ class AuthenticationViewModel extends ChangeNotifier {
_localAuthService.authenticate().then((value) async {
if (value) {
// we have to handle this if verification true;
if(!_appState.isAuthenticated) {
if (!_appState.isAuthenticated) {
loginTypeEnum = (_appState.deviceTypeID == 1 ? LoginTypeEnum.face : LoginTypeEnum.fingerprint);
print(loginTypeEnum);
checkActivationCode(otpTypeEnum:OTPTypeEnum.faceIDFingerprint , activationCode: null, onWrongActivationCode: (String? message) {});
checkActivationCode(otpTypeEnum: OTPTypeEnum.faceIDFingerprint, activationCode: null, onWrongActivationCode: (String? message) {});
insertPatientIMEIData((_appState.deviceTypeID == 1 ? LoginTypeEnum.face.toInt : LoginTypeEnum.fingerprint.toInt));
}else {
} else {
// authenticated = true;
insertPatientIMEIData((_appState.deviceTypeID == 1 ? LoginTypeEnum.face.toInt : LoginTypeEnum.fingerprint.toInt));
}
@ -534,20 +543,19 @@ class AuthenticationViewModel extends ChangeNotifier {
checkLastLoginStatus(Function() onSuccess) async {
Future.delayed(Duration(seconds: 1), () {
if (_appState.getSelectDeviceByImeiRespModelElement != null &&
(_appState.getSelectDeviceByImeiRespModelElement!.logInType == 1 || _appState.getSelectDeviceByImeiRespModelElement!.logInType == 4)) {
phoneNumberController.text = (_appState.getAuthenticatedUser()!.mobileNumber!.startsWith("0")
? _appState.getAuthenticatedUser()!.mobileNumber!.replaceFirst("0", "")
: _appState.getAuthenticatedUser()!.mobileNumber)!;
if (_appState.getSelectDeviceByImeiRespModelElement != null &&
(_appState.getSelectDeviceByImeiRespModelElement!.logInType == 1 || _appState.getSelectDeviceByImeiRespModelElement!.logInType == 4)) {
phoneNumberController.text =
(_appState.getAuthenticatedUser()!.mobileNumber!.startsWith("0") ? _appState.getAuthenticatedUser()!.mobileNumber!.replaceFirst("0", "") : _appState.getAuthenticatedUser()!.mobileNumber)!;
nationalIdController.text = _appState.getAuthenticatedUser()!.nationalityId!;
onSuccess();
} else if((loginTypeEnum == LoginTypeEnum.sms || loginTypeEnum == LoginTypeEnum.whatsapp && _appState.getSelectDeviceByImeiRespModelElement == null) && _appState.getAuthenticatedUser() != null){
phoneNumberController.text = (_appState.getAuthenticatedUser()!.mobileNumber!.startsWith("0")
? _appState.getAuthenticatedUser()!.mobileNumber!.replaceFirst("0", "")
: _appState.getAuthenticatedUser()!.mobileNumber)!;
nationalIdController.text = _appState.getAuthenticatedUser()!.nationalityId!;
onSuccess();
}
} else if ((loginTypeEnum == LoginTypeEnum.sms || loginTypeEnum == LoginTypeEnum.whatsapp && _appState.getSelectDeviceByImeiRespModelElement == null) &&
_appState.getAuthenticatedUser() != null) {
phoneNumberController.text =
(_appState.getAuthenticatedUser()!.mobileNumber!.startsWith("0") ? _appState.getAuthenticatedUser()!.mobileNumber!.replaceFirst("0", "") : _appState.getAuthenticatedUser()!.mobileNumber)!;
nationalIdController.text = _appState.getAuthenticatedUser()!.nationalityId!;
onSuccess();
}
});
}
@ -582,9 +590,7 @@ class AuthenticationViewModel extends ChangeNotifier {
});
}
Future<void> onRegistrationComplete() async{
Future<void> onRegistrationComplete() async {
// if (emailAddress.text.isEmpty) {
// Utils.showErrorToast(TranslationBase.of(context).enterEmailAddress);
// return;
@ -595,7 +601,6 @@ class AuthenticationViewModel extends ChangeNotifier {
//authVM!.clearDefaultInputValues();
}
Future<void> checkUserStatusForRegistration({required dynamic response, required dynamic request}) async {
if (response is Map) {
_appState.setAppAuthToken = response["LogInTokenID"];
@ -702,22 +707,52 @@ class AuthenticationViewModel extends ChangeNotifier {
_appState.setUserRegistrationPayload = RegistrationDataModelPayload.fromJson(request);
}
Future<void> insertPatientIMEIData(int loginType) async{
final resultEither = await _authenticationRepo.insertPatientIMEIData(patientIMEIDataRequest: PatientInsertDeviceImei(imei: _appState.deviceToken, deviceTypeId: _appState.getDeviceTypeID(), patientId: _appState.getAuthenticatedUser()!.patientId!, patientIdentificationNo:_appState.getAuthenticatedUser()!.nationalityId!, firstName: _appState.getAuthenticatedUser()!.firstName!, lastName: _appState.getAuthenticatedUser()!.lastName!, patientTypeId: _appState.getAuthenticatedUser()!.patientType, mobileNo:_appState.getAuthenticatedUser()!.mobileNumber!, logInTypeId: loginType, patientOutSa:_appState.getAuthenticatedUser()!.outSa!, outSa: _appState.getAuthenticatedUser()!.outSa == 1 ? true :false, biometricEnabled: loginType == 1 || loginType ==2 ? false :true, firstNameN:_appState.getAuthenticatedUser()!.firstNameN , lastNameN:_appState.getAuthenticatedUser()!.lastNameN ).toJson());
resultEither.fold((failure) async => await _errorHandlerService.handleError(failure: failure), (apiResponse) async {
if (apiResponse.messageStatus == 1) {
log("Insert IMEI Success");
insertPatientDeviceData( loginType);
} else {
log("Insert IMEI Failed");
}
});
}
Future<void> insertPatientDeviceData(int loginType) async{
Future<void> insertPatientIMEIData(int loginType) async {
final resultEither = await _authenticationRepo.insertPatientIMEIData(
patientIMEIDataRequest: PatientInsertDeviceImei(
imei: _appState.deviceToken,
deviceTypeId: _appState.getDeviceTypeID(),
patientId: _appState.getAuthenticatedUser()!.patientId!,
patientIdentificationNo: _appState.getAuthenticatedUser()!.patientIdentificationNo!,
firstName: _appState.getAuthenticatedUser()!.firstName!,
lastName: _appState.getAuthenticatedUser()!.lastName!,
patientTypeId: _appState.getAuthenticatedUser()!.patientType,
mobileNo: _appState.getAuthenticatedUser()!.mobileNumber!,
logInTypeId: loginType,
patientOutSa: _appState.getAuthenticatedUser()!.outSa!,
outSa: _appState.getAuthenticatedUser()!.outSa == 1 ? true : false,
biometricEnabled: loginType == 1 || loginType == 2 ? false : true,
firstNameN: _appState.getAuthenticatedUser()!.firstNameN,
lastNameN: _appState.getAuthenticatedUser()!.lastNameN,
).toJson());
resultEither.fold((failure) async => await _errorHandlerService.handleError(failure: failure), (apiResponse) async {
if (apiResponse.messageStatus == 1) {
log("Insert IMEI Success");
insertPatientDeviceData(loginType);
} else {
log("Insert IMEI Failed");
}
});
}
final resultEither = await _authenticationRepo.insertPatientDeviceData(patientDeviceDataRequest: InsertPatientMobileDeviceInfo(deviceToken: _appState.deviceToken, deviceTypeId: _appState.getDeviceTypeID(), patientId: _appState.getAuthenticatedUser()!.patientId!, patientTypeId: _appState.getAuthenticatedUser()!.patientType, patientOutSa:_appState.getAuthenticatedUser()!.outSa!, loginType: loginType, languageId: _appState.getLanguageID(), latitude: _appState.userLat, longitude:_appState.userLong, voipToken: "", deviceType: _appState.deviceTypeID, patientMobileNumber:_appState.getAuthenticatedUser()!.mobileNumber, nationalId: _appState.getAuthenticatedUser()!.patientIdentificationNo, gender: _appState.getAuthenticatedUser()!.gender ).toJson());
Future<void> insertPatientDeviceData(int loginType) async {
final resultEither = await _authenticationRepo.insertPatientDeviceData(
patientDeviceDataRequest: InsertPatientMobileDeviceInfo(
deviceToken: _appState.deviceToken,
deviceTypeId: _appState.getDeviceTypeID(),
patientId: _appState.getAuthenticatedUser()!.patientId!,
patientTypeId: _appState.getAuthenticatedUser()!.patientType,
patientOutSa: _appState.getAuthenticatedUser()!.outSa!,
loginType: loginType,
languageId: _appState.getLanguageID(),
latitude: _appState.userLat,
longitude: _appState.userLong,
voipToken: "",
deviceType: _appState.deviceTypeID,
patientMobileNumber: _appState.getAuthenticatedUser()!.mobileNumber,
nationalId: _appState.getAuthenticatedUser()!.patientIdentificationNo,
gender: _appState.getAuthenticatedUser()!.gender)
.toJson());
resultEither.fold((failure) async => await _errorHandlerService.handleError(failure: failure), (apiResponse) async {
if (apiResponse.messageStatus == 1) {
log("Insert Device Data Success");
@ -725,7 +760,6 @@ class AuthenticationViewModel extends ChangeNotifier {
log("Insert IMEI Failed");
}
});
}
@override
@ -735,4 +769,41 @@ class AuthenticationViewModel extends ChangeNotifier {
myFocusNode.dispose();
super.dispose();
}
Future<void> getServicePrivilege() async {
final resultEither = await _authenticationRepo.getServicePrivilege();
List<PrivilegeModel> privilegeModelList = [];
List<VidaPlusProjectListModel> vidaPlusProjectListModel = [];
List<HMCProjectListModel> hMCProjectListModel = [];
List<ProjectDetailListModel> projectDetailListModel = [];
resultEither.fold(
(failure) async => await _errorHandlerService.handleError(failure: failure),
(apiResponse) async {
if (apiResponse.messageStatus == 2) {
await _dialogService.showErrorBottomSheet(message: apiResponse.errorMessage ?? "ErrorEmpty");
} else {
apiResponse.data["ServicePrivilegeList"].forEach((v) {
privilegeModelList.add(PrivilegeModel.fromJson(v));
});
_appState.setPrivilegeModelList(privilegeModelList);
apiResponse.data["ProjectListVidaPlus"].forEach((v) {
vidaPlusProjectListModel.add(VidaPlusProjectListModel.fromJson(v));
});
_appState.setVidaPlusProjectList(vidaPlusProjectListModel);
apiResponse.data["HMCProjectList"].forEach((v) {
hMCProjectListModel.add(HMCProjectListModel.fromJson(v));
});
_appState.setHMCProjectList(hMCProjectListModel);
apiResponse.data["ProjectDetailList"].forEach((v) {
projectDetailListModel.add(ProjectDetailListModel.fromJson(v));
});
_appState.setProjectsDetailList(projectDetailListModel);
}
},
);
}
}

@ -106,7 +106,7 @@ class InsuranceRepoImp implements InsuranceRepo {
@override
Future<Either<Failure, GenericApiModel<PatientInsuranceUpdateResponseModel>>> getPatientInsuranceDetailsForUpdate({required String patientId, required String identificationNo}) async {
final mapDevice = {"SetupID": "010266", "ProjectID": 15, "PatientIdentificationID": identificationNo, "IsFamily": false, "ParentID": 0};
Map<String, dynamic> mapDevice = {"SetupID": "010266", "ProjectID": 15, "PatientIdentificationID": identificationNo, "IsFamily": false, "ParentID": 0};
try {
GenericApiModel<PatientInsuranceUpdateResponseModel>? apiResponse;

@ -8,8 +8,7 @@ import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/
import 'package:hmg_patient_app_new/services/logger_service.dart';
abstract class MyAppointmentsRepo {
Future<Either<Failure, GenericApiModel<List<PatientAppointmentHistoryResponseModel>>>> getPatientAppointments(
{required bool isActiveAppointment, required bool isArrivedAppointments});
Future<Either<Failure, GenericApiModel<List<PatientAppointmentHistoryResponseModel>>>> getPatientAppointments({required bool isActiveAppointment, required bool isArrivedAppointments});
Future<Either<Failure, GenericApiModel<PatientAppointmentShareResponseModel>>> getPatientShareAppointment({required int projectID, required int clinicID, required String appointmentNo});
@ -28,6 +27,11 @@ abstract class MyAppointmentsRepo {
Future<Either<Failure, GenericApiModel<dynamic>>> generateAppointmentQR({required int clinicID, required int projectID, required String appointmentNo, required int isFollowUp});
Future<Either<Failure, GenericApiModel<dynamic>>> cancelAppointment({required PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel});
Future<Either<Failure, GenericApiModel<dynamic>>> confirmAppointment({required PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel});
Future<Either<Failure, GenericApiModel<dynamic>>> sendCheckInNfcRequest(
{required PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel, required String scannedCode, required int checkInType});
}
class MyAppointmentsRepoImp implements MyAppointmentsRepo {
@ -37,8 +41,7 @@ class MyAppointmentsRepoImp implements MyAppointmentsRepo {
MyAppointmentsRepoImp({required this.loggerService, required this.apiClient});
@override
Future<Either<Failure, GenericApiModel<List<PatientAppointmentHistoryResponseModel>>>> getPatientAppointments(
{required bool isActiveAppointment, required bool isArrivedAppointments}) async {
Future<Either<Failure, GenericApiModel<List<PatientAppointmentHistoryResponseModel>>>> getPatientAppointments({required bool isActiveAppointment, required bool isArrivedAppointments}) async {
Map<String, dynamic> mapDevice = {
"IsActiveAppointment": isActiveAppointment,
"isDentalAllowedBackend": false,
@ -310,4 +313,86 @@ class MyAppointmentsRepoImp implements MyAppointmentsRepo {
return Left(UnknownFailure(e.toString()));
}
}
@override
Future<Either<Failure, GenericApiModel>> confirmAppointment({required PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel}) async {
Map<String, dynamic> requestBody = {
"AppointmentNumber": patientAppointmentHistoryResponseModel.appointmentNo,
"IsLiveCareAppointment": patientAppointmentHistoryResponseModel.isLiveCareAppointment,
"ClinicID": patientAppointmentHistoryResponseModel.clinicID,
"ProjectID": patientAppointmentHistoryResponseModel.projectID,
"ConfirmationBy": 102,
};
try {
GenericApiModel<dynamic>? apiResponse;
Failure? failure;
await apiClient.post(
CONFIRM_APPOINTMENT,
body: requestBody,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
apiResponse = GenericApiModel<dynamic>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: null,
data: response,
);
} catch (e) {
failure = DataParsingFailure(e.toString());
}
},
);
if (failure != null) return Left(failure!);
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
return Right(apiResponse!);
} catch (e) {
return Left(UnknownFailure(e.toString()));
}
}
@override
Future<Either<Failure, GenericApiModel>> sendCheckInNfcRequest(
{required PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel, required String scannedCode, required int checkInType}) async {
Map<String, dynamic> requestBody = {
"AppointmentNo": patientAppointmentHistoryResponseModel.appointmentNo,
"NFC_Code": scannedCode,
"ProjectID": patientAppointmentHistoryResponseModel.projectID,
"ClinicID": patientAppointmentHistoryResponseModel.clinicID,
"CheckinBy": checkInType,
};
try {
GenericApiModel<dynamic>? apiResponse;
Failure? failure;
await apiClient.post(
SEND_CHECK_IN_NFC_REQUEST,
body: requestBody,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
apiResponse = GenericApiModel<dynamic>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: errorMessage,
data: response,
);
} catch (e) {
failure = DataParsingFailure(e.toString());
}
},
isAllowAny: true,
);
if (failure != null) return Left(failure!);
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
return Right(apiResponse!);
} catch (e) {
return Left(UnknownFailure(e.toString()));
}
}
}

@ -14,6 +14,10 @@ class MyAppointmentsViewModel extends ChangeNotifier {
bool isAppointmentPatientShareLoading = false;
List<PatientAppointmentHistoryResponseModel> patientAppointmentsHistoryList = [];
List<PatientAppointmentHistoryResponseModel> patientUpcomingAppointmentsHistoryList = [];
List<PatientAppointmentHistoryResponseModel> patientArrivedAppointmentsHistoryList = [];
PatientAppointmentShareResponseModel? patientAppointmentShareResponseModel;
MyAppointmentsViewModel({required this.myAppointmentsRepo, required this.errorHandlerService});
@ -25,6 +29,8 @@ class MyAppointmentsViewModel extends ChangeNotifier {
initAppointmentsViewModel() {
patientAppointmentsHistoryList.clear();
patientUpcomingAppointmentsHistoryList.clear();
patientArrivedAppointmentsHistoryList.clear();
isMyAppointmentsLoading = true;
isAppointmentPatientShareLoading = true;
notifyListeners();
@ -50,6 +56,7 @@ class MyAppointmentsViewModel extends ChangeNotifier {
Future<void> getPatientAppointments(bool isActiveAppointment, bool isArrivedAppointments, {Function(dynamic)? onSuccess, Function(String)? onError}) async {
final result = await myAppointmentsRepo.getPatientAppointments(isActiveAppointment: isActiveAppointment, isArrivedAppointments: isArrivedAppointments);
final resultArrived = await myAppointmentsRepo.getPatientAppointments(isActiveAppointment: false, isArrivedAppointments: true);
result.fold(
(failure) async => await errorHandlerService.handleError(failure: failure),
@ -57,7 +64,7 @@ class MyAppointmentsViewModel extends ChangeNotifier {
if (apiResponse.messageStatus == 2) {
// dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
} else if (apiResponse.messageStatus == 1) {
patientAppointmentsHistoryList = apiResponse.data!;
patientUpcomingAppointmentsHistoryList = apiResponse.data!;
isMyAppointmentsLoading = false;
notifyListeners();
if (onSuccess != null) {
@ -66,6 +73,29 @@ class MyAppointmentsViewModel extends ChangeNotifier {
}
},
);
resultArrived.fold(
(failure) async => await errorHandlerService.handleError(failure: failure),
(apiResponse) {
if (apiResponse.messageStatus == 2) {
// dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
} else if (apiResponse.messageStatus == 1) {
patientArrivedAppointmentsHistoryList = apiResponse.data!;
isMyAppointmentsLoading = false;
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse);
}
}
},
);
patientAppointmentsHistoryList.addAll(patientUpcomingAppointmentsHistoryList);
patientAppointmentsHistoryList.addAll(patientArrivedAppointmentsHistoryList);
print('Upcoming Appointments: ${patientUpcomingAppointmentsHistoryList.length}');
print('Arrived Appointments: ${patientArrivedAppointmentsHistoryList.length}');
print('All Appointments: ${patientAppointmentsHistoryList.length}');
}
Future<void> getPatientShareAppointment(int projectID, int clinicID, String appointmentNo, {Function(dynamic)? onSuccess, Function(String)? onError}) async {
@ -145,6 +175,25 @@ class MyAppointmentsViewModel extends ChangeNotifier {
);
}
Future<void> confirmAppointment({required PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel, Function(dynamic)? onSuccess, Function(String)? onError}) async {
final result = await myAppointmentsRepo.confirmAppointment(patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel);
result.fold(
(failure) async => await errorHandlerService.handleError(failure: failure),
(apiResponse) {
if (apiResponse.messageStatus == 2) {
onError!(apiResponse.errorMessage!);
// dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
} else if (apiResponse.messageStatus == 1) {
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse);
}
}
},
);
}
Future<void> createAdvancePayment(
{required String paymentMethodName,
required int projectID,
@ -180,4 +229,28 @@ class MyAppointmentsViewModel extends ChangeNotifier {
},
);
}
Future<void> sendCheckInNfcRequest(
{required PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel,
required String scannedCode,
required int checkInType,
Function(dynamic)? onSuccess,
Function(String)? onError}) async {
final result = await myAppointmentsRepo.sendCheckInNfcRequest(patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel, scannedCode: scannedCode, checkInType: checkInType);
result.fold(
(failure) async => await errorHandlerService.handleError(failure: failure),
(apiResponse) {
if (apiResponse.messageStatus == 2) {
onError!(apiResponse.errorMessage!);
// dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
} else if (apiResponse.messageStatus == 1) {
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse);
}
}
},
);
}
}

@ -43,6 +43,25 @@ class AppointmentType {
}
}
static Color getNextActionTextColor(nextAction) {
switch (nextAction) {
case 0:
return AppColors.successColor;
case 10:
return AppColors.successColor;
case 15:
return AppColors.textColor;
case 20:
return AppColors.infoColor;
case 50:
return AppColors.successColor;
case 90:
return AppColors.alertColor;
default:
return AppColors.successColor;
}
}
static Color getNextActionButtonColor(nextAction) {
switch (nextAction) {
case 0:

@ -21,23 +21,7 @@ class PrescriptionsRepoImp implements PrescriptionsRepo {
@override
Future<Either<Failure, GenericApiModel<List<PatientPrescriptionsResponseModel>>>> getPatientPrescriptionOrders({required String patientId}) async {
final mapDevice = {
"isDentalAllowedBackend": false,
"VersionID": 50.0,
"Channel": 3,
"LanguageID": 2,
"IPAdress": "10.20.10.20",
"generalid": "Cs2020@2016\$2958",
"Latitude": 0.0,
"Longitude": 0.0,
"DeviceTypeID": 1,
"PatientType": 1,
"PatientTypeID": 1,
"TokenID": "@dm!n",
"PatientID": "1018977",
"PatientOutSA": "0",
"SessionID": "03478TYC02N80874CTYN04883475!?"
};
Map<String, dynamic> mapDevice = {};
try {
GenericApiModel<List<PatientPrescriptionsResponseModel>>? apiResponse;
@ -52,7 +36,7 @@ class PrescriptionsRepoImp implements PrescriptionsRepo {
try {
final list = response['PatientPrescriptionList'];
if (list == null || list.isEmpty) {
throw Exception("lab list is empty");
// throw Exception("lab list is empty");
}
final prescriptionOrders = list.map((item) => PatientPrescriptionsResponseModel.fromJson(item as Map<String, dynamic>)).toList().cast<PatientPrescriptionsResponseModel>();
@ -78,28 +62,13 @@ class PrescriptionsRepoImp implements PrescriptionsRepo {
@override
Future<Either<Failure, GenericApiModel<List<PrescriptionDetailResponseModel>>>> getPatientPrescriptionDetails({required PatientPrescriptionsResponseModel prescriptionsResponseModel}) async {
final mapDevice = {
Map<String, dynamic> mapDevice = {
"AppointmentNo": prescriptionsResponseModel.appointmentNo.toString(),
"SetupID": prescriptionsResponseModel.setupID,
"EpisodeID": prescriptionsResponseModel.episodeID.toString(),
"ClinicID": prescriptionsResponseModel.clinicID.toString(),
"ProjectID": prescriptionsResponseModel.projectID.toString(),
"DischargeNo": prescriptionsResponseModel.dischargeNo.toString(),
"isDentalAllowedBackend": false,
"VersionID": 50.0,
"Channel": 3,
"LanguageID": 2,
"IPAdress": "10.20.10.20",
"generalid": "Cs2020@2016\$2958",
"Latitude": 0.0,
"Longitude": 0.0,
"DeviceTypeID": 1,
"PatientType": 1,
"PatientTypeID": 1,
"TokenID": "@dm!n",
"PatientID": "1018977",
"PatientOutSA": "0",
"SessionID": "03478TYC02N80874CTYN04883475!?"
"DischargeNo": prescriptionsResponseModel.dischargeNo.toString()
};
try {
@ -115,7 +84,7 @@ class PrescriptionsRepoImp implements PrescriptionsRepo {
try {
final list = prescriptionsResponseModel.isInOutPatient! ? response['ListPRM'] : response['INP_GetPrescriptionReport_List'];
if (list == null || list.isEmpty) {
throw Exception("prescription list is empty");
// throw Exception("prescription list is empty");
}
final prescriptionOrders = list.map((item) => PrescriptionDetailResponseModel.fromJson(item as Map<String, dynamic>)).toList().cast<PrescriptionDetailResponseModel>();

@ -31,7 +31,6 @@ class PrescriptionsViewModel extends ChangeNotifier {
patientPrescriptionOrdersByHospital.clear();
patientPrescriptionOrdersViewList.clear();
isPrescriptionsOrdersLoading = true;
isPrescriptionsDetailsLoading = true;
isSortByClinic = true;
getPatientPrescriptionOrders();
notifyListeners();

@ -1,6 +1,9 @@
import 'dart:async';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
@ -11,16 +14,24 @@ import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/patient_appointment_history_response_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/utils/appointment_type.dart';
import 'package:hmg_patient_app_new/features/prescriptions/models/resp_models/patient_prescriptions_response_model.dart';
import 'package:hmg_patient_app_new/features/prescriptions/prescriptions_view_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/appointments/appointment_payment_page.dart';
import 'package:hmg_patient_app_new/presentation/appointments/widgets/appointment_checkin_bottom_sheet.dart';
import 'package:hmg_patient_app_new/presentation/appointments/widgets/appointment_doctor_card.dart';
import 'package:hmg_patient_app_new/presentation/prescriptions/prescription_detail_page.dart';
import 'package:hmg_patient_app_new/presentation/prescriptions/prescriptions_list_page.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
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/shimmer/movies_shimmer_widget.dart';
import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart';
import 'package:maps_launcher/maps_launcher.dart';
import 'package:provider/provider.dart';
import '../medical_file/widgets/medical_file_card.dart';
class AppointmentDetailsPage extends StatefulWidget {
AppointmentDetailsPage({super.key, required this.patientAppointmentHistoryResponseModel});
@ -32,10 +43,23 @@ class AppointmentDetailsPage extends StatefulWidget {
class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
late MyAppointmentsViewModel myAppointmentsViewModel;
late PrescriptionsViewModel prescriptionsViewModel;
@override
void initState() {
scheduleMicrotask(() {
if (AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel)) {
prescriptionsViewModel.setPrescriptionsDetailsLoading();
prescriptionsViewModel.getPrescriptionDetails(getPrescriptionRequestModel());
}
});
super.initState();
}
@override
Widget build(BuildContext context) {
myAppointmentsViewModel = Provider.of<MyAppointmentsViewModel>(context);
prescriptionsViewModel = Provider.of<PrescriptionsViewModel>(context);
return Scaffold(
backgroundColor: AppColors.bgScaffoldColor,
appBar: AppBar(
@ -52,7 +76,23 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
"Appointment Details".needTranslation.toText24(isBold: true),
"Appointment Details".needTranslation.toText20(isBold: true),
if (AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel))
CustomButton(
text: "Report".needTranslation,
onPressed: () {},
backgroundColor: AppColors.secondaryLightRedColor,
borderColor: AppColors.secondaryLightRedColor,
textColor: AppColors.primaryRedColor,
fontSize: 14,
fontWeight: FontWeight.w500,
borderRadius: 12,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 40.h,
iconSize: 16.h,
icon: AppAssets.report_icon,
iconColor: AppColors.primaryRedColor,
)
],
),
SizedBox(height: 24.h),
@ -74,8 +114,7 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
Navigator.of(context).pop();
showCommonBottomSheet(context,
child: Utils.getSuccessWidget(loadingText: "Appointment Cancelled Successfully".needTranslation),
callBackFunc: (str) {
},
callBackFunc: (str) {},
title: "",
height: ResponsiveExtension.screenHeight * 0.3,
isCloseButtonVisible: false,
@ -89,106 +128,266 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
onRescheduleTap: () {},
),
SizedBox(height: 16.h),
if (!AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel))
Column(
children: [
Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 20.h,
hasShadow: false,
),
child: Padding(
padding: EdgeInsets.all(16.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
"Appointment Status".needTranslation.toText16(isBold: true),
],
),
SizedBox(height: 4.h),
(!AppointmentType.isConfirmed(widget.patientAppointmentHistoryResponseModel)
? "Not Confirmed".needTranslation.toText12(color: AppColors.primaryRedColor, fontWeight: FontWeight.w500)
: "Confirmed".needTranslation.toText12(color: AppColors.successColor, fontWeight: FontWeight.w500)),
SizedBox(height: 16.h),
Stack(
!AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel)
? Column(
children: [
Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 20.h,
hasShadow: false,
),
child: Padding(
padding: EdgeInsets.all(16.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ClipRRect(
clipBehavior: Clip.hardEdge,
borderRadius: BorderRadius.circular(24),
child: Image.network(
"https://maps.googleapis.com/maps/api/staticmap?center=${widget.patientAppointmentHistoryResponseModel.latitude},${widget.patientAppointmentHistoryResponseModel.longitude}&zoom=14&size=350x165&maptype=roadmap&markers=color:red%7C${widget.patientAppointmentHistoryResponseModel.latitude},${widget.patientAppointmentHistoryResponseModel.longitude}&key=AIzaSyB6TERnxIr0yJ3qG4ULBZbu0sAD4tGqtng",
fit: BoxFit.contain,
),
Row(
children: [
"Appointment Status".needTranslation.toText16(isBold: true),
],
),
Positioned(
bottom: 0,
child: SizedBox(
width: MediaQuery.of(context).size.width * 0.785,
child: CustomButton(
text: "Get Directions".needTranslation,
onPressed: () {
MapsLauncher.launchCoordinates(double.parse(widget.patientAppointmentHistoryResponseModel.latitude!),
double.parse(widget.patientAppointmentHistoryResponseModel.longitude!), widget.patientAppointmentHistoryResponseModel.projectName);
},
backgroundColor: AppColors.textColor.withOpacity(0.8),
borderColor: AppointmentType.getNextActionButtonColor(widget.patientAppointmentHistoryResponseModel.nextAction).withOpacity(0.01),
textColor: AppColors.whiteColor,
fontSize: 14,
fontWeight: FontWeight.w500,
borderRadius: 12.h,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 40.h,
icon: AppAssets.directions_icon,
iconColor: AppColors.whiteColor,
iconSize: 13.h,
).paddingAll(12.h),
),
SizedBox(height: 4.h),
(!AppointmentType.isConfirmed(widget.patientAppointmentHistoryResponseModel)
? "Not Confirmed".needTranslation.toText12(color: AppColors.primaryRedColor, fontWeight: FontWeight.w500)
: "Confirmed".needTranslation.toText12(color: AppColors.successColor, fontWeight: FontWeight.w500)),
SizedBox(height: 16.h),
Stack(
children: [
ClipRRect(
clipBehavior: Clip.hardEdge,
borderRadius: BorderRadius.circular(24),
child: Image.network(
"https://maps.googleapis.com/maps/api/staticmap?center=${widget.patientAppointmentHistoryResponseModel.latitude},${widget.patientAppointmentHistoryResponseModel.longitude}&zoom=14&size=350x165&maptype=roadmap&markers=color:red%7C${widget.patientAppointmentHistoryResponseModel.latitude},${widget.patientAppointmentHistoryResponseModel.longitude}&key=AIzaSyB6TERnxIr0yJ3qG4ULBZbu0sAD4tGqtng",
fit: BoxFit.contain,
),
),
Positioned(
bottom: 0,
child: SizedBox(
width: MediaQuery.of(context).size.width * 0.785,
child: CustomButton(
text: "Get Directions".needTranslation,
onPressed: () {
MapsLauncher.launchCoordinates(double.parse(widget.patientAppointmentHistoryResponseModel.latitude!),
double.parse(widget.patientAppointmentHistoryResponseModel.longitude!), widget.patientAppointmentHistoryResponseModel.projectName);
},
backgroundColor: AppColors.textColor.withOpacity(0.8),
borderColor: AppointmentType.getNextActionButtonColor(widget.patientAppointmentHistoryResponseModel.nextAction).withOpacity(0.01),
textColor: AppColors.whiteColor,
fontSize: 14,
fontWeight: FontWeight.w500,
borderRadius: 12.h,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 40.h,
icon: AppAssets.directions_icon,
iconColor: AppColors.whiteColor,
iconSize: 13.h,
).paddingAll(12.h),
),
),
],
),
],
),
],
),
),
),
),
SizedBox(height: 16.h),
Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 20.h,
hasShadow: false,
),
child: Row(
mainAxisSize: MainAxisSize.max,
children: [
Utils.buildSvgWithAssets(icon: AppAssets.prescription_reminder_icon, width: 35.h, height: 35.h),
SizedBox(width: 8.h),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
SizedBox(height: 16.h),
Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 20.h,
hasShadow: false,
),
child: Row(
mainAxisSize: MainAxisSize.max,
children: [
LocaleKeys.setReminder.tr(context: context).toText13(isBold: true),
"Notify me before the appointment".needTranslation.toText11(color: AppColors.textColorLight, weight: FontWeight.w500),
Utils.buildSvgWithAssets(icon: AppAssets.prescription_reminder_icon, width: 35.h, height: 35.h),
SizedBox(width: 8.h),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
LocaleKeys.setReminder.tr(context: context).toText13(isBold: true),
"Notify me before the appointment".needTranslation.toText11(color: AppColors.textColorLight, weight: FontWeight.w500),
],
),
const Spacer(),
Switch(
activeColor: AppColors.successColor,
activeTrackColor: AppColors.successColor.withValues(alpha: .15),
value: widget.patientAppointmentHistoryResponseModel.hasReminder!,
onChanged: (newValue) {
setState(() {
myAppointmentsViewModel.setAppointmentReminder(newValue, widget.patientAppointmentHistoryResponseModel);
});
},
),
],
),
const Spacer(),
Switch(
activeColor: AppColors.successColor,
activeTrackColor: AppColors.successColor.withValues(alpha: .15),
value: widget.patientAppointmentHistoryResponseModel.hasReminder!,
onChanged: (newValue) {
setState(() {
myAppointmentsViewModel.setAppointmentReminder(newValue, widget.patientAppointmentHistoryResponseModel);
});
},
),
],
).paddingSymmetrical(16.h, 16.h),
).paddingSymmetrical(16.h, 16.h),
),
SizedBox(height: 16.h),
],
)
: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
"Lab & Radiology".needTranslation.toText18(isBold: true),
SizedBox(height: 16.h),
GridView(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2, crossAxisSpacing: 13.h, mainAxisSpacing: 13.h, childAspectRatio: 7 / 6),
physics: NeverScrollableScrollPhysics(),
shrinkWrap: true,
children: [
MedicalFileCard(
label: LocaleKeys.labResults.tr(context: context),
textColor: AppColors.blackColor,
backgroundColor: AppColors.whiteColor,
svgIcon: AppAssets.lab_result_icon,
iconSize: 40,
isLargeText: true,
),
MedicalFileCard(
label: LocaleKeys.radiology.tr(context: context),
textColor: AppColors.blackColor,
backgroundColor: AppColors.whiteColor,
svgIcon: AppAssets.radiology_icon,
iconSize: 40,
isLargeText: true,
),
],
),
LocaleKeys.prescriptions.tr().toText18(isBold: true),
SizedBox(height: 16.h),
Consumer<PrescriptionsViewModel>(builder: (context, prescriptionVM, child) {
return prescriptionVM.isPrescriptionsDetailsLoading
? const MoviesShimmerWidget()
: Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: Colors.white,
borderRadius: 20.h,
),
child: Padding(
padding: EdgeInsets.all(16.h),
child: Column(
children: [
ListView.separated(
itemCount: prescriptionVM.prescriptionDetailsList.length,
shrinkWrap: true,
padding: const EdgeInsets.only(left: 0, right: 8),
physics: NeverScrollableScrollPhysics(),
itemBuilder: (context, index) {
return AnimationConfiguration.staggeredList(
position: index,
duration: const Duration(milliseconds: 500),
child: SlideAnimation(
verticalOffset: 100.0,
child: FadeInAnimation(
child: Row(
children: [
Utils.buildSvgWithAssets(
icon: AppAssets.prescription_item_icon,
width: 40.h,
height: 40.h,
),
SizedBox(width: 8.h),
Row(
mainAxisSize: MainAxisSize.max,
children: [
Column(
children: [
SizedBox(width: 150.h, child: prescriptionVM.prescriptionDetailsList[index].itemDescription!.toText12(isBold: true, maxLine: 1)),
SizedBox(
width: 150.h,
child:
"Prescribed By: ${widget.patientAppointmentHistoryResponseModel.doctorTitle} ${widget.patientAppointmentHistoryResponseModel.doctorNameObj}"
.needTranslation
.toText10(weight: FontWeight.w500, color: AppColors.greyTextColor, letterSpacing: -0.4),
),
],
),
SizedBox(width: 68.h),
Utils.buildSvgWithAssets(
icon: AppAssets.forward_arrow_icon,
iconColor: AppColors.blackColor,
width: 18.h,
height: 13.h,
fit: BoxFit.contain,
),
],
),
],
),
),
),
);
},
separatorBuilder: (BuildContext cxt, int index) => SizedBox(height: 16.h),
).onPress(() {
prescriptionVM.setPrescriptionsDetailsLoading();
Navigator.of(context).push(
FadePage(
page: PrescriptionDetailPage(prescriptionsResponseModel: getPrescriptionRequestModel()),
),
);
}),
SizedBox(height: 16.h),
const Divider(color: AppColors.dividerColor),
SizedBox(height: 16.h),
Row(
children: [
// Expanded(
// child: CustomButton(
// text: widget.prescriptionsResponseModel.isHomeMedicineDeliverySupported! ? LocaleKeys.resendOrder.tr(context: context) : LocaleKeys.prescriptionDeliveryError.tr(context: context),
// onPressed: () {},
// backgroundColor: AppColors.secondaryLightRedColor,
// borderColor: AppColors.secondaryLightRedColor,
// textColor: AppColors.primaryRedColor,
// fontSize: 14,
// fontWeight: FontWeight.w500,
// borderRadius: 12.h,
// height: 40.h,
// icon: AppAssets.appointment_calendar_icon,
// iconColor: AppColors.primaryRedColor,
// iconSize: 16.h,
// ),
// ),
// SizedBox(width: 16.h),
Expanded(
child: CustomButton(
text: "All Prescriptions".needTranslation,
onPressed: () {
Navigator.of(context)
.push(
FadePage(
page: PrescriptionsListPage(),
),
)
.then((val) {
prescriptionsViewModel.setPrescriptionsDetailsLoading();
prescriptionsViewModel.getPrescriptionDetails(getPrescriptionRequestModel());
});
},
backgroundColor: AppColors.secondaryLightRedColor,
borderColor: AppColors.secondaryLightRedColor,
textColor: AppColors.primaryRedColor,
fontSize: 14,
fontWeight: FontWeight.w500,
borderRadius: 12.h,
height: 40.h,
icon: AppAssets.requests,
iconColor: AppColors.primaryRedColor,
iconSize: 16.h,
),
),
],
),
],
),
),
);
}),
],
),
SizedBox(height: 16.h),
],
),
],
).paddingSymmetrical(24.h, 24.h),
),
@ -242,28 +441,39 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
)
],
).paddingOnly(left: 16.h, top: 24.h, right: 16.h, bottom: 0.h),
CustomButton(
text: AppointmentType.getNextActionText(widget.patientAppointmentHistoryResponseModel.nextAction),
onPressed: () {
myAppointmentsViewModel.setIsPatientAppointmentShareLoading(true);
Navigator.of(context).push(
FadePage(
page: AppointmentPaymentPage(patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel),
),
);
},
backgroundColor: AppointmentType.getNextActionButtonColor(widget.patientAppointmentHistoryResponseModel.nextAction),
borderColor: AppointmentType.getNextActionButtonColor(widget.patientAppointmentHistoryResponseModel.nextAction).withOpacity(0.01),
textColor: AppColors.whiteColor,
fontSize: 16,
fontWeight: FontWeight.w500,
borderRadius: 12,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 50.h,
icon: AppointmentType.getNextActionIcon(widget.patientAppointmentHistoryResponseModel.nextAction),
iconColor: AppColors.whiteColor,
iconSize: 18.h,
).paddingSymmetrical(16.h, 24.h),
AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel)
? CustomButton(
text: "Re-book Appointment".needTranslation,
onPressed: () {},
backgroundColor: AppColors.primaryRedColor,
borderColor: AppColors.primaryRedColor,
textColor: AppColors.whiteColor,
fontSize: 16,
fontWeight: FontWeight.w500,
borderRadius: 12,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 50.h,
icon: AppAssets.add_icon,
iconColor: AppColors.whiteColor,
iconSize: 18.h,
).paddingSymmetrical(16.h, 24.h)
: CustomButton(
text: AppointmentType.getNextActionText(widget.patientAppointmentHistoryResponseModel.nextAction),
onPressed: () {
handleAppointmentNextAction(widget.patientAppointmentHistoryResponseModel.nextAction);
},
backgroundColor: AppointmentType.getNextActionButtonColor(widget.patientAppointmentHistoryResponseModel.nextAction),
borderColor: AppointmentType.getNextActionButtonColor(widget.patientAppointmentHistoryResponseModel.nextAction).withOpacity(0.01),
textColor: widget.patientAppointmentHistoryResponseModel.nextAction == 15 ? AppColors.textColor : AppColors.whiteColor,
fontSize: 16,
fontWeight: FontWeight.w500,
borderRadius: 12,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 50.h,
icon: AppointmentType.getNextActionIcon(widget.patientAppointmentHistoryResponseModel.nextAction),
iconColor: AppColors.whiteColor,
iconSize: 18.h,
).paddingSymmetrical(16.h, 24.h),
],
),
),
@ -272,4 +482,71 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
),
);
}
Future<void> handleAppointmentNextAction(nextAction) async {
switch (nextAction) {
case 0:
break;
case 10:
showCommonBottomSheet(context,
child: Utils.getLoadingWidget(), callBackFunc: (str) {}, title: "", height: ResponsiveExtension.screenHeight * 0.3, isCloseButtonVisible: false, isDismissible: false, isFullScreen: false);
await myAppointmentsViewModel.confirmAppointment(
patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel,
onSuccess: (apiResponse) {
Navigator.of(context).pop();
showCommonBottomSheet(context,
child: Utils.getSuccessWidget(loadingText: "Appointment Confirmed Successfully".needTranslation),
callBackFunc: (str) {},
title: "",
height: ResponsiveExtension.screenHeight * 0.3,
isCloseButtonVisible: false,
isDismissible: false,
isFullScreen: false,
isSuccessDialog: true);
});
Navigator.of(context).pop();
Navigator.of(context).pop();
case 15:
break;
case 20:
myAppointmentsViewModel.setIsPatientAppointmentShareLoading(true);
Navigator.of(context).push(
FadePage(
page: AppointmentPaymentPage(patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel),
),
);
case 50:
// return LocaleKeys.confirmLiveCare.tr();
case 90:
showCommonBottomSheetWithoutHeight(context,
title: LocaleKeys.onlineCheckIn.tr(),
child: AppointmentCheckinBottomSheet(
patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel,
myAppointmentsViewModel: myAppointmentsViewModel,
),
callBackFunc: () {},
isFullScreen: false);
default:
// return "No Action".needTranslation;
}
}
PatientPrescriptionsResponseModel getPrescriptionRequestModel() {
return PatientPrescriptionsResponseModel(
appointmentNo: widget.patientAppointmentHistoryResponseModel.appointmentNo,
setupID: widget.patientAppointmentHistoryResponseModel.setupID,
episodeID: widget.patientAppointmentHistoryResponseModel.episodeID,
clinicID: widget.patientAppointmentHistoryResponseModel.clinicID,
projectID: widget.patientAppointmentHistoryResponseModel.projectID,
dischargeNo: 0,
isInOutPatient: widget.patientAppointmentHistoryResponseModel.isInOutPatient,
isHomeMedicineDeliverySupported: false,
doctorImageURL: widget.patientAppointmentHistoryResponseModel.doctorImageURL,
doctorName: "${widget.patientAppointmentHistoryResponseModel.doctorTitle} ${widget.patientAppointmentHistoryResponseModel.doctorNameObj}",
appointmentDate: widget.patientAppointmentHistoryResponseModel.appointmentDate,
clinicDescription: widget.patientAppointmentHistoryResponseModel.clinicName,
decimalDoctorRate: widget.patientAppointmentHistoryResponseModel.decimalDoctorRate,
name: widget.patientAppointmentHistoryResponseModel.projectName,
);
}
}

@ -19,12 +19,16 @@ import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/
import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart';
import 'package:hmg_patient_app_new/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/home/navigation_screen.dart';
import 'package:hmg_patient_app_new/presentation/insurance/insurance_home_page.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/shimmer/movies_shimmer_widget.dart';
import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart';
import 'package:provider/provider.dart';
import 'package:smooth_corner/smooth_corner.dart';
@ -220,7 +224,13 @@ class _AppointmentPaymentPageState extends State<AppointmentPaymentPage> {
"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: () {},
onPressed: () {
Navigator.of(context).push(
FadePage(
page: InsuranceHomePage(),
),
);
},
backgroundColor: AppColors.primaryRedColor,
borderColor: AppColors.secondaryLightRedBorderColor,
textColor: AppColors.whiteColor,
@ -366,14 +376,24 @@ class _AppointmentPaymentPageState extends State<AppointmentPaymentPage> {
clinicID: widget.patientAppointmentHistoryResponseModel.clinicID,
projectID: widget.patientAppointmentHistoryResponseModel.projectID,
appointmentNo: widget.patientAppointmentHistoryResponseModel.appointmentNo.toString(),
isFollowUp: myAppointmentsViewModel.patientAppointmentShareResponseModel!.isFollowup!);
isFollowUp: myAppointmentsViewModel.patientAppointmentShareResponseModel!.isFollowup!,
onSuccess: (apiResponse) {
Future.delayed(Duration(milliseconds: 500), () {
Navigator.of(context).pop();
Navigator.pushAndRemoveUntil(
context,
FadePage(
page: LandingNavigation(),
),
(r) => false);
Navigator.of(context).push(
FadePage(page: MyAppointmentsPage()),
);
});
});
}
});
});
Future.delayed(Duration(milliseconds: 500), () {
Navigator.of(context).pop();
print(payfortViewModel.payfortCheckPaymentStatusResponseModel!.responseMessage!);
});
} else {}
});
// checkPaymentStatus(appo);

@ -64,49 +64,154 @@ class _MyAppointmentsPageState extends State<MyAppointmentsPage> {
],
onTabChange: (index) {
print(index);
myAppointmentsViewModel.onTabChange(index);
},
).paddingSymmetrical(24.h, 0.h),
Consumer<MyAppointmentsViewModel>(builder: (context, myAppointmentsVM, child) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 24.h),
// Expandable list
ListView.separated(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemCount: myAppointmentsVM.isMyAppointmentsLoading ? 5 : myAppointmentsVM.patientAppointmentsHistoryList.isNotEmpty ? myAppointmentsVM.patientAppointmentsHistoryList.length : 1,
itemBuilder: (context, index) {
return myAppointmentsVM.isMyAppointmentsLoading
? const MoviesShimmerWidget().paddingSymmetrical(24.h, 0.h)
: myAppointmentsVM.patientAppointmentsHistoryList.isNotEmpty ? AnimationConfiguration.staggeredList(
position: index,
duration: const Duration(milliseconds: 500),
child: SlideAnimation(
verticalOffset: 100.0,
child: FadeInAnimation(
child: AnimatedContainer(
duration: Duration(milliseconds: 300),
curve: Curves.easeInOut,
margin: EdgeInsets.symmetric(vertical: 8.h),
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: true),
child: AppointmentCard(
patientAppointmentHistoryResponseModel: myAppointmentsVM.patientAppointmentsHistoryList[index],
myAppointmentsViewModel: myAppointmentsViewModel,
),
).paddingSymmetrical(24.h, 0.h),
),
),
) : Utils.getNoDataWidget(context);
},
separatorBuilder: (BuildContext cxt, int index) => SizedBox(height: 16.h),
),
],
);
return getSelectedTabData(myAppointmentsVM.selectedTabIndex, myAppointmentsVM);
}),
],
),
),
);
}
Widget getSelectedTabData(int index, MyAppointmentsViewModel myAppointmentsVM) {
switch (index) {
case 0:
//All Appointments Tab Data
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 24.h),
// Expandable list
ListView.separated(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemCount: myAppointmentsVM.isMyAppointmentsLoading
? 5
: myAppointmentsVM.patientAppointmentsHistoryList.isNotEmpty
? myAppointmentsVM.patientAppointmentsHistoryList.length
: 1,
itemBuilder: (context, index) {
return myAppointmentsVM.isMyAppointmentsLoading
? const MoviesShimmerWidget().paddingSymmetrical(24.h, 0.h)
: myAppointmentsVM.patientAppointmentsHistoryList.isNotEmpty
? AnimationConfiguration.staggeredList(
position: index,
duration: const Duration(milliseconds: 500),
child: SlideAnimation(
verticalOffset: 100.0,
child: FadeInAnimation(
child: AnimatedContainer(
duration: Duration(milliseconds: 300),
curve: Curves.easeInOut,
margin: EdgeInsets.symmetric(vertical: 8.h),
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: true),
child: AppointmentCard(
patientAppointmentHistoryResponseModel: myAppointmentsVM.patientAppointmentsHistoryList[index],
myAppointmentsViewModel: myAppointmentsViewModel,
),
).paddingSymmetrical(24.h, 0.h),
),
),
)
: Utils.getNoDataWidget(context);
},
separatorBuilder: (BuildContext cxt, int index) => SizedBox(height: 16.h),
),
],
);
case 1:
//Upcoming Appointments Tab Data
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 24.h),
// Expandable list
ListView.separated(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemCount: myAppointmentsVM.isMyAppointmentsLoading
? 5
: myAppointmentsVM.patientUpcomingAppointmentsHistoryList.isNotEmpty
? myAppointmentsVM.patientUpcomingAppointmentsHistoryList.length
: 1,
itemBuilder: (context, index) {
return myAppointmentsVM.isMyAppointmentsLoading
? const MoviesShimmerWidget().paddingSymmetrical(24.h, 0.h)
: myAppointmentsVM.patientUpcomingAppointmentsHistoryList.isNotEmpty
? AnimationConfiguration.staggeredList(
position: index,
duration: const Duration(milliseconds: 500),
child: SlideAnimation(
verticalOffset: 100.0,
child: FadeInAnimation(
child: AnimatedContainer(
duration: Duration(milliseconds: 300),
curve: Curves.easeInOut,
margin: EdgeInsets.symmetric(vertical: 8.h),
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: true),
child: AppointmentCard(
patientAppointmentHistoryResponseModel: myAppointmentsVM.patientUpcomingAppointmentsHistoryList[index],
myAppointmentsViewModel: myAppointmentsViewModel,
),
).paddingSymmetrical(24.h, 0.h),
),
),
)
: Utils.getNoDataWidget(context);
},
separatorBuilder: (BuildContext cxt, int index) => SizedBox(height: 16.h),
),
],
);
case 2:
//Completed Appointments Tab Data
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 24.h),
// Expandable list
ListView.separated(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemCount: myAppointmentsVM.isMyAppointmentsLoading
? 5
: myAppointmentsVM.patientArrivedAppointmentsHistoryList.isNotEmpty
? myAppointmentsVM.patientArrivedAppointmentsHistoryList.length
: 1,
itemBuilder: (context, index) {
return myAppointmentsVM.isMyAppointmentsLoading
? const MoviesShimmerWidget().paddingSymmetrical(24.h, 0.h)
: myAppointmentsVM.patientArrivedAppointmentsHistoryList.isNotEmpty
? AnimationConfiguration.staggeredList(
position: index,
duration: const Duration(milliseconds: 500),
child: SlideAnimation(
verticalOffset: 100.0,
child: FadeInAnimation(
child: AnimatedContainer(
duration: Duration(milliseconds: 300),
curve: Curves.easeInOut,
margin: EdgeInsets.symmetric(vertical: 8.h),
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: true),
child: AppointmentCard(
patientAppointmentHistoryResponseModel: myAppointmentsVM.patientArrivedAppointmentsHistoryList[index],
myAppointmentsViewModel: myAppointmentsViewModel,
),
).paddingSymmetrical(24.h, 0.h),
),
),
)
: Utils.getNoDataWidget(context);
},
separatorBuilder: (BuildContext cxt, int index) => SizedBox(height: 16.h),
),
],
);
default:
return Container();
}
}
}

@ -1,3 +1,4 @@
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';
@ -10,6 +11,7 @@ import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/patient_appointment_history_response_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/utils/appointment_type.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/appointments/appointment_details_page.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
@ -33,11 +35,16 @@ class _AppointmentCardState extends State<AppointmentCard> {
AppState appState = getIt.get<AppState>();
return InkWell(
onTap: () {
Navigator.of(context).push(
Navigator.of(context)
.push(
FadePage(
page: AppointmentDetailsPage(patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel),
),
);
)
.then((val) {
widget.myAppointmentsViewModel.initAppointmentsViewModel();
widget.myAppointmentsViewModel.getPatientAppointments(true, false);
});
},
child: Padding(
padding: EdgeInsets.all(14.h),
@ -94,24 +101,26 @@ class _AppointmentCardState extends State<AppointmentCard> {
),
),
// TODO: Implement the logic to enable/disable the switch based on reminder status
Switch(
activeColor: AppColors.successColor,
activeTrackColor: AppColors.successColor.withValues(alpha: .15),
thumbIcon: WidgetStateProperty.resolveWith<Icon?>(
(Set<WidgetState> states) {
if (states.contains(WidgetState.selected)) {
return const Icon(Icons.check); // Icon when switch is ON
}
return const Icon(Icons.close); // Icon when switch is OFF
},
),
value: widget.patientAppointmentHistoryResponseModel.hasReminder!,
onChanged: (newValue) {
setState(() {
widget.myAppointmentsViewModel.setAppointmentReminder(newValue, widget.patientAppointmentHistoryResponseModel);
});
},
),
AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel)
? SizedBox()
: Switch(
activeColor: AppColors.successColor,
activeTrackColor: AppColors.successColor.withValues(alpha: .15),
thumbIcon: WidgetStateProperty.resolveWith<Icon?>(
(Set<WidgetState> states) {
if (states.contains(WidgetState.selected)) {
return const Icon(Icons.check); // Icon when switch is ON
}
return const Icon(Icons.close); // Icon when switch is OFF
},
),
value: widget.patientAppointmentHistoryResponseModel.hasReminder!,
onChanged: (newValue) {
setState(() {
widget.myAppointmentsViewModel.setAppointmentReminder(newValue, widget.patientAppointmentHistoryResponseModel);
});
},
),
],
),
SizedBox(height: 16.h),
@ -155,30 +164,32 @@ class _AppointmentCardState extends State<AppointmentCard> {
children: [
Expanded(
flex: 6,
child: CustomButton(
text: AppointmentType.getNextActionText(widget.patientAppointmentHistoryResponseModel.nextAction),
onPressed: () {
Navigator.of(context)
.push(FadePage(
page: AppointmentDetailsPage(patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel),
))
.then((val) {
widget.myAppointmentsViewModel.initAppointmentsViewModel();
widget.myAppointmentsViewModel.getPatientAppointments(true, false);
});
},
backgroundColor: AppointmentType.getNextActionButtonColor(widget.patientAppointmentHistoryResponseModel.nextAction).withOpacity(0.1),
borderColor: AppointmentType.getNextActionButtonColor(widget.patientAppointmentHistoryResponseModel.nextAction).withOpacity(0.01),
textColor: AppointmentType.getNextActionButtonColor(widget.patientAppointmentHistoryResponseModel.nextAction),
fontSize: 14,
fontWeight: FontWeight.w500,
borderRadius: 12,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 40.h,
icon: AppointmentType.getNextActionIcon(widget.patientAppointmentHistoryResponseModel.nextAction),
iconColor: AppointmentType.getNextActionButtonColor(widget.patientAppointmentHistoryResponseModel.nextAction),
iconSize: 14.h,
),
child: AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel)
? getArrivedAppointmentButton()
: CustomButton(
text: AppointmentType.getNextActionText(widget.patientAppointmentHistoryResponseModel.nextAction),
onPressed: () {
Navigator.of(context)
.push(FadePage(
page: AppointmentDetailsPage(patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel),
))
.then((val) {
widget.myAppointmentsViewModel.initAppointmentsViewModel();
widget.myAppointmentsViewModel.getPatientAppointments(true, false);
});
},
backgroundColor: AppointmentType.getNextActionButtonColor(widget.patientAppointmentHistoryResponseModel.nextAction).withOpacity(0.15),
borderColor: AppointmentType.getNextActionButtonColor(widget.patientAppointmentHistoryResponseModel.nextAction).withOpacity(0.01),
textColor: AppointmentType.getNextActionTextColor(widget.patientAppointmentHistoryResponseModel.nextAction),
fontSize: 14,
fontWeight: FontWeight.w500,
borderRadius: 12,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 40.h,
icon: AppointmentType.getNextActionIcon(widget.patientAppointmentHistoryResponseModel.nextAction),
iconColor: AppointmentType.getNextActionTextColor(widget.patientAppointmentHistoryResponseModel.nextAction),
iconSize: 14.h,
),
),
SizedBox(width: 8.h),
Expanded(
@ -200,11 +211,16 @@ class _AppointmentCardState extends State<AppointmentCard> {
),
),
).onPress(() {
Navigator.of(context).push(
Navigator.of(context)
.push(
FadePage(
page: AppointmentDetailsPage(patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel),
),
);
)
.then((val) {
widget.myAppointmentsViewModel.initAppointmentsViewModel();
widget.myAppointmentsViewModel.getPatientAppointments(true, false);
});
}),
),
],
@ -215,5 +231,39 @@ class _AppointmentCardState extends State<AppointmentCard> {
);
}
Widget getArrivedAppointmentButton() {
return DateTime.now().difference(DateUtil.convertStringToDate(widget.patientAppointmentHistoryResponseModel.appointmentDate)).inDays <= 15
? CustomButton(
text: LocaleKeys.askDoctor.tr(context: context),
onPressed: () {},
backgroundColor: AppColors.secondaryLightRedColor,
borderColor: AppColors.secondaryLightRedColor,
textColor: AppColors.primaryRedColor,
fontSize: 14,
fontWeight: FontWeight.w500,
borderRadius: 12,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 40.h,
icon: AppAssets.ask_doctor_icon,
iconColor: AppColors.primaryRedColor,
iconSize: 16.h,
)
: CustomButton(
text: "Rebook with same doctor".needTranslation,
onPressed: () {},
backgroundColor: AppColors.greyColor,
borderColor: AppColors.greyColor,
textColor: AppColors.blackColor,
fontSize: 14,
fontWeight: FontWeight.w500,
borderRadius: 12,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 40.h,
icon: AppAssets.rebook_appointment_icon,
iconColor: AppColors.blackColor,
iconSize: 16.h,
);
}
void performAppointmentNextAction(nextAction) {}
}

@ -0,0 +1,164 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_nfc_kit/flutter_nfc_kit.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/common_models/privilege/ProjectDetailListModel.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/core/location_util.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/patient_appointment_history_response_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/appointments/my_appointments_page.dart';
import 'package:hmg_patient_app_new/presentation/home/navigation_screen.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:barcode_scan2/barcode_scan2.dart';
import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart';
import 'package:hmg_patient_app_new/widgets/nfc/nfc_reader_sheet.dart';
import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart';
class AppointmentCheckinBottomSheet extends StatelessWidget {
AppointmentCheckinBottomSheet({super.key, required this.patientAppointmentHistoryResponseModel, required this.myAppointmentsViewModel});
PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel;
MyAppointmentsViewModel myAppointmentsViewModel;
bool _supportsNFC = false;
late LocationUtils locationUtils;
ProjectDetailListModel projectDetailListModel = ProjectDetailListModel();
@override
Widget build(BuildContext context) {
AppState _appState = getIt.get<AppState>();
FlutterNfcKit.nfcAvailability.then((value) {
_supportsNFC = (value == NFCAvailability.available);
});
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
checkInOptionCard(
AppAssets.checkin_location_icon,
"Live Location".needTranslation,
"Verify your location to be at hospital to check in".needTranslation,
).onPress(() {
// locationUtils = LocationUtils(
// isShowConfirmDialog: false,
// navigationService: myAppointmentsViewModel.navigationService,
// appState: myAppointmentsViewModel.appState,
// );
locationUtils.getCurrentLocation(callBack: (value) {
projectDetailListModel = Utils.getProjectDetailObj(_appState, patientAppointmentHistoryResponseModel.projectID);
double dist = Utils.distance(value.latitude, value.longitude, double.parse(projectDetailListModel.latitude!), double.parse(projectDetailListModel.longitude!)).ceilToDouble() * 1000;
print(dist);
if (dist <= projectDetailListModel.geofenceRadius!) {
sendCheckInRequest(projectDetailListModel.checkInQrCode!, context);
} else {
showCommonBottomSheetWithoutHeight(context,
title: "Error".needTranslation,
child: Utils.getErrorWidget(loadingText: "Please ensure you're within the hospital location to perform online check-in.".needTranslation), callBackFunc: () {
Navigator.of(context).pop();
}, isFullScreen: false);
}
});
}),
SizedBox(height: 16.h),
checkInOptionCard(
AppAssets.checkin_nfc_icon,
"NFC (Near Field Communication)".needTranslation,
"Scan your phone via NFC board to check in".needTranslation,
).onPress(() {
Future.delayed(const Duration(milliseconds: 500), () {
showNfcReader(context, onNcfScan: (String nfcId) {
Future.delayed(const Duration(milliseconds: 100), () {
sendCheckInRequest(nfcId, context);
});
}, onCancel: () {});
});
}),
SizedBox(height: 16.h),
checkInOptionCard(
AppAssets.checkin_qr_icon,
"QR Code".needTranslation,
"Scan QR code with your camera to check in".needTranslation,
).onPress(() async {
String onlineCheckInQRCode = (await BarcodeScanner.scan().then((value) => value.rawContent));
if (onlineCheckInQRCode != "") {
sendCheckInRequest(onlineCheckInQRCode, context);
} else {}
}),
],
);
}
Widget checkInOptionCard(String icon, String title, String subTitle) {
return Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 20.h,
hasShadow: false,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Utils.buildSvgWithAssets(icon: icon, width: 40.h, height: 40.h, fit: BoxFit.fill),
SizedBox(height: 16.h),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
title.toText16(isBold: true, color: AppColors.textColor),
subTitle.toText12(fontWeight: FontWeight.w500, color: AppColors.greyTextColor),
],
),
Utils.buildSvgWithAssets(
icon: AppAssets.forward_arrow_icon,
iconColor: AppColors.blackColor,
width: 18.h,
height: 13.h,
fit: BoxFit.contain,
),
],
),
],
).paddingAll(16.h),
);
}
void sendCheckInRequest(String scannedCode, BuildContext context) async {
showCommonBottomSheet(context,
child: Utils.getLoadingWidget(), callBackFunc: (str) {}, title: "", height: ResponsiveExtension.screenHeight * 0.3, isCloseButtonVisible: false, isDismissible: false, isFullScreen: false);
await myAppointmentsViewModel.sendCheckInNfcRequest(
patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel,
scannedCode: scannedCode,
checkInType: 2,
onSuccess: (apiResponse) {
Navigator.of(context).pop();
showCommonBottomSheetWithoutHeight(context, title: "Success".needTranslation, child: Utils.getSuccessWidget(loadingText: LocaleKeys.success.tr()), callBackFunc: () {
Navigator.of(context).pop();
Navigator.pushAndRemoveUntil(
context,
FadePage(
page: LandingNavigation(),
),
(r) => false);
Navigator.of(context).push(
FadePage(page: MyAppointmentsPage()),
);
}, isFullScreen: false);
},
onError: (error) {
Navigator.of(context).pop();
showCommonBottomSheetWithoutHeight(context, title: "Error".needTranslation, child: Utils.getErrorWidget(loadingText: error), callBackFunc: () {
Navigator.of(context).pop();
}, isFullScreen: false);
},
);
}
}

@ -59,10 +59,7 @@ class AppointmentDoctorCard extends StatelessWidget {
icon: AppAssets.doctor_calendar_icon,
labelText:
"${DateUtil.formatDateToDate(DateUtil.convertStringToDate(patientAppointmentHistoryResponseModel.appointmentDate), false)}, ${DateUtil.formatDateToTimeLang(DateUtil.convertStringToDate(patientAppointmentHistoryResponseModel.appointmentDate), false)}"),
AppCustomChipWidget(
icon: AppAssets.rating_icon,
iconColor: AppColors.ratingColorYellow,
labelText: "Rating: ${patientAppointmentHistoryResponseModel.decimalDoctorRate}"),
AppCustomChipWidget(icon: AppAssets.rating_icon, iconColor: AppColors.ratingColorYellow, labelText: "Rating: ${patientAppointmentHistoryResponseModel.decimalDoctorRate}"),
],
),
],
@ -82,20 +79,37 @@ class AppointmentDoctorCard extends StatelessWidget {
Widget getAppointmentActionButtons(bool isAppointmentArrived) {
if (isAppointmentArrived) {
return CustomButton(
text: LocaleKeys.askDoctor.tr(),
onPressed: () {},
backgroundColor: AppColors.secondaryLightRedColor,
borderColor: AppColors.secondaryLightRedColor,
textColor: AppColors.primaryRedColor,
fontSize: 14,
fontWeight: FontWeight.w500,
borderRadius: 12,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 40.h,
icon: AppAssets.ask_doctor_icon,
iconColor: AppColors.primaryRedColor,
);
return DateTime.now().difference(DateUtil.convertStringToDate(patientAppointmentHistoryResponseModel.appointmentDate)).inDays <= 15
? CustomButton(
text: LocaleKeys.askDoctor.tr(),
onPressed: () {},
backgroundColor: AppColors.secondaryLightRedColor,
borderColor: AppColors.secondaryLightRedColor,
textColor: AppColors.primaryRedColor,
fontSize: 14,
fontWeight: FontWeight.w500,
borderRadius: 12,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 40.h,
iconSize: 16.h,
icon: AppAssets.ask_doctor_icon,
iconColor: AppColors.primaryRedColor,
)
: CustomButton(
text: "Rebook with same doctor".needTranslation,
onPressed: () {},
backgroundColor: AppColors.greyColor,
borderColor: AppColors.greyColor,
textColor: AppColors.blackColor,
fontSize: 14,
fontWeight: FontWeight.w500,
borderRadius: 12,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 40.h,
icon: AppAssets.rebook_appointment_icon,
iconColor: AppColors.blackColor,
iconSize: 16.h,
);
} else {
return Row(
children: [

@ -122,7 +122,7 @@ class _QuickLogin extends State<QuickLogin> {
child: CustomButton(
text: LocaleKeys.notNow.tr(),
onPressed: () {
Navigator.pop(context, true);
Navigator.pop(context, "true");
},
backgroundColor: Color(0xffFEE9EA),
borderColor: Color(0xffFEE9EA),

@ -34,13 +34,17 @@ class _SavedLogin extends State<SavedLogin> {
void initState() {
authVm = context.read<AuthenticationViewModel>();
appState = getIt.get<AppState>();
loginType = LoginTypeExtension.fromValue(appState.getSelectDeviceByImeiRespModelElement!.logInType!)!;
loginType = LoginTypeExtension.fromValue(appState.getSelectDeviceByImeiRespModelElement!.logInType!)!;
authVm.phoneNumberController.text = appState.getSelectDeviceByImeiRespModelElement!.mobile!.startsWith("0")
? appState.getSelectDeviceByImeiRespModelElement!.mobile!.replaceFirst("0", "")
: appState.getSelectDeviceByImeiRespModelElement!.mobile!;
authVm.nationalIdController.text = appState.getSelectDeviceByImeiRespModelElement!.identificationNo!;
if (loginType == LoginTypeEnum.fingerprint || loginType == LoginTypeEnum.face) {
authVm.loginWithFingerPrintFace();
}
super.initState();
}
@ -80,8 +84,7 @@ class _SavedLogin extends State<SavedLogin> {
children: [
// Last login info
("${LocaleKeys.lastloginBy.tr()} ${loginType.displayName}")
.toText14(isBold: true, color: AppColors.greyTextColor),
("${LocaleKeys.lastloginBy.tr()} ${loginType.displayName}").toText14(isBold: true, color: AppColors.greyTextColor),
(appState.getSelectDeviceByImeiRespModelElement!.createdOn != null
? DateUtil.getFormattedDate(DateUtil.convertStringToDate(appState.getSelectDeviceByImeiRespModelElement!.createdOn!), "d MMMM, y 'at' HH:mm")
: '--')
@ -89,7 +92,8 @@ class _SavedLogin extends State<SavedLogin> {
Container(
margin: EdgeInsets.all(16.h),
child: Utils.buildSvgWithAssets(icon: getTypeIcons(appState.getSelectDeviceByImeiRespModelElement!.logInType!), height: 54, width: 54, iconColor: loginType.toInt == 4 ? null : AppColors.primaryRedColor)),
child: Utils.buildSvgWithAssets(
icon: getTypeIcons(appState.getSelectDeviceByImeiRespModelElement!.logInType!), height: 54, width: 54, iconColor: loginType.toInt == 4 ? null : AppColors.primaryRedColor)),
// Face ID login button
SizedBox(
height: 45,
@ -232,7 +236,6 @@ class _SavedLogin extends State<SavedLogin> {
textColor: Color(0xFF2E3039),
borderWidth: 2,
padding: EdgeInsets.fromLTRB(0, 14, 0, 14),
),
const Spacer(flex: 2),
// OR divider

@ -42,7 +42,7 @@ class _LandingPageState extends State<LandingPage> {
authVM.savePushTokenToAppState();
if (mounted) {
authVM.checkLastLoginStatus(() {
showQuickLogin(context, false);
showQuickLogin(context, false);
});
}
super.initState();
@ -326,19 +326,17 @@ class _LandingPageState extends State<LandingPage> {
context,
title: "",
child: QuickLogin(
isDone: isDone,
isDone: isDone,
onPressed: () {
// sharedPref.setBool(HAS_ENABLED_QUICK_LOGIN, true);
authVM.loginWithFingerPrintFace();
},
),
height:isDone == false ? ResponsiveExtension.screenHeight * 0.5 : ResponsiveExtension.screenHeight * 0.3,
height: isDone == false ? ResponsiveExtension.screenHeight * 0.5 : ResponsiveExtension.screenHeight * 0.3,
isFullScreen: false,
callBackFunc: (str) {
isDone = true;
setState(() {
});
setState(() {});
},
);
}

@ -10,6 +10,7 @@ import 'package:hmg_patient_app_new/features/insurance/insurance_view_model.dart
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/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/shimmer/movies_shimmer_widget.dart';
import 'package:provider/provider.dart';
@ -68,42 +69,14 @@ class PatientInsuranceCardUpdateCard extends StatelessWidget {
Wrap(
direction: Axis.horizontal,
spacing: 4.h,
runSpacing: 4.h,
runSpacing: -8.h,
children: [
Row(
children: [
CustomButton(
icon: AppAssets.doctor_calendar_icon,
iconColor: AppColors.blackColor,
iconSize: 13.h,
text: "${LocaleKeys.expiryOn.tr(context: context)} ${insuranceViewModel.patientInsuranceUpdateResponseModel!.effectiveTo}",
onPressed: () {},
backgroundColor: AppColors.greyColor,
borderColor: AppColors.greyColor,
textColor: AppColors.blackColor,
fontSize: 10,
fontWeight: FontWeight.w500,
borderRadius: 12,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 30.h,
),
],
AppCustomChipWidget(
icon: AppAssets.doctor_calendar_icon,
labelText: "${LocaleKeys.expiryOn.tr(context: context)} ${insuranceViewModel.patientInsuranceUpdateResponseModel!.effectiveTo}",
),
Row(
children: [
CustomButton(
text: "Member ID: ${insuranceViewModel.patientInsuranceUpdateResponseModel!.memberID!}",
onPressed: () {},
backgroundColor: AppColors.greyColor,
borderColor: AppColors.greyColor,
textColor: AppColors.blackColor,
fontSize: 10,
fontWeight: FontWeight.w500,
borderRadius: 12,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 30.h,
),
],
AppCustomChipWidget(
labelText: "Member ID: ${insuranceViewModel.patientInsuranceUpdateResponseModel!.memberID!}",
),
],
),

@ -13,6 +13,7 @@ import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/insurance/widgets/insurance_update_details_card.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart';
import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart';
import 'package:provider/provider.dart';
@ -73,44 +74,13 @@ class PatientInsuranceCard extends StatelessWidget {
SizedBox(height: 8.h),
Wrap(
direction: Axis.horizontal,
spacing: 6.h,
runSpacing: 6.h,
spacing: 4.h,
runSpacing: -8.h,
children: [
Row(
children: [
CustomButton(
icon: AppAssets.doctor_calendar_icon,
iconColor: AppColors.blackColor,
iconSize: 13.h,
text: "${LocaleKeys.expiryDate.tr(context: context)} ${DateUtil.formatDateToDate(DateUtil.convertStringToDate(insuranceCardDetailsModel.cardValidTo), false)}",
onPressed: () {},
backgroundColor: AppColors.greyColor,
borderColor: AppColors.greyColor,
textColor: AppColors.blackColor,
fontSize: 10,
fontWeight: FontWeight.w500,
borderRadius: 12,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 30.h,
),
],
),
Row(
children: [
CustomButton(
text: "Patient Card ID: ${insuranceCardDetailsModel.patientCardID}",
onPressed: () {},
backgroundColor: AppColors.greyColor,
borderColor: AppColors.greyColor,
textColor: AppColors.blackColor,
fontSize: 10,
fontWeight: FontWeight.w500,
borderRadius: 12,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 30.h,
),
],
),
AppCustomChipWidget(
icon: AppAssets.doctor_calendar_icon,
labelText: "${LocaleKeys.expiryDate.tr(context: context)} ${DateUtil.formatDateToDate(DateUtil.convertStringToDate(insuranceCardDetailsModel.cardValidTo), false)}"),
AppCustomChipWidget(labelText: "Patient Card ID: ${insuranceCardDetailsModel.patientCardID}"),
],
),
SizedBox(height: 10.h),

@ -167,22 +167,22 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 30.h,
),
SizedBox(width: 4.h),
CustomButton(
icon: AppAssets.insurance_active_icon,
iconColor: AppColors.successColor,
iconSize: 13.h,
text: "Insurance Active",
onPressed: () {},
backgroundColor: AppColors.bgGreenColor.withOpacity(0.20),
borderColor: AppColors.bgGreenColor.withOpacity(0.0),
textColor: AppColors.blackColor,
fontSize: 10,
fontWeight: FontWeight.normal,
borderRadius: 12,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 30.h,
),
// SizedBox(width: 4.h),
// CustomButton(
// icon: AppAssets.insurance_active_icon,
// iconColor: AppColors.successColor,
// iconSize: 13.h,
// text: "Insurance Active",
// onPressed: () {},
// backgroundColor: AppColors.bgGreenColor.withOpacity(0.20),
// borderColor: AppColors.bgGreenColor.withOpacity(0.0),
// textColor: AppColors.blackColor,
// fontSize: 10,
// fontWeight: FontWeight.normal,
// borderRadius: 12,
// padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
// height: 30.h,
// ),
],
),
SizedBox(height: 8.h),
@ -255,7 +255,7 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
? const MoviesShimmerWidget().paddingSymmetrical(24.h, 0.0)
: PatientInsuranceCard(
insuranceCardDetailsModel: insuranceVM.patientInsuranceList.first,
isInsuranceExpired: DateTime.now().isBefore(DateUtil.convertStringToDate(insuranceVM.patientInsuranceList.first.cardValidTo)));
isInsuranceExpired: DateTime.now().isAfter(DateUtil.convertStringToDate(insuranceVM.patientInsuranceList.first.cardValidTo)));
}),
SizedBox(height: 10.h),
GridView(
@ -264,10 +264,10 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
padding: EdgeInsets.only(top: 12),
shrinkWrap: true,
children: [
MedicalFileCard(label: "Update Insurance", textColor: AppColors.blackColor, backgroundColor: AppColors.whiteColor, svgIcon: AppAssets.eye_result_icon),
MedicalFileCard(label: "Insurance Approvals", textColor: AppColors.blackColor, backgroundColor: AppColors.whiteColor, svgIcon: AppAssets.eye_result_icon),
MedicalFileCard(label: "My Invoices List", textColor: AppColors.blackColor, backgroundColor: AppColors.whiteColor, svgIcon: AppAssets.eye_result_icon),
MedicalFileCard(label: "Ancillary Orders List", textColor: AppColors.blackColor, backgroundColor: AppColors.whiteColor, svgIcon: AppAssets.eye_result_icon),
MedicalFileCard(label: "Update Insurance".needTranslation, textColor: AppColors.blackColor, backgroundColor: AppColors.whiteColor, svgIcon: AppAssets.eye_result_icon),
MedicalFileCard(label: "Insurance Approvals".needTranslation, textColor: AppColors.blackColor, backgroundColor: AppColors.whiteColor, svgIcon: AppAssets.eye_result_icon),
MedicalFileCard(label: "My Invoices List".needTranslation, textColor: AppColors.blackColor, backgroundColor: AppColors.whiteColor, svgIcon: AppAssets.eye_result_icon),
MedicalFileCard(label: "Ancillary Orders List".needTranslation, textColor: AppColors.blackColor, backgroundColor: AppColors.whiteColor, svgIcon: AppAssets.eye_result_icon),
],
).paddingSymmetrical(24.h, 0.0),
SizedBox(height: 16.h),

@ -6,18 +6,19 @@ import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
class MedicalFileCard extends StatelessWidget {
final String label;
// final Color svgColor;
final Color textColor;
final Color backgroundColor;
final String svgIcon;
final num iconSize;
bool isLargeText;
MedicalFileCard({
required this.label,
// required this.svgColor,
required this.textColor,
required this.backgroundColor,
this.svgIcon = "",
this.iconSize = 30,
this.isLargeText = false
});
@override
@ -33,9 +34,9 @@ class MedicalFileCard extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Utils.buildSvgWithAssets(icon: svgIcon, width: 30.h, height: 30.h, fit: BoxFit.contain),
Utils.buildSvgWithAssets(icon: svgIcon, width: iconSize.h, height: iconSize.h, fit: BoxFit.contain),
SizedBox(height: 12.h),
label.toText11(color: textColor, isBold: true),
isLargeText ? label.toText14(color: textColor, isBold: true) : label.toText11(color: textColor, isBold: true),
],
),
),

@ -17,6 +17,7 @@ import 'package:hmg_patient_app_new/presentation/prescriptions/prescription_item
import 'package:hmg_patient_app_new/presentation/prescriptions/prescription_reminder_view.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart';
import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart';
import 'package:hmg_patient_app_new/widgets/shimmer/movies_shimmer_widget.dart';
import 'package:provider/provider.dart';
@ -41,6 +42,7 @@ class _PrescriptionDetailPageState extends State<PrescriptionDetailPage> {
// locationUtils = new LocationUtils(isShowConfirmDialog: true, context: context);
// WidgetsBinding.instance.addPostFrameCallback((_) => locationUtils.getCurrentLocation());
scheduleMicrotask(() {
prescriptionsViewModel.setPrescriptionsDetailsLoading();
prescriptionsViewModel.getPrescriptionDetails(widget.prescriptionsResponseModel);
});
super.initState();
@ -92,82 +94,23 @@ class _PrescriptionDetailPageState extends State<PrescriptionDetailPage> {
SizedBox(height: 16.h),
Wrap(
direction: Axis.horizontal,
spacing: 6.h,
runSpacing: 6.h,
spacing: 4.h,
runSpacing: -8.h,
children: [
Row(
mainAxisSize: MainAxisSize.min,
children: [
CustomButton(
icon: AppAssets.doctor_calendar_icon,
iconColor: AppColors.textColor,
iconSize: 13.h,
text: DateUtil.formatDateToDate(DateUtil.convertStringToDate(widget.prescriptionsResponseModel.appointmentDate), false),
onPressed: () {},
backgroundColor: AppColors.greyColor,
borderColor: AppColors.greyColor,
textColor: AppColors.blackColor,
fontSize: 10,
fontWeight: FontWeight.w500,
borderRadius: 8,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 30.h,
),
],
AppCustomChipWidget(
icon: AppAssets.doctor_calendar_icon,
labelText: DateUtil.formatDateToDate(DateUtil.convertStringToDate(widget.prescriptionsResponseModel.appointmentDate), false),
),
Row(
mainAxisSize: MainAxisSize.min,
children: [
CustomButton(
text: widget.prescriptionsResponseModel.clinicDescription!,
onPressed: () {},
backgroundColor: AppColors.greyColor,
borderColor: AppColors.greyColor,
textColor: AppColors.blackColor,
fontSize: 10,
fontWeight: FontWeight.w500,
borderRadius: 8,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 30.h,
),
],
AppCustomChipWidget(
labelText: widget.prescriptionsResponseModel.clinicDescription!,
),
Row(
mainAxisSize: MainAxisSize.min,
children: [
CustomButton(
icon: AppAssets.rating_icon,
iconColor: AppColors.ratingColorYellow,
iconSize: 13.h,
text: "Rating: ${widget.prescriptionsResponseModel.decimalDoctorRate}",
onPressed: () {},
backgroundColor: AppColors.greyColor,
borderColor: AppColors.greyColor,
textColor: AppColors.blackColor,
fontSize: 10,
fontWeight: FontWeight.w500,
borderRadius: 8,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 30.h,
),
],
AppCustomChipWidget(
icon: AppAssets.rating_icon,
iconColor: AppColors.ratingColorYellow,
labelText: "Rating: ${widget.prescriptionsResponseModel.decimalDoctorRate}".needTranslation,
),
Row(
mainAxisSize: MainAxisSize.min,
children: [
CustomButton(
text: widget.prescriptionsResponseModel.name!,
onPressed: () {},
backgroundColor: AppColors.greyColor,
borderColor: AppColors.greyColor,
textColor: AppColors.blackColor,
fontSize: 10,
fontWeight: FontWeight.w500,
borderRadius: 8,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 30.h,
),
],
AppCustomChipWidget(
labelText: widget.prescriptionsResponseModel.name!,
),
],
),

@ -9,6 +9,8 @@ import 'package:flutter_zoom_videosdk/native/zoom_videosdk.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/utils/utils.dart';
import 'package:hmg_patient_app_new/features/authentication/authentication_view_model.dart';
// import 'package:hmg_patient_app_new/presentation/authantication/login.dart';
import 'package:hmg_patient_app_new/presentation/home/landing_page.dart';
import 'package:hmg_patient_app_new/presentation/home/navigation_screen.dart';
@ -26,6 +28,8 @@ class SplashPage extends StatefulWidget {
}
class _SplashScreenState extends State<SplashPage> {
late AuthenticationViewModel authVm;
Future<void> initializeStuff() async {
Timer(
Duration(milliseconds: 500),
@ -34,7 +38,8 @@ class _SplashScreenState extends State<SplashPage> {
PushNotificationHandler().init(context); // Asyncronously
},
);
Timer(Duration(seconds: 3, milliseconds: 500), () async {
await authVm.getServicePrivilege();
Timer(Duration(seconds: 2, milliseconds: 500), () async {
LocalNotification.init(onNotificationClick: (payload) {});
Navigator.of(context).pushReplacement(
FadePage(
@ -51,8 +56,6 @@ class _SplashScreenState extends State<SplashPage> {
zoom.initSdk(initConfig);
}
/// load the Privilege from service
Future loadPrivilege() async {
// ProjectViewModel projectProvider = Provider.of<ProjectViewModel>(context, listen: false);
@ -71,9 +74,9 @@ class _SplashScreenState extends State<SplashPage> {
PushNotificationHandler().init(context); // Asyncronously
}
@override
void initState() {
authVm = context.read<AuthenticationViewModel>();
super.initState();
initializeStuff();
}
@ -87,8 +90,7 @@ class _SplashScreenState extends State<SplashPage> {
children: [
Padding(
padding: EdgeInsets.symmetric(horizontal: 53),
child: Image.asset(AppAssets.hmg_logo,
fit: BoxFit.fitWidth, width: MediaQuery.of(context).size.width),
child: Image.asset(AppAssets.hmg_logo, fit: BoxFit.fitWidth, width: MediaQuery.of(context).size.width),
),
Align(
alignment: Alignment.bottomCenter,
@ -97,18 +99,12 @@ class _SplashScreenState extends State<SplashPage> {
children: [
Text(
"Powered by",
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w400,
color: AppColors.textColor,
letterSpacing: -0.56,
height: 16 / 14),
style: TextStyle(fontSize: 14, fontWeight: FontWeight.w400, color: AppColors.textColor, letterSpacing: -0.56, height: 16 / 14),
),
SizedBox(
height: 5,
),
Utils.buildSvgWithAssets(
icon: AppAssets.cloud_logo, width: 40, height: 40),
Utils.buildSvgWithAssets(icon: AppAssets.cloud_logo, width: 40, height: 40),
SizedBox(height: 7),
// Text(
// "Version 1.1.0",

@ -0,0 +1,212 @@
import 'dart:io';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_nfc_kit/flutter_nfc_kit.dart';
import 'package:flutter_svg/flutter_svg.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/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';
void showNfcReader(BuildContext context, {required Function onNcfScan, required VoidCallback onCancel}) {
showModalBottomSheet(
context: context,
enableDrag: false,
isDismissible: true,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.only(topLeft: Radius.circular(12), topRight: Radius.circular(12)),
),
backgroundColor: Colors.white,
builder: (context) {
return NfcLayout(
onNcfScan: onNcfScan,
onCancel: onCancel,
);
});
}
class NfcLayout extends StatefulWidget {
Function? onNcfScan;
VoidCallback? onCancel;
NfcLayout({this.onNcfScan, this.onCancel});
@override
_NfcLayoutState createState() => _NfcLayoutState();
}
class _NfcLayoutState extends State<NfcLayout> {
bool _reading = false;
Widget? mainWidget;
late String nfcId;
@override
void initState() {
super.initState();
readNFC();
}
void readNFC() async {
FlutterNfcKit.finish();
FlutterNfcKit.poll(timeout: Duration(seconds: 10), androidPlatformSound: true, androidCheckNDEF: false, iosMultipleTagMessage: "Multiple tags found!").then((value) async {
setState(() {
_reading = true;
mainWidget = doneNfc();
});
Future.delayed(const Duration(milliseconds: 500), () async {
await FlutterNfcKit.finish();
widget.onNcfScan!(nfcId);
Navigator.pop(context);
});
nfcId = value.id;
}).catchError((err) {
print(err);
Navigator.of(context).pop();
});
}
@override
Widget build(BuildContext context) {
// return SizedBox();
(mainWidget == null && !_reading) ? mainWidget = scanNfc() : mainWidget = doneNfc();
return Platform.isAndroid ? AnimatedSwitcher(duration: Duration(milliseconds: 500), child: mainWidget) : SizedBox.shrink();
}
Widget scanNfc() {
return Container(
key: ValueKey(1),
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
SizedBox(
height: 30,
),
Text(
"Ready To Scan",
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 24,
),
),
SizedBox(
height: 30,
),
SvgPicture.asset(
"assets/images/nfc/contactless.svg",
height: MediaQuery.of(context).size.width / 3,
),
SizedBox(
height: 30,
),
Text(
"Approach an NFC Tag",
style: TextStyle(
fontSize: 18,
),
),
SizedBox(
height: 30,
),
ButtonTheme(
minWidth: MediaQuery.of(context).size.width / 1.2,
height: 45.0,
buttonColor: Colors.grey[300],
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(6),
),
child: CustomButton(
text: LocaleKeys.cancel.tr(),
onPressed: () {
widget.onCancel!();
Navigator.pop(context);
},
backgroundColor: AppColors.primaryRedColor,
borderColor: AppColors.primaryRedColor,
textColor: AppColors.whiteColor,
fontSize: 14,
fontWeight: FontWeight.w500,
borderRadius: 12.h,
height: 40.h,
icon: AppAssets.cancel,
iconColor: AppColors.whiteColor,
iconSize: 16.h,
),
),
SizedBox(
height: 30,
),
],
),
);
}
Widget doneNfc() {
return Container(
key: ValueKey(2),
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
SizedBox(
height: 30,
),
Text(
"Successfully Scanned",
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 24,
),
),
SizedBox(
height: 30,
),
Image.asset(
"assets/images/nfc/ic_done.png",
height: MediaQuery.of(context).size.width / 3,
),
SizedBox(
height: 30,
),
Text(
"Approach an NFC Tag",
style: TextStyle(
fontSize: 18,
),
),
SizedBox(
height: 30,
),
ButtonTheme(
minWidth: MediaQuery.of(context).size.width / 1.2,
height: 45.0,
buttonColor: Colors.grey[300],
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(6),
),
child: CustomButton(
text: LocaleKeys.done.tr(),
onPressed: () {
widget.onCancel!();
Navigator.pop(context);
},
backgroundColor: AppColors.primaryRedColor,
borderColor: AppColors.primaryRedColor,
textColor: AppColors.whiteColor,
fontSize: 14,
fontWeight: FontWeight.w500,
borderRadius: 12.h,
height: 40.h,
icon: AppAssets.cancel,
iconColor: AppColors.whiteColor,
iconSize: 16.h,
),
),
SizedBox(
height: 30,
),
],
),
);
}
}

@ -73,6 +73,8 @@ dependencies:
maps_launcher: ^3.0.0+1
amazon_payfort: ^1.1.4
network_info_plus: ^6.1.4
flutter_nfc_kit: ^3.6.0
barcode_scan2: ^4.5.1
dev_dependencies:
flutter_test:

Loading…
Cancel
Save