Compare commits
23 Commits
4ee0d1d3c8
...
3f5bd0a759
| Author | SHA1 | Date |
|---|---|---|
|
|
3f5bd0a759 | 1 month ago |
|
|
17f32e34d3 | 2 months ago |
|
|
853a866567 | 2 months ago |
|
|
210da3404c | 2 months ago |
|
|
32ee47ca66 | 2 months ago |
|
|
ba211ea4c2 | 2 months ago |
|
|
b10d715d55 | 2 months ago |
|
|
f19d06512a | 2 months ago |
|
|
05e5b41dc6 | 2 months ago |
|
|
b7a32c7453 | 2 months ago |
|
|
6f067d92d6 | 2 months ago |
|
|
14842677e5 | 2 months ago |
|
|
d62324044a | 2 months ago |
|
|
dc8031bb41 | 2 months ago |
|
|
5a1fe2b5ca | 2 months ago |
|
|
f7021f685a | 2 months ago |
|
|
1dce2a5c3a | 2 months ago |
|
|
93979707e4 | 2 months ago |
|
|
cc7239a473 | 2 months ago |
|
|
213662e718 | 2 months ago |
|
|
0895d94df2 | 2 months ago |
|
|
a8b80f3f26 | 2 months ago |
|
|
e083cbc82b | 2 months ago |
|
After Width: | Height: | Size: 2.4 MiB |
@ -0,0 +1,4 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M4 13C4 8.58172 7.58172 5 12 5C16.4183 5 20 8.58172 20 13C20 13.5523 20.4477 14 21 14C21.5523 14 22 13.5523 22 13C22 7.47715 17.5228 3 12 3C6.47715 3 2 7.47715 2 13C2 13.5523 2.44772 14 3 14C3.55228 14 4 13.5523 4 13Z" fill="#D48D05"/>
|
||||
<path d="M12 9C12.5523 9 13 9.44772 13 10L13 15.1707C14.1652 15.5825 15 16.6938 15 18C15 19.6569 13.6569 21 12 21C10.3431 21 9 19.6569 9 18C9 16.6938 9.83481 15.5825 11 15.1707L11 10C11 9.44772 11.4477 9 12 9Z" fill="#D48D05"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 576 B |
@ -0,0 +1,4 @@
|
||||
<svg width="20" height="18" viewBox="0 0 20 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M2 10C2 5.58172 5.58172 2 10 2C14.4183 2 18 5.58172 18 10C18 10.5523 18.4477 11 19 11C19.5523 11 20 10.5523 20 10C20 4.47715 15.5228 0 10 0C4.47715 0 0 4.47715 0 10C0 10.5523 0.447715 11 1 11C1.55228 11 2 10.5523 2 10Z" fill="white"/>
|
||||
<path d="M10 6C10.5523 6 11 6.44772 11 7L11 12.1707C12.1652 12.5825 13 13.6938 13 15C13 16.6569 11.6569 18 10 18C8.34315 18 7 16.6569 7 15C7 13.6938 7.83481 12.5825 9 12.1707L9 7C9 6.44772 9.44772 6 10 6Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 568 B |
@ -0,0 +1,3 @@
|
||||
<svg width="22" height="22" viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M4 6.75C4 3.02208 7.02208 0 10.75 0C14.4779 0 17.5 3.02208 17.5 6.75C17.5 6.81385 17.5 6.84578 17.4946 6.87394C17.4654 7.02516 17.324 7.13066 17.1708 7.11566C17.1422 7.11287 17.1041 7.10157 17.028 7.07899C16.306 6.86492 15.5414 6.75 14.75 6.75C11.4211 6.75 8.56706 8.78325 7.36206 11.6756C7.21403 12.031 7.14001 12.2086 6.99327 12.2449C6.84654 12.2812 6.71484 12.1723 6.45145 11.9546L6.45144 11.9546C4.95411 10.7165 4 8.84466 4 6.75ZM8 14.75C8 11.0221 11.0221 8 14.75 8C18.4779 8 21.5 11.0221 21.5 14.75C21.5 18.4779 18.4779 21.5 14.75 21.5C11.0221 21.5 8 18.4779 8 14.75ZM6.77538 14.11C6.75857 14.3217 6.75 14.5358 6.75 14.7519C6.75 16.87 7.57313 18.7958 8.91695 20.227L8.91696 20.227C9.22203 20.5519 9.37457 20.7143 9.33165 20.8754C9.28873 21.0365 9.09982 21.0941 8.72199 21.2093C8.09819 21.3996 7.43605 21.5019 6.75 21.5019C3.02208 21.5019 0 18.4798 0 14.7519C0 12.6572 0.954182 10.7853 2.45161 9.54723C2.715 9.32946 2.8467 9.22057 2.99343 9.25688C3.14016 9.29319 3.21416 9.4708 3.36215 9.82601C3.9893 11.3312 5.0627 12.6033 6.41512 13.4766L6.4968 13.5294C6.64871 13.6275 6.72467 13.6765 6.76134 13.7507C6.79802 13.8249 6.79047 13.9199 6.77538 14.11Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 6.1 KiB |
@ -0,0 +1,4 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M19.8271 3.35515C16.8461 1.52662 14.1719 2.25537 12.5563 3.46861C12.4618 3.5396 12.3778 3.60261 12.3027 3.6585C12.1487 3.77312 12.0717 3.83043 11.9724 3.83043C11.873 3.83043 11.796 3.77312 11.642 3.6585C11.5669 3.60262 11.4829 3.5396 11.3884 3.46861C9.77284 2.25537 7.09861 1.52662 4.11763 3.35515C2.63347 4.26554 1.61665 5.88968 1.31555 7.86051C1.27161 8.14807 1.24965 8.29185 1.33944 8.39639C1.42923 8.50094 1.58234 8.50092 1.88856 8.50089L5.14448 8.50059C5.55656 8.49886 6.25061 8.49594 6.92509 8.74131C7.18957 8.83753 7.42861 8.9636 7.64109 9.10184C7.94838 9.30175 8.10203 9.40171 8.22623 9.37305C8.35044 9.34439 8.43459 9.20413 8.60289 8.92363L9.32823 7.71474C9.78963 6.94573 10.6276 6.48278 11.5242 6.50152C12.4208 6.52027 13.2387 7.01784 13.6676 7.80546L14.9648 10.1879C15.0479 10.3405 15.0895 10.4168 15.1603 10.4589C15.2311 10.501 15.318 10.501 15.4918 10.501H15.972C17.3527 10.501 18.472 11.6203 18.472 13.001C18.472 14.3817 17.3527 15.501 15.972 15.501L14.8556 15.5031C14.4407 15.5127 13.1951 15.5415 12.106 14.705C12.0741 14.6805 12.0429 14.6555 12.0124 14.6302C11.6751 14.3499 11.5064 14.2097 11.3704 14.2328C11.2345 14.2559 11.1366 14.419 10.9409 14.7453L10.6157 15.2872C10.1827 16.0089 9.41568 16.4642 8.5748 16.4989C7.73393 16.5335 6.93207 16.1427 6.44122 15.4591L5.21484 13.7511C5.12681 13.6285 5.0828 13.5672 5.01833 13.5341C4.95385 13.501 4.87839 13.501 4.72747 13.501L3.23319 13.501C2.80205 13.501 2.58648 13.501 2.49975 13.6503C2.41302 13.7997 2.51637 13.9808 2.72307 14.3432C3.7896 16.2127 5.46319 18.1083 7.86568 19.8865C9.31705 20.9614 10.3823 21.7503 11.9723 21.7503C13.5624 21.7503 14.6276 20.9614 16.079 19.8865C20.2586 16.7929 22.2322 13.3443 22.6412 10.2951C23.0481 7.2606 21.8883 4.61952 19.8271 3.35515Z" fill="#18C273"/>
|
||||
<path d="M12.3509 8.5218C12.1794 8.20675 11.8522 8.00772 11.4936 8.00022C11.1349 7.99272 10.7997 8.1779 10.6152 8.48551L8.39777 12.1812L7.72833 11.2488C7.71432 11.2293 7.69962 11.2103 7.68425 11.1919C7.66865 11.1731 7.65154 11.1522 7.63294 11.1295C7.4192 10.8684 7.00838 10.3666 6.41298 10.15C5.9948 9.99782 5.53719 9.99883 5.08287 9.99984L4.97266 10L2.97266 10C2.42037 10 1.97266 10.4477 1.97266 11C1.97266 11.5523 2.42037 12 2.97266 12H4.97266C5.60991 12 5.68621 12.0141 5.72762 12.0289C5.73136 12.0307 5.76392 12.0476 5.83105 12.1122C5.91437 12.1924 5.9964 12.2905 6.12473 12.4445L7.66036 14.5832C7.8567 14.8567 8.17744 15.013 8.51379 14.9992C8.85014 14.9853 9.15695 14.8032 9.33015 14.5145L11.4306 11.0138L12.2854 12.5838C12.295 12.6013 12.305 12.6186 12.3156 12.6356L12.3573 12.7026C12.5191 12.9635 12.7156 13.2804 13.0204 13.5144C13.6863 14.0259 14.4768 14.0096 14.863 14.0016C14.9044 14.0008 14.9412 14 14.9727 14H15.9727C16.5249 14 16.9727 13.5523 16.9727 13C16.9727 12.4477 16.5249 12 15.9727 12H14.9727C14.9119 12 14.8581 12.0003 14.8096 12.0005C14.6334 12.0015 14.5272 12.002 14.416 11.9872C14.3192 11.9743 14.276 11.9545 14.248 11.9352C14.2434 11.9296 14.2355 11.9197 14.2239 11.9037C14.1807 11.8444 14.1277 11.7611 14.0275 11.601L12.3509 8.5218Z" fill="#18C273"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.1 KiB |
@ -0,0 +1,3 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M16.083 8.3418C16.1804 8.26933 16.3034 8.26171 16.4092 8.32129C16.4449 8.34147 16.4818 8.37722 16.5537 8.44922C18.2206 9.92249 19.4021 12.5001 18.3545 14.9922C18.0491 15.7186 17.3429 16.2001 16.5498 16.2002L16.2725 16.1982C16.045 16.1964 15.9309 16.1958 15.8486 16.2598C15.7666 16.3238 15.7397 16.4344 15.6855 16.6553L14.7207 20.5879C14.4117 21.8473 13.2957 22.7461 11.998 22.7461C10.7005 22.746 9.5844 21.8473 9.27539 20.5879L8.31152 16.6553C8.25731 16.4342 8.2297 16.3238 8.14746 16.2598C8.06513 16.1957 7.95135 16.1964 7.72363 16.1982L7.44727 16.2002C6.6541 16.2002 5.94699 15.7187 5.6416 14.9922C4.59407 12.5 5.77638 9.92243 7.44336 8.44922C7.51481 8.37768 7.55044 8.34145 7.58594 8.32129C7.69173 8.26141 7.81555 8.26931 7.91309 8.3418C7.94584 8.36621 7.98081 8.41097 8.0498 8.5C8.14907 8.62808 8.25089 8.75011 8.35254 8.86426C8.78359 9.34823 9.29846 9.79001 9.86914 10.124C10.4188 10.4457 11.1568 10.7461 11.998 10.7461C12.8393 10.7461 13.5773 10.4457 14.127 10.124C14.6976 9.79001 15.2125 9.34823 15.6436 8.86426C15.7452 8.75013 15.847 8.62806 15.9463 8.5C16.0154 8.41081 16.0502 8.36627 16.083 8.3418ZM12 1.25C14.0711 1.25 15.75 2.92893 15.75 5C15.75 6.07361 15.1945 7.1188 14.5254 7.87012C14.1835 8.25405 13.7876 8.58923 13.3711 8.83301C12.9615 9.07273 12.4873 9.25 12 9.25C11.5127 9.25 11.0385 9.07273 10.6289 8.83301C10.2124 8.58923 9.81654 8.25405 9.47461 7.87012C8.80554 7.1188 8.25 6.07361 8.25 5C8.25 2.92893 9.92893 1.25 12 1.25Z" fill="#0B85F7"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
@ -0,0 +1,3 @@
|
||||
<svg width="17" height="17" viewBox="0 0 17 17" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.0625 16.125C3.6097 16.125 0 12.5153 0 8.0625C0 3.6097 3.6097 0 8.0625 0C12.5153 0 16.125 3.6097 16.125 8.0625C16.125 12.5153 12.5153 16.125 8.0625 16.125ZM8.0625 4.3125C8.47671 4.3125 8.8125 4.64829 8.8125 5.0625V8.43749H8.93401C9.15181 8.43742 9.38226 8.43734 9.56413 8.45912C9.69673 8.47499 10.1846 8.53481 10.4097 8.99153C10.635 9.4486 10.3763 9.85561 10.3061 9.96553C10.2094 10.1169 10.0646 10.2914 9.92764 10.4564L9.90413 10.4848C9.68853 10.7449 9.43178 11.0409 9.1743 11.2764C9.04572 11.394 8.89699 11.5151 8.73553 11.6109C8.58584 11.6996 8.34945 11.8125 8.0625 11.8125C7.77556 11.8125 7.53916 11.6996 7.38947 11.6109C7.22801 11.5151 7.07929 11.394 6.95071 11.2764C6.69322 11.0409 6.43647 10.7449 6.22087 10.4848L6.19736 10.4564C6.06041 10.2914 5.91565 10.1169 5.81894 9.96552C5.74871 9.85561 5.49001 9.4486 5.71529 8.99153C5.94038 8.53481 6.42827 8.47499 6.56087 8.45912C6.74274 8.43734 6.9732 8.43742 7.191 8.43749H7.3125L7.3125 5.0625C7.3125 4.64829 7.64829 4.3125 8.0625 4.3125Z" fill="#FF9E15"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
@ -0,0 +1,7 @@
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="20" height="3" rx="1.5" fill="#18C273"/>
|
||||
<rect y="4" width="20" height="3" rx="1.5" fill="#18C273" fill-opacity="0.7"/>
|
||||
<rect y="8" width="20" height="3" rx="1.5" fill="#18C273" fill-opacity="0.5"/>
|
||||
<rect y="12" width="20" height="3" rx="1.5" fill="#18C273" fill-opacity="0.35"/>
|
||||
<rect y="16" width="20" height="3" rx="1.5" fill="#18C273" fill-opacity="0.2"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 476 B |
@ -0,0 +1,3 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M10.9999 3.125C10.9999 2.57272 11.4476 2.125 11.9999 2.125C12.5522 2.125 12.9999 2.57272 12.9999 3.125V5.25648L13.6002 5.6567C13.6063 5.64329 13.6125 5.62994 13.6188 5.61666C13.7792 5.27762 14.0126 4.95992 14.3466 4.72784C14.6864 4.4917 15.0865 4.375 15.5234 4.375C17.3868 4.375 19.2054 5.74706 20.5136 7.78194C21.844 9.8515 22.75 12.745 22.75 16.125C22.75 18.5571 21.9318 20.0447 20.8453 20.9014C19.7942 21.7302 18.6097 21.875 18.0144 21.875C16.8144 21.875 15.5948 21.6036 14.6787 20.7304C13.7563 19.8512 13.2788 18.4938 13.2788 16.625C13.2842 16.2851 13.3181 15.4637 13.4101 14.8979C13.5229 14.0671 13.7417 12.9986 14.1873 12.0938C13.709 10.979 13.3028 9.33793 13.2548 7.89942C13.254 7.87588 13.2533 7.85231 13.2527 7.82873L11.9999 6.99352L10.7473 7.82858C10.7467 7.85222 10.746 7.87583 10.7452 7.89942C10.6973 9.33793 10.291 10.979 9.81277 12.0938C10.2583 12.9986 10.4772 14.0671 10.5899 14.8979C10.682 15.4637 10.7158 16.2851 10.7213 16.625C10.7213 18.4938 10.2438 19.8512 9.32137 20.7304C8.40522 21.6036 7.18561 21.875 5.98563 21.875C5.39035 21.875 4.20581 21.7302 3.15476 20.9014C2.06825 20.0447 1.25 18.5571 1.25 16.125C1.25 12.745 2.15607 9.8515 3.48648 7.78194C4.79459 5.74706 6.61322 4.375 8.47665 4.375C8.91351 4.375 9.3136 4.4917 9.65343 4.72784C9.98742 4.95992 10.2208 5.27762 10.3812 5.61666C10.3875 5.6299 10.3937 5.64322 10.3997 5.65659L10.9999 5.25648V3.125Z" fill="#18C273"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.5 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="M8.52965 0.0727261C9.98692 -0.024143 11.5101 -0.024341 12.9704 0.0727261C17.537 0.376286 21.1603 4.06258 21.4582 8.67235C21.5139 9.53477 21.5139 10.4268 21.4582 11.2892C21.1603 15.899 17.537 19.5853 12.9704 19.8889C11.5101 19.9859 9.98692 19.9857 8.52965 19.8889C7.96472 19.8513 7.34978 19.7177 6.80839 19.4948C6.5703 19.3967 6.40868 19.3304 6.29041 19.287C6.20909 19.3429 6.10108 19.4223 5.9438 19.5383C5.15136 20.1226 4.15092 20.5325 2.73117 20.498L2.68544 20.4969C2.41155 20.4903 2.11961 20.4834 1.88152 20.4373C1.59475 20.3818 1.23996 20.2431 1.01791 19.8645C0.776229 19.4525 0.873128 19.0358 0.966883 18.7734C1.05536 18.5257 1.20874 18.2352 1.36542 17.9385L1.3869 17.8978C1.85323 17.0141 1.98314 16.2919 1.73381 15.8104C0.901479 14.554 0.152725 13.0056 0.0418013 11.2892C-0.0139338 10.4268 -0.0139338 9.53477 0.0418013 8.67235C0.339716 4.06258 3.963 0.376286 8.52965 0.0727261ZM6.5 7.75C6.5 8.16421 6.83579 8.5 7.25 8.5H10.75C11.1642 8.5 11.5 8.16421 11.5 7.75C11.5 7.33579 11.1642 7 10.75 7L7.25 7C6.83579 7 6.5 7.33579 6.5 7.75ZM6.5 12.75C6.5 13.1642 6.83579 13.5 7.25 13.5L14.25 13.5C14.6642 13.5 15 13.1642 15 12.75C15 12.3358 14.6642 12 14.25 12L7.25 12C6.83579 12 6.5 12.3358 6.5 12.75Z" fill="#ED1C2B"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
@ -0,0 +1,6 @@
|
||||
<svg width="67" height="74" viewBox="0 0 67 74" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M66.6078 32.2697C66.1744 30.4738 65.1769 29.2328 63.7991 28.7779C63.3117 28.6169 62.7964 28.5751 62.2712 28.6277C63.2293 24.3157 64.7466 19.7654 64.5501 15.5873C64.1645 7.35177 58.7442 0.579242 41.8415 0.0148163C31.0287 -0.346213 20.1447 5.91597 20.6036 16.0287C20.8633 21.7671 21.9015 25.0884 22.6027 28.1906L22.5955 28.6872C21.9433 28.5588 21.3005 28.5802 20.7048 28.7779C19.3245 29.2328 18.327 30.4738 17.8953 32.2697C17.4945 33.929 17.6233 35.8995 18.2581 37.8187C19.2879 40.9412 21.4426 43.2444 23.6866 43.6967C24.8086 48.1266 26.7007 52.0335 29.1423 55.1157L28.172 72.4976C28.1488 72.947 28.4967 73.3275 28.9447 73.3489C28.9581 73.3504 28.9715 73.3518 28.9853 73.3518C29.4158 73.3518 29.7757 73.0131 29.7974 72.5777L30.6851 56.8736C33.9673 60.2504 37.9975 62.271 42.3673 62.271C46.7608 62.271 50.8119 60.229 54.1046 56.8167L54.9949 72.5777C55.0166 73.0131 55.3771 73.3518 55.8074 73.3518C55.8211 73.3518 55.8345 73.3504 55.848 73.3489C56.296 73.3275 56.6436 72.947 56.6204 72.4976L55.646 55.0476C58.0739 51.9631 59.9511 48.0599 61.0666 43.6372C63.217 43.0688 65.2513 40.8234 66.2445 37.8187C66.8779 35.8995 67.0069 33.9304 66.6078 32.2697ZM19.8023 37.3084C19.2636 35.6723 19.1472 34.0197 19.4762 32.6514C19.7725 31.421 20.3896 30.5942 21.2152 30.3207C21.6323 30.184 22.0894 30.1988 22.5741 30.3493L22.5226 34.373C22.5226 36.9688 22.7961 39.47 23.2684 41.8561C21.862 41.1861 20.5169 39.474 19.8023 37.3084ZM42.3673 60.6429C32.3223 60.6429 24.1495 48.8586 24.1495 34.3838L24.2782 24.4253C24.2957 23.0704 23.6463 18.5731 28.6563 18.5731C35.6019 18.5731 35.2 23.0896 41.772 23.0896C48.4944 23.0896 49.0054 18.4254 55.848 18.4254C61.0748 18.4254 60.4495 23.4953 60.4698 25.083L60.5874 34.373C60.5874 48.8586 52.4154 60.6429 42.3673 60.6429ZM64.6994 37.3084C64.028 39.3398 62.8019 40.9779 61.4919 41.7277C61.9508 39.3764 62.2157 36.9159 62.2157 34.3635L62.1642 30.287C62.5604 30.2003 62.9383 30.2071 63.2873 30.3221C64.1118 30.5942 64.7292 31.421 65.0258 32.6514C65.3557 34.0197 65.2381 35.6723 64.6994 37.3084Z" fill="#2B353E"/>
|
||||
<path d="M14.4426 56.2374C14.4426 59.3856 11.8899 61.9383 8.74195 61.9383C5.59398 61.9383 3.04248 59.3856 3.04248 56.2374C3.04248 53.0909 5.59398 50.5382 8.74195 50.5382C11.8899 50.5382 14.4426 53.0909 14.4426 56.2374Z" fill="#EC1B2A"/>
|
||||
<path d="M8.74187 64.9804C3.92094 64.9804 0 61.058 0 56.2374C0 53.2991 1.50647 50.5382 3.9587 48.9287V19.423C3.9587 16.7865 6.10398 14.6387 8.74187 14.6387C11.3809 14.6387 13.5276 16.7865 13.5276 19.423V48.9302C15.9784 50.5382 17.4849 53.2991 17.4849 56.2374C17.4849 61.058 13.5617 64.9804 8.74187 64.9804ZM8.74187 16.1909C6.95934 16.1909 5.51009 17.6405 5.51009 19.423V49.807L5.1256 50.0307C2.92082 51.3189 1.55138 53.6967 1.55138 56.2374C1.55138 60.2029 4.77773 63.4293 8.74187 63.4293C12.706 63.4293 15.9326 60.2029 15.9326 56.2374C15.9326 53.6982 14.5644 51.3189 12.361 50.0318L11.9751 49.807V19.423C11.9751 17.6405 10.5258 16.1909 8.74187 16.1909Z" fill="#2B353E"/>
|
||||
<path d="M10.4569 51.7886H7.02686V25.6216H10.4569V51.7886Z" fill="#EC1B2A"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.1 KiB |
@ -0,0 +1,3 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M13.1459 1.27079C12.8412 1.24999 12.47 1.24999 12.0253 1.25C11.5806 1.24999 11.1588 1.24999 10.8541 1.27079C10.5368 1.29245 10.2372 1.33914 9.94665 1.45953C9.27321 1.73863 8.73814 2.2737 8.45905 2.94713C8.33865 3.23764 8.29196 3.53731 8.2703 3.85456C8.2495 4.15932 8.2495 4.53054 8.24951 4.97522L8.24951 12.6414C7.0263 13.6949 6.25 15.2569 6.25 17C6.25 20.1756 8.82436 22.75 12 22.75C15.1756 22.75 17.75 20.1756 17.75 17C17.75 15.2569 16.9737 13.6949 15.7505 12.6414L15.7505 4.97524C15.7505 4.53055 15.7505 4.15933 15.7297 3.85456C15.708 3.53731 15.6614 3.23764 15.541 2.94713C15.2619 2.2737 14.7268 1.73863 14.0534 1.45953C13.7628 1.33914 13.4632 1.29245 13.1459 1.27079ZM12 7C12.5523 7 13 7.44772 13 8L13 14.4375C14.0243 14.8375 14.75 15.834 14.75 17C14.75 18.5188 13.5188 19.75 12 19.75C10.4812 19.75 9.25 18.5188 9.25 17C9.25 15.834 9.97566 14.8375 11 14.4375L11 8C11 7.44772 11.4477 7 12 7Z" fill="#ED1C2B"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
@ -0,0 +1,3 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.6439 1.25L13.3561 1.25C14.259 1.24997 15.0211 1.24994 15.6249 1.32042C16.255 1.39396 16.8707 1.55847 17.3706 2.00915C17.5958 2.21222 17.7873 2.44899 17.9376 2.71113C18.2326 3.22573 18.2812 3.77334 18.2347 4.31609C19.2103 4.4287 20.0196 4.68604 20.6521 5.31966C21.2538 5.92238 21.5125 6.68113 21.6335 7.58241C21.75 8.4507 21.75 9.55477 21.75 10.9249V16.043C21.75 17.4131 21.75 18.5172 21.6335 19.3855C21.5125 20.2867 21.2538 21.0455 20.6521 21.6482C20.0502 22.2511 19.2923 22.5105 18.3919 22.6318C17.5249 22.7486 16.4225 22.7486 15.055 22.7485L8.94504 22.7485C7.57752 22.7486 6.47513 22.7486 5.60808 22.6318C4.70774 22.5105 3.94976 22.2511 3.3479 21.6482C2.74621 21.0455 2.48747 20.2867 2.3665 19.3855C2.24997 18.5172 2.24998 17.4131 2.25 16.0431L2.25 10.9248C2.24998 9.55478 2.24997 8.45069 2.3665 7.58241C2.48747 6.68113 2.74621 5.92238 3.3479 5.31966C3.98043 4.68604 4.78975 4.4287 5.76534 4.31608C5.71879 3.77334 5.76736 3.22573 6.06239 2.71113C6.21268 2.44899 6.40422 2.21222 6.62944 2.00915C7.1293 1.55847 7.74503 1.39396 8.37506 1.32042C8.97893 1.24994 9.74097 1.24997 10.6439 1.25ZM8.60067 3.2073C8.13641 3.26149 7.99252 3.35241 7.92575 3.4126C7.8493 3.48154 7.78534 3.56095 7.73568 3.64756C7.69637 3.71613 7.64027 3.8608 7.69746 4.30137C7.75673 4.75797 7.91092 5.36081 8.14823 6.27796C8.33725 7.00848 8.46246 7.48783 8.59555 7.84684C8.72171 8.18716 8.82647 8.33584 8.9287 8.43308C9.00978 8.51021 9.10016 8.57847 9.1982 8.63631C9.32461 8.71091 9.5056 8.77465 9.88389 8.81094C10.1378 8.83531 10.4406 8.84448 10.8301 8.84793L11.5514 6.68404C11.726 6.1601 12.2924 5.87694 12.8163 6.05159C13.3402 6.22624 13.6234 6.79256 13.4488 7.3165L12.9378 8.84935C13.4424 8.84743 13.8138 8.83994 14.1161 8.81094C14.4944 8.77465 14.6754 8.71091 14.8018 8.63631C14.8998 8.57847 14.9902 8.51021 15.0713 8.43308C15.1735 8.33584 15.2783 8.18716 15.4045 7.84684C15.5375 7.48783 15.6628 7.00848 15.8518 6.27796C16.0891 5.36081 16.2433 4.75797 16.3025 4.30137C16.3597 3.8608 16.3036 3.71613 16.2643 3.64756C16.2147 3.56095 16.1507 3.48153 16.0742 3.4126C16.0075 3.35241 15.8636 3.26149 15.3993 3.2073C14.9218 3.15157 14.2747 3.15 13.2979 3.15L10.7021 3.15C9.72532 3.15 9.07816 3.15157 8.60067 3.2073ZM10 17C9.44772 17 9 17.4477 9 18C9 18.5523 9.44772 19 10 19H14C14.5523 19 15 18.5523 15 18C15 17.4477 14.5523 17 14 17L10 17Z" fill="#18C273"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.4 KiB |
@ -0,0 +1,3 @@
|
||||
<svg width="20" height="22" viewBox="0 0 20 22" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.39392 1.28104e-06L11.1061 1.28104e-06C12.009 -2.95155e-05 12.7711 -5.55068e-05 13.3749 0.0704231C14.005 0.143956 14.6207 0.308473 15.1206 0.759151C15.3458 0.962221 15.5373 1.19899 15.6876 1.46113C15.9826 1.97573 16.0312 2.52334 15.9847 3.06609C16.9603 3.1787 17.7696 3.43604 18.4021 4.06966C19.0038 4.67238 19.2625 5.43113 19.3835 6.33241C19.5 7.2007 19.5 8.30477 19.5 9.67486V14.793C19.5 16.1631 19.5 17.2672 19.3835 18.1355C19.2625 19.0367 19.0038 19.7955 18.4021 20.3982C17.8002 21.0011 17.0423 21.2605 16.1419 21.3818C15.2749 21.4986 14.1725 21.4986 12.805 21.4985L6.69504 21.4985C5.32752 21.4986 4.22513 21.4986 3.35808 21.3818C2.45774 21.2605 1.69976 21.0011 1.0979 20.3982C0.496209 19.7955 0.237469 19.0367 0.116503 18.1355C-3.37213e-05 17.2672 -1.84605e-05 16.1631 4.81163e-07 14.7931L4.81163e-07 9.67484C-1.84605e-05 8.30478 -3.37213e-05 7.20069 0.116503 6.33241C0.237469 5.43113 0.496209 4.67238 1.0979 4.06966C1.73043 3.43604 2.53975 3.1787 3.51534 3.06608C3.46879 2.52334 3.51736 1.97573 3.81239 1.46113C3.96268 1.19899 4.15422 0.962221 4.37944 0.759151C4.8793 0.308473 5.49503 0.143956 6.12506 0.0704231C6.72893 -5.54994e-05 7.49097 -2.95155e-05 8.39392 1.28104e-06ZM6.35067 1.9573C5.88641 2.01149 5.74252 2.10241 5.67575 2.1626C5.5993 2.23154 5.53534 2.31095 5.48568 2.39756C5.44637 2.46613 5.39027 2.6108 5.44746 3.05137C5.50673 3.50797 5.66092 4.11081 5.89823 5.02796C6.08725 5.75848 6.21246 6.23783 6.34555 6.59684C6.47171 6.93716 6.57647 7.08584 6.6787 7.18308C6.75978 7.26021 6.85016 7.32847 6.9482 7.38631C7.07461 7.46091 7.2556 7.52465 7.63389 7.56094C7.88784 7.58531 8.19058 7.59448 8.58009 7.59793L9.30138 5.43404C9.47603 4.9101 10.0424 4.62694 10.5663 4.80159C11.0902 4.97624 11.3734 5.54256 11.1988 6.0665L10.6878 7.59935C11.1924 7.59743 11.5638 7.58994 11.8661 7.56094C12.2444 7.52465 12.4254 7.46091 12.5518 7.38631C12.6498 7.32847 12.7402 7.26021 12.8213 7.18308C12.9235 7.08584 13.0283 6.93716 13.1545 6.59684C13.2875 6.23783 13.4128 5.75848 13.6018 5.02796C13.8391 4.11081 13.9933 3.50797 14.0525 3.05137C14.1097 2.6108 14.0536 2.46613 14.0143 2.39756C13.9647 2.31095 13.9007 2.23153 13.8242 2.1626C13.7575 2.10241 13.6136 2.01149 13.1493 1.9573C12.6718 1.90157 12.0247 1.9 11.0479 1.9L8.45213 1.9C7.47532 1.9 6.82816 1.90157 6.35067 1.9573ZM7.75 15.75C7.19772 15.75 6.75 16.1977 6.75 16.75C6.75 17.3023 7.19772 17.75 7.75 17.75H11.75C12.3023 17.75 12.75 17.3023 12.75 16.75C12.75 16.1977 12.3023 15.75 11.75 15.75L7.75 15.75Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.6 KiB |
@ -0,0 +1,752 @@
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:hmg_patient_app_new/core/api/api_client.dart';
|
||||
import 'package:hmg_patient_app_new/core/api_consts.dart';
|
||||
import 'package:hmg_patient_app_new/core/common_models/generic_api_model.dart';
|
||||
import 'package:hmg_patient_app_new/core/exceptions/api_failure.dart';
|
||||
import 'package:hmg_patient_app_new/services/logger_service.dart';
|
||||
|
||||
/// Progress types to request different ranges from the progress API.
|
||||
enum ProgressType { today, week, month }
|
||||
|
||||
abstract class HealthTrackersRepo {
|
||||
// ==================== BLOOD SUGAR (DIABETIC) ====================
|
||||
/// Get blood sugar result averages (week, month, year).
|
||||
Future<Either<Failure, GenericApiModel<dynamic>>> getDiabeticResultAverage();
|
||||
|
||||
/// Get blood sugar results (week, month, year).
|
||||
Future<Either<Failure, GenericApiModel<dynamic>>> getDiabeticResults();
|
||||
|
||||
/// Add new blood sugar result.
|
||||
Future<Either<Failure, GenericApiModel<dynamic>>> addDiabeticResult({
|
||||
required String bloodSugarDateChart,
|
||||
required String bloodSugarResult,
|
||||
required String diabeticUnit,
|
||||
required int measuredTime,
|
||||
});
|
||||
|
||||
/// Update existing blood sugar result.
|
||||
Future<Either<Failure, GenericApiModel<dynamic>>> updateDiabeticResult({
|
||||
required DateTime month,
|
||||
required DateTime hour,
|
||||
required String bloodSugarResult,
|
||||
required String diabeticUnit,
|
||||
required int measuredTime,
|
||||
required int lineItemNo,
|
||||
});
|
||||
|
||||
/// Deactivate blood sugar record.
|
||||
Future<Either<Failure, GenericApiModel<dynamic>>> deactivateDiabeticStatus({
|
||||
required int lineItemNo,
|
||||
});
|
||||
|
||||
// ==================== BLOOD PRESSURE ====================
|
||||
/// Get blood pressure result averages (week, month, year).
|
||||
Future<Either<Failure, GenericApiModel<dynamic>>> getBloodPressureResultAverage();
|
||||
|
||||
/// Get blood pressure results (week, month, year).
|
||||
Future<Either<Failure, GenericApiModel<dynamic>>> getBloodPressureResults();
|
||||
|
||||
/// Add new blood pressure result.
|
||||
Future<Either<Failure, GenericApiModel<dynamic>>> addBloodPressureResult({
|
||||
required String bloodPressureDate,
|
||||
required String diastolicPressure,
|
||||
required String systolicePressure,
|
||||
required int measuredArm,
|
||||
});
|
||||
|
||||
/// Update existing blood pressure result.
|
||||
Future<Either<Failure, GenericApiModel<dynamic>>> updateBloodPressureResult({
|
||||
required String bloodPressureDate,
|
||||
required String diastolicPressure,
|
||||
required String systolicePressure,
|
||||
required int measuredArm,
|
||||
required int lineItemNo,
|
||||
});
|
||||
|
||||
/// Deactivate blood pressure record.
|
||||
Future<Either<Failure, GenericApiModel<dynamic>>> deactivateBloodPressureStatus({
|
||||
required int lineItemNo,
|
||||
});
|
||||
|
||||
// ==================== WEIGHT MEASUREMENT ====================
|
||||
/// Get weight measurement result averages (week, month, year).
|
||||
Future<Either<Failure, GenericApiModel<dynamic>>> getWeightMeasurementResultAverage();
|
||||
|
||||
/// Get weight measurement results (week, month, year).
|
||||
Future<Either<Failure, GenericApiModel<dynamic>>> getWeightMeasurementResults();
|
||||
|
||||
/// Add new weight measurement result.
|
||||
Future<Either<Failure, GenericApiModel<dynamic>>> addWeightMeasurementResult({
|
||||
required String weightDate,
|
||||
required String weightMeasured,
|
||||
required int weightUnit,
|
||||
});
|
||||
|
||||
/// Update existing weight measurement result.
|
||||
Future<Either<Failure, GenericApiModel<dynamic>>> updateWeightMeasurementResult({
|
||||
required int lineItemNo,
|
||||
required int weightUnit,
|
||||
required String weightMeasured,
|
||||
required String weightDate,
|
||||
});
|
||||
|
||||
/// Deactivate weight measurement record.
|
||||
Future<Either<Failure, GenericApiModel<dynamic>>> deactivateWeightMeasurementStatus({
|
||||
required int lineItemNo,
|
||||
});
|
||||
}
|
||||
|
||||
class HealthTrackersRepoImp implements HealthTrackersRepo {
|
||||
final ApiClient apiClient;
|
||||
final LoggerService loggerService;
|
||||
|
||||
HealthTrackersRepoImp({required this.loggerService, required this.apiClient});
|
||||
|
||||
// ==================== BLOOD SUGAR (DIABETIC) METHODS ====================
|
||||
|
||||
@override
|
||||
Future<Either<Failure, GenericApiModel<dynamic>>> getDiabeticResultAverage() async {
|
||||
try {
|
||||
GenericApiModel<dynamic>? apiResponse;
|
||||
Failure? failure;
|
||||
|
||||
await apiClient.post(
|
||||
ApiConsts.getDiabeticResultAverage,
|
||||
body: {},
|
||||
onFailure: (error, statusCode, {messageStatus, failureType}) {
|
||||
failure = failureType;
|
||||
},
|
||||
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
|
||||
try {
|
||||
dynamic extracted;
|
||||
if (response is Map) {
|
||||
// Extract average lists
|
||||
extracted = {
|
||||
'monthAverageList': response['List_MonthDiabtectResultAverage'] ?? [],
|
||||
'weekAverageList': response['List_WeekDiabtectResultAverage'] ?? [],
|
||||
'yearAverageList': response['List_YearDiabtecResultAverage'] ?? [],
|
||||
};
|
||||
} else {
|
||||
extracted = response;
|
||||
}
|
||||
|
||||
apiResponse = GenericApiModel<dynamic>(
|
||||
messageStatus: messageStatus,
|
||||
statusCode: statusCode,
|
||||
errorMessage: errorMessage,
|
||||
data: extracted,
|
||||
);
|
||||
} catch (e) {
|
||||
failure = DataParsingFailure(e.toString());
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
if (failure != null) return Left(failure!);
|
||||
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
|
||||
return Right(apiResponse!);
|
||||
} catch (e) {
|
||||
return Left(UnknownFailure(e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, GenericApiModel<dynamic>>> getDiabeticResults() async {
|
||||
try {
|
||||
GenericApiModel<dynamic>? apiResponse;
|
||||
Failure? failure;
|
||||
|
||||
await apiClient.post(
|
||||
ApiConsts.getDiabeticResult,
|
||||
body: {},
|
||||
onFailure: (error, statusCode, {messageStatus, failureType}) {
|
||||
failure = failureType;
|
||||
},
|
||||
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
|
||||
try {
|
||||
dynamic extracted;
|
||||
if (response is Map) {
|
||||
// Extract patient result lists
|
||||
extracted = {
|
||||
'monthResultList': response['List_MonthDiabtecPatientResult'] ?? [],
|
||||
'weekResultList': response['List_WeekDiabtecPatientResult'] ?? [],
|
||||
'yearResultList': response['List_YearDiabtecPatientResult'] ?? [],
|
||||
};
|
||||
} else {
|
||||
extracted = response;
|
||||
}
|
||||
|
||||
apiResponse = GenericApiModel<dynamic>(
|
||||
messageStatus: messageStatus,
|
||||
statusCode: statusCode,
|
||||
errorMessage: errorMessage,
|
||||
data: extracted,
|
||||
);
|
||||
} catch (e) {
|
||||
failure = DataParsingFailure(e.toString());
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
if (failure != null) return Left(failure!);
|
||||
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
|
||||
return Right(apiResponse!);
|
||||
} catch (e) {
|
||||
return Left(UnknownFailure(e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, GenericApiModel<dynamic>>> addDiabeticResult({
|
||||
required String bloodSugarDateChart,
|
||||
required String bloodSugarResult,
|
||||
required String diabeticUnit,
|
||||
required int measuredTime,
|
||||
}) async {
|
||||
try {
|
||||
GenericApiModel<dynamic>? apiResponse;
|
||||
Failure? failure;
|
||||
|
||||
Map<String, dynamic> body = {
|
||||
'BloodSugerDateChart': bloodSugarDateChart,
|
||||
'BloodSugerResult': bloodSugarResult,
|
||||
'DiabtecUnit': diabeticUnit,
|
||||
'MeasuredTime': measuredTime + 1, // Add 1 as per old service
|
||||
};
|
||||
|
||||
await apiClient.post(
|
||||
ApiConsts.addDiabeticResult,
|
||||
body: body,
|
||||
onFailure: (error, statusCode, {messageStatus, failureType}) {
|
||||
failure = failureType;
|
||||
},
|
||||
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
|
||||
apiResponse = GenericApiModel<dynamic>(
|
||||
messageStatus: messageStatus,
|
||||
statusCode: statusCode,
|
||||
errorMessage: errorMessage,
|
||||
data: response,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
if (failure != null) return Left(failure!);
|
||||
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
|
||||
return Right(apiResponse!);
|
||||
} catch (e) {
|
||||
return Left(UnknownFailure(e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, GenericApiModel<dynamic>>> updateDiabeticResult({
|
||||
required DateTime month,
|
||||
required DateTime hour,
|
||||
required String bloodSugarResult,
|
||||
required String diabeticUnit,
|
||||
required int measuredTime,
|
||||
required int lineItemNo,
|
||||
}) async {
|
||||
try {
|
||||
GenericApiModel<dynamic>? apiResponse;
|
||||
Failure? failure;
|
||||
|
||||
// Format: 'YYYY-MM-DD HH:MM:SS' as per old service
|
||||
String formattedDate = '${month.year}-${month.month}-${month.day} ${hour.hour}:${hour.minute}:00';
|
||||
|
||||
Map<String, dynamic> body = {
|
||||
'BloodSugerDateChart': formattedDate,
|
||||
'BloodSugerResult': bloodSugarResult,
|
||||
'DiabtecUnit': diabeticUnit,
|
||||
'MeasuredTime': measuredTime + 1, // Add 1 as per old service
|
||||
'LineItemNo': lineItemNo,
|
||||
};
|
||||
|
||||
await apiClient.post(
|
||||
ApiConsts.updateDiabeticResult,
|
||||
body: body,
|
||||
onFailure: (error, statusCode, {messageStatus, failureType}) {
|
||||
failure = failureType;
|
||||
},
|
||||
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
|
||||
apiResponse = GenericApiModel<dynamic>(
|
||||
messageStatus: messageStatus,
|
||||
statusCode: statusCode,
|
||||
errorMessage: errorMessage,
|
||||
data: response,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
if (failure != null) return Left(failure!);
|
||||
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
|
||||
return Right(apiResponse!);
|
||||
} catch (e) {
|
||||
return Left(UnknownFailure(e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, GenericApiModel<dynamic>>> deactivateDiabeticStatus({
|
||||
required int lineItemNo,
|
||||
}) async {
|
||||
try {
|
||||
GenericApiModel<dynamic>? apiResponse;
|
||||
Failure? failure;
|
||||
|
||||
Map<String, dynamic> body = {
|
||||
'LineItemNo': lineItemNo,
|
||||
};
|
||||
|
||||
await apiClient.post(
|
||||
ApiConsts.deactivateDiabeticStatus,
|
||||
body: body,
|
||||
onFailure: (error, statusCode, {messageStatus, failureType}) {
|
||||
failure = failureType;
|
||||
},
|
||||
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
|
||||
apiResponse = GenericApiModel<dynamic>(
|
||||
messageStatus: messageStatus,
|
||||
statusCode: statusCode,
|
||||
errorMessage: errorMessage,
|
||||
data: response,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
if (failure != null) return Left(failure!);
|
||||
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
|
||||
return Right(apiResponse!);
|
||||
} catch (e) {
|
||||
return Left(UnknownFailure(e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== BLOOD PRESSURE METHODS ====================
|
||||
|
||||
@override
|
||||
Future<Either<Failure, GenericApiModel<dynamic>>> getBloodPressureResultAverage() async {
|
||||
try {
|
||||
GenericApiModel<dynamic>? apiResponse;
|
||||
Failure? failure;
|
||||
|
||||
await apiClient.post(
|
||||
ApiConsts.getBloodPressureResultAverage,
|
||||
body: {},
|
||||
onFailure: (error, statusCode, {messageStatus, failureType}) {
|
||||
failure = failureType;
|
||||
},
|
||||
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
|
||||
try {
|
||||
dynamic extracted;
|
||||
if (response is Map) {
|
||||
// Extract all three list types
|
||||
extracted = {
|
||||
'monthList': response['List_MonthBloodPressureResultAverage'] ?? [],
|
||||
'weekList': response['List_WeekBloodPressureResultAverage'] ?? [],
|
||||
'yearList': response['List_YearBloodPressureResultAverage'] ?? [],
|
||||
};
|
||||
} else {
|
||||
extracted = response;
|
||||
}
|
||||
|
||||
apiResponse = GenericApiModel<dynamic>(
|
||||
messageStatus: messageStatus,
|
||||
statusCode: statusCode,
|
||||
errorMessage: errorMessage,
|
||||
data: extracted,
|
||||
);
|
||||
} catch (e) {
|
||||
failure = DataParsingFailure(e.toString());
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
if (failure != null) return Left(failure!);
|
||||
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
|
||||
return Right(apiResponse!);
|
||||
} catch (e) {
|
||||
return Left(UnknownFailure(e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, GenericApiModel<dynamic>>> getBloodPressureResults() async {
|
||||
try {
|
||||
GenericApiModel<dynamic>? apiResponse;
|
||||
Failure? failure;
|
||||
|
||||
await apiClient.post(
|
||||
ApiConsts.getBloodPressureResult,
|
||||
body: {},
|
||||
onFailure: (error, statusCode, {messageStatus, failureType}) {
|
||||
failure = failureType;
|
||||
},
|
||||
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
|
||||
try {
|
||||
dynamic extracted;
|
||||
if (response is Map) {
|
||||
// Extract all three list types
|
||||
extracted = {
|
||||
'weekList': response['List_WeekBloodPressureResult'] ?? [],
|
||||
'monthList': response['List_MonthBloodPressureResult'] ?? [],
|
||||
'yearList': response['List_YearBloodPressureResult'] ?? [],
|
||||
};
|
||||
} else {
|
||||
extracted = response;
|
||||
}
|
||||
|
||||
apiResponse = GenericApiModel<dynamic>(
|
||||
messageStatus: messageStatus,
|
||||
statusCode: statusCode,
|
||||
errorMessage: errorMessage,
|
||||
data: extracted,
|
||||
);
|
||||
} catch (e) {
|
||||
failure = DataParsingFailure(e.toString());
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
if (failure != null) return Left(failure!);
|
||||
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
|
||||
return Right(apiResponse!);
|
||||
} catch (e) {
|
||||
return Left(UnknownFailure(e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, GenericApiModel<dynamic>>> addBloodPressureResult({
|
||||
required String bloodPressureDate,
|
||||
required String diastolicPressure,
|
||||
required String systolicePressure,
|
||||
required int measuredArm,
|
||||
}) async {
|
||||
try {
|
||||
GenericApiModel<dynamic>? apiResponse;
|
||||
Failure? failure;
|
||||
|
||||
Map<String, dynamic> body = {
|
||||
'BloodPressureDate': bloodPressureDate,
|
||||
'DiastolicPressure': diastolicPressure,
|
||||
'SystolicePressure': systolicePressure,
|
||||
'MeasuredArm': measuredArm,
|
||||
};
|
||||
|
||||
await apiClient.post(
|
||||
ApiConsts.addBloodPressureResult,
|
||||
body: body,
|
||||
onFailure: (error, statusCode, {messageStatus, failureType}) {
|
||||
failure = failureType;
|
||||
},
|
||||
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
|
||||
apiResponse = GenericApiModel<dynamic>(
|
||||
messageStatus: messageStatus,
|
||||
statusCode: statusCode,
|
||||
errorMessage: errorMessage,
|
||||
data: response,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
if (failure != null) return Left(failure!);
|
||||
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
|
||||
return Right(apiResponse!);
|
||||
} catch (e) {
|
||||
return Left(UnknownFailure(e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, GenericApiModel<dynamic>>> updateBloodPressureResult({
|
||||
required String bloodPressureDate,
|
||||
required String diastolicPressure,
|
||||
required String systolicePressure,
|
||||
required int measuredArm,
|
||||
required int lineItemNo,
|
||||
}) async {
|
||||
try {
|
||||
GenericApiModel<dynamic>? apiResponse;
|
||||
Failure? failure;
|
||||
|
||||
Map<String, dynamic> body = {
|
||||
'BloodPressureDate': bloodPressureDate,
|
||||
'DiastolicPressure': diastolicPressure,
|
||||
'SystolicePressure': systolicePressure,
|
||||
'MeasuredArm': measuredArm,
|
||||
'LineItemNo': lineItemNo,
|
||||
};
|
||||
|
||||
await apiClient.post(
|
||||
ApiConsts.updateBloodPressureResult,
|
||||
body: body,
|
||||
onFailure: (error, statusCode, {messageStatus, failureType}) {
|
||||
failure = failureType;
|
||||
},
|
||||
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
|
||||
apiResponse = GenericApiModel<dynamic>(
|
||||
messageStatus: messageStatus,
|
||||
statusCode: statusCode,
|
||||
errorMessage: errorMessage,
|
||||
data: response,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
if (failure != null) return Left(failure!);
|
||||
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
|
||||
return Right(apiResponse!);
|
||||
} catch (e) {
|
||||
return Left(UnknownFailure(e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, GenericApiModel<dynamic>>> deactivateBloodPressureStatus({
|
||||
required int lineItemNo,
|
||||
}) async {
|
||||
try {
|
||||
GenericApiModel<dynamic>? apiResponse;
|
||||
Failure? failure;
|
||||
|
||||
Map<String, dynamic> body = {
|
||||
'LineItemNo': lineItemNo,
|
||||
};
|
||||
|
||||
await apiClient.post(
|
||||
ApiConsts.deactivateBloodPressureStatus,
|
||||
body: body,
|
||||
onFailure: (error, statusCode, {messageStatus, failureType}) {
|
||||
failure = failureType;
|
||||
},
|
||||
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
|
||||
apiResponse = GenericApiModel<dynamic>(
|
||||
messageStatus: messageStatus,
|
||||
statusCode: statusCode,
|
||||
errorMessage: errorMessage,
|
||||
data: response,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
if (failure != null) return Left(failure!);
|
||||
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
|
||||
return Right(apiResponse!);
|
||||
} catch (e) {
|
||||
return Left(UnknownFailure(e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== WEIGHT MEASUREMENT METHODS ====================
|
||||
|
||||
@override
|
||||
Future<Either<Failure, GenericApiModel<dynamic>>> getWeightMeasurementResultAverage() async {
|
||||
try {
|
||||
GenericApiModel<dynamic>? apiResponse;
|
||||
Failure? failure;
|
||||
|
||||
await apiClient.post(
|
||||
ApiConsts.getWeightMeasurementResultAverage,
|
||||
body: {},
|
||||
onFailure: (error, statusCode, {messageStatus, failureType}) {
|
||||
failure = failureType;
|
||||
},
|
||||
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
|
||||
try {
|
||||
dynamic extracted;
|
||||
if (response is Map) {
|
||||
// Extract average lists
|
||||
extracted = {
|
||||
'monthAverageList': response['List_MonthWeightMeasurementResultAverage'] ?? [],
|
||||
'weekAverageList': response['List_WeekWeightMeasurementResultAverage'] ?? [],
|
||||
'yearAverageList': response['List_YearWeightMeasurementResultAverage'] ?? [],
|
||||
};
|
||||
} else {
|
||||
extracted = response;
|
||||
}
|
||||
|
||||
apiResponse = GenericApiModel<dynamic>(
|
||||
messageStatus: messageStatus,
|
||||
statusCode: statusCode,
|
||||
errorMessage: errorMessage,
|
||||
data: extracted,
|
||||
);
|
||||
} catch (e) {
|
||||
failure = DataParsingFailure(e.toString());
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
if (failure != null) return Left(failure!);
|
||||
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
|
||||
return Right(apiResponse!);
|
||||
} catch (e) {
|
||||
return Left(UnknownFailure(e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, GenericApiModel<dynamic>>> getWeightMeasurementResults() async {
|
||||
try {
|
||||
GenericApiModel<dynamic>? apiResponse;
|
||||
Failure? failure;
|
||||
|
||||
await apiClient.post(
|
||||
ApiConsts.getWeightMeasurementResult,
|
||||
body: {},
|
||||
onFailure: (error, statusCode, {messageStatus, failureType}) {
|
||||
failure = failureType;
|
||||
},
|
||||
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
|
||||
try {
|
||||
dynamic extracted;
|
||||
if (response is Map) {
|
||||
// Extract result lists
|
||||
extracted = {
|
||||
'weekResultList': response['List_WeekWeightMeasurementResult'] ?? [],
|
||||
'monthResultList': response['List_MonthWeightMeasurementResult'] ?? [],
|
||||
'yearResultList': response['List_YearWeightMeasurementResult'] ?? [],
|
||||
};
|
||||
} else {
|
||||
extracted = response;
|
||||
}
|
||||
|
||||
apiResponse = GenericApiModel<dynamic>(
|
||||
messageStatus: messageStatus,
|
||||
statusCode: statusCode,
|
||||
errorMessage: errorMessage,
|
||||
data: extracted,
|
||||
);
|
||||
} catch (e) {
|
||||
failure = DataParsingFailure(e.toString());
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
if (failure != null) return Left(failure!);
|
||||
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
|
||||
return Right(apiResponse!);
|
||||
} catch (e) {
|
||||
return Left(UnknownFailure(e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, GenericApiModel<dynamic>>> addWeightMeasurementResult({
|
||||
required String weightDate,
|
||||
required String weightMeasured,
|
||||
required int weightUnit,
|
||||
}) async {
|
||||
try {
|
||||
GenericApiModel<dynamic>? apiResponse;
|
||||
Failure? failure;
|
||||
|
||||
Map<String, dynamic> body = {
|
||||
'WeightDate': weightDate,
|
||||
'WeightMeasured': weightMeasured,
|
||||
'weightUnit': weightUnit,
|
||||
};
|
||||
|
||||
await apiClient.post(
|
||||
ApiConsts.addWeightMeasurementResult,
|
||||
body: body,
|
||||
onFailure: (error, statusCode, {messageStatus, failureType}) {
|
||||
failure = failureType;
|
||||
},
|
||||
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
|
||||
apiResponse = GenericApiModel<dynamic>(
|
||||
messageStatus: messageStatus,
|
||||
statusCode: statusCode,
|
||||
errorMessage: errorMessage,
|
||||
data: response,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
if (failure != null) return Left(failure!);
|
||||
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
|
||||
return Right(apiResponse!);
|
||||
} catch (e) {
|
||||
return Left(UnknownFailure(e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, GenericApiModel<dynamic>>> updateWeightMeasurementResult({
|
||||
required int lineItemNo,
|
||||
required int weightUnit,
|
||||
required String weightMeasured,
|
||||
required String weightDate,
|
||||
}) async {
|
||||
try {
|
||||
GenericApiModel<dynamic>? apiResponse;
|
||||
Failure? failure;
|
||||
|
||||
Map<String, dynamic> body = {
|
||||
'LineItemNo': lineItemNo,
|
||||
'weightUnit': '$weightUnit', // Convert to string as per old service
|
||||
'WeightMeasured': weightMeasured,
|
||||
'WeightDate': weightDate,
|
||||
};
|
||||
|
||||
await apiClient.post(
|
||||
ApiConsts.updateWeightMeasurementResult,
|
||||
body: body,
|
||||
onFailure: (error, statusCode, {messageStatus, failureType}) {
|
||||
failure = failureType;
|
||||
},
|
||||
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
|
||||
apiResponse = GenericApiModel<dynamic>(
|
||||
messageStatus: messageStatus,
|
||||
statusCode: statusCode,
|
||||
errorMessage: errorMessage,
|
||||
data: response,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
if (failure != null) return Left(failure!);
|
||||
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
|
||||
return Right(apiResponse!);
|
||||
} catch (e) {
|
||||
return Left(UnknownFailure(e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, GenericApiModel<dynamic>>> deactivateWeightMeasurementStatus({
|
||||
required int lineItemNo,
|
||||
}) async {
|
||||
try {
|
||||
GenericApiModel<dynamic>? apiResponse;
|
||||
Failure? failure;
|
||||
|
||||
Map<String, dynamic> body = {
|
||||
'LineItemNo': lineItemNo,
|
||||
};
|
||||
|
||||
await apiClient.post(
|
||||
ApiConsts.deactivateWeightMeasurementStatus,
|
||||
body: body,
|
||||
onFailure: (error, statusCode, {messageStatus, failureType}) {
|
||||
failure = failureType;
|
||||
},
|
||||
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
|
||||
apiResponse = GenericApiModel<dynamic>(
|
||||
messageStatus: messageStatus,
|
||||
statusCode: statusCode,
|
||||
errorMessage: errorMessage,
|
||||
data: response,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
if (failure != null) return Left(failure!);
|
||||
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
|
||||
return Right(apiResponse!);
|
||||
} catch (e) {
|
||||
return Left(UnknownFailure(e.toString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,87 @@
|
||||
import 'package:hmg_patient_app_new/core/utils/date_util.dart';
|
||||
|
||||
class BloodPressureResult {
|
||||
int? patientID;
|
||||
int? lineItemNo;
|
||||
DateTime? bloodPressureDate;
|
||||
int? measuredArm;
|
||||
int? systolicePressure;
|
||||
int? diastolicPressure;
|
||||
dynamic remark;
|
||||
bool? isActive;
|
||||
int? chartYear;
|
||||
String? chartMonth;
|
||||
dynamic yearSystolicePressureAverageResult;
|
||||
dynamic monthSystolicePressureResult;
|
||||
dynamic weekSystolicePressureResult;
|
||||
int? yearDiastolicPressureAverageResult;
|
||||
dynamic monthDiastolicPressureResult;
|
||||
dynamic weekDiastolicPressureResult;
|
||||
String? measuredArmDesc;
|
||||
dynamic weekDesc;
|
||||
|
||||
BloodPressureResult(
|
||||
{this.patientID,
|
||||
this.lineItemNo,
|
||||
this.bloodPressureDate,
|
||||
this.measuredArm,
|
||||
this.systolicePressure,
|
||||
this.diastolicPressure,
|
||||
this.remark,
|
||||
this.isActive,
|
||||
this.chartYear,
|
||||
this.chartMonth,
|
||||
this.yearSystolicePressureAverageResult,
|
||||
this.monthSystolicePressureResult,
|
||||
this.weekSystolicePressureResult,
|
||||
this.yearDiastolicPressureAverageResult,
|
||||
this.monthDiastolicPressureResult,
|
||||
this.weekDiastolicPressureResult,
|
||||
this.measuredArmDesc,
|
||||
this.weekDesc});
|
||||
|
||||
BloodPressureResult.fromJson(Map<String, dynamic> json) {
|
||||
patientID = json['PatientID'];
|
||||
lineItemNo = json['LineItemNo'];
|
||||
bloodPressureDate = DateUtil.convertStringToDate(json['BloodPressureDate']);
|
||||
measuredArm = json['MeasuredArm'];
|
||||
systolicePressure = json['SystolicePressure'];
|
||||
diastolicPressure = json['DiastolicPressure'];
|
||||
remark = json['Remark'];
|
||||
isActive = json['IsActive'];
|
||||
chartYear = json['ChartYear'];
|
||||
chartMonth = json['ChartMonth'];
|
||||
yearSystolicePressureAverageResult = json['YearSystolicePressureAverageResult'];
|
||||
monthSystolicePressureResult = json['MonthSystolicePressureResult'];
|
||||
weekSystolicePressureResult = json['WeekSystolicePressureResult'];
|
||||
yearDiastolicPressureAverageResult = json['YearDiastolicPressureAverageResult'];
|
||||
monthDiastolicPressureResult = json['MonthDiastolicPressureResult'];
|
||||
weekDiastolicPressureResult = json['WeekDiastolicPressureResult'];
|
||||
measuredArmDesc = json['MeasuredArmDesc'];
|
||||
weekDesc = json['WeekDesc'];
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = <String, dynamic>{};
|
||||
data['PatientID'] = patientID;
|
||||
data['LineItemNo'] = lineItemNo;
|
||||
data['BloodPressureDate'] = bloodPressureDate;
|
||||
data['MeasuredArm'] = measuredArm;
|
||||
data['SystolicePressure'] = systolicePressure;
|
||||
data['DiastolicPressure'] = diastolicPressure;
|
||||
data['Remark'] = remark;
|
||||
data['IsActive'] = isActive;
|
||||
data['ChartYear'] = chartYear;
|
||||
data['ChartMonth'] = chartMonth;
|
||||
data['YearSystolicePressureAverageResult'] = yearSystolicePressureAverageResult;
|
||||
data['MonthSystolicePressureResult'] = monthSystolicePressureResult;
|
||||
data['WeekSystolicePressureResult'] = weekSystolicePressureResult;
|
||||
data['YearDiastolicPressureAverageResult'] = yearDiastolicPressureAverageResult;
|
||||
data['MonthDiastolicPressureResult'] = monthDiastolicPressureResult;
|
||||
data['WeekDiastolicPressureResult'] = weekDiastolicPressureResult;
|
||||
data['MeasuredArmDesc'] = measuredArmDesc;
|
||||
data['WeekDesc'] = weekDesc;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,58 @@
|
||||
class MonthBloodPressureResultAverage {
|
||||
dynamic weekfourSystolicePressureAverageResult;
|
||||
dynamic weekfourDiastolicPressureAverageResult;
|
||||
dynamic weekthreeSystolicePressureAverageResult;
|
||||
dynamic weekthreeDiastolicPressureAverageResult;
|
||||
dynamic weektwoSystolicePressureAverageResult;
|
||||
dynamic weektwoDiastolicPressureAverageResult;
|
||||
dynamic weekoneSystolicePressureAverageResult;
|
||||
dynamic weekoneDiastolicPressureAverageResult;
|
||||
String? weekDesc;
|
||||
int? weekDiastolicPressureAverageResult;
|
||||
int? weekSystolicePressureAverageResult;
|
||||
|
||||
MonthBloodPressureResultAverage({
|
||||
this.weekfourSystolicePressureAverageResult,
|
||||
this.weekfourDiastolicPressureAverageResult,
|
||||
this.weekthreeSystolicePressureAverageResult,
|
||||
this.weekthreeDiastolicPressureAverageResult,
|
||||
this.weektwoSystolicePressureAverageResult,
|
||||
this.weektwoDiastolicPressureAverageResult,
|
||||
this.weekoneSystolicePressureAverageResult,
|
||||
this.weekoneDiastolicPressureAverageResult,
|
||||
this.weekDesc,
|
||||
this.weekDiastolicPressureAverageResult,
|
||||
this.weekSystolicePressureAverageResult,
|
||||
});
|
||||
|
||||
MonthBloodPressureResultAverage.fromJson(Map<String, dynamic> json) {
|
||||
weekfourSystolicePressureAverageResult = json['weekfourSystolicePressureAverageResult'];
|
||||
weekfourDiastolicPressureAverageResult = json['weekfourDiastolicPressureAverageResult'];
|
||||
weekthreeSystolicePressureAverageResult = json['weekthreeSystolicePressureAverageResult'];
|
||||
weekthreeDiastolicPressureAverageResult = json['weekthreeDiastolicPressureAverageResult'];
|
||||
weektwoSystolicePressureAverageResult = json['weektwoSystolicePressureAverageResult'];
|
||||
weektwoDiastolicPressureAverageResult = json['weektwoDiastolicPressureAverageResult'];
|
||||
weekoneSystolicePressureAverageResult = json['weekoneSystolicePressureAverageResult'];
|
||||
weekoneDiastolicPressureAverageResult = json['weekoneDiastolicPressureAverageResult'];
|
||||
weekDesc = json['WeekDesc'];
|
||||
weekDiastolicPressureAverageResult = json['WeekDiastolicPressureAverageResult'];
|
||||
weekSystolicePressureAverageResult = json['WeekSystolicePressureAverageResult'];
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = <String, dynamic>{};
|
||||
data['weekfourSystolicePressureAverageResult'] = weekfourSystolicePressureAverageResult;
|
||||
data['weekfourDiastolicPressureAverageResult'] = weekfourDiastolicPressureAverageResult;
|
||||
data['weekthreeSystolicePressureAverageResult'] = weekthreeSystolicePressureAverageResult;
|
||||
data['weekthreeDiastolicPressureAverageResult'] = weekthreeDiastolicPressureAverageResult;
|
||||
data['weektwoSystolicePressureAverageResult'] = weektwoSystolicePressureAverageResult;
|
||||
data['weektwoDiastolicPressureAverageResult'] = weektwoDiastolicPressureAverageResult;
|
||||
data['weekoneSystolicePressureAverageResult'] = weekoneSystolicePressureAverageResult;
|
||||
data['weekoneDiastolicPressureAverageResult'] = weekoneDiastolicPressureAverageResult;
|
||||
data['WeekDesc'] = weekDesc;
|
||||
data['WeekDiastolicPressureAverageResult'] = weekDiastolicPressureAverageResult;
|
||||
data['WeekSystolicePressureAverageResult'] = weekSystolicePressureAverageResult;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,24 @@
|
||||
import 'package:hmg_patient_app_new/core/utils/date_util.dart';
|
||||
|
||||
class WeekBloodPressureResultAverage {
|
||||
int? dailySystolicePressureAverageResult;
|
||||
int? dailyDiastolicPressureAverageResult;
|
||||
DateTime? bloodPressureDate;
|
||||
|
||||
WeekBloodPressureResultAverage({this.dailySystolicePressureAverageResult, this.dailyDiastolicPressureAverageResult, this.bloodPressureDate});
|
||||
|
||||
WeekBloodPressureResultAverage.fromJson(Map<String, dynamic> json) {
|
||||
dailySystolicePressureAverageResult = json['DailySystolicePressureAverageResult'];
|
||||
dailyDiastolicPressureAverageResult = json['DailyDiastolicPressureAverageResult'];
|
||||
bloodPressureDate = DateUtil.convertStringToDate(json['BloodPressureDate']);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = <String, dynamic>{};
|
||||
data['DailySystolicePressureAverageResult'] = dailySystolicePressureAverageResult;
|
||||
data['DailyDiastolicPressureAverageResult'] = dailyDiastolicPressureAverageResult;
|
||||
data['BloodPressureDate'] = bloodPressureDate;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,37 @@
|
||||
import 'package:hmg_patient_app_new/core/utils/date_util.dart';
|
||||
|
||||
class YearBloodPressureResultAverage {
|
||||
int? monthSystolicePressureAverageResult;
|
||||
int? monthDiastolicPressureAverageResult;
|
||||
dynamic monthNumber;
|
||||
String? monthName;
|
||||
String? yearName;
|
||||
DateTime? date;
|
||||
|
||||
YearBloodPressureResultAverage({
|
||||
this.monthSystolicePressureAverageResult,
|
||||
this.monthDiastolicPressureAverageResult,
|
||||
this.monthNumber,
|
||||
this.monthName,
|
||||
this.yearName,
|
||||
});
|
||||
|
||||
YearBloodPressureResultAverage.fromJson(Map<String, dynamic> json) {
|
||||
monthSystolicePressureAverageResult = json['monthSystolicePressureAverageResult'];
|
||||
monthDiastolicPressureAverageResult = json['monthDiastolicPressureAverageResult'];
|
||||
monthNumber = json['monthNumber'];
|
||||
monthName = json['monthName'];
|
||||
yearName = json['yearName'];
|
||||
date = DateUtil.getMonthDateTime(monthName!, yearName);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = <String, dynamic>{};
|
||||
data['monthSystolicePressureAverageResult'] = monthSystolicePressureAverageResult;
|
||||
data['monthDiastolicPressureAverageResult'] = monthDiastolicPressureAverageResult;
|
||||
data['monthNumber'] = monthNumber;
|
||||
data['monthName'] = monthName;
|
||||
data['yearName'] = yearName;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,99 @@
|
||||
import 'package:hmg_patient_app_new/core/utils/date_util.dart';
|
||||
|
||||
class DiabeticPatientResult {
|
||||
String? chartMonth;
|
||||
var chartYear;
|
||||
DateTime? dateChart;
|
||||
var description;
|
||||
var descriptionN;
|
||||
int? diabtecAvarage;
|
||||
bool? isActive;
|
||||
int? lineItemNo;
|
||||
var listMonth;
|
||||
var listWeek;
|
||||
int? measured;
|
||||
String? measuredDesc;
|
||||
var monthAverageResult;
|
||||
int? patientID;
|
||||
var remark;
|
||||
var resultDesc;
|
||||
dynamic resultValue;
|
||||
String? unit;
|
||||
var weekAverageResult;
|
||||
String? weekDesc;
|
||||
var yearAverageResult;
|
||||
|
||||
DiabeticPatientResult(
|
||||
{this.chartMonth,
|
||||
this.chartYear,
|
||||
this.dateChart,
|
||||
this.description,
|
||||
this.descriptionN,
|
||||
this.diabtecAvarage,
|
||||
this.isActive,
|
||||
this.lineItemNo,
|
||||
this.listMonth,
|
||||
this.listWeek,
|
||||
this.measured,
|
||||
this.measuredDesc,
|
||||
this.monthAverageResult,
|
||||
this.patientID,
|
||||
this.remark,
|
||||
this.resultDesc,
|
||||
this.resultValue,
|
||||
this.unit,
|
||||
this.weekAverageResult,
|
||||
this.weekDesc,
|
||||
this.yearAverageResult});
|
||||
|
||||
DiabeticPatientResult.fromJson(Map<String, dynamic> json) {
|
||||
chartMonth = json['ChartMonth'];
|
||||
chartYear = json['ChartYear'];
|
||||
dateChart = DateUtil.convertStringToDate(json['DateChart']);
|
||||
description = json['Description'];
|
||||
descriptionN = json['DescriptionN'];
|
||||
diabtecAvarage = json['DiabtecAvarage'];
|
||||
isActive = json['IsActive'];
|
||||
lineItemNo = json['LineItemNo'];
|
||||
listMonth = json['List_Month'];
|
||||
listWeek = json['List_Week'];
|
||||
measured = json['Measured'];
|
||||
measuredDesc = json['MeasuredDesc'];
|
||||
monthAverageResult = json['MonthAverageResult'];
|
||||
patientID = json['PatientID'];
|
||||
remark = json['Remark'];
|
||||
resultDesc = json['ResultDesc'];
|
||||
resultValue = json['ResultValue'];
|
||||
unit = json['Unit'];
|
||||
weekAverageResult = json['WeekAverageResult'];
|
||||
weekDesc = json['WeekDesc'];
|
||||
yearAverageResult = json['YearAverageResult'];
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = <String, dynamic>{};
|
||||
data['ChartMonth'] = chartMonth;
|
||||
data['ChartYear'] = chartYear;
|
||||
data['DateChart'] = DateUtil.convertDateToString(dateChart!);
|
||||
data['Description'] = description;
|
||||
data['DescriptionN'] = descriptionN;
|
||||
data['DiabtecAvarage'] = diabtecAvarage;
|
||||
data['IsActive'] = isActive;
|
||||
data['LineItemNo'] = lineItemNo;
|
||||
data['List_Month'] = listMonth;
|
||||
data['List_Week'] = listWeek;
|
||||
data['Measured'] = measured;
|
||||
data['MeasuredDesc'] = measuredDesc;
|
||||
data['MonthAverageResult'] = monthAverageResult;
|
||||
data['PatientID'] = patientID;
|
||||
data['Remark'] = remark;
|
||||
data['ResultDesc'] = resultDesc;
|
||||
data['ResultValue'] = resultValue;
|
||||
data['Unit'] = unit;
|
||||
data['WeekAverageResult'] = weekAverageResult;
|
||||
data['WeekDesc'] = weekDesc;
|
||||
data['YearAverageResult'] = yearAverageResult;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,37 @@
|
||||
class MonthDiabeticResultAverage {
|
||||
var weekfourAverageResult;
|
||||
var weekthreeAverageResult;
|
||||
var weektwoAverageResult;
|
||||
var weekoneAverageResult;
|
||||
dynamic weekAverageResult;
|
||||
String? weekDesc;
|
||||
|
||||
MonthDiabeticResultAverage(
|
||||
{this.weekfourAverageResult,
|
||||
this.weekthreeAverageResult,
|
||||
this.weektwoAverageResult,
|
||||
this.weekoneAverageResult,
|
||||
this.weekAverageResult,
|
||||
this.weekDesc});
|
||||
|
||||
MonthDiabeticResultAverage.fromJson(Map<String, dynamic> json) {
|
||||
weekfourAverageResult = json['weekfourAverageResult'];
|
||||
weekthreeAverageResult = json['weekthreeAverageResult'];
|
||||
weektwoAverageResult = json['weektwoAverageResult'];
|
||||
weekoneAverageResult = json['weekoneAverageResult'];
|
||||
weekAverageResult = json['WeekAverageResult'];
|
||||
weekDesc = json['WeekDesc'];
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = <String, dynamic>{};
|
||||
data['weekfourAverageResult'] = weekfourAverageResult;
|
||||
data['weekthreeAverageResult'] = weekthreeAverageResult;
|
||||
data['weektwoAverageResult'] = weektwoAverageResult;
|
||||
data['weekoneAverageResult'] = weekoneAverageResult;
|
||||
data['WeekAverageResult'] = weekAverageResult;
|
||||
data['WeekDesc'] = weekDesc;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,21 @@
|
||||
import 'package:hmg_patient_app_new/core/utils/date_util.dart';
|
||||
|
||||
class WeekDiabeticResultAverage {
|
||||
dynamic dailyAverageResult;
|
||||
DateTime? dateChart;
|
||||
|
||||
WeekDiabeticResultAverage({this.dailyAverageResult, this.dateChart});
|
||||
|
||||
WeekDiabeticResultAverage.fromJson(Map<String, dynamic> json) {
|
||||
dailyAverageResult = json['DailyAverageResult'];
|
||||
dateChart = DateUtil.convertStringToDate(json['DateChart']);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = <String, dynamic>{};
|
||||
data['DailyAverageResult'] = dailyAverageResult;
|
||||
data['DateChart'] = DateUtil.convertDateToString(dateChart!);
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,35 @@
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:hmg_patient_app_new/core/utils/date_util.dart';
|
||||
|
||||
class YearDiabeticResultAverage {
|
||||
dynamic monthAverageResult;
|
||||
var monthNumber;
|
||||
String? monthName;
|
||||
String? yearName;
|
||||
DateTime? date;
|
||||
|
||||
YearDiabeticResultAverage({this.monthAverageResult, this.monthNumber, this.monthName, this.yearName});
|
||||
|
||||
YearDiabeticResultAverage.fromJson(Map<String, dynamic> json) {
|
||||
try {
|
||||
monthAverageResult = json['monthAverageResult'];
|
||||
monthNumber = json['monthNumber'];
|
||||
monthName = json['monthName'];
|
||||
yearName = json['yearName'];
|
||||
date = DateUtil.getMonthDateTime(monthName!, yearName);
|
||||
} catch (e) {
|
||||
log(e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = <String, dynamic>{};
|
||||
data['monthAverageResult'] = monthAverageResult;
|
||||
data['monthNumber'] = monthNumber;
|
||||
data['monthName'] = monthName;
|
||||
data['yearName'] = yearName;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,37 @@
|
||||
class MonthWeightMeasurementResultAverage {
|
||||
dynamic weekfourAverageResult;
|
||||
dynamic weekthreeAverageResult;
|
||||
dynamic weektwoAverageResult;
|
||||
dynamic weekoneAverageResult;
|
||||
dynamic weekAverageResult;
|
||||
String? weekDesc;
|
||||
|
||||
MonthWeightMeasurementResultAverage(
|
||||
{this.weekfourAverageResult,
|
||||
this.weekthreeAverageResult,
|
||||
this.weektwoAverageResult,
|
||||
this.weekoneAverageResult,
|
||||
this.weekAverageResult,
|
||||
this.weekDesc});
|
||||
|
||||
MonthWeightMeasurementResultAverage.fromJson(Map<String, dynamic> json) {
|
||||
weekfourAverageResult = json['weekfourAverageResult'];
|
||||
weekthreeAverageResult = json['weekthreeAverageResult'];
|
||||
weektwoAverageResult = json['weektwoAverageResult'];
|
||||
weekoneAverageResult = json['weekoneAverageResult'];
|
||||
weekAverageResult = json['WeekAverageResult'];
|
||||
weekDesc = json['WeekDesc'];
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = <String, dynamic>{};
|
||||
data['weekfourAverageResult'] = weekfourAverageResult;
|
||||
data['weekthreeAverageResult'] = weekthreeAverageResult;
|
||||
data['weektwoAverageResult'] = weektwoAverageResult;
|
||||
data['weekoneAverageResult'] = weekoneAverageResult;
|
||||
data['WeekAverageResult'] = weekAverageResult;
|
||||
data['WeekDesc'] = weekDesc;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,21 @@
|
||||
import 'package:hmg_patient_app_new/core/utils/date_util.dart';
|
||||
|
||||
class WeekWeightMeasurementResultAverage {
|
||||
dynamic dailyAverageResult;
|
||||
DateTime? weightDate;
|
||||
|
||||
WeekWeightMeasurementResultAverage({this.dailyAverageResult, this.weightDate});
|
||||
|
||||
WeekWeightMeasurementResultAverage.fromJson(Map<String, dynamic> json) {
|
||||
dailyAverageResult = json['DailyAverageResult'];
|
||||
weightDate = DateUtil.convertStringToDate(json['WeightDate']);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = <String, dynamic>{};
|
||||
data['DailyAverageResult'] = dailyAverageResult;
|
||||
data['WeightDate'] = weightDate;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,77 @@
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:hmg_patient_app_new/core/utils/date_util.dart';
|
||||
|
||||
class WeightMeasurementResult {
|
||||
int? patientID;
|
||||
int? lineItemNo;
|
||||
int? weightMeasured;
|
||||
DateTime? weightDate;
|
||||
dynamic remark;
|
||||
bool? isActive;
|
||||
int? measured;
|
||||
dynamic unit;
|
||||
int? chartYear;
|
||||
dynamic chartMonth;
|
||||
double? yearAverageResult;
|
||||
dynamic monthAverageResult;
|
||||
dynamic weekAverageResult;
|
||||
dynamic weekDesc;
|
||||
|
||||
WeightMeasurementResult(
|
||||
{this.patientID,
|
||||
this.lineItemNo,
|
||||
this.weightMeasured,
|
||||
this.weightDate,
|
||||
this.remark,
|
||||
this.isActive,
|
||||
this.measured,
|
||||
this.unit,
|
||||
this.chartYear,
|
||||
this.chartMonth,
|
||||
this.yearAverageResult,
|
||||
this.monthAverageResult,
|
||||
this.weekAverageResult,
|
||||
this.weekDesc});
|
||||
|
||||
WeightMeasurementResult.fromJson(Map<String, dynamic> json) {
|
||||
try {
|
||||
patientID = json['PatientID'];
|
||||
lineItemNo = json['LineItemNo'];
|
||||
weightMeasured = json['WeightMeasured'];
|
||||
weightDate = DateUtil.convertStringToDate(json['WeightDate']);
|
||||
remark = json['Remark'];
|
||||
isActive = json['IsActive'];
|
||||
measured = json['Measured'];
|
||||
unit = json['Unit'];
|
||||
chartYear = json['ChartYear'];
|
||||
chartMonth = json['ChartMonth'];
|
||||
// Convert to double safely since API may return int
|
||||
yearAverageResult = json['YearAverageResult'] != null ? (json['YearAverageResult'] as num).toDouble() : null;
|
||||
monthAverageResult = json['MonthAverageResult'];
|
||||
weekAverageResult = json['WeekAverageResult'];
|
||||
weekDesc = json['WeekDesc'];
|
||||
} catch (e) {
|
||||
log(e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = <String, dynamic>{};
|
||||
data['PatientID'] = patientID;
|
||||
data['LineItemNo'] = lineItemNo;
|
||||
data['WeightMeasured'] = weightMeasured;
|
||||
data['WeightDate'] = weightDate;
|
||||
data['Remark'] = remark;
|
||||
data['IsActive'] = isActive;
|
||||
data['Measured'] = measured;
|
||||
data['Unit'] = unit;
|
||||
data['ChartYear'] = chartYear;
|
||||
data['ChartMonth'] = chartMonth;
|
||||
data['YearAverageResult'] = yearAverageResult;
|
||||
data['MonthAverageResult'] = monthAverageResult;
|
||||
data['WeekAverageResult'] = weekAverageResult;
|
||||
data['WeekDesc'] = weekDesc;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,29 @@
|
||||
import 'package:hmg_patient_app_new/core/utils/date_util.dart';
|
||||
|
||||
class YearWeightMeasurementResultAverage {
|
||||
dynamic monthAverageResult;
|
||||
int? monthNumber;
|
||||
String? monthName;
|
||||
String? yearName;
|
||||
DateTime? date;
|
||||
|
||||
YearWeightMeasurementResultAverage({this.monthAverageResult, this.monthNumber, this.monthName, this.yearName});
|
||||
|
||||
YearWeightMeasurementResultAverage.fromJson(Map<String, dynamic> json) {
|
||||
monthAverageResult = json['monthAverageResult'];
|
||||
monthNumber = json['monthNumber'];
|
||||
monthName = json['monthName'];
|
||||
yearName = json['yearName'];
|
||||
date = DateUtil.getMonthDateTime(monthName!, yearName);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = <String, dynamic>{};
|
||||
data['monthAverageResult'] = monthAverageResult;
|
||||
data['monthNumber'] = monthNumber;
|
||||
data['monthName'] = monthName;
|
||||
data['yearName'] = yearName;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,102 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hmg_patient_app_new/theme/colors.dart';
|
||||
|
||||
/// UI-only helper model for Vital Sign cards.
|
||||
///
|
||||
/// Keeps presentation logic (chip colors, icon colors, simple status rules)
|
||||
/// in one place so it can be reused across multiple pages.
|
||||
class VitalSignUiModel {
|
||||
final Color iconBg;
|
||||
final Color iconFg;
|
||||
final Color chipBg;
|
||||
final Color chipFg;
|
||||
|
||||
const VitalSignUiModel({
|
||||
required this.iconBg,
|
||||
required this.iconFg,
|
||||
required this.chipBg,
|
||||
required this.chipFg,
|
||||
});
|
||||
|
||||
/// Returns a color scheme for a card based on its [status] and [label].
|
||||
///
|
||||
/// Rules (mirrors existing behavior in Medical File page):
|
||||
/// - Height is always blue.
|
||||
/// - High => red scheme.
|
||||
/// - Low => yellow scheme.
|
||||
/// - Otherwise => green scheme (Normal).
|
||||
static VitalSignUiModel scheme({required String? status, required String label}) {
|
||||
final s = (status ?? '').toLowerCase();
|
||||
final l = label.toLowerCase();
|
||||
|
||||
// Height should always be blue.
|
||||
if (l.contains('height')) {
|
||||
return VitalSignUiModel(
|
||||
iconBg: AppColors.infoColor.withValues(alpha: 0.12),
|
||||
iconFg: AppColors.infoColor,
|
||||
chipBg: AppColors.infoColor.withValues(alpha: 0.12),
|
||||
chipFg: AppColors.infoColor,
|
||||
);
|
||||
}
|
||||
|
||||
if (s.contains('high')) {
|
||||
return const VitalSignUiModel(
|
||||
iconBg: AppColors.chipSecondaryLightRedColor,
|
||||
iconFg: AppColors.primaryRedColor,
|
||||
chipBg: AppColors.chipSecondaryLightRedColor,
|
||||
chipFg: AppColors.primaryRedColor,
|
||||
);
|
||||
}
|
||||
|
||||
if (s.contains('low')) {
|
||||
final Color yellowBg = AppColors.warningColor.withValues(alpha: 0.12);
|
||||
return VitalSignUiModel(
|
||||
iconBg: yellowBg,
|
||||
iconFg: AppColors.warningColor,
|
||||
chipBg: yellowBg,
|
||||
chipFg: AppColors.warningColor,
|
||||
);
|
||||
}
|
||||
|
||||
// Normal (green)
|
||||
final Color greenBg = AppColors.lightGreenColor;
|
||||
return VitalSignUiModel(
|
||||
iconBg: greenBg,
|
||||
iconFg: AppColors.bgGreenColor,
|
||||
chipBg: greenBg,
|
||||
chipFg: AppColors.bgGreenColor,
|
||||
);
|
||||
}
|
||||
|
||||
/// Simple, user-friendly classification:
|
||||
/// - Low: systolic < 90 OR diastolic < 60
|
||||
/// - High: systolic >= 140 OR diastolic >= 90
|
||||
/// - Normal: otherwise
|
||||
/// Returns null if values are missing/unparseable.
|
||||
static String? bloodPressureStatus({dynamic systolic, dynamic diastolic}) {
|
||||
final int? s = toIntOrNull(systolic);
|
||||
final int? d = toIntOrNull(diastolic);
|
||||
if (s == null || d == null) return null;
|
||||
|
||||
if (s < 90 || d < 60) return 'Low';
|
||||
if (s >= 140 || d >= 90) return 'High';
|
||||
return 'Normal';
|
||||
}
|
||||
|
||||
static int? toIntOrNull(dynamic v) {
|
||||
if (v == null) return null;
|
||||
if (v is int) return v;
|
||||
if (v is double) return v.round();
|
||||
return int.tryParse(v.toString());
|
||||
}
|
||||
|
||||
static String bmiStatus(dynamic bmi) {
|
||||
if (bmi == null) return 'N/A';
|
||||
final double bmiValue = double.tryParse(bmi.toString()) ?? 0;
|
||||
if (bmiValue < 18.5) return 'Underweight';
|
||||
if (bmiValue < 25) return 'Normal';
|
||||
if (bmiValue < 30) return 'Overweight';
|
||||
return 'High';
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,489 @@
|
||||
class GetInvoiceDetailsResponseModel {
|
||||
int? projectID;
|
||||
int? doctorID;
|
||||
num? grandTotal;
|
||||
num? quantity;
|
||||
num? total;
|
||||
num? discount;
|
||||
num? subTotal;
|
||||
int? invoiceNo;
|
||||
String? createdOn;
|
||||
String? procedureID;
|
||||
String? procedureName;
|
||||
String? procedureNameN;
|
||||
num? procedurePrice;
|
||||
num? patientShare;
|
||||
num? companyShare;
|
||||
num? totalPatientShare;
|
||||
num? totalCompanyShare;
|
||||
num? totalShare;
|
||||
num? discountAmount;
|
||||
num? vATPercentage;
|
||||
num? patientVATAmount;
|
||||
num? companyVATAmount;
|
||||
num? totalVATAmount;
|
||||
num? price;
|
||||
int? patientID;
|
||||
String? patientIdentificationNo;
|
||||
String? patientName;
|
||||
String? patientNameN;
|
||||
String? nationalityID;
|
||||
String? doctorName;
|
||||
String? doctorNameN;
|
||||
int? clinicID;
|
||||
String? clinicDescription;
|
||||
String? clinicDescriptionN;
|
||||
String? appointmentDate;
|
||||
int? appointmentNo;
|
||||
String? insuranceID;
|
||||
int? companyID;
|
||||
String? companyName;
|
||||
String? companyNameN;
|
||||
String? companyAddress;
|
||||
String? companyAddressN;
|
||||
String? companyGroupAddress;
|
||||
String? groupName;
|
||||
String? groupNameN;
|
||||
String? patientAddress;
|
||||
String? vATNo;
|
||||
String? paymentDate;
|
||||
String? projectName;
|
||||
num? totalDiscount;
|
||||
num? totalPatientShareWithQuantity;
|
||||
String? legalName;
|
||||
String? legalNameN;
|
||||
num? advanceAdjustment;
|
||||
String? patientCityName;
|
||||
String? patientCityNameN;
|
||||
String? doctorImageURL;
|
||||
List<ListConsultation>? listConsultation;
|
||||
|
||||
GetInvoiceDetailsResponseModel(
|
||||
{this.projectID,
|
||||
this.doctorID,
|
||||
this.grandTotal,
|
||||
this.quantity,
|
||||
this.total,
|
||||
this.discount,
|
||||
this.subTotal,
|
||||
this.invoiceNo,
|
||||
this.createdOn,
|
||||
this.procedureID,
|
||||
this.procedureName,
|
||||
this.procedureNameN,
|
||||
this.procedurePrice,
|
||||
this.patientShare,
|
||||
this.companyShare,
|
||||
this.totalPatientShare,
|
||||
this.totalCompanyShare,
|
||||
this.totalShare,
|
||||
this.discountAmount,
|
||||
this.vATPercentage,
|
||||
this.patientVATAmount,
|
||||
this.companyVATAmount,
|
||||
this.totalVATAmount,
|
||||
this.price,
|
||||
this.patientID,
|
||||
this.patientIdentificationNo,
|
||||
this.patientName,
|
||||
this.patientNameN,
|
||||
this.nationalityID,
|
||||
this.doctorName,
|
||||
this.doctorNameN,
|
||||
this.clinicID,
|
||||
this.clinicDescription,
|
||||
this.clinicDescriptionN,
|
||||
this.appointmentDate,
|
||||
this.appointmentNo,
|
||||
this.insuranceID,
|
||||
this.companyID,
|
||||
this.companyName,
|
||||
this.companyNameN,
|
||||
this.companyAddress,
|
||||
this.companyAddressN,
|
||||
this.companyGroupAddress,
|
||||
this.groupName,
|
||||
this.groupNameN,
|
||||
this.patientAddress,
|
||||
this.vATNo,
|
||||
this.paymentDate,
|
||||
this.projectName,
|
||||
this.totalDiscount,
|
||||
this.totalPatientShareWithQuantity,
|
||||
this.legalName,
|
||||
this.legalNameN,
|
||||
this.advanceAdjustment,
|
||||
this.patientCityName,
|
||||
this.patientCityNameN,
|
||||
this.doctorImageURL,
|
||||
this.listConsultation});
|
||||
|
||||
GetInvoiceDetailsResponseModel.fromJson(Map<String, dynamic> json) {
|
||||
projectID = json['ProjectID'];
|
||||
doctorID = json['DoctorID'];
|
||||
grandTotal = json['GrandTotal'];
|
||||
quantity = json['Quantity'];
|
||||
total = json['Total'];
|
||||
discount = json['Discount'];
|
||||
subTotal = json['SubTotal'];
|
||||
invoiceNo = json['InvoiceNo'];
|
||||
createdOn = json['CreatedOn'];
|
||||
procedureID = json['ProcedureID'];
|
||||
procedureName = json['ProcedureName'];
|
||||
procedureNameN = json['ProcedureNameN'];
|
||||
procedurePrice = json['ProcedurePrice'];
|
||||
patientShare = json['PatientShare'];
|
||||
companyShare = json['CompanyShare'];
|
||||
totalPatientShare = json['TotalPatientShare'];
|
||||
totalCompanyShare = json['TotalCompanyShare'];
|
||||
totalShare = json['TotalShare'];
|
||||
discountAmount = json['DiscountAmount'];
|
||||
vATPercentage = json['VATPercentage'];
|
||||
patientVATAmount = json['PatientVATAmount'];
|
||||
companyVATAmount = json['CompanyVATAmount'];
|
||||
totalVATAmount = json['TotalVATAmount'];
|
||||
price = json['Price'];
|
||||
patientID = json['PatientID'];
|
||||
patientIdentificationNo = json['PatientIdentificationNo'];
|
||||
patientName = json['PatientName'];
|
||||
patientNameN = json['PatientNameN'];
|
||||
nationalityID = json['NationalityID'];
|
||||
doctorName = json['DoctorName'];
|
||||
doctorNameN = json['DoctorNameN'];
|
||||
clinicID = json['ClinicID'];
|
||||
clinicDescription = json['ClinicDescription'];
|
||||
clinicDescriptionN = json['ClinicDescriptionN'];
|
||||
appointmentDate = json['AppointmentDate'];
|
||||
appointmentNo = json['AppointmentNo'];
|
||||
insuranceID = json['InsuranceID'];
|
||||
companyID = json['CompanyID'];
|
||||
companyName = json['CompanyName'];
|
||||
companyNameN = json['CompanyNameN'];
|
||||
companyAddress = json['CompanyAddress'];
|
||||
companyAddressN = json['CompanyAddressN'];
|
||||
companyGroupAddress = json['CompanyGroupAddress'];
|
||||
groupName = json['GroupName'];
|
||||
groupNameN = json['GroupNameN'];
|
||||
patientAddress = json['PatientAddress'];
|
||||
vATNo = json['VATNo'];
|
||||
paymentDate = json['PaymentDate'];
|
||||
projectName = json['ProjectName'];
|
||||
totalDiscount = json['TotalDiscount'];
|
||||
totalPatientShareWithQuantity = json['TotalPatientShareWithQuantity'];
|
||||
legalName = json['LegalName'];
|
||||
legalNameN = json['LegalNameN'];
|
||||
advanceAdjustment = json['AdvanceAdjustment'];
|
||||
patientCityName = json['PatientCityName'];
|
||||
patientCityNameN = json['PatientCityNameN'];
|
||||
doctorImageURL = json['DoctorImageURL'];
|
||||
if (json['listConsultation'] != null) {
|
||||
listConsultation = <ListConsultation>[];
|
||||
json['listConsultation'].forEach((v) {
|
||||
listConsultation!.add(new ListConsultation.fromJson(v));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = new Map<String, dynamic>();
|
||||
data['ProjectID'] = this.projectID;
|
||||
data['DoctorID'] = this.doctorID;
|
||||
data['GrandTotal'] = this.grandTotal;
|
||||
data['Quantity'] = this.quantity;
|
||||
data['Total'] = this.total;
|
||||
data['Discount'] = this.discount;
|
||||
data['SubTotal'] = this.subTotal;
|
||||
data['InvoiceNo'] = this.invoiceNo;
|
||||
data['CreatedOn'] = this.createdOn;
|
||||
data['ProcedureID'] = this.procedureID;
|
||||
data['ProcedureName'] = this.procedureName;
|
||||
data['ProcedureNameN'] = this.procedureNameN;
|
||||
data['ProcedurePrice'] = this.procedurePrice;
|
||||
data['PatientShare'] = this.patientShare;
|
||||
data['CompanyShare'] = this.companyShare;
|
||||
data['TotalPatientShare'] = this.totalPatientShare;
|
||||
data['TotalCompanyShare'] = this.totalCompanyShare;
|
||||
data['TotalShare'] = this.totalShare;
|
||||
data['DiscountAmount'] = this.discountAmount;
|
||||
data['VATPercentage'] = this.vATPercentage;
|
||||
data['PatientVATAmount'] = this.patientVATAmount;
|
||||
data['CompanyVATAmount'] = this.companyVATAmount;
|
||||
data['TotalVATAmount'] = this.totalVATAmount;
|
||||
data['Price'] = this.price;
|
||||
data['PatientID'] = this.patientID;
|
||||
data['PatientIdentificationNo'] = this.patientIdentificationNo;
|
||||
data['PatientName'] = this.patientName;
|
||||
data['PatientNameN'] = this.patientNameN;
|
||||
data['NationalityID'] = this.nationalityID;
|
||||
data['DoctorName'] = this.doctorName;
|
||||
data['DoctorNameN'] = this.doctorNameN;
|
||||
data['ClinicID'] = this.clinicID;
|
||||
data['ClinicDescription'] = this.clinicDescription;
|
||||
data['ClinicDescriptionN'] = this.clinicDescriptionN;
|
||||
data['AppointmentDate'] = this.appointmentDate;
|
||||
data['AppointmentNo'] = this.appointmentNo;
|
||||
data['InsuranceID'] = this.insuranceID;
|
||||
data['CompanyID'] = this.companyID;
|
||||
data['CompanyName'] = this.companyName;
|
||||
data['CompanyNameN'] = this.companyNameN;
|
||||
data['CompanyAddress'] = this.companyAddress;
|
||||
data['CompanyAddressN'] = this.companyAddressN;
|
||||
data['CompanyGroupAddress'] = this.companyGroupAddress;
|
||||
data['GroupName'] = this.groupName;
|
||||
data['GroupNameN'] = this.groupNameN;
|
||||
data['PatientAddress'] = this.patientAddress;
|
||||
data['VATNo'] = this.vATNo;
|
||||
data['PaymentDate'] = this.paymentDate;
|
||||
data['ProjectName'] = this.projectName;
|
||||
data['TotalDiscount'] = this.totalDiscount;
|
||||
data['TotalPatientShareWithQuantity'] = this.totalPatientShareWithQuantity;
|
||||
data['LegalName'] = this.legalName;
|
||||
data['LegalNameN'] = this.legalNameN;
|
||||
data['AdvanceAdjustment'] = this.advanceAdjustment;
|
||||
data['PatientCityName'] = this.patientCityName;
|
||||
data['PatientCityNameN'] = this.patientCityNameN;
|
||||
data['DoctorImageURL'] = this.doctorImageURL;
|
||||
if (this.listConsultation != null) {
|
||||
data['listConsultation'] =
|
||||
this.listConsultation!.map((v) => v.toJson()).toList();
|
||||
}
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
class ListConsultation {
|
||||
int? projectID;
|
||||
int? doctorID;
|
||||
num? grandTotal;
|
||||
int? quantity;
|
||||
num? total;
|
||||
num? discount;
|
||||
num? subTotal;
|
||||
int? invoiceNo;
|
||||
String? createdOn;
|
||||
String? procedureID;
|
||||
String? procedureName;
|
||||
String? procedureNameN;
|
||||
num? procedurePrice;
|
||||
num? patientShare;
|
||||
num? companyShare;
|
||||
num? totalPatientShare;
|
||||
num? totalCompanyShare;
|
||||
num? totalShare;
|
||||
num? discountAmount;
|
||||
num? vATPercentage;
|
||||
num? patientVATAmount;
|
||||
num? companyVATAmount;
|
||||
num? totalVATAmount;
|
||||
num? price;
|
||||
int? patientID;
|
||||
int? patientIdentificationNo;
|
||||
String? patientName;
|
||||
String? patientNameN;
|
||||
String? nationalityID;
|
||||
String? doctorName;
|
||||
String? doctorNameN;
|
||||
int? clinicID;
|
||||
String? clinicDescription;
|
||||
String? clinicDescriptionN;
|
||||
String? appointmentDate;
|
||||
dynamic appointmentNo;
|
||||
dynamic insuranceID;
|
||||
dynamic companyID;
|
||||
String? companyName;
|
||||
String? companyNameN;
|
||||
String? companyAddress;
|
||||
String? companyAddressN;
|
||||
String? companyGroupAddress;
|
||||
String? groupName;
|
||||
String? groupNameN;
|
||||
String? patientAddress;
|
||||
String? vATNo;
|
||||
String? paymentDate;
|
||||
String? projectName;
|
||||
num? totalDiscount;
|
||||
num? totalPatientShareWithQuantity;
|
||||
String? legalName;
|
||||
String? legalNameN;
|
||||
num? advanceAdjustment;
|
||||
String? patientCityName;
|
||||
String? patientCityNameN;
|
||||
|
||||
ListConsultation(
|
||||
{this.projectID,
|
||||
this.doctorID,
|
||||
this.grandTotal,
|
||||
this.quantity,
|
||||
this.total,
|
||||
this.discount,
|
||||
this.subTotal,
|
||||
this.invoiceNo,
|
||||
this.createdOn,
|
||||
this.procedureID,
|
||||
this.procedureName,
|
||||
this.procedureNameN,
|
||||
this.procedurePrice,
|
||||
this.patientShare,
|
||||
this.companyShare,
|
||||
this.totalPatientShare,
|
||||
this.totalCompanyShare,
|
||||
this.totalShare,
|
||||
this.discountAmount,
|
||||
this.vATPercentage,
|
||||
this.patientVATAmount,
|
||||
this.companyVATAmount,
|
||||
this.totalVATAmount,
|
||||
this.price,
|
||||
this.patientID,
|
||||
this.patientIdentificationNo,
|
||||
this.patientName,
|
||||
this.patientNameN,
|
||||
this.nationalityID,
|
||||
this.doctorName,
|
||||
this.doctorNameN,
|
||||
this.clinicID,
|
||||
this.clinicDescription,
|
||||
this.clinicDescriptionN,
|
||||
this.appointmentDate,
|
||||
this.appointmentNo,
|
||||
this.insuranceID,
|
||||
this.companyID,
|
||||
this.companyName,
|
||||
this.companyNameN,
|
||||
this.companyAddress,
|
||||
this.companyAddressN,
|
||||
this.companyGroupAddress,
|
||||
this.groupName,
|
||||
this.groupNameN,
|
||||
this.patientAddress,
|
||||
this.vATNo,
|
||||
this.paymentDate,
|
||||
this.projectName,
|
||||
this.totalDiscount,
|
||||
this.totalPatientShareWithQuantity,
|
||||
this.legalName,
|
||||
this.legalNameN,
|
||||
this.advanceAdjustment,
|
||||
this.patientCityName,
|
||||
this.patientCityNameN});
|
||||
|
||||
ListConsultation.fromJson(Map<String, dynamic> json) {
|
||||
projectID = json['ProjectID'];
|
||||
doctorID = json['DoctorID'];
|
||||
grandTotal = json['GrandTotal'];
|
||||
quantity = json['Quantity'];
|
||||
total = json['Total'];
|
||||
discount = json['Discount'];
|
||||
subTotal = json['SubTotal'];
|
||||
invoiceNo = json['InvoiceNo'];
|
||||
createdOn = json['CreatedOn'];
|
||||
procedureID = json['ProcedureID'];
|
||||
procedureName = json['ProcedureName'];
|
||||
procedureNameN = json['ProcedureNameN'];
|
||||
procedurePrice = json['ProcedurePrice'];
|
||||
patientShare = json['PatientShare'];
|
||||
companyShare = json['CompanyShare'];
|
||||
totalPatientShare = json['TotalPatientShare'];
|
||||
totalCompanyShare = json['TotalCompanyShare'];
|
||||
totalShare = json['TotalShare'];
|
||||
discountAmount = json['DiscountAmount'];
|
||||
vATPercentage = json['VATPercentage'];
|
||||
patientVATAmount = json['PatientVATAmount'];
|
||||
companyVATAmount = json['CompanyVATAmount'];
|
||||
totalVATAmount = json['TotalVATAmount'];
|
||||
price = json['Price'];
|
||||
patientID = json['PatientID'];
|
||||
patientIdentificationNo = json['PatientIdentificationNo'];
|
||||
patientName = json['PatientName'];
|
||||
patientNameN = json['PatientNameN'];
|
||||
nationalityID = json['NationalityID'];
|
||||
doctorName = json['DoctorName'];
|
||||
doctorNameN = json['DoctorNameN'];
|
||||
clinicID = json['ClinicID'];
|
||||
clinicDescription = json['ClinicDescription'];
|
||||
clinicDescriptionN = json['ClinicDescriptionN'];
|
||||
appointmentDate = json['AppointmentDate'];
|
||||
appointmentNo = json['AppointmentNo'];
|
||||
insuranceID = json['InsuranceID'];
|
||||
companyID = json['CompanyID'];
|
||||
companyName = json['CompanyName'];
|
||||
companyNameN = json['CompanyNameN'];
|
||||
companyAddress = json['CompanyAddress'];
|
||||
companyAddressN = json['CompanyAddressN'];
|
||||
companyGroupAddress = json['CompanyGroupAddress'];
|
||||
groupName = json['GroupName'];
|
||||
groupNameN = json['GroupNameN'];
|
||||
patientAddress = json['PatientAddress'];
|
||||
vATNo = json['VATNo'];
|
||||
paymentDate = json['PaymentDate'];
|
||||
projectName = json['ProjectName'];
|
||||
totalDiscount = json['TotalDiscount'];
|
||||
totalPatientShareWithQuantity = json['TotalPatientShareWithQuantity'];
|
||||
legalName = json['LegalName'];
|
||||
legalNameN = json['LegalNameN'];
|
||||
advanceAdjustment = json['AdvanceAdjustment'];
|
||||
patientCityName = json['PatientCityName'];
|
||||
patientCityNameN = json['PatientCityNameN'];
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = new Map<String, dynamic>();
|
||||
data['ProjectID'] = this.projectID;
|
||||
data['DoctorID'] = this.doctorID;
|
||||
data['GrandTotal'] = this.grandTotal;
|
||||
data['Quantity'] = this.quantity;
|
||||
data['Total'] = this.total;
|
||||
data['Discount'] = this.discount;
|
||||
data['SubTotal'] = this.subTotal;
|
||||
data['InvoiceNo'] = this.invoiceNo;
|
||||
data['CreatedOn'] = this.createdOn;
|
||||
data['ProcedureID'] = this.procedureID;
|
||||
data['ProcedureName'] = this.procedureName;
|
||||
data['ProcedureNameN'] = this.procedureNameN;
|
||||
data['ProcedurePrice'] = this.procedurePrice;
|
||||
data['PatientShare'] = this.patientShare;
|
||||
data['CompanyShare'] = this.companyShare;
|
||||
data['TotalPatientShare'] = this.totalPatientShare;
|
||||
data['TotalCompanyShare'] = this.totalCompanyShare;
|
||||
data['TotalShare'] = this.totalShare;
|
||||
data['DiscountAmount'] = this.discountAmount;
|
||||
data['VATPercentage'] = this.vATPercentage;
|
||||
data['PatientVATAmount'] = this.patientVATAmount;
|
||||
data['CompanyVATAmount'] = this.companyVATAmount;
|
||||
data['TotalVATAmount'] = this.totalVATAmount;
|
||||
data['Price'] = this.price;
|
||||
data['PatientID'] = this.patientID;
|
||||
data['PatientIdentificationNo'] = this.patientIdentificationNo;
|
||||
data['PatientName'] = this.patientName;
|
||||
data['PatientNameN'] = this.patientNameN;
|
||||
data['NationalityID'] = this.nationalityID;
|
||||
data['DoctorName'] = this.doctorName;
|
||||
data['DoctorNameN'] = this.doctorNameN;
|
||||
data['ClinicID'] = this.clinicID;
|
||||
data['ClinicDescription'] = this.clinicDescription;
|
||||
data['ClinicDescriptionN'] = this.clinicDescriptionN;
|
||||
data['AppointmentDate'] = this.appointmentDate;
|
||||
data['AppointmentNo'] = this.appointmentNo;
|
||||
data['InsuranceID'] = this.insuranceID;
|
||||
data['CompanyID'] = this.companyID;
|
||||
data['CompanyName'] = this.companyName;
|
||||
data['CompanyNameN'] = this.companyNameN;
|
||||
data['CompanyAddress'] = this.companyAddress;
|
||||
data['CompanyAddressN'] = this.companyAddressN;
|
||||
data['CompanyGroupAddress'] = this.companyGroupAddress;
|
||||
data['GroupName'] = this.groupName;
|
||||
data['GroupNameN'] = this.groupNameN;
|
||||
data['PatientAddress'] = this.patientAddress;
|
||||
data['VATNo'] = this.vATNo;
|
||||
data['PaymentDate'] = this.paymentDate;
|
||||
data['ProjectName'] = this.projectName;
|
||||
data['TotalDiscount'] = this.totalDiscount;
|
||||
data['TotalPatientShareWithQuantity'] = this.totalPatientShareWithQuantity;
|
||||
data['LegalName'] = this.legalName;
|
||||
data['LegalNameN'] = this.legalNameN;
|
||||
data['AdvanceAdjustment'] = this.advanceAdjustment;
|
||||
data['PatientCityName'] = this.patientCityName;
|
||||
data['PatientCityNameN'] = this.patientCityNameN;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,88 @@
|
||||
class GetInvoicesListResponseModel {
|
||||
String? setupId;
|
||||
int? projectID;
|
||||
int? patientID;
|
||||
int? appointmentNo;
|
||||
String? appointmentDate;
|
||||
String? appointmentDateN;
|
||||
int? clinicID;
|
||||
int? doctorID;
|
||||
int? invoiceNo;
|
||||
int? status;
|
||||
String? arrivedOn;
|
||||
String? doctorName;
|
||||
String? doctorNameN;
|
||||
String? clinicName;
|
||||
double? decimalDoctorRate;
|
||||
String? doctorImageURL;
|
||||
int? doctorRate;
|
||||
int? patientNumber;
|
||||
String? projectName;
|
||||
|
||||
GetInvoicesListResponseModel(
|
||||
{this.setupId,
|
||||
this.projectID,
|
||||
this.patientID,
|
||||
this.appointmentNo,
|
||||
this.appointmentDate,
|
||||
this.appointmentDateN,
|
||||
this.clinicID,
|
||||
this.doctorID,
|
||||
this.invoiceNo,
|
||||
this.status,
|
||||
this.arrivedOn,
|
||||
this.doctorName,
|
||||
this.doctorNameN,
|
||||
this.clinicName,
|
||||
this.decimalDoctorRate,
|
||||
this.doctorImageURL,
|
||||
this.doctorRate,
|
||||
this.patientNumber,
|
||||
this.projectName});
|
||||
|
||||
GetInvoicesListResponseModel.fromJson(Map<String, dynamic> json) {
|
||||
setupId = json['SetupId'];
|
||||
projectID = json['ProjectID'];
|
||||
patientID = json['PatientID'];
|
||||
appointmentNo = json['AppointmentNo'];
|
||||
appointmentDate = json['AppointmentDate'];
|
||||
appointmentDateN = json['AppointmentDateN'];
|
||||
clinicID = json['ClinicID'];
|
||||
doctorID = json['DoctorID'];
|
||||
invoiceNo = json['InvoiceNo'];
|
||||
status = json['Status'];
|
||||
arrivedOn = json['ArrivedOn'];
|
||||
doctorName = json['DoctorName'];
|
||||
doctorNameN = json['DoctorNameN'];
|
||||
clinicName = json['ClinicName'];
|
||||
decimalDoctorRate = json['DecimalDoctorRate'];
|
||||
doctorImageURL = json['DoctorImageURL'];
|
||||
doctorRate = json['DoctorRate'];
|
||||
patientNumber = json['PatientNumber'];
|
||||
projectName = json['ProjectName'];
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = <String, dynamic>{};
|
||||
data['SetupId'] = this.setupId;
|
||||
data['ProjectID'] = this.projectID;
|
||||
data['PatientID'] = this.patientID;
|
||||
data['AppointmentNo'] = this.appointmentNo;
|
||||
data['AppointmentDate'] = this.appointmentDate;
|
||||
data['AppointmentDateN'] = this.appointmentDateN;
|
||||
data['ClinicID'] = this.clinicID;
|
||||
data['DoctorID'] = this.doctorID;
|
||||
data['InvoiceNo'] = this.invoiceNo;
|
||||
data['Status'] = this.status;
|
||||
data['ArrivedOn'] = this.arrivedOn;
|
||||
data['DoctorName'] = this.doctorName;
|
||||
data['DoctorNameN'] = this.doctorNameN;
|
||||
data['ClinicName'] = this.clinicName;
|
||||
data['DecimalDoctorRate'] = this.decimalDoctorRate;
|
||||
data['DoctorImageURL'] = this.doctorImageURL;
|
||||
data['DoctorRate'] = this.doctorRate;
|
||||
data['PatientNumber'] = this.patientNumber;
|
||||
data['ProjectName'] = this.projectName;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,141 @@
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:hmg_patient_app_new/core/api/api_client.dart';
|
||||
import 'package:hmg_patient_app_new/core/api_consts.dart';
|
||||
import 'package:hmg_patient_app_new/core/common_models/generic_api_model.dart';
|
||||
import 'package:hmg_patient_app_new/core/exceptions/api_failure.dart';
|
||||
import 'package:hmg_patient_app_new/features/my_invoices/models/get_invoice_details_response_model.dart';
|
||||
import 'package:hmg_patient_app_new/features/my_invoices/models/get_invoices_list_response_model.dart';
|
||||
import 'package:hmg_patient_app_new/services/logger_service.dart';
|
||||
|
||||
abstract class MyInvoicesRepo {
|
||||
Future<Either<Failure, GenericApiModel<List<GetInvoicesListResponseModel>>>> getAllInvoicesList();
|
||||
|
||||
Future<Either<Failure, GenericApiModel<GetInvoiceDetailsResponseModel>>> getInvoiceDetails({required num appointmentNo, required num invoiceNo, required int projectID});
|
||||
|
||||
Future<Either<Failure, GenericApiModel<dynamic>>> sendInvoiceEmail({required num appointmentNo, required int projectID});
|
||||
}
|
||||
|
||||
class MyInvoicesRepoImp implements MyInvoicesRepo {
|
||||
final ApiClient apiClient;
|
||||
final LoggerService loggerService;
|
||||
|
||||
MyInvoicesRepoImp({required this.loggerService, required this.apiClient});
|
||||
|
||||
@override
|
||||
Future<Either<Failure, GenericApiModel<List<GetInvoicesListResponseModel>>>> getAllInvoicesList() async {
|
||||
Map<String, dynamic> mapDevice = {};
|
||||
|
||||
try {
|
||||
GenericApiModel<List<GetInvoicesListResponseModel>>? apiResponse;
|
||||
Failure? failure;
|
||||
await apiClient.post(
|
||||
GET_ALL_APPOINTMENTS_FOR_DENTAL_CLINIC,
|
||||
body: mapDevice,
|
||||
onFailure: (error, statusCode, {messageStatus, failureType}) {
|
||||
failure = failureType;
|
||||
},
|
||||
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
|
||||
try {
|
||||
final list = response['List_DentalAppointments'];
|
||||
|
||||
final invoicesList = list.map((item) => GetInvoicesListResponseModel.fromJson(item as Map<String, dynamic>)).toList().cast<GetInvoicesListResponseModel>();
|
||||
|
||||
apiResponse = GenericApiModel<List<GetInvoicesListResponseModel>>(
|
||||
messageStatus: messageStatus,
|
||||
statusCode: statusCode,
|
||||
errorMessage: null,
|
||||
data: invoicesList,
|
||||
);
|
||||
} 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<GetInvoiceDetailsResponseModel>>> getInvoiceDetails({required num appointmentNo, required num invoiceNo, required int projectID}) async {
|
||||
Map<String, dynamic> mapDevice = {
|
||||
"AppointmentNo": appointmentNo,
|
||||
"InvoiceNo": invoiceNo,
|
||||
"IsRegistered": true,
|
||||
"ProjectID": projectID,
|
||||
};
|
||||
|
||||
try {
|
||||
GenericApiModel<GetInvoiceDetailsResponseModel>? apiResponse;
|
||||
Failure? failure;
|
||||
await apiClient.post(
|
||||
GET_DENTAL_APPOINTMENT_INVOICE,
|
||||
body: mapDevice,
|
||||
onFailure: (error, statusCode, {messageStatus, failureType}) {
|
||||
failure = failureType;
|
||||
},
|
||||
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
|
||||
try {
|
||||
final list = response['List_eInvoiceForDental'];
|
||||
final invoicesList = GetInvoiceDetailsResponseModel.fromJson(list[0]);
|
||||
|
||||
apiResponse = GenericApiModel<GetInvoiceDetailsResponseModel>(
|
||||
messageStatus: messageStatus,
|
||||
statusCode: statusCode,
|
||||
errorMessage: null,
|
||||
data: invoicesList,
|
||||
);
|
||||
} 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>> sendInvoiceEmail({required num appointmentNo, required int projectID}) async {
|
||||
Map<String, dynamic> mapDevice = {
|
||||
"AppointmentNo": appointmentNo,
|
||||
"IsRegistered": true,
|
||||
"ProjectID": projectID,
|
||||
};
|
||||
|
||||
try {
|
||||
GenericApiModel<GetInvoiceDetailsResponseModel>? apiResponse;
|
||||
Failure? failure;
|
||||
await apiClient.post(
|
||||
SEND_DENTAL_APPOINTMENT_INVOICE_EMAIL,
|
||||
body: mapDevice,
|
||||
onFailure: (error, statusCode, {messageStatus, failureType}) {
|
||||
failure = failureType;
|
||||
},
|
||||
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
|
||||
try {
|
||||
apiResponse = GenericApiModel(
|
||||
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()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,99 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hmg_patient_app_new/features/my_invoices/models/get_invoice_details_response_model.dart';
|
||||
import 'package:hmg_patient_app_new/features/my_invoices/models/get_invoices_list_response_model.dart';
|
||||
import 'package:hmg_patient_app_new/features/my_invoices/my_invoices_repo.dart';
|
||||
import 'package:hmg_patient_app_new/services/error_handler_service.dart';
|
||||
import 'package:hmg_patient_app_new/services/navigation_service.dart';
|
||||
|
||||
class MyInvoicesViewModel extends ChangeNotifier {
|
||||
bool isInvoicesListLoading = false;
|
||||
bool isInvoiceDetailsLoading = false;
|
||||
|
||||
MyInvoicesRepo myInvoicesRepo;
|
||||
ErrorHandlerService errorHandlerService;
|
||||
NavigationService navServices;
|
||||
|
||||
List<GetInvoicesListResponseModel> allInvoicesList = [];
|
||||
late GetInvoiceDetailsResponseModel invoiceDetailsResponseModel;
|
||||
|
||||
MyInvoicesViewModel({required this.myInvoicesRepo, required this.errorHandlerService, required this.navServices});
|
||||
|
||||
setInvoicesListLoading() {
|
||||
isInvoicesListLoading = true;
|
||||
allInvoicesList.clear();
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
setInvoiceDetailLoading() {
|
||||
isInvoiceDetailsLoading = true;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<void> getAllInvoicesList({Function(dynamic)? onSuccess, Function(String)? onError}) async {
|
||||
final result = await myInvoicesRepo.getAllInvoicesList();
|
||||
|
||||
result.fold(
|
||||
(failure) async {
|
||||
isInvoicesListLoading = false;
|
||||
notifyListeners();
|
||||
},
|
||||
(apiResponse) {
|
||||
if (apiResponse.messageStatus == 2) {
|
||||
// dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
|
||||
} else if (apiResponse.messageStatus == 1) {
|
||||
allInvoicesList = apiResponse.data!;
|
||||
isInvoicesListLoading = false;
|
||||
|
||||
notifyListeners();
|
||||
if (onSuccess != null) {
|
||||
onSuccess(apiResponse);
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> getInvoiceDetails({required num appointmentNo, required num invoiceNo, required int projectID, Function(dynamic)? onSuccess, Function(String)? onError}) async {
|
||||
final result = await myInvoicesRepo.getInvoiceDetails(appointmentNo: appointmentNo, invoiceNo: invoiceNo, projectID: projectID);
|
||||
|
||||
result.fold(
|
||||
(failure) async {
|
||||
isInvoiceDetailsLoading = false;
|
||||
notifyListeners();
|
||||
},
|
||||
(apiResponse) {
|
||||
if (apiResponse.messageStatus == 2) {
|
||||
// dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
|
||||
} else if (apiResponse.messageStatus == 1) {
|
||||
invoiceDetailsResponseModel = apiResponse.data!;
|
||||
isInvoiceDetailsLoading = false;
|
||||
|
||||
notifyListeners();
|
||||
if (onSuccess != null) {
|
||||
onSuccess(apiResponse);
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> sendInvoiceEmail({required num appointmentNo, required int projectID, Function(dynamic)? onSuccess, Function(String)? onError}) async {
|
||||
final result = await myInvoicesRepo.sendInvoiceEmail(appointmentNo: appointmentNo, projectID: projectID);
|
||||
|
||||
result.fold(
|
||||
(failure) async {
|
||||
notifyListeners();
|
||||
},
|
||||
(apiResponse) {
|
||||
if (apiResponse.messageStatus == 2) {
|
||||
// dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
|
||||
} else if (apiResponse.messageStatus == 1) {
|
||||
notifyListeners();
|
||||
if (onSuccess != null) {
|
||||
onSuccess(apiResponse);
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,567 @@
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hmg_patient_app_new/core/app_assets.dart';
|
||||
import 'package:hmg_patient_app_new/core/app_export.dart';
|
||||
import 'package:hmg_patient_app_new/core/dependencies.dart';
|
||||
import 'package:hmg_patient_app_new/core/enums.dart';
|
||||
import 'package:hmg_patient_app_new/core/utils/utils.dart';
|
||||
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
|
||||
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
|
||||
import 'package:hmg_patient_app_new/presentation/health_trackers/health_trackers_view_model.dart';
|
||||
import 'package:hmg_patient_app_new/services/dialog_service.dart';
|
||||
import 'package:hmg_patient_app_new/theme/colors.dart';
|
||||
import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
|
||||
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
|
||||
import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart';
|
||||
import 'package:hmg_patient_app_new/widgets/input_widget.dart';
|
||||
import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class AddHealthTrackerEntryPage extends StatefulWidget {
|
||||
final HealthTrackerTypeEnum trackerType;
|
||||
|
||||
const AddHealthTrackerEntryPage({
|
||||
super.key,
|
||||
required this.trackerType,
|
||||
});
|
||||
|
||||
@override
|
||||
State<AddHealthTrackerEntryPage> createState() => _AddHealthTrackerEntryPageState();
|
||||
}
|
||||
|
||||
class _AddHealthTrackerEntryPageState extends State<AddHealthTrackerEntryPage> {
|
||||
late DialogService dialogService;
|
||||
|
||||
// Controllers for date and time
|
||||
final TextEditingController dateController = TextEditingController();
|
||||
final TextEditingController timeController = TextEditingController();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
dialogService = getIt.get<DialogService>();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
dateController.dispose();
|
||||
timeController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
/// Get page title based on tracker type
|
||||
String _getPageTitle() {
|
||||
switch (widget.trackerType) {
|
||||
case HealthTrackerTypeEnum.bloodSugar:
|
||||
return "Add Blood Sugar".needTranslation;
|
||||
case HealthTrackerTypeEnum.bloodPressure:
|
||||
return "Add Blood Pressure".needTranslation;
|
||||
case HealthTrackerTypeEnum.weightTracker:
|
||||
return "Add Weight".needTranslation;
|
||||
}
|
||||
}
|
||||
|
||||
/// Get success message based on tracker type
|
||||
String _getSuccessMessage() {
|
||||
switch (widget.trackerType) {
|
||||
case HealthTrackerTypeEnum.bloodSugar:
|
||||
return "Blood Sugar Data saved successfully".needTranslation;
|
||||
case HealthTrackerTypeEnum.bloodPressure:
|
||||
return "Blood Pressure Data saved successfully".needTranslation;
|
||||
case HealthTrackerTypeEnum.weightTracker:
|
||||
return "Weight Data saved successfully".needTranslation;
|
||||
}
|
||||
}
|
||||
|
||||
/// Save entry based on tracker type
|
||||
Future<void> _saveEntry(HealthTrackersViewModel viewModel) async {
|
||||
switch (widget.trackerType) {
|
||||
case HealthTrackerTypeEnum.bloodSugar:
|
||||
await _saveBloodSugarEntry(viewModel);
|
||||
break;
|
||||
case HealthTrackerTypeEnum.bloodPressure:
|
||||
await _saveBloodPressureEntry(viewModel);
|
||||
break;
|
||||
case HealthTrackerTypeEnum.weightTracker:
|
||||
await _saveWeightEntry(viewModel);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Save Blood Sugar entry
|
||||
Future<void> _saveBloodSugarEntry(HealthTrackersViewModel viewModel) async {
|
||||
LoaderBottomSheet.showLoader(loadingText: "Please wait".needTranslation);
|
||||
// Combine date and time
|
||||
final dateTime = "${dateController.text} ${timeController.text}";
|
||||
|
||||
// Call ViewModel method with callbacks
|
||||
await viewModel.saveBloodSugarEntry(
|
||||
dateTime: dateTime,
|
||||
measureTime: viewModel.selectedBloodSugarMeasureTime,
|
||||
onSuccess: () {
|
||||
LoaderBottomSheet.hideLoader();
|
||||
_showSuccessAndPop();
|
||||
},
|
||||
onFailure: (error) {
|
||||
LoaderBottomSheet.hideLoader();
|
||||
dialogService.showErrorBottomSheet(message: error);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// Save Weight entry
|
||||
Future<void> _saveWeightEntry(HealthTrackersViewModel viewModel) async {
|
||||
LoaderBottomSheet.showLoader(loadingText: "Please wait".needTranslation);
|
||||
// Combine date and time
|
||||
final dateTime = "${dateController.text} ${timeController.text}";
|
||||
|
||||
// Call ViewModel method with callbacks
|
||||
await viewModel.saveWeightEntry(
|
||||
dateTime: dateTime,
|
||||
onSuccess: () {
|
||||
LoaderBottomSheet.hideLoader();
|
||||
_showSuccessAndPop();
|
||||
},
|
||||
onFailure: (error) {
|
||||
LoaderBottomSheet.hideLoader();
|
||||
dialogService.showErrorBottomSheet(message: error);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// Save Blood Pressure entry
|
||||
Future<void> _saveBloodPressureEntry(HealthTrackersViewModel viewModel) async {
|
||||
LoaderBottomSheet.showLoader(loadingText: "Please wait".needTranslation);
|
||||
// Combine date and time
|
||||
final dateTime = "${dateController.text} ${timeController.text}";
|
||||
|
||||
// Call ViewModel method with callbacks
|
||||
await viewModel.saveBloodPressureEntry(
|
||||
dateTime: dateTime,
|
||||
onSuccess: () {
|
||||
LoaderBottomSheet.hideLoader();
|
||||
_showSuccessAndPop();
|
||||
},
|
||||
onFailure: (error) {
|
||||
LoaderBottomSheet.hideLoader();
|
||||
dialogService.showErrorBottomSheet(message: error);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// Show success message and pop back
|
||||
void _showSuccessAndPop() {
|
||||
showCommonBottomSheetWithoutHeight(
|
||||
context,
|
||||
child: Utils.getSuccessWidget(
|
||||
loadingText: _getSuccessMessage(),
|
||||
),
|
||||
callBackFunc: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
isCloseButtonVisible: false,
|
||||
isDismissible: true,
|
||||
isFullScreen: false,
|
||||
);
|
||||
}
|
||||
|
||||
// Reusable method to build selection row widget
|
||||
Widget _buildSelectionRow({
|
||||
required String value,
|
||||
required String groupValue,
|
||||
required VoidCallback onTap,
|
||||
bool useUpperCase = false,
|
||||
}) {
|
||||
return SizedBox(
|
||||
height: 70.h,
|
||||
child: Row(
|
||||
spacing: 8.h,
|
||||
children: [
|
||||
Radio<String>(
|
||||
value: value,
|
||||
groupValue: groupValue,
|
||||
activeColor: AppColors.errorColor,
|
||||
onChanged: (_) => onTap(),
|
||||
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||
),
|
||||
(useUpperCase ? value.toUpperCase() : value.toCamelCase)
|
||||
.toText16(weight: FontWeight.w500, textOverflow: TextOverflow.ellipsis, maxlines: 1)
|
||||
.expanded,
|
||||
],
|
||||
).onPress(onTap),
|
||||
);
|
||||
}
|
||||
|
||||
// Reusable method to show selection bottom sheet
|
||||
void _showSelectionBottomSheet({
|
||||
required BuildContext context,
|
||||
required String title,
|
||||
required List<String> items,
|
||||
required String selectedValue,
|
||||
required Function(String) onSelected,
|
||||
bool useUpperCase = false,
|
||||
}) {
|
||||
dialogService.showFamilyBottomSheetWithoutHWithChild(
|
||||
label: title.needTranslation,
|
||||
message: "",
|
||||
child: Container(
|
||||
constraints: BoxConstraints(maxHeight: MediaQuery.of(context).size.height * 0.7),
|
||||
padding: EdgeInsets.only(left: 16.w, right: 16.w, top: 4.h, bottom: 4.h),
|
||||
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(20.r)),
|
||||
child: ListView.separated(
|
||||
shrinkWrap: true,
|
||||
itemCount: items.length,
|
||||
itemBuilder: (context, index) {
|
||||
final item = items[index];
|
||||
return _buildSelectionRow(
|
||||
value: item,
|
||||
groupValue: selectedValue,
|
||||
useUpperCase: useUpperCase,
|
||||
onTap: () {
|
||||
onSelected(item);
|
||||
Navigator.pop(context);
|
||||
},
|
||||
);
|
||||
},
|
||||
separatorBuilder: (_, __) => Divider(height: 1, color: AppColors.dividerColor),
|
||||
),
|
||||
),
|
||||
onOkPressed: () {},
|
||||
);
|
||||
}
|
||||
|
||||
// Blood Sugar unit selection
|
||||
void _showBloodSugarUnitSelectionBottomSheet(BuildContext context, HealthTrackersViewModel viewModel) {
|
||||
FocusScope.of(context).unfocus();
|
||||
_showSelectionBottomSheet(
|
||||
context: context,
|
||||
title: "Select Unit".needTranslation,
|
||||
items: viewModel.bloodSugarUnit,
|
||||
selectedValue: viewModel.selectedBloodSugarUnit,
|
||||
onSelected: viewModel.setBloodSugarUnit,
|
||||
useUpperCase: false,
|
||||
);
|
||||
}
|
||||
|
||||
// Blood Sugar measure time selection
|
||||
void _showBloodSugarEntryTimeBottomSheet(BuildContext context, HealthTrackersViewModel viewModel) {
|
||||
FocusScope.of(context).unfocus();
|
||||
_showSelectionBottomSheet(
|
||||
context: context,
|
||||
title: "Select Measure Time".needTranslation,
|
||||
items: viewModel.bloodSugarMeasureTimeEnList,
|
||||
selectedValue: viewModel.selectedBloodSugarMeasureTime,
|
||||
onSelected: viewModel.setBloodSugarMeasureTime,
|
||||
useUpperCase: false,
|
||||
);
|
||||
}
|
||||
|
||||
// Weight unit selection
|
||||
void _showWeightUnitSelectionBottomSheet(BuildContext context, HealthTrackersViewModel viewModel) {
|
||||
FocusScope.of(context).unfocus();
|
||||
_showSelectionBottomSheet(
|
||||
context: context,
|
||||
title: "Select Unit".needTranslation,
|
||||
items: viewModel.weightUnits,
|
||||
selectedValue: viewModel.selectedWeightUnit,
|
||||
onSelected: viewModel.setWeightUnit,
|
||||
useUpperCase: false,
|
||||
);
|
||||
}
|
||||
|
||||
// Blood Pressure measured arm selection
|
||||
void _showMeasuredArmSelectionBottomSheet(BuildContext context, HealthTrackersViewModel viewModel) {
|
||||
FocusScope.of(context).unfocus();
|
||||
_showSelectionBottomSheet(
|
||||
context: context,
|
||||
title: "Select Arm".needTranslation,
|
||||
items: viewModel.measuredArmList,
|
||||
selectedValue: viewModel.selectedMeasuredArm,
|
||||
onSelected: viewModel.setMeasuredArm,
|
||||
useUpperCase: false,
|
||||
);
|
||||
}
|
||||
|
||||
// Reusable method to build text field
|
||||
Widget _buildTextField(TextEditingController controller, String hintText, {TextInputType keyboardType = TextInputType.name}) {
|
||||
return TextField(
|
||||
controller: controller,
|
||||
keyboardType: keyboardType,
|
||||
maxLines: 1,
|
||||
cursorHeight: 14.h,
|
||||
textAlignVertical: TextAlignVertical.center,
|
||||
decoration: InputDecoration(
|
||||
border: InputBorder.none,
|
||||
contentPadding: EdgeInsets.zero,
|
||||
isCollapsed: true,
|
||||
hintText: hintText,
|
||||
hintStyle: const TextStyle(color: Colors.grey),
|
||||
),
|
||||
style: TextStyle(
|
||||
fontSize: 14.f,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: AppColors.textColor,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Reusable method to build settings row
|
||||
Widget _buildSettingsRow({
|
||||
required String icon,
|
||||
required String label,
|
||||
String? value,
|
||||
Widget? inputField,
|
||||
String? unit,
|
||||
VoidCallback? onUnitTap,
|
||||
VoidCallback? onRowTap,
|
||||
Color? iconColor,
|
||||
bool showDivider = true,
|
||||
}) {
|
||||
return Column(
|
||||
children: [
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Container(
|
||||
height: 40.w,
|
||||
width: 40.w,
|
||||
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
|
||||
color: AppColors.greyColor,
|
||||
borderRadius: 10.r,
|
||||
hasShadow: false,
|
||||
),
|
||||
child: Center(child: Utils.buildSvgWithAssets(icon: icon, height: 22.w, width: 22.w, iconColor: iconColor)),
|
||||
),
|
||||
SizedBox(width: 12.w),
|
||||
Expanded(
|
||||
flex: unit != null ? 2 : 1,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
label.toText12(fontWeight: FontWeight.w500, color: AppColors.inputLabelTextColor),
|
||||
if (inputField != null)
|
||||
Container(
|
||||
height: 20.w,
|
||||
alignment: Alignment.centerLeft,
|
||||
child: inputField,
|
||||
)
|
||||
else if (value != null && value.isNotEmpty)
|
||||
value.toCamelCase.toText12(fontWeight: FontWeight.w500, color: AppColors.textColor),
|
||||
],
|
||||
),
|
||||
),
|
||||
if (unit != null) ...[
|
||||
Container(
|
||||
width: 1.w,
|
||||
height: 30.w,
|
||||
color: AppColors.dividerColor,
|
||||
).paddingOnly(right: 10.w),
|
||||
Expanded(
|
||||
child: Row(
|
||||
children: [
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
"Unit".toText12(fontWeight: FontWeight.w500, color: AppColors.inputLabelTextColor),
|
||||
unit.toText12(fontWeight: FontWeight.w500, color: AppColors.textColor),
|
||||
],
|
||||
),
|
||||
SizedBox(width: 12.w),
|
||||
Utils.buildSvgWithAssets(icon: AppAssets.arrow_down)
|
||||
],
|
||||
).onPress(onUnitTap ?? () {}),
|
||||
),
|
||||
] else if (onRowTap != null) ...[
|
||||
Utils.buildSvgWithAssets(icon: AppAssets.arrow_down),
|
||||
SizedBox(width: 8.w),
|
||||
],
|
||||
],
|
||||
).paddingSymmetrical(0.w, 16.w).onPress(onRowTap ?? () {}),
|
||||
if (showDivider) Divider(height: 1, color: AppColors.dividerColor),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
/// Build form fields based on tracker type
|
||||
Widget _buildFormFields(HealthTrackersViewModel viewModel) {
|
||||
switch (widget.trackerType) {
|
||||
case HealthTrackerTypeEnum.bloodSugar:
|
||||
return _buildBloodSugarForm(viewModel);
|
||||
case HealthTrackerTypeEnum.bloodPressure:
|
||||
return _buildBloodPressureForm(viewModel);
|
||||
case HealthTrackerTypeEnum.weightTracker:
|
||||
return _buildWeightForm(viewModel);
|
||||
}
|
||||
}
|
||||
|
||||
/// Blood Sugar form fields
|
||||
Widget _buildBloodSugarForm(HealthTrackersViewModel viewModel) {
|
||||
return Column(
|
||||
children: [
|
||||
_buildSettingsRow(
|
||||
icon: AppAssets.heightIcon,
|
||||
label: "Enter Blood Sugar".needTranslation,
|
||||
inputField: _buildTextField(viewModel.bloodSugarController, '', keyboardType: TextInputType.number),
|
||||
unit: viewModel.selectedBloodSugarUnit,
|
||||
onUnitTap: () => _showBloodSugarUnitSelectionBottomSheet(context, viewModel),
|
||||
),
|
||||
_buildDateTimeFields(),
|
||||
Divider(height: 1, color: AppColors.dividerColor),
|
||||
_buildSettingsRow(
|
||||
icon: AppAssets.weight_tracker_icon,
|
||||
label: "Select Measure Time".needTranslation,
|
||||
value: viewModel.selectedBloodSugarMeasureTime,
|
||||
onRowTap: () => _showBloodSugarEntryTimeBottomSheet(context, viewModel),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
/// Blood Pressure form fields
|
||||
Widget _buildBloodPressureForm(HealthTrackersViewModel viewModel) {
|
||||
return Column(
|
||||
children: [
|
||||
_buildSettingsRow(
|
||||
icon: AppAssets.bloodPressureIcon,
|
||||
iconColor: AppColors.greyTextColor,
|
||||
label: "Enter Systolic Value".needTranslation,
|
||||
inputField: _buildTextField(viewModel.systolicController, '', keyboardType: TextInputType.number),
|
||||
),
|
||||
_buildSettingsRow(
|
||||
icon: AppAssets.bloodPressureIcon,
|
||||
iconColor: AppColors.greyTextColor,
|
||||
label: "Enter Diastolic Value".needTranslation,
|
||||
inputField: _buildTextField(viewModel.diastolicController, '', keyboardType: TextInputType.number),
|
||||
),
|
||||
_buildSettingsRow(
|
||||
icon: AppAssets.bodyIcon,
|
||||
iconColor: AppColors.greyTextColor,
|
||||
label: "Select Arm".needTranslation,
|
||||
value: viewModel.selectedMeasuredArm,
|
||||
onRowTap: () => _showMeasuredArmSelectionBottomSheet(context, viewModel),
|
||||
),
|
||||
_buildDateTimeFields(),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
/// Weight form fields
|
||||
Widget _buildWeightForm(HealthTrackersViewModel viewModel) {
|
||||
return Column(
|
||||
children: [
|
||||
_buildSettingsRow(
|
||||
icon: AppAssets.weightScale,
|
||||
label: "Enter Weight".needTranslation,
|
||||
inputField: _buildTextField(viewModel.weightController, '', keyboardType: TextInputType.number),
|
||||
unit: viewModel.selectedWeightUnit,
|
||||
onUnitTap: () => _showWeightUnitSelectionBottomSheet(context, viewModel),
|
||||
),
|
||||
_buildDateTimeFields(),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
/// Common date and time fields
|
||||
Widget _buildDateTimeFields() {
|
||||
return Column(
|
||||
children: [
|
||||
TextInputWidget(
|
||||
controller: dateController,
|
||||
isReadOnly: true,
|
||||
isArrowTrailing: true,
|
||||
labelText: "Date",
|
||||
hintText: "Select date".needTranslation,
|
||||
focusNode: FocusNode(),
|
||||
isEnable: true,
|
||||
prefix: null,
|
||||
isAllowRadius: true,
|
||||
isBorderAllowed: false,
|
||||
isAllowLeadingIcon: true,
|
||||
padding: EdgeInsets.symmetric(vertical: 8.h),
|
||||
leadingIcon: AppAssets.calendarGrey,
|
||||
selectionType: SelectionTypeEnum.calendar,
|
||||
isHideSwitcher: true,
|
||||
onCalendarTypeChanged: (val) {},
|
||||
onChange: (val) {
|
||||
if (val == null) return;
|
||||
try {
|
||||
final parsedDate = DateTime.parse(val);
|
||||
final formattedDate = DateFormat('dd MMM yyyy').format(parsedDate);
|
||||
dateController.text = formattedDate;
|
||||
log("date: $formattedDate");
|
||||
} catch (e) {
|
||||
dateController.text = val;
|
||||
log("date: $val");
|
||||
}
|
||||
},
|
||||
),
|
||||
TextInputWidget(
|
||||
controller: timeController,
|
||||
isReadOnly: true,
|
||||
isArrowTrailing: true,
|
||||
labelText: "Time",
|
||||
hintText: "Select time".needTranslation,
|
||||
focusNode: FocusNode(),
|
||||
isEnable: true,
|
||||
prefix: null,
|
||||
isAllowRadius: true,
|
||||
isBorderAllowed: false,
|
||||
isAllowLeadingIcon: true,
|
||||
padding: EdgeInsets.symmetric(vertical: 8.h),
|
||||
leadingIcon: AppAssets.calendarGrey,
|
||||
selectionType: SelectionTypeEnum.time,
|
||||
isHideSwitcher: true,
|
||||
onCalendarTypeChanged: (val) {},
|
||||
onChange: (val) {
|
||||
if (val == null) return;
|
||||
timeController.text = val;
|
||||
log("time: $val");
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final viewModel = context.watch<HealthTrackersViewModel>();
|
||||
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
FocusScope.of(context).unfocus();
|
||||
},
|
||||
child: Scaffold(
|
||||
backgroundColor: AppColors.bgScaffoldColor,
|
||||
body: CollapsingListView(
|
||||
title: _getPageTitle(),
|
||||
bottomChild: Container(
|
||||
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
|
||||
color: AppColors.whiteColor,
|
||||
borderRadius: 24.r,
|
||||
hasShadow: true,
|
||||
),
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(24.w),
|
||||
child: CustomButton(
|
||||
text: "Save".needTranslation,
|
||||
onPressed: () async => await _saveEntry(viewModel),
|
||||
borderRadius: 12.r,
|
||||
padding: EdgeInsets.symmetric(vertical: 14.h),
|
||||
),
|
||||
),
|
||||
),
|
||||
child: Container(
|
||||
margin: EdgeInsets.symmetric(horizontal: 24.w, vertical: 24.h),
|
||||
padding: EdgeInsets.only(left: 16.w, right: 16.w, top: 4.h, bottom: 4.h),
|
||||
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.r, hasShadow: true),
|
||||
child: _buildFormFields(viewModel),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,118 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hmg_patient_app_new/core/app_assets.dart';
|
||||
import 'package:hmg_patient_app_new/core/app_export.dart';
|
||||
import 'package:hmg_patient_app_new/core/enums.dart';
|
||||
import 'package:hmg_patient_app_new/core/utils/utils.dart';
|
||||
import 'package:hmg_patient_app_new/extensions/route_extensions.dart';
|
||||
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
|
||||
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
|
||||
import 'package:hmg_patient_app_new/theme/colors.dart';
|
||||
import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
|
||||
|
||||
class HealthTrackersPage extends StatefulWidget {
|
||||
const HealthTrackersPage({super.key});
|
||||
|
||||
@override
|
||||
State<HealthTrackersPage> createState() => _HealthTrackersPageState();
|
||||
}
|
||||
|
||||
Widget buildHealthTrackerCard({
|
||||
required String icon,
|
||||
required String title,
|
||||
required String description,
|
||||
required Color iconBgColor,
|
||||
required VoidCallback onTap,
|
||||
}) {
|
||||
return Container(
|
||||
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: Colors.white, borderRadius: 20.r),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: iconBgColor, borderRadius: 10.r),
|
||||
height: 40.w,
|
||||
width: 40.w,
|
||||
child: Utils.buildSvgWithAssets(
|
||||
icon: icon,
|
||||
fit: BoxFit.none,
|
||||
height: 22.w,
|
||||
width: 22.w,
|
||||
),
|
||||
),
|
||||
SizedBox(width: 12.w),
|
||||
Flexible(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
title.toText16(weight: FontWeight.w600),
|
||||
description.toText12(
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Color(0xFF8F9AA3),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox(width: 12.w),
|
||||
Utils.buildSvgWithAssets(
|
||||
icon: AppAssets.arrowRight,
|
||||
width: 24.w,
|
||||
height: 24.h,
|
||||
fit: BoxFit.contain,
|
||||
iconColor: AppColors.textColor,
|
||||
),
|
||||
],
|
||||
).paddingAll(16.w),
|
||||
).onPress(onTap);
|
||||
}
|
||||
|
||||
class _HealthTrackersPageState extends State<HealthTrackersPage> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return CollapsingListView(
|
||||
title: "Health Trackers".needTranslation,
|
||||
child: Column(
|
||||
children: [
|
||||
buildHealthTrackerCard(
|
||||
iconBgColor: AppColors.primaryRedColor,
|
||||
icon: AppAssets.bloodSugarOnlyIcon,
|
||||
title: "Blood Sugar".needTranslation,
|
||||
description: "Track your glucose levels, understand trends, and get personalized insights for better health.".needTranslation,
|
||||
onTap: () {
|
||||
context.navigateWithName(
|
||||
AppRoutes.healthTrackerDetailPage,
|
||||
arguments: HealthTrackerTypeEnum.bloodSugar,
|
||||
);
|
||||
},
|
||||
),
|
||||
SizedBox(height: 16.h),
|
||||
buildHealthTrackerCard(
|
||||
iconBgColor: AppColors.infoColor,
|
||||
icon: AppAssets.bloodPressureIcon,
|
||||
title: "Blood Pressure".needTranslation,
|
||||
description: "Monitor your blood pressure levels, track systolic and diastolic readings, and maintain a healthy heart.".needTranslation,
|
||||
onTap: () {
|
||||
context.navigateWithName(
|
||||
AppRoutes.healthTrackerDetailPage,
|
||||
arguments: HealthTrackerTypeEnum.bloodPressure,
|
||||
);
|
||||
},
|
||||
),
|
||||
SizedBox(height: 16.h),
|
||||
buildHealthTrackerCard(
|
||||
iconBgColor: AppColors.successColor,
|
||||
icon: AppAssets.weightIcon,
|
||||
title: "Weight".needTranslation,
|
||||
description: "Track your weight progress, set goals, and maintain a healthy body mass for overall wellness.".needTranslation,
|
||||
onTap: () {
|
||||
context.navigateWithName(
|
||||
AppRoutes.healthTrackerDetailPage,
|
||||
arguments: HealthTrackerTypeEnum.weightTracker,
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
).paddingSymmetrical(20.w, 24.h),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,271 @@
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hmg_patient_app_new/core/app_assets.dart';
|
||||
import 'package:hmg_patient_app_new/core/app_export.dart';
|
||||
import 'package:hmg_patient_app_new/core/enums.dart';
|
||||
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
|
||||
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
|
||||
import 'package:hmg_patient_app_new/presentation/health_trackers/health_trackers_view_model.dart';
|
||||
import 'package:hmg_patient_app_new/theme/colors.dart';
|
||||
import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:shimmer/shimmer.dart';
|
||||
|
||||
class TrackerLastValueCard extends StatelessWidget {
|
||||
final HealthTrackerTypeEnum trackerType;
|
||||
|
||||
const TrackerLastValueCard({super.key, required this.trackerType});
|
||||
|
||||
/// Get status text and color based on blood sugar value
|
||||
(String status, Color color, Color bgColor) _getBloodSugarStatus(double value) {
|
||||
if (value < 70) {
|
||||
return ('Low'.needTranslation, AppColors.errorColor, AppColors.errorColor.withValues(alpha: 0.5));
|
||||
} else if (value <= 100) {
|
||||
return ('Normal'.needTranslation, AppColors.successColor, AppColors.successLightBgColor);
|
||||
} else if (value <= 125) {
|
||||
return ('Pre-diabetic'.needTranslation, AppColors.ratingColorYellow, AppColors.errorColor.withValues(alpha: 0.4));
|
||||
} else {
|
||||
return ('High'.needTranslation, AppColors.errorColor, AppColors.errorColor.withValues(alpha: 0.4));
|
||||
}
|
||||
}
|
||||
|
||||
/// Get status text and color based on blood pressure value (systolic)
|
||||
(String status, Color color, Color bgColor) _getBloodPressureStatus(int systolic) {
|
||||
if (systolic < 90) {
|
||||
return ('Low'.needTranslation, AppColors.errorColor, AppColors.errorColor.withValues(alpha: 0.5));
|
||||
} else if (systolic <= 120) {
|
||||
return ('Normal'.needTranslation, AppColors.successColor, AppColors.successLightBgColor);
|
||||
} else if (systolic <= 140) {
|
||||
return ('Elevated'.needTranslation, AppColors.ratingColorYellow, AppColors.errorColor.withValues(alpha: 0.4));
|
||||
} else {
|
||||
return ('High'.needTranslation, AppColors.errorColor, AppColors.errorColor.withValues(alpha: 0.4));
|
||||
}
|
||||
}
|
||||
|
||||
/// Get status for weight (neutral - no good/bad status)
|
||||
(String status, Color color, Color bgColor) _getWeightStatus() {
|
||||
return ('Recorded'.needTranslation, AppColors.successColor, AppColors.successLightBgColor);
|
||||
}
|
||||
|
||||
/// Get default unit based on tracker type
|
||||
String _getDefaultUnit() {
|
||||
switch (trackerType) {
|
||||
case HealthTrackerTypeEnum.bloodSugar:
|
||||
return 'mg/dL';
|
||||
case HealthTrackerTypeEnum.bloodPressure:
|
||||
return 'mmHg';
|
||||
case HealthTrackerTypeEnum.weightTracker:
|
||||
return 'kg';
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Consumer<HealthTrackersViewModel>(
|
||||
builder: (context, viewModel, child) {
|
||||
// Get the last record based on tracker type
|
||||
dynamic lastRecord;
|
||||
String displayValue = '--';
|
||||
String unit = _getDefaultUnit();
|
||||
DateTime? lastDate;
|
||||
String status = '';
|
||||
Color statusColor = AppColors.greyTextColor;
|
||||
|
||||
switch (trackerType) {
|
||||
case HealthTrackerTypeEnum.bloodSugar:
|
||||
final allResults = [
|
||||
...viewModel.weekDiabeticPatientResult,
|
||||
...viewModel.monthDiabeticPatientResult,
|
||||
...viewModel.yearDiabeticPatientResult,
|
||||
];
|
||||
if (allResults.isNotEmpty) {
|
||||
allResults.sort((a, b) {
|
||||
final dateA = a.dateChart ?? DateTime(1900);
|
||||
final dateB = b.dateChart ?? DateTime(1900);
|
||||
return dateB.compareTo(dateA);
|
||||
});
|
||||
lastRecord = allResults.first;
|
||||
final lastValue = lastRecord.resultValue?.toDouble() ?? 0.0;
|
||||
displayValue = lastValue.toStringAsFixed(0);
|
||||
unit = lastRecord.unit ?? 'mg/dL';
|
||||
lastDate = lastRecord.dateChart;
|
||||
final (s, c, _) = _getBloodSugarStatus(lastValue);
|
||||
status = s;
|
||||
statusColor = c;
|
||||
}
|
||||
break;
|
||||
|
||||
case HealthTrackerTypeEnum.bloodPressure:
|
||||
final allResults = [
|
||||
...viewModel.weekBloodPressureResult,
|
||||
...viewModel.monthBloodPressureResult,
|
||||
...viewModel.yearBloodPressureResult,
|
||||
];
|
||||
if (allResults.isNotEmpty) {
|
||||
allResults.sort((a, b) {
|
||||
final dateA = a.bloodPressureDate ?? DateTime(1900);
|
||||
final dateB = b.bloodPressureDate ?? DateTime(1900);
|
||||
return dateB.compareTo(dateA);
|
||||
});
|
||||
lastRecord = allResults.first;
|
||||
final systolic = lastRecord.systolicePressure ?? 0;
|
||||
final diastolic = lastRecord.diastolicPressure ?? 0;
|
||||
displayValue = '$systolic/$diastolic';
|
||||
unit = 'mmHg';
|
||||
lastDate = lastRecord.bloodPressureDate;
|
||||
final (s, c, _) = _getBloodPressureStatus(systolic);
|
||||
status = s;
|
||||
statusColor = c;
|
||||
}
|
||||
break;
|
||||
|
||||
case HealthTrackerTypeEnum.weightTracker:
|
||||
final allResults = [
|
||||
...viewModel.weekWeightMeasurementResult,
|
||||
...viewModel.monthWeightMeasurementResult,
|
||||
...viewModel.yearWeightMeasurementResult,
|
||||
];
|
||||
if (allResults.isNotEmpty) {
|
||||
allResults.sort((a, b) {
|
||||
final dateA = a.weightDate ?? DateTime(1900);
|
||||
final dateB = b.weightDate ?? DateTime(1900);
|
||||
return dateB.compareTo(dateA);
|
||||
});
|
||||
lastRecord = allResults.first;
|
||||
final weightValue = lastRecord.weightMeasured?.toDouble() ?? 0.0;
|
||||
displayValue = weightValue.toStringAsFixed(0);
|
||||
unit = lastRecord.unit ?? 'kg';
|
||||
lastDate = lastRecord.weightDate;
|
||||
final (s, c, _) = _getWeightStatus();
|
||||
status = s;
|
||||
statusColor = c;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
final formattedDate = lastDate != null ? DateFormat('EEE DD MMM, yy').format(lastDate) : DateFormat('EEE DD MMM, yy').format(DateTime.now());
|
||||
|
||||
// Show shimmer while loading
|
||||
if (viewModel.isLoading) {
|
||||
return Container(
|
||||
margin: EdgeInsets.symmetric(horizontal: 24.w),
|
||||
padding: EdgeInsets.all(16.h),
|
||||
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
|
||||
color: AppColors.whiteColor,
|
||||
borderRadius: 24.r,
|
||||
hasShadow: true,
|
||||
),
|
||||
child: Shimmer.fromColors(
|
||||
baseColor: AppColors.shimmerBaseColor,
|
||||
highlightColor: AppColors.shimmerHighlightColor,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
height: 40.h,
|
||||
width: 120.w,
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.whiteColor,
|
||||
borderRadius: BorderRadius.circular(8.r),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 8.h),
|
||||
Row(
|
||||
children: [
|
||||
Container(
|
||||
height: 32.h,
|
||||
width: 150.w,
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.whiteColor,
|
||||
borderRadius: BorderRadius.circular(16.r),
|
||||
),
|
||||
),
|
||||
SizedBox(width: 8.w),
|
||||
Container(
|
||||
height: 32.h,
|
||||
width: 80.w,
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.whiteColor,
|
||||
borderRadius: BorderRadius.circular(16.r),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Show empty state if no records
|
||||
if (lastRecord == null) {
|
||||
return Container(
|
||||
margin: EdgeInsets.symmetric(horizontal: 24.w),
|
||||
padding: EdgeInsets.all(16.h),
|
||||
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
|
||||
color: AppColors.whiteColor,
|
||||
borderRadius: 24.r,
|
||||
hasShadow: true,
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
"--".toText32(isBold: true, color: AppColors.greyTextColor),
|
||||
SizedBox(width: 6.w),
|
||||
unit.toText12(color: AppColors.greyTextColor, fontWeight: FontWeight.w500).paddingOnly(top: 8.h),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 8.h),
|
||||
AppCustomChipWidget(
|
||||
labelText: "No records yet".needTranslation,
|
||||
icon: AppAssets.doctor_calendar_icon,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return Container(
|
||||
margin: EdgeInsets.symmetric(horizontal: 24.w),
|
||||
padding: EdgeInsets.all(16.h),
|
||||
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
|
||||
color: AppColors.whiteColor,
|
||||
borderRadius: 24.r,
|
||||
hasShadow: true,
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
displayValue.toText32(isBold: true, color: statusColor),
|
||||
SizedBox(width: 6.w),
|
||||
unit.toText12(color: AppColors.greyTextColor, fontWeight: FontWeight.w500).paddingOnly(top: 8.h),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 8.h),
|
||||
Row(
|
||||
children: [
|
||||
AppCustomChipWidget(
|
||||
labelText: "${"Last Record".needTranslation}: $formattedDate",
|
||||
icon: AppAssets.doctor_calendar_icon,
|
||||
),
|
||||
SizedBox(width: 8.w),
|
||||
if (trackerType != HealthTrackerTypeEnum.weightTracker) ...[
|
||||
AppCustomChipWidget(
|
||||
labelText: status.needTranslation,
|
||||
icon: AppAssets.normalStatusGreenIcon,
|
||||
iconColor: statusColor,
|
||||
),
|
||||
]
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,272 @@
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hmg_patient_app_new/core/app_assets.dart';
|
||||
import 'package:hmg_patient_app_new/core/app_state.dart';
|
||||
import 'package:hmg_patient_app_new/core/dependencies.dart';
|
||||
import 'package:hmg_patient_app_new/core/utils/date_util.dart';
|
||||
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
|
||||
import 'package:hmg_patient_app_new/core/utils/utils.dart';
|
||||
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
|
||||
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
|
||||
import 'package:hmg_patient_app_new/features/my_invoices/models/get_invoice_details_response_model.dart';
|
||||
import 'package:hmg_patient_app_new/features/my_invoices/my_invoices_view_model.dart';
|
||||
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
|
||||
import 'package:hmg_patient_app_new/theme/colors.dart';
|
||||
import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
|
||||
import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart';
|
||||
import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart';
|
||||
import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class MyInvoicesDetailsPage extends StatefulWidget {
|
||||
GetInvoiceDetailsResponseModel getInvoiceDetailsResponseModel;
|
||||
|
||||
MyInvoicesDetailsPage({super.key, required this.getInvoiceDetailsResponseModel});
|
||||
|
||||
@override
|
||||
State<MyInvoicesDetailsPage> createState() => _MyInvoicesDetailsPageState();
|
||||
}
|
||||
|
||||
class _MyInvoicesDetailsPageState extends State<MyInvoicesDetailsPage> {
|
||||
late MyInvoicesViewModel myInvoicesViewModel;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
myInvoicesViewModel = Provider.of<MyInvoicesViewModel>(context, listen: false);
|
||||
return Scaffold(
|
||||
backgroundColor: AppColors.bgScaffoldColor,
|
||||
body: Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: CollapsingListView(
|
||||
title: "Invoice Details".needTranslation,
|
||||
sendEmail: () async {
|
||||
LoaderBottomSheet.showLoader(loadingText: "Sending email, Please wait...".needTranslation);
|
||||
await myInvoicesViewModel.sendInvoiceEmail(
|
||||
appointmentNo: widget.getInvoiceDetailsResponseModel.appointmentNo!,
|
||||
projectID: widget.getInvoiceDetailsResponseModel.projectID!,
|
||||
onSuccess: (val) {
|
||||
LoaderBottomSheet.hideLoader();
|
||||
showCommonBottomSheetWithoutHeight(
|
||||
context,
|
||||
child: Utils.getSuccessWidget(loadingText: "Email sent successfully.".needTranslation),
|
||||
callBackFunc: () {},
|
||||
isFullScreen: false,
|
||||
isCloseButtonVisible: true,
|
||||
);
|
||||
},
|
||||
onError: (err) {
|
||||
LoaderBottomSheet.hideLoader();
|
||||
showCommonBottomSheetWithoutHeight(
|
||||
context,
|
||||
child: Utils.getErrorWidget(loadingText: err),
|
||||
callBackFunc: () {},
|
||||
isFullScreen: false,
|
||||
isCloseButtonVisible: true,
|
||||
);
|
||||
});
|
||||
},
|
||||
child: SingleChildScrollView(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(24.h),
|
||||
child: Column(
|
||||
children: [
|
||||
Container(
|
||||
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
|
||||
color: AppColors.whiteColor,
|
||||
borderRadius: 20.h,
|
||||
hasShadow: true,
|
||||
),
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(14.h),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Column(
|
||||
children: [
|
||||
Image.network(
|
||||
widget.getInvoiceDetailsResponseModel.doctorImageURL!,
|
||||
width: 63.h,
|
||||
height: 63.h,
|
||||
fit: BoxFit.cover,
|
||||
).circle(100.r),
|
||||
],
|
||||
),
|
||||
SizedBox(width: 16.w),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
(getIt<AppState>().isArabic() ? widget.getInvoiceDetailsResponseModel.doctorNameN! : widget.getInvoiceDetailsResponseModel.doctorName!).toText16(isBold: true),
|
||||
SizedBox(height: 8.h),
|
||||
Wrap(
|
||||
direction: Axis.horizontal,
|
||||
spacing: 6.w,
|
||||
runSpacing: 6.h,
|
||||
children: [
|
||||
AppCustomChipWidget(
|
||||
labelText: "${LocaleKeys.invoiceNo}: ${widget.getInvoiceDetailsResponseModel.invoiceNo!}",
|
||||
labelPadding: EdgeInsetsDirectional.only(start: 6.w, end: 6.w),
|
||||
),
|
||||
AppCustomChipWidget(
|
||||
labelText: (widget.getInvoiceDetailsResponseModel.clinicDescription!.length > 15
|
||||
? '${widget.getInvoiceDetailsResponseModel.clinicDescription!.substring(0, 12)}...'
|
||||
: widget.getInvoiceDetailsResponseModel.clinicDescription!),
|
||||
labelPadding: EdgeInsetsDirectional.only(start: 4.w, end: 4.w),
|
||||
),
|
||||
AppCustomChipWidget(
|
||||
labelText: widget.getInvoiceDetailsResponseModel.projectName!,
|
||||
labelPadding: EdgeInsetsDirectional.only(start: 6.w, end: 6.w),
|
||||
),
|
||||
AppCustomChipWidget(
|
||||
labelPadding: EdgeInsetsDirectional.only(start: -4.w, end: 6.w),
|
||||
icon: AppAssets.doctor_calendar_icon,
|
||||
labelText: DateUtil.formatDateToDate(DateUtil.convertStringToDate(widget.getInvoiceDetailsResponseModel.appointmentDate), false),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 16.h),
|
||||
Container(
|
||||
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
|
||||
color: AppColors.whiteColor,
|
||||
borderRadius: 20.h,
|
||||
hasShadow: true,
|
||||
),
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(14.h),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
widget.getInvoiceDetailsResponseModel.listConsultation!.first.procedureName!.toText16(isBold: true),
|
||||
SizedBox(height: 16.h),
|
||||
Wrap(
|
||||
direction: Axis.horizontal,
|
||||
spacing: 6.w,
|
||||
runSpacing: 6.h,
|
||||
children: [
|
||||
AppCustomChipWidget(
|
||||
labelText: "${LocaleKeys.quantity.tr()}: ${widget.getInvoiceDetailsResponseModel.listConsultation!.first.quantity!}",
|
||||
labelPadding: EdgeInsetsDirectional.only(start: 6.w, end: 6.w),
|
||||
),
|
||||
AppCustomChipWidget(
|
||||
labelText: "${LocaleKeys.price.tr()}: ${widget.getInvoiceDetailsResponseModel.listConsultation!.first.price!} ${LocaleKeys.sar.tr()}",
|
||||
labelPadding: EdgeInsetsDirectional.only(start: 6.w, end: 6.w),
|
||||
),
|
||||
AppCustomChipWidget(
|
||||
labelText: "${LocaleKeys.total.tr()}: ${widget.getInvoiceDetailsResponseModel.listConsultation!.first.total!} ${LocaleKeys.sar.tr()}",
|
||||
labelPadding: EdgeInsetsDirectional.only(start: 6.w, end: 6.w),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 16.h),
|
||||
Container(
|
||||
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
|
||||
color: AppColors.whiteColor,
|
||||
borderRadius: 20.h,
|
||||
hasShadow: true,
|
||||
),
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(14.h),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
"Insurance Details".toText16(isBold: true),
|
||||
SizedBox(height: 16.h),
|
||||
widget.getInvoiceDetailsResponseModel.groupName!.toText14(isBold: true),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(child: widget.getInvoiceDetailsResponseModel.companyName!.toText14(isBold: true)),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 12.h),
|
||||
Row(
|
||||
children: [
|
||||
AppCustomChipWidget(
|
||||
labelText: "Insurance ID: ${widget.getInvoiceDetailsResponseModel.insuranceID ?? "-"}",
|
||||
labelPadding: EdgeInsetsDirectional.only(start: 6.w, end: 6.w),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
|
||||
color: AppColors.whiteColor,
|
||||
borderRadius: 24.h,
|
||||
hasShadow: false,
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SizedBox(height: 24.h),
|
||||
"Total Balance".needTranslation.toText18(isBold: true).paddingSymmetrical(24.h, 0.h),
|
||||
SizedBox(height: 17.h),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
"Amount before tax".needTranslation.toText14(isBold: true),
|
||||
Utils.getPaymentAmountWithSymbol(widget.getInvoiceDetailsResponseModel.listConsultation!.first.totalShare.toString().toText16(isBold: true), AppColors.blackColor, 13,
|
||||
isSaudiCurrency: true),
|
||||
],
|
||||
).paddingSymmetrical(24.h, 0.h),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
"VAT 15%".needTranslation.toText14(isBold: true, color: AppColors.greyTextColor),
|
||||
Utils.getPaymentAmountWithSymbol(
|
||||
widget.getInvoiceDetailsResponseModel.listConsultation!.first.totalVATAmount!.toString().toText14(isBold: true, color: AppColors.greyTextColor), AppColors.greyTextColor, 13,
|
||||
isSaudiCurrency: true),
|
||||
],
|
||||
).paddingSymmetrical(24.h, 0.h),
|
||||
SizedBox(height: 16.h),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
"Discount".needTranslation.toText14(isBold: true),
|
||||
Utils.getPaymentAmountWithSymbol(widget.getInvoiceDetailsResponseModel.listConsultation!.first.discountAmount!.toString().toText14(isBold: true, color: AppColors.primaryRedColor),
|
||||
AppColors.primaryRedColor, 13,
|
||||
isSaudiCurrency: true),
|
||||
],
|
||||
).paddingSymmetrical(24.h, 0.h),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
"Paid".needTranslation.toText14(isBold: true),
|
||||
Utils.getPaymentAmountWithSymbol(
|
||||
widget.getInvoiceDetailsResponseModel.listConsultation!.first.grandTotal!.toString().toText14(isBold: true, color: AppColors.textColor), AppColors.textColor, 13,
|
||||
isSaudiCurrency: true),
|
||||
],
|
||||
).paddingSymmetrical(24.h, 0.h),
|
||||
SizedBox(height: 24.h),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,116 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
|
||||
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
|
||||
import 'package:hmg_patient_app_new/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_invoices/my_invoices_view_model.dart';
|
||||
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
|
||||
import 'package:hmg_patient_app_new/presentation/appointments/widgets/appointment_card.dart';
|
||||
import 'package:hmg_patient_app_new/presentation/lab/lab_result_item_view.dart';
|
||||
import 'package:hmg_patient_app_new/presentation/my_invoices/my_invoices_details_page.dart';
|
||||
import 'package:hmg_patient_app_new/presentation/my_invoices/widgets/invoice_list_card.dart';
|
||||
import 'package:hmg_patient_app_new/theme/colors.dart';
|
||||
import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
|
||||
import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart';
|
||||
import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart';
|
||||
import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class MyInvoicesList extends StatefulWidget {
|
||||
const MyInvoicesList({super.key});
|
||||
|
||||
@override
|
||||
State<MyInvoicesList> createState() => _MyInvoicesListState();
|
||||
}
|
||||
|
||||
class _MyInvoicesListState extends State<MyInvoicesList> {
|
||||
late MyInvoicesViewModel myInvoicesViewModel;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
scheduleMicrotask(() {
|
||||
myInvoicesViewModel.setInvoicesListLoading();
|
||||
myInvoicesViewModel.getAllInvoicesList();
|
||||
});
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
myInvoicesViewModel = Provider.of<MyInvoicesViewModel>(context, listen: false);
|
||||
return CollapsingListView(
|
||||
title: LocaleKeys.invoiceList.tr(context: context),
|
||||
child: SingleChildScrollView(
|
||||
child: Consumer<MyInvoicesViewModel>(builder: (context, myInvoicesVM, child) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SizedBox(height: 24.h),
|
||||
ListView.builder(
|
||||
itemCount: myInvoicesVM.isInvoicesListLoading ? 4 : myInvoicesVM.allInvoicesList.length,
|
||||
physics: NeverScrollableScrollPhysics(),
|
||||
shrinkWrap: true,
|
||||
padding: EdgeInsetsGeometry.zero,
|
||||
itemBuilder: (context, index) {
|
||||
return myInvoicesVM.isInvoicesListLoading
|
||||
? LabResultItemView(
|
||||
onTap: () {},
|
||||
labOrder: null,
|
||||
index: index,
|
||||
isLoading: true,
|
||||
)
|
||||
: 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,
|
||||
child: InvoiceListCard(
|
||||
getInvoicesListResponseModel: myInvoicesVM.allInvoicesList[index],
|
||||
onTap: () async {
|
||||
myInvoicesVM.setInvoiceDetailLoading();
|
||||
LoaderBottomSheet.showLoader(loadingText: "Fetching invoice details, Please wait...".needTranslation);
|
||||
await myInvoicesVM.getInvoiceDetails(
|
||||
appointmentNo: myInvoicesVM.allInvoicesList[index].appointmentNo!,
|
||||
invoiceNo: myInvoicesVM.allInvoicesList[index].invoiceNo!,
|
||||
projectID: myInvoicesVM.allInvoicesList[index].projectID!,
|
||||
onSuccess: (val) {
|
||||
LoaderBottomSheet.hideLoader();
|
||||
Navigator.of(context).push(
|
||||
CustomPageRoute(
|
||||
page: MyInvoicesDetailsPage(getInvoiceDetailsResponseModel: myInvoicesVM.invoiceDetailsResponseModel),
|
||||
),
|
||||
);
|
||||
},
|
||||
onError: (err) {
|
||||
LoaderBottomSheet.hideLoader();
|
||||
showCommonBottomSheetWithoutHeight(
|
||||
context,
|
||||
child: Utils.getErrorWidget(loadingText: err),
|
||||
callBackFunc: () {},
|
||||
isFullScreen: false,
|
||||
isCloseButtonVisible: true,
|
||||
);
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}).paddingSymmetrical(24.w, 0.h),
|
||||
],
|
||||
);
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,151 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hmg_patient_app_new/core/app_assets.dart';
|
||||
import 'package:hmg_patient_app_new/core/app_state.dart';
|
||||
import 'package:hmg_patient_app_new/core/dependencies.dart';
|
||||
import 'package:hmg_patient_app_new/core/utils/date_util.dart';
|
||||
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
|
||||
import 'package:hmg_patient_app_new/core/utils/utils.dart';
|
||||
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
|
||||
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
|
||||
import 'package:hmg_patient_app_new/features/my_invoices/models/get_invoices_list_response_model.dart';
|
||||
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
|
||||
import 'package:hmg_patient_app_new/theme/colors.dart';
|
||||
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
|
||||
import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart';
|
||||
|
||||
class InvoiceListCard extends StatelessWidget {
|
||||
final GetInvoicesListResponseModel getInvoicesListResponseModel;
|
||||
Function? onTap;
|
||||
|
||||
InvoiceListCard({super.key, required this.getInvoicesListResponseModel, required this.onTap});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
|
||||
color: AppColors.whiteColor,
|
||||
borderRadius: 20.h,
|
||||
hasShadow: true,
|
||||
),
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(14.h),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Wrap(
|
||||
alignment: WrapAlignment.start,
|
||||
direction: Axis.horizontal,
|
||||
spacing: 6.w,
|
||||
runSpacing: 6.h,
|
||||
children: [
|
||||
AppCustomChipWidget(
|
||||
icon: AppAssets.walkin_appointment_icon,
|
||||
iconColor: AppColors.textColor,
|
||||
labelText: 'Walk In'.needTranslation,
|
||||
textColor: AppColors.textColor,
|
||||
),
|
||||
AppCustomChipWidget(
|
||||
labelText: 'OutPatient'.needTranslation,
|
||||
backgroundColor: AppColors.primaryRedColor.withValues(alpha: 0.1),
|
||||
textColor: AppColors.primaryRedColor,
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 16.h),
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Column(
|
||||
children: [
|
||||
Image.network(
|
||||
getInvoicesListResponseModel.doctorImageURL!,
|
||||
width: 63.h,
|
||||
height: 63.h,
|
||||
fit: BoxFit.cover,
|
||||
).circle(100.r),
|
||||
Transform.translate(
|
||||
offset: Offset(0.0, -20.h),
|
||||
child: Container(
|
||||
width: 40.w,
|
||||
height: 40.h,
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.whiteColor,
|
||||
shape: BoxShape.circle, // Makes the container circular
|
||||
border: Border.all(
|
||||
color: AppColors.scaffoldBgColor, // Color of the border
|
||||
width: 1.5.w, // Width of the border
|
||||
),
|
||||
),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Utils.buildSvgWithAssets(icon: AppAssets.rating_icon, width: 15.w, height: 15.h),
|
||||
SizedBox(height: 2.h),
|
||||
"${getInvoicesListResponseModel.decimalDoctorRate}".toText11(isBold: true, color: AppColors.textColor),
|
||||
],
|
||||
),
|
||||
).circle(100),
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(width: 16.w),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
(getIt<AppState>().isArabic() ? getInvoicesListResponseModel.doctorNameN! : getInvoicesListResponseModel.doctorName!).toText16(isBold: true),
|
||||
SizedBox(height: 8.h),
|
||||
Wrap(
|
||||
direction: Axis.horizontal,
|
||||
spacing: 6.w,
|
||||
runSpacing: 6.h,
|
||||
children: [
|
||||
AppCustomChipWidget(
|
||||
labelText: "${LocaleKeys.invoiceNo}: ${getInvoicesListResponseModel.invoiceNo!}",
|
||||
labelPadding: EdgeInsetsDirectional.only(start: 6.w, end: 6.w),
|
||||
),
|
||||
AppCustomChipWidget(
|
||||
labelText:
|
||||
(getInvoicesListResponseModel.clinicName!.length > 15 ? '${getInvoicesListResponseModel.clinicName!.substring(0, 12)}...' : getInvoicesListResponseModel.clinicName!),
|
||||
labelPadding: EdgeInsetsDirectional.only(start: 4.w, end: 4.w),
|
||||
),
|
||||
AppCustomChipWidget(
|
||||
labelText: getInvoicesListResponseModel.projectName!,
|
||||
labelPadding: EdgeInsetsDirectional.only(start: 6.w, end: 6.w),
|
||||
),
|
||||
AppCustomChipWidget(
|
||||
labelPadding: EdgeInsetsDirectional.only(start: -4.w, end: 6.w),
|
||||
icon: AppAssets.doctor_calendar_icon,
|
||||
labelText: DateUtil.formatDateToDate(DateUtil.convertStringToDate(getInvoicesListResponseModel.appointmentDate), false),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 16.h),
|
||||
CustomButton(
|
||||
text: "View invoice details".needTranslation,
|
||||
onPressed: () {
|
||||
if (onTap != null) {
|
||||
onTap!();
|
||||
}
|
||||
},
|
||||
backgroundColor: AppColors.primaryRedColor.withValues(alpha: 0.1),
|
||||
borderColor: AppColors.primaryRedColor.withValues(alpha: 0.01),
|
||||
textColor: AppColors.primaryRedColor,
|
||||
fontSize: 14.f,
|
||||
fontWeight: FontWeight.w500,
|
||||
borderRadius: 12.r,
|
||||
padding: EdgeInsets.symmetric(horizontal: 10.w),
|
||||
height: 40.h,
|
||||
iconSize: 14.h,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
).paddingOnly(bottom: 16.h);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,93 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
|
||||
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
|
||||
import 'package:hmg_patient_app_new/theme/colors.dart';
|
||||
|
||||
class VitalSignShimmerWidget extends StatelessWidget {
|
||||
const VitalSignShimmerWidget({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Row(
|
||||
children: [
|
||||
// BMI Card Shimmer
|
||||
Expanded(child: _buildShimmerCard()),
|
||||
SizedBox(width: 8.w),
|
||||
// Height Card Shimmer
|
||||
Expanded(child: _buildShimmerCard()),
|
||||
SizedBox(width: 8.w),
|
||||
// Weight Card Shimmer
|
||||
Expanded(child: _buildShimmerCard()),
|
||||
SizedBox(width: 8.w),
|
||||
// Blood Pressure Card Shimmer
|
||||
Expanded(child: _buildShimmerCard()),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildShimmerCard() {
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.whiteColor,
|
||||
borderRadius: BorderRadius.circular(12.r),
|
||||
),
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(12.w),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
// Icon shimmer
|
||||
Container(
|
||||
width: 32.w,
|
||||
height: 32.h,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(8.r),
|
||||
),
|
||||
).toShimmer(),
|
||||
SizedBox(height: 8.h),
|
||||
// Label shimmer
|
||||
Container(
|
||||
width: 50.w,
|
||||
height: 10.h,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(4.r),
|
||||
),
|
||||
).toShimmer(),
|
||||
SizedBox(height: 4.h),
|
||||
// Value shimmer
|
||||
Container(
|
||||
width: 40.w,
|
||||
height: 16.h,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(4.r),
|
||||
),
|
||||
).toShimmer(),
|
||||
SizedBox(height: 4.h),
|
||||
// Chip shimmer
|
||||
Container(
|
||||
width: 45.w,
|
||||
height: 18.h,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(12.r),
|
||||
),
|
||||
).toShimmer(),
|
||||
SizedBox(height: 4.h),
|
||||
// Arrow shimmer
|
||||
Align(
|
||||
alignment: AlignmentDirectional.centerEnd,
|
||||
child: Container(
|
||||
width: 10.w,
|
||||
height: 10.h,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(2.r),
|
||||
),
|
||||
).toShimmer(),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,348 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hmg_patient_app_new/core/app_assets.dart';
|
||||
import 'package:hmg_patient_app_new/core/app_export.dart';
|
||||
import 'package:hmg_patient_app_new/core/utils/utils.dart';
|
||||
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
|
||||
import 'package:hmg_patient_app_new/theme/colors.dart';
|
||||
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
|
||||
|
||||
/// A reusable time picker widget that can be used anywhere in the app
|
||||
/// Shows a bottom sheet with iOS-style time picker
|
||||
class TimePickerWidget {
|
||||
/// Shows a time picker bottom sheet
|
||||
///
|
||||
/// [context] - BuildContext for showing the bottom sheet
|
||||
/// [initialTime] - Initial time to display (defaults to current time)
|
||||
/// [use24HourFormat] - Whether to use 24-hour format (defaults to false)
|
||||
/// [onTimeSelected] - Callback when time is selected
|
||||
///
|
||||
/// Returns the selected TimeOfDay or null if cancelled
|
||||
static Future<TimeOfDay?> show(
|
||||
BuildContext context, {
|
||||
TimeOfDay? initialTime,
|
||||
bool use24HourFormat = false,
|
||||
bool displaySelectedTime = false,
|
||||
Function(TimeOfDay)? onTimeSelected,
|
||||
}) async {
|
||||
final selectedTime = initialTime ?? TimeOfDay.now();
|
||||
|
||||
final result = await showModalBottomSheet<TimeOfDay>(
|
||||
context: context,
|
||||
backgroundColor: Colors.transparent,
|
||||
isScrollControlled: true,
|
||||
builder: (BuildContext context) {
|
||||
return _TimePickerBottomSheet(
|
||||
initialTime: selectedTime,
|
||||
use24HourFormat: use24HourFormat,
|
||||
displaySelectedTime: displaySelectedTime,
|
||||
onTimeChanged: (time) {
|
||||
// Time is being changed in real-time
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
if (result != null && onTimeSelected != null) {
|
||||
onTimeSelected(result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Formats TimeOfDay to string (HH:mm format)
|
||||
static String formatTime(TimeOfDay time, {bool use24HourFormat = false}) {
|
||||
if (use24HourFormat) {
|
||||
return '${time.hour.toString().padLeft(2, '0')}:${time.minute.toString().padLeft(2, '0')}';
|
||||
} else {
|
||||
final hour = time.hourOfPeriod == 0 ? 12 : time.hourOfPeriod;
|
||||
final period = time.period == DayPeriod.am ? 'AM' : 'PM';
|
||||
return '${hour.toString().padLeft(2, '0')}:${time.minute.toString().padLeft(2, '0')} $period';
|
||||
}
|
||||
}
|
||||
|
||||
/// Parses time string to TimeOfDay
|
||||
static TimeOfDay? parseTime(String timeString) {
|
||||
try {
|
||||
final parts = timeString.split(':');
|
||||
if (parts.length == 2) {
|
||||
final hour = int.parse(parts[0]);
|
||||
final minute = int.parse(parts[1].split(' ')[0]);
|
||||
return TimeOfDay(hour: hour, minute: minute);
|
||||
}
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
class _TimePickerBottomSheet extends StatefulWidget {
|
||||
final TimeOfDay initialTime;
|
||||
final bool use24HourFormat;
|
||||
final bool displaySelectedTime;
|
||||
final Function(TimeOfDay) onTimeChanged;
|
||||
|
||||
const _TimePickerBottomSheet({
|
||||
required this.initialTime,
|
||||
required this.use24HourFormat,
|
||||
required this.displaySelectedTime,
|
||||
required this.onTimeChanged,
|
||||
});
|
||||
|
||||
@override
|
||||
State<_TimePickerBottomSheet> createState() => _TimePickerBottomSheetState();
|
||||
}
|
||||
|
||||
class _TimePickerBottomSheetState extends State<_TimePickerBottomSheet> {
|
||||
late int selectedHour;
|
||||
late int selectedMinute;
|
||||
late DayPeriod selectedPeriod;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
selectedHour = widget.use24HourFormat ? widget.initialTime.hour : widget.initialTime.hourOfPeriod;
|
||||
if (selectedHour == 0 && !widget.use24HourFormat) selectedHour = 12;
|
||||
selectedMinute = widget.initialTime.minute;
|
||||
selectedPeriod = widget.initialTime.period;
|
||||
}
|
||||
|
||||
TimeOfDay _getCurrentTime() {
|
||||
if (widget.use24HourFormat) {
|
||||
return TimeOfDay(hour: selectedHour, minute: selectedMinute);
|
||||
} else {
|
||||
int hour = selectedHour;
|
||||
if (selectedPeriod == DayPeriod.pm && hour != 12) {
|
||||
hour += 12;
|
||||
} else if (selectedPeriod == DayPeriod.am && hour == 12) {
|
||||
hour = 0;
|
||||
}
|
||||
return TimeOfDay(hour: hour, minute: selectedMinute);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.whiteColor,
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: Radius.circular(20.r),
|
||||
topRight: Radius.circular(20.r),
|
||||
),
|
||||
),
|
||||
child: SafeArea(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
// Header
|
||||
Container(
|
||||
padding: EdgeInsets.symmetric(horizontal: 20.w, vertical: 16.h),
|
||||
decoration: BoxDecoration(
|
||||
border: Border(
|
||||
bottom: BorderSide(
|
||||
color: AppColors.dividerColor,
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
"Select Time".needTranslation.toText18(
|
||||
weight: FontWeight.w600,
|
||||
color: AppColors.textColor,
|
||||
),
|
||||
GestureDetector(
|
||||
onTap: () => Navigator.pop(context),
|
||||
child: Utils.buildSvgWithAssets(
|
||||
icon: AppAssets.cancel,
|
||||
width: 24.h,
|
||||
height: 24.h,
|
||||
iconColor: AppColors.textColor,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
// Time Picker
|
||||
SizedBox(
|
||||
height: 250.h,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
// Hour Picker
|
||||
Expanded(
|
||||
child: CupertinoPicker(
|
||||
scrollController: FixedExtentScrollController(
|
||||
initialItem: widget.use24HourFormat ? selectedHour : (selectedHour - 1),
|
||||
),
|
||||
itemExtent: 50.h,
|
||||
onSelectedItemChanged: (index) {
|
||||
setState(() {
|
||||
if (widget.use24HourFormat) {
|
||||
selectedHour = index;
|
||||
} else {
|
||||
selectedHour = index + 1;
|
||||
}
|
||||
widget.onTimeChanged(_getCurrentTime());
|
||||
});
|
||||
},
|
||||
children: List.generate(
|
||||
widget.use24HourFormat ? 24 : 12,
|
||||
(index) {
|
||||
final hour = widget.use24HourFormat ? index : index + 1;
|
||||
return Center(
|
||||
child: Text(
|
||||
hour.toString().padLeft(2, '0'),
|
||||
style: TextStyle(
|
||||
fontSize: 24.f,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: AppColors.textColor,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
// Separator
|
||||
Text(
|
||||
':',
|
||||
style: TextStyle(
|
||||
fontSize: 24.f,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: AppColors.textColor,
|
||||
),
|
||||
),
|
||||
|
||||
// Minute Picker
|
||||
Expanded(
|
||||
child: CupertinoPicker(
|
||||
scrollController: FixedExtentScrollController(
|
||||
initialItem: selectedMinute,
|
||||
),
|
||||
itemExtent: 50.h,
|
||||
onSelectedItemChanged: (index) {
|
||||
setState(() {
|
||||
selectedMinute = index;
|
||||
widget.onTimeChanged(_getCurrentTime());
|
||||
});
|
||||
},
|
||||
children: List.generate(
|
||||
60,
|
||||
(index) => Center(
|
||||
child: Text(
|
||||
index.toString().padLeft(2, '0'),
|
||||
style: TextStyle(
|
||||
fontSize: 24.f,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: AppColors.textColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
// AM/PM Picker (only for 12-hour format)
|
||||
if (!widget.use24HourFormat)
|
||||
Expanded(
|
||||
child: CupertinoPicker(
|
||||
scrollController: FixedExtentScrollController(
|
||||
initialItem: selectedPeriod == DayPeriod.am ? 0 : 1,
|
||||
),
|
||||
itemExtent: 50.h,
|
||||
onSelectedItemChanged: (index) {
|
||||
setState(() {
|
||||
selectedPeriod = index == 0 ? DayPeriod.am : DayPeriod.pm;
|
||||
widget.onTimeChanged(_getCurrentTime());
|
||||
});
|
||||
},
|
||||
children: [
|
||||
Center(
|
||||
child: Text(
|
||||
'AM',
|
||||
style: TextStyle(
|
||||
fontSize: 24.f,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: AppColors.textColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
Center(
|
||||
child: Text(
|
||||
'PM',
|
||||
style: TextStyle(
|
||||
fontSize: 24.f,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: AppColors.textColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
if (widget.displaySelectedTime)
|
||||
// Current Time Display
|
||||
Container(
|
||||
margin: EdgeInsets.symmetric(horizontal: 20.w, vertical: 16.h),
|
||||
padding: EdgeInsets.symmetric(vertical: 12.h),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.lightGrayBGColor,
|
||||
borderRadius: BorderRadius.circular(12.r),
|
||||
),
|
||||
child: Center(
|
||||
child: TimePickerWidget.formatTime(
|
||||
_getCurrentTime(),
|
||||
use24HourFormat: widget.use24HourFormat,
|
||||
).toText20(
|
||||
weight: FontWeight.w600,
|
||||
color: AppColors.textColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
// Action Buttons
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 20.w, vertical: 16.h),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: CustomButton(
|
||||
height: 56.h,
|
||||
text: "Cancel".needTranslation,
|
||||
onPressed: () => Navigator.pop(context),
|
||||
textColor: AppColors.textColor,
|
||||
backgroundColor: AppColors.greyColor,
|
||||
borderColor: AppColors.greyColor,
|
||||
),
|
||||
),
|
||||
SizedBox(width: 12.w),
|
||||
Expanded(
|
||||
child: CustomButton(
|
||||
height: 56.h,
|
||||
text: "Confirm".needTranslation,
|
||||
onPressed: () {
|
||||
Navigator.pop(context, _getCurrentTime());
|
||||
},
|
||||
textColor: AppColors.whiteColor,
|
||||
backgroundColor: AppColors.primaryRedColor,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,165 @@
|
||||
// Example usage of TimePickerWidget
|
||||
//
|
||||
// This file demonstrates how to use the TimePickerWidget in your Flutter app.
|
||||
// The TimePickerWidget is a reusable component that shows a bottom sheet with
|
||||
// an iOS-style time picker.
|
||||
|
||||
// ============================================================================
|
||||
// EXAMPLE 1: Using with TextInputWidget
|
||||
// ============================================================================
|
||||
/*
|
||||
TextInputWidget(
|
||||
labelText: "Appointment Time",
|
||||
hintText: "Select time",
|
||||
controller: timeController,
|
||||
selectionType: SelectionTypeEnum.time,
|
||||
isReadOnly: true,
|
||||
onChange: (value) {
|
||||
print("Selected time: $value");
|
||||
},
|
||||
)
|
||||
*/
|
||||
|
||||
// ============================================================================
|
||||
// EXAMPLE 2: Direct usage with custom button
|
||||
// ============================================================================
|
||||
/*
|
||||
ElevatedButton(
|
||||
onPressed: () async {
|
||||
final selectedTime = await TimePickerWidget.show(
|
||||
context,
|
||||
initialTime: TimeOfDay.now(),
|
||||
use24HourFormat: false,
|
||||
onTimeSelected: (time) {
|
||||
print("Time selected: ${TimePickerWidget.formatTime(time)}");
|
||||
},
|
||||
);
|
||||
|
||||
if (selectedTime != null) {
|
||||
print("Final time: ${TimePickerWidget.formatTime(selectedTime)}");
|
||||
}
|
||||
},
|
||||
child: Text("Pick Time"),
|
||||
)
|
||||
*/
|
||||
|
||||
// ============================================================================
|
||||
// EXAMPLE 3: Using with 24-hour format
|
||||
// ============================================================================
|
||||
/*
|
||||
TextInputWidget(
|
||||
labelText: "Meeting Time",
|
||||
hintText: "Select time (24h)",
|
||||
controller: timeController,
|
||||
selectionType: SelectionTypeEnum.time,
|
||||
isReadOnly: true,
|
||||
onChange: (value) {
|
||||
// The value will be formatted as "14:30" for 2:30 PM in 24h format
|
||||
print("Selected time (24h): $value");
|
||||
},
|
||||
)
|
||||
|
||||
// Or programmatically:
|
||||
final time = await TimePickerWidget.show(
|
||||
context,
|
||||
use24HourFormat: true, // Enable 24-hour format
|
||||
);
|
||||
*/
|
||||
|
||||
// ============================================================================
|
||||
// EXAMPLE 4: Parsing and formatting times
|
||||
// ============================================================================
|
||||
/*
|
||||
// Parse time string to TimeOfDay
|
||||
String timeString = "02:30 PM";
|
||||
TimeOfDay? parsedTime = TimePickerWidget.parseTime(timeString);
|
||||
|
||||
// Format TimeOfDay to string
|
||||
TimeOfDay time = TimeOfDay(hour: 14, minute: 30);
|
||||
String formatted12h = TimePickerWidget.formatTime(time); // "02:30 PM"
|
||||
String formatted24h = TimePickerWidget.formatTime(time, use24HourFormat: true); // "14:30"
|
||||
*/
|
||||
|
||||
// ============================================================================
|
||||
// EXAMPLE 5: Complete form example with date and time
|
||||
// ============================================================================
|
||||
/*
|
||||
class AppointmentForm extends StatefulWidget {
|
||||
@override
|
||||
_AppointmentFormState createState() => _AppointmentFormState();
|
||||
}
|
||||
|
||||
class _AppointmentFormState extends State<AppointmentForm> {
|
||||
final TextEditingController dateController = TextEditingController();
|
||||
final TextEditingController timeController = TextEditingController();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
children: [
|
||||
// Date picker
|
||||
TextInputWidget(
|
||||
labelText: "Appointment Date",
|
||||
hintText: "Select date",
|
||||
controller: dateController,
|
||||
selectionType: SelectionTypeEnum.calendar,
|
||||
isReadOnly: true,
|
||||
onChange: (value) {
|
||||
print("Date selected: $value");
|
||||
},
|
||||
),
|
||||
|
||||
SizedBox(height: 16),
|
||||
|
||||
// Time picker
|
||||
TextInputWidget(
|
||||
labelText: "Appointment Time",
|
||||
hintText: "Select time",
|
||||
controller: timeController,
|
||||
selectionType: SelectionTypeEnum.time,
|
||||
isReadOnly: true,
|
||||
onChange: (value) {
|
||||
print("Time selected: $value");
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
// ============================================================================
|
||||
// Features:
|
||||
// ============================================================================
|
||||
// ✅ iOS-style cupertino picker (works on both iOS and Android)
|
||||
// ✅ Support for 12-hour format (with AM/PM)
|
||||
// ✅ Support for 24-hour format
|
||||
// ✅ Beautiful bottom sheet UI matching app design
|
||||
// ✅ Real-time preview of selected time
|
||||
// ✅ Confirm/Cancel buttons
|
||||
// ✅ Easy integration with TextInputWidget
|
||||
// ✅ Parse and format time utilities
|
||||
// ✅ Fully customizable and reusable
|
||||
|
||||
// ============================================================================
|
||||
// API Reference:
|
||||
// ============================================================================
|
||||
// TimePickerWidget.show() - Shows the time picker bottom sheet
|
||||
// Parameters:
|
||||
// - context: BuildContext (required)
|
||||
// - initialTime: TimeOfDay? (optional, defaults to current time)
|
||||
// - use24HourFormat: bool (optional, defaults to false)
|
||||
// - onTimeSelected: Function(TimeOfDay)? (optional callback)
|
||||
// Returns: Future<TimeOfDay?>
|
||||
//
|
||||
// TimePickerWidget.formatTime() - Formats TimeOfDay to string
|
||||
// Parameters:
|
||||
// - time: TimeOfDay (required)
|
||||
// - use24HourFormat: bool (optional, defaults to false)
|
||||
// Returns: String (e.g., "02:30 PM" or "14:30")
|
||||
//
|
||||
// TimePickerWidget.parseTime() - Parses time string to TimeOfDay
|
||||
// Parameters:
|
||||
// - timeString: String (required, e.g., "02:30 PM")
|
||||
// Returns: TimeOfDay?
|
||||
|
||||